@d34dman/flowdrop 0.0.21 → 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 +69 -260
- package/dist/components/ConfigForm.svelte +357 -267
- package/dist/components/ConfigForm.svelte.d.ts +12 -3
- package/dist/components/ConfigPanel.svelte +160 -0
- package/dist/components/ConfigPanel.svelte.d.ts +32 -0
- package/dist/components/ReadOnlyDetails.svelte +168 -0
- package/dist/components/ReadOnlyDetails.svelte.d.ts +25 -0
- package/dist/components/WorkflowEditor.svelte +1 -1
- 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/NotesNode.svelte +89 -307
- package/dist/components/nodes/NotesNode.svelte.d.ts +3 -22
- 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 +4 -4
- package/dist/index.js +3 -4
- 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/components/ConfigSidebar.svelte +0 -916
- package/dist/components/ConfigSidebar.svelte.d.ts +0 -51
- 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
|
@@ -1,29 +1,55 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
ConfigForm Component
|
|
3
|
-
Handles dynamic form rendering for node configuration
|
|
3
|
+
Handles dynamic form rendering for node or entity configuration
|
|
4
|
+
Supports both node-based config and direct schema/values
|
|
4
5
|
Uses reactive $state for proper Svelte 5 reactivity
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Dynamic form generation from JSON Schema using modular form components
|
|
9
|
+
- UI Extensions support for display settings (e.g., hide unconnected handles)
|
|
10
|
+
- Extensible architecture for complex schema types (array, object)
|
|
11
|
+
|
|
12
|
+
Accessibility features:
|
|
13
|
+
- Proper label associations with for/id attributes
|
|
14
|
+
- ARIA describedby for field descriptions
|
|
15
|
+
- Focus-visible states for keyboard navigation
|
|
16
|
+
- Required field indicators
|
|
5
17
|
-->
|
|
6
18
|
|
|
7
19
|
<script lang="ts">
|
|
8
|
-
import
|
|
20
|
+
import Icon from '@iconify/svelte';
|
|
21
|
+
import type { ConfigSchema, WorkflowNode, NodeUIExtensions } from '../types/index.js';
|
|
22
|
+
import { FormField, FormFieldWrapper, FormToggle } from './form/index.js';
|
|
23
|
+
import type { FieldSchema } from './form/index.js';
|
|
9
24
|
|
|
10
25
|
interface Props {
|
|
11
|
-
node
|
|
12
|
-
|
|
26
|
+
/** Optional workflow node (if provided, schema and values are derived from it) */
|
|
27
|
+
node?: WorkflowNode;
|
|
28
|
+
/** Direct config schema (used when node is not provided) */
|
|
29
|
+
schema?: ConfigSchema;
|
|
30
|
+
/** Direct config values (used when node is not provided) */
|
|
31
|
+
values?: Record<string, unknown>;
|
|
32
|
+
/** Whether to show UI extension settings section */
|
|
33
|
+
showUIExtensions?: boolean;
|
|
34
|
+
/** Callback when form is saved (includes both config and extensions if enabled) */
|
|
35
|
+
onSave: (config: Record<string, unknown>, uiExtensions?: NodeUIExtensions) => void;
|
|
36
|
+
/** Callback when form is cancelled */
|
|
13
37
|
onCancel: () => void;
|
|
14
38
|
}
|
|
15
39
|
|
|
16
|
-
let { node, onSave, onCancel }: Props = $props();
|
|
40
|
+
let { node, schema, values, showUIExtensions = true, onSave, onCancel }: Props = $props();
|
|
17
41
|
|
|
18
42
|
/**
|
|
19
|
-
* Get the configuration schema from node metadata
|
|
43
|
+
* Get the configuration schema from node metadata or direct prop
|
|
20
44
|
*/
|
|
21
|
-
const configSchema = $derived(
|
|
45
|
+
const configSchema = $derived(
|
|
46
|
+
schema ?? (node?.data.metadata?.configSchema as ConfigSchema | undefined)
|
|
47
|
+
);
|
|
22
48
|
|
|
23
49
|
/**
|
|
24
|
-
* Get the current node
|
|
50
|
+
* Get the current configuration from node or direct prop
|
|
25
51
|
*/
|
|
26
|
-
const
|
|
52
|
+
const initialConfig = $derived(values ?? node?.data.config ?? {});
|
|
27
53
|
|
|
28
54
|
/**
|
|
29
55
|
* Create reactive configuration values using $state
|
|
@@ -32,361 +58,425 @@
|
|
|
32
58
|
let configValues = $state<Record<string, unknown>>({});
|
|
33
59
|
|
|
34
60
|
/**
|
|
35
|
-
*
|
|
61
|
+
* UI Extension values for display settings
|
|
62
|
+
* Merges node type defaults with instance overrides
|
|
63
|
+
*/
|
|
64
|
+
let uiExtensionValues = $state<NodeUIExtensions>({});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get initial UI extensions from node (instance level overrides type level)
|
|
68
|
+
*/
|
|
69
|
+
const initialUIExtensions = $derived.by<NodeUIExtensions>(() => {
|
|
70
|
+
if (!node) return {};
|
|
71
|
+
// Merge type-level defaults with instance-level overrides
|
|
72
|
+
const typeDefaults = node.data.metadata?.extensions?.ui ?? {};
|
|
73
|
+
const instanceOverrides = node.data.extensions?.ui ?? {};
|
|
74
|
+
return { ...typeDefaults, ...instanceOverrides };
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Initialize config values when node/schema changes
|
|
36
79
|
*/
|
|
37
80
|
$effect(() => {
|
|
38
81
|
if (configSchema?.properties) {
|
|
39
82
|
const mergedConfig: Record<string, unknown> = {};
|
|
40
83
|
Object.entries(configSchema.properties).forEach(([key, field]) => {
|
|
41
|
-
const fieldConfig = field as
|
|
84
|
+
const fieldConfig = field as Record<string, unknown>;
|
|
42
85
|
// Use existing value if available, otherwise use default
|
|
43
|
-
mergedConfig[key] =
|
|
86
|
+
mergedConfig[key] =
|
|
87
|
+
initialConfig[key] !== undefined ? initialConfig[key] : fieldConfig.default;
|
|
44
88
|
});
|
|
45
89
|
configValues = mergedConfig;
|
|
46
90
|
}
|
|
47
91
|
});
|
|
48
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Initialize UI extension values when node changes
|
|
95
|
+
*/
|
|
96
|
+
$effect(() => {
|
|
97
|
+
uiExtensionValues = {
|
|
98
|
+
hideUnconnectedHandles: initialUIExtensions.hideUnconnectedHandles ?? false
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if a field is required based on schema
|
|
104
|
+
*/
|
|
105
|
+
function isFieldRequired(key: string): boolean {
|
|
106
|
+
if (!configSchema?.required) return false;
|
|
107
|
+
return configSchema.required.includes(key);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Handle field value changes from FormField components
|
|
112
|
+
*/
|
|
113
|
+
function handleFieldChange(key: string, value: unknown): void {
|
|
114
|
+
configValues[key] = value;
|
|
115
|
+
}
|
|
116
|
+
|
|
49
117
|
/**
|
|
50
118
|
* Handle form submission
|
|
119
|
+
* Collects both config values and UI extension values
|
|
51
120
|
*/
|
|
52
121
|
function handleSave(): void {
|
|
53
122
|
// Collect all form values including hidden fields
|
|
54
|
-
const form = document.querySelector('.
|
|
123
|
+
const form = document.querySelector('.config-form');
|
|
55
124
|
const updatedConfig: Record<string, unknown> = { ...configValues };
|
|
56
125
|
|
|
57
126
|
if (form) {
|
|
58
127
|
const inputs = form.querySelectorAll('input, select, textarea');
|
|
59
|
-
inputs.forEach((input:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
updatedConfig[
|
|
65
|
-
} else if (
|
|
128
|
+
inputs.forEach((input: Element) => {
|
|
129
|
+
const inputEl = input as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
130
|
+
// Skip UI extension fields (prefixed with ext-)
|
|
131
|
+
if (inputEl.id && !inputEl.id.startsWith('ext-')) {
|
|
132
|
+
if (inputEl instanceof HTMLInputElement && inputEl.type === 'checkbox') {
|
|
133
|
+
updatedConfig[inputEl.id] = inputEl.checked;
|
|
134
|
+
} else if (inputEl instanceof HTMLInputElement && inputEl.type === 'number') {
|
|
135
|
+
updatedConfig[inputEl.id] = inputEl.value ? Number(inputEl.value) : inputEl.value;
|
|
136
|
+
} else if (inputEl instanceof HTMLInputElement && inputEl.type === 'hidden') {
|
|
66
137
|
// Parse hidden field values that might be JSON
|
|
67
138
|
try {
|
|
68
|
-
const parsed = JSON.parse(
|
|
69
|
-
updatedConfig[
|
|
139
|
+
const parsed = JSON.parse(inputEl.value);
|
|
140
|
+
updatedConfig[inputEl.id] = parsed;
|
|
70
141
|
} catch {
|
|
71
142
|
// If not JSON, use raw value
|
|
72
|
-
updatedConfig[
|
|
143
|
+
updatedConfig[inputEl.id] = inputEl.value;
|
|
73
144
|
}
|
|
74
145
|
} else {
|
|
75
|
-
updatedConfig[
|
|
146
|
+
updatedConfig[inputEl.id] = inputEl.value;
|
|
76
147
|
}
|
|
77
148
|
}
|
|
78
149
|
});
|
|
79
150
|
}
|
|
80
151
|
|
|
81
152
|
// Preserve hidden field values from original config if not collected from form
|
|
82
|
-
if (
|
|
83
|
-
Object.entries(configSchema.properties).forEach(
|
|
84
|
-
|
|
85
|
-
updatedConfig
|
|
153
|
+
if (initialConfig && configSchema?.properties) {
|
|
154
|
+
Object.entries(configSchema.properties).forEach(
|
|
155
|
+
([key, property]: [string, Record<string, unknown>]) => {
|
|
156
|
+
if (property.format === 'hidden' && !(key in updatedConfig) && key in initialConfig) {
|
|
157
|
+
updatedConfig[key] = initialConfig[key];
|
|
158
|
+
}
|
|
86
159
|
}
|
|
87
|
-
|
|
160
|
+
);
|
|
88
161
|
}
|
|
89
162
|
|
|
90
|
-
|
|
163
|
+
// Pass UI extensions only if enabled
|
|
164
|
+
if (showUIExtensions && node) {
|
|
165
|
+
onSave(updatedConfig, uiExtensionValues);
|
|
166
|
+
} else {
|
|
167
|
+
onSave(updatedConfig);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Convert ConfigProperty to FieldSchema for FormField component
|
|
173
|
+
*/
|
|
174
|
+
function toFieldSchema(property: Record<string, unknown>): FieldSchema {
|
|
175
|
+
return property as FieldSchema;
|
|
91
176
|
}
|
|
92
177
|
</script>
|
|
93
178
|
|
|
94
179
|
{#if configSchema}
|
|
95
|
-
<
|
|
180
|
+
<form
|
|
181
|
+
class="config-form"
|
|
182
|
+
onsubmit={(e) => {
|
|
183
|
+
e.preventDefault();
|
|
184
|
+
handleSave();
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
96
187
|
{#if configSchema.properties}
|
|
97
|
-
|
|
98
|
-
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
value={String(option)}
|
|
113
|
-
checked={Array.isArray(configValues[key]) &&
|
|
114
|
-
configValues[key].includes(String(option))}
|
|
115
|
-
onchange={(e) => {
|
|
116
|
-
const checked = e.currentTarget.checked;
|
|
117
|
-
const currentValues = Array.isArray(configValues[key])
|
|
118
|
-
? [...(configValues[key] as unknown[])]
|
|
119
|
-
: [];
|
|
120
|
-
if (checked) {
|
|
121
|
-
if (!currentValues.includes(String(option))) {
|
|
122
|
-
configValues[key] = [...currentValues, String(option)];
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
configValues[key] = currentValues.filter((v) => v !== String(option));
|
|
126
|
-
}
|
|
127
|
-
}}
|
|
128
|
-
/>
|
|
129
|
-
<span class="flowdrop-config-sidebar__checkbox-label">
|
|
130
|
-
{String(option)}
|
|
131
|
-
</span>
|
|
132
|
-
</label>
|
|
133
|
-
{/each}
|
|
134
|
-
</div>
|
|
135
|
-
{:else if fieldConfig.enum}
|
|
136
|
-
<!-- Select for enum with single selection -->
|
|
137
|
-
<select
|
|
138
|
-
id={key}
|
|
139
|
-
class="flowdrop-config-sidebar__select"
|
|
140
|
-
bind:value={configValues[key]}
|
|
141
|
-
>
|
|
142
|
-
{#each fieldConfig.enum as option (String(option))}
|
|
143
|
-
<option value={String(option)}>{String(option)}</option>
|
|
144
|
-
{/each}
|
|
145
|
-
</select>
|
|
146
|
-
{:else if fieldConfig.type === 'string' && fieldConfig.format === 'multiline'}
|
|
147
|
-
<!-- Textarea for multiline strings -->
|
|
148
|
-
<textarea
|
|
149
|
-
id={key}
|
|
150
|
-
class="flowdrop-config-sidebar__textarea"
|
|
151
|
-
bind:value={configValues[key]}
|
|
152
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
153
|
-
rows="4"
|
|
154
|
-
></textarea>
|
|
155
|
-
{:else if fieldConfig.type === 'string'}
|
|
156
|
-
<input
|
|
157
|
-
id={key}
|
|
158
|
-
type="text"
|
|
159
|
-
class="flowdrop-config-sidebar__input"
|
|
160
|
-
bind:value={configValues[key]}
|
|
161
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
162
|
-
/>
|
|
163
|
-
{:else if fieldConfig.type === 'number'}
|
|
164
|
-
<input
|
|
165
|
-
id={key}
|
|
166
|
-
type="number"
|
|
167
|
-
class="flowdrop-config-sidebar__input"
|
|
168
|
-
bind:value={configValues[key]}
|
|
169
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
170
|
-
/>
|
|
171
|
-
{:else if fieldConfig.type === 'boolean'}
|
|
172
|
-
<input
|
|
173
|
-
id={key}
|
|
174
|
-
type="checkbox"
|
|
175
|
-
class="flowdrop-config-sidebar__checkbox"
|
|
176
|
-
checked={Boolean(configValues[key] || fieldConfig.default || false)}
|
|
177
|
-
onchange={(e) => {
|
|
178
|
-
configValues[key] = e.currentTarget.checked;
|
|
179
|
-
}}
|
|
180
|
-
/>
|
|
181
|
-
{:else if fieldConfig.type === 'select' || fieldConfig.options}
|
|
182
|
-
<select
|
|
183
|
-
id={key}
|
|
184
|
-
class="flowdrop-config-sidebar__select"
|
|
185
|
-
bind:value={configValues[key]}
|
|
186
|
-
>
|
|
187
|
-
{#if fieldConfig.options}
|
|
188
|
-
{#each fieldConfig.options as option (String(option.value))}
|
|
189
|
-
{@const optionConfig = option as any}
|
|
190
|
-
<option value={String(optionConfig.value)}>{String(optionConfig.label)}</option>
|
|
191
|
-
{/each}
|
|
192
|
-
{/if}
|
|
193
|
-
</select>
|
|
194
|
-
{:else}
|
|
195
|
-
<!-- Fallback for unknown field types -->
|
|
196
|
-
<input
|
|
197
|
-
id={key}
|
|
198
|
-
type="text"
|
|
199
|
-
class="flowdrop-config-sidebar__input"
|
|
200
|
-
bind:value={configValues[key]}
|
|
201
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
202
|
-
/>
|
|
203
|
-
{/if}
|
|
204
|
-
{#if fieldConfig.description}
|
|
205
|
-
<p class="flowdrop-config-sidebar__field-description">
|
|
206
|
-
{String(fieldConfig.description)}
|
|
207
|
-
</p>
|
|
208
|
-
{/if}
|
|
209
|
-
</div>
|
|
210
|
-
{/if}
|
|
211
|
-
{/each}
|
|
188
|
+
<div class="config-form__fields">
|
|
189
|
+
{#each Object.entries(configSchema.properties) as [key, field], index (key)}
|
|
190
|
+
{@const fieldSchema = toFieldSchema(field as Record<string, unknown>)}
|
|
191
|
+
{@const required = isFieldRequired(key)}
|
|
192
|
+
|
|
193
|
+
<FormField
|
|
194
|
+
fieldKey={key}
|
|
195
|
+
schema={fieldSchema}
|
|
196
|
+
value={configValues[key]}
|
|
197
|
+
{required}
|
|
198
|
+
animationIndex={index}
|
|
199
|
+
onChange={(val) => handleFieldChange(key, val)}
|
|
200
|
+
/>
|
|
201
|
+
{/each}
|
|
202
|
+
</div>
|
|
212
203
|
{:else}
|
|
213
204
|
<!-- If no properties, show the raw schema for debugging -->
|
|
214
|
-
<div class="
|
|
215
|
-
<
|
|
216
|
-
|
|
205
|
+
<div class="config-form__debug">
|
|
206
|
+
<div class="config-form__debug-header">
|
|
207
|
+
<Icon icon="heroicons:bug-ant" class="config-form__debug-icon" />
|
|
208
|
+
<span>Debug - Config Schema</span>
|
|
209
|
+
</div>
|
|
210
|
+
<pre class="config-form__debug-content">{JSON.stringify(configSchema, null, 2)}</pre>
|
|
217
211
|
</div>
|
|
218
212
|
{/if}
|
|
219
|
-
</div>
|
|
220
213
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
214
|
+
<!-- UI Extensions Section -->
|
|
215
|
+
{#if showUIExtensions && node}
|
|
216
|
+
<div class="config-form__extensions">
|
|
217
|
+
<div class="config-form__extensions-header">
|
|
218
|
+
<Icon icon="heroicons:adjustments-horizontal" class="config-form__extensions-icon" />
|
|
219
|
+
<span>Display Settings</span>
|
|
220
|
+
</div>
|
|
221
|
+
<div class="config-form__extensions-content">
|
|
222
|
+
<!-- Hide Unconnected Handles Toggle -->
|
|
223
|
+
<FormFieldWrapper
|
|
224
|
+
id="ext-hideUnconnectedHandles"
|
|
225
|
+
label="Hide Unconnected Ports"
|
|
226
|
+
description="Hide input and output ports that are not connected to reduce visual clutter"
|
|
227
|
+
>
|
|
228
|
+
<FormToggle
|
|
229
|
+
id="ext-hideUnconnectedHandles"
|
|
230
|
+
value={Boolean(uiExtensionValues.hideUnconnectedHandles)}
|
|
231
|
+
onLabel="Hidden"
|
|
232
|
+
offLabel="Visible"
|
|
233
|
+
ariaDescribedBy="ext-hideUnconnectedHandles-description"
|
|
234
|
+
onChange={(val) => {
|
|
235
|
+
uiExtensionValues.hideUnconnectedHandles = val;
|
|
236
|
+
}}
|
|
237
|
+
/>
|
|
238
|
+
</FormFieldWrapper>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
{/if}
|
|
242
|
+
|
|
243
|
+
<!-- Footer Actions -->
|
|
244
|
+
<div class="config-form__footer">
|
|
245
|
+
<button
|
|
246
|
+
type="button"
|
|
247
|
+
class="config-form__button config-form__button--secondary"
|
|
248
|
+
onclick={onCancel}
|
|
249
|
+
>
|
|
250
|
+
<Icon icon="heroicons:x-mark" class="config-form__button-icon" />
|
|
251
|
+
<span>Cancel</span>
|
|
252
|
+
</button>
|
|
253
|
+
<button type="submit" class="config-form__button config-form__button--primary">
|
|
254
|
+
<Icon icon="heroicons:check" class="config-form__button-icon" />
|
|
255
|
+
<span>Save Changes</span>
|
|
256
|
+
</button>
|
|
257
|
+
</div>
|
|
258
|
+
</form>
|
|
236
259
|
{:else}
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
|
|
260
|
+
<div class="config-form__empty">
|
|
261
|
+
<div class="config-form__empty-icon">
|
|
262
|
+
<Icon icon="heroicons:cog-6-tooth" />
|
|
263
|
+
</div>
|
|
264
|
+
<p class="config-form__empty-text">No configuration options available for this node.</p>
|
|
265
|
+
</div>
|
|
240
266
|
{/if}
|
|
241
267
|
|
|
242
268
|
<style>
|
|
243
|
-
|
|
269
|
+
/* ============================================
|
|
270
|
+
CONFIG FORM - Container Styles
|
|
271
|
+
Individual field styles are in form/ components
|
|
272
|
+
============================================ */
|
|
273
|
+
|
|
274
|
+
.config-form {
|
|
244
275
|
display: flex;
|
|
245
276
|
flex-direction: column;
|
|
246
|
-
gap:
|
|
277
|
+
gap: 1.5rem;
|
|
247
278
|
}
|
|
248
279
|
|
|
249
|
-
.
|
|
280
|
+
.config-form__fields {
|
|
250
281
|
display: flex;
|
|
251
282
|
flex-direction: column;
|
|
252
|
-
gap:
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.flowdrop-config-sidebar__field-label {
|
|
256
|
-
font-size: 0.875rem;
|
|
257
|
-
font-weight: 500;
|
|
258
|
-
color: #374151;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.flowdrop-config-sidebar__input,
|
|
262
|
-
.flowdrop-config-sidebar__select {
|
|
263
|
-
padding: 0.5rem;
|
|
264
|
-
border: 1px solid #d1d5db;
|
|
265
|
-
border-radius: 0.375rem;
|
|
266
|
-
font-size: 0.875rem;
|
|
267
|
-
transition:
|
|
268
|
-
border-color 0.2s,
|
|
269
|
-
box-shadow 0.2s;
|
|
283
|
+
gap: 1.25rem;
|
|
270
284
|
}
|
|
271
285
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
border-color: #3b82f6;
|
|
276
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
277
|
-
}
|
|
286
|
+
/* ============================================
|
|
287
|
+
FOOTER ACTIONS
|
|
288
|
+
============================================ */
|
|
278
289
|
|
|
279
|
-
.
|
|
290
|
+
.config-form__footer {
|
|
280
291
|
display: flex;
|
|
281
|
-
|
|
282
|
-
|
|
292
|
+
gap: 0.75rem;
|
|
293
|
+
justify-content: flex-end;
|
|
294
|
+
padding-top: 1rem;
|
|
295
|
+
border-top: 1px solid var(--color-ref-gray-100, #f3f4f6);
|
|
296
|
+
margin-top: 0.5rem;
|
|
283
297
|
}
|
|
284
298
|
|
|
285
|
-
.
|
|
286
|
-
display: flex;
|
|
299
|
+
.config-form__button {
|
|
300
|
+
display: inline-flex;
|
|
287
301
|
align-items: center;
|
|
302
|
+
justify-content: center;
|
|
288
303
|
gap: 0.5rem;
|
|
304
|
+
padding: 0.625rem 1rem;
|
|
305
|
+
border-radius: 0.5rem;
|
|
306
|
+
font-size: 0.875rem;
|
|
307
|
+
font-weight: 600;
|
|
308
|
+
font-family: inherit;
|
|
289
309
|
cursor: pointer;
|
|
310
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
311
|
+
border: 1px solid transparent;
|
|
312
|
+
min-height: 2.5rem;
|
|
290
313
|
}
|
|
291
314
|
|
|
292
|
-
.
|
|
315
|
+
.config-form__button :global(svg) {
|
|
293
316
|
width: 1rem;
|
|
294
317
|
height: 1rem;
|
|
295
|
-
|
|
296
|
-
cursor: pointer;
|
|
318
|
+
flex-shrink: 0;
|
|
297
319
|
}
|
|
298
320
|
|
|
299
|
-
.
|
|
300
|
-
|
|
301
|
-
color: #
|
|
302
|
-
|
|
321
|
+
.config-form__button--secondary {
|
|
322
|
+
background-color: #ffffff;
|
|
323
|
+
border-color: var(--color-ref-gray-200, #e5e7eb);
|
|
324
|
+
color: var(--color-ref-gray-700, #374151);
|
|
325
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
303
326
|
}
|
|
304
327
|
|
|
305
|
-
.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
border-radius: 0.375rem;
|
|
310
|
-
font-size: 0.875rem;
|
|
311
|
-
background-color: #ffffff;
|
|
312
|
-
transition: all 0.2s ease-in-out;
|
|
313
|
-
resize: vertical;
|
|
314
|
-
min-height: 4rem;
|
|
328
|
+
.config-form__button--secondary:hover {
|
|
329
|
+
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
330
|
+
border-color: var(--color-ref-gray-300, #d1d5db);
|
|
331
|
+
color: var(--color-ref-gray-900, #111827);
|
|
315
332
|
}
|
|
316
333
|
|
|
317
|
-
.
|
|
334
|
+
.config-form__button--secondary:focus-visible {
|
|
318
335
|
outline: none;
|
|
319
|
-
|
|
320
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
336
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
|
321
337
|
}
|
|
322
338
|
|
|
323
|
-
.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
339
|
+
.config-form__button--primary {
|
|
340
|
+
background: linear-gradient(
|
|
341
|
+
135deg,
|
|
342
|
+
var(--color-ref-blue-500, #3b82f6) 0%,
|
|
343
|
+
var(--color-ref-blue-600, #2563eb) 100%
|
|
344
|
+
);
|
|
345
|
+
color: #ffffff;
|
|
346
|
+
box-shadow:
|
|
347
|
+
0 1px 3px rgba(59, 130, 246, 0.3),
|
|
348
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
328
349
|
}
|
|
329
350
|
|
|
330
|
-
.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
351
|
+
.config-form__button--primary:hover {
|
|
352
|
+
background: linear-gradient(
|
|
353
|
+
135deg,
|
|
354
|
+
var(--color-ref-blue-600, #2563eb) 0%,
|
|
355
|
+
var(--color-ref-blue-700, #1d4ed8) 100%
|
|
356
|
+
);
|
|
357
|
+
box-shadow:
|
|
358
|
+
0 4px 12px rgba(59, 130, 246, 0.35),
|
|
359
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
360
|
+
transform: translateY(-1px);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.config-form__button--primary:active {
|
|
364
|
+
transform: translateY(0);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.config-form__button--primary:focus-visible {
|
|
368
|
+
outline: none;
|
|
369
|
+
box-shadow:
|
|
370
|
+
0 0 0 3px rgba(59, 130, 246, 0.4),
|
|
371
|
+
0 4px 12px rgba(59, 130, 246, 0.35);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/* ============================================
|
|
375
|
+
UI EXTENSIONS SECTION
|
|
376
|
+
============================================ */
|
|
377
|
+
|
|
378
|
+
.config-form__extensions {
|
|
379
|
+
background-color: var(--color-ref-slate-50, #f8fafc);
|
|
380
|
+
border: 1px solid var(--color-ref-slate-200, #e2e8f0);
|
|
381
|
+
border-radius: 0.5rem;
|
|
382
|
+
overflow: hidden;
|
|
383
|
+
margin-top: 0.5rem;
|
|
335
384
|
}
|
|
336
385
|
|
|
337
|
-
.
|
|
386
|
+
.config-form__extensions-header {
|
|
338
387
|
display: flex;
|
|
339
|
-
gap: 0.75rem;
|
|
340
|
-
justify-content: flex-end;
|
|
341
|
-
height: 40px;
|
|
342
388
|
align-items: center;
|
|
389
|
+
gap: 0.5rem;
|
|
390
|
+
padding: 0.75rem 1rem;
|
|
391
|
+
background-color: var(--color-ref-slate-100, #f1f5f9);
|
|
392
|
+
border-bottom: 1px solid var(--color-ref-slate-200, #e2e8f0);
|
|
393
|
+
font-size: 0.8125rem;
|
|
394
|
+
font-weight: 600;
|
|
395
|
+
color: var(--color-ref-slate-700, #334155);
|
|
343
396
|
}
|
|
344
397
|
|
|
345
|
-
.
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
font-weight: 500;
|
|
350
|
-
cursor: pointer;
|
|
351
|
-
transition: all 0.2s;
|
|
352
|
-
border: 1px solid transparent;
|
|
398
|
+
.config-form__extensions-header :global(svg) {
|
|
399
|
+
width: 1rem;
|
|
400
|
+
height: 1rem;
|
|
401
|
+
color: var(--color-ref-slate-500, #64748b);
|
|
353
402
|
}
|
|
354
403
|
|
|
355
|
-
.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
404
|
+
.config-form__extensions-content {
|
|
405
|
+
padding: 1rem;
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-direction: column;
|
|
408
|
+
gap: 1rem;
|
|
359
409
|
}
|
|
360
410
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
411
|
+
/* ============================================
|
|
412
|
+
DEBUG SECTION
|
|
413
|
+
============================================ */
|
|
365
414
|
|
|
366
|
-
.
|
|
367
|
-
background-color: #
|
|
368
|
-
|
|
415
|
+
.config-form__debug {
|
|
416
|
+
background-color: var(--color-ref-amber-50, #fffbeb);
|
|
417
|
+
border: 1px solid var(--color-ref-amber-200, #fde68a);
|
|
418
|
+
border-radius: 0.5rem;
|
|
419
|
+
overflow: hidden;
|
|
369
420
|
}
|
|
370
421
|
|
|
371
|
-
.
|
|
372
|
-
|
|
422
|
+
.config-form__debug-header {
|
|
423
|
+
display: flex;
|
|
424
|
+
align-items: center;
|
|
425
|
+
gap: 0.5rem;
|
|
426
|
+
padding: 0.75rem 1rem;
|
|
427
|
+
background-color: var(--color-ref-amber-100, #fef3c7);
|
|
428
|
+
border-bottom: 1px solid var(--color-ref-amber-200, #fde68a);
|
|
429
|
+
font-size: 0.8125rem;
|
|
430
|
+
font-weight: 600;
|
|
431
|
+
color: var(--color-ref-amber-800, #92400e);
|
|
373
432
|
}
|
|
374
433
|
|
|
375
|
-
.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
border-radius: 0.375rem;
|
|
379
|
-
padding: 1rem;
|
|
380
|
-
margin: 1rem 0;
|
|
434
|
+
.config-form__debug-header :global(svg) {
|
|
435
|
+
width: 1rem;
|
|
436
|
+
height: 1rem;
|
|
381
437
|
}
|
|
382
438
|
|
|
383
|
-
.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
border-radius: 0.25rem;
|
|
387
|
-
padding: 0.75rem;
|
|
439
|
+
.config-form__debug-content {
|
|
440
|
+
margin: 0;
|
|
441
|
+
padding: 1rem;
|
|
388
442
|
font-size: 0.75rem;
|
|
443
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
444
|
+
color: var(--color-ref-gray-700, #374151);
|
|
389
445
|
overflow-x: auto;
|
|
390
|
-
|
|
446
|
+
background-color: #ffffff;
|
|
447
|
+
line-height: 1.5;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* ============================================
|
|
451
|
+
EMPTY STATE
|
|
452
|
+
============================================ */
|
|
453
|
+
|
|
454
|
+
.config-form__empty {
|
|
455
|
+
display: flex;
|
|
456
|
+
flex-direction: column;
|
|
457
|
+
align-items: center;
|
|
458
|
+
justify-content: center;
|
|
459
|
+
padding: 3rem 1.5rem;
|
|
460
|
+
text-align: center;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.config-form__empty-icon {
|
|
464
|
+
width: 3rem;
|
|
465
|
+
height: 3rem;
|
|
466
|
+
margin-bottom: 1rem;
|
|
467
|
+
color: var(--color-ref-gray-300, #d1d5db);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.config-form__empty-icon :global(svg) {
|
|
471
|
+
width: 100%;
|
|
472
|
+
height: 100%;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.config-form__empty-text {
|
|
476
|
+
margin: 0;
|
|
477
|
+
font-size: 0.875rem;
|
|
478
|
+
color: var(--color-ref-gray-500, #6b7280);
|
|
479
|
+
font-style: italic;
|
|
480
|
+
line-height: 1.5;
|
|
391
481
|
}
|
|
392
482
|
</style>
|