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
This commit is contained in:
centdix
2025-09-18 09:20:26 +00:00
committed by GitHub
parent 4b7149527b
commit 56ddad2d5a
2 changed files with 91 additions and 0 deletions
@@ -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<string, any>; 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)
)
</script>
{#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}
<S3ArrayHelperButton
{connecting}
onClick={() =>
switchToJsAndConnect((path) => appendPathToArrayExpr(arg.expr, path))}
/>
{/if}
{:else if arg.expr != undefined}
<div class="border mt-2">
<SimpleEditor
@@ -712,6 +771,18 @@
{#if !hideHelpButton}
<DynamicInputHelpBox />
{/if}
{#if shouldShowS3ArrayHelper}
<S3ArrayHelperButton
{connecting}
onClick={() =>
focusProp?.(argName, 'connect', (path) => {
appendPathToArrayExpr(arg.expr, path)
return true
})}
/>
{/if}
<div class="mb-2"></div>
{:else}
Not recognized input type {argName} ({arg.expr}, {propertyType})
@@ -0,0 +1,20 @@
<script lang="ts">
import { Button } from '$lib/components/common'
import { Plug } from 'lucide-svelte'
interface Props {
label?: string
onClick: () => void
connecting?: boolean
}
let { label = 'Add object from an expression', onClick, connecting = false }: Props = $props()
</script>
{#if !connecting}
<div class="mt-2 mb-2">
<Button variant="border" color="light" size="xs" startIcon={{ icon: Plug }} onclick={onClick}>
{label}
</Button>
</div>
{/if}