From 56ddad2d5a960c782fad89c2c86ad9cc2c4dd8cb Mon Sep 17 00:00:00 2001 From: centdix <40307056+centdix@users.noreply.github.com> Date: Thu, 18 Sep 2025 11:20:26 +0200 Subject: [PATCH] feat(flow): Add helper to add expression to arrays (#6629) * Implement array expression helper for number arrays in JS mode - Add showArrayExprPicker state to manage helper UI - Add shouldShowArrayHelper() to check conditions (JS mode + number array) - Add 'Add item' button that shows picker UI when clicked - Implement picker row with disabled input, FlowPlugConnect, and Cancel button - Connect callback sets array expression [property] and updates Monaco editor - Include helpful text and proper cleanup on cancel/connect * Enhance array expression helper to append items to existing arrays - Check if current expr is already an array expression [...] - If it is, append new item to existing content: [existing, newItem] - If not or empty, create new array with single item: [newItem] - Update helper text to reflect append behavior - Maintains backward compatibility with non-array expressions * Add S3 resource array helper for JavaScript mode - Add shouldShowS3ArrayHelper() function to detect S3 resource arrays - Show direct FlowPlugConnect for S3 arrays instead of Add item button - Apply same append logic: add to existing array or create new one - Include helpful text explaining S3 resource connection - Support both s3_object and s3object resourceType variants * Add S3 resource catalog helper for static mode arrays - Add shouldShowS3ArrayStaticHelper() to detect S3 arrays in static mode - Show 'Add an object from the catalog' button below static S3 array inputs - Button switches to JavaScript mode and immediately activates connect mode - Sets initial empty array [] then replaces with [selectedPath] when connected - Includes helpful text explaining the mode switch and connection * Fix reactivity issue when switching from static to JS mode - Make button click handler async and await tick() before activating connect mode - Add Monaco editor update after setting expression in connect callback - Use tick().then() to ensure Monaco is available before calling setCode() - This ensures the SimpleEditor displays the new array expression immediately * Add plug icon to 'Add object from an expression' button - Import Plug icon from lucide-svelte - Add startIcon with Plug to the S3 array static helper button - Makes the button visually consistent with other connection-related UI elements * Unify S3 resource button style across static and JS modes - Replace 'Add S3 resource:' text + FlowPlugConnect with consistent Button style - Use same variant, color, size, and plug icon as static mode button - Maintain same functionality but with unified visual appearance - Both S3 helpers now use identical button styling * Consolidate and clean up array expression helpers - Extract appendPathToArrayExpr() to eliminate duplicate array building logic - Add switchToJsAndConnect() helper for consistent mode switching flow - Add emitChange() and updateEditor() utilities for consistent updates - Add safety reset of showArrayExprPicker when switching away from JS mode - Reduce code duplication across number and S3 array helpers - Improve maintainability and consistency * Remove number array helper functionality - Remove shouldShowArrayHelper() function for number arrays - Remove showArrayExprPicker state variable and related UI - Remove number array 'Add item' button and picker interface - Keep only S3 resource array helpers (static and JS modes) - Clean up unused safety reset logic for array picker * Create reusable S3ArrayHelperButton component - Extract S3 array helper button into dedicated component - Add consistent styling with Plug icon and configurable label - Replace both static and JavaScript mode button implementations - Reduce code duplication and improve maintainability - Component dispatches click event for parent handling * cleaning * Hide S3ArrayHelperButton when in connect mode - Add connecting prop to S3ArrayHelperButton component - Hide button when connecting is true to avoid UI clutter - Pass connecting state from InputTransformForm to both button instances - Improves UX by removing unnecessary button when plug is already active * cleaning * cleaning --- .../lib/components/InputTransformForm.svelte | 71 +++++++++++++++++++ .../lib/components/S3ArrayHelperButton.svelte | 20 ++++++ 2 files changed, 91 insertions(+) create mode 100644 frontend/src/lib/components/S3ArrayHelperButton.svelte diff --git a/frontend/src/lib/components/InputTransformForm.svelte b/frontend/src/lib/components/InputTransformForm.svelte index fdf354da96..c14a89da7b 100644 --- a/frontend/src/lib/components/InputTransformForm.svelte +++ b/frontend/src/lib/components/InputTransformForm.svelte @@ -38,6 +38,7 @@ import { twMerge } from 'tailwind-merge' import FlowPlugConnect from './FlowPlugConnect.svelte' import { deepEqual } from 'fast-equals' + import S3ArrayHelperButton from './S3ArrayHelperButton.svelte' interface Props { schema: Schema | { properties?: Record; required?: string[] } @@ -241,6 +242,51 @@ return inputCat === 'string' || inputCat === 'sql' || inputCat == 'yaml' } + function appendPathToArrayExpr(currentExpr: string | undefined, path: string) { + const trimmedExpr = currentExpr?.trim() || '' + + let newExpr = trimmedExpr + if (trimmedExpr.startsWith('[') && trimmedExpr.endsWith(']')) { + // Parse existing array and append new item + const innerContent = trimmedExpr.slice(1, -1).trim() + if (innerContent) { + newExpr = `[${innerContent}, ${path}]` + } else { + newExpr = `[${path}]` + } + } else { + // Create new array with single item + newExpr = `[${path}]` + } + arg.expr = newExpr + arg.type = 'javascript' + + // Update Monaco editor after setting the expression + tick().then(() => { + monaco?.setCode(newExpr) + }) + + // Dispatch change + dispatch('change', { argName, arg }) + } + + async function switchToJsAndConnect(onPath: (path: string) => void) { + // Switch to JavaScript mode + propertyType = 'javascript' + arg.type = 'javascript' + arg.expr = arg.expr || '[]' + arg.value = undefined + + // Wait for the component to re-render and Monaco to be available + await tick() + + // Activate connect mode + focusProp?.(argName, 'connect', (path) => { + onPath(path) + return true + }) + } + function connectProperty(rawValue: string) { // Extract path from variable('x') or resource('x') format const varMatch = variableMatch(rawValue) @@ -416,6 +462,10 @@ let connecting = $derived( $propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'connect' ) + let shouldShowS3ArrayHelper = $derived( + inputCat === 'list' && + ['s3object', 's3_object'].includes(schema?.properties?.[argName]?.items?.resourceType) + ) {#if arg != undefined && !hidden} @@ -492,6 +542,7 @@ on:selected={(e) => { if (e.detail == propertyType) return const staticTemplate = isStaticTemplate(inputCat) + if (e.detail === 'javascript') { if (arg.expr == undefined) { arg.expr = getDefaultExpr( @@ -683,6 +734,14 @@ bind:title={schema.properties[argName].title} bind:placeholder={schema.properties[argName].placeholder} /> + + {#if shouldShowS3ArrayHelper} + + switchToJsAndConnect((path) => appendPathToArrayExpr(arg.expr, path))} + /> + {/if} {:else if arg.expr != undefined}
{/if} + + {#if shouldShowS3ArrayHelper} + + focusProp?.(argName, 'connect', (path) => { + appendPathToArrayExpr(arg.expr, path) + return true + })} + /> + {/if} +
{:else} Not recognized input type {argName} ({arg.expr}, {propertyType}) diff --git a/frontend/src/lib/components/S3ArrayHelperButton.svelte b/frontend/src/lib/components/S3ArrayHelperButton.svelte new file mode 100644 index 0000000000..2ea6450602 --- /dev/null +++ b/frontend/src/lib/components/S3ArrayHelperButton.svelte @@ -0,0 +1,20 @@ + + +{#if !connecting} +
+ +
+{/if}