@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
@@ -0,0 +1,545 @@
1
+ <script lang="ts">
2
+ import { FormValidator } from '../forms';
3
+
4
+ interface DemoFormData {
5
+ name: string;
6
+ instance: string;
7
+ class: string;
8
+ }
9
+
10
+ // Demo state
11
+ let existingInstances = ['fs-slider-1', 'fs-slider-2', 'fs-tabs-1'];
12
+ let isEditMode = false;
13
+ let currentInstance = 'fs-slider-1';
14
+ let classValidationEnabled = true;
15
+ let refreshTrigger = 0;
16
+
17
+ // Create form validator
18
+ const formValidator = new FormValidator<DemoFormData>(
19
+ 'demo-form',
20
+ {
21
+ name: 'New Component',
22
+ instance: 'fs-component',
23
+ class: 'fs-component'
24
+ },
25
+ { existingInstances }
26
+ );
27
+
28
+ // Subscribe to form state using derived stores
29
+ const formValues = formValidator.values;
30
+ const formErrors = formValidator.errors;
31
+ const formTouched = formValidator.touched;
32
+ const formIsValid = formValidator.isValid;
33
+ const formIsDirty = formValidator.isDirty;
34
+ const formIsSubmitting = formValidator.isSubmitting;
35
+
36
+ function forceRefresh() {
37
+ refreshTrigger++;
38
+ }
39
+
40
+ function generateNewNames() {
41
+ const generated = FormValidator.generateNames(existingInstances, 'demo', 'Demo Component');
42
+ formValidator.setFields(generated);
43
+ forceRefresh();
44
+ }
45
+
46
+ function toggleEditMode() {
47
+ isEditMode = !isEditMode;
48
+ if (isEditMode) {
49
+ formValidator.ignoreInstanceValidation(currentInstance, existingInstances);
50
+ formValidator.setFields({
51
+ name: currentInstance,
52
+ instance: currentInstance,
53
+ class: currentInstance
54
+ });
55
+ } else {
56
+ formValidator.validateWithInstances(existingInstances);
57
+ }
58
+ forceRefresh();
59
+ }
60
+
61
+ function toggleClassValidation() {
62
+ classValidationEnabled = !classValidationEnabled;
63
+ formValidator.enableClassValidation(classValidationEnabled);
64
+ forceRefresh();
65
+ }
66
+
67
+ function addExistingInstance() {
68
+ const newInstance = `fs-new-${Date.now()}`;
69
+ existingInstances = [...existingInstances, newInstance];
70
+ formValidator.validateWithInstances(existingInstances);
71
+ forceRefresh();
72
+ }
73
+
74
+ function resetForm() {
75
+ formValidator.reset();
76
+ isEditMode = false;
77
+ forceRefresh();
78
+ }
79
+
80
+ function submitForm(event: SubmitEvent) {
81
+ event.preventDefault();
82
+ if (!$formIsValid) return;
83
+
84
+ formValidator.setSubmitting(true);
85
+
86
+ // Simulate submission
87
+ setTimeout(() => {
88
+ formValidator.setSubmitting(false);
89
+ console.log('Form submitted:', $formValues);
90
+ forceRefresh();
91
+ }, 1000);
92
+ }
93
+
94
+ function handleClassInput(e: Event) {
95
+ const target = e.target as HTMLInputElement;
96
+ const sanitized = FormValidator.sanitizeClassName(target.value);
97
+ formValidator.setField('class', sanitized);
98
+ forceRefresh();
99
+ }
100
+ </script>
101
+
102
+ {#key refreshTrigger}
103
+ <div class="demo-container">
104
+ <h3>Form Validation System Demo</h3>
105
+
106
+ <div class="demo-controls">
107
+ <div class="control-section">
108
+ <h4>Demo Controls</h4>
109
+ <div class="button-group">
110
+ <button onclick={generateNewNames} class="btn btn--secondary"> Generate Names </button>
111
+ <button onclick={toggleEditMode} class="btn btn--secondary">
112
+ {isEditMode ? 'Exit Edit Mode' : 'Enter Edit Mode'}
113
+ </button>
114
+ <button onclick={toggleClassValidation} class="btn btn--secondary">
115
+ {classValidationEnabled ? 'Disable' : 'Enable'} Class Validation
116
+ </button>
117
+ <button onclick={addExistingInstance} class="btn btn--secondary">
118
+ Add Existing Instance
119
+ </button>
120
+ <button onclick={resetForm} class="btn btn--secondary"> Reset Form </button>
121
+ </div>
122
+ </div>
123
+
124
+ <div class="status-section">
125
+ <h4>Form Status</h4>
126
+ <div class="status-grid">
127
+ <div class="status-item">
128
+ <span>Valid:</span>
129
+ <strong class="status-{$formIsValid}">{$formIsValid ? 'YES' : 'NO'}</strong>
130
+ </div>
131
+ <div class="status-item">
132
+ <span>Dirty:</span>
133
+ <strong class="status-{$formIsDirty}">{$formIsDirty ? 'YES' : 'NO'}</strong>
134
+ </div>
135
+ <div class="status-item">
136
+ <span>Submitting:</span>
137
+ <strong class="status-{$formIsSubmitting}">{$formIsSubmitting ? 'YES' : 'NO'}</strong>
138
+ </div>
139
+ <div class="status-item">
140
+ <span>Edit Mode:</span>
141
+ <strong class="status-{isEditMode}">{isEditMode ? 'ON' : 'OFF'}</strong>
142
+ </div>
143
+ <div class="status-item">
144
+ <span>Class Validation:</span>
145
+ <strong class="status-{classValidationEnabled}"
146
+ >{classValidationEnabled ? 'ON' : 'OFF'}</strong
147
+ >
148
+ </div>
149
+ </div>
150
+ </div>
151
+ </div>
152
+
153
+ <form class="demo-form" onsubmit={submitForm}>
154
+ <div class="form-section">
155
+ <h4>Component Form</h4>
156
+
157
+ <div class="field">
158
+ <label for="name">Component Name</label>
159
+ <input
160
+ id="name"
161
+ type="text"
162
+ value={$formValues.name}
163
+ oninput={(e) => {
164
+ const target = e.target as HTMLInputElement;
165
+ formValidator.setField('name', target.value);
166
+ forceRefresh();
167
+ }}
168
+ class:error={($formErrors.name?.length || 0) > 0}
169
+ placeholder="Enter component name"
170
+ />
171
+ {#if $formErrors.name?.length > 0}
172
+ <span class="error">{$formErrors.name[0]}</span>
173
+ {/if}
174
+ </div>
175
+
176
+ <div class="field">
177
+ <label for="instance">Instance Name</label>
178
+ <input
179
+ id="instance"
180
+ type="text"
181
+ value={$formValues.instance}
182
+ oninput={(e) => {
183
+ const target = e.target as HTMLInputElement;
184
+ formValidator.setField('instance', target.value);
185
+ forceRefresh();
186
+ }}
187
+ class:error={($formErrors.instance?.length || 0) > 0}
188
+ placeholder="Enter instance name (must be unique)"
189
+ />
190
+ {#if $formErrors.instance?.length > 0}
191
+ <span class="error">{$formErrors.instance[0]}</span>
192
+ {/if}
193
+ </div>
194
+
195
+ <div class="field">
196
+ <label for="class">CSS Class Name</label>
197
+ <input
198
+ id="class"
199
+ type="text"
200
+ value={$formValues.class}
201
+ oninput={handleClassInput}
202
+ class:error={($formErrors.class?.length || 0) > 0}
203
+ placeholder="Enter CSS class name"
204
+ disabled={!classValidationEnabled}
205
+ />
206
+ {#if $formErrors.class?.length > 0}
207
+ <span class="error">{$formErrors.class[0]}</span>
208
+ {/if}
209
+ {#if !classValidationEnabled}
210
+ <span class="helper">Class validation is disabled</span>
211
+ {/if}
212
+ </div>
213
+
214
+ <button
215
+ type="submit"
216
+ disabled={!$formIsValid || $formIsSubmitting}
217
+ class="btn btn--primary"
218
+ >
219
+ {#if $formIsSubmitting}
220
+ Submitting...
221
+ {:else if isEditMode}
222
+ Update Component
223
+ {:else}
224
+ Create Component
225
+ {/if}
226
+ </button>
227
+ </div>
228
+ </form>
229
+
230
+ <div class="debug-section">
231
+ <h4>Debug Information</h4>
232
+
233
+ <div class="debug-grid">
234
+ <div class="debug-item">
235
+ <h5>Existing Instances</h5>
236
+ <code>{JSON.stringify(existingInstances, null, 2)}</code>
237
+ </div>
238
+
239
+ <div class="debug-item">
240
+ <h5>Form Values</h5>
241
+ <code>{JSON.stringify($formValues, null, 2)}</code>
242
+ </div>
243
+
244
+ <div class="debug-item">
245
+ <h5>Form Errors</h5>
246
+ <code>{JSON.stringify($formErrors, null, 2)}</code>
247
+ </div>
248
+
249
+ <div class="debug-item">
250
+ <h5>Touched Fields</h5>
251
+ <code>{JSON.stringify($formTouched, null, 2)}</code>
252
+ </div>
253
+ </div>
254
+ </div>
255
+
256
+ <div class="examples-section">
257
+ <h4>Static Method Examples</h4>
258
+
259
+ <div class="example">
260
+ <h5>FormValidator.generateNames()</h5>
261
+ <button
262
+ onclick={() => {
263
+ const result = FormValidator.generateNames(existingInstances, 'test', 'Test Component');
264
+ console.log('Generated names:', result);
265
+ }}
266
+ class="btn btn--secondary"
267
+ >
268
+ Generate Test Names (check console)
269
+ </button>
270
+ </div>
271
+
272
+ <div class="example">
273
+ <h5>FormValidator.sanitizeClassName()</h5>
274
+ <div class="sanitize-demo">
275
+ <input
276
+ type="text"
277
+ placeholder="Enter invalid class name"
278
+ oninput={(e) => {
279
+ const target = e.target as HTMLInputElement;
280
+ const sanitized = FormValidator.sanitizeClassName(target.value);
281
+ const output = target.nextElementSibling as HTMLElement;
282
+ if (output) output.textContent = sanitized;
283
+ }}
284
+ />
285
+ <span class="sanitized-output">Sanitized output will appear here</span>
286
+ </div>
287
+ </div>
288
+ </div>
289
+ </div>
290
+ {/key}
291
+
292
+ <style>
293
+ .demo-container {
294
+ padding: 1.5rem;
295
+ max-width: 800px;
296
+ margin: 0 auto;
297
+ background: var(--background1);
298
+ color: var(--text1);
299
+ font-family: var(--font-stack);
300
+ }
301
+
302
+ .demo-controls {
303
+ display: grid;
304
+ grid-template-columns: 1fr 1fr;
305
+ gap: 1.5rem;
306
+ margin-bottom: 2rem;
307
+ }
308
+
309
+ .control-section,
310
+ .status-section {
311
+ padding: 1rem;
312
+ border: 1px solid var(--border2);
313
+ border-radius: var(--border-radius);
314
+ background: var(--background2);
315
+ }
316
+
317
+ .control-section h4,
318
+ .status-section h4,
319
+ .form-section h4,
320
+ .debug-section h4,
321
+ .examples-section h4 {
322
+ margin: 0 0 1rem 0;
323
+ color: var(--text1);
324
+ font-size: var(--font-size-large);
325
+ font-weight: var(--font-weight-medium);
326
+ }
327
+
328
+ .button-group {
329
+ display: flex;
330
+ flex-wrap: wrap;
331
+ gap: 0.5rem;
332
+ }
333
+
334
+ .status-grid {
335
+ display: grid;
336
+ grid-template-columns: 1fr 1fr;
337
+ gap: 0.5rem;
338
+ }
339
+
340
+ .status-item {
341
+ display: flex;
342
+ justify-content: space-between;
343
+ padding: 0.25rem 0;
344
+ font-size: var(--font-size-small);
345
+ }
346
+
347
+ .demo-form {
348
+ margin-bottom: 2rem;
349
+ }
350
+
351
+ .form-section {
352
+ padding: 1rem;
353
+ border: 1px solid var(--border2);
354
+ border-radius: var(--border-radius);
355
+ background: var(--background2);
356
+ }
357
+
358
+ .field {
359
+ margin-bottom: 1rem;
360
+ }
361
+
362
+ label {
363
+ display: block;
364
+ margin-bottom: 0.25rem;
365
+ font-size: var(--font-size-small);
366
+ font-weight: var(--font-weight-medium);
367
+ color: var(--text1);
368
+ }
369
+
370
+ input {
371
+ width: 100%;
372
+ padding: var(--padding-small);
373
+ border: 1px solid var(--border2);
374
+ border-radius: var(--border-radius);
375
+ background: var(--background1);
376
+ color: var(--text1);
377
+ font-size: var(--font-size-small);
378
+ font-family: var(--font-stack);
379
+ }
380
+
381
+ input:focus {
382
+ outline: none;
383
+ border-color: var(--actionPrimaryBackground);
384
+ }
385
+
386
+ input.error {
387
+ border-color: var(--redText);
388
+ }
389
+
390
+ input:disabled {
391
+ opacity: 0.6;
392
+ cursor: not-allowed;
393
+ }
394
+
395
+ .error {
396
+ color: var(--redText);
397
+ font-size: var(--font-size-tiny);
398
+ margin-top: 0.25rem;
399
+ display: block;
400
+ }
401
+
402
+ .helper {
403
+ color: var(--text3);
404
+ font-size: var(--font-size-tiny);
405
+ margin-top: 0.25rem;
406
+ display: block;
407
+ }
408
+
409
+ .btn {
410
+ padding: var(--padding-small) var(--padding-regular);
411
+ border: none;
412
+ border-radius: var(--border-radius);
413
+ cursor: pointer;
414
+ font-size: var(--font-size-small);
415
+ font-weight: var(--font-weight-normal);
416
+ font-family: var(--font-stack);
417
+ transition: all 0.2s ease;
418
+ }
419
+
420
+ .btn--primary {
421
+ background: var(--actionPrimaryBackground);
422
+ color: var(--actionPrimaryText);
423
+ box-shadow: var(--boxShadows-action-colored);
424
+ }
425
+
426
+ .btn--primary:hover:not(:disabled) {
427
+ background: var(--actionPrimaryBackgroundHover);
428
+ color: var(--actionPrimaryTextHover);
429
+ }
430
+
431
+ .btn--secondary {
432
+ background: var(--actionSecondaryBackground);
433
+ color: var(--actionSecondaryText);
434
+ box-shadow: var(--boxShadows-action-secondary);
435
+ }
436
+
437
+ .btn--secondary:hover:not(:disabled) {
438
+ background: var(--actionSecondaryBackgroundHover);
439
+ color: var(--actionSecondaryTextHover);
440
+ }
441
+
442
+ .btn:disabled {
443
+ opacity: 0.6;
444
+ cursor: not-allowed;
445
+ }
446
+
447
+ .status-true {
448
+ color: var(--greenText);
449
+ font-weight: var(--font-weight-medium);
450
+ }
451
+
452
+ .status-false {
453
+ color: var(--redText);
454
+ font-weight: var(--font-weight-medium);
455
+ }
456
+
457
+ .debug-section {
458
+ margin-bottom: 2rem;
459
+ padding: 1rem;
460
+ border: 1px solid var(--border2);
461
+ border-radius: var(--border-radius);
462
+ background: var(--background2);
463
+ }
464
+
465
+ .debug-grid {
466
+ display: grid;
467
+ grid-template-columns: 1fr 1fr;
468
+ gap: 1rem;
469
+ }
470
+
471
+ .debug-item h5 {
472
+ margin: 0 0 0.5rem 0;
473
+ font-size: var(--font-size-small);
474
+ font-weight: var(--font-weight-medium);
475
+ color: var(--text2);
476
+ }
477
+
478
+ .examples-section {
479
+ padding: 1rem;
480
+ border: 1px solid var(--border2);
481
+ border-radius: var(--border-radius);
482
+ background: var(--background2);
483
+ }
484
+
485
+ .example {
486
+ margin-bottom: 1rem;
487
+ }
488
+
489
+ .example h5 {
490
+ margin: 0 0 0.5rem 0;
491
+ font-size: var(--font-size-small);
492
+ font-weight: var(--font-weight-medium);
493
+ color: var(--text2);
494
+ }
495
+
496
+ .sanitize-demo {
497
+ display: flex;
498
+ gap: 0.5rem;
499
+ align-items: center;
500
+ }
501
+
502
+ .sanitize-demo input {
503
+ flex: 1;
504
+ }
505
+
506
+ .sanitized-output {
507
+ flex: 1;
508
+ padding: var(--padding-small);
509
+ background: var(--background3);
510
+ border: 1px solid var(--border2);
511
+ border-radius: var(--border-radius);
512
+ font-family: monospace;
513
+ font-size: var(--font-size-tiny);
514
+ color: var(--text3);
515
+ }
516
+
517
+ code {
518
+ background: var(--background3);
519
+ border: 1px solid var(--border2);
520
+ padding: var(--padding-tiny);
521
+ border-radius: var(--border-radius);
522
+ font-family: monospace;
523
+ font-size: var(--font-size-tiny);
524
+ color: var(--text2);
525
+ display: block;
526
+ white-space: pre-wrap;
527
+ max-height: 150px;
528
+ overflow-y: auto;
529
+ }
530
+
531
+ @media (max-width: 768px) {
532
+ .demo-controls {
533
+ grid-template-columns: 1fr;
534
+ }
535
+
536
+ .debug-grid {
537
+ grid-template-columns: 1fr;
538
+ }
539
+
540
+ .sanitize-demo {
541
+ flex-direction: column;
542
+ align-items: stretch;
543
+ }
544
+ }
545
+ </style>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const FormDemo: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type FormDemo = InstanceType<typeof FormDemo>;
18
+ export default FormDemo;
@@ -125,7 +125,7 @@
125
125
  let shouldShowTooltip = $derived(!!tooltip?.message || !!tooltip?.tooltipContent);
126
126
 
127
127
  // Default styling (no size variations)
128
- let computedPadding = $derived(padding || '4px');
128
+ let computedPadding = $derived(padding || '4px 8px');
129
129
  let computedIconSize = $derived(iconSize);
130
130
 
131
131
  // Computed classes
@@ -1,6 +1,6 @@
1
1
  import CopyText from './CopyText.svelte';
2
2
  const meta = {
3
- title: 'Components/CopyText',
3
+ title: 'Ui/CopyText',
4
4
  component: CopyText,
5
5
  parameters: {
6
6
  layout: 'centered',
@@ -165,36 +165,34 @@
165
165
  }
166
166
 
167
167
  .copy-button {
168
+ box-shadow:
169
+ rgba(255, 255, 255, 0.12) 0px 0.5px 0.5px inset,
170
+ rgb(0, 0, 0) 0px 0.5px 1px;
168
171
  display: flex;
169
172
  align-items: flex-start;
173
+ align-self: stretch;
174
+ color: var(--text2);
175
+ font-family: Inter;
176
+ font-size: 12px;
177
+ font-style: normal;
178
+ font-weight: 500;
179
+ line-height: 16px;
180
+ cursor: pointer;
170
181
  justify-content: space-between;
171
- gap: 8px;
172
- padding: 6px 8px;
182
+ flex-direction: row;
173
183
  border-radius: 4px;
174
184
  background: var(
175
- --button-secondary-background,
185
+ --background3,
176
186
  linear-gradient(180deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.1) 100%),
177
187
  rgba(255, 255, 255, 0.08)
178
188
  );
179
- box-shadow:
180
- 0px 0.5px 0.5px 0px rgba(255, 255, 255, 0.12) inset,
181
- 0px 0.5px 1px 0px #000;
182
- color: var(--text-color-secondary, #d9d9d9);
183
- font-family: Inter, sans-serif;
184
- font-size: 11px;
185
- font-weight: 500;
186
- line-height: 16px;
187
- cursor: pointer;
188
- transition: all 0.2s ease-in-out;
189
- outline: none;
189
+ padding: 6px var(--Spacing-8, 8px);
190
+ gap: var(--Spacing-8, 8px);
191
+ flex: 1 0 0px;
190
192
  }
191
193
 
192
194
  .copy-button:hover:not(.copy-button--disabled) {
193
- background: var(
194
- --button-secondary-background-hover,
195
- linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.14) 100%),
196
- rgba(255, 255, 255, 0.12)
197
- );
195
+ cursor: copy;
198
196
  }
199
197
 
200
198
  .copy-button:focus-visible {