native-document 1.0.13 → 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.
Files changed (37) hide show
  1. package/dist/native-document.dev.js +1297 -804
  2. package/dist/native-document.min.js +1 -1
  3. package/docs/anchor.md +216 -53
  4. package/docs/conditional-rendering.md +25 -24
  5. package/docs/core-concepts.md +20 -19
  6. package/docs/elements.md +21 -20
  7. package/docs/getting-started.md +28 -27
  8. package/docs/lifecycle-events.md +2 -2
  9. package/docs/list-rendering.md +607 -0
  10. package/docs/memory-management.md +1 -1
  11. package/docs/observables.md +15 -14
  12. package/docs/routing.md +22 -22
  13. package/docs/state-management.md +8 -8
  14. package/docs/validation.md +0 -2
  15. package/index.js +6 -1
  16. package/package.json +1 -1
  17. package/readme.md +5 -4
  18. package/src/data/MemoryManager.js +8 -20
  19. package/src/data/Observable.js +2 -180
  20. package/src/data/ObservableChecker.js +26 -21
  21. package/src/data/ObservableItem.js +158 -79
  22. package/src/data/observable-helpers/array.js +74 -0
  23. package/src/data/observable-helpers/batch.js +22 -0
  24. package/src/data/observable-helpers/computed.js +28 -0
  25. package/src/data/observable-helpers/object.js +111 -0
  26. package/src/elements/anchor.js +54 -9
  27. package/src/elements/control/for-each-array.js +280 -0
  28. package/src/elements/control/for-each.js +100 -56
  29. package/src/elements/index.js +1 -0
  30. package/src/elements/list.js +4 -0
  31. package/src/utils/helpers.js +44 -21
  32. package/src/wrappers/AttributesWrapper.js +5 -18
  33. package/src/wrappers/DocumentObserver.js +58 -29
  34. package/src/wrappers/ElementCreator.js +114 -0
  35. package/src/wrappers/HtmlElementEventsWrapper.js +52 -65
  36. package/src/wrappers/HtmlElementWrapper.js +11 -167
  37. 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.on.click(() =>
26
+ Button('Login').nd.onClick(() =>
27
27
  user.set({ ...user.val(), isLoggedIn: true })
28
28
  ),
29
- Button('Logout').nd.on.click(() =>
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.on.click(() => --temperature.$value),
69
- Button('+').nd.on.click(() => ++temperature.$value),
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.on.click(() => isDarkMode.set(!isDarkMode.val())),
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.on.click(() =>
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.on.click(() =>
177
+ Button('Pause').nd.onClick(() =>
178
178
  gameState.set({ ...gameState.val(), phase: 'paused' })
179
179
  ),
180
- Button('Game Over').nd.on.click(() =>
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.on.click(() =>
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.on.click(() =>
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.$val();
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.on.click(() => {
323
- console.log('Creating account...', formData.$val());
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.on.click(() =>
350
+ Button('Settings').nd.onClick(() =>
351
351
  appState.currentView.set('settings')
352
352
  ),
353
- Button('Logout').nd.on.click(() => {
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.on.click(() =>
360
+ Button('Sign In').nd.onClick(() =>
361
361
  appState.currentView.set('login')
362
362
  ),
363
- Button('Sign Up').nd.on.click(() =>
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.on.click(loadData),
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.on.click(loadData)
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
- - **[Routing](docs/routing.md)** - Navigation and URL management
625
- - **[State Management](docs/state-management.md)** - Global state patterns
626
- - **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
627
- - **[Memory Management](docs/memory-management.md)** - Memory management
628
- - **[Anchor](docs/anchor.md)** - Anchor
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
@@ -145,15 +145,15 @@ const element = Div({
145
145
  const counter = Observable(0);
146
146
 
147
147
  const button = Button('Click me')
148
- .nd.on.click(() => {
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.on.focus(e => console.log('Focused'))
155
- .nd.on.blur(e => console.log('Blurred'))
156
- .nd.on.input(e => console.log('Value:', e.target.value));
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.on.click(() => {
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.on.click(() => {
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.on.click(() => count.set(count.val() - 1)),
273
- Button('+').nd.on.click(() => count.set(count.val() + 1)),
274
- Button('Reset').nd.on.click(() => count.set(initialValue))
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.on.click(() => {
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.on.click(() => {
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](docs/observables.md)** - Reactive state management
507
- - **[Elements](docs/elements.md)** - Creating and composing UI
508
- - **[Conditional Rendering](docs/conditional-rendering.md)** - Dynamic content
509
- - **[Routing](docs/routing.md)** - Navigation and URL management
510
- - **[State Management](docs/state-management.md)** - Global state patterns
511
- - **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
512
- - **[Memory Management](docs/memory-management.md)** - Memory management
513
- - **[Anchor](docs/anchor.md)** - Anchor
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.on.click(() => {
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.on.click(() => {
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.on.focus(() => console.log("Input focused"))
126
- .nd.on.blur(() => console.log("Input blurred"))
127
- .nd.on.input(event => console.log("Input value:", event.target.value));
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.on.prevent.submit(event => {
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.on.click(() => {
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.on.click(() => {
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.$val();
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.on.blur(validateForm),
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.on.blur(validateForm),
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.on.blur(validateForm),
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.on.prevent.submit(() => {
336
+ .nd.onPreventSubmit(() => {
337
337
  if (validateForm()) {
338
- console.log("Form is valid!", formData.$val());
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](docs/conditional-rendering.md)** - Dynamic content
379
- - **[Routing](docs/routing.md)** - Navigation and URL management
380
- - **[State Management](docs/state-management.md)** - Global state patterns
381
- - **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
382
- - **[Memory Management](docs/memory-management.md)** - Memory management
383
- - **[Anchor](docs/anchor.md)** - Anchor
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
@@ -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.on.click(() => count.set(count.val() + 1))
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.on.click(() => count.set(count.val() + 1))
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.on.click(() => {
146
- count.set(count.val() - 1);
145
+ Button('Decrease').nd.onClick(() => {
146
+ count.$value--;
147
147
  }),
148
148
 
149
- Button('Reset').nd.on.click(() => {
149
+ Button('Reset').nd.onClick(() => {
150
150
  count.set(0);
151
151
  }),
152
152
 
153
- Button('Increase').nd.on.click(() => {
154
- count.set(count.val() + 1);
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.on.click()` attaches click event listeners
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.on.click(addTodo)
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.on.change((e) => {
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.on.click(() => {
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.on.click(() => filter.set('all')),
253
- Button('Active').nd.on.click(() => filter.set('active')),
254
- Button('Completed').nd.on.click(() => filter.set('completed'))
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.on.click()` - Add click event listener
277
- - `.nd.on.change()` - Add change event listener
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.on.change(onToggle),
315
+ }).nd.onChange(onToggle),
316
316
 
317
317
  Div(['Task: ', todo.text]),
318
318
 
319
- Button('Delete').nd.on.click(onDelete)
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](docs/core-concepts.md)** - Understanding the fundamentals
354
- - **[Observables](docs/observables.md)** - Reactive state management
355
- - **[Elements](docs/elements.md)** - Creating and composing UI
356
- - **[Conditional Rendering](docs/conditional-rendering.md)** - Dynamic content
357
- - **[Routing](docs/routing.md)** - Navigation and URL management
358
- - **[State Management](docs/state-management.md)** - Global state patterns
359
- - **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
360
- - **[Memory Management](docs/memory-management.md)** - Memory management
361
- - **[Anchor](docs/anchor.md)** - Anchor
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
 
@@ -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](docs/memory-management.md)** - Memory management
102
- - **[Anchor](docs/anchor.md)** - Anchor
101
+ - **[Memory Management](memory-management.md)** - Memory management
102
+ - **[Anchor](anchor.md)** - Anchor
103
103
 
104
104
 
105
105