ziko 2.0.0-beta.15.1 → 2.0.0-beta.16.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/ziko.cjs +1178 -329
- package/dist/ziko.js +1178 -329
- package/dist/ziko.min.js +6 -5
- package/dist/ziko.min.js.map +1 -0
- package/dist/ziko.mjs +1158 -327
- package/package.json +6 -4
- package/src/components/index.js +1 -1
- package/src/dom/UIElement/index.js +168 -74
- package/src/dom/tags/index.js +8 -6
- package/src/index.d.ts +1 -0
- package/src/index.js +3 -1
package/dist/ziko.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
|
-
Project: ziko
|
|
3
|
+
Project: ziko
|
|
4
4
|
Author: Zakaria Elalaoui
|
|
5
|
-
Date
|
|
6
|
-
Git-Repo : https://github.com/
|
|
7
|
-
Git-Wiki : https://github.com/
|
|
5
|
+
Date: 2026-10-05
|
|
6
|
+
Git-Repo : https://github.com/zikojs/ziko
|
|
7
|
+
Git-Wiki : https://github.com/zikojs/ziko/wiki
|
|
8
8
|
Released under MIT License
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -44,7 +44,7 @@ const apply_fun = (x, fn) => {
|
|
|
44
44
|
x.cols,
|
|
45
45
|
x.arr.flat(1).map(fn)
|
|
46
46
|
)
|
|
47
|
-
if (x instanceof Array) mapfun(fn, ...x)
|
|
47
|
+
if (x instanceof Array) return mapfun(fn, ...x)
|
|
48
48
|
return fn(x)
|
|
49
49
|
};
|
|
50
50
|
|
|
@@ -63,7 +63,7 @@ const clamp = (x, min, max) => apply_fun(
|
|
|
63
63
|
x,
|
|
64
64
|
v => Math.min(Math.max(v, min), max)
|
|
65
65
|
);
|
|
66
|
-
const map
|
|
66
|
+
const map = (x, a, b, c, d) => apply_fun(
|
|
67
67
|
x,
|
|
68
68
|
v => lerp(norm(v, a, b), c, d)
|
|
69
69
|
);
|
|
@@ -106,7 +106,7 @@ class UINode {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
function parseQueryParams(queryString) {
|
|
109
|
+
function parseQueryParams$1(queryString) {
|
|
110
110
|
const params = {};
|
|
111
111
|
queryString.replace(/[A-Z0-9]+?=([\w|:|\/\.]*)/gi, (match) => {
|
|
112
112
|
const [key, value] = match.split('=');
|
|
@@ -119,7 +119,7 @@ function defineParamsGetter(target ){
|
|
|
119
119
|
Object.defineProperties(target, {
|
|
120
120
|
'QueryParams': {
|
|
121
121
|
get: function() {
|
|
122
|
-
return parseQueryParams(globalThis.location.search.substring(1));
|
|
122
|
+
return parseQueryParams$1(globalThis.location.search.substring(1));
|
|
123
123
|
},
|
|
124
124
|
configurable: false,
|
|
125
125
|
enumerable: true
|
|
@@ -375,41 +375,6 @@ function _register_to_class_(target, mixin) {
|
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
-
function mount(target = this.target, delay = 0) {
|
|
379
|
-
if (delay > 0) {
|
|
380
|
-
setTimeout(() => this.mount(target, 0), delay);
|
|
381
|
-
return this;
|
|
382
|
-
}
|
|
383
|
-
if (this.isBody) return this;
|
|
384
|
-
if (target?.isUIElement) target = target.element;
|
|
385
|
-
this.target = target;
|
|
386
|
-
this.target?.appendChild(this.element);
|
|
387
|
-
return this;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function unmount(delay = 0) {
|
|
391
|
-
if (delay > 0) {
|
|
392
|
-
setTimeout(() => this.unmount(0), delay);
|
|
393
|
-
return this;
|
|
394
|
-
}
|
|
395
|
-
if (this.cache.parent) {
|
|
396
|
-
this.cache.parent.remove(this);
|
|
397
|
-
} else if (
|
|
398
|
-
this.target?.children?.length &&
|
|
399
|
-
[...this.target.children].includes(this.element)
|
|
400
|
-
) {
|
|
401
|
-
this.target.removeChild(this.element);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
return this;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
var LifecycleMethods = /*#__PURE__*/Object.freeze({
|
|
408
|
-
__proto__: null,
|
|
409
|
-
mount: mount,
|
|
410
|
-
unmount: unmount
|
|
411
|
-
});
|
|
412
|
-
|
|
413
378
|
const STATE_GETTER = Symbol.for("ziko/hooks/STATE_GETTER");
|
|
414
379
|
|
|
415
380
|
function useState(initialValue) {
|
|
@@ -464,11 +429,97 @@ function useState(initialValue) {
|
|
|
464
429
|
|
|
465
430
|
const isStateGetter = (arg) => typeof arg === "function" && arg[STATE_GETTER] === true;
|
|
466
431
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
432
|
+
const camel2hyphencase = (text = '') =>
|
|
433
|
+
text.replace(/[A-Z]/g, (match, index) =>
|
|
434
|
+
index ? `-${match.toLowerCase()}` : match.toLowerCase()
|
|
435
|
+
);
|
|
436
|
+
const camel2snakecase = (text = '') =>
|
|
437
|
+
text.replace(/[A-Z]/g, (match, index) =>
|
|
438
|
+
index ? `_${match.toLowerCase()}` : match.toLowerCase()
|
|
439
|
+
);
|
|
440
|
+
const camel2pascalcase = (text = '') => text.charAt(0).toUpperCase() + text.slice(1);
|
|
441
|
+
const camel2constantcase = (text = '') => text
|
|
442
|
+
.replace(/[A-Z]/g, (match, index) =>
|
|
443
|
+
index ? `_${match}` : match
|
|
444
|
+
)
|
|
445
|
+
.toUpperCase();
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
const pascal2snakecase = (text = '') =>
|
|
449
|
+
text.replace(/[A-Z]/g, (match, offset) =>
|
|
450
|
+
offset === 0
|
|
451
|
+
? match.toLowerCase()
|
|
452
|
+
: `_${match.toLowerCase()}`
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
const pascal2hyphencase = (text = '') =>
|
|
456
|
+
text.replace(/[A-Z]/g, (match, offset) =>
|
|
457
|
+
offset === 0
|
|
458
|
+
? match.toLowerCase()
|
|
459
|
+
: `-${match.toLowerCase()}`
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
const pascal2constantcase = (text = '') =>
|
|
463
|
+
text
|
|
464
|
+
.replace(/[A-Z]/g, (match, offset) =>
|
|
465
|
+
offset === 0 ? match : `_${match}`
|
|
466
|
+
)
|
|
467
|
+
.toUpperCase();
|
|
468
|
+
const pascal2camelcase = (text = '') => text.charAt(0).toLowerCase() + text.slice(1);
|
|
469
|
+
|
|
470
|
+
const snake2camelcase = (text = '') => text.replace(/_([a-zA-Z])/g, (_, char) => char.toUpperCase());
|
|
471
|
+
const snake2hyphencase = (text = '') => text.replace(/_/g, '-');
|
|
472
|
+
const snake2pascalcase = (text = '') => text
|
|
473
|
+
.split('_')
|
|
474
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
475
|
+
.join('');
|
|
476
|
+
const snake2constantcase = (text = '') => text.toUpperCase();
|
|
477
|
+
|
|
478
|
+
const hyphen2camelcase = (text = '') => text.replace(/-([a-zA-Z])/g, (_, char) => char.toUpperCase());
|
|
479
|
+
const hyphen2snakecase = (text = '') => text.replace(/-/g, '_');
|
|
480
|
+
const hyphen2pascalcase = (text = '') => text
|
|
481
|
+
.split('-')
|
|
482
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
483
|
+
.join('');
|
|
484
|
+
const hyphen2constantcase = (text = '') => text.replace(/-/g, '_').toUpperCase();
|
|
485
|
+
|
|
486
|
+
const constant2camelcase = (text = '') => text.toLowerCase().replace(/_([a-z])/g, (_, char) => char.toUpperCase());
|
|
487
|
+
const constant2snakecase = (text = '') => text.toLowerCase();
|
|
488
|
+
const constant2pascalcase = (text = '') => text
|
|
489
|
+
.toLowerCase()
|
|
490
|
+
.split('_')
|
|
491
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
492
|
+
.join('');
|
|
493
|
+
const constant2hyphencase = (text = '') => text.toLowerCase().replace(/_/g, '-');
|
|
494
|
+
|
|
495
|
+
const is_camelcase = (text = '') =>{
|
|
496
|
+
if (text.length === 0) return false;
|
|
497
|
+
const camelCasePattern = /^[a-z][a-zA-Z0-9]*$/;
|
|
498
|
+
return camelCasePattern.test(text);
|
|
499
|
+
};
|
|
500
|
+
const is_hyphencase = (text = '') => text.split('-').length > 0;
|
|
501
|
+
const is_snakeCase = (text = '') => text.split('_').length > 0;
|
|
502
|
+
const is_pascalcalse = (text = '') => {
|
|
503
|
+
if (text.length === 0) return false;
|
|
504
|
+
const PascalCasePattern = /^[A-Z][a-zA-Z0-9]*$/;
|
|
505
|
+
return PascalCasePattern.test(text);
|
|
506
|
+
};
|
|
471
507
|
|
|
508
|
+
const is_palindrome = text =>{
|
|
509
|
+
const str = text.toLocaleLowerCase();
|
|
510
|
+
let l = str.length,i;
|
|
511
|
+
for(i=0;i<l/2;i++)if(str[i]!=str[l-i-1])return false;
|
|
512
|
+
return true;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const is_anagram = (word, words) =>{
|
|
516
|
+
word=word.split("").sort();
|
|
517
|
+
words=words.split("").sort();
|
|
518
|
+
return JSON.stringify(word)===JSON.stringify(words);
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
const is_isogram = (text = '') => [...new Set(text.toLowerCase())].length === text.length;
|
|
522
|
+
|
|
472
523
|
function setAttr(name, value) {
|
|
473
524
|
if(name instanceof Object){
|
|
474
525
|
const [names,values]=[Object.keys(name),Object.values(name)];
|
|
@@ -499,7 +550,7 @@ function setContentEditable(bool = true) {
|
|
|
499
550
|
|
|
500
551
|
function _set_attrs_(name, value){
|
|
501
552
|
if(globalThis.SVGAElement && this.element instanceof globalThis.SVGAElement) name = is_camelcase(name) ? camel2hyphencase(name) : name;
|
|
502
|
-
if(this?.attr[name] && this?.attr[name]===value) return;
|
|
553
|
+
// if(this?.attr[name] && this?.attr[name]===value) return;
|
|
503
554
|
if(isStateGetter(value)){
|
|
504
555
|
const getter = value();
|
|
505
556
|
getter._subscribe(
|
|
@@ -508,7 +559,7 @@ function _set_attrs_(name, value){
|
|
|
508
559
|
);
|
|
509
560
|
}
|
|
510
561
|
else this.element?.setAttribute(name, value);
|
|
511
|
-
Object.assign(this.cache.attributes, {[name]:value});
|
|
562
|
+
// Object.assign(this.cache.attributes, {[name]:value});
|
|
512
563
|
}
|
|
513
564
|
|
|
514
565
|
var AttrsMethods = /*#__PURE__*/Object.freeze({
|
|
@@ -531,176 +582,6 @@ class UIText extends UINode {
|
|
|
531
582
|
}
|
|
532
583
|
const text = (...str) => new UIText(...str);
|
|
533
584
|
|
|
534
|
-
function append(...ele) {
|
|
535
|
-
__addItems__.call(this, "append", "push", ...ele);
|
|
536
|
-
return this;
|
|
537
|
-
}
|
|
538
|
-
function prepend(...ele) {
|
|
539
|
-
this.__addItems__.call(this, "prepend", "unshift", ...ele);
|
|
540
|
-
return this;
|
|
541
|
-
}
|
|
542
|
-
function insertAt(index, ...ele) {
|
|
543
|
-
const target = this.itemsTarget;
|
|
544
|
-
if (index >= target.items.length) return this.append(...ele);
|
|
545
|
-
for (let i = 0; i < ele.length; i++) {
|
|
546
|
-
if (["number", "string"].includes(typeof ele[i]))
|
|
547
|
-
ele[i] = text(ele[i]);
|
|
548
|
-
target.element?.insertBefore(
|
|
549
|
-
ele[i].element,
|
|
550
|
-
target.items[index].element
|
|
551
|
-
);
|
|
552
|
-
target.items.splice(index, 0, ele[i]);
|
|
553
|
-
}
|
|
554
|
-
target.maintain();
|
|
555
|
-
return this;
|
|
556
|
-
}
|
|
557
|
-
function remove(...ele) {
|
|
558
|
-
const remove = (ele) => {
|
|
559
|
-
if (typeof ele === "number") ele = this.items[ele];
|
|
560
|
-
if (ele?.isUIElement) this.itemsTarget.element?.removeChild(ele.element);
|
|
561
|
-
this.items = this.items.filter((n) => n !== ele);
|
|
562
|
-
};
|
|
563
|
-
for (let i = 0; i < ele.length; i++) remove(ele[i]);
|
|
564
|
-
for (let i = 0; i < this.items.length; i++)
|
|
565
|
-
Object.assign(this, { [[i]]: this.items[i] });
|
|
566
|
-
// Remove from item
|
|
567
|
-
return this;
|
|
568
|
-
}
|
|
569
|
-
function clear(){
|
|
570
|
-
this?.items?.forEach(n=>n.unmount());
|
|
571
|
-
this.itemsTarget.element.innerHTML = '';
|
|
572
|
-
return this;
|
|
573
|
-
}
|
|
574
|
-
function replaceElementWith(new_element){
|
|
575
|
-
this.cache.element.replaceWith(new_element);
|
|
576
|
-
this.cache.element = new_element;
|
|
577
|
-
// To do : Dispose Events and States
|
|
578
|
-
return this
|
|
579
|
-
}
|
|
580
|
-
function after(ui){
|
|
581
|
-
if(ui?.isUIElement) ui = ui.element;
|
|
582
|
-
this.itemsTarget.element?.after(ui);
|
|
583
|
-
return this;
|
|
584
|
-
}
|
|
585
|
-
function before(ui){
|
|
586
|
-
if(ui?.isUIElement) ui = ui.element;
|
|
587
|
-
this.itemsTarget.element?.before(ui);
|
|
588
|
-
return this;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
async function __addItem__(adder, pusher, item, referenceNode = null, index = null) {
|
|
592
|
-
const { element: itemsTargetEl, items } = this.itemsTarget;
|
|
593
|
-
if (["number", "string"].includes(typeof item)) item = text(item);
|
|
594
|
-
if (typeof item === "function" && isStateGetter(item)) {
|
|
595
|
-
const getter = item();
|
|
596
|
-
item = text(getter.value);
|
|
597
|
-
getter._subscribe(
|
|
598
|
-
(newValue) => { item.element.textContent = newValue; },
|
|
599
|
-
item
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
if (typeof globalThis?.Node === "function" && item instanceof globalThis.Node)
|
|
603
|
-
item = new this.constructor(item);
|
|
604
|
-
if (item instanceof Promise) item = await item;
|
|
605
|
-
if (item?.isUINode) {
|
|
606
|
-
item.cache.parent = this;
|
|
607
|
-
item.target = itemsTargetEl;
|
|
608
|
-
if (adder === "insertBefore" && itemsTargetEl)
|
|
609
|
-
itemsTargetEl.insertBefore(item.element, referenceNode);
|
|
610
|
-
else if (typeof itemsTargetEl?.[adder] === "function")
|
|
611
|
-
itemsTargetEl[adder](item.element);
|
|
612
|
-
if (pusher === "splice" && index !== null) items.splice(index, 0, item);
|
|
613
|
-
else if (typeof items?.[pusher] === "function") items[pusher](item);
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
async function __addItems__(adder, pusher, ...elements) {
|
|
619
|
-
for (const item of elements) {
|
|
620
|
-
await this.__addItem__(adder, pusher, item);
|
|
621
|
-
}
|
|
622
|
-
this.maintain();
|
|
623
|
-
return this;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
var DomMethods = /*#__PURE__*/Object.freeze({
|
|
627
|
-
__proto__: null,
|
|
628
|
-
__addItem__: __addItem__,
|
|
629
|
-
__addItems__: __addItems__,
|
|
630
|
-
after: after,
|
|
631
|
-
append: append,
|
|
632
|
-
before: before,
|
|
633
|
-
clear: clear,
|
|
634
|
-
insertAt: insertAt,
|
|
635
|
-
prepend: prepend,
|
|
636
|
-
remove: remove,
|
|
637
|
-
replaceElementWith: replaceElementWith
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
function at(index) {
|
|
641
|
-
return this.items.at(index);
|
|
642
|
-
}
|
|
643
|
-
function forEach(callback) {
|
|
644
|
-
this.items.forEach(callback);
|
|
645
|
-
return this;
|
|
646
|
-
}
|
|
647
|
-
function map(callback) {
|
|
648
|
-
return this.items.map(callback);
|
|
649
|
-
}
|
|
650
|
-
function find(condition) {
|
|
651
|
-
return this.items.filter(condition);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
var IndexingMethods = /*#__PURE__*/Object.freeze({
|
|
655
|
-
__proto__: null,
|
|
656
|
-
at: at,
|
|
657
|
-
find: find,
|
|
658
|
-
forEach: forEach,
|
|
659
|
-
map: map
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
function style(styles){
|
|
663
|
-
if(!this.element?.style) return this;
|
|
664
|
-
for(let key in styles){
|
|
665
|
-
const value = styles[key];
|
|
666
|
-
if(isStateGetter(value)){
|
|
667
|
-
const getter = value();
|
|
668
|
-
Object.assign(this.element.style, {[key] : getter.value});
|
|
669
|
-
getter._subscribe(
|
|
670
|
-
(newValue) => {
|
|
671
|
-
console.log({newValue});
|
|
672
|
-
Object.assign(this.element.style, {[key] : newValue});
|
|
673
|
-
},
|
|
674
|
-
// this
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
else Object.assign(this.element.style, {[key] : value});
|
|
678
|
-
}
|
|
679
|
-
return this;
|
|
680
|
-
}
|
|
681
|
-
function size(width, height){
|
|
682
|
-
return this.style({width, height})
|
|
683
|
-
}
|
|
684
|
-
function hide(){
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
function show(){
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
function animate(keyframe, {duration=1000, iterations=1, easing="ease"}={}){
|
|
691
|
-
this.element?.animate(keyframe,{duration, iterations, easing});
|
|
692
|
-
return this;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
var StyleMethods = /*#__PURE__*/Object.freeze({
|
|
696
|
-
__proto__: null,
|
|
697
|
-
animate: animate,
|
|
698
|
-
hide: hide,
|
|
699
|
-
show: show,
|
|
700
|
-
size: size,
|
|
701
|
-
style: style
|
|
702
|
-
});
|
|
703
|
-
|
|
704
585
|
class EventController {
|
|
705
586
|
constructor(target, category){
|
|
706
587
|
this.cache = {
|
|
@@ -1267,118 +1148,280 @@ function register_swipe_event(
|
|
|
1267
1148
|
};
|
|
1268
1149
|
}
|
|
1269
1150
|
|
|
1270
|
-
class UIElement extends UIElement$1{
|
|
1271
|
-
constructor({
|
|
1151
|
+
class UIElement extends UIElement$1 {
|
|
1152
|
+
constructor({
|
|
1153
|
+
element,
|
|
1154
|
+
name = "",
|
|
1155
|
+
type = "html",
|
|
1156
|
+
render = __Ziko__.__Config__.default.render,
|
|
1157
|
+
props,
|
|
1158
|
+
} = {}) {
|
|
1272
1159
|
super();
|
|
1273
1160
|
this.exp = {
|
|
1274
|
-
events
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1161
|
+
events: {},
|
|
1277
1162
|
};
|
|
1278
1163
|
register_to_class(
|
|
1279
|
-
this,
|
|
1280
|
-
LifecycleMethods,
|
|
1281
|
-
AttrsMethods,
|
|
1282
|
-
DomMethods,
|
|
1283
|
-
StyleMethods,
|
|
1284
|
-
IndexingMethods,
|
|
1164
|
+
this,
|
|
1165
|
+
// LifecycleMethods,
|
|
1166
|
+
AttrsMethods,
|
|
1167
|
+
// DomMethods,
|
|
1168
|
+
// StyleMethods,
|
|
1169
|
+
// IndexingMethods,
|
|
1285
1170
|
PtrListeners,
|
|
1286
1171
|
ClickListeners,
|
|
1287
1172
|
KeyListeners,
|
|
1288
1173
|
ViewListeners,
|
|
1289
1174
|
);
|
|
1290
1175
|
|
|
1291
|
-
if(element) this.init({element, name, type, render, props});
|
|
1176
|
+
if (element) this.init({ element, name, type, render, props });
|
|
1292
1177
|
}
|
|
1293
|
-
on(
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1178
|
+
on(
|
|
1179
|
+
event_name,
|
|
1180
|
+
callback,
|
|
1181
|
+
{
|
|
1182
|
+
details_setter,
|
|
1183
|
+
category = "global",
|
|
1184
|
+
isCustom = false,
|
|
1185
|
+
preventDefault = false,
|
|
1186
|
+
} = {},
|
|
1187
|
+
) {
|
|
1188
|
+
if (event_name instanceof Array)
|
|
1189
|
+
event_name.forEach((event) =>
|
|
1190
|
+
this.on(event, callback, {
|
|
1191
|
+
details_setter,
|
|
1192
|
+
category,
|
|
1193
|
+
isCustom,
|
|
1194
|
+
preventDefault,
|
|
1195
|
+
}),
|
|
1196
|
+
);
|
|
1197
|
+
if (category && !this.exp.events.hasOwnProperty(category))
|
|
1198
|
+
this.exp.events[category] = new EventController(this, category);
|
|
1302
1199
|
isCustom && this.exp.events[category].cache.customEvents.add(event_name);
|
|
1303
1200
|
const EVENT = this.exp.events[category];
|
|
1304
|
-
EVENT.addListener(
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1201
|
+
EVENT.addListener(
|
|
1202
|
+
event_name,
|
|
1203
|
+
(e) => {
|
|
1204
|
+
if (details_setter) details_setter(EVENT);
|
|
1205
|
+
callback(e);
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
preventDefault,
|
|
1209
|
+
},
|
|
1210
|
+
);
|
|
1310
1211
|
return this;
|
|
1311
1212
|
}
|
|
1312
|
-
|
|
1213
|
+
off(event, category = "global") {
|
|
1313
1214
|
this.exp.events[category].removeListener(event);
|
|
1314
|
-
return this
|
|
1215
|
+
return this;
|
|
1315
1216
|
}
|
|
1316
|
-
get element(){
|
|
1217
|
+
get element() {
|
|
1317
1218
|
return this.cache.element;
|
|
1318
1219
|
}
|
|
1319
|
-
get itemsTarget(){
|
|
1320
|
-
return this.cache.itemsTarget;
|
|
1220
|
+
get itemsTarget() {
|
|
1221
|
+
return this.cache.itemsTarget;
|
|
1321
1222
|
}
|
|
1322
|
-
get itemsTargetElement(){
|
|
1223
|
+
get itemsTargetElement() {
|
|
1323
1224
|
return this.itemsTarget.element;
|
|
1324
1225
|
}
|
|
1325
|
-
setItemsTarget(parent){
|
|
1226
|
+
setItemsTarget(parent) {
|
|
1326
1227
|
this.cache.itemsTarget = parent;
|
|
1327
1228
|
this.items = parent.items;
|
|
1328
1229
|
return this;
|
|
1329
1230
|
}
|
|
1330
|
-
isInteractive(){
|
|
1231
|
+
isInteractive() {
|
|
1331
1232
|
return this.cache.isInteractive;
|
|
1332
1233
|
}
|
|
1333
|
-
useClient(directive){
|
|
1334
|
-
if(!this.cache.isInteractive){
|
|
1335
|
-
this.element.setAttribute(
|
|
1336
|
-
|
|
1234
|
+
useClient(directive) {
|
|
1235
|
+
if (!this.cache.isInteractive) {
|
|
1236
|
+
this.element.setAttribute(
|
|
1237
|
+
"data-hydration-index",
|
|
1238
|
+
globalThis.__Ziko__.__HYDRATION__.index,
|
|
1239
|
+
);
|
|
1240
|
+
globalThis.__Ziko__.__HYDRATION__.register(() => this.constructor);
|
|
1337
1241
|
this.cache.isInteractive = true;
|
|
1338
1242
|
}
|
|
1339
|
-
if(directive)
|
|
1243
|
+
if (directive)
|
|
1244
|
+
this.element.setAttribute("data-hydration-directive", directive);
|
|
1340
1245
|
return this;
|
|
1341
1246
|
}
|
|
1342
1247
|
// get st(){
|
|
1343
1248
|
// return this.cache.style;
|
|
1344
1249
|
// }
|
|
1345
|
-
get attr(){
|
|
1250
|
+
get attr() {
|
|
1346
1251
|
return this.cache.attributes;
|
|
1347
1252
|
}
|
|
1348
|
-
get evt(){
|
|
1253
|
+
get evt() {
|
|
1349
1254
|
return this.events;
|
|
1350
1255
|
}
|
|
1351
|
-
get html(){
|
|
1256
|
+
get html() {
|
|
1352
1257
|
return this.element.innerHTML;
|
|
1353
1258
|
}
|
|
1354
|
-
get text(){
|
|
1259
|
+
get text() {
|
|
1355
1260
|
return this.element.textContent;
|
|
1356
1261
|
}
|
|
1357
1262
|
// get isBody(){
|
|
1358
1263
|
// return this.element === globalThis?.document.body;
|
|
1359
1264
|
// }
|
|
1360
|
-
get parent(){
|
|
1265
|
+
get parent() {
|
|
1361
1266
|
return this.cache.parent;
|
|
1362
1267
|
}
|
|
1363
|
-
get
|
|
1364
|
-
return this.element.getBoundingClientRect()
|
|
1268
|
+
get rect() {
|
|
1269
|
+
return this.element.getBoundingClientRect();
|
|
1270
|
+
}
|
|
1271
|
+
// get width(){
|
|
1272
|
+
// return this.element.getBoundingClientRect().width;
|
|
1273
|
+
// }
|
|
1274
|
+
// get height(){
|
|
1275
|
+
// return this.element.getBoundingClientRect().height;
|
|
1276
|
+
// }
|
|
1277
|
+
// get top(){
|
|
1278
|
+
// return this.element.getBoundingClientRect().top;
|
|
1279
|
+
// }
|
|
1280
|
+
// get right(){
|
|
1281
|
+
// return this.element.getBoundingClientRect().right;
|
|
1282
|
+
// }
|
|
1283
|
+
// get bottom(){
|
|
1284
|
+
// return this.element.getBoundingClientRect().bottom;
|
|
1285
|
+
// }
|
|
1286
|
+
// get left(){
|
|
1287
|
+
// return this.element.getBoundingClientRect().left;
|
|
1288
|
+
// }
|
|
1289
|
+
|
|
1290
|
+
// Lifecycle
|
|
1291
|
+
|
|
1292
|
+
mount(target = this.target, delay = 0) {
|
|
1293
|
+
if (this.isBody) return this;
|
|
1294
|
+
if (target?.isUIElement) target = target.element;
|
|
1295
|
+
this.target = target;
|
|
1296
|
+
this.target?.appendChild(this.element);
|
|
1297
|
+
return this;
|
|
1298
|
+
}
|
|
1299
|
+
unmount() {
|
|
1300
|
+
if (this.cache.parent) {
|
|
1301
|
+
this.cache.parent.remove(this);
|
|
1302
|
+
} else if (
|
|
1303
|
+
this.target?.children?.length &&
|
|
1304
|
+
[...this.target.children].includes(this.element)
|
|
1305
|
+
)
|
|
1306
|
+
this.target.removeChild(this.element);
|
|
1307
|
+
return this;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// Indexing
|
|
1311
|
+
|
|
1312
|
+
at(index) {
|
|
1313
|
+
return this.items.at(index);
|
|
1314
|
+
}
|
|
1315
|
+
forEach(callback) {
|
|
1316
|
+
this.items.forEach(callback);
|
|
1317
|
+
return this;
|
|
1318
|
+
}
|
|
1319
|
+
map(callback) {
|
|
1320
|
+
return this.items.map(callback);
|
|
1321
|
+
}
|
|
1322
|
+
find(condition) {
|
|
1323
|
+
return this.items.filter(condition);
|
|
1324
|
+
}
|
|
1325
|
+
// Styling
|
|
1326
|
+
style(styles) {
|
|
1327
|
+
if (!this.element?.style) return this;
|
|
1328
|
+
for (let key in styles) {
|
|
1329
|
+
const value = styles[key];
|
|
1330
|
+
if (isStateGetter(value)) {
|
|
1331
|
+
const getter = value();
|
|
1332
|
+
Object.assign(this.element.style, { [key]: getter.value });
|
|
1333
|
+
getter._subscribe(
|
|
1334
|
+
(newValue) => {
|
|
1335
|
+
Object.assign(this.element.style, { [key]: newValue });
|
|
1336
|
+
},
|
|
1337
|
+
// this
|
|
1338
|
+
);
|
|
1339
|
+
} else Object.assign(this.element.style, { [key]: value });
|
|
1340
|
+
}
|
|
1341
|
+
return this;
|
|
1365
1342
|
}
|
|
1366
|
-
|
|
1367
|
-
return this.
|
|
1343
|
+
size(width, height) {
|
|
1344
|
+
return this.style({ width, height });
|
|
1368
1345
|
}
|
|
1369
|
-
|
|
1370
|
-
|
|
1346
|
+
hide() {}
|
|
1347
|
+
show() {}
|
|
1348
|
+
|
|
1349
|
+
// Dom
|
|
1350
|
+
|
|
1351
|
+
append(...ele) {
|
|
1352
|
+
this.__addItems__("append", "push", ...ele);
|
|
1353
|
+
return this;
|
|
1371
1354
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1355
|
+
prepend(...ele) {
|
|
1356
|
+
this.this.__addItems__("prepend", "unshift", ...ele);
|
|
1357
|
+
return this;
|
|
1374
1358
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1359
|
+
insertAt(index, ...ele) {
|
|
1360
|
+
const target = this.itemsTarget;
|
|
1361
|
+
if (index >= target.items.length) return this.append(...ele);
|
|
1362
|
+
for (let i = 0; i < ele.length; i++) {
|
|
1363
|
+
if (["number", "string"].includes(typeof ele[i])) ele[i] = text(ele[i]);
|
|
1364
|
+
target.element?.insertBefore(ele[i].element, target.items[index].element);
|
|
1365
|
+
target.items.splice(index, 0, ele[i]);
|
|
1366
|
+
}
|
|
1367
|
+
target.maintain();
|
|
1368
|
+
return this;
|
|
1377
1369
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1370
|
+
remove(...ele) {
|
|
1371
|
+
const remove = (ele) => {
|
|
1372
|
+
if (typeof ele === "number") ele = this.items[ele];
|
|
1373
|
+
if (ele?.isUIElement) this.itemsTarget.element?.removeChild(ele.element);
|
|
1374
|
+
this.items = this.items.filter((n) => n !== ele);
|
|
1375
|
+
};
|
|
1376
|
+
for (let i = 0; i < ele.length; i++) remove(ele[i]);
|
|
1377
|
+
for (let i = 0; i < this.items.length; i++)
|
|
1378
|
+
Object.assign(this, { [[i]]: this.items[i] });
|
|
1379
|
+
// Remove from item
|
|
1380
|
+
return this;
|
|
1380
1381
|
}
|
|
1381
1382
|
|
|
1383
|
+
async __addItem__(adder, pusher, item, referenceNode = null, index = null) {
|
|
1384
|
+
const { element: itemsTargetEl, items } = this.itemsTarget;
|
|
1385
|
+
if (["number", "string"].includes(typeof item)) item = text(item);
|
|
1386
|
+
if (typeof item === "function" && isStateGetter(item)) {
|
|
1387
|
+
const getter = item();
|
|
1388
|
+
item = getter.value;
|
|
1389
|
+
|
|
1390
|
+
getter._subscribe((newValue) => {
|
|
1391
|
+
if (newValue?.isUIElement?.()) {
|
|
1392
|
+
item.element.replaceWith(newValue.element);
|
|
1393
|
+
item = newValue;
|
|
1394
|
+
} else {
|
|
1395
|
+
item.element.textContent = newValue;
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
if (
|
|
1400
|
+
typeof globalThis?.Node === "function" &&
|
|
1401
|
+
item instanceof globalThis.Node
|
|
1402
|
+
)
|
|
1403
|
+
item = new UIElement({element : item});
|
|
1404
|
+
if (item instanceof Promise) item = await item;
|
|
1405
|
+
if (item?.isUINode) {
|
|
1406
|
+
item.cache.parent = this;
|
|
1407
|
+
item.target = itemsTargetEl;
|
|
1408
|
+
if (adder === "insertBefore" && itemsTargetEl)
|
|
1409
|
+
itemsTargetEl.insertBefore(item.element, referenceNode);
|
|
1410
|
+
else if (typeof itemsTargetEl?.[adder] === "function")
|
|
1411
|
+
itemsTargetEl[adder](item.element);
|
|
1412
|
+
if (pusher === "splice" && index !== null) items.splice(index, 0, item);
|
|
1413
|
+
else if (typeof items?.[pusher] === "function") items[pusher](item);
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
async __addItems__(adder, pusher, ...elements) {
|
|
1419
|
+
for (const item of elements) {
|
|
1420
|
+
await this.__addItem__(adder, pusher, item);
|
|
1421
|
+
}
|
|
1422
|
+
this.maintain();
|
|
1423
|
+
return this;
|
|
1424
|
+
}
|
|
1382
1425
|
}
|
|
1383
1426
|
|
|
1384
1427
|
const HTMLTags = [
|
|
@@ -1517,6 +1560,20 @@ const MathMLTags = [
|
|
|
1517
1560
|
`munder`, `munderover`
|
|
1518
1561
|
];
|
|
1519
1562
|
|
|
1563
|
+
const call_with_optional_props = (Component) => {
|
|
1564
|
+
return (...args) => {
|
|
1565
|
+
const first = args[0];
|
|
1566
|
+
|
|
1567
|
+
const isChild = first?.isUIElement?.() || is_primitive(first);
|
|
1568
|
+
|
|
1569
|
+
if (isChild) {
|
|
1570
|
+
return new Component({}, ...args);
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
return new Component(first, ...args.slice(1));
|
|
1574
|
+
};
|
|
1575
|
+
};
|
|
1576
|
+
|
|
1520
1577
|
const tags = new Proxy({}, {
|
|
1521
1578
|
get(target, prop) {
|
|
1522
1579
|
if (typeof prop !== 'string') return undefined;
|
|
@@ -1526,15 +1583,17 @@ const tags = new Proxy({}, {
|
|
|
1526
1583
|
if(SVGTags.includes(tag)) type = 'svg';
|
|
1527
1584
|
if(MathMLTags.includes(tag)) type = 'mathml';
|
|
1528
1585
|
return (...args) => {
|
|
1586
|
+
const ui = new UIElement({element : tag, name : tag, type});
|
|
1587
|
+
const first = args[0];
|
|
1529
1588
|
if(args.length === 0) {
|
|
1530
|
-
return
|
|
1589
|
+
return ui
|
|
1531
1590
|
}
|
|
1532
1591
|
if(
|
|
1533
|
-
['string', 'number'].includes(typeof
|
|
1534
|
-
||
|
|
1535
|
-
|| isStateGetter(
|
|
1536
|
-
||
|
|
1537
|
-
) return
|
|
1592
|
+
['string', 'number'].includes(typeof first)
|
|
1593
|
+
|| first instanceof UIElement
|
|
1594
|
+
|| isStateGetter(first)
|
|
1595
|
+
|| first instanceof HTMLElement
|
|
1596
|
+
) return ui.append(...args);
|
|
1538
1597
|
return new UIElement({element : tag, type, props : args.shift()}).append(...args)
|
|
1539
1598
|
}
|
|
1540
1599
|
}
|
|
@@ -1682,58 +1741,830 @@ class UseEventEmitter {
|
|
|
1682
1741
|
|
|
1683
1742
|
const useEventEmitter = (maxListeners) => new UseEventEmitter(maxListeners);
|
|
1684
1743
|
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1744
|
+
function normalize_path(inputPath, root = './src/pages', extensions = ['js', 'ts', 'jsx', 'tsx']) {
|
|
1745
|
+
let cleanRoot = root.endsWith('/') ? root.slice(0, -1) : root;
|
|
1746
|
+
const normalizedPath = inputPath.replace(/\\/g, '/');
|
|
1747
|
+
|
|
1748
|
+
// 1. Extract path relative to root
|
|
1749
|
+
let relativePath = normalizedPath;
|
|
1750
|
+
if (cleanRoot && normalizedPath.includes(cleanRoot)) {
|
|
1751
|
+
relativePath = normalizedPath.split(cleanRoot).pop();
|
|
1752
|
+
}
|
|
1689
1753
|
|
|
1690
|
-
|
|
1691
|
-
const
|
|
1692
|
-
|
|
1693
|
-
const pascal2constantcase = (text = '') => text.replace(/([A-Z])/g, (match, offset) => offset ? '_' + match : match).toUpperCase();
|
|
1754
|
+
// 2. Split directory parts & strip route groups like (auth)
|
|
1755
|
+
const rawParts = relativePath.split('/').filter(Boolean).filter(p => !/^\([^)]+\)$/.test(p));
|
|
1756
|
+
if (rawParts.length === 0) return '/';
|
|
1694
1757
|
|
|
1695
|
-
const
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
const
|
|
1758
|
+
const lastPart = rawParts[rawParts.length - 1];
|
|
1759
|
+
const extRegex = new RegExp(`\\.(${extensions.join('|')})$`);
|
|
1760
|
+
|
|
1761
|
+
const isIndexFile = extensions.some(ext => lastPart === `index.${ext}`);
|
|
1762
|
+
const fileNameWithoutExt = lastPart.replace(extRegex, '');
|
|
1699
1763
|
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
const hyphen2pascalcase = (text = '') => text.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
|
|
1703
|
-
const hyphen2constantcase = (text = '') => text.replace(/-/g, '_').toUpperCase();
|
|
1764
|
+
// Remove original file name from directory segments
|
|
1765
|
+
rawParts.pop();
|
|
1704
1766
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
const
|
|
1767
|
+
// 3. Handle flat dot notation without breaking [..slug] or [[..slug]]
|
|
1768
|
+
if (!isIndexFile) {
|
|
1769
|
+
// Splits by '.' ONLY if the dot is outside of bracketed patterns like [...] or [[...]]
|
|
1770
|
+
const dotSegments = fileNameWithoutExt.split(/\.(?![^\[]*\])/);
|
|
1771
|
+
rawParts.push(...dotSegments);
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
return '/' + rawParts.join('/');
|
|
1775
|
+
}
|
|
1709
1776
|
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
const
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1777
|
+
function routes_matcher(mask, route) {
|
|
1778
|
+
const maskSegments = mask.split("/").filter(Boolean);
|
|
1779
|
+
const routeSegments = route.split("/").filter(Boolean);
|
|
1780
|
+
let i = 0, j = 0;
|
|
1781
|
+
|
|
1782
|
+
while (i < maskSegments.length && j < routeSegments.length) {
|
|
1783
|
+
const maskSegment = maskSegments[i];
|
|
1784
|
+
|
|
1785
|
+
// Optional Catch-all [[...slug]]
|
|
1786
|
+
if (maskSegment.startsWith("[[...") && maskSegment.endsWith("]]")) {
|
|
1787
|
+
const remainingMaskSegments = maskSegments.length - i - 1;
|
|
1788
|
+
if (remainingMaskSegments === 0) return true;
|
|
1789
|
+
let requiredSegments = 0;
|
|
1790
|
+
for (let k = i + 1; k < maskSegments.length; k++) {
|
|
1791
|
+
if (!maskSegments[k].endsWith("]+")) requiredSegments++;
|
|
1792
|
+
}
|
|
1793
|
+
const remainingRouteSegments = routeSegments.length - j;
|
|
1794
|
+
const segmentsToConsume = remainingRouteSegments - requiredSegments;
|
|
1795
|
+
if (segmentsToConsume < 0) return false;
|
|
1796
|
+
j += segmentsToConsume;
|
|
1797
|
+
i++;
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1722
1800
|
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1801
|
+
// Required Catch-all [...slug]
|
|
1802
|
+
if (maskSegment.startsWith("[...") && maskSegment.endsWith("]")) {
|
|
1803
|
+
const remainingMaskSegments = maskSegments.length - i - 1;
|
|
1804
|
+
if (remainingMaskSegments === 0) return true;
|
|
1805
|
+
let requiredSegments = 0;
|
|
1806
|
+
for (let k = i + 1; k < maskSegments.length; k++) {
|
|
1807
|
+
if (!maskSegments[k].endsWith("]+")) requiredSegments++;
|
|
1808
|
+
}
|
|
1809
|
+
const remainingRouteSegments = routeSegments.length - j;
|
|
1810
|
+
if (remainingRouteSegments < requiredSegments) return false;
|
|
1811
|
+
const segmentsToConsume = remainingRouteSegments - requiredSegments;
|
|
1812
|
+
if (segmentsToConsume < 1) return false;
|
|
1813
|
+
j += segmentsToConsume;
|
|
1814
|
+
i++;
|
|
1815
|
+
continue;
|
|
1816
|
+
}
|
|
1729
1817
|
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
}
|
|
1818
|
+
if (maskSegment.startsWith("[") && maskSegment.endsWith("]+")) {
|
|
1819
|
+
if (routeSegments[j]) j++;
|
|
1820
|
+
i++;
|
|
1821
|
+
continue;
|
|
1822
|
+
}
|
|
1735
1823
|
|
|
1736
|
-
|
|
1824
|
+
if (maskSegment.startsWith("[") && maskSegment.endsWith("]")) {
|
|
1825
|
+
i++;
|
|
1826
|
+
j++;
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
if (maskSegment !== routeSegments[j]) return false;
|
|
1831
|
+
i++;
|
|
1832
|
+
j++;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
while (i < maskSegments.length) {
|
|
1836
|
+
const seg = maskSegments[i];
|
|
1837
|
+
// [[...slug]] and [param]+ can match empty segments at the end
|
|
1838
|
+
if (seg.startsWith("[[...") && seg.endsWith("]]")) {
|
|
1839
|
+
i++;
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
if (seg.endsWith("]+")) {
|
|
1843
|
+
i++;
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
return i === maskSegments.length && j === routeSegments.length;
|
|
1850
|
+
}
|
|
1851
|
+
// // DEMO
|
|
1852
|
+
// console.log("=== EXISTING TESTS ===");
|
|
1853
|
+
// console.log(routes_matcher("/user/[id]+", "/user")); // true
|
|
1854
|
+
// console.log(routes_matcher("/user/[id]+", "/user/42")); // true
|
|
1855
|
+
// console.log(routes_matcher("/blog/[...slug]", "/blog/a/b")); // true
|
|
1856
|
+
// console.log(routes_matcher("/blog/[id]", "/blog")); // false
|
|
1857
|
+
// console.log(routes_matcher("/product/:id", "/product/99")); // true
|
|
1858
|
+
|
|
1859
|
+
function is_dynamic(path) {
|
|
1860
|
+
const DynamicPattern = /(:\w+|\[\[\.\.\.\w+\]\]|\[\.\.\.\w+\]|\[\w+\]\+?)/;
|
|
1861
|
+
return DynamicPattern.test(path);
|
|
1862
|
+
}
|
|
1863
|
+
function sort_routes(routeKeys) {
|
|
1864
|
+
return [...routeKeys].sort((a, b) => {
|
|
1865
|
+
const aIsOptionalCatchAll = a.includes('[[...');
|
|
1866
|
+
const bIsOptionalCatchAll = b.includes('[[...');
|
|
1867
|
+
|
|
1868
|
+
const aIsCatchAll = a.includes('[...') && !aIsOptionalCatchAll;
|
|
1869
|
+
const bIsCatchAll = b.includes('[...') && !bIsOptionalCatchAll;
|
|
1870
|
+
|
|
1871
|
+
const aIsDynamic = is_dynamic(a);
|
|
1872
|
+
const bIsDynamic = is_dynamic(b);
|
|
1873
|
+
|
|
1874
|
+
// 1. Optional catch-alls [[...slug]] ALWAYS go last
|
|
1875
|
+
if (aIsOptionalCatchAll && !bIsOptionalCatchAll) return 1;
|
|
1876
|
+
if (!aIsOptionalCatchAll && bIsOptionalCatchAll) return -1;
|
|
1877
|
+
|
|
1878
|
+
// 2. Catch-alls [...slug] go right before optional catch-alls
|
|
1879
|
+
if (aIsCatchAll && !bIsCatchAll) return 1;
|
|
1880
|
+
if (!aIsCatchAll && bIsCatchAll) return -1;
|
|
1881
|
+
|
|
1882
|
+
// 3. Static routes go before standard dynamic routes ([id])
|
|
1883
|
+
if (!aIsDynamic && bIsDynamic) return -1;
|
|
1884
|
+
if (aIsDynamic && !bIsDynamic) return 1;
|
|
1885
|
+
|
|
1886
|
+
// 4. Deeper/longer routes take precedence over shorter ones
|
|
1887
|
+
return b.length - a.length;
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
function dynamic_routes_parser(mask, route) {
|
|
1892
|
+
const maskSegments = mask.split("/").filter(Boolean);
|
|
1893
|
+
const routeSegments = route.split("/").filter(Boolean);
|
|
1894
|
+
const params = {};
|
|
1895
|
+
let i = 0, j = 0;
|
|
1896
|
+
|
|
1897
|
+
while (i < maskSegments.length && j < routeSegments.length) {
|
|
1898
|
+
const maskSegment = maskSegments[i];
|
|
1899
|
+
|
|
1900
|
+
// Handle [[...slug]]
|
|
1901
|
+
if (maskSegment.startsWith("[[...") && maskSegment.endsWith("]]")) {
|
|
1902
|
+
const paramName = maskSegment.slice(5, -2);
|
|
1903
|
+
const remainingMaskSegments = maskSegments.length - i - 1;
|
|
1904
|
+
if (remainingMaskSegments === 0) {
|
|
1905
|
+
params[paramName] = routeSegments.slice(j).join("/");
|
|
1906
|
+
break;
|
|
1907
|
+
}
|
|
1908
|
+
let requiredSegments = 0;
|
|
1909
|
+
for (let k = i + 1; k < maskSegments.length; k++) {
|
|
1910
|
+
if (!maskSegments[k].endsWith("]+")) requiredSegments++;
|
|
1911
|
+
}
|
|
1912
|
+
const remainingRouteSegments = routeSegments.length - j;
|
|
1913
|
+
const segmentsToConsume = remainingRouteSegments - requiredSegments;
|
|
1914
|
+
if (segmentsToConsume >= 0) {
|
|
1915
|
+
params[paramName] = routeSegments.slice(j, j + segmentsToConsume).join("/");
|
|
1916
|
+
j += segmentsToConsume;
|
|
1917
|
+
} else return {};
|
|
1918
|
+
i++;
|
|
1919
|
+
continue;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
// Handle [...slug]
|
|
1923
|
+
if (maskSegment.startsWith("[...") && maskSegment.endsWith("]")) {
|
|
1924
|
+
const paramName = maskSegment.slice(4, -1);
|
|
1925
|
+
const remainingMaskSegments = maskSegments.length - i - 1;
|
|
1926
|
+
if (remainingMaskSegments === 0) {
|
|
1927
|
+
params[paramName] = routeSegments.slice(j).join("/");
|
|
1928
|
+
break;
|
|
1929
|
+
}
|
|
1930
|
+
let requiredSegments = 0;
|
|
1931
|
+
for (let k = i + 1; k < maskSegments.length; k++) {
|
|
1932
|
+
if (!maskSegments[k].endsWith("]+")) requiredSegments++;
|
|
1933
|
+
}
|
|
1934
|
+
const remainingRouteSegments = routeSegments.length - j;
|
|
1935
|
+
const segmentsToConsume = remainingRouteSegments - requiredSegments;
|
|
1936
|
+
if (segmentsToConsume >= 1) {
|
|
1937
|
+
params[paramName] = routeSegments.slice(j, j + segmentsToConsume).join("/");
|
|
1938
|
+
j += segmentsToConsume;
|
|
1939
|
+
} else return {};
|
|
1940
|
+
i++;
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
if (maskSegment.startsWith("[") && maskSegment.endsWith("]+")) {
|
|
1945
|
+
const paramName = maskSegment.slice(1, -2);
|
|
1946
|
+
if (routeSegments[j]) {
|
|
1947
|
+
params[paramName] = routeSegments[j];
|
|
1948
|
+
j++;
|
|
1949
|
+
}
|
|
1950
|
+
i++;
|
|
1951
|
+
continue;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
if (maskSegment.startsWith("[") && maskSegment.endsWith("]")) {
|
|
1955
|
+
const paramName = maskSegment.slice(1, -1);
|
|
1956
|
+
params[paramName] = routeSegments[j];
|
|
1957
|
+
} else if (maskSegment !== routeSegments[j]) return {};
|
|
1958
|
+
|
|
1959
|
+
i++;
|
|
1960
|
+
j++;
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
// Set default empty string for remaining uncaptured optional catch-alls
|
|
1964
|
+
while (i < maskSegments.length) {
|
|
1965
|
+
const maskSegment = maskSegments[i];
|
|
1966
|
+
if (maskSegment.startsWith("[[...") && maskSegment.endsWith("]]")) {
|
|
1967
|
+
const paramName = maskSegment.slice(5, -2);
|
|
1968
|
+
if (!(paramName in params)) params[paramName] = "";
|
|
1969
|
+
}
|
|
1970
|
+
i++;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
return params;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
|
|
1977
|
+
// console.log("\n=== PARSER TESTS ===");
|
|
1978
|
+
// console.log(dynamic_routes_parser("/user/[id]+", "/user"));
|
|
1979
|
+
// // 👉 {}
|
|
1980
|
+
|
|
1981
|
+
// console.log(dynamic_routes_parser("/user/[id]+", "/user/42"));
|
|
1982
|
+
// // 👉 { id: "42" }
|
|
1983
|
+
|
|
1984
|
+
// console.log(dynamic_routes_parser("/blog/[...slug]", "/blog/2025/oct/post"));
|
|
1985
|
+
// // 👉 { slug: "2025/oct/post" }
|
|
1986
|
+
|
|
1987
|
+
// console.log(
|
|
1988
|
+
// dynamic_routes_parser("/product/[category]/[id]+", "/product/electronics"),
|
|
1989
|
+
// );
|
|
1990
|
+
// // 👉 { category: "electronics" }
|
|
1991
|
+
|
|
1992
|
+
// console.log("\n=== FIX TEST ===");
|
|
1993
|
+
// console.log(dynamic_routes_parser("/[...slug]/[id]", "/sl1/sl2/9"));
|
|
1994
|
+
// // 👉 { slug: "sl1/sl2", id: "9" }
|
|
1995
|
+
|
|
1996
|
+
// console.log(dynamic_routes_parser("/[slug]/[...id]", "/sl1/id1/id2"));
|
|
1997
|
+
// // 👉 { slug: "sl1", id: "id1/id2" }
|
|
1998
|
+
|
|
1999
|
+
// console.log(dynamic_routes_parser("/blog/lang/[lang]/id/[id]", "/blog/lang/en/id/10"));
|
|
2000
|
+
// // 👉 { lang: "en", id: "10" }
|
|
2001
|
+
|
|
2002
|
+
function get_root(paths) {
|
|
2003
|
+
if (paths.length === 0) return '';
|
|
2004
|
+
|
|
2005
|
+
// Strip trailing file names (e.g. /index.js or /about.js) to compare directory structures only
|
|
2006
|
+
const dirPaths = paths.map(path => {
|
|
2007
|
+
const parts = path.split('/');
|
|
2008
|
+
parts.pop(); // Remove the filename
|
|
2009
|
+
return parts;
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
const minLength = Math.min(...dirPaths.map(parts => parts.length));
|
|
2013
|
+
let commonParts = [];
|
|
2014
|
+
|
|
2015
|
+
for (let i = 0; i < minLength; i++) {
|
|
2016
|
+
const part = dirPaths[0][i];
|
|
2017
|
+
if (dirPaths.every(parts => parts[i] === part || parts[i].startsWith('['))) {
|
|
2018
|
+
commonParts.push(part);
|
|
2019
|
+
}
|
|
2020
|
+
else break;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
const root = commonParts.join('/') + (commonParts.length ? '/' : '');
|
|
2024
|
+
return root;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
const { meta, link, script } = tags;
|
|
2028
|
+
|
|
2029
|
+
class UILayout {
|
|
2030
|
+
|
|
2031
|
+
constructor(
|
|
2032
|
+
{
|
|
2033
|
+
title,
|
|
2034
|
+
icon,
|
|
2035
|
+
description,
|
|
2036
|
+
lang,
|
|
2037
|
+
links = [],
|
|
2038
|
+
script: scripts = [],
|
|
2039
|
+
meta: metas = [],
|
|
2040
|
+
} = {},
|
|
2041
|
+
...ui
|
|
2042
|
+
) {
|
|
2043
|
+
this.options = {
|
|
2044
|
+
title,
|
|
2045
|
+
icon,
|
|
2046
|
+
description,
|
|
2047
|
+
lang,
|
|
2048
|
+
links,
|
|
2049
|
+
meta: metas,
|
|
2050
|
+
script: scripts,
|
|
2051
|
+
};
|
|
2052
|
+
|
|
2053
|
+
this.ui = ui;
|
|
2054
|
+
this.render();
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
render() {
|
|
2058
|
+
if (typeof document === "undefined") {
|
|
2059
|
+
return {
|
|
2060
|
+
...this.options,
|
|
2061
|
+
body: this.ui,
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
const {
|
|
2066
|
+
title,
|
|
2067
|
+
icon,
|
|
2068
|
+
description,
|
|
2069
|
+
lang,
|
|
2070
|
+
links,
|
|
2071
|
+
meta: metas,
|
|
2072
|
+
script: scripts,
|
|
2073
|
+
} = this.options;
|
|
2074
|
+
|
|
2075
|
+
if (title) {
|
|
2076
|
+
document.title = title;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
if (lang) {
|
|
2080
|
+
document.documentElement.lang = lang;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
if (icon) {
|
|
2084
|
+
document.head.appendChild(
|
|
2085
|
+
link({
|
|
2086
|
+
rel: "icon",
|
|
2087
|
+
href: icon,
|
|
2088
|
+
})
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
if (description) {
|
|
2093
|
+
document.head.appendChild(
|
|
2094
|
+
meta({
|
|
2095
|
+
name: "description",
|
|
2096
|
+
content: description,
|
|
2097
|
+
})
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
for (const attrs of links) {
|
|
2102
|
+
document.head.appendChild(link(attrs));
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
for (const attrs of metas) {
|
|
2106
|
+
document.head.appendChild(meta(attrs));
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
for (const attrs of scripts) {
|
|
2110
|
+
document.head.appendChild(script(attrs));
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
mount(target){
|
|
2115
|
+
this.ui.forEach(el => el.mount(target));
|
|
2116
|
+
return this
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
const renderer = async (target, component, props, wrapper) =>{
|
|
2122
|
+
if(!component) return;
|
|
2123
|
+
let mounted = await component(props);
|
|
2124
|
+
if(wrapper) mounted = wrapper(mounted);
|
|
2125
|
+
|
|
2126
|
+
if(mounted instanceof UILayout)
|
|
2127
|
+
return mounted.ui.forEach(el => el.mount(target))
|
|
2128
|
+
|
|
2129
|
+
mounted instanceof Array
|
|
2130
|
+
? mounted.forEach(el => el.mount(target))
|
|
2131
|
+
: mounted.mount(target);
|
|
2132
|
+
};
|
|
2133
|
+
|
|
2134
|
+
async function createFileBasedRouter({
|
|
2135
|
+
pages = {},
|
|
2136
|
+
url = typeof location !== 'undefined' ? location.pathname : '/',
|
|
2137
|
+
target = typeof document !== 'undefined' ? document.body : null,
|
|
2138
|
+
extensions = ['js', 'ts'],
|
|
2139
|
+
base = '/',
|
|
2140
|
+
lazy = true,
|
|
2141
|
+
renderer: renderer$1 = renderer,
|
|
2142
|
+
wrapper,
|
|
2143
|
+
namedExportHandler = {
|
|
2144
|
+
Get: async (exportedFn, context) => {
|
|
2145
|
+
if (typeof exportedFn === 'function') {
|
|
2146
|
+
return await exportedFn(context);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
} = {}) {
|
|
2151
|
+
// Normalize target element safely for UI frameworks/DOM wrapper objects
|
|
2152
|
+
let mountTarget = target;
|
|
2153
|
+
if (target && typeof target === 'object' && 'element' in target) {
|
|
2154
|
+
mountTarget = target.element;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// 1. Normalize base path
|
|
2158
|
+
let cleanBase = base === '.' ? '' : base.replace(/\/$/, '');
|
|
2159
|
+
if (cleanBase && !cleanBase.startsWith('/')) {
|
|
2160
|
+
cleanBase = '/' + cleanBase;
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
// 2. Normalize and extract current URL path
|
|
2164
|
+
let rawPath = decodeURIComponent(url.replace(/\/$/, '')) || '/';
|
|
2165
|
+
|
|
2166
|
+
// Strip base prefix if matched
|
|
2167
|
+
if (cleanBase && rawPath.startsWith(cleanBase)) {
|
|
2168
|
+
rawPath = rawPath.slice(cleanBase.length) || '/';
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
let currentPath = rawPath.startsWith('/') ? rawPath : '/' + rawPath;
|
|
2172
|
+
|
|
2173
|
+
const rawRoutes = Object.keys(pages);
|
|
2174
|
+
const root = get_root(rawRoutes);
|
|
2175
|
+
|
|
2176
|
+
let mask = null;
|
|
2177
|
+
let component = null;
|
|
2178
|
+
let module = null;
|
|
2179
|
+
let rawRoute = null;
|
|
2180
|
+
let namedExports = {};
|
|
2181
|
+
|
|
2182
|
+
if (lazy) {
|
|
2183
|
+
// --- LAZY MODE ---
|
|
2184
|
+
// 3a. Map normalized masks to raw routes without executing module imports
|
|
2185
|
+
const maskToRawRouteMap = {};
|
|
2186
|
+
for (const route of rawRoutes) {
|
|
2187
|
+
const maskKey = normalize_path(route, root, extensions);
|
|
2188
|
+
maskToRawRouteMap[maskKey] = route;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
// 4a. Sort route masks by precedence
|
|
2192
|
+
const sortedMasks = sort_routes(Object.keys(maskToRawRouteMap));
|
|
2193
|
+
|
|
2194
|
+
// 5a. Match current path against route masks first
|
|
2195
|
+
for (const routeMask of sortedMasks) {
|
|
2196
|
+
if (routes_matcher(routeMask, currentPath)) {
|
|
2197
|
+
mask = routeMask;
|
|
2198
|
+
break;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
// Early exit if no route matches (0 dynamic imports loaded)
|
|
2203
|
+
if (mask === null) {
|
|
2204
|
+
return { mask: null, component: null, params: {}, matched: false };
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
// 6a. Import ONLY the matched module
|
|
2208
|
+
rawRoute = maskToRawRouteMap[mask];
|
|
2209
|
+
try {
|
|
2210
|
+
module = await pages[rawRoute]();
|
|
2211
|
+
const { default: cmp, ...restExports } = module;
|
|
2212
|
+
component = cmp;
|
|
2213
|
+
namedExports = restExports;
|
|
2214
|
+
} catch (error) {
|
|
2215
|
+
console.error(`[Router] Failed to load module for route: ${rawRoute}`, error);
|
|
2216
|
+
return { mask: null, component: null, params: {}, matched: false, error };
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
} else {
|
|
2220
|
+
// --- EAGER MODE ---
|
|
2221
|
+
// 3b. Load all modules and map normalized keys upfront
|
|
2222
|
+
const pairs = {};
|
|
2223
|
+
const modules = {};
|
|
2224
|
+
|
|
2225
|
+
for (const route of rawRoutes) {
|
|
2226
|
+
const loadedModule = await pages[route]();
|
|
2227
|
+
const { default: cmp } = loadedModule;
|
|
2228
|
+
const normalizedKey = normalize_path(route, root, extensions);
|
|
2229
|
+
|
|
2230
|
+
pairs[normalizedKey] = cmp;
|
|
2231
|
+
modules[normalizedKey] = { module: loadedModule, rawRoute: route };
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
// 4b. Sort route keys by precedence
|
|
2235
|
+
const sortedRouteKeys = sort_routes(Object.keys(pairs));
|
|
2236
|
+
|
|
2237
|
+
// 5b. Match route
|
|
2238
|
+
for (const routePath of sortedRouteKeys) {
|
|
2239
|
+
if (routes_matcher(routePath, currentPath)) {
|
|
2240
|
+
mask = routePath;
|
|
2241
|
+
component = pairs[routePath];
|
|
2242
|
+
break;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
if (mask === null) {
|
|
2247
|
+
return { mask: null, component: null, params: {}, matched: false };
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
if (mask in modules) {
|
|
2251
|
+
module = modules[mask].module;
|
|
2252
|
+
rawRoute = modules[mask].rawRoute;
|
|
2253
|
+
const { default: _, ...restExports } = module;
|
|
2254
|
+
namedExports = restExports;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// 7. Parse dynamic parameters
|
|
2259
|
+
const params = is_dynamic(mask) ? dynamic_routes_parser(mask, currentPath) : {};
|
|
2260
|
+
|
|
2261
|
+
// 8. Execute named exports handler for matched module
|
|
2262
|
+
for (const exportName in namedExportHandler) {
|
|
2263
|
+
if (exportName in namedExports && typeof namedExportHandler[exportName] === 'function') {
|
|
2264
|
+
await namedExportHandler[exportName](namedExports[exportName], {
|
|
2265
|
+
route: rawRoute,
|
|
2266
|
+
mask,
|
|
2267
|
+
module,
|
|
2268
|
+
currentPath,
|
|
2269
|
+
params
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// 9. Render component
|
|
2275
|
+
if (mountTarget && typeof renderer$1 === 'function') {
|
|
2276
|
+
await renderer$1(mountTarget, component, params, wrapper);
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// Return router state
|
|
2280
|
+
return {
|
|
2281
|
+
mask,
|
|
2282
|
+
component,
|
|
2283
|
+
params,
|
|
2284
|
+
namedExports,
|
|
2285
|
+
matched: true
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
const parseQueryParams = queryString => Object.fromEntries(new URLSearchParams(globalThis?.location?.search));
|
|
2290
|
+
|
|
2291
|
+
function useQueryParams() {
|
|
2292
|
+
const getParams = () =>
|
|
2293
|
+
parseQueryParams();
|
|
2294
|
+
|
|
2295
|
+
const setParams = (updates, merge = true) => {
|
|
2296
|
+
const current = getParams();
|
|
2297
|
+
|
|
2298
|
+
const next =
|
|
2299
|
+
typeof updates === "function"
|
|
2300
|
+
? updates(current)
|
|
2301
|
+
: updates;
|
|
2302
|
+
|
|
2303
|
+
const finalParams = merge
|
|
2304
|
+
? { ...current, ...next }
|
|
2305
|
+
: next;
|
|
2306
|
+
|
|
2307
|
+
const search = new URLSearchParams(finalParams).toString();
|
|
2308
|
+
|
|
2309
|
+
window.history.pushState(
|
|
2310
|
+
{},
|
|
2311
|
+
"",
|
|
2312
|
+
`${window.location.pathname}${search ? `?${search}` : ""}`
|
|
2313
|
+
);
|
|
2314
|
+
|
|
2315
|
+
window.dispatchEvent(
|
|
2316
|
+
new CustomEvent("queryparamschange", {
|
|
2317
|
+
detail: finalParams
|
|
2318
|
+
})
|
|
2319
|
+
);
|
|
2320
|
+
};
|
|
2321
|
+
|
|
2322
|
+
return [getParams, setParams];
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
function watchQueryParams(callback) {
|
|
2326
|
+
let previousSearch = location.search;
|
|
2327
|
+
|
|
2328
|
+
const notify = () => {
|
|
2329
|
+
const currentSearch = location.search;
|
|
2330
|
+
|
|
2331
|
+
if (currentSearch === previousSearch) {
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
previousSearch = currentSearch;
|
|
2336
|
+
callback(parseQueryParams());
|
|
2337
|
+
};
|
|
2338
|
+
|
|
2339
|
+
window.addEventListener("popstate", notify);
|
|
2340
|
+
|
|
2341
|
+
const pushState = history.pushState;
|
|
2342
|
+
history.pushState = function (...args) {
|
|
2343
|
+
pushState.apply(this, args);
|
|
2344
|
+
notify();
|
|
2345
|
+
};
|
|
2346
|
+
|
|
2347
|
+
const replaceState = history.replaceState;
|
|
2348
|
+
history.replaceState = function (...args) {
|
|
2349
|
+
replaceState.apply(this, args);
|
|
2350
|
+
notify();
|
|
2351
|
+
};
|
|
2352
|
+
|
|
2353
|
+
callback(parseQueryParams());
|
|
2354
|
+
|
|
2355
|
+
return () => {
|
|
2356
|
+
window.removeEventListener("popstate", notify);
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
class UIView extends UIElement{
|
|
2361
|
+
constructor(...items){
|
|
2362
|
+
super({element : 'div', name : 'view'});
|
|
2363
|
+
this.append(...items);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
const View = (...items) => call_with_optional_props(UIElement);
|
|
2368
|
+
|
|
2369
|
+
class UISuspense extends UIElement{
|
|
2370
|
+
constructor(fallback_ui, callback){
|
|
2371
|
+
super({element : "div", name : "suspense"});
|
|
2372
|
+
this.setAttr({
|
|
2373
|
+
dataTemp : "suspense"
|
|
2374
|
+
});
|
|
2375
|
+
this.fallback_ui = fallback_ui;
|
|
2376
|
+
this.append(fallback_ui);
|
|
2377
|
+
(async ()=>{
|
|
2378
|
+
try{
|
|
2379
|
+
const ui = await callback();
|
|
2380
|
+
fallback_ui.unmount();
|
|
2381
|
+
this.append(ui);
|
|
2382
|
+
}
|
|
2383
|
+
catch(error){
|
|
2384
|
+
console.log({error});
|
|
2385
|
+
}
|
|
2386
|
+
})();
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
const Suspense = (fallback_ui, callback) => new UISuspense(fallback_ui, callback);
|
|
2391
|
+
|
|
2392
|
+
class UISwap extends UIElement {
|
|
2393
|
+
#DISPLAYS_MAP = new WeakMap()
|
|
2394
|
+
constructor({ activeIndex = 0 } = {}, ...items) {
|
|
2395
|
+
super({ element: 'div' });
|
|
2396
|
+
this.style({ display: 'contents' });
|
|
2397
|
+
|
|
2398
|
+
this.states = {
|
|
2399
|
+
activeIndex
|
|
2400
|
+
};
|
|
2401
|
+
|
|
2402
|
+
this.append(...items);
|
|
2403
|
+
requestAnimationFrame(() => {
|
|
2404
|
+
this.render();
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
get activeItem(){
|
|
2408
|
+
return this.items[this.states.activeIndex]
|
|
2409
|
+
}
|
|
2410
|
+
render() {
|
|
2411
|
+
this.items.forEach((n, i) => {
|
|
2412
|
+
|
|
2413
|
+
const initialDisplay = getComputedStyle(n.element).display;
|
|
2414
|
+
this.#DISPLAYS_MAP.set(n, initialDisplay);
|
|
2415
|
+
|
|
2416
|
+
if (i !== this.states.activeIndex) {
|
|
2417
|
+
n.style({ display: 'none' });
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
get DS(){
|
|
2422
|
+
return this.#DISPLAYS_MAP
|
|
2423
|
+
}
|
|
2424
|
+
next(n = 1) {
|
|
2425
|
+
return this.activate(this.states.activeIndex + n);
|
|
2426
|
+
}
|
|
2427
|
+
previous(n = 1) {
|
|
2428
|
+
return this.activate(this.states.activeIndex - n);
|
|
2429
|
+
}
|
|
2430
|
+
activate(index) {
|
|
2431
|
+
if (!this.items.length) return this;
|
|
2432
|
+
|
|
2433
|
+
const currentItem = this.items.at(this.states.activeIndex);
|
|
2434
|
+
|
|
2435
|
+
if (currentItem) {
|
|
2436
|
+
currentItem.style({ display: 'none' });
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
const len = this.items.length;
|
|
2440
|
+
this.states.activeIndex = ((index % len) + len) % len;
|
|
2441
|
+
|
|
2442
|
+
const activeItem = this.items.at(this.states.activeIndex);
|
|
2443
|
+
if (activeItem) {
|
|
2444
|
+
const restoredDisplay = this.#DISPLAYS_MAP.get(activeItem) || 'block';
|
|
2445
|
+
activeItem.style({ display: restoredDisplay === 'none' ? 'block' : restoredDisplay });
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
return this;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
const Swap = call_with_optional_props(UISwap);
|
|
2453
|
+
|
|
2454
|
+
class UIFragment extends UIElement{
|
|
2455
|
+
constructor(...items){
|
|
2456
|
+
super({ element : document.createDocumentFragment()});
|
|
2457
|
+
this.append(...items);
|
|
2458
|
+
}
|
|
2459
|
+
isFragment(){
|
|
2460
|
+
return true
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
const Fragment = (...items) => new UIFragment(...items);
|
|
2465
|
+
|
|
2466
|
+
class UIFor extends UIFragment{
|
|
2467
|
+
constructor({each, fallback, mapFn = () => {}} = {}){
|
|
2468
|
+
super();
|
|
2469
|
+
this.config = {
|
|
2470
|
+
each,
|
|
2471
|
+
fallback,
|
|
2472
|
+
mapFn
|
|
2473
|
+
};
|
|
2474
|
+
this.append(
|
|
2475
|
+
...this.config.each.map((n,i) => this.config.mapFn(n, i))
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
const For = ({each, fallback, mapFn} = {}) => new UIFor({each, fallback, mapFn});
|
|
2481
|
+
|
|
2482
|
+
class UISwitch extends UIElement{
|
|
2483
|
+
constructor(key, cases){
|
|
2484
|
+
super();
|
|
2485
|
+
this.key = key;
|
|
2486
|
+
this.cases = cases;
|
|
2487
|
+
this.init();
|
|
2488
|
+
}
|
|
2489
|
+
init(){
|
|
2490
|
+
Object.values(this.cases).filter(n=>n != this.current).forEach(n=>n.unmount());
|
|
2491
|
+
super.init(this.current.element);
|
|
2492
|
+
}
|
|
2493
|
+
get current(){
|
|
2494
|
+
const matched = Object.keys(this.cases).find(n => n == this.key) ?? 'default';
|
|
2495
|
+
return this.cases[matched]
|
|
2496
|
+
}
|
|
2497
|
+
updateKey(key){
|
|
2498
|
+
this.key = key;
|
|
2499
|
+
this.replaceElementWith(this.current.element);
|
|
2500
|
+
// this.cache.element.replaceWith(this.current.element)
|
|
2501
|
+
// this.cache.element = this.current.element;
|
|
2502
|
+
return this;
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
const Switch=({key, cases})=> new UISwitch(key, cases);
|
|
2508
|
+
|
|
2509
|
+
// export const Switch=({key, cases}) => {
|
|
2510
|
+
// const matched = Object.keys(cases).find(n => n == key) ?? 'default';
|
|
2511
|
+
// return this.cases[matched]()
|
|
2512
|
+
// }
|
|
2513
|
+
|
|
2514
|
+
class UIHTMLWrapper extends UIElement {
|
|
2515
|
+
constructor(content){
|
|
2516
|
+
super({element : 'div', name : 'html_wrappper'});
|
|
2517
|
+
this.element.append(html2dom(content));
|
|
2518
|
+
this.style({
|
|
2519
|
+
display : 'contents'
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
function html2dom(htmlString) {
|
|
2525
|
+
if(globalThis?.DOMParser){
|
|
2526
|
+
const parser = new DOMParser();
|
|
2527
|
+
const doc = parser.parseFromString(`<div>${htmlString}</div>`, 'text/html');
|
|
2528
|
+
doc.body.firstChild.style.display = "contents";
|
|
2529
|
+
return doc.body.firstChild;
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
const HTMLWrapper = (content) => new UIHTMLWrapper(content);
|
|
2534
|
+
|
|
2535
|
+
class UISVGWrapper extends UIElement {
|
|
2536
|
+
constructor(content){
|
|
2537
|
+
super({element : 'div', name : 'html_wrappper'});
|
|
2538
|
+
this.element.append(svg2dom(content));
|
|
2539
|
+
this.style({
|
|
2540
|
+
display : 'contents'
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
function svg2dom(svgString) {
|
|
2546
|
+
if (typeof DOMParser !== "undefined") {
|
|
2547
|
+
const parser = new DOMParser();
|
|
2548
|
+
const doc = parser.parseFromString(svgString.trim(), "image/svg+xml");
|
|
2549
|
+
const svg = doc.documentElement;
|
|
2550
|
+
|
|
2551
|
+
if (svg.nodeName === "parsererror") {
|
|
2552
|
+
throw new Error("Invalid SVG string");
|
|
2553
|
+
}
|
|
2554
|
+
if(svg.hasAttribute('xmlns')) return svg
|
|
2555
|
+
// TO Fix ...
|
|
2556
|
+
const {children, attributes} = svg;
|
|
2557
|
+
const element = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
2558
|
+
for(let {name, value} of attributes){
|
|
2559
|
+
element.setAttribute(name, value);
|
|
2560
|
+
}
|
|
2561
|
+
element.append(...children);
|
|
2562
|
+
return element;
|
|
2563
|
+
}
|
|
2564
|
+
throw new Error("DOMParser is not available in this environment");
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
const SVGWrapper = (content) => new UISVGWrapper(content);
|
|
1737
2568
|
|
|
1738
2569
|
if(globalThis?.document){
|
|
1739
2570
|
document?.addEventListener('DOMContentLoaded', __Ziko__.__Config__.init());
|
|
@@ -1747,20 +2578,35 @@ exports.Clock = Clock;
|
|
|
1747
2578
|
exports.E = E;
|
|
1748
2579
|
exports.EPSILON = EPSILON;
|
|
1749
2580
|
exports.EventController = EventController;
|
|
2581
|
+
exports.For = For;
|
|
2582
|
+
exports.Fragment = Fragment;
|
|
2583
|
+
exports.HTMLWrapper = HTMLWrapper;
|
|
1750
2584
|
exports.KeyListeners = KeyListeners;
|
|
1751
2585
|
exports.PI = PI;
|
|
1752
2586
|
exports.PtrListeners = PtrListeners;
|
|
2587
|
+
exports.SVGWrapper = SVGWrapper;
|
|
1753
2588
|
exports.Scheduler = Scheduler;
|
|
2589
|
+
exports.Suspense = Suspense;
|
|
2590
|
+
exports.Swap = Swap;
|
|
1754
2591
|
exports.SwipeEvent = SwipeEvent;
|
|
2592
|
+
exports.Switch = Switch;
|
|
1755
2593
|
exports.Tick = Tick;
|
|
1756
2594
|
exports.TimeScheduler = TimeScheduler;
|
|
1757
2595
|
exports.UIElement = UIElement;
|
|
2596
|
+
exports.UIFragment = UIFragment;
|
|
2597
|
+
exports.UIHTMLWrapper = UIHTMLWrapper;
|
|
2598
|
+
exports.UISVGWrapper = UISVGWrapper;
|
|
2599
|
+
exports.UISuspense = UISuspense;
|
|
2600
|
+
exports.UISwap = UISwap;
|
|
2601
|
+
exports.UISwitch = UISwitch;
|
|
2602
|
+
exports.UIView = UIView;
|
|
2603
|
+
exports.View = View;
|
|
1758
2604
|
exports.ViewEvent = ViewEvent;
|
|
1759
2605
|
exports.ViewListeners = ViewListeners;
|
|
1760
2606
|
exports.apply_fun = apply_fun;
|
|
1761
2607
|
exports.atan2 = atan2;
|
|
1762
2608
|
exports.camel2constantcase = camel2constantcase;
|
|
1763
|
-
exports.camel2hyphencase = camel2hyphencase
|
|
2609
|
+
exports.camel2hyphencase = camel2hyphencase;
|
|
1764
2610
|
exports.camel2pascalcase = camel2pascalcase;
|
|
1765
2611
|
exports.camel2snakecase = camel2snakecase;
|
|
1766
2612
|
exports.clamp = clamp;
|
|
@@ -1769,6 +2615,7 @@ exports.constant2camelcase = constant2camelcase;
|
|
|
1769
2615
|
exports.constant2hyphencase = constant2hyphencase;
|
|
1770
2616
|
exports.constant2pascalcase = constant2pascalcase;
|
|
1771
2617
|
exports.constant2snakecase = constant2snakecase;
|
|
2618
|
+
exports.createFileBasedRouter = createFileBasedRouter;
|
|
1772
2619
|
exports.debounce = debounce;
|
|
1773
2620
|
exports.deg2rad = deg2rad;
|
|
1774
2621
|
exports.hyphen2camelcase = hyphen2camelcase;
|
|
@@ -1778,14 +2625,14 @@ exports.hyphen2snakecase = hyphen2snakecase;
|
|
|
1778
2625
|
exports.hypot = hypot;
|
|
1779
2626
|
exports.isStateGetter = isStateGetter;
|
|
1780
2627
|
exports.is_anagram = is_anagram;
|
|
1781
|
-
exports.is_camelcase = is_camelcase
|
|
2628
|
+
exports.is_camelcase = is_camelcase;
|
|
1782
2629
|
exports.is_hyphencase = is_hyphencase;
|
|
1783
2630
|
exports.is_isogram = is_isogram;
|
|
1784
2631
|
exports.is_palindrome = is_palindrome;
|
|
1785
2632
|
exports.is_pascalcalse = is_pascalcalse;
|
|
1786
2633
|
exports.is_snakeCase = is_snakeCase;
|
|
1787
2634
|
exports.lerp = lerp;
|
|
1788
|
-
exports.map = map
|
|
2635
|
+
exports.map = map;
|
|
1789
2636
|
exports.mapfun = mapfun;
|
|
1790
2637
|
exports.norm = norm;
|
|
1791
2638
|
exports.pascal2camelcase = pascal2camelcase;
|
|
@@ -1808,5 +2655,7 @@ exports.timeout = timeout;
|
|
|
1808
2655
|
exports.useDerived = useDerived;
|
|
1809
2656
|
exports.useEffect = useEffect;
|
|
1810
2657
|
exports.useEventEmitter = useEventEmitter;
|
|
2658
|
+
exports.useQueryParams = useQueryParams;
|
|
1811
2659
|
exports.useReactive = useReactive;
|
|
1812
2660
|
exports.useState = useState;
|
|
2661
|
+
exports.watchQueryParams = watchQueryParams;
|