j20 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -390,7 +390,6 @@ const styleObjectToString = (style) => {
390
390
  }
391
391
  return styleString.trim();
392
392
  };
393
-
394
393
  const update = (node, key, oldValue, newValue) => {
395
394
  if (isEvent(key)) {
396
395
  node.removeEventListener(getEventName(key), oldValue);
@@ -447,6 +446,28 @@ const add = (node, key, newValue) => {
447
446
  }
448
447
  }
449
448
  };
449
+ const bindNode = (node, props) => {
450
+ let oldProps = {};
451
+ effect(() => {
452
+ const newProps = { ...props() };
453
+ const newKeys = Object.keys(newProps);
454
+ const oldKeys = Object.keys(oldProps);
455
+ const allKeys = new Set([...newKeys, ...oldKeys]);
456
+ for (const key of allKeys) {
457
+ if (key === "children")
458
+ continue;
459
+ oldKeys.includes(key)
460
+ ? newKeys.includes(key)
461
+ ? Object.is(oldProps[key], newProps[key])
462
+ ? null
463
+ : update(node, key, oldProps[key], newProps[key])
464
+ : unset(node, key, oldProps[key])
465
+ : add(node, key, newProps[key]);
466
+ }
467
+ oldProps = newProps;
468
+ });
469
+ };
470
+
450
471
  const createDom = (tag, props, children) => {
451
472
  const node = tag;
452
473
  let oldProps = {};
@@ -481,137 +502,8 @@ const createDom = (tag, props, children) => {
481
502
  return node;
482
503
  };
483
504
 
484
- const stack = [];
485
- const getCurrentInstance = () => stack.at(-1);
486
- // api security guard
487
- const securityGetCurrentInstance = () => {
488
- const instance = getCurrentInstance();
489
- if (!instance)
490
- throw new Error("Do not run this api out of component");
491
- return instance;
492
- };
493
- const instanceInit = (parent) => {
494
- const id = generateId();
495
- const parentInstance = parent ?? getCurrentInstance();
496
- const instance = {
497
- parent: parentInstance,
498
- id,
499
- range: [document.createTextNode(""), document.createTextNode("")],
500
- };
501
- if (parentInstance) {
502
- if (parentInstance.children) {
503
- parentInstance.children.push(instance);
504
- }
505
- else {
506
- parentInstance.children = [instance];
507
- }
508
- }
509
- return instance;
510
- };
511
- const mounts = [];
512
- const instanceCreateElement = (instance, runner) => {
513
- stack.push(instance);
514
- const fragment = createDom(document.createDocumentFragment(), undefined, () => runner());
515
- fragment.prepend(instance.range[0]);
516
- fragment.append(instance.range[1]);
517
- if (instance.mounts) {
518
- mounts.push(...instance.mounts);
519
- instance.mounts = undefined;
520
- }
521
- stack.pop();
522
- if (stack.length === 0) {
523
- requestAnimationFrame(() => {
524
- mounts.forEach((mount) => mount());
525
- mounts.length = 0;
526
- });
527
- }
528
- return fragment;
529
- };
530
- const instanceCreate = (runner, parent) => {
531
- const instance = instanceInit(parent);
532
- return [instance, instanceCreateElement(instance, runner)];
533
- };
534
- const instanceGetElements = (instance) => {
535
- const [start, end] = instance.range;
536
- const els = [start];
537
- let index = start;
538
- while ((index = index.nextSibling)) {
539
- els.push(index);
540
- if (index === end)
541
- break;
542
- }
543
- return els;
544
- };
545
- const instanceDestroy = (parent, instance) => {
546
- // 使用栈来实现迭代式的深度优先销毁,避免递归开销
547
- // 关键:需要跟踪每个实例的销毁状态(是否已销毁子节点)
548
- const stack = [
549
- { inst: instance, childrenDestroyed: false },
550
- ];
551
- while (stack.length > 0) {
552
- const current = stack[stack.length - 1];
553
- if (!current.childrenDestroyed) {
554
- // 第一次访问:先标记子节点已销毁,然后将子节点入栈
555
- current.childrenDestroyed = true;
556
- if (current.inst.children && current.inst.children.length > 0) {
557
- // 倒序添加子节点,保持与递归相同的销毁顺序
558
- for (let i = current.inst.children.length - 1; i >= 0; i--) {
559
- stack.push({
560
- inst: current.inst.children[i],
561
- childrenDestroyed: false,
562
- });
563
- }
564
- }
565
- }
566
- else {
567
- // 第二次访问:子节点已全部销毁,现在销毁当前节点
568
- const inst = current.inst;
569
- // 执行清理函数
570
- inst.disposes?.forEach((dispose) => {
571
- try {
572
- dispose();
573
- }
574
- catch (e) {
575
- console.error("Error during instance dispose:", e);
576
- }
577
- });
578
- // 清空 dispose 数组,但保留数组引用
579
- if (inst.disposes) {
580
- inst.disposes.length = 0;
581
- }
582
- // 清空子节点引用
583
- if (inst.children) {
584
- inst.children = [];
585
- }
586
- stack.pop();
587
- }
588
- }
589
- // 从父实例中移除此实例的引用
590
- if (parent && parent.children) {
591
- parent.children = parent.children.filter((c) => c !== instance);
592
- }
593
- };
594
-
595
- const effect = (effectFn) => {
596
- const effectInstance = effect$1(effectFn);
597
- const currentInstance = securityGetCurrentInstance();
598
- if (!currentInstance.disposes)
599
- currentInstance.disposes = [];
600
- currentInstance.disposes.push(() => effectInstance.dispose());
601
- return effectInstance;
602
- };
603
-
604
- function ref(init) {
605
- return {
606
- current: init ?? null,
607
- };
608
- }
609
-
610
505
  const BRAND = "j20";
611
506
 
612
- let host;
613
- const setHost = (h) => (host = h);
614
- const getHost = () => host;
615
507
  const registerWebComponent = (Comp) => {
616
508
  if (!Comp.customElement) {
617
509
  throw new Error("Custom element options is not provided");
@@ -640,14 +532,13 @@ const buildClass = (Comp) => {
640
532
  .map(([, value]) => value.attribute)
641
533
  .filter((i) => i != undefined);
642
534
  }
535
+ static brand = BRAND;
536
+ brand = BRAND;
643
537
  #shadow;
644
538
  #props = {};
645
- brand = BRAND;
646
539
  customElement = customElement;
647
- lazy;
648
- constructor(lazy) {
540
+ constructor() {
649
541
  super();
650
- this.lazy = lazy;
651
542
  if (customElement.tag && customElement.mode) {
652
543
  this.#shadow = this.attachShadow({ mode: customElement.mode });
653
544
  if (styleSheet)
@@ -661,9 +552,9 @@ const buildClass = (Comp) => {
661
552
  }, {});
662
553
  }
663
554
  lazyInit() {
664
- setHost(this);
665
555
  const host = this;
666
- const [instance, fragment] = instanceCreate(() => {
556
+ hostStack.push(host);
557
+ const [, fragment] = instanceCreate(() => {
667
558
  return Comp(computed(() => {
668
559
  return new Proxy(this.#props, {
669
560
  get(target, p, receiver) {
@@ -679,8 +570,7 @@ const buildClass = (Comp) => {
679
570
  });
680
571
  }));
681
572
  }, getCurrentInstance());
682
- setHost(undefined);
683
- instance.host = this;
573
+ hostStack.pop();
684
574
  if (this.#shadow) {
685
575
  this.#shadow.appendChild(fragment);
686
576
  }
@@ -715,8 +605,6 @@ const buildClass = (Comp) => {
715
605
  }
716
606
  }
717
607
  attributeChangedCallback(name, oldValue, newValue) {
718
- if (this.lazy)
719
- return;
720
608
  const attrs = NewElementClass.observedAttributes;
721
609
  const propSignal = this.#props[a2p[name]];
722
610
  if (propSignal && attrs.includes(name)) {
@@ -758,8 +646,183 @@ const buildMap = (customElement) => {
758
646
  return { a2p, p2a, a2v };
759
647
  };
760
648
 
649
+ const hostStack = [];
650
+ const getCurrentHost = () => {
651
+ return hostStack.at(-1);
652
+ };
653
+ const createWebComponent = (tag, props) => {
654
+ const customElement = tag.customElement;
655
+ const Exist = customElements.get(customElement.tag);
656
+ let el;
657
+ if (Exist) {
658
+ if (Exist.brand !== BRAND) {
659
+ throw new Error(`Custom element [${customElement.tag}] is already registered`);
660
+ }
661
+ else {
662
+ el = new Exist();
663
+ }
664
+ }
665
+ else {
666
+ const NewClass = buildClass(tag);
667
+ customElements.define(customElement.tag, NewClass);
668
+ el = new NewClass();
669
+ }
670
+ props && bindNode(el, props);
671
+ const runner = () => {
672
+ let childrenGetter = () => untrack(() => (props ? props().children : undefined));
673
+ const ret = tag(computed(() => {
674
+ const retProps = props ? props() : {};
675
+ childrenGetter =
676
+ Object.getOwnPropertyDescriptor(retProps, "children")?.get ??
677
+ (() => undefined);
678
+ delete retProps.children;
679
+ return retProps;
680
+ }));
681
+ if (el.customElement.mode) {
682
+ el.appendToShadowDom(getChildren([].concat(ret)));
683
+ childrenGetter &&
684
+ el.appendToLightDom(getChildren([].concat(childrenGetter())));
685
+ }
686
+ else {
687
+ el.appendToBody(getChildren([].concat(ret)));
688
+ }
689
+ return el;
690
+ };
691
+ hostStack.push(el);
692
+ let [, fragment] = instanceCreate(runner);
693
+ hostStack.pop();
694
+ return fragment;
695
+ };
696
+
697
+ const instanceStack = [];
698
+ const getCurrentInstance = () => instanceStack.at(-1);
699
+ // api security guard
700
+ const securityGetCurrentInstance = () => {
701
+ const instance = getCurrentInstance();
702
+ if (!instance)
703
+ throw new Error("Do not run this api out of component");
704
+ return instance;
705
+ };
706
+ const instanceInit = (parent) => {
707
+ const id = generateId();
708
+ const parentInstance = parent ?? getCurrentInstance();
709
+ const instance = {
710
+ parent: parentInstance,
711
+ id,
712
+ host: getCurrentHost(),
713
+ range: [document.createTextNode(""), document.createTextNode("")],
714
+ };
715
+ if (parentInstance) {
716
+ if (parentInstance.children) {
717
+ parentInstance.children.push(instance);
718
+ }
719
+ else {
720
+ parentInstance.children = [instance];
721
+ }
722
+ }
723
+ return instance;
724
+ };
725
+ const mounts = [];
726
+ const instanceCreateElement = (instance, runner) => {
727
+ instanceStack.push(instance);
728
+ const fragment = createDom(document.createDocumentFragment(), undefined, () => runner());
729
+ fragment.prepend(instance.range[0]);
730
+ fragment.append(instance.range[1]);
731
+ if (instance.mounts) {
732
+ mounts.push(...instance.mounts);
733
+ instance.mounts = undefined;
734
+ }
735
+ instanceStack.pop();
736
+ if (instanceStack.length === 0) {
737
+ requestAnimationFrame(() => {
738
+ mounts.forEach((mount) => mount());
739
+ mounts.length = 0;
740
+ });
741
+ }
742
+ return fragment;
743
+ };
744
+ const instanceCreate = (runner, parent) => {
745
+ const instance = instanceInit(parent);
746
+ return [instance, instanceCreateElement(instance, runner)];
747
+ };
748
+ const instanceGetElements = (instance) => {
749
+ const [start, end] = instance.range;
750
+ const els = [start];
751
+ let index = start;
752
+ while ((index = index.nextSibling)) {
753
+ els.push(index);
754
+ if (index === end)
755
+ break;
756
+ }
757
+ return els;
758
+ };
759
+ const instanceDestroy = (parent, instance) => {
760
+ // 使用栈来实现迭代式的深度优先销毁,避免递归开销
761
+ // 关键:需要跟踪每个实例的销毁状态(是否已销毁子节点)
762
+ const stack = [
763
+ { inst: instance, childrenDestroyed: false },
764
+ ];
765
+ while (stack.length > 0) {
766
+ const current = stack[stack.length - 1];
767
+ if (!current.childrenDestroyed) {
768
+ // 第一次访问:先标记子节点已销毁,然后将子节点入栈
769
+ current.childrenDestroyed = true;
770
+ if (current.inst.children && current.inst.children.length > 0) {
771
+ // 倒序添加子节点,保持与递归相同的销毁顺序
772
+ for (let i = current.inst.children.length - 1; i >= 0; i--) {
773
+ stack.push({
774
+ inst: current.inst.children[i],
775
+ childrenDestroyed: false,
776
+ });
777
+ }
778
+ }
779
+ }
780
+ else {
781
+ // 第二次访问:子节点已全部销毁,现在销毁当前节点
782
+ const inst = current.inst;
783
+ // 执行清理函数
784
+ inst.disposes?.forEach((dispose) => {
785
+ try {
786
+ dispose();
787
+ }
788
+ catch (e) {
789
+ console.error("Error during instance dispose:", e);
790
+ }
791
+ });
792
+ // 清空 dispose 数组,但保留数组引用
793
+ if (inst.disposes) {
794
+ inst.disposes.length = 0;
795
+ }
796
+ // 清空子节点引用
797
+ if (inst.children) {
798
+ inst.children = [];
799
+ }
800
+ stack.pop();
801
+ }
802
+ }
803
+ // 从父实例中移除此实例的引用
804
+ if (parent && parent.children) {
805
+ parent.children = parent.children.filter((c) => c !== instance);
806
+ }
807
+ };
808
+
809
+ const effect = (effectFn) => {
810
+ const effectInstance = effect$1(effectFn);
811
+ const currentInstance = securityGetCurrentInstance();
812
+ if (!currentInstance.disposes)
813
+ currentInstance.disposes = [];
814
+ currentInstance.disposes.push(() => effectInstance.dispose());
815
+ return effectInstance;
816
+ };
817
+
818
+ function ref(init) {
819
+ return {
820
+ current: init ?? null,
821
+ };
822
+ }
823
+
761
824
  const wc = () => {
762
- const host = getHost();
825
+ const host = getCurrentHost();
763
826
  if (!host) {
764
827
  throw new Error("host not found, please use `wc` in web component");
765
828
  }
@@ -857,7 +920,7 @@ const onMount = (callback) => {
857
920
 
858
921
  const onDestroy = (callback) => effect(() => callback);
859
922
 
860
- const style = (css) => {
923
+ const sheet = (css) => {
861
924
  let instance = getCurrentInstance();
862
925
  const sheet = new CSSStyleSheet();
863
926
  css && sheet.replaceSync(css);
@@ -879,6 +942,18 @@ const style = (css) => {
879
942
  }
880
943
  return sheet;
881
944
  };
945
+ const css = (css) => {
946
+ const styleId = id();
947
+ const replacedCss = css.replace(/\.(\w[\w-]*)\s*?\{/g, (_, className) => {
948
+ return `.${className}_${styleId} {`;
949
+ });
950
+ sheet(replacedCss);
951
+ return new Proxy({}, {
952
+ get: (_, prop) => {
953
+ return `${prop}_${styleId}`;
954
+ },
955
+ });
956
+ };
882
957
 
883
958
  function insertAfter(parentNode, newNode, targetNode) {
884
959
  parentNode?.insertBefore(newNode, targetNode.nextSibling);
@@ -1276,49 +1351,6 @@ const createLogicComponent = (tag, props) => {
1276
1351
  return tag(computed(() => (props ? props() : {})));
1277
1352
  };
1278
1353
 
1279
- const createWebComponent = (tag, props) => {
1280
- const runner = () => {
1281
- const customElement = tag.customElement;
1282
- const Exist = customElements.get(customElement.tag);
1283
- let el;
1284
- if (Exist) {
1285
- if (Exist.brand !== BRAND) {
1286
- throw new Error(`Custom element [${customElement.tag}] is already registered`);
1287
- }
1288
- else {
1289
- el = new Exist(true);
1290
- }
1291
- }
1292
- else {
1293
- const NewClass = buildClass(tag);
1294
- customElements.define(customElement.tag, NewClass);
1295
- el = new NewClass(true);
1296
- }
1297
- let childrenGetter = () => untrack(() => (props ? props().children : undefined));
1298
- setHost(el);
1299
- const ret = tag(computed(() => {
1300
- const retProps = props ? props() : {};
1301
- childrenGetter =
1302
- Object.getOwnPropertyDescriptor(retProps, "children")?.get ??
1303
- (() => undefined);
1304
- delete retProps.children;
1305
- return retProps;
1306
- }));
1307
- setHost(undefined);
1308
- if (el.customElement.mode) {
1309
- el.appendToShadowDom(getChildren([].concat(ret)));
1310
- childrenGetter &&
1311
- el.appendToLightDom(getChildren([].concat(childrenGetter())));
1312
- }
1313
- else {
1314
- el.appendToBody(getChildren([].concat(ret)));
1315
- }
1316
- return el;
1317
- };
1318
- let [, fragment] = instanceCreate(runner);
1319
- return fragment;
1320
- };
1321
-
1322
1354
  const createElement = (tag, props, children) => {
1323
1355
  return tag instanceof Node
1324
1356
  ? createDom(tag, props, children)
@@ -1349,15 +1381,15 @@ const template = (template) => {
1349
1381
  if (!dom) {
1350
1382
  const templateEl = isSvg
1351
1383
  ? document.createElementNS("http://www.w3.org/2000/svg", "svg")
1352
- : document.createElement("template");
1384
+ : document.createElement("div");
1353
1385
  templateEl.innerHTML = template;
1354
1386
  dom = isSvg
1355
1387
  ? templateEl.firstChild
1356
- : templateEl.content.firstChild;
1388
+ : templateEl.firstChild;
1357
1389
  }
1358
1390
  return dom.cloneNode();
1359
1391
  };
1360
1392
  };
1361
1393
 
1362
- export { $, $useContext, Case, Computed, Default, Effect, For, Fragment, If, Replace, Signal, Some, Switch, batch, computed, createComponent, createContext, createDom, createElement, createLogicComponent, createRoot, effect, getCurrentInstance, h2, id, instanceCreate, instanceCreateElement, instanceDestroy, instanceGetElements, instanceInit, jsx, jsxs, mounts, onDestroy, onMount, ref, registerWebComponent, securityGetCurrentInstance, signal, style, template, untrack, wc };
1394
+ export { $, $useContext, Case, Computed, Default, Effect, For, Fragment, If, Replace, Signal, Some, Switch, batch, computed, createComponent, createContext, createDom, createElement, createLogicComponent, createRoot, css, effect, getCurrentInstance, h2, id, instanceCreate, instanceCreateElement, instanceDestroy, instanceGetElements, instanceInit, jsx, jsxs, mounts, onDestroy, onMount, ref, registerWebComponent, securityGetCurrentInstance, sheet, signal, template, untrack, wc };
1363
1395
  //# sourceMappingURL=index.js.map