Fix frontend scripts settings (#1492)

* fix(frontend): fix recomputa all

* fix(frontend): fix frontend scripts

* fix(frontend): fix frontend scripts

* fix(frontend): wip

* fix(frontend): wip

* fix(frontend): fix isScriptByNameDefined

* fix(frontend): fix isScriptByNameDefined

* fix(frontend): fix

* fix(frontend): remove console.log

* fix(frontend): fix multiselect

* fix(frontend): revert changes

* fix(frontend): add migration code

* fix(frontend): fix isRunnableDefined
This commit is contained in:
Faton Ramadani
2023-04-28 19:17:32 +02:00
committed by GitHub
parent fb05a09955
commit 52e9887752
16 changed files with 216 additions and 203 deletions
@@ -118,6 +118,7 @@
{outputs}
{extraKey}
refreshOnStart={resolvedConfig.triggerOnAppLoad}
triggerable
>
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
{#if errorsMessage}
@@ -71,6 +71,7 @@
runnableClass="!block"
runnableStyle={css?.container?.style}
{outputs}
triggerable
>
<AlignWrapper {horizontalAlignment}>
<div
@@ -91,6 +91,7 @@
runnableClass="!block"
{outputs}
doOnSuccess={resolvedConfig.onSuccess}
triggerable
>
<div class="flex flex-col gap-2 px-4 w-full">
<div>
@@ -14,9 +14,9 @@
export let name: string
export let inlineScript: InlineScript | undefined
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
export let recomputeOnInputChanged: boolean
export let recomputableByRefreshButton: boolean
export let autoRefresh: boolean
export let noBackendValue: any = undefined
export let recomputeOnInputChanged: boolean
const { worldStore, staticExporter, noBackend } = getContext<AppViewerContext>('AppViewerContext')
@@ -38,10 +38,10 @@
render={false}
{id}
{fields}
autoRefresh={true}
{recomputeOnInputChanged}
{autoRefresh}
bind:result
transformer={undefined}
{recomputeOnInputChanged}
runnable={{
name,
inlineScript,
@@ -49,7 +49,6 @@
}}
wrapperClass="hidden"
{outputs}
{recomputableByRefreshButton}
>
<slot />
</RunnableComponent>
@@ -34,7 +34,6 @@
export let extraKey = ''
export let recomputeOnInputChanged: boolean = true
export let loading = false
export let recomputableByRefreshButton: boolean = true
export let refreshOnStart: boolean = false
const {
@@ -351,8 +350,8 @@
}
$runnableComponents[id] = {
autoRefresh: autoRefresh && recomputableByRefreshButton,
refreshOnStart,
autoRefresh: autoRefresh,
refreshOnStart: refreshOnStart,
cb: cancellableRun
}
@@ -42,6 +42,7 @@
export let outputs: { result: Output<any>; loading: Output<boolean> }
export let extraKey: string | undefined = undefined
export let refreshOnStart: boolean = false
export let triggerable: boolean = false
const { staticExporter, noBackend, componentControl, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
@@ -59,8 +60,18 @@
}
})
// We need to make sure that old apps have correct values. Triggerable (button, form, etc) have both autoRefresh and recomputeOnInputChanged set to false
$: if (triggerable && componentInput?.type === 'runnable' && componentInput.autoRefresh) {
componentInput.autoRefresh = false
componentInput.recomputeOnInputChanged = false
}
function isRunnableDefined(componentInput) {
return isScriptByNameDefined(componentInput) || isScriptByPathDefined(componentInput)
return (
(isScriptByNameDefined(componentInput) &&
componentInput.runnable.inlineScript != undefined) ||
isScriptByPathDefined(componentInput)
)
}
export function onSuccess() {
@@ -113,11 +124,9 @@
bind:result
runnable={componentInput.runnable}
transformer={componentInput.transformer}
autoRefresh={autoRefresh !== undefined
autoRefresh={componentInput.autoRefresh === undefined
? autoRefresh
: componentInput?.type === 'runnable'
? componentInput.autoRefresh
: false}
: componentInput.autoRefresh}
bind:recomputeOnInputChanged={componentInput.recomputeOnInputChanged}
{id}
{extraQueryParams}
@@ -48,7 +48,7 @@
}
}
$: value && outputs?.result.set(value.map((v) => v.value))
$: value ? outputs?.result.set(value.map((v) => v.value)) : outputs?.result.set([])
$: css = concatCustomCss($app.css?.multiselectcomponent, customCss)
</script>
@@ -171,7 +171,7 @@
name={script.name}
fields={script.fields}
recomputeOnInputChanged={script.recomputeOnInputChanged ?? true}
recomputableByRefreshButton={script.autoRefresh ?? false}
autoRefresh={script.autoRefresh ?? false}
noBackendValue={script.noBackendValue}
/>
{/if}
@@ -129,7 +129,7 @@
name={script.name}
fields={script.fields}
recomputeOnInputChanged={script.recomputeOnInputChanged ?? true}
recomputableByRefreshButton={script.autoRefresh ?? false}
autoRefresh={script.autoRefresh ?? false}
/>
{/if}
{/each}
@@ -68,8 +68,6 @@
hiddenInlineScript.script.doNotRecomputeOnInputChanged = undefined
}
}
$: hasScript = hiddenInlineScript?.script?.inlineScript != undefined
</script>
{#if componentSettings}
@@ -101,7 +99,6 @@
<BackgroundScriptSettings
bind:script={hiddenInlineScript.script}
id={`bg_${hiddenInlineScript.index}`}
{hasScript}
/>
<div>
@@ -59,9 +59,9 @@
push(history, $app)
const id = componentSettings?.item?.id
const onDelete = id ? $componentControl[id]?.onDelete : undefined
if (onDelete) {
onDelete()
const onDeleteComponentControl = id ? $componentControl[id]?.onDelete : undefined
if (onDeleteComponentControl) {
onDeleteComponentControl()
}
if (componentSettings?.item.id) {
@@ -8,28 +8,29 @@
export let script: HiddenInlineScript
export let id: string
export let hasScript: boolean
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
function updateAutoRefresh() {
const autoRefresh = script.autoRefresh
if ($runnableComponents?.[id]?.autoRefresh !== autoRefresh && autoRefresh !== undefined) {
$runnableComponents[id] = {
...$runnableComponents[id],
autoRefresh
}
}
}
</script>
<div class={'border-y border-gray-200 divide-y'}>
<ScriptSettingHeader name={script.name} />
{#if hasScript}
{#if script.inlineScript}
<ScriptRunConfiguration
bind:autoRefresh={script.autoRefresh}
bind:recomputeOnInputChanged={script.recomputeOnInputChanged}
canConfigureRecomputeOnInputChanged={script.inlineScript?.language !== 'frontend'}
on:updateAutoRefresh={() => {
const autoRefresh = script.autoRefresh
if ($runnableComponents?.[id]?.autoRefresh !== autoRefresh && autoRefresh !== undefined) {
$runnableComponents[id] = {
...$runnableComponents[id],
autoRefresh
}
}
}}
on:updateAutoRefresh={updateAutoRefresh}
/>
<BackgroundScriptTriggerBy
bind:script
@@ -10,7 +10,7 @@
<script lang="ts">
import type { ResultAppInput } from '$lib/components/apps/inputType'
import type { ButtonType } from '$lib/components/common/button/model'
import { isTriggerable } from './utils'
import { isTriggerable, isFrontend } from './utils'
import type { AppComponent } from '../../component'
import ScriptTransformer from './shared/ScriptTransformer.svelte'
@@ -29,6 +29,23 @@
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
export let actions: ActionType[] = []
function updateAutoRefresh() {
const autoRefresh =
appComponent.componentInput?.type === 'runnable' && appComponent?.componentInput?.autoRefresh
if (
appComponent.componentInput?.type === 'runnable' &&
$runnableComponents?.[appComponent.id]?.autoRefresh !== autoRefresh &&
!isTriggerable(appComponent.type) &&
autoRefresh !== undefined
) {
$runnableComponents[appComponent.id] = {
...$runnableComponents[appComponent.id],
autoRefresh
}
}
}
</script>
<div class={'border border-gray-200 divide-y'}>
@@ -44,27 +61,12 @@
{#if hasScript}
<ScriptTransformer bind:appInput bind:appComponent />
<ScriptRunConfiguration
canConfigureRecomputeOnInputChanged={!isTriggerable(appComponent.type)}
canConfigureRecomputeOnInputChanged={!isTriggerable(appComponent.type) &&
!isFrontend(appInput.runnable)}
canConfigureRunOnStart={!isTriggerable(appComponent.type)}
bind:autoRefresh={appInput.autoRefresh}
bind:recomputeOnInputChanged={appInput.recomputeOnInputChanged}
on:updateAutoRefresh={() => {
const autoRefresh =
appComponent.componentInput?.type === 'runnable' &&
appComponent?.componentInput?.autoRefresh
if (
appComponent.componentInput?.type === 'runnable' &&
$runnableComponents?.[appComponent.id]?.autoRefresh !== autoRefresh &&
!isTriggerable(appComponent.type) &&
autoRefresh !== undefined
) {
$runnableComponents[appComponent.id] = {
...$runnableComponents[appComponent.id],
autoRefresh
}
}
}}
on:updateAutoRefresh={updateAutoRefresh}
/>
<ComponentScriptTriggerBy {appComponent} {appInput} />
{:else}
@@ -1,124 +1,22 @@
<script lang="ts">
import type { AppViewerContext, HiddenInlineScript } from '$lib/components/apps/types'
import Alert from '$lib/components/common/alert/Alert.svelte'
import Button from '$lib/components/common/button/Button.svelte'
import { classNames } from '$lib/utils'
import { Plus, X } from 'lucide-svelte'
import ScriptSettingsSection from './ScriptSettingsSection.svelte'
import { getContext } from 'svelte'
import type { InputConnection } from '$lib/components/apps/inputType'
import type { HiddenInlineScript } from '$lib/components/apps/types'
import { getDependencies } from '../utils'
import ScriptTriggers from './ScriptTriggers.svelte'
export let script: HiddenInlineScript
export let recomputeOnInputChanged: boolean | undefined = undefined
$: isFrontend = script.inlineScript?.language === 'frontend'
$: triggerEvents = script.autoRefresh ? ['start', 'refresh'] : []
$: changeEvents = isFrontend
? script.inlineScript?.refreshOn
? script.inlineScript.refreshOn.map((x) => `${x.id} - ${x.key}`)
: []
: getDependencies(script.fields)
$: hasNoTriggers =
triggerEvents.length === 0 && (changeEvents.length === 0 || !recomputeOnInputChanged)
const badgeClass = 'inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium border'
const colors = {
green: 'text-green-800 border-green-600 bg-green-100',
indigo: 'text-indigo-800 border-indigo-600 bg-indigo-100',
blue: 'text-blue-800 border-blue-600 bg-blue-100'
}
const { app } = getContext<AppViewerContext>('AppViewerContext')
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
function applyConnection(connection: InputConnection) {
const refresh = {
id: connection.componentId,
key: connection.path
}
if (!script.inlineScript) {
return
}
if (script.inlineScript.refreshOn?.find((y) => y.id === refresh.id && y.key === refresh.key)) {
return
}
if (!script.inlineScript.refreshOn) {
script.inlineScript.refreshOn = [refresh]
} else {
script.inlineScript.refreshOn.push(refresh)
}
script.inlineScript = JSON.parse(JSON.stringify(script.inlineScript))
$app = $app
}
</script>
<ScriptSettingsSection title="Triggers">
{#if isFrontend}
<div class="flex mb-4">
<Button
size="xs2"
color="dark"
on:click={() => {
$connectingInput = {
opened: true,
input: undefined,
hoveredComponent: undefined,
onConnect: applyConnection
}
}}
>
<div class="flex flex-row gap-1 items-center">
<Plus size={14} />
Add dependency
</div>
</Button>
</div>
{/if}
{#if hasNoTriggers}
<Alert type="warning" title="No triggers" size="xs">
This script has no triggers. It will never run.
</Alert>
{:else}
{#if triggerEvents.length > 0}
<div class="text-xs font-semibold text-slate-800 mb-1">Events</div>
<div class="flex flex-row gap-2 flex-wrap">
{#each triggerEvents as triggerEvent}
<span class={classNames(badgeClass, colors['green'])}>{triggerEvent}</span>
{/each}
</div>
{/if}
{#if changeEvents.length > 0 && (recomputeOnInputChanged || isFrontend)}
<div class="text-xs font-semibold text-slate-800 mb-1 mt-2">Change on value</div>
<div class="flex flex-row gap-2 flex-wrap">
{#each changeEvents as changeEvent}
<span class={classNames(badgeClass, colors['blue'])}>
{changeEvent}
{#if isFrontend}
<button
class="bg-blue-300 ml-2 p-0.5 rounded-md hover:bg-blue-400 cursor-pointer"
on:click={() => {
if (script.inlineScript?.refreshOn) {
script.inlineScript.refreshOn = script.inlineScript.refreshOn.filter(
(x) => `${x.id} - ${x.key}` !== changeEvent
)
script.inlineScript = JSON.parse(JSON.stringify(script.inlineScript))
}
}}
>
<X size="14" />
</button>
{/if}
</span>
<!-- delete button -->
{/each}
</div>
{/if}
{/if}
</ScriptSettingsSection>
{#if script.inlineScript}
<ScriptTriggers
bind:inlineScript={script.inlineScript}
{triggerEvents}
dependencies={getDependencies(script.fields)}
{isFrontend}
shoudlDisplayChangeEvents={recomputeOnInputChanged || isFrontend}
/>
{/if}
@@ -1,48 +1,34 @@
<script lang="ts">
import type { ResultAppInput } from '$lib/components/apps/inputType'
import Alert from '$lib/components/common/alert/Alert.svelte'
import { classNames } from '$lib/utils'
import type { AppComponent } from '../../../component'
import { getAllTriggerEvents, isTriggerable, getDependencies } from '../utils'
import ScriptSettingsSection from './ScriptSettingsSection.svelte'
import ScriptTriggers from './ScriptTriggers.svelte'
export let appComponent: AppComponent
export let appInput: ResultAppInput
$: triggerEvents = getAllTriggerEvents(appComponent, appInput.autoRefresh)
$: changeEvents = getDependencies(appInput.fields)
$: hasNoTriggers =
triggerEvents.length === 0 && (changeEvents.length === 0 || !appInput.recomputeOnInputChanged)
const badgeClass = 'inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium border'
const colors = {
green: 'text-green-800 border-green-600 bg-green-100',
indigo: 'text-indigo-800 border-indigo-600 bg-indigo-100',
blue: 'text-blue-800 border-blue-600 bg-blue-100'
}
$: isFrontend =
appInput.runnable?.type == 'runnableByName' &&
appInput.runnable?.inlineScript?.language === 'frontend'
$: shoudlDisplayChangeEvents =
appInput.recomputeOnInputChanged && !isTriggerable(appComponent.type)
</script>
<ScriptSettingsSection title="Triggers">
{#if hasNoTriggers}
<Alert type="warning" title="No triggers" size="xs">
This script has no triggers. It will never run.
</Alert>
{:else}
{#if triggerEvents.length > 0}
<div class="text-xs font-semibold text-slate-800 mb-1">Events</div>
<div class="flex flex-row gap-2 flex-wrap">
{#each triggerEvents as triggerEvent}
<span class={classNames(badgeClass, colors['green'])}>{triggerEvent}</span>
{/each}
</div>
{/if}
{#if changeEvents.length > 0 && appInput.recomputeOnInputChanged && !isTriggerable(appComponent.type)}
<div class="text-xs font-semibold text-slate-800 mb-1 mt-2">Change on value</div>
<div class="flex flex-row gap-2 flex-wrap">
{#each changeEvents as changeEvent}
<span class={classNames(badgeClass, colors['blue'])}>{changeEvent}</span>
{/each}
</div>
{/if}
{/if}
</ScriptSettingsSection>
{#if appInput?.runnable?.type === 'runnableByName'}
<ScriptTriggers
bind:inlineScript={appInput.runnable.inlineScript}
dependencies={getDependencies(appInput.fields)}
{isFrontend}
{triggerEvents}
{shoudlDisplayChangeEvents}
/>
{:else}
<ScriptTriggers
dependencies={getDependencies(appInput.fields)}
{triggerEvents}
{isFrontend}
{shoudlDisplayChangeEvents}
/>
{/if}
@@ -0,0 +1,119 @@
<script lang="ts">
import type { InputConnection } from '$lib/components/apps/inputType'
import Alert from '$lib/components/common/alert/Alert.svelte'
import { classNames } from '$lib/utils'
import { Plus, X } from 'lucide-svelte'
import ScriptSettingsSection from './ScriptSettingsSection.svelte'
import { Button } from '$lib/components/common'
import { getContext } from 'svelte'
import type { AppViewerContext, InlineScript } from '$lib/components/apps/types'
export let triggerEvents: string[] = []
export let inlineScript: InlineScript | undefined = undefined
export let isFrontend: boolean = false
export let dependencies: string[] = []
export let shoudlDisplayChangeEvents: boolean = false
$: changeEvents = isFrontend
? inlineScript?.refreshOn
? inlineScript.refreshOn.map((x) => `${x.id} - ${x.key}`)
: []
: dependencies
$: hasNoTriggers = triggerEvents.length === 0 && changeEvents.length === 0
const badgeClass = 'inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium border'
const colors = {
green: 'text-green-800 border-green-600 bg-green-100',
indigo: 'text-indigo-800 border-indigo-600 bg-indigo-100',
blue: 'text-blue-800 border-blue-600 bg-blue-100'
}
const { connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
function applyConnection(connection: InputConnection) {
const refresh = {
id: connection.componentId,
key: connection.path
}
if (!inlineScript) {
return
}
if (inlineScript.refreshOn?.find((y) => y.id === refresh.id && y.key === refresh.key)) {
return
}
if (!inlineScript.refreshOn) {
inlineScript.refreshOn = [refresh]
} else {
inlineScript.refreshOn.push(refresh)
}
inlineScript = JSON.parse(JSON.stringify(inlineScript))
$app = $app
}
</script>
<ScriptSettingsSection title="Triggers">
{#if isFrontend}
<div class="flex mb-4">
<Button
size="xs2"
color="dark"
on:click={() => {
$connectingInput = {
opened: true,
input: undefined,
hoveredComponent: undefined,
onConnect: applyConnection
}
}}
>
<div class="flex flex-row gap-1 items-center">
<Plus size={14} />
Add dependency
</div>
</Button>
</div>
{/if}
{#if hasNoTriggers}
<Alert type="warning" title="No triggers" size="xs">
This script has no triggers. It will never run.
</Alert>
{:else}
{#if triggerEvents.length > 0}
<div class="text-xs font-semibold text-slate-800 mb-1">Events</div>
<div class="flex flex-row gap-2 flex-wrap">
{#each triggerEvents as triggerEvent}
<span class={classNames(badgeClass, colors['green'])}>{triggerEvent}</span>
{/each}
</div>
{/if}
{#if changeEvents.length > 0 && shoudlDisplayChangeEvents}
<div class="text-xs font-semibold text-slate-800 mb-1 mt-2">Change on value</div>
<div class="flex flex-row gap-2 flex-wrap">
{#each changeEvents as changeEvent}
<span class={classNames(badgeClass, colors['blue'])}>
{changeEvent}
{#if isFrontend}
<button
class="bg-blue-300 ml-2 p-0.5 rounded-md hover:bg-blue-400 cursor-pointer"
on:click={() => {
if (inlineScript?.refreshOn) {
inlineScript.refreshOn = inlineScript.refreshOn.filter(
(x) => `${x.id} - ${x.key}` !== changeEvent
)
inlineScript = JSON.parse(JSON.stringify(inlineScript))
}
}}
>
<X size="14" />
</button>
{/if}
</span>
{/each}
</div>
{/if}
{/if}
</ScriptSettingsSection>