native-document 1.0.14 → 1.0.15
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.dev.js +1262 -839
- package/dist/native-document.min.js +1 -1
- package/docs/anchor.md +216 -53
- package/docs/conditional-rendering.md +25 -24
- package/docs/core-concepts.md +20 -19
- package/docs/elements.md +21 -20
- package/docs/getting-started.md +28 -27
- package/docs/lifecycle-events.md +2 -2
- package/docs/list-rendering.md +607 -0
- package/docs/memory-management.md +1 -1
- package/docs/observables.md +15 -14
- package/docs/routing.md +22 -22
- package/docs/state-management.md +8 -8
- package/docs/validation.md +0 -2
- package/index.js +6 -1
- package/package.json +1 -1
- package/readme.md +5 -4
- package/src/data/MemoryManager.js +8 -20
- package/src/data/Observable.js +2 -180
- package/src/data/ObservableChecker.js +25 -24
- package/src/data/ObservableItem.js +158 -79
- package/src/data/observable-helpers/array.js +74 -0
- package/src/data/observable-helpers/batch.js +22 -0
- package/src/data/observable-helpers/computed.js +28 -0
- package/src/data/observable-helpers/object.js +111 -0
- package/src/elements/anchor.js +54 -9
- package/src/elements/control/for-each-array.js +280 -0
- package/src/elements/control/for-each.js +87 -110
- package/src/elements/index.js +1 -0
- package/src/elements/list.js +4 -0
- package/src/utils/helpers.js +44 -21
- package/src/wrappers/AttributesWrapper.js +5 -18
- package/src/wrappers/DocumentObserver.js +58 -29
- package/src/wrappers/ElementCreator.js +114 -0
- package/src/wrappers/HtmlElementEventsWrapper.js +52 -65
- package/src/wrappers/HtmlElementWrapper.js +11 -167
- package/src/wrappers/NdPrototype.js +109 -0
|
@@ -1,44 +1,73 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {debounce} from "../utils/helpers";
|
|
2
2
|
|
|
3
3
|
const DocumentObserver = {
|
|
4
|
-
|
|
4
|
+
mounted: new WeakMap(),
|
|
5
|
+
mountedSupposedSize: 0,
|
|
6
|
+
unmounted: new WeakMap(),
|
|
7
|
+
unmountedSupposedSize: 0,
|
|
5
8
|
observer: null,
|
|
6
|
-
checkMutation:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
checkMutation: debounce(function(mutationsList) {
|
|
10
|
+
let i = 0;
|
|
11
|
+
for(const mutation of mutationsList) {
|
|
12
|
+
if(DocumentObserver.mountedSupposedSize > 0 ) {
|
|
13
|
+
for(const node of mutation.addedNodes) {
|
|
14
|
+
const data = DocumentObserver.mounted.get(node);
|
|
15
|
+
if(!data) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
data.inDom = true;
|
|
19
|
+
data.mounted && data.mounted(node);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if(DocumentObserver.unmountedSupposedSize > 0 ) {
|
|
24
|
+
for(const node of mutation.removedNodes) {
|
|
25
|
+
const data = DocumentObserver.unmounted.get(node);
|
|
26
|
+
if(!data) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
data.inDom = false;
|
|
31
|
+
if(data.unmounted && data.unmounted(node) === true) {
|
|
32
|
+
data.disconnect();
|
|
33
|
+
node.nd?.remove();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
15
36
|
}
|
|
16
37
|
}
|
|
17
|
-
},
|
|
38
|
+
}, 16),
|
|
18
39
|
/**
|
|
19
40
|
*
|
|
20
41
|
* @param {HTMLElement} element
|
|
42
|
+
* @param {boolean} inDom
|
|
21
43
|
* @returns {{watch: (function(): Map<any, any>), disconnect: (function(): boolean), mounted: (function(*): Set<any>), unmounted: (function(*): Set<any>)}}
|
|
22
44
|
*/
|
|
23
|
-
watch: function(element) {
|
|
24
|
-
let data = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
45
|
+
watch: function(element, inDom = false) {
|
|
46
|
+
let data = {
|
|
47
|
+
inDom,
|
|
48
|
+
mounted: null,
|
|
49
|
+
unmounted: null,
|
|
50
|
+
disconnect: () => {
|
|
51
|
+
DocumentObserver.mounted.delete(element);
|
|
52
|
+
DocumentObserver.unmounted.delete(element);
|
|
53
|
+
DocumentObserver.mountedSupposedSize--;
|
|
54
|
+
DocumentObserver.unmountedSupposedSize--;
|
|
55
|
+
data = null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
36
58
|
|
|
37
59
|
return {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
60
|
+
disconnect: data.disconnect,
|
|
61
|
+
mounted: (callback) => {
|
|
62
|
+
data.mounted = callback;
|
|
63
|
+
DocumentObserver.mounted.set(element, data);
|
|
64
|
+
DocumentObserver.mountedSupposedSize++;
|
|
65
|
+
},
|
|
66
|
+
unmounted: (callback) => {
|
|
67
|
+
data.unmounted = callback;
|
|
68
|
+
DocumentObserver.unmounted.set(element, data);
|
|
69
|
+
DocumentObserver.unmountedSupposedSize++;
|
|
70
|
+
}
|
|
42
71
|
};
|
|
43
72
|
}
|
|
44
73
|
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import Anchor from "../elements/anchor";
|
|
2
|
+
import Validator from "../utils/validator";
|
|
3
|
+
import AttributesWrapper from "./AttributesWrapper";
|
|
4
|
+
|
|
5
|
+
const $nodeCache = new Map();
|
|
6
|
+
let $textNodeCache = null;
|
|
7
|
+
|
|
8
|
+
export const ElementCreator = {
|
|
9
|
+
createTextNode() {
|
|
10
|
+
if(!$textNodeCache) {
|
|
11
|
+
$textNodeCache = document.createTextNode('');
|
|
12
|
+
}
|
|
13
|
+
return $textNodeCache.cloneNode();
|
|
14
|
+
},
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @param {HTMLElement|DocumentFragment} parent
|
|
18
|
+
* @param {ObservableItem} observable
|
|
19
|
+
* @returns {Text}
|
|
20
|
+
*/
|
|
21
|
+
createObservableNode(parent, observable) {
|
|
22
|
+
const text = ElementCreator.createTextNode();
|
|
23
|
+
observable.subscribe(value => text.nodeValue = String(value));
|
|
24
|
+
text.nodeValue = observable.val();
|
|
25
|
+
parent && parent.appendChild(text);
|
|
26
|
+
return text;
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @param {HTMLElement|DocumentFragment} parent
|
|
32
|
+
* @param {*} value
|
|
33
|
+
* @returns {Text}
|
|
34
|
+
*/
|
|
35
|
+
createStaticTextNode(parent, value) {
|
|
36
|
+
let text = ElementCreator.createTextNode();
|
|
37
|
+
text.nodeValue = String(value);
|
|
38
|
+
parent && parent.appendChild(text);
|
|
39
|
+
return text;
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @param {string} name
|
|
44
|
+
* @returns {HTMLElement|DocumentFragment}
|
|
45
|
+
*/
|
|
46
|
+
createElement(name) {
|
|
47
|
+
if(name) {
|
|
48
|
+
if($nodeCache.has(name)) {
|
|
49
|
+
return $nodeCache.get(name).cloneNode();
|
|
50
|
+
}
|
|
51
|
+
const node = document.createElement(name);
|
|
52
|
+
$nodeCache.set(name, node);
|
|
53
|
+
return node.cloneNode();
|
|
54
|
+
}
|
|
55
|
+
return new Anchor('Fragment');
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
*
|
|
59
|
+
* @param {*} children
|
|
60
|
+
* @param {HTMLElement|DocumentFragment} parent
|
|
61
|
+
*/
|
|
62
|
+
processChildren(children, parent) {
|
|
63
|
+
if(children === null) return;
|
|
64
|
+
const childrenArray = Array.isArray(children) ? children : [children];
|
|
65
|
+
|
|
66
|
+
for(let i = 0, length = childrenArray.length; i < length; i++) {
|
|
67
|
+
let child = childrenArray[i];
|
|
68
|
+
if (child === null) continue;
|
|
69
|
+
if(Validator.isString(child) && Validator.isFunction(child.resolveObservableTemplate)) {
|
|
70
|
+
child = child.resolveObservableTemplate();
|
|
71
|
+
}
|
|
72
|
+
if(Validator.isFunction(child)) {
|
|
73
|
+
this.processChildren(child(), parent);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if(Validator.isArray(child)) {
|
|
77
|
+
this.processChildren(child, parent);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (Validator.isElement(child)) {
|
|
81
|
+
parent.appendChild(child);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (Validator.isObservable(child)) {
|
|
85
|
+
ElementCreator.createObservableNode(parent, child);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (child) {
|
|
89
|
+
ElementCreator.createStaticTextNode(parent, child);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
*
|
|
95
|
+
* @param {HTMLElement} element
|
|
96
|
+
* @param {Object} attributes
|
|
97
|
+
*/
|
|
98
|
+
processAttributes(element, attributes) {
|
|
99
|
+
if(Validator.isFragment(element)) return;
|
|
100
|
+
if (attributes) {
|
|
101
|
+
AttributesWrapper(element, attributes);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
/**
|
|
105
|
+
*
|
|
106
|
+
* @param {HTMLElement} element
|
|
107
|
+
* @param {Object} attributes
|
|
108
|
+
* @param {?Function} customWrapper
|
|
109
|
+
* @returns {HTMLElement|DocumentFragment}
|
|
110
|
+
*/
|
|
111
|
+
setup(element, attributes, customWrapper) {
|
|
112
|
+
return element;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
@@ -1,6 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {HTMLElement} element
|
|
6
|
+
* @returns {HTMLElement}
|
|
7
|
+
*/
|
|
8
|
+
function HtmlElementPreventEventsWrapper(element) {
|
|
9
|
+
const events = {};
|
|
10
|
+
return new Proxy(events, {
|
|
11
|
+
get(target, property) {
|
|
12
|
+
if(events[property]) {
|
|
13
|
+
return events[property];
|
|
14
|
+
}
|
|
15
|
+
events[property] = function(callback) {
|
|
16
|
+
element.addEventListener(property.toLowerCase(), function(event) {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
callback.apply(this, [event]);
|
|
19
|
+
});
|
|
20
|
+
return element
|
|
21
|
+
};
|
|
22
|
+
return events[property];
|
|
23
|
+
},
|
|
24
|
+
set(target, property, newValue, receiver) {
|
|
25
|
+
//...
|
|
26
|
+
this.get(target, property)(newValue);
|
|
27
|
+
return element;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
4
30
|
}
|
|
5
31
|
|
|
6
32
|
/**
|
|
@@ -9,69 +35,30 @@ function eventWrapper(element, name, callback) {
|
|
|
9
35
|
* @returns {HTMLElement}
|
|
10
36
|
*/
|
|
11
37
|
export default function HtmlElementEventsWrapper(element) {
|
|
38
|
+
const events = {};
|
|
39
|
+
return new Proxy(events, {
|
|
40
|
+
get(target, property) {
|
|
41
|
+
if(events[property]) {
|
|
42
|
+
return events[property];
|
|
43
|
+
}
|
|
44
|
+
if(property === 'prevent') {
|
|
45
|
+
events[property] = HtmlElementPreventEventsWrapper(element);
|
|
46
|
+
return events[property];
|
|
47
|
+
}
|
|
48
|
+
if(property === 'removeAll') {
|
|
12
49
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
element.nd.on = function(events) {
|
|
21
|
-
for(const event in events) {
|
|
22
|
-
const callback = events[event];
|
|
23
|
-
eventWrapper(element, event, callback);
|
|
24
|
-
}
|
|
25
|
-
return element;
|
|
26
|
-
};
|
|
27
|
-
element.nd.on.prevent = function(events) {
|
|
28
|
-
for(const event in events) {
|
|
29
|
-
const callback = events[event];
|
|
30
|
-
eventWrapper(element, event, (event) => {
|
|
31
|
-
event.preventDefault();
|
|
32
|
-
callback && callback(event);
|
|
33
|
-
return element;
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return element;
|
|
37
|
-
};
|
|
38
|
-
const events = {
|
|
39
|
-
click: (callback) => eventWrapper(element, 'click', callback),
|
|
40
|
-
focus: (callback) => eventWrapper(element, 'focus', callback),
|
|
41
|
-
blur: (callback) => eventWrapper(element, 'blur', callback),
|
|
42
|
-
input: (callback) => eventWrapper(element, 'input', callback),
|
|
43
|
-
change: (callback) => eventWrapper(element, 'change', callback),
|
|
44
|
-
keyup: (callback) => eventWrapper(element, 'keyup', callback),
|
|
45
|
-
keydown: (callback) => eventWrapper(element, 'keydown', callback),
|
|
46
|
-
beforeInput: (callback) => eventWrapper(element, 'beforeinput', callback),
|
|
47
|
-
mouseOver: (callback) => eventWrapper(element, 'mouseover', callback),
|
|
48
|
-
mouseOut: (callback) => eventWrapper(element, 'mouseout', callback),
|
|
49
|
-
mouseDown: (callback) => eventWrapper(element, 'mousedown', callback),
|
|
50
|
-
mouseUp: (callback) => eventWrapper(element, 'mouseup', callback),
|
|
51
|
-
mouseMove: (callback) => eventWrapper(element, 'mousemove', callback),
|
|
52
|
-
hover: (mouseInCallback, mouseOutCallback) => {
|
|
53
|
-
element.addEventListener('mouseover', mouseInCallback);
|
|
54
|
-
element.addEventListener('mouseout', mouseOutCallback);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
events[property] = function(callback) {
|
|
53
|
+
element.addEventListener(property.toLowerCase(), callback);
|
|
54
|
+
return element
|
|
55
|
+
};
|
|
56
|
+
return events[property];
|
|
55
57
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
dragStart: (callback) => eventWrapper(element, 'dragstart', callback),
|
|
60
|
-
drop: (callback) => eventWrapper(element, 'drop', callback),
|
|
61
|
-
dragOver: (callback) => eventWrapper(element, 'dragover', callback),
|
|
62
|
-
dragEnter: (callback) => eventWrapper(element, 'dragenter', callback),
|
|
63
|
-
dragLeave: (callback) => eventWrapper(element, 'dragleave', callback),
|
|
64
|
-
};
|
|
65
|
-
for(let event in events) {
|
|
66
|
-
element.nd.on[event] = events[event];
|
|
67
|
-
element.nd.on.prevent[event] = function(callback) {
|
|
68
|
-
eventWrapper(element, event.toLowerCase(), (event) => {
|
|
69
|
-
event.preventDefault();
|
|
70
|
-
callback && callback(event);
|
|
71
|
-
});
|
|
58
|
+
set(target, property, newValue, receiver) {
|
|
59
|
+
//...
|
|
60
|
+
this.get(target, property)(newValue);
|
|
72
61
|
return element;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return element;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
77
64
|
}
|
|
@@ -1,91 +1,7 @@
|
|
|
1
|
-
import HtmlElementEventsWrapper from "./HtmlElementEventsWrapper";
|
|
2
|
-
import AttributesWrapper from "./AttributesWrapper";
|
|
3
|
-
import NativeDocumentError from "../errors/NativeDocumentError";
|
|
4
|
-
import DocumentObserver from "./DocumentObserver";
|
|
5
1
|
import Validator from "../utils/validator";
|
|
6
2
|
import DebugManager from "../utils/debug-manager";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
-
* @param {HTMLElement|DocumentFragment} parent
|
|
13
|
-
* @param {ObservableItem} observable
|
|
14
|
-
* @returns {Text}
|
|
15
|
-
*/
|
|
16
|
-
const createObservableNode = function(parent, observable) {
|
|
17
|
-
const text = document.createTextNode('');
|
|
18
|
-
observable.subscribe(value => text.textContent = String(value));
|
|
19
|
-
text.textContent = observable.val();
|
|
20
|
-
parent && parent.appendChild(text);
|
|
21
|
-
return text;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
* @param {HTMLElement|DocumentFragment} parent
|
|
27
|
-
* @param {*} value
|
|
28
|
-
* @returns {Text}
|
|
29
|
-
*/
|
|
30
|
-
const createStaticTextNode = function(parent, value) {
|
|
31
|
-
const text = document.createTextNode('');
|
|
32
|
-
text.textContent = String(value);
|
|
33
|
-
parent && parent.appendChild(text);
|
|
34
|
-
return text;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
* @param {HTMLElement} element
|
|
40
|
-
*/
|
|
41
|
-
const addUtilsMethods = function(element) {
|
|
42
|
-
element.nd.wrap = (callback) => {
|
|
43
|
-
if(!Validator.isFunction(callback)) {
|
|
44
|
-
throw new NativeDocumentError('Callback must be a function');
|
|
45
|
-
}
|
|
46
|
-
callback && callback(element);
|
|
47
|
-
return element;
|
|
48
|
-
};
|
|
49
|
-
element.nd.ref = (target, name) => {
|
|
50
|
-
target[name] = element;
|
|
51
|
-
return element;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
let $observer = null;
|
|
55
|
-
|
|
56
|
-
element.nd.appendChild = function(child) {
|
|
57
|
-
if(Validator.isArray(child)) {
|
|
58
|
-
ElementCreator.processChildren(child, element);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if(Validator.isFunction(child)) {
|
|
62
|
-
child = child();
|
|
63
|
-
ElementCreator.processChildren(child(), element);
|
|
64
|
-
}
|
|
65
|
-
if(Validator.isElement(child)) {
|
|
66
|
-
ElementCreator.processChildren(child, element);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
element.nd.lifecycle = function(states) {
|
|
71
|
-
$observer = $observer || DocumentObserver.watch(element);
|
|
72
|
-
|
|
73
|
-
states.mounted && $observer.mounted(states.mounted);
|
|
74
|
-
states.unmounted && $observer.unmounted(states.unmounted);
|
|
75
|
-
return element;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
element.nd.mounted = (callback) => {
|
|
79
|
-
$observer = $observer || DocumentObserver.watch(element);
|
|
80
|
-
$observer.mounted(callback);
|
|
81
|
-
return element;
|
|
82
|
-
};
|
|
83
|
-
element.nd.unmounted = (callback) => {
|
|
84
|
-
$observer = $observer || DocumentObserver.watch(element);
|
|
85
|
-
$observer.unmounted(callback);
|
|
86
|
-
return element;
|
|
87
|
-
};
|
|
88
|
-
};
|
|
3
|
+
import {ElementCreator} from "./ElementCreator";
|
|
4
|
+
import './NdPrototype';
|
|
89
5
|
|
|
90
6
|
/**
|
|
91
7
|
*
|
|
@@ -94,84 +10,10 @@ const addUtilsMethods = function(element) {
|
|
|
94
10
|
*/
|
|
95
11
|
export const createTextNode = function(value) {
|
|
96
12
|
return (Validator.isObservable(value))
|
|
97
|
-
? createObservableNode(null, value)
|
|
98
|
-
: createStaticTextNode(null, value);
|
|
13
|
+
? ElementCreator.createObservableNode(null, value)
|
|
14
|
+
: ElementCreator.createStaticTextNode(null, value);
|
|
99
15
|
};
|
|
100
16
|
|
|
101
|
-
export const ElementCreator = {
|
|
102
|
-
/**
|
|
103
|
-
*
|
|
104
|
-
* @param {string} name
|
|
105
|
-
* @returns {HTMLElement|DocumentFragment}
|
|
106
|
-
*/
|
|
107
|
-
createElement(name) {
|
|
108
|
-
return name ? document.createElement(name) : new Anchor('Fragment');
|
|
109
|
-
},
|
|
110
|
-
/**
|
|
111
|
-
*
|
|
112
|
-
* @param {*} children
|
|
113
|
-
* @param {HTMLElement|DocumentFragment} parent
|
|
114
|
-
*/
|
|
115
|
-
processChildren(children, parent) {
|
|
116
|
-
if(children === null) return;
|
|
117
|
-
const childrenArray = Array.isArray(children) ? children : [children];
|
|
118
|
-
childrenArray.forEach(child => {
|
|
119
|
-
if (child === null) return;
|
|
120
|
-
if(Validator.isString(child) && Validator.isFunction(child.resolveObservableTemplate)) {
|
|
121
|
-
child = child.resolveObservableTemplate();
|
|
122
|
-
}
|
|
123
|
-
if(Validator.isFunction(child)) {
|
|
124
|
-
this.processChildren(child(), parent);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if(Validator.isArray(child)) {
|
|
128
|
-
this.processChildren(child, parent);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
if (Validator.isElement(child)) {
|
|
132
|
-
parent.appendChild(child);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
if (Validator.isObservable(child)) {
|
|
136
|
-
createObservableNode(parent, child);
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (child) {
|
|
140
|
-
createStaticTextNode(parent, child);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
},
|
|
144
|
-
/**
|
|
145
|
-
*
|
|
146
|
-
* @param {HTMLElement} element
|
|
147
|
-
* @param {Object} attributes
|
|
148
|
-
*/
|
|
149
|
-
processAttributes(element, attributes) {
|
|
150
|
-
if(Validator.isFragment(element)) return;
|
|
151
|
-
if (attributes) {
|
|
152
|
-
AttributesWrapper(element, attributes);
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
/**
|
|
156
|
-
*
|
|
157
|
-
* @param {HTMLElement} element
|
|
158
|
-
* @param {Object} attributes
|
|
159
|
-
* @param {?Function} customWrapper
|
|
160
|
-
* @returns {HTMLElement|DocumentFragment}
|
|
161
|
-
*/
|
|
162
|
-
setup(element, attributes, customWrapper) {
|
|
163
|
-
element.nd = {};
|
|
164
|
-
HtmlElementEventsWrapper(element);
|
|
165
|
-
const item = (typeof customWrapper === 'function') ? customWrapper(element) : element;
|
|
166
|
-
addUtilsMethods(item);
|
|
167
|
-
|
|
168
|
-
PluginsManager.list().forEach(plugin => {
|
|
169
|
-
plugin?.element?.setup && plugin.element.setup(item, attributes);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
return item;
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
17
|
|
|
176
18
|
/**
|
|
177
19
|
*
|
|
@@ -180,7 +22,7 @@ export const ElementCreator = {
|
|
|
180
22
|
* @returns {Function}
|
|
181
23
|
*/
|
|
182
24
|
export default function HtmlElementWrapper(name, customWrapper) {
|
|
183
|
-
const $tagName = name.toLowerCase()
|
|
25
|
+
const $tagName = name.toLowerCase();
|
|
184
26
|
|
|
185
27
|
const builder = function(attributes, children = null) {
|
|
186
28
|
try {
|
|
@@ -190,11 +32,12 @@ export default function HtmlElementWrapper(name, customWrapper) {
|
|
|
190
32
|
attributes = tempChildren;
|
|
191
33
|
}
|
|
192
34
|
const element = ElementCreator.createElement($tagName);
|
|
35
|
+
const finalElement = (typeof customWrapper === 'function') ? customWrapper(element) : element;
|
|
193
36
|
|
|
194
|
-
ElementCreator.processAttributes(
|
|
195
|
-
ElementCreator.processChildren(children,
|
|
37
|
+
ElementCreator.processAttributes(finalElement, attributes);
|
|
38
|
+
ElementCreator.processChildren(children, finalElement);
|
|
196
39
|
|
|
197
|
-
return ElementCreator.setup(
|
|
40
|
+
return ElementCreator.setup(finalElement, attributes, customWrapper);
|
|
198
41
|
} catch (error) {
|
|
199
42
|
DebugManager.error('ElementCreation', `Error creating ${$tagName}`, error);
|
|
200
43
|
}
|
|
@@ -203,4 +46,5 @@ export default function HtmlElementWrapper(name, customWrapper) {
|
|
|
203
46
|
builder.hold = (children, attributes) => (() => builder(children, attributes));
|
|
204
47
|
|
|
205
48
|
return builder;
|
|
206
|
-
}
|
|
49
|
+
}
|
|
50
|
+
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import DocumentObserver from "./DocumentObserver";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(HTMLElement.prototype, 'nd', {
|
|
4
|
+
get() {
|
|
5
|
+
if(this.$ndProx) {
|
|
6
|
+
return this.$ndProx;
|
|
7
|
+
}
|
|
8
|
+
let element = this;
|
|
9
|
+
let lifecycle = null;
|
|
10
|
+
|
|
11
|
+
this.$ndProx = new Proxy({}, {
|
|
12
|
+
get(target, property) {
|
|
13
|
+
if(/^on[A-Z]/.test(property)) {
|
|
14
|
+
const event = property.replace(/^on/, '').toLowerCase();
|
|
15
|
+
const shouldPrevent = event.toLowerCase().startsWith('prevent');
|
|
16
|
+
let eventName = event.replace(/^prevent/i, '');
|
|
17
|
+
const shouldStop = event.toLowerCase().startsWith('stop');
|
|
18
|
+
eventName = eventName.replace(/^stop/i, '');
|
|
19
|
+
|
|
20
|
+
return function(callback) {
|
|
21
|
+
if(shouldPrevent && !shouldStop) {
|
|
22
|
+
element.addEventListener(eventName, function(event) {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
callback(event);
|
|
25
|
+
});
|
|
26
|
+
return element;
|
|
27
|
+
}
|
|
28
|
+
if(!shouldPrevent && shouldStop) {
|
|
29
|
+
element.addEventListener(eventName, function(event) {
|
|
30
|
+
event.stopPropagation();
|
|
31
|
+
callback(event);
|
|
32
|
+
});
|
|
33
|
+
return element;
|
|
34
|
+
}
|
|
35
|
+
if(shouldPrevent && shouldStop) {
|
|
36
|
+
element.addEventListener(eventName, function(event) {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
event.stopPropagation();
|
|
39
|
+
callback(event);
|
|
40
|
+
});
|
|
41
|
+
return element;
|
|
42
|
+
}
|
|
43
|
+
element.addEventListener(eventName, callback);
|
|
44
|
+
return element;
|
|
45
|
+
};
|
|
46
|
+
return fn;
|
|
47
|
+
}
|
|
48
|
+
if(property === 'ref') {
|
|
49
|
+
if(ref) {
|
|
50
|
+
return ref;
|
|
51
|
+
}
|
|
52
|
+
return function(target, name) {
|
|
53
|
+
target[name] = element;
|
|
54
|
+
return element;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if(property === 'unmountChildren') {
|
|
58
|
+
return () => {
|
|
59
|
+
for(let i = 0, length = element.children.length; i < length; i++) {
|
|
60
|
+
let elementchildren = element.children[i];
|
|
61
|
+
if(!elementchildren.$ndProx) {
|
|
62
|
+
elementchildren.nd?.remove();
|
|
63
|
+
}
|
|
64
|
+
elementchildren = null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if(property === 'remove') {
|
|
69
|
+
return function() {
|
|
70
|
+
element.nd.unmountChildren();
|
|
71
|
+
lifecycle = null;
|
|
72
|
+
element.$ndProx = null;
|
|
73
|
+
delete element.nd?.on?.prevent;
|
|
74
|
+
delete element.nd?.on;
|
|
75
|
+
delete element.nd;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if(property === 'hasLifecycle') {
|
|
79
|
+
return lifecycle !== null;
|
|
80
|
+
}
|
|
81
|
+
if(property === 'lifecycle') {
|
|
82
|
+
if(lifecycle) {
|
|
83
|
+
return lifecycle;
|
|
84
|
+
}
|
|
85
|
+
let $observer = null;
|
|
86
|
+
lifecycle = function(states) {
|
|
87
|
+
$observer = $observer || DocumentObserver.watch(element);
|
|
88
|
+
|
|
89
|
+
states.mounted && $observer.mounted(states.mounted);
|
|
90
|
+
states.unmounted && $observer.unmounted(states.unmounted);
|
|
91
|
+
return element;
|
|
92
|
+
};
|
|
93
|
+
return lifecycle;
|
|
94
|
+
}
|
|
95
|
+
if(property === 'mounted' || property === 'unmounted') {
|
|
96
|
+
return function(callback) {
|
|
97
|
+
element.nd.lifecycle({ [property]: callback});
|
|
98
|
+
return element;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
set(target, p, newValue, receiver) {
|
|
103
|
+
|
|
104
|
+
},
|
|
105
|
+
configurable: true
|
|
106
|
+
});
|
|
107
|
+
return this.$ndProx;
|
|
108
|
+
}
|
|
109
|
+
});
|