@d34dman/flowdrop 0.0.10 → 0.0.11

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.
@@ -1,555 +1,406 @@
1
1
  <!--
2
2
  ConfigForm Component
3
- Generates configuration forms from JSON Schema and manages user values
4
- Separates schema (form structure) from values (user input)
3
+ Handles dynamic form rendering for node configuration
4
+ Uses reactive $state for proper Svelte 5 reactivity
5
5
  -->
6
6
 
7
7
  <script lang="ts">
8
- import type { ConfigSchema, ConfigProperty, ConfigValues } from '../types/index.js';
9
- import { createEventDispatcher } from 'svelte';
8
+ import type { ConfigSchema, WorkflowNode } from "../types/index.js"
10
9
 
11
10
  interface Props {
12
- schema: ConfigSchema;
13
- values: ConfigValues;
14
- disabled?: boolean;
11
+ node: WorkflowNode
12
+ onSave: (config: Record<string, unknown>) => void
13
+ onCancel: () => void
15
14
  }
16
15
 
17
- let props: Props = $props();
18
- let dispatch = createEventDispatcher<{
19
- change: { values: ConfigValues };
20
- validate: { isValid: boolean; errors: string[] };
21
- }>();
22
-
23
- // Local copy of values for editing
24
- let localValues = $state<ConfigValues>({ ...props.values });
25
-
26
- // Validation errors
27
- let validationErrors = $state<Record<string, string>>({});
16
+ let { node, onSave, onCancel }: Props = $props()
28
17
 
29
18
  /**
30
- * Validate a single field against its schema
19
+ * Get the configuration schema from node metadata
31
20
  */
32
- function validateField(
33
- fieldName: string,
34
- value: unknown,
35
- property: ConfigProperty
36
- ): string | null {
37
- // Check if schema exists
38
- if (!props.schema) {
39
- return null;
40
- }
41
-
42
- // Required field validation
43
- if (
44
- props.schema.required?.includes(fieldName) &&
45
- (value === null || value === undefined || value === '')
46
- ) {
47
- return `${property.title || fieldName} is required`;
48
- }
49
-
50
- // Type validation
51
- if (value !== null && value !== undefined) {
52
- switch (property.type) {
53
- case 'string':
54
- if (typeof value !== 'string') {
55
- return `${property.title || fieldName} must be a string`;
56
- }
57
- if (property.minLength && value.length < property.minLength) {
58
- return `${property.title || fieldName} must be at least ${property.minLength} characters`;
59
- }
60
- if (property.maxLength && value.length > property.maxLength) {
61
- return `${property.title || fieldName} must be at most ${property.maxLength} characters`;
62
- }
63
- if (property.pattern && !new RegExp(property.pattern).test(value)) {
64
- return `${property.title || fieldName} format is invalid`;
65
- }
66
- break;
67
-
68
- case 'number':
69
- if (typeof value !== 'number') {
70
- return `${property.title || fieldName} must be a number`;
71
- }
72
- if (property.minimum !== undefined && value < property.minimum) {
73
- return `${property.title || fieldName} must be at least ${property.minimum}`;
74
- }
75
- if (property.maximum !== undefined && value > property.maximum) {
76
- return `${property.title || fieldName} must be at most ${property.maximum}`;
77
- }
78
- break;
79
-
80
- case 'boolean':
81
- if (typeof value !== 'boolean') {
82
- return `${property.title || fieldName} must be a boolean`;
83
- }
84
- break;
85
-
86
- case 'array':
87
- if (!Array.isArray(value)) {
88
- return `${property.title || fieldName} must be an array`;
89
- }
90
- break;
91
- }
92
- }
93
-
94
- return null;
95
- }
21
+ const configSchema = $derived(
22
+ node.data.metadata?.configSchema as ConfigSchema | undefined
23
+ )
96
24
 
97
25
  /**
98
- * Validate all fields
26
+ * Get the current node configuration
99
27
  */
100
- function validateForm(): { isValid: boolean; errors: string[] } {
101
- const errors: string[] = [];
102
- const newValidationErrors: Record<string, string> = {};
103
-
104
- // Check if schema and properties exist
105
- if (!props.schema || !props.schema.properties || typeof props.schema.properties !== 'object') {
106
- console.warn('ConfigForm: Invalid schema or properties:', {
107
- schema: props.schema,
108
- properties: props.schema?.properties
109
- });
110
- return { isValid: true, errors: [] };
111
- }
112
-
113
- Object.entries(props.schema.properties).forEach(([fieldName, property]) => {
114
- const value = localValues[fieldName];
115
- const error = validateField(fieldName, value, property);
116
-
117
- if (error) {
118
- errors.push(error);
119
- newValidationErrors[fieldName] = error;
120
- }
121
- });
122
-
123
- validationErrors = newValidationErrors;
124
-
125
- const isValid = errors.length === 0;
126
- dispatch('validate', { isValid, errors });
127
-
128
- return { isValid, errors };
129
- }
28
+ const nodeConfig = $derived(node.data.config || {})
130
29
 
131
30
  /**
132
- * Handle field value change
133
- * Preserves hidden field values from the original configuration
31
+ * Create reactive configuration values using $state
32
+ * This fixes the Svelte 5 reactivity warnings
134
33
  */
135
- function handleFieldChange(fieldName: string, value: unknown): void {
136
- localValues[fieldName] = value;
137
-
138
- // Validate the changed field
139
- if (props.schema && props.schema.properties && props.schema.properties[fieldName]) {
140
- const property = props.schema.properties[fieldName];
141
- const error = validateField(fieldName, value, property);
142
-
143
- if (error) {
144
- validationErrors[fieldName] = error;
145
- } else {
146
- delete validationErrors[fieldName];
147
- }
148
- }
149
-
150
- // Merge hidden field values from original props to ensure they're preserved
151
- const hiddenFieldValues: ConfigValues = {};
152
- if (props.schema?.properties) {
153
- Object.entries(props.schema.properties).forEach(([key, property]) => {
154
- if (property.format === 'hidden' && key in props.values) {
155
- hiddenFieldValues[key] = props.values[key];
156
- }
157
- });
158
- }
159
-
160
- // Emit change event with hidden fields preserved
161
- dispatch('change', { values: { ...localValues, ...hiddenFieldValues } });
162
-
163
- // Validate entire form
164
- validateForm();
165
- }
34
+ let configValues = $state<Record<string, unknown>>({})
166
35
 
167
36
  /**
168
- * Get default value for a field
37
+ * Initialize config values when node or schema changes
169
38
  */
170
- function getDefaultValue(property: ConfigProperty): unknown {
171
- if (property.default !== undefined) {
172
- return property.default;
39
+ $effect(() => {
40
+ if (configSchema?.properties) {
41
+ const mergedConfig: Record<string, unknown> = {}
42
+ Object.entries(configSchema.properties).forEach(([key, field]) => {
43
+ const fieldConfig = field as any
44
+ // Use existing value if available, otherwise use default
45
+ mergedConfig[key] =
46
+ nodeConfig[key] !== undefined ? nodeConfig[key] : fieldConfig.default
47
+ })
48
+ configValues = mergedConfig
173
49
  }
50
+ })
174
51
 
175
- switch (property.type) {
176
- case 'string':
177
- // If enum with multiple selection, default to empty array
178
- if (property.enum && property.multiple) {
179
- return [];
52
+ /**
53
+ * Handle form submission
54
+ */
55
+ function handleSave(): void {
56
+ // Collect all form values including hidden fields
57
+ const form = document.querySelector(".flowdrop-config-sidebar__form")
58
+ const updatedConfig: Record<string, unknown> = { ...configValues }
59
+
60
+ if (form) {
61
+ const inputs = form.querySelectorAll("input, select, textarea")
62
+ inputs.forEach((input: any) => {
63
+ if (input.id) {
64
+ if (input.type === "checkbox") {
65
+ updatedConfig[input.id] = input.checked
66
+ } else if (input.type === "number") {
67
+ updatedConfig[input.id] = input.value ? Number(input.value) : input.value
68
+ } else if (input.type === "hidden") {
69
+ // Parse hidden field values that might be JSON
70
+ try {
71
+ const parsed = JSON.parse(input.value)
72
+ updatedConfig[input.id] = parsed
73
+ } catch {
74
+ // If not JSON, use raw value
75
+ updatedConfig[input.id] = input.value
76
+ }
77
+ } else {
78
+ updatedConfig[input.id] = input.value
79
+ }
180
80
  }
181
- return '';
182
- case 'number':
183
- return 0;
184
- case 'boolean':
185
- return false;
186
- case 'array':
187
- return [];
188
- case 'object':
189
- return {};
190
- default:
191
- return null;
81
+ })
192
82
  }
193
- }
194
83
 
195
- // Sync local values with props and initialize with defaults
196
- $effect(() => {
197
- // Update local values when props change
198
- localValues = { ...props.values };
199
-
200
- if (props.schema) {
201
- Object.entries(props.schema.properties).forEach(([fieldName, property]) => {
202
- if (localValues[fieldName] === undefined) {
203
- localValues[fieldName] = getDefaultValue(property);
84
+ // Preserve hidden field values from original config if not collected from form
85
+ if (node.data.config && configSchema?.properties) {
86
+ Object.entries(configSchema.properties).forEach(([key, property]: [string, any]) => {
87
+ if (
88
+ property.format === "hidden" &&
89
+ !(key in updatedConfig) &&
90
+ key in node.data.config
91
+ ) {
92
+ updatedConfig[key] = node.data.config[key]
204
93
  }
205
- });
206
-
207
- // Validate on initialization
208
- validateForm();
94
+ })
209
95
  }
210
- });
96
+
97
+ onSave(updatedConfig)
98
+ }
211
99
  </script>
212
100
 
213
- <div class="flowdrop-config-form">
214
- {#if props.schema && props.schema.properties && typeof props.schema.properties === 'object'}
215
- <form class="flowdrop-form" onsubmit={(e) => e.preventDefault()}>
216
- {#each Object.entries(props.schema.properties) as [fieldName, property] (fieldName)}
217
- {#if property.format === 'hidden'}
218
- <!-- Hidden field to preserve value -->
219
- <input
220
- type="hidden"
221
- id={fieldName}
222
- value={typeof (localValues[fieldName] ?? property.default) === 'object'
223
- ? JSON.stringify(localValues[fieldName] ?? property.default ?? {})
224
- : (localValues[fieldName] ?? property.default ?? '')}
225
- />
226
- {:else}
227
- <div class="flowdrop-form-field">
228
- <label class="flowdrop-form-label" for={fieldName}>
229
- {property.title || fieldName}
230
- {#if props.schema.required?.includes(fieldName)}
231
- <span class="flowdrop-form-required">*</span>
232
- {/if}
101
+ {#if configSchema}
102
+ <div class="flowdrop-config-sidebar__form">
103
+ {#if configSchema.properties}
104
+ {#each Object.entries(configSchema.properties) as [key, field] (key)}
105
+ {@const fieldConfig = field as any}
106
+ {#if fieldConfig.format !== "hidden"}
107
+ <div class="flowdrop-config-sidebar__field">
108
+ <label class="flowdrop-config-sidebar__field-label" for={key}>
109
+ {fieldConfig.title || fieldConfig.description || key}
233
110
  </label>
234
-
235
- {#if property.type === 'string'}
236
- {#if property.enum && property.multiple}
237
- <!-- Checkboxes for enum with multiple selection -->
238
- <div class="flowdrop-form-checkbox-group">
239
- {#each property.enum as option (option)}
240
- <label class="flowdrop-form-checkbox">
241
- <input
242
- type="checkbox"
243
- class="flowdrop-form-checkbox__input"
244
- value={option}
245
- checked={Array.isArray(localValues[fieldName]) &&
246
- localValues[fieldName].includes(option)}
247
- disabled={props.disabled || false}
248
- onchange={(e) => {
249
- const checked = (e.target as HTMLInputElement).checked;
250
- const currentValues = Array.isArray(localValues[fieldName])
251
- ? [...localValues[fieldName]]
252
- : [];
253
- if (checked) {
254
- if (!currentValues.includes(option)) {
255
- handleFieldChange(fieldName, [...currentValues, option]);
256
- }
257
- } else {
258
- handleFieldChange(
259
- fieldName,
260
- currentValues.filter((v) => v !== option)
261
- );
111
+ {#if fieldConfig.enum && fieldConfig.multiple}
112
+ <!-- Checkboxes for enum with multiple selection -->
113
+ <div class="flowdrop-config-sidebar__checkbox-group">
114
+ {#each fieldConfig.enum as option (String(option))}
115
+ <label class="flowdrop-config-sidebar__checkbox-item">
116
+ <input
117
+ type="checkbox"
118
+ class="flowdrop-config-sidebar__checkbox"
119
+ value={String(option)}
120
+ checked={Array.isArray(configValues[key]) &&
121
+ configValues[key].includes(String(option))}
122
+ onchange={(e) => {
123
+ const checked = e.currentTarget.checked
124
+ const currentValues = Array.isArray(configValues[key])
125
+ ? [...(configValues[key] as unknown[])]
126
+ : []
127
+ if (checked) {
128
+ if (!currentValues.includes(String(option))) {
129
+ configValues[key] = [...currentValues, String(option)]
262
130
  }
263
- }}
264
- />
265
- <span class="flowdrop-form-checkbox__label">{option}</span>
266
- </label>
267
- {/each}
268
- </div>
269
- {:else if property.enum}
270
- <!-- Select field for enum with single selection -->
271
- <select
272
- id={fieldName}
273
- class="flowdrop-form-select {validationErrors[fieldName]
274
- ? 'flowdrop-form-select--error'
275
- : ''}"
276
- disabled={props.disabled || false}
277
- onchange={(e) =>
278
- handleFieldChange(fieldName, (e.target as HTMLSelectElement).value)}
279
- >
280
- {#each property.enum as option (option)}
281
- <option value={option} selected={localValues[fieldName] === option}>
282
- {option}
283
- </option>
284
- {/each}
285
- </select>
286
- {:else if property.format === 'multiline' || (property.maxLength && property.maxLength > 100)}
287
- <!-- Textarea for multiline or long text -->
288
- <textarea
289
- id={fieldName}
290
- class="flowdrop-form-textarea {validationErrors[fieldName]
291
- ? 'flowdrop-form-textarea--error'
292
- : ''}"
293
- placeholder={property.description || ''}
294
- rows="4"
295
- disabled={props.disabled || false}
296
- onchange={(e) =>
297
- handleFieldChange(fieldName, (e.target as HTMLTextAreaElement).value)}
298
- >{localValues[fieldName] || ''}</textarea
299
- >
300
- {:else}
301
- <!-- Regular text input -->
302
- <input
303
- id={fieldName}
304
- type="text"
305
- class="flowdrop-form-input {validationErrors[fieldName]
306
- ? 'flowdrop-form-input--error'
307
- : ''}"
308
- value={localValues[fieldName] || ''}
309
- placeholder={property.description || ''}
310
- disabled={props.disabled || false}
311
- onchange={(e) =>
312
- handleFieldChange(fieldName, (e.target as HTMLInputElement).value)}
313
- />
314
- {/if}
315
- {:else if property.type === 'number'}
316
- <!-- Number input as text field -->
317
- <input
318
- id={fieldName}
319
- type="text"
320
- class="flowdrop-form-input {validationErrors[fieldName]
321
- ? 'flowdrop-form-input--error'
322
- : ''}"
323
- value={localValues[fieldName] || ''}
324
- placeholder="Enter a number"
325
- disabled={props.disabled || false}
326
- oninput={(e) => {
327
- const value = (e.target as HTMLInputElement).value;
328
- const numValue = value === '' ? 0 : parseFloat(value);
329
- if (!isNaN(numValue)) {
330
- handleFieldChange(fieldName, numValue);
331
- }
332
- }}
333
- onblur={(e) => {
334
- const value = (e.target as HTMLInputElement).value;
335
- const numValue = value === '' ? 0 : parseFloat(value);
336
- if (!isNaN(numValue)) {
337
- handleFieldChange(fieldName, numValue);
338
- }
339
- }}
340
- />
341
- {:else if property.type === 'boolean'}
342
- <!-- Checkbox -->
343
- <label class="flowdrop-form-checkbox">
344
- <input
345
- id={fieldName}
346
- type="checkbox"
347
- class="flowdrop-form-checkbox__input"
348
- checked={Boolean(localValues[fieldName])}
349
- disabled={props.disabled || false}
350
- onchange={(e) =>
351
- handleFieldChange(fieldName, (e.target as HTMLInputElement).checked)}
352
- />
353
- <span class="flowdrop-form-checkbox__label">
354
- {property.description || ''}
355
- </span>
356
- </label>
357
- {:else if property.type === 'array'}
358
- <!-- Array input (comma-separated) -->
359
- <textarea
360
- id={fieldName}
361
- class="flowdrop-form-textarea {validationErrors[fieldName]
362
- ? 'flowdrop-form-textarea--error'
363
- : ''}"
364
- placeholder="Enter values separated by commas"
365
- rows="3"
366
- disabled={props.disabled || false}
367
- onchange={(e) =>
368
- handleFieldChange(
369
- fieldName,
370
- (e.target as HTMLTextAreaElement).value
371
- .split(',')
372
- .map((v) => v.trim())
373
- .filter((v) => v)
374
- )}
375
- >{Array.isArray(localValues[fieldName])
376
- ? localValues[fieldName].join(', ')
377
- : ''}</textarea
131
+ } else {
132
+ configValues[key] = currentValues.filter(
133
+ (v) => v !== String(option)
134
+ )
135
+ }
136
+ }}
137
+ />
138
+ <span class="flowdrop-config-sidebar__checkbox-label">
139
+ {String(option)}
140
+ </span>
141
+ </label>
142
+ {/each}
143
+ </div>
144
+ {:else if fieldConfig.enum}
145
+ <!-- Select for enum with single selection -->
146
+ <select
147
+ id={key}
148
+ class="flowdrop-config-sidebar__select"
149
+ bind:value={configValues[key]}
378
150
  >
379
- {:else if property.type === 'object'}
380
- <!-- JSON object input -->
151
+ {#each fieldConfig.enum as option (String(option))}
152
+ <option value={String(option)}>{String(option)}</option>
153
+ {/each}
154
+ </select>
155
+ {:else if fieldConfig.type === "string" && fieldConfig.format === "multiline"}
156
+ <!-- Textarea for multiline strings -->
381
157
  <textarea
382
- id={fieldName}
383
- class="flowdrop-form-textarea {validationErrors[fieldName]
384
- ? 'flowdrop-form-textarea--error'
385
- : ''}"
386
- placeholder="Enter JSON object"
158
+ id={key}
159
+ class="flowdrop-config-sidebar__textarea"
160
+ bind:value={configValues[key]}
161
+ placeholder={String(fieldConfig.placeholder || "")}
387
162
  rows="4"
388
- disabled={props.disabled || false}
163
+ ></textarea>
164
+ {:else if fieldConfig.type === "string"}
165
+ <input
166
+ id={key}
167
+ type="text"
168
+ class="flowdrop-config-sidebar__input"
169
+ bind:value={configValues[key]}
170
+ placeholder={String(fieldConfig.placeholder || "")}
171
+ />
172
+ {:else if fieldConfig.type === "number"}
173
+ <input
174
+ id={key}
175
+ type="number"
176
+ class="flowdrop-config-sidebar__input"
177
+ bind:value={configValues[key]}
178
+ placeholder={String(fieldConfig.placeholder || "")}
179
+ />
180
+ {:else if fieldConfig.type === "boolean"}
181
+ <input
182
+ id={key}
183
+ type="checkbox"
184
+ class="flowdrop-config-sidebar__checkbox"
185
+ checked={Boolean(configValues[key] || fieldConfig.default || false)}
389
186
  onchange={(e) => {
390
- try {
391
- handleFieldChange(
392
- fieldName,
393
- JSON.parse((e.target as HTMLTextAreaElement).value)
394
- );
395
- } catch {
396
- // Handle JSON parse error
397
- }
187
+ configValues[key] = e.currentTarget.checked
398
188
  }}
399
- >{typeof localValues[fieldName] === 'object'
400
- ? JSON.stringify(localValues[fieldName], null, 2)
401
- : ''}</textarea
189
+ />
190
+ {:else if fieldConfig.type === "select" || fieldConfig.options}
191
+ <select
192
+ id={key}
193
+ class="flowdrop-config-sidebar__select"
194
+ bind:value={configValues[key]}
402
195
  >
196
+ {#if fieldConfig.options}
197
+ {#each fieldConfig.options as option (String(option.value))}
198
+ {@const optionConfig = option as any}
199
+ <option value={String(optionConfig.value)}
200
+ >{String(optionConfig.label)}</option
201
+ >
202
+ {/each}
203
+ {/if}
204
+ </select>
403
205
  {:else}
404
- <!-- Default text input -->
206
+ <!-- Fallback for unknown field types -->
405
207
  <input
406
- id={fieldName}
208
+ id={key}
407
209
  type="text"
408
- class="flowdrop-form-input {validationErrors[fieldName]
409
- ? 'flowdrop-form-input--error'
410
- : ''}"
411
- value={localValues[fieldName] || ''}
412
- placeholder={property.description || ''}
413
- disabled={props.disabled || false}
414
- onchange={(e) => handleFieldChange(fieldName, (e.target as HTMLInputElement).value)}
210
+ class="flowdrop-config-sidebar__input"
211
+ bind:value={configValues[key]}
212
+ placeholder={String(fieldConfig.placeholder || "")}
415
213
  />
416
214
  {/if}
417
-
418
- {#if validationErrors[fieldName]}
419
- <div class="flowdrop-form-error">{validationErrors[fieldName]}</div>
420
- {/if}
421
-
422
- {#if property.description}
423
- <div class="flowdrop-form-help">{property.description}</div>
215
+ {#if fieldConfig.description}
216
+ <p class="flowdrop-config-sidebar__field-description">
217
+ {String(fieldConfig.description)}
218
+ </p>
424
219
  {/if}
425
220
  </div>
426
221
  {/if}
427
222
  {/each}
428
- </form>
429
- {:else}
430
- <div class="flowdrop-form-empty">
431
- <p class="flowdrop-text--sm flowdrop-text--gray">
432
- No configuration schema available for this node.
433
- </p>
434
- </div>
435
- {/if}
436
- </div>
223
+ {:else}
224
+ <!-- If no properties, show the raw schema for debugging -->
225
+ <div class="flowdrop-config-sidebar__debug">
226
+ <p><strong>Debug - Config Schema:</strong></p>
227
+ <pre>{JSON.stringify(configSchema, null, 2)}</pre>
228
+ </div>
229
+ {/if}
230
+ </div>
231
+
232
+ <!-- Footer -->
233
+ <div class="flowdrop-config-sidebar__footer">
234
+ <button
235
+ class="flowdrop-config-sidebar__button flowdrop-config-sidebar__button--secondary"
236
+ onclick={onCancel}
237
+ >
238
+ Cancel
239
+ </button>
240
+ <button
241
+ class="flowdrop-config-sidebar__button flowdrop-config-sidebar__button--primary"
242
+ onclick={handleSave}
243
+ >
244
+ Save Changes
245
+ </button>
246
+ </div>
247
+ {:else}
248
+ <p class="flowdrop-config-sidebar__no-config">
249
+ No configuration options available for this node.
250
+ </p>
251
+ {/if}
437
252
 
438
253
  <style>
439
- .flowdrop-config-form {
440
- padding: 1rem;
441
- }
442
-
443
- .flowdrop-form {
254
+ .flowdrop-config-sidebar__form {
444
255
  display: flex;
445
256
  flex-direction: column;
446
257
  gap: 1rem;
447
258
  }
448
259
 
449
- .flowdrop-form-field {
260
+ .flowdrop-config-sidebar__field {
450
261
  display: flex;
451
262
  flex-direction: column;
452
263
  gap: 0.5rem;
453
264
  }
454
265
 
455
- .flowdrop-form-label {
266
+ .flowdrop-config-sidebar__field-label {
456
267
  font-size: 0.875rem;
457
268
  font-weight: 500;
458
269
  color: #374151;
459
- display: flex;
460
- align-items: center;
461
- gap: 0.25rem;
462
- }
463
-
464
- .flowdrop-form-required {
465
- color: #dc2626;
466
- font-weight: 700;
467
270
  }
468
271
 
469
- .flowdrop-form-input,
470
- .flowdrop-form-textarea,
471
- .flowdrop-form-select {
472
- padding: 0.5rem 0.75rem;
272
+ .flowdrop-config-sidebar__input,
273
+ .flowdrop-config-sidebar__select {
274
+ padding: 0.5rem;
473
275
  border: 1px solid #d1d5db;
474
276
  border-radius: 0.375rem;
475
277
  font-size: 0.875rem;
476
- background-color: #ffffff;
477
- transition: all 0.2s ease-in-out;
278
+ transition:
279
+ border-color 0.2s,
280
+ box-shadow 0.2s;
478
281
  }
479
282
 
480
- .flowdrop-form-input:focus,
481
- .flowdrop-form-textarea:focus,
482
- .flowdrop-form-select:focus {
283
+ .flowdrop-config-sidebar__input:focus,
284
+ .flowdrop-config-sidebar__select:focus {
483
285
  outline: none;
484
286
  border-color: #3b82f6;
485
287
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
486
288
  }
487
289
 
488
- .flowdrop-form-input--error,
489
- .flowdrop-form-textarea--error,
490
- .flowdrop-form-select--error {
491
- border-color: #dc2626;
492
- }
493
-
494
- .flowdrop-form-input--error:focus,
495
- .flowdrop-form-textarea--error:focus,
496
- .flowdrop-form-select--error:focus {
497
- border-color: #dc2626;
498
- box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);
499
- }
500
-
501
- .flowdrop-form-textarea {
502
- resize: vertical;
503
- min-height: 4rem;
504
- }
505
-
506
- .flowdrop-form-checkbox-group {
290
+ .flowdrop-config-sidebar__checkbox-group {
507
291
  display: flex;
508
292
  flex-direction: column;
509
293
  gap: 0.5rem;
510
294
  }
511
295
 
512
- .flowdrop-form-checkbox {
296
+ .flowdrop-config-sidebar__checkbox-item {
513
297
  display: flex;
514
298
  align-items: center;
515
299
  gap: 0.5rem;
516
300
  cursor: pointer;
517
301
  }
518
302
 
519
- .flowdrop-form-checkbox__input {
303
+ .flowdrop-config-sidebar__checkbox {
520
304
  width: 1rem;
521
305
  height: 1rem;
522
306
  accent-color: #3b82f6;
307
+ cursor: pointer;
523
308
  }
524
309
 
525
- .flowdrop-form-checkbox__label {
310
+ .flowdrop-config-sidebar__checkbox-label {
526
311
  font-size: 0.875rem;
527
312
  color: #374151;
313
+ cursor: pointer;
528
314
  }
529
315
 
530
- .flowdrop-form-error {
531
- font-size: 0.75rem;
532
- color: #dc2626;
533
- margin-top: 0.25rem;
316
+ .flowdrop-config-sidebar__textarea {
317
+ width: 100%;
318
+ padding: 0.5rem 0.75rem;
319
+ border: 1px solid #d1d5db;
320
+ border-radius: 0.375rem;
321
+ font-size: 0.875rem;
322
+ background-color: #ffffff;
323
+ transition: all 0.2s ease-in-out;
324
+ resize: vertical;
325
+ min-height: 4rem;
326
+ }
327
+
328
+ .flowdrop-config-sidebar__textarea:focus {
329
+ outline: none;
330
+ border-color: #3b82f6;
331
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
534
332
  }
535
333
 
536
- .flowdrop-form-help {
334
+ .flowdrop-config-sidebar__field-description {
335
+ margin: 0;
537
336
  font-size: 0.75rem;
538
337
  color: #6b7280;
539
- margin-top: 0.25rem;
338
+ line-height: 1.4;
540
339
  }
541
340
 
542
- .flowdrop-form-empty {
341
+ .flowdrop-config-sidebar__no-config {
543
342
  text-align: center;
544
- padding: 2rem;
545
343
  color: #6b7280;
344
+ font-style: italic;
345
+ padding: 2rem 1rem;
346
+ }
347
+
348
+ .flowdrop-config-sidebar__footer {
349
+ padding: 1rem;
350
+ border-top: 1px solid #e5e7eb;
351
+ background-color: #f9fafb;
352
+ display: flex;
353
+ gap: 0.75rem;
354
+ justify-content: flex-end;
355
+ height: 40px;
356
+ align-items: center;
357
+ }
358
+
359
+ .flowdrop-config-sidebar__button {
360
+ padding: 0.5rem 1rem;
361
+ border-radius: 0.375rem;
362
+ font-size: 0.875rem;
363
+ font-weight: 500;
364
+ cursor: pointer;
365
+ transition: all 0.2s;
366
+ border: 1px solid transparent;
367
+ }
368
+
369
+ .flowdrop-config-sidebar__button--secondary {
370
+ background-color: #ffffff;
371
+ border-color: #d1d5db;
372
+ color: #374151;
546
373
  }
547
374
 
548
- .flowdrop-form-input:disabled,
549
- .flowdrop-form-textarea:disabled,
550
- .flowdrop-form-select:disabled {
375
+ .flowdrop-config-sidebar__button--secondary:hover {
551
376
  background-color: #f9fafb;
552
- color: #9ca3af;
553
- cursor: not-allowed;
377
+ border-color: #9ca3af;
378
+ }
379
+
380
+ .flowdrop-config-sidebar__button--primary {
381
+ background-color: #3b82f6;
382
+ color: #ffffff;
383
+ }
384
+
385
+ .flowdrop-config-sidebar__button--primary:hover {
386
+ background-color: #2563eb;
387
+ }
388
+
389
+ .flowdrop-config-sidebar__debug {
390
+ background-color: #f3f4f6;
391
+ border: 1px solid #d1d5db;
392
+ border-radius: 0.375rem;
393
+ padding: 1rem;
394
+ margin: 1rem 0;
395
+ }
396
+
397
+ .flowdrop-config-sidebar__debug pre {
398
+ background-color: #ffffff;
399
+ border: 1px solid #e5e7eb;
400
+ border-radius: 0.25rem;
401
+ padding: 0.75rem;
402
+ font-size: 0.75rem;
403
+ overflow-x: auto;
404
+ margin: 0.5rem 0 0 0;
554
405
  }
555
406
  </style>