native-document 1.0.14 → 1.0.15
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/native-document.dev.js +1262 -839
- package/dist/native-document.min.js +1 -1
- package/docs/anchor.md +216 -53
- package/docs/conditional-rendering.md +25 -24
- package/docs/core-concepts.md +20 -19
- package/docs/elements.md +21 -20
- package/docs/getting-started.md +28 -27
- package/docs/lifecycle-events.md +2 -2
- package/docs/list-rendering.md +607 -0
- package/docs/memory-management.md +1 -1
- package/docs/observables.md +15 -14
- package/docs/routing.md +22 -22
- package/docs/state-management.md +8 -8
- package/docs/validation.md +0 -2
- package/index.js +6 -1
- package/package.json +1 -1
- package/readme.md +5 -4
- package/src/data/MemoryManager.js +8 -20
- package/src/data/Observable.js +2 -180
- package/src/data/ObservableChecker.js +25 -24
- package/src/data/ObservableItem.js +158 -79
- package/src/data/observable-helpers/array.js +74 -0
- package/src/data/observable-helpers/batch.js +22 -0
- package/src/data/observable-helpers/computed.js +28 -0
- package/src/data/observable-helpers/object.js +111 -0
- package/src/elements/anchor.js +54 -9
- package/src/elements/control/for-each-array.js +280 -0
- package/src/elements/control/for-each.js +87 -110
- package/src/elements/index.js +1 -0
- package/src/elements/list.js +4 -0
- package/src/utils/helpers.js +44 -21
- package/src/wrappers/AttributesWrapper.js +5 -18
- package/src/wrappers/DocumentObserver.js +58 -29
- package/src/wrappers/ElementCreator.js +114 -0
- package/src/wrappers/HtmlElementEventsWrapper.js +52 -65
- package/src/wrappers/HtmlElementWrapper.js +11 -167
- package/src/wrappers/NdPrototype.js +109 -0
|
@@ -23,10 +23,10 @@ The `ShowIf` function renders content only when the Observable condition is trut
|
|
|
23
23
|
const user = Observable({ name: 'Alice', isLoggedIn: false });
|
|
24
24
|
|
|
25
25
|
const App = Div([
|
|
26
|
-
Button('Login').nd.
|
|
26
|
+
Button('Login').nd.onClick(() =>
|
|
27
27
|
user.set({ ...user.val(), isLoggedIn: true })
|
|
28
28
|
),
|
|
29
|
-
Button('Logout').nd.
|
|
29
|
+
Button('Logout').nd.onClick(() =>
|
|
30
30
|
user.set({ ...user.val(), isLoggedIn: false })
|
|
31
31
|
),
|
|
32
32
|
|
|
@@ -65,8 +65,8 @@ const WeatherApp = Div([
|
|
|
65
65
|
ShowIf(isCold, Div({ class: 'cold' }, '🧥 It\'s cold! Wear a jacket.')),
|
|
66
66
|
ShowIf(isHot, Div({ class: 'hot' }, '☀️ It\'s hot! Stay hydrated.')),
|
|
67
67
|
Div([
|
|
68
|
-
Button('-').nd.
|
|
69
|
-
Button('+').nd.
|
|
68
|
+
Button('-').nd.onClick(() => --temperature.$value),
|
|
69
|
+
Button('+').nd.onClick(() => ++temperature.$value),
|
|
70
70
|
])
|
|
71
71
|
]);
|
|
72
72
|
```
|
|
@@ -110,7 +110,7 @@ ShowIf(condition.check(val => !val), content)
|
|
|
110
110
|
const isDarkMode = Observable(false);
|
|
111
111
|
|
|
112
112
|
const ThemeToggle = Div([
|
|
113
|
-
Button('Toggle Theme').nd.
|
|
113
|
+
Button('Toggle Theme').nd.onClick(() => isDarkMode.set(!isDarkMode.val())),
|
|
114
114
|
|
|
115
115
|
Switch(isDarkMode,
|
|
116
116
|
Div({ class: 'dark-indicator' }, '🌙 Dark Mode'), // when true
|
|
@@ -166,7 +166,7 @@ const gameState = Observable({
|
|
|
166
166
|
const GameDisplay = Match(gameState.check(state => state.phase), {
|
|
167
167
|
menu: () => Div({ class: 'game-menu' }, [
|
|
168
168
|
H1('Welcome to the Game'),
|
|
169
|
-
Button('Start Game').nd.
|
|
169
|
+
Button('Start Game').nd.onClick(() =>
|
|
170
170
|
gameState.set({ ...gameState.val(), phase: 'playing' })
|
|
171
171
|
)
|
|
172
172
|
]),
|
|
@@ -174,17 +174,17 @@ const GameDisplay = Match(gameState.check(state => state.phase), {
|
|
|
174
174
|
playing: () => Div({ class: 'game-ui' }, [
|
|
175
175
|
Div(['Score: ', gameState.check(s => s.score)]),
|
|
176
176
|
Div(['Level: ', gameState.check(s => s.level)]),
|
|
177
|
-
Button('Pause').nd.
|
|
177
|
+
Button('Pause').nd.onClick(() =>
|
|
178
178
|
gameState.set({ ...gameState.val(), phase: 'paused' })
|
|
179
179
|
),
|
|
180
|
-
Button('Game Over').nd.
|
|
180
|
+
Button('Game Over').nd.onClick(() =>
|
|
181
181
|
gameState.set({ ...gameState.val(), phase: 'gameOver' })
|
|
182
182
|
)
|
|
183
183
|
]),
|
|
184
184
|
|
|
185
185
|
paused: () => Div({ class: 'game-paused' }, [
|
|
186
186
|
H2('Game Paused'),
|
|
187
|
-
Button('Resume').nd.
|
|
187
|
+
Button('Resume').nd.onClick(() =>
|
|
188
188
|
gameState.set({ ...gameState.val(), phase: 'playing' })
|
|
189
189
|
)
|
|
190
190
|
]),
|
|
@@ -192,7 +192,7 @@ const GameDisplay = Match(gameState.check(state => state.phase), {
|
|
|
192
192
|
gameOver: () => Div({ class: 'game-over' }, [
|
|
193
193
|
H2('Game Over'),
|
|
194
194
|
Div(['Final Score: ', gameState.check(s => s.score)]),
|
|
195
|
-
Button('Play Again').nd.
|
|
195
|
+
Button('Play Again').nd.onClick(() =>
|
|
196
196
|
gameState.set({ phase: 'menu', score: 0, level: 1 })
|
|
197
197
|
)
|
|
198
198
|
])
|
|
@@ -275,7 +275,7 @@ const isValidEmail = formData.email.check(e =>
|
|
|
275
275
|
const isValidPassword = formData.password.check(p => p.length >= 8);
|
|
276
276
|
|
|
277
277
|
const passwordsMatch = Observable.computed(() => {
|
|
278
|
-
const data = formData.$
|
|
278
|
+
const data = formData.$value;
|
|
279
279
|
return data.password === data.confirmPassword && data.password.length > 0;
|
|
280
280
|
}, [formData.password, formData.confirmPassword]);
|
|
281
281
|
|
|
@@ -319,8 +319,8 @@ const RegistrationForm = Div({ class: 'registration-form' }, [
|
|
|
319
319
|
|
|
320
320
|
// Submit button
|
|
321
321
|
Switch(canSubmit,
|
|
322
|
-
Button('Create Account').nd.
|
|
323
|
-
console.log('Creating account...', formData.$
|
|
322
|
+
Button('Create Account').nd.onClick(() => {
|
|
323
|
+
console.log('Creating account...', formData.$value);
|
|
324
324
|
}),
|
|
325
325
|
Button({ disabled: true, class: 'disabled' }, 'Create Account')
|
|
326
326
|
)
|
|
@@ -347,20 +347,20 @@ const App = Div({ class: 'app' }, [
|
|
|
347
347
|
// Authenticated header
|
|
348
348
|
() => Div({ class: 'user-menu' }, [
|
|
349
349
|
Span(['Welcome, ', appState.user.val().name]),
|
|
350
|
-
Button('Settings').nd.
|
|
350
|
+
Button('Settings').nd.onClick(() =>
|
|
351
351
|
appState.currentView.set('settings')
|
|
352
352
|
),
|
|
353
|
-
Button('Logout').nd.
|
|
353
|
+
Button('Logout').nd.onClick(() => {
|
|
354
354
|
appState.user.set(null);
|
|
355
355
|
appState.currentView.set('welcome');
|
|
356
356
|
})
|
|
357
357
|
]),
|
|
358
358
|
// Guest header
|
|
359
359
|
Div({ class: 'auth-buttons' }, [
|
|
360
|
-
Button('Sign In').nd.
|
|
360
|
+
Button('Sign In').nd.onClick(() =>
|
|
361
361
|
appState.currentView.set('login')
|
|
362
362
|
),
|
|
363
|
-
Button('Sign Up').nd.
|
|
363
|
+
Button('Sign Up').nd.onClick(() =>
|
|
364
364
|
appState.currentView.set('register')
|
|
365
365
|
)
|
|
366
366
|
])
|
|
@@ -544,12 +544,12 @@ const requestState = Observable.object({
|
|
|
544
544
|
});
|
|
545
545
|
|
|
546
546
|
const DataView = Match(requestState.status, {
|
|
547
|
-
idle: Button('Load Data').nd.
|
|
547
|
+
idle: Button('Load Data').nd.onClick(loadData),
|
|
548
548
|
loading: Div({ class: 'loading' }, 'Loading...'),
|
|
549
549
|
success: () => DataDisplay(requestState.data.val()),
|
|
550
550
|
error: () => Div({ class: 'error' }, [
|
|
551
551
|
'Error: ', requestState.error,
|
|
552
|
-
Button('Retry').nd.
|
|
552
|
+
Button('Retry').nd.onClick(loadData)
|
|
553
553
|
])
|
|
554
554
|
});
|
|
555
555
|
```
|
|
@@ -621,8 +621,9 @@ const StatusContent = Match(status, {
|
|
|
621
621
|
|
|
622
622
|
Now that you understand conditional rendering, explore these related topics:
|
|
623
623
|
|
|
624
|
-
- **[
|
|
625
|
-
- **[
|
|
626
|
-
- **[
|
|
627
|
-
- **[
|
|
628
|
-
- **[
|
|
624
|
+
- **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
|
|
625
|
+
- **[Routing](routing.md)** - Navigation and URL management
|
|
626
|
+
- **[State Management](state-management.md)** - Global state patterns
|
|
627
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
628
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
629
|
+
- **[Anchor](anchor.md)** - Anchor
|
package/docs/core-concepts.md
CHANGED
|
@@ -145,15 +145,15 @@ const element = Div({
|
|
|
145
145
|
const counter = Observable(0);
|
|
146
146
|
|
|
147
147
|
const button = Button('Click me')
|
|
148
|
-
.nd.
|
|
148
|
+
.nd.onClick(() => {
|
|
149
149
|
counter.set(counter.val() + 1);
|
|
150
150
|
});
|
|
151
151
|
|
|
152
152
|
// Multiple events
|
|
153
153
|
const input = Input()
|
|
154
|
-
.nd.
|
|
155
|
-
.nd.
|
|
156
|
-
.nd.
|
|
154
|
+
.nd.onFocus(e => console.log('Focused'))
|
|
155
|
+
.nd.onBlur(e => console.log('Blurred'))
|
|
156
|
+
.nd.onInput(e => console.log('Value:', e.target.value));
|
|
157
157
|
|
|
158
158
|
//Or
|
|
159
159
|
const input = Input()
|
|
@@ -210,7 +210,7 @@ const items = Observable.array(['Apple', 'Banana']);
|
|
|
210
210
|
const list = ForEach(items, (item) =>
|
|
211
211
|
Div([
|
|
212
212
|
item,
|
|
213
|
-
Button('Remove').nd.
|
|
213
|
+
Button('Remove').nd.onClick(() => {
|
|
214
214
|
// User action → State change → UI update
|
|
215
215
|
const index = items.val().indexOf(item);
|
|
216
216
|
items.splice(index, 1);
|
|
@@ -249,7 +249,7 @@ function UserCard(user) {
|
|
|
249
249
|
return Div({ class: 'user-card' }, [
|
|
250
250
|
Div({ class: 'name' }, user.name),
|
|
251
251
|
Div({ class: 'email' }, user.email),
|
|
252
|
-
Button('Edit').nd.
|
|
252
|
+
Button('Edit').nd.onClick(() => {
|
|
253
253
|
// Handle edit
|
|
254
254
|
})
|
|
255
255
|
]);
|
|
@@ -269,9 +269,9 @@ function Counter(initialValue = 0) {
|
|
|
269
269
|
|
|
270
270
|
return Div({ class: 'counter' }, [
|
|
271
271
|
Div(['Count: ', count]),
|
|
272
|
-
Button('-').nd.
|
|
273
|
-
Button('+').nd.
|
|
274
|
-
Button('Reset').nd.
|
|
272
|
+
Button('-').nd.onClick(() => count.set(count.val() - 1)),
|
|
273
|
+
Button('+').nd.onClick(() => count.set(count.val() + 1)),
|
|
274
|
+
Button('Reset').nd.onClick(() => count.set(initialValue))
|
|
275
275
|
]);
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -314,7 +314,7 @@ function TodoForm() {
|
|
|
314
314
|
return Div([
|
|
315
315
|
Input({ placeholder: 'Enter todo...', value: text }),
|
|
316
316
|
Button('Add')
|
|
317
|
-
.nd.
|
|
317
|
+
.nd.onClick(() => {
|
|
318
318
|
if (isValid.val()) {
|
|
319
319
|
// Add todo logic
|
|
320
320
|
text.set('');
|
|
@@ -353,7 +353,7 @@ function TodoList() {
|
|
|
353
353
|
return ForEach(TodoStore.todos, (todo) =>
|
|
354
354
|
Div([
|
|
355
355
|
todo.text,
|
|
356
|
-
Button('Delete').nd.
|
|
356
|
+
Button('Delete').nd.onClick(() => {
|
|
357
357
|
TodoStore.removeTodo(todo.id);
|
|
358
358
|
})
|
|
359
359
|
])
|
|
@@ -503,11 +503,12 @@ const data = Observable('');
|
|
|
503
503
|
|
|
504
504
|
Now that you understand NativeDocument's core concepts, explore these advanced topics:
|
|
505
505
|
|
|
506
|
-
- **[Observables](
|
|
507
|
-
- **[Elements](
|
|
508
|
-
- **[Conditional Rendering](
|
|
509
|
-
- **[
|
|
510
|
-
- **[
|
|
511
|
-
- **[
|
|
512
|
-
- **[
|
|
513
|
-
- **[
|
|
506
|
+
- **[Observables](observables.md)** - Reactive state management
|
|
507
|
+
- **[Elements](elements.md)** - Creating and composing UI
|
|
508
|
+
- **[Conditional Rendering](conditional-rendering.md)** - Dynamic content
|
|
509
|
+
- **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
|
|
510
|
+
- **[Routing](routing.md)** - Navigation and URL management
|
|
511
|
+
- **[State Management](state-management.md)** - Global state patterns
|
|
512
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
513
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
514
|
+
- **[Anchor](anchor.md)** - Anchor
|
package/docs/elements.md
CHANGED
|
@@ -110,21 +110,21 @@ The `.nd` (NativeDocument) API provides a fluent interface for adding functional
|
|
|
110
110
|
|
|
111
111
|
```javascript
|
|
112
112
|
const button = Button("Click me")
|
|
113
|
-
.nd.
|
|
113
|
+
.nd.onClick(() => {
|
|
114
114
|
console.log("Button clicked!");
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
// With attributes and events
|
|
118
118
|
const styledButton = Button({ class: "btn" }, "Click me")
|
|
119
|
-
.nd.
|
|
119
|
+
.nd.onClick(() => {
|
|
120
120
|
console.log("Button clicked!");
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
// Multiple events
|
|
124
124
|
const input = Input({ type: "text", placeholder: "Type here..." })
|
|
125
|
-
.nd.
|
|
126
|
-
.nd.
|
|
127
|
-
.nd.
|
|
125
|
+
.nd.onFocus(() => console.log("Input focused"))
|
|
126
|
+
.nd.onBlur(() => console.log("Input blurred"))
|
|
127
|
+
.nd.onInput(event => console.log("Input value:", event.target.value));
|
|
128
128
|
|
|
129
129
|
// Or
|
|
130
130
|
const input = Input({ type: "text", placeholder: "Type here..." })
|
|
@@ -136,7 +136,7 @@ const input = Input({ type: "text", placeholder: "Type here..." })
|
|
|
136
136
|
|
|
137
137
|
// Prevent default behavior
|
|
138
138
|
const form = Form()
|
|
139
|
-
.nd.
|
|
139
|
+
.nd.onPreventSubmit(event => {
|
|
140
140
|
console.log("Form submitted without page reload");
|
|
141
141
|
// Handle form submission
|
|
142
142
|
});
|
|
@@ -225,7 +225,7 @@ const app = Div([
|
|
|
225
225
|
.nd.ref(refs, "nameInput"), // Store reference as refs.nameInput
|
|
226
226
|
|
|
227
227
|
Button("Focus Input")
|
|
228
|
-
.nd.
|
|
228
|
+
.nd.onClick(() => {
|
|
229
229
|
refs.nameInput.focus(); // Use the reference
|
|
230
230
|
})
|
|
231
231
|
]);
|
|
@@ -240,7 +240,7 @@ const incrementButton = Button({
|
|
|
240
240
|
class: "btn btn-primary",
|
|
241
241
|
type: "button"
|
|
242
242
|
}, "Increment")
|
|
243
|
-
.nd.
|
|
243
|
+
.nd.onClick(() => {
|
|
244
244
|
count.set(count.val() + 1);
|
|
245
245
|
});
|
|
246
246
|
|
|
@@ -272,7 +272,7 @@ const errors = Observable.object({
|
|
|
272
272
|
|
|
273
273
|
// Validation function
|
|
274
274
|
const validateForm = () => {
|
|
275
|
-
const data = formData.$
|
|
275
|
+
const data = formData.$value;
|
|
276
276
|
const newErrors = {};
|
|
277
277
|
|
|
278
278
|
newErrors.name = data.name.length < 2 ? "Name must be at least 2 characters" : "";
|
|
@@ -292,7 +292,7 @@ const contactForm = Form({ class: "contact-form" }, [
|
|
|
292
292
|
type: "text",
|
|
293
293
|
value: formData.name,
|
|
294
294
|
placeholder: "Enter your name"
|
|
295
|
-
}).nd.
|
|
295
|
+
}).nd.onBlur(validateForm),
|
|
296
296
|
|
|
297
297
|
ShowIf(errors.name.check(err => err !== ""),
|
|
298
298
|
Span({ class: "error" }, errors.name)
|
|
@@ -306,7 +306,7 @@ const contactForm = Form({ class: "contact-form" }, [
|
|
|
306
306
|
type: "email",
|
|
307
307
|
value: formData.email,
|
|
308
308
|
placeholder: "Enter your email"
|
|
309
|
-
}).nd.
|
|
309
|
+
}).nd.onBlur(validateForm),
|
|
310
310
|
|
|
311
311
|
ShowIf(errors.email.check(err => err !== ""),
|
|
312
312
|
Span({ class: "error" }, errors.email)
|
|
@@ -320,7 +320,7 @@ const contactForm = Form({ class: "contact-form" }, [
|
|
|
320
320
|
type: "number",
|
|
321
321
|
value: formData.age,
|
|
322
322
|
placeholder: "Enter your age"
|
|
323
|
-
}).nd.
|
|
323
|
+
}).nd.onBlur(validateForm),
|
|
324
324
|
|
|
325
325
|
ShowIf(errors.age.check(err => err !== ""),
|
|
326
326
|
Span({ class: "error" }, errors.age)
|
|
@@ -333,9 +333,9 @@ const contactForm = Form({ class: "contact-form" }, [
|
|
|
333
333
|
class: "btn btn-primary"
|
|
334
334
|
}, "Submit")
|
|
335
335
|
])
|
|
336
|
-
.nd.
|
|
336
|
+
.nd.onPreventSubmit(() => {
|
|
337
337
|
if (validateForm()) {
|
|
338
|
-
console.log("Form is valid!", formData.$
|
|
338
|
+
console.log("Form is valid!", formData.$value);
|
|
339
339
|
// Handle successful submission
|
|
340
340
|
} else {
|
|
341
341
|
console.log("Form has errors");
|
|
@@ -375,9 +375,10 @@ And many more following the same naming pattern!
|
|
|
375
375
|
|
|
376
376
|
Now that you understand NativeDocument's elements, explore these advanced topics:
|
|
377
377
|
|
|
378
|
-
- **[Conditional Rendering](
|
|
379
|
-
- **[
|
|
380
|
-
- **[
|
|
381
|
-
- **[
|
|
382
|
-
- **[
|
|
383
|
-
- **[
|
|
378
|
+
- **[Conditional Rendering](conditional-rendering.md)** - Dynamic content
|
|
379
|
+
- **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
|
|
380
|
+
- **[Routing](routing.md)** - Navigation and URL management
|
|
381
|
+
- **[State Management](state-management.md)** - Global state patterns
|
|
382
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
383
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
384
|
+
- **[Anchor](anchor.md)** - Anchor
|
package/docs/getting-started.md
CHANGED
|
@@ -29,7 +29,7 @@ The fastest way to get started is using our CDN. Simply add this script tag to y
|
|
|
29
29
|
|
|
30
30
|
const App = Div({ class: 'app' }, [
|
|
31
31
|
Div(['Count: ', count]),
|
|
32
|
-
Button('Increment').nd.
|
|
32
|
+
Button('Increment').nd.onClick(() => count.set(count.val() + 1))
|
|
33
33
|
]);
|
|
34
34
|
|
|
35
35
|
document.body.appendChild(App);
|
|
@@ -75,7 +75,7 @@ const count = Observable(0);
|
|
|
75
75
|
|
|
76
76
|
const App = Div({ class: 'app' }, [
|
|
77
77
|
Div(['Count: ', count]),
|
|
78
|
-
Button('Increment').nd.
|
|
78
|
+
Button('Increment').nd.onClick(() => count.set(count.val() + 1))
|
|
79
79
|
]);
|
|
80
80
|
|
|
81
81
|
document.body.appendChild(App);
|
|
@@ -142,16 +142,16 @@ const CounterApp = Div({ class: 'counter-app' }, [
|
|
|
142
142
|
]),
|
|
143
143
|
|
|
144
144
|
Div([
|
|
145
|
-
Button('Decrease').nd.
|
|
146
|
-
count
|
|
145
|
+
Button('Decrease').nd.onClick(() => {
|
|
146
|
+
count.$value--;
|
|
147
147
|
}),
|
|
148
148
|
|
|
149
|
-
Button('Reset').nd.
|
|
149
|
+
Button('Reset').nd.onClick(() => {
|
|
150
150
|
count.set(0);
|
|
151
151
|
}),
|
|
152
152
|
|
|
153
|
-
Button('Increase').nd.
|
|
154
|
-
count
|
|
153
|
+
Button('Increase').nd.onClick(() => {
|
|
154
|
+
count.$value++;
|
|
155
155
|
})
|
|
156
156
|
])
|
|
157
157
|
]);
|
|
@@ -165,7 +165,7 @@ document.body.appendChild(CounterApp);
|
|
|
165
165
|
1. **Imported Components**: We used `Div`, `Button`, and `H1` from `NativeDocument.elements`
|
|
166
166
|
2. **Created Reactive State**: `Observable(0)` creates a reactive value that starts at 0
|
|
167
167
|
3. **Built the UI**: Elements are created with attributes and children
|
|
168
|
-
4. **Added Event Handlers**: `.nd.
|
|
168
|
+
4. **Added Event Handlers**: `.nd.onClick()` attaches click event listeners
|
|
169
169
|
5. **Automatic Updates**: When `count` changes, the UI updates automatically
|
|
170
170
|
|
|
171
171
|
## Todo List Application
|
|
@@ -216,7 +216,7 @@ const TodoApp = Div({ class: 'todo-app' }, [
|
|
|
216
216
|
placeholder: 'What needs to be done?',
|
|
217
217
|
value: newTodo
|
|
218
218
|
}),
|
|
219
|
-
Button('Add').nd.
|
|
219
|
+
Button('Add').nd.onClick(addTodo)
|
|
220
220
|
]),
|
|
221
221
|
|
|
222
222
|
// Todo list container
|
|
@@ -230,7 +230,7 @@ const TodoApp = Div({ class: 'todo-app' }, [
|
|
|
230
230
|
Input({
|
|
231
231
|
type: 'checkbox',
|
|
232
232
|
checked: Observable(todo.done)
|
|
233
|
-
}).nd.
|
|
233
|
+
}).nd.onChange((e) => {
|
|
234
234
|
const todoList = todos.val();
|
|
235
235
|
todoList[index.val()].done = e.target.checked;
|
|
236
236
|
todos.set([...todoList]);
|
|
@@ -238,7 +238,7 @@ const TodoApp = Div({ class: 'todo-app' }, [
|
|
|
238
238
|
|
|
239
239
|
Div(['Task: ', todo.text]),
|
|
240
240
|
|
|
241
|
-
Button('Delete').nd.
|
|
241
|
+
Button('Delete').nd.onClick(() => {
|
|
242
242
|
todos.splice(index.val(), 1);
|
|
243
243
|
})
|
|
244
244
|
]),
|
|
@@ -249,9 +249,9 @@ const TodoApp = Div({ class: 'todo-app' }, [
|
|
|
249
249
|
|
|
250
250
|
// Filters
|
|
251
251
|
Div({ class: 'filters' }, [
|
|
252
|
-
Button('All').nd.
|
|
253
|
-
Button('Active').nd.
|
|
254
|
-
Button('Completed').nd.
|
|
252
|
+
Button('All').nd.onClick(() => filter.set('all')),
|
|
253
|
+
Button('Active').nd.onClick(() => filter.set('active')),
|
|
254
|
+
Button('Completed').nd.onClick(() => filter.set('completed'))
|
|
255
255
|
])
|
|
256
256
|
|
|
257
257
|
]);
|
|
@@ -273,8 +273,8 @@ document.body.appendChild(TodoApp);
|
|
|
273
273
|
- Children can be strings, numbers, elements, or observables
|
|
274
274
|
|
|
275
275
|
### Event Handling
|
|
276
|
-
- `.nd.
|
|
277
|
-
- `.nd.
|
|
276
|
+
- `.nd.onClick()` - Add click event listener
|
|
277
|
+
- `.nd.onChange()` - Add change event listener
|
|
278
278
|
- Event handlers receive the native event object
|
|
279
279
|
|
|
280
280
|
### Conditional Rendering
|
|
@@ -312,11 +312,11 @@ export function TodoItem(todo, onToggle, onDelete) {
|
|
|
312
312
|
Input({
|
|
313
313
|
type: 'checkbox',
|
|
314
314
|
checked: todo.done
|
|
315
|
-
}).nd.
|
|
315
|
+
}).nd.onChange(onToggle),
|
|
316
316
|
|
|
317
317
|
Div(['Task: ', todo.text]),
|
|
318
318
|
|
|
319
|
-
Button('Delete').nd.
|
|
319
|
+
Button('Delete').nd.onClick(onDelete)
|
|
320
320
|
]);
|
|
321
321
|
}
|
|
322
322
|
```
|
|
@@ -350,15 +350,16 @@ NativeDocument works in all modern browsers that support:
|
|
|
350
350
|
|
|
351
351
|
Now that you've built your first NativeDocument applications, explore these topics:
|
|
352
352
|
|
|
353
|
-
- **[Core Concepts](
|
|
354
|
-
- **[Observables](
|
|
355
|
-
- **[Elements](
|
|
356
|
-
- **[Conditional Rendering](
|
|
357
|
-
- **[
|
|
358
|
-
- **[
|
|
359
|
-
- **[
|
|
360
|
-
- **[
|
|
361
|
-
- **[
|
|
353
|
+
- **[Core Concepts](core-concepts.md)** - Understanding the fundamentals
|
|
354
|
+
- **[Observables](observables.md)** - Reactive state management
|
|
355
|
+
- **[Elements](elements.md)** - Creating and composing UI
|
|
356
|
+
- **[Conditional Rendering](conditional-rendering.md)** - Dynamic content
|
|
357
|
+
- **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
|
|
358
|
+
- **[Routing](routing.md)** - Navigation and URL management
|
|
359
|
+
- **[State Management](state-management.md)** - Global state patterns
|
|
360
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
361
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
362
|
+
- **[Anchor](anchor.md)** - Anchor
|
|
362
363
|
|
|
363
364
|
## Common Issues
|
|
364
365
|
|
package/docs/lifecycle-events.md
CHANGED
|
@@ -98,8 +98,8 @@ document.body.appendChild(reusableComponent);
|
|
|
98
98
|
|
|
99
99
|
Now that you understand lifecycle events, explore these related topics:
|
|
100
100
|
|
|
101
|
-
- **[Memory Management](
|
|
102
|
-
- **[Anchor](
|
|
101
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
102
|
+
- **[Anchor](anchor.md)** - Anchor
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
|