lahama 2.5.2 → 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 +107 -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) {
|
|
@@ -177,15 +40,31 @@ function hFragment(vNodes) {
|
|
|
177
40
|
function addEventListener(
|
|
178
41
|
eventName,
|
|
179
42
|
handler,
|
|
180
|
-
el
|
|
43
|
+
el,
|
|
44
|
+
hostComponent = null
|
|
181
45
|
) {
|
|
182
|
-
|
|
183
|
-
|
|
46
|
+
function boundHandler() {
|
|
47
|
+
hostComponent
|
|
48
|
+
? handler.apply(hostComponent, arguments)
|
|
49
|
+
: handler(...arguments);
|
|
50
|
+
}
|
|
51
|
+
el.addEventListener(eventName, boundHandler);
|
|
52
|
+
return boundHandler;
|
|
184
53
|
}
|
|
185
|
-
function addEventListeners(
|
|
54
|
+
function addEventListeners(
|
|
55
|
+
listeners = {},
|
|
56
|
+
el,
|
|
57
|
+
hostComponent = null
|
|
58
|
+
) {
|
|
186
59
|
const addedListeners = {};
|
|
187
60
|
Object.entries(listeners).forEach(([eventName, handler]) => {
|
|
188
|
-
|
|
61
|
+
const listener = addEventListener(
|
|
62
|
+
eventName,
|
|
63
|
+
handler,
|
|
64
|
+
el,
|
|
65
|
+
hostComponent
|
|
66
|
+
);
|
|
67
|
+
addedListeners[eventName] = listener;
|
|
189
68
|
});
|
|
190
69
|
return addedListeners
|
|
191
70
|
}
|
|
@@ -195,45 +74,6 @@ function removeEventListeners(listeners = {}, el) {
|
|
|
195
74
|
});
|
|
196
75
|
}
|
|
197
76
|
|
|
198
|
-
function destroyDom(vdom) {
|
|
199
|
-
const { type } = vdom;
|
|
200
|
-
switch (type) {
|
|
201
|
-
case DOM_TYPES.TEXT : {
|
|
202
|
-
removeTextNode(vdom);
|
|
203
|
-
break
|
|
204
|
-
}
|
|
205
|
-
case DOM_TYPES.ELEMENT : {
|
|
206
|
-
removeElementNode(vdom);
|
|
207
|
-
break
|
|
208
|
-
}
|
|
209
|
-
case DOM_TYPES.FRAGMENT : {
|
|
210
|
-
removeFragmentNode(vdom);
|
|
211
|
-
break
|
|
212
|
-
}
|
|
213
|
-
default : {
|
|
214
|
-
throw new Error(`Can't destroy DOM of type ${type}`)
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
delete vdom.el;
|
|
218
|
-
}
|
|
219
|
-
function removeTextNode(vdom) {
|
|
220
|
-
const { el } = vdom;
|
|
221
|
-
el.remove();
|
|
222
|
-
}
|
|
223
|
-
function removeElementNode(vdom) {
|
|
224
|
-
const { el , children, listeners } = vdom;
|
|
225
|
-
el.remove();
|
|
226
|
-
children.forEach(destroyDom);
|
|
227
|
-
if (listeners) {
|
|
228
|
-
removeEventListeners(listeners, el);
|
|
229
|
-
delete vdom.listeners;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
function removeFragmentNode(vdom) {
|
|
233
|
-
const { children } = vdom;
|
|
234
|
-
children.forEach(destroyDom);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
77
|
function setAttributes(el, attrs) {
|
|
238
78
|
const { class : className, style, ...otherAttrs} = attrs;
|
|
239
79
|
if (className) {
|
|
@@ -260,9 +100,6 @@ function setClass(el, className) {
|
|
|
260
100
|
function setStyle(el, name, value) {
|
|
261
101
|
el.style[name] = value;
|
|
262
102
|
}
|
|
263
|
-
function removeStyle(el, name) {
|
|
264
|
-
el.style[name] = null;
|
|
265
|
-
}
|
|
266
103
|
function removeAttributeCustom(el, name) {
|
|
267
104
|
el[name] = null;
|
|
268
105
|
el.removeAttribute(name);
|
|
@@ -277,18 +114,33 @@ function setAttribute(el, name, value) {
|
|
|
277
114
|
}
|
|
278
115
|
}
|
|
279
116
|
|
|
280
|
-
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
|
+
) {
|
|
281
129
|
switch (vdom.type) {
|
|
282
130
|
case DOM_TYPES.TEXT : {
|
|
283
|
-
createTextNode(vdom, parentEl, index);
|
|
131
|
+
createTextNode(vdom, parentEl, index, );
|
|
284
132
|
break
|
|
285
133
|
}
|
|
286
134
|
case DOM_TYPES.ELEMENT : {
|
|
287
|
-
createElementNode(vdom, parentEl, index);
|
|
135
|
+
createElementNode(vdom, parentEl, index, hostComponent);
|
|
288
136
|
break
|
|
289
137
|
}
|
|
290
138
|
case DOM_TYPES.FRAGMENT : {
|
|
291
|
-
createFragmentNodes(vdom, parentEl, index);
|
|
139
|
+
createFragmentNodes(vdom, parentEl, index, hostComponent);
|
|
140
|
+
break
|
|
141
|
+
}
|
|
142
|
+
case DOM_TYPES.COMPONENT : {
|
|
143
|
+
createComponentNode(vdom, parentEl, index, hostComponent);
|
|
292
144
|
break
|
|
293
145
|
}
|
|
294
146
|
default : {
|
|
@@ -318,295 +170,103 @@ function createTextNode(vdom, parentEl, index) {
|
|
|
318
170
|
vdom.el = textNode;
|
|
319
171
|
insert(textNode, parentEl, index);
|
|
320
172
|
}
|
|
321
|
-
function createElementNode(vdom, parentEl, index) {
|
|
322
|
-
const { tag,
|
|
173
|
+
function createElementNode(vdom, parentEl, index, hostComponent) {
|
|
174
|
+
const { tag, children } = vdom;
|
|
323
175
|
const element = document.createElement(tag);
|
|
324
|
-
addProps(element,
|
|
176
|
+
addProps(element, vdom, hostComponent);
|
|
325
177
|
vdom.el = element;
|
|
326
178
|
//!function mountDom(vnode, parentEl) {
|
|
327
|
-
children.forEach((child) => mountDom(child, element));
|
|
179
|
+
children.forEach((child) => mountDom(child, element, null, hostComponent));
|
|
328
180
|
insert(element, parentEl, index);
|
|
329
181
|
}
|
|
330
|
-
function addProps(el, props, vdom) {
|
|
331
|
-
const {
|
|
332
|
-
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);
|
|
333
185
|
setAttributes(el, attrs);
|
|
334
186
|
}
|
|
335
|
-
function createFragmentNodes(vdom, parentEl, index) {
|
|
187
|
+
function createFragmentNodes(vdom, parentEl, index, hostComponent) {
|
|
336
188
|
const { children } = vdom;
|
|
337
189
|
vdom.el = parentEl;
|
|
338
|
-
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;
|
|
339
199
|
}
|
|
340
200
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
const handlersArray = this.#subs.get(commandName);
|
|
349
|
-
if (handlersArray.includes(handler)) {
|
|
350
|
-
return () => {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
handlersArray.push(handler);
|
|
354
|
-
return () => {
|
|
355
|
-
const idx = handlersArray.indexOf(handler);
|
|
356
|
-
handlersArray.splice(idx, 1);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
afterEveryCommand(handler) {
|
|
360
|
-
this.#afterHandlers.push(handler);
|
|
361
|
-
return () => {
|
|
362
|
-
const idx = this.#afterHandlers.indexOf(handler);
|
|
363
|
-
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
|
|
364
207
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
369
|
-
} else {
|
|
370
|
-
console.warn(`No handlers for command : ${commandName}`);
|
|
208
|
+
case DOM_TYPES.ELEMENT : {
|
|
209
|
+
removeElementNode(vdom);
|
|
210
|
+
break
|
|
371
211
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
function areNodesEqual(nodeOne, nodeTwo) {
|
|
377
|
-
if (!nodeOne || !nodeTwo) {
|
|
378
|
-
console.error('Invalid VDOM node', { nodeOne, nodeTwo });
|
|
379
|
-
}
|
|
380
|
-
if (!nodeOne.type || !nodeTwo.type) {
|
|
381
|
-
console.error('Invalid VDOM node', { nodeOne, nodeTwo });
|
|
382
|
-
}
|
|
383
|
-
if (nodeOne.type !== nodeTwo.type) {
|
|
384
|
-
return false
|
|
385
|
-
}
|
|
386
|
-
if (nodeOne.type === DOM_TYPES.ELEMENT) {
|
|
387
|
-
const { tag : tagOne } = nodeOne;
|
|
388
|
-
const { tag : tagTwo} = nodeTwo;
|
|
389
|
-
return tagOne === tagTwo
|
|
390
|
-
}
|
|
391
|
-
return true
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function objectsDiff(oldObj, newObj) {
|
|
395
|
-
const oldKeys = Object.keys(oldObj);
|
|
396
|
-
const newKeys = Object.keys(newObj);
|
|
397
|
-
return {
|
|
398
|
-
added : newKeys.filter((key) => !(key in oldObj)),
|
|
399
|
-
removed : oldKeys.filter((key) => !(key in newObj)),
|
|
400
|
-
updated : newKeys.filter(
|
|
401
|
-
(key) => key in oldObj && oldObj[key] !== newObj[key]
|
|
402
|
-
),
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
function isNotEmptyString(str) {
|
|
407
|
-
return str !== ''
|
|
408
|
-
}
|
|
409
|
-
function isNotBlankOrEmptyString(str) {
|
|
410
|
-
return isNotEmptyString(str.trim())
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function patchDOM(oldVdom, newVdom, parentEl) {
|
|
414
|
-
if (!areNodesEqual(oldVdom, newVdom)) {
|
|
415
|
-
const index = findIndexInParent(parentEl, oldVdom.el);
|
|
416
|
-
destroyDom(oldVdom);
|
|
417
|
-
mountDom(newVdom, parentEl, index);
|
|
418
|
-
return newVdom
|
|
419
|
-
}
|
|
420
|
-
newVdom.el = oldVdom.el;
|
|
421
|
-
switch (newVdom.type) {
|
|
422
|
-
case DOM_TYPES.TEXT: {
|
|
423
|
-
patchText(oldVdom, newVdom);
|
|
424
|
-
return newVdom
|
|
212
|
+
case DOM_TYPES.FRAGMENT : {
|
|
213
|
+
removeFragmentNode(vdom);
|
|
214
|
+
break
|
|
425
215
|
}
|
|
426
|
-
case DOM_TYPES.
|
|
427
|
-
|
|
216
|
+
case DOM_TYPES.COMPONENT : {
|
|
217
|
+
vdom.component.unmount();
|
|
428
218
|
break
|
|
429
219
|
}
|
|
220
|
+
default : {
|
|
221
|
+
throw new Error(`Can't destroy DOM of type ${type}`)
|
|
222
|
+
}
|
|
430
223
|
}
|
|
431
|
-
|
|
432
|
-
return newVdom
|
|
433
|
-
}
|
|
434
|
-
function patchText(oldVdom, newVdom) {
|
|
435
|
-
const el = oldVdom.el;
|
|
436
|
-
const { value : oldText} = oldVdom;
|
|
437
|
-
const { value : newText} = newVdom;
|
|
438
|
-
if (oldText !== newText) {
|
|
439
|
-
el.nodeValue = newText;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
function findIndexInParent(parentEl, el) {
|
|
443
|
-
const index = Array.from(parentEl.childNodes).indexOf(el);
|
|
444
|
-
if (index < 0) {
|
|
445
|
-
return null
|
|
446
|
-
}
|
|
447
|
-
return index
|
|
448
|
-
}
|
|
449
|
-
function patchElement(oldVdom, newVdom) {
|
|
450
|
-
const el = oldVdom.el;
|
|
451
|
-
const {
|
|
452
|
-
class : oldClass,
|
|
453
|
-
style : oldStyle,
|
|
454
|
-
on: oldEvents,
|
|
455
|
-
...oldAttrs
|
|
456
|
-
} = oldVdom.props;
|
|
457
|
-
const {
|
|
458
|
-
class: newClass,
|
|
459
|
-
style: newStyle,
|
|
460
|
-
on: newEvents,
|
|
461
|
-
...newAttrs
|
|
462
|
-
} = newVdom.props;
|
|
463
|
-
const { listeners: oldListeners } = oldVdom;
|
|
464
|
-
patchAttrs(el, oldAttrs, newAttrs);
|
|
465
|
-
patchClasses(el, oldClass, newClass);
|
|
466
|
-
patchStyles(el, oldStyle, newStyle);
|
|
467
|
-
newVdom.listeners = patchEvents(el, oldListeners, oldEvents, newEvents);
|
|
468
|
-
}
|
|
469
|
-
function patchAttrs(el, oldAttrs, newAttrs) {
|
|
470
|
-
const { added, removed, updated } = objectsDiff(oldAttrs, newAttrs);
|
|
471
|
-
for (const attr of removed) {
|
|
472
|
-
removeAttributeCustom(el, attr);
|
|
473
|
-
}
|
|
474
|
-
for (const attr of added.concat(updated)) {
|
|
475
|
-
setAttribute(el, attr, newAttrs[attr]);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
function patchClasses(el, oldClass, newClass) {
|
|
479
|
-
const oldClasses = toClassList(oldClass);
|
|
480
|
-
const newClasses = toClassList(newClass);
|
|
481
|
-
const {added, removed} =
|
|
482
|
-
arraysDiff(oldClasses, newClasses);
|
|
483
|
-
if (removed.length > 0) {
|
|
484
|
-
el.classList.remove(...removed);
|
|
485
|
-
}
|
|
486
|
-
if (added.length > 0) {
|
|
487
|
-
el.classList.add(...added);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
function toClassList(classes = '') {
|
|
491
|
-
return Array.isArray(classes)
|
|
492
|
-
? classes.filter(isNotBlankOrEmptyString)
|
|
493
|
-
: classes.split(/(\s+)/)
|
|
494
|
-
.filter(isNotBlankOrEmptyString)
|
|
495
|
-
}
|
|
496
|
-
function patchStyles(el, oldStyle = {}, newStyle = {}) {
|
|
497
|
-
const {added, removed, updated } = objectsDiff(oldStyle, newStyle);
|
|
498
|
-
for (const style of removed) {
|
|
499
|
-
removeStyle(el, style);
|
|
500
|
-
}
|
|
501
|
-
for (const style of added.concat(updated)) {
|
|
502
|
-
setStyle(el, style, newStyle[style]);
|
|
503
|
-
}
|
|
224
|
+
delete vdom.el;
|
|
504
225
|
}
|
|
505
|
-
function
|
|
506
|
-
el
|
|
507
|
-
|
|
508
|
-
oldEvents = {},
|
|
509
|
-
newEvents = {},
|
|
510
|
-
) {
|
|
511
|
-
const { removed, added, updated} =
|
|
512
|
-
objectsDiff(oldEvents, newEvents);
|
|
513
|
-
for (const eventName of removed.concat(updated)) {
|
|
514
|
-
el.removeEventListener(eventName, oldListeners[eventName]);
|
|
515
|
-
}
|
|
516
|
-
const addedListeners = {};
|
|
517
|
-
for (const eventName of added.concat(updated)) {
|
|
518
|
-
const listener = addEventListener(eventName, newEvents[eventName], el);
|
|
519
|
-
addedListeners[eventName] = listener;
|
|
520
|
-
}
|
|
521
|
-
return addedListeners
|
|
226
|
+
function removeTextNode(vdom) {
|
|
227
|
+
const { el } = vdom;
|
|
228
|
+
el.remove();
|
|
522
229
|
}
|
|
523
|
-
function
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
children.push(...extractChildren(child));
|
|
531
|
-
} else {
|
|
532
|
-
children.push(child);
|
|
533
|
-
}
|
|
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;
|
|
534
237
|
}
|
|
535
|
-
return children
|
|
536
238
|
}
|
|
537
|
-
function
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const parentEl = oldVdom.el;
|
|
541
|
-
const diffSeq = arraysDiffSequence(oldChildren, newChildren, areNodesEqual);
|
|
542
|
-
for (const operation of diffSeq) {
|
|
543
|
-
const { originalIndex, index, item} = operation;
|
|
544
|
-
switch (operation.op) {
|
|
545
|
-
case ARRAY_DIFF_OP.ADD: {
|
|
546
|
-
mountDom(item, parentEl, index);
|
|
547
|
-
break
|
|
548
|
-
}
|
|
549
|
-
case ARRAY_DIFF_OP.REMOVE: {
|
|
550
|
-
destroyDom(item);
|
|
551
|
-
break
|
|
552
|
-
}
|
|
553
|
-
case ARRAY_DIFF_OP.MOVE: {
|
|
554
|
-
const oldChild = oldChildren[originalIndex];
|
|
555
|
-
const newChild = newChildren[index];
|
|
556
|
-
const el = oldChild.el;
|
|
557
|
-
const elAtTargetIndex = parentEl.childNodes[index];
|
|
558
|
-
parentEl.insertBefore(el, elAtTargetIndex);
|
|
559
|
-
patchDOM(oldChildren[originalIndex], newChild, parentEl);
|
|
560
|
-
break
|
|
561
|
-
}
|
|
562
|
-
case ARRAY_DIFF_OP.NOOP : {
|
|
563
|
-
patchDOM(oldChildren[originalIndex], newChildren[index], parentEl);
|
|
564
|
-
break
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
239
|
+
function removeFragmentNode(vdom) {
|
|
240
|
+
const { children } = vdom;
|
|
241
|
+
children.forEach(destroyDom);
|
|
568
242
|
}
|
|
569
243
|
|
|
570
|
-
function createApp(
|
|
244
|
+
function createApp(RootComponent, props = {}) {
|
|
571
245
|
let parentEl = null;
|
|
572
|
-
let vdom = null;
|
|
573
246
|
let isMounted = false;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
for (const actionName in reducers) {
|
|
580
|
-
const reducer = reducers[actionName];
|
|
581
|
-
const subs = dispatcher.subscribe(actionName, (payload) => {
|
|
582
|
-
state = reducer(state, payload);
|
|
583
|
-
});
|
|
584
|
-
subscriptions.push(subs);
|
|
585
|
-
}
|
|
586
|
-
function renderApp() {
|
|
587
|
-
const newVdom = view(state, emit);
|
|
588
|
-
if (!newVdom) {
|
|
589
|
-
console.error('View returned', newVdom);
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
vdom = patchDOM(vdom, newVdom, parentEl);
|
|
247
|
+
let vdom = null;
|
|
248
|
+
function reset() {
|
|
249
|
+
parentEl = null;
|
|
250
|
+
isMounted = false;
|
|
251
|
+
vdom = null;
|
|
593
252
|
}
|
|
594
253
|
return {
|
|
595
254
|
mount(_parentEl) {
|
|
596
255
|
if (isMounted) {
|
|
597
|
-
throw new Error(
|
|
256
|
+
throw new Error(`The application is already mounted`)
|
|
598
257
|
}
|
|
599
258
|
parentEl = _parentEl;
|
|
600
|
-
vdom =
|
|
259
|
+
vdom = h(RootComponent, props);
|
|
601
260
|
mountDom(vdom, parentEl);
|
|
602
261
|
isMounted = true;
|
|
603
262
|
},
|
|
604
263
|
unmount() {
|
|
264
|
+
if (!isMounted) {
|
|
265
|
+
throw new Error(`The application is not mounted`)
|
|
266
|
+
}
|
|
605
267
|
destroyDom(vdom);
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
isMounted = false;
|
|
609
|
-
},
|
|
268
|
+
reset();
|
|
269
|
+
}
|
|
610
270
|
}
|
|
611
271
|
}
|
|
612
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
|
}
|