@finsweet/webflow-apps-utils 1.0.4 → 1.0.5

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.
Files changed (35) hide show
  1. package/dist/providers/GlobalProvider.stories.d.ts +5 -0
  2. package/dist/providers/GlobalProvider.stories.js +419 -0
  3. package/dist/providers/GlobalProviderDemo.svelte +266 -0
  4. package/dist/providers/GlobalProviderDemo.svelte.d.ts +3 -0
  5. package/dist/providers/configuratorUtils.d.ts +11 -14
  6. package/dist/providers/configuratorUtils.js +68 -115
  7. package/dist/providers/index.d.ts +1 -1
  8. package/dist/providers/index.js +1 -1
  9. package/dist/router/Router.stories.js +519 -2
  10. package/dist/stores/forms/Form.stories.d.ts +5 -0
  11. package/dist/stores/forms/Form.stories.js +342 -0
  12. package/dist/stores/forms/FormDemo.svelte +545 -0
  13. package/dist/stores/forms/FormDemo.svelte.d.ts +18 -0
  14. package/dist/ui/components/button/Button.svelte +1 -1
  15. package/dist/ui/components/copy-text/CopyText.stories.js +1 -1
  16. package/dist/ui/components/copy-text/CopyText.svelte +17 -19
  17. package/dist/ui/components/layout/Layout.svelte +38 -5
  18. package/dist/ui/components/layout/Layout.svelte.d.ts +24 -1
  19. package/dist/ui/components/layout/examples/ExampleLayout.svelte +12 -12
  20. package/dist/ui/components/section/Section.svelte +4 -2
  21. package/dist/ui/index.css +6 -2
  22. package/dist/utils/diff-mapper/DiffMapper.stories.d.ts +5 -0
  23. package/dist/utils/diff-mapper/DiffMapper.stories.js +185 -0
  24. package/dist/utils/diff-mapper/DiffMapperDemo.svelte +351 -0
  25. package/dist/utils/diff-mapper/DiffMapperDemo.svelte.d.ts +18 -0
  26. package/dist/utils/diff-mapper/deepDiffMapper.d.ts +31 -0
  27. package/dist/utils/diff-mapper/deepDiffMapper.js +264 -0
  28. package/dist/utils/diff-mapper/index.d.ts +1 -0
  29. package/dist/utils/diff-mapper/index.js +1 -0
  30. package/dist/utils/index.d.ts +1 -0
  31. package/dist/utils/index.js +1 -0
  32. package/package.json +1 -1
  33. package/dist/providers/GlobalProvider.mdx +0 -322
  34. package/dist/router/Router.mdx +0 -958
  35. package/dist/stores/docs/Form.mdx +0 -542
@@ -1,542 +0,0 @@
1
- # Form Validation System
2
-
3
- A comprehensive form validation system built with Svelte stores and Zod for type-safe form handling in Webflow apps.
4
-
5
- ## Overview
6
-
7
- The form validation system provides:
8
-
9
- - Type-safe form state management with Zod validation
10
- - Unique instance name generation for Webflow components
11
- - Real-time validation with error handling
12
- - Form registry for managing multiple forms
13
- - Class name sanitization for HTML compliance
14
-
15
- ## Core Classes and Functions
16
-
17
- ### FormValidator Class
18
-
19
- The main class for creating and managing form validation state.
20
-
21
- ```typescript
22
- import { FormValidator } from '$lib/stores/forms';
23
-
24
- // Define your form data structure
25
- interface MyFormData {
26
- name: string;
27
- instance: string;
28
- class: string;
29
- }
30
-
31
- // Create a form validator
32
- const formValidator = new FormValidator<MyFormData>(
33
- 'my-form-id',
34
- {
35
- name: 'My Component',
36
- instance: 'fs-component',
37
- class: 'fs-component'
38
- },
39
- {
40
- existingInstances: ['fs-existing-1', 'fs-existing-2']
41
- }
42
- );
43
- ```
44
-
45
- #### Constructor Parameters
46
-
47
- - `identifier: string` - Unique identifier for the form (used in global registry)
48
- - `initialValues: T` - Initial form values
49
- - `options.existingInstances?: string[]` - Array of existing instance names for uniqueness validation
50
-
51
- **Note:** Class validation is enabled by default. Use `enableClassValidation(false)` to disable it if your component doesn't require CSS classes.
52
-
53
- ### Static Methods
54
-
55
- #### generateNames()
56
-
57
- Generates unique names, instances, and class names based on existing instances:
58
-
59
- ```typescript
60
- const generated = FormValidator.generateNames(
61
- ['fs-slider-1', 'fs-slider-2'], // existing instances
62
- 'slider', // solution name
63
- 'Slider Component' // display name
64
- );
65
-
66
- // Returns:
67
- // {
68
- // name: 'Slider Component 3',
69
- // instance: 'fs-slider-3',
70
- // class: 'fs-slider-3'
71
- // }
72
- ```
73
-
74
- #### sanitizeClassName()
75
-
76
- Cleans up class names to ensure HTML compliance:
77
-
78
- ```typescript
79
- const cleaned = FormValidator.sanitizeClassName('my/invalid class-name!');
80
- // Returns: 'my-invalid-class-name'
81
- ```
82
-
83
- ### Instance Methods
84
-
85
- #### setField()
86
-
87
- Updates a single form field:
88
-
89
- ```typescript
90
- formValidator.setField('name', 'New Component Name');
91
- ```
92
-
93
- #### setFields()
94
-
95
- Updates multiple form fields at once:
96
-
97
- ```typescript
98
- formValidator.setFields({
99
- name: 'New Name',
100
- instance: 'fs-new-instance'
101
- });
102
- ```
103
-
104
- #### validateWithInstances()
105
-
106
- Updates the list of existing instances and re-validates:
107
-
108
- ```typescript
109
- formValidator.validateWithInstances(['fs-new-1', 'fs-new-2']);
110
- ```
111
-
112
- #### ignoreInstanceValidation()
113
-
114
- Temporarily ignores a specific instance during validation (useful for edit mode):
115
-
116
- ```typescript
117
- formValidator.ignoreInstanceValidation('fs-current-instance', existingInstances);
118
- ```
119
-
120
- #### reset()
121
-
122
- Resets the form to its initial state:
123
-
124
- ```typescript
125
- formValidator.reset();
126
- ```
127
-
128
- #### enableClassValidation()
129
-
130
- Enables or disables class name validation dynamically:
131
-
132
- ```typescript
133
- // Disable class validation (useful for certain component types)
134
- formValidator.enableClassValidation(false);
135
-
136
- // Re-enable class validation
137
- formValidator.enableClassValidation(true);
138
- ```
139
-
140
- #### getState()
141
-
142
- Gets the current form state:
143
-
144
- ```typescript
145
- const state = formValidator.getState();
146
- console.log(state.isValid, state.errors, state.values);
147
- ```
148
-
149
- #### setSubmitting()
150
-
151
- Sets the form submission state:
152
-
153
- ```typescript
154
- formValidator.setSubmitting(true);
155
- // ... perform submission
156
- formValidator.setSubmitting(false);
157
- ```
158
-
159
- ## Form State Structure
160
-
161
- The form state contains:
162
-
163
- ```typescript
164
- interface FormState<T> {
165
- values: T; // Current form values
166
- errors: Record<keyof T, string[]>; // Validation errors by field
167
- touched: Record<keyof T, boolean>; // Fields that have been interacted with
168
- isValid: boolean; // Overall form validity
169
- isDirty: boolean; // Whether form has been modified
170
- isSubmitting: boolean; // Submission state
171
- }
172
- ```
173
-
174
- ## Global Form Registry
175
-
176
- The system maintains a global registry of all forms for cross-component access.
177
-
178
- ### getFormById()
179
-
180
- Retrieve a form instance by its identifier:
181
-
182
- ```typescript
183
- import { getFormById } from '$lib/stores/forms';
184
-
185
- const form = getFormById('my-form-id');
186
- if (form) {
187
- console.log(form.getState());
188
- }
189
- ```
190
-
191
- ### isFormValid()
192
-
193
- Check if a specific form is valid:
194
-
195
- ```typescript
196
- import { isFormValid } from '$lib/stores/forms';
197
-
198
- if (isFormValid('my-form-id')) {
199
- console.log('Form is valid!');
200
- }
201
- ```
202
-
203
- ### getFormErrors()
204
-
205
- Get error messages for a specific form:
206
-
207
- ```typescript
208
- import { getFormErrors } from '$lib/stores/forms';
209
-
210
- const errors = getFormErrors('my-form-id');
211
- console.log(errors);
212
- ```
213
-
214
- ### resetForm()
215
-
216
- Reset a form by its identifier:
217
-
218
- ```typescript
219
- import { resetForm } from '$lib/stores/forms';
220
-
221
- resetForm('my-form-id');
222
- ```
223
-
224
- ## Svelte Integration
225
-
226
- ### Using in Svelte Components
227
-
228
- ```svelte
229
- <script lang="ts">
230
- import { FormValidator } from '$lib/stores/forms';
231
-
232
- interface ComponentForm {
233
- name: string;
234
- instance: string;
235
- class: string;
236
- }
237
-
238
- // Create form validator
239
- const formValidator = new FormValidator<ComponentForm>('component-form', {
240
- name: '',
241
- instance: '',
242
- class: ''
243
- });
244
-
245
- // Subscribe to form state
246
- $: formState = formValidator.form;
247
- </script>
248
-
249
- <form>
250
- <input
251
- type="text"
252
- bind:value={$formState.values.name}
253
- on:input={(e) => formValidator.setField('name', e.target.value)}
254
- class:error={$formState.errors.name?.length > 0}
255
- />
256
-
257
- {#if $formState.errors.name?.length > 0}
258
- <div class="error-message">
259
- {$formState.errors.name[0]}
260
- </div>
261
- {/if}
262
-
263
- <button type="submit" disabled={!$formState.isValid || $formState.isSubmitting}>
264
- {$formState.isSubmitting ? 'Saving...' : 'Save'}
265
- </button>
266
- </form>
267
- ```
268
-
269
- ### Form Subscription Helper
270
-
271
- For components that need to subscribe to form state changes:
272
-
273
- ```typescript
274
- import { createFormSubscription } from '$lib/stores/forms';
275
-
276
- const formSubscription = createFormSubscription('my-form-id');
277
-
278
- // Use in component
279
- $: formState = formSubscription;
280
-
281
- // Cleanup when component is destroyed
282
- onDestroy(() => {
283
- formSubscription.destroy();
284
- });
285
- ```
286
-
287
- ## Validation Rules
288
-
289
- The system includes built-in validation for Webflow component forms:
290
-
291
- ### Name Field
292
-
293
- - Required (minimum 1 character)
294
-
295
- ### Instance Field
296
-
297
- - Required (minimum 1 character)
298
- - Must be unique across existing instances
299
- - Case-insensitive uniqueness check
300
-
301
- ### Class Field
302
-
303
- - Required by default (minimum 1 character when validation is enabled)
304
- - Must contain only letters, numbers, underscores, and hyphens
305
- - Automatically sanitized with `sanitizeClassName()`
306
- - Can be disabled using `enableClassValidation(false)` for components that don't require CSS classes
307
-
308
- ## Global Loading States
309
-
310
- The module also exports global loading state stores:
311
-
312
- ```typescript
313
- import { isAddingOrUpdating, loadingSelectedElement } from '$lib/stores/forms';
314
-
315
- // Track if form is being added or updated to Canvas
316
- $: $isAddingOrUpdating = true;
317
-
318
- // Track if selected element is loading
319
- $: $loadingSelectedElement = true;
320
- ```
321
-
322
- ## Dynamic Class Validation
323
-
324
- The form validator supports enabling or disabling class name validation based on your component's requirements. This is useful for components that don't require CSS classes or when you want to allow more flexible class naming.
325
-
326
- ### When to Disable Class Validation
327
-
328
- - Components that only use inline styles
329
- - Third-party integrations that don't require CSS classes
330
- - Temporary or test components
331
- - Components where class names are generated programmatically
332
-
333
- ### Usage Examples
334
-
335
- ```typescript
336
- // Create form with class validation enabled (default)
337
- const formValidator = new FormValidator('component-form', initialValues);
338
-
339
- // Disable class validation for a component that doesn't need CSS
340
- formValidator.enableClassValidation(false);
341
-
342
- // Re-enable class validation if needed later
343
- formValidator.enableClassValidation(true);
344
- ```
345
-
346
- ### Conditional Class Validation
347
-
348
- ```svelte
349
- <script lang="ts">
350
- import { FormValidator } from '$lib/stores/forms';
351
-
352
- export let componentType: 'styled' | 'utility' | 'inline';
353
-
354
- const formValidator = new FormValidator('dynamic-form', initialValues);
355
-
356
- // Disable class validation for inline components
357
- $: {
358
- const needsClassValidation = componentType === 'styled' || componentType === 'utility';
359
- formValidator.enableClassValidation(needsClassValidation);
360
- }
361
- </script>
362
- ```
363
-
364
- ### Component-Specific Validation
365
-
366
- ```typescript
367
- // Different validation rules for different component types
368
- const createFormForComponent = (componentType: string, initialValues: FormData) => {
369
- const formValidator = new FormValidator(`${componentType}-form`, initialValues);
370
-
371
- // Disable class validation for certain component types
372
- const noClassComponents = ['embed', 'script', 'raw-html'];
373
- if (noClassComponents.includes(componentType)) {
374
- formValidator.enableClassValidation(false);
375
- }
376
-
377
- return formValidator;
378
- };
379
- ```
380
-
381
- ## Best Practices
382
-
383
- ### 1. Unique Form Identifiers
384
-
385
- Always use unique identifiers for each form to prevent conflicts:
386
-
387
- ```typescript
388
- const formValidator = new FormValidator(`${componentType}-${componentId}-form`, initialValues);
389
- ```
390
-
391
- ### 2. Handle Existing Instances
392
-
393
- Always provide existing instances for proper uniqueness validation:
394
-
395
- ```typescript
396
- const formValidator = new FormValidator('form-id', initialValues, {
397
- existingInstances: getAllExistingInstances()
398
- });
399
- ```
400
-
401
- ### 3. Edit Mode Handling
402
-
403
- Use `ignoreInstanceValidation()` when editing existing components:
404
-
405
- ```typescript
406
- if (isEditMode) {
407
- formValidator.ignoreInstanceValidation(currentInstance, existingInstances);
408
- }
409
- ```
410
-
411
- ### 4. Cleanup
412
-
413
- Always clean up form subscriptions to prevent memory leaks:
414
-
415
- ```typescript
416
- onDestroy(() => {
417
- formSubscription.destroy();
418
- });
419
- ```
420
-
421
- ## Example: Complete Component Form
422
-
423
- ```svelte
424
- <script lang="ts">
425
- import { onDestroy } from 'svelte';
426
- import { FormValidator } from '$lib/stores/forms';
427
-
428
- export let existingInstances: string[] = [];
429
- export let isEditMode = false;
430
- export let currentInstance = '';
431
- export let componentType: 'styled' | 'utility' | 'embed' = 'styled';
432
-
433
- interface SliderForm {
434
- name: string;
435
- instance: string;
436
- class: string;
437
- }
438
-
439
- // Generate initial values
440
- const initialValues = isEditMode
441
- ? { name: currentInstance, instance: currentInstance, class: currentInstance }
442
- : FormValidator.generateNames(existingInstances, 'slider', 'Slider Component');
443
-
444
- // Create form validator
445
- const formValidator = new FormValidator<SliderForm>('slider-form', initialValues, {
446
- existingInstances
447
- });
448
-
449
- // Handle edit mode
450
- if (isEditMode) {
451
- formValidator.ignoreInstanceValidation(currentInstance, existingInstances);
452
- }
453
-
454
- // Handle class validation based on component type
455
- $: {
456
- const needsClassValidation = componentType !== 'embed';
457
- formValidator.enableClassValidation(needsClassValidation);
458
- }
459
-
460
- // Subscribe to form state
461
- $: formState = formValidator.form;
462
-
463
- async function handleSubmit() {
464
- if (!$formState.isValid) return;
465
-
466
- formValidator.setSubmitting(true);
467
-
468
- try {
469
- // Submit form data
470
- await submitToWebflow($formState.values);
471
- } finally {
472
- formValidator.setSubmitting(false);
473
- }
474
- }
475
- </script>
476
-
477
- <form on:submit|preventDefault={handleSubmit}>
478
- <div class="field">
479
- <label for="name">Component Name</label>
480
- <input
481
- id="name"
482
- type="text"
483
- bind:value={$formState.values.name}
484
- on:input={(e) => formValidator.setField('name', e.target.value)}
485
- class:error={$formState.errors.name?.length > 0}
486
- />
487
- {#if $formState.errors.name?.length > 0}
488
- <span class="error">{$formState.errors.name[0]}</span>
489
- {/if}
490
- </div>
491
-
492
- <div class="field">
493
- <label for="instance">Instance Name</label>
494
- <input
495
- id="instance"
496
- type="text"
497
- bind:value={$formState.values.instance}
498
- on:input={(e) => formValidator.setField('instance', e.target.value)}
499
- class:error={$formState.errors.instance?.length > 0}
500
- />
501
- {#if $formState.errors.instance?.length > 0}
502
- <span class="error">{$formState.errors.instance[0]}</span>
503
- {/if}
504
- </div>
505
-
506
- <div class="field">
507
- <label for="class">CSS Class</label>
508
- <input
509
- id="class"
510
- type="text"
511
- bind:value={$formState.values.class}
512
- on:input={(e) =>
513
- formValidator.setField('class', FormValidator.sanitizeClassName(e.target.value))}
514
- class:error={$formState.errors.class?.length > 0}
515
- />
516
- {#if $formState.errors.class?.length > 0}
517
- <span class="error">{$formState.errors.class[0]}</span>
518
- {/if}
519
- </div>
520
-
521
- <button type="submit" disabled={!$formState.isValid || $formState.isSubmitting}>
522
- {$formState.isSubmitting ? 'Saving...' : isEditMode ? 'Update' : 'Create'}
523
- </button>
524
- </form>
525
-
526
- <style>
527
- .field {
528
- margin-bottom: 1rem;
529
- }
530
-
531
- .error {
532
- color: red;
533
- font-size: 0.875rem;
534
- }
535
-
536
- input.error {
537
- border-color: red;
538
- }
539
- </style>
540
- ```
541
-
542
- This form validation system provides a robust, type-safe foundation for managing form state in Webflow component applications, ensuring data integrity and providing excellent developer experience.