native-document 1.0.166 → 1.0.168
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/.vitepress/config.js +166 -0
- package/CHANGELOG.md +153 -0
- package/components.js +2 -1
- package/dist/native-document.components.min.js +495 -228
- package/dist/native-document.dev.js +7 -0
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/tutorials/.gitkeep +0 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/package.json +34 -13
- package/readme.md +269 -89
- package/src/components/card/Card.js +93 -39
- package/src/components/card/index.js +1 -1
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +41 -107
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +76 -59
- package/src/components/list/ListItem.js +117 -69
- package/src/components/list/index.js +3 -1
- package/src/components/list/types/ListItem.d.ts +45 -34
- package/src/components/spacer/Spacer.js +1 -1
- package/src/core/data/ObservableResource.js +5 -0
- package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- package/src/ui/index.js +8 -0
package/docs/state-management.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
---
|
|
2
|
+
title: State Management
|
|
3
|
+
description: Manage application-wide state with NativeDocument's Store system - groups, persistence, computed stores, and reactive updates
|
|
4
|
+
---
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
# State Management
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
NativeDocument's state management operates on two levels: local component state using Observables, and global application state using the Store system. The Store allows multiple components to share and react to the same state changes.
|
|
8
9
|
|
|
9
10
|
```javascript
|
|
10
11
|
import { Store, Observable } from 'native-document';
|
|
11
12
|
|
|
12
|
-
// Create global state
|
|
13
13
|
const userStore = Store.create('user', {
|
|
14
14
|
id: null,
|
|
15
15
|
name: '',
|
|
@@ -17,45 +17,34 @@ const userStore = Store.create('user', {
|
|
|
17
17
|
isLoggedIn: false
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
// Components automatically update when store changes
|
|
21
20
|
const UserGreeting = () => {
|
|
22
21
|
const user = Store.use('user');
|
|
23
|
-
|
|
24
|
-
return ShowIf(user.check(u => u.isLoggedIn),
|
|
22
|
+
return ShowIf(user.is(u => u.isLoggedIn),
|
|
25
23
|
() => Div(['Welcome back, ', user.$value.name, '!'])
|
|
26
24
|
);
|
|
27
25
|
};
|
|
28
26
|
```
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
---
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
## Store Creation
|
|
33
31
|
|
|
34
|
-
###
|
|
32
|
+
### `Store.create(name, initialValue)`
|
|
35
33
|
|
|
36
34
|
```javascript
|
|
37
|
-
|
|
38
|
-
const themeStore = Store.create('theme', 'light');
|
|
35
|
+
Store.create('theme', 'light');
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
const appStore = Store.create('app', {
|
|
37
|
+
Store.create('app', {
|
|
42
38
|
currentPage: 'home',
|
|
43
39
|
sidebarOpen: false,
|
|
44
40
|
notifications: []
|
|
45
41
|
});
|
|
46
|
-
|
|
47
|
-
// Create with initial complex data
|
|
48
|
-
const cartStore = Store.create('cart', {
|
|
49
|
-
items: [],
|
|
50
|
-
total: 0,
|
|
51
|
-
currency: 'USD',
|
|
52
|
-
discountCode: null
|
|
53
|
-
});
|
|
54
42
|
```
|
|
55
43
|
|
|
56
|
-
###
|
|
44
|
+
### `Store.createResettable(name, initialValue)`
|
|
45
|
+
|
|
46
|
+
Use when the store needs to return to its initial value - for example on logout or route change:
|
|
57
47
|
|
|
58
|
-
Use `createResettable()` when the store needs to return to its initial value — for example on logout or route change.
|
|
59
48
|
```javascript
|
|
60
49
|
const userStore = Store.createResettable('user', {
|
|
61
50
|
id: null,
|
|
@@ -64,30 +53,21 @@ const userStore = Store.createResettable('user', {
|
|
|
64
53
|
isLoggedIn: false
|
|
65
54
|
});
|
|
66
55
|
|
|
67
|
-
// Reset to initial value at any time
|
|
68
56
|
Store.reset('user');
|
|
69
57
|
// -> { id: null, name: '', email: '', isLoggedIn: false }
|
|
70
58
|
// -> all subscribers are notified automatically
|
|
71
59
|
|
|
72
60
|
// Standard create() does not support reset
|
|
73
|
-
Store.reset('theme'); //
|
|
61
|
+
Store.reset('theme'); // throws - this store is not resettable
|
|
74
62
|
```
|
|
75
63
|
|
|
76
|
-
### Store
|
|
64
|
+
### `Store.createComposed(name, fn, deps)`
|
|
77
65
|
|
|
78
|
-
For a computed value
|
|
79
|
-
```javascript
|
|
80
|
-
const cartTotal = Observable.computed(() => {
|
|
81
|
-
const cart = cartStore.val();
|
|
82
|
-
const subtotal = cart.items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
83
|
-
return subtotal + (subtotal * cart.taxRate);
|
|
84
|
-
}, [cartStore]);
|
|
85
|
-
```
|
|
66
|
+
For a computed value accessible globally via the Store registry:
|
|
86
67
|
|
|
87
|
-
For a computed value that must be accessible globally via the Store registry, use `createComposed()` :
|
|
88
68
|
```javascript
|
|
89
69
|
Store.create('products', [{ id: 1, price: 10 }]);
|
|
90
|
-
Store.create('cart',
|
|
70
|
+
Store.create('cart', [{ productId: 1, quantity: 2 }]);
|
|
91
71
|
|
|
92
72
|
Store.createComposed('total', () => {
|
|
93
73
|
const products = Store.get('products').val();
|
|
@@ -98,26 +78,34 @@ Store.createComposed('total', () => {
|
|
|
98
78
|
}, 0);
|
|
99
79
|
}, ['products', 'cart']);
|
|
100
80
|
|
|
101
|
-
// Access like any other store — read-only
|
|
102
81
|
const total = Store.follow('total');
|
|
103
|
-
total.val(); //
|
|
82
|
+
total.val(); // 20
|
|
104
83
|
|
|
105
|
-
// Store.use('total')
|
|
106
|
-
// Store.reset('total') ->
|
|
84
|
+
// Store.use('total') -> throws - composed stores are read-only
|
|
85
|
+
// Store.reset('total') -> throws - composed stores cannot be reset
|
|
107
86
|
```
|
|
108
87
|
|
|
109
|
-
|
|
88
|
+
For a computed value that stays local to a component, use `Observable.computed()` instead - see [Observables](./observables.md).
|
|
89
|
+
|
|
90
|
+
### `Store.createPersistent(name, initialValue, storageKey?)`
|
|
91
|
+
|
|
92
|
+
Automatically saved to localStorage on every change and restored on page load:
|
|
110
93
|
|
|
111
|
-
Use `createPersistent()` when the store needs to survive page reloads. The value is automatically restored from localStorage on load and saved on every change.
|
|
112
94
|
```javascript
|
|
113
|
-
|
|
114
|
-
theme.set('dark'); // saved
|
|
95
|
+
Store.createPersistent('theme', 'light');
|
|
96
|
+
Store.get('theme').set('dark'); // saved automatically
|
|
97
|
+
|
|
98
|
+
// On next page load
|
|
99
|
+
Store.get('theme').val(); // "dark" - restored
|
|
115
100
|
|
|
116
|
-
//
|
|
117
|
-
Store.
|
|
101
|
+
// Optional custom localStorage key
|
|
102
|
+
Store.createPersistent('theme', 'light', 'app:theme');
|
|
118
103
|
```
|
|
119
104
|
|
|
120
|
-
|
|
105
|
+
### `Store.createPersistentResettable(name, initialValue, storageKey?)`
|
|
106
|
+
|
|
107
|
+
Both persistent and resettable. Reset clears localStorage and restores the default:
|
|
108
|
+
|
|
121
109
|
```javascript
|
|
122
110
|
const session = Store.createPersistentResettable('session', { id: null, name: '' });
|
|
123
111
|
session.set({ id: 1, name: 'John' }); // saved
|
|
@@ -127,22 +115,18 @@ Store.reset('session');
|
|
|
127
115
|
// -> localStorage entry removed
|
|
128
116
|
```
|
|
129
117
|
|
|
130
|
-
|
|
131
|
-
```javascript
|
|
132
|
-
Store.createPersistent('theme', 'light', 'app:theme');
|
|
133
|
-
Store.createPersistentResettable('session', null, 'app:session');
|
|
134
|
-
```
|
|
118
|
+
---
|
|
135
119
|
|
|
136
|
-
|
|
120
|
+
## Store Groups
|
|
121
|
+
|
|
122
|
+
`Store.group()` creates an isolated namespace. Each group is a fully independent `StoreFactory` instance - no key conflicts, no shared state with the parent store:
|
|
137
123
|
|
|
138
|
-
Use `Store.group()` to create an isolated store namespace. Each group is a fully independent `StoreFactory` instance — no key conflicts, no shared state with the parent store.
|
|
139
124
|
```javascript
|
|
140
125
|
const EventStore = Store.group('events', (group) => {
|
|
141
126
|
group.create('catalog', []);
|
|
142
127
|
group.create('filters', { category: null, date: null, city: null });
|
|
143
|
-
|
|
144
128
|
group.createResettable('selected', null);
|
|
145
|
-
|
|
129
|
+
|
|
146
130
|
group.createComposed('filtered', () => {
|
|
147
131
|
const catalog = EventStore.get('catalog').val();
|
|
148
132
|
const filters = EventStore.get('filters').val();
|
|
@@ -154,21 +138,21 @@ const EventStore = Store.group('events', (group) => {
|
|
|
154
138
|
}, ['catalog', 'filters']);
|
|
155
139
|
});
|
|
156
140
|
|
|
157
|
-
//
|
|
141
|
+
// Same API as Store
|
|
158
142
|
EventStore.use('catalog'); // two-way follower
|
|
159
143
|
EventStore.follow('filtered'); // read-only follower
|
|
160
144
|
EventStore.get('filters'); // raw observable
|
|
161
145
|
|
|
162
|
-
// Direct property access
|
|
146
|
+
// Direct property access - raw observable, always read-only
|
|
163
147
|
EventStore.catalog;
|
|
164
148
|
EventStore.filters;
|
|
165
149
|
```
|
|
166
150
|
|
|
167
|
-
Groups can reference each other in `createComposed()
|
|
151
|
+
Groups can reference each other in `createComposed()`:
|
|
152
|
+
|
|
168
153
|
```javascript
|
|
169
154
|
const CartStore = Store.group('cart', (group) => {
|
|
170
155
|
group.create('items', []);
|
|
171
|
-
|
|
172
156
|
group.createComposed('total', () => {
|
|
173
157
|
return CartStore.get('items').val()
|
|
174
158
|
.reduce((sum, item) => sum + item.price * item.qty, 0);
|
|
@@ -176,89 +160,74 @@ const CartStore = Store.group('cart', (group) => {
|
|
|
176
160
|
});
|
|
177
161
|
|
|
178
162
|
const OrderStore = Store.group('orders', (group) => {
|
|
179
|
-
group.createComposed('summary', () => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}, [CartStore.get('items'), EventStore.get('catalog')]);
|
|
163
|
+
group.createComposed('summary', () => ({
|
|
164
|
+
items: CartStore.get('items').val(),
|
|
165
|
+
events: EventStore.get('catalog').val()
|
|
166
|
+
}), [CartStore.get('items'), EventStore.get('catalog')]);
|
|
184
167
|
});
|
|
185
168
|
```
|
|
186
169
|
|
|
187
|
-
Groups can
|
|
170
|
+
Groups can be created without a name:
|
|
171
|
+
|
|
188
172
|
```javascript
|
|
189
173
|
const CartStore = Store.group((group) => {
|
|
190
174
|
group.create('items', []);
|
|
191
175
|
});
|
|
192
176
|
```
|
|
193
177
|
|
|
194
|
-
|
|
178
|
+
Use groups to organize state by domain:
|
|
179
|
+
|
|
195
180
|
```javascript
|
|
196
|
-
// Good
|
|
197
|
-
const UserStore = Store.group('user',
|
|
198
|
-
const EventStore = Store.group('events',
|
|
199
|
-
const CartStore = Store.group('cart',
|
|
200
|
-
|
|
201
|
-
// Avoid
|
|
202
|
-
Store.create('userSession',
|
|
203
|
-
Store.create('eventCatalog',
|
|
204
|
-
Store.create('cartItems',
|
|
181
|
+
// Good - domain-driven grouping
|
|
182
|
+
const UserStore = Store.group('user', g => g.create('session', null));
|
|
183
|
+
const EventStore = Store.group('events', g => g.create('catalog', []));
|
|
184
|
+
const CartStore = Store.group('cart', g => g.create('items', []));
|
|
185
|
+
|
|
186
|
+
// Avoid - flat global stores for everything
|
|
187
|
+
Store.create('userSession', null);
|
|
188
|
+
Store.create('eventCatalog', []);
|
|
189
|
+
Store.create('cartItems', []);
|
|
205
190
|
```
|
|
206
191
|
|
|
192
|
+
---
|
|
207
193
|
|
|
208
|
-
##
|
|
194
|
+
## Accessing Stores
|
|
209
195
|
|
|
210
|
-
|
|
196
|
+
### `Store.use(name)` - Two-way reactive
|
|
211
197
|
|
|
212
|
-
|
|
198
|
+
Returns a reactive reference. The observable's `.set()` method is inherited from `Observable` - any change notifies all subscribers:
|
|
213
199
|
|
|
214
200
|
```javascript
|
|
215
201
|
const UserProfile = () => {
|
|
216
|
-
// Get reactive reference to store
|
|
217
202
|
const user = Store.use('user');
|
|
218
|
-
|
|
219
|
-
return Div([
|
|
220
|
-
H1(['User Profile: ', user.name]),
|
|
221
|
-
P(['Email: ', user.email]),
|
|
222
|
-
P(['Status: ', user.check(u => u.isLoggedIn ? 'Online' : 'Offline')])
|
|
223
|
-
]);
|
|
224
|
-
};
|
|
225
203
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return Nav([
|
|
231
|
-
ShowIf(user.check(u => u.isLoggedIn), [
|
|
232
|
-
Link({ to: '/profile' }, 'My Profile'),
|
|
233
|
-
Button('Logout').nd.onClick(() => {
|
|
234
|
-
user.set({ ...user.$value, isLoggedIn: false });
|
|
235
|
-
})
|
|
236
|
-
])
|
|
204
|
+
return Div([
|
|
205
|
+
H1(['User Profile: ', user.select(u => u.name)]),
|
|
206
|
+
P(['Email: ', user.select(u => u.email)]),
|
|
207
|
+
P(['Status: ', user.format(u => u.isLoggedIn ? 'Online' : 'Offline')])
|
|
237
208
|
]);
|
|
238
209
|
};
|
|
239
210
|
```
|
|
240
211
|
|
|
241
|
-
### Store.follow() - Read-only
|
|
212
|
+
### `Store.follow(name)` - Read-only reactive
|
|
213
|
+
|
|
214
|
+
Any attempt to call `.set()`, `.toggle()`, or `.reset()` throws a `NativeDocumentError`:
|
|
242
215
|
|
|
243
216
|
```javascript
|
|
244
217
|
const NotificationBadge = () => {
|
|
245
|
-
// Follow returns a read-only reference — any attempt to call
|
|
246
|
-
// .set(), .toggle() or .reset() will throw a NativeDocumentError.
|
|
247
|
-
// Use this when a component should only read from the store.
|
|
248
218
|
const notifications = Store.follow('notifications');
|
|
249
|
-
// notifications.set(...) ->
|
|
250
|
-
|
|
251
|
-
return ShowIf(notifications.
|
|
252
|
-
() => Span({ class: 'badge' }, notifications
|
|
219
|
+
// notifications.set(...) -> throws NativeDocumentError
|
|
220
|
+
|
|
221
|
+
return ShowIf(notifications.isNotEmpty(),
|
|
222
|
+
() => Span({ class: 'badge' }, notifications.toLength())
|
|
253
223
|
);
|
|
254
224
|
};
|
|
255
225
|
```
|
|
256
226
|
|
|
257
|
-
### Store.get() - Raw
|
|
227
|
+
### `Store.get(name)` - Raw observable
|
|
258
228
|
|
|
259
|
-
Returns the raw store observable directly
|
|
229
|
+
Returns the raw store observable directly - no follower, no cleanup contract:
|
|
260
230
|
|
|
261
|
-
> **Warning:** mutations on this observer impact all subscribers immediately.
|
|
262
231
|
```javascript
|
|
263
232
|
const userStore = Store.get('user');
|
|
264
233
|
|
|
@@ -271,167 +240,91 @@ userStore.subscribe(newUser => {
|
|
|
271
240
|
});
|
|
272
241
|
```
|
|
273
242
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
Stores and groups expose their observables as direct properties via a Proxy. Property access returns the raw observable — equivalent to calling `Store.get()`. Any attempt to assign or delete a property will throw.
|
|
277
|
-
```javascript
|
|
278
|
-
// Equivalent to Store.get('catalog')
|
|
279
|
-
EventStore.catalog;
|
|
280
|
-
|
|
281
|
-
// For a read-only follower, use follow() explicitly
|
|
282
|
-
EventStore.follow('catalog');
|
|
283
|
-
|
|
284
|
-
// Direct assignment is forbidden
|
|
285
|
-
EventStore.catalog = []; // ❌ throws : Store structure is immutable
|
|
286
|
-
delete EventStore.catalog; // ❌ throws : Store keys cannot be deleted
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
## Updating Store State
|
|
243
|
+
> Mutations on a raw observable from `Store.get()` impact all subscribers immediately.
|
|
290
244
|
|
|
291
|
-
|
|
245
|
+
### Direct property access
|
|
292
246
|
|
|
293
|
-
|
|
247
|
+
Stores and groups expose their observables as direct properties via a Proxy - equivalent to `Store.get()`:
|
|
294
248
|
|
|
295
249
|
```javascript
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
theme.set(current === 'light' ? 'dark' : 'light');
|
|
302
|
-
});
|
|
303
|
-
};
|
|
250
|
+
EventStore.catalog; // same as EventStore.get('catalog')
|
|
251
|
+
|
|
252
|
+
// Assignment and deletion are forbidden
|
|
253
|
+
EventStore.catalog = []; // throws - Store structure is immutable
|
|
254
|
+
delete EventStore.catalog; // throws - Store keys cannot be deleted
|
|
304
255
|
```
|
|
305
256
|
|
|
306
|
-
|
|
257
|
+
---
|
|
307
258
|
|
|
308
|
-
|
|
309
|
-
const LoginForm = () => {
|
|
310
|
-
const user = Store.use('user');
|
|
311
|
-
const email = Observable('');
|
|
312
|
-
const password = Observable('');
|
|
313
|
-
|
|
314
|
-
const handleLogin = () => {
|
|
315
|
-
// Update multiple properties
|
|
316
|
-
user.set({
|
|
317
|
-
...user.$value,
|
|
318
|
-
email: email.$value,
|
|
319
|
-
isLoggedIn: true,
|
|
320
|
-
name: 'User Name' // ...
|
|
321
|
-
});
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
return Form([
|
|
325
|
-
Input({ type: 'email', value: email, placeholder: 'Email' }),
|
|
326
|
-
Input({ type: 'password', value: password, placeholder: 'Password' }),
|
|
327
|
-
Button('Login').nd.onClick(handleLogin)
|
|
328
|
-
]);
|
|
329
|
-
};
|
|
330
|
-
```
|
|
259
|
+
## Updating Store State
|
|
331
260
|
|
|
332
|
-
|
|
261
|
+
`.set()` is inherited from `Observable` and works the same way:
|
|
333
262
|
|
|
334
263
|
```javascript
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
user.set({
|
|
348
|
-
...user.$value,
|
|
349
|
-
preferences: {
|
|
350
|
-
...user.$value.preferences,
|
|
351
|
-
...prefs
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
return Div([
|
|
357
|
-
Input({
|
|
358
|
-
value: user.check(u => u.name),
|
|
359
|
-
placeholder: 'Name'
|
|
360
|
-
}).nd.onInput(e => updateName(e.target.value)),
|
|
361
|
-
|
|
362
|
-
Button('Dark Mode').nd.onClick(() =>
|
|
363
|
-
updatePreferences({ theme: 'dark' })
|
|
364
|
-
)
|
|
365
|
-
]);
|
|
366
|
-
};
|
|
264
|
+
// Direct value
|
|
265
|
+
const theme = Store.use('theme');
|
|
266
|
+
theme.set('dark');
|
|
267
|
+
theme.toggle(); // for boolean stores
|
|
268
|
+
|
|
269
|
+
// Function update
|
|
270
|
+
const counter = Store.use('counter');
|
|
271
|
+
counter.set(current => current + 1);
|
|
272
|
+
|
|
273
|
+
// Object spread
|
|
274
|
+
const user = Store.use('user');
|
|
275
|
+
user.set({ ...user.$value, name: 'Alice', isLoggedIn: true });
|
|
367
276
|
```
|
|
368
277
|
|
|
369
|
-
|
|
278
|
+
---
|
|
370
279
|
|
|
371
|
-
|
|
280
|
+
## Other Store Methods
|
|
372
281
|
|
|
373
|
-
|
|
374
|
-
// Get store reference without reactivity
|
|
375
|
-
const userStore = Store.get('user');
|
|
282
|
+
### `Store.has(name)`
|
|
376
283
|
|
|
377
|
-
|
|
378
|
-
if (userStore.$value.isLoggedIn) {
|
|
379
|
-
console.log('User is logged in');
|
|
380
|
-
}
|
|
284
|
+
Check if a store exists before accessing it:
|
|
381
285
|
|
|
382
|
-
// Subscribe to changes manually
|
|
383
|
-
userStore.subscribe(newUser => {
|
|
384
|
-
console.log('User changed:', newUser);
|
|
385
|
-
});
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Checking Store Existence
|
|
389
286
|
```javascript
|
|
390
|
-
// Check if a store exists before accessing it
|
|
391
287
|
if (Store.has('cart')) {
|
|
392
288
|
const cart = Store.use('cart');
|
|
393
|
-
// ...
|
|
394
289
|
}
|
|
395
290
|
|
|
396
|
-
// Typical use
|
|
291
|
+
// Typical use: dynamic module initialization
|
|
397
292
|
if (!Store.has('cart')) {
|
|
398
293
|
Store.create('cart', { items: [], total: 0 });
|
|
399
294
|
}
|
|
400
295
|
```
|
|
401
296
|
|
|
402
|
-
### Store
|
|
297
|
+
### `Store.delete(name)`
|
|
298
|
+
|
|
299
|
+
Destroys a store - cleans up all followers and the observable, then removes it from the registry:
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
Store.delete('session'); // cleans up all subscribers and removes the store
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
> After deletion, any existing follower references become stale. Always check with `Store.has()` before accessing a store that may have been deleted.
|
|
306
|
+
|
|
307
|
+
### `Store.reset(name)`
|
|
308
|
+
|
|
309
|
+
Resets a resettable store to its initial value:
|
|
403
310
|
|
|
404
311
|
```javascript
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
const auth = Store.create('auth', defaultAuth);
|
|
408
|
-
const cart = Store.create('cart', defaultCart);
|
|
409
|
-
const settings = Store.create('settings', defaultSettings);
|
|
410
|
-
|
|
411
|
-
// Computed store that combines others
|
|
412
|
-
Store.createComposed('appStatus', () => ({
|
|
413
|
-
isLoggedIn: Store.get('auth').val().user !== null,
|
|
414
|
-
cartItems: Store.get('cart').val().items.length,
|
|
415
|
-
theme: Store.get('settings').val().theme
|
|
416
|
-
}), ['auth', 'cart', 'settings']);
|
|
417
|
-
|
|
418
|
-
return { auth, cart, settings, appStatus };
|
|
419
|
-
};
|
|
312
|
+
Store.reset('user'); // works - created with createResettable()
|
|
313
|
+
Store.reset('theme'); // throws - created with create()
|
|
420
314
|
```
|
|
421
315
|
|
|
422
|
-
|
|
316
|
+
---
|
|
423
317
|
|
|
424
|
-
|
|
318
|
+
## Next Steps
|
|
425
319
|
|
|
426
|
-
- **[
|
|
427
|
-
- **[
|
|
428
|
-
- **[
|
|
429
|
-
- **[Args Validation](validation.md)** - Function
|
|
430
|
-
- **[Memory Management](memory-management.md)** - Memory management
|
|
431
|
-
- **[Anchor](anchor.md)** - Anchor
|
|
320
|
+
- **[Observables](./observables.md)** - Local state and reactive primitives
|
|
321
|
+
- **[Lifecycle Events](./lifecycle-events.md)** - Lifecycle events
|
|
322
|
+
- **[NDElement](./native-document-element.md)** - Native Document Element
|
|
323
|
+
- **[Args Validation](./validation.md)** - Function argument validation
|
|
324
|
+
- **[Memory Management](./memory-management.md)** - Memory management
|
|
432
325
|
|
|
433
326
|
## Utilities
|
|
434
327
|
|
|
435
|
-
- **[Cache](
|
|
436
|
-
- **[NativeFetch](
|
|
437
|
-
- **[Filters](
|
|
328
|
+
- **[Cache](./cache.md)** - Lazy initialization and singleton patterns
|
|
329
|
+
- **[NativeFetch](./native-fetch.md)** - HTTP client with interceptors
|
|
330
|
+
- **[Filters](./filters.md)** - Data filtering helpers
|