diff --git a/frontend/src/lib/components/ResourcePicker.svelte b/frontend/src/lib/components/ResourcePicker.svelte index 67ec0185e3..6e3033f72d 100644 --- a/frontend/src/lib/components/ResourcePicker.svelte +++ b/frontend/src/lib/components/ResourcePicker.svelte @@ -88,7 +88,7 @@ }} items={collection} class="text-clip grow min-w-0" - placeholder="{resourceType} resource" + placeholder="{resourceType ?? 'any'} resource" inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles} containerStyles={SELECT_INPUT_DEFAULT_STYLE.containerStyles} /> diff --git a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte index 86e8e2ba22..b8a9ffe9d8 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte @@ -43,6 +43,8 @@ : [] if (defaultValue) { value = JSON.stringify(defaultValue) + } else if (listItems.length > 0) { + value = listItems[0]?.value } } diff --git a/frontend/src/lib/components/apps/editor/AppEditor.svelte b/frontend/src/lib/components/apps/editor/AppEditor.svelte index 0bf0e68936..93374a8826 100644 --- a/frontend/src/lib/components/apps/editor/AppEditor.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditor.svelte @@ -212,7 +212,7 @@
- +
diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index 754b2ad88d..ca6279d0ca 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -471,8 +471,8 @@
{ $app = undo(history, $app) }} diff --git a/frontend/src/lib/components/apps/editor/ComponentHeader.svelte b/frontend/src/lib/components/apps/editor/ComponentHeader.svelte index 4a13c27ed8..1a280e5ed4 100644 --- a/frontend/src/lib/components/apps/editor/ComponentHeader.svelte +++ b/frontend/src/lib/components/apps/editor/ComponentHeader.svelte @@ -56,15 +56,15 @@ {/if} {/if} @@ -73,7 +73,7 @@ title="Move" on:mousedown|stopPropagation|capture class={classNames( - 'text-gray-600 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit absolute border border-gray-300 -top-1 shadow right-[4.5rem] z-50 cursor-move', + 'text-gray-600 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit absolute border border-gray-300 -top-1 shadow right-[0.5rem] z-50 cursor-move', 'bg-gray-200/80' )} > diff --git a/frontend/src/lib/components/apps/editor/GridEditor.svelte b/frontend/src/lib/components/apps/editor/GridEditor.svelte index e0041c44cb..818c9779dd 100644 --- a/frontend/src/lib/components/apps/editor/GridEditor.svelte +++ b/frontend/src/lib/components/apps/editor/GridEditor.svelte @@ -11,10 +11,11 @@ import HiddenComponent from '../components/helpers/HiddenComponent.svelte' import Component from './component/Component.svelte' import { deepEqual } from 'fast-equals' - import { push } from '$lib/history' + import { push, type History } from '$lib/history' import { expandGriditem, findGridItem } from './appUtils' export let policy: Policy + export let history: History | undefined = undefined const { selectedComponent, @@ -29,8 +30,6 @@ breakpoint } = getContext('AppViewerContext') - const { history } = getContext('AppEditorContext') - // The drag is disabled when the user is connecting an input // or when the user is previewing the app // or when the focused grid is a subgrid @@ -108,13 +107,17 @@ let pointerdown = false let lastapp: App | undefined = undefined const onpointerdown = () => { - lastapp = JSON.parse(JSON.stringify($app)) - pointerdown = true + if (history) { + lastapp = JSON.parse(JSON.stringify($app)) + pointerdown = true + } } const onpointerup = () => { - pointerdown = false - if (!deepEqual(lastapp, $app)) { - push(history, lastapp, false, true) + if (history) { + pointerdown = false + if (!deepEqual(lastapp, $app)) { + push(history, lastapp, false, true) + } } } diff --git a/frontend/src/lib/components/apps/editor/component/Component.svelte b/frontend/src/lib/components/apps/editor/component/Component.svelte index 375814ac31..18b3e2b664 100644 --- a/frontend/src/lib/components/apps/editor/component/Component.svelte +++ b/frontend/src/lib/components/apps/editor/component/Component.svelte @@ -222,7 +222,7 @@ recomputeIds={component.recomputeIds} {render} /> - {:else if component.type === 'selectcomponent'} + {:else if component.type === 'selectcomponent' || component.type === 'resourceselectcomponent'} export type DisplayComponent = BaseComponent<'displaycomponent'> export type ImageComponent = BaseComponent<'imagecomponent'> export type InputComponent = BaseComponent<'inputcomponent'> -export type SelectComponent = BaseComponent<'selectcomponent'> +export type SelectComponent = BaseComponent<'resourceselectcomponent'> +export type ResourceSelectComponent = BaseComponent<'selectcomponent'> export type MultiSelectComponent = BaseComponent<'multiselectcomponent'> export type CheckboxComponent = BaseComponent<'checkboxcomponent'> export type RadioComponent = BaseComponent<'radiocomponent'> @@ -116,6 +117,7 @@ export type AppComponent = BaseAppComponent & | PieChartComponent | ScatterChartComponent | SelectComponent + | ResourceSelectComponent | MultiSelectComponent | CheckboxComponent | FormComponent @@ -911,7 +913,7 @@ Hello \${ctx.username} } }, multiselectcomponent: { - name: 'MultiSelect', + name: 'Multi Select', icon: List, dims: '2:1-3:1', data: { @@ -940,6 +942,36 @@ Hello \${ctx.username} softWrap: true } }, + resourceselectcomponent: { + name: 'Resource Select', + icon: List, + dims: '2:1-3:1', + data: { + verticalAlignment: 'center', + id: '', + type: 'resourceselectcomponent', + componentInput: undefined, + configuration: { + items: { + type: 'static', + fieldType: 'array', + subFieldType: 'labeledresource', + value: [] + }, + placeholder: { + type: 'static', + fieldType: 'text', + value: 'Select an item', + onlyStatic: true + } + }, + customCss: { + input: { style: '' } + } as const, + card: false, + softWrap: true + } + }, numberinputcomponent: { name: 'Number', icon: Binary, diff --git a/frontend/src/lib/components/apps/editor/component/sets.ts b/frontend/src/lib/components/apps/editor/component/sets.ts index 7677acfb10..59662af0dc 100644 --- a/frontend/src/lib/components/apps/editor/component/sets.ts +++ b/frontend/src/lib/components/apps/editor/component/sets.ts @@ -32,6 +32,7 @@ const inputs: ComponentSet = { 'fileinputcomponent', 'checkboxcomponent', 'selectcomponent', + 'resourceselectcomponent', 'multiselectcomponent', ] } as const diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte index 7a458bd810..7b5f43c340 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte @@ -22,6 +22,8 @@ componentInput.value.push(0) } else if (componentInput.subFieldType === 'object') { componentInput.value.push({}) + } else if (componentInput.subFieldType === 'labeledresource') { + componentInput.value.push({ value: '', label: '' }) } else if ( componentInput.subFieldType === 'text' || componentInput.subFieldType === 'textarea' || diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte index 79371146f2..73065d1970 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte @@ -39,6 +39,24 @@ {:else if componentInput.fieldType === 'icon-select'} + {:else if componentInput.fieldType === 'labeledresource'} + {#if componentInput?.value && typeof componentInput?.value == 'object' && 'label' in componentInput?.value} +
+ + { + let path = e.detail + + if (componentInput && path) { + componentInput.value['value'] = `$res:${path}` + } + }} + /> +
+ {:else} + Inconsistent labeled resource object + {/if} {:else if componentInput.fieldType === 'color'} {:else if componentInput.fieldType === 'object'} diff --git a/frontend/src/lib/components/apps/inputType.ts b/frontend/src/lib/components/apps/inputType.ts index e6334fa136..8e6d43c168 100644 --- a/frontend/src/lib/components/apps/inputType.ts +++ b/frontend/src/lib/components/apps/inputType.ts @@ -17,6 +17,7 @@ export type InputType = | 'object' | 'array' | 'any' + | 'labeledresource' // Connection to an output of another component // defined by the id of the component and the path of the output @@ -139,11 +140,11 @@ export type AppInput = | AppInputSpec<'object', Record> | AppInputSpec<'object', string> | (AppInputSpec<'select', string> & { - /** - * One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues` - */ - optionValuesKey: keyof typeof staticValues - }) + /** + * One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues` + */ + optionValuesKey: keyof typeof staticValues + }) | (AppInputSpec<'select', string> & StaticOptions) | AppInputSpec<'icon-select', string> | AppInputSpec<'color', string> @@ -156,9 +157,12 @@ export type AppInput = | AppInputSpec<'array', string[], 'datetime'> | AppInputSpec<'array', object[], 'object'> | (AppInputSpec<'array', string[], 'select'> & { - optionValuesKey: keyof typeof staticValues - }) + optionValuesKey: keyof typeof staticValues + }) | (AppInputSpec<'array', string[], 'select'> & StaticOptions) + | AppInputSpec<'array', object[], 'labeledresource'> + | AppInputSpec<'labeledresource', object> + export type RowAppInput = Extract export type StaticAppInput = Extract diff --git a/frontend/src/lib/components/apps/types.ts b/frontend/src/lib/components/apps/types.ts index 17d9396a59..c65386ed43 100644 --- a/frontend/src/lib/components/apps/types.ts +++ b/frontend/src/lib/components/apps/types.ts @@ -137,7 +137,7 @@ export type AppViewerContext = { } export type AppEditorContext = { - history: History + history: History | undefined componentControl: Writable boolean; right?: () => boolean }>> pickVariableCallback: Writable<((path: string) => void) | undefined> } diff --git a/frontend/src/lib/history.ts b/frontend/src/lib/history.ts index 7f3633142a..983228d32c 100644 --- a/frontend/src/lib/history.ts +++ b/frontend/src/lib/history.ts @@ -7,34 +7,43 @@ export function initHistory(initial: T): History { return writable({ history: [initial], index: 0 }) } -export function undo(history: History, now: T): T { - let chistory = get(history) - if (chistory.index == 0) return now - if (!deepEqual(chistory.history[chistory.index], now)) { - push(history, now, true) +export function undo(history: History | undefined, now: T): T { + if (history) { + + let chistory = get(history) + if (chistory.index == 0) return now + if (!deepEqual(chistory.history[chistory.index], now)) { + push(history, now, true) + } else { + history.update((history) => { + history.index-- + return history + }) + } + let nhistory = get(history) + return nhistory.history[nhistory.index] } else { - history.update((history) => { - history.index-- + return now + } +} + +export function redo(history: History | undefined): T { + if (history) { + history?.update((history) => { + if (history.index < history.history.length - 1) { + history.index++ + } return history }) + let nhistory = get(history) + return nhistory.history[nhistory.index] + } else { + return undefined as T } - let nhistory = get(history) - return nhistory.history[nhistory.index] } -export function redo(history: History): T { - history.update((history) => { - if (history.index < history.history.length - 1) { - history.index++ - } - return history - }) - let nhistory = get(history) - return nhistory.history[nhistory.index] -} - -export function push(history: History, value: T, noSetIndex: boolean = false, skipCopy: boolean = false) { - history.update((history) => { +export function push(history: History | undefined, value: T, noSetIndex: boolean = false, skipCopy: boolean = false) { + history?.update((history) => { history.history = JSON.parse(JSON.stringify(history.history.slice(0, history.index + 1))) const toPush = skipCopy ? value : JSON.parse(JSON.stringify(value)) history.history.push(toPush)