betal-fe 2.1.0 → 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.
Files changed (2) hide show
  1. package/dist/betal-fe.js +351 -155
  2. 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: DOM_TYPES.ELEMENT,
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 mountDOM(vDom, parentElement, index) {
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) => mountDOM(child, parentElement, index ? index + 1 : null));
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, props, children } = vDom;
294
+ function createElementNode(vDom, parentElement, index, hostComponent) {
295
+ const { tag, children } = vDom;
346
296
  const element = document.createElement(tag);
347
- addProps(element, props, vDom);
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, props, vdom) {
353
- const { on: events, ...attrs } = props;
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,29 @@ 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);
503
588
  for (const eventName of removed.concat(updated)) {
504
589
  el.removeEventListener(eventName, oldListeners[eventName]);
505
590
  }
506
591
  const addedListeners = {};
507
592
  for (const eventName of added.concat(updated)) {
508
- const listener = addEventListener(eventName, newEvents[eventName], el);
593
+ const listener = addEventListener(eventName, newEvents[eventName], el, hostComponent);
509
594
  addedListeners[eventName] = listener;
510
595
  }
511
596
  return addedListeners;
512
597
  }
513
- function patchChildren(oldVdom, newVdom) {
598
+ function patchChildren(oldVdom, newVdom, hostComponent) {
514
599
  const oldChildren = extractChildren(oldVdom);
515
600
  const newChildren = extractChildren(newVdom);
516
601
  const parentEl = oldVdom.el;
517
- const diffSeq = arraysDiffSequence(
518
- oldChildren,
519
- newChildren,
520
- areNodesEqual
521
- );
602
+ const diffSeq = arraysDiffSequence(oldChildren, newChildren, areNodesEqual);
522
603
  for (const operation of diffSeq) {
523
604
  const { originalIndex, index, item } = operation;
605
+ const offset = hostComponent?.offset ?? 0;
524
606
  switch (operation.op) {
525
607
  case ARRAY_DIFF_OP.ADD: {
526
- mountDOM(item, parentEl, index);
608
+ mountDOM(item, parentEl, index + offset, hostComponent);
527
609
  break;
528
610
  }
529
611
  case ARRAY_DIFF_OP.REMOVE: {
@@ -534,53 +616,167 @@ function patchChildren(oldVdom, newVdom) {
534
616
  const oldChild = oldChildren[originalIndex];
535
617
  const newChild = newChildren[index];
536
618
  const el = oldChild.el;
537
- const elAtTargetIndex = parentEl.childNodes[index];
619
+ const elAtTargetIndex = parentEl.childNodes[index + offset];
538
620
  parentEl.insertBefore(el, elAtTargetIndex);
539
- patchDOM(oldChild, newChild, parentEl);
621
+ patchDOM(oldChild, newChild, parentEl, hostComponent);
540
622
  break;
541
623
  }
542
624
  case ARRAY_DIFF_OP.NOOP: {
543
- patchDOM(oldChildren[originalIndex], newChildren[index], parentEl);
625
+ patchDOM(oldChildren[originalIndex], newChildren[index], parentEl, hostComponent);
544
626
  break;
545
627
  }
546
628
  }
547
629
  }
548
630
  }
631
+ function patchComponent(oldVdom, newVdom) {
632
+ const { component } = oldVdom;
633
+ const { props } = extractPropsAndEvents(newVdom);
634
+ component.updateProps(props);
635
+ newVdom.component = component;
636
+ newVdom.el = component.firstElement;
637
+ }
549
638
 
550
- function createApp({ state, view, reducers = {} }) {
551
- let parentEl = null;
552
- let vdom = null;
553
- const dispatcher = new Dispatcher();
554
- const subscriptions = [dispatcher.afterEveryCommand(renderApp)];
555
- function emit(eventName, payload) {
556
- dispatcher.dispatch(eventName, payload);
557
- }
558
- for (const actionName in reducers) {
559
- const reducer = reducers[actionName];
560
- const subs = dispatcher.subscribe(actionName, (payload) => {
561
- state = reducer(state, payload);
562
- });
563
- subscriptions.push(subs);
639
+ class Dispatcher {
640
+ #subs = new Map();
641
+ #afterHandlers = [];
642
+ subscribe(commandName, handler) {
643
+ if (!this.#subs.has(commandName)) {
644
+ this.#subs.set(commandName, []);
645
+ }
646
+ const handlers = this.#subs.get(commandName);
647
+ if (handlers.includes(handler)) {
648
+ return () => {};
649
+ }
650
+ handlers.push(handler);
651
+ return () => {
652
+ const idx = handlers.indexOf(handler);
653
+ handlers.splice(idx, 1);
654
+ };
564
655
  }
565
- function renderApp() {
566
- const newVdom = view(state, emit);
567
- vdom = patchDOM(vdom, newVdom, parentEl);
656
+ afterEveryCommand(handler) {
657
+ this.#afterHandlers.push(handler);
658
+ return () => {
659
+ const idx = this.#afterHandlers.indexOf(handler);
660
+ this.#afterHandlers.splice(idx, 1);
661
+ };
568
662
  }
569
- return {
570
- mount(_parentEl) {
571
- if (vdom) {
572
- throw new Error('App is already mounted');
663
+ dispatch(commandName, payload) {
664
+ if (this.#subs.has(commandName)) {
665
+ this.#subs.get(commandName).forEach((handler) => handler(payload));
666
+ }
667
+ else {
668
+ console.warn(`No handlers for command: ${commandName}`);
669
+ }
670
+ this.#afterHandlers.forEach((handler) => handler());
671
+ }
672
+ }
673
+
674
+ function defineComponent({ render, state, ...methods }) {
675
+ class Component {
676
+ #vdom = null;
677
+ #isMounted = false;
678
+ #hostEl = null;
679
+ #eventHandlers = null;
680
+ #parentComponent = null;
681
+ #dispatcher = new Dispatcher();
682
+ #subscriptions = [];
683
+ constructor(props = {}, eventHandlers = {}, parentComponent = null) {
684
+ this.props = props;
685
+ this.state = state ? state(props) : {};
686
+ this.#eventHandlers = eventHandlers;
687
+ this.#parentComponent = parentComponent;
688
+ }
689
+ updateState(newState) {
690
+ this.state = { ...this.state, ...newState };
691
+ this.#patch();
692
+ }
693
+ render() {
694
+ return render.call(this);
695
+ }
696
+ mount(hostEl, index = null) {
697
+ if (this.#isMounted) {
698
+ throw new Error("Component is already mounted");
573
699
  }
574
- parentEl = _parentEl;
575
- vdom = view(state, emit);
576
- mountDOM(vdom, parentEl);
577
- },
700
+ this.#vdom = this.render();
701
+ mountDOM(this.#vdom, hostEl, index, this);
702
+ this.#wireEventHandlers();
703
+ this.#hostEl = hostEl;
704
+ this.#isMounted = true;
705
+ }
578
706
  unmount() {
579
- destroyDOM(vdom);
580
- vdom = null;
581
- subscriptions.forEach((unsub) => unsub());
707
+ if (!this.#isMounted) {
708
+ throw new Error("Component is not mounted");
709
+ }
710
+ destroyDOM(this.#vdom);
711
+ this.#subscriptions.forEach((unsubscribe) => unsubscribe());
712
+ this.#subscriptions = [];
713
+ this.#vdom = null;
714
+ this.#hostEl = null;
715
+ this.#isMounted = false;
716
+ }
717
+ updateProps(props) {
718
+ const newProps = { ...this.props, ...props };
719
+ if (equal(this.props, newProps)) {
720
+ return;
721
+ }
722
+ this.props = newProps;
723
+ this.#patch();
724
+ }
725
+ emit(eventName, payload) {
726
+ this.#dispatcher.dispatch(eventName, payload);
727
+ }
728
+ #patch() {
729
+ if (!this.#isMounted) {
730
+ throw new Error("Component is not mounted");
731
+ }
732
+ const vdom = this.render();
733
+ this.#vdom = patchDOM(this.#vdom, vdom, this.#hostEl, this);
734
+ }
735
+ #wireEventHandlers() {
736
+ this.#subscriptions = Object.entries(this.#eventHandlers).map(
737
+ ([eventName, handler]) => this.#wireEventHandler(eventName, handler)
738
+ );
739
+ }
740
+ #wireEventHandler(eventName, handler) {
741
+ return this.#dispatcher.subscribe(eventName, (payload) => {
742
+ if (this.#parentComponent) {
743
+ handler.call(this.#parentComponent, payload);
744
+ } else {
745
+ handler(payload);
746
+ }
747
+ });
748
+ }
749
+ get elements() {
750
+ if (this.#vdom == null) {
751
+ return [];
752
+ }
753
+ if (this.#vdom.type === DOM_TYPES.FRAGMENT) {
754
+ return extractChildren(this.#vdom).flatMap((child) => {
755
+ if (child.type === DOM_TYPES.COMPONENT) {
756
+ return child.component.elements;
757
+ }
758
+ return [child.el];
759
+ });
760
+ }
761
+ return [this.#vdom.el];
762
+ }
763
+ get firstElement() {
764
+ return this.elements[0];
765
+ }
766
+ get offset() {
767
+ if (this.#vdom.type === DOM_TYPES.FRAGMENT) {
768
+ return Array.from(this.#hostEl.children).indexOf(this.firstElement);
769
+ }
770
+ return 0;
771
+ }
772
+ }
773
+ for(const methodName in methods) {
774
+ if (hasOwnProperty(Component, methodName)) {
775
+ throw new Error(`Method "${methodName}()" already exists in the component`);
582
776
  }
777
+ Component.prototype[methodName] = methods[methodName];
583
778
  }
779
+ return Component;
584
780
  }
585
781
 
586
- export { createApp, h, hFragment, hString };
782
+ export { createApp, defineComponent, h, hFragment, hString };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "betal-fe",
3
- "version": "2.1.0",
3
+ "version": "3.0.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
  }