From 49c5553f3b496c2aaf03376689ee0fd42ecbd2bf Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 8 Sep 2023 09:48:28 +0200 Subject: [PATCH] feat: introduce container groups --- .../apps/components/GroupWrapper.svelte | 11 ++ .../apps/components/helpers/InputValue.svelte | 22 +++- .../helpers/RunnableComponent.svelte | 12 +- .../components/layout/AppContainer.svelte | 50 ++++++--- .../lib/components/apps/editor/appUtils.ts | 9 +- .../components/apps/editor/appUtilsInfer.ts | 5 +- .../apps/editor/component/Component.svelte | 1 + .../apps/editor/component/components.ts | 5 +- .../contextPanel/ComponentOutput.svelte | 6 +- .../contextPanel/ComponentOutputViewer.svelte | 1 + .../settingsPanel/ComponentPanel.svelte | 8 +- .../editor/settingsPanel/GridGroup.svelte | 104 ++++++++++++++++++ .../apps/editor/settingsPanel/GridTab.svelte | 9 -- .../settingsPanel/InputsSpecEditor.svelte | 2 +- .../settingsPanel/InputsSpecsEditor.svelte | 9 ++ frontend/src/lib/components/apps/inputType.ts | 2 + frontend/src/lib/components/apps/types.ts | 2 + frontend/src/lib/components/apps/utils.ts | 4 +- 18 files changed, 219 insertions(+), 43 deletions(-) create mode 100644 frontend/src/lib/components/apps/components/GroupWrapper.svelte create mode 100644 frontend/src/lib/components/apps/editor/settingsPanel/GridGroup.svelte diff --git a/frontend/src/lib/components/apps/components/GroupWrapper.svelte b/frontend/src/lib/components/apps/components/GroupWrapper.svelte new file mode 100644 index 0000000000..ad8624e743 --- /dev/null +++ b/frontend/src/lib/components/apps/components/GroupWrapper.svelte @@ -0,0 +1,11 @@ + + + diff --git a/frontend/src/lib/components/apps/components/helpers/InputValue.svelte b/frontend/src/lib/components/apps/components/helpers/InputValue.svelte index 50b290fd74..69295338d7 100644 --- a/frontend/src/lib/components/apps/components/helpers/InputValue.svelte +++ b/frontend/src/lib/components/apps/components/helpers/InputValue.svelte @@ -7,7 +7,7 @@ TemplateV2Input, UploadAppInput } from '../../inputType' - import type { AppViewerContext, ListContext, RichConfiguration } from '../../types' + import type { AppViewerContext, GroupContext, ListContext, RichConfiguration } from '../../types' import { accessPropertyByPath } from '../../utils' import { computeGlobalContext, eval_like } from './eval' import deepEqualWithOrderedArray from './deepEqualWithOrderedArray' @@ -26,6 +26,7 @@ const iterContext = getContext('ListWrapperContext') const rowContext = getContext('RowWrapperContext') + const groupContext = getContext('GroupContext') let previousConnectedValue: any | undefined = undefined @@ -33,19 +34,28 @@ $: fullContext = { iter: iterContext ? $iterContext : undefined, - row: rowContext ? $rowContext : undefined + row: rowContext ? $rowContext : undefined, + group: groupContext ? $groupContext : undefined } $: lastInput?.type == 'evalv2' && - (fullContext.iter != undefined || fullContext.row != undefined) && - lastInput.connections.some((x) => x.componentId == 'row' || x.componentId == 'iter') && + (fullContext.iter != undefined || + fullContext.row != undefined || + fullContext.group != undefined) && + lastInput.connections.some( + (x) => x.componentId == 'row' || x.componentId == 'iter' || x.componentId == 'group' + ) && debounceEval() $: lastInput && lastInput.type == 'templatev2' && isCodeInjection(lastInput.eval) && - (fullContext.iter != undefined || fullContext.row != undefined) && - lastInput.connections.some((x) => x.componentId == 'row' || x.componentId == 'iter') && + (fullContext.iter != undefined || + fullContext.row != undefined || + fullContext.group != undefined) && + lastInput.connections.some( + (x) => x.componentId == 'row' || x.componentId == 'iter' || x.componentId == 'group' + ) && debounceTemplate() const dispatch = createEventDispatcher() diff --git a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte index d5f4520325..ba11f2dfcd 100644 --- a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte +++ b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte @@ -10,7 +10,13 @@ import { createEventDispatcher, getContext, onDestroy, onMount } from 'svelte' import type { AppInputs, Runnable } from '../../inputType' import type { Output } from '../../rx' - import type { AppViewerContext, CancelablePromise, InlineScript, ListContext } from '../../types' + import type { + AppViewerContext, + CancelablePromise, + GroupContext, + InlineScript, + ListContext + } from '../../types' import { computeGlobalContext, eval_like } from './eval' import InputValue from './InputValue.svelte' import RefreshButton from './RefreshButton.svelte' @@ -65,6 +71,7 @@ } = getContext('AppViewerContext') const iterContext = getContext('ListWrapperContext') const rowContext = getContext('RowWrapperContext') + const groupContext = getContext('GroupContext') const dispatch = createEventDispatcher() @@ -179,7 +186,8 @@ runnable.inlineScript?.content, computeGlobalContext($worldStore, { iter: iterContext ? $iterContext : undefined, - row: rowContext ? $rowContext : undefined + row: rowContext ? $rowContext : undefined, + group: groupContext ? $groupContext : undefined }), false, $state, diff --git a/frontend/src/lib/components/apps/components/layout/AppContainer.svelte b/frontend/src/lib/components/apps/components/layout/AppContainer.svelte index 7de891fc79..f03599939a 100644 --- a/frontend/src/lib/components/apps/components/layout/AppContainer.svelte +++ b/frontend/src/lib/components/apps/components/layout/AppContainer.svelte @@ -2,20 +2,28 @@ import { getContext } from 'svelte' import { initOutput } from '../../editor/appUtils' import SubGridEditor from '../../editor/SubGridEditor.svelte' - import type { AppViewerContext, ComponentCustomCSS } from '../../types' + import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types' import { concatCustomCss } from '../../utils' import InitializeComponent from '../helpers/InitializeComponent.svelte' + // import type { EvalV2AppInput, StaticAppInput } from '../../inputType' + import { writable } from 'svelte/store' + import { InputValue } from '../helpers' + import GroupWrapper from '../GroupWrapper.svelte' export let id: string export let componentContainerHeight: number export let customCss: ComponentCustomCSS<'containercomponent'> | undefined = undefined export let render: boolean + export let groupFields: RichConfigurations | undefined = undefined const { app, focusedGrid, selectedComponent, worldStore, connectingInput } = getContext('AppViewerContext') - //used so that we can count number of outputs setup for first refresh - initOutput($worldStore, id, {}) + let groupContext = writable({}) + + let outputs = initOutput($worldStore, id, { group: $groupContext }) + + $: outputs.group.set($groupContext, true) function onFocus() { $focusedGrid = { @@ -29,21 +37,29 @@ +{#each Object.keys(groupFields ?? {}) as field} + {#if groupFields && field in groupFields} + + {/if} +{/each} +
{#if $app.subgrids?.[`${id}-0`]} - { - if (!$connectingInput.opened) { - $selectedComponent = [id] - } - onFocus() - }} - /> + + { + if (!$connectingInput.opened) { + $selectedComponent = [id] + } + onFocus() + }} + /> + {/if}
diff --git a/frontend/src/lib/components/apps/editor/appUtils.ts b/frontend/src/lib/components/apps/editor/appUtils.ts index 473f431693..16a30efc8c 100644 --- a/frontend/src/lib/components/apps/editor/appUtils.ts +++ b/frontend/src/lib/components/apps/editor/appUtils.ts @@ -13,7 +13,8 @@ import { getRecommendedDimensionsByComponent, type AppComponent, type BaseComponent, - type InitialAppComponent + type InitialAppComponent, + type TypedComponent } from './component' import { gridColumns } from '../gridUtils' import { allItems } from '../utils' @@ -598,7 +599,8 @@ export function sortGridItemsPosition( export function connectInput( connectingInput: ConnectingInput, componentId: string, - path: string + path: string, + componentType?: TypedComponent['type'] ): ConnectingInput { if (connectingInput) { if (connectingInput.onConnect) { @@ -610,7 +612,8 @@ export function connectInput( input: { connection: { componentId, - path + path, + componentType }, type: 'connected' }, diff --git a/frontend/src/lib/components/apps/editor/appUtilsInfer.ts b/frontend/src/lib/components/apps/editor/appUtilsInfer.ts index 272e7cc0e0..fa6e07f8a0 100644 --- a/frontend/src/lib/components/apps/editor/appUtilsInfer.ts +++ b/frontend/src/lib/components/apps/editor/appUtilsInfer.ts @@ -15,7 +15,10 @@ export async function inferDeps( const noutputs = outputs .filter( ([componentId, id]) => - componentId == 'row' || componentId == 'iter' || id in (worldOutputs[componentId] ?? {}) + componentId == 'row' || + componentId == 'iter' || + componentId == 'group' || + id in (worldOutputs[componentId] ?? {}) ) .map(([componentId, id]) => ({ componentId: componentId, diff --git a/frontend/src/lib/components/apps/editor/component/Component.svelte b/frontend/src/lib/components/apps/editor/component/Component.svelte index 7587d4e502..a9e586070d 100644 --- a/frontend/src/lib/components/apps/editor/component/Component.svelte +++ b/frontend/src/lib/components/apps/editor/component/Component.svelte @@ -480,6 +480,7 @@ /> {:else if component.type === 'containercomponent'} & { disabledTabs: RichConfiguration[] } export type ListComponent = BaseComponent<'listcomponent'> -export type ContainerComponent = BaseComponent<'containercomponent'> +export type ContainerComponent = BaseComponent<'containercomponent'> & { + groupFields: RichConfigurations +} export type DrawerComponent = BaseComponent<'drawercomponent'> export type MapComponent = BaseComponent<'mapcomponent'> export type VerticalSplitPanesComponent = BaseComponent<'verticalsplitpanescomponent'> & { diff --git a/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutput.svelte b/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutput.svelte index c2ad19e963..0350e81af8 100644 --- a/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutput.svelte +++ b/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutput.svelte @@ -39,7 +39,11 @@ componentId={gridItem.id} on:select={({ detail }) => { if ($connectingInput.opened) { - $connectingInput = connectInput($connectingInput, gridItem.id, detail) + let typ = gridItem?.data?.type + let splitted = detail?.split('.') + let componentId = typ == 'containercomponent' ? splitted?.[0] : gridItem.id + let path = typ == 'containercomponent' ? splitted?.[1] : detail + $connectingInput = connectInput($connectingInput, componentId, path, typ) } }} /> diff --git a/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutputViewer.svelte b/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutputViewer.svelte index cd6f8fafb2..e9365cf976 100644 --- a/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutputViewer.svelte +++ b/frontend/src/lib/components/apps/editor/contextPanel/ComponentOutputViewer.svelte @@ -17,6 +17,7 @@ if (observableOutputs) { Object.entries(observableOutputs).forEach(([k, output]) => { object[k] = undefined + console.log('subscribing to', k, output) output?.subscribe( { id: 'alloutputs' + componentId + '-' + k, diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte index a662696d64..fb6b2722cd 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte @@ -32,6 +32,7 @@ import { inferDeps } from '../appUtilsInfer' import EvalV2InputEditor from './inputEditor/EvalV2InputEditor.svelte' import type { ResultAppInput } from '../../inputType' + import GridGroup from './GridGroup.svelte' export let componentSettings: { item: GridItem; parent: string | undefined } | undefined = undefined @@ -310,6 +311,11 @@ bind:component={componentSettings.item.data} word="Step" /> + {:else if componentSettings.item.data.type === 'containercomponent'} + {:else if componentSettings.item.data.type === 'conditionalwrapper'} - {:else} + {:else if componentSettings.item.data.type != 'containercomponent'}
{ccomponents[component.type].name} has no configuration
diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/GridGroup.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/GridGroup.svelte new file mode 100644 index 0000000000..21c07e4af8 --- /dev/null +++ b/frontend/src/lib/components/apps/editor/settingsPanel/GridGroup.svelte @@ -0,0 +1,104 @@ + + +
+ { + if (e.detail) { + groupFields = {} + } else { + groupFields = undefined + } + console.log(groupFields) + }} + options={{ right: 'container is a component group' }} + /> + Group fields allow inner components to depend on the group fields which make the container a + group of component that is encapsulated. Inside the group, it is possible to retrieve the values + using `group.` where x is the group field name +
+{#if groupFields != undefined} + 0 + ? `(${Object.keys(groupFields ?? {}).length ?? 0})` + : '' + }`} + > + {#if Object.keys(groupFields ?? {}).length == 0} + No group fields + {/if} +
+ { + if (!groupFields) { + return + } + delete groupFields[e.detail] + groupFields = groupFields + }} + id={component.id} + shouldCapitalize={false} + displayType + deletable + bind:inputSpecs={groupFields} + /> +
+ { + switch (event.key) { + case 'Enter': + event.preventDefault() + addField(fieldName) + break + } + }} + placeholder="Group Field Name" + bind:value={fieldName} + /> + +
+
+
+{/if} diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/GridTab.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/GridTab.svelte index dd3c223eba..bccb3beacb 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/GridTab.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/GridTab.svelte @@ -17,15 +17,6 @@ export let canDisableTabs: boolean = false - // Migration code - $: if (tabs?.length !== disabledTabs?.length && canDisableTabs) { - disabledTabs = Array(tabs.length).fill({ - type: 'static', - value: false, - fieldType: 'boolean' - }) - } - export let word: string = 'Tab' export let component: AppComponent diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte index 9da4f0ad3b..1aa8e4c73d 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte @@ -75,7 +75,7 @@ {/if} {#if displayType} -
+
{fieldType === 'array' && subFieldType ? `${fieldTypeToTsType(subFieldType)}[]` : fieldTypeToTsType(fieldType)} diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecsEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecsEditor.svelte index fdd00bfb40..e0a7c10f6e 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecsEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecsEditor.svelte @@ -1,4 +1,6 @@ {#if inputSpecs} @@ -48,6 +53,10 @@ customTitle={meta?.['customTitle']} {displayType} /> + {#if deletable} +
+ dispatch('delete', k)} /> +
{/if} {/if} {/each}
diff --git a/frontend/src/lib/components/apps/inputType.ts b/frontend/src/lib/components/apps/inputType.ts index d3b80d2761..eab9fb04a3 100644 --- a/frontend/src/lib/components/apps/inputType.ts +++ b/frontend/src/lib/components/apps/inputType.ts @@ -1,4 +1,5 @@ import type { ReadFileAs } from '../common/fileInput/model' +import type { TypedComponent } from './editor/component' import type { InlineScript } from './types' export type InputType = @@ -25,6 +26,7 @@ export type InputType = // Connection to an output of another component // defined by the id of the component and the path of the output export type InputConnection = { + componentType?: TypedComponent['type'] componentId: string path: string } diff --git a/frontend/src/lib/components/apps/types.ts b/frontend/src/lib/components/apps/types.ts index 0e95ef08c8..9f492a5266 100644 --- a/frontend/src/lib/components/apps/types.ts +++ b/frontend/src/lib/components/apps/types.ts @@ -163,6 +163,8 @@ export type ListContext = Writable<{ export type ListInputs = (id: string, value: any) => void +export type GroupContext = Writable> + export type AppViewerContext = { worldStore: Writable app: Writable diff --git a/frontend/src/lib/components/apps/utils.ts b/frontend/src/lib/components/apps/utils.ts index 0b15043007..b1395c507c 100644 --- a/frontend/src/lib/components/apps/utils.ts +++ b/frontend/src/lib/components/apps/utils.ts @@ -185,7 +185,9 @@ declare function closeModal(id: string): void; } declare const state: ${JSON.stringify(state)}; declare const iter: {index: number, value: any}; -declare const row: {index: number, value: any}; +declare const row: Record; +declare const group: Record; + ` }