native-document 1.0.91 → 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 +1168 -138
- package/dist/native-document.dev.js +792 -217
- 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 +223 -80
- 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 +21 -3
- 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
|
@@ -5,12 +5,18 @@ import Anchor from "../../elements/anchor";
|
|
|
5
5
|
import {ElementCreator} from "../../wrappers/ElementCreator";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Conditionally shows an element based on an observable condition.
|
|
9
|
+
* The element is mounted/unmounted from the DOM as the condition changes.
|
|
9
10
|
*
|
|
10
|
-
* @param {ObservableItem
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {{
|
|
13
|
-
* @
|
|
11
|
+
* @param {ObservableItem<boolean>|ObservableChecker<boolean>|ObservableWhen} condition - Observable condition to watch
|
|
12
|
+
* @param {ValidChild} child - Element or content to show/hide
|
|
13
|
+
* @param {Object} [options={}] - Configuration options
|
|
14
|
+
* @param {string|null} [options.comment=null] - Comment for debugging
|
|
15
|
+
* @param {boolean} [options.shouldKeepInCache=true] - Whether to cache the element when hidden
|
|
16
|
+
* @returns {AnchorDocumentFragment} Anchor fragment managing the conditional content
|
|
17
|
+
* @example
|
|
18
|
+
* const isVisible = Observable(false);
|
|
19
|
+
* ShowIf(isVisible, Div({}, 'Hello World'));
|
|
14
20
|
*/
|
|
15
21
|
export const ShowIf = function(condition, child, { comment = null, shouldKeepInCache = true} = {}) {
|
|
16
22
|
if(!(Validator.isObservable(condition)) && !Validator.isObservableWhenResult(condition)) {
|
|
@@ -47,11 +53,18 @@ export const ShowIf = function(condition, child, { comment = null, shouldKeepInC
|
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* @param {
|
|
54
|
-
* @
|
|
56
|
+
* Conditionally hides an element when the observable condition is true.
|
|
57
|
+
* Inverse of ShowIf - element is shown when condition is false.
|
|
58
|
+
*
|
|
59
|
+
* @param {ObservableItem<boolean>|ObservableChecker<boolean>} condition - Observable condition to watch
|
|
60
|
+
* @param {ValidChild} child - Element or content to show/hide
|
|
61
|
+
* @param {Object} [configs] - Configuration options
|
|
62
|
+
* @param {string|null} [configs.comment] - Comment for debugging
|
|
63
|
+
* @param {boolean} [configs.shouldKeepInCache] - Whether to cache element when hidden
|
|
64
|
+
* @returns {AnchorDocumentFragment} Anchor fragment managing the conditional content
|
|
65
|
+
* @example
|
|
66
|
+
* const hasError = Observable(false);
|
|
67
|
+
* HideIf(hasError, Div({}, 'Content'));
|
|
55
68
|
*/
|
|
56
69
|
export const HideIf = function(condition, child, configs) {
|
|
57
70
|
const hideCondition = Observable(!condition.val());
|
|
@@ -61,12 +74,15 @@ export const HideIf = function(condition, child, configs) {
|
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
/**
|
|
64
|
-
*
|
|
77
|
+
* Conditionally hides an element when the observable condition is false.
|
|
78
|
+
* Same as ShowIf - element is shown when condition is true.
|
|
65
79
|
*
|
|
66
|
-
* @param {ObservableItem
|
|
67
|
-
* @param {
|
|
68
|
-
* @param {
|
|
69
|
-
* @
|
|
80
|
+
* @param {ObservableItem<boolean>|ObservableChecker<boolean>|ObservableWhen} condition - Observable condition to watch
|
|
81
|
+
* @param {ValidChild} child - Element or content to show/hide
|
|
82
|
+
* @param {Object} [configs] - Configuration options
|
|
83
|
+
* @param {string|null} [configs.comment] - Comment for debugging
|
|
84
|
+
* @param {boolean} [configs.shouldKeepInCache] - Whether to cache element when hidden
|
|
85
|
+
* @returns {AnchorDocumentFragment} Anchor fragment managing the conditional content
|
|
70
86
|
*/
|
|
71
87
|
export const HideIfNot = function(condition, child, configs) {
|
|
72
88
|
return ShowIf(condition, child, configs);
|
|
@@ -2,6 +2,29 @@ import Validator from "../../utils/validator.js";
|
|
|
2
2
|
import NativeDocumentError from "../../errors/NativeDocumentError.js";
|
|
3
3
|
import {ShowIf} from "./show-if.js";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Shows content when an observable equals a specific value.
|
|
7
|
+
* Can be called with 2 or 3 arguments.
|
|
8
|
+
*
|
|
9
|
+
* @overload
|
|
10
|
+
* @param {ObservableWhen} observerWhenResult - Result from observable.when(value)
|
|
11
|
+
* @param {ValidChild} view - Content to show when condition matches
|
|
12
|
+
* @returns {AnchorDocumentFragment}
|
|
13
|
+
*
|
|
14
|
+
* @overload
|
|
15
|
+
* @param {ObservableItem} observer - Observable to watch
|
|
16
|
+
* @param {*} target - Value to match
|
|
17
|
+
* @param {ValidChild} view - Content to show when observable equals target
|
|
18
|
+
* @returns {AnchorDocumentFragment}
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // 2 arguments
|
|
22
|
+
* const status = Observable('idle');
|
|
23
|
+
* ShowWhen(status.when('loading'), LoadingSpinner());
|
|
24
|
+
*
|
|
25
|
+
* // 3 arguments
|
|
26
|
+
* ShowWhen(status, 'loading', LoadingSpinner());
|
|
27
|
+
*/
|
|
5
28
|
export const ShowWhen = function() {
|
|
6
29
|
if(arguments.length === 2) {
|
|
7
30
|
const [observer, target] = arguments;
|
|
@@ -6,11 +6,24 @@ import {ElementCreator} from "../../wrappers/ElementCreator";
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
+
* Displays different content based on the current value of an observable.
|
|
10
|
+
* Like a switch statement for UI - shows the content corresponding to current value.
|
|
9
11
|
*
|
|
10
|
-
* @param {ObservableItem|ObservableChecker} $condition
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {
|
|
13
|
-
* @returns {
|
|
12
|
+
* @param {ObservableItem|ObservableChecker} $condition - Observable to watch
|
|
13
|
+
* @param {Object<string|number, ValidChild>} values - Map of values to their corresponding content
|
|
14
|
+
* @param {boolean} [shouldKeepInCache=true] - Whether to cache rendered views
|
|
15
|
+
* @returns {AnchorDocumentFragment & {add: Function, remove: Function}} Fragment with dynamic methods
|
|
16
|
+
* @example
|
|
17
|
+
* const status = Observable('idle');
|
|
18
|
+
* const view = Match(status, {
|
|
19
|
+
* idle: Div({}, 'Ready'),
|
|
20
|
+
* loading: Div({}, 'Loading...'),
|
|
21
|
+
* error: Div({}, 'Error occurred')
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Dynamic addition
|
|
25
|
+
* view.add('success', Div({}, 'Success!'));
|
|
26
|
+
* view.remove('idle');
|
|
14
27
|
*/
|
|
15
28
|
export const Match = function($condition, values, shouldKeepInCache = true) {
|
|
16
29
|
|
|
@@ -67,11 +80,19 @@ export const Match = function($condition, values, shouldKeepInCache = true) {
|
|
|
67
80
|
|
|
68
81
|
|
|
69
82
|
/**
|
|
83
|
+
* Displays one of two views based on a boolean observable condition.
|
|
84
|
+
* Simplified version of Match for true/false cases.
|
|
70
85
|
*
|
|
71
|
-
* @param {ObservableItem
|
|
72
|
-
* @param {
|
|
73
|
-
* @param {
|
|
74
|
-
* @returns {
|
|
86
|
+
* @param {ObservableItem<boolean>|ObservableChecker<boolean>} $condition - Boolean observable to watch
|
|
87
|
+
* @param {ValidChild} onTrue - Content to show when condition is true
|
|
88
|
+
* @param {ValidChild} onFalse - Content to show when condition is false
|
|
89
|
+
* @returns {AnchorDocumentFragment} Fragment managing the conditional content
|
|
90
|
+
* @example
|
|
91
|
+
* const isLoggedIn = Observable(false);
|
|
92
|
+
* Switch(isLoggedIn,
|
|
93
|
+
* Div({}, 'Welcome back!'),
|
|
94
|
+
* Div({}, 'Please login')
|
|
95
|
+
* );
|
|
75
96
|
*/
|
|
76
97
|
export const Switch = function ($condition, onTrue, onFalse) {
|
|
77
98
|
if(!Validator.isObservable($condition)) {
|
|
@@ -85,9 +106,15 @@ export const Switch = function ($condition, onTrue, onFalse) {
|
|
|
85
106
|
}
|
|
86
107
|
|
|
87
108
|
/**
|
|
109
|
+
* Provides a fluent API for conditional rendering with show/otherwise pattern.
|
|
88
110
|
*
|
|
89
|
-
* @param {ObservableItem
|
|
90
|
-
* @returns {{show: Function, otherwise:
|
|
111
|
+
* @param {ObservableItem<boolean>|ObservableChecker<boolean>} $condition - Boolean observable to watch
|
|
112
|
+
* @returns {{show: Function, otherwise: Function}} Object with fluent methods
|
|
113
|
+
* @example
|
|
114
|
+
* const isLoading = Observable(false);
|
|
115
|
+
* When(isLoading)
|
|
116
|
+
* .show(LoadingSpinner())
|
|
117
|
+
* .otherwise(Content());
|
|
91
118
|
*/
|
|
92
119
|
export const When = function($condition) {
|
|
93
120
|
if(!Validator.isObservable($condition)) {
|
|
@@ -105,6 +132,9 @@ export const When = function($condition) {
|
|
|
105
132
|
otherwise(onFalse) {
|
|
106
133
|
$onFalse = onFalse;
|
|
107
134
|
return Switch($condition, $onTrue, $onFalse);
|
|
135
|
+
},
|
|
136
|
+
toNdElement() {
|
|
137
|
+
return Switch($condition, $onTrue, $onFalse);
|
|
108
138
|
}
|
|
109
139
|
}
|
|
110
140
|
}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
export const once = (fn) => {
|
|
4
4
|
let result = null;
|
|
5
5
|
return (...args) => {
|
|
6
|
-
if(result
|
|
7
|
-
result
|
|
6
|
+
if(result) {
|
|
7
|
+
return result;
|
|
8
8
|
}
|
|
9
|
+
result = fn(...args);
|
|
9
10
|
return result;
|
|
10
11
|
};
|
|
11
12
|
};
|
|
@@ -14,9 +15,10 @@ export const autoOnce = (fn) => {
|
|
|
14
15
|
let target = null;
|
|
15
16
|
return new Proxy({}, {
|
|
16
17
|
get: (_, key) => {
|
|
17
|
-
if(target
|
|
18
|
-
target
|
|
18
|
+
if(target) {
|
|
19
|
+
return target[key];
|
|
19
20
|
}
|
|
21
|
+
target = fn();
|
|
20
22
|
return target[key];
|
|
21
23
|
}
|
|
22
24
|
});
|
|
@@ -26,10 +28,13 @@ export const memoize = (fn) => {
|
|
|
26
28
|
const cache = new Map();
|
|
27
29
|
return (...args) => {
|
|
28
30
|
const [key, ...rest] = args;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
const cached = cache.get(key);
|
|
32
|
+
if(cached) {
|
|
33
|
+
return cached;
|
|
31
34
|
}
|
|
32
|
-
|
|
35
|
+
const result = fn(...rest);
|
|
36
|
+
cache.set(key, result);
|
|
37
|
+
return result;
|
|
33
38
|
};
|
|
34
39
|
};
|
|
35
40
|
|
|
@@ -37,17 +42,21 @@ export const autoMemoize = (fn) => {
|
|
|
37
42
|
const cache = new Map();
|
|
38
43
|
return new Proxy({}, {
|
|
39
44
|
get: (_, key) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const cached = cache.get(key);
|
|
46
|
+
if(cached) {
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if(fn.length > 0) {
|
|
51
|
+
return (...args) => {
|
|
52
|
+
const result = fn(...args, key);
|
|
53
|
+
cache.set(key, result);
|
|
54
|
+
return result;
|
|
47
55
|
}
|
|
48
|
-
cache.set(key, fn());
|
|
49
56
|
}
|
|
50
|
-
|
|
57
|
+
const result = fn(key);
|
|
58
|
+
cache.set(key, result);
|
|
59
|
+
return result;
|
|
51
60
|
}
|
|
52
61
|
});
|
|
53
62
|
};
|
|
@@ -27,13 +27,14 @@ Function.prototype.cached = function(...args) {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
Function.prototype.errorBoundary = function(callback) {
|
|
30
|
-
|
|
30
|
+
const handler = (...args) => {
|
|
31
31
|
try {
|
|
32
32
|
return this.apply(this, args);
|
|
33
33
|
} catch(e) {
|
|
34
|
-
return callback(e);
|
|
34
|
+
return callback(e, {caller: handler, args: args });
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
+
return handler;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
String.prototype.use = function(args) {
|
|
@@ -17,7 +17,7 @@ export function bindClassAttribute(element, data) {
|
|
|
17
17
|
continue;
|
|
18
18
|
}
|
|
19
19
|
if(value.__$isObservableWhen) {
|
|
20
|
-
element.classes.toggle(className, value.
|
|
20
|
+
element.classes.toggle(className, value.isActive());
|
|
21
21
|
value.subscribe((shouldAdd) => element.classes.toggle(className, shouldAdd));
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
@@ -103,12 +103,35 @@ NDElement.prototype.closedShadow = function(style = null) {
|
|
|
103
103
|
return this.shadow('closed', style);
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Attaches a template binding to the element by hydrating it with the specified method.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} methodName - Name of the hydration method to call
|
|
110
|
+
* @param {BindingHydrator} bindingHydrator - Template binding with $hydrate method
|
|
111
|
+
* @returns {HTMLElement} The underlying HTML element
|
|
112
|
+
* @example
|
|
113
|
+
* const onClick = $binder.attach((event, data) => console.log(data));
|
|
114
|
+
* element.nd.attach('onClick', onClick);
|
|
115
|
+
*/
|
|
106
116
|
NDElement.prototype.attach = function(methodName, bindingHydrator) {
|
|
107
117
|
bindingHydrator.$hydrate(this.$element, methodName);
|
|
108
118
|
return this.$element;
|
|
109
119
|
};
|
|
110
120
|
|
|
111
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Extends the current NDElement instance with custom methods.
|
|
123
|
+
* Methods are bound to the instance and available for chaining.
|
|
124
|
+
*
|
|
125
|
+
* @param {Object} methods - Object containing method definitions
|
|
126
|
+
* @returns {this} The NDElement instance with added methods for chaining
|
|
127
|
+
* @example
|
|
128
|
+
* element.nd.with({
|
|
129
|
+
* highlight() {
|
|
130
|
+
* this.$element.style.background = 'yellow';
|
|
131
|
+
* return this;
|
|
132
|
+
* }
|
|
133
|
+
* }).highlight().onClick(() => console.log('Clicked'));
|
|
134
|
+
*/
|
|
112
135
|
NDElement.prototype.with = function(methods) {
|
|
113
136
|
if (!methods || typeof methods !== 'object') {
|
|
114
137
|
throw new NativeDocumentError('extend() requires an object of methods');
|
|
@@ -139,6 +162,23 @@ NDElement.prototype.with = function(methods) {
|
|
|
139
162
|
return this;
|
|
140
163
|
}
|
|
141
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Extends the NDElement prototype with new methods available to all NDElement instances.
|
|
167
|
+
* Use this to add global methods to all NDElements.
|
|
168
|
+
*
|
|
169
|
+
* @param {Object} methods - Object containing method definitions to add to prototype
|
|
170
|
+
* @returns {typeof NDElement} The NDElement constructor
|
|
171
|
+
* @throws {NativeDocumentError} If methods is not an object or contains non-function values
|
|
172
|
+
* @example
|
|
173
|
+
* NDElement.extend({
|
|
174
|
+
* fadeIn() {
|
|
175
|
+
* this.$element.style.opacity = '1';
|
|
176
|
+
* return this;
|
|
177
|
+
* }
|
|
178
|
+
* });
|
|
179
|
+
* // Now all NDElements have .fadeIn() method
|
|
180
|
+
* Div().nd.fadeIn();
|
|
181
|
+
*/
|
|
142
182
|
NDElement.extend = function(methods) {
|
|
143
183
|
if (!methods || typeof methods !== 'object') {
|
|
144
184
|
throw new NativeDocumentError('NDElement.extend() requires an object of methods');
|
|
@@ -38,6 +38,10 @@ EVENTS_WITH_STOP.forEach(eventSourceName => {
|
|
|
38
38
|
_stop(this.$element, eventName, callback);
|
|
39
39
|
return this;
|
|
40
40
|
};
|
|
41
|
+
NDElement.prototype['onPreventStop'+eventSourceName] = function(callback = null) {
|
|
42
|
+
_preventStop(this.$element, eventName, callback);
|
|
43
|
+
return this;
|
|
44
|
+
};
|
|
41
45
|
});
|
|
42
46
|
|
|
43
47
|
EVENTS_WITH_PREVENT.forEach(eventSourceName => {
|
|
@@ -65,7 +65,12 @@ const bindAttachMethods = function(node, bindDingData, data) {
|
|
|
65
65
|
|
|
66
66
|
const applyBindingTreePath = (root, target, data, path) => {
|
|
67
67
|
if(path.fn) {
|
|
68
|
-
path.fn
|
|
68
|
+
if(typeof path.fn === 'string') {
|
|
69
|
+
ElementCreator.bindTextNode(target, data[0][path.fn]);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
path.fn(data, target, root);
|
|
73
|
+
}
|
|
69
74
|
}
|
|
70
75
|
if(path.children) {
|
|
71
76
|
for(let i = 0, length = path.children.length; i < length; i++) {
|
|
@@ -91,15 +96,16 @@ export function TemplateCloner($fn) {
|
|
|
91
96
|
if(bindDingData && bindDingData.value) {
|
|
92
97
|
currentPath.fn = bindDingData.value;
|
|
93
98
|
const textNode = node.cloneNode();
|
|
99
|
+
if(typeof bindDingData.value === 'string') {
|
|
100
|
+
ElementCreator.bindTextNode(textNode, data[0][bindDingData.value]);
|
|
101
|
+
return textNode;
|
|
102
|
+
}
|
|
94
103
|
bindDingData.value(data, textNode);
|
|
95
104
|
return textNode;
|
|
96
105
|
}
|
|
97
106
|
return node.cloneNode(true);
|
|
98
107
|
}
|
|
99
|
-
const nodeCloned = node.cloneNode(
|
|
100
|
-
if(node.fullCloneNode) {
|
|
101
|
-
return nodeCloned;
|
|
102
|
-
}
|
|
108
|
+
const nodeCloned = node.cloneNode();
|
|
103
109
|
if(bindDingData) {
|
|
104
110
|
bindAttributes(nodeCloned, bindDingData, data);
|
|
105
111
|
bindAttachMethods(nodeCloned, bindDingData, data);
|
|
@@ -164,12 +170,9 @@ export function TemplateCloner($fn) {
|
|
|
164
170
|
}
|
|
165
171
|
this.value = (callbackOrProperty) => {
|
|
166
172
|
if(typeof callbackOrProperty !== 'function') {
|
|
167
|
-
return createBinding(
|
|
168
|
-
const firstArgument = data[0];
|
|
169
|
-
ElementCreator.bindTextNode(textNode, firstArgument[callbackOrProperty]);
|
|
170
|
-
}, 'value');
|
|
173
|
+
return createBinding(callbackOrProperty, 'value');
|
|
171
174
|
}
|
|
172
|
-
return createBinding(
|
|
175
|
+
return createBinding((data, textNode) => {
|
|
173
176
|
ElementCreator.bindTextNode(textNode, callbackOrProperty(...data));
|
|
174
177
|
}, 'value');
|
|
175
178
|
};
|
|
@@ -9,7 +9,7 @@ ObservableItem.prototype.bindNdClass = function(element, className) {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
ObservableWhen.prototype.bindNdClass = function(element, className) {
|
|
12
|
-
element.classes.toggle(className, this.
|
|
12
|
+
element.classes.toggle(className, this.isMatch());
|
|
13
13
|
this.subscribe((shouldAdd) => element.classes.toggle(className, shouldAdd));
|
|
14
14
|
};
|
|
15
15
|
|
|
@@ -4,6 +4,7 @@ import TemplateBinding from "../TemplateBinding";
|
|
|
4
4
|
import {ElementCreator} from "../ElementCreator";
|
|
5
5
|
import PluginsManager from "../../utils/plugins-manager";
|
|
6
6
|
import Validator from "../../utils/validator";
|
|
7
|
+
import ObservableChecker from "../../data/ObservableChecker";
|
|
7
8
|
|
|
8
9
|
String.prototype.toNdElement = function () {
|
|
9
10
|
const formattedChild = this.resolveObservableTemplate ? this.resolveObservableTemplate() : this;
|
|
@@ -33,6 +34,8 @@ ObservableItem.prototype.toNdElement = function () {
|
|
|
33
34
|
return ElementCreator.createObservableNode(null, this);
|
|
34
35
|
};
|
|
35
36
|
|
|
37
|
+
ObservableChecker.prototype.toNdElement = ObservableItem.prototype.toNdElement;
|
|
38
|
+
|
|
36
39
|
NDElement.prototype.toNdElement = function () {
|
|
37
40
|
return this.$element ?? this.$build?.() ?? this.build?.() ?? null;
|
|
38
41
|
};
|
package/src/router/Route.js
CHANGED
|
@@ -5,11 +5,16 @@ export const RouteParamPatterns = {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
+
* Creates a new Route instance.
|
|
8
9
|
*
|
|
9
|
-
* @param {string} $path
|
|
10
|
-
* @param {Function} $component
|
|
11
|
-
* @param {
|
|
12
|
-
* @
|
|
10
|
+
* @param {string} $path - URL pattern with optional parameters (e.g., '/user/{id:number}')
|
|
11
|
+
* @param {Function} $component - Component function that returns HTMLElement or DocumentFragment
|
|
12
|
+
* @param {Object} [$options={}] - Route configuration options
|
|
13
|
+
* @param {string} [$options.name] - Unique name for the route (used for navigation)
|
|
14
|
+
* @param {Function[]} [$options.middlewares] - Array of middleware functions
|
|
15
|
+
* @param {boolean} [$options.shouldRebuild] - Whether to rebuild component on each navigation
|
|
16
|
+
* @param {Object} [$options.with] - Custom parameter validation patterns
|
|
17
|
+
* @param {Function} [$options.layout] - Layout component wrapper function
|
|
13
18
|
*/
|
|
14
19
|
export function Route($path, $component, $options = {}) {
|
|
15
20
|
|
package/src/router/Router.js
CHANGED
|
@@ -14,7 +14,7 @@ export const DEFAULT_ROUTER_NAME = 'default';
|
|
|
14
14
|
/**
|
|
15
15
|
*
|
|
16
16
|
* @param {{mode: 'memory'|'history'|'hash'}} $options
|
|
17
|
-
* @
|
|
17
|
+
* @class
|
|
18
18
|
*/
|
|
19
19
|
export default function Router($options = {}) {
|
|
20
20
|
|
|
@@ -72,11 +72,20 @@ export default function Router($options = {}) {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
+
* Groups routes under a common path prefix with shared options.
|
|
75
76
|
*
|
|
76
|
-
* @param {string} suffix
|
|
77
|
-
* @param {
|
|
78
|
-
* @param {Function}
|
|
79
|
-
* @
|
|
77
|
+
* @param {string} suffix - Path prefix to prepend to all routes in the group
|
|
78
|
+
* @param {Object} options - Group configuration options
|
|
79
|
+
* @param {Function[]} [options.middlewares] - Middlewares applied to all routes in group
|
|
80
|
+
* @param {string} [options.name] - Name prefix for all routes in group
|
|
81
|
+
* @param {Function} [options.layout] - Layout component for all routes in group
|
|
82
|
+
* @param {Function} callback - Function that defines routes within the group
|
|
83
|
+
* @returns {this} Router instance for chaining
|
|
84
|
+
* @example
|
|
85
|
+
* router.group('/admin', { middlewares: [authMiddleware], layout: AdminLayout }, () => {
|
|
86
|
+
* router.add('/users', UsersPage, { name: 'users' });
|
|
87
|
+
* router.add('/settings', SettingsPage, { name: 'settings' });
|
|
88
|
+
* });
|
|
80
89
|
*/
|
|
81
90
|
this.group = function(suffix, options, callback) {
|
|
82
91
|
if(!Validator.isFunction(callback)) {
|
|
@@ -194,14 +203,24 @@ export default function Router($options = {}) {
|
|
|
194
203
|
Router.routers = {};
|
|
195
204
|
|
|
196
205
|
/**
|
|
206
|
+
* Creates and initializes a new router instance.
|
|
197
207
|
*
|
|
198
|
-
* @param {
|
|
199
|
-
* @param {
|
|
200
|
-
* @param {
|
|
208
|
+
* @param {Object} options - Router configuration
|
|
209
|
+
* @param {'memory'|'history'|'hash'} options.mode - Routing mode
|
|
210
|
+
* @param {string} [options.name] - Router name for multi-router apps
|
|
211
|
+
* @param {string} [options.entry] - Initial route path
|
|
212
|
+
* @param {Function} callback - Setup function that receives the router instance
|
|
213
|
+
* @returns {Router} The configured router instance with mount() method
|
|
214
|
+
* @example
|
|
215
|
+
* const router = Router.create({ mode: 'history' }, (r) => {
|
|
216
|
+
* r.add('/home', HomePage, { name: 'home' });
|
|
217
|
+
* r.add('/about', AboutPage, { name: 'about' });
|
|
218
|
+
* });
|
|
219
|
+
* router.mount('#app');
|
|
201
220
|
*/
|
|
202
221
|
Router.create = function(options, callback) {
|
|
203
222
|
if(!Validator.isFunction(callback)) {
|
|
204
|
-
DebugManager.error('Router', 'Callback must be a function'
|
|
223
|
+
DebugManager.error('Router', 'Callback must be a function');
|
|
205
224
|
throw new RouterError('Callback must be a function');
|
|
206
225
|
}
|
|
207
226
|
const router = new Router(options);
|
package/types/control-flow.d.ts
CHANGED
|
@@ -12,7 +12,10 @@ export interface HideIfFunction {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export interface MatchFunction {
|
|
15
|
-
<T extends string | number>(condition: ObservableItem<T> | ObservableChecker<T>, values: Record<T, ValidChild>, shouldKeepInCache?: boolean): DocumentFragment
|
|
15
|
+
<T extends string | number>(condition: ObservableItem<T> | ObservableChecker<T>, values: Record<T, ValidChild>, shouldKeepInCache?: boolean): DocumentFragment & {
|
|
16
|
+
add(key: T, child: ValidChild, shouldFocusOn?: boolean): void;
|
|
17
|
+
remove(key: T): void;
|
|
18
|
+
};
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
export interface SwitchFunction {
|
|
@@ -30,14 +33,14 @@ export interface WhenFunction {
|
|
|
30
33
|
export interface ForEachFunction {
|
|
31
34
|
<T>(data: T[] | Record<string, T> | ObservableItem<T[]> | ObservableItem<Record<string, T>>,
|
|
32
35
|
callback: (item: T, index?: ObservableItem<number>) => ValidChild,
|
|
33
|
-
key?: string | ((item: T, defaultKey: string | number) => string),
|
|
34
|
-
|
|
36
|
+
key?: string | ((item: T, defaultKey: string | number) => string | number),
|
|
37
|
+
options?: { shouldKeepItemsInCache: boolean, isParentUniqueChild: boolean, }): DocumentFragment,
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
export interface ForEachArrayFunction {
|
|
38
41
|
<T>(data: ObservableArray<T>,
|
|
39
42
|
callback: (item: T, index?: ObservableItem<number>) => ValidChild,
|
|
40
|
-
configs?: {
|
|
43
|
+
configs?: { isParentUniqueChild: boolean, shouldKeepItemsInCache?: boolean }): DocumentFragment;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export interface HideIfNotFunction {
|
|
@@ -45,8 +48,8 @@ export interface HideIfNotFunction {
|
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
|
|
48
|
-
export function ShowWhen(
|
|
49
|
-
export function ShowWhen(observer:
|
|
51
|
+
export function ShowWhen(observerWhenResult: ObservableWhen, view: ValidChild): ReturnType<typeof ShowIf>;
|
|
52
|
+
export function ShowWhen(observer: ObservableItem, target: any, view: ValidChild): ReturnType<typeof ShowIf>;
|
|
50
53
|
|
|
51
54
|
// Control Flow Components
|
|
52
55
|
export declare const ShowIf: ShowIfFunction;
|
package/types/elements.d.ts
CHANGED
|
@@ -130,12 +130,15 @@ export declare type AnchorDocumentFragment = DocumentFragment & {
|
|
|
130
130
|
clear: () => void;
|
|
131
131
|
remove: () => void;
|
|
132
132
|
removeChildren: () => void;
|
|
133
|
-
insertBefore: (child: ValidChild, before: HTMLElement|Comment) => void;
|
|
133
|
+
insertBefore: (child: ValidChild, before: HTMLElement|Comment|null) => void;
|
|
134
134
|
replaceContent: (child: ValidChild) => void;
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
setContent: (child: ValidChild) => void;
|
|
136
|
+
appendElement: (child: ValidChild, before: HTMLElement|Comment|null) => void;
|
|
137
|
+
append: (...args: ValidChild[]) => void;
|
|
138
|
+
getByIndex: (index: number) => HTMLElement|null;
|
|
137
139
|
endElement: () => Comment;
|
|
138
140
|
startElement: () => Comment;
|
|
141
|
+
removeWithAnchors: () => void;
|
|
139
142
|
};
|
|
140
143
|
|
|
141
144
|
// Anchor
|