From 77e7e49372a765ec74a1ccda0fbe972952fe557e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 28 May 2023 18:02:42 +0200 Subject: [PATCH] feat(apps): add setValue to frontend script's SDK --- .../src/lib/components/SimpleEditor.svelte | 2 +- .../src/lib/components/TemplateEditor.svelte | 1 + .../components/buttons/AppSchemaForm.svelte | 8 +++- .../display/AppDisplayComponent.svelte | 8 +++- .../apps/components/display/AppText.svelte | 10 ++++- .../apps/components/helpers/InputValue.svelte | 37 ++++++++++--------- .../apps/components/helpers/eval.ts | 13 +++++-- .../apps/components/inputs/AppCheckbox.svelte | 33 +++++++++++++---- .../components/inputs/AppDateInput.svelte | 9 ++++- .../components/inputs/AppMultiSelect.svelte | 10 ++++- .../components/inputs/AppNumberInput.svelte | 9 ++++- .../components/inputs/AppRangeInput.svelte | 8 +++- .../apps/components/inputs/AppSelect.svelte | 17 +++++++-- .../components/inputs/AppSelectStep.svelte | 30 +++++++++++---- .../components/inputs/AppSelectTab.svelte | 11 +++++- .../components/inputs/AppSliderInputs.svelte | 9 ++++- .../components/inputs/AppTextInput.svelte | 8 +++- .../inputs/currency/AppCurrencyInput.svelte | 9 ++++- .../EmptyInlineScript.svelte | 1 + .../InlineScriptsPanelList.svelte | 4 +- frontend/src/lib/components/apps/types.ts | 1 + frontend/src/lib/components/apps/utils.ts | 1 + 22 files changed, 185 insertions(+), 54 deletions(-) diff --git a/frontend/src/lib/components/SimpleEditor.svelte b/frontend/src/lib/components/SimpleEditor.svelte index 2dc242d359..d4e74dbeb7 100644 --- a/frontend/src/lib/components/SimpleEditor.svelte +++ b/frontend/src/lib/components/SimpleEditor.svelte @@ -20,7 +20,7 @@ import { createEventDispatcher, onDestroy, onMount } from 'svelte' import libStdContent from '$lib/es5.d.ts.txt?raw' - import { buildWorkerDefinition } from 'monaco-editor-workers' + import { buildWorkerDefinition } from './build_workers' meditor.defineTheme('myTheme', { base: 'vs', diff --git a/frontend/src/lib/components/TemplateEditor.svelte b/frontend/src/lib/components/TemplateEditor.svelte index fd3407098b..09af14bcd1 100644 --- a/frontend/src/lib/components/TemplateEditor.svelte +++ b/frontend/src/lib/components/TemplateEditor.svelte @@ -390,6 +390,7 @@ if ($selectedComponent) { $componentControl[$selectedComponent[0]] = { + ...$componentControl[$selectedComponent[0]], setCode: (value: string) => { code = value setCode(value) diff --git a/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte b/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte index 6e83f13bcd..9cb50d6366 100644 --- a/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte +++ b/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte @@ -18,7 +18,7 @@ export let configuration: RichConfigurations export let customCss: ComponentCustomCSS<'schemaformcomponent'> | undefined = undefined - const { worldStore, connectingInput, app, selectedComponent } = + const { worldStore, connectingInput, app, selectedComponent, componentControl } = getContext('AppViewerContext') const outputs = initOutput($worldStore, id, { @@ -43,6 +43,12 @@ outputs.values.set(newArgs, true) } + $componentControl[id] = { + setValue(nvalue: any) { + args = nvalue + } + } + $: args && handleArgsChange() $: outputs.valid.set(valid) diff --git a/frontend/src/lib/components/apps/components/display/AppDisplayComponent.svelte b/frontend/src/lib/components/apps/components/display/AppDisplayComponent.svelte index 27f59a7e60..2e595343b4 100644 --- a/frontend/src/lib/components/apps/components/display/AppDisplayComponent.svelte +++ b/frontend/src/lib/components/apps/components/display/AppDisplayComponent.svelte @@ -19,9 +19,15 @@ export let render: boolean const requireHtmlApproval = getContext(IS_APP_PUBLIC_CONTEXT_KEY) - const { app, worldStore } = getContext('AppViewerContext') + const { app, worldStore, componentControl } = getContext('AppViewerContext') let result: any = undefined + $componentControl[id] = { + setValue(value: string) { + result = value + } + } + const outputs = initOutput($worldStore, id, { result: undefined, loading: false diff --git a/frontend/src/lib/components/apps/components/display/AppText.svelte b/frontend/src/lib/components/apps/components/display/AppText.svelte index 5a03ba309a..293f0b8ad3 100644 --- a/frontend/src/lib/components/apps/components/display/AppText.svelte +++ b/frontend/src/lib/components/apps/components/display/AppText.svelte @@ -48,6 +48,13 @@ initializing = false } + $componentControl[id] = { + ...$componentControl[id], + setValue(value: string) { + result = value + } + } + const outputs = initOutput($worldStore, id, { result, loading: initializing @@ -119,7 +126,7 @@ $: resolvedConfig.style && (component = getComponent()) $: resolvedConfig.style && (classes = getClasses()) $: initialValue = componentInput?.type == 'template' ? componentInput.eval : '' - $: editableValue = initialValue ? JSON.parse(JSON.stringify(initialValue)) : '' + $: editableValue = initialValue ?? '' let rows = 1 @@ -128,7 +135,6 @@ if (target.value) { $componentControl[id]?.setCode?.(target.value) - editableValue autosize() } } diff --git a/frontend/src/lib/components/apps/components/helpers/InputValue.svelte b/frontend/src/lib/components/apps/components/helpers/InputValue.svelte index ec39fc0bf4..8f0c081e1f 100644 --- a/frontend/src/lib/components/apps/components/helpers/InputValue.svelte +++ b/frontend/src/lib/components/apps/components/helpers/InputValue.svelte @@ -72,28 +72,29 @@ $: lastInput && $worldStore && debounce(handleConnection) - $: lastInput && - lastInput.type == 'template' && - $stateId && - $state && - debounce(async () => { - let nvalue = await getValue(lastInput) - if (!deepEqual(nvalue, value)) { - value = nvalue - } - dispatch('done') - }) + const debounceTemplate = async () => { + console.log('template') + let nvalue = await getValue(lastInput) + if (!deepEqual(nvalue, value)) { + value = nvalue + } + } $: lastInput && - lastInput.type == 'eval' && + lastInput.type == 'template' && + isCodeInjection(lastInput.eval) && $stateId && $state && - debounce2(async () => { - let nvalue = await evalExpr(lastInput) - if (!deepEqual(nvalue, value)) { - value = nvalue - } - }) + debounce(debounceTemplate) + + const debounceEval = async () => { + let nvalue = await evalExpr(lastInput) + if (!deepEqual(nvalue, value)) { + value = nvalue + } + } + + $: lastInput && lastInput.type == 'eval' && $stateId && $state && debounce2(debounceEval) async function handleConnection() { if (lastInput?.type === 'connected') { diff --git a/frontend/src/lib/components/apps/components/helpers/eval.ts b/frontend/src/lib/components/apps/components/helpers/eval.ts index f1901cc0f1..c27237d6bf 100644 --- a/frontend/src/lib/components/apps/components/helpers/eval.ts +++ b/frontend/src/lib/components/apps/components/helpers/eval.ts @@ -20,7 +20,7 @@ export function computeGlobalContext(world: World | undefined, extraContext: any function create_context_function_template(eval_string, context, noReturn: boolean) { return ` -return async function (context, state, goto, setTab, recompute, getAgGrid) { +return async function (context, state, goto, setTab, recompute, getAgGrid, setValue) { "use strict"; ${ Object.keys(context).length > 0 @@ -36,7 +36,7 @@ function make_context_evaluator( eval_string, context, noReturn: boolean -): (context, state, goto, setTab, recompute, getAgGrid) => Promise { +): (context, state, goto, setTab, recompute, getAgGrid, setValue) => Promise { let template = create_context_function_template(eval_string, context, noReturn) let functor = Function(template) return functor() @@ -82,7 +82,11 @@ export async function eval_like( editor: boolean, controlComponents: Record< string, - { setTab?: (index: number) => void; agGrid?: { api: any; columnApi: any } } + { + setTab?: (index: number) => void + agGrid?: { api: any; columnApi: any } + setValue?: (value: any) => void + } >, worldStore: World | undefined, runnableComponents: Record void }> @@ -126,6 +130,9 @@ export async function eval_like( }, (id) => { return controlComponents[id]?.agGrid + }, + (id, value) => { + controlComponents[id]?.setValue?.(value) } ) } diff --git a/frontend/src/lib/components/apps/components/inputs/AppCheckbox.svelte b/frontend/src/lib/components/apps/components/inputs/AppCheckbox.svelte index 9cd8374cd0..70c650fe47 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppCheckbox.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppCheckbox.svelte @@ -30,8 +30,16 @@ configuration ) + let value: boolean = (resolvedConfig.defaultValue as boolean | undefined) ?? false + + $componentControl[id] = { + setValue(nvalue: boolean) { + value = nvalue + } + } + if (controls) { - $componentControl[id] = controls + $componentControl[id] = { ...$componentControl[id], ...controls } } // As the checkbox is a special case and has no input @@ -41,7 +49,21 @@ result: false }) - $: resolvedConfig.defaultValue != undefined && outputs?.result.set(resolvedConfig.defaultValue) + function handleInput() { + outputs.result.set(value) + if (recomputeIds) { + recomputeIds.forEach((id) => $runnableComponents?.[id]?.cb()) + } + } + + function handleDefault() { + value = resolvedConfig.defaultValue ?? false + handleInput() + } + + $: value != undefined && handleInput() + + $: resolvedConfig.defaultValue != undefined && handleDefault() $: css = concatCustomCss($app.css?.checkboxcomponent, customCss) @@ -60,16 +82,13 @@ { preclickAction?.() - outputs.result.set(e.detail) - if (recomputeIds) { - recomputeIds.forEach((id) => $runnableComponents?.[id]?.cb()) - } + value = e.detail }} /> diff --git a/frontend/src/lib/components/apps/components/inputs/AppDateInput.svelte b/frontend/src/lib/components/apps/components/inputs/AppDateInput.svelte index ae93a287f6..2abba3e5cd 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppDateInput.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppDateInput.svelte @@ -15,7 +15,8 @@ export let customCss: ComponentCustomCSS<'dateinputcomponent'> | undefined = undefined export let render: boolean - const { app, worldStore, selectedComponent } = getContext('AppViewerContext') + const { app, worldStore, selectedComponent, componentControl } = + getContext('AppViewerContext') let labelValue: string = 'Title' let minValue: string = '' let maxValue: string = '' @@ -23,6 +24,12 @@ let value: string | undefined = undefined + $componentControl[id] = { + setValue(nvalue: string) { + value = nvalue + } + } + let outputs = initOutput($worldStore, id, { result: undefined as string | undefined }) diff --git a/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte b/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte index a5ff6b53f8..8bb8a75122 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte @@ -16,7 +16,8 @@ export let customCss: ComponentCustomCSS<'multiselectcomponent'> | undefined = undefined export let render: boolean - const { app, worldStore, selectedComponent } = getContext('AppViewerContext') + const { app, worldStore, selectedComponent, componentControl } = + getContext('AppViewerContext') let items: string[] const resolvedConfig = initConfig( @@ -30,6 +31,13 @@ let value: string[] | undefined = outputs?.result.peak() + $componentControl[id] = { + setValue(nvalue: string[]) { + value = nvalue + outputs?.result.set([...(value ?? [])]) + } + } + $: resolvedConfig.items && handleItems() function handleItems() { diff --git a/frontend/src/lib/components/apps/components/inputs/AppNumberInput.svelte b/frontend/src/lib/components/apps/components/inputs/AppNumberInput.svelte index 103d7c1ee5..6e9a2b5954 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppNumberInput.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppNumberInput.svelte @@ -14,12 +14,19 @@ export let customCss: ComponentCustomCSS<'numberinputcomponent'> | undefined = undefined export let render: boolean - const { app, worldStore, selectedComponent } = getContext('AppViewerContext') + const { app, worldStore, selectedComponent, componentControl } = + getContext('AppViewerContext') let defaultValue: number | undefined = undefined let placeholder: string | undefined = undefined let value: number | undefined = undefined + $componentControl[id] = { + setValue(nvalue: number) { + value = nvalue + } + } + let min: number | undefined = undefined let max: number | undefined = undefined let step = 1 diff --git a/frontend/src/lib/components/apps/components/inputs/AppRangeInput.svelte b/frontend/src/lib/components/apps/components/inputs/AppRangeInput.svelte index 4ed6175503..0f729d888a 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppRangeInput.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppRangeInput.svelte @@ -15,7 +15,7 @@ export let customCss: ComponentCustomCSS<'rangecomponent'> | undefined = undefined export let render: boolean - const { app, worldStore } = getContext('AppViewerContext') + const { app, worldStore, componentControl } = getContext('AppViewerContext') let min = 0 let max = 42 let step = 1 @@ -27,6 +27,12 @@ result: null as [number, number] | null }) + $componentControl[id] = { + setValue(nvalue: [number, number]) { + values = nvalue + } + } + $: outputs?.result.set(values) $: css = concatCustomCss($app.css?.rangecomponent, customCss) diff --git a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte index 3853de4acb..7daf68b17b 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte @@ -31,8 +31,14 @@ componentControl } = getContext('AppViewerContext') + $componentControl[id] = { + setValue(nvalue: string) { + setValue(JSON.stringify(nvalue)) + } + } + if (controls) { - $componentControl[id] = controls + $componentControl[id] = { ...$componentControl[id], ...controls } } let resolvedConfig = initConfig( @@ -80,13 +86,16 @@ return i }) } - preclickAction?.() + setValue(e.detail?.['value']) + } + + function setValue(nvalue: any) { let result: any = undefined try { - result = JSON.parse(e.detail?.['value']) + result = JSON.parse(nvalue) } catch (_) {} - value = e.detail?.['value'] + value = nvalue outputs?.result.set(result) if (recomputeIds) { recomputeIds.forEach((id) => $runnableComponents?.[id]?.cb()) diff --git a/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte b/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte index 516a31d531..2fd1dae856 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte @@ -15,7 +15,7 @@ export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined export let render: boolean - const { worldStore } = getContext('AppViewerContext') + const { worldStore, componentControl } = getContext('AppViewerContext') const resolvedConfig = initConfig( components['selectstepcomponent'].initialData.configuration, @@ -26,13 +26,31 @@ }) let selected: string = '' - $: selected === '' && resolvedConfig && setDefaultValue() + let selectedIndex: number = 0 + + $: resolvedConfig.defaultValue != undefined && setDefaultValue() + + $componentControl[id] = { + setValue(nvalue: string) { + selected = nvalue + selectedIndex = resolvedConfig.items.findIndex((item) => item.value === nvalue) + }, + setTab(index) { + selected = resolvedConfig.items?.[index]?.value + selectedIndex = index + } + } function setDefaultValue() { - if (resolvedConfig.defaultValue === undefined) { + if (resolvedConfig.defaultValue != undefined) { + selectedIndex = resolvedConfig.items.findIndex( + (item) => item.value === resolvedConfig.defaultValue + ) + } + if (selectedIndex === -1 || resolvedConfig.defaultValue == undefined) { selected = resolvedConfig.items[0].value - } else if (resolvedConfig.defaultValue?.value) { - selected = resolvedConfig.defaultValue?.value + } else if (resolvedConfig.defaultValue) { + selected = resolvedConfig.items[selectedIndex].value } } @@ -51,8 +69,6 @@ } $: selected && handleSelection(selected) - - let selectedIndex: number = 0 {#each Object.keys(components['selectstepcomponent'].initialData.configuration) as key (key)} diff --git a/frontend/src/lib/components/apps/components/inputs/AppSelectTab.svelte b/frontend/src/lib/components/apps/components/inputs/AppSelectTab.svelte index a6c8579cdd..dd112aa1cf 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSelectTab.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSelectTab.svelte @@ -17,7 +17,7 @@ export let customCss: ComponentCustomCSS<'selecttabcomponent'> | undefined = undefined export let render: boolean - const { app, worldStore } = getContext('AppViewerContext') + const { app, worldStore, componentControl } = getContext('AppViewerContext') const resolvedConfig = initConfig( components['selecttabcomponent'].initialData.configuration, @@ -30,6 +30,15 @@ let selected: string = '' $: selected === '' && resolvedConfig && setDefaultValue() + $componentControl[id] = { + setValue(nvalue: string) { + selected = nvalue + }, + setTab(index) { + selected = resolvedConfig.items?.[index]?.value + } + } + function setDefaultValue() { if (resolvedConfig.defaultValue === undefined) { selected = resolvedConfig.items[0].value diff --git a/frontend/src/lib/components/apps/components/inputs/AppSliderInputs.svelte b/frontend/src/lib/components/apps/components/inputs/AppSliderInputs.svelte index 9abb0452ca..ccc29b5655 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSliderInputs.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSliderInputs.svelte @@ -15,7 +15,8 @@ export let customCss: ComponentCustomCSS<'slidercomponent'> | undefined = undefined export let render: boolean - const { app, worldStore, selectedComponent } = getContext('AppViewerContext') + const { app, worldStore, selectedComponent, componentControl } = + getContext('AppViewerContext') let min = 0 let max = 42 let step = 1 @@ -28,6 +29,12 @@ let values: [number] = [outputs?.result.peak() ?? 0] + $componentControl[id] = { + setValue(nvalue: number) { + values[0] = nvalue + } + } + $: values && handleValues() function handleValues() { diff --git a/frontend/src/lib/components/apps/components/inputs/AppTextInput.svelte b/frontend/src/lib/components/apps/components/inputs/AppTextInput.svelte index 0d124620a1..91423d61c4 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppTextInput.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppTextInput.svelte @@ -20,7 +20,7 @@ | 'textareainputcomponent' = 'textinputcomponent' export let render: boolean - const { app, worldStore, selectedComponent, connectingInput } = + const { app, worldStore, selectedComponent, connectingInput, componentControl } = getContext('AppViewerContext') let placeholder: string | undefined = undefined @@ -31,6 +31,12 @@ result: '' }) + $componentControl[id] = { + setValue(nvalue: string) { + value = nvalue + } + } + $: handleDefault(defaultValue) $: outputs?.result.set(value ?? '') diff --git a/frontend/src/lib/components/apps/components/inputs/currency/AppCurrencyInput.svelte b/frontend/src/lib/components/apps/components/inputs/currency/AppCurrencyInput.svelte index d24c6fd586..64542945a5 100644 --- a/frontend/src/lib/components/apps/components/inputs/currency/AppCurrencyInput.svelte +++ b/frontend/src/lib/components/apps/components/inputs/currency/AppCurrencyInput.svelte @@ -15,7 +15,8 @@ export let customCss: ComponentCustomCSS<'currencycomponent'> | undefined = undefined export let render: boolean - const { app, worldStore, selectedComponent } = getContext('AppViewerContext') + const { app, worldStore, selectedComponent, componentControl } = + getContext('AppViewerContext') const outputs = initOutput($worldStore, id, { result: null as number | null @@ -28,6 +29,12 @@ let locale: string | undefined = undefined let value: number | undefined = undefined + $componentControl[id] = { + setValue(nvalue: number) { + value = nvalue + } + } + function handleInput() { outputs?.result.set(value ?? null) } diff --git a/frontend/src/lib/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte b/frontend/src/lib/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte index d4259e8bcd..fe7146de66 100644 --- a/frontend/src/lib/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte +++ b/frontend/src/lib/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte @@ -237,6 +237,7 @@ state.foo += 1 // you may also just reassign as next statement 'state.foo = state.foo' // you can also navigate (goto), recompute a script (recompute), or set a tab (setTab) +// Inputs and display components support settings their value directly (setValue) return state.foo`, language: 'frontend', diff --git a/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptsPanelList.svelte b/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptsPanelList.svelte index 5ba032ca0d..59d91e2fda 100644 --- a/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptsPanelList.svelte +++ b/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptsPanelList.svelte @@ -161,8 +161,8 @@ class="mb-0.5" documentationLink="https://docs.windmill.dev/docs/apps/app-runnable#background-runnable" > - Background runnables are triggered upon global refresh or when their input changes. The - result of a background runnable can be shared among many components. + Background runnables can be triggered on app refresh or when their input changes. The + result can be shared among many components.