@d34dman/flowdrop 0.0.22 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/App.svelte +26 -25
- package/dist/components/ConfigForm.svelte +140 -519
- package/dist/components/ConfigForm.svelte.d.ts +5 -3
- package/dist/components/form/FormArray.svelte +1049 -0
- package/dist/components/form/FormArray.svelte.d.ts +22 -0
- package/dist/components/form/FormCheckboxGroup.svelte +152 -0
- package/dist/components/form/FormCheckboxGroup.svelte.d.ts +15 -0
- package/dist/components/form/FormField.svelte +279 -0
- package/dist/components/form/FormField.svelte.d.ts +18 -0
- package/dist/components/form/FormFieldWrapper.svelte +133 -0
- package/dist/components/form/FormFieldWrapper.svelte.d.ts +18 -0
- package/dist/components/form/FormNumberField.svelte +109 -0
- package/dist/components/form/FormNumberField.svelte.d.ts +23 -0
- package/dist/components/form/FormSelect.svelte +126 -0
- package/dist/components/form/FormSelect.svelte.d.ts +18 -0
- package/dist/components/form/FormTextField.svelte +88 -0
- package/dist/components/form/FormTextField.svelte.d.ts +17 -0
- package/dist/components/form/FormTextarea.svelte +94 -0
- package/dist/components/form/FormTextarea.svelte.d.ts +19 -0
- package/dist/components/form/FormToggle.svelte +123 -0
- package/dist/components/form/FormToggle.svelte.d.ts +17 -0
- package/dist/components/form/index.d.ts +41 -0
- package/dist/components/form/index.js +45 -0
- package/dist/components/form/types.d.ts +208 -0
- package/dist/components/form/types.js +29 -0
- package/dist/components/nodes/GatewayNode.svelte +84 -12
- package/dist/components/nodes/SimpleNode.svelte +41 -5
- package/dist/components/nodes/SimpleNode.svelte.d.ts +2 -1
- package/dist/components/nodes/SquareNode.svelte +41 -5
- package/dist/components/nodes/SquareNode.svelte.d.ts +2 -1
- package/dist/components/nodes/WorkflowNode.svelte +88 -5
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -3
- package/dist/stores/workflowStore.d.ts +15 -0
- package/dist/stores/workflowStore.js +28 -0
- package/dist/types/index.d.ts +77 -0
- package/dist/types/index.js +16 -0
- package/package.json +3 -3
- package/dist/config/demo.d.ts +0 -58
- package/dist/config/demo.js +0 -142
- package/dist/data/samples.d.ts +0 -51
- package/dist/data/samples.js +0 -3245
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Components Module
|
|
3
|
+
*
|
|
4
|
+
* Modular form components for dynamic form rendering based on JSON Schema.
|
|
5
|
+
* Designed for extensibility to support complex types like arrays and objects.
|
|
6
|
+
*
|
|
7
|
+
* @module form
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```svelte
|
|
11
|
+
* <script>
|
|
12
|
+
* import { FormField } from "./";
|
|
13
|
+
* import type { FieldSchema } from "./";
|
|
14
|
+
*
|
|
15
|
+
* const schema: FieldSchema = {
|
|
16
|
+
* type: "string",
|
|
17
|
+
* title: "Name",
|
|
18
|
+
* description: "Enter your name"
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* let value = $state("");
|
|
22
|
+
* </script>
|
|
23
|
+
*
|
|
24
|
+
* <FormField
|
|
25
|
+
* fieldKey="name"
|
|
26
|
+
* {schema}
|
|
27
|
+
* {value}
|
|
28
|
+
* onChange={(v) => value = v}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
// Types
|
|
33
|
+
export * from "./types.js";
|
|
34
|
+
// Main factory component
|
|
35
|
+
export { default as FormField } from "./FormField.svelte";
|
|
36
|
+
// Wrapper component
|
|
37
|
+
export { default as FormFieldWrapper } from "./FormFieldWrapper.svelte";
|
|
38
|
+
// Individual field components
|
|
39
|
+
export { default as FormTextField } from "./FormTextField.svelte";
|
|
40
|
+
export { default as FormTextarea } from "./FormTextarea.svelte";
|
|
41
|
+
export { default as FormNumberField } from "./FormNumberField.svelte";
|
|
42
|
+
export { default as FormToggle } from "./FormToggle.svelte";
|
|
43
|
+
export { default as FormSelect } from "./FormSelect.svelte";
|
|
44
|
+
export { default as FormCheckboxGroup } from "./FormCheckboxGroup.svelte";
|
|
45
|
+
export { default as FormArray } from "./FormArray.svelte";
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Field Types
|
|
3
|
+
* Shared types for form components in the FlowDrop workflow editor
|
|
4
|
+
*
|
|
5
|
+
* These types provide a foundation for dynamic form rendering based on JSON Schema
|
|
6
|
+
* and support extensibility for complex field types like arrays and objects.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Supported field types for form rendering
|
|
10
|
+
* Can be extended to support complex types like 'array' and 'object'
|
|
11
|
+
*/
|
|
12
|
+
export type FieldType = "string" | "number" | "integer" | "boolean" | "select" | "array" | "object";
|
|
13
|
+
/**
|
|
14
|
+
* Field format for specialized rendering
|
|
15
|
+
* - multiline: Renders as textarea
|
|
16
|
+
* - hidden: Field is hidden from UI but included in form submission
|
|
17
|
+
*/
|
|
18
|
+
export type FieldFormat = "multiline" | "hidden" | string;
|
|
19
|
+
/**
|
|
20
|
+
* Option type for select and checkbox group fields
|
|
21
|
+
*/
|
|
22
|
+
export interface FieldOption {
|
|
23
|
+
/** The value stored when this option is selected */
|
|
24
|
+
value: string;
|
|
25
|
+
/** The display label for this option */
|
|
26
|
+
label: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Base properties shared by all form fields
|
|
30
|
+
*/
|
|
31
|
+
export interface BaseFieldProps {
|
|
32
|
+
/** Unique identifier for the field (used for id and name attributes) */
|
|
33
|
+
id: string;
|
|
34
|
+
/** Current value of the field */
|
|
35
|
+
value: unknown;
|
|
36
|
+
/** Whether the field is required */
|
|
37
|
+
required?: boolean;
|
|
38
|
+
/** Whether the field is disabled */
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
/** Placeholder text for input fields */
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
/** ARIA description ID for accessibility */
|
|
43
|
+
ariaDescribedBy?: string;
|
|
44
|
+
/** Callback when the field value changes */
|
|
45
|
+
onChange: (value: unknown) => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Properties for text input fields
|
|
49
|
+
*/
|
|
50
|
+
export interface TextFieldProps extends BaseFieldProps {
|
|
51
|
+
value: string;
|
|
52
|
+
onChange: (value: string) => void;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Properties for multiline text fields (textarea)
|
|
56
|
+
*/
|
|
57
|
+
export interface TextareaFieldProps extends BaseFieldProps {
|
|
58
|
+
value: string;
|
|
59
|
+
/** Number of visible rows */
|
|
60
|
+
rows?: number;
|
|
61
|
+
onChange: (value: string) => void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Properties for number input fields
|
|
65
|
+
*/
|
|
66
|
+
export interface NumberFieldProps extends BaseFieldProps {
|
|
67
|
+
value: number | string;
|
|
68
|
+
/** Minimum allowed value */
|
|
69
|
+
min?: number;
|
|
70
|
+
/** Maximum allowed value */
|
|
71
|
+
max?: number;
|
|
72
|
+
/** Step increment for the input */
|
|
73
|
+
step?: number;
|
|
74
|
+
onChange: (value: number | string) => void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Properties for boolean toggle fields
|
|
78
|
+
*/
|
|
79
|
+
export interface ToggleFieldProps extends BaseFieldProps {
|
|
80
|
+
value: boolean;
|
|
81
|
+
/** Label shown when toggle is on */
|
|
82
|
+
onLabel?: string;
|
|
83
|
+
/** Label shown when toggle is off */
|
|
84
|
+
offLabel?: string;
|
|
85
|
+
onChange: (value: boolean) => void;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Properties for select dropdown fields
|
|
89
|
+
*/
|
|
90
|
+
export interface SelectFieldProps extends BaseFieldProps {
|
|
91
|
+
value: string;
|
|
92
|
+
/** Available options for selection */
|
|
93
|
+
options: FieldOption[] | string[];
|
|
94
|
+
onChange: (value: string) => void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Properties for checkbox group fields (multiple selection)
|
|
98
|
+
*/
|
|
99
|
+
export interface CheckboxGroupFieldProps extends BaseFieldProps {
|
|
100
|
+
value: string[];
|
|
101
|
+
/** Available options for selection */
|
|
102
|
+
options: string[];
|
|
103
|
+
onChange: (value: string[]) => void;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Properties for array fields (dynamic lists)
|
|
107
|
+
*/
|
|
108
|
+
export interface ArrayFieldProps extends BaseFieldProps {
|
|
109
|
+
value: unknown[];
|
|
110
|
+
/** Schema for array items */
|
|
111
|
+
itemSchema: FieldSchema;
|
|
112
|
+
/** Minimum number of items required */
|
|
113
|
+
minItems?: number;
|
|
114
|
+
/** Maximum number of items allowed */
|
|
115
|
+
maxItems?: number;
|
|
116
|
+
/** Label for the add button */
|
|
117
|
+
addLabel?: string;
|
|
118
|
+
onChange: (value: unknown[]) => void;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Field schema definition derived from JSON Schema property
|
|
122
|
+
* Used to determine which field component to render
|
|
123
|
+
*/
|
|
124
|
+
export interface FieldSchema {
|
|
125
|
+
/** The field type from JSON Schema */
|
|
126
|
+
type: FieldType | string;
|
|
127
|
+
/** Display title for the field */
|
|
128
|
+
title?: string;
|
|
129
|
+
/** Description for help text */
|
|
130
|
+
description?: string;
|
|
131
|
+
/** Default value for the field */
|
|
132
|
+
default?: unknown;
|
|
133
|
+
/** Enum values for select/checkbox fields */
|
|
134
|
+
enum?: unknown[];
|
|
135
|
+
/** Whether multiple values can be selected */
|
|
136
|
+
multiple?: boolean;
|
|
137
|
+
/** Format hint for specialized rendering */
|
|
138
|
+
format?: FieldFormat;
|
|
139
|
+
/** Options for select type fields (alternative to enum) */
|
|
140
|
+
options?: FieldOption[];
|
|
141
|
+
/** Placeholder text */
|
|
142
|
+
placeholder?: string;
|
|
143
|
+
/** Minimum value for numbers */
|
|
144
|
+
minimum?: number;
|
|
145
|
+
/** Maximum value for numbers */
|
|
146
|
+
maximum?: number;
|
|
147
|
+
/** Minimum length for strings */
|
|
148
|
+
minLength?: number;
|
|
149
|
+
/** Maximum length for strings */
|
|
150
|
+
maxLength?: number;
|
|
151
|
+
/** Validation pattern for strings */
|
|
152
|
+
pattern?: string;
|
|
153
|
+
/** Item schema for array fields */
|
|
154
|
+
items?: FieldSchema;
|
|
155
|
+
/** Minimum number of items for array fields */
|
|
156
|
+
minItems?: number;
|
|
157
|
+
/** Maximum number of items for array fields */
|
|
158
|
+
maxItems?: number;
|
|
159
|
+
/** Property schemas for object fields (future use) */
|
|
160
|
+
properties?: Record<string, FieldSchema>;
|
|
161
|
+
/** Required properties for object fields (future use) */
|
|
162
|
+
required?: string[];
|
|
163
|
+
/** Allow additional properties not defined by the schema */
|
|
164
|
+
[key: string]: unknown;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Props for the FormField factory component
|
|
168
|
+
* Renders the appropriate field component based on schema
|
|
169
|
+
*/
|
|
170
|
+
export interface FormFieldFactoryProps {
|
|
171
|
+
/** Unique key/id for the field */
|
|
172
|
+
fieldKey: string;
|
|
173
|
+
/** Field schema definition */
|
|
174
|
+
schema: FieldSchema;
|
|
175
|
+
/** Current field value */
|
|
176
|
+
value: unknown;
|
|
177
|
+
/** Whether the field is required */
|
|
178
|
+
required?: boolean;
|
|
179
|
+
/** Animation delay index for staggered animations */
|
|
180
|
+
animationIndex?: number;
|
|
181
|
+
/** Callback when the field value changes */
|
|
182
|
+
onChange: (value: unknown) => void;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Props for the FormFieldWrapper component
|
|
186
|
+
* Provides label, description, and layout for field content
|
|
187
|
+
*/
|
|
188
|
+
export interface FormFieldWrapperProps {
|
|
189
|
+
/** Field identifier for label association */
|
|
190
|
+
id: string;
|
|
191
|
+
/** Display label text */
|
|
192
|
+
label: string;
|
|
193
|
+
/** Whether the field is required */
|
|
194
|
+
required?: boolean;
|
|
195
|
+
/** Description/help text for the field */
|
|
196
|
+
description?: string;
|
|
197
|
+
/** Animation delay in milliseconds */
|
|
198
|
+
animationDelay?: number;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Type guard to check if options are FieldOption objects
|
|
202
|
+
*/
|
|
203
|
+
export declare function isFieldOptionArray(options: FieldOption[] | string[]): options is FieldOption[];
|
|
204
|
+
/**
|
|
205
|
+
* Normalize options to FieldOption format
|
|
206
|
+
* Converts string arrays to FieldOption arrays for consistent handling
|
|
207
|
+
*/
|
|
208
|
+
export declare function normalizeOptions(options: FieldOption[] | string[] | unknown[]): FieldOption[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Field Types
|
|
3
|
+
* Shared types for form components in the FlowDrop workflow editor
|
|
4
|
+
*
|
|
5
|
+
* These types provide a foundation for dynamic form rendering based on JSON Schema
|
|
6
|
+
* and support extensibility for complex field types like arrays and objects.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Type guard to check if options are FieldOption objects
|
|
10
|
+
*/
|
|
11
|
+
export function isFieldOptionArray(options) {
|
|
12
|
+
return options.length > 0 && typeof options[0] === "object" && "value" in options[0];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Normalize options to FieldOption format
|
|
16
|
+
* Converts string arrays to FieldOption arrays for consistent handling
|
|
17
|
+
*/
|
|
18
|
+
export function normalizeOptions(options) {
|
|
19
|
+
if (!options || options.length === 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
if (isFieldOptionArray(options)) {
|
|
23
|
+
return options;
|
|
24
|
+
}
|
|
25
|
+
return options.map((opt) => ({
|
|
26
|
+
value: String(opt),
|
|
27
|
+
label: String(opt)
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
@@ -3,19 +3,29 @@
|
|
|
3
3
|
Visual representation of gateway/branch nodes with branching flow indicators
|
|
4
4
|
Shows active branches and execution paths
|
|
5
5
|
Styled with BEM syntax following WorkflowNode pattern
|
|
6
|
+
|
|
7
|
+
UI Extensions Support:
|
|
8
|
+
- hideUnconnectedHandles: Hides ports that are not connected to reduce visual clutter
|
|
6
9
|
-->
|
|
7
10
|
|
|
8
11
|
<script lang="ts">
|
|
9
12
|
import { Position, Handle } from '@xyflow/svelte';
|
|
10
|
-
import type { WorkflowNode } from '../../types/index.js';
|
|
13
|
+
import type { WorkflowNode, NodePort } from '../../types/index.js';
|
|
11
14
|
import Icon from '@iconify/svelte';
|
|
12
15
|
import { getNodeIcon } from '../../utils/icons.js';
|
|
13
16
|
import { getDataTypeColorToken, getCategoryColorToken } from '../../utils/colors.js';
|
|
17
|
+
import { connectedHandles } from '../../stores/workflowStore.js';
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Branch interface for gateway nodes
|
|
21
|
+
* - name: Internal identifier used for handle IDs and connections
|
|
22
|
+
* - label: Display label shown in the UI (optional, defaults to name)
|
|
23
|
+
* - value: Optional value associated with the branch (e.g., for Switch matching)
|
|
24
|
+
*/
|
|
16
25
|
interface Branch {
|
|
17
26
|
name: string;
|
|
18
|
-
label
|
|
27
|
+
label?: string;
|
|
28
|
+
value?: string;
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
interface Props {
|
|
@@ -28,10 +38,70 @@
|
|
|
28
38
|
|
|
29
39
|
let props: Props = $props();
|
|
30
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Get the hideUnconnectedHandles setting from extensions
|
|
43
|
+
* Merges node type defaults with instance overrides
|
|
44
|
+
*/
|
|
45
|
+
const hideUnconnectedHandles = $derived(() => {
|
|
46
|
+
const typeDefault = props.data.metadata?.extensions?.ui?.hideUnconnectedHandles ?? false;
|
|
47
|
+
const instanceOverride = props.data.extensions?.ui?.hideUnconnectedHandles;
|
|
48
|
+
return instanceOverride ?? typeDefault;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a port should be visible based on connection state and settings
|
|
53
|
+
* @param port - The port to check
|
|
54
|
+
* @param type - Whether this is an 'input' or 'output' port
|
|
55
|
+
* @returns true if the port should be visible
|
|
56
|
+
*/
|
|
57
|
+
function isPortVisible(port: NodePort, type: 'input' | 'output'): boolean {
|
|
58
|
+
// Always show if hideUnconnectedHandles is disabled
|
|
59
|
+
if (!hideUnconnectedHandles()) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Always show required ports
|
|
64
|
+
if (port.required) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check if port is connected
|
|
69
|
+
const handleId = `${props.data.nodeId}-${type}-${port.id}`;
|
|
70
|
+
return $connectedHandles.has(handleId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Derived list of visible input ports based on hideUnconnectedHandles setting
|
|
75
|
+
*/
|
|
76
|
+
const visibleInputPorts = $derived(
|
|
77
|
+
props.data.metadata.inputs.filter((port) => isPortVisible(port, 'input'))
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if a branch output should be visible based on connection state
|
|
82
|
+
* @param branchName - The branch name to check
|
|
83
|
+
* @returns true if the branch should be visible
|
|
84
|
+
*/
|
|
85
|
+
function isBranchVisible(branchName: string): boolean {
|
|
86
|
+
// Always show if hideUnconnectedHandles is disabled
|
|
87
|
+
if (!hideUnconnectedHandles()) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check if branch output is connected
|
|
92
|
+
const handleId = `${props.data.nodeId}-output-${branchName}`;
|
|
93
|
+
return $connectedHandles.has(handleId);
|
|
94
|
+
}
|
|
95
|
+
|
|
31
96
|
// Gateway-specific data - branches are calculated at runtime from config
|
|
32
97
|
let branches = $derived((props.data.config?.branches as Branch[]) || []);
|
|
33
98
|
let activeBranches = $derived((props.data.executionInfo as any)?.output?.active_branches || []);
|
|
34
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Derived list of visible branches based on hideUnconnectedHandles setting
|
|
102
|
+
*/
|
|
103
|
+
const visibleBranches = $derived(branches.filter((branch) => isBranchVisible(branch.name)));
|
|
104
|
+
|
|
35
105
|
/**
|
|
36
106
|
* Handle node click - only handle selection, no config opening
|
|
37
107
|
*/
|
|
@@ -107,14 +177,14 @@
|
|
|
107
177
|
</p>
|
|
108
178
|
</div>
|
|
109
179
|
|
|
110
|
-
<!-- Input Ports Container -->
|
|
111
|
-
{#if
|
|
180
|
+
<!-- Input Ports Container (filtered based on hideUnconnectedHandles) -->
|
|
181
|
+
{#if visibleInputPorts.length > 0}
|
|
112
182
|
<div class="flowdrop-workflow-node__ports">
|
|
113
183
|
<div class="flowdrop-workflow-node__ports-header">
|
|
114
184
|
<h5 class="flowdrop-workflow-node__ports-title">Inputs</h5>
|
|
115
185
|
</div>
|
|
116
186
|
<div class="flowdrop-workflow-node__ports-list">
|
|
117
|
-
{#each
|
|
187
|
+
{#each visibleInputPorts as port (port.id)}
|
|
118
188
|
<div class="flowdrop-workflow-node__port">
|
|
119
189
|
<!-- Input Handle -->
|
|
120
190
|
<Handle
|
|
@@ -158,17 +228,17 @@
|
|
|
158
228
|
</div>
|
|
159
229
|
{/if}
|
|
160
230
|
|
|
161
|
-
<!-- Branches Section (Output Ports) -->
|
|
162
|
-
{#if
|
|
231
|
+
<!-- Branches Section (Output Ports) - filtered based on hideUnconnectedHandles -->
|
|
232
|
+
{#if visibleBranches.length > 0}
|
|
163
233
|
<div class="flowdrop-workflow-node__ports">
|
|
164
234
|
<div class="flowdrop-workflow-node__ports-header">
|
|
165
235
|
<h5 class="flowdrop-workflow-node__ports-title">
|
|
166
236
|
<Icon icon="mdi:source-branch" />
|
|
167
|
-
<span>Branches ({
|
|
237
|
+
<span>Branches ({visibleBranches.length})</span>
|
|
168
238
|
</h5>
|
|
169
239
|
</div>
|
|
170
240
|
<div class="flowdrop-workflow-node__ports-list">
|
|
171
|
-
{#each
|
|
241
|
+
{#each visibleBranches as branch (branch.name)}
|
|
172
242
|
{@const isActive = isBranchActive(branch.name)}
|
|
173
243
|
<div class="flowdrop-workflow-node__port">
|
|
174
244
|
<!-- Port Info -->
|
|
@@ -185,7 +255,7 @@
|
|
|
185
255
|
class="flowdrop-text--xs flowdrop-font--medium"
|
|
186
256
|
class:flowdrop-text--active={isActive}
|
|
187
257
|
>
|
|
188
|
-
{branch.name}
|
|
258
|
+
{branch.label || branch.name}
|
|
189
259
|
</span>
|
|
190
260
|
<span
|
|
191
261
|
class="flowdrop-badge flowdrop-badge--sm"
|
|
@@ -213,7 +283,8 @@
|
|
|
213
283
|
{/each}
|
|
214
284
|
</div>
|
|
215
285
|
</div>
|
|
216
|
-
{:else}
|
|
286
|
+
{:else if branches.length === 0}
|
|
287
|
+
<!-- No branches configured at all -->
|
|
217
288
|
<div class="flowdrop-workflow-node__ports">
|
|
218
289
|
<div class="workflow-node__no-branches">
|
|
219
290
|
<Icon icon="mdi:alert-circle-outline" />
|
|
@@ -221,6 +292,7 @@
|
|
|
221
292
|
</div>
|
|
222
293
|
</div>
|
|
223
294
|
{/if}
|
|
295
|
+
<!-- Note: When all branches are hidden due to hideUnconnectedHandles, we don't show anything -->
|
|
224
296
|
|
|
225
297
|
<!-- Config button -->
|
|
226
298
|
<button
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
Simple Node Component
|
|
3
3
|
A simple node with optional input and output ports
|
|
4
4
|
Styled with BEM syntax
|
|
5
|
+
|
|
6
|
+
UI Extensions Support:
|
|
7
|
+
- hideUnconnectedHandles: Hides trigger ports that are not connected to reduce visual clutter
|
|
5
8
|
-->
|
|
6
9
|
|
|
7
10
|
<script lang="ts">
|
|
8
11
|
import { Position, Handle } from '@xyflow/svelte';
|
|
9
|
-
import type { ConfigValues, NodeMetadata } from '../../types/index.js';
|
|
12
|
+
import type { ConfigValues, NodeMetadata, NodeExtensions } from '../../types/index.js';
|
|
10
13
|
import Icon from '@iconify/svelte';
|
|
11
14
|
import { getDataTypeColor } from '../../utils/colors.js';
|
|
15
|
+
import { connectedHandles } from '../../stores/workflowStore.js';
|
|
12
16
|
|
|
13
17
|
const props = $props<{
|
|
14
18
|
data: {
|
|
@@ -16,6 +20,7 @@
|
|
|
16
20
|
config: ConfigValues;
|
|
17
21
|
metadata: NodeMetadata;
|
|
18
22
|
nodeId?: string;
|
|
23
|
+
extensions?: NodeExtensions;
|
|
19
24
|
onConfigOpen?: (node: {
|
|
20
25
|
id: string;
|
|
21
26
|
type: string;
|
|
@@ -27,6 +32,37 @@
|
|
|
27
32
|
isError?: boolean;
|
|
28
33
|
}>();
|
|
29
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Get the hideUnconnectedHandles setting from extensions
|
|
37
|
+
* Merges node type defaults with instance overrides
|
|
38
|
+
*/
|
|
39
|
+
const hideUnconnectedHandles = $derived(() => {
|
|
40
|
+
const typeDefault = props.data.metadata?.extensions?.ui?.hideUnconnectedHandles ?? false;
|
|
41
|
+
const instanceOverride = props.data.extensions?.ui?.hideUnconnectedHandles;
|
|
42
|
+
return instanceOverride ?? typeDefault;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if a trigger port is connected
|
|
47
|
+
* @param portId - The port ID to check
|
|
48
|
+
* @param type - Whether this is an 'input' or 'output' port
|
|
49
|
+
*/
|
|
50
|
+
function isTriggerPortConnected(portId: string, type: 'input' | 'output'): boolean {
|
|
51
|
+
const handleId = `${props.data.nodeId}-${type}-${portId}`;
|
|
52
|
+
return $connectedHandles.has(handleId);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if a trigger port should be visible
|
|
57
|
+
* Always shows if hideUnconnectedHandles is disabled or if port is connected
|
|
58
|
+
*/
|
|
59
|
+
function shouldShowTriggerPort(portId: string, type: 'input' | 'output'): boolean {
|
|
60
|
+
if (!hideUnconnectedHandles()) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return isTriggerPortConnected(portId, type);
|
|
64
|
+
}
|
|
65
|
+
|
|
30
66
|
// Removed local config state - now using global ConfigSidebar
|
|
31
67
|
|
|
32
68
|
// Prioritize metadata icon over config icon for simple nodes (metadata is the node definition)
|
|
@@ -124,8 +160,8 @@
|
|
|
124
160
|
id={`${props.data.nodeId}-input-${firstDataInputPort.id}`}
|
|
125
161
|
/>
|
|
126
162
|
{/if}
|
|
127
|
-
{#if triggerInputPort}
|
|
128
|
-
<!-- Trigger Input - positioned at bottom-left -->
|
|
163
|
+
{#if triggerInputPort && shouldShowTriggerPort(triggerInputPort.id, 'input')}
|
|
164
|
+
<!-- Trigger Input - positioned at bottom-left (hidden if hideUnconnectedHandles enabled and not connected) -->
|
|
129
165
|
<Handle
|
|
130
166
|
type="target"
|
|
131
167
|
position={Position.Left}
|
|
@@ -217,8 +253,8 @@
|
|
|
217
253
|
)}; border-color: '#ffffff'; top: {hasBothOutputTypes ? '25%' : '50%'}; z-index: 30;"
|
|
218
254
|
/>
|
|
219
255
|
{/if}
|
|
220
|
-
{#if triggerOutputPort}
|
|
221
|
-
<!-- Trigger Output - positioned at bottom-right -->
|
|
256
|
+
{#if triggerOutputPort && shouldShowTriggerPort(triggerOutputPort.id, 'output')}
|
|
257
|
+
<!-- Trigger Output - positioned at bottom-right (hidden if hideUnconnectedHandles enabled and not connected) -->
|
|
222
258
|
<Handle
|
|
223
259
|
type="source"
|
|
224
260
|
position={Position.Right}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { ConfigValues, NodeMetadata } from '../../types/index.js';
|
|
1
|
+
import type { ConfigValues, NodeMetadata, NodeExtensions } from '../../types/index.js';
|
|
2
2
|
type $$ComponentProps = {
|
|
3
3
|
data: {
|
|
4
4
|
label: string;
|
|
5
5
|
config: ConfigValues;
|
|
6
6
|
metadata: NodeMetadata;
|
|
7
7
|
nodeId?: string;
|
|
8
|
+
extensions?: NodeExtensions;
|
|
8
9
|
onConfigOpen?: (node: {
|
|
9
10
|
id: string;
|
|
10
11
|
type: string;
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
Square Node Component
|
|
3
3
|
A simple square node with optional input and output ports
|
|
4
4
|
Styled with BEM syntax
|
|
5
|
+
|
|
6
|
+
UI Extensions Support:
|
|
7
|
+
- hideUnconnectedHandles: Hides trigger ports that are not connected to reduce visual clutter
|
|
5
8
|
-->
|
|
6
9
|
|
|
7
10
|
<script lang="ts">
|
|
8
11
|
import { Position, Handle } from '@xyflow/svelte';
|
|
9
|
-
import type { ConfigValues, NodeMetadata } from '../../types/index.js';
|
|
12
|
+
import type { ConfigValues, NodeMetadata, NodeExtensions } from '../../types/index.js';
|
|
10
13
|
import Icon from '@iconify/svelte';
|
|
11
14
|
import { getDataTypeColor } from '../../utils/colors.js';
|
|
15
|
+
import { connectedHandles } from '../../stores/workflowStore.js';
|
|
12
16
|
|
|
13
17
|
const props = $props<{
|
|
14
18
|
data: {
|
|
@@ -16,6 +20,7 @@
|
|
|
16
20
|
config: ConfigValues;
|
|
17
21
|
metadata: NodeMetadata;
|
|
18
22
|
nodeId?: string;
|
|
23
|
+
extensions?: NodeExtensions;
|
|
19
24
|
onConfigOpen?: (node: {
|
|
20
25
|
id: string;
|
|
21
26
|
type: string;
|
|
@@ -27,6 +32,37 @@
|
|
|
27
32
|
isError?: boolean;
|
|
28
33
|
}>();
|
|
29
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Get the hideUnconnectedHandles setting from extensions
|
|
37
|
+
* Merges node type defaults with instance overrides
|
|
38
|
+
*/
|
|
39
|
+
const hideUnconnectedHandles = $derived(() => {
|
|
40
|
+
const typeDefault = props.data.metadata?.extensions?.ui?.hideUnconnectedHandles ?? false;
|
|
41
|
+
const instanceOverride = props.data.extensions?.ui?.hideUnconnectedHandles;
|
|
42
|
+
return instanceOverride ?? typeDefault;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if a trigger port is connected
|
|
47
|
+
* @param portId - The port ID to check
|
|
48
|
+
* @param type - Whether this is an 'input' or 'output' port
|
|
49
|
+
*/
|
|
50
|
+
function isTriggerPortConnected(portId: string, type: 'input' | 'output'): boolean {
|
|
51
|
+
const handleId = `${props.data.nodeId}-${type}-${portId}`;
|
|
52
|
+
return $connectedHandles.has(handleId);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if a trigger port should be visible
|
|
57
|
+
* Always shows if hideUnconnectedHandles is disabled or if port is connected
|
|
58
|
+
*/
|
|
59
|
+
function shouldShowTriggerPort(portId: string, type: 'input' | 'output'): boolean {
|
|
60
|
+
if (!hideUnconnectedHandles()) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return isTriggerPortConnected(portId, type);
|
|
64
|
+
}
|
|
65
|
+
|
|
30
66
|
// Removed local config state - now using global ConfigSidebar
|
|
31
67
|
|
|
32
68
|
// Prioritize metadata icon over config icon for square nodes (metadata is the node definition)
|
|
@@ -110,8 +146,8 @@
|
|
|
110
146
|
id={`${props.data.nodeId}-input-${firstDataInputPort.id}`}
|
|
111
147
|
/>
|
|
112
148
|
{/if}
|
|
113
|
-
{#if triggerInputPort}
|
|
114
|
-
<!-- Trigger Input - positioned at bottom-left -->
|
|
149
|
+
{#if triggerInputPort && shouldShowTriggerPort(triggerInputPort.id, 'input')}
|
|
150
|
+
<!-- Trigger Input - positioned at bottom-left (hidden if hideUnconnectedHandles enabled and not connected) -->
|
|
115
151
|
<Handle
|
|
116
152
|
type="target"
|
|
117
153
|
position={Position.Left}
|
|
@@ -179,8 +215,8 @@
|
|
|
179
215
|
)}; border-color: '#ffffff'; top: {hasBothOutputTypes ? '25%' : '50%'}; z-index: 30;"
|
|
180
216
|
/>
|
|
181
217
|
{/if}
|
|
182
|
-
{#if triggerOutputPort}
|
|
183
|
-
<!-- Trigger Output - positioned at bottom-right -->
|
|
218
|
+
{#if triggerOutputPort && shouldShowTriggerPort(triggerOutputPort.id, 'output')}
|
|
219
|
+
<!-- Trigger Output - positioned at bottom-right (hidden if hideUnconnectedHandles enabled and not connected) -->
|
|
184
220
|
<Handle
|
|
185
221
|
type="source"
|
|
186
222
|
position={Position.Right}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { ConfigValues, NodeMetadata } from '../../types/index.js';
|
|
1
|
+
import type { ConfigValues, NodeMetadata, NodeExtensions } from '../../types/index.js';
|
|
2
2
|
type $$ComponentProps = {
|
|
3
3
|
data: {
|
|
4
4
|
label: string;
|
|
5
5
|
config: ConfigValues;
|
|
6
6
|
metadata: NodeMetadata;
|
|
7
7
|
nodeId?: string;
|
|
8
|
+
extensions?: NodeExtensions;
|
|
8
9
|
onConfigOpen?: (node: {
|
|
9
10
|
id: string;
|
|
10
11
|
type: string;
|