betal-fe 2.1.0 → 3.1.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/betal-fe.js +355 -158
- package/package.json +6 -1
package/dist/betal-fe.js
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
function addEventListener(eventName, handler, el) {
|
|
2
|
-
el.addEventListener(eventName, handler);
|
|
3
|
-
return handler;
|
|
4
|
-
}
|
|
5
|
-
function addEventListeners(events = {}, el) {
|
|
6
|
-
const addedEventListeners = {};
|
|
7
|
-
Object.entries(events).forEach(([eventName, handler]) => {
|
|
8
|
-
addedEventListeners[eventName] = addEventListener(eventName, handler, el);
|
|
9
|
-
});
|
|
10
|
-
return addedEventListeners;
|
|
11
|
-
}
|
|
12
|
-
function removeEventListeners(listeners = {}, el) {
|
|
13
|
-
Object.entries(listeners).forEach(([eventName, handler]) => {
|
|
14
|
-
el.removeEventListener(eventName, handler);
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
1
|
const ARRAY_DIFF_OP = {
|
|
19
2
|
ADD: "add",
|
|
20
3
|
REMOVE: "remove",
|
|
@@ -157,10 +140,12 @@ const DOM_TYPES = {
|
|
|
157
140
|
TEXT: "text",
|
|
158
141
|
ELEMENT: "element",
|
|
159
142
|
FRAGMENT: "fragment",
|
|
143
|
+
COMPONENT: "component",
|
|
160
144
|
};
|
|
161
145
|
function h(tag, props = {}, children = []) {
|
|
146
|
+
const type = typeof tag === "string" ? DOM_TYPES.ELEMENT : DOM_TYPES.COMPONENT;
|
|
162
147
|
return {
|
|
163
|
-
type
|
|
148
|
+
type,
|
|
164
149
|
tag,
|
|
165
150
|
props,
|
|
166
151
|
children: mapTextNodes(withoutNulls(children)),
|
|
@@ -195,80 +180,6 @@ function extractChildren(vdom) {
|
|
|
195
180
|
return children;
|
|
196
181
|
}
|
|
197
182
|
|
|
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
|
-
removeFragmentNodes(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 removeFragmentNodes(vDom) {
|
|
233
|
-
const { children } = vDom;
|
|
234
|
-
children.forEach(destroyDOM);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
class Dispatcher {
|
|
238
|
-
#subs = new Map();
|
|
239
|
-
#afterHandlers = [];
|
|
240
|
-
subscribe(commandName, handler) {
|
|
241
|
-
if (!this.#subs.has(commandName)) {
|
|
242
|
-
this.#subs.set(commandName, []);
|
|
243
|
-
}
|
|
244
|
-
const handlers = this.#subs.get(commandName);
|
|
245
|
-
if (handlers.includes(handler)) {
|
|
246
|
-
return () => {};
|
|
247
|
-
}
|
|
248
|
-
handlers.push(handler);
|
|
249
|
-
return () => {
|
|
250
|
-
const idx = handlers.indexOf(handler);
|
|
251
|
-
handlers.splice(idx, 1);
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
afterEveryCommand(handler) {
|
|
255
|
-
this.#afterHandlers.push(handler);
|
|
256
|
-
return () => {
|
|
257
|
-
const idx = this.#afterHandlers.indexOf(handler);
|
|
258
|
-
this.#afterHandlers.splice(idx, 1);
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
dispatch(commandName, payload) {
|
|
262
|
-
if (this.#subs.has(commandName)) {
|
|
263
|
-
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
console.warn(`No handlers for command: ${commandName}`);
|
|
267
|
-
}
|
|
268
|
-
this.#afterHandlers.forEach((handler) => handler());
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
183
|
function setAttributes(el, attrs) {
|
|
273
184
|
const { class: className, style, ...otherAttrs } = attrs;
|
|
274
185
|
if (className) {
|
|
@@ -311,22 +222,59 @@ function removeAttribute(el, name) {
|
|
|
311
222
|
el.removeAttribute(name);
|
|
312
223
|
}
|
|
313
224
|
|
|
314
|
-
function
|
|
225
|
+
function addEventListener(eventName, handler, el, hostComponent = null) {
|
|
226
|
+
function boundHandler() {
|
|
227
|
+
hostComponent
|
|
228
|
+
? handler.apply(hostComponent, arguments)
|
|
229
|
+
: handler(...arguments);
|
|
230
|
+
}
|
|
231
|
+
el.addEventListener(eventName, boundHandler);
|
|
232
|
+
return boundHandler;
|
|
233
|
+
}
|
|
234
|
+
function addEventListeners(events = {}, el, hostComponent = null) {
|
|
235
|
+
const addedEventListeners = {};
|
|
236
|
+
Object.entries(events).forEach(([eventName, handler]) => {
|
|
237
|
+
addedEventListeners[eventName] = addEventListener(
|
|
238
|
+
eventName,
|
|
239
|
+
handler,
|
|
240
|
+
el,
|
|
241
|
+
hostComponent
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
return addedEventListeners;
|
|
245
|
+
}
|
|
246
|
+
function removeEventListeners(listeners = {}, el) {
|
|
247
|
+
Object.entries(listeners).forEach(([eventName, handler]) => {
|
|
248
|
+
el.removeEventListener(eventName, handler);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function extractPropsAndEvents(vdom) {
|
|
253
|
+
const { on: events = {}, ...props } = vdom.props;
|
|
254
|
+
delete props.key;
|
|
255
|
+
return { props, events };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function mountDOM(vDom, parentElement, index, hostComponent = null) {
|
|
315
259
|
switch (vDom.type) {
|
|
316
260
|
case DOM_TYPES.TEXT: {
|
|
317
261
|
createTextNode(vDom, parentElement, index);
|
|
318
262
|
break;
|
|
319
263
|
}
|
|
320
264
|
case DOM_TYPES.ELEMENT: {
|
|
321
|
-
createElementNode(vDom, parentElement, index);
|
|
265
|
+
createElementNode(vDom, parentElement, index, hostComponent);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case DOM_TYPES.COMPONENT: {
|
|
269
|
+
createComponentNode(vDom, parentElement, index, hostComponent);
|
|
322
270
|
break;
|
|
323
271
|
}
|
|
324
272
|
case DOM_TYPES.FRAGMENT: {
|
|
325
|
-
createFragmentNode(vDom, parentElement, index);
|
|
273
|
+
createFragmentNode(vDom, parentElement, index, hostComponent);
|
|
326
274
|
break;
|
|
327
275
|
}
|
|
328
276
|
default: {
|
|
329
|
-
throw new Error(`Can't mount DOM of type: ${vDom.type}`)
|
|
277
|
+
throw new Error(`Can't mount DOM of type: ${vDom.type}`);
|
|
330
278
|
}
|
|
331
279
|
}
|
|
332
280
|
}
|
|
@@ -336,22 +284,24 @@ function createTextNode(vDom, parentElement, index) {
|
|
|
336
284
|
vDom.el = textNode;
|
|
337
285
|
insert(textNode, parentElement, index);
|
|
338
286
|
}
|
|
339
|
-
function createFragmentNode(vDom, parentElement, index) {
|
|
287
|
+
function createFragmentNode(vDom, parentElement, index, hostComponent) {
|
|
340
288
|
const { children } = vDom;
|
|
341
289
|
vDom.el = parentElement;
|
|
342
|
-
children.forEach((child) =>
|
|
290
|
+
children.forEach((child) =>
|
|
291
|
+
mountDOM(child, parentElement, index ? index + 1 : null, hostComponent)
|
|
292
|
+
);
|
|
343
293
|
}
|
|
344
|
-
function createElementNode(vDom, parentElement, index) {
|
|
345
|
-
const { tag,
|
|
294
|
+
function createElementNode(vDom, parentElement, index, hostComponent) {
|
|
295
|
+
const { tag, children } = vDom;
|
|
346
296
|
const element = document.createElement(tag);
|
|
347
|
-
addProps(element,
|
|
297
|
+
addProps(element, vDom, hostComponent);
|
|
348
298
|
vDom.el = element;
|
|
349
|
-
children.forEach((child) => mountDOM(child, element));
|
|
299
|
+
children.forEach((child) => mountDOM(child, element, null, hostComponent));
|
|
350
300
|
insert(element, parentElement, index);
|
|
351
301
|
}
|
|
352
|
-
function addProps(el,
|
|
353
|
-
const {
|
|
354
|
-
vdom.listeners = addEventListeners(events, el);
|
|
302
|
+
function addProps(el,vdom, hostComponent) {
|
|
303
|
+
const { props: attrs, events } = extractPropsAndEvents(vdom);
|
|
304
|
+
vdom.listeners = addEventListeners(events, el, hostComponent);
|
|
355
305
|
setAttributes(el, attrs);
|
|
356
306
|
}
|
|
357
307
|
function insert(el, parentEl, index) {
|
|
@@ -369,15 +319,143 @@ function insert(el, parentEl, index) {
|
|
|
369
319
|
parentEl.insertBefore(el, children[index]);
|
|
370
320
|
}
|
|
371
321
|
}
|
|
322
|
+
function createComponentNode(vdom, parentEl, index, hostComponent) {
|
|
323
|
+
const Component = vdom.tag;
|
|
324
|
+
const { props, events } = extractPropsAndEvents(vdom);
|
|
325
|
+
const component = new Component(props, events, hostComponent);
|
|
326
|
+
component.mount(parentEl, index);
|
|
327
|
+
vdom.component = component;
|
|
328
|
+
vdom.el = component.firstElement;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function destroyDOM(vDom) {
|
|
332
|
+
const { type } = vDom;
|
|
333
|
+
switch (type) {
|
|
334
|
+
case DOM_TYPES.TEXT: {
|
|
335
|
+
removeTextNode(vDom);
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
case DOM_TYPES.ELEMENT: {
|
|
339
|
+
removeElementNode(vDom);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case DOM_TYPES.FRAGMENT: {
|
|
343
|
+
removeFragmentNodes(vDom);
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
case DOM_TYPES.COMPONENT: {
|
|
347
|
+
vDom.component.unmount();
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
default: {
|
|
351
|
+
throw new Error(`Can't destroy DOM of type: ${type}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
delete vDom.el;
|
|
355
|
+
}
|
|
356
|
+
function removeTextNode(vDom) {
|
|
357
|
+
const { el } = vDom;
|
|
358
|
+
el.remove();
|
|
359
|
+
}
|
|
360
|
+
function removeElementNode(vdom) {
|
|
361
|
+
const { el, children, listeners } = vdom;
|
|
362
|
+
el.remove();
|
|
363
|
+
children.forEach(destroyDOM);
|
|
364
|
+
if (listeners) {
|
|
365
|
+
removeEventListeners(listeners, el);
|
|
366
|
+
delete vdom.listeners;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function removeFragmentNodes(vDom) {
|
|
370
|
+
const { children } = vDom;
|
|
371
|
+
children.forEach(destroyDOM);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function createApp(RootComponent, props = {}) {
|
|
375
|
+
let parentEl = null;
|
|
376
|
+
let isMounted = false;
|
|
377
|
+
let vdom = null;
|
|
378
|
+
function reset() {
|
|
379
|
+
parentEl = null;
|
|
380
|
+
isMounted = false;
|
|
381
|
+
vdom = null;
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
mount(_parentEl) {
|
|
385
|
+
if (isMounted) {
|
|
386
|
+
throw new Error("The application is already mounted");
|
|
387
|
+
}
|
|
388
|
+
parentEl = _parentEl;
|
|
389
|
+
vdom = h(RootComponent, props);
|
|
390
|
+
mountDOM(vdom, parentEl);
|
|
391
|
+
isMounted = true;
|
|
392
|
+
},
|
|
393
|
+
unmount() {
|
|
394
|
+
if (!isMounted) {
|
|
395
|
+
throw new Error("The application is not mounted");
|
|
396
|
+
}
|
|
397
|
+
destroyDOM(vdom);
|
|
398
|
+
reset();
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function getDefaultExportFromCjs (x) {
|
|
404
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
var fastDeepEqual;
|
|
408
|
+
var hasRequiredFastDeepEqual;
|
|
409
|
+
function requireFastDeepEqual () {
|
|
410
|
+
if (hasRequiredFastDeepEqual) return fastDeepEqual;
|
|
411
|
+
hasRequiredFastDeepEqual = 1;
|
|
412
|
+
fastDeepEqual = function equal(a, b) {
|
|
413
|
+
if (a === b) return true;
|
|
414
|
+
if (a && b && typeof a == 'object' && typeof b == 'object') {
|
|
415
|
+
if (a.constructor !== b.constructor) return false;
|
|
416
|
+
var length, i, keys;
|
|
417
|
+
if (Array.isArray(a)) {
|
|
418
|
+
length = a.length;
|
|
419
|
+
if (length != b.length) return false;
|
|
420
|
+
for (i = length; i-- !== 0;)
|
|
421
|
+
if (!equal(a[i], b[i])) return false;
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
|
|
425
|
+
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
|
|
426
|
+
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
|
|
427
|
+
keys = Object.keys(a);
|
|
428
|
+
length = keys.length;
|
|
429
|
+
if (length !== Object.keys(b).length) return false;
|
|
430
|
+
for (i = length; i-- !== 0;)
|
|
431
|
+
if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
|
|
432
|
+
for (i = length; i-- !== 0;) {
|
|
433
|
+
var key = keys[i];
|
|
434
|
+
if (!equal(a[key], b[key])) return false;
|
|
435
|
+
}
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
return a!==a && b!==b;
|
|
439
|
+
};
|
|
440
|
+
return fastDeepEqual;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
var fastDeepEqualExports = requireFastDeepEqual();
|
|
444
|
+
var equal = /*@__PURE__*/getDefaultExportFromCjs(fastDeepEqualExports);
|
|
372
445
|
|
|
373
446
|
function areNodesEqual(nodeOne, nodeTwo) {
|
|
374
447
|
if (nodeOne.type !== nodeTwo.type) {
|
|
375
448
|
return false;
|
|
376
449
|
}
|
|
377
450
|
if (nodeOne.type === DOM_TYPES.ELEMENT) {
|
|
378
|
-
const { tag: tagOne } = nodeOne;
|
|
379
|
-
const { tag: tagTwo } = nodeTwo;
|
|
380
|
-
return tagOne === tagTwo;
|
|
451
|
+
const { tag: tagOne, props: { key: keyOne } } = nodeOne;
|
|
452
|
+
const { tag: tagTwo, props: { key: keyTwo } } = nodeTwo;
|
|
453
|
+
return tagOne === tagTwo && keyOne === keyTwo;
|
|
454
|
+
}
|
|
455
|
+
if (nodeOne.type === DOM_TYPES.COMPONENT) {
|
|
456
|
+
const { tag: componentOne, props: { key: keyOne } } = nodeOne;
|
|
457
|
+
const { tag: componentTwo, props: { key: keyTwo } } = nodeTwo;
|
|
458
|
+
return componentOne === componentTwo && keyOne === keyTwo;
|
|
381
459
|
}
|
|
382
460
|
return true;
|
|
383
461
|
}
|
|
@@ -401,6 +479,9 @@ function objectsDiff(oldObj, newObj) {
|
|
|
401
479
|
updated,
|
|
402
480
|
};
|
|
403
481
|
}
|
|
482
|
+
function hasOwnProperty(obj, prop) {
|
|
483
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
484
|
+
}
|
|
404
485
|
|
|
405
486
|
function isNotEmptyString(str) {
|
|
406
487
|
return str !== ''
|
|
@@ -409,11 +490,11 @@ function isNotBlankOrEmptyString(str) {
|
|
|
409
490
|
return isNotEmptyString(str.trim())
|
|
410
491
|
}
|
|
411
492
|
|
|
412
|
-
function patchDOM(oldVdom, newVdom, parentEl) {
|
|
493
|
+
function patchDOM(oldVdom, newVdom, parentEl, hostComponent = null) {
|
|
413
494
|
if (!areNodesEqual(oldVdom, newVdom)) {
|
|
414
495
|
const index = findIndexInParent(parentEl, oldVdom.el);
|
|
415
496
|
destroyDOM(oldVdom);
|
|
416
|
-
mountDOM(newVdom, parentEl, index);
|
|
497
|
+
mountDOM(newVdom, parentEl, index, hostComponent);
|
|
417
498
|
return newVdom;
|
|
418
499
|
}
|
|
419
500
|
newVdom.el = oldVdom.el;
|
|
@@ -423,11 +504,15 @@ function patchDOM(oldVdom, newVdom, parentEl) {
|
|
|
423
504
|
return newVdom;
|
|
424
505
|
}
|
|
425
506
|
case DOM_TYPES.ELEMENT: {
|
|
426
|
-
patchElement(oldVdom, newVdom);
|
|
507
|
+
patchElement(oldVdom, newVdom, hostComponent);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case DOM_TYPES.COMPONENT: {
|
|
511
|
+
patchComponent(oldVdom, newVdom);
|
|
427
512
|
break;
|
|
428
513
|
}
|
|
429
514
|
}
|
|
430
|
-
patchChildren(oldVdom, newVdom);
|
|
515
|
+
patchChildren(oldVdom, newVdom, hostComponent);
|
|
431
516
|
return newVdom;
|
|
432
517
|
}
|
|
433
518
|
function findIndexInParent(parentEl, el) {
|
|
@@ -444,7 +529,7 @@ function patchText(oldVdom, newVdom) {
|
|
|
444
529
|
newVdom.el.nodeValue = newText;
|
|
445
530
|
}
|
|
446
531
|
}
|
|
447
|
-
function patchElement(oldVdom, newVdom) {
|
|
532
|
+
function patchElement(oldVdom, newVdom, hostComponent) {
|
|
448
533
|
const el = oldVdom.el;
|
|
449
534
|
const {
|
|
450
535
|
class: oldClass,
|
|
@@ -462,7 +547,7 @@ function patchElement(oldVdom, newVdom) {
|
|
|
462
547
|
patchAttrs(el, oldAttrs, newAttrs);
|
|
463
548
|
patchClasses(el, oldClass, newClass);
|
|
464
549
|
patchStyles(el, oldStyle, newStyle);
|
|
465
|
-
newVdom.listeners = patchEvents(el, oldListeners, oldEvents, newEvents);
|
|
550
|
+
newVdom.listeners = patchEvents(el, oldListeners, oldEvents, newEvents, hostComponent);
|
|
466
551
|
}
|
|
467
552
|
function patchAttrs(el, oldAttrs, newAttrs) {
|
|
468
553
|
const { added, removed, updated } = objectsDiff(oldAttrs, newAttrs);
|
|
@@ -498,32 +583,30 @@ function patchStyles(el, oldStyle = {}, newStyle = {}) {
|
|
|
498
583
|
setStyle(el, style, newStyle[style]);
|
|
499
584
|
}
|
|
500
585
|
}
|
|
501
|
-
function patchEvents(el, oldListeners = {}, oldEvents = {}, newEvents = {}) {
|
|
586
|
+
function patchEvents(el, oldListeners = {}, oldEvents = {}, newEvents = {}, hostComponent) {
|
|
502
587
|
const { removed, added, updated } = objectsDiff(oldEvents, newEvents);
|
|
588
|
+
const listeners = { ...oldListeners };
|
|
503
589
|
for (const eventName of removed.concat(updated)) {
|
|
504
590
|
el.removeEventListener(eventName, oldListeners[eventName]);
|
|
591
|
+
delete listeners[eventName];
|
|
505
592
|
}
|
|
506
|
-
const addedListeners = {};
|
|
507
593
|
for (const eventName of added.concat(updated)) {
|
|
508
|
-
const listener = addEventListener(eventName, newEvents[eventName], el);
|
|
509
|
-
|
|
594
|
+
const listener = addEventListener(eventName, newEvents[eventName], el, hostComponent);
|
|
595
|
+
listeners[eventName] = listener;
|
|
510
596
|
}
|
|
511
|
-
return
|
|
597
|
+
return listeners;
|
|
512
598
|
}
|
|
513
|
-
function patchChildren(oldVdom, newVdom) {
|
|
599
|
+
function patchChildren(oldVdom, newVdom, hostComponent) {
|
|
514
600
|
const oldChildren = extractChildren(oldVdom);
|
|
515
601
|
const newChildren = extractChildren(newVdom);
|
|
516
602
|
const parentEl = oldVdom.el;
|
|
517
|
-
const diffSeq = arraysDiffSequence(
|
|
518
|
-
oldChildren,
|
|
519
|
-
newChildren,
|
|
520
|
-
areNodesEqual
|
|
521
|
-
);
|
|
603
|
+
const diffSeq = arraysDiffSequence(oldChildren, newChildren, areNodesEqual);
|
|
522
604
|
for (const operation of diffSeq) {
|
|
523
605
|
const { originalIndex, index, item } = operation;
|
|
606
|
+
const offset = hostComponent?.offset ?? 0;
|
|
524
607
|
switch (operation.op) {
|
|
525
608
|
case ARRAY_DIFF_OP.ADD: {
|
|
526
|
-
mountDOM(item, parentEl, index);
|
|
609
|
+
mountDOM(item, parentEl, index + offset, hostComponent);
|
|
527
610
|
break;
|
|
528
611
|
}
|
|
529
612
|
case ARRAY_DIFF_OP.REMOVE: {
|
|
@@ -534,53 +617,167 @@ function patchChildren(oldVdom, newVdom) {
|
|
|
534
617
|
const oldChild = oldChildren[originalIndex];
|
|
535
618
|
const newChild = newChildren[index];
|
|
536
619
|
const el = oldChild.el;
|
|
537
|
-
const elAtTargetIndex = parentEl.childNodes[index];
|
|
620
|
+
const elAtTargetIndex = parentEl.childNodes[index + offset];
|
|
538
621
|
parentEl.insertBefore(el, elAtTargetIndex);
|
|
539
|
-
patchDOM(oldChild, newChild, parentEl);
|
|
622
|
+
patchDOM(oldChild, newChild, parentEl, hostComponent);
|
|
540
623
|
break;
|
|
541
624
|
}
|
|
542
625
|
case ARRAY_DIFF_OP.NOOP: {
|
|
543
|
-
patchDOM(oldChildren[originalIndex], newChildren[index], parentEl);
|
|
626
|
+
patchDOM(oldChildren[originalIndex], newChildren[index], parentEl, hostComponent);
|
|
544
627
|
break;
|
|
545
628
|
}
|
|
546
629
|
}
|
|
547
630
|
}
|
|
548
631
|
}
|
|
632
|
+
function patchComponent(oldVdom, newVdom) {
|
|
633
|
+
const { component } = oldVdom;
|
|
634
|
+
const { props } = extractPropsAndEvents(newVdom);
|
|
635
|
+
component.updateProps(props);
|
|
636
|
+
newVdom.component = component;
|
|
637
|
+
newVdom.el = component.firstElement;
|
|
638
|
+
}
|
|
549
639
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
640
|
+
class Dispatcher {
|
|
641
|
+
#subs = new Map();
|
|
642
|
+
#afterHandlers = [];
|
|
643
|
+
subscribe(commandName, handler) {
|
|
644
|
+
if (!this.#subs.has(commandName)) {
|
|
645
|
+
this.#subs.set(commandName, []);
|
|
646
|
+
}
|
|
647
|
+
const handlers = this.#subs.get(commandName);
|
|
648
|
+
if (handlers.includes(handler)) {
|
|
649
|
+
return () => {};
|
|
650
|
+
}
|
|
651
|
+
handlers.push(handler);
|
|
652
|
+
return () => {
|
|
653
|
+
const idx = handlers.indexOf(handler);
|
|
654
|
+
handlers.splice(idx, 1);
|
|
655
|
+
};
|
|
564
656
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
657
|
+
afterEveryCommand(handler) {
|
|
658
|
+
this.#afterHandlers.push(handler);
|
|
659
|
+
return () => {
|
|
660
|
+
const idx = this.#afterHandlers.indexOf(handler);
|
|
661
|
+
this.#afterHandlers.splice(idx, 1);
|
|
662
|
+
};
|
|
568
663
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
664
|
+
dispatch(commandName, payload) {
|
|
665
|
+
if (this.#subs.has(commandName)) {
|
|
666
|
+
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
console.warn(`No handlers for command: ${commandName}`);
|
|
670
|
+
}
|
|
671
|
+
this.#afterHandlers.forEach((handler) => handler());
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function defineComponent({ render, state, ...methods }) {
|
|
676
|
+
class Component {
|
|
677
|
+
#vdom = null;
|
|
678
|
+
#isMounted = false;
|
|
679
|
+
#hostEl = null;
|
|
680
|
+
#eventHandlers = null;
|
|
681
|
+
#parentComponent = null;
|
|
682
|
+
#dispatcher = new Dispatcher();
|
|
683
|
+
#subscriptions = [];
|
|
684
|
+
constructor(props = {}, eventHandlers = {}, parentComponent = null) {
|
|
685
|
+
this.props = props;
|
|
686
|
+
this.state = state ? state(props) : {};
|
|
687
|
+
this.#eventHandlers = eventHandlers;
|
|
688
|
+
this.#parentComponent = parentComponent;
|
|
689
|
+
}
|
|
690
|
+
updateState(newState) {
|
|
691
|
+
this.state = { ...this.state, ...newState };
|
|
692
|
+
this.#patch();
|
|
693
|
+
}
|
|
694
|
+
render() {
|
|
695
|
+
return render.call(this);
|
|
696
|
+
}
|
|
697
|
+
mount(hostEl, index = null) {
|
|
698
|
+
if (this.#isMounted) {
|
|
699
|
+
throw new Error("Component is already mounted");
|
|
573
700
|
}
|
|
574
|
-
|
|
575
|
-
vdom
|
|
576
|
-
|
|
577
|
-
|
|
701
|
+
this.#vdom = this.render();
|
|
702
|
+
mountDOM(this.#vdom, hostEl, index, this);
|
|
703
|
+
this.#wireEventHandlers();
|
|
704
|
+
this.#hostEl = hostEl;
|
|
705
|
+
this.#isMounted = true;
|
|
706
|
+
}
|
|
578
707
|
unmount() {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
708
|
+
if (!this.#isMounted) {
|
|
709
|
+
throw new Error("Component is not mounted");
|
|
710
|
+
}
|
|
711
|
+
destroyDOM(this.#vdom);
|
|
712
|
+
this.#subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
713
|
+
this.#subscriptions = [];
|
|
714
|
+
this.#vdom = null;
|
|
715
|
+
this.#hostEl = null;
|
|
716
|
+
this.#isMounted = false;
|
|
717
|
+
}
|
|
718
|
+
updateProps(props) {
|
|
719
|
+
const newProps = { ...this.props, ...props };
|
|
720
|
+
if (equal(this.props, newProps)) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
this.props = newProps;
|
|
724
|
+
this.#patch();
|
|
725
|
+
}
|
|
726
|
+
emit(eventName, payload) {
|
|
727
|
+
this.#dispatcher.dispatch(eventName, payload);
|
|
728
|
+
}
|
|
729
|
+
#patch() {
|
|
730
|
+
if (!this.#isMounted) {
|
|
731
|
+
throw new Error("Component is not mounted");
|
|
732
|
+
}
|
|
733
|
+
const vdom = this.render();
|
|
734
|
+
this.#vdom = patchDOM(this.#vdom, vdom, this.#hostEl, this);
|
|
735
|
+
}
|
|
736
|
+
#wireEventHandlers() {
|
|
737
|
+
this.#subscriptions = Object.entries(this.#eventHandlers).map(
|
|
738
|
+
([eventName, handler]) => this.#wireEventHandler(eventName, handler)
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
#wireEventHandler(eventName, handler) {
|
|
742
|
+
return this.#dispatcher.subscribe(eventName, (payload) => {
|
|
743
|
+
if (this.#parentComponent) {
|
|
744
|
+
handler.call(this.#parentComponent, payload);
|
|
745
|
+
} else {
|
|
746
|
+
handler(payload);
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
get elements() {
|
|
751
|
+
if (this.#vdom == null) {
|
|
752
|
+
return [];
|
|
753
|
+
}
|
|
754
|
+
if (this.#vdom.type === DOM_TYPES.FRAGMENT) {
|
|
755
|
+
return extractChildren(this.#vdom).flatMap((child) => {
|
|
756
|
+
if (child.type === DOM_TYPES.COMPONENT) {
|
|
757
|
+
return child.component.elements;
|
|
758
|
+
}
|
|
759
|
+
return [child.el];
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
return [this.#vdom.el];
|
|
763
|
+
}
|
|
764
|
+
get firstElement() {
|
|
765
|
+
return this.elements[0];
|
|
766
|
+
}
|
|
767
|
+
get offset() {
|
|
768
|
+
if (this.#vdom.type === DOM_TYPES.FRAGMENT) {
|
|
769
|
+
return Array.from(this.#hostEl.children).indexOf(this.firstElement);
|
|
770
|
+
}
|
|
771
|
+
return 0;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
for(const methodName in methods) {
|
|
775
|
+
if (hasOwnProperty(Component, methodName)) {
|
|
776
|
+
throw new Error(`Method "${methodName}()" already exists in the component`);
|
|
582
777
|
}
|
|
778
|
+
Component.prototype[methodName] = methods[methodName];
|
|
583
779
|
}
|
|
780
|
+
return Component;
|
|
584
781
|
}
|
|
585
782
|
|
|
586
|
-
export { createApp, h, hFragment, hString };
|
|
783
|
+
export { createApp, defineComponent, h, hFragment, hString };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "betal-fe",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/betal-fe.js",
|
|
6
6
|
"files": [
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"description": "",
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@eslint/js": "^9.38.0",
|
|
23
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
24
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
23
25
|
"eslint": "^9.38.0",
|
|
24
26
|
"eslint-plugin-import": "^2.32.0",
|
|
25
27
|
"globals": "^16.4.0",
|
|
@@ -28,5 +30,8 @@
|
|
|
28
30
|
"rollup-plugin-cleanup": "^3.2.1",
|
|
29
31
|
"rollup-plugin-filesize": "^10.0.0",
|
|
30
32
|
"vitest": "^3.2.4"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"fast-deep-equal": "^3.1.3"
|
|
31
36
|
}
|
|
32
37
|
}
|