native-document 1.0.10 → 1.0.12

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.
@@ -0,0 +1,513 @@
1
+ # Core Concepts
2
+
3
+ This guide covers the fundamental concepts and philosophy behind NativeDocument. Understanding these principles will help you build better applications and make the most of the framework's capabilities.
4
+
5
+ ## Philosophy
6
+
7
+ NativeDocument was designed with several core principles in mind:
8
+
9
+ ### Native-First Approach
10
+ Unlike frameworks that abstract away the DOM, NativeDocument embraces it. Every element you create is a real DOM node, and every interaction happens through native browser APIs. This means:
11
+
12
+ - No virtual DOM overhead
13
+ - Direct access to all browser features
14
+ - Familiar debugging experience
15
+ - Better performance for DOM-heavy applications
16
+
17
+ ### Reactive by Design
18
+ Reactivity is built into the core of NativeDocument through observables. When data changes, the UI updates automatically without manual DOM manipulation:
19
+
20
+ ```javascript
21
+ const { Div } = NativeDocument.elements;
22
+ const { Observable } = NativeDocument;
23
+
24
+ const message = Observable('Hello World');
25
+ const display = Div(message);
26
+
27
+ // UI updates automatically
28
+ message.set('Hello NativeDocument!');
29
+ ```
30
+
31
+ ### Zero Build Requirement
32
+ While you can use build tools, NativeDocument works perfectly without them. Load it from a CDN and start building immediately:
33
+
34
+ ```html
35
+ <script src="https://cdn.jsdelivr.net/gh/afrocodeur/native-document@latest/dist/native-document.min.js"></script>
36
+ <script>
37
+ // Start building immediately
38
+ const { Div } = NativeDocument.elements;
39
+ // Your app here
40
+ </script>
41
+ ```
42
+
43
+ ## Core Architecture
44
+
45
+ ### Observables - The Reactive Foundation
46
+
47
+ Observables are the heart of NativeDocument's reactivity system. They wrap values and notify subscribers when changes occur.
48
+
49
+ #### Basic Observable
50
+ ```javascript
51
+ const count = Observable(0);
52
+
53
+ // Subscribe to changes
54
+ count.subscribe(newValue => {
55
+ console.log('Count changed to:', newValue);
56
+ });
57
+
58
+ // Update the value
59
+ count.set(5); // Logs: "Count changed to: 5"
60
+ ```
61
+
62
+ #### Observable Objects
63
+ ```javascript
64
+ const user = Observable({ name: 'John', age: 25 });
65
+
66
+ // Access values
67
+ console.log(user.val().name); // "John"
68
+ console.log(user.$value.name); // "John" (proxy syntax)
69
+
70
+ // Update object - replaces entire value
71
+ user.set({ ...user.val(), name: 'Jane' });
72
+
73
+ // This won't trigger reactivity (common mistake)
74
+ // user.name = 'Jane'; // Wrong!
75
+ ```
76
+
77
+ #### Observable Arrays
78
+ ```javascript
79
+ const todos = Observable.array([]);
80
+
81
+ // Array methods trigger reactivity
82
+ todos.push({ text: 'Learn NativeDocument', done: false });
83
+ todos.pop();
84
+ todos.sort((a, b) => a.text.localeCompare(b.text));
85
+
86
+ // Access like normal array
87
+ console.log(todos.val().length);
88
+ ```
89
+
90
+ #### Computed Observables
91
+ ```javascript
92
+ const firstName = Observable('John');
93
+ const lastName = Observable('Doe');
94
+
95
+ const fullName = Observable.computed(() => {
96
+ return `${firstName.val()} ${lastName.val()}`;
97
+ }, [firstName, lastName]);
98
+
99
+ // Updates automatically when dependencies change
100
+ firstName.set('Jane'); // fullName becomes "Jane Doe"
101
+ ```
102
+
103
+ ### Elements - Building the UI
104
+
105
+ Elements in NativeDocument are functions that create and return DOM nodes. They follow a consistent pattern:
106
+
107
+ #### Basic Element Creation
108
+ ```javascript
109
+ const { Div, Button, Input } = NativeDocument.elements;
110
+
111
+ // Element with no attributes or children
112
+ const simpleDiv = Div();
113
+
114
+ // Element with attributes only
115
+ const styledDiv = Div({ class: 'container', id: 'main' });
116
+
117
+ // Element with children only
118
+ const textDiv = Div('Hello World');
119
+ const arrayDiv = Div(['Hello ', 'World']);
120
+
121
+ // Element with both attributes and children
122
+ const fullDiv = Div({ class: 'card' }, [
123
+ 'Content here'
124
+ ]);
125
+ ```
126
+
127
+ #### Reactive Attributes
128
+ ```javascript
129
+ const isVisible = Observable(true);
130
+ const theme = Observable('dark');
131
+
132
+ const element = Div({
133
+ class: {
134
+ 'visible': isVisible,
135
+ 'dark-theme': theme.check(t => t === 'dark')
136
+ },
137
+ style: {
138
+ opacity: isVisible.check(v => v ? 1 : 0.5)
139
+ }
140
+ });
141
+ ```
142
+
143
+ #### Event Handling
144
+ ```javascript
145
+ const counter = Observable(0);
146
+
147
+ const button = Button('Click me')
148
+ .nd.on.click(() => {
149
+ counter.set(counter.val() + 1);
150
+ });
151
+
152
+ // Multiple events
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));
157
+
158
+ //Or
159
+ const input = Input()
160
+ .nd.on({
161
+ focus: e => console.log('Focused'),
162
+ blur: e => console.log('Blurred'),
163
+ input: e => console.log('Value:', e.target.value)
164
+ })
165
+ ```
166
+
167
+ ### Lifecycle and Memory Management
168
+
169
+ NativeDocument includes automatic memory management to prevent memory leaks.
170
+
171
+ #### Element Lifecycle
172
+ ```javascript
173
+ const element = Div('Content')
174
+ .nd.mounted(el => {
175
+ console.log('Element added to DOM');
176
+ })
177
+ .nd.unmounted(el => {
178
+ console.log('Element removed from DOM');
179
+ });
180
+ ```
181
+
182
+ #### Manual Cleanup
183
+ For complex scenarios, you can manage cleanup manually:
184
+
185
+ ```javascript
186
+ const observable = Observable(0);
187
+
188
+ // Manual subscription
189
+ const unsubscribe = observable.subscribe(value => {
190
+ console.log('Value:', value);
191
+ });
192
+
193
+ // Clean up when needed
194
+ unsubscribe();
195
+ ```
196
+
197
+ ## Reactivity Model
198
+
199
+ ### Data Flow
200
+ NativeDocument follows a unidirectional data flow:
201
+
202
+ 1. **State Change**: An observable value is updated
203
+ 2. **Notification**: All subscribers are notified
204
+ 3. **DOM Update**: UI elements update automatically
205
+ 4. **Event Handling**: User interactions trigger new state changes
206
+
207
+ ```javascript
208
+ const items = Observable.array(['Apple', 'Banana']);
209
+
210
+ const list = ForEach(items, (item) =>
211
+ Div([
212
+ item,
213
+ Button('Remove').nd.on.click(() => {
214
+ // User action → State change → UI update
215
+ const index = items.val().indexOf(item);
216
+ items.splice(index, 1);
217
+ })
218
+ ])
219
+ );
220
+ ```
221
+
222
+ ### Reactive Chains
223
+ Observables can depend on other observables, creating reactive chains:
224
+
225
+ ```javascript
226
+ const price = Observable(100);
227
+ const quantity = Observable(2);
228
+ const discount = Observable(0.1);
229
+
230
+ const subtotal = Observable.computed(() => {
231
+ return price.val() * quantity.val();
232
+ }, [price, quantity]);
233
+
234
+ const total = Observable.computed(() => {
235
+ return subtotal.val() * (1 - discount.val());
236
+ }, [subtotal, discount]);
237
+
238
+ // Changing any value updates the chain
239
+ price.set(150); // subtotal and total update automatically
240
+ ```
241
+
242
+ ## Component Patterns
243
+
244
+ ### Functional Components
245
+ Create reusable components as functions:
246
+
247
+ ```javascript
248
+ function UserCard(user) {
249
+ return Div({ class: 'user-card' }, [
250
+ Div({ class: 'name' }, user.name),
251
+ Div({ class: 'email' }, user.email),
252
+ Button('Edit').nd.on.click(() => {
253
+ // Handle edit
254
+ })
255
+ ]);
256
+ }
257
+
258
+ // Usage
259
+ const user = { name: 'John Doe', email: 'john@example.com' };
260
+ const card = UserCard(user);
261
+ ```
262
+
263
+ ### Stateful Components
264
+ Components with internal state:
265
+
266
+ ```javascript
267
+ function Counter(initialValue = 0) {
268
+ const count = Observable(initialValue);
269
+
270
+ return Div({ class: 'counter' }, [
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))
275
+ ]);
276
+ }
277
+
278
+ // Usage
279
+ const myCounter = Counter(10);
280
+ ```
281
+
282
+ ### Higher-Order Components
283
+ Components that enhance other components:
284
+
285
+ ```javascript
286
+ function withLoading(component, isLoading) {
287
+ return When(isLoading.check(loading => !loading))
288
+ .show(component)
289
+ .otherwise(Div({ class: 'loading' }, 'Loading...'));
290
+ // Or
291
+
292
+ // return Switch(isLoading.check(loading => !loading),
293
+ // component,
294
+ // Div({ class: 'loading' }, 'Loading...')
295
+ // );
296
+ }
297
+
298
+ // Usage
299
+ const isLoading = Observable(true);
300
+ const content = Div('Main content');
301
+ const wrappedContent = withLoading(content, isLoading);
302
+ ```
303
+
304
+ ## State Management Patterns
305
+
306
+ ### Local State
307
+ For component-specific state, use observables directly:
308
+
309
+ ```javascript
310
+ function TodoForm() {
311
+ const text = Observable('');
312
+ const isValid = Observable.computed(() => text.val().trim().length > 0, [text]);
313
+
314
+ return Div([
315
+ Input({ placeholder: 'Enter todo...', value: text }),
316
+ Button('Add')
317
+ .nd.on.click(() => {
318
+ if (isValid.val()) {
319
+ // Add todo logic
320
+ text.set('');
321
+ }
322
+ })
323
+ ]);
324
+ }
325
+ ```
326
+
327
+ ### Shared State
328
+ For state shared between components, create a store:
329
+
330
+ ```javascript
331
+ // Create a shared store
332
+ const TodoStore = {
333
+ todos: Observable.array([]),
334
+
335
+ addTodo(text) {
336
+ this.todos.push({
337
+ id: Date.now(),
338
+ text: text,
339
+ done: false
340
+ });
341
+ },
342
+
343
+ removeTodo(id) {
344
+ const index = this.todos.val().findIndex(todo => todo.id === id);
345
+ if (index !== -1) {
346
+ this.todos.splice(index, 1);
347
+ }
348
+ }
349
+ };
350
+
351
+ // Use in components
352
+ function TodoList() {
353
+ return ForEach(TodoStore.todos, (todo) =>
354
+ Div([
355
+ todo.text,
356
+ Button('Delete').nd.on.click(() => {
357
+ TodoStore.removeTodo(todo.id);
358
+ })
359
+ ])
360
+ );
361
+ }
362
+ ```
363
+
364
+ ### Global State with Store
365
+ For complex applications, use the built-in Store:
366
+
367
+ ```javascript
368
+ const { Store } = NativeDocument;
369
+
370
+ // Create global observables
371
+ const userStore = Store.create('user', { name: '', isLoggedIn: false });
372
+ const themeStore = Store.create('theme', 'light');
373
+
374
+ // Use in components
375
+ function Header() {
376
+ const user = Store.use('user');
377
+ const theme = Store.use('theme');
378
+
379
+ return Div({ class: `theme-${theme}` }, [
380
+ ShowIf(user.check(u => u.isLoggedIn),
381
+ Div(['Welcome, ', user.$value.name])
382
+ )
383
+ ]);
384
+ }
385
+ ```
386
+
387
+ ## Error Handling
388
+
389
+ ### Graceful Error Handling
390
+ Wrap potentially failing operations:
391
+
392
+ ```javascript
393
+ function SafeComponent() {
394
+ try {
395
+ return riskyOperation();
396
+ } catch (error) {
397
+ console.error('Component error:', error);
398
+ return Div({ class: 'error' }, 'Something went wrong');
399
+ }
400
+ }
401
+ ```
402
+
403
+ ### Error Boundaries
404
+ Use error boundaries for robust applications:
405
+
406
+ ```javascript
407
+ function withErrorBoundary(component) {
408
+ return component.errorBoundary((error) => {
409
+ console.error('Error caught:', error);
410
+ return Div({ class: 'error-boundary' }, [
411
+ 'An error occurred. Please try again.'
412
+ ]);
413
+ });
414
+ }
415
+ ```
416
+
417
+ ## Performance Considerations
418
+
419
+ ### Efficient Updates
420
+ NativeDocument optimizes updates automatically, but you can help:
421
+
422
+ ```javascript
423
+ // Good: Batch related updates
424
+ function updateUser(newData) {
425
+ user.set({ ...user.val(), ...newData });
426
+ }
427
+
428
+ // Less efficient: Multiple separate updates
429
+ function updateUserSeparately(name, email) {
430
+ user.set({ ...user.val(), name });
431
+ user.set({ ...user.val(), email });
432
+ }
433
+ ```
434
+
435
+ ### List Rendering
436
+ Use key functions for efficient list updates:
437
+
438
+ ```javascript
439
+ ForEach(items, (item) =>
440
+ Div(['Item: ', item.name]),
441
+ // Key function for efficient updates
442
+ (item) => item.id
443
+ );
444
+ // or
445
+ ForEach(items, (item) =>
446
+ Div(['Item: ', item.name]),
447
+ // Key property for efficient updates
448
+ 'id'
449
+ );
450
+ ```
451
+
452
+ ## Best Practices
453
+
454
+ ### 1. Keep Components Small and Focused
455
+ ```javascript
456
+ // Good: Focused component
457
+ function UserName(user) {
458
+ return Div({ class: 'user-name' }, user.name);
459
+ }
460
+
461
+ // Less ideal: Component doing too much
462
+ function UserEverything(user) {
463
+ // Handles name, email, avatar, settings, etc.
464
+ }
465
+ ```
466
+
467
+ ### 2. Use Computed Values for Derived State
468
+ ```javascript
469
+ // Good: Computed value
470
+ const filteredItems = Observable.computed(() => {
471
+ return items.val().filter(item => item.visible);
472
+ }, [items]);
473
+
474
+ // Less efficient: Manual filtering on each render
475
+ ```
476
+
477
+ ### 3. Separate Concerns
478
+ ```javascript
479
+ // Good: Separate data logic from UI
480
+ const UserService = {
481
+ async loadUser(id) {
482
+ // Data loading logic
483
+ }
484
+ };
485
+
486
+ function UserProfile(userId) {
487
+ // UI rendering logic
488
+ }
489
+ ```
490
+
491
+ ### 4. Use Meaningful Names
492
+ ```javascript
493
+ // Good: Clear naming
494
+ const isUserLoggedIn = Observable(false);
495
+ const currentUserName = Observable('');
496
+
497
+ // Less clear
498
+ const flag = Observable(false);
499
+ const data = Observable('');
500
+ ```
501
+
502
+ ## Next Steps
503
+
504
+ Now that you understand NativeDocument's core concepts, explore these advanced topics:
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