@finsweet/webflow-apps-utils 1.0.3 → 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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- 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.d.ts +6 -0
- package/dist/router/Router.stories.js +564 -0
- package/dist/router/examples/RouterExample.svelte +271 -0
- package/dist/router/examples/RouterExample.svelte.d.ts +18 -0
- package/dist/router/examples/index.d.ts +4 -0
- package/dist/router/examples/index.js +4 -0
- package/dist/router/examples/pages/AboutPage.svelte +568 -0
- package/dist/router/examples/pages/AboutPage.svelte.d.ts +13 -0
- package/dist/router/examples/pages/HomePage.svelte +200 -0
- package/dist/router/examples/pages/HomePage.svelte.d.ts +14 -0
- package/dist/router/examples/pages/NotFoundPage.svelte +307 -0
- package/dist/router/examples/pages/NotFoundPage.svelte.d.ts +17 -0
- package/dist/router/hooks.svelte.d.ts +2 -2
- package/dist/router/index.d.ts +3 -0
- package/dist/router/index.js +3 -0
- package/dist/router/{Link.svelte → providers/Link.svelte} +1 -1
- package/dist/router/{Route.svelte → providers/Route.svelte} +1 -1
- package/dist/router/{Route.svelte.d.ts → providers/Route.svelte.d.ts} +1 -1
- package/dist/router/{Router.svelte → providers/RouterProvider.svelte} +22 -5
- package/dist/router/{Router.svelte.d.ts → providers/RouterProvider.svelte.d.ts} +8 -4
- package/dist/router/providers/index.d.ts +3 -0
- package/dist/router/providers/index.js +3 -0
- package/dist/router/{index.svelte.d.ts → router.svelte.d.ts} +1 -3
- package/dist/router/{index.svelte.js → router.svelte.js} +1 -4
- 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/stores/forms.d.ts +41 -4
- package/dist/stores/forms.js +86 -32
- package/dist/types/customCode.d.ts +1 -1
- package/dist/types/window.d.ts +1 -0
- package/dist/ui/components/button/Button.svelte +1 -1
- package/dist/ui/components/copy-text/CopyText.stories.d.ts +70 -0
- package/dist/ui/components/copy-text/CopyText.stories.js +241 -0
- package/dist/ui/components/copy-text/CopyText.svelte +247 -0
- package/dist/ui/components/copy-text/CopyText.svelte.d.ts +4 -0
- package/dist/ui/components/copy-text/index.d.ts +2 -0
- package/dist/ui/components/copy-text/index.js +1 -0
- package/dist/ui/components/copy-text/types.d.ts +52 -0
- package/dist/ui/components/copy-text/types.js +1 -0
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/input/Input.stories.d.ts +9 -0
- package/dist/ui/components/input/Input.stories.js +78 -0
- package/dist/ui/components/input/Input.svelte +39 -3
- package/dist/ui/components/input/types.d.ts +6 -0
- package/dist/ui/components/layout/Layout.svelte +45 -64
- package/dist/ui/components/layout/Layout.svelte.d.ts +26 -3
- package/dist/ui/components/layout/examples/ExampleLayout.svelte +32 -27
- package/dist/ui/components/layout/index.d.ts +1 -1
- package/dist/ui/components/layout/test-helpers/TestLayoutWithFooter.svelte +20 -0
- package/dist/ui/components/layout/test-helpers/TestLayoutWithFooter.svelte.d.ts +7 -0
- package/dist/ui/components/layout/types.d.ts +1 -10
- package/dist/ui/components/notification/Notification.stories.svelte +12 -1
- package/dist/ui/components/notification/Notification.svelte +10 -5
- package/dist/ui/components/notification/Notification.svelte.d.ts +1 -1
- package/dist/ui/components/notification/types.d.ts +1 -1
- package/dist/ui/components/section/Section.svelte +8 -4
- package/dist/ui/components/section/types.d.ts +8 -0
- package/dist/ui/components/text/Text.stories.svelte +67 -1
- package/dist/ui/components/text/Text.svelte +209 -8
- package/dist/ui/components/text/types.d.ts +4 -0
- package/dist/ui/index.css +6 -2
- package/dist/utils/animations/factory.d.ts +7 -0
- package/dist/utils/animations/factory.js +101 -0
- package/dist/utils/animations/index.d.ts +7 -0
- package/dist/utils/animations/index.js +62 -0
- package/dist/utils/animations/types.d.ts +39 -0
- package/dist/utils/animations/types.js +1 -0
- package/dist/utils/custom-code/configs.d.ts +22 -0
- package/dist/utils/custom-code/configs.js +40 -0
- package/dist/utils/custom-code/index.d.ts +1 -0
- package/dist/utils/custom-code/index.js +1 -0
- 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/helpers/capitalizeFirstLetter.d.ts +4 -0
- package/dist/utils/helpers/capitalizeFirstLetter.js +9 -0
- package/dist/utils/helpers/getTimeNow.d.ts +4 -0
- package/dist/utils/helpers/getTimeNow.js +8 -0
- package/dist/utils/helpers/index.d.ts +4 -0
- package/dist/utils/helpers/index.js +4 -0
- package/dist/utils/helpers/minifyCode.d.ts +10 -0
- package/dist/utils/helpers/minifyCode.js +73 -0
- package/dist/utils/helpers/objectsToModuleExports.d.ts +1 -1
- package/dist/utils/helpers/objectsToModuleExports.js +1 -0
- package/dist/utils/helpers/toHumanReadableList.d.ts +4 -0
- package/dist/utils/helpers/toHumanReadableList.js +11 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/webflow-canvas/getAllChildren.d.ts +16 -0
- package/dist/utils/webflow-canvas/getAllChildren.js +65 -0
- package/dist/utils/webflow-canvas/getElementClassList.d.ts +9 -0
- package/dist/utils/webflow-canvas/getElementClassList.js +19 -0
- package/dist/utils/webflow-canvas/index.d.ts +2 -0
- package/dist/utils/webflow-canvas/index.js +2 -0
- package/package.json +6 -1
- package/dist/providers/GlobalProvider.mdx +0 -322
- package/dist/router/README.md +0 -397
- /package/dist/router/{Link.svelte.d.ts → providers/Link.svelte.d.ts} +0 -0
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import GlobalProviderDemo from './GlobalProviderDemo.svelte';
|
|
2
|
+
const meta = {
|
|
3
|
+
title: 'Utils/GlobalProvider',
|
|
4
|
+
component: GlobalProviderDemo,
|
|
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
|
+
# GlobalProvider
|
|
18
|
+
|
|
19
|
+
The \`GlobalProvider\` is a comprehensive context management system built on Svelte 5 that manages multiple application contexts in a type-safe and reactive way. It includes advanced configurator state management with intelligent change detection using the DiffMapper utilities.
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **Multiple Contexts**: Manage different types of state (form, app, data, etc.) in separate contexts
|
|
24
|
+
- **Type Safety**: Full TypeScript support with branded types and generics
|
|
25
|
+
- **Reactive**: Built on Svelte 5 runes for optimal reactivity
|
|
26
|
+
- **Event System**: Subscribe to context changes and global events (batched for performance)
|
|
27
|
+
- **Intelligent Change Detection**: Uses DiffMapper for smart configurator state comparison
|
|
28
|
+
- **Performance Optimized**: Includes caching, debouncing, and memory management
|
|
29
|
+
- **Advanced Configurator Support**: Built-in configurator state with watch options and change tracking
|
|
30
|
+
|
|
31
|
+
## Basic Usage
|
|
32
|
+
|
|
33
|
+
### Wrap Your App
|
|
34
|
+
|
|
35
|
+
\`\`\`svelte
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
import { GlobalProvider } from './';
|
|
38
|
+
|
|
39
|
+
const initialContexts = {
|
|
40
|
+
app: {
|
|
41
|
+
editMode: false,
|
|
42
|
+
repairMode: false,
|
|
43
|
+
title: 'My App'
|
|
44
|
+
},
|
|
45
|
+
form: {
|
|
46
|
+
formKey: null,
|
|
47
|
+
formUpdateKey: null
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<GlobalProvider {initialContexts} debug={true}>
|
|
53
|
+
<App />
|
|
54
|
+
</GlobalProvider>
|
|
55
|
+
\`\`\`
|
|
56
|
+
|
|
57
|
+
### Default Contexts
|
|
58
|
+
|
|
59
|
+
The \`GlobalProvider\` automatically creates these default contexts:
|
|
60
|
+
|
|
61
|
+
- **\`app\`**: Application state (editMode, repairMode, title, configurator)
|
|
62
|
+
- **\`form\`**: Form state (formKey, formUpdateKey)
|
|
63
|
+
- **\`data\`**: General data state
|
|
64
|
+
|
|
65
|
+
### Use Contexts in Components
|
|
66
|
+
|
|
67
|
+
\`\`\`svelte
|
|
68
|
+
<script lang="ts">
|
|
69
|
+
import { useAppContext, useFormContext, useDataContext } from './';
|
|
70
|
+
|
|
71
|
+
const appContext = useAppContext();
|
|
72
|
+
const formContext = useFormContext();
|
|
73
|
+
const dataContext = useDataContext();
|
|
74
|
+
|
|
75
|
+
let appData = $derived(appContext.get());
|
|
76
|
+
let formData = $derived(formContext.get());
|
|
77
|
+
|
|
78
|
+
function toggleEditMode() {
|
|
79
|
+
appContext.update((current) => ({
|
|
80
|
+
...current,
|
|
81
|
+
editMode: !current?.editMode
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<div>
|
|
87
|
+
<p>Edit Mode: {appData?.editMode}</p>
|
|
88
|
+
<p>Form Key: {formData?.formKey}</p>
|
|
89
|
+
<button onclick={toggleEditMode}>Toggle Edit Mode</button>
|
|
90
|
+
</div>
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### Context Operations
|
|
96
|
+
|
|
97
|
+
#### \`get(): T | null\`
|
|
98
|
+
Returns the current context data. Returns \`undefined\` if the context has been reset (completely removed).
|
|
99
|
+
|
|
100
|
+
#### \`set(data: Partial<T>): void\`
|
|
101
|
+
Sets context data (merges with existing data).
|
|
102
|
+
|
|
103
|
+
#### \`update(updater: (current: T | null) => T): void\`
|
|
104
|
+
Updates context data using an updater function.
|
|
105
|
+
|
|
106
|
+
#### \`clear(): void\`
|
|
107
|
+
Clears context data (sets to null). The context remains active but with null data.
|
|
108
|
+
|
|
109
|
+
#### \`reset(): void\`
|
|
110
|
+
Completely removes the context. After reset, \`hasContext()\` returns false and \`get()\` returns \`undefined\`.
|
|
111
|
+
|
|
112
|
+
#### \`subscribe(callback: (data: T | null) => void): () => void\`
|
|
113
|
+
Subscribes to context changes. Returns unsubscribe function. **Note**: Events are batched and emitted asynchronously for performance.
|
|
114
|
+
|
|
115
|
+
### Global Operations
|
|
116
|
+
|
|
117
|
+
- \`getContext<T>(key: string): ContextOperations<T>\` - Get context operations for a key
|
|
118
|
+
- \`hasContext(key: string): boolean\` - Check if context exists
|
|
119
|
+
- \`removeContext(key: string): void\` - Remove a specific context
|
|
120
|
+
- \`clearAll(): void\` - Clear all context data (set to null)
|
|
121
|
+
- \`resetAll(): void\` - Reset all contexts (completely remove them)
|
|
122
|
+
- \`resetByKey(key: string): void\` - Reset a specific context by key
|
|
123
|
+
- \`getActiveContexts(): string[]\` - Get list of active context keys
|
|
124
|
+
- \`getAllContexts(): Record<string, unknown>\` - Get all context data
|
|
125
|
+
- \`getContextMetadata(key: string)\` - Get metadata (version, updatedAt, isActive) for a context
|
|
126
|
+
- \`subscribe(callback): () => void\` - Subscribe to global context events
|
|
127
|
+
|
|
128
|
+
## Advanced Configurator Support
|
|
129
|
+
|
|
130
|
+
The \`GlobalProvider\` includes sophisticated configurator state management with intelligent change detection powered by the DiffMapper utilities. The configurator system automatically detects changes using smart type coercion, whitespace handling, and performance optimization.
|
|
131
|
+
|
|
132
|
+
### Core Configurator Features
|
|
133
|
+
|
|
134
|
+
- **Automatic Change Detection**: Uses \`hasChangesViaDiff\` internally for intelligent comparison
|
|
135
|
+
- **Type Coercion**: Handles string/number/boolean conversions automatically
|
|
136
|
+
- **Watch Options**: Configure which keys to monitor for changes
|
|
137
|
+
- **Debounced Updates**: Performance-optimized change detection with configurable debouncing
|
|
138
|
+
- **Caching**: Automatic caching for expensive diff operations
|
|
139
|
+
|
|
140
|
+
### Using the Configurator Context
|
|
141
|
+
|
|
142
|
+
\`\`\`svelte
|
|
143
|
+
<script lang="ts">
|
|
144
|
+
import { useConfiguratorContext, useAppContext } from './';
|
|
145
|
+
|
|
146
|
+
// Define your configurator type
|
|
147
|
+
type MyConfiguratorType = {
|
|
148
|
+
theme: 'light' | 'dark';
|
|
149
|
+
layout: 'grid' | 'list';
|
|
150
|
+
itemsPerPage: number;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Use typed configurator context
|
|
154
|
+
const configurator = useConfiguratorContext<MyConfiguratorType>();
|
|
155
|
+
|
|
156
|
+
// Or use typed app context
|
|
157
|
+
const appContext = useAppContext<MyConfiguratorType>();
|
|
158
|
+
|
|
159
|
+
// Set configurator data with watch options (fully typed)
|
|
160
|
+
configurator.setConfigurator(
|
|
161
|
+
{ theme: 'dark', layout: 'grid', itemsPerPage: 10 },
|
|
162
|
+
{ watchKeys: ['theme'], debounceMs: 100 }
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Check if configurator has changed
|
|
166
|
+
let hasChanged = $derived(configurator.hasChanged);
|
|
167
|
+
let currentConfig = $derived(configurator.configurator); // Type: MyConfiguratorType | null
|
|
168
|
+
let cachedConfig = $derived(configurator.configuratorCache); // Type: MyConfiguratorType | null
|
|
169
|
+
|
|
170
|
+
// Save current state to cache
|
|
171
|
+
function saveToCache() {
|
|
172
|
+
configurator.saveToCache();
|
|
173
|
+
}
|
|
174
|
+
</script>
|
|
175
|
+
|
|
176
|
+
<div>
|
|
177
|
+
<p>Has Changed: {hasChanged}</p>
|
|
178
|
+
<p>Current Theme: {currentConfig?.theme}</p>
|
|
179
|
+
<p>Cached Theme: {cachedConfig?.theme}</p>
|
|
180
|
+
<button onclick={saveToCache}>Save to Cache</button>
|
|
181
|
+
</div>
|
|
182
|
+
\`\`\`
|
|
183
|
+
|
|
184
|
+
### Configurator API
|
|
185
|
+
|
|
186
|
+
- \`configurator\` - Current configurator data
|
|
187
|
+
- \`configuratorCache\` - Cached configurator data
|
|
188
|
+
- \`hasChanged\` - Boolean indicating if configurator differs from cache
|
|
189
|
+
- \`watchOptions\` - Current watch configuration
|
|
190
|
+
- \`setConfigurator(data, watchOptions?)\` - Set configurator data
|
|
191
|
+
- \`setConfiguratorCache(data)\` - Set cache data
|
|
192
|
+
- \`saveToCache()\` - Save current configurator to cache
|
|
193
|
+
- \`updateWatchOptions(options)\` - Update watch configuration
|
|
194
|
+
|
|
195
|
+
### Watch Options
|
|
196
|
+
|
|
197
|
+
\`\`\`typescript
|
|
198
|
+
interface ConfiguratorWatchOptions {
|
|
199
|
+
watchAll?: boolean; // Watch all keys (default: true)
|
|
200
|
+
watchKeys?: string[]; // Specific keys to watch
|
|
201
|
+
debounceMs?: number; // Debounce delay (default: 50ms, min: 16ms)
|
|
202
|
+
}
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
### Change Detection Examples
|
|
206
|
+
|
|
207
|
+
The configurator uses intelligent change detection that handles various edge cases:
|
|
208
|
+
|
|
209
|
+
\`\`\`typescript
|
|
210
|
+
// Type coercion examples - these are considered UNCHANGED
|
|
211
|
+
const config1 = { count: '5', active: 'true', value: '' };
|
|
212
|
+
const config2 = { count: 5, active: true, value: 0 };
|
|
213
|
+
|
|
214
|
+
configurator.setConfigurator(config1);
|
|
215
|
+
configurator.setConfiguratorCache(config2);
|
|
216
|
+
console.log(configurator.hasChanged); // false - intelligent type coercion
|
|
217
|
+
|
|
218
|
+
// Whitespace handling - these are considered UNCHANGED
|
|
219
|
+
const config3 = { name: 'John' };
|
|
220
|
+
const config4 = { name: ' John ' };
|
|
221
|
+
|
|
222
|
+
// Nested object changes - detected intelligently
|
|
223
|
+
const config5 = { user: { name: 'John', age: 30 }, settings: { theme: 'dark' } };
|
|
224
|
+
const config6 = { user: { name: 'John', age: 31 }, settings: { theme: 'dark' } };
|
|
225
|
+
// Only user.age is different, hasChanged will be true
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
## TypeScript Support
|
|
229
|
+
|
|
230
|
+
### Generic Context Usage
|
|
231
|
+
|
|
232
|
+
\`\`\`typescript
|
|
233
|
+
import type { ContextOperations, AppContextData, DataContextData } from './';
|
|
234
|
+
|
|
235
|
+
// For custom contexts
|
|
236
|
+
const userContext: ContextOperations<UserType> = useContext<UserType>('user');
|
|
237
|
+
|
|
238
|
+
// For typed app context with configurator
|
|
239
|
+
type MyConfiguratorType = {
|
|
240
|
+
theme: 'light' | 'dark';
|
|
241
|
+
layout: 'grid' | 'list';
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const appContext = useAppContext<MyConfiguratorType>();
|
|
245
|
+
const configurator = useConfiguratorContext<MyConfiguratorType>();
|
|
246
|
+
|
|
247
|
+
// For typed data context
|
|
248
|
+
type MyDataType = {
|
|
249
|
+
users: User[];
|
|
250
|
+
products: Product[];
|
|
251
|
+
currentPage: number;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const dataContext = useDataContext<MyDataType>();
|
|
255
|
+
|
|
256
|
+
// The configurator and configuratorCache will both be typed as MyConfiguratorType | null
|
|
257
|
+
// The data context state will be typed as MyDataType | null
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
## Utility Helper Functions
|
|
261
|
+
|
|
262
|
+
The GlobalProvider system includes several powerful utility functions for advanced use cases and manual comparisons.
|
|
263
|
+
|
|
264
|
+
### DiffMapper Utilities
|
|
265
|
+
|
|
266
|
+
For detailed object comparison and change analysis:
|
|
267
|
+
|
|
268
|
+
\`\`\`typescript
|
|
269
|
+
import { hasChangesViaDiff, getDetailedDiff, compareKeys } from './';
|
|
270
|
+
|
|
271
|
+
// Quick change detection
|
|
272
|
+
const hasChanges = hasChangesViaDiff(oldConfig, newConfig);
|
|
273
|
+
|
|
274
|
+
// Detailed diff analysis
|
|
275
|
+
const diff = getDetailedDiff(oldConfig, newConfig);
|
|
276
|
+
console.log(diff);
|
|
277
|
+
// Returns detailed DiffMap showing exactly what changed
|
|
278
|
+
|
|
279
|
+
// Compare specific keys only
|
|
280
|
+
const hasKeyChanges = !compareKeys(oldConfig, newConfig, ['theme', 'layout']);
|
|
281
|
+
\`\`\`
|
|
282
|
+
|
|
283
|
+
### Configurator Utilities
|
|
284
|
+
|
|
285
|
+
For manual configurator state management:
|
|
286
|
+
|
|
287
|
+
\`\`\`typescript
|
|
288
|
+
import {
|
|
289
|
+
createDefaultConfiguratorState,
|
|
290
|
+
hasConfiguratorChanged,
|
|
291
|
+
validateWatchOptions,
|
|
292
|
+
extractKeys,
|
|
293
|
+
createDebouncedUpdate
|
|
294
|
+
} from './';
|
|
295
|
+
|
|
296
|
+
// Create default configurator state
|
|
297
|
+
const defaultState = createDefaultConfiguratorState<MyConfigType>();
|
|
298
|
+
|
|
299
|
+
// Manual change detection with watch options
|
|
300
|
+
const watchOptions = { watchKeys: ['theme'], debounceMs: 100 };
|
|
301
|
+
const hasChanged = hasConfiguratorChanged(current, cached, watchOptions);
|
|
302
|
+
|
|
303
|
+
// Validate and normalize watch options
|
|
304
|
+
const validatedOptions = validateWatchOptions(userOptions);
|
|
305
|
+
|
|
306
|
+
// Extract specific keys from configuration
|
|
307
|
+
const extractedConfig = extractKeys(fullConfig, ['theme', 'layout']);
|
|
308
|
+
|
|
309
|
+
// Create debounced update function
|
|
310
|
+
const debouncedSave = createDebouncedUpdate(saveConfig, 300);
|
|
311
|
+
\`\`\`
|
|
312
|
+
|
|
313
|
+
### Performance Optimization Helpers
|
|
314
|
+
|
|
315
|
+
\`\`\`typescript
|
|
316
|
+
import { createDebouncedUpdate } from './';
|
|
317
|
+
|
|
318
|
+
// Create debounced functions for expensive operations
|
|
319
|
+
const debouncedValidation = createDebouncedUpdate((config) => {
|
|
320
|
+
// Expensive validation logic
|
|
321
|
+
validateConfiguration(config);
|
|
322
|
+
}, 500); // 500ms debounce
|
|
323
|
+
|
|
324
|
+
// Use in components
|
|
325
|
+
function handleConfigChange(newConfig) {
|
|
326
|
+
setConfig(newConfig);
|
|
327
|
+
debouncedValidation(newConfig);
|
|
328
|
+
}
|
|
329
|
+
\`\`\`
|
|
330
|
+
|
|
331
|
+
### Memory Management
|
|
332
|
+
|
|
333
|
+
The GlobalProvider includes automatic memory management for performance:
|
|
334
|
+
|
|
335
|
+
\`\`\`typescript
|
|
336
|
+
// Automatic cache cleanup in diff operations
|
|
337
|
+
// Cache entries older than 1 second are automatically removed
|
|
338
|
+
// Cache size is limited to 100 entries
|
|
339
|
+
|
|
340
|
+
// Event listener cleanup
|
|
341
|
+
const unsubscribe = globalContext.subscribe((event) => {
|
|
342
|
+
console.log('Context changed:', event);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Always clean up subscriptions
|
|
346
|
+
onDestroy(() => {
|
|
347
|
+
unsubscribe();
|
|
348
|
+
});
|
|
349
|
+
\`\`\`
|
|
350
|
+
|
|
351
|
+
### Debugging and Development
|
|
352
|
+
|
|
353
|
+
For development and debugging:
|
|
354
|
+
|
|
355
|
+
\`\`\`typescript
|
|
356
|
+
// Enable debug mode in GlobalProvider
|
|
357
|
+
<GlobalProvider debug={true} initialContexts={...}>
|
|
358
|
+
|
|
359
|
+
// Get context metadata
|
|
360
|
+
const metadata = globalContext.getContextMetadata('app');
|
|
361
|
+
console.log(metadata);
|
|
362
|
+
// { updatedAt: 1234567890, version: 5, isActive: true }
|
|
363
|
+
|
|
364
|
+
// Get all active contexts
|
|
365
|
+
const activeContexts = globalContext.getActiveContexts();
|
|
366
|
+
console.log(activeContexts); // ['app', 'form', 'data', 'userPreferences']
|
|
367
|
+
|
|
368
|
+
// Get complete state snapshot
|
|
369
|
+
const allData = globalContext.getAllContexts();
|
|
370
|
+
console.log(allData); // Complete state object
|
|
371
|
+
\`\`\`
|
|
372
|
+
|
|
373
|
+
### Error Handling Best Practices
|
|
374
|
+
|
|
375
|
+
\`\`\`typescript
|
|
376
|
+
try {
|
|
377
|
+
const context = getGlobalContext();
|
|
378
|
+
const appData = context.getContext('app').get();
|
|
379
|
+
// Handle operations...
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (error.message.includes('Global context not found')) {
|
|
382
|
+
console.error('GlobalProvider not found. Make sure to wrap your app properly.');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Safe context access with fallbacks
|
|
387
|
+
const safeGetContext = <T>(key: string): T | null => {
|
|
388
|
+
try {
|
|
389
|
+
const context = getGlobalContext();
|
|
390
|
+
return context.getContext<T>(key).get();
|
|
391
|
+
} catch {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
\`\`\`
|
|
396
|
+
|
|
397
|
+
## Related Documentation
|
|
398
|
+
|
|
399
|
+
- **Performance Considerations**: All diff operations include automatic caching and optimization
|
|
400
|
+
- **Type Safety**: Full TypeScript support with branded types and generics
|
|
401
|
+
|
|
402
|
+
Click the buttons in the interactive demo below to see these features in action!
|
|
403
|
+
`
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
tags: ['autodocs']
|
|
408
|
+
};
|
|
409
|
+
export default meta;
|
|
410
|
+
export const Interactive = {
|
|
411
|
+
name: 'Interactive Demo',
|
|
412
|
+
parameters: {
|
|
413
|
+
docs: {
|
|
414
|
+
description: {
|
|
415
|
+
story: 'Interactive demo showing GlobalProvider features including context management, configurator state, and change detection.'
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
|
|
4
|
+
import { GlobalProvider, useAppContext, useConfiguratorContext, useFormContext } from './index';
|
|
5
|
+
|
|
6
|
+
// Demo configurator type
|
|
7
|
+
type DemoConfigType = {
|
|
8
|
+
theme: 'light' | 'dark';
|
|
9
|
+
layout: 'grid' | 'list';
|
|
10
|
+
itemsPerPage: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const initialContexts = {
|
|
14
|
+
app: {
|
|
15
|
+
editMode: false,
|
|
16
|
+
repairMode: false,
|
|
17
|
+
title: 'GlobalProvider Demo',
|
|
18
|
+
configurator: {
|
|
19
|
+
configurator: { theme: 'light', layout: 'grid', itemsPerPage: 10 },
|
|
20
|
+
configuratorCache: { theme: 'light', layout: 'grid', itemsPerPage: 10 },
|
|
21
|
+
hasChanged: false,
|
|
22
|
+
watchOptions: { watchAll: true, watchKeys: [], debounceMs: 100 }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
form: {
|
|
26
|
+
formKey: 'demo-form',
|
|
27
|
+
formUpdateKey: null
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let demoStarted = $state(false);
|
|
32
|
+
|
|
33
|
+
function startDemo() {
|
|
34
|
+
demoStarted = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Simple demo state management
|
|
38
|
+
let refreshTrigger = $state(0);
|
|
39
|
+
|
|
40
|
+
function forceRefresh() {
|
|
41
|
+
refreshTrigger++;
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div class="demo-container">
|
|
46
|
+
{#if !demoStarted}
|
|
47
|
+
<div class="start-demo">
|
|
48
|
+
<h3>GlobalProvider Interactive Demo</h3>
|
|
49
|
+
<p>Click below to start the demo and explore GlobalProvider features.</p>
|
|
50
|
+
<button onclick={startDemo} class="btn btn--primary">Start Demo</button>
|
|
51
|
+
</div>
|
|
52
|
+
{:else}
|
|
53
|
+
<GlobalProvider {initialContexts} debug={false}>
|
|
54
|
+
{@render DemoContent()}
|
|
55
|
+
</GlobalProvider>
|
|
56
|
+
{/if}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{#snippet DemoContent()}
|
|
60
|
+
<!-- Get contexts and data reactively -->
|
|
61
|
+
{@const appContext = useAppContext<DemoConfigType>()}
|
|
62
|
+
{@const formContext = useFormContext()}
|
|
63
|
+
{@const configuratorContext = useConfiguratorContext<DemoConfigType>()}
|
|
64
|
+
|
|
65
|
+
<!-- Force reactive updates with trigger -->
|
|
66
|
+
{#key refreshTrigger}
|
|
67
|
+
{@const appData = appContext.get()}
|
|
68
|
+
{@const formData = formContext.get()}
|
|
69
|
+
{@const configuratorData = configuratorContext.configurator}
|
|
70
|
+
{@const hasChanged = configuratorContext.hasChanged}
|
|
71
|
+
|
|
72
|
+
<div class="demo-content">
|
|
73
|
+
<h3>GlobalProvider Demo</h3>
|
|
74
|
+
|
|
75
|
+
<div class="context-section">
|
|
76
|
+
<h4>App Context</h4>
|
|
77
|
+
<p>Edit Mode: <strong>{appData?.editMode ? 'ON' : 'OFF'}</strong></p>
|
|
78
|
+
<p>Title: <strong>{appData?.title || 'N/A'}</strong></p>
|
|
79
|
+
<button
|
|
80
|
+
onclick={() => {
|
|
81
|
+
const current = appContext.get();
|
|
82
|
+
appContext.set({
|
|
83
|
+
...current,
|
|
84
|
+
editMode: !current?.editMode
|
|
85
|
+
});
|
|
86
|
+
forceRefresh();
|
|
87
|
+
}}
|
|
88
|
+
class="btn btn--secondary"
|
|
89
|
+
>
|
|
90
|
+
Toggle Edit Mode
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="context-section">
|
|
95
|
+
<h4>Form Context</h4>
|
|
96
|
+
<p>Form Key: <strong>{formData?.formKey || 'N/A'}</strong></p>
|
|
97
|
+
<button
|
|
98
|
+
onclick={() => {
|
|
99
|
+
const current = formContext.get();
|
|
100
|
+
formContext.set({
|
|
101
|
+
...current,
|
|
102
|
+
formKey: `form-${Date.now()}`
|
|
103
|
+
});
|
|
104
|
+
forceRefresh();
|
|
105
|
+
}}
|
|
106
|
+
class="btn btn--secondary"
|
|
107
|
+
>
|
|
108
|
+
Update Form Key
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div class="context-section">
|
|
113
|
+
<h4>Configurator Context</h4>
|
|
114
|
+
<p>Theme: <strong>{configuratorData?.theme || 'N/A'}</strong></p>
|
|
115
|
+
<p>Layout: <strong>{configuratorData?.layout || 'N/A'}</strong></p>
|
|
116
|
+
<p>Items Per Page: <strong>{configuratorData?.itemsPerPage || 'N/A'}</strong></p>
|
|
117
|
+
<p>
|
|
118
|
+
Has Changed: <strong class="status-{hasChanged}">{hasChanged ? 'YES' : 'NO'}</strong>
|
|
119
|
+
</p>
|
|
120
|
+
|
|
121
|
+
<div class="button-group">
|
|
122
|
+
<button
|
|
123
|
+
onclick={() => {
|
|
124
|
+
const current = configuratorContext.configurator;
|
|
125
|
+
if (current) {
|
|
126
|
+
configuratorContext.setConfigurator({
|
|
127
|
+
...current,
|
|
128
|
+
theme: current.theme === 'light' ? 'dark' : 'light'
|
|
129
|
+
});
|
|
130
|
+
forceRefresh();
|
|
131
|
+
}
|
|
132
|
+
}}
|
|
133
|
+
class="btn btn--secondary"
|
|
134
|
+
>
|
|
135
|
+
Toggle Theme
|
|
136
|
+
</button>
|
|
137
|
+
<button
|
|
138
|
+
onclick={() => {
|
|
139
|
+
configuratorContext.saveToCache();
|
|
140
|
+
forceRefresh();
|
|
141
|
+
}}
|
|
142
|
+
class="btn btn--primary"
|
|
143
|
+
>
|
|
144
|
+
Save to Cache
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div class="context-section">
|
|
150
|
+
<h4>Debug Info</h4>
|
|
151
|
+
<p>App Data: <code>{JSON.stringify(appData, null, 2)}</code></p>
|
|
152
|
+
<p>Form Data: <code>{JSON.stringify(formData, null, 2)}</code></p>
|
|
153
|
+
<p>Configurator Data: <code>{JSON.stringify(configuratorData, null, 2)}</code></p>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
{/key}
|
|
157
|
+
{/snippet}
|
|
158
|
+
|
|
159
|
+
<style>
|
|
160
|
+
.demo-container {
|
|
161
|
+
padding: 1rem;
|
|
162
|
+
max-width: 600px;
|
|
163
|
+
margin: 0 auto;
|
|
164
|
+
background: var(--background1);
|
|
165
|
+
color: var(--text1);
|
|
166
|
+
font-family: var(--font-stack);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.start-demo {
|
|
170
|
+
text-align: center;
|
|
171
|
+
padding: 2rem;
|
|
172
|
+
border: 2px dashed var(--border2);
|
|
173
|
+
border-radius: var(--border-radius);
|
|
174
|
+
background: var(--background2);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.demo-content {
|
|
178
|
+
display: flex;
|
|
179
|
+
flex-direction: column;
|
|
180
|
+
gap: 1.5rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.context-section {
|
|
184
|
+
padding: 1rem;
|
|
185
|
+
border: 1px solid var(--border2);
|
|
186
|
+
border-radius: var(--border-radius);
|
|
187
|
+
background: var(--background2);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.context-section h4 {
|
|
191
|
+
margin: 0 0 0.5rem 0;
|
|
192
|
+
color: var(--text1);
|
|
193
|
+
font-size: var(--font-size-large);
|
|
194
|
+
font-weight: var(--font-weight-medium);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.context-section p {
|
|
198
|
+
margin: 0.25rem 0;
|
|
199
|
+
font-size: var(--font-size-small);
|
|
200
|
+
color: var(--text2);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.button-group {
|
|
204
|
+
display: flex;
|
|
205
|
+
gap: 0.5rem;
|
|
206
|
+
margin-top: 0.75rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.btn {
|
|
210
|
+
padding: var(--padding-small) var(--padding-regular);
|
|
211
|
+
border: none;
|
|
212
|
+
border-radius: var(--border-radius);
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
font-size: var(--font-size-small);
|
|
215
|
+
font-weight: var(--font-weight-normal);
|
|
216
|
+
font-family: var(--font-stack);
|
|
217
|
+
transition: all 0.2s ease;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.btn--primary {
|
|
221
|
+
background: var(--actionPrimaryBackground);
|
|
222
|
+
color: var(--actionPrimaryText);
|
|
223
|
+
box-shadow: var(--boxShadows-action-colored);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.btn--primary:hover {
|
|
227
|
+
background: var(--actionPrimaryBackgroundHover);
|
|
228
|
+
color: var(--actionPrimaryTextHover);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.btn--secondary {
|
|
232
|
+
background: var(--actionSecondaryBackground);
|
|
233
|
+
color: var(--actionSecondaryText);
|
|
234
|
+
box-shadow: var(--boxShadows-action-secondary);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.btn--secondary:hover {
|
|
238
|
+
background: var(--actionSecondaryBackgroundHover);
|
|
239
|
+
color: var(--actionSecondaryTextHover);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.status-true {
|
|
243
|
+
color: var(--redText);
|
|
244
|
+
font-weight: var(--font-weight-medium);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.status-false {
|
|
248
|
+
color: var(--greenText);
|
|
249
|
+
font-weight: var(--font-weight-medium);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
code {
|
|
253
|
+
background: var(--background3);
|
|
254
|
+
border: 1px solid var(--border2);
|
|
255
|
+
padding: var(--padding-tiny);
|
|
256
|
+
border-radius: var(--border-radius);
|
|
257
|
+
font-family: monospace;
|
|
258
|
+
font-size: var(--font-size-small);
|
|
259
|
+
color: var(--text2);
|
|
260
|
+
display: block;
|
|
261
|
+
margin-top: var(--spacing-4);
|
|
262
|
+
white-space: pre-wrap;
|
|
263
|
+
max-height: 150px;
|
|
264
|
+
overflow-y: auto;
|
|
265
|
+
}
|
|
266
|
+
</style>
|