native-document 1.0.86 → 1.0.88

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.
@@ -9,26 +9,6 @@ export function toggleElementClass(element, className, shouldAdd) {
9
9
  element.classes.toggle(className, shouldAdd);
10
10
  }
11
11
 
12
- export function toggleElementStyle(element, styleName, newValue) {
13
- element.style[styleName] = newValue;
14
- }
15
-
16
- export function updateInputFromObserver(element, attributeName, newValue) {
17
- if(Validator.isBoolean(newValue)) {
18
- element[attributeName] = newValue;
19
- return;
20
- }
21
- element[attributeName] = newValue === element.value;
22
- }
23
-
24
- export function updateObserverFromInput(element, attributeName, defaultValue, value) {
25
- if(Validator.isBoolean(defaultValue)) {
26
- value.set(element[attributeName]);
27
- return;
28
- }
29
- value.set(element.value);
30
- }
31
-
32
12
  /**
33
13
  *
34
14
  * @param {HTMLElement} element
@@ -37,12 +17,12 @@ export function updateObserverFromInput(element, attributeName, defaultValue, va
37
17
  export function bindClassAttribute(element, data) {
38
18
  for(let className in data) {
39
19
  const value = data[className];
40
- if(Validator.isObservable(value)) {
20
+ if(value.__$isObservable) {
41
21
  element.classes.toggle(className, value.val());
42
22
  value.subscribe(toggleElementClass.bind(null, element, className));
43
23
  continue;
44
24
  }
45
- if(Validator.isObservableWhenResult(value)) {
25
+ if(value.__$isObservableWhen) {
46
26
  element.classes.toggle(className, value.isMath());
47
27
  value.subscribe(toggleElementClass.bind(null, element, className));
48
28
  continue;
@@ -64,9 +44,9 @@ export function bindClassAttribute(element, data) {
64
44
  export function bindStyleAttribute(element, data) {
65
45
  for(let styleName in data) {
66
46
  const value = data[styleName];
67
- if(Validator.isObservable(value)) {
47
+ if(value.__$isObservable) {
68
48
  element.style[styleName] = value.val();
69
- value.subscribe(toggleElementStyle.bind(null, element, styleName));
49
+ value.subscribe((newValue) => element.style[styleName] = newValue);
70
50
  continue;
71
51
  }
72
52
  element.style[styleName] = value;
@@ -80,18 +60,26 @@ export function bindStyleAttribute(element, data) {
80
60
  * @param {boolean|number|Observable} value
81
61
  */
82
62
  export function bindBooleanAttribute(element, attributeName, value) {
83
- const defaultValue = Validator.isObservable(value) ? value.val() : value;
63
+ const isObservable = value.__$isObservable;
64
+ const defaultValue = isObservable? value.val() : value;
84
65
  if(Validator.isBoolean(defaultValue)) {
85
66
  element[attributeName] = defaultValue;
86
67
  }
87
68
  else {
88
69
  element[attributeName] = defaultValue === element.value;
89
70
  }
90
- if(Validator.isObservable(value)) {
91
- if(['checked'].includes(attributeName)) {
92
- element.addEventListener('input', updateObserverFromInput.bind(null, element, attributeName, defaultValue, value));
71
+ if(isObservable) {
72
+ if(attributeName === 'checked') {
73
+ if(typeof defaultValue === 'boolean') {
74
+ element.addEventListener('input', () => value.set(element[attributeName]));
75
+ }
76
+ else {
77
+ element.addEventListener('input', () => value.set(element.value));
78
+ }
79
+ value.subscribe((newValue) => element[attributeName] = newValue);
80
+ return;
93
81
  }
94
- value.subscribe(updateInputFromObserver.bind(null, element, attributeName));
82
+ value.subscribe((newValue) => element[attributeName] = (newValue === element.value));
95
83
  }
96
84
  }
97
85
 
@@ -103,19 +91,15 @@ export function bindBooleanAttribute(element, attributeName, value) {
103
91
  * @param {Observable} value
104
92
  */
105
93
  export function bindAttributeWithObservable(element, attributeName, value) {
106
- const applyValue = (newValue) => {
107
- if(attributeName === 'value') {
108
- element.value = newValue;
109
- return;
110
- }
111
- element.setAttribute(attributeName, newValue);
112
- };
113
- applyValue(value.val());
94
+ const applyValue = attributeName === 'value' ? (newValue) => element.value = newValue : (newValue) => element.setAttribute(attributeName, newValue);
114
95
  value.subscribe(applyValue);
115
96
 
116
97
  if(attributeName === 'value') {
98
+ element.value = value.val();
117
99
  element.addEventListener('input', () => value.set(element.value));
100
+ return;
118
101
  }
102
+ element.setAttribute(attributeName, value.val());
119
103
  }
120
104
 
121
105
  /**
@@ -127,10 +111,6 @@ export default function AttributesWrapper(element, attributes) {
127
111
 
128
112
  Validator.validateAttributes(attributes);
129
113
 
130
- if(!Validator.isObject(attributes)) {
131
- throw new NativeDocumentError('Attributes must be an object');
132
- }
133
-
134
114
  for(let key in attributes) {
135
115
  const attributeName = key.toLowerCase();
136
116
  let value = attributes[attributeName];
@@ -69,6 +69,14 @@ export const ElementCreator = {
69
69
  }
70
70
  return Anchor('Fragment');
71
71
  },
72
+ bindTextNode(textNode, value) {
73
+ if(value?.__$isObservable) {
74
+ value.subscribe(newValue => textNode.nodeValue = newValue);
75
+ textNode.nodeValue = value.val();
76
+ return;
77
+ }
78
+ textNode.nodeValue = value;
79
+ },
72
80
  /**
73
81
  *
74
82
  * @param {*} children
@@ -1,5 +1,4 @@
1
1
  import {ElementCreator} from "./ElementCreator";
2
- import {createTextNode} from "./HtmlElementWrapper";
3
2
  import TemplateBinding from "./TemplateBinding";
4
3
 
5
4
  const cloneBindingsDataCache = new WeakMap();
@@ -65,9 +64,8 @@ const bindAttachMethods = function(node, bindDingData, data) {
65
64
 
66
65
 
67
66
  const applyBindingTreePath = (root, target, data, path) => {
68
- let newTarget = null;
69
67
  if(path.fn) {
70
- newTarget = path.fn(data, target, root);
68
+ path.fn(data, target, root);
71
69
  }
72
70
  if(path.children) {
73
71
  for(let i = 0, length = path.children.length; i < length; i++) {
@@ -76,7 +74,6 @@ const applyBindingTreePath = (root, target, data, path) => {
76
74
  applyBindingTreePath(root, pathTargetNode, data, currentPath);
77
75
  }
78
76
  }
79
- return newTarget;
80
77
  };
81
78
 
82
79
  export function TemplateCloner($fn) {
@@ -92,15 +89,10 @@ export function TemplateCloner($fn) {
92
89
  const bindDingData = cloneBindingsDataCache.get(node);
93
90
  if(node.nodeType === 3) {
94
91
  if(bindDingData && bindDingData.value) {
95
- currentPath.fn = (data, targetNode, currentRoot) => {
96
- const newNode = bindDingData.value(data);
97
- if (targetNode === currentRoot) {
98
- return newNode;
99
- }
100
- targetNode.replaceWith(newNode);
101
- return null;
102
- };
103
- return bindDingData.value(data);
92
+ currentPath.fn = bindDingData.value;
93
+ const textNode = node.cloneNode();
94
+ bindDingData.value(data, textNode);
95
+ return textNode;
104
96
  }
105
97
  return node.cloneNode(true);
106
98
  }
@@ -137,11 +129,7 @@ export function TemplateCloner($fn) {
137
129
  const cloneWithBindingPaths = (data) => {
138
130
  let root = $node.cloneNode(true);
139
131
 
140
- const newRoot = applyBindingTreePath(root, root, data, $bindingTreePath);
141
- if(newRoot) {
142
- root = newRoot;
143
- }
144
-
132
+ applyBindingTreePath(root, root, data, $bindingTreePath);
145
133
  return root;
146
134
  };
147
135
 
@@ -176,13 +164,13 @@ export function TemplateCloner($fn) {
176
164
  }
177
165
  this.value = (callbackOrProperty) => {
178
166
  if(typeof callbackOrProperty !== 'function') {
179
- return createBinding(function(data) {
167
+ return createBinding(function(data, textNode) {
180
168
  const firstArgument = data[0];
181
- return createTextNode(firstArgument[callbackOrProperty]);
169
+ ElementCreator.bindTextNode(textNode, firstArgument[callbackOrProperty]);
182
170
  }, 'value');
183
171
  }
184
- return createBinding(function(data) {
185
- return createTextNode(callbackOrProperty(...data));
172
+ return createBinding(function(data, textNode) {
173
+ ElementCreator.bindTextNode(textNode, callbackOrProperty(...data));
186
174
  }, 'value');
187
175
  };
188
176
  this.attr = (fn) => {
@@ -1,4 +1,5 @@
1
1
  import Validator from "../core/utils/validator";
2
+ import {Anchor} from "../../elements";
2
3
 
3
4
  /**
4
5
  *
@@ -8,22 +9,84 @@ import Validator from "../core/utils/validator";
8
9
  export function RouterComponent(router, container) {
9
10
 
10
11
  const $cache = new Map();
12
+ const $layoutCache = new WeakMap();
13
+ const $routeInstanceAnchors = new WeakMap();
14
+ let $currentLayout = null;
15
+
11
16
  let $lastNodeInserted = null;
12
17
 
13
- const updateContainer = function(node, route) {
14
- container.innerHTML = '';
18
+ const getNodeAnchorForLayout = (node, path) => {
19
+ const existingAnchor = $routeInstanceAnchors.get(node);
20
+ if(existingAnchor) {
21
+ return existingAnchor;
22
+ }
23
+
24
+ let anchor = node;
25
+ if(!Validator.isAnchor(node)) {
26
+ anchor = Anchor(path);
27
+ anchor.appendChild(node);
28
+ }
29
+ $routeInstanceAnchors.set(node, anchor);
30
+ return anchor;
31
+ };
32
+
33
+ const removeLastNodeInserted = () => {
34
+ if(Validator.isAnchor($lastNodeInserted)) {
35
+ $lastNodeInserted.remove();
36
+ }
37
+ };
38
+ const cleanContainer = () => {
39
+ container.nodeValue = '';
40
+ removeLastNodeInserted();
41
+
42
+ if($currentLayout) {
43
+ $currentLayout.remove();
44
+ }
45
+ };
46
+
47
+ const getNodeToInsert = (node) => {
15
48
  let nodeToInsert = node;
16
- const layout = route.layout();
17
49
  if(Validator.isNDElement(node)) {
18
50
  nodeToInsert = node.node();
19
51
  }
20
- if(layout) {
21
- container.appendChild(layout(nodeToInsert));
52
+ return nodeToInsert;
53
+ };
54
+
55
+ const updateContainerByLayout = (layout, node, route, path) => {
56
+ let nodeToInsert = getNodeToInsert(node);
57
+
58
+ const cachedLayout = $layoutCache.get(nodeToInsert);
59
+ if(cachedLayout) {
60
+ if(cachedLayout === $currentLayout) {
61
+ const layoutAnchor = getNodeAnchorForLayout(nodeToInsert, path);
62
+ removeLastNodeInserted();
63
+ layoutAnchor.replaceContent(nodeToInsert);
64
+ return;
65
+ }
66
+ cleanContainer();
67
+ $currentLayout = cachedLayout;
68
+ const layoutAnchor = getNodeAnchorForLayout(nodeToInsert, path);
69
+ layoutAnchor.replaceContent(nodeToInsert);
70
+ container.appendChild($currentLayout);
22
71
  return;
23
72
  }
24
- if(Validator.isAnchor($lastNodeInserted)) {
25
- $lastNodeInserted.remove();
73
+ cleanContainer();
74
+ const anchor = getNodeAnchorForLayout(nodeToInsert, path);
75
+
76
+ $currentLayout = layout(anchor);
77
+ $layoutCache.set(nodeToInsert, $currentLayout);
78
+ container.appendChild($currentLayout);
79
+ }
80
+
81
+ const updateContainer = function(node, route, path) {
82
+ const layout = route.layout();
83
+ if(layout) {
84
+ updateContainerByLayout(layout, node, route, path);
85
+ return;
26
86
  }
87
+ let nodeToInsert = getNodeToInsert(node);
88
+
89
+ cleanContainer();
27
90
  container.appendChild(nodeToInsert);
28
91
  $lastNodeInserted = node;
29
92
  };
@@ -41,7 +104,7 @@ export function RouterComponent(router, container) {
41
104
  const Component = route.component();
42
105
  const node = Component({ params, query });
43
106
  $cache.set(path, node);
44
- updateContainer(node, route);
107
+ updateContainer(node, route, path);
45
108
  };
46
109
 
47
110
  router.subscribe(handleCurrentRouterState);