From 1d2e8c0eeffb73c198a53546c8fdd97f60c5c7c7 Mon Sep 17 00:00:00 2001 From: Guilhem Date: Tue, 5 Nov 2024 20:58:16 +0100 Subject: [PATCH] fix(frontend): improve flow prop picker design * improve input picker * Put back colors for types * Add full path to popover * Copy instead of toast if no input selected * Add popover and copy to value * Add shadow en transition when selecting input * Add border and transition when selecting input * revert unwanted changes * hide scrollbar when not hovering * Add connexion animated border effect * fix display * fix proppicker display * Remove badge * Change animated button gradient * revert scrollbar on hover * clean design * clean design * clean design * clean design * clean design * clean design * clean design * improve search --------- Co-authored-by: Ruben Fiszel --- .../lib/components/InputTransformForm.svelte | 280 ++++++++++-------- .../InputTransformSchemaForm.svelte | 32 +- .../contextPanel/ComponentOutputViewer.svelte | 14 +- .../common/button/AnimatedButton.svelte | 93 ++++++ .../clearableInput/ClearableInput.svelte | 4 +- .../flows/content/FlowModuleComponent.svelte | 3 +- .../flows/propPicker/PropPickerWrapper.svelte | 92 +++--- .../propertyPicker/ObjectViewer.svelte | 116 +++++--- .../propertyPicker/PropPicker.svelte | 218 +++++++------- .../propertyPicker/PropPickerResult.svelte | 20 +- .../lib/components/propertyPicker/utils.ts | 57 ++-- 11 files changed, 547 insertions(+), 382 deletions(-) create mode 100644 frontend/src/lib/components/common/button/AnimatedButton.svelte diff --git a/frontend/src/lib/components/InputTransformForm.svelte b/frontend/src/lib/components/InputTransformForm.svelte index 64530dd3ba..706d00b7ae 100644 --- a/frontend/src/lib/components/InputTransformForm.svelte +++ b/frontend/src/lib/components/InputTransformForm.svelte @@ -9,9 +9,11 @@ import type { PropPickerWrapperContext } from './flows/propPicker/PropPickerWrapper.svelte' import { codeToStaticTemplate, getDefaultExpr } from './flows/utils' import SimpleEditor from './SimpleEditor.svelte' - import { Button } from './common' + import { Button } from '$lib/components/common' + import AnimatedButton from '$lib/components/common/button/AnimatedButton.svelte' import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte' import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' + import { fade } from 'svelte/transition' import type VariableEditor from './VariableEditor.svelte' import type ItemPicker from './ItemPicker.svelte' @@ -23,7 +25,7 @@ import type { FlowCopilotContext } from './copilot/flow' import StepInputGen from './copilot/StepInputGen.svelte' import type { PickableProperties } from './flows/previousResults' - + import { twMerge } from 'tailwind-merge' export let schema: Schema | { properties?: Record; required?: string[] } export let arg: InputTransform | any export let argName: string @@ -186,11 +188,22 @@ let stepInputGen: StepInputGen | undefined = undefined loadResourceTypes() + + $: connecting = + $propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'connect' {#if arg != undefined} -
-
+
+
- + +
{/if}
-
- {#if $propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'connect'} +
+ - {#if isStaticTemplate(inputCat) && propertyType == 'static' && !noDynamicToggle} - {#if argName && schema?.properties?.[argName]?.description} -
-
{schema.properties[argName].description}
-
- {/if} -
- {#if arg} - +
+ {#if isStaticTemplate(inputCat) && propertyType == 'static' && !noDynamicToggle} + {#if argName && schema?.properties?.[argName]?.description} +
+
{schema.properties[argName].description}
+
+ {/if} +
+ {#if arg} + { + focused = false + }} + bind:code={arg.value} + fontSize={14} + on:change={() => { + dispatch('change', { argName }) + }} + /> + {/if} +
+ {:else if (propertyType === undefined || propertyType == 'static') && schema?.properties?.[argName]} + { focused = false }} - bind:code={arg.value} - fontSize={14} + shouldDispatchChanges on:change={() => { dispatch('change', { argName }) }} + label={argName} + bind:editor={monaco} + bind:description={schema.properties[argName].description} + bind:value={arg.value} + type={schema.properties[argName].type} + oneOf={schema.properties[argName].oneOf} + required={schema.required?.includes(argName)} + bind:pattern={schema.properties[argName].pattern} + bind:valid={inputCheck} + defaultValue={schema.properties[argName].default} + bind:enum_={schema.properties[argName].enum} + bind:format={schema.properties[argName].format} + contentEncoding={schema.properties[argName].contentEncoding} + bind:itemsType={schema.properties[argName].items} + properties={schema.properties[argName].properties} + nestedRequired={schema.properties[argName].required} + displayHeader={false} + extra={argExtra} + {variableEditor} + {itemPicker} + bind:pickForField + showSchemaExplorer + nullable={schema.properties[argName].nullable} + bind:title={schema.properties[argName].title} + bind:placeholder={schema.properties[argName].placeholder} /> + {:else if arg.expr != undefined} +
+ { + dispatch('change', { argName }) + }} + {extraLib} + lang="javascript" + shouldBindKey={false} + on:focus={() => { + focused = true + focusProp(argName, 'insert', (path) => { + monaco?.insertAtCursor(path) + return false + }) + }} + on:change={() => { + dispatch('change', { argName }) + }} + on:blur={() => { + focused = false + }} + autoHeight + /> +
+ +
+ {:else} + Not recognized input type {argName} ({arg.expr}, {propertyType}) +
+
{/if}
- {:else if (propertyType === undefined || propertyType == 'static') && schema?.properties?.[argName]} - { - focused = false - }} - shouldDispatchChanges - on:change={() => { - dispatch('change', { argName }) - }} - label={argName} - bind:editor={monaco} - bind:description={schema.properties[argName].description} - bind:value={arg.value} - type={schema.properties[argName].type} - oneOf={schema.properties[argName].oneOf} - required={schema.required?.includes(argName)} - bind:pattern={schema.properties[argName].pattern} - bind:valid={inputCheck} - defaultValue={schema.properties[argName].default} - bind:enum_={schema.properties[argName].enum} - bind:format={schema.properties[argName].format} - contentEncoding={schema.properties[argName].contentEncoding} - bind:itemsType={schema.properties[argName].items} - properties={schema.properties[argName].properties} - nestedRequired={schema.properties[argName].required} - displayHeader={false} - extra={argExtra} - {variableEditor} - {itemPicker} - bind:pickForField - showSchemaExplorer - nullable={schema.properties[argName].nullable} - bind:title={schema.properties[argName].title} - bind:placeholder={schema.properties[argName].placeholder} - /> - {:else if arg.expr != undefined} -
- { - dispatch('change', { argName }) - }} - {extraLib} - lang="javascript" - shouldBindKey={false} - on:focus={() => { - focused = true - focusProp(argName, 'insert', (path) => { - monaco?.insertAtCursor(path) - return false - }) - }} - on:change={() => { - dispatch('change', { argName }) - }} - on:blur={() => { - focused = false - }} - autoHeight - /> -
- -
- {:else} - Not recognized input type {argName} ({arg.expr}, {propertyType}) -
-
- {/if} + + {#if $propPickerConfig?.propName == argName} +
+ + + +
+ {:else} +
+ {/if} +
{/if} diff --git a/frontend/src/lib/components/InputTransformSchemaForm.svelte b/frontend/src/lib/components/InputTransformSchemaForm.svelte index 488797c6db..e9a0f4899d 100644 --- a/frontend/src/lib/components/InputTransformSchemaForm.svelte +++ b/frontend/src/lib/components/InputTransformSchemaForm.svelte @@ -67,24 +67,26 @@
{#if enableAi} - - Object.keys(schema.properties ?? {}).includes(argName) && - Object.keys(args ?? {}).includes(argName) && - ((args[argName].type === 'static' && !args[argName].value) || - (args[argName].type === 'javascript' && !args[argName].expr)) - ) - : []} - {schema} - /> +
+ + Object.keys(schema.properties ?? {}).includes(argName) && + Object.keys(args ?? {}).includes(argName) && + ((args[argName].type === 'static' && !args[argName].value) || + (args[argName].type === 'javascript' && !args[argName].expr)) + ) + : []} + {schema} + /> +
{/if} {#if keys.length > 0} {#each keys as argName (argName)} {#if (!filter || filter.includes(argName)) && Object.keys(schema.properties ?? {}).includes(argName)} -
+
+ {/if} + { - dispatch('select', `results.${e.detail}`) - }} + prefix="results" + on:select />
{/if} {/if} {#if displayContext} - Variables
+ Variables: + {#if displayVariable} -
- -
+ + dispatch('select', `variable('${e.detail}')`)} + prefix="variable" + on:select /> {:else} - + {'{...}'} + {/if}
- Resources +
+ Resources: + {#if displayResources} - dispatch('select', `resource('${e.detail}')`)} + prefix="resource" + on:select /> {:else} - + {'{...}'} + {/if}
{/if} diff --git a/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte b/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte index adf9d76aec..cbb2f9c33c 100644 --- a/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte +++ b/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte @@ -1,31 +1,21 @@
- Result + Result
- +
{#if flow_input} - Flow Input + Flow Input
- dispatch('select', `flow_input.${e.detail}`)} - /> +
{/if}
diff --git a/frontend/src/lib/components/propertyPicker/utils.ts b/frontend/src/lib/components/propertyPicker/utils.ts index 129bfdf671..27515b4190 100644 --- a/frontend/src/lib/components/propertyPicker/utils.ts +++ b/frontend/src/lib/components/propertyPicker/utils.ts @@ -1,44 +1,35 @@ -function filterByKey(obj: Object, key: string): Object { - if (Object(obj) !== obj) { - return obj +function filterByKey(obj: Object, key: string): any { + if (typeof obj !== 'object' || obj === null) { + return undefined } else if (Array.isArray(obj)) { - return obj.map((o) => filterByKey(o, key)) - } else { - return Object.fromEntries( - Object.entries(obj) - .filter(([k, v]) => !k.includes(key)) - .map(([k, v]) => [k, filterByKey(v, key)]) - ) - } -} - -function diff(target: Object, source: Object): Object { - if (Array.isArray(target)) { - return target - } - - const result = {} - - Object.keys(target ?? {}).forEach((key: string) => { - if (typeof source[key] === 'object') { - const difference = diff(target[key], source[key]) - - if (Object.keys(difference).length > 0) { - result[key] = difference - } - } else if (source[key] !== target[key]) { - result[key] = target[key] + let a = obj + .map((k, o) => + typeof o == 'object' ? filterByKey(o, key) : String(k - 1).includes(key) ? o : undefined + ) + .filter((v) => v !== undefined) + if (a.length === 0) { + return undefined + } else { + return a } - }) - - return result + } else { + let o = Object.fromEntries( + Object.entries(obj) + .map(([k, v]) => (k.includes(key) ? [k, v] : [k, filterByKey(v, key)])) + .filter(([k, v]) => v !== undefined) + ) + if (Object.keys(o).length === 0) { + return undefined + } + return o + } } export function keepByKey(json: Object | undefined, key: string): Object { if (!json) { return {} } - return diff(json, filterByKey(json, key)) + return filterByKey(json, key) ?? {} } // https://stackoverflow.com/questions/23377217/way-to-test-if-a-string-is-valid-identifier-name-in-javascript