@d34dman/flowdrop 0.0.22 → 0.0.24
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 +141 -520
- 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 +297 -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/FormRangeField.svelte +252 -0
- package/dist/components/form/FormRangeField.svelte.d.ts +21 -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 +42 -0
- package/dist/components/form/index.js +46 -0
- package/dist/components/form/types.d.ts +224 -0
- package/dist/components/form/types.js +29 -0
- package/dist/components/nodes/GatewayNode.svelte +76 -16
- 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 +176 -1
- 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
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
Supports both node-based config and direct schema/values
|
|
5
5
|
Uses reactive $state for proper Svelte 5 reactivity
|
|
6
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
|
+
|
|
7
12
|
Accessibility features:
|
|
8
13
|
- Proper label associations with for/id attributes
|
|
9
14
|
- ARIA describedby for field descriptions
|
|
@@ -13,7 +18,9 @@
|
|
|
13
18
|
|
|
14
19
|
<script lang="ts">
|
|
15
20
|
import Icon from '@iconify/svelte';
|
|
16
|
-
import type { ConfigSchema, WorkflowNode } from '../types/index.js';
|
|
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';
|
|
17
24
|
|
|
18
25
|
interface Props {
|
|
19
26
|
/** Optional workflow node (if provided, schema and values are derived from it) */
|
|
@@ -22,13 +29,15 @@
|
|
|
22
29
|
schema?: ConfigSchema;
|
|
23
30
|
/** Direct config values (used when node is not provided) */
|
|
24
31
|
values?: Record<string, unknown>;
|
|
25
|
-
/**
|
|
26
|
-
|
|
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;
|
|
27
36
|
/** Callback when form is cancelled */
|
|
28
37
|
onCancel: () => void;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
let { node, schema, values, onSave, onCancel }: Props = $props();
|
|
40
|
+
let { node, schema, values, showUIExtensions = true, onSave, onCancel }: Props = $props();
|
|
32
41
|
|
|
33
42
|
/**
|
|
34
43
|
* Get the configuration schema from node metadata or direct prop
|
|
@@ -48,6 +57,23 @@
|
|
|
48
57
|
*/
|
|
49
58
|
let configValues = $state<Record<string, unknown>>({});
|
|
50
59
|
|
|
60
|
+
/**
|
|
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
|
+
|
|
51
77
|
/**
|
|
52
78
|
* Initialize config values when node/schema changes
|
|
53
79
|
*/
|
|
@@ -64,6 +90,15 @@
|
|
|
64
90
|
}
|
|
65
91
|
});
|
|
66
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Initialize UI extension values when node changes
|
|
95
|
+
*/
|
|
96
|
+
$effect(() => {
|
|
97
|
+
uiExtensionValues = {
|
|
98
|
+
hideUnconnectedHandles: initialUIExtensions.hideUnconnectedHandles ?? false
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
|
|
67
102
|
/**
|
|
68
103
|
* Check if a field is required based on schema
|
|
69
104
|
*/
|
|
@@ -72,8 +107,16 @@
|
|
|
72
107
|
return configSchema.required.includes(key);
|
|
73
108
|
}
|
|
74
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
|
+
|
|
75
117
|
/**
|
|
76
118
|
* Handle form submission
|
|
119
|
+
* Collects both config values and UI extension values
|
|
77
120
|
*/
|
|
78
121
|
function handleSave(): void {
|
|
79
122
|
// Collect all form values including hidden fields
|
|
@@ -84,10 +127,11 @@
|
|
|
84
127
|
const inputs = form.querySelectorAll('input, select, textarea');
|
|
85
128
|
inputs.forEach((input: Element) => {
|
|
86
129
|
const inputEl = input as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
87
|
-
|
|
130
|
+
// Skip UI extension fields (prefixed with ext-)
|
|
131
|
+
if (inputEl.id && !inputEl.id.startsWith('ext-')) {
|
|
88
132
|
if (inputEl instanceof HTMLInputElement && inputEl.type === 'checkbox') {
|
|
89
133
|
updatedConfig[inputEl.id] = inputEl.checked;
|
|
90
|
-
} else if (inputEl instanceof HTMLInputElement && inputEl.type === 'number') {
|
|
134
|
+
} else if (inputEl instanceof HTMLInputElement && (inputEl.type === 'number' || inputEl.type === 'range')) {
|
|
91
135
|
updatedConfig[inputEl.id] = inputEl.value ? Number(inputEl.value) : inputEl.value;
|
|
92
136
|
} else if (inputEl instanceof HTMLInputElement && inputEl.type === 'hidden') {
|
|
93
137
|
// Parse hidden field values that might be JSON
|
|
@@ -116,7 +160,19 @@
|
|
|
116
160
|
);
|
|
117
161
|
}
|
|
118
162
|
|
|
119
|
-
|
|
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;
|
|
120
176
|
}
|
|
121
177
|
</script>
|
|
122
178
|
|
|
@@ -131,179 +187,17 @@
|
|
|
131
187
|
{#if configSchema.properties}
|
|
132
188
|
<div class="config-form__fields">
|
|
133
189
|
{#each Object.entries(configSchema.properties) as [key, field], index (key)}
|
|
134
|
-
{@const
|
|
190
|
+
{@const fieldSchema = toFieldSchema(field as Record<string, unknown>)}
|
|
135
191
|
{@const required = isFieldRequired(key)}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
</span>
|
|
146
|
-
{#if required}
|
|
147
|
-
<span class="config-form__required" aria-label="required">*</span>
|
|
148
|
-
{/if}
|
|
149
|
-
</label>
|
|
150
|
-
|
|
151
|
-
<!-- Field Input Container -->
|
|
152
|
-
<div class="config-form__input-wrapper">
|
|
153
|
-
{#if fieldConfig.enum && fieldConfig.multiple}
|
|
154
|
-
<!-- Checkboxes for enum with multiple selection -->
|
|
155
|
-
{@const enumOptions = fieldConfig.enum as string[]}
|
|
156
|
-
<div
|
|
157
|
-
class="config-form__checkbox-group"
|
|
158
|
-
role="group"
|
|
159
|
-
aria-labelledby="{key}-label"
|
|
160
|
-
aria-describedby={descriptionId}
|
|
161
|
-
>
|
|
162
|
-
{#each enumOptions as option (String(option))}
|
|
163
|
-
{@const currentValue = configValues[key]}
|
|
164
|
-
{@const valueArray = Array.isArray(currentValue) ? currentValue : []}
|
|
165
|
-
<label class="config-form__checkbox-item">
|
|
166
|
-
<input
|
|
167
|
-
type="checkbox"
|
|
168
|
-
class="config-form__checkbox-input"
|
|
169
|
-
value={String(option)}
|
|
170
|
-
checked={valueArray.includes(String(option))}
|
|
171
|
-
onchange={(e) => {
|
|
172
|
-
const checked = e.currentTarget.checked;
|
|
173
|
-
const existingValue = configValues[key];
|
|
174
|
-
const currentValues: unknown[] = Array.isArray(existingValue)
|
|
175
|
-
? [...existingValue]
|
|
176
|
-
: [];
|
|
177
|
-
if (checked) {
|
|
178
|
-
if (!currentValues.includes(String(option))) {
|
|
179
|
-
configValues[key] = [...currentValues, String(option)];
|
|
180
|
-
}
|
|
181
|
-
} else {
|
|
182
|
-
configValues[key] = currentValues.filter((v) => v !== String(option));
|
|
183
|
-
}
|
|
184
|
-
}}
|
|
185
|
-
/>
|
|
186
|
-
<span class="config-form__checkbox-custom" aria-hidden="true">
|
|
187
|
-
<Icon icon="heroicons:check" />
|
|
188
|
-
</span>
|
|
189
|
-
<span class="config-form__checkbox-label">
|
|
190
|
-
{String(option)}
|
|
191
|
-
</span>
|
|
192
|
-
</label>
|
|
193
|
-
{/each}
|
|
194
|
-
</div>
|
|
195
|
-
{:else if fieldConfig.enum}
|
|
196
|
-
<!-- Select for enum with single selection -->
|
|
197
|
-
{@const enumOptions = fieldConfig.enum as string[]}
|
|
198
|
-
<div class="config-form__select-wrapper">
|
|
199
|
-
<select
|
|
200
|
-
id={key}
|
|
201
|
-
class="config-form__select"
|
|
202
|
-
bind:value={configValues[key]}
|
|
203
|
-
aria-describedby={descriptionId}
|
|
204
|
-
aria-required={required}
|
|
205
|
-
>
|
|
206
|
-
{#each enumOptions as option (String(option))}
|
|
207
|
-
<option value={String(option)}>{String(option)}</option>
|
|
208
|
-
{/each}
|
|
209
|
-
</select>
|
|
210
|
-
<span class="config-form__select-icon" aria-hidden="true">
|
|
211
|
-
<Icon icon="heroicons:chevron-down" />
|
|
212
|
-
</span>
|
|
213
|
-
</div>
|
|
214
|
-
{:else if fieldConfig.type === 'string' && fieldConfig.format === 'multiline'}
|
|
215
|
-
<!-- Textarea for multiline strings -->
|
|
216
|
-
<textarea
|
|
217
|
-
id={key}
|
|
218
|
-
class="config-form__textarea"
|
|
219
|
-
bind:value={configValues[key]}
|
|
220
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
221
|
-
rows="4"
|
|
222
|
-
aria-describedby={descriptionId}
|
|
223
|
-
aria-required={required}
|
|
224
|
-
></textarea>
|
|
225
|
-
{:else if fieldConfig.type === 'string'}
|
|
226
|
-
<input
|
|
227
|
-
id={key}
|
|
228
|
-
type="text"
|
|
229
|
-
class="config-form__input"
|
|
230
|
-
bind:value={configValues[key]}
|
|
231
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
232
|
-
aria-describedby={descriptionId}
|
|
233
|
-
aria-required={required}
|
|
234
|
-
/>
|
|
235
|
-
{:else if fieldConfig.type === 'number'}
|
|
236
|
-
<input
|
|
237
|
-
id={key}
|
|
238
|
-
type="number"
|
|
239
|
-
class="config-form__input config-form__input--number"
|
|
240
|
-
bind:value={configValues[key]}
|
|
241
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
242
|
-
aria-describedby={descriptionId}
|
|
243
|
-
aria-required={required}
|
|
244
|
-
/>
|
|
245
|
-
{:else if fieldConfig.type === 'boolean'}
|
|
246
|
-
<!-- Toggle Switch for boolean -->
|
|
247
|
-
<label class="config-form__toggle">
|
|
248
|
-
<input
|
|
249
|
-
id={key}
|
|
250
|
-
type="checkbox"
|
|
251
|
-
class="config-form__toggle-input"
|
|
252
|
-
checked={Boolean(configValues[key] || fieldConfig.default || false)}
|
|
253
|
-
onchange={(e) => {
|
|
254
|
-
configValues[key] = e.currentTarget.checked;
|
|
255
|
-
}}
|
|
256
|
-
aria-describedby={descriptionId}
|
|
257
|
-
/>
|
|
258
|
-
<span class="config-form__toggle-track">
|
|
259
|
-
<span class="config-form__toggle-thumb"></span>
|
|
260
|
-
</span>
|
|
261
|
-
<span class="config-form__toggle-label">
|
|
262
|
-
{configValues[key] ? 'Enabled' : 'Disabled'}
|
|
263
|
-
</span>
|
|
264
|
-
</label>
|
|
265
|
-
{:else if fieldConfig.type === 'select' || fieldConfig.options}
|
|
266
|
-
{@const selectOptions = (fieldConfig.options ?? []) as Array<{
|
|
267
|
-
value: unknown;
|
|
268
|
-
label: unknown;
|
|
269
|
-
}>}
|
|
270
|
-
<div class="config-form__select-wrapper">
|
|
271
|
-
<select
|
|
272
|
-
id={key}
|
|
273
|
-
class="config-form__select"
|
|
274
|
-
bind:value={configValues[key]}
|
|
275
|
-
aria-describedby={descriptionId}
|
|
276
|
-
aria-required={required}
|
|
277
|
-
>
|
|
278
|
-
{#each selectOptions as option (String(option.value))}
|
|
279
|
-
<option value={String(option.value)}>{String(option.label)}</option>
|
|
280
|
-
{/each}
|
|
281
|
-
</select>
|
|
282
|
-
<span class="config-form__select-icon" aria-hidden="true">
|
|
283
|
-
<Icon icon="heroicons:chevron-down" />
|
|
284
|
-
</span>
|
|
285
|
-
</div>
|
|
286
|
-
{:else}
|
|
287
|
-
<!-- Fallback for unknown field types -->
|
|
288
|
-
<input
|
|
289
|
-
id={key}
|
|
290
|
-
type="text"
|
|
291
|
-
class="config-form__input"
|
|
292
|
-
bind:value={configValues[key]}
|
|
293
|
-
placeholder={String(fieldConfig.placeholder || '')}
|
|
294
|
-
aria-describedby={descriptionId}
|
|
295
|
-
/>
|
|
296
|
-
{/if}
|
|
297
|
-
</div>
|
|
298
|
-
|
|
299
|
-
<!-- Field Description -->
|
|
300
|
-
{#if hasDescription && fieldConfig.title}
|
|
301
|
-
<p id={descriptionId} class="config-form__description">
|
|
302
|
-
{String(fieldConfig.description)}
|
|
303
|
-
</p>
|
|
304
|
-
{/if}
|
|
305
|
-
</div>
|
|
306
|
-
{/if}
|
|
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
|
+
/>
|
|
307
201
|
{/each}
|
|
308
202
|
</div>
|
|
309
203
|
{:else}
|
|
@@ -317,6 +211,35 @@
|
|
|
317
211
|
</div>
|
|
318
212
|
{/if}
|
|
319
213
|
|
|
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
|
+
|
|
320
243
|
<!-- Footer Actions -->
|
|
321
244
|
<div class="config-form__footer">
|
|
322
245
|
<button
|
|
@@ -344,7 +267,8 @@
|
|
|
344
267
|
|
|
345
268
|
<style>
|
|
346
269
|
/* ============================================
|
|
347
|
-
CONFIG FORM -
|
|
270
|
+
CONFIG FORM - Container Styles
|
|
271
|
+
Individual field styles are in form/ components
|
|
348
272
|
============================================ */
|
|
349
273
|
|
|
350
274
|
.config-form {
|
|
@@ -359,346 +283,6 @@
|
|
|
359
283
|
gap: 1.25rem;
|
|
360
284
|
}
|
|
361
285
|
|
|
362
|
-
/* ============================================
|
|
363
|
-
FIELD CONTAINER
|
|
364
|
-
============================================ */
|
|
365
|
-
|
|
366
|
-
.config-form__field {
|
|
367
|
-
display: flex;
|
|
368
|
-
flex-direction: column;
|
|
369
|
-
gap: 0.5rem;
|
|
370
|
-
animation: fieldFadeIn 0.3s ease-out forwards;
|
|
371
|
-
opacity: 0;
|
|
372
|
-
transform: translateY(4px);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
@keyframes fieldFadeIn {
|
|
376
|
-
to {
|
|
377
|
-
opacity: 1;
|
|
378
|
-
transform: translateY(0);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/* ============================================
|
|
383
|
-
LABELS
|
|
384
|
-
============================================ */
|
|
385
|
-
|
|
386
|
-
.config-form__label {
|
|
387
|
-
display: flex;
|
|
388
|
-
align-items: center;
|
|
389
|
-
gap: 0.25rem;
|
|
390
|
-
font-size: 0.8125rem;
|
|
391
|
-
font-weight: 600;
|
|
392
|
-
color: var(--color-ref-gray-700, #374151);
|
|
393
|
-
letter-spacing: -0.01em;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.config-form__label-text {
|
|
397
|
-
line-height: 1.4;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
.config-form__required {
|
|
401
|
-
color: var(--color-ref-red-500, #ef4444);
|
|
402
|
-
font-weight: 500;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/* ============================================
|
|
406
|
-
INPUT WRAPPER
|
|
407
|
-
============================================ */
|
|
408
|
-
|
|
409
|
-
.config-form__input-wrapper {
|
|
410
|
-
position: relative;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/* ============================================
|
|
414
|
-
TEXT INPUTS
|
|
415
|
-
============================================ */
|
|
416
|
-
|
|
417
|
-
.config-form__input {
|
|
418
|
-
width: 100%;
|
|
419
|
-
padding: 0.625rem 0.875rem;
|
|
420
|
-
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
421
|
-
border-radius: 0.5rem;
|
|
422
|
-
font-size: 0.875rem;
|
|
423
|
-
font-family: inherit;
|
|
424
|
-
color: var(--color-ref-gray-900, #111827);
|
|
425
|
-
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
426
|
-
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
427
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
.config-form__input::placeholder {
|
|
431
|
-
color: var(--color-ref-gray-400, #9ca3af);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
.config-form__input:hover {
|
|
435
|
-
border-color: var(--color-ref-gray-300, #d1d5db);
|
|
436
|
-
background-color: #ffffff;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
.config-form__input:focus {
|
|
440
|
-
outline: none;
|
|
441
|
-
border-color: var(--color-ref-blue-500, #3b82f6);
|
|
442
|
-
background-color: #ffffff;
|
|
443
|
-
box-shadow:
|
|
444
|
-
0 0 0 3px rgba(59, 130, 246, 0.12),
|
|
445
|
-
0 1px 2px rgba(0, 0, 0, 0.04);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
.config-form__input--number {
|
|
449
|
-
font-variant-numeric: tabular-nums;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/* ============================================
|
|
453
|
-
TEXTAREA
|
|
454
|
-
============================================ */
|
|
455
|
-
|
|
456
|
-
.config-form__textarea {
|
|
457
|
-
width: 100%;
|
|
458
|
-
padding: 0.625rem 0.875rem;
|
|
459
|
-
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
460
|
-
border-radius: 0.5rem;
|
|
461
|
-
font-size: 0.875rem;
|
|
462
|
-
font-family: inherit;
|
|
463
|
-
color: var(--color-ref-gray-900, #111827);
|
|
464
|
-
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
465
|
-
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
466
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
467
|
-
resize: vertical;
|
|
468
|
-
min-height: 5rem;
|
|
469
|
-
line-height: 1.5;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
.config-form__textarea::placeholder {
|
|
473
|
-
color: var(--color-ref-gray-400, #9ca3af);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
.config-form__textarea:hover {
|
|
477
|
-
border-color: var(--color-ref-gray-300, #d1d5db);
|
|
478
|
-
background-color: #ffffff;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
.config-form__textarea:focus {
|
|
482
|
-
outline: none;
|
|
483
|
-
border-color: var(--color-ref-blue-500, #3b82f6);
|
|
484
|
-
background-color: #ffffff;
|
|
485
|
-
box-shadow:
|
|
486
|
-
0 0 0 3px rgba(59, 130, 246, 0.12),
|
|
487
|
-
0 1px 2px rgba(0, 0, 0, 0.04);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
/* ============================================
|
|
491
|
-
SELECT DROPDOWN
|
|
492
|
-
============================================ */
|
|
493
|
-
|
|
494
|
-
.config-form__select-wrapper {
|
|
495
|
-
position: relative;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
.config-form__select {
|
|
499
|
-
width: 100%;
|
|
500
|
-
padding: 0.625rem 2.5rem 0.625rem 0.875rem;
|
|
501
|
-
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
502
|
-
border-radius: 0.5rem;
|
|
503
|
-
font-size: 0.875rem;
|
|
504
|
-
font-family: inherit;
|
|
505
|
-
color: var(--color-ref-gray-900, #111827);
|
|
506
|
-
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
507
|
-
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
508
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
509
|
-
cursor: pointer;
|
|
510
|
-
appearance: none;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
.config-form__select:hover {
|
|
514
|
-
border-color: var(--color-ref-gray-300, #d1d5db);
|
|
515
|
-
background-color: #ffffff;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
.config-form__select:focus {
|
|
519
|
-
outline: none;
|
|
520
|
-
border-color: var(--color-ref-blue-500, #3b82f6);
|
|
521
|
-
background-color: #ffffff;
|
|
522
|
-
box-shadow:
|
|
523
|
-
0 0 0 3px rgba(59, 130, 246, 0.12),
|
|
524
|
-
0 1px 2px rgba(0, 0, 0, 0.04);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
.config-form__select-icon {
|
|
528
|
-
position: absolute;
|
|
529
|
-
right: 0.75rem;
|
|
530
|
-
top: 50%;
|
|
531
|
-
transform: translateY(-50%);
|
|
532
|
-
pointer-events: none;
|
|
533
|
-
color: var(--color-ref-gray-400, #9ca3af);
|
|
534
|
-
display: flex;
|
|
535
|
-
align-items: center;
|
|
536
|
-
transition: color 0.2s;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
.config-form__select-icon :global(svg) {
|
|
540
|
-
width: 1rem;
|
|
541
|
-
height: 1rem;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
.config-form__select:focus + .config-form__select-icon {
|
|
545
|
-
color: var(--color-ref-blue-500, #3b82f6);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/* ============================================
|
|
549
|
-
CHECKBOX GROUP
|
|
550
|
-
============================================ */
|
|
551
|
-
|
|
552
|
-
.config-form__checkbox-group {
|
|
553
|
-
display: flex;
|
|
554
|
-
flex-direction: column;
|
|
555
|
-
gap: 0.625rem;
|
|
556
|
-
padding: 0.75rem;
|
|
557
|
-
background-color: var(--color-ref-gray-50, #f9fafb);
|
|
558
|
-
border: 1px solid var(--color-ref-gray-200, #e5e7eb);
|
|
559
|
-
border-radius: 0.5rem;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
.config-form__checkbox-item {
|
|
563
|
-
display: flex;
|
|
564
|
-
align-items: center;
|
|
565
|
-
gap: 0.625rem;
|
|
566
|
-
cursor: pointer;
|
|
567
|
-
padding: 0.375rem;
|
|
568
|
-
margin: -0.375rem;
|
|
569
|
-
border-radius: 0.375rem;
|
|
570
|
-
transition: background-color 0.15s;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
.config-form__checkbox-item:hover {
|
|
574
|
-
background-color: var(--color-ref-gray-100, #f3f4f6);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
.config-form__checkbox-input {
|
|
578
|
-
position: absolute;
|
|
579
|
-
opacity: 0;
|
|
580
|
-
width: 0;
|
|
581
|
-
height: 0;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
.config-form__checkbox-custom {
|
|
585
|
-
width: 1.125rem;
|
|
586
|
-
height: 1.125rem;
|
|
587
|
-
border: 1.5px solid var(--color-ref-gray-300, #d1d5db);
|
|
588
|
-
border-radius: 0.25rem;
|
|
589
|
-
background-color: #ffffff;
|
|
590
|
-
display: flex;
|
|
591
|
-
align-items: center;
|
|
592
|
-
justify-content: center;
|
|
593
|
-
transition: all 0.15s;
|
|
594
|
-
flex-shrink: 0;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
.config-form__checkbox-custom :global(svg) {
|
|
598
|
-
width: 0.75rem;
|
|
599
|
-
height: 0.75rem;
|
|
600
|
-
color: #ffffff;
|
|
601
|
-
opacity: 0;
|
|
602
|
-
transform: scale(0.5);
|
|
603
|
-
transition: all 0.15s;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
.config-form__checkbox-input:checked + .config-form__checkbox-custom {
|
|
607
|
-
background-color: var(--color-ref-blue-500, #3b82f6);
|
|
608
|
-
border-color: var(--color-ref-blue-500, #3b82f6);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
.config-form__checkbox-input:checked + .config-form__checkbox-custom :global(svg) {
|
|
612
|
-
opacity: 1;
|
|
613
|
-
transform: scale(1);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
.config-form__checkbox-input:focus-visible + .config-form__checkbox-custom {
|
|
617
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
.config-form__checkbox-label {
|
|
621
|
-
font-size: 0.875rem;
|
|
622
|
-
color: var(--color-ref-gray-700, #374151);
|
|
623
|
-
line-height: 1.4;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/* ============================================
|
|
627
|
-
TOGGLE SWITCH (Boolean)
|
|
628
|
-
============================================ */
|
|
629
|
-
|
|
630
|
-
.config-form__toggle {
|
|
631
|
-
display: flex;
|
|
632
|
-
align-items: center;
|
|
633
|
-
gap: 0.75rem;
|
|
634
|
-
cursor: pointer;
|
|
635
|
-
padding: 0.5rem 0;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
.config-form__toggle-input {
|
|
639
|
-
position: absolute;
|
|
640
|
-
opacity: 0;
|
|
641
|
-
width: 0;
|
|
642
|
-
height: 0;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
.config-form__toggle-track {
|
|
646
|
-
position: relative;
|
|
647
|
-
width: 2.75rem;
|
|
648
|
-
height: 1.5rem;
|
|
649
|
-
background-color: var(--color-ref-gray-300, #d1d5db);
|
|
650
|
-
border-radius: 0.75rem;
|
|
651
|
-
transition: background-color 0.2s;
|
|
652
|
-
flex-shrink: 0;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
.config-form__toggle-thumb {
|
|
656
|
-
position: absolute;
|
|
657
|
-
top: 0.125rem;
|
|
658
|
-
left: 0.125rem;
|
|
659
|
-
width: 1.25rem;
|
|
660
|
-
height: 1.25rem;
|
|
661
|
-
background-color: #ffffff;
|
|
662
|
-
border-radius: 50%;
|
|
663
|
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
664
|
-
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
.config-form__toggle-input:checked + .config-form__toggle-track {
|
|
668
|
-
background-color: var(--color-ref-blue-500, #3b82f6);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
.config-form__toggle-input:checked + .config-form__toggle-track .config-form__toggle-thumb {
|
|
672
|
-
transform: translateX(1.25rem);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
.config-form__toggle-input:focus-visible + .config-form__toggle-track {
|
|
676
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
.config-form__toggle-label {
|
|
680
|
-
font-size: 0.875rem;
|
|
681
|
-
color: var(--color-ref-gray-600, #4b5563);
|
|
682
|
-
font-weight: 500;
|
|
683
|
-
min-width: 4.5rem;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
.config-form__toggle-input:checked ~ .config-form__toggle-label {
|
|
687
|
-
color: var(--color-ref-blue-600, #2563eb);
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
/* ============================================
|
|
691
|
-
FIELD DESCRIPTION
|
|
692
|
-
============================================ */
|
|
693
|
-
|
|
694
|
-
.config-form__description {
|
|
695
|
-
margin: 0;
|
|
696
|
-
font-size: 0.75rem;
|
|
697
|
-
color: var(--color-ref-gray-500, #6b7280);
|
|
698
|
-
line-height: 1.5;
|
|
699
|
-
padding-left: 0.125rem;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
286
|
/* ============================================
|
|
703
287
|
FOOTER ACTIONS
|
|
704
288
|
============================================ */
|
|
@@ -787,6 +371,43 @@
|
|
|
787
371
|
0 4px 12px rgba(59, 130, 246, 0.35);
|
|
788
372
|
}
|
|
789
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;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.config-form__extensions-header {
|
|
387
|
+
display: flex;
|
|
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);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.config-form__extensions-header :global(svg) {
|
|
399
|
+
width: 1rem;
|
|
400
|
+
height: 1rem;
|
|
401
|
+
color: var(--color-ref-slate-500, #64748b);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.config-form__extensions-content {
|
|
405
|
+
padding: 1rem;
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-direction: column;
|
|
408
|
+
gap: 1rem;
|
|
409
|
+
}
|
|
410
|
+
|
|
790
411
|
/* ============================================
|
|
791
412
|
DEBUG SECTION
|
|
792
413
|
============================================ */
|