native-document 1.0.92 → 1.0.93
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.components.min.js +1088 -65
- package/dist/native-document.dev.js +695 -142
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.devtools.min.js +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +814 -0
- package/docs/anchor.md +71 -11
- package/docs/cache.md +888 -0
- package/docs/conditional-rendering.md +91 -1
- package/docs/core-concepts.md +9 -2
- package/docs/elements.md +127 -2
- package/docs/extending-native-document-element.md +7 -1
- package/docs/filters.md +1216 -0
- package/docs/getting-started.md +12 -3
- package/docs/lifecycle-events.md +10 -2
- package/docs/list-rendering.md +453 -54
- package/docs/memory-management.md +9 -7
- package/docs/native-document-element.md +30 -9
- package/docs/native-fetch.md +744 -0
- package/docs/observables.md +135 -6
- package/docs/routing.md +7 -1
- package/docs/state-management.md +7 -1
- package/docs/validation.md +8 -1
- package/eslint.config.js +3 -3
- package/package.json +3 -2
- package/readme.md +53 -14
- package/src/components/$traits/HasItems.js +42 -1
- package/src/components/BaseComponent.js +4 -1
- package/src/components/accordion/Accordion.js +112 -8
- package/src/components/accordion/AccordionItem.js +93 -4
- package/src/components/alert/Alert.js +164 -4
- package/src/components/avatar/Avatar.js +236 -22
- package/src/components/menu/index.js +1 -2
- package/src/core/data/ObservableArray.js +120 -2
- package/src/core/data/ObservableChecker.js +50 -0
- package/src/core/data/ObservableItem.js +124 -4
- package/src/core/data/ObservableWhen.js +36 -6
- package/src/core/data/observable-helpers/array.js +12 -3
- package/src/core/data/observable-helpers/computed.js +17 -4
- package/src/core/data/observable-helpers/object.js +19 -3
- package/src/core/elements/control/for-each-array.js +20 -2
- package/src/core/elements/control/for-each.js +17 -5
- package/src/core/elements/control/show-if.js +31 -15
- package/src/core/elements/control/show-when.js +23 -0
- package/src/core/elements/control/switch.js +40 -10
- package/src/core/utils/cache.js +5 -0
- package/src/core/utils/memoize.js +25 -16
- package/src/core/utils/prototypes.js +3 -2
- package/src/core/wrappers/AttributesWrapper.js +1 -1
- package/src/core/wrappers/NDElement.js +41 -1
- package/src/core/wrappers/NdPrototype.js +4 -0
- package/src/core/wrappers/TemplateCloner.js +13 -10
- package/src/core/wrappers/prototypes/bind-class-extensions.js +1 -1
- package/src/core/wrappers/prototypes/nd-element-extensions.js +3 -0
- package/src/router/Route.js +9 -4
- package/src/router/Router.js +28 -9
- package/src/router/errors/RouterError.js +0 -1
- package/types/control-flow.d.ts +9 -6
- package/types/elements.d.ts +6 -3
- package/types/filters/index.d.ts +4 -0
- package/types/nd-element.d.ts +5 -238
- package/types/observable.d.ts +9 -3
- package/types/router.d.ts +5 -1
- package/types/template-cloner.ts +1 -0
- package/types/validator.ts +11 -1
- package/utils.d.ts +2 -1
- package/utils.js +4 -4
- package/src/core/utils/service.js +0 -6
package/docs/observables.md
CHANGED
|
@@ -615,10 +615,12 @@ console.log(isAdult.val()); // true
|
|
|
615
615
|
const data = Observable("test");
|
|
616
616
|
|
|
617
617
|
// Create a subscription
|
|
618
|
-
const
|
|
618
|
+
const handler = value => console.log(value);
|
|
619
|
+
data.subscribe(handler);
|
|
620
|
+
|
|
619
621
|
|
|
620
622
|
// Clean up manually if needed
|
|
621
|
-
unsubscribe();
|
|
623
|
+
data.unsubscribe('test', handler);
|
|
622
624
|
|
|
623
625
|
// Complete observable cleanup
|
|
624
626
|
data.cleanup(); // Removes all listeners and prevents new subscriptions
|
|
@@ -626,9 +628,6 @@ data.cleanup(); // Removes all listeners and prevents new subscriptions
|
|
|
626
628
|
// Manual trigger (useful for forcing updates)
|
|
627
629
|
data.trigger(); // Notifies all subscribers without changing the value
|
|
628
630
|
|
|
629
|
-
// Get original value (useful for reset functionality)
|
|
630
|
-
console.log(data.originalValue()); // Returns the initial value
|
|
631
|
-
|
|
632
631
|
// Extract values from any observable structure
|
|
633
632
|
const complexData = Observable.object({
|
|
634
633
|
user: "John",
|
|
@@ -637,6 +636,130 @@ const complexData = Observable.object({
|
|
|
637
636
|
console.log(Observable.value(complexData)); // Plain object with extracted values
|
|
638
637
|
```
|
|
639
638
|
|
|
639
|
+
## Utility Methods
|
|
640
|
+
|
|
641
|
+
### `off(value, callback?)` - Remove Watchers
|
|
642
|
+
|
|
643
|
+
Remove specific value watchers created with `.on()`:
|
|
644
|
+
```javascript
|
|
645
|
+
const status = Observable("idle");
|
|
646
|
+
|
|
647
|
+
const loadingHandler = (isActive) => console.log("Loading:", isActive);
|
|
648
|
+
status.on("loading", loadingHandler);
|
|
649
|
+
|
|
650
|
+
// Remove specific callback
|
|
651
|
+
status.off("loading", loadingHandler);
|
|
652
|
+
|
|
653
|
+
// Remove all watchers for a value
|
|
654
|
+
status.off("loading");
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
### `once(predicate, callback)` - Single-Time Listener
|
|
658
|
+
|
|
659
|
+
Execute callback only once when condition is met:
|
|
660
|
+
```javascript
|
|
661
|
+
const count = Observable(0);
|
|
662
|
+
|
|
663
|
+
// Wait for specific value
|
|
664
|
+
count.once(5, (value) => {
|
|
665
|
+
console.log("Reached 5!"); // Only called once
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// With predicate function
|
|
669
|
+
count.once(val => val > 10, (value) => {
|
|
670
|
+
console.log("Greater than 10!"); // Only called once
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
count.set(5); // Callback fires and unsubscribes
|
|
674
|
+
count.set(5); // Callback doesn't fire again
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### `toggle()` - Boolean Toggle
|
|
678
|
+
|
|
679
|
+
Toggle boolean observables:
|
|
680
|
+
```javascript
|
|
681
|
+
const isVisible = Observable(false);
|
|
682
|
+
|
|
683
|
+
isVisible.toggle(); // true
|
|
684
|
+
isVisible.toggle(); // false
|
|
685
|
+
isVisible.toggle(); // true
|
|
686
|
+
|
|
687
|
+
// Useful with buttons
|
|
688
|
+
Button("Toggle").nd.onClick(() => isVisible.toggle());
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### `reset()` - Reset to Initial Value
|
|
692
|
+
|
|
693
|
+
Reset observable to its initial value (requires `reset: true` config):
|
|
694
|
+
```javascript
|
|
695
|
+
const name = Observable("Alice", { reset: true });
|
|
696
|
+
|
|
697
|
+
name.set("Bob");
|
|
698
|
+
console.log(name.val()); // "Bob"
|
|
699
|
+
|
|
700
|
+
name.reset();
|
|
701
|
+
console.log(name.val()); // "Alice" (initial value)
|
|
702
|
+
|
|
703
|
+
// With objects
|
|
704
|
+
const user = Observable({ name: "Alice", age: 25 }, { reset: true });
|
|
705
|
+
user.set({ name: "Bob", age: 30 });
|
|
706
|
+
user.reset(); // Back to { name: "Alice", age: 25 }
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### `equals(other)` - Value Comparison
|
|
710
|
+
|
|
711
|
+
Compare observable values:
|
|
712
|
+
```javascript
|
|
713
|
+
const num1 = Observable(5);
|
|
714
|
+
const num2 = Observable(5);
|
|
715
|
+
const num3 = Observable(10);
|
|
716
|
+
|
|
717
|
+
console.log(num1.equals(num2)); // true (same value)
|
|
718
|
+
console.log(num1.equals(5)); // true (compare with raw value)
|
|
719
|
+
console.log(num1.equals(num3)); // false
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
### `toBool()` - Boolean Conversion
|
|
723
|
+
|
|
724
|
+
Convert observable value to boolean:
|
|
725
|
+
```javascript
|
|
726
|
+
const text = Observable("");
|
|
727
|
+
console.log(text.toBool()); // false
|
|
728
|
+
|
|
729
|
+
text.set("Hello");
|
|
730
|
+
console.log(text.toBool()); // true
|
|
731
|
+
|
|
732
|
+
// Useful for conditions
|
|
733
|
+
const hasContent = text.toBool();
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### `intercept(callback)` - Value Interception
|
|
737
|
+
|
|
738
|
+
Intercept and modify values before they're set:
|
|
739
|
+
```javascript
|
|
740
|
+
const age = Observable(0);
|
|
741
|
+
|
|
742
|
+
// Intercept sets to enforce constraints
|
|
743
|
+
age.intercept((newValue, oldValue) => {
|
|
744
|
+
if (newValue < 0) return 0;
|
|
745
|
+
if (newValue > 120) return 120;
|
|
746
|
+
return newValue;
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
age.set(-5); // Actually sets 0
|
|
750
|
+
age.set(150); // Actually sets 120
|
|
751
|
+
age.set(25); // Sets 25
|
|
752
|
+
|
|
753
|
+
// Practical example: sanitize input
|
|
754
|
+
const username = Observable("");
|
|
755
|
+
username.intercept((value) => {
|
|
756
|
+
return value.toLowerCase().trim();
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
username.set(" JohnDoe ");
|
|
760
|
+
console.log(username.val()); // "johndoe"
|
|
761
|
+
```
|
|
762
|
+
|
|
640
763
|
## Best Practices
|
|
641
764
|
|
|
642
765
|
1. **Use descriptive names** for your observables
|
|
@@ -663,4 +786,10 @@ Now that you understand NativeDocument's observable, explore these advanced topi
|
|
|
663
786
|
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
664
787
|
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
665
788
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
666
|
-
- **[Anchor](anchor.md)** - Anchor
|
|
789
|
+
- **[Anchor](anchor.md)** - Anchor
|
|
790
|
+
|
|
791
|
+
## Utilities
|
|
792
|
+
|
|
793
|
+
- **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
|
|
794
|
+
- **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
|
|
795
|
+
- **[Filters](docs/utils/filters.md)** - Data filtering helpers
|
package/docs/routing.md
CHANGED
|
@@ -817,4 +817,10 @@ Explore these related topics to build complete applications:
|
|
|
817
817
|
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
818
818
|
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
819
819
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
820
|
-
- **[Anchor](anchor.md)** - Anchor
|
|
820
|
+
- **[Anchor](anchor.md)** - Anchor
|
|
821
|
+
|
|
822
|
+
## Utilities
|
|
823
|
+
|
|
824
|
+
- **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
|
|
825
|
+
- **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
|
|
826
|
+
- **[Filters](docs/utils/filters.md)** - Data filtering helpers
|
package/docs/state-management.md
CHANGED
|
@@ -423,4 +423,10 @@ Now that you understand state management, explore these related topics:
|
|
|
423
423
|
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
424
424
|
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
425
425
|
- **[Memory Management](memory-management.md)** - Memory management
|
|
426
|
-
- **[Anchor](anchor.md)** - Anchor
|
|
426
|
+
- **[Anchor](anchor.md)** - Anchor
|
|
427
|
+
|
|
428
|
+
## Utilities
|
|
429
|
+
|
|
430
|
+
- **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
|
|
431
|
+
- **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
|
|
432
|
+
- **[Filters](docs/utils/filters.md)** - Data filtering helpers
|
package/docs/validation.md
CHANGED
|
@@ -190,4 +190,11 @@ registerUser.args(
|
|
|
190
190
|
- **[Lifecycle Events](lifecycle-events.md)** - Validate lifecycle callback arguments
|
|
191
191
|
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
192
192
|
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
193
|
-
- **[
|
|
193
|
+
- **[Advanced Components](advanced-components.md)** - Template caching and singleton views
|
|
194
|
+
- **[Memory Management](memory-management.md)** - Debugging memory issues with validation
|
|
195
|
+
|
|
196
|
+
## Utilities
|
|
197
|
+
|
|
198
|
+
- **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
|
|
199
|
+
- **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
|
|
200
|
+
- **[Filters](docs/utils/filters.md)** - Data filtering helpers
|
package/eslint.config.js
CHANGED
|
@@ -3,18 +3,18 @@ import globals from 'globals'
|
|
|
3
3
|
|
|
4
4
|
export default [
|
|
5
5
|
{ ignores: ['dist'] },
|
|
6
|
+
js.configs.recommended,
|
|
6
7
|
{
|
|
7
8
|
files: ['**/*.{js}'],
|
|
8
9
|
languageOptions: {
|
|
9
|
-
ecmaVersion:
|
|
10
|
+
ecmaVersion: 'latest',
|
|
10
11
|
globals: globals.browser,
|
|
11
12
|
parserOptions: {
|
|
12
13
|
ecmaVersion: 'latest',
|
|
13
14
|
sourceType: 'module',
|
|
14
15
|
},
|
|
15
16
|
},
|
|
16
|
-
plugins: {
|
|
17
|
-
},
|
|
17
|
+
plugins: {},
|
|
18
18
|
rules: {
|
|
19
19
|
...js.configs.recommended.rules,
|
|
20
20
|
},
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "native-document",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.93",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "rollup --config rollup.config.js --watch",
|
|
8
|
-
"lint": "eslint
|
|
8
|
+
"lint": "eslint ./src"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [],
|
|
11
11
|
"author": "",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"@rollup/plugin-replace": "^6.0.2",
|
|
19
19
|
"@rollup/plugin-terser": "^0.4.4",
|
|
20
20
|
"eslint": "^9.33.0",
|
|
21
|
+
"eslint-plugin-jsdoc": "^62.5.4",
|
|
21
22
|
"magic-string": "^0.30.21",
|
|
22
23
|
"rollup": "^4.53.3"
|
|
23
24
|
},
|
package/readme.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
NativeDocument combines the familiarity of vanilla JavaScript with the power of modern reactivity. No compilation, no virtual DOM, just pure JavaScript with an intuitive API.
|
|
11
11
|
|
|
12
12
|
## Why NativeDocument?
|
|
13
|
+
> **Note**: NativeDocument works best with a bundler (Vite, Webpack, Rollup) for tree-shaking and optimal bundle size. The CDN version includes all features
|
|
13
14
|
|
|
14
15
|
### **Instant Start**
|
|
15
16
|
```html
|
|
@@ -18,7 +19,7 @@ NativeDocument combines the familiarity of vanilla JavaScript with the power of
|
|
|
18
19
|
|
|
19
20
|
### **Familiar API**
|
|
20
21
|
```javascript
|
|
21
|
-
import { Div, Button } from 'native-document/
|
|
22
|
+
import { Div, Button } from 'native-document/elements';
|
|
22
23
|
import { Observable } from 'native-document';
|
|
23
24
|
|
|
24
25
|
// CDN
|
|
@@ -29,7 +30,6 @@ const count = Observable(0);
|
|
|
29
30
|
|
|
30
31
|
const App = Div({ class: 'app' }, [
|
|
31
32
|
Div([ 'Count ', count]),
|
|
32
|
-
// OR Div(`Count ${count}`),
|
|
33
33
|
Button('Increment').nd.onClick(() => count.set(count.val() + 1))
|
|
34
34
|
]);
|
|
35
35
|
|
|
@@ -74,7 +74,7 @@ yarn add native-document
|
|
|
74
74
|
## Quick Example
|
|
75
75
|
|
|
76
76
|
```javascript
|
|
77
|
-
import { Div, Input, Button, ShowIf, ForEach } from 'native-document/
|
|
77
|
+
import { Div, Input, Button, ShowIf, ForEach } from 'native-document/elements'
|
|
78
78
|
import { Observable } from 'native-document'
|
|
79
79
|
|
|
80
80
|
// CDN
|
|
@@ -105,7 +105,7 @@ const TodoApp = Div({ class: 'todo-app' }, [
|
|
|
105
105
|
Input({ type: 'checkbox', checked: todo.done }),
|
|
106
106
|
`${todo.text}`,
|
|
107
107
|
Button('Delete').nd.onClick(() => todos.splice(index.val(), 1))
|
|
108
|
-
]),
|
|
108
|
+
]), (item) => item.id ), // Key function - use unique identifier
|
|
109
109
|
|
|
110
110
|
// Empty state
|
|
111
111
|
ShowIf(
|
|
@@ -122,7 +122,7 @@ document.body.appendChild(TodoApp)
|
|
|
122
122
|
### Observables
|
|
123
123
|
Reactive data that automatically updates the DOM:
|
|
124
124
|
```javascript
|
|
125
|
-
import { Div } from 'native-document/
|
|
125
|
+
import { Div } from 'native-document/elements'
|
|
126
126
|
import { Observable } from 'native-document'
|
|
127
127
|
|
|
128
128
|
// CDN
|
|
@@ -135,16 +135,19 @@ const greeting = Observable.computed(() => `Hello ${user.$value.name}!`, [user])
|
|
|
135
135
|
|
|
136
136
|
document.body.appendChild(Div(greeting));
|
|
137
137
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
// Direct mutation won't trigger updates
|
|
139
|
+
user.name = 'Fausty';
|
|
140
|
+
|
|
141
|
+
// These will trigger updates:
|
|
142
|
+
user.$value = { ...user.$value, name: ' Hermes!' };
|
|
143
|
+
user.set(data => ({ ...data, name: 'Hermes!' }));
|
|
141
144
|
user.set({ ...user.val(), name: 'Hermes!' });
|
|
142
145
|
```
|
|
143
146
|
|
|
144
147
|
### Elements
|
|
145
148
|
Familiar HTML element creation with reactive bindings:
|
|
146
149
|
```javascript
|
|
147
|
-
import { Div, Button } from 'native-document/
|
|
150
|
+
import { Div, Button } from 'native-document/elements'
|
|
148
151
|
import { Observable } from 'native-document'
|
|
149
152
|
|
|
150
153
|
// CDN
|
|
@@ -185,6 +188,30 @@ When(condition)
|
|
|
185
188
|
.otherwise(onFalse)
|
|
186
189
|
```
|
|
187
190
|
|
|
191
|
+
### List Rendering
|
|
192
|
+
Efficient rendering of lists with automatic updates:
|
|
193
|
+
```javascript
|
|
194
|
+
import { ForEach, Div } from 'native-document/elements'
|
|
195
|
+
import { Observable } from 'native-document'
|
|
196
|
+
|
|
197
|
+
const items = Observable.array(['Apple', 'Banana', 'Cherry'])
|
|
198
|
+
|
|
199
|
+
ForEach(items, (item, index) =>
|
|
200
|
+
Div([index, '. ', item])
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
// With object arrays - use key function
|
|
204
|
+
const users = Observable.array([
|
|
205
|
+
{ id: 1, name: 'Alice' },
|
|
206
|
+
{ id: 2, name: 'Bob' }
|
|
207
|
+
])
|
|
208
|
+
|
|
209
|
+
ForEach(users, (user) =>
|
|
210
|
+
Div(user.name),
|
|
211
|
+
(user) => user.id // Key for efficient updates
|
|
212
|
+
)
|
|
213
|
+
```
|
|
214
|
+
|
|
188
215
|
## Documentation
|
|
189
216
|
|
|
190
217
|
- **[Getting Started](docs/getting-started.md)** - Installation and first steps
|
|
@@ -198,10 +225,17 @@ When(condition)
|
|
|
198
225
|
- **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
|
|
199
226
|
- **[NDElement](docs/native-document-element.md)** - Native Document Element
|
|
200
227
|
- **[Extending NDElement](docs/extending-native-document-element.md)** - Custom Methods Guide
|
|
228
|
+
- **[Advanced Components](docs/advanced-components.md)** - Template caching and singleton views
|
|
201
229
|
- **[Args Validation](docs/validation.md)** - Function Argument Validation
|
|
202
230
|
- **[Memory Management](docs/memory-management.md)** - Memory management
|
|
203
231
|
- **[Anchor](docs/anchor.md)** - Anchor
|
|
204
232
|
|
|
233
|
+
### Utilities
|
|
234
|
+
|
|
235
|
+
- **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
|
|
236
|
+
- **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
|
|
237
|
+
- **[Filters](docs/utils/filters.md)** - Data filtering helpers
|
|
238
|
+
|
|
205
239
|
|
|
206
240
|
## Key Features Deep Dive
|
|
207
241
|
|
|
@@ -213,6 +247,8 @@ When(condition)
|
|
|
213
247
|
|
|
214
248
|
### Developer Experience
|
|
215
249
|
```javascript
|
|
250
|
+
import { ArgTypes } from 'native-document'
|
|
251
|
+
|
|
216
252
|
// Built-in debugging
|
|
217
253
|
Observable.debug.enable()
|
|
218
254
|
|
|
@@ -221,12 +257,15 @@ const createUser = (function (name, age) {
|
|
|
221
257
|
// Auto-validates argument types
|
|
222
258
|
}).args(ArgTypes.string('name'), ArgTypes.number('age'))
|
|
223
259
|
|
|
224
|
-
// Error boundaries
|
|
225
|
-
const AppWithBoundayError = App.errorBoundary(() => {
|
|
226
|
-
return Div('Error in the Create User component');
|
|
227
|
-
})
|
|
228
260
|
|
|
229
|
-
|
|
261
|
+
const SafeApp = App.errorBoundary((error, { caller, args }) => {
|
|
262
|
+
return Div({ class: 'error' }, [
|
|
263
|
+
'An error occurred: ',
|
|
264
|
+
error.message
|
|
265
|
+
])
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
document.body.appendChild(SafeApp());
|
|
230
269
|
```
|
|
231
270
|
|
|
232
271
|
## Contributing
|
|
@@ -1,22 +1,45 @@
|
|
|
1
1
|
import {$} from "../../../index";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Mixin for managing a collection of items with manipulation methods
|
|
5
|
+
* @class
|
|
6
|
+
*/
|
|
3
7
|
export default function HasItems() {}
|
|
4
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Sets a dynamic observable array to store items
|
|
11
|
+
* @param {ObservableArray?} [observableArray=null] - Observable array to use, or creates a new one if null
|
|
12
|
+
* @returns {HasItems}
|
|
13
|
+
*/
|
|
5
14
|
HasItems.prototype.dynamic = function(observableArray = null) {
|
|
6
15
|
this.$description.items = observableArray || $.array([]);
|
|
7
16
|
return this;
|
|
8
17
|
};
|
|
9
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Replaces all existing items with a new array of items
|
|
21
|
+
* @param {Array} items - Array of new items
|
|
22
|
+
* @returns {HasItems}
|
|
23
|
+
*/
|
|
10
24
|
HasItems.prototype.items = function(items) {
|
|
11
25
|
this.$description.items.splice(0, this.$description.items.length, ...items);
|
|
12
26
|
return this;
|
|
13
27
|
};
|
|
14
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Adds an item to the collection
|
|
31
|
+
* @param {*} item - The item to add
|
|
32
|
+
* @returns {HasItems}
|
|
33
|
+
*/
|
|
15
34
|
HasItems.prototype.item = function(item) {
|
|
16
35
|
this.$description.items.push(item);
|
|
17
36
|
return this;
|
|
18
37
|
};
|
|
19
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Clears all items from the collection
|
|
41
|
+
* @returns {HasItems}
|
|
42
|
+
*/
|
|
20
43
|
HasItems.prototype.clear = function() {
|
|
21
44
|
const items = this.$description.items;
|
|
22
45
|
if(Array.isArray(items)) {
|
|
@@ -27,11 +50,29 @@ HasItems.prototype.clear = function() {
|
|
|
27
50
|
return this;
|
|
28
51
|
};
|
|
29
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Removes a specific item from the collection
|
|
55
|
+
* @param {*} item - The item to remove
|
|
56
|
+
* @returns {HasItems}
|
|
57
|
+
*/
|
|
30
58
|
HasItems.prototype.removeItem = function(item) {
|
|
31
|
-
|
|
59
|
+
const items = this.$description.items;
|
|
60
|
+
if(Array.isArray(items)) {
|
|
61
|
+
const index = items.indexOf(item);
|
|
62
|
+
if(index > -1) {
|
|
63
|
+
items.splice(index, 1);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
items.removeItem(item);
|
|
32
68
|
return this;
|
|
33
69
|
};
|
|
34
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Sets the render function for items
|
|
73
|
+
* @param {(element: *) => ValidChildren} renderFn - Render function to apply
|
|
74
|
+
* @returns {HasItems}
|
|
75
|
+
*/
|
|
35
76
|
HasItems.prototype.render = function(renderFn) {
|
|
36
77
|
this.$description.render = renderFn;
|
|
37
78
|
return this;
|