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 CHANGED
@@ -1,10 +1,10 @@
1
1
 
2
2
  /*
3
- Project: ziko.js
3
+ Project: ziko
4
4
  Author: Zakaria Elalaoui
5
- Date : Mon Sep 07 2026 22:41:50 GMT+0100 (UTC+01:00)
6
- Git-Repo : https://github.com/zakarialaoui10/ziko.js
7
- Git-Wiki : https://github.com/zakarialaoui10/ziko.js/wiki
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$1 = (x, a, b, c, d) => apply_fun(
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
- // import {
468
- // is_camelcase,
469
- // camel2hyphencase
470
- // } from '../../../data/string/index.js'
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({element, name ='', type = 'html', render = __Ziko__.__Config__.default.render, props}={}){
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(event_name, callback, {details_setter, category = 'global', isCustom = false, preventDefault = false} = {}){
1294
- if(event_name instanceof Array) event_name.forEach(
1295
- event => this.on(
1296
- event,
1297
- callback,
1298
- {details_setter, category, isCustom, preventDefault}
1299
- )
1300
- );
1301
- if(category && !this.exp.events.hasOwnProperty(category)) this.exp.events[category] = new EventController(this, category);
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(event_name, (e)=>{
1305
- if(details_setter) details_setter(EVENT);
1306
- callback(e);
1307
- },{
1308
- preventDefault
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
- _off(event, category = 'global'){
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('data-hydration-index', globalThis.__Ziko__.__HYDRATION__.index);
1336
- globalThis.__Ziko__.__HYDRATION__.register(() => this);
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)this.element.setAttribute('data-hydration-directive', 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 width(){
1364
- return this.element.getBoundingClientRect().width;
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
- get height(){
1367
- return this.element.getBoundingClientRect().height;
1343
+ size(width, height) {
1344
+ return this.style({ width, height });
1368
1345
  }
1369
- get top(){
1370
- return this.element.getBoundingClientRect().top;
1346
+ hide() {}
1347
+ show() {}
1348
+
1349
+ // Dom
1350
+
1351
+ append(...ele) {
1352
+ this.__addItems__("append", "push", ...ele);
1353
+ return this;
1371
1354
  }
1372
- get right(){
1373
- return this.element.getBoundingClientRect().right;
1355
+ prepend(...ele) {
1356
+ this.this.__addItems__("prepend", "unshift", ...ele);
1357
+ return this;
1374
1358
  }
1375
- get bottom(){
1376
- return this.element.getBoundingClientRect().bottom;
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
- get left(){
1379
- return this.element.getBoundingClientRect().left;
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 new UIElement({element : tag, name : tag, type})
1589
+ return ui
1531
1590
  }
1532
1591
  if(
1533
- ['string', 'number'].includes(typeof args[0])
1534
- || args[0] instanceof UIElement
1535
- || isStateGetter(args[0])
1536
- || args[0] instanceof HTMLElement
1537
- ) return new UIElement({element : tag, name : tag, type}).append(...args);
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
- const camel2hyphencase$1 = (text = '') => text.replace(/[A-Z]/g, match => '-' + match.toLowerCase());
1686
- const camel2snakecase = (text = '') => text.replace(/[A-Z]/g, match => '_' + match.toLowerCase());
1687
- const camel2pascalcase = (text = '') => text.charAt(0).toUpperCase() + text.slice(1);
1688
- const camel2constantcase = (text = '') => text.replace(/[A-Z]/g, match => '_' + match).toUpperCase();
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
- const pascal2snakecase = (text = '') => text.replace(/([A-Z])/g, (match, offset) => offset ? '_' + match.toLowerCase() : match.toLowerCase());
1691
- const pascal2hyphencase = (text = '') => text.replace(/([A-Z])/g, (match, offset) => offset ? '-' + match.toLowerCase() : match.toLowerCase());
1692
- const pascal2camelcase = (text = '') => text.charAt(0).toLowerCase() + text.slice(1);
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 snake2camelcase = (text = '') => text.replace(/(_\w)/g, match => match[1].toUpperCase());
1696
- const snake2hyphencase = (text = '') => text.replace(/_/g, "-");
1697
- const snake2pascalcase = (text = '') => text.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
1698
- const snake2constantcase = (text = '') => text.toUpperCase();
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
- const hyphen2camelcase = (text = '') => text.replace(/-([a-z])/g, match => match[1].toUpperCase());
1701
- const hyphen2snakecase = (text = '') => text.replace(/-/g, '_');
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
- const constant2camelcase = (text = '') => text.toLowerCase().replace(/_([a-z])/g, match => match[1].toUpperCase());
1706
- const constant2snakecase = (text = '') => text.toLowerCase();
1707
- const constant2pascalcase = (text = '') => text.toLowerCase().split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
1708
- const constant2hyphencase = (text = '') => text.toLowerCase().replace(/_/g, '-');
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
- const is_camelcase$1 = (text = '') =>{
1711
- if (text.length === 0) return false;
1712
- const camelCasePattern = /^[a-z][a-zA-Z0-9]*$/;
1713
- return camelCasePattern.test(text);
1714
- };
1715
- const is_hyphencase = (text = '') => text.split('-').length > 0;
1716
- const is_snakeCase = (text = '') => text.split('_').length > 0;
1717
- const is_pascalcalse = (text = '') => {
1718
- if (text.length === 0) return false;
1719
- const PascalCasePattern = /^[A-Z][a-zA-Z0-9]*$/;
1720
- return PascalCasePattern.test(text);
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
- const is_palindrome = text =>{
1724
- const str = text.toLocaleLowerCase();
1725
- let l = str.length,i;
1726
- for(i=0;i<l/2;i++)if(str[i]!=str[l-i-1])return false;
1727
- return true;
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
- const is_anagram = (word, words) =>{
1731
- word=word.split("").sort();
1732
- words=words.split("").sort();
1733
- return JSON.stringify(word)===JSON.stringify(words);
1734
- };
1818
+ if (maskSegment.startsWith("[") && maskSegment.endsWith("]+")) {
1819
+ if (routeSegments[j]) j++;
1820
+ i++;
1821
+ continue;
1822
+ }
1735
1823
 
1736
- const is_isogram = (text = '') => [...new Set(text.toLowerCase())].length === text.length;
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$1;
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$1;
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$1;
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;