native-document 1.0.19 → 1.0.20
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/docs/anchor.md +3 -0
- package/docs/conditional-rendering.md +3 -0
- package/docs/core-concepts.md +3 -0
- package/docs/elements.md +3 -0
- package/docs/extending-native-document-element.md +268 -0
- package/docs/getting-started.md +3 -0
- package/docs/lifecycle-events.md +3 -0
- package/docs/list-rendering.md +6 -0
- package/docs/native-document-element.md +279 -0
- package/docs/observables.md +3 -0
- package/docs/routing.md +3 -0
- package/docs/state-management.md +3 -0
- package/docs/validation.md +4 -2
- package/eslint.config.js +22 -0
- package/package.json +5 -3
- package/readme.md +4 -21
- package/src/data/ObservableChecker.js +2 -1
- package/src/data/ObservableItem.js +2 -1
- package/src/wrappers/NDElement.js +7 -7
package/docs/anchor.md
CHANGED
|
@@ -372,4 +372,7 @@ anchor.replaceContent(content2);
|
|
|
372
372
|
- **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
|
|
373
373
|
- **[Routing](routing.md)** - Navigation and URL management
|
|
374
374
|
- **[State Management](state-management.md)** - Global state patterns
|
|
375
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
376
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
377
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
375
378
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
@@ -625,5 +625,8 @@ Now that you understand conditional rendering, explore these related topics:
|
|
|
625
625
|
- **[Routing](routing.md)** - Navigation and URL management
|
|
626
626
|
- **[State Management](state-management.md)** - Global state patterns
|
|
627
627
|
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
628
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
629
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
630
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
628
631
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
629
632
|
- **[Anchor](anchor.md)** - Anchor
|
package/docs/core-concepts.md
CHANGED
|
@@ -510,5 +510,8 @@ Now that you understand NativeDocument's core concepts, explore these advanced t
|
|
|
510
510
|
- **[Routing](routing.md)** - Navigation and URL management
|
|
511
511
|
- **[State Management](state-management.md)** - Global state patterns
|
|
512
512
|
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
513
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
514
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
515
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
513
516
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
514
517
|
- **[Anchor](anchor.md)** - Anchor
|
package/docs/elements.md
CHANGED
|
@@ -380,5 +380,8 @@ Now that you understand NativeDocument's elements, explore these advanced topics
|
|
|
380
380
|
- **[Routing](routing.md)** - Navigation and URL management
|
|
381
381
|
- **[State Management](state-management.md)** - Global state patterns
|
|
382
382
|
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
383
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
384
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
385
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
383
386
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
384
387
|
- **[Anchor](anchor.md)** - Anchor
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Extending NDElement - Custom Methods Guide
|
|
2
|
+
|
|
3
|
+
NDElement is designed to be extensible, allowing developers to add custom methods that make their code more readable and maintainable. This guide shows how to create custom NDElement extensions for common patterns.
|
|
4
|
+
|
|
5
|
+
## Why Extend NDElement?
|
|
6
|
+
|
|
7
|
+
Extending NDElement allows you to:
|
|
8
|
+
- **Encapsulate common patterns** into reusable methods
|
|
9
|
+
- **Improve code readability** with domain-specific method names
|
|
10
|
+
- **Reduce boilerplate** by abstracting complex event handling
|
|
11
|
+
- **Create a consistent API** across your application
|
|
12
|
+
|
|
13
|
+
## Basic Extension Pattern
|
|
14
|
+
|
|
15
|
+
The simplest way to extend NDElement is by adding methods to its prototype:
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
// Basic extension
|
|
19
|
+
NDElement.prototype.customMethod = function(/* parameters */) {
|
|
20
|
+
// Your logic here
|
|
21
|
+
return this; // Return 'this' for method chaining
|
|
22
|
+
};
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Common Extension Examples
|
|
26
|
+
|
|
27
|
+
### 1. Keyboard Event Shortcuts
|
|
28
|
+
|
|
29
|
+
Instead of writing complex keyboard event handlers, create semantic shortcuts:
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
// Enter key handler
|
|
33
|
+
NDElement.prototype.onEnter = function(callback) {
|
|
34
|
+
this.$element.addEventListener('keyup', e => {
|
|
35
|
+
if (e.key === 'Enter') {
|
|
36
|
+
callback(e);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return this;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Escape key handler
|
|
43
|
+
NDElement.prototype.onEscape = function(callback) {
|
|
44
|
+
this.$element.addEventListener('keyup', e => {
|
|
45
|
+
if (e.key === 'Escape') {
|
|
46
|
+
callback(e);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return this;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Arrow keys handler
|
|
53
|
+
NDElement.prototype.onArrowKey = function(callback) {
|
|
54
|
+
this.$element.addEventListener('keydown', e => {
|
|
55
|
+
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
|
56
|
+
callback(e, e.key);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return this;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Usage
|
|
63
|
+
Input({ type: 'text' })
|
|
64
|
+
.nd.onEnter(e => console.log('Form submitted'))
|
|
65
|
+
.onEscape(e => e.target.blur())
|
|
66
|
+
.onArrowKey((e, direction) => console.log('Arrow pressed:', direction));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Form Validation Extensions
|
|
70
|
+
|
|
71
|
+
Create semantic validation methods:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
// Required field validation
|
|
75
|
+
NDElement.prototype.required = function(message = 'This field is required') {
|
|
76
|
+
this.$element.addEventListener('blur', e => {
|
|
77
|
+
const value = e.target.value.trim();
|
|
78
|
+
if (!value) {
|
|
79
|
+
this.showError(message);
|
|
80
|
+
} else {
|
|
81
|
+
this.clearError();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return this;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Email validation
|
|
88
|
+
NDElement.prototype.email = function(message = 'Please enter a valid email') {
|
|
89
|
+
this.$element.addEventListener('blur', e => {
|
|
90
|
+
const email = e.target.value.trim();
|
|
91
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
92
|
+
if (email && !emailRegex.test(email)) {
|
|
93
|
+
this.showError(message);
|
|
94
|
+
} else {
|
|
95
|
+
this.clearError();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return this;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Min length validation
|
|
102
|
+
NDElement.prototype.minLength = function(length, message) {
|
|
103
|
+
message = message || `Minimum ${length} characters required`;
|
|
104
|
+
this.$element.addEventListener('input', e => {
|
|
105
|
+
if (e.target.value.length < length && e.target.value.length > 0) {
|
|
106
|
+
this.showError(message);
|
|
107
|
+
} else {
|
|
108
|
+
this.clearError();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return this;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Error display helpers
|
|
115
|
+
NDElement.prototype.showError = function(message) {
|
|
116
|
+
this.clearError();
|
|
117
|
+
const errorElement = Span({
|
|
118
|
+
class: 'error-message',
|
|
119
|
+
style: 'color: red; font-size: 0.8rem'
|
|
120
|
+
}, message);
|
|
121
|
+
|
|
122
|
+
this.$element.parentNode.appendChild(errorElement);
|
|
123
|
+
this.$element.classList.add('error');
|
|
124
|
+
return this;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
NDElement.prototype.clearError = function() {
|
|
128
|
+
const parent = this.$element.parentNode;
|
|
129
|
+
const existingError = parent.querySelector('.error-message');
|
|
130
|
+
if (existingError) {
|
|
131
|
+
existingError.remove();
|
|
132
|
+
}
|
|
133
|
+
this.$element.classList.remove('error');
|
|
134
|
+
return this;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Usage
|
|
138
|
+
Input({ type: 'email', placeholder: 'Email' })
|
|
139
|
+
.nd.required()
|
|
140
|
+
.email();
|
|
141
|
+
|
|
142
|
+
Input({ type: 'password', placeholder: 'Password' })
|
|
143
|
+
.nd.required()
|
|
144
|
+
.minLength(8, 'Password must be at least 8 characters');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 3. Animation Extensions
|
|
148
|
+
|
|
149
|
+
Create smooth animation helpers:
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Fade in animation
|
|
153
|
+
NDElement.prototype.fadeIn = function(duration = 300) {
|
|
154
|
+
this.$element.style.opacity = '0';
|
|
155
|
+
this.$element.style.transition = `opacity ${duration}ms ease-in-out`;
|
|
156
|
+
|
|
157
|
+
requestAnimationFrame(() => {
|
|
158
|
+
this.$element.style.opacity = '1';
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return this;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Fade out animation
|
|
165
|
+
NDElement.prototype.fadeOut = function(duration = 300, callback) {
|
|
166
|
+
this.$element.style.transition = `opacity ${duration}ms ease-in-out`;
|
|
167
|
+
this.$element.style.opacity = '0';
|
|
168
|
+
|
|
169
|
+
setTimeout(() => {
|
|
170
|
+
if (callback) callback();
|
|
171
|
+
}, duration);
|
|
172
|
+
|
|
173
|
+
return this;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Slide down animation
|
|
177
|
+
NDElement.prototype.slideDown = function(duration = 300) {
|
|
178
|
+
const element = this.$element;
|
|
179
|
+
element.style.maxHeight = '0';
|
|
180
|
+
element.style.overflow = 'hidden';
|
|
181
|
+
element.style.transition = `max-height ${duration}ms ease-in-out`;
|
|
182
|
+
|
|
183
|
+
requestAnimationFrame(() => {
|
|
184
|
+
element.style.maxHeight = element.scrollHeight + 'px';
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return this;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Usage
|
|
191
|
+
Div("Animated content")
|
|
192
|
+
.nd.onClick(function() {
|
|
193
|
+
this.nd.fadeOut(300, () => this.remove());
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Best Practices
|
|
198
|
+
|
|
199
|
+
### 1. Always Return `this`
|
|
200
|
+
Enable method chaining by returning the NDElement instance:
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
NDElement.prototype.myMethod = function() {
|
|
204
|
+
// Your logic here
|
|
205
|
+
return this; // Enable chaining
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 2. Use Descriptive Method Names
|
|
210
|
+
Choose names that clearly describe what the method does:
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
// Good
|
|
214
|
+
NDElement.prototype.onEnter = function(callback) { /* ... */ };
|
|
215
|
+
NDElement.prototype.fadeIn = function(duration) { /* ... */ };
|
|
216
|
+
|
|
217
|
+
// Avoid
|
|
218
|
+
NDElement.prototype.ke = function(callback) { /* ... */ }; // Unclear
|
|
219
|
+
NDElement.prototype.doStuff = function() { /* ... */ }; // Too vague
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 3. Handle Edge Cases
|
|
223
|
+
Always consider edge cases and provide sensible defaults:
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
NDElement.prototype.fadeIn = function(duration = 300) {
|
|
227
|
+
// Ensure duration is valid
|
|
228
|
+
duration = Math.max(0, parseInt(duration) || 300);
|
|
229
|
+
|
|
230
|
+
// Check if element exists
|
|
231
|
+
if (!this.$element) return this;
|
|
232
|
+
|
|
233
|
+
// Your animation logic
|
|
234
|
+
return this;
|
|
235
|
+
};
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 4. Document Your Extensions
|
|
239
|
+
|
|
240
|
+
Always document your custom methods:
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
/**
|
|
244
|
+
* Handles Enter key press events
|
|
245
|
+
* @param {Function} callback - Function to call when Enter is pressed
|
|
246
|
+
* @returns {NDElement} Returns this for method chaining
|
|
247
|
+
* @example
|
|
248
|
+
* Input().nd.onEnter(e => console.log('Enter pressed'));
|
|
249
|
+
*/
|
|
250
|
+
NDElement.prototype.onEnter = function(callback) {
|
|
251
|
+
this.$element.addEventListener('keyup', e => {
|
|
252
|
+
if (e.key === 'Enter') {
|
|
253
|
+
callback(e);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return this;
|
|
257
|
+
};
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
By extending NDElement thoughtfully, you can create a powerful, domain-specific API that makes your code more readable, maintainable, and enjoyable to work with.
|
|
261
|
+
|
|
262
|
+
## Next Steps
|
|
263
|
+
|
|
264
|
+
Explore these related topics to build complete applications:
|
|
265
|
+
|
|
266
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
267
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
268
|
+
- **[Anchor](anchor.md)** - Anchor
|
package/docs/getting-started.md
CHANGED
|
@@ -358,6 +358,9 @@ Now that you've built your first NativeDocument applications, explore these topi
|
|
|
358
358
|
- **[Routing](routing.md)** - Navigation and URL management
|
|
359
359
|
- **[State Management](state-management.md)** - Global state patterns
|
|
360
360
|
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
361
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
362
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
363
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
361
364
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
362
365
|
- **[Anchor](anchor.md)** - Anchor
|
|
363
366
|
|
package/docs/lifecycle-events.md
CHANGED
|
@@ -98,6 +98,9 @@ document.body.appendChild(reusableComponent);
|
|
|
98
98
|
|
|
99
99
|
Now that you understand lifecycle events, explore these related topics:
|
|
100
100
|
|
|
101
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
102
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
103
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
101
104
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
102
105
|
- **[Anchor](anchor.md)** - Anchor
|
|
103
106
|
|
package/docs/list-rendering.md
CHANGED
|
@@ -603,5 +603,11 @@ const DebugList = ForEachArray(items, (item, index) => {
|
|
|
603
603
|
Now that you understand list rendering, explore these related topics:
|
|
604
604
|
|
|
605
605
|
- **[Conditional Rendering](conditional-rendering.md)** - Show/hide content dynamically
|
|
606
|
+
- **[Routing](routing.md)** - Navigation and URL management
|
|
607
|
+
- **[State Management](state-management.md)** - Global state patterns
|
|
608
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
609
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
610
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
611
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
606
612
|
- **[State Management](state-management.md)** - Managing application state
|
|
607
613
|
- **[Memory Management](memory-management.md)** - Understanding cleanup and memory
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# NDElement
|
|
2
|
+
|
|
3
|
+
`NDElement` is a wrapper class that enhances native HTML elements with utility methods and simplified event handlers. It enables fluent DOM manipulation while preserving access to the underlying HTML element.
|
|
4
|
+
|
|
5
|
+
## Accessing NDElement
|
|
6
|
+
|
|
7
|
+
Every HTML element created with NativeDocument automatically has an `nd` property that returns an `NDElement` instance:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const element = Div("Hello World");
|
|
11
|
+
const ndElement = element.nd; // NDElement instance
|
|
12
|
+
|
|
13
|
+
// Or directly with method chaining
|
|
14
|
+
Div("Hello").nd.onClick(() => console.log("Clicked!"));
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Constructor
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
new NDElement(element)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Parameters:**
|
|
24
|
+
- `element`: The HTML element to wrap
|
|
25
|
+
|
|
26
|
+
## Properties
|
|
27
|
+
|
|
28
|
+
### `$element`
|
|
29
|
+
The encapsulated native HTML element.
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
const div = Div("Content");
|
|
33
|
+
const htmlElement = div.nd.$element; // Native HTMLDivElement
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `$observer`
|
|
37
|
+
Lifecycle observer (used internally for DOM monitoring).
|
|
38
|
+
|
|
39
|
+
## Event Handling Methods
|
|
40
|
+
|
|
41
|
+
NDElement automatically generates methods for all standard DOM events with multiple variants:
|
|
42
|
+
|
|
43
|
+
### Basic Events
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// Standard event
|
|
47
|
+
element.nd.onClick(callback)
|
|
48
|
+
element.nd.onMouseOver(callback)
|
|
49
|
+
element.nd.onKeyDown(callback)
|
|
50
|
+
|
|
51
|
+
// Examples
|
|
52
|
+
Button("Click me").nd.onClick(e => console.log("Button clicked!"));
|
|
53
|
+
Input().nd.onInput(e => console.log("Input changed:", e.target.value));
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Prevention Variants
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
// Prevents default behavior
|
|
60
|
+
element.nd.onPreventClick(callback) // preventDefault()
|
|
61
|
+
element.nd.onPreventSubmit(callback)
|
|
62
|
+
|
|
63
|
+
// Example
|
|
64
|
+
Link({ href: "/page" }).nd.onPreventClick(e => {
|
|
65
|
+
// Link won't navigate, custom behavior
|
|
66
|
+
router.push("/page");
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Propagation Stop Variants
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
// Stops event propagation
|
|
74
|
+
element.nd.onStopClick(callback) // stopPropagation()
|
|
75
|
+
element.nd.onStopKeyDown(callback)
|
|
76
|
+
|
|
77
|
+
// Example
|
|
78
|
+
Div([
|
|
79
|
+
Button("Child").nd.onStopClick(e => {
|
|
80
|
+
console.log("Child clicked - won't bubble up");
|
|
81
|
+
})
|
|
82
|
+
]).nd.onClick(() => console.log("This won't be called"));
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Combined Variants
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
// Combines preventDefault() and stopPropagation()
|
|
89
|
+
element.nd.onPreventStopSubmit(callback)
|
|
90
|
+
element.nd.onPreventStopClick(callback)
|
|
91
|
+
|
|
92
|
+
// Example
|
|
93
|
+
Form().nd.onPreventStopSubmit(e => {
|
|
94
|
+
// Prevents submission AND stops propagation
|
|
95
|
+
handleFormSubmit(e);
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Supported Events List
|
|
100
|
+
|
|
101
|
+
All standard DOM events are supported with the 4 variants:
|
|
102
|
+
|
|
103
|
+
**Mouse:** Click, DblClick, MouseDown, MouseEnter, MouseLeave, MouseMove, MouseOut, MouseOver, MouseUp, Wheel
|
|
104
|
+
|
|
105
|
+
**Keyboard:** KeyDown, KeyPress, KeyUp
|
|
106
|
+
|
|
107
|
+
**Form:** Blur, Change, Focus, Input, Invalid, Reset, Search, Select, Submit
|
|
108
|
+
|
|
109
|
+
**Drag & Drop:** Drag, DragEnd, DragEnter, DragLeave, DragOver, DragStart, Drop
|
|
110
|
+
|
|
111
|
+
**Media:** Abort, CanPlay, CanPlayThrough, DurationChange, Emptied, Ended, LoadedData, LoadedMetadata, LoadStart, Pause, Play, Playing, Progress, RateChange, Seeked, Seeking, Stalled, Suspend, TimeUpdate, VolumeChange, Waiting
|
|
112
|
+
|
|
113
|
+
**Window:** AfterPrint, BeforePrint, BeforeUnload, Error, HashChange, Load, Offline, Online, PageHide, PageShow, Resize, Scroll, Unload
|
|
114
|
+
|
|
115
|
+
## Utility Methods
|
|
116
|
+
|
|
117
|
+
### `ref(target, name)`
|
|
118
|
+
Assigns the HTML element to a property of a target object.
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
const refs = {};
|
|
122
|
+
Div("Content").nd.ref(refs, 'contentDiv');
|
|
123
|
+
console.log(refs.contentDiv); // HTMLDivElement
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `htmlElement()` / `node()`
|
|
127
|
+
Returns the native HTML element (alias for `$element`).
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const div = Div("Hello");
|
|
131
|
+
const htmlElement = div.nd.htmlElement(); // HTMLDivElement
|
|
132
|
+
const node = div.nd.node(); // Same thing, alias
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `remove()`
|
|
136
|
+
Removes the element and cleans up its internal references.
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
const element = Div("To be removed");
|
|
140
|
+
element.nd.remove(); // Element removed and cleaned
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `unmountChildren()`
|
|
144
|
+
Unmounts all child elements and cleans up their references.
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
const container = Div([
|
|
148
|
+
Div("Child 1"),
|
|
149
|
+
Div("Child 2")
|
|
150
|
+
]);
|
|
151
|
+
container.nd.unmountChildren(); // Children cleaned up
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Lifecycle Management
|
|
155
|
+
|
|
156
|
+
### `lifecycle(states)`
|
|
157
|
+
Configures lifecycle callbacks.
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
element.nd.lifecycle({
|
|
161
|
+
mounted: (element) => console.log("Element added to DOM"),
|
|
162
|
+
unmounted: (element) => console.log("Element removed from DOM")
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `mounted(callback)`
|
|
167
|
+
Shortcut to define only the mount callback.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
Div("Content").nd.mounted(element => {
|
|
171
|
+
console.log("Element is now in the DOM!");
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Practical Examples
|
|
176
|
+
|
|
177
|
+
### Custom Event Handler
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
// Extending NDElement with a custom handler
|
|
181
|
+
NDElement.prototype.onEnter = function(callback) {
|
|
182
|
+
this.$element.addEventListener('keyup', e => {
|
|
183
|
+
if (e.key === 'Enter') {
|
|
184
|
+
callback(e);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return this;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Usage
|
|
191
|
+
Input({ type: 'text' })
|
|
192
|
+
.nd.onEnter(e => console.log("Enter pressed!"));
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Fluent Chaining
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
const interactiveButton = Button("Interactive")
|
|
199
|
+
.nd.onClick(e => console.log("Clicked"))
|
|
200
|
+
.nd.onMouseEnter(e => e.target.style.background = "blue")
|
|
201
|
+
.nd.onMouseLeave(e => e.target.style.background = "")
|
|
202
|
+
.nd.mounted(el => console.log("Button mounted"));
|
|
203
|
+
|
|
204
|
+
// OR
|
|
205
|
+
|
|
206
|
+
const interactiveButton = Button("Interactive")
|
|
207
|
+
.nd.onClick(e => console.log("Clicked"))
|
|
208
|
+
.onMouseEnter(e => e.target.style.background = "blue")
|
|
209
|
+
.onMouseLeave(e => e.target.style.background = "")
|
|
210
|
+
.mounted(el => console.log("Button mounted"));
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Form with Event Handling
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
const todoForm = Form([
|
|
218
|
+
Input({ type: 'text', value: newTodo })
|
|
219
|
+
.nd.onEnter(addTodo),
|
|
220
|
+
|
|
221
|
+
Button('Add', { type: 'submit' })
|
|
222
|
+
]).nd.onPreventSubmit(addTodo);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Reference Management
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
const components = {};
|
|
229
|
+
|
|
230
|
+
const app = Div([
|
|
231
|
+
Input().nd.ref(components, 'input'),
|
|
232
|
+
Button('Focus Input').nd.onClick(() => {
|
|
233
|
+
components.input.focus();
|
|
234
|
+
})
|
|
235
|
+
]);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Integration with Observables
|
|
239
|
+
|
|
240
|
+
NDElement works seamlessly with NativeDocument's Observable system:
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
const isVisible = Observable(false);
|
|
244
|
+
const message = Observable("Hello");
|
|
245
|
+
|
|
246
|
+
Div([
|
|
247
|
+
Button("Toggle").nd.onClick(() => isVisible.set(!isVisible.val())),
|
|
248
|
+
ShowIf(isVisible, () =>
|
|
249
|
+
P(message).nd.onClick(() =>
|
|
250
|
+
message.set("Clicked!")
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
]);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Best Practices
|
|
257
|
+
|
|
258
|
+
1. **Fluent chaining**: Use method chaining for concise syntax
|
|
259
|
+
2. **Cleanup**: Call `remove()` to clean up dynamic elements
|
|
260
|
+
3. **Extensions**: Add your own methods to the prototype for specific needs
|
|
261
|
+
4. **Lifecycle**: Use `mounted`/`unmounted` for initialization/cleanup
|
|
262
|
+
5. **References**: Use `ref()` for direct element access when needed
|
|
263
|
+
|
|
264
|
+
## Limitations
|
|
265
|
+
|
|
266
|
+
- Event handlers are not automatically removed (manual management required if needed)
|
|
267
|
+
- Access to native HTML element is still necessary for advanced APIs
|
|
268
|
+
|
|
269
|
+
NDElement thus provides a practical abstraction layer while preserving the power and performance of native DOM.
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
## Next Steps
|
|
273
|
+
|
|
274
|
+
Explore these related topics to build complete applications:
|
|
275
|
+
|
|
276
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
277
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
278
|
+
- **[Memory Management](memory-management.md)** - Memory management
|
|
279
|
+
- **[Anchor](anchor.md)** - Anchor
|
package/docs/observables.md
CHANGED
|
@@ -542,5 +542,8 @@ Now that you understand NativeDocument's observable, explore these advanced topi
|
|
|
542
542
|
- **[Routing](routing.md)** - Navigation and URL management
|
|
543
543
|
- **[State Management](state-management.md)** - Global state patterns
|
|
544
544
|
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
545
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
546
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
547
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
545
548
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
546
549
|
- **[Anchor](anchor.md)** - Anchor
|
package/docs/routing.md
CHANGED
|
@@ -813,5 +813,8 @@ Explore these related topics to build complete applications:
|
|
|
813
813
|
|
|
814
814
|
- **[State Management](state-management.md)** - Global state patterns
|
|
815
815
|
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
816
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
817
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
818
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
816
819
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
817
820
|
- **[Anchor](anchor.md)** - Anchor
|
package/docs/state-management.md
CHANGED
|
@@ -419,5 +419,8 @@ const createAppState = () => {
|
|
|
419
419
|
Now that you understand state management, explore these related topics:
|
|
420
420
|
|
|
421
421
|
- **[Lifecycle Events](lifecycle-events.md)** - Lifecycle events
|
|
422
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
423
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
424
|
+
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
422
425
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
423
426
|
- **[Anchor](anchor.md)** - Anchor
|
package/docs/validation.md
CHANGED
|
@@ -187,5 +187,7 @@ registerUser.args(
|
|
|
187
187
|
|
|
188
188
|
## Next Steps
|
|
189
189
|
|
|
190
|
-
- **[
|
|
191
|
-
- **[
|
|
190
|
+
- **[Lifecycle Events](lifecycle-events.md)** - Validate lifecycle callback arguments
|
|
191
|
+
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
192
|
+
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
193
|
+
- **[Memory Management](memory-management.md)** - Debugging memory issues with validation
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
{ ignores: ['dist'] },
|
|
6
|
+
{
|
|
7
|
+
files: ['**/*.{js}'],
|
|
8
|
+
languageOptions: {
|
|
9
|
+
ecmaVersion: 2020,
|
|
10
|
+
globals: globals.browser,
|
|
11
|
+
parserOptions: {
|
|
12
|
+
ecmaVersion: 'latest',
|
|
13
|
+
sourceType: 'module',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
plugins: {
|
|
17
|
+
},
|
|
18
|
+
rules: {
|
|
19
|
+
...js.configs.recommended.rules,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
]
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "native-document",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "rollup --config rollup.config.js --watch"
|
|
7
|
+
"build": "rollup --config rollup.config.js --watch",
|
|
8
|
+
"lint": "eslint ."
|
|
8
9
|
},
|
|
9
10
|
"keywords": [],
|
|
10
11
|
"author": "",
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
"description": "",
|
|
13
14
|
"devDependencies": {
|
|
14
15
|
"@rollup/plugin-replace": "^6.0.2",
|
|
15
|
-
"@rollup/plugin-terser": "^0.4.4"
|
|
16
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
17
|
+
"eslint": "^9.33.0"
|
|
16
18
|
}
|
|
17
19
|
}
|
package/readme.md
CHANGED
|
@@ -60,7 +60,7 @@ document.body.appendChild(App);
|
|
|
60
60
|
npx degit afrocodeur/native-document-vite my-app
|
|
61
61
|
cd my-app
|
|
62
62
|
npm install
|
|
63
|
-
npm run
|
|
63
|
+
npm run start
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### Option 3: NPM/Yarn
|
|
@@ -195,29 +195,12 @@ When(condition)
|
|
|
195
195
|
- **[Routing](docs/routing.md)** - Navigation and URL management
|
|
196
196
|
- **[State Management](docs/state-management.md)** - Global state patterns
|
|
197
197
|
- **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
|
|
198
|
+
- **[NDElement](docs/native-document-element.md)** - Native Document Element
|
|
199
|
+
- **[Extending NDElement](docs/extending-native-document-element.md)** - Custom Methods Guide
|
|
200
|
+
- **[Args Validation](docs/validation.md)** - Function Argument Validation
|
|
198
201
|
- **[Memory Management](docs/memory-management.md)** - Memory management
|
|
199
202
|
- **[Anchor](docs/anchor.md)** - Anchor
|
|
200
203
|
|
|
201
|
-
## Examples
|
|
202
|
-
|
|
203
|
-
### Todo App
|
|
204
|
-
```bash
|
|
205
|
-
# Complete todo application with local storage
|
|
206
|
-
git clone https://github.com/afrocodeur/native-document-examples
|
|
207
|
-
cd examples/todo-app
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### SPA Router
|
|
211
|
-
```bash
|
|
212
|
-
# Single Page Application with routing
|
|
213
|
-
cd examples/routing-spa
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Reusable Components
|
|
217
|
-
```bash
|
|
218
|
-
# Component library patterns
|
|
219
|
-
cd examples/components
|
|
220
|
-
```
|
|
221
204
|
|
|
222
205
|
## Key Features Deep Dive
|
|
223
206
|
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
* @class ObservableChecker
|
|
6
6
|
*/
|
|
7
7
|
export default function ObservableChecker($observable, $checker) {
|
|
8
|
-
this.__$isObservableChecker = true;
|
|
9
8
|
this.observable = $observable;
|
|
10
9
|
this.checker = $checker;
|
|
11
10
|
this.unSubscriptions = [];
|
|
12
11
|
}
|
|
13
12
|
|
|
13
|
+
ObservableChecker.prototype.__$isObservableChecker = true;
|
|
14
|
+
|
|
14
15
|
ObservableChecker.prototype.subscribe = function(callback) {
|
|
15
16
|
const unSubscribe = this.observable.subscribe((value) => {
|
|
16
17
|
callback && callback(this.checker(value));
|
|
@@ -16,7 +16,6 @@ export default function ObservableItem(value) {
|
|
|
16
16
|
throw new NativeDocumentError('ObservableItem cannot be an Observable');
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
this.__$isObservable = true;
|
|
20
19
|
this.$previousValue = value;
|
|
21
20
|
this.$currentValue = value;
|
|
22
21
|
this.$isCleanedUp = false;
|
|
@@ -37,6 +36,8 @@ Object.defineProperty(ObservableItem.prototype, '$value', {
|
|
|
37
36
|
configurable: true,
|
|
38
37
|
});
|
|
39
38
|
|
|
39
|
+
ObservableItem.prototype.__$isObservable = true;
|
|
40
|
+
|
|
40
41
|
ObservableItem.prototype.triggerListeners = function(operations) {
|
|
41
42
|
const $listeners = this.$listeners;
|
|
42
43
|
const $previousValue = this.$previousValue;
|
|
@@ -2,10 +2,10 @@ import DocumentObserver from "./DocumentObserver";
|
|
|
2
2
|
import { EVENTS } from "../utils/events";
|
|
3
3
|
|
|
4
4
|
export function NDElement(element) {
|
|
5
|
-
this.__$isNDElement = true;
|
|
6
5
|
this.$element = element;
|
|
7
6
|
this.$observer = null;
|
|
8
7
|
}
|
|
8
|
+
NDElement.prototype.__$isNDElement = true;
|
|
9
9
|
|
|
10
10
|
for(const event of EVENTS) {
|
|
11
11
|
const eventName = event.toLowerCase();
|
|
@@ -38,18 +38,18 @@ for(const event of EVENTS) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
NDElement.prototype.ref = function(target, name) {
|
|
41
|
-
target[name] = element;
|
|
41
|
+
target[name] = this.$element;
|
|
42
42
|
return this;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
NDElement.prototype.unmountChildren = function() {
|
|
46
46
|
let element = this.$element;
|
|
47
47
|
for(let i = 0, length = element.children.length; i < length; i++) {
|
|
48
|
-
let
|
|
49
|
-
if(!
|
|
50
|
-
|
|
48
|
+
let elementChildren = element.children[i];
|
|
49
|
+
if(!elementChildren.$ndProx) {
|
|
50
|
+
elementChildren.nd?.remove();
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
elementChildren = null;
|
|
53
53
|
}
|
|
54
54
|
element = null;
|
|
55
55
|
return this;
|
|
@@ -77,7 +77,7 @@ NDElement.prototype.mounted = function(callback) {
|
|
|
77
77
|
return this.lifecycle({ mounted: callback });
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
-
NDElement.prototype.
|
|
80
|
+
NDElement.prototype.unmounted = function(callback) {
|
|
81
81
|
return this.lifecycle({ unmounted: callback });
|
|
82
82
|
};
|
|
83
83
|
|