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.
Files changed (37) hide show
  1. package/dist/native-document.dev.js +1262 -839
  2. package/dist/native-document.min.js +1 -1
  3. package/docs/anchor.md +216 -53
  4. package/docs/conditional-rendering.md +25 -24
  5. package/docs/core-concepts.md +20 -19
  6. package/docs/elements.md +21 -20
  7. package/docs/getting-started.md +28 -27
  8. package/docs/lifecycle-events.md +2 -2
  9. package/docs/list-rendering.md +607 -0
  10. package/docs/memory-management.md +1 -1
  11. package/docs/observables.md +15 -14
  12. package/docs/routing.md +22 -22
  13. package/docs/state-management.md +8 -8
  14. package/docs/validation.md +0 -2
  15. package/index.js +6 -1
  16. package/package.json +1 -1
  17. package/readme.md +5 -4
  18. package/src/data/MemoryManager.js +8 -20
  19. package/src/data/Observable.js +2 -180
  20. package/src/data/ObservableChecker.js +25 -24
  21. package/src/data/ObservableItem.js +158 -79
  22. package/src/data/observable-helpers/array.js +74 -0
  23. package/src/data/observable-helpers/batch.js +22 -0
  24. package/src/data/observable-helpers/computed.js +28 -0
  25. package/src/data/observable-helpers/object.js +111 -0
  26. package/src/elements/anchor.js +54 -9
  27. package/src/elements/control/for-each-array.js +280 -0
  28. package/src/elements/control/for-each.js +87 -110
  29. package/src/elements/index.js +1 -0
  30. package/src/elements/list.js +4 -0
  31. package/src/utils/helpers.js +44 -21
  32. package/src/wrappers/AttributesWrapper.js +5 -18
  33. package/src/wrappers/DocumentObserver.js +58 -29
  34. package/src/wrappers/ElementCreator.js +114 -0
  35. package/src/wrappers/HtmlElementEventsWrapper.js +52 -65
  36. package/src/wrappers/HtmlElementWrapper.js +11 -167
  37. package/src/wrappers/NdPrototype.js +109 -0
@@ -0,0 +1,280 @@
1
+ import Anchor from "../anchor";
2
+ import {Observable} from "../../data/Observable";
3
+ import Validator from "../../utils/validator";
4
+ import {createTextNode} from "../../wrappers/HtmlElementWrapper";
5
+ import DebugManager from "../../utils/debug-manager";
6
+ import {getKey} from "../../utils/helpers";
7
+
8
+ export function ForEachArray(data, callback, key, configs = {}) {
9
+ const element = new Anchor('ForEach Array');
10
+ const blockEnd = element.endElement();
11
+ const blockStart = element.startElement();
12
+
13
+ let cache = new Map();
14
+ let nodeCacheByElement = new WeakMap();
15
+ let lastNumberOfItems = 0;
16
+
17
+ const keysCache = new WeakMap();
18
+
19
+ const clear = () => {
20
+ element.removeChildren();
21
+ cleanCache();
22
+ lastNumberOfItems = 0;
23
+ };
24
+ const getItemKey = (item, indexKey) => {
25
+ if(keysCache.has(item)) {
26
+ return keysCache.get(item);
27
+ }
28
+ return getKey(item, indexKey, key);
29
+ }
30
+
31
+ const updateIndexObservers = (items, startFrom = 0) => {
32
+ if(callback.length < 2) {
33
+ return;
34
+ }
35
+ let index = startFrom;
36
+ for(let i = startFrom, length = items?.length; i < length; i++) {
37
+ const cacheItem = cache.get(getItemKey(items[i], i));
38
+ if(!cacheItem) {
39
+ continue;
40
+ }
41
+ cacheItem.indexObserver?.deref()?.set(index);
42
+ index++;
43
+ }
44
+ };
45
+
46
+ const removeCacheItem = (cacheItem, removeChild = true) => {
47
+ if(!cacheItem) {
48
+ return;
49
+ }
50
+ const child = cacheItem.child?.deref();
51
+ cacheItem.indexObserver?.deref()?.cleanup();
52
+ cacheItem.child = null;
53
+ cacheItem.indexObserver = null;
54
+ nodeCacheByElement.delete(cacheItem.item);
55
+ keysCache.delete(cacheItem.item);
56
+ cacheItem.item = null;
57
+ if(removeChild) {
58
+ child?.remove();
59
+ cache.delete(cacheItem.keyId);
60
+ }
61
+ }
62
+
63
+ const removeCacheItemByKey = (keyId, removeChild = true) => {
64
+ removeCacheItem(cache.get(keyId), removeChild);
65
+ };
66
+
67
+ const cleanCache = () => {
68
+ for (const [keyId, cacheItem] of cache.entries()) {
69
+ removeCacheItem(cacheItem, false);
70
+ }
71
+ cache.clear();
72
+ }
73
+
74
+ const buildItem = (item, indexKey) => {
75
+ const keyId = getItemKey(item, indexKey);
76
+
77
+ if(cache.has(keyId)) {
78
+ const cacheItem = cache.get(keyId);
79
+ cacheItem.indexObserver?.deref()?.set(indexKey);
80
+ cacheItem.isNew = false;
81
+ const child = cacheItem.child?.deref();
82
+ if(child) {
83
+ return child;
84
+ }
85
+ cache.delete(keyId);
86
+ }
87
+
88
+ try {
89
+ const indexObserver = callback.length >= 2 ? Observable(indexKey) : null;
90
+ let child = callback(item, indexObserver);
91
+ if(Validator.isStringOrObservable(child)) {
92
+ child = createTextNode(child);
93
+ }
94
+ cache.set(keyId, {
95
+ keyId,
96
+ isNew: true,
97
+ item,
98
+ child: new WeakRef(child),
99
+ indexObserver: (indexObserver ? new WeakRef(indexObserver) : null)
100
+ });
101
+ keysCache.set(item, keyId);
102
+ if(Validator.isObject(item)) {
103
+ nodeCacheByElement.set(item, child);
104
+ }
105
+ return child;
106
+ } catch (e) {
107
+ DebugManager.error('ForEach', `Error creating element for key ${keyId}` , e);
108
+ throw e;
109
+ }
110
+ };
111
+ const getChildByKey = function(keyId, fragment) {
112
+ const cacheItem = cache.get(keyId);
113
+ if(!cacheItem) {
114
+ return null;
115
+ }
116
+ const child = cacheItem.child?.deref();
117
+ if(!child) {
118
+ removeCacheItem(cacheItem, false);
119
+ return null;
120
+ }
121
+ return child;
122
+ };
123
+
124
+ const removeByKey = function(keyId, fragment) {
125
+ const cacheItem = cache.get(keyId);
126
+ if(!cacheItem) {
127
+ return null;
128
+ }
129
+ const child = cacheItem.child?.deref();
130
+ if(!child) {
131
+ return null;
132
+ }
133
+
134
+ if(fragment) {
135
+ fragment.appendChild(child);
136
+ return;
137
+ }
138
+ child.remove();
139
+ }
140
+
141
+ const Actions = {
142
+ toFragment(items, startIndexFrom = 0){
143
+ const fragment = document.createDocumentFragment();
144
+ for(let i = 0, length = items.length; i < length; i++) {
145
+ fragment.append(buildItem(items[i], lastNumberOfItems));
146
+ lastNumberOfItems++;
147
+ }
148
+ return fragment;
149
+ },
150
+ add(items, delay = 0) {
151
+ setTimeout(() => {
152
+ element.appendElement(Actions.toFragment(items))
153
+ }, delay);
154
+ },
155
+ replace(items) {
156
+ clear();
157
+ Actions.add(items);
158
+ },
159
+ reOrder(items) {
160
+ let child = null;
161
+ const fragment = document.createDocumentFragment();
162
+ for(const item of items) {
163
+ child = nodeCacheByElement.get(item);
164
+ if(child) {
165
+ fragment.appendChild(child);
166
+ }
167
+ }
168
+ child = null;
169
+ element.appendElement(fragment, blockEnd);
170
+ },
171
+ removeOne(element, index) {
172
+ let child = nodeCacheByElement.get(element);
173
+ if(child) {
174
+ child.remove();
175
+ nodeCacheByElement.delete(element);
176
+ removeCacheItemByKey(getItemKey(element, index));
177
+ }
178
+ child = null;
179
+ },
180
+ clear,
181
+ push(items) {
182
+ let delay = 0;
183
+ if(configs.pushDelay) {
184
+ delay = configs.pushDelay(items) ?? 0;
185
+ } else {
186
+ delay = (items.length >= 1000) ? 10 : 0;
187
+ }
188
+ Actions.add(items, delay);
189
+ },
190
+ unshift(values){
191
+ element.insertBefore(Actions.toFragment(values), blockStart.nextSibling);
192
+ },
193
+ splice(args, deleted) {
194
+ const [start, deleteCount, ...values] = args;
195
+ let elementBeforeFirst = null;
196
+ const garbageFragment = document.createDocumentFragment();
197
+
198
+ if(deleted.length > 0) {
199
+ let firstKey = getItemKey(deleted[0], start);
200
+ if(deleted.length === 1) {
201
+ removeByKey(firstKey, garbageFragment);
202
+ } else if(deleted.length > 1) {
203
+ const firstChildRemoved = getChildByKey(firstKey);
204
+ elementBeforeFirst = firstChildRemoved?.previousSibling;
205
+
206
+ for(let i = 0; i < deleted.length; i++) {
207
+ const keyId = getItemKey(deleted[i], start + i, key);
208
+ removeByKey(keyId, garbageFragment);
209
+ }
210
+ }
211
+ } else {
212
+ elementBeforeFirst = blockEnd;
213
+ }
214
+ garbageFragment.replaceChildren();
215
+
216
+ if(values && values.length && elementBeforeFirst) {
217
+ element.insertBefore(Actions.toFragment(values), elementBeforeFirst.nextSibling);
218
+ }
219
+
220
+ },
221
+ reverse(_, reversed) {
222
+ Actions.reOrder(reversed);
223
+ },
224
+ sort(_, sorted) {
225
+ Actions.reOrder(sorted);
226
+ },
227
+ remove(_, deleted) {
228
+ Actions.removeOne(deleted);
229
+ },
230
+ pop(_, deleted) {
231
+ Actions.removeOne(deleted);
232
+ },
233
+ shift(_, deleted) {
234
+ Actions.removeOne(deleted);
235
+ },
236
+ swap(args, elements) {
237
+ const parent = blockEnd.parentNode;
238
+
239
+ let childA = nodeCacheByElement.get(elements[0]);
240
+ let childB = nodeCacheByElement.get(elements[1]);
241
+ if(!childA || !childB) {
242
+ return;
243
+ }
244
+
245
+ const childBNext = childB.nextSibling;
246
+ parent.insertBefore(childB, childA);
247
+ parent.insertBefore(childA, childBNext);
248
+ childA = null;
249
+ childB = null;
250
+ }
251
+ };
252
+
253
+ const buildContent = (items, _, operations) => {
254
+ if(operations.action === 'clear' || !items.length) {
255
+ if(lastNumberOfItems === 0) {
256
+ return;
257
+ }
258
+ clear();
259
+ }
260
+
261
+ if(!operations?.action) {
262
+ if(lastNumberOfItems === 0) {
263
+ Actions.add(items);
264
+ return;
265
+ }
266
+ Actions.replace(items);
267
+ }
268
+ else if(Actions[operations.action]) {
269
+ Actions[operations.action](operations.args, operations.result);
270
+ }
271
+ updateIndexObservers(items, 0);
272
+ };
273
+
274
+ buildContent(data.val(), null, {action: null});
275
+ if(Validator.isObservable(data)) {
276
+ data.subscribe(buildContent);
277
+ }
278
+
279
+ return element;
280
+ }
@@ -2,49 +2,9 @@ import ObservableItem from "../../data/ObservableItem";
2
2
  import {Observable} from "../../data/Observable";
3
3
  import {createTextNode} from "../../wrappers/HtmlElementWrapper";
4
4
  import Validator from "../../utils/validator";
5
- import {throttle} from "../../utils/helpers.js";
6
5
  import Anchor from "../anchor";
7
6
  import DebugManager from "../../utils/debug-manager";
8
-
9
-
10
- /**
11
- *
12
- * @param {*} item
13
- * @param {string|null} defaultKey
14
- * @param {?Function} key
15
- * @returns {*}
16
- */
17
- const getKey = (item, defaultKey, key) => {
18
- if(Validator.isFunction(key)) return key(item, defaultKey);
19
- if(Validator.isObservable(item)) {
20
- const val = item.val();
21
- return (val && key) ? val[key] : defaultKey;
22
- }
23
- return item[key] ?? defaultKey;
24
- }
25
-
26
- /**
27
- *
28
- * @param {Map} cache
29
- * @param {Set} keyIds
30
- */
31
- const cleanBlockByCache = (cache, keyIds) => {
32
- const toRemove = [];
33
- for(const [key, cacheItem] of cache.entries()) {
34
- if(keyIds.has(key)) {
35
- continue;
36
- }
37
- toRemove.push({ key, cacheItem });
38
- }
39
- if(toRemove.length === 0) {
40
- return;
41
- }
42
- toRemove.forEach(({ key, cacheItem }) => {
43
- cacheItem.child.remove();
44
- cacheItem.indexObserver.cleanup();
45
- cache.delete(key);
46
- });
47
- }
7
+ import {getKey} from "../../utils/helpers";
48
8
 
49
9
  /**
50
10
  *
@@ -59,111 +19,128 @@ export function ForEach(data, callback, key) {
59
19
  const blockStart = element.startElement();
60
20
 
61
21
  let cache = new Map();
22
+ let lastKeyOrder = null;
62
23
  const keyIds = new Set();
63
24
 
25
+ const clear = () => {
26
+ element.removeChildren();
27
+ cleanCache();
28
+ };
29
+
30
+ const cleanCache = (parent) => {
31
+ for(const [keyId, cacheItem] of cache.entries()) {
32
+ if(keyIds.has(keyId)) {
33
+ continue;
34
+ }
35
+ const child = cacheItem.child?.deref();
36
+ if(parent && child) {
37
+ parent.removeChild(child);
38
+ }
39
+ cacheItem.indexObserver?.cleanup();
40
+ cacheItem.child = null;
41
+ cacheItem.indexObserver = null;
42
+ cache.delete(cacheItem.keyId);
43
+ lastKeyOrder && lastKeyOrder.delete(cacheItem.keyId);
44
+ }
45
+ };
46
+
64
47
  const handleContentItem = (item, indexKey) => {
65
48
  const keyId = getKey(item, indexKey, key);
66
49
 
67
50
  if(cache.has(keyId)) {
68
51
  const cacheItem = cache.get(keyId);
69
- cacheItem.indexObserver.set(indexKey);
52
+ cacheItem.indexObserver?.set(indexKey);
70
53
  cacheItem.isNew = false;
54
+ if(cacheItem.child?.deref()) {
55
+ return keyId;
56
+ }
57
+ cache.delete(keyId);
71
58
  }
72
- else {
73
-
74
- try {
75
- const indexObserver = Observable(indexKey);
76
- let child = callback(item, indexObserver);
77
- if(Validator.isStringOrObservable(child)) {
78
- child = createTextNode(child);
79
- }
80
- cache.set(keyId, { isNew: true, child, indexObserver});
81
- } catch (e) {
82
- DebugManager.error('ForEach', `Error creating element for key ${keyId}` , e);
83
- throw e;
59
+
60
+ try {
61
+ const indexObserver = callback.length >= 2 ? Observable(indexKey) : null;
62
+ let child = callback(item, indexObserver);
63
+ if(Validator.isStringOrObservable(child)) {
64
+ child = createTextNode(child);
84
65
  }
66
+ cache.set(keyId, { keyId, isNew: true, child: new WeakRef(child), indexObserver});
67
+ } catch (e) {
68
+ DebugManager.error('ForEach', `Error creating element for key ${keyId}` , e);
69
+ throw e;
85
70
  }
86
71
  return keyId;
87
72
  };
88
73
 
89
- const batchDOMUpdates = () => {
90
- const parent = blockEnd.parentNode;
91
- if(!parent) {
92
- return;
74
+ const batchDOMUpdates = (parent) => {
75
+ const fragment = document.createDocumentFragment();
76
+ for(const itemKey of keyIds) {
77
+ const cacheItem = cache.get(itemKey);
78
+ if(!cacheItem) {
79
+ continue;
80
+ }
81
+ const child = cacheItem.child?.deref();
82
+ child && fragment.appendChild(child);
93
83
  }
84
+ parent.insertBefore(fragment, blockEnd);
85
+ }
94
86
 
95
- let previousElementSibling = blockStart;
96
- const elementsToInsert = [];
97
- const elementsToMove = [];
98
- let fragment = null;
87
+ const diffingDOMUpdates = (parent) => {
88
+ const operations = [];
89
+ let fragment = document.createDocumentFragment();
90
+ const newKeys = Array.from(keyIds);
91
+ const oldKeys = Array.from(lastKeyOrder);
99
92
 
100
- let saveFragment = (beforeTarget) => {
101
- if(fragment) {
102
- elementsToInsert.push({ child: fragment, before: beforeTarget });
103
- fragment = null;
104
- }
105
- };
93
+ let currentPosition = blockStart;
106
94
 
107
- const keyIdsArray = Array.from(keyIds);
108
- for(let i = 0; i < keyIdsArray.length; i++) {
109
- const itemKey = keyIdsArray[i];
95
+ for(const index in newKeys) {
96
+ const itemKey = newKeys[index];
110
97
  const cacheItem = cache.get(itemKey);
111
98
  if(!cacheItem) {
112
99
  continue;
113
100
  }
114
-
115
- if(previousElementSibling && previousElementSibling.nextSibling === cacheItem.child) {
116
- previousElementSibling = cacheItem.child;
117
- saveFragment(cacheItem.child);
118
- continue;
119
- }
120
- if(cacheItem.isNew) {
121
- fragment = fragment || document.createDocumentFragment();
122
- fragment.append(cacheItem.child);
123
- cacheItem.isNew = false;
101
+ const child = cacheItem.child.deref();
102
+ if(!child) {
124
103
  continue;
125
104
  }
126
- saveFragment(cacheItem.child);
127
- const nextChild = cache.get(keyIdsArray[i + 1])?.child;
128
- if(nextChild) {
129
- if(cacheItem.child.nextSibling !== nextChild) {
130
- elementsToMove.push({ child: cacheItem.child, before: nextChild });
131
- }
132
- }
133
-
134
- previousElementSibling = cacheItem.child;
105
+ fragment.appendChild(child);
135
106
  }
136
- saveFragment(blockEnd);
137
-
138
- elementsToInsert.forEach(({ child, before }) => {
139
- if(before) {
140
- parent.insertBefore(child, before);
141
- } else {
142
- element.appendChild(child);
143
- }
144
- });
145
-
146
- elementsToMove.forEach(({ child, before }) => {
147
- parent.insertBefore(child, before);
148
- })
149
- saveFragment = null;
150
-
107
+ element.replaceContent(fragment);
151
108
  };
152
109
 
153
110
  const buildContent = () => {
111
+ const parent = blockEnd.parentNode;
112
+ if(!parent) {
113
+ return;
114
+ }
115
+
154
116
  const items = (Validator.isObservable(data)) ? data.val() : data;
155
117
  keyIds.clear();
156
118
  if(Array.isArray(items)) {
157
- items.forEach((item, index) => keyIds.add(handleContentItem(item, index)));
119
+ for(let i = 0, length = items.length; i < length; i++) {
120
+ const keyId= handleContentItem(items[i], i);
121
+ keyIds.add(keyId);
122
+ }
158
123
  } else {
159
124
  for(const indexKey in items) {
160
- keyIds.add(handleContentItem(items[indexKey], indexKey));
125
+ const keyId = handleContentItem(items[indexKey], indexKey);
126
+ keyIds.add(keyId);
161
127
  }
162
128
  }
163
129
 
164
- cleanBlockByCache(cache, keyIds);
130
+ if(keyIds.size === 0) {
131
+ clear();
132
+ lastKeyOrder?.clear();
133
+ return;
134
+ }
165
135
 
166
- batchDOMUpdates();
136
+ cleanCache(parent);
137
+ if(!lastKeyOrder || lastKeyOrder.size === 0) {
138
+ batchDOMUpdates(parent);
139
+ } else {
140
+ diffingDOMUpdates(parent);
141
+ }
142
+ lastKeyOrder?.clear();
143
+ lastKeyOrder = new Set([...keyIds]);
167
144
  };
168
145
 
169
146
  buildContent();
@@ -171,4 +148,4 @@ export function ForEach(data, callback, key) {
171
148
  data.subscribe(buildContent)
172
149
  }
173
150
  return element;
174
- }
151
+ }
@@ -1,6 +1,7 @@
1
1
  import HtmlElementWrapper from "../wrappers/HtmlElementWrapper";
2
2
 
3
3
  export * from './control/for-each';
4
+ export * from './control/for-each-array';
4
5
  export * from './control/show-if';
5
6
  export * from './control/switch';
6
7
  export * from './content-formatter';
@@ -4,3 +4,7 @@ export const OrderedList = HtmlElementWrapper('ol');
4
4
  export const UnorderedList = HtmlElementWrapper('ul');
5
5
  export const ListItem = HtmlElementWrapper('li');
6
6
 
7
+ export const Li = ListItem;
8
+ export const Ol = OrderedList;
9
+ export const Ul = UnorderedList;
10
+
@@ -1,35 +1,58 @@
1
+ import Validator from "./validator";
2
+
3
+ const invoke = function(fn, args, context) {
4
+ if(context) {
5
+ fn.apply(context, args);
6
+ } else {
7
+ fn(...args);
8
+ }
9
+ };
1
10
  /**
2
11
  *
3
12
  * @param {Function} fn
4
13
  * @param {number} delay
5
- * @param {{leading?:Boolean, trailing?:Boolean, debounce?:Boolean}}options
14
+ * @param {{leading?:Boolean, trailing?:Boolean, debounce?:Boolean, check: Function}}options
6
15
  * @returns {(function(...[*]): void)|*}
7
16
  */
8
- export const throttle = function(fn, delay, options = {}) {
17
+ export const debounce = function(fn, delay, options = {}) {
9
18
  let timer = null;
10
- let lastExecTime = 0;
11
- const { leading = true, trailing = true, debounce = false } = options;
19
+ let lastArgs = null;
12
20
 
13
21
  return function(...args) {
14
- const now = Date.now();
15
- if (debounce) {
16
- // debounce mode: reset the timer for each call
17
- clearTimeout(timer);
18
- timer = setTimeout(() => fn.apply(this, args), delay);
19
- return;
20
- }
21
- if (leading && now - lastExecTime >= delay) {
22
- fn.apply(this, args);
23
- lastExecTime = now;
24
- }
25
- if (trailing && !timer) {
26
- timer = setTimeout(() => {
27
- fn.apply(this, args);
28
- lastExecTime = Date.now();
29
- timer = null;
30
- }, delay - (now - lastExecTime));
22
+ const context = options.context === true ? this : null;
23
+ let scopeDelay = delay;
24
+ if(options.check) {
25
+ const response = options.check(...args);
26
+ if(typeof response === 'number') {
27
+ scopeDelay = response;
28
+ }
31
29
  }
30
+ lastArgs = args;
31
+
32
+ // debounce mode: reset the timer for each call
33
+ clearTimeout(timer);
34
+ timer = setTimeout(() => invoke(fn, lastArgs, context), delay);
35
+ }
36
+ };
37
+
38
+
39
+ /**
40
+ *
41
+ * @param {*} item
42
+ * @param {string|null} defaultKey
43
+ * @param {?Function} key
44
+ * @returns {*}
45
+ */
46
+ export const getKey = (item, defaultKey, key) => {
47
+ if(Validator.isFunction(key)) return key(item, defaultKey);
48
+ if(Validator.isObservable(item)) {
49
+ const val = item.val();
50
+ return (val && key) ? val[key] : defaultKey;
51
+ }
52
+ if(!Validator.isObject(item)) {
53
+ return item;
32
54
  }
55
+ return item[key] ?? defaultKey;
33
56
  };
34
57
 
35
58
  export const trim = function(str, char) {
@@ -3,20 +3,6 @@ import NativeDocumentError from "../errors/NativeDocumentError";
3
3
  import {BOOLEAN_ATTRIBUTES} from "./constants.js";
4
4
  import {Observable} from "../data/Observable";
5
5
 
6
- /**
7
- *
8
- * @param {HTMLElement} element
9
- * @param {string} className
10
- * @param {string} value
11
- */
12
- const toggleClassItem = function(element, className, value) {
13
- if(value) {
14
- element.classList.add(className);
15
- } else {
16
- element.classList.remove(className);
17
- }
18
- }
19
-
20
6
  /**
21
7
  *
22
8
  * @param {HTMLElement} element
@@ -26,11 +12,11 @@ function bindClassAttribute(element, data) {
26
12
  for(let className in data) {
27
13
  const value = data[className];
28
14
  if(Validator.isObservable(value)) {
29
- toggleClassItem(element, className, value.val());
30
- value.subscribe(newValue => toggleClassItem(element, className, newValue));
15
+ element.classList.toggle(className, value.val());
16
+ value.subscribe(newValue => element.classList.toggle(className, newValue));
31
17
  continue;
32
18
  }
33
- toggleClassItem(element, className, value);
19
+ element.classList.toggle(className, value)
34
20
  }
35
21
  }
36
22
 
@@ -102,8 +88,8 @@ function bindAttributeWithObservable(element, attributeName, value) {
102
88
  }
103
89
  element.setAttribute(attributeName, newValue);
104
90
  };
91
+ applyValue(value.val());
105
92
  value.subscribe(applyValue);
106
- applyValue(value.val())
107
93
 
108
94
  if(attributeName === 'value') {
109
95
  element.addEventListener('input', () => value.set(element.value));
@@ -152,6 +138,7 @@ export default function AttributesWrapper(element, attributes) {
152
138
  continue;
153
139
  }
154
140
  element.setAttribute(attributeName, value);
141
+
155
142
  }
156
143
  return element;
157
144
  }