@oscarpalmer/abydon 0.21.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.
Files changed (42) hide show
  1. package/README.md +2 -0
  2. package/dist/abydon.full.mjs +422 -359
  3. package/dist/attribute/index.d.mts +7 -0
  4. package/dist/attribute/index.mjs +47 -0
  5. package/dist/{node/attribute → attribute}/value.d.mts +2 -2
  6. package/dist/attribute/value.mjs +53 -0
  7. package/dist/constants.d.mts +8 -8
  8. package/dist/constants.mjs +15 -15
  9. package/dist/fragment.d.mts +30 -13
  10. package/dist/fragment.mjs +40 -18
  11. package/dist/fragments.d.mts +3 -7
  12. package/dist/fragments.mjs +6 -12
  13. package/dist/helpers/dom.d.mts +2 -3
  14. package/dist/helpers/dom.mjs +2 -4
  15. package/dist/helpers/index.d.mts +16 -2
  16. package/dist/helpers/index.mjs +17 -1
  17. package/dist/index.d.mts +35 -7
  18. package/dist/index.mjs +39 -8
  19. package/dist/models.d.mts +6 -4
  20. package/dist/node/event.mjs +9 -4
  21. package/dist/node/index.mjs +26 -32
  22. package/dist/node/value.mjs +70 -56
  23. package/dist/parse.mjs +6 -4
  24. package/package.json +6 -5
  25. package/src/attribute/index.ts +89 -0
  26. package/src/attribute/value.ts +107 -0
  27. package/src/constants.ts +19 -19
  28. package/src/fragment.ts +43 -21
  29. package/src/fragments.ts +8 -15
  30. package/src/helpers/dom.ts +3 -5
  31. package/src/helpers/index.ts +25 -1
  32. package/src/index.ts +47 -7
  33. package/src/models.ts +6 -4
  34. package/src/node/event.ts +12 -5
  35. package/src/node/index.ts +36 -56
  36. package/src/node/value.ts +121 -104
  37. package/src/parse.ts +15 -5
  38. package/dist/node/attribute/index.d.mts +0 -6
  39. package/dist/node/attribute/index.mjs +0 -51
  40. package/dist/node/attribute/value.mjs +0 -65
  41. package/src/node/attribute/index.ts +0 -100
  42. package/src/node/attribute/value.ts +0 -158
@@ -156,6 +156,11 @@ function unsubscribe(state, callback) {
156
156
  //#endregion
157
157
  //#region node_modules/@oscarpalmer/mora/dist/value/reactive.mjs
158
158
  var Reactive = class {
159
+ /**
160
+ * Internal state of the reactive value
161
+ *
162
+ * @internal
163
+ */
159
164
  state = {
160
165
  computeds: /* @__PURE__ */ new Set(),
161
166
  effects: /* @__PURE__ */ new Set(),
@@ -269,6 +274,44 @@ function setValue$2(instance, state, callback) {
269
274
  }
270
275
  }
271
276
  //#endregion
277
+ //#region node_modules/@oscarpalmer/mora/node_modules/@oscarpalmer/atoms/dist/internal/is.mjs
278
+ /**
279
+ * Is the value a key?
280
+ * @param value Value to check
281
+ * @returns `true` if the value is a `Key` _(`number` or `string`)_, otherwise `false`
282
+ */
283
+ function isKey(value) {
284
+ return typeof value === "number" || typeof value === "string";
285
+ }
286
+ /**
287
+ * Is the value a plain object?
288
+ * @param value Value to check
289
+ * @returns `true` if the value is a plain object, otherwise `false`
290
+ */
291
+ function isPlainObject$1(value) {
292
+ if (value === null || typeof value !== "object") return false;
293
+ if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
294
+ const prototype = Object.getPrototypeOf(value);
295
+ return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
296
+ }
297
+ //#endregion
298
+ //#region node_modules/@oscarpalmer/mora/node_modules/@oscarpalmer/atoms/dist/math.mjs
299
+ /**
300
+ * Round a number
301
+ * @param value Number to round
302
+ * @param decimals Number of decimal places to round to _(defaults to `0`)_
303
+ * @returns Rounded number, or `NaN` if the value if unable to be rounded
304
+ */
305
+ function round(value, decimals) {
306
+ return roundNumber(Math.round, value, decimals);
307
+ }
308
+ function roundNumber(callback, value, decimals) {
309
+ if (typeof value !== "number") return NaN;
310
+ if (typeof decimals !== "number" || decimals < 1) return callback(value);
311
+ const mod = 10 ** decimals;
312
+ return callback((value + Number.EPSILON) * mod) / mod;
313
+ }
314
+ //#endregion
272
315
  //#region node_modules/@oscarpalmer/mora/dist/helpers/value.mjs
273
316
  function emitValue(state) {
274
317
  for (const computed of state.computeds) computed.effect.dirty = true;
@@ -281,7 +324,7 @@ function equalArrays(state, first, second) {
281
324
  if (length !== second.length) return false;
282
325
  let offset = 0;
283
326
  if (length >= 100) {
284
- offset = Math.round(length / 10);
327
+ offset = round(length / 10);
285
328
  offset = offset > 25 ? 25 : offset;
286
329
  for (let index = 0; index < offset; index += 1) if (!state.equal(first[index], second[index])) return false;
287
330
  }
@@ -575,27 +618,6 @@ function setAtIndex(state, index, value) {
575
618
  if (actual > -1) state.value[actual] = value;
576
619
  }
577
620
  //#endregion
578
- //#region node_modules/@oscarpalmer/mora/node_modules/@oscarpalmer/atoms/dist/internal/is.mjs
579
- /**
580
- * Is the value a key?
581
- * @param value Value to check
582
- * @returns `true` if the value is a `Key` _(`number` or `string`)_, otherwise `false`
583
- */
584
- function isKey(value) {
585
- return typeof value === "number" || typeof value === "string";
586
- }
587
- /**
588
- * Is the value a plain object?
589
- * @param value Value to check
590
- * @returns `true` if the value is a plain object, otherwise `false`
591
- */
592
- function isPlainObject$2(value) {
593
- if (value === null || typeof value !== "object") return false;
594
- if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
595
- const prototype = Object.getPrototypeOf(value);
596
- return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
597
- }
598
- //#endregion
599
621
  //#region node_modules/@oscarpalmer/mora/dist/value/store.mjs
600
622
  var Store = class extends Reactive {
601
623
  #keyed = /* @__PURE__ */ new Map();
@@ -636,11 +658,11 @@ var Store = class extends Reactive {
636
658
  */
637
659
  update(callback) {
638
660
  const updated = callback(this.state.value);
639
- if (updated == null || isPlainObject$2(updated)) setObject(this.state, updated);
661
+ if (updated == null || isPlainObject$1(updated)) setObject(this.state, updated);
640
662
  }
641
663
  };
642
664
  function isStoreObject(value) {
643
- return value == null || isPlainObject$2(value);
665
+ return value == null || isPlainObject$1(value);
644
666
  }
645
667
  function setObject(state, value) {
646
668
  startBatch();
@@ -671,11 +693,19 @@ function store(value, options) {
671
693
  //#endregion
672
694
  //#region node_modules/@oscarpalmer/atoms/dist/internal/is.mjs
673
695
  /**
696
+ * Is the value a number?
697
+ * @param value Value to check
698
+ * @returns `true` if the value is a `number`, otherwise `false`
699
+ */
700
+ function isNumber(value) {
701
+ return typeof value === "number" && !Number.isNaN(value);
702
+ }
703
+ /**
674
704
  * Is the value a plain object?
675
705
  * @param value Value to check
676
706
  * @returns `true` if the value is a plain object, otherwise `false`
677
707
  */
678
- function isPlainObject$1(value) {
708
+ function isPlainObject(value) {
679
709
  if (value === null || typeof value !== "object") return false;
680
710
  if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
681
711
  const prototype = Object.getPrototypeOf(value);
@@ -688,14 +718,42 @@ function isPlainObject$1(value) {
688
718
  * @param value Original value
689
719
  * @returns String representation of the value
690
720
  */
691
- function getString$1(value) {
721
+ function getString(value) {
692
722
  if (typeof value === "string") return value;
693
723
  if (value == null) return "";
694
- if (typeof value === "function") return getString$1(value());
724
+ if (typeof value === "function") return getString(value());
695
725
  if (typeof value !== "object") return String(value);
696
726
  const asString = String(value.valueOf?.() ?? value);
697
727
  return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
698
728
  }
729
+ /**
730
+ * Join an array of values into a string _(while ignoring empty values)_
731
+ *
732
+ * _(`null`, `undefined`, and any values that become whitespace-only strings are considered empty)_
733
+ * @param array Array of values
734
+ * @param delimiter Delimiter to use between values
735
+ * @returns Joined string
736
+ */
737
+ function join(array, delimiter) {
738
+ if (!Array.isArray(array)) return "";
739
+ const { length } = array;
740
+ if (length === 0) return "";
741
+ const values = [];
742
+ for (let index = 0; index < length; index += 1) {
743
+ const item = getString(array[index]);
744
+ if (item.trim().length > 0) values.push(item);
745
+ }
746
+ return values.join(typeof delimiter === "string" ? delimiter : "");
747
+ }
748
+ /**
749
+ * Split a string into words _(and other readable parts)_
750
+ * @param value Original string
751
+ * @returns Array of words found in the string
752
+ */
753
+ function words(value) {
754
+ return typeof value === "string" ? value.match(EXPRESSION_WORDS) ?? [] : [];
755
+ }
756
+ const EXPRESSION_WORDS = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
699
757
  //#endregion
700
758
  //#region node_modules/@oscarpalmer/atoms/dist/is.mjs
701
759
  /**
@@ -703,10 +761,10 @@ function getString$1(value) {
703
761
  * @param value Value to check
704
762
  * @returns `true` if the value is nullable or matches a whitespace-only string, otherwise `false`
705
763
  */
706
- function isNullableOrWhitespace$1(value) {
707
- return value == null || EXPRESSION_WHITESPACE$2.test(getString$1(value));
764
+ function isNullableOrWhitespace(value) {
765
+ return value == null || EXPRESSION_WHITESPACE$1.test(getString(value));
708
766
  }
709
- const EXPRESSION_WHITESPACE$2 = /^\s*$/;
767
+ const EXPRESSION_WHITESPACE$1 = /^\s*$/;
710
768
  //#endregion
711
769
  //#region node_modules/@oscarpalmer/toretto/dist/internal/is.mjs
712
770
  /**
@@ -725,6 +783,14 @@ function isEventTarget(value) {
725
783
  function isHTMLOrSVGElement(value) {
726
784
  return value instanceof HTMLElement || value instanceof SVGElement;
727
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
+ }
728
794
  //#endregion
729
795
  //#region node_modules/@oscarpalmer/toretto/dist/is.mjs
730
796
  /**
@@ -743,85 +809,7 @@ const CHILD_NODE_TYPES = new Set([
743
809
  Node.DOCUMENT_TYPE_NODE
744
810
  ]);
745
811
  //#endregion
746
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/internal/is.mjs
747
- /**
748
- * Is the value a number?
749
- * @param value Value to check
750
- * @returns `true` if the value is a `number`, otherwise `false`
751
- */
752
- function isNumber(value) {
753
- return typeof value === "number" && !Number.isNaN(value);
754
- }
755
- /**
756
- * Is the value a plain object?
757
- * @param value Value to check
758
- * @returns `true` if the value is a plain object, otherwise `false`
759
- */
760
- function isPlainObject(value) {
761
- if (value === null || typeof value !== "object") return false;
762
- if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
763
- const prototype = Object.getPrototypeOf(value);
764
- return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
765
- }
766
- //#endregion
767
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/internal/array/compact.mjs
768
- function compact(array, strict) {
769
- if (!Array.isArray(array)) return [];
770
- if (strict === true) return array.filter(Boolean);
771
- const { length } = array;
772
- const compacted = [];
773
- for (let index = 0; index < length; index += 1) {
774
- const item = array[index];
775
- if (item != null) compacted.push(item);
776
- }
777
- return compacted;
778
- }
779
- //#endregion
780
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/internal/string.mjs
781
- /**
782
- * Get the string value from any value
783
- * @param value Original value
784
- * @returns String representation of the value
785
- */
786
- function getString(value) {
787
- if (typeof value === "string") return value;
788
- if (value == null) return "";
789
- if (typeof value === "function") return getString(value());
790
- if (typeof value !== "object") return String(value);
791
- const asString = String(value.valueOf?.() ?? value);
792
- return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
793
- }
794
- /**
795
- * Join an array of values into a string
796
- * @param value Array of values
797
- * @param delimiter Delimiter to use between values
798
- * @returns Joined string
799
- */
800
- function join(value, delimiter) {
801
- return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
802
- }
803
- /**
804
- * Split a string into words _(and other readable parts)_
805
- * @param value Original string
806
- * @returns Array of words found in the string
807
- */
808
- function words(value) {
809
- return typeof value === "string" ? value.match(EXPRESSION_WORDS) ?? [] : [];
810
- }
811
- const EXPRESSION_WORDS = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
812
- //#endregion
813
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/is.mjs
814
- /**
815
- * Is the value `undefined`, `null`, or a whitespace-only string?
816
- * @param value Value to check
817
- * @returns `true` if the value is nullable or a whitespace-only string, otherwise `false`
818
- */
819
- function isNullableOrWhitespace(value) {
820
- return value == null || EXPRESSION_WHITESPACE$1.test(getString(value));
821
- }
822
- const EXPRESSION_WHITESPACE$1 = /^\s*$/;
823
- //#endregion
824
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/internal/number.mjs
812
+ //#region node_modules/@oscarpalmer/atoms/dist/internal/number.mjs
825
813
  /**
826
814
  * Clamp a number between a minimum and maximum value
827
815
  * @param value Value to clamp
@@ -840,7 +828,7 @@ function clamp(value, minimum, maximum, loop) {
840
828
  return value > maximum ? loop === true ? minimum : maximum : value;
841
829
  }
842
830
  //#endregion
843
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/internal/sized.mjs
831
+ //#region node_modules/@oscarpalmer/atoms/dist/internal/sized.mjs
844
832
  function getSizedMaximum(first, second) {
845
833
  let actual;
846
834
  if (typeof first === "number") actual = first;
@@ -850,7 +838,7 @@ function getSizedMaximum(first, second) {
850
838
  const MAXIMUM_ABSOLUTE = 16777216;
851
839
  const MAXIMUM_DEFAULT = 1048576;
852
840
  //#endregion
853
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/sized/map.mjs
841
+ //#region node_modules/@oscarpalmer/atoms/dist/sized/map.mjs
854
842
  /**
855
843
  * A Map with a maximum size
856
844
  *
@@ -904,7 +892,10 @@ var SizedMap = class extends Map {
904
892
  }
905
893
  };
906
894
  //#endregion
907
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/function/memoize.mjs
895
+ //#region node_modules/@oscarpalmer/atoms/dist/function/memoize.mjs
896
+ /**
897
+ * A memoized function, caching and retrieving results based on the its parameters _(or a custom cache key)_
898
+ */
908
899
  var Memoized = class {
909
900
  #state;
910
901
  /**
@@ -1000,7 +991,7 @@ function memoize(callback, options) {
1000
991
  const DEFAULT_CACHE_SIZE = 1024;
1001
992
  const SEPARATOR = "_";
1002
993
  //#endregion
1003
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/string/case.mjs
994
+ //#region node_modules/@oscarpalmer/atoms/dist/string/case.mjs
1004
995
  /**
1005
996
  * Convert a string to camel case _(thisIsCamelCase)_
1006
997
  * @param value String to convert
@@ -1079,16 +1070,23 @@ const delimiters = {
1079
1070
  let memoizedCapitalize;
1080
1071
  //#endregion
1081
1072
  //#region node_modules/@oscarpalmer/toretto/dist/internal/element-value.mjs
1082
- function setElementValue(element, first, second, third, callback) {
1073
+ function ignoreSetAttribute(element, name) {
1074
+ if (element instanceof HTMLTextAreaElement && name === "value") return true;
1075
+ return false;
1076
+ }
1077
+ function normalizeKey(key, style) {
1078
+ return style && key.startsWith(CSS_VARIABLE_PREFIX) ? key : kebabCase(key);
1079
+ }
1080
+ function setElementValue(element, first, second, third, callback, style) {
1083
1081
  if (!isHTMLOrSVGElement(element)) return;
1084
- if (typeof first === "string") setElementValues(element, first, second, third, callback);
1085
- else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback);
1082
+ if (typeof first === "string") setElementValues(element, first, second, third, callback, style);
1083
+ else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback, style);
1086
1084
  }
1087
- function setElementValues(element, first, second, third, callback) {
1085
+ function setElementValues(element, first, second, third, callback, style) {
1088
1086
  if (!isHTMLOrSVGElement(element)) return;
1089
1087
  const dispatch = third !== false;
1090
1088
  if (typeof first === "string") {
1091
- callback(element, kebabCase(first), second, dispatch);
1089
+ callback(element, normalizeKey(first, style), second, dispatch);
1092
1090
  return;
1093
1091
  }
1094
1092
  const isArray = Array.isArray(first);
@@ -1100,22 +1098,31 @@ function setElementValues(element, first, second, third, callback) {
1100
1098
  const { length } = entries;
1101
1099
  for (let index = 0; index < length; index += 1) {
1102
1100
  const entry = entries[index];
1103
- if (typeof entry === "object" && typeof entry?.name === "string") callback(element, kebabCase(entry.name), entry.value, dispatch);
1101
+ if (typeof entry === "object" && typeof entry?.name === "string") callback(element, normalizeKey(entry.name, style), entry.value, dispatch);
1104
1102
  }
1105
1103
  }
1106
1104
  function updateElementValue(element, key, value, set, remove, isBoolean, json) {
1107
1105
  if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
1108
- 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));
1109
1107
  }
1108
+ const CSS_VARIABLE_PREFIX = "--";
1110
1109
  //#endregion
1111
1110
  //#region node_modules/@oscarpalmer/toretto/dist/internal/property.mjs
1112
- function updateProperty$1(element, name, value, dispatch) {
1111
+ function getPropertyValue(element, name, value) {
1112
+ if (isInputElement(element) && name === "value") return getString(value);
1113
+ return value;
1114
+ }
1115
+ function updateProperty(element, name, value, dispatch) {
1113
1116
  let property = name;
1114
1117
  if (!(property in element)) property = camelCase(name);
1115
- if (!(property in element) || Object.is(element[property], value)) return;
1116
- 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;
1117
1121
  const event = dispatch && elementEvents[element.tagName]?.[property];
1118
- if (typeof event === "string") element.dispatchEvent(new Event(event, { bubbles: true }));
1122
+ if (typeof event === "string") element.dispatchEvent(new Event(event, {
1123
+ bubbles: true,
1124
+ cancelable: true
1125
+ }));
1119
1126
  }
1120
1127
  const elementEvents = {
1121
1128
  DETAILS: { open: "toggle" },
@@ -1151,7 +1158,7 @@ function handleAttribute(callback, decode, first, second) {
1151
1158
  let value;
1152
1159
  if (isAttribute(first)) {
1153
1160
  name = first.name;
1154
- value = String(first.value);
1161
+ value = getString(first.value);
1155
1162
  } else if (typeof first === "string" && typeof second === "string") {
1156
1163
  name = first;
1157
1164
  value = second;
@@ -1166,9 +1173,6 @@ function isAttribute(value) {
1166
1173
  function _isBadAttribute(first, second, decode) {
1167
1174
  return handleAttribute(badAttributeHandler, decode, first, second);
1168
1175
  }
1169
- function _isBooleanAttribute(first, decode) {
1170
- return handleAttribute((name) => booleanAttributesSet.has(name?.toLowerCase()), decode, first, "");
1171
- }
1172
1176
  function _isInvalidBooleanAttribute(first, second, decode) {
1173
1177
  return handleAttribute(booleanAttributeHandler, decode, first, second);
1174
1178
  }
@@ -1179,7 +1183,7 @@ function updateAttribute(element, name, value, dispatch) {
1179
1183
  const lowerCaseName = name.toLowerCase();
1180
1184
  const isBoolean = booleanAttributesSet.has(lowerCaseName);
1181
1185
  const next = isBoolean ? value === true || typeof value === "string" && (value === "" || value.toLowerCase() === lowerCaseName) : value == null ? "" : value;
1182
- if (isBoolean || dispatchedAttributes.has(name)) updateProperty$1(element, name, next, dispatch);
1186
+ if (isBoolean || dispatchedAttributes.has(name)) updateProperty(element, name, next, dispatch);
1183
1187
  updateElementValue(element, name, isBoolean ? next ? "" : null : value, element.setAttribute, element.removeAttribute, isBoolean, false);
1184
1188
  }
1185
1189
  const EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
@@ -1424,8 +1428,9 @@ window.templates = templates;
1424
1428
  const ARRAY_COMPARISON_ADDED = "added";
1425
1429
  const ARRAY_COMPARISON_DISSIMILAR = "dissimilar";
1426
1430
  const ARRAY_COMPARISON_REMOVED = "removed";
1431
+ const CHANGE_INPUTS = new Set(["checkbox", "radio"]);
1427
1432
  const ERROR_FRAGMENT = "Fragment function must return a Fragment instance";
1428
- const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<id>'";
1433
+ const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<>'";
1429
1434
  const ERROR_IDENTIFIER_TYPE = "Identifier cannot be null or undefined";
1430
1435
  const EVENT_CHANGE = "change";
1431
1436
  const EVENT_INPUT = "input";
@@ -1438,39 +1443,23 @@ const EVENT_DEFAULTS = {
1438
1443
  SELECT: EVENT_CHANGE,
1439
1444
  TEXTAREA: EVENT_INPUT
1440
1445
  };
1441
- const EXPRESSION_ABYDON_ATTRIBUTE_FULL = /(@?[\w-]+)="<!--abydon.(\d+)-->"/g;
1442
- const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX = /^_/;
1443
- const EXPRESSION_ABYDON_CONTENT = /^@?abydon\.(\d+)@?$/;
1446
+ const EXPRESSION_ABYDON_ATTRIBUTE_FULL = /(@?[\w-.]+(?::[a-z:]+)?)="<!--abydon.(\d+)-->"/g;
1447
+ const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX = /^abydon-/;
1448
+ const EXPRESSION_ABYDON_CONTENT = /^abydon\.(\d+)$/;
1444
1449
  const EXPRESSION_ATTRIBUTE_CLASS = /^class\./;
1445
1450
  const EXPRESSION_ATTRIBUTE_STYLE_FULL = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
1446
1451
  const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
1447
- const EXPRESSION_EVENT_CHANGE_TYPES = /^(checkbox|radio)$/;
1448
- const EXPRESSION_EVENT_NAME = /^@([\w-]+)(?::([a-z:]+))?$/i;
1449
- const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
1450
- const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(apture)$/i;
1451
- const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(nce)$/i;
1452
+ const EXPRESSION_ATTRIBUTE_STYLE_VARIABLE = /^style\.--/;
1453
+ const EXPRESSION_EVENT_ATTRIBUTE = /^@([\w-]+)(?::([a-z:]+))?="$/i;
1454
+ const EXPRESSION_EVENT_NAME = /^@?([\w-]+)(?::([a-z:]+))?$/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;
1452
1458
  const EXPRESSION_EVENT_PREFIX = /^@/;
1453
- const EXPRESSION_PERIOD = /\./;
1454
- const EXPRESSION_TEXTAREA_VALUE = /<!--abydon\.(\d+)-->/;
1459
+ const EXPRESSION_TEXTAREA_VALUE = /(?:<|&lt;)!--abydon\.(\d+)--(?:>|&gt;)/;
1455
1460
  const NAME_FRAGMENT = "$fragment";
1456
1461
  const NAME_FRAGMENTS = "$fragments";
1457
- const PROPERTY_CHECKED = "checked";
1458
- const PROPERTY_VALUE = "value";
1459
- //#endregion
1460
- //#region node_modules/@oscarpalmer/atoms/dist/string/index.mjs
1461
- /**
1462
- * Parse a JSON string into its proper value _(or `undefined` if it fails)_
1463
- * @param value JSON string to parse
1464
- * @param reviver Reviver function to transform the parsed values
1465
- * @returns Parsed value or `undefined` if parsing fails
1466
- */
1467
- function parse$1(value, reviver) {
1468
- try {
1469
- return JSON.parse(value, reviver);
1470
- } catch {
1471
- return;
1472
- }
1473
- }
1462
+ const WHITESPACE = /\s+/g;
1474
1463
  //#endregion
1475
1464
  //#region src/helpers/index.ts
1476
1465
  function compareArrays(first, second) {
@@ -1480,25 +1469,34 @@ function compareArrays(first, second) {
1480
1469
  if (!from.filter((key) => to.includes(key)).every((key, index) => to[index] === key)) return ARRAY_COMPARISON_DISSIMILAR;
1481
1470
  return firstIsLarger ? ARRAY_COMPARISON_REMOVED : ARRAY_COMPARISON_ADDED;
1482
1471
  }
1472
+ /**
1473
+ * Is the value a _Fragment_?
1474
+ * @param value Value to check
1475
+ * @returns `true` if the value is a _Fragment_, otherwise `false`
1476
+ */
1483
1477
  function isFragment(value) {
1484
1478
  return isNamed(value, NAME_FRAGMENT);
1485
1479
  }
1480
+ /**
1481
+ * Is the value a _Fragments_ instance?
1482
+ * @param value Value to check
1483
+ * @returns `true` if the value is a _Fragments_ instance, otherwise `false`
1484
+ */
1486
1485
  function isFragments(value) {
1487
1486
  return isNamed(value, NAME_FRAGMENTS);
1488
1487
  }
1489
1488
  function isNamed(value, name) {
1490
1489
  return typeof value === "object" && value != null && name in value && value[name] === true;
1491
1490
  }
1491
+ function setComputedValue(data, callback, after) {
1492
+ const computation = computed(callback);
1493
+ data.mora.values.add(computation);
1494
+ after(computation);
1495
+ }
1492
1496
  //#endregion
1493
1497
  //#region src/fragments.ts
1494
1498
  var Fragments = class {
1495
1499
  #state;
1496
- /**
1497
- * Fragment items
1498
- */
1499
- get items() {
1500
- return this.#state.mapped;
1501
- }
1502
1500
  constructor(items, identify, fragment) {
1503
1501
  Object.defineProperty(this, NAME_FRAGMENTS, { value: true });
1504
1502
  this.#state = {
@@ -1509,13 +1507,13 @@ var Fragments = class {
1509
1507
  mapped: array([]),
1510
1508
  subscriber: void 0
1511
1509
  };
1512
- states.set(this, this.#state);
1510
+ fragmentsStates.set(this, this.#state);
1513
1511
  initializeFragments(this.#state);
1514
1512
  }
1515
1513
  };
1516
1514
  function handleFragments(item, remove) {
1517
- const state = isFragments(item) ? states.get(item) : item;
1518
- if (state != null) (remove ? removeFragments$1 : initializeFragments)(state);
1515
+ const state = isFragments(item) ? fragmentsStates.get(item) : item;
1516
+ (remove ? removeFragments : initializeFragments)(state);
1519
1517
  }
1520
1518
  function handleItems(state, items) {
1521
1519
  const keys = /* @__PURE__ */ new Set();
@@ -1525,14 +1523,14 @@ function handleItems(state, items) {
1525
1523
  const item = items[index];
1526
1524
  const identifier = state.identify(item);
1527
1525
  if (identifier == null) throw new TypeError(ERROR_IDENTIFIER_TYPE);
1528
- const key = getString$1(identifier);
1526
+ const key = getString(identifier);
1529
1527
  if (keys.has(key)) throw new Error(ERROR_IDENTIFIER_DUPLICATE.replace("<>", key));
1530
1528
  let instance = state.instances[key];
1531
1529
  if (instance == null) {
1532
1530
  instance = state.fragment(item);
1533
1531
  if (!isFragment(instance)) throw new Error(ERROR_FRAGMENT);
1534
1532
  }
1535
- instance.identify(key);
1533
+ instance.configure({ identifier: key });
1536
1534
  state.instances[key] = instance;
1537
1535
  keys.add(key);
1538
1536
  mapped.push(instance);
@@ -1545,7 +1543,7 @@ function initializeFragments(state) {
1545
1543
  handleItems(state, items);
1546
1544
  });
1547
1545
  }
1548
- function removeFragments$1(state) {
1546
+ function removeFragments(state) {
1549
1547
  state.subscriber?.();
1550
1548
  updateFragments(state);
1551
1549
  state.subscriber = void 0;
@@ -1563,16 +1561,13 @@ function updateFragments(state, active) {
1563
1561
  state.instances = next;
1564
1562
  active?.clear();
1565
1563
  }
1566
- const states = /* @__PURE__ */ new WeakMap();
1564
+ const fragmentsStates = /* @__PURE__ */ new WeakMap();
1567
1565
  //#endregion
1568
1566
  //#region src/helpers/dom.ts
1569
1567
  function createNodes(value) {
1570
1568
  if (isFragment(value)) return value.get();
1571
1569
  if (isChildNode(value)) return [value];
1572
- return [new Text(getString$1(value))];
1573
- }
1574
- function isInputElement(node) {
1575
- return node instanceof HTMLInputElement || node instanceof HTMLSelectElement;
1570
+ return [new Text(getString(value))];
1576
1571
  }
1577
1572
  function removeNodes(nodes) {
1578
1573
  const { length } = nodes;
@@ -1582,6 +1577,7 @@ function replaceNodes(from, to) {
1582
1577
  from[0]?.replaceWith(...to);
1583
1578
  const { length } = from;
1584
1579
  for (let index = 1; index < length; index += 1) from[index].remove();
1580
+ return to;
1585
1581
  }
1586
1582
  //#endregion
1587
1583
  //#region node_modules/@oscarpalmer/toretto/dist/internal/get-value.mjs
@@ -1679,7 +1675,7 @@ const EVENT_TYPES = new Set([
1679
1675
  const HANDLER_ACTIVE = delegatedEventHandler.bind(false);
1680
1676
  const HANDLER_PASSIVE = delegatedEventHandler.bind(true);
1681
1677
  //#endregion
1682
- //#region node_modules/@oscarpalmer/toretto/node_modules/@oscarpalmer/atoms/dist/internal/function/misc.mjs
1678
+ //#region node_modules/@oscarpalmer/atoms/dist/internal/function/misc.mjs
1683
1679
  /**
1684
1680
  * A function that does nothing, which can be useful, I guess…
1685
1681
  */
@@ -1718,130 +1714,134 @@ function getOptions(options) {
1718
1714
  function getType(element, type) {
1719
1715
  if (type !== "on") return type;
1720
1716
  if (element instanceof HTMLInputElement) {
1721
- if (EXPRESSION_EVENT_CHANGE_TYPES.test(element.type)) return EVENT_CHANGE;
1717
+ if (CHANGE_INPUTS.has(element.type)) return EVENT_CHANGE;
1722
1718
  return element.type === "submit" ? EVENT_SUBMIT : EVENT_INPUT;
1723
1719
  }
1724
1720
  return EVENT_DEFAULTS[element.tagName] ?? type;
1725
1721
  }
1726
1722
  function mapEvent(element, name, value) {
1727
- const [, type, options] = EXPRESSION_EVENT_NAME.exec(name) ?? [];
1728
- if (type != null && typeof value === "function") on(element, getType(element, type), value, getOptions(options ?? ""));
1723
+ const [, type, options] = EXPRESSION_EVENT_NAME.exec(name);
1724
+ const values = Array.isArray(value) ? value : [value];
1725
+ const { length } = values;
1726
+ for (let index = 0; index < length; index += 1) {
1727
+ const item = values[index];
1728
+ if (typeof item === "function") on(element, getType(element, type), item, getOptions(options ?? ""));
1729
+ }
1729
1730
  }
1730
1731
  //#endregion
1731
- //#region node_modules/@oscarpalmer/toretto/dist/attribute/index.mjs
1732
- function isBooleanAttribute(first) {
1733
- return _isBooleanAttribute(first, true);
1734
- }
1732
+ //#region node_modules/@oscarpalmer/toretto/dist/style.mjs
1733
+ /**
1734
+ * Set a style on an element
1735
+ * @param element Element to set the style on
1736
+ * @param property Style name
1737
+ * @param value Style value
1738
+ */
1739
+ function setStyle(element, property, value) {
1740
+ setElementValues(element, property, value, null, updateStyleProperty, true);
1741
+ }
1742
+ function updateStyleProperty(element, key, value) {
1743
+ updateElementValue(element, key, value, function(property, style) {
1744
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, getString(style));
1745
+ else this.style[property] = getString(style);
1746
+ }, function(property) {
1747
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
1748
+ else this.style[property] = "";
1749
+ if (this.getAttribute(ATTRIBUTE_STYLE) === "") this.removeAttribute(ATTRIBUTE_STYLE);
1750
+ }, false, false);
1751
+ }
1752
+ const ATTRIBUTE_STYLE = "style";
1753
+ const VARIABLE_PREFIX = "--";
1735
1754
  //#endregion
1736
- //#region src/node/attribute/value.ts
1737
- function getCallback(element, name) {
1738
- if (isBooleanAttribute(name) && name in element) return name === "checked" ? updateChecked : updateProperty;
1739
- return name === "value" ? updateValue : setAttribute$1;
1755
+ //#region src/attribute/value.ts
1756
+ function getStyleValue(value, unit, isVariable) {
1757
+ if (removeStyleValue(value, unit, isVariable)) return;
1758
+ return value === true || value === "true" ? unit : `${getString(value)}${unit ?? ""}`;
1759
+ }
1760
+ function removeStyleValue(value, unit, isVariable) {
1761
+ if (value == null || value === false || isVariable && isNullableOrWhitespace(value)) return true;
1762
+ return unit == null && (value === true || value === "true");
1740
1763
  }
1741
1764
  function setAttribute(data, element, name, value) {
1742
1765
  switch (true) {
1743
1766
  case EXPRESSION_ATTRIBUTE_CLASS.test(name):
1744
- setClasses(data, element, name, value);
1767
+ setClassValues(data, element, name, value);
1745
1768
  return;
1746
1769
  case EXPRESSION_ATTRIBUTE_STYLE_PREFIX.test(name):
1747
- setStyle(data, element, name, value);
1770
+ setStyleValues(data, element, name, value);
1748
1771
  return;
1749
1772
  default:
1750
1773
  setValue(data, element, name, value);
1751
1774
  break;
1752
1775
  }
1753
1776
  }
1754
- function setClasses(data, element, name, value) {
1755
- function update(value) {
1756
- if (value === true) element.classList.add(...classes);
1757
- else element.classList.remove(...classes);
1758
- }
1777
+ function setClassValues(data, element, name, value) {
1759
1778
  const classes = name.slice(6).split(".");
1760
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
1761
- else update(value);
1779
+ updateValue(data, value, (value) => {
1780
+ if (value === true || value === "true") element.classList.add(...classes);
1781
+ else element.classList.remove(...classes);
1782
+ });
1762
1783
  }
1763
- function setStyle(data, element, name, value) {
1764
- const [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name) ?? [];
1765
- if (property == null) return;
1766
- function update(value) {
1767
- if (value == null || value === false || value === true && unit == null) element.style.removeProperty(property);
1768
- else element.style.setProperty(property, value === true ? unit : String(value));
1769
- }
1770
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
1771
- else update(value);
1784
+ function setStyleValues(data, element, name, value) {
1785
+ let [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name);
1786
+ const isVariable = EXPRESSION_ATTRIBUTE_STYLE_VARIABLE.test(name);
1787
+ updateValue(data, value, (value) => {
1788
+ setStyle(element, property, getStyleValue(value, unit, isVariable));
1789
+ });
1772
1790
  }
1773
1791
  function setValue(data, element, name, value) {
1774
- const callback = getCallback(element, name);
1775
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe((next) => {
1776
- callback(element, name, next);
1777
- }));
1778
- else callback(element, name, value);
1779
- }
1780
- function updateChecked(element, name, value) {
1781
- updateElement(EVENT_CHANGE, PROPERTY_CHECKED, element, name, value, value === true);
1782
- }
1783
- function updateElement(event, property, element, name, value, next) {
1784
- if (!(property in element) || element[property] === next) return;
1785
- setAttribute$1(element, name, value);
1786
- if (element[property] === next) return;
1787
- element[property] = next;
1788
- element.dispatchEvent(new Event(event, { bubbles: true }));
1789
- }
1790
- function updateProperty(element, name, value) {
1791
- setAttribute$1(element, name, value === true);
1792
+ updateValue(data, value, (value) => {
1793
+ setAttribute$1(element, name, value);
1794
+ });
1792
1795
  }
1793
- function updateValue(element, name, value) {
1794
- updateElement(element instanceof HTMLSelectElement ? EVENT_CHANGE : EVENT_INPUT, PROPERTY_VALUE, element, name, value, String(value));
1796
+ function updateValue(data, value, updater) {
1797
+ if (isReactive(value)) data.mora.subscribers.add(value.subscribe(updater));
1798
+ else updater(value);
1795
1799
  }
1796
1800
  //#endregion
1797
- //#region src/node/attribute/index.ts
1801
+ //#region src/attribute/index.ts
1802
+ function compareAttributes(first, second) {
1803
+ return first.name.localeCompare(second.name);
1804
+ }
1798
1805
  function getValue(data, original) {
1799
- const matches = EXPRESSION_ABYDON_CONTENT.exec(original ?? "");
1806
+ const matches = EXPRESSION_ABYDON_CONTENT.exec(original);
1800
1807
  return matches == null ? original : data.values[+matches[1]];
1801
1808
  }
1802
- function mapAttributes(data, element) {
1803
- const attributes = [...element.attributes];
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);
1804
1818
  const { length } = attributes;
1805
1819
  for (let index = 0; index < length; index += 1) {
1806
1820
  const { name, value } = attributes[index];
1807
1821
  const actualName = name.replace(EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, "");
1808
1822
  const actualValue = getValue(data, value);
1809
1823
  if (actualName !== name) element.removeAttribute(name);
1824
+ if (actualName === "value" && ignoreValue) continue;
1810
1825
  switch (true) {
1811
1826
  case EXPRESSION_EVENT_PREFIX.test(actualName):
1812
1827
  mapEvent(element, actualName, actualValue);
1813
1828
  break;
1814
- case EXPRESSION_PERIOD.test(actualName):
1815
- case typeof actualValue === "function":
1816
- case isReactive(actualValue):
1817
- mapValue$1(data, element, actualName, actualValue);
1829
+ default:
1830
+ mapAttributeValue(data, element, actualName, actualValue);
1818
1831
  break;
1819
- default: break;
1820
1832
  }
1821
1833
  }
1822
1834
  }
1823
- function mapValue$1(data, element, name, value) {
1824
- if (typeof value === "function") setComputedAttribute(data, element, name, value);
1825
- else setAttribute(data, element, name, value);
1826
- if (!isInputElement(element) || !isSignal(value)) return;
1827
- let property;
1828
- if (name === "checked" && element.type === "checkbox") property = PROPERTY_CHECKED;
1829
- else if (name === "value") property = PROPERTY_VALUE;
1830
- if (property != null) mapEvent(element, "@on", () => {
1831
- const next = element[property];
1832
- value.set(parse$1(String(next)) ?? next);
1833
- });
1834
- }
1835
1835
  function setComputedAttribute(data, element, name, callback) {
1836
- const value = computed(callback);
1837
- data.mora.values.add(value);
1838
- setAttribute(data, element, name, value);
1836
+ setComputedValue(data, callback, (computation) => {
1837
+ setAttribute(data, element, name, computation);
1838
+ });
1839
1839
  }
1840
1840
  //#endregion
1841
1841
  //#region src/node/value.ts
1842
1842
  function addToArray(identifiers, items, nodes, added) {
1843
1843
  let position = nodes[0];
1844
- const before = added && !identifiers.previous.has(items.templates[0].identifier);
1844
+ const before = added && !identifiers.previous.set.has(items.templates[0].identifier);
1845
1845
  const next = items.next.flatMap((fragment) => fragment.get().flatMap((node) => ({
1846
1846
  identifier: fragment.identifier,
1847
1847
  value: node
@@ -1849,19 +1849,48 @@ function addToArray(identifiers, items, nodes, added) {
1849
1849
  const { length } = next;
1850
1850
  for (let index = 0; index < length; index += 1) {
1851
1851
  const node = next[index];
1852
- 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);
1853
1853
  else position.after(node.value);
1854
1854
  position = node.value;
1855
1855
  }
1856
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
+ }
1857
1886
  function handleArray(identifiers, items, nodes) {
1858
- const next = items.templates.map((template) => items.fragments?.find((fragment) => fragment.identifier === template.identifier) ?? template);
1859
- 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);
1860
1889
  if (comparison !== "removed") addToArray(identifiers, {
1861
1890
  ...items,
1862
1891
  next
1863
1892
  }, nodes, comparison === ARRAY_COMPARISON_ADDED);
1864
- const toRemove = items.fragments?.filter((fragment) => !identifiers.next.has(fragment.identifier)) ?? [];
1893
+ const toRemove = items.fragments.filter((fragment) => !identifiers.next.set.has(fragment.identifier));
1865
1894
  const { length } = toRemove;
1866
1895
  for (let index = 0; index < length; index += 1) toRemove[index].remove();
1867
1896
  return {
@@ -1869,87 +1898,72 @@ function handleArray(identifiers, items, nodes) {
1869
1898
  nodes: next.flatMap((fragment) => fragment.get())
1870
1899
  };
1871
1900
  }
1872
- function removeFragments(fragments) {
1873
- if (fragments != null) {
1874
- const { length } = fragments;
1875
- for (let index = 0; index < length; index += 1) fragments[index].remove();
1876
- }
1901
+ function removeArray(item, comment) {
1902
+ const fragments = (item.fragments ?? []).slice();
1903
+ const result = { nodes: setText(item, comment) };
1904
+ removeFragmentsItems(fragments);
1905
+ return result;
1877
1906
  }
1878
- function replaceText(item, comment, isNullable) {
1879
- let to;
1880
- if (isNullable) to = [comment];
1881
- else to = item.text == null ? [] : [item.text];
1882
- replaceNodes(item.nodes ?? [], to);
1907
+ function removeFragmentsItems(fragments) {
1908
+ const { length } = fragments;
1909
+ for (let index = 0; index < length; index += 1) fragments[index].remove();
1883
1910
  }
1884
1911
  function setArray(item, comment, value) {
1885
- if (value.length === 0) return { nodes: setText(item, comment, value) };
1886
- let templates = value.filter((item) => isFragment(item) && item.identifier != null);
1887
- const next = templates.map((fragment) => fragment.identifier);
1888
- const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
1889
- if (new Set(next).size !== templates.length) templates = [];
1890
- const noTemplates = templates.length === 0;
1891
- if (noTemplates || item.nodes == null || previous.some((identifier) => identifier == null)) return {
1892
- fragments: noTemplates ? void 0 : templates,
1893
- nodes: setNodes(item, comment, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
1894
- };
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
+ }
1895
1923
  return handleArray({
1896
- next: new Set(next),
1897
- previous: new Set(previous)
1924
+ next,
1925
+ previous
1898
1926
  }, {
1899
- templates,
1900
- fragments: item.fragments ?? []
1927
+ fragments: item.fragments ?? [],
1928
+ templates: template.items
1901
1929
  }, item.nodes);
1902
1930
  }
1903
1931
  function setNodes(item, comment, next) {
1904
- if (item.nodes == null) {
1905
- if (comment.parentNode != null) replaceNodes([comment], next);
1906
- else if (item.text?.parentNode != null) replaceNodes([item.text], next);
1907
- } else replaceNodes(item.nodes, next);
1908
- removeFragments(item.fragments);
1909
- return next;
1932
+ return replaceNodes(item.nodes ?? [comment], next);
1910
1933
  }
1911
1934
  function setReactiveValue(data, comment, reactive) {
1912
1935
  let item = data.items.find((item) => item.nodes?.includes(comment));
1913
1936
  item ??= {};
1914
- item.text = new Text();
1915
1937
  data.mora.subscribers.add(reactive.subscribe((value) => {
1916
1938
  if (Array.isArray(value)) setReactiveValueForArray(item, comment, value);
1917
1939
  else setReactiveValueForSingle(item, comment, value);
1918
- item.nodes = [...item.nodes ?? [comment]];
1919
1940
  }));
1920
1941
  }
1921
1942
  function setReactiveValueForArray(item, comment, value) {
1922
- const result = setArray(item, comment, value);
1923
- item.fragments = typeof result === "boolean" ? void 0 : result?.fragments;
1924
- if (typeof result === "boolean") if (result) item.nodes = item.text == null ? [] : [item.text];
1925
- else item.nodes = void 0;
1926
- else item.nodes = result?.nodes;
1943
+ const { fragments, nodes } = setArray(item, comment, value);
1944
+ item.fragments = fragments;
1945
+ item.nodes = nodes;
1927
1946
  }
1928
1947
  function setReactiveValueForSingle(item, comment, value) {
1948
+ const fragments = (item.fragments ?? []).slice();
1929
1949
  const valueIsFragment = isFragment(value);
1930
- item.fragments = valueIsFragment ? [value] : void 0;
1931
1950
  if (valueIsFragment || isChildNode(value)) item.nodes = setNodes(item, comment, createNodes(value));
1932
1951
  else item.nodes = setText(item, comment, value);
1952
+ item.fragments = valueIsFragment ? [value] : void 0;
1953
+ removeFragmentsItems(fragments);
1933
1954
  }
1934
1955
  function setText(item, comment, value) {
1935
- const isNullable = isNullableOrWhitespace$1(value);
1936
- if (item.text != null) item.text.textContent = isNullable ? "" : getString$1(value);
1937
- let result = false;
1938
- if (item.nodes != null) {
1939
- replaceText(item, comment, isNullable);
1940
- result = !isNullable;
1941
- } else if (isNullable && comment.parentNode == null) item.text?.replaceWith(comment);
1942
- else if (!isNullable && item?.text?.parentNode == null) {
1943
- if (item.text != null) comment.replaceWith(item.text);
1944
- result = true;
1945
- }
1946
- removeFragments(item.fragments);
1947
- 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;
1948
1962
  }
1949
1963
  //#endregion
1950
1964
  //#region src/node/index.ts
1951
1965
  function mapNode(data, comment) {
1952
- const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent ?? "");
1966
+ const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent);
1953
1967
  const value = matches == null ? null : data.values[+matches[1]];
1954
1968
  if (value != null) mapValue(data, comment, value);
1955
1969
  }
@@ -1961,39 +1975,29 @@ function mapNodes(data, nodes) {
1961
1975
  mapNode(data, node);
1962
1976
  continue;
1963
1977
  }
1964
- if (node instanceof HTMLTextAreaElement) mapTextarea(data, node);
1965
- 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
+ }
1966
1983
  if (node.hasChildNodes()) mapNodes(data, [...node.childNodes]);
1967
1984
  }
1968
1985
  }
1969
1986
  function mapTextarea(data, element) {
1970
- const [, index] = EXPRESSION_TEXTAREA_VALUE.exec(element.value) ?? [];
1971
- if (index == null) return;
1987
+ const [, index] = EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ?? EXPRESSION_TEXTAREA_VALUE.exec(element.value) ?? [];
1988
+ if (index == null) return false;
1989
+ element.textContent = "";
1972
1990
  element.value = "";
1973
- const value = data.values[Number.parseInt(index, 10)];
1974
- if (isSignal(value)) {
1975
- element.value = String(value.peek());
1976
- mapEvent(element, "@on", () => {
1977
- value.set(element.value);
1978
- });
1979
- return;
1980
- }
1981
- let reactive;
1982
- if (typeof value === "function") reactive = computed(value);
1983
- else if (isComputed(value)) reactive = value;
1984
- if (reactive == null) element.value = String(value);
1985
- else data.mora.subscribers.add(reactive.subscribe((value) => {
1986
- element.value = String(value);
1987
- }));
1991
+ mapAttributeValue(data, element, "value", data.values[Number.parseInt(index, 10)]);
1992
+ return true;
1988
1993
  }
1989
1994
  function mapValue(data, comment, value) {
1990
1995
  switch (true) {
1991
1996
  case typeof value === "function":
1992
- setComputedValue(data, comment, value);
1997
+ setComputedNode(data, comment, value);
1993
1998
  break;
1994
1999
  case isFragments(value):
1995
- handleFragments(value, false);
1996
- setReactiveValue(data, comment, value.items);
2000
+ setFragmentsNode(data, comment, value);
1997
2001
  break;
1998
2002
  case isReactive(value):
1999
2003
  setReactiveValue(data, comment, value);
@@ -2012,22 +2016,29 @@ function replaceComment(data, comment, value) {
2012
2016
  }
2013
2017
  comment.replaceWith(...nodes);
2014
2018
  }
2015
- function setComputedValue(data, comment, callback) {
2016
- const value = computed(callback);
2017
- data.mora.values.add(value);
2018
- setReactiveValue(data, comment, value);
2019
+ function setComputedNode(data, comment, callback) {
2020
+ setComputedValue(data, callback, (computation) => {
2021
+ setReactiveValue(data, comment, computation);
2022
+ });
2023
+ }
2024
+ function setFragmentsNode(data, comment, fragments) {
2025
+ const state = fragmentsStates.get(fragments);
2026
+ handleFragments(state, false);
2027
+ setReactiveValue(data, comment, state.mapped);
2019
2028
  }
2020
2029
  //#endregion
2021
2030
  //#region src/parse.ts
2022
2031
  function handleExpression(data, prefix, expression) {
2023
2032
  if (Array.isArray(expression)) {
2033
+ if (EXPRESSION_EVENT_ATTRIBUTE.test(prefix.split(WHITESPACE).at(-1))) return transformExpression(prefix, data.values.push(expression) - 1);
2024
2034
  const { length } = expression;
2025
2035
  let expressions = "";
2026
2036
  for (let index = 0; index < length; index += 1) expressions += handleExpression(data, "", expression[index]);
2027
2037
  return `${prefix}${expressions}`;
2028
2038
  }
2029
2039
  if (typeof expression === "function" || typeof expression === "object" && expression != null) return transformExpression(prefix, data.values.push(expression) - 1);
2030
- return isNullableOrWhitespace$1(expression) ? prefix : `${prefix}${expression}`;
2040
+ const asString = getString(expression);
2041
+ return asString.trim().length === 0 ? prefix : `${prefix}${asString}`;
2031
2042
  }
2032
2043
  function parse(data) {
2033
2044
  if (data.template != null) return data.template;
@@ -2040,7 +2051,7 @@ function parse(data) {
2040
2051
  return data.template;
2041
2052
  }
2042
2053
  function transformAttribute(_, name, index) {
2043
- return `_${name}="@abydon.${index}@"`;
2054
+ return `abydon-${name}="abydon.${index}"`;
2044
2055
  }
2045
2056
  function transformExpression(prefix, index) {
2046
2057
  return `${prefix}<!--abydon.${index}-->`;
@@ -2054,7 +2065,15 @@ var Fragment = class {
2054
2065
  cache: true
2055
2066
  };
2056
2067
  /**
2057
- * Fragment identifier
2068
+ * Is template caching enabled?
2069
+ */
2070
+ get cache() {
2071
+ return this.#configuration.cache;
2072
+ }
2073
+ /**
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_
2058
2077
  */
2059
2078
  get identifier() {
2060
2079
  return this.#configuration.identifier;
@@ -2073,25 +2092,41 @@ var Fragment = class {
2073
2092
  };
2074
2093
  }
2075
2094
  /**
2076
- * 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
2077
2103
  * @param element Element to append to
2078
2104
  */
2079
2105
  appendTo(element) {
2080
2106
  element.append(...this.get());
2081
2107
  }
2082
2108
  /**
2083
- * 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_
2084
2119
  * @param configuration Configuration options
2085
- * @returns Fragment
2120
+ * @returns _Fragment_
2086
2121
  */
2087
2122
  configure(configuration) {
2088
- const actual = isPlainObject$1(configuration) ? configuration : {};
2123
+ const actual = isPlainObject(configuration) ? configuration : {};
2089
2124
  if ("identifier" in actual) this.#configuration.identifier = actual.identifier;
2090
2125
  if (typeof actual.cache === "boolean") this.#configuration.cache = actual.cache;
2091
2126
  return this;
2092
2127
  }
2093
2128
  /**
2094
- * Get a list of the fragment's nodes
2129
+ * Get a list of the _Fragment_'s nodes
2095
2130
  * @returns List of nodes
2096
2131
  */
2097
2132
  get() {
@@ -2099,24 +2134,22 @@ var Fragment = class {
2099
2134
  if (data.items.length === 0) {
2100
2135
  const templated = html$1(parse(data), { cache: this.#configuration.cache });
2101
2136
  data.items.splice(0, data.items.length, ...templated.map((node) => ({ nodes: [node] })));
2102
- mapNodes(data, data.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes ?? []));
2137
+ mapNodes(data, data.items.flatMap((item) => item.nodes));
2103
2138
  }
2104
- return data.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes ?? []);
2139
+ return data.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes);
2105
2140
  }
2106
2141
  /**
2107
- * Set an identifier for the fragment
2108
- *
2109
- * _An identifier can be used to uniquely identify a fragment,
2110
- * which helps prevent re-rendering in certain scenarios._
2111
- * @param identifier Identifier
2112
- * @returns Fragment
2142
+ * Prepend the _Fragment_ to the given element
2143
+ * @param element Element to prepend to
2113
2144
  */
2114
- identify(identifier) {
2115
- this.#configuration.identifier = identifier;
2116
- return this;
2145
+ prependTo(element) {
2146
+ element.prepend(...this.get());
2117
2147
  }
2118
2148
  /**
2119
- * 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_
2120
2153
  */
2121
2154
  remove() {
2122
2155
  removeFragment(this.#data);
@@ -2129,7 +2162,7 @@ function removeFragment(data) {
2129
2162
  const { fragments, nodes } = data.items[index];
2130
2163
  const fragmentsLength = fragments?.length ?? 0;
2131
2164
  for (let fragmentIndex = 0; fragmentIndex < fragmentsLength; fragmentIndex += 1) fragments?.[fragmentIndex]?.remove();
2132
- removeNodes(nodes ?? []);
2165
+ removeNodes(nodes);
2133
2166
  }
2134
2167
  data.items.length = 0;
2135
2168
  length = data.values.length;
@@ -2147,21 +2180,51 @@ function removeMora(data) {
2147
2180
  //#endregion
2148
2181
  //#region src/index.ts
2149
2182
  /**
2150
- * Create 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
+ *
2151
2200
  * @param array Reactive array
2152
- * @param identify Function to identify item
2153
- * @param fragment Function to create fragment from item
2154
- * @returns Fragments
2201
+ * @param identify Function to identify item uniquely _(non-nullable)_
2202
+ * @param fragment Function to create _Fragment_ from item
2203
+ * @returns _Fragments_
2155
2204
  */
2156
2205
  function fragments(array, identify, fragment) {
2206
+ if (!isArray(array)) throw new TypeError("Fragments array must be a reactive array");
2207
+ if (typeof identify !== "function") throw new TypeError("Fragments identify must be a function");
2208
+ if (typeof fragment !== "function") throw new TypeError("Fragments fragment must be a function");
2157
2209
  return new Fragments(array, identify, fragment);
2158
2210
  }
2159
2211
  /**
2160
- * Create Fragment from a template
2161
- * @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_
2162
2225
  */
2163
2226
  function html(template, ...values) {
2164
2227
  return new Fragment(template, values);
2165
2228
  }
2166
2229
  //#endregion
2167
- export { array, computed, effect, fragments, html, isArray, isComputed, isEffect, isReactive, isSignal, signal, startBatch, stopBatch, store };
2230
+ export { array, computed, effect, fragments, html, isArray, isComputed, isEffect, isFragment, isFragments, isReactive, isSignal, signal, startBatch, stopBatch, store };