native-document 1.0.9 → 1.0.11

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,403 @@
1
+ # Getting Started
2
+
3
+ Welcome to NativeDocument! This guide will help you set up and create your first application with NativeDocument.
4
+
5
+ ## Installation
6
+
7
+ NativeDocument offers multiple installation methods to fit your development workflow.
8
+
9
+ ### Method 1: CDN (Recommended for beginners)
10
+
11
+ The fastest way to get started is using our CDN. Simply add this script tag to your HTML:
12
+
13
+ ```html
14
+ <!DOCTYPE html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="UTF-8">
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ <title>My NativeDocument App</title>
20
+ </head>
21
+ <body>
22
+ <script src="https://cdn.jsdelivr.net/gh/afrocodeur/native-document@latest/dist/native-document.min.js"></script>
23
+ <script>
24
+ const { Div, Button } = NativeDocument.elements;
25
+ const { Observable } = NativeDocument;
26
+
27
+ // Your code here
28
+ const count = Observable(0);
29
+
30
+ const App = Div({ class: 'app' }, [
31
+ Div(['Count: ', count]),
32
+ Button('Increment').nd.on.click(() => count.set(count.val() + 1))
33
+ ]);
34
+
35
+ document.body.appendChild(App);
36
+ </script>
37
+ </body>
38
+ </html>
39
+ ```
40
+
41
+ ### Method 2: Vite Template (Recommended for projects)
42
+
43
+ For a complete development setup with Vite, use our official template:
44
+
45
+ ```bash
46
+ npx degit afrocodeur/native-document-vite my-app
47
+ cd my-app
48
+ npm install
49
+ npm run dev
50
+ ```
51
+
52
+ This template includes:
53
+ - Pre-configured Vite setup
54
+ - Development server with auto reload
55
+ - Build optimization
56
+ - Example components
57
+
58
+ ### Method 3: NPM/Yarn Package
59
+
60
+ Install NativeDocument as a dependency in your existing project:
61
+
62
+ ```bash
63
+ npm install native-document
64
+ # or
65
+ yarn add native-document
66
+ ```
67
+
68
+ Then import what you need:
69
+
70
+ ```javascript
71
+ import { Div, Button } from 'native-document/src/elements'
72
+ import { Observable } from 'native-document'
73
+
74
+ const count = Observable(0);
75
+
76
+ const App = Div({ class: 'app' }, [
77
+ Div(['Count: ', count]),
78
+ Button('Increment').nd.on.click(() => count.set(count.val() + 1))
79
+ ]);
80
+
81
+ document.body.appendChild(App);
82
+ ```
83
+
84
+ ## Your First Application
85
+
86
+ Let's build a simple counter application to understand NativeDocument basics.
87
+
88
+ ### Step 1: Create the HTML Structure
89
+
90
+ ```html
91
+ <!DOCTYPE html>
92
+ <html lang="en">
93
+ <head>
94
+ <meta charset="UTF-8">
95
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
96
+ <title>Counter App</title>
97
+ <style>
98
+ .counter-app {
99
+ max-width: 400px;
100
+ margin: 50px auto;
101
+ padding: 20px;
102
+ text-align: center;
103
+ font-family: Arial, sans-serif;
104
+ }
105
+ .count-display {
106
+ font-size: 2rem;
107
+ margin: 20px 0;
108
+ color: #333;
109
+ }
110
+ button {
111
+ margin: 0 10px;
112
+ padding: 10px 20px;
113
+ font-size: 1rem;
114
+ cursor: pointer;
115
+ }
116
+ </style>
117
+ </head>
118
+ <body>
119
+ <script src="https://cdn.jsdelivr.net/gh/afrocodeur/native-document@latest/dist/native-document.min.js"></script>
120
+ <script>
121
+ // Your JavaScript code will go here
122
+ </script>
123
+ </body>
124
+ </html>
125
+ ```
126
+
127
+ ### Step 2: Add the JavaScript Logic
128
+
129
+ ```javascript
130
+ const { Div, Button, H1 } = NativeDocument.elements;
131
+ const { Observable } = NativeDocument;
132
+
133
+ // Create reactive state
134
+ const count = Observable(0);
135
+
136
+ // Create the application
137
+ const CounterApp = Div({ class: 'counter-app' }, [
138
+ H1('Counter Application'),
139
+
140
+ Div({ class: 'count-display' }, [
141
+ 'Current count: ', count
142
+ ]),
143
+
144
+ Div([
145
+ Button('Decrease').nd.on.click(() => {
146
+ count.set(count.val() - 1);
147
+ }),
148
+
149
+ Button('Reset').nd.on.click(() => {
150
+ count.set(0);
151
+ }),
152
+
153
+ Button('Increase').nd.on.click(() => {
154
+ count.set(count.val() + 1);
155
+ })
156
+ ])
157
+ ]);
158
+
159
+ // Mount the application
160
+ document.body.appendChild(CounterApp);
161
+ ```
162
+
163
+ ### Step 3: Understanding What Happened
164
+
165
+ 1. **Imported Components**: We used `Div`, `Button`, and `H1` from `NativeDocument.elements`
166
+ 2. **Created Reactive State**: `Observable(0)` creates a reactive value that starts at 0
167
+ 3. **Built the UI**: Elements are created with attributes and children
168
+ 4. **Added Event Handlers**: `.nd.on.click()` attaches click event listeners
169
+ 5. **Automatic Updates**: When `count` changes, the UI updates automatically
170
+
171
+ ## Todo List Application
172
+
173
+ Let's build something more complex - a todo list with add, delete, and filter functionality:
174
+
175
+ ```javascript
176
+ const { Div, Input, Button, ShowIf, ForEach } = NativeDocument.elements;
177
+ const { Observable } = NativeDocument;
178
+
179
+ // Reactive state
180
+ const todos = Observable.array([]);
181
+ const newTodo = Observable('');
182
+ const filter = Observable('all'); // 'all', 'active', 'completed'
183
+
184
+ // Computed values
185
+ const filteredTodos = Observable.computed(() => {
186
+ const allTodos = todos.val();
187
+ const currentFilter = filter.val();
188
+
189
+ if (currentFilter === 'active') {
190
+ return allTodos.filter(todo => !todo.done);
191
+ }
192
+ if (currentFilter === 'completed') {
193
+ return allTodos.filter(todo => todo.done);
194
+ }
195
+
196
+ return [...allTodos];
197
+ }, [todos, filter]);
198
+
199
+ const addTodo = () => {
200
+ if (!newTodo.val().trim()) {
201
+ return;
202
+ }
203
+ todos.push({
204
+ id: Date.now(),
205
+ text: newTodo.val().trim(),
206
+ done: false
207
+ });
208
+ newTodo.set('');
209
+ };
210
+
211
+ // Todo application
212
+ const TodoApp = Div({ class: 'todo-app' }, [
213
+ // Header
214
+ Div({ class: 'header' }, [
215
+ Input({
216
+ placeholder: 'What needs to be done?',
217
+ value: newTodo
218
+ }),
219
+ Button('Add').nd.on.click(addTodo)
220
+ ]),
221
+
222
+ // Todo list container
223
+ Div({ class: 'todos-list'}, [
224
+ ShowIf(todos.check(list => list.length === 0),
225
+ Div({ class: 'empty' }, 'No todos yet! Add one above.')), // Empty state
226
+
227
+ // List of todos
228
+ ForEach(filteredTodos, (todo, index) => // Todo list
229
+ Div({ class: 'todo-item' }, [
230
+ Input({
231
+ type: 'checkbox',
232
+ checked: Observable(todo.done)
233
+ }).nd.on.change((e) => {
234
+ const todoList = todos.val();
235
+ todoList[index.val()].done = e.target.checked;
236
+ todos.set([...todoList]);
237
+ }),
238
+
239
+ Div(['Task: ', todo.text]),
240
+
241
+ Button('Delete').nd.on.click(() => {
242
+ todos.splice(index.val(), 1);
243
+ })
244
+ ]),
245
+ // Key function for efficient updates
246
+ (item) => item.id
247
+ )
248
+ ]),
249
+
250
+ // Filters
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'))
255
+ ])
256
+
257
+ ]);
258
+
259
+ document.body.appendChild(TodoApp);
260
+ ```
261
+
262
+ ## Key Concepts Demonstrated
263
+
264
+ ### Observables
265
+ - `Observable(value)` - Creates reactive primitive values
266
+ - `Observable.array([])` - Creates reactive arrays with array methods
267
+ - `Observable.computed(() => {}, [deps])` - Creates computed values
268
+
269
+ ### Elements
270
+ - Elements are functions that return DOM nodes
271
+ - First parameter is attributes object (optional)
272
+ - Second parameter is children array or single child
273
+ - Children can be strings, numbers, elements, or observables
274
+
275
+ ### Event Handling
276
+ - `.nd.on.click()` - Add click event listener
277
+ - `.nd.on.change()` - Add change event listener
278
+ - Event handlers receive the native event object
279
+
280
+ ### Conditional Rendering
281
+ - `ShowIf(condition, content)` - Show content when condition is true
282
+ - `ForEach(array, callback, propertyKey || keyFn)` - Render lists efficiently
283
+
284
+ ## Project Structure
285
+
286
+ For larger applications, organize your code like this:
287
+
288
+ ```
289
+ my-app/
290
+ ├── index.html
291
+ ├── src/
292
+ │ ├── main.js # Application entry point
293
+ │ ├── components/ # Reusable components
294
+ │ │ ├── TodoItem.js
295
+ │ │ └── Header.js
296
+ │ ├── stores/ # Global state
297
+ │ │ └── TodoStore.js
298
+ │ └── utils/ # Helper functions
299
+ │ └── validators.js
300
+ ├── styles/
301
+ │ └── main.css
302
+ └── package.json
303
+ ```
304
+
305
+ ### Example Component (components/TodoItem.js)
306
+
307
+ ```javascript
308
+ import { Div, Input, Button } from 'native-document/src/elements';
309
+
310
+ export function TodoItem(todo, onToggle, onDelete) {
311
+ return Div({ class: 'todo-item' }, [
312
+ Input({
313
+ type: 'checkbox',
314
+ checked: todo.done
315
+ }).nd.on.change(onToggle),
316
+
317
+ Div(['Task: ', todo.text]),
318
+
319
+ Button('Delete').nd.on.click(onDelete)
320
+ ]);
321
+ }
322
+ ```
323
+
324
+ ## Development Workflow
325
+
326
+ ### Auto Reload with Vite
327
+
328
+ When using the Vite template, your development server automatically reloads when you make changes:
329
+
330
+ ```bash
331
+ npm run dev # Start development server
332
+ npm run build # Build for production
333
+ npm run preview # Preview production build
334
+ ```
335
+
336
+ ## Browser Support
337
+
338
+ NativeDocument works in all modern browsers that support:
339
+ - ES6 Modules
340
+ - Proxy objects
341
+ - FinalizationRegistry (for automatic memory management)
342
+
343
+ **Supported browsers:**
344
+ - Chrome 84+
345
+ - Firefox 79+
346
+ - Safari 14.1+
347
+ - Edge 84+
348
+
349
+ ## Next Steps
350
+
351
+ Now that you've built your first NativeDocument applications, explore these topics:
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
362
+
363
+ ## Common Issues
364
+
365
+ ### Import Errors
366
+ **Problem**: `Cannot resolve module 'native-document'`
367
+
368
+ **Solution**: Make sure you're using the correct import path:
369
+ ```javascript
370
+ // Correct
371
+ import { Div } from 'native-document/src/elements'
372
+ import { Observable } from 'native-document'
373
+
374
+ // CDN
375
+ const { Div } = NativeDocument.elements;
376
+ const { Observable } = NativeDocument;
377
+ ```
378
+
379
+ ### Observable Not Updating
380
+ **Problem**: UI doesn't update when you change observable values
381
+
382
+ **Solution**: Make sure you're using `.set()` method:
383
+ ```javascript
384
+ // Wrong
385
+ count = 5;
386
+
387
+ // Correct
388
+ count.set(5);
389
+ // Correct
390
+ count.$value = 5;
391
+ ```
392
+
393
+ ### Memory Leaks
394
+ **Problem**: Application slows down over time
395
+
396
+ **Solution**: NativeDocument has automatic memory management, but you can help by cleaning up manual subscriptions:
397
+ ```javascript
398
+ const unsubscribe = observable.subscribe(callback);
399
+ // Later...
400
+ unsubscribe(); // Clean up manually if needed
401
+ ```
402
+
403
+ Ready to dive deeper? Continue with [Core Concepts](core-concepts.md)!
@@ -0,0 +1,106 @@
1
+ # Lifecycle Events
2
+
3
+ NativeDocument provides lifecycle hooks that let you execute code when elements are added to or removed from the DOM. This is essential for setup, cleanup, and managing resources.
4
+
5
+ ## Basic Lifecycle Hooks
6
+
7
+ ### mounted() - Element Added to DOM
8
+
9
+ ```javascript
10
+ const myComponent = Div("Hello World")
11
+ .nd.mounted(element => {
12
+ console.log("Element is now in the DOM!", element);
13
+ // Setup code here
14
+ });
15
+
16
+ document.body.appendChild(myComponent); // Triggers mounted callback
17
+ ```
18
+
19
+ ### unmounted() - Element Removed from DOM
20
+
21
+ ```javascript
22
+ const myComponent = Div("Temporary content")
23
+ .nd.unmounted(element => {
24
+ console.log("Element removed from DOM!", element);
25
+ // Cleanup external resources only
26
+ // Element can be re-injected later
27
+ });
28
+
29
+ // Later, when element is removed
30
+ myComponent.remove(); // Triggers unmounted callback
31
+ ```
32
+
33
+ ## Combined Lifecycle Management
34
+
35
+ ```javascript
36
+ const timer = Div("Timer: 0")
37
+ .nd.lifecycle({
38
+ mounted(element) {
39
+ console.log("Timer started");
40
+ element.intervalId = setInterval(() => {
41
+ element.textContent = `Timer: ${Date.now()}`;
42
+ }, 1000);
43
+ },
44
+ unmounted(element) {
45
+ console.log("Timer stopped");
46
+ clearInterval(element.intervalId);
47
+ }
48
+ });
49
+ ```
50
+
51
+ ## Practical Examples
52
+
53
+ ### Auto-focus Input Field
54
+
55
+ ```javascript
56
+ const focusInput = Input({ placeholder: "Auto-focused" })
57
+ .nd.mounted(input => {
58
+ input.focus();
59
+ });
60
+ ```
61
+
62
+ ### Event Listener Cleanup
63
+
64
+ ```javascript
65
+
66
+ const MyButton = function() {
67
+ const handler = () => console.log("Global click detected");
68
+ return Button("Click me")
69
+ .nd.mounted(button => {
70
+ document.addEventListener('click', handler);
71
+ })
72
+ .nd.unmounted(button => {
73
+ // Clean up external listeners, but keep observables intact
74
+ document.removeEventListener('click', handler);
75
+ // DON'T cleanup observables unless element won't be reused
76
+ });
77
+ }
78
+ ```
79
+
80
+ ### Observable Management Warning
81
+
82
+ ```javascript
83
+ const reusableComponent = Div()
84
+ .nd.unmounted(element => {
85
+ // AVOID: Don't cleanup observables if element might be re-injected
86
+ // myObservable.cleanup(); // Only do this if element is permanently destroyed
87
+
88
+ // GOOD: Only cleanup external resources
89
+ clearInterval(element.timerId);
90
+ element.websocket?.close();
91
+ });
92
+
93
+ // Element can be safely re-appended later
94
+ document.body.appendChild(reusableComponent);
95
+ ```
96
+
97
+ ## Next Steps
98
+
99
+ Now that you understand lifecycle events, explore these related topics:
100
+
101
+ - **[Memory Management](docs/memory-management.md)** - Memory management
102
+ - **[Anchor](docs/anchor.md)** - Anchor
103
+
104
+
105
+
106
+
@@ -0,0 +1,90 @@
1
+ # Memory Management
2
+
3
+ NativeDocument includes an advanced automatic memory management system that prevents memory leaks and optimizes performance using modern browser APIs like FinalizationRegistry.
4
+
5
+ ## Automatic Observable Cleanup
6
+
7
+ ### How It Works
8
+
9
+ ```javascript
10
+ const data = Observable("test");
11
+
12
+ // When 'data' goes out of scope and is garbage collected,
13
+ // NativeDocument automatically cleans up its internal listeners
14
+ // No manual cleanup required!
15
+
16
+ function createComponent() {
17
+ const localObservable = Observable(42);
18
+ return Div(localObservable); // Observable auto-cleaned when component is GC'd
19
+ }
20
+ ```
21
+
22
+ ### Memory Registry System
23
+
24
+ ```javascript
25
+ // Each observable is automatically registered for cleanup
26
+ const count = Observable(0);
27
+ console.log(count.toString()); // "{{#ObItem::(1)}}" - Shows internal ID
28
+
29
+ // When count becomes unreachable, cleanup happens automatically
30
+ // via FinalizationRegistry
31
+ ```
32
+
33
+ ## Manual Memory Management
34
+
35
+ ### Force Cleanup
36
+
37
+ ```javascript
38
+ const obs = Observable("data");
39
+ const unsubscribe = obs.subscribe(console.log);
40
+
41
+ // Manual cleanup if needed
42
+ obs.cleanup(); // Removes all listeners immediately
43
+ // obs is now unusable - will warn on new subscriptions
44
+ ```
45
+
46
+ ### Global Cleanup
47
+
48
+ ```javascript
49
+ // Clean all orphaned observables
50
+ Observable.cleanup(); // Force cleanup of all registered observables
51
+
52
+ // Auto-cleanup configuration
53
+ Observable.autoCleanup(true, {
54
+ interval: 60000, // Check every minute
55
+ threshold: 100 // Clean when 100+ orphaned observables
56
+ });
57
+ ```
58
+
59
+ ## Performance Monitoring
60
+
61
+ ### Debug Mode
62
+
63
+ ```javascript
64
+ // Enable memory debugging
65
+ NativeDocument.debug.enable();
66
+
67
+ // Monitor cleanup events in console
68
+ const obs = Observable("test");
69
+ obs = null; // Will log cleanup when GC runs
70
+ ```
71
+
72
+ ### Memory Leak Detection
73
+
74
+ ```javascript
75
+ // Check for potential leaks
76
+ window.addEventListener('beforeunload', () => {
77
+ // Force cleanup on page unload
78
+ });
79
+ ```
80
+
81
+ ## Best Practices
82
+
83
+ 1. **Trust the system** - Let automatic cleanup handle most cases
84
+ 2. **Manual cleanup** only for critical resources or immediate needs
85
+ 3. **Enable auto-cleanup** in long-running applications
86
+ 4. **Use debug mode** during development to monitor memory usage
87
+
88
+ ## Next Steps
89
+
90
+ - **[Anchor](docs/anchor.md)** - Anchor