tutuca 0.9.3 → 0.9.5

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.
@@ -1857,6 +1857,14 @@ class Pair {
1857
1857
  v = v.tail;
1858
1858
  }
1859
1859
  }
1860
+ lookupNode(pred) {
1861
+ for (const node of this) {
1862
+ if (pred(node)) {
1863
+ return node;
1864
+ }
1865
+ }
1866
+ return null;
1867
+ }
1860
1868
  }
1861
1869
 
1862
1870
  class BindFrame {
@@ -1945,7 +1953,7 @@ class Stack {
1945
1953
  return this.ctx.lookupName(name);
1946
1954
  }
1947
1955
  lookupComputed(name) {
1948
- return this.comps.lookupComputed(this.it, name);
1956
+ return this.comps.lookupComputed(this.binds.lookupNode((v) => v.isFrame)?.it, name);
1949
1957
  }
1950
1958
  getInputHandler(name) {
1951
1959
  return this.comps.getInputHandlerFor(this.it, name);
@@ -2107,6 +2115,7 @@ class Transaction {
2107
2115
  }
2108
2116
  }
2109
2117
  var isMac2 = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
2118
+ var toNullIfNaN = (v) => Number.isNaN(v) ? null : v;
2110
2119
  function getValue(e) {
2111
2120
  return e.target.type === "checkbox" ? e.target.checked : (e instanceof CustomEvent ? e.detail : e.target.value) ?? null;
2112
2121
  }
@@ -2138,6 +2147,10 @@ class InputEvent extends Transaction {
2138
2147
  switch (name) {
2139
2148
  case "value":
2140
2149
  return getValue(e);
2150
+ case "valueAsInt":
2151
+ return toNullIfNaN(parseInt(getValue(e), 10));
2152
+ case "valueAsFloat":
2153
+ return toNullIfNaN(parseFloat(getValue(e)));
2141
2154
  case "target":
2142
2155
  return e.target;
2143
2156
  case "event":
@@ -2389,7 +2402,7 @@ class App {
2389
2402
  this.render();
2390
2403
  }
2391
2404
  });
2392
- injectCss("tutuca-app", this.comps.compileStyles());
2405
+ injectCss("tutuca-app", this.comps.compileStyles(), opts?.head ?? document.head);
2393
2406
  if (opts?.noCache) {
2394
2407
  this.renderer.setNullCache();
2395
2408
  this.comps.setNullComputedCache();
@@ -2434,15 +2447,15 @@ class App {
2434
2447
  this._evictCacheId = null;
2435
2448
  }
2436
2449
  }
2437
- function injectCss(nodeId, style) {
2450
+ function injectCss(nodeId, style, styleTarget = document.head) {
2438
2451
  const styleNode = document.createElement("style");
2439
- const currentNodeWithId = document.head.querySelector(`#${nodeId}`);
2452
+ const currentNodeWithId = styleTarget.querySelector(`#${nodeId}`);
2440
2453
  if (currentNodeWithId) {
2441
- document.head.removeChild(currentNodeWithId);
2454
+ styleTarget.removeChild(currentNodeWithId);
2442
2455
  }
2443
2456
  styleNode.id = nodeId;
2444
2457
  styleNode.innerHTML = style;
2445
- document.head.appendChild(styleNode);
2458
+ styleTarget.appendChild(styleNode);
2446
2459
  }
2447
2460
  function getClosestDropTarget(target, rootNode, count) {
2448
2461
  let node = target;
@@ -2496,7 +2509,7 @@ class ParseCtxClassSetCollector extends ParseContext {
2496
2509
  const { value, thenVal, elseVal } = attr;
2497
2510
  if (thenVal !== undefined) {
2498
2511
  this._addClasses(thenVal.value);
2499
- if (elseVal) {
2512
+ if (typeof elseVal?.value === "string") {
2500
2513
  this._addClasses(elseVal.value);
2501
2514
  }
2502
2515
  } else if (typeof value?.value === "string") {
@@ -7449,12 +7462,6 @@ seqInfoByClass.set(KList, ["data-sk", klistEntries]);
7449
7462
  function isHtmlAttribute(propName) {
7450
7463
  return propName[4] === "-" && (propName[0] === "d" || propName[0] === "a");
7451
7464
  }
7452
- function getDomProp(node, propName) {
7453
- return node[propName];
7454
- }
7455
- function setDomProp(node, propName, value) {
7456
- node[propName] = value;
7457
- }
7458
7465
  function applyProperties(node, props, previous) {
7459
7466
  for (const propName in props) {
7460
7467
  const propValue = props[propName];
@@ -7462,37 +7469,42 @@ function applyProperties(node, props, previous) {
7462
7469
  removeProperty(node, propName, previous);
7463
7470
  } else if (isHtmlAttribute(propName)) {
7464
7471
  node.setAttribute(propName, propValue);
7472
+ } else if (propName === "dangerouslySetInnerHTML") {
7473
+ node.innerHTML = propValue.__html ?? "";
7474
+ } else if (typeof propValue === "object" && propValue !== null) {
7475
+ patchObject(node, previous, propName, propValue);
7476
+ } else if (propName === "className") {
7477
+ node.setAttribute("class", propValue);
7465
7478
  } else {
7466
- if (typeof propValue === "object" && propValue !== null) {
7467
- patchObject(node, previous, propName, propValue);
7468
- } else {
7469
- setDomProp(node, propName, propValue);
7470
- }
7479
+ node[propName] = propValue;
7471
7480
  }
7472
7481
  }
7473
7482
  }
7474
7483
  function removeProperty(node, propName, previous) {
7475
7484
  const previousValue = previous[propName];
7476
- if (isHtmlAttribute(propName)) {
7485
+ if (propName === "dangerouslySetInnerHTML") {
7486
+ node.innerHTML = "";
7487
+ } else if (isHtmlAttribute(propName)) {
7477
7488
  node.removeAttribute(propName);
7478
7489
  } else if (typeof previousValue === "string") {
7479
- setDomProp(node, propName, "");
7490
+ if (propName !== "className")
7491
+ node[propName] = "";
7480
7492
  const attrName = propName === "className" ? "class" : propName === "htmlFor" ? "for" : propName;
7481
7493
  node.removeAttribute(attrName);
7482
7494
  } else {
7483
- setDomProp(node, propName, null);
7495
+ node[propName] = null;
7484
7496
  }
7485
7497
  }
7486
7498
  function patchObject(node, previous, propName, propValue) {
7487
7499
  const previousValue = previous?.[propName];
7488
7500
  if (previousValue && typeof previousValue === "object" && Object.getPrototypeOf(previousValue) !== Object.getPrototypeOf(propValue)) {
7489
- setDomProp(node, propName, propValue);
7501
+ node[propName] = propValue;
7490
7502
  return;
7491
7503
  }
7492
- let current = getDomProp(node, propName);
7504
+ let current = node[propName];
7493
7505
  if (typeof current !== "object" || current === null) {
7494
- setDomProp(node, propName, {});
7495
- current = getDomProp(node, propName);
7506
+ node[propName] = {};
7507
+ current = node[propName];
7496
7508
  }
7497
7509
  const target = current;
7498
7510
  for (const k in propValue) {
@@ -7500,23 +7512,6 @@ function patchObject(node, previous, propName, propValue) {
7500
7512
  }
7501
7513
  }
7502
7514
 
7503
- class Warning {
7504
- constructor(type, message) {
7505
- this.type = type;
7506
- this.message = message;
7507
- }
7508
- }
7509
-
7510
- class DuplicatedKeysWarning extends Warning {
7511
- constructor(duplicatedKeys, parentTag, parentIndex) {
7512
- const keys = [...duplicatedKeys].join(", ");
7513
- super("DuplicatedKeys", `Duplicate keys found: [${keys}] in ${parentTag || "fragment"} at index ${parentIndex}. Nodes with duplicated keys are matched positionally.`);
7514
- this.duplicatedKeys = duplicatedKeys;
7515
- this.parentTag = parentTag;
7516
- this.parentIndex = parentIndex;
7517
- }
7518
- }
7519
-
7520
7515
  class VBase {
7521
7516
  isEqualTo(other) {
7522
7517
  return this === other;
@@ -7525,12 +7520,8 @@ class VBase {
7525
7520
  return null;
7526
7521
  }
7527
7522
  }
7528
- function getKey(node) {
7529
- return node instanceof VNode2 ? node.key : undefined;
7530
- }
7531
- function effectiveKey(node, duplicatedKeys) {
7532
- const key = getKey(node);
7533
- return key && duplicatedKeys?.has(key) ? undefined : key;
7523
+ function getKey(child) {
7524
+ return child instanceof VNode2 ? child.key : undefined;
7534
7525
  }
7535
7526
  function isIterable(obj) {
7536
7527
  return obj != null && typeof obj !== "string" && typeof obj[Symbol.iterator] === "function";
@@ -7627,13 +7618,12 @@ class VNode2 extends VBase {
7627
7618
  this.childs = childs ?? [];
7628
7619
  this.key = key != null ? String(key) : undefined;
7629
7620
  this.namespace = typeof namespace === "string" ? namespace : null;
7630
- this.attrCount = Object.keys(this.attrs).length;
7631
7621
  }
7632
7622
  get nodeType() {
7633
7623
  return 1;
7634
7624
  }
7635
7625
  isEqualTo(other) {
7636
- if (!(other instanceof VNode2) || this.tag !== other.tag || this.key !== other.key || this.namespace !== other.namespace || this.attrCount !== other.attrCount || this.childs.length !== other.childs.length) {
7626
+ if (!(other instanceof VNode2) || this.tag !== other.tag || this.key !== other.key || this.namespace !== other.namespace || this.childs.length !== other.childs.length) {
7637
7627
  return false;
7638
7628
  }
7639
7629
  for (const key in this.attrs) {
@@ -7641,6 +7631,11 @@ class VNode2 extends VBase {
7641
7631
  return false;
7642
7632
  }
7643
7633
  }
7634
+ for (const key in other.attrs) {
7635
+ if (!Object.hasOwn(this.attrs, key)) {
7636
+ return false;
7637
+ }
7638
+ }
7644
7639
  for (let i = 0;i < this.childs.length; i++) {
7645
7640
  if (!this.childs[i].isEqualTo(other.childs[i])) {
7646
7641
  return false;
@@ -7695,155 +7690,6 @@ function diffProps(a, b) {
7695
7690
  }
7696
7691
  return diff;
7697
7692
  }
7698
- function reorder(oldChildren, newChildren) {
7699
- const rawNew = keyIndex(newChildren);
7700
- if (rawNew.free.length === newChildren.length) {
7701
- return {
7702
- children: newChildren,
7703
- moves: null,
7704
- duplicatedKeys: rawNew.duplicatedKeys
7705
- };
7706
- }
7707
- const rawOld = keyIndex(oldChildren);
7708
- const duplicatedKeys = rawNew.duplicatedKeys || rawOld.duplicatedKeys ? new Set([...rawNew.duplicatedKeys || [], ...rawOld.duplicatedKeys || []]) : null;
7709
- if (rawOld.free.length === oldChildren.length) {
7710
- return {
7711
- children: newChildren,
7712
- moves: null,
7713
- duplicatedKeys
7714
- };
7715
- }
7716
- let newKeys;
7717
- let newFree;
7718
- let oldKeys;
7719
- if (duplicatedKeys) {
7720
- const updatedNew = keyIndex(newChildren, duplicatedKeys);
7721
- newKeys = updatedNew.keys;
7722
- newFree = updatedNew.free;
7723
- oldKeys = keyIndex(oldChildren, duplicatedKeys).keys;
7724
- } else {
7725
- newKeys = rawNew.keys;
7726
- newFree = rawNew.free;
7727
- oldKeys = rawOld.keys;
7728
- }
7729
- const reordered = [];
7730
- let freeIndex = 0;
7731
- const freeCount = newFree.length;
7732
- let deletedItems = 0;
7733
- for (let i = 0;i < oldChildren.length; i++) {
7734
- const oldItem = oldChildren[i];
7735
- const oldKey = effectiveKey(oldItem, duplicatedKeys);
7736
- if (oldKey) {
7737
- if (Object.hasOwn(newKeys, oldKey)) {
7738
- const itemIndex = newKeys[oldKey];
7739
- reordered.push(newChildren[itemIndex]);
7740
- } else {
7741
- deletedItems++;
7742
- reordered.push(null);
7743
- }
7744
- } else {
7745
- if (freeIndex < freeCount) {
7746
- const itemIndex = newFree[freeIndex++];
7747
- reordered.push(newChildren[itemIndex]);
7748
- } else {
7749
- deletedItems++;
7750
- reordered.push(null);
7751
- }
7752
- }
7753
- }
7754
- const lastFreeIndex = freeIndex >= newFree.length ? newChildren.length : newFree[freeIndex];
7755
- for (let j = 0;j < newChildren.length; j++) {
7756
- const newItem = newChildren[j];
7757
- const newKey = effectiveKey(newItem, duplicatedKeys);
7758
- if (newKey) {
7759
- if (!Object.hasOwn(oldKeys, newKey)) {
7760
- reordered.push(newItem);
7761
- }
7762
- } else if (j >= lastFreeIndex) {
7763
- reordered.push(newItem);
7764
- }
7765
- }
7766
- const moves = computeMoves(reordered, newChildren, newKeys, duplicatedKeys, deletedItems);
7767
- return { children: reordered, moves, duplicatedKeys };
7768
- }
7769
- function computeMoves(reordered, newChildren, newKeys, duplicatedKeys, deletedItems) {
7770
- const simulate = reordered.slice();
7771
- let simulateIndex = 0;
7772
- const removes = [];
7773
- const inserts = [];
7774
- const wantedKeys = new Array(newChildren.length);
7775
- for (let i = 0;i < newChildren.length; i++) {
7776
- wantedKeys[i] = effectiveKey(newChildren[i], duplicatedKeys);
7777
- }
7778
- for (let k = 0;k < newChildren.length; ) {
7779
- const wantedKey = wantedKeys[k];
7780
- let simulateItem = simulate[simulateIndex];
7781
- let simulateKey = effectiveKey(simulateItem, duplicatedKeys);
7782
- while (simulateItem === null && simulate.length) {
7783
- simulate.splice(simulateIndex, 1);
7784
- removes.push({ from: simulateIndex, key: null });
7785
- simulateItem = simulate[simulateIndex];
7786
- simulateKey = effectiveKey(simulateItem, duplicatedKeys);
7787
- }
7788
- if (simulateItem && simulateKey === wantedKey) {
7789
- simulateIndex++;
7790
- k++;
7791
- continue;
7792
- }
7793
- if (wantedKey) {
7794
- if (simulateKey && newKeys[simulateKey] !== k + 1) {
7795
- simulate.splice(simulateIndex, 1);
7796
- removes.push({ from: simulateIndex, key: simulateKey });
7797
- simulateItem = simulate[simulateIndex];
7798
- simulateKey = effectiveKey(simulateItem, duplicatedKeys);
7799
- if (simulateItem && simulateKey === wantedKey) {
7800
- simulateIndex++;
7801
- k++;
7802
- continue;
7803
- }
7804
- }
7805
- inserts.push({ key: wantedKey, to: k });
7806
- k++;
7807
- continue;
7808
- }
7809
- if (simulateKey) {
7810
- simulate.splice(simulateIndex, 1);
7811
- removes.push({ from: simulateIndex, key: simulateKey });
7812
- continue;
7813
- }
7814
- k++;
7815
- }
7816
- while (simulateIndex < simulate.length) {
7817
- const simulateItem = simulate[simulateIndex];
7818
- simulate.splice(simulateIndex, 1);
7819
- removes.push({
7820
- from: simulateIndex,
7821
- key: effectiveKey(simulateItem, duplicatedKeys)
7822
- });
7823
- }
7824
- if (removes.length === deletedItems && !inserts.length) {
7825
- return null;
7826
- }
7827
- return { removes, inserts };
7828
- }
7829
- function keyIndex(children, excludeKeys) {
7830
- const keys = {};
7831
- const free = [];
7832
- let duplicatedKeys = null;
7833
- for (let i = 0;i < children.length; i++) {
7834
- const key = getKey(children[i]);
7835
- if (key && !excludeKeys?.has(key)) {
7836
- if (key in keys) {
7837
- duplicatedKeys ??= new Set;
7838
- duplicatedKeys.add(key);
7839
- }
7840
- keys[key] = i;
7841
- } else {
7842
- free.push(i);
7843
- }
7844
- }
7845
- return { keys, free, duplicatedKeys };
7846
- }
7847
7693
  function replaceNode(domNode, vnode, options) {
7848
7694
  const parentNode = domNode.parentNode;
7849
7695
  const newNode = vnode.toDom(options);
@@ -7864,7 +7710,9 @@ function morphNode(domNode, source, target, opts) {
7864
7710
  if (propsDiff) {
7865
7711
  applyProperties(domNode, propsDiff, source.attrs);
7866
7712
  }
7867
- morphChildren(domNode, source.childs, target.childs, source.tag, opts);
7713
+ if (!target.attrs.dangerouslySetInnerHTML) {
7714
+ morphChildren(domNode, source.childs, target.childs, source.tag, opts);
7715
+ }
7868
7716
  return domNode;
7869
7717
  }
7870
7718
  if (source instanceof VFragment && target instanceof VFragment) {
@@ -7873,7 +7721,7 @@ function morphNode(domNode, source, target, opts) {
7873
7721
  }
7874
7722
  return replaceNode(domNode, target, opts);
7875
7723
  }
7876
- function morphChildren(parentDom, oldChilds, newChilds, parentTag, opts) {
7724
+ function morphChildren(parentDom, oldChilds, newChilds, _parentTag, opts) {
7877
7725
  if (oldChilds.length === 0) {
7878
7726
  for (const child of newChilds) {
7879
7727
  const node = child.toDom(opts);
@@ -7888,58 +7736,50 @@ function morphChildren(parentDom, oldChilds, newChilds, parentTag, opts) {
7888
7736
  }
7889
7737
  return;
7890
7738
  }
7891
- const orderedSet = reorder(oldChilds, newChilds);
7892
- const reorderedChilds = orderedSet.children;
7893
- if (orderedSet.duplicatedKeys && opts.onWarning) {
7894
- opts.onWarning(new DuplicatedKeysWarning(orderedSet.duplicatedKeys, parentTag, 0));
7895
- }
7896
- const domChildren = Array.from(parentDom.childNodes);
7897
- const oldLen = oldChilds.length;
7898
- const reorderedLen = reorderedChilds.length;
7899
- const len = Math.max(oldLen, reorderedLen);
7900
- const toRemove = [];
7901
- for (let i = 0;i < len; i++) {
7902
- const leftNode = oldChilds[i];
7903
- const rightNode = reorderedChilds[i];
7904
- if (!leftNode && rightNode) {
7905
- const newNode = rightNode.toDom(opts);
7906
- if (newNode)
7907
- parentDom.appendChild(newNode);
7908
- } else if (leftNode && rightNode) {
7909
- const domChild = domChildren[i];
7910
- if (domChild) {
7911
- morphNode(domChild, leftNode, rightNode, opts);
7739
+ const domNodes = Array.from(parentDom.childNodes);
7740
+ const oldKeyMap = {};
7741
+ for (let i = 0;i < oldChilds.length; i++) {
7742
+ const key = getKey(oldChilds[i]);
7743
+ if (key != null)
7744
+ oldKeyMap[key] = i;
7745
+ }
7746
+ const used = new Uint8Array(oldChilds.length);
7747
+ let unkeyedCursor = 0;
7748
+ for (let j = 0;j < newChilds.length; j++) {
7749
+ const newChild = newChilds[j];
7750
+ const newKey = getKey(newChild);
7751
+ let oldIdx = -1;
7752
+ if (newKey != null) {
7753
+ if (newKey in oldKeyMap && !used[oldKeyMap[newKey]]) {
7754
+ oldIdx = oldKeyMap[newKey];
7912
7755
  }
7913
- } else if (leftNode && !rightNode) {
7914
- if (!orderedSet.moves && domChildren[i]) {
7915
- toRemove.push(domChildren[i]);
7756
+ } else {
7757
+ while (unkeyedCursor < oldChilds.length) {
7758
+ if (!used[unkeyedCursor] && getKey(oldChilds[unkeyedCursor]) == null) {
7759
+ oldIdx = unkeyedCursor++;
7760
+ break;
7761
+ }
7762
+ unkeyedCursor++;
7916
7763
  }
7917
7764
  }
7918
- }
7919
- for (const node of toRemove) {
7920
- if (node.parentNode === parentDom) {
7921
- parentDom.removeChild(node);
7765
+ if (oldIdx >= 0) {
7766
+ used[oldIdx] = 1;
7767
+ const dom = domNodes[oldIdx];
7768
+ const newDom = morphNode(dom, oldChilds[oldIdx], newChild, opts);
7769
+ const ref = parentDom.childNodes[j] ?? null;
7770
+ if (newDom !== ref)
7771
+ parentDom.insertBefore(newDom, ref);
7772
+ } else {
7773
+ const dom = newChild.toDom(opts);
7774
+ if (dom) {
7775
+ const ref = parentDom.childNodes[j] ?? null;
7776
+ parentDom.insertBefore(dom, ref);
7777
+ }
7922
7778
  }
7923
7779
  }
7924
- if (orderedSet.moves) {
7925
- applyMoves(parentDom, orderedSet.moves);
7926
- }
7927
- }
7928
- function applyMoves(domNode, moves) {
7929
- const childNodes = domNode.childNodes;
7930
- const keyMap = {};
7931
- for (const remove2 of moves.removes) {
7932
- const node = childNodes[remove2.from];
7933
- if (remove2.key)
7934
- keyMap[remove2.key] = node;
7935
- domNode.removeChild(node);
7936
- }
7937
- let length = childNodes.length;
7938
- for (let j = 0;j < moves.inserts.length; j++) {
7939
- const insert = moves.inserts[j];
7940
- const node = keyMap[insert.key];
7941
- if (node) {
7942
- domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to]);
7780
+ for (let i = oldChilds.length - 1;i >= 0; i--) {
7781
+ if (!used[i] && domNodes[i].parentNode === parentDom) {
7782
+ parentDom.removeChild(domNodes[i]);
7943
7783
  }
7944
7784
  }
7945
7785
  }
@@ -8095,6 +7935,8 @@ function checkEventModifiers(lx, view) {
8095
7935
  function isKnownHandlerName(name) {
8096
7936
  switch (name) {
8097
7937
  case "value":
7938
+ case "valueAsInt":
7939
+ case "valueAsFloat":
8098
7940
  case "target":
8099
7941
  case "event":
8100
7942
  case "isAlt":