lahama 3.0.0 → 5.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 +682 -4
- package/package.json +1 -1
package/dist/lahama.js
CHANGED
|
@@ -1,17 +1,158 @@
|
|
|
1
|
-
import 'vitest/dist/chunks/reporters.d.OXEK7y4s.d.ts';
|
|
2
|
-
|
|
3
1
|
function withoutNulls(arr) {
|
|
4
2
|
return arr.filter((item) => item != null)
|
|
5
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
|
+
};
|
|
6
20
|
const a = { };
|
|
7
21
|
const b = { };
|
|
8
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
|
+
}
|
|
9
149
|
|
|
10
150
|
const DOM_TYPES = {
|
|
11
151
|
TEXT : 'text',
|
|
12
152
|
ELEMENT : 'element',
|
|
13
153
|
FRAGMENT : 'fragment',
|
|
14
154
|
COMPONENT : 'component',
|
|
155
|
+
SLOT : 'slot'
|
|
15
156
|
};
|
|
16
157
|
function h(tag, props = {} , children = []) {
|
|
17
158
|
const type =
|
|
@@ -36,6 +177,17 @@ function hFragment(vNodes) {
|
|
|
36
177
|
children : mapTextNodes(withoutNulls(vNodes)),
|
|
37
178
|
}
|
|
38
179
|
}
|
|
180
|
+
let hSlotCalled = false;
|
|
181
|
+
function didCreateSlot() {
|
|
182
|
+
return hSlotCalled
|
|
183
|
+
}
|
|
184
|
+
function resetDidCreateSlot() {
|
|
185
|
+
hSlotCalled = false;
|
|
186
|
+
}
|
|
187
|
+
function hSlot(children = []) {
|
|
188
|
+
hSlotCalled = true;
|
|
189
|
+
return { type : DOM_TYPES.SLOT, children}
|
|
190
|
+
}
|
|
39
191
|
|
|
40
192
|
function addEventListener(
|
|
41
193
|
eventName,
|
|
@@ -100,6 +252,9 @@ function setClass(el, className) {
|
|
|
100
252
|
function setStyle(el, name, value) {
|
|
101
253
|
el.style[name] = value;
|
|
102
254
|
}
|
|
255
|
+
function removeStyle(el, name) {
|
|
256
|
+
el.style[name] = null;
|
|
257
|
+
}
|
|
103
258
|
function removeAttributeCustom(el, name) {
|
|
104
259
|
el[name] = null;
|
|
105
260
|
el.removeAttribute(name);
|
|
@@ -120,6 +275,40 @@ function extractPropsAndEvents(vdom) {
|
|
|
120
275
|
return { props, events }
|
|
121
276
|
}
|
|
122
277
|
|
|
278
|
+
let isScheduled = false;
|
|
279
|
+
const jobs = [];
|
|
280
|
+
function enqueueJob(job) {
|
|
281
|
+
jobs.push(job);
|
|
282
|
+
scheduleUpdate();
|
|
283
|
+
}
|
|
284
|
+
function scheduleUpdate() {
|
|
285
|
+
if (isScheduled) return
|
|
286
|
+
isScheduled = true;
|
|
287
|
+
queueMicrotask(processJobs);
|
|
288
|
+
}
|
|
289
|
+
function processJobs() {
|
|
290
|
+
while (jobs.length > 0) {
|
|
291
|
+
const job = jobs.shift();
|
|
292
|
+
const result = job();
|
|
293
|
+
Promise.resolve(result).then(
|
|
294
|
+
() => {
|
|
295
|
+
},
|
|
296
|
+
(error) => {
|
|
297
|
+
console.error(`[scheduler]: ${error}`);
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
job();
|
|
301
|
+
}
|
|
302
|
+
isScheduled = false;
|
|
303
|
+
}
|
|
304
|
+
function nextTick() {
|
|
305
|
+
scheduleUpdate();
|
|
306
|
+
return flushPromises()
|
|
307
|
+
}
|
|
308
|
+
function flushPromises() {
|
|
309
|
+
return new Promise((resolve) => setTimeout(resolve))
|
|
310
|
+
}
|
|
311
|
+
|
|
123
312
|
function mountDom(
|
|
124
313
|
vdom,
|
|
125
314
|
parentEl,
|
|
@@ -141,6 +330,7 @@ function mountDom(
|
|
|
141
330
|
}
|
|
142
331
|
case DOM_TYPES.COMPONENT : {
|
|
143
332
|
createComponentNode(vdom, parentEl, index, hostComponent);
|
|
333
|
+
enqueueJob(() => vdom.component.onMounted());
|
|
144
334
|
break
|
|
145
335
|
}
|
|
146
336
|
default : {
|
|
@@ -190,9 +380,10 @@ function createFragmentNodes(vdom, parentEl, index, hostComponent) {
|
|
|
190
380
|
children.forEach((child, i) => mountDom(child, parentEl, index ? index + i : null, hostComponent));
|
|
191
381
|
}
|
|
192
382
|
function createComponentNode(vdom, parentEl, index, hostComponent) {
|
|
193
|
-
const Component = vdom
|
|
383
|
+
const { tag : Component, children } = vdom;
|
|
194
384
|
const { props, events } = extractPropsAndEvents(vdom);
|
|
195
385
|
const component = new Component(props, events, hostComponent);
|
|
386
|
+
component.setExternalContent(children);
|
|
196
387
|
component.mount(parentEl, index);
|
|
197
388
|
vdom.component = component;
|
|
198
389
|
vdom.el = component.firstElement;
|
|
@@ -215,6 +406,7 @@ function destroyDom(vdom) {
|
|
|
215
406
|
}
|
|
216
407
|
case DOM_TYPES.COMPONENT : {
|
|
217
408
|
vdom.component.unmount();
|
|
409
|
+
enqueueJob(() => vdom.component.onUnMounted());
|
|
218
410
|
break
|
|
219
411
|
}
|
|
220
412
|
default : {
|
|
@@ -270,4 +462,490 @@ function createApp(RootComponent, props = {}) {
|
|
|
270
462
|
}
|
|
271
463
|
}
|
|
272
464
|
|
|
273
|
-
|
|
465
|
+
function areNodesEqual(nodeOne, nodeTwo) {
|
|
466
|
+
if (nodeOne.type !== nodeTwo.type) {
|
|
467
|
+
return false
|
|
468
|
+
}
|
|
469
|
+
if (nodeOne.type === DOM_TYPES.ELEMENT) {
|
|
470
|
+
const {
|
|
471
|
+
tag : tagOne,
|
|
472
|
+
props : { key : keyOne}
|
|
473
|
+
} = nodeOne;
|
|
474
|
+
const {
|
|
475
|
+
tag : tagTwo,
|
|
476
|
+
props : { key : keyTwo}
|
|
477
|
+
} = nodeTwo;
|
|
478
|
+
return tagOne === tagTwo && keyOne === keyTwo
|
|
479
|
+
}
|
|
480
|
+
if (nodeOne.type === DOM_TYPES.COMPONENT) {
|
|
481
|
+
const {
|
|
482
|
+
tag : componentOne,
|
|
483
|
+
props : { key : keyOne},
|
|
484
|
+
} = nodeOne;
|
|
485
|
+
const {
|
|
486
|
+
tag : componentTwo,
|
|
487
|
+
props : {key : keyTwo},
|
|
488
|
+
} = nodeTwo;
|
|
489
|
+
return componentOne === componentTwo && keyOne === keyTwo
|
|
490
|
+
}
|
|
491
|
+
return true
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function objectsDiff(oldObj, newObj) {
|
|
495
|
+
const oldKeys = Object.keys(oldObj);
|
|
496
|
+
const newKeys = Object.keys(newObj);
|
|
497
|
+
return {
|
|
498
|
+
added : newKeys.filter((key) => !(key in oldObj)),
|
|
499
|
+
removed : oldKeys.filter((key) => !(key in newObj)),
|
|
500
|
+
updated : newKeys.filter(
|
|
501
|
+
(key) => key in oldObj && oldObj[key] !== newObj[key]
|
|
502
|
+
),
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function hasOwnProperty(obj, prop) {
|
|
506
|
+
return Object.prototype.hasOwnProperty.call(obj, prop)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function isNotEmptyString(str) {
|
|
510
|
+
return str !== ''
|
|
511
|
+
}
|
|
512
|
+
function isNotBlankOrEmptyString(str) {
|
|
513
|
+
return isNotEmptyString(str.trim())
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function patchDOM(oldVdom, newVdom, parentEl, hostComponent = null) {
|
|
517
|
+
if (!areNodesEqual(oldVdom, newVdom)) {
|
|
518
|
+
const index = findIndexInParent(parentEl, oldVdom.el);
|
|
519
|
+
destroyDom(oldVdom);
|
|
520
|
+
mountDom(newVdom, parentEl, index, hostComponent);
|
|
521
|
+
return newVdom
|
|
522
|
+
}
|
|
523
|
+
newVdom.el = oldVdom.el;
|
|
524
|
+
switch (newVdom.type) {
|
|
525
|
+
case DOM_TYPES.TEXT: {
|
|
526
|
+
patchText(oldVdom, newVdom);
|
|
527
|
+
return newVdom
|
|
528
|
+
}
|
|
529
|
+
case DOM_TYPES.ELEMENT: {
|
|
530
|
+
patchElement(oldVdom, newVdom, hostComponent);
|
|
531
|
+
break
|
|
532
|
+
}
|
|
533
|
+
case DOM_TYPES.COMPONENT: {
|
|
534
|
+
patchComponent(oldVdom, newVdom);
|
|
535
|
+
break
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
patchChildren(oldVdom, newVdom, hostComponent);
|
|
539
|
+
return newVdom
|
|
540
|
+
}
|
|
541
|
+
function patchText(oldVdom, newVdom) {
|
|
542
|
+
const el = oldVdom.el;
|
|
543
|
+
const { value : oldText} = oldVdom;
|
|
544
|
+
const { value : newText} = newVdom;
|
|
545
|
+
if (oldText !== newText) {
|
|
546
|
+
el.nodeValue = newText;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
function findIndexInParent(parentEl, el) {
|
|
550
|
+
const index = Array.from(parentEl.childNodes).indexOf(el);
|
|
551
|
+
if (index < 0) {
|
|
552
|
+
return null
|
|
553
|
+
}
|
|
554
|
+
return index
|
|
555
|
+
}
|
|
556
|
+
function patchElement(oldVdom, newVdom, hostComponent) {
|
|
557
|
+
const el = oldVdom.el;
|
|
558
|
+
const {
|
|
559
|
+
class : oldClass,
|
|
560
|
+
style : oldStyle,
|
|
561
|
+
on: oldEvents,
|
|
562
|
+
...oldAttrs
|
|
563
|
+
} = oldVdom.props;
|
|
564
|
+
const {
|
|
565
|
+
class: newClass,
|
|
566
|
+
style: newStyle,
|
|
567
|
+
on: newEvents,
|
|
568
|
+
...newAttrs
|
|
569
|
+
} = newVdom.props;
|
|
570
|
+
const { listeners: oldListeners } = oldVdom;
|
|
571
|
+
patchAttrs(el, oldAttrs, newAttrs);
|
|
572
|
+
patchClasses(el, oldClass, newClass);
|
|
573
|
+
patchStyles(el, oldStyle, newStyle);
|
|
574
|
+
newVdom.listeners = patchEvents(
|
|
575
|
+
el,
|
|
576
|
+
oldListeners,
|
|
577
|
+
oldEvents,
|
|
578
|
+
newEvents,
|
|
579
|
+
hostComponent
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
function patchAttrs(el, oldAttrs, newAttrs) {
|
|
583
|
+
const { added, removed, updated } = objectsDiff(oldAttrs, newAttrs);
|
|
584
|
+
for (const attr of removed) {
|
|
585
|
+
removeAttributeCustom(el, attr);
|
|
586
|
+
}
|
|
587
|
+
for (const attr of added.concat(updated)) {
|
|
588
|
+
setAttribute(el, attr, newAttrs[attr]);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function patchClasses(el, oldClass, newClass) {
|
|
592
|
+
const oldClasses = toClassList(oldClass);
|
|
593
|
+
const newClasses = toClassList(newClass);
|
|
594
|
+
const {added, removed} =
|
|
595
|
+
arraysDiff(oldClasses, newClasses);
|
|
596
|
+
if (removed.length > 0) {
|
|
597
|
+
el.classList.remove(...removed);
|
|
598
|
+
}
|
|
599
|
+
if (added.length > 0) {
|
|
600
|
+
el.classList.add(...added);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function toClassList(classes = '') {
|
|
604
|
+
return Array.isArray(classes)
|
|
605
|
+
? classes.filter(isNotBlankOrEmptyString)
|
|
606
|
+
: classes.split(/(\s+)/)
|
|
607
|
+
.filter(isNotBlankOrEmptyString)
|
|
608
|
+
}
|
|
609
|
+
function patchStyles(el, oldStyle = {}, newStyle = {}) {
|
|
610
|
+
const {added, removed, updated } = objectsDiff(oldStyle, newStyle);
|
|
611
|
+
for (const style of removed) {
|
|
612
|
+
removeStyle(el, style);
|
|
613
|
+
}
|
|
614
|
+
for (const style of added.concat(updated)) {
|
|
615
|
+
setStyle(el, style, newStyle[style]);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function patchEvents(
|
|
619
|
+
el,
|
|
620
|
+
oldListeners = {},
|
|
621
|
+
oldEvents = {},
|
|
622
|
+
newEvents = {},
|
|
623
|
+
hostComponent
|
|
624
|
+
) {
|
|
625
|
+
const { removed, added, updated} =
|
|
626
|
+
objectsDiff(oldEvents, newEvents);
|
|
627
|
+
for (const eventName of removed.concat(updated)) {
|
|
628
|
+
el.removeEventListener(eventName, oldListeners[eventName]);
|
|
629
|
+
}
|
|
630
|
+
const addedListeners = {};
|
|
631
|
+
for (const eventName of added.concat(updated)) {
|
|
632
|
+
const listener = addEventListener(
|
|
633
|
+
eventName,
|
|
634
|
+
newEvents[eventName],
|
|
635
|
+
el,
|
|
636
|
+
hostComponent
|
|
637
|
+
);
|
|
638
|
+
addedListeners[eventName] = listener;
|
|
639
|
+
}
|
|
640
|
+
return addedListeners
|
|
641
|
+
}
|
|
642
|
+
function patchComponent(oldVdom, newVdom) {
|
|
643
|
+
const { component } = oldVdom;
|
|
644
|
+
const { children } = newVdom;
|
|
645
|
+
const { props } = extractPropsAndEvents(newVdom);
|
|
646
|
+
component.setExternalContent(children);
|
|
647
|
+
component.updateProps(props);
|
|
648
|
+
newVdom.component = component;
|
|
649
|
+
newVdom.el = component.firstElement;
|
|
650
|
+
}
|
|
651
|
+
function extractChildren(vdom) {
|
|
652
|
+
if (vdom.children == null) {
|
|
653
|
+
return []
|
|
654
|
+
}
|
|
655
|
+
const children = [];
|
|
656
|
+
for (const child of vdom.children) {
|
|
657
|
+
if (child.type === DOM_TYPES.FRAGMENT) {
|
|
658
|
+
children.push(...extractChildren(child));
|
|
659
|
+
} else {
|
|
660
|
+
children.push(child);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return children
|
|
664
|
+
}
|
|
665
|
+
function patchChildren(oldVdom, newVdom, hostComponent) {
|
|
666
|
+
const oldChildren = extractChildren(oldVdom);
|
|
667
|
+
const newChildren = extractChildren(newVdom);
|
|
668
|
+
const parentEl = oldVdom.el;
|
|
669
|
+
const diffSeq = arraysDiffSequence(oldChildren, newChildren, areNodesEqual);
|
|
670
|
+
for (const operation of diffSeq) {
|
|
671
|
+
const { originalIndex, index, item} = operation;
|
|
672
|
+
const offset = hostComponent?.offset ?? 0;
|
|
673
|
+
switch (operation.op) {
|
|
674
|
+
case ARRAY_DIFF_OP.ADD: {
|
|
675
|
+
mountDom(item, parentEl, index + offset, hostComponent);
|
|
676
|
+
break
|
|
677
|
+
}
|
|
678
|
+
case ARRAY_DIFF_OP.REMOVE: {
|
|
679
|
+
destroyDom(item);
|
|
680
|
+
break
|
|
681
|
+
}
|
|
682
|
+
case ARRAY_DIFF_OP.MOVE: {
|
|
683
|
+
const oldChild = oldChildren[originalIndex];
|
|
684
|
+
const newChild = newChildren[index];
|
|
685
|
+
const el = oldChild.el;
|
|
686
|
+
const elAtTargetIndex = parentEl.childNodes[index + offset];
|
|
687
|
+
parentEl.insertBefore(el, elAtTargetIndex);
|
|
688
|
+
patchDOM(oldChildren[originalIndex], newChild, parentEl, hostComponent);
|
|
689
|
+
break
|
|
690
|
+
}
|
|
691
|
+
case ARRAY_DIFF_OP.NOOP : {
|
|
692
|
+
patchDOM(oldChildren[originalIndex], newChildren[index], parentEl, hostComponent);
|
|
693
|
+
break
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function getDefaultExportFromCjs (x) {
|
|
700
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
var fastDeepEqual;
|
|
704
|
+
var hasRequiredFastDeepEqual;
|
|
705
|
+
function requireFastDeepEqual () {
|
|
706
|
+
if (hasRequiredFastDeepEqual) return fastDeepEqual;
|
|
707
|
+
hasRequiredFastDeepEqual = 1;
|
|
708
|
+
fastDeepEqual = function equal(a, b) {
|
|
709
|
+
if (a === b) return true;
|
|
710
|
+
if (a && b && typeof a == 'object' && typeof b == 'object') {
|
|
711
|
+
if (a.constructor !== b.constructor) return false;
|
|
712
|
+
var length, i, keys;
|
|
713
|
+
if (Array.isArray(a)) {
|
|
714
|
+
length = a.length;
|
|
715
|
+
if (length != b.length) return false;
|
|
716
|
+
for (i = length; i-- !== 0;)
|
|
717
|
+
if (!equal(a[i], b[i])) return false;
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
|
|
721
|
+
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
|
|
722
|
+
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
|
|
723
|
+
keys = Object.keys(a);
|
|
724
|
+
length = keys.length;
|
|
725
|
+
if (length !== Object.keys(b).length) return false;
|
|
726
|
+
for (i = length; i-- !== 0;)
|
|
727
|
+
if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
|
|
728
|
+
for (i = length; i-- !== 0;) {
|
|
729
|
+
var key = keys[i];
|
|
730
|
+
if (!equal(a[key], b[key])) return false;
|
|
731
|
+
}
|
|
732
|
+
return true;
|
|
733
|
+
}
|
|
734
|
+
return a!==a && b!==b;
|
|
735
|
+
};
|
|
736
|
+
return fastDeepEqual;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
var fastDeepEqualExports = requireFastDeepEqual();
|
|
740
|
+
var equal = /*@__PURE__*/getDefaultExportFromCjs(fastDeepEqualExports);
|
|
741
|
+
|
|
742
|
+
class Dispatcher {
|
|
743
|
+
#subs = new Map()
|
|
744
|
+
#afterHandlers = []
|
|
745
|
+
subscribe(commandName, handler) {
|
|
746
|
+
if (!this.#subs.has(commandName)) {
|
|
747
|
+
this.#subs.set(commandName, []);
|
|
748
|
+
}
|
|
749
|
+
const handlersArray = this.#subs.get(commandName);
|
|
750
|
+
if (handlersArray.includes(handler)) {
|
|
751
|
+
return () => {
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
handlersArray.push(handler);
|
|
755
|
+
return () => {
|
|
756
|
+
const idx = handlersArray.indexOf(handler);
|
|
757
|
+
handlersArray.splice(idx, 1);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
afterEveryCommand(handler) {
|
|
761
|
+
this.#afterHandlers.push(handler);
|
|
762
|
+
return () => {
|
|
763
|
+
const idx = this.#afterHandlers.indexOf(handler);
|
|
764
|
+
this.#afterHandlers.splice(idx, 1);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
dispatch(commandName, payload) {
|
|
768
|
+
if (this.#subs.has(commandName)) {
|
|
769
|
+
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
770
|
+
} else {
|
|
771
|
+
console.warn(`No handlers for command : ${commandName}`);
|
|
772
|
+
}
|
|
773
|
+
this.#afterHandlers.forEach((handler) => handler());
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function traverseDFS(
|
|
778
|
+
vdom,
|
|
779
|
+
processNode,
|
|
780
|
+
shouldSkipBranch = () => false,
|
|
781
|
+
parentNode,
|
|
782
|
+
index = null
|
|
783
|
+
) {
|
|
784
|
+
if (shouldSkipBranch(vdom)) return
|
|
785
|
+
processNode(vdom, parentNode, index);
|
|
786
|
+
if (vdom.children) {
|
|
787
|
+
vdom.children.forEach((child, i) =>
|
|
788
|
+
traverseDFS(child, processNode, shouldSkipBranch, vdom, i)
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function fillSlots(vdom, externalContent = []) {
|
|
794
|
+
function processNode(node, parent, index) {
|
|
795
|
+
insertViewInSlot(node, parent, index, externalContent);
|
|
796
|
+
}
|
|
797
|
+
traverseDFS(vdom, processNode, shouldSkipBranch);
|
|
798
|
+
}
|
|
799
|
+
function insertViewInSlot(node, parent, index, externalContent) {
|
|
800
|
+
if (node.type !== DOM_TYPES.SLOT) return
|
|
801
|
+
const defaultContent = node.children;
|
|
802
|
+
const views = externalContent.length > 0 ? externalContent : defaultContent;
|
|
803
|
+
const hasContent = views.length > 0;
|
|
804
|
+
if (hasContent) {
|
|
805
|
+
parent.children.splice(index, 1, hFragment(views));
|
|
806
|
+
} else {
|
|
807
|
+
parent.children(index, 1);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function shouldSkipBranch(node) {
|
|
811
|
+
return node.type === DOM_TYPES.COMPONENT
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const emptyFn = () => {};
|
|
815
|
+
function defineComponent({
|
|
816
|
+
render,
|
|
817
|
+
state,
|
|
818
|
+
onMounted = emptyFn,
|
|
819
|
+
onUnmounted = emptyFn,
|
|
820
|
+
...methods
|
|
821
|
+
}) {
|
|
822
|
+
class Component {
|
|
823
|
+
#isMounted = false
|
|
824
|
+
#vdom = null
|
|
825
|
+
#hostEl = null
|
|
826
|
+
#eventHandlers = null
|
|
827
|
+
#parentComponent = null
|
|
828
|
+
#dispatcher = new Dispatcher()
|
|
829
|
+
#subscriptions = []
|
|
830
|
+
#children = []
|
|
831
|
+
setExternalContent(children) {
|
|
832
|
+
this.#children = children;
|
|
833
|
+
}
|
|
834
|
+
constructor(
|
|
835
|
+
props = {},
|
|
836
|
+
eventHandlers = {},
|
|
837
|
+
parentComponent = null
|
|
838
|
+
) {
|
|
839
|
+
this.props = props;
|
|
840
|
+
this.state = state ? state(props) : {};
|
|
841
|
+
this.#eventHandlers = eventHandlers;
|
|
842
|
+
this.#parentComponent = parentComponent;
|
|
843
|
+
}
|
|
844
|
+
onMounted() {
|
|
845
|
+
return Promise.resolve(onMounted.call(this))
|
|
846
|
+
}
|
|
847
|
+
onUnMounted() {
|
|
848
|
+
return Promise.resolve(onUnmounted.call(this))
|
|
849
|
+
}
|
|
850
|
+
get elements() {
|
|
851
|
+
if (this.#vdom == null) {
|
|
852
|
+
return []
|
|
853
|
+
}
|
|
854
|
+
if (this.#vdom.type === DOM_TYPES.FRAGMENT) {
|
|
855
|
+
return extractChildren(this.#vdom).flatMap((child) => {
|
|
856
|
+
if (child.type === DOM_TYPES.COMPONENT) ;
|
|
857
|
+
return [child.el]
|
|
858
|
+
})
|
|
859
|
+
}
|
|
860
|
+
return [this.#vdom.el]
|
|
861
|
+
}
|
|
862
|
+
get firstElement() {
|
|
863
|
+
return this.elements[0]
|
|
864
|
+
}
|
|
865
|
+
get offset() {
|
|
866
|
+
if (this.#vdom.type === DOM_TYPES.ELEMENT) {
|
|
867
|
+
return Array.from(this.#hostEl.children).indexOf(this.firstElement)
|
|
868
|
+
}
|
|
869
|
+
return 0
|
|
870
|
+
}
|
|
871
|
+
emit(eventName, payload) {
|
|
872
|
+
this.#dispatcher.dispatch(eventName, payload);
|
|
873
|
+
}
|
|
874
|
+
updateProps(props) {
|
|
875
|
+
//! creates a new props object by merging the old and new props
|
|
876
|
+
const newProps = { ...this.props, ...props};
|
|
877
|
+
if (equal(this.props, newProps)) {
|
|
878
|
+
return
|
|
879
|
+
}
|
|
880
|
+
this.props = newProps;
|
|
881
|
+
this.#patch();
|
|
882
|
+
}
|
|
883
|
+
updateState(state) {
|
|
884
|
+
this.state = { ...this.state, ...state};
|
|
885
|
+
this.#patch();
|
|
886
|
+
}
|
|
887
|
+
render() {
|
|
888
|
+
const vdom = render.call(this);
|
|
889
|
+
if (didCreateSlot()) {
|
|
890
|
+
fillSlots(vdom, this.#children);
|
|
891
|
+
resetDidCreateSlot();
|
|
892
|
+
}
|
|
893
|
+
return vdom
|
|
894
|
+
}
|
|
895
|
+
mount(hostEl, index = null) {
|
|
896
|
+
if (this.#isMounted) {
|
|
897
|
+
throw new Error('Component is already mounted')
|
|
898
|
+
}
|
|
899
|
+
this.#vdom = this.render();
|
|
900
|
+
mountDom(this.#vdom, hostEl, index, this); //!this - passes the component reference to the mountDOM fucntion
|
|
901
|
+
this.#wireEventHandlers();
|
|
902
|
+
this.#hostEl = hostEl;
|
|
903
|
+
this.#isMounted = true;
|
|
904
|
+
}
|
|
905
|
+
#patch() {
|
|
906
|
+
if (!this.#isMounted) {
|
|
907
|
+
throw new Error('Component is not mounted')
|
|
908
|
+
}
|
|
909
|
+
const vdom = this.render();
|
|
910
|
+
this.#vdom = patchDOM(this.#vdom, vdom, this.#hostEl, this);
|
|
911
|
+
}
|
|
912
|
+
unmount() {
|
|
913
|
+
if (!this.#isMounted) {
|
|
914
|
+
throw new Error(`Component is not mounted`)
|
|
915
|
+
}
|
|
916
|
+
destroyDom(this.#vdom);
|
|
917
|
+
this.#subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
918
|
+
this.#vdom = null;
|
|
919
|
+
this.#hostEl = null;
|
|
920
|
+
this.#isMounted = false;
|
|
921
|
+
this.#subscriptions = [];
|
|
922
|
+
}
|
|
923
|
+
#wireEventHandlers() {
|
|
924
|
+
this.#subscriptions = Object.entries(this.#eventHandlers).map(
|
|
925
|
+
([eventName, handler]) => {
|
|
926
|
+
this.#wireEventHandlers(eventName, handler);
|
|
927
|
+
}
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
#wireEventHandler(eventName, handler) {
|
|
931
|
+
return this.#dispatcher.subscribe(eventName, (payload) => {
|
|
932
|
+
if (this.#parentComponent) {
|
|
933
|
+
handler.call(this.#parentComponent, payload);
|
|
934
|
+
} else {
|
|
935
|
+
handler(payload);
|
|
936
|
+
}
|
|
937
|
+
})
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
for (const methodName in methods) {
|
|
941
|
+
if (hasOwnProperty(Component, methodName)) {
|
|
942
|
+
throw new Error(
|
|
943
|
+
`Method "${methodName}()" already exists in the component.`
|
|
944
|
+
)
|
|
945
|
+
}
|
|
946
|
+
Component.prototype[methodName] = methods[methodName];
|
|
947
|
+
}
|
|
948
|
+
return Component;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
export { DOM_TYPES, createApp, defineComponent, h, hFragment, hSlot, hString, nextTick };
|