@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.
- package/dist/providers/GlobalProvider.stories.d.ts +5 -0
- package/dist/providers/GlobalProvider.stories.js +419 -0
- package/dist/providers/GlobalProviderDemo.svelte +266 -0
- package/dist/providers/GlobalProviderDemo.svelte.d.ts +3 -0
- package/dist/providers/configuratorUtils.d.ts +11 -14
- package/dist/providers/configuratorUtils.js +68 -115
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/router/Router.stories.js +519 -2
- package/dist/stores/forms/Form.stories.d.ts +5 -0
- package/dist/stores/forms/Form.stories.js +342 -0
- package/dist/stores/forms/FormDemo.svelte +545 -0
- package/dist/stores/forms/FormDemo.svelte.d.ts +18 -0
- package/dist/ui/components/button/Button.svelte +1 -1
- package/dist/ui/components/copy-text/CopyText.stories.js +1 -1
- package/dist/ui/components/copy-text/CopyText.svelte +17 -19
- package/dist/ui/components/layout/Layout.svelte +38 -5
- package/dist/ui/components/layout/Layout.svelte.d.ts +24 -1
- package/dist/ui/components/layout/examples/ExampleLayout.svelte +12 -12
- package/dist/ui/components/section/Section.svelte +4 -2
- package/dist/ui/index.css +6 -2
- package/dist/utils/diff-mapper/DiffMapper.stories.d.ts +5 -0
- package/dist/utils/diff-mapper/DiffMapper.stories.js +185 -0
- package/dist/utils/diff-mapper/DiffMapperDemo.svelte +351 -0
- package/dist/utils/diff-mapper/DiffMapperDemo.svelte.d.ts +18 -0
- package/dist/utils/diff-mapper/deepDiffMapper.d.ts +31 -0
- package/dist/utils/diff-mapper/deepDiffMapper.js +264 -0
- package/dist/utils/diff-mapper/index.d.ts +1 -0
- package/dist/utils/diff-mapper/index.js +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +1 -1
- package/dist/providers/GlobalProvider.mdx +0 -322
- package/dist/router/Router.mdx +0 -958
- package/dist/stores/docs/Form.mdx +0 -542
|
@@ -9,32 +9,56 @@
|
|
|
9
9
|
WarningCircleOutlineIcon
|
|
10
10
|
} from '../../icons';
|
|
11
11
|
|
|
12
|
+
import Section from '../section/Section.svelte';
|
|
12
13
|
import { Tooltip } from '../tooltip';
|
|
13
14
|
import { EditModeMessage } from './common';
|
|
14
15
|
import type { LayoutTab } from './types';
|
|
15
16
|
|
|
16
17
|
interface LayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
18
|
+
/** The currently active tab path */
|
|
17
19
|
activeTab: string;
|
|
20
|
+
/** Array of available tabs to display in the navbar */
|
|
18
21
|
tabs: LayoutTab[];
|
|
22
|
+
/** Function to handle tab switching */
|
|
19
23
|
switchTab: (tab: string) => void;
|
|
24
|
+
/** Unique key for the form context */
|
|
20
25
|
formKey: string;
|
|
21
|
-
|
|
26
|
+
/** Whether to show the edit mode message banner */
|
|
27
|
+
showEditModeMessage?: boolean;
|
|
28
|
+
/** Whether to display the footer section */
|
|
22
29
|
showFooter?: boolean;
|
|
30
|
+
/** Whether to display the sidebar */
|
|
23
31
|
showSidebar?: boolean;
|
|
32
|
+
/** Whether to display the tab navigation bar */
|
|
24
33
|
showTabs?: boolean;
|
|
34
|
+
/** The height at which the main content area becomes scrollable */
|
|
35
|
+
mainContentScrollableAt?: number;
|
|
36
|
+
/** Whether to display the preview bar */
|
|
25
37
|
showPreviewBar?: boolean;
|
|
38
|
+
/** Width of the sidebar (CSS value) */
|
|
26
39
|
sidebarWidth?: string;
|
|
40
|
+
/** Whether to use container mode (100% dimensions) instead of viewport mode */
|
|
27
41
|
containerMode?: boolean;
|
|
42
|
+
/** Size variant for the footer */
|
|
28
43
|
footerSize?: 'normal' | 'large';
|
|
44
|
+
/** Array of notification objects for tab status indicators */
|
|
29
45
|
notifications?: Array<{
|
|
46
|
+
/** Tab path this notification applies to */
|
|
30
47
|
path: string;
|
|
48
|
+
/** Whether the notification indicates success */
|
|
31
49
|
success: boolean;
|
|
50
|
+
/** Notification message content */
|
|
32
51
|
message: string;
|
|
52
|
+
/** Whether to show the notification badge */
|
|
33
53
|
showNotification: boolean;
|
|
34
54
|
}>;
|
|
55
|
+
/** Sidebar content snippet */
|
|
35
56
|
sidebar?: Snippet;
|
|
57
|
+
/** Main content area snippet */
|
|
36
58
|
main?: Snippet;
|
|
59
|
+
/** Preview bar content snippet */
|
|
37
60
|
previewBar?: Snippet;
|
|
61
|
+
/** Footer content snippet */
|
|
38
62
|
footer?: Snippet;
|
|
39
63
|
}
|
|
40
64
|
|
|
@@ -43,7 +67,8 @@
|
|
|
43
67
|
tabs,
|
|
44
68
|
switchTab,
|
|
45
69
|
formKey,
|
|
46
|
-
|
|
70
|
+
mainContentScrollableAt,
|
|
71
|
+
showEditModeMessage = false,
|
|
47
72
|
showFooter = true,
|
|
48
73
|
showSidebar = true,
|
|
49
74
|
showTabs = true,
|
|
@@ -216,8 +241,16 @@
|
|
|
216
241
|
<div class="main-content" data-area="main">
|
|
217
242
|
{#if main}
|
|
218
243
|
<div class="main-content-container">
|
|
219
|
-
|
|
220
|
-
|
|
244
|
+
{#if showEditModeMessage}
|
|
245
|
+
<EditModeMessage />
|
|
246
|
+
{/if}
|
|
247
|
+
{#if mainContentScrollableAt}
|
|
248
|
+
<Section height={`${mainContentScrollableAt}px`} scrollable padding="0">
|
|
249
|
+
{@render main()}
|
|
250
|
+
</Section>
|
|
251
|
+
{:else}
|
|
252
|
+
{@render main()}
|
|
253
|
+
{/if}
|
|
221
254
|
</div>
|
|
222
255
|
{:else}
|
|
223
256
|
<div class="main-placeholder">
|
|
@@ -231,7 +264,7 @@
|
|
|
231
264
|
<li>Show Tabs: {showTabs}</li>
|
|
232
265
|
<li>Show Preview Bar: {showPreviewBar}</li>
|
|
233
266
|
<li>Show Footer: {showFooter}</li>
|
|
234
|
-
<li>Edit Mode: {
|
|
267
|
+
<li>Edit Mode: {showEditModeMessage}</li>
|
|
235
268
|
</ul>
|
|
236
269
|
</div>
|
|
237
270
|
</div>
|
|
@@ -2,27 +2,50 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
3
|
import type { LayoutTab } from './types';
|
|
4
4
|
interface LayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** The currently active tab path */
|
|
5
6
|
activeTab: string;
|
|
7
|
+
/** Array of available tabs to display in the navbar */
|
|
6
8
|
tabs: LayoutTab[];
|
|
9
|
+
/** Function to handle tab switching */
|
|
7
10
|
switchTab: (tab: string) => void;
|
|
11
|
+
/** Unique key for the form context */
|
|
8
12
|
formKey: string;
|
|
9
|
-
|
|
13
|
+
/** Whether to show the edit mode message banner */
|
|
14
|
+
showEditModeMessage?: boolean;
|
|
15
|
+
/** Whether to display the footer section */
|
|
10
16
|
showFooter?: boolean;
|
|
17
|
+
/** Whether to display the sidebar */
|
|
11
18
|
showSidebar?: boolean;
|
|
19
|
+
/** Whether to display the tab navigation bar */
|
|
12
20
|
showTabs?: boolean;
|
|
21
|
+
/** The height at which the main content area becomes scrollable */
|
|
22
|
+
mainContentScrollableAt?: number;
|
|
23
|
+
/** Whether to display the preview bar */
|
|
13
24
|
showPreviewBar?: boolean;
|
|
25
|
+
/** Width of the sidebar (CSS value) */
|
|
14
26
|
sidebarWidth?: string;
|
|
27
|
+
/** Whether to use container mode (100% dimensions) instead of viewport mode */
|
|
15
28
|
containerMode?: boolean;
|
|
29
|
+
/** Size variant for the footer */
|
|
16
30
|
footerSize?: 'normal' | 'large';
|
|
31
|
+
/** Array of notification objects for tab status indicators */
|
|
17
32
|
notifications?: Array<{
|
|
33
|
+
/** Tab path this notification applies to */
|
|
18
34
|
path: string;
|
|
35
|
+
/** Whether the notification indicates success */
|
|
19
36
|
success: boolean;
|
|
37
|
+
/** Notification message content */
|
|
20
38
|
message: string;
|
|
39
|
+
/** Whether to show the notification badge */
|
|
21
40
|
showNotification: boolean;
|
|
22
41
|
}>;
|
|
42
|
+
/** Sidebar content snippet */
|
|
23
43
|
sidebar?: Snippet;
|
|
44
|
+
/** Main content area snippet */
|
|
24
45
|
main?: Snippet;
|
|
46
|
+
/** Preview bar content snippet */
|
|
25
47
|
previewBar?: Snippet;
|
|
48
|
+
/** Footer content snippet */
|
|
26
49
|
footer?: Snippet;
|
|
27
50
|
}
|
|
28
51
|
declare const Layout: import("svelte").Component<LayoutProps, {}, "">;
|
|
@@ -18,20 +18,20 @@
|
|
|
18
18
|
let showTabs = $state(true);
|
|
19
19
|
let showPreviewBar = $state(true);
|
|
20
20
|
let showFooter = $state(true);
|
|
21
|
-
let
|
|
21
|
+
let showEditModeMessage = $state(false);
|
|
22
22
|
let sidebarWidth = $state('239px');
|
|
23
23
|
let footerSize = $state<'normal' | 'large'>('normal');
|
|
24
24
|
let showNotifications = $state(false);
|
|
25
25
|
let notificationSuccess = $state(true);
|
|
26
26
|
let inputValue = $state('');
|
|
27
27
|
|
|
28
|
-
// Get app context and keep
|
|
28
|
+
// Get app context and keep showEditModeMessage in sync
|
|
29
29
|
const appContext = useAppContext();
|
|
30
30
|
|
|
31
|
-
// Subscribe to context changes to keep local
|
|
31
|
+
// Subscribe to context changes to keep local showEditModeMessage in sync
|
|
32
32
|
appContext.subscribe((data) => {
|
|
33
|
-
if (data?.editMode !== undefined && data.editMode !==
|
|
34
|
-
|
|
33
|
+
if (data?.editMode !== undefined && data.editMode !== showEditModeMessage) {
|
|
34
|
+
showEditModeMessage = data.editMode;
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
watchOptions: { watchAll: true, debounceMs: 50 }
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
|
-
// Initialize local
|
|
96
|
-
|
|
95
|
+
// Initialize local showEditModeMessage state
|
|
96
|
+
showEditModeMessage = true;
|
|
97
97
|
});
|
|
98
98
|
</script>
|
|
99
99
|
|
|
@@ -182,12 +182,12 @@
|
|
|
182
182
|
<div class="toggle-control">
|
|
183
183
|
<input
|
|
184
184
|
type="checkbox"
|
|
185
|
-
bind:checked={
|
|
185
|
+
bind:checked={showEditModeMessage}
|
|
186
186
|
id="edit-mode"
|
|
187
187
|
class="checkbox-input"
|
|
188
188
|
onchange={() => {
|
|
189
189
|
const current = appContext.get();
|
|
190
|
-
appContext.set({ ...current, editMode });
|
|
190
|
+
appContext.set({ ...current, editMode: showEditModeMessage });
|
|
191
191
|
}}
|
|
192
192
|
/>
|
|
193
193
|
<label for="edit-mode" class="checkbox-label">Edit Mode</label>
|
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
{tabs}
|
|
228
228
|
{switchTab}
|
|
229
229
|
formKey="dummy-layout"
|
|
230
|
-
{
|
|
230
|
+
{showEditModeMessage}
|
|
231
231
|
{showFooter}
|
|
232
232
|
{showSidebar}
|
|
233
233
|
{showTabs}
|
|
@@ -329,11 +329,11 @@
|
|
|
329
329
|
variant="secondary"
|
|
330
330
|
icon={CheckCircleIcon}
|
|
331
331
|
onclick={handleSaveClick}
|
|
332
|
-
disabled={
|
|
332
|
+
disabled={showEditModeMessage}
|
|
333
333
|
>
|
|
334
334
|
Save Changes
|
|
335
335
|
</Button>
|
|
336
|
-
<Button variant="primary" onclick={handleApplyClick} disabled={
|
|
336
|
+
<Button variant="primary" onclick={handleApplyClick} disabled={showEditModeMessage}>
|
|
337
337
|
Apply Settings
|
|
338
338
|
</Button>
|
|
339
339
|
{/snippet}
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
scrollbars: {
|
|
141
141
|
theme: 'os-theme-dark',
|
|
142
142
|
visibility: 'auto',
|
|
143
|
-
autoHide: '
|
|
143
|
+
autoHide: 'leave',
|
|
144
144
|
autoHideDelay: 800
|
|
145
145
|
}
|
|
146
146
|
};
|
|
@@ -186,6 +186,7 @@
|
|
|
186
186
|
tooltipIconColor="var(--yellowText)"
|
|
187
187
|
message={defaultDisabledMessage}
|
|
188
188
|
width={disabledTooltipWidth}
|
|
189
|
+
class="not-allowed"
|
|
189
190
|
>
|
|
190
191
|
{#snippet target()}
|
|
191
192
|
{@render sectionContent()}
|
|
@@ -271,6 +272,7 @@
|
|
|
271
272
|
/* Disabled in edit mode styles */
|
|
272
273
|
.section-wrap.disabled-in-edit-mode {
|
|
273
274
|
pointer-events: none;
|
|
275
|
+
cursor: not-allowed;
|
|
274
276
|
}
|
|
275
277
|
|
|
276
278
|
.section-wrap.disabled-in-edit-mode :global(.label-popup .labels span) {
|
|
@@ -301,7 +303,7 @@
|
|
|
301
303
|
|
|
302
304
|
/* OverlayScrollbars dark theme customization using design system */
|
|
303
305
|
.section-wrap :global(.os-scrollbar) {
|
|
304
|
-
--os-size: var(--sb-size
|
|
306
|
+
--os-size: var(--sb-size);
|
|
305
307
|
--os-padding-perpendicular: 2px;
|
|
306
308
|
--os-padding-axis: 2px;
|
|
307
309
|
}
|
package/dist/ui/index.css
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/* custom scrollbar related */
|
|
5
5
|
--sb-track-color: #1e1e1e;
|
|
6
6
|
--sb-thumb-color: #373737;
|
|
7
|
-
--sb-size:
|
|
7
|
+
--sb-size: 8px;
|
|
8
8
|
|
|
9
9
|
/* Webflow colors */
|
|
10
10
|
--background1: #292929;
|
|
@@ -264,9 +264,13 @@ label {
|
|
|
264
264
|
.disabled,
|
|
265
265
|
.login-required {
|
|
266
266
|
cursor: not-allowed !important;
|
|
267
|
-
opacity: 0.
|
|
267
|
+
opacity: 0.8 !important;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
/* Used for disabled sections */
|
|
271
|
+
.not-allowed {
|
|
272
|
+
cursor: not-allowed !important;
|
|
273
|
+
}
|
|
270
274
|
/* OverlayScrollbars Global Theme */
|
|
271
275
|
:root {
|
|
272
276
|
/* OverlayScrollbars theme variables using design system colors */
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import DiffMapperDemo from './DiffMapperDemo.svelte';
|
|
2
|
+
const meta = {
|
|
3
|
+
title: 'Utils/DiffMapper',
|
|
4
|
+
component: DiffMapperDemo,
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: 'centered',
|
|
7
|
+
backgrounds: {
|
|
8
|
+
default: 'dark',
|
|
9
|
+
values: [
|
|
10
|
+
{ name: 'dark', value: '#292929' },
|
|
11
|
+
{ name: 'light', value: '#ffffff' }
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component: `
|
|
17
|
+
# DiffMapper Utilities
|
|
18
|
+
|
|
19
|
+
The DiffMapper utility provides intelligent object comparison with type coercion, whitespace handling, and advanced diff detection. Built specifically for configurator state management and general object comparison needs.
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **Smart Type Coercion**: Handles string/number/boolean conversions intelligently
|
|
24
|
+
- **Whitespace Handling**: Automatically trims and compares string values
|
|
25
|
+
- **Deep Comparison**: Recursively compares nested objects and arrays
|
|
26
|
+
- **Performance Optimized**: Includes caching and circular reference detection
|
|
27
|
+
- **Detailed Diff Output**: Provides granular change information
|
|
28
|
+
- **Type Safety**: Full TypeScript support with proper type definitions
|
|
29
|
+
|
|
30
|
+
## API Reference
|
|
31
|
+
|
|
32
|
+
### Core Functions
|
|
33
|
+
|
|
34
|
+
#### \`hasChangesViaDiff<T>(current: T, updated: T): boolean\`
|
|
35
|
+
|
|
36
|
+
Determines if there are any meaningful changes between two objects.
|
|
37
|
+
|
|
38
|
+
\`\`\`typescript
|
|
39
|
+
const config1 = { theme: 'dark', notifications: true };
|
|
40
|
+
const config2 = { theme: 'light', notifications: true };
|
|
41
|
+
|
|
42
|
+
const hasChanges = hasChangesViaDiff(config1, config2); // true
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
**Features:**
|
|
46
|
+
- Automatic caching for performance
|
|
47
|
+
- Type coercion handling
|
|
48
|
+
- Whitespace normalization
|
|
49
|
+
- Deep nested comparison
|
|
50
|
+
|
|
51
|
+
#### \`getDetailedDiff<T>(current: T, updated: T): DiffResult | DiffMap\`
|
|
52
|
+
|
|
53
|
+
Returns a detailed diff map showing exactly what changed.
|
|
54
|
+
|
|
55
|
+
\`\`\`typescript
|
|
56
|
+
const user1 = {
|
|
57
|
+
profile: { name: 'John', age: 30 },
|
|
58
|
+
settings: { theme: 'dark' }
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const user2 = {
|
|
62
|
+
profile: { name: 'John', age: 31 },
|
|
63
|
+
settings: { theme: 'light', notifications: true }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const diff = getDetailedDiff(user1, user2);
|
|
67
|
+
// Returns detailed diff structure showing exactly what changed
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
### Diff Types
|
|
71
|
+
|
|
72
|
+
\`\`\`typescript
|
|
73
|
+
enum DiffType {
|
|
74
|
+
UNCHANGED = 'UNCHANGED', // Value is identical
|
|
75
|
+
UPDATED = 'UPDATED', // Value was modified
|
|
76
|
+
CREATED = 'CREATED', // Property was added
|
|
77
|
+
DELETED = 'DELETED' // Property was removed
|
|
78
|
+
}
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
## Type Coercion Examples
|
|
82
|
+
|
|
83
|
+
The diff mapper intelligently handles type coercion:
|
|
84
|
+
|
|
85
|
+
### String/Number Coercion
|
|
86
|
+
\`\`\`typescript
|
|
87
|
+
// These are considered UNCHANGED
|
|
88
|
+
hasChangesViaDiff({ value: '42' }, { value: 42 }); // false
|
|
89
|
+
hasChangesViaDiff({ value: '0' }, { value: 0 }); // false
|
|
90
|
+
hasChangesViaDiff({ value: '' }, { value: 0 }); // false
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
### String/Boolean Coercion
|
|
94
|
+
\`\`\`typescript
|
|
95
|
+
// These are considered UNCHANGED
|
|
96
|
+
hasChangesViaDiff({ active: 'true' }, { active: true }); // false
|
|
97
|
+
hasChangesViaDiff({ active: 'false' }, { active: false }); // false
|
|
98
|
+
hasChangesViaDiff({ active: '' }, { active: false }); // false
|
|
99
|
+
\`\`\`
|
|
100
|
+
|
|
101
|
+
### Whitespace Handling
|
|
102
|
+
\`\`\`typescript
|
|
103
|
+
// These are considered UNCHANGED
|
|
104
|
+
hasChangesViaDiff({ name: 'John' }, { name: ' John ' }); // false
|
|
105
|
+
hasChangesViaDiff({ value: '42' }, { value: ' 42 ' }); // false
|
|
106
|
+
|
|
107
|
+
// But these are UPDATED (whitespace-only strings ≠ numbers)
|
|
108
|
+
hasChangesViaDiff({ value: ' ' }, { value: 0 }); // true
|
|
109
|
+
hasChangesViaDiff({ value: ' ' }, { value: false }); // true
|
|
110
|
+
\`\`\`
|
|
111
|
+
|
|
112
|
+
## Performance Features
|
|
113
|
+
|
|
114
|
+
### Caching Strategy
|
|
115
|
+
Results are cached for 1 second to avoid expensive re-computation:
|
|
116
|
+
|
|
117
|
+
\`\`\`typescript
|
|
118
|
+
const cacheKey = JSON.stringify([current, updated]);
|
|
119
|
+
const cached = diffCache.get(cacheKey);
|
|
120
|
+
|
|
121
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
122
|
+
return cached.result;
|
|
123
|
+
}
|
|
124
|
+
\`\`\`
|
|
125
|
+
|
|
126
|
+
### Memory Management
|
|
127
|
+
Automatic cache cleanup when size exceeds 100 entries or TTL expires.
|
|
128
|
+
|
|
129
|
+
## GlobalProvider Integration
|
|
130
|
+
|
|
131
|
+
The diff mapper is seamlessly integrated with the GlobalProvider system:
|
|
132
|
+
|
|
133
|
+
\`\`\`typescript
|
|
134
|
+
import { useConfiguratorContext } from '../../providers';
|
|
135
|
+
|
|
136
|
+
const configurator = useConfiguratorContext<MyConfigType>();
|
|
137
|
+
|
|
138
|
+
// Automatic change detection using diff mapper
|
|
139
|
+
const hasChanged = configurator.hasChanged; // Uses hasChangesViaDiff internally
|
|
140
|
+
|
|
141
|
+
// Manual comparison
|
|
142
|
+
const currentConfig = configurator.configurator;
|
|
143
|
+
const cachedConfig = configurator.configuratorCache;
|
|
144
|
+
const changes = getDetailedDiff(currentConfig, cachedConfig);
|
|
145
|
+
\`\`\`
|
|
146
|
+
|
|
147
|
+
## Usage with GlobalProvider
|
|
148
|
+
|
|
149
|
+
For manual configurator state management:
|
|
150
|
+
|
|
151
|
+
\`\`\`typescript
|
|
152
|
+
import {
|
|
153
|
+
createDefaultConfiguratorState,
|
|
154
|
+
hasConfiguratorChanged,
|
|
155
|
+
validateWatchOptions,
|
|
156
|
+
extractKeys,
|
|
157
|
+
createDebouncedUpdate
|
|
158
|
+
} from '../../providers';
|
|
159
|
+
|
|
160
|
+
// Create default configurator state
|
|
161
|
+
const defaultState = createDefaultConfiguratorState<MyConfigType>();
|
|
162
|
+
|
|
163
|
+
// Manual change detection with watch options
|
|
164
|
+
const watchOptions = { watchKeys: ['theme'], debounceMs: 100 };
|
|
165
|
+
const hasChanged = hasConfiguratorChanged(current, cached, watchOptions);
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
Click the buttons in the interactive demo below to see these features in action!
|
|
169
|
+
`
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
tags: ['autodocs']
|
|
174
|
+
};
|
|
175
|
+
export default meta;
|
|
176
|
+
export const Interactive = {
|
|
177
|
+
name: 'Interactive Demo',
|
|
178
|
+
parameters: {
|
|
179
|
+
docs: {
|
|
180
|
+
description: {
|
|
181
|
+
story: 'Click the buttons to run diff examples and see results. All functionality is combined in a single interactive demo.'
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|