lahama 2.5.1 → 3.0.0
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/lahama.js +100 -447
- package/package.json +6 -1
package/dist/lahama.js
CHANGED
|
@@ -1,163 +1,26 @@
|
|
|
1
|
+
import 'vitest/dist/chunks/reporters.d.OXEK7y4s.d.ts';
|
|
2
|
+
|
|
1
3
|
function withoutNulls(arr) {
|
|
2
4
|
return arr.filter((item) => item != null)
|
|
3
5
|
}
|
|
4
|
-
function arraysDiff(oldArray, newArray) {
|
|
5
|
-
return {
|
|
6
|
-
added : newArray.filter(
|
|
7
|
-
(newItem) => !oldArray.includes(newItem)
|
|
8
|
-
),
|
|
9
|
-
removed : oldArray.filter(
|
|
10
|
-
(oldItem) => !newArray.includes(oldItem)
|
|
11
|
-
)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
const ARRAY_DIFF_OP = {
|
|
15
|
-
ADD : 'add',
|
|
16
|
-
REMOVE : 'remove',
|
|
17
|
-
MOVE : 'move',
|
|
18
|
-
NOOP : 'noop'
|
|
19
|
-
};
|
|
20
6
|
const a = { };
|
|
21
7
|
const b = { };
|
|
22
8
|
console.log(a === b);
|
|
23
|
-
class ArrayWithOriginalIndices {
|
|
24
|
-
#array = []
|
|
25
|
-
#originalIndices = []
|
|
26
|
-
#equalsFn
|
|
27
|
-
constructor(array, equalsFn) {
|
|
28
|
-
this.#array = [...array];
|
|
29
|
-
this.#originalIndices = array.map((_, i) => i);
|
|
30
|
-
this.#equalsFn = equalsFn;
|
|
31
|
-
}
|
|
32
|
-
get length() {
|
|
33
|
-
return this.#array.length
|
|
34
|
-
}
|
|
35
|
-
isRemoval(index, newArray) {
|
|
36
|
-
if (index >= this.length) {
|
|
37
|
-
return false
|
|
38
|
-
}
|
|
39
|
-
const item = this.#array[index];
|
|
40
|
-
const indexInNewArray = newArray.findIndex((newItem) =>
|
|
41
|
-
this.#equalsFn(item, newItem)
|
|
42
|
-
);
|
|
43
|
-
return indexInNewArray === -1
|
|
44
|
-
}
|
|
45
|
-
removeItem(index) {
|
|
46
|
-
const operation = {
|
|
47
|
-
op : ARRAY_DIFF_OP.REMOVE,
|
|
48
|
-
index,
|
|
49
|
-
item : this.#array[index],
|
|
50
|
-
};
|
|
51
|
-
this.#array.splice(index, 1);
|
|
52
|
-
return operation
|
|
53
|
-
}
|
|
54
|
-
isNoop(index, newArray) {
|
|
55
|
-
if (index >= this.length) {
|
|
56
|
-
return false
|
|
57
|
-
}
|
|
58
|
-
const item = this.#array[index];
|
|
59
|
-
const newItem = newArray[index];
|
|
60
|
-
return this.#equalsFn(item, newItem)
|
|
61
|
-
}
|
|
62
|
-
originalIndexAt(index) {
|
|
63
|
-
return this.#originalIndices[index]
|
|
64
|
-
}
|
|
65
|
-
noopItem(index) {
|
|
66
|
-
return {
|
|
67
|
-
op : ARRAY_DIFF_OP.NOOP,
|
|
68
|
-
originalIndex : this.originalIndexAt(index),
|
|
69
|
-
item : this.#array[index],
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
isAddition(item, fromIdx) {
|
|
73
|
-
return this.findIndexFrom(item, fromIdx) === -1
|
|
74
|
-
}
|
|
75
|
-
findIndexFrom(item, fromIndex) {
|
|
76
|
-
for (let i = fromIndex; i < this.length; i++) {
|
|
77
|
-
if (this.#equalsFn(item, this.#array[i])) {
|
|
78
|
-
return i
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return -1
|
|
82
|
-
}
|
|
83
|
-
addItem(item, index) {
|
|
84
|
-
const operation = {
|
|
85
|
-
op : ARRAY_DIFF_OP.ADD,
|
|
86
|
-
index,
|
|
87
|
-
item
|
|
88
|
-
};
|
|
89
|
-
this.#array.splice(index, 0, item);
|
|
90
|
-
this.#originalIndices.splice(index, 0, -1);
|
|
91
|
-
return operation
|
|
92
|
-
}
|
|
93
|
-
moveItem(item, toIndex) {
|
|
94
|
-
const fromIndex = this.findIndexFrom(item, toIndex);
|
|
95
|
-
const operation = {
|
|
96
|
-
op : ARRAY_DIFF_OP.MOVE,
|
|
97
|
-
originalIndex : this.originalIndexAt(fromIndex),
|
|
98
|
-
from : fromIndex,
|
|
99
|
-
index : toIndex,
|
|
100
|
-
item : this.#array[fromIndex]
|
|
101
|
-
};
|
|
102
|
-
const [_item] = this.#array.splice(fromIndex, 1);
|
|
103
|
-
this.#array.splice(toIndex, 0 , _item);
|
|
104
|
-
const [originalIndex] =
|
|
105
|
-
this.#originalIndices.splice(fromIndex, 1);
|
|
106
|
-
this.#originalIndices.splice(toIndex, 0, originalIndex);
|
|
107
|
-
return operation
|
|
108
|
-
}
|
|
109
|
-
removeItemsAfter(index) {
|
|
110
|
-
const operations = [];
|
|
111
|
-
while (this.length > index) {
|
|
112
|
-
operations.push(this.removeItem(index));
|
|
113
|
-
}
|
|
114
|
-
return operations
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
function arraysDiffSequence(
|
|
118
|
-
oldArray,
|
|
119
|
-
newArray,
|
|
120
|
-
equalsFn = (a, b) => a === b
|
|
121
|
-
) {
|
|
122
|
-
const sequence = [];
|
|
123
|
-
const array = new ArrayWithOriginalIndices(oldArray, equalsFn);
|
|
124
|
-
for (let index = 0; index < newArray.length; index++) {
|
|
125
|
-
//!REMOVE CASE
|
|
126
|
-
if (array.isRemoval(index, newArray)) {
|
|
127
|
-
sequence.push(array.removeItem(index));
|
|
128
|
-
index--;
|
|
129
|
-
continue
|
|
130
|
-
}
|
|
131
|
-
//!noop case
|
|
132
|
-
if (array.isNoop(index, newArray)) {
|
|
133
|
-
sequence.push(array.noopItem(index));
|
|
134
|
-
continue
|
|
135
|
-
}
|
|
136
|
-
//!addition case
|
|
137
|
-
const item = newArray[index];
|
|
138
|
-
if (array.isAddition(item , index)) {
|
|
139
|
-
sequence.push(array.addItem(item, index));
|
|
140
|
-
continue
|
|
141
|
-
}
|
|
142
|
-
//!move case
|
|
143
|
-
sequence.push(array.moveItem(item, index));
|
|
144
|
-
}
|
|
145
|
-
//!remove extra items
|
|
146
|
-
sequence.push(...array.removeItemsAfter(newArray.length));
|
|
147
|
-
return sequence
|
|
148
|
-
}
|
|
149
9
|
|
|
150
10
|
const DOM_TYPES = {
|
|
151
11
|
TEXT : 'text',
|
|
152
12
|
ELEMENT : 'element',
|
|
153
13
|
FRAGMENT : 'fragment',
|
|
14
|
+
COMPONENT : 'component',
|
|
154
15
|
};
|
|
155
16
|
function h(tag, props = {} , children = []) {
|
|
17
|
+
const type =
|
|
18
|
+
typeof tag === 'string' ? DOM_TYPES.ELEMENT : DOM_TYPES.COMPONENT;
|
|
156
19
|
return {
|
|
157
20
|
tag,
|
|
158
21
|
props,
|
|
22
|
+
type,
|
|
159
23
|
children: mapTextNodes(withoutNulls(children)),
|
|
160
|
-
type : DOM_TYPES.ELEMENT,
|
|
161
24
|
}
|
|
162
25
|
}
|
|
163
26
|
function mapTextNodes(children) {
|
|
@@ -182,16 +45,26 @@ function addEventListener(
|
|
|
182
45
|
) {
|
|
183
46
|
function boundHandler() {
|
|
184
47
|
hostComponent
|
|
185
|
-
|
|
48
|
+
? handler.apply(hostComponent, arguments)
|
|
186
49
|
: handler(...arguments);
|
|
187
50
|
}
|
|
188
51
|
el.addEventListener(eventName, boundHandler);
|
|
189
|
-
return boundHandler
|
|
52
|
+
return boundHandler;
|
|
190
53
|
}
|
|
191
|
-
function addEventListeners(
|
|
54
|
+
function addEventListeners(
|
|
55
|
+
listeners = {},
|
|
56
|
+
el,
|
|
57
|
+
hostComponent = null
|
|
58
|
+
) {
|
|
192
59
|
const addedListeners = {};
|
|
193
60
|
Object.entries(listeners).forEach(([eventName, handler]) => {
|
|
194
|
-
|
|
61
|
+
const listener = addEventListener(
|
|
62
|
+
eventName,
|
|
63
|
+
handler,
|
|
64
|
+
el,
|
|
65
|
+
hostComponent
|
|
66
|
+
);
|
|
67
|
+
addedListeners[eventName] = listener;
|
|
195
68
|
});
|
|
196
69
|
return addedListeners
|
|
197
70
|
}
|
|
@@ -201,45 +74,6 @@ function removeEventListeners(listeners = {}, el) {
|
|
|
201
74
|
});
|
|
202
75
|
}
|
|
203
76
|
|
|
204
|
-
function destroyDom(vdom) {
|
|
205
|
-
const { type } = vdom;
|
|
206
|
-
switch (type) {
|
|
207
|
-
case DOM_TYPES.TEXT : {
|
|
208
|
-
removeTextNode(vdom);
|
|
209
|
-
break
|
|
210
|
-
}
|
|
211
|
-
case DOM_TYPES.ELEMENT : {
|
|
212
|
-
removeElementNode(vdom);
|
|
213
|
-
break
|
|
214
|
-
}
|
|
215
|
-
case DOM_TYPES.FRAGMENT : {
|
|
216
|
-
removeFragmentNode(vdom);
|
|
217
|
-
break
|
|
218
|
-
}
|
|
219
|
-
default : {
|
|
220
|
-
throw new Error(`Can't destroy DOM of type ${type}`)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
delete vdom.el;
|
|
224
|
-
}
|
|
225
|
-
function removeTextNode(vdom) {
|
|
226
|
-
const { el } = vdom;
|
|
227
|
-
el.remove();
|
|
228
|
-
}
|
|
229
|
-
function removeElementNode(vdom) {
|
|
230
|
-
const { el , children, listeners } = vdom;
|
|
231
|
-
el.remove();
|
|
232
|
-
children.forEach(destroyDom);
|
|
233
|
-
if (listeners) {
|
|
234
|
-
removeEventListeners(listeners, el);
|
|
235
|
-
delete vdom.listeners;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
function removeFragmentNode(vdom) {
|
|
239
|
-
const { children } = vdom;
|
|
240
|
-
children.forEach(destroyDom);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
77
|
function setAttributes(el, attrs) {
|
|
244
78
|
const { class : className, style, ...otherAttrs} = attrs;
|
|
245
79
|
if (className) {
|
|
@@ -266,9 +100,6 @@ function setClass(el, className) {
|
|
|
266
100
|
function setStyle(el, name, value) {
|
|
267
101
|
el.style[name] = value;
|
|
268
102
|
}
|
|
269
|
-
function removeStyle(el, name) {
|
|
270
|
-
el.style[name] = null;
|
|
271
|
-
}
|
|
272
103
|
function removeAttributeCustom(el, name) {
|
|
273
104
|
el[name] = null;
|
|
274
105
|
el.removeAttribute(name);
|
|
@@ -283,18 +114,33 @@ function setAttribute(el, name, value) {
|
|
|
283
114
|
}
|
|
284
115
|
}
|
|
285
116
|
|
|
286
|
-
function
|
|
117
|
+
function extractPropsAndEvents(vdom) {
|
|
118
|
+
const { on : events = {}, ...props } = vdom.props;
|
|
119
|
+
delete props.key;
|
|
120
|
+
return { props, events }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function mountDom(
|
|
124
|
+
vdom,
|
|
125
|
+
parentEl,
|
|
126
|
+
index,
|
|
127
|
+
hostComponent = null
|
|
128
|
+
) {
|
|
287
129
|
switch (vdom.type) {
|
|
288
130
|
case DOM_TYPES.TEXT : {
|
|
289
|
-
createTextNode(vdom, parentEl, index);
|
|
131
|
+
createTextNode(vdom, parentEl, index, );
|
|
290
132
|
break
|
|
291
133
|
}
|
|
292
134
|
case DOM_TYPES.ELEMENT : {
|
|
293
|
-
createElementNode(vdom, parentEl, index);
|
|
135
|
+
createElementNode(vdom, parentEl, index, hostComponent);
|
|
294
136
|
break
|
|
295
137
|
}
|
|
296
138
|
case DOM_TYPES.FRAGMENT : {
|
|
297
|
-
createFragmentNodes(vdom, parentEl, index);
|
|
139
|
+
createFragmentNodes(vdom, parentEl, index, hostComponent);
|
|
140
|
+
break
|
|
141
|
+
}
|
|
142
|
+
case DOM_TYPES.COMPONENT : {
|
|
143
|
+
createComponentNode(vdom, parentEl, index, hostComponent);
|
|
298
144
|
break
|
|
299
145
|
}
|
|
300
146
|
default : {
|
|
@@ -324,296 +170,103 @@ function createTextNode(vdom, parentEl, index) {
|
|
|
324
170
|
vdom.el = textNode;
|
|
325
171
|
insert(textNode, parentEl, index);
|
|
326
172
|
}
|
|
327
|
-
function createElementNode(vdom, parentEl, index) {
|
|
328
|
-
const { tag,
|
|
173
|
+
function createElementNode(vdom, parentEl, index, hostComponent) {
|
|
174
|
+
const { tag, children } = vdom;
|
|
329
175
|
const element = document.createElement(tag);
|
|
330
|
-
addProps(element,
|
|
176
|
+
addProps(element, vdom, hostComponent);
|
|
331
177
|
vdom.el = element;
|
|
332
178
|
//!function mountDom(vnode, parentEl) {
|
|
333
|
-
children.forEach((child) => mountDom(child, element));
|
|
179
|
+
children.forEach((child) => mountDom(child, element, null, hostComponent));
|
|
334
180
|
insert(element, parentEl, index);
|
|
335
181
|
}
|
|
336
|
-
function addProps(el, props, vdom) {
|
|
337
|
-
const {
|
|
338
|
-
vdom.listeners = addEventListeners(events, el);
|
|
182
|
+
function addProps(el, props, vdom, hostComponent) {
|
|
183
|
+
const { props : attrs, events } = extractPropsAndEvents(vdom);
|
|
184
|
+
vdom.listeners = addEventListeners(events, el, hostComponent);
|
|
339
185
|
setAttributes(el, attrs);
|
|
340
186
|
}
|
|
341
|
-
function createFragmentNodes(vdom, parentEl, index) {
|
|
187
|
+
function createFragmentNodes(vdom, parentEl, index, hostComponent) {
|
|
342
188
|
const { children } = vdom;
|
|
343
189
|
vdom.el = parentEl;
|
|
344
|
-
children.forEach((child, i) => mountDom(child, parentEl, index ? index + i : null));
|
|
190
|
+
children.forEach((child, i) => mountDom(child, parentEl, index ? index + i : null, hostComponent));
|
|
191
|
+
}
|
|
192
|
+
function createComponentNode(vdom, parentEl, index, hostComponent) {
|
|
193
|
+
const Component = vdom.tag;
|
|
194
|
+
const { props, events } = extractPropsAndEvents(vdom);
|
|
195
|
+
const component = new Component(props, events, hostComponent);
|
|
196
|
+
component.mount(parentEl, index);
|
|
197
|
+
vdom.component = component;
|
|
198
|
+
vdom.el = component.firstElement;
|
|
345
199
|
}
|
|
346
200
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
const handlersArray = this.#subs.get(commandName);
|
|
355
|
-
if (handlersArray.includes(handler)) {
|
|
356
|
-
return () => {
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
handlersArray.push(handler);
|
|
360
|
-
return () => {
|
|
361
|
-
const idx = handlersArray.indexOf(handler);
|
|
362
|
-
handlersArray.splice(idx, 1);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
afterEveryCommand(handler) {
|
|
366
|
-
this.#afterHandlers.push(handler);
|
|
367
|
-
return () => {
|
|
368
|
-
const idx = this.#afterHandlers.indexOf(handler);
|
|
369
|
-
this.#afterHandlers.splice(idx, 1);
|
|
201
|
+
function destroyDom(vdom) {
|
|
202
|
+
const { type } = vdom;
|
|
203
|
+
switch (type) {
|
|
204
|
+
case DOM_TYPES.TEXT : {
|
|
205
|
+
removeTextNode(vdom);
|
|
206
|
+
break
|
|
370
207
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
375
|
-
} else {
|
|
376
|
-
console.warn(`No handlers for command : ${commandName}`);
|
|
208
|
+
case DOM_TYPES.ELEMENT : {
|
|
209
|
+
removeElementNode(vdom);
|
|
210
|
+
break
|
|
377
211
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
function areNodesEqual(nodeOne, nodeTwo) {
|
|
383
|
-
if (!nodeOne || !nodeTwo) {
|
|
384
|
-
console.error('Invalid VDOM node', { nodeOne, nodeTwo });
|
|
385
|
-
}
|
|
386
|
-
if (!nodeOne.type || !nodeTwo.type) {
|
|
387
|
-
console.error('Invalid VDOM node', { nodeOne, nodeTwo });
|
|
388
|
-
}
|
|
389
|
-
if (nodeOne.type !== nodeTwo.type) {
|
|
390
|
-
return false
|
|
391
|
-
}
|
|
392
|
-
if (nodeOne.type === DOM_TYPES.ELEMENT) {
|
|
393
|
-
const { tag : tagOne } = nodeOne;
|
|
394
|
-
const { tag : tagTwo} = nodeTwo;
|
|
395
|
-
return tagOne === tagTwo
|
|
396
|
-
}
|
|
397
|
-
return true
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function objectsDiff(oldObj, newObj) {
|
|
401
|
-
const oldKeys = Object.keys(oldObj);
|
|
402
|
-
const newKeys = Object.keys(newObj);
|
|
403
|
-
return {
|
|
404
|
-
added : newKeys.filter((key) => !(key in oldObj)),
|
|
405
|
-
removed : oldKeys.filter((key) => !(key in newObj)),
|
|
406
|
-
updated : newKeys.filter(
|
|
407
|
-
(key) => key in oldObj && oldObj[key] !== newObj[key]
|
|
408
|
-
),
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function isNotEmptyString(str) {
|
|
413
|
-
return str !== ''
|
|
414
|
-
}
|
|
415
|
-
function isNotBlankOrEmptyString(str) {
|
|
416
|
-
return isNotEmptyString(str.trim())
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function patchDOM(oldVdom, newVdom, parentEl) {
|
|
420
|
-
if (!areNodesEqual(oldVdom, newVdom)) {
|
|
421
|
-
const index = findIndexInParent(parentEl, oldVdom.el);
|
|
422
|
-
destroyDom(oldVdom);
|
|
423
|
-
mountDom(newVdom, parentEl, index);
|
|
424
|
-
return newVdom
|
|
425
|
-
}
|
|
426
|
-
newVdom.el = oldVdom.el;
|
|
427
|
-
switch (newVdom.type) {
|
|
428
|
-
case DOM_TYPES.TEXT: {
|
|
429
|
-
patchText(oldVdom, newVdom);
|
|
430
|
-
return newVdom
|
|
212
|
+
case DOM_TYPES.FRAGMENT : {
|
|
213
|
+
removeFragmentNode(vdom);
|
|
214
|
+
break
|
|
431
215
|
}
|
|
432
|
-
case DOM_TYPES.
|
|
433
|
-
|
|
216
|
+
case DOM_TYPES.COMPONENT : {
|
|
217
|
+
vdom.component.unmount();
|
|
434
218
|
break
|
|
435
219
|
}
|
|
220
|
+
default : {
|
|
221
|
+
throw new Error(`Can't destroy DOM of type ${type}`)
|
|
222
|
+
}
|
|
436
223
|
}
|
|
437
|
-
|
|
438
|
-
return newVdom
|
|
439
|
-
}
|
|
440
|
-
function patchText(oldVdom, newVdom) {
|
|
441
|
-
const el = oldVdom.el;
|
|
442
|
-
const { value : oldText} = oldVdom;
|
|
443
|
-
const { value : newText} = newVdom;
|
|
444
|
-
if (oldText !== newText) {
|
|
445
|
-
el.nodeValue = newText;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
function findIndexInParent(parentEl, el) {
|
|
449
|
-
const index = Array.from(parentEl.childNodes).indexOf(el);
|
|
450
|
-
if (index < 0) {
|
|
451
|
-
return null
|
|
452
|
-
}
|
|
453
|
-
return index
|
|
454
|
-
}
|
|
455
|
-
function patchElement(oldVdom, newVdom) {
|
|
456
|
-
const el = oldVdom.el;
|
|
457
|
-
const {
|
|
458
|
-
class : oldClass,
|
|
459
|
-
style : oldStyle,
|
|
460
|
-
on: oldEvents,
|
|
461
|
-
...oldAttrs
|
|
462
|
-
} = oldVdom.props;
|
|
463
|
-
const {
|
|
464
|
-
class: newClass,
|
|
465
|
-
style: newStyle,
|
|
466
|
-
on: newEvents,
|
|
467
|
-
...newAttrs
|
|
468
|
-
} = newVdom.props;
|
|
469
|
-
const { listeners: oldListeners } = oldVdom;
|
|
470
|
-
patchAttrs(el, oldAttrs, newAttrs);
|
|
471
|
-
patchClasses(el, oldClass, newClass);
|
|
472
|
-
patchStyles(el, oldStyle, newStyle);
|
|
473
|
-
newVdom.listeners = patchEvents(el, oldListeners, oldEvents, newEvents);
|
|
474
|
-
}
|
|
475
|
-
function patchAttrs(el, oldAttrs, newAttrs) {
|
|
476
|
-
const { added, removed, updated } = objectsDiff(oldAttrs, newAttrs);
|
|
477
|
-
for (const attr of removed) {
|
|
478
|
-
removeAttributeCustom(el, attr);
|
|
479
|
-
}
|
|
480
|
-
for (const attr of added.concat(updated)) {
|
|
481
|
-
setAttribute(el, attr, newAttrs[attr]);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
function patchClasses(el, oldClass, newClass) {
|
|
485
|
-
const oldClasses = toClassList(oldClass);
|
|
486
|
-
const newClasses = toClassList(newClass);
|
|
487
|
-
const {added, removed} =
|
|
488
|
-
arraysDiff(oldClasses, newClasses);
|
|
489
|
-
if (removed.length > 0) {
|
|
490
|
-
el.classList.remove(...removed);
|
|
491
|
-
}
|
|
492
|
-
if (added.length > 0) {
|
|
493
|
-
el.classList.add(...added);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
function toClassList(classes = '') {
|
|
497
|
-
return Array.isArray(classes)
|
|
498
|
-
? classes.filter(isNotBlankOrEmptyString)
|
|
499
|
-
: classes.split(/(\s+)/)
|
|
500
|
-
.filter(isNotBlankOrEmptyString)
|
|
501
|
-
}
|
|
502
|
-
function patchStyles(el, oldStyle = {}, newStyle = {}) {
|
|
503
|
-
const {added, removed, updated } = objectsDiff(oldStyle, newStyle);
|
|
504
|
-
for (const style of removed) {
|
|
505
|
-
removeStyle(el, style);
|
|
506
|
-
}
|
|
507
|
-
for (const style of added.concat(updated)) {
|
|
508
|
-
setStyle(el, style, newStyle[style]);
|
|
509
|
-
}
|
|
224
|
+
delete vdom.el;
|
|
510
225
|
}
|
|
511
|
-
function
|
|
512
|
-
el
|
|
513
|
-
|
|
514
|
-
oldEvents = {},
|
|
515
|
-
newEvents = {},
|
|
516
|
-
) {
|
|
517
|
-
const { removed, added, updated} =
|
|
518
|
-
objectsDiff(oldEvents, newEvents);
|
|
519
|
-
for (const eventName of removed.concat(updated)) {
|
|
520
|
-
el.removeEventListener(eventName, oldListeners[eventName]);
|
|
521
|
-
}
|
|
522
|
-
const addedListeners = {};
|
|
523
|
-
for (const eventName of added.concat(updated)) {
|
|
524
|
-
const listener =
|
|
525
|
-
addEventListener(eventName, newEvents[eventName], el);
|
|
526
|
-
addedListeners[eventName] = listener;
|
|
527
|
-
}
|
|
528
|
-
return addedListeners
|
|
226
|
+
function removeTextNode(vdom) {
|
|
227
|
+
const { el } = vdom;
|
|
228
|
+
el.remove();
|
|
529
229
|
}
|
|
530
|
-
function
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
children.push(...extractChildren(child));
|
|
538
|
-
} else {
|
|
539
|
-
children.push(child);
|
|
540
|
-
}
|
|
230
|
+
function removeElementNode(vdom) {
|
|
231
|
+
const { el , children, listeners } = vdom;
|
|
232
|
+
el.remove();
|
|
233
|
+
children.forEach(destroyDom);
|
|
234
|
+
if (listeners) {
|
|
235
|
+
removeEventListeners(listeners, el);
|
|
236
|
+
delete vdom.listeners;
|
|
541
237
|
}
|
|
542
|
-
return children
|
|
543
238
|
}
|
|
544
|
-
function
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const parentEl = oldVdom.el;
|
|
548
|
-
const diffSeq = arraysDiffSequence(oldChildren, newChildren, areNodesEqual);
|
|
549
|
-
for (const operation of diffSeq) {
|
|
550
|
-
const { originalIndex, index, item} = operation;
|
|
551
|
-
switch (operation.op) {
|
|
552
|
-
case ARRAY_DIFF_OP.ADD: {
|
|
553
|
-
mountDom(item, parentEl, index);
|
|
554
|
-
break
|
|
555
|
-
}
|
|
556
|
-
case ARRAY_DIFF_OP.REMOVE: {
|
|
557
|
-
destroyDom(item);
|
|
558
|
-
break
|
|
559
|
-
}
|
|
560
|
-
case ARRAY_DIFF_OP.MOVE: {
|
|
561
|
-
const oldChild = oldChildren[originalIndex];
|
|
562
|
-
const newChild = newChildren[index];
|
|
563
|
-
const el = oldChild.el;
|
|
564
|
-
const elAtTargetIndex = parentEl.childNodes[index];
|
|
565
|
-
parentEl.insertBefore(el, elAtTargetIndex);
|
|
566
|
-
patchDOM(oldChildren[originalIndex], newChild, parentEl);
|
|
567
|
-
break
|
|
568
|
-
}
|
|
569
|
-
case ARRAY_DIFF_OP.NOOP : {
|
|
570
|
-
patchDOM(oldChildren[originalIndex], newChildren[index], parentEl);
|
|
571
|
-
break
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
239
|
+
function removeFragmentNode(vdom) {
|
|
240
|
+
const { children } = vdom;
|
|
241
|
+
children.forEach(destroyDom);
|
|
575
242
|
}
|
|
576
243
|
|
|
577
|
-
function createApp(
|
|
244
|
+
function createApp(RootComponent, props = {}) {
|
|
578
245
|
let parentEl = null;
|
|
579
|
-
let vdom = null;
|
|
580
246
|
let isMounted = false;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
for (const actionName in reducers) {
|
|
587
|
-
const reducer = reducers[actionName];
|
|
588
|
-
const subs = dispatcher.subscribe(actionName, (payload) => {
|
|
589
|
-
state = reducer(state, payload);
|
|
590
|
-
});
|
|
591
|
-
subscriptions.push(subs);
|
|
592
|
-
}
|
|
593
|
-
function renderApp() {
|
|
594
|
-
const newVdom = view(state, emit);
|
|
595
|
-
if (!newVdom) {
|
|
596
|
-
console.error('View returned', newVdom);
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
vdom = patchDOM(vdom, newVdom, parentEl);
|
|
247
|
+
let vdom = null;
|
|
248
|
+
function reset() {
|
|
249
|
+
parentEl = null;
|
|
250
|
+
isMounted = false;
|
|
251
|
+
vdom = null;
|
|
600
252
|
}
|
|
601
253
|
return {
|
|
602
254
|
mount(_parentEl) {
|
|
603
255
|
if (isMounted) {
|
|
604
|
-
throw new Error(
|
|
256
|
+
throw new Error(`The application is already mounted`)
|
|
605
257
|
}
|
|
606
258
|
parentEl = _parentEl;
|
|
607
|
-
vdom =
|
|
259
|
+
vdom = h(RootComponent, props);
|
|
608
260
|
mountDom(vdom, parentEl);
|
|
609
261
|
isMounted = true;
|
|
610
262
|
},
|
|
611
263
|
unmount() {
|
|
264
|
+
if (!isMounted) {
|
|
265
|
+
throw new Error(`The application is not mounted`)
|
|
266
|
+
}
|
|
612
267
|
destroyDom(vdom);
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
isMounted = false;
|
|
616
|
-
},
|
|
268
|
+
reset();
|
|
269
|
+
}
|
|
617
270
|
}
|
|
618
271
|
}
|
|
619
272
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lahama",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/lahama.js",
|
|
6
6
|
"files": [
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"type": "module",
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@eslint/js": "^9.39.0",
|
|
23
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
24
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
23
25
|
"globals": "^16.5.0",
|
|
24
26
|
"jsdom": "^27.2.0",
|
|
25
27
|
"rimraf": "^3.0.2",
|
|
@@ -27,5 +29,8 @@
|
|
|
27
29
|
"rollup-plugin-cleanup": "^3.2.1",
|
|
28
30
|
"rollup-plugin-filesize": "^10.0.0",
|
|
29
31
|
"vitest": "^4.0.15"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"fast-deep-equal": "^3.1.3"
|
|
30
35
|
}
|
|
31
36
|
}
|