cradova 3.14.0 → 3.15.1

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/index.js CHANGED
@@ -14,13 +14,13 @@ var makeElement = (element, ElementChildrenAndPropertyList) => {
14
14
  }
15
15
  if (Array.isArray(child)) {
16
16
  if (child[1] instanceof Signal) {
17
- child[1].notify([child[0]], () => {
17
+ child[1].computed(child[0], () => {
18
18
  element.innerHTML = "";
19
19
  element.appendChild(unroll_child_list([
20
- child[1].store[child[0]]
20
+ child[1].data[child[0]]
21
21
  ]));
22
22
  });
23
- element.appendChild(unroll_child_list([child[1].store[child[0]]]));
23
+ element.appendChild(unroll_child_list([child[1].data[child[0]]]));
24
24
  continue;
25
25
  }
26
26
  element.appendChild(unroll_child_list(child));
@@ -61,10 +61,10 @@ var makeElement = (element, ElementChildrenAndPropertyList) => {
61
61
  if (value.length === 2 && value[1] instanceof Signal && typeof value[0] === "string") {
62
62
  const eventName = value[0];
63
63
  const signalInstance = value[1];
64
- signalInstance.notify([eventName], () => {
65
- element.setAttribute(prop, signalInstance.store[eventName]);
64
+ signalInstance.computed([eventName], () => {
65
+ element.setAttribute(prop, signalInstance.data[eventName]);
66
66
  });
67
- element.setAttribute(prop, signalInstance.store[eventName]);
67
+ element.setAttribute(prop, signalInstance.data[eventName]);
68
68
  continue;
69
69
  }
70
70
  }
@@ -325,11 +325,8 @@ var compManager = {
325
325
  },
326
326
  recall(component) {
327
327
  if (component.rendered && component.published) {
328
- setTimeout(() => {
329
- this.activate(component);
330
- }, 0);
328
+ this.activate(component);
331
329
  }
332
- window.CradovaEvent.dispatchEvent("after_page_is_killed");
333
330
  window.CradovaEvent.dispatchEvent("after_comp_is_mounted");
334
331
  },
335
332
  activate(component) {
@@ -485,111 +482,65 @@ class cradovaEvent {
485
482
  }
486
483
  window.CradovaEvent = new cradovaEvent;
487
484
 
488
- class Store {
489
- $_internal_data;
490
- constructor(data, notifier) {
491
- this.$_internal_data = data;
492
- for (const key in this.$_internal_data) {
493
- if (this.$_internal_data.hasOwnProperty(key)) {
494
- Object.defineProperty(this, key, {
495
- get() {
496
- return this.$_internal_data[key];
497
- },
498
- set(value) {
499
- this.$_internal_data[key] = value;
500
- notifier(key, value);
501
- },
502
- enumerable: true,
503
- configurable: true
504
- });
505
- }
506
- }
507
- }
508
- _set(data) {
509
- this.$_internal_data = data;
510
- return Object.keys(this.$_internal_data);
511
- }
512
- }
513
-
514
- class SilentStore {
515
- $store;
516
- constructor(store) {
517
- this.$store = store;
518
- for (const key in this.$store.$_internal_data) {
519
- if (this.$store.$_internal_data.hasOwnProperty(key)) {
520
- Object.defineProperty(this, key, {
521
- set(value) {
522
- this.$store.$_internal_data[key] = value;
523
- },
524
- enumerable: true,
525
- configurable: true
526
- });
527
- }
528
- }
529
- }
530
- }
531
-
532
485
  class Signal {
533
486
  pn;
534
487
  subscribers = {};
535
- store;
536
- silentStore = undefined;
488
+ data = {};
489
+ picker = {};
490
+ soft = {};
537
491
  passers;
492
+ queue = new Set;
538
493
  constructor(initial, props) {
539
494
  if (!initial || typeof initial !== "object" || Array.isArray(initial)) {
540
495
  throw new Error("Initial signal value must be an object");
541
496
  }
542
- this.store = new Store(initial, (key) => {
543
- this.publish(key);
544
- });
545
- this.silentStore = new SilentStore(this.store);
497
+ this.picker = initial;
546
498
  if (props && props.persistName) {
547
499
  this.pn = props.persistName;
548
500
  const key = localStorage.getItem(props.persistName);
549
501
  if (key && key !== "undefined") {
550
502
  const restored = JSON.parse(key);
551
- this.store = new Store(Object.assign(initial, restored), (key2) => {
552
- this.publish(key2);
553
- });
554
- this.silentStore = new SilentStore(this.store);
503
+ this.picker = Object.assign(initial, restored);
555
504
  }
556
505
  }
557
- }
558
- publish(eventName) {
559
- this.subscribers[eventName]?.forEach((c) => {
560
- if (c.published) {
561
- compManager.recall(c);
562
- } else {
563
- c();
564
- }
565
- });
566
- this.subscribers["__ALL__"]?.forEach((c) => {
567
- if (c.published) {
568
- compManager.recall(c);
569
- } else {
570
- c();
571
- }
572
- });
573
- if (this.pn) {
574
- localStorage.setItem(this.pn, JSON.stringify(this.store));
506
+ for (const key in this.picker) {
507
+ Object.defineProperty(this.data, key, {
508
+ set: (value) => {
509
+ this.picker[key] = value;
510
+ this.queue.add(key);
511
+ queueMicrotask(() => this.queue.size && this.publish());
512
+ },
513
+ get: () => this.picker[key],
514
+ enumerable: true,
515
+ configurable: true
516
+ });
517
+ Object.defineProperty(this.soft, key, {
518
+ set: (value) => {
519
+ this.picker[key] = value;
520
+ },
521
+ enumerable: true,
522
+ configurable: true
523
+ });
575
524
  }
576
525
  }
577
- set(NEW) {
526
+ publish() {
578
527
  const s = new Set;
579
- const events = this.store._set(NEW);
580
- for (const event of events) {
581
- const subs2 = this.subscribers[event];
582
- if (subs2) {
583
- for (const fn of subs2) {
584
- s.add(fn);
528
+ this.queue.add("__ALL__");
529
+ for (const k of this.queue) {
530
+ if (this.picker.hasOwnProperty(k)) {
531
+ const subs2 = this.subscribers[k];
532
+ if (subs2) {
533
+ for (const fn of subs2) {
534
+ s.add(fn);
535
+ }
536
+ }
537
+ } else {
538
+ if (k !== "__ALL__") {
539
+ delete this.subscribers[k];
585
540
  }
586
541
  }
587
542
  }
588
- if (this.subscribers["__ALL__"]) {
589
- for (const fn of this.subscribers["__ALL__"]) {
590
- s.add(fn);
591
- }
592
- }
543
+ this.queue.clear();
593
544
  for (const c of s.values()) {
594
545
  if (c.published) {
595
546
  compManager.recall(c);
@@ -598,89 +549,44 @@ class Signal {
598
549
  }
599
550
  }
600
551
  if (this.pn) {
601
- localStorage.setItem(this.pn, JSON.stringify(this.store));
552
+ localStorage.setItem(this.pn, JSON.stringify(this.picker));
602
553
  }
603
554
  }
604
- notify(eventName, listener) {
555
+ set(data) {
556
+ Object.assign(this.picker, data);
557
+ this.queue = new Set(Object.keys(this.subscribers));
558
+ queueMicrotask(() => this.publish());
559
+ }
560
+ computed(eventName, listener) {
605
561
  if (!eventName) {
606
562
  console.error(` ✘ Cradova err: eventName ${String(eventName)} or listener ${String(listener)} is not a valid event name or function`);
607
563
  return;
608
564
  }
609
565
  if (typeof eventName === "function") {
610
566
  listener = eventName;
611
- eventName = Object.keys(this.store);
612
- }
613
- if (typeof listener !== "function" || !eventName) {
614
- console.error(` ✘ Cradova err: listener or eventName ${String(listener)} is not a valid listener function or string`);
615
- return;
616
- }
617
- if (Array.isArray(eventName)) {
618
- eventName.forEach((en) => {
619
- this.notify(en, listener);
620
- });
621
- return;
567
+ eventName = "__ALL__";
622
568
  }
623
569
  if (!this.subscribers[eventName]) {
624
570
  this.subscribers[eventName] = [];
625
571
  }
626
- if (!isArrowFunc(listener)) {
627
- listener = toCompNoRender(listener);
628
- }
629
- this.subscribers[eventName].push(listener);
630
- }
631
- computed(eventName, element) {
632
- if (!eventName) {
633
- console.error(` ✘ Cradova err: eventName ${String(eventName)} or element ${String(element)} is not a valid event name or function`);
634
- return;
635
- }
636
- if (typeof eventName === "function") {
637
- element = eventName;
638
- eventName = "__ALL__";
639
- }
640
- const isComp = !isArrowFunc(element);
641
- let el;
572
+ const isComp = !isArrowFunc(listener);
642
573
  if (isComp) {
643
- el = toComp(element);
644
- } else {
645
- el = element?.();
646
- }
647
- if (el === undefined || !(el instanceof HTMLElement)) {
648
- console.error(` ✘ Cradova err: ${String(element)} is not a valid element or function`);
649
- return;
650
- }
651
- const listener = () => {
652
- if (!document.body.contains(listener.element)) {
653
- listener.element?.remove();
654
- this.subscribers[eventName] = this.subscribers[eventName].filter((f) => listener.idx !== f.idx);
574
+ const el = toComp(listener);
575
+ if (el === undefined || !(el instanceof HTMLElement)) {
576
+ console.error(` ✘ Cradova err: ${String(listener)} is not a valid element or function`);
655
577
  return;
656
578
  }
657
- let newEl;
658
- if (isComp) {
659
- newEl = toComp(element);
660
- } else {
661
- newEl = element?.();
662
- }
663
- if (newEl === undefined || !(newEl instanceof HTMLElement)) {
664
- console.error(` ✘ Cradova err: ${String(element)} is not a valid element or function`);
665
- return;
666
- }
667
- listener.element.insertAdjacentElement("beforebegin", newEl);
668
- listener.element.remove();
669
- listener.element = newEl;
670
- };
671
- listener.element = el;
672
- if (!this.subscribers[eventName]) {
673
- this.subscribers[eventName] = [];
579
+ this.subscribers[eventName].push(listener);
580
+ return el;
674
581
  }
675
- listener.idx = this.subscribers[eventName].length;
676
582
  this.subscribers[eventName].push(listener);
677
- return el;
583
+ return;
678
584
  }
679
585
  get pass() {
680
586
  if (this.passers) {
681
587
  return this.passers;
682
588
  }
683
- const keys = Object.keys(this.store.$_internal_data);
589
+ const keys = Object.keys(this.picker);
684
590
  this.passers = {};
685
591
  for (const key of keys) {
686
592
  this.passers[key] = [key, this];
@@ -694,6 +600,203 @@ class Signal {
694
600
  }
695
601
  }
696
602
 
603
+ class List {
604
+ state;
605
+ item;
606
+ length;
607
+ itemHeight = 35;
608
+ windowCoverage = 500;
609
+ overscan = 20;
610
+ scrollingDirection = "vertical";
611
+ opts;
612
+ columns = 1;
613
+ container;
614
+ rendered = false;
615
+ subscribers = [];
616
+ scrollPos = 0;
617
+ list;
618
+ startIndex = 0;
619
+ listContainer;
620
+ constructor(state, item, opts) {
621
+ this.state = state;
622
+ this.item = item || ((item2, i2) => div(String(item2) + " " + i2));
623
+ this.length = state.length;
624
+ this.opts = opts;
625
+ this.itemHeight = opts?.itemHeight || 35;
626
+ this.columns = opts?.columns || 1;
627
+ this.windowCoverage = opts?.windowHeight || opts?.windowWidth || 500;
628
+ this.overscan = opts?.overscan || 20;
629
+ this.scrollingDirection = opts?.scrollingDirection || "vertical";
630
+ this.container = div({
631
+ className: this.opts?.className,
632
+ onscroll: (e) => {
633
+ this.scrollPos = Math.floor(this.scrollingDirection === "vertical" ? e.target.scrollTop : e.target.scrollLeft);
634
+ requestAnimationFrame(() => this.render());
635
+ },
636
+ style: {
637
+ overflowY: this.scrollingDirection === "vertical" ? "scroll" : "hidden",
638
+ overflowX: this.scrollingDirection === "horizontal" ? "scroll" : "hidden",
639
+ height: this.opts?.windowHeight ? `${this.opts?.windowHeight}px` : "500px",
640
+ width: this.opts?.windowWidth ? `${this.opts?.windowWidth}px` : "100%"
641
+ }
642
+ }, div({
643
+ id: "listContainer",
644
+ style: {
645
+ height: `${Math.round(this.length * this.itemHeight / this.columns)}px`
646
+ }
647
+ }, div({
648
+ id: "list",
649
+ className: this.opts?.className,
650
+ style: {
651
+ transform: this.scrollingDirection === "vertical" ? `translateY(${this.scrollPos}px)` : `translateX(${this.scrollPos}px)`
652
+ }
653
+ })));
654
+ this.listContainer = this.container.querySelector("#listContainer");
655
+ this.list = this.container.querySelector("#list");
656
+ }
657
+ get Element() {
658
+ if (!this.rendered) {
659
+ this.render();
660
+ this.rendered = true;
661
+ const relativeScrolling = () => {
662
+ const rect = this.container.getBoundingClientRect();
663
+ if (rect.top < 0 && rect.bottom > window.innerHeight) {
664
+ this.scrollPos = Math.abs(rect.top);
665
+ requestAnimationFrame(() => this.render());
666
+ }
667
+ };
668
+ window.addEventListener("scroll", relativeScrolling);
669
+ window.CradovaEvent.after_page_is_killed.push(() => {
670
+ window.removeEventListener("scroll", relativeScrolling);
671
+ });
672
+ }
673
+ return this.container;
674
+ }
675
+ render() {
676
+ const startIndex = Math.floor(this.scrollPos / this.itemHeight) * this.columns;
677
+ this.list.style.transform = this.scrollingDirection === "vertical" ? `translateY(${Math.floor(this.scrollPos / this.itemHeight) * this.itemHeight}px)` : `translateX(${Math.floor(this.scrollPos / this.itemHeight) * this.itemHeight}px)`;
678
+ let renderedNodesCount = (Math.ceil(this.windowCoverage / this.itemHeight) + this.overscan) * this.columns;
679
+ renderedNodesCount = Math.min(this.length - startIndex, renderedNodesCount);
680
+ for (;this.list.firstElementChild; )
681
+ this.list.firstElementChild.remove();
682
+ let index = 0;
683
+ for (let i2 = 0;i2 < renderedNodesCount; i2++) {
684
+ index = i2 + startIndex;
685
+ if (this.state[index]) {
686
+ this.list.appendChild(this.item(this.state[index], index));
687
+ }
688
+ }
689
+ if (index + 1 === this.length) {
690
+ this.opts?.onScrollEnd?.();
691
+ }
692
+ this.startIndex = startIndex;
693
+ }
694
+ computed(listener) {
695
+ if (!listener) {
696
+ console.error(` ✘ Cradova err: listener ${String(listener)} is not a valid event name or function`);
697
+ return;
698
+ }
699
+ if (!this.subscribers) {
700
+ this.subscribers = [];
701
+ }
702
+ const isComp = !isArrowFunc(listener);
703
+ if (isComp) {
704
+ const el = toComp(listener);
705
+ if (el === undefined || !(el instanceof HTMLElement)) {
706
+ console.error(` ✘ Cradova err: ${String(listener)} is not a valid element or function`);
707
+ return;
708
+ }
709
+ this.subscribers.push(listener);
710
+ return el;
711
+ }
712
+ this.subscribers.push(listener);
713
+ return;
714
+ }
715
+ diffDOMBeforeUpdatingState(newState) {
716
+ this.length = newState.length;
717
+ let startIndex = Math.floor(this.scrollPos / this.itemHeight) * this.columns;
718
+ startIndex = Math.floor(startIndex / this.columns) * this.columns;
719
+ let renderedNodesCount = this.list.childElementCount;
720
+ if (renderedNodesCount < this.overscan) {
721
+ this.state = newState;
722
+ this.render();
723
+ } else {
724
+ for (let i2 = 0;i2 < renderedNodesCount; i2++) {
725
+ const index = i2 + startIndex;
726
+ if (newState[index] === undefined) {
727
+ this.list.children[index]?.remove();
728
+ continue;
729
+ }
730
+ const item = this.item(newState[index], index);
731
+ if (this.list.children[index]) {
732
+ this.list.replaceChild(item, this.list.children[index]);
733
+ } else {
734
+ this.list.appendChild(item);
735
+ }
736
+ }
737
+ this.list.style.transform = this.scrollingDirection === "vertical" ? `translateY(${Math.floor(this.scrollPos / this.itemHeight) * this.itemHeight}px)` : `translateX(${Math.floor(this.scrollPos / this.itemHeight) * this.itemHeight}px)`;
738
+ this.state = newState;
739
+ }
740
+ if (this.length !== newState.length) {
741
+ this.listContainer.style.height = `${Math.round(this.length * this.itemHeight / this.columns)}px`;
742
+ }
743
+ queueMicrotask(() => {
744
+ this.subscribers.forEach((sub) => {
745
+ const isComp = !isArrowFunc(sub);
746
+ if (isComp) {
747
+ compManager.recall(sub);
748
+ } else {
749
+ sub?.();
750
+ }
751
+ });
752
+ });
753
+ }
754
+ get data() {
755
+ return {
756
+ [Symbol.iterator]: () => {
757
+ return this.state[Symbol.iterator]();
758
+ },
759
+ next: () => {
760
+ return this.state[Symbol.iterator]().next();
761
+ }
762
+ };
763
+ }
764
+ get(index) {
765
+ return this.state[index];
766
+ }
767
+ indexOf(item) {
768
+ return this.state.indexOf(item);
769
+ }
770
+ update(index, newItemData) {
771
+ const newState = [...this.state];
772
+ if (index >= 0 && index < this.state.length && this.state[index] !== newItemData) {
773
+ newState[index] = newItemData;
774
+ }
775
+ this.diffDOMBeforeUpdatingState(newState);
776
+ }
777
+ push(itemData) {
778
+ const newState = [...this.state];
779
+ newState.push(itemData);
780
+ this.diffDOMBeforeUpdatingState(newState);
781
+ }
782
+ map(callback) {
783
+ const newState = [...this.state];
784
+ return newState.map(callback);
785
+ }
786
+ splice(index, count = 1, ...items) {
787
+ const newState = [...this.state];
788
+ if (index >= 0 && index < this.state.length && count > 0) {
789
+ newState.splice(index, count, ...items);
790
+ }
791
+ this.diffDOMBeforeUpdatingState(newState);
792
+ }
793
+ set(newData) {
794
+ const newState = [...this.state];
795
+ this.state = newData instanceof Function ? newData(this.state) : newData || [];
796
+ this.diffDOMBeforeUpdatingState(newState);
797
+ }
798
+ }
799
+
697
800
  class Page {
698
801
  _name;
699
802
  _html;
@@ -752,6 +855,9 @@ class RouterBoxClass {
752
855
  window.location.hash = "paused";
753
856
  return;
754
857
  }
858
+ if (url === this.lastNavigatedRoute) {
859
+ return;
860
+ }
755
861
  if (this.nextRouteController) {
756
862
  route = this.nextRouteController;
757
863
  this.nextRouteController = undefined;
@@ -954,231 +1060,6 @@ class RefInstance {
954
1060
  return [this, name];
955
1061
  }
956
1062
  }
957
-
958
- class List {
959
- state;
960
- item;
961
- length;
962
- options;
963
- renderingRange;
964
- container;
965
- firstItemIndex = 0;
966
- lastItemIndex = 0;
967
- rendered = false;
968
- subscribers = [];
969
- constructor(state, item, options) {
970
- this.state = state;
971
- this.item = item || ((item2) => div(String(item2)));
972
- this.length = state.length;
973
- this.options = options;
974
- this.renderingRange = Math.round(Math.min(this.length > 50 ? this.length * 0.5 : this.length, window.innerHeight / (this.options?.itemHeight || 1)));
975
- this.lastItemIndex = this.renderingRange - 1;
976
- this.container = document.createElement("div");
977
- if (this.options?.className) {
978
- this.container.className = this.options?.className;
979
- }
980
- if (this.options?.id) {
981
- this.container.id = this.options?.id;
982
- }
983
- }
984
- get Element() {
985
- if (this.rendered) {
986
- return this.container;
987
- }
988
- for (let i2 = 0;i2 < this.renderingRange; i2++) {
989
- const item = this.item(this.state[i2]);
990
- item.setAttribute("data-index", i2.toString());
991
- this.container.appendChild(item);
992
- }
993
- this.rendered = true;
994
- const domObser = () => {
995
- const observer = new IntersectionObserver((entries) => {
996
- entries.forEach((entry) => {
997
- if (entry.isIntersecting) {
998
- const isBottom = entry.target === this.container.lastElementChild;
999
- const isTop = !isBottom;
1000
- observer.unobserve(entry.target);
1001
- const index = Number(entry.target.getAttribute("data-index"));
1002
- if (isBottom) {
1003
- for (let i2 = index + 1;i2 < this.length; i2++) {
1004
- const item = this.item(this.state[i2]);
1005
- item.setAttribute("data-index", i2.toString());
1006
- this.container.appendChild(item);
1007
- }
1008
- for (let i2 = index - this.renderingRange;i2 > 0; i2--) {
1009
- this.container.removeChild(this.container.children[i2]);
1010
- }
1011
- this.firstItemIndex = Number(this.container.firstElementChild?.getAttribute("data-index") || 0);
1012
- this.lastItemIndex = Number(this.container.lastElementChild?.getAttribute("data-index") || 0);
1013
- }
1014
- if (isTop) {
1015
- for (let i2 = index - 1;i2 > 0; i2--) {
1016
- const item = this.item(this.state[i2]);
1017
- item.setAttribute("data-index", i2.toString());
1018
- this.container.appendChild(item);
1019
- }
1020
- for (let i2 = index + this.renderingRange;i2 < this.length; i2++) {
1021
- this.container.removeChild(this.container.children[i2]);
1022
- }
1023
- this.lastItemIndex = Number(this.container.lastElementChild?.getAttribute("data-index") || 0);
1024
- this.firstItemIndex = Number(this.container.firstElementChild?.getAttribute("data-index") || 0);
1025
- }
1026
- }
1027
- });
1028
- });
1029
- observer.observe(this.container.lastElementChild);
1030
- observer.observe(this.container.firstElementChild);
1031
- };
1032
- window.addEventListener("scroll", domObser);
1033
- window.CradovaEvent.after_page_is_killed.push(() => {
1034
- window.removeEventListener("scroll", domObser);
1035
- });
1036
- return this.container;
1037
- }
1038
- computed(element) {
1039
- if (!element) {
1040
- console.error(` ✘ Cradova err: element ${String(element)} is not a valid element or function`);
1041
- return;
1042
- }
1043
- const isComp = !isArrowFunc(element);
1044
- let el;
1045
- if (isComp) {
1046
- el = toComp(element);
1047
- } else {
1048
- el = element?.();
1049
- }
1050
- if (el === undefined || !(el instanceof HTMLElement)) {
1051
- console.error(` ✘ Cradova err: ${String(element)} is not a valid element or function`);
1052
- return;
1053
- }
1054
- const listener = () => {
1055
- if (!document.body.contains(listener.element)) {
1056
- listener.element?.remove();
1057
- this.subscribers.filter((f) => listener.idx !== f.idx);
1058
- return;
1059
- }
1060
- let newEl;
1061
- if (isComp) {
1062
- newEl = toComp(element);
1063
- } else {
1064
- newEl = element?.();
1065
- }
1066
- if (newEl === undefined || !(newEl instanceof HTMLElement)) {
1067
- console.error(` ✘ Cradova err: ${String(element)} is not a valid element or function`);
1068
- return;
1069
- }
1070
- listener.element.insertAdjacentElement("beforebegin", newEl);
1071
- listener.element.remove();
1072
- listener.element = newEl;
1073
- };
1074
- listener.element = el;
1075
- listener.idx = this.subscribers.length;
1076
- this.subscribers.push(listener);
1077
- return el;
1078
- }
1079
- diffDOMBeforeUpdatingState(newState) {
1080
- this.length = newState.length;
1081
- this.renderingRange = Math.round(Math.min(this.length > 100 ? this.length * 0.5 : this.length, window.innerHeight / (this.options?.itemHeight || 1)));
1082
- this.lastItemIndex = this.firstItemIndex + this.renderingRange;
1083
- for (let i2 = this.lastItemIndex;i2 >= this.firstItemIndex; i2--) {
1084
- if ((this.state[i2] === undefined || newState[i2] === undefined) && this.container.children[i2] !== undefined) {
1085
- this.container.removeChild(this.container.children[i2]);
1086
- continue;
1087
- }
1088
- if (JSON.stringify(this.state[i2]) === JSON.stringify(newState[i2])) {
1089
- continue;
1090
- }
1091
- const item = this.item(newState[i2]);
1092
- item.setAttribute("data-index", i2.toString());
1093
- if (this.container.children[i2]) {
1094
- this.container.replaceChild(item, this.container.children[i2]);
1095
- } else {
1096
- this.container.appendChild(item);
1097
- }
1098
- }
1099
- this.lastItemIndex = Number(this.container.lastElementChild?.getAttribute("data-index") || 0);
1100
- this.firstItemIndex = Number(this.container.firstElementChild?.getAttribute("data-index") || 0);
1101
- this.state = newState;
1102
- this.subscribers.forEach((sub) => {
1103
- const isComp = !isArrowFunc(sub);
1104
- if (isComp) {
1105
- compManager.recall(sub);
1106
- } else {
1107
- sub?.();
1108
- }
1109
- });
1110
- }
1111
- get data() {
1112
- return {
1113
- [Symbol.iterator]: () => {
1114
- return this.state[Symbol.iterator]();
1115
- },
1116
- next: () => {
1117
- return this.state[Symbol.iterator]().next();
1118
- }
1119
- };
1120
- }
1121
- get(index) {
1122
- return this.state[index];
1123
- }
1124
- indexOf(item) {
1125
- return this.state.indexOf(item);
1126
- }
1127
- update(index, newItemData) {
1128
- const newState = [...this.state];
1129
- if (index >= 0 && index < this.state.length && this.state[index] !== newItemData) {
1130
- newState[index] = newItemData;
1131
- }
1132
- this.diffDOMBeforeUpdatingState(newState);
1133
- }
1134
- push(itemData) {
1135
- const newState = [...this.state];
1136
- newState.push(itemData);
1137
- this.diffDOMBeforeUpdatingState(newState);
1138
- }
1139
- map(callback) {
1140
- const newState = [...this.state];
1141
- return newState.map(callback);
1142
- }
1143
- splice(index, count = 1, ...items) {
1144
- const newState = [...this.state];
1145
- if (index >= 0 && index < this.state.length && count > 0) {
1146
- newState.splice(index, count, ...items);
1147
- }
1148
- this.diffDOMBeforeUpdatingState(newState);
1149
- }
1150
- set(newData) {
1151
- const newState = [...this.state];
1152
- this.state = newData instanceof Function ? newData(this.state) : newData || [];
1153
- this.diffDOMBeforeUpdatingState(newState);
1154
- }
1155
- destroy() {
1156
- this.container.remove();
1157
- this.container = null;
1158
- this.state.length = 0;
1159
- this.state = null;
1160
- this.item = null;
1161
- this.length = 0;
1162
- this.options = null;
1163
- this.renderingRange = 0;
1164
- this.firstItemIndex = 0;
1165
- this.lastItemIndex = 0;
1166
- }
1167
- notify(listener) {
1168
- if (!listener) {
1169
- console.error(` ✘ Cradova err: listener ${String(listener)} is not a valid listener function or string`);
1170
- return;
1171
- }
1172
- if (typeof listener !== "function") {
1173
- console.error(` ✘ Cradova err: listener or eventName ${String(listener)} is not a valid listener function or string`);
1174
- return;
1175
- }
1176
- if (!isArrowFunc(listener)) {
1177
- listener = toCompNoRender(listener);
1178
- }
1179
- this.subscribers.push(listener);
1180
- }
1181
- }
1182
1063
  export {
1183
1064
  video,
1184
1065
  useExternalEffect,
@@ -10,28 +10,28 @@ import type { browserPageType, Comp, CradovaPageType } from "./types.js";
10
10
  * @constructor initial: Record<string, any>, props: {persist}
11
11
  */
12
12
  export declare class Signal<Type extends Record<string, any> = any> {
13
- store: Type;
14
- silentStore: Type extends Array<any> ? never : Type;
13
+ data: Type;
14
+ soft: Type;
15
15
  passers?: Record<keyof Type, [string, Signal<Type>]>;
16
+ private queue;
16
17
  constructor(initial: Type, props?: {
17
18
  persistName?: string | undefined;
18
19
  });
19
20
  /**
20
21
  * Cradova Signal
21
22
  * ----
22
- * fires actions if any available
23
+ * publish events to subscribers
23
24
  */
24
- set(NEW: Type): void;
25
+ set(data: Type): void;
25
26
  /**
26
27
  * Cradova Signal
27
28
  * ----
28
- * subscribe to an event
29
+ * subscribe a function, component to an event
29
30
  *
30
- * @param name of event.
31
- * @param callback function to call.
31
+ * @param eventName of event.
32
+ * @param element to render.
32
33
  */
33
- notify<T extends keyof Type>(eventName: (T | "dataChanged" | "itemUpdated" | T[]) | (() => HTMLElement | void) | Comp | ((ctx: Comp) => HTMLElement), listener?: (() => void) | Comp | ((ctx: Comp) => HTMLElement)): void;
34
- computed<T extends keyof Type>(eventName: (T | "dataChanged" | "itemUpdated") | (() => HTMLElement) | Comp | ((ctx: Comp) => HTMLElement), element?: (() => HTMLElement) | Comp | ((ctx: Comp) => HTMLElement)): HTMLElement | undefined;
34
+ computed<T extends keyof Type>(eventName: T | "__ALL__" | (() => void) | Comp | ((ctx: Comp) => HTMLElement), listener?: (() => void) | Comp | ((ctx: Comp) => HTMLElement)): HTMLElement | undefined;
35
35
  /**
36
36
  * Cradova Signal
37
37
  * ----
@@ -45,6 +45,47 @@ export declare class Signal<Type extends Record<string, any> = any> {
45
45
  */
46
46
  clearPersist(): void;
47
47
  }
48
+ /**
49
+ * Cradova List
50
+ * ---
51
+ * A virtual list store and component for efficient rendering of large lists.
52
+ */
53
+ export declare class List<T> {
54
+ length: number;
55
+ private windowCoverage;
56
+ private overscan;
57
+ private scrollingDirection;
58
+ private opts?;
59
+ private columns;
60
+ private rendered;
61
+ subscribers: Function[];
62
+ scrollPos: number;
63
+ list: HTMLElement;
64
+ startIndex: number;
65
+ listContainer: HTMLElement;
66
+ constructor(state: T[], item?: (item: T, i: number) => HTMLElement, opts?: {
67
+ itemHeight?: number;
68
+ className?: string;
69
+ columns?: number;
70
+ windowHeight?: number;
71
+ windowWidth?: number;
72
+ overscan?: number;
73
+ scrollingDirection?: "vertical" | "horizontal";
74
+ onScrollEnd?: () => void;
75
+ });
76
+ get Element(): HTMLElement;
77
+ private render;
78
+ computed(listener?: (() => void) | Comp | ((ctx: Comp) => HTMLElement)): HTMLElement | undefined;
79
+ private diffDOMBeforeUpdatingState;
80
+ get data(): IterableIterator<T>;
81
+ get(index: number): T;
82
+ indexOf(item: T): number;
83
+ update(index: number, newItemData: T): void;
84
+ push(itemData: T): void;
85
+ map<K>(callback: (item: T, index: number) => K): K[];
86
+ splice(index: number, count?: number, ...items: T[]): void;
87
+ set(newData: T[] | ((prevItem: T[]) => T[])): void;
88
+ }
48
89
  /**
49
90
  * Cradova Page
50
91
  * ---
@@ -107,10 +148,7 @@ export declare class Router {
107
148
  /**
108
149
  * Cradova Router
109
150
  * ------
110
- *
111
151
  * return last set router params
112
- *
113
- * .
114
152
  */
115
153
  static get PageData(): {
116
154
  params: Record<string, string>;
@@ -141,39 +179,3 @@ export declare class RefInstance<T = unknown> {
141
179
  */
142
180
  bind(name: string): [RefInstance<T>, string];
143
181
  }
144
- /**
145
- * Cradova List
146
- * ---
147
- * A virtual list store and component for efficient rendering of large lists.
148
- */
149
- export declare class List<T> {
150
- length: number;
151
- private rendered;
152
- subscribers: Function[];
153
- constructor(state: T[], item?: (item: T) => HTMLElement, options?: {
154
- itemHeight: number;
155
- className?: string;
156
- id?: string;
157
- });
158
- get Element(): HTMLElement;
159
- computed<T extends keyof List<T>>(element?: (() => HTMLElement) | Comp | ((ctx: Comp) => HTMLElement)): HTMLElement | undefined;
160
- private diffDOMBeforeUpdatingState;
161
- get data(): IterableIterator<T>;
162
- get(index: number): T;
163
- indexOf(item: T): number;
164
- update(index: number, newItemData: T): void;
165
- push(itemData: T): void;
166
- map<K>(callback: (item: T, index: number) => K): K[];
167
- splice(index: number, count?: number, ...items: T[]): void;
168
- set(newData: T[] | ((prevItem: T[]) => T[])): void;
169
- destroy(): void;
170
- /**
171
- * Cradova Signal
172
- * ----
173
- * subscribe to an event
174
- *
175
- * @param name of event.
176
- * @param callback function to call.
177
- */
178
- notify<T extends keyof List<T>>(listener?: (() => void) | Comp | ((ctx: Comp) => HTMLElement)): void;
179
- }
@@ -2,11 +2,10 @@ import * as CSS from "csstype";
2
2
  import { Page, RefInstance, Signal } from "./classes.js";
3
3
  interface Attributes<E extends HTMLElement> {
4
4
  ref?: [RefInstance<any>, string];
5
- value?: any;
6
5
  style?: Partial<CSS.Properties>;
7
- [key: `data-${string}`]: string | undefined;
8
- [key: `aria-${string}`]: string | undefined;
9
- [key: `on${string}`]: ((this: E, event: StandardEvents) => void) | undefined;
6
+ [key: `data-${string}`]: string;
7
+ [key: `aria-${string}`]: string;
8
+ [key: `on${string}`]: (this: E, event: StandardEvents) => void;
10
9
  }
11
10
  type StandardEvents = KeyboardEvent | MouseEvent | TouchEvent | WheelEvent | DragEvent | ClipboardEvent | CompositionEvent | FocusEvent | InputEvent | AnimationEvent | TransitionEvent | Event;
12
11
  type OmitFunctions<E> = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cradova",
3
- "version": "3.14.0",
3
+ "version": "3.15.1",
4
4
  "description": "Build Powerful ⚡ Web Apps with Ease",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -55,7 +55,8 @@
55
55
  },
56
56
  "homepage": "https://github.com/CodeDynasty-dev/cradova",
57
57
  "dependencies": {
58
- "csstype": "^3.1.3"
58
+ "csstype": "^3.1.3",
59
+ "virtualized-list": "^2.2.0"
59
60
  },
60
61
  "docmach": {
61
62
  "docs-directory": "docs/docs",