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 +261 -380
- package/dist/primitives/classes.d.ts +50 -48
- package/dist/primitives/types.d.ts +3 -4
- package/package.json +3 -2
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].
|
|
17
|
+
child[1].computed(child[0], () => {
|
|
18
18
|
element.innerHTML = "";
|
|
19
19
|
element.appendChild(unroll_child_list([
|
|
20
|
-
child[1].
|
|
20
|
+
child[1].data[child[0]]
|
|
21
21
|
]));
|
|
22
22
|
});
|
|
23
|
-
element.appendChild(unroll_child_list([child[1].
|
|
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.
|
|
65
|
-
element.setAttribute(prop, signalInstance.
|
|
64
|
+
signalInstance.computed([eventName], () => {
|
|
65
|
+
element.setAttribute(prop, signalInstance.data[eventName]);
|
|
66
66
|
});
|
|
67
|
-
element.setAttribute(prop, signalInstance.
|
|
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
|
-
|
|
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
|
-
|
|
536
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
526
|
+
publish() {
|
|
578
527
|
const s = new Set;
|
|
579
|
-
|
|
580
|
-
for (const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
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
|
-
|
|
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.
|
|
552
|
+
localStorage.setItem(this.pn, JSON.stringify(this.picker));
|
|
602
553
|
}
|
|
603
554
|
}
|
|
604
|
-
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
658
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
*
|
|
23
|
+
* publish events to subscribers
|
|
23
24
|
*/
|
|
24
|
-
set(
|
|
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
|
|
31
|
-
* @param
|
|
31
|
+
* @param eventName of event.
|
|
32
|
+
* @param element to render.
|
|
32
33
|
*/
|
|
33
|
-
|
|
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
|
|
8
|
-
[key: `aria-${string}`]: string
|
|
9
|
-
[key: `on${string}`]: (
|
|
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.
|
|
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",
|