diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx index 2a07018026..3c9328b241 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx @@ -1,7 +1,15 @@ import { useEffect, useRef, useState } from 'react' import { Plus } from 'lucide-react' import { useParams } from 'next/navigation' -import { Badge, Button, Combobox, Input, Label, Textarea } from '@/components/emcn' +import { + Badge, + Button, + Combobox, + type ComboboxOption, + Input, + Label, + Textarea, +} from '@/components/emcn' import { Trash } from '@/components/emcn/icons/trash' import { cn } from '@/lib/core/utils/cn' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' @@ -38,6 +46,14 @@ const DEFAULT_ASSIGNMENT: Omit = { isExisting: false, } +/** + * Boolean value options for Combobox + */ +const BOOLEAN_OPTIONS: ComboboxOption[] = [ + { label: 'true', value: 'true' }, + { label: 'false', value: 'false' }, +] + /** * Parses a value that might be a JSON string or already an array of VariableAssignment. * This handles the case where workflows are imported with stringified values. @@ -104,8 +120,6 @@ export function VariablesInput({ const allVariablesAssigned = !hasNoWorkflowVariables && getAvailableVariablesFor('new').length === 0 - // Initialize with one empty assignment if none exist and not in preview/disabled mode - // Also add assignment when first variable is created useEffect(() => { if (!isReadOnly && assignments.length === 0 && currentWorkflowVariables.length > 0) { const initialAssignment: VariableAssignment = { @@ -116,45 +130,46 @@ export function VariablesInput({ } }, [currentWorkflowVariables.length, isReadOnly, assignments.length, setStoreValue]) - // Clean up assignments when their associated variables are deleted useEffect(() => { if (isReadOnly || assignments.length === 0) return const currentVariableIds = new Set(currentWorkflowVariables.map((v) => v.id)) const validAssignments = assignments.filter((assignment) => { - // Keep assignments that haven't selected a variable yet if (!assignment.variableId) return true - // Keep assignments whose variable still exists return currentVariableIds.has(assignment.variableId) }) - // If all variables were deleted, clear all assignments if (currentWorkflowVariables.length === 0) { setStoreValue([]) } else if (validAssignments.length !== assignments.length) { - // Some assignments reference deleted variables, remove them setStoreValue(validAssignments.length > 0 ? validAssignments : []) } }, [currentWorkflowVariables, assignments, isReadOnly, setStoreValue]) const addAssignment = () => { - if (isPreview || disabled || allVariablesAssigned) return + if (isReadOnly || allVariablesAssigned) return const newAssignment: VariableAssignment = { ...DEFAULT_ASSIGNMENT, id: crypto.randomUUID(), } - setStoreValue([...(assignments || []), newAssignment]) + setStoreValue([...assignments, newAssignment]) } const removeAssignment = (id: string) => { - if (isPreview || disabled) return - setStoreValue((assignments || []).filter((a) => a.id !== id)) + if (isReadOnly) return + + if (assignments.length === 1) { + setStoreValue([{ ...DEFAULT_ASSIGNMENT, id: crypto.randomUUID() }]) + return + } + + setStoreValue(assignments.filter((a) => a.id !== id)) } const updateAssignment = (id: string, updates: Partial) => { - if (isPreview || disabled) return - setStoreValue((assignments || []).map((a) => (a.id === id ? { ...a, ...updates } : a))) + if (isReadOnly) return + setStoreValue(assignments.map((a) => (a.id === id ? { ...a, ...updates } : a))) } const handleVariableSelect = (assignmentId: string, variableId: string) => { @@ -169,19 +184,12 @@ export function VariablesInput({ } } - const handleTagSelect = (tag: string) => { + const handleTagSelect = (newValue: string) => { if (!activeFieldId) return const assignment = assignments.find((a) => a.id === activeFieldId) - if (!assignment) return - - const currentValue = assignment.value || '' - - const textBeforeCursor = currentValue.slice(0, cursorPosition) - const lastOpenBracket = textBeforeCursor.lastIndexOf('<') - - const newValue = - currentValue.slice(0, lastOpenBracket) + tag + currentValue.slice(cursorPosition) + const originalValue = assignment?.value || '' + const textAfterCursor = originalValue.slice(cursorPosition) updateAssignment(activeFieldId, { value: newValue }) setShowTags(false) @@ -190,7 +198,7 @@ export function VariablesInput({ const inputEl = valueInputRefs.current[activeFieldId] if (inputEl) { inputEl.focus() - const newCursorPos = lastOpenBracket + tag.length + const newCursorPos = newValue.length - textAfterCursor.length inputEl.setSelectionRange(newCursorPos, newCursorPos) } }, 10) @@ -272,6 +280,18 @@ export function VariablesInput({ })) } + const syncOverlayScroll = (assignmentId: string, scrollLeft: number) => { + const overlay = overlayRefs.current[assignmentId] + if (overlay) overlay.scrollLeft = scrollLeft + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Escape') { + setShowTags(false) + setActiveSourceBlockId(null) + } + } + if (isPreview && (!assignments || assignments.length === 0)) { return (
@@ -302,7 +322,7 @@ export function VariablesInput({ return (
- {assignments && assignments.length > 0 && ( + {assignments.length > 0 && (
{assignments.map((assignment, index) => { const collapsed = collapsedAssignments[assignment.id] || false @@ -334,7 +354,7 @@ export function VariablesInput({
- {assignment.type === 'object' || assignment.type === 'array' ? ( + {assignment.type === 'boolean' ? ( + + !isReadOnly && updateAssignment(assignment.id, { value: v }) + } + placeholder='Select value' + disabled={isReadOnly} + /> + ) : assignment.type === 'object' || assignment.type === 'array' ? (