@oscarpalmer/abydon 0.23.0 → 0.24.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.
@@ -1,7 +1,5 @@
1
- import { FragmentData } from "../models.mjs";
2
-
1
+ import { FragmentState } from "../models.mjs";
3
2
  //#region src/attribute/index.d.ts
4
- declare function mapAttributeValue(data: FragmentData, element: HTMLElement | SVGElement, name: string, value: unknown): void;
5
- declare function mapAttributes(data: FragmentData, element: HTMLElement | SVGElement, ignoreValue: boolean): void;
6
- //#endregion
7
- export { mapAttributeValue, mapAttributes };
3
+ export declare function mapAttributeValue(data: FragmentState, element: HTMLElement | SVGElement, name: string, value: unknown): void;
4
+ export declare function mapAttributes(data: FragmentState, element: HTMLElement | SVGElement, ignoreValue: boolean): void;
5
+ //#endregion
@@ -2,6 +2,7 @@ import { EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, EXPRESSION_ABYDON_CONTENT, EXPRESSI
2
2
  import { setComputedValue } from "../helpers/index.mjs";
3
3
  import { mapEvent } from "../node/event.mjs";
4
4
  import { setAttribute } from "./value.mjs";
5
+ import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
5
6
  import { isSignal } from "@oscarpalmer/mora";
6
7
  import { isInputElement } from "@oscarpalmer/toretto/is";
7
8
  //#region src/attribute/index.ts
@@ -25,16 +26,14 @@ function mapAttributes(data, element, ignoreValue) {
25
26
  for (let index = 0; index < length; index += 1) {
26
27
  const { name, value } = attributes[index];
27
28
  const actualName = name.replace(EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, "");
28
- const actualValue = getValue(data, value);
29
29
  if (actualName !== name) element.removeAttribute(name);
30
- if (actualName === "value" && ignoreValue) continue;
30
+ if (isNullableOrWhitespace(value) || actualName === "value" && ignoreValue) continue;
31
+ const actualValue = getValue(data, value);
31
32
  switch (true) {
32
33
  case EXPRESSION_EVENT_PREFIX.test(actualName):
33
34
  mapEvent(element, actualName, actualValue);
34
35
  break;
35
- default:
36
- mapAttributeValue(data, element, actualName, actualValue);
37
- break;
36
+ default: mapAttributeValue(data, element, actualName, actualValue);
38
37
  }
39
38
  }
40
39
  }
@@ -1,6 +1,4 @@
1
- import { FragmentData } from "../models.mjs";
2
-
1
+ import { FragmentState } from "../models.mjs";
3
2
  //#region src/attribute/value.d.ts
4
- declare function setAttribute(data: FragmentData, element: HTMLElement | SVGElement, name: string, value: unknown): void;
5
- //#endregion
6
- export { setAttribute };
3
+ export declare function setAttribute(data: FragmentState, element: HTMLElement | SVGElement, name: string, value: unknown): void;
4
+ //#endregion
@@ -21,9 +21,7 @@ function setAttribute(data, element, name, value) {
21
21
  case EXPRESSION_ATTRIBUTE_STYLE_PREFIX.test(name):
22
22
  setStyleValues(data, element, name, value);
23
23
  return;
24
- default:
25
- setValue(data, element, name, value);
26
- break;
24
+ default: setValue(data, element, name, value);
27
25
  }
28
26
  }
29
27
  function setClassValues(data, element, name, value) {
@@ -46,7 +44,7 @@ function setValue(data, element, name, value) {
46
44
  });
47
45
  }
48
46
  function updateValue(data, value, updater) {
49
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe(updater));
47
+ if (isReactive(value)) data.mora.subscriptions.add(value.subscribe(updater));
50
48
  else updater(value);
51
49
  }
52
50
  //#endregion
@@ -1,39 +1,43 @@
1
1
  //#region src/constants.d.ts
2
- declare const ARRAY_COMPARISON_ADDED = "added";
3
- declare const ARRAY_COMPARISON_DISSIMILAR = "dissimilar";
4
- declare const ARRAY_COMPARISON_REMOVED = "removed";
5
- declare const ATTRIBUTE_CLASS_PREFIX_LENGTH: number;
6
- declare const ATTRIBUTE_NAME_DELIMITER = ".";
7
- declare const CHANGE_INPUTS: Set<string>;
8
- declare const ERROR_FRAGMENT = "Fragment function must return a Fragment instance";
9
- declare const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<>'";
10
- declare const ERROR_IDENTIFIER_TYPE = "Identifier cannot be null or undefined";
11
- declare const EVENT_CHANGE = "change";
12
- declare const EVENT_INPUT = "input";
13
- declare const EVENT_SUBMIT = "submit";
14
- declare const EVENT_DEFAULTS: Record<string, string>;
15
- declare const EVENT_ON_VALUE = "on";
16
- declare const EVENT_OPTIONS_DELIMITER = ":";
17
- declare const EXPRESSION_ABYDON_ATTRIBUTE_FULL: RegExp;
18
- declare const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX: RegExp;
19
- declare const EXPRESSION_ABYDON_CONTENT: RegExp;
20
- declare const EXPRESSION_ATTRIBUTE_CLASS: RegExp;
21
- declare const EXPRESSION_ATTRIBUTE_STYLE_FULL: RegExp;
22
- declare const EXPRESSION_ATTRIBUTE_STYLE_PREFIX: RegExp;
23
- declare const EXPRESSION_ATTRIBUTE_STYLE_VARIABLE: RegExp;
24
- declare const EXPRESSION_EVENT_ATTRIBUTE: RegExp;
25
- declare const EXPRESSION_EVENT_NAME: RegExp;
26
- declare const EXPRESSION_EVENT_OPTIONS_ACTIVE: RegExp;
27
- declare const EXPRESSION_EVENT_OPTIONS_CAPTURE: RegExp;
28
- declare const EXPRESSION_EVENT_OPTIONS_ONCE: RegExp;
29
- declare const EXPRESSION_EVENT_PREFIX: RegExp;
30
- declare const EXPRESSION_TEXTAREA_VALUE: RegExp;
31
- declare const NAME_FRAGMENT = "$fragment";
32
- declare const NAME_FRAGMENTS = "$fragments";
33
- declare const PROPERTY_IDENTIFIER = "identifier";
34
- declare const PROPERTY_VALUE = "value";
35
- declare const TEMPLATE_ITEM = "<>";
36
- declare const VALUE_TRUE = "true";
37
- declare const WHITESPACE: RegExp;
38
- //#endregion
39
- export { ARRAY_COMPARISON_ADDED, ARRAY_COMPARISON_DISSIMILAR, ARRAY_COMPARISON_REMOVED, ATTRIBUTE_CLASS_PREFIX_LENGTH, ATTRIBUTE_NAME_DELIMITER, CHANGE_INPUTS, ERROR_FRAGMENT, ERROR_IDENTIFIER_DUPLICATE, ERROR_IDENTIFIER_TYPE, EVENT_CHANGE, EVENT_DEFAULTS, EVENT_INPUT, EVENT_ON_VALUE, EVENT_OPTIONS_DELIMITER, EVENT_SUBMIT, EXPRESSION_ABYDON_ATTRIBUTE_FULL, EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, EXPRESSION_ABYDON_CONTENT, EXPRESSION_ATTRIBUTE_CLASS, EXPRESSION_ATTRIBUTE_STYLE_FULL, EXPRESSION_ATTRIBUTE_STYLE_PREFIX, EXPRESSION_ATTRIBUTE_STYLE_VARIABLE, EXPRESSION_EVENT_ATTRIBUTE, EXPRESSION_EVENT_NAME, EXPRESSION_EVENT_OPTIONS_ACTIVE, EXPRESSION_EVENT_OPTIONS_CAPTURE, EXPRESSION_EVENT_OPTIONS_ONCE, EXPRESSION_EVENT_PREFIX, EXPRESSION_TEXTAREA_VALUE, NAME_FRAGMENT, NAME_FRAGMENTS, PROPERTY_IDENTIFIER, PROPERTY_VALUE, TEMPLATE_ITEM, VALUE_TRUE, WHITESPACE };
2
+ export declare const ARRAY_COMPARISON_ADDED = "added";
3
+ export declare const ARRAY_COMPARISON_DISSIMILAR = "dissimilar";
4
+ export declare const ARRAY_COMPARISON_REMOVED = "removed";
5
+ export declare const ATTRIBUTE_CLASS_PREFIX_LENGTH: number;
6
+ export declare const ATTRIBUTE_NAME_DELIMITER = ".";
7
+ export declare const CHANGE_INPUTS: Set<string>;
8
+ export declare const EVENT_CHANGE = "change";
9
+ export declare const EVENT_INPUT = "input";
10
+ export declare const EVENT_SUBMIT = "submit";
11
+ export declare const EVENT_DEFAULTS: Record<string, string>;
12
+ export declare const EVENT_ON_VALUE = "on";
13
+ export declare const EVENT_OPTIONS_DELIMITER = ":";
14
+ export declare const EXPRESSION_ABYDON_ATTRIBUTE_FULL: RegExp;
15
+ export declare const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX: RegExp;
16
+ export declare const EXPRESSION_ABYDON_CONTENT: RegExp;
17
+ export declare const EXPRESSION_ATTRIBUTE_CLASS: RegExp;
18
+ export declare const EXPRESSION_ATTRIBUTE_STYLE_FULL: RegExp;
19
+ export declare const EXPRESSION_ATTRIBUTE_STYLE_PREFIX: RegExp;
20
+ export declare const EXPRESSION_ATTRIBUTE_STYLE_VARIABLE: RegExp;
21
+ export declare const EXPRESSION_EVENT_ATTRIBUTE: RegExp;
22
+ export declare const EXPRESSION_EVENT_NAME: RegExp;
23
+ export declare const EXPRESSION_EVENT_OPTIONS_ACTIVE: RegExp;
24
+ export declare const EXPRESSION_EVENT_OPTIONS_CAPTURE: RegExp;
25
+ export declare const EXPRESSION_EVENT_OPTIONS_ONCE: RegExp;
26
+ export declare const EXPRESSION_EVENT_PREFIX: RegExp;
27
+ export declare const EXPRESSION_TEXTAREA_VALUE: RegExp;
28
+ export declare const MESSAGE_FRAGMENT_VALUE = "Fragment template must be a string or a template strings array";
29
+ export declare const MESSAGE_FRAGMENTS_FRAGMENT_RESULT = "Fragment function must return a Fragment instance";
30
+ export declare const MESSAGE_FRAGMENTS_FRAGMENT_TYPE = "Fragment handler must be a function";
31
+ export declare const MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_DUPLICATE = "Duplicate identifier found: '<>'";
32
+ export declare const MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_TYPE = "Identifier cannot be null or undefined";
33
+ export declare const MESSAGE_FRAGMENTS_IDENTIFIER_TYPE = "Identifier handler must be a function";
34
+ export declare const MESSAGE_FRAGMENTS_VALUE = "Fragments array must be a reactive array";
35
+ export declare const NAME_FRAGMENT = "fragment";
36
+ export declare const NAME_FRAGMENTS = "fragments";
37
+ export declare const PROPERTY_IDENTIFIER = "identifier";
38
+ export declare const PROPERTY_VALUE = "value";
39
+ export declare const SYMBOL: unique symbol;
40
+ export declare const TEMPLATE_ITEM = "<>";
41
+ export declare const VALUE_TRUE = "true";
42
+ export declare const WHITESPACE: RegExp;
43
+ //#endregion
@@ -4,10 +4,7 @@ const ARRAY_COMPARISON_DISSIMILAR = "dissimilar";
4
4
  const ARRAY_COMPARISON_REMOVED = "removed";
5
5
  const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
6
6
  const ATTRIBUTE_NAME_DELIMITER = ".";
7
- const CHANGE_INPUTS = new Set(["checkbox", "radio"]);
8
- const ERROR_FRAGMENT = "Fragment function must return a Fragment instance";
9
- const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<>'";
10
- const ERROR_IDENTIFIER_TYPE = "Identifier cannot be null or undefined";
7
+ const CHANGE_INPUTS = /* @__PURE__ */ new Set(["checkbox", "radio"]);
11
8
  const EVENT_CHANGE = "change";
12
9
  const EVENT_INPUT = "input";
13
10
  const EVENT_SUBMIT = "submit";
@@ -35,12 +32,20 @@ const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(?:apture)$/i;
35
32
  const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(?:nce)$/i;
36
33
  const EXPRESSION_EVENT_PREFIX = /^@/;
37
34
  const EXPRESSION_TEXTAREA_VALUE = /(?:<|&lt;)!--abydon\.(\d+)--(?:>|&gt;)/;
38
- const NAME_FRAGMENT = "$fragment";
39
- const NAME_FRAGMENTS = "$fragments";
35
+ const MESSAGE_FRAGMENT_VALUE = "Fragment template must be a string or a template strings array";
36
+ const MESSAGE_FRAGMENTS_FRAGMENT_RESULT = "Fragment function must return a Fragment instance";
37
+ const MESSAGE_FRAGMENTS_FRAGMENT_TYPE = "Fragment handler must be a function";
38
+ const MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_DUPLICATE = "Duplicate identifier found: '<>'";
39
+ const MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_TYPE = "Identifier cannot be null or undefined";
40
+ const MESSAGE_FRAGMENTS_IDENTIFIER_TYPE = "Identifier handler must be a function";
41
+ const MESSAGE_FRAGMENTS_VALUE = "Fragments array must be a reactive array";
42
+ const NAME_FRAGMENT = "fragment";
43
+ const NAME_FRAGMENTS = "fragments";
40
44
  const PROPERTY_IDENTIFIER = "identifier";
41
45
  const PROPERTY_VALUE = "value";
46
+ const SYMBOL = Symbol("abydon");
42
47
  const TEMPLATE_ITEM = "<>";
43
48
  const VALUE_TRUE = "true";
44
49
  const WHITESPACE = /\s+/g;
45
50
  //#endregion
46
- export { ARRAY_COMPARISON_ADDED, ARRAY_COMPARISON_DISSIMILAR, ARRAY_COMPARISON_REMOVED, ATTRIBUTE_CLASS_PREFIX_LENGTH, ATTRIBUTE_NAME_DELIMITER, CHANGE_INPUTS, ERROR_FRAGMENT, ERROR_IDENTIFIER_DUPLICATE, ERROR_IDENTIFIER_TYPE, EVENT_CHANGE, EVENT_DEFAULTS, EVENT_INPUT, EVENT_ON_VALUE, EVENT_OPTIONS_DELIMITER, EVENT_SUBMIT, EXPRESSION_ABYDON_ATTRIBUTE_FULL, EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, EXPRESSION_ABYDON_CONTENT, EXPRESSION_ATTRIBUTE_CLASS, EXPRESSION_ATTRIBUTE_STYLE_FULL, EXPRESSION_ATTRIBUTE_STYLE_PREFIX, EXPRESSION_ATTRIBUTE_STYLE_VARIABLE, EXPRESSION_EVENT_ATTRIBUTE, EXPRESSION_EVENT_NAME, EXPRESSION_EVENT_OPTIONS_ACTIVE, EXPRESSION_EVENT_OPTIONS_CAPTURE, EXPRESSION_EVENT_OPTIONS_ONCE, EXPRESSION_EVENT_PREFIX, EXPRESSION_TEXTAREA_VALUE, NAME_FRAGMENT, NAME_FRAGMENTS, PROPERTY_IDENTIFIER, PROPERTY_VALUE, TEMPLATE_ITEM, VALUE_TRUE, WHITESPACE };
51
+ export { ARRAY_COMPARISON_ADDED, ARRAY_COMPARISON_DISSIMILAR, ARRAY_COMPARISON_REMOVED, ATTRIBUTE_CLASS_PREFIX_LENGTH, ATTRIBUTE_NAME_DELIMITER, CHANGE_INPUTS, EVENT_CHANGE, EVENT_DEFAULTS, EVENT_INPUT, EVENT_ON_VALUE, EVENT_OPTIONS_DELIMITER, EVENT_SUBMIT, EXPRESSION_ABYDON_ATTRIBUTE_FULL, EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, EXPRESSION_ABYDON_CONTENT, EXPRESSION_ATTRIBUTE_CLASS, EXPRESSION_ATTRIBUTE_STYLE_FULL, EXPRESSION_ATTRIBUTE_STYLE_PREFIX, EXPRESSION_ATTRIBUTE_STYLE_VARIABLE, EXPRESSION_EVENT_ATTRIBUTE, EXPRESSION_EVENT_NAME, EXPRESSION_EVENT_OPTIONS_ACTIVE, EXPRESSION_EVENT_OPTIONS_CAPTURE, EXPRESSION_EVENT_OPTIONS_ONCE, EXPRESSION_EVENT_PREFIX, EXPRESSION_TEXTAREA_VALUE, MESSAGE_FRAGMENTS_FRAGMENT_RESULT, MESSAGE_FRAGMENTS_FRAGMENT_TYPE, MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_DUPLICATE, MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_TYPE, MESSAGE_FRAGMENTS_IDENTIFIER_TYPE, MESSAGE_FRAGMENTS_VALUE, MESSAGE_FRAGMENT_VALUE, NAME_FRAGMENT, NAME_FRAGMENTS, PROPERTY_IDENTIFIER, PROPERTY_VALUE, SYMBOL, TEMPLATE_ITEM, VALUE_TRUE, WHITESPACE };
@@ -1,59 +1,33 @@
1
- import { FragmentConfiguration } from "./models.mjs";
2
-
1
+ import { Fragment } from "./models.mjs";
3
2
  //#region src/fragment.d.ts
4
- declare class Fragment {
5
- #private;
6
- /**
7
- * Is template caching enabled?
8
- */
9
- get cache(): boolean;
10
- /**
11
- * Identifier for the _Fragment_
12
- *
13
- * _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
14
- */
15
- get identifier(): unknown;
16
- constructor(strings: TemplateStringsArray, expressions: unknown[]);
17
- /**
18
- * Insert the _Fragment_ after the given element
19
- * @param element Element to insert after
20
- */
21
- after(element: Element): void;
22
- /**
23
- * Append the _Fragment_ to the given element
24
- * @param element Element to append to
25
- */
26
- appendTo(element: Element): void;
27
- /**
28
- * Insert the _Fragment_ before the given element
29
- * @param element Element to insert before
30
- */
31
- before(element: Element): void;
32
- /**
33
- * Configure the _Fragment_
34
- *
35
- * _Returns the Fragment instance for chaining_
36
- * @param configuration Configuration options
37
- * @returns _Fragment_
38
- */
39
- configure(configuration: FragmentConfiguration): Fragment;
40
- /**
41
- * Get a list of the _Fragment_'s nodes
42
- * @returns List of nodes
43
- */
44
- get(): ChildNode[];
45
- /**
46
- * Prepend the _Fragment_ to the given element
47
- * @param element Element to prepend to
48
- */
49
- prependTo(element: Element): void;
50
- /**
51
- * Remove the _Fragment_ _(and all its descendants)_ from the _DOM_
52
- *
53
- * - _Any events, reactive values, and Fragments will also be cleaned up and removed_
54
- * - _After being removed, the Fragment can be re-inserted into the DOM_
55
- */
56
- remove(): void;
57
- }
3
+ declare function Fragment(this: any, strings: TemplateStringsArray, expressions: unknown[]): void;
4
+ /**
5
+ * Create a _Fragment_ from a template
6
+ *
7
+ * _A Fragment can be used to efficiently render a template that may change over time, only updating the necessary parts of the DOM._
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const name = signal('World');
12
+ * const fragment = fragment`<p>Hello, ${name}!</p>`; // or `html`<p>Hello, ${name}!</p>`
13
+ * fragment.appendTo(document.body); // Renders '<p>Hello, World!</p>'
14
+ * name.set('Alice'); // Replaces 'World' with 'Alice'
15
+ * ```
16
+ *
17
+ * @returns _Fragment_
18
+ */
19
+ export declare function fragment(template: TemplateStringsArray, ...values: unknown[]): Fragment;
20
+ /**
21
+ * Create a _Fragment_ from a simple template
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const fragment = fragment('<p>Hello, World!</p>'); // or `html`('<p>Hello, World!</p>')
26
+ * fragment.appendTo(document.body); // Renders '<p>Hello, World!</p>'
27
+ * ```
28
+ *
29
+ * @returns _Fragment_
30
+ */
31
+ export declare function fragment(template: string): Fragment;
58
32
  //#endregion
59
- export { Fragment };
33
+ export { fragment as html };
package/dist/fragment.mjs CHANGED
@@ -1,130 +1,105 @@
1
- import { NAME_FRAGMENT } from "./constants.mjs";
1
+ import { MESSAGE_FRAGMENT_VALUE, NAME_FRAGMENT, SYMBOL } from "./constants.mjs";
2
2
  import { isFragments } from "./helpers/index.mjs";
3
3
  import { handleFragments } from "./fragments.mjs";
4
4
  import { removeNodes } from "./helpers/dom.mjs";
5
5
  import { mapNodes } from "./node/index.mjs";
6
6
  import { parse } from "./parse.mjs";
7
- import { isPlainObject } from "@oscarpalmer/atoms/is";
7
+ import { isNonTemplateStringsArray, isPlainObject } from "@oscarpalmer/atoms/is";
8
8
  import { html } from "@oscarpalmer/toretto/html";
9
9
  //#region src/fragment.ts
10
- var Fragment = class {
11
- #data;
12
- #configuration = {
10
+ function Fragment(strings, expressions) {
11
+ this[SYMBOL] = {
12
+ expressions,
13
+ strings,
14
+ cache: true,
13
15
  identifier: void 0,
14
- cache: true
16
+ items: [],
17
+ mora: {
18
+ subscriptions: /* @__PURE__ */ new Set(),
19
+ values: /* @__PURE__ */ new Set()
20
+ },
21
+ name: NAME_FRAGMENT,
22
+ values: []
15
23
  };
16
- /**
17
- * Is template caching enabled?
18
- */
19
- get cache() {
20
- return this.#configuration.cache;
21
- }
22
- /**
23
- * Identifier for the _Fragment_
24
- *
25
- * _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
26
- */
27
- get identifier() {
28
- return this.#configuration.identifier;
29
- }
30
- constructor(strings, expressions) {
31
- Object.defineProperty(this, NAME_FRAGMENT, { value: true });
32
- this.#data = {
33
- expressions,
34
- strings,
35
- items: [],
36
- mora: {
37
- subscribers: /* @__PURE__ */ new Set(),
38
- values: /* @__PURE__ */ new Set()
39
- },
40
- values: []
41
- };
42
- }
43
- /**
44
- * Insert the _Fragment_ after the given element
45
- * @param element Element to insert after
46
- */
47
- after(element) {
48
- element.after(...this.get());
49
- }
50
- /**
51
- * Append the _Fragment_ to the given element
52
- * @param element Element to append to
53
- */
54
- appendTo(element) {
55
- element.append(...this.get());
56
- }
57
- /**
58
- * Insert the _Fragment_ before the given element
59
- * @param element Element to insert before
60
- */
61
- before(element) {
62
- element.before(...this.get());
63
- }
64
- /**
65
- * Configure the _Fragment_
66
- *
67
- * _Returns the Fragment instance for chaining_
68
- * @param configuration Configuration options
69
- * @returns _Fragment_
70
- */
71
- configure(configuration) {
72
- const actual = isPlainObject(configuration) ? configuration : {};
73
- if ("identifier" in actual) this.#configuration.identifier = actual.identifier;
74
- if (typeof actual.cache === "boolean") this.#configuration.cache = actual.cache;
75
- return this;
76
- }
77
- /**
78
- * Get a list of the _Fragment_'s nodes
79
- * @returns List of nodes
80
- */
81
- get() {
82
- const data = this.#data;
83
- if (data.items.length === 0) {
84
- const templated = html(parse(data), { cache: this.#configuration.cache });
85
- data.items.splice(0, data.items.length, ...templated.map((node) => ({ nodes: [node] })));
86
- mapNodes(data, data.items.flatMap((item) => item.nodes));
87
- }
88
- return data.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes);
89
- }
90
- /**
91
- * Prepend the _Fragment_ to the given element
92
- * @param element Element to prepend to
93
- */
94
- prependTo(element) {
95
- element.prepend(...this.get());
24
+ }
25
+ Fragment.prototype.after = insertFragmentAfter;
26
+ Fragment.prototype.appendTo = appendFragmentTo;
27
+ Fragment.prototype.before = insertFragmentBefore;
28
+ Fragment.prototype.configure = configureFragment;
29
+ Fragment.prototype.get = getFragmentNodes;
30
+ Fragment.prototype.prependTo = prependFragmentTo;
31
+ Fragment.prototype.remove = removeFragment;
32
+ Object.defineProperties(Fragment.prototype, {
33
+ cache: {
34
+ enumerable: true,
35
+ get: getFragmentCache
36
+ },
37
+ identifier: {
38
+ enumerable: true,
39
+ get: getFragmentIdentifier
96
40
  }
97
- /**
98
- * Remove the _Fragment_ _(and all its descendants)_ from the _DOM_
99
- *
100
- * - _Any events, reactive values, and Fragments will also be cleaned up and removed_
101
- * - _After being removed, the Fragment can be re-inserted into the DOM_
102
- */
103
- remove() {
104
- removeFragment(this.#data);
41
+ });
42
+ function appendFragmentTo(element) {
43
+ element.append(...this.get());
44
+ }
45
+ function configureFragment(configuration) {
46
+ const state = this[SYMBOL];
47
+ const actual = isPlainObject(configuration) ? configuration : {};
48
+ if ("identifier" in actual) state.identifier = actual.identifier;
49
+ if (typeof actual.cache === "boolean") state.cache = actual.cache;
50
+ return this;
51
+ }
52
+ function fragment(template, ...values) {
53
+ if (typeof template !== "string" && isNonTemplateStringsArray(template)) throw new TypeError(MESSAGE_FRAGMENT_VALUE);
54
+ return new Fragment(template, values);
55
+ }
56
+ function getFragmentCache() {
57
+ return this[SYMBOL].cache;
58
+ }
59
+ function getFragmentIdentifier() {
60
+ return this[SYMBOL].identifier;
61
+ }
62
+ function getFragmentNodes() {
63
+ const state = this[SYMBOL];
64
+ if (state.items.length === 0) {
65
+ const parsed = parse(state);
66
+ const templated = html(parsed, { cache: state.cache });
67
+ state.items.splice(0, state.items.length, ...templated.map((node) => ({ nodes: [node] })));
68
+ mapNodes(state, state.items.flatMap((item) => item.nodes));
105
69
  }
106
- };
107
- function removeFragment(data) {
108
- removeMora(data);
109
- let { length } = data.items;
70
+ return state.items.flatMap((item) => item.fragments?.flatMap((fragment) => fragment.get()) ?? item.nodes);
71
+ }
72
+ function insertFragmentAfter(element) {
73
+ element.after(...this.get());
74
+ }
75
+ function insertFragmentBefore(element) {
76
+ element.before(...this.get());
77
+ }
78
+ function prependFragmentTo(element) {
79
+ element.prepend(...this.get());
80
+ }
81
+ function removeFragment() {
82
+ const state = this[SYMBOL];
83
+ removeMora(state);
84
+ let { length } = state.items;
110
85
  for (let index = 0; index < length; index += 1) {
111
- const { fragments, nodes } = data.items[index];
86
+ const { fragments, nodes } = state.items[index];
112
87
  const fragmentsLength = fragments?.length ?? 0;
113
88
  for (let fragmentIndex = 0; fragmentIndex < fragmentsLength; fragmentIndex += 1) fragments?.[fragmentIndex]?.remove();
114
89
  removeNodes(nodes);
115
90
  }
116
- data.items.length = 0;
117
- length = data.values.length;
91
+ state.items.length = 0;
92
+ length = state.values.length;
118
93
  for (let index = 0; index < length; index += 1) {
119
- const value = data.values[index];
94
+ const value = state.values[index];
120
95
  if (isFragments(value)) handleFragments(value, true);
121
96
  }
122
97
  }
123
- function removeMora(data) {
124
- const unsubscribers = [...data.mora.subscribers];
125
- data.mora.subscribers.clear();
126
- data.mora.values.clear();
127
- for (const unsubscribe of unsubscribers) unsubscribe();
98
+ function removeMora(state) {
99
+ const subscriptions = [...state.mora.subscriptions];
100
+ state.mora.subscriptions.clear();
101
+ state.mora.values.clear();
102
+ for (const subscription of subscriptions) subscription.unsubscribe();
128
103
  }
129
104
  //#endregion
130
- export { Fragment };
105
+ export { fragment, fragment as html };
@@ -1,13 +1,30 @@
1
- import { Fragment } from "./fragment.mjs";
2
- import { FragmentsState } from "./models.mjs";
1
+ import { Fragment, Fragments } from "./models.mjs";
3
2
  import { ReactiveArray } from "@oscarpalmer/mora";
4
-
5
3
  //#region src/fragments.d.ts
6
- declare class Fragments {
7
- #private;
8
- constructor(items: ReactiveArray<unknown>, identify: (item: unknown) => unknown, fragment: (item: unknown) => Fragment);
9
- }
10
- declare function handleFragments(item: Fragments | FragmentsState, remove: boolean): void;
11
- declare const fragmentsStates: WeakMap<Fragments, FragmentsState>;
12
- //#endregion
13
- export { Fragments, fragmentsStates, handleFragments };
4
+ declare function Fragments(this: any, items: ReactiveArray<unknown>, identify: (item: unknown) => unknown, fragment: (item: unknown) => Fragment): void;
5
+ /**
6
+ * Create a _Fragments_ instance from a reactive array
7
+ *
8
+ * _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._
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const fruits = array(['Apple', 'Banana', 'Cherry']);
13
+ * const items = fragments(
14
+ * fruits,
15
+ * fruit => fruit, // Identifies a unique item
16
+ * fruit => html`<p>${fruit}</p>`, // Creates a Fragment from an item
17
+ * );
18
+ * html`${items}`.appendTo(document.body) // Renders '<p>Apple</p><p>Banana</p><p>Cherry</p>'
19
+ * fruits.push(['Date', 'Elderberry', 'Fig']); // Appends '<p>Date</p><p>Elderberry</p><p>Fig</p>'
20
+ * // without re-rendering the existing Fragments
21
+ * ```
22
+ *
23
+ * @param array Reactive array
24
+ * @param identify Function to identify item uniquely _(non-nullable)_
25
+ * @param fragment Function to create _Fragment_ from item
26
+ * @returns _Fragments_
27
+ */
28
+ export declare function fragments<Item>(array: ReactiveArray<Item>, identify: (item: Item) => unknown, fragment: (item: Item) => Fragment): Fragments;
29
+ export declare function handleFragments(instance: Fragments, remove: boolean): void;
30
+ //#endregion