lahama 2.1.0 → 2.3.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 +348 -3
- package/package.json +1 -1
package/dist/lahama.js
CHANGED
|
@@ -1,9 +1,151 @@
|
|
|
1
1
|
function withoutNulls(arr) {
|
|
2
2
|
return arr.filter((item) => item != null)
|
|
3
3
|
}
|
|
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
|
+
};
|
|
4
20
|
const a = { };
|
|
5
21
|
const b = { };
|
|
6
22
|
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
|
+
}
|
|
7
149
|
|
|
8
150
|
const DOM_TYPES = {
|
|
9
151
|
TEXT : 'text',
|
|
@@ -32,14 +174,14 @@ function hFragment(vNodes) {
|
|
|
32
174
|
}
|
|
33
175
|
}
|
|
34
176
|
|
|
35
|
-
function addEventListener(eventName, handler, el) {
|
|
177
|
+
function addEventListener$1(eventName, handler, el) {
|
|
36
178
|
el.addEventListener(eventName, handler);
|
|
37
179
|
return handler
|
|
38
180
|
}
|
|
39
181
|
function addEventListeners(listeners = {}, el) {
|
|
40
182
|
const addedListeners = {};
|
|
41
183
|
Object.entries(listeners).forEach(([eventName, handler]) => {
|
|
42
|
-
addedListeners[eventName] = addEventListener(eventName, handler, el);
|
|
184
|
+
addedListeners[eventName] = addEventListener$1(eventName, handler, el);
|
|
43
185
|
});
|
|
44
186
|
return addedListeners
|
|
45
187
|
}
|
|
@@ -114,6 +256,9 @@ function setClass(el, className) {
|
|
|
114
256
|
function setStyle(el, name, value) {
|
|
115
257
|
el.style[name] = value;
|
|
116
258
|
}
|
|
259
|
+
function removeStyle(el, name) {
|
|
260
|
+
el.style[name] = null;
|
|
261
|
+
}
|
|
117
262
|
function removeAttributeCustom(el, name) {
|
|
118
263
|
el[name] = null;
|
|
119
264
|
el.removeAttribute(name);
|
|
@@ -224,6 +369,206 @@ class Dispatcher {
|
|
|
224
369
|
}
|
|
225
370
|
}
|
|
226
371
|
|
|
372
|
+
function areNodesEqual(nodeOne, nodeTwo) {
|
|
373
|
+
if (nodeOne.type !== nodeTwo.type) {
|
|
374
|
+
return false
|
|
375
|
+
}
|
|
376
|
+
if (nodeOne.type === DOM_TYPES.ELEMENT) {
|
|
377
|
+
const { tag : tagOne } = nodeOne;
|
|
378
|
+
const { tag : tagTwo} = nodeTwo;
|
|
379
|
+
return tagOne === tagTwo
|
|
380
|
+
}
|
|
381
|
+
return true
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function objectsDiff(oldObj, newObj) {
|
|
385
|
+
const oldKeys = Object.keys(oldObj);
|
|
386
|
+
const newKeys = Object.keys(newObj);
|
|
387
|
+
return {
|
|
388
|
+
added : newKeys.filter((key) => !(key in oldObj)),
|
|
389
|
+
removed : oldKeys.filter((key) => !(key in newObj)),
|
|
390
|
+
updated : newKeys.filter(
|
|
391
|
+
(key) => key in oldObj && oldObj[key] !== newObj[key]
|
|
392
|
+
),
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function isNotEmptyString(str) {
|
|
397
|
+
return str !== ''
|
|
398
|
+
}
|
|
399
|
+
function isNotBlankOrEmptyString(str) {
|
|
400
|
+
return isNotEmptyString(str.trim())
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function patchDOM(oldVdom, newVdom, parentEl) {
|
|
404
|
+
if (!areNodesEqual(oldVdom, newVdom)) {
|
|
405
|
+
const index = findIndexInParent(parentEl, oldVdom.el);
|
|
406
|
+
destroyDom(oldVdom);
|
|
407
|
+
mountDom(newVdom, parentEl, index);
|
|
408
|
+
return newVdom
|
|
409
|
+
}
|
|
410
|
+
newVdom.el = oldVdom.el;
|
|
411
|
+
switch (newVdom.type) {
|
|
412
|
+
case DOM_TYPES.TEXT : {
|
|
413
|
+
patchText(oldVdom, newVdom);
|
|
414
|
+
return newVdom
|
|
415
|
+
}
|
|
416
|
+
case DOM_TYPES.ELEMENT : {
|
|
417
|
+
patchElement(oldVdom, newVdom);
|
|
418
|
+
break
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
patchChildren(oldVdom, newVdom);
|
|
422
|
+
return newVdom
|
|
423
|
+
}
|
|
424
|
+
function patchText(oldVdom, newVdom) {
|
|
425
|
+
const el = oldVdom.el;
|
|
426
|
+
const { value : oldText} = oldVdom;
|
|
427
|
+
const { value : newText} = newVdom;
|
|
428
|
+
if (oldText !== newText) {
|
|
429
|
+
el.nodeValue = newText;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function findIndexInParent(parentEl, el) {
|
|
433
|
+
const index = Array.from(parentEl.childNodes).indexOf(el);
|
|
434
|
+
if (index < 0) {
|
|
435
|
+
return null
|
|
436
|
+
}
|
|
437
|
+
return index
|
|
438
|
+
}
|
|
439
|
+
function patchElement(oldVdom, newVdom) {
|
|
440
|
+
const el = oldVdom.el;
|
|
441
|
+
const {
|
|
442
|
+
class : oldClass,
|
|
443
|
+
style : oldStyle,
|
|
444
|
+
on : oldEvents,
|
|
445
|
+
...oldAttrs
|
|
446
|
+
} = oldVdom.props;
|
|
447
|
+
const {
|
|
448
|
+
class : newClass,
|
|
449
|
+
style : newStyle,
|
|
450
|
+
on : newEvents,
|
|
451
|
+
...newAttrs
|
|
452
|
+
} = newVdom.props;
|
|
453
|
+
const { listeners : oldListeners } = oldVdom;
|
|
454
|
+
patchAttrs(el, oldAttrs, newAttrs);
|
|
455
|
+
patchClasses(el, oldClass, newClass);
|
|
456
|
+
patchStyles(el, oldStyle, newStyle);
|
|
457
|
+
newVdom.listeners = patchEvents(el, oldListeners, oldEvents, newEvents);
|
|
458
|
+
}
|
|
459
|
+
function patchAttrs(el, oldAttrs, newAttrs) {
|
|
460
|
+
const { added, removed, updated } = objectsDiff(oldAttrs, newAttrs);
|
|
461
|
+
for (const attr of removed) {
|
|
462
|
+
removeAttributeCustom(el, attr);
|
|
463
|
+
}
|
|
464
|
+
for (const attr of added.concat(updated)) {
|
|
465
|
+
setAttribute(el, attr, newAttrs[attr]);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function patchClasses(el, oldClass, newClass) {
|
|
469
|
+
const oldClasses = toClassList(oldClass);
|
|
470
|
+
const newClasses = toClassList(newClass);
|
|
471
|
+
const {added, removed} =
|
|
472
|
+
arraysDiff(oldClasses, newClasses);
|
|
473
|
+
if (removed.length > 0) {
|
|
474
|
+
el.classList.remove(...removed);
|
|
475
|
+
}
|
|
476
|
+
if (added.length > 0) {
|
|
477
|
+
el.classList.add(...added);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function toClassList(classes = '') {
|
|
481
|
+
return Array.isArray(classes)
|
|
482
|
+
? classes.filter(isNotBlankOrEmptyString)
|
|
483
|
+
: classes.split(/(\s+)/)
|
|
484
|
+
.filter(isNotBlankOrEmptyString)
|
|
485
|
+
}
|
|
486
|
+
function patchStyles(el, oldStyle = {}, newStyle = {}) {
|
|
487
|
+
const {added, removed, updated } = objectsDiff(oldStyle, newStyle);
|
|
488
|
+
for (const style of removed) {
|
|
489
|
+
removeStyle(el, style);
|
|
490
|
+
}
|
|
491
|
+
for (const style of added.concat(updated)) {
|
|
492
|
+
setStyle(el, style, newStyle[style]);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function addEventListener(eventName, handler, el) {
|
|
496
|
+
function boundHandler(event) {
|
|
497
|
+
handler(event);
|
|
498
|
+
}
|
|
499
|
+
el.addEventListener(eventName, boundHandler);
|
|
500
|
+
//!el.addEventListener("click", boundHandler) {
|
|
501
|
+
//! Element: el
|
|
502
|
+
//! Event: "click"
|
|
503
|
+
//! Listener : boundHandler!}
|
|
504
|
+
return boundHandler
|
|
505
|
+
}
|
|
506
|
+
function patchEvents(
|
|
507
|
+
el,
|
|
508
|
+
oldListeners = {},
|
|
509
|
+
oldEvents = {},
|
|
510
|
+
newEvents = {},
|
|
511
|
+
) {
|
|
512
|
+
const { removed, added, updated} =
|
|
513
|
+
objectsDiff(oldEvents, newEvents);
|
|
514
|
+
for (const eventName of removed.concat(updated)) {
|
|
515
|
+
el.removeEventListener(eventName, oldListeners[eventName]);
|
|
516
|
+
}
|
|
517
|
+
const addedListeners = {};
|
|
518
|
+
for (const eventName of added.concat(updated)) {
|
|
519
|
+
const listener =
|
|
520
|
+
addEventListener(eventName, newEvents[eventName], el);
|
|
521
|
+
addedListeners[eventName] = listener;
|
|
522
|
+
}
|
|
523
|
+
return addedListeners
|
|
524
|
+
}
|
|
525
|
+
function extractChildren(vdom) {
|
|
526
|
+
if (vdom.children == null) {
|
|
527
|
+
return []
|
|
528
|
+
}
|
|
529
|
+
const children = [];
|
|
530
|
+
for (const child of vdom.children) {
|
|
531
|
+
if (child.type === DOM_TYPES.FRAGMENT) {
|
|
532
|
+
children.push(...extractChildren(child));
|
|
533
|
+
} else {
|
|
534
|
+
children.push(child);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return children
|
|
538
|
+
}
|
|
539
|
+
function patchChildren(oldVdom, newVdom) {
|
|
540
|
+
const oldChildren = extractChildren(oldVdom);
|
|
541
|
+
const newChildren = extractChildren(newVdom);
|
|
542
|
+
const parentEl = oldVdom.el;
|
|
543
|
+
const diffSeq = arraysDiffSequence(oldChildren, newChildren, areNodesEqual);
|
|
544
|
+
for (const operation of diffSeq) {
|
|
545
|
+
const { originalIndex, index, item} = operation;
|
|
546
|
+
switch (operation.op) {
|
|
547
|
+
case ARRAY_DIFF_OP.ADD: {
|
|
548
|
+
mountDom(item, parentEl, index);
|
|
549
|
+
break
|
|
550
|
+
}
|
|
551
|
+
case ARRAY_DIFF_OP.REMOVE: {
|
|
552
|
+
destroyDom(item);
|
|
553
|
+
break
|
|
554
|
+
}
|
|
555
|
+
case ARRAY_DIFF_OP.MOVE: {
|
|
556
|
+
const oldChild = oldChildren[originalIndex];
|
|
557
|
+
const newChild = newChildren[index];
|
|
558
|
+
const el = oldChild.el;
|
|
559
|
+
const elAtTargetIndex = parentEl.childNodes[index];
|
|
560
|
+
parentEl.insertBefore(el, elAtTargetIndex);
|
|
561
|
+
patchDOM(oldChildren[originalIndex], newChild, parentEl);
|
|
562
|
+
break
|
|
563
|
+
}
|
|
564
|
+
case ARRAY_DIFF_OP.NOOP : {
|
|
565
|
+
patchDOM(oldChildren[originalIndex], newChildren[index], parentEl);
|
|
566
|
+
break
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
227
572
|
function createApp({ state, view, reducers = {} }) {
|
|
228
573
|
let parentEl = null;
|
|
229
574
|
let vdom = null;
|
|
@@ -242,7 +587,7 @@ function createApp({ state, view, reducers = {} }) {
|
|
|
242
587
|
}
|
|
243
588
|
function renderApp() {
|
|
244
589
|
const newVdom = view(state, emit);
|
|
245
|
-
vdom =
|
|
590
|
+
vdom = patchDOM(vdom, newVdom, parentEl);
|
|
246
591
|
}
|
|
247
592
|
return {
|
|
248
593
|
mount(_parentEl) {
|