@oscarpalmer/abydon 0.22.0 → 0.23.0

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.
@@ -783,6 +783,14 @@ function isEventTarget(value) {
783
783
  function isHTMLOrSVGElement(value) {
784
784
  return value instanceof HTMLElement || value instanceof SVGElement;
785
785
  }
786
+ /**
787
+ * Is the value an input element? _(`<input>`, `<select>`, or `<textarea>`)_
788
+ * @param value Value to check
789
+ * @returns `true` if it's an input element, otherwise `false`
790
+ */
791
+ function isInputElement(value) {
792
+ return value instanceof HTMLInputElement || value instanceof HTMLSelectElement || value instanceof HTMLTextAreaElement;
793
+ }
786
794
  //#endregion
787
795
  //#region node_modules/@oscarpalmer/toretto/dist/is.mjs
788
796
  /**
@@ -1062,6 +1070,10 @@ const delimiters = {
1062
1070
  let memoizedCapitalize;
1063
1071
  //#endregion
1064
1072
  //#region node_modules/@oscarpalmer/toretto/dist/internal/element-value.mjs
1073
+ function ignoreSetAttribute(element, name) {
1074
+ if (element instanceof HTMLTextAreaElement && name === "value") return true;
1075
+ return false;
1076
+ }
1065
1077
  function normalizeKey(key, style) {
1066
1078
  return style && key.startsWith(CSS_VARIABLE_PREFIX) ? key : kebabCase(key);
1067
1079
  }
@@ -1091,16 +1103,21 @@ function setElementValues(element, first, second, third, callback, style) {
1091
1103
  }
1092
1104
  function updateElementValue(element, key, value, set, remove, isBoolean, json) {
1093
1105
  if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
1094
- else set.call(element, key, json ? JSON.stringify(value) : String(value));
1106
+ else if (!ignoreSetAttribute(element, key)) set.call(element, key, json ? JSON.stringify(value) : getString(value));
1095
1107
  }
1096
1108
  const CSS_VARIABLE_PREFIX = "--";
1097
1109
  //#endregion
1098
1110
  //#region node_modules/@oscarpalmer/toretto/dist/internal/property.mjs
1111
+ function getPropertyValue(element, name, value) {
1112
+ if (isInputElement(element) && name === "value") return getString(value);
1113
+ return value;
1114
+ }
1099
1115
  function updateProperty(element, name, value, dispatch) {
1100
1116
  let property = name;
1101
1117
  if (!(property in element)) property = camelCase(name);
1102
- if (!(property in element) || Object.is(element[property], value)) return;
1103
- element[property] = value;
1118
+ const next = getPropertyValue(element, name, value);
1119
+ if (!(property in element) || Object.is(element[property], next)) return;
1120
+ element[property] = next;
1104
1121
  const event = dispatch && elementEvents[element.tagName]?.[property];
1105
1122
  if (typeof event === "string") element.dispatchEvent(new Event(event, {
1106
1123
  bubbles: true,
@@ -1141,7 +1158,7 @@ function handleAttribute(callback, decode, first, second) {
1141
1158
  let value;
1142
1159
  if (isAttribute(first)) {
1143
1160
  name = first.name;
1144
- value = String(first.value);
1161
+ value = getString(first.value);
1145
1162
  } else if (typeof first === "string" && typeof second === "string") {
1146
1163
  name = first;
1147
1164
  value = second;
@@ -1413,7 +1430,7 @@ const ARRAY_COMPARISON_DISSIMILAR = "dissimilar";
1413
1430
  const ARRAY_COMPARISON_REMOVED = "removed";
1414
1431
  const CHANGE_INPUTS = new Set(["checkbox", "radio"]);
1415
1432
  const ERROR_FRAGMENT = "Fragment function must return a Fragment instance";
1416
- const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<id>'";
1433
+ const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<>'";
1417
1434
  const ERROR_IDENTIFIER_TYPE = "Identifier cannot be null or undefined";
1418
1435
  const EVENT_CHANGE = "change";
1419
1436
  const EVENT_INPUT = "input";
@@ -1432,12 +1449,12 @@ const EXPRESSION_ABYDON_CONTENT = /^abydon\.(\d+)$/;
1432
1449
  const EXPRESSION_ATTRIBUTE_CLASS = /^class\./;
1433
1450
  const EXPRESSION_ATTRIBUTE_STYLE_FULL = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
1434
1451
  const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
1435
- const EXPRESSION_ATTRIBUTE_STYLE_PROPERTY = /^style\.--/;
1452
+ const EXPRESSION_ATTRIBUTE_STYLE_VARIABLE = /^style\.--/;
1436
1453
  const EXPRESSION_EVENT_ATTRIBUTE = /^@([\w-]+)(?::([a-z:]+))?="$/i;
1437
1454
  const EXPRESSION_EVENT_NAME = /^@?([\w-]+)(?::([a-z:]+))?$/i;
1438
- const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
1439
- const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(apture)$/i;
1440
- const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(nce)$/i;
1455
+ const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(?:ctive)$/i;
1456
+ const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(?:apture)$/i;
1457
+ const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(?:nce)$/i;
1441
1458
  const EXPRESSION_EVENT_PREFIX = /^@/;
1442
1459
  const EXPRESSION_TEXTAREA_VALUE = /(?:<|&lt;)!--abydon\.(\d+)--(?:>|&gt;)/;
1443
1460
  const NAME_FRAGMENT = "$fragment";
@@ -1453,17 +1470,17 @@ function compareArrays(first, second) {
1453
1470
  return firstIsLarger ? ARRAY_COMPARISON_REMOVED : ARRAY_COMPARISON_ADDED;
1454
1471
  }
1455
1472
  /**
1456
- * Is the value a Fragment?
1473
+ * Is the value a _Fragment_?
1457
1474
  * @param value Value to check
1458
- * @returns `true` if the value is a Fragment, otherwise `false`
1475
+ * @returns `true` if the value is a _Fragment_, otherwise `false`
1459
1476
  */
1460
1477
  function isFragment(value) {
1461
1478
  return isNamed(value, NAME_FRAGMENT);
1462
1479
  }
1463
1480
  /**
1464
- * Is the value a Fragments?
1481
+ * Is the value a _Fragments_ instance?
1465
1482
  * @param value Value to check
1466
- * @returns `true` if the value is a Fragments, otherwise `false`
1483
+ * @returns `true` if the value is a _Fragments_ instance, otherwise `false`
1467
1484
  */
1468
1485
  function isFragments(value) {
1469
1486
  return isNamed(value, NAME_FRAGMENTS);
@@ -1471,6 +1488,11 @@ function isFragments(value) {
1471
1488
  function isNamed(value, name) {
1472
1489
  return typeof value === "object" && value != null && name in value && value[name] === true;
1473
1490
  }
1491
+ function setComputedValue(data, callback, after) {
1492
+ const computation = computed(callback);
1493
+ data.mora.values.add(computation);
1494
+ after(computation);
1495
+ }
1474
1496
  //#endregion
1475
1497
  //#region src/fragments.ts
1476
1498
  var Fragments = class {
@@ -1491,7 +1513,7 @@ var Fragments = class {
1491
1513
  };
1492
1514
  function handleFragments(item, remove) {
1493
1515
  const state = isFragments(item) ? fragmentsStates.get(item) : item;
1494
- (remove ? removeFragments$1 : initializeFragments)(state);
1516
+ (remove ? removeFragments : initializeFragments)(state);
1495
1517
  }
1496
1518
  function handleItems(state, items) {
1497
1519
  const keys = /* @__PURE__ */ new Set();
@@ -1508,7 +1530,7 @@ function handleItems(state, items) {
1508
1530
  instance = state.fragment(item);
1509
1531
  if (!isFragment(instance)) throw new Error(ERROR_FRAGMENT);
1510
1532
  }
1511
- instance.identify(key);
1533
+ instance.configure({ identifier: key });
1512
1534
  state.instances[key] = instance;
1513
1535
  keys.add(key);
1514
1536
  mapped.push(instance);
@@ -1521,7 +1543,7 @@ function initializeFragments(state) {
1521
1543
  handleItems(state, items);
1522
1544
  });
1523
1545
  }
1524
- function removeFragments$1(state) {
1546
+ function removeFragments(state) {
1525
1547
  state.subscriber?.();
1526
1548
  updateFragments(state);
1527
1549
  state.subscriber = void 0;
@@ -1547,9 +1569,6 @@ function createNodes(value) {
1547
1569
  if (isChildNode(value)) return [value];
1548
1570
  return [new Text(getString(value))];
1549
1571
  }
1550
- function isInputElement(node) {
1551
- return node instanceof HTMLInputElement || node instanceof HTMLSelectElement;
1552
- }
1553
1572
  function removeNodes(nodes) {
1554
1573
  const { length } = nodes;
1555
1574
  for (let index = 0; index < length; index += 1) nodes[index].remove();
@@ -1558,6 +1577,7 @@ function replaceNodes(from, to) {
1558
1577
  from[0]?.replaceWith(...to);
1559
1578
  const { length } = from;
1560
1579
  for (let index = 1; index < length; index += 1) from[index].remove();
1580
+ return to;
1561
1581
  }
1562
1582
  //#endregion
1563
1583
  //#region node_modules/@oscarpalmer/toretto/dist/internal/get-value.mjs
@@ -1721,8 +1741,8 @@ function setStyle(element, property, value) {
1721
1741
  }
1722
1742
  function updateStyleProperty(element, key, value) {
1723
1743
  updateElementValue(element, key, value, function(property, style) {
1724
- if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, String(style));
1725
- else this.style[property] = String(style);
1744
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, getString(style));
1745
+ else this.style[property] = getString(style);
1726
1746
  }, function(property) {
1727
1747
  if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
1728
1748
  else this.style[property] = "";
@@ -1733,12 +1753,12 @@ const ATTRIBUTE_STYLE = "style";
1733
1753
  const VARIABLE_PREFIX = "--";
1734
1754
  //#endregion
1735
1755
  //#region src/attribute/value.ts
1736
- function getStyleValue(value, unit, isProperty) {
1737
- if (removeStyleValue(value, unit, isProperty)) return;
1756
+ function getStyleValue(value, unit, isVariable) {
1757
+ if (removeStyleValue(value, unit, isVariable)) return;
1738
1758
  return value === true || value === "true" ? unit : `${getString(value)}${unit ?? ""}`;
1739
1759
  }
1740
- function removeStyleValue(value, unit, isProperty) {
1741
- if (value == null || value === false || isProperty && isNullableOrWhitespace(value)) return true;
1760
+ function removeStyleValue(value, unit, isVariable) {
1761
+ if (value == null || value === false || isVariable && isNullableOrWhitespace(value)) return true;
1742
1762
  return unit == null && (value === true || value === "true");
1743
1763
  }
1744
1764
  function setAttribute(data, element, name, value) {
@@ -1755,78 +1775,73 @@ function setAttribute(data, element, name, value) {
1755
1775
  }
1756
1776
  }
1757
1777
  function setClassValues(data, element, name, value) {
1758
- function update(value) {
1778
+ const classes = name.slice(6).split(".");
1779
+ updateValue(data, value, (value) => {
1759
1780
  if (value === true || value === "true") element.classList.add(...classes);
1760
1781
  else element.classList.remove(...classes);
1761
- }
1762
- const classes = name.slice(6).split(".");
1763
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
1764
- else update(value);
1782
+ });
1765
1783
  }
1766
1784
  function setStyleValues(data, element, name, value) {
1767
1785
  let [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name);
1768
- const isProperty = EXPRESSION_ATTRIBUTE_STYLE_PROPERTY.test(name);
1769
- property = camelCase(property);
1770
- if (isProperty) property = `--${property}`;
1771
- function update(value) {
1772
- setStyle(element, property, getStyleValue(value, unit, isProperty));
1773
- }
1774
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
1775
- else update(value);
1786
+ const isVariable = EXPRESSION_ATTRIBUTE_STYLE_VARIABLE.test(name);
1787
+ updateValue(data, value, (value) => {
1788
+ setStyle(element, property, getStyleValue(value, unit, isVariable));
1789
+ });
1776
1790
  }
1777
1791
  function setValue(data, element, name, value) {
1778
- const isReactiveValue = isReactive(value);
1779
- unsetValue(element, name, isReactiveValue ? value.peek() : value);
1780
- if (isReactiveValue) data.mora.subscribers.add(value.subscribe((next) => {
1781
- setAttribute$1(element, name, next);
1782
- }));
1783
- else setAttribute$1(element, name, value);
1792
+ updateValue(data, value, (value) => {
1793
+ setAttribute$1(element, name, value);
1794
+ });
1784
1795
  }
1785
- function unsetValue(element, name, value) {
1786
- if (name === "checked") element.checked = !(value === true || value === "true");
1787
- if (name === "value") element.value = element.value === "" ? "_" : "";
1796
+ function updateValue(data, value, updater) {
1797
+ if (isReactive(value)) data.mora.subscribers.add(value.subscribe(updater));
1798
+ else updater(value);
1788
1799
  }
1789
1800
  //#endregion
1790
1801
  //#region src/attribute/index.ts
1802
+ function compareAttributes(first, second) {
1803
+ return first.name.localeCompare(second.name);
1804
+ }
1791
1805
  function getValue(data, original) {
1792
1806
  const matches = EXPRESSION_ABYDON_CONTENT.exec(original);
1793
1807
  return matches == null ? original : data.values[+matches[1]];
1794
1808
  }
1795
- function mapAttributes(data, element) {
1796
- const attributes = [...element.attributes].sort((first, second) => first.name.localeCompare(second.name));
1809
+ function mapAttributeValue(data, element, name, value) {
1810
+ if (typeof value === "function") setComputedAttribute(data, element, name, value);
1811
+ else setAttribute(data, element, name, value);
1812
+ if (isInputElement(element) && isSignal(value) && name in element) mapEvent(element, "on", () => {
1813
+ value.set(element[name]);
1814
+ });
1815
+ }
1816
+ function mapAttributes(data, element, ignoreValue) {
1817
+ const attributes = [...element.attributes].sort(compareAttributes);
1797
1818
  const { length } = attributes;
1798
1819
  for (let index = 0; index < length; index += 1) {
1799
1820
  const { name, value } = attributes[index];
1800
1821
  const actualName = name.replace(EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, "");
1801
1822
  const actualValue = getValue(data, value);
1802
1823
  if (actualName !== name) element.removeAttribute(name);
1824
+ if (actualName === "value" && ignoreValue) continue;
1803
1825
  switch (true) {
1804
1826
  case EXPRESSION_EVENT_PREFIX.test(actualName):
1805
1827
  mapEvent(element, actualName, actualValue);
1806
1828
  break;
1807
1829
  default:
1808
- mapValue$1(data, element, actualName, actualValue);
1830
+ mapAttributeValue(data, element, actualName, actualValue);
1809
1831
  break;
1810
1832
  }
1811
1833
  }
1812
1834
  }
1813
- function mapValue$1(data, element, name, value) {
1814
- if (typeof value === "function") setComputedAttribute(data, element, name, value);
1815
- else setAttribute(data, element, name, value);
1816
- if (isInputElement(element) && isSignal(value) && name in element) mapEvent(element, "on", () => {
1817
- value.set(element[name]);
1818
- });
1819
- }
1820
1835
  function setComputedAttribute(data, element, name, callback) {
1821
- const value = computed(callback);
1822
- data.mora.values.add(value);
1823
- setAttribute(data, element, name, value);
1836
+ setComputedValue(data, callback, (computation) => {
1837
+ setAttribute(data, element, name, computation);
1838
+ });
1824
1839
  }
1825
1840
  //#endregion
1826
1841
  //#region src/node/value.ts
1827
1842
  function addToArray(identifiers, items, nodes, added) {
1828
1843
  let position = nodes[0];
1829
- const before = added && !identifiers.previous.has(items.templates[0].identifier);
1844
+ const before = added && !identifiers.previous.set.has(items.templates[0].identifier);
1830
1845
  const next = items.next.flatMap((fragment) => fragment.get().flatMap((node) => ({
1831
1846
  identifier: fragment.identifier,
1832
1847
  value: node
@@ -1834,19 +1849,48 @@ function addToArray(identifiers, items, nodes, added) {
1834
1849
  const { length } = next;
1835
1850
  for (let index = 0; index < length; index += 1) {
1836
1851
  const node = next[index];
1837
- if (!(added && identifiers.previous.has(node.identifier))) if (index === 0 && before) position.before(node.value);
1852
+ if (!(added && identifiers.previous.set.has(node.identifier))) if (index === 0 && before) position.before(node.value);
1838
1853
  else position.after(node.value);
1839
1854
  position = node.value;
1840
1855
  }
1841
1856
  }
1857
+ function getArrayData(item, values) {
1858
+ const { length } = values;
1859
+ const next = [];
1860
+ let templates = [];
1861
+ for (let index = 0; index < length; index += 1) {
1862
+ const value = values[index];
1863
+ if (isFragment(value) && value.identifier != null) {
1864
+ next.push(value.identifier);
1865
+ templates.push(value);
1866
+ }
1867
+ }
1868
+ const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
1869
+ const nextSet = new Set(next);
1870
+ if (nextSet.size !== templates.length) templates = [];
1871
+ return {
1872
+ next: {
1873
+ array: next,
1874
+ set: nextSet
1875
+ },
1876
+ previous: {
1877
+ array: previous,
1878
+ set: new Set(previous)
1879
+ },
1880
+ template: {
1881
+ empty: templates.length === 0,
1882
+ items: templates
1883
+ }
1884
+ };
1885
+ }
1842
1886
  function handleArray(identifiers, items, nodes) {
1843
- const next = items.templates.map((template) => items.fragments?.find((fragment) => fragment.identifier === template.identifier) ?? template);
1844
- const comparison = compareArrays(items.fragments ?? [], items.templates);
1887
+ const next = items.templates.map((template) => items.fragments.find((fragment) => fragment.identifier === template.identifier) ?? template);
1888
+ const comparison = compareArrays(identifiers.previous.array, identifiers.next.array);
1845
1889
  if (comparison !== "removed") addToArray(identifiers, {
1846
1890
  ...items,
1847
1891
  next
1848
1892
  }, nodes, comparison === ARRAY_COMPARISON_ADDED);
1849
- const toRemove = items.fragments?.filter((fragment) => !identifiers.next.has(fragment.identifier)) ?? [];
1893
+ const toRemove = items.fragments.filter((fragment) => !identifiers.next.set.has(fragment.identifier));
1850
1894
  const { length } = toRemove;
1851
1895
  for (let index = 0; index < length; index += 1) toRemove[index].remove();
1852
1896
  return {
@@ -1854,52 +1898,45 @@ function handleArray(identifiers, items, nodes) {
1854
1898
  nodes: next.flatMap((fragment) => fragment.get())
1855
1899
  };
1856
1900
  }
1857
- function removeFragments(fragments) {
1858
- if (fragments != null) {
1859
- const { length } = fragments;
1860
- for (let index = 0; index < length; index += 1) fragments[index].remove();
1861
- }
1901
+ function removeArray(item, comment) {
1902
+ const fragments = (item.fragments ?? []).slice();
1903
+ const result = { nodes: setText(item, comment) };
1904
+ removeFragmentsItems(fragments);
1905
+ return result;
1862
1906
  }
1863
- function replaceText(item, comment, isNullable) {
1864
- let to;
1865
- if (isNullable) to = [comment];
1866
- else to = item.text == null ? [] : [item.text];
1867
- replaceNodes(item.nodes ?? [], to);
1907
+ function removeFragmentsItems(fragments) {
1908
+ const { length } = fragments;
1909
+ for (let index = 0; index < length; index += 1) fragments[index].remove();
1868
1910
  }
1869
1911
  function setArray(item, comment, value) {
1870
- if (value.length === 0) return { nodes: setText(item, comment, value) };
1871
- let templates = value.filter((item) => isFragment(item) && item.identifier != null);
1872
- const next = templates.map((fragment) => fragment.identifier);
1873
- const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
1874
- const nextSet = new Set(next);
1875
- if (nextSet.size !== templates.length) templates = [];
1876
- const noTemplates = templates.length === 0;
1877
- if (noTemplates || item.nodes == null || previous.some((identifier) => identifier == null)) return {
1878
- fragments: noTemplates ? void 0 : templates,
1879
- nodes: setNodes(item, comment, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
1880
- };
1912
+ if (value.length === 0) return removeArray(item, comment);
1913
+ const { next, previous, template } = getArrayData(item, value);
1914
+ if (template.empty || item.nodes == null || previous.array.some((identifier) => identifier == null)) {
1915
+ const fragments = (item.fragments ?? []).slice();
1916
+ const result = {
1917
+ fragments: template.empty ? void 0 : template.items,
1918
+ nodes: replaceNodes(item.nodes ?? [comment], template.empty ? value.flatMap((item) => createNodes(item)) : template.items.flatMap((template) => template.get()))
1919
+ };
1920
+ removeFragmentsItems(fragments);
1921
+ return result;
1922
+ }
1881
1923
  return handleArray({
1882
- next: nextSet,
1883
- previous: new Set(previous)
1924
+ next,
1925
+ previous
1884
1926
  }, {
1885
- templates,
1886
- fragments: item.fragments ?? []
1927
+ fragments: item.fragments ?? [],
1928
+ templates: template.items
1887
1929
  }, item.nodes);
1888
1930
  }
1889
1931
  function setNodes(item, comment, next) {
1890
- if (item.nodes == null) replaceNodes([comment], next);
1891
- else replaceNodes(item.nodes, next);
1892
- removeFragments(item.fragments);
1893
- return next;
1932
+ return replaceNodes(item.nodes ?? [comment], next);
1894
1933
  }
1895
1934
  function setReactiveValue(data, comment, reactive) {
1896
1935
  let item = data.items.find((item) => item.nodes?.includes(comment));
1897
1936
  item ??= {};
1898
- item.text = new Text();
1899
1937
  data.mora.subscribers.add(reactive.subscribe((value) => {
1900
1938
  if (Array.isArray(value)) setReactiveValueForArray(item, comment, value);
1901
1939
  else setReactiveValueForSingle(item, comment, value);
1902
- item.nodes ??= [comment];
1903
1940
  }));
1904
1941
  }
1905
1942
  function setReactiveValueForArray(item, comment, value) {
@@ -1908,25 +1945,20 @@ function setReactiveValueForArray(item, comment, value) {
1908
1945
  item.nodes = nodes;
1909
1946
  }
1910
1947
  function setReactiveValueForSingle(item, comment, value) {
1948
+ const fragments = (item.fragments ?? []).slice();
1911
1949
  const valueIsFragment = isFragment(value);
1912
- item.fragments = valueIsFragment ? [value] : void 0;
1913
1950
  if (valueIsFragment || isChildNode(value)) item.nodes = setNodes(item, comment, createNodes(value));
1914
1951
  else item.nodes = setText(item, comment, value);
1952
+ item.fragments = valueIsFragment ? [value] : void 0;
1953
+ removeFragmentsItems(fragments);
1915
1954
  }
1916
1955
  function setText(item, comment, value) {
1917
- const isNullable = isNullableOrWhitespace(value);
1918
- if (item.text != null) item.text.textContent = isNullable ? "" : getString(value);
1919
- let result = false;
1920
- if (item.nodes != null) {
1921
- replaceText(item, comment, isNullable);
1922
- result = !isNullable;
1923
- } else if (isNullable && comment.parentNode == null) item.text?.replaceWith(comment);
1924
- else if (!isNullable && item?.text?.parentNode == null) {
1925
- if (item.text != null) comment.replaceWith(item.text);
1926
- result = true;
1927
- }
1928
- removeFragments(item.fragments);
1929
- if (result) return item.text == null ? [] : [item.text];
1956
+ const valueIsNullable = isNullableOrWhitespace(value);
1957
+ item.text ??= new Text();
1958
+ item.text.textContent = valueIsNullable ? "" : getString(value);
1959
+ const result = valueIsNullable ? [comment] : [item.text];
1960
+ replaceNodes(item.nodes ?? [comment], result);
1961
+ return result;
1930
1962
  }
1931
1963
  //#endregion
1932
1964
  //#region src/node/index.ts
@@ -1943,42 +1975,29 @@ function mapNodes(data, nodes) {
1943
1975
  mapNode(data, node);
1944
1976
  continue;
1945
1977
  }
1946
- if (node instanceof HTMLTextAreaElement) mapTextarea(data, node);
1947
- if (isHTMLOrSVGElement(node)) mapAttributes(data, node);
1978
+ if (isHTMLOrSVGElement(node)) {
1979
+ let ignoreValueAttribute = false;
1980
+ if (node instanceof HTMLTextAreaElement) ignoreValueAttribute = mapTextarea(data, node);
1981
+ mapAttributes(data, node, ignoreValueAttribute);
1982
+ }
1948
1983
  if (node.hasChildNodes()) mapNodes(data, [...node.childNodes]);
1949
1984
  }
1950
1985
  }
1951
1986
  function mapTextarea(data, element) {
1952
1987
  const [, index] = EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ?? EXPRESSION_TEXTAREA_VALUE.exec(element.value) ?? [];
1953
- if (index == null) return;
1988
+ if (index == null) return false;
1954
1989
  element.textContent = "";
1955
1990
  element.value = "";
1956
- const value = data.values[Number.parseInt(index, 10)];
1957
- if (isSignal(value)) {
1958
- element.value = getString(value.peek());
1959
- mapEvent(element, "on", () => {
1960
- value.set(element.value);
1961
- });
1962
- data.mora.subscribers.add(value.subscribe((value) => {
1963
- element.value = getString(value);
1964
- }));
1965
- return;
1966
- }
1967
- let reactive;
1968
- if (typeof value === "function") reactive = computed(value);
1969
- else if (isComputed(value)) reactive = value;
1970
- if (reactive == null) element.value = "";
1971
- else data.mora.subscribers.add(reactive.subscribe((value) => {
1972
- element.value = getString(value);
1973
- }));
1991
+ mapAttributeValue(data, element, "value", data.values[Number.parseInt(index, 10)]);
1992
+ return true;
1974
1993
  }
1975
1994
  function mapValue(data, comment, value) {
1976
1995
  switch (true) {
1977
1996
  case typeof value === "function":
1978
- setComputedValue(data, comment, value);
1997
+ setComputedNode(data, comment, value);
1979
1998
  break;
1980
1999
  case isFragments(value):
1981
- setFragmentsValue(data, comment, value);
2000
+ setFragmentsNode(data, comment, value);
1982
2001
  break;
1983
2002
  case isReactive(value):
1984
2003
  setReactiveValue(data, comment, value);
@@ -1997,12 +2016,12 @@ function replaceComment(data, comment, value) {
1997
2016
  }
1998
2017
  comment.replaceWith(...nodes);
1999
2018
  }
2000
- function setComputedValue(data, comment, callback) {
2001
- const value = computed(callback);
2002
- data.mora.values.add(value);
2003
- setReactiveValue(data, comment, value);
2019
+ function setComputedNode(data, comment, callback) {
2020
+ setComputedValue(data, callback, (computation) => {
2021
+ setReactiveValue(data, comment, computation);
2022
+ });
2004
2023
  }
2005
- function setFragmentsValue(data, comment, fragments) {
2024
+ function setFragmentsNode(data, comment, fragments) {
2006
2025
  const state = fragmentsStates.get(fragments);
2007
2026
  handleFragments(state, false);
2008
2027
  setReactiveValue(data, comment, state.mapped);
@@ -2048,11 +2067,13 @@ var Fragment = class {
2048
2067
  /**
2049
2068
  * Is template caching enabled?
2050
2069
  */
2051
- get caching() {
2070
+ get cache() {
2052
2071
  return this.#configuration.cache;
2053
2072
  }
2054
2073
  /**
2055
- * Fragment identifier
2074
+ * Identifier for the _Fragment_
2075
+ *
2076
+ * _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
2056
2077
  */
2057
2078
  get identifier() {
2058
2079
  return this.#configuration.identifier;
@@ -2071,16 +2092,32 @@ var Fragment = class {
2071
2092
  };
2072
2093
  }
2073
2094
  /**
2074
- * Append the fragment to the given element
2095
+ * Insert the _Fragment_ after the given element
2096
+ * @param element Element to insert after
2097
+ */
2098
+ after(element) {
2099
+ element.after(...this.get());
2100
+ }
2101
+ /**
2102
+ * Append the _Fragment_ to the given element
2075
2103
  * @param element Element to append to
2076
2104
  */
2077
2105
  appendTo(element) {
2078
2106
  element.append(...this.get());
2079
2107
  }
2080
2108
  /**
2081
- * Configure the fragment
2109
+ * Insert the _Fragment_ before the given element
2110
+ * @param element Element to insert before
2111
+ */
2112
+ before(element) {
2113
+ element.before(...this.get());
2114
+ }
2115
+ /**
2116
+ * Configure the _Fragment_
2117
+ *
2118
+ * _Returns the Fragment instance for chaining_
2082
2119
  * @param configuration Configuration options
2083
- * @returns Fragment
2120
+ * @returns _Fragment_
2084
2121
  */
2085
2122
  configure(configuration) {
2086
2123
  const actual = isPlainObject(configuration) ? configuration : {};
@@ -2089,7 +2126,7 @@ var Fragment = class {
2089
2126
  return this;
2090
2127
  }
2091
2128
  /**
2092
- * Get a list of the fragment's nodes
2129
+ * Get a list of the _Fragment_'s nodes
2093
2130
  * @returns List of nodes
2094
2131
  */
2095
2132
  get() {
@@ -2102,19 +2139,17 @@ var Fragment = class {
2102
2139
  return data.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes);
2103
2140
  }
2104
2141
  /**
2105
- * Set an identifier for the fragment
2106
- *
2107
- * _An identifier can be used to uniquely identify a fragment,
2108
- * which helps prevent re-rendering in certain scenarios._
2109
- * @param identifier Identifier
2110
- * @returns Fragment
2142
+ * Prepend the _Fragment_ to the given element
2143
+ * @param element Element to prepend to
2111
2144
  */
2112
- identify(identifier) {
2113
- this.#configuration.identifier = identifier;
2114
- return this;
2145
+ prependTo(element) {
2146
+ element.prepend(...this.get());
2115
2147
  }
2116
2148
  /**
2117
- * Remove the fragment from the DOM
2149
+ * Remove the _Fragment_ _(and all its descendants)_ from the _DOM_
2150
+ *
2151
+ * - _Any events, reactive values, and Fragments will also be cleaned up and removed_
2152
+ * - _After being removed, the Fragment can be re-inserted into the DOM_
2118
2153
  */
2119
2154
  remove() {
2120
2155
  removeFragment(this.#data);
@@ -2145,11 +2180,27 @@ function removeMora(data) {
2145
2180
  //#endregion
2146
2181
  //#region src/index.ts
2147
2182
  /**
2148
- * Create a Fragments from a reactive array
2183
+ * Create a _Fragments_ instance from a reactive array
2184
+ *
2185
+ * _A Fragments instance can be used to efficiently render a list of items that may change over time, using unique identifiers to track each item, only adding, removing, or updating each related Fragment._
2186
+ *
2187
+ * @example
2188
+ * ```ts
2189
+ * const fruits = array(['Apple', 'Banana', 'Cherry']);
2190
+ * const items = fragments(
2191
+ * fruits,
2192
+ * fruit => fruit, // Identifies a unique item
2193
+ * fruit => html`<p>${fruit}</p>`, // Creates a Fragment from an item
2194
+ * );
2195
+ * html`${items}`.appendTo(document.body) // Renders '<p>Apple</p><p>Banana</p><p>Cherry</p>'
2196
+ * fruits.push(['Date', 'Elderberry', 'Fig']); // Appends '<p>Date</p><p>Elderberry</p><p>Fig</p>'
2197
+ * // without re-rendering the existing Fragments
2198
+ * ```
2199
+ *
2149
2200
  * @param array Reactive array
2150
- * @param identify Function to identify item
2151
- * @param fragment Function to create fragment from item
2152
- * @returns Fragments
2201
+ * @param identify Function to identify item uniquely _(non-nullable)_
2202
+ * @param fragment Function to create _Fragment_ from item
2203
+ * @returns _Fragments_
2153
2204
  */
2154
2205
  function fragments(array, identify, fragment) {
2155
2206
  if (!isArray(array)) throw new TypeError("Fragments array must be a reactive array");
@@ -2158,8 +2209,19 @@ function fragments(array, identify, fragment) {
2158
2209
  return new Fragments(array, identify, fragment);
2159
2210
  }
2160
2211
  /**
2161
- * Create a Fragment from a template
2162
- * @returns Fragment
2212
+ * Create a _Fragment_ from a template
2213
+ *
2214
+ * _A Fragment can be used to efficiently render a template that may change over time, only updating the necessary parts of the DOM._
2215
+ *
2216
+ * @example
2217
+ * ```ts
2218
+ * const name = signal('World');
2219
+ * const fragment = html`<p>Hello, ${name}!</p>`;
2220
+ * fragment.appendTo(document.body); // Renders '<p>Hello, World!</p>'
2221
+ * name.set('Alice'); // Replaces 'World' with 'Alice'
2222
+ * ```
2223
+ *
2224
+ * @returns _Fragment_
2163
2225
  */
2164
2226
  function html(template, ...values) {
2165
2227
  return new Fragment(template, values);