@oscarpalmer/abydon 0.14.0 → 0.15.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.
@@ -0,0 +1,74 @@
1
+ import { isFragment, isFragments } from "./helpers/index.js";
2
+ import { array } from "@oscarpalmer/mora";
3
+ import { getString } from "@oscarpalmer/atoms/string";
4
+ var Fragments = class {
5
+ #state;
6
+ get items() {
7
+ return this.#state.mapped;
8
+ }
9
+ constructor(items, identify, fragment) {
10
+ Object.defineProperty(this, "$fragments", { value: true });
11
+ this.#state = {
12
+ fragment,
13
+ identify,
14
+ array: items,
15
+ instances: {},
16
+ mapped: array([]),
17
+ subscriber: void 0
18
+ };
19
+ states.set(this, this.#state);
20
+ initializeFragments(this.#state);
21
+ }
22
+ };
23
+ function handleFragments(item, remove) {
24
+ const state = isFragments(item) ? states.get(item) : item;
25
+ if (state != null) (remove ? removeFragments : initializeFragments)(state);
26
+ }
27
+ function handleItems(state, items) {
28
+ const keys = /* @__PURE__ */ new Set();
29
+ const mapped = [];
30
+ const { length } = items;
31
+ for (let index = 0; index < length; index += 1) {
32
+ const item = items[index];
33
+ const identifier = state.identify(item);
34
+ if (identifier == null) throw new Error("Identifier cannot be null or undefined");
35
+ const key = getString(identifier);
36
+ if (keys.has(key)) throw new Error(`Duplicate identifier found: "${key}"`);
37
+ let instance = state.instances[key];
38
+ if (instance == null) {
39
+ instance = state.fragment(item);
40
+ if (!isFragment(instance)) throw new Error("Fragment function must return a Fragment instance");
41
+ }
42
+ instance.identify(key);
43
+ state.instances[key] = instance;
44
+ keys.add(key);
45
+ mapped.push(instance);
46
+ }
47
+ state.mapped.set(mapped);
48
+ updateFragments(state, keys);
49
+ }
50
+ function initializeFragments(state) {
51
+ state.subscriber ??= state.array.subscribe((items) => {
52
+ handleItems(state, items);
53
+ });
54
+ }
55
+ function removeFragments(state) {
56
+ state.subscriber?.();
57
+ updateFragments(state);
58
+ state.subscriber = void 0;
59
+ }
60
+ function updateFragments(state, active) {
61
+ const next = {};
62
+ const previous = { ...state.instances };
63
+ const keys = Object.keys(previous);
64
+ const { length } = keys;
65
+ for (let index = 0; index < length; index += 1) {
66
+ const key = keys[index];
67
+ if (active?.has(key)) next[key] = previous[key];
68
+ else previous[key].remove();
69
+ }
70
+ state.instances = next;
71
+ active?.clear();
72
+ }
73
+ var states = /* @__PURE__ */ new WeakMap();
74
+ export { Fragments, handleFragments, initializeFragments };
@@ -1,5 +1,5 @@
1
- import { removeEvents } from "../node/event.js";
2
1
  import { isFragment } from "./index.js";
2
+ import { removeEvents } from "../node/event.js";
3
3
  import { getString } from "@oscarpalmer/atoms/string";
4
4
  import { isChildNode, isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
5
5
  function createNodes(value) {
@@ -8,4 +8,7 @@ function compareArrays(first, second) {
8
8
  function isFragment(value) {
9
9
  return typeof value === "object" && value != null && "$fragment" in value && value.$fragment === true;
10
10
  }
11
- export { compareArrays, isFragment };
11
+ function isFragments(value) {
12
+ return typeof value === "object" && value != null && "$fragments" in value && value.$fragments === true;
13
+ }
14
+ export { compareArrays, isFragment, isFragments };
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
+ import { Fragments } from "./fragments.js";
1
2
  import { Fragment } from "./fragment.js";
2
3
  import "@oscarpalmer/mora";
3
4
  export * from "@oscarpalmer/mora";
5
+ function fragments(array$1, identify, fragment) {
6
+ return new Fragments(array$1, identify, fragment);
7
+ }
4
8
  function html(strings, ...values) {
5
9
  return new Fragment(strings, values);
6
10
  }
7
- export { html };
11
+ export { fragments, html };
@@ -1,5 +1,6 @@
1
+ import { isFragment, isFragments } from "../helpers/index.js";
2
+ import { handleFragments } from "../fragments.js";
1
3
  import { EXPRESSION_COMMENT_CONTENT } from "../constants.js";
2
- import { isFragment } from "../helpers/index.js";
3
4
  import { createNodes } from "../helpers/dom.js";
4
5
  import { mapAttributes } from "./attribute/index.js";
5
6
  import { setReactiveValue } from "./value.js";
@@ -27,6 +28,10 @@ function mapValue(data, comment, value) {
27
28
  case typeof value === "function":
28
29
  setComputedValue(data, comment, value);
29
30
  break;
31
+ case isFragments(value):
32
+ handleFragments(value, false);
33
+ setReactiveValue(data, comment, value.items);
34
+ break;
30
35
  case isReactive(value):
31
36
  setReactiveValue(data, comment, value);
32
37
  break;
@@ -36,7 +41,7 @@ function mapValue(data, comment, value) {
36
41
  }
37
42
  }
38
43
  function replaceComment(data, comment, value) {
39
- const item = data.items.find((item$1) => item$1.nodes.includes(comment));
44
+ const item = data.items.find((item$1) => item$1.nodes?.includes(comment));
40
45
  const nodes = createNodes(value);
41
46
  if (item != null) {
42
47
  item.fragments = isFragment(value) ? [value] : void 0;
@@ -39,70 +39,75 @@ function removeFragments(fragments) {
39
39
  for (let index = 0; index < length; index += 1) fragments[index].remove();
40
40
  }
41
41
  }
42
- function setArray(state, comment, value) {
43
- if (value.length === 0) return { nodes: setText(state, comment, value) };
44
- let templates = value.filter((item) => isFragment(item) && item.identifier != null);
42
+ function replaceText(item, comment, isNullable) {
43
+ let to;
44
+ if (isNullable) to = [comment];
45
+ else to = item.text == null ? [] : [item.text];
46
+ replaceNodes(item.nodes ?? [], to);
47
+ }
48
+ function setArray(item, comment, value) {
49
+ if (value.length === 0) return { nodes: setText(item, comment, value) };
50
+ let templates = value.filter((item$1) => isFragment(item$1) && item$1.identifier != null);
45
51
  const next = templates.map((fragment) => fragment.identifier);
46
- const previous = state.fragments?.map((fragment) => fragment.identifier) ?? [];
52
+ const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
47
53
  if (new Set(next).size !== templates.length) templates = [];
48
54
  const noTemplates = templates.length === 0;
49
- if (noTemplates || state.nodes == null || previous.some((identifier) => identifier == null)) return {
55
+ if (noTemplates || item.nodes == null || previous.some((identifier) => identifier == null)) return {
50
56
  fragments: noTemplates ? void 0 : templates,
51
- nodes: setNodes(state, comment, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
57
+ nodes: setNodes(item, comment, noTemplates ? value.flatMap((item$1) => createNodes(item$1)) : templates.flatMap((template) => template.get()))
52
58
  };
53
59
  return handleArray({
54
60
  next,
55
61
  previous
56
62
  }, {
57
63
  templates,
58
- fragments: state.fragments ?? []
59
- }, state.nodes);
64
+ fragments: item.fragments ?? []
65
+ }, item.nodes);
60
66
  }
61
- function setNodes(state, comment, next) {
62
- if (state.nodes == null) {
67
+ function setNodes(item, comment, next) {
68
+ if (item.nodes == null) {
63
69
  if (comment.parentNode != null) replaceNodes([comment], next);
64
- else if (state.text.parentNode != null) replaceNodes([state.text], next);
65
- } else replaceNodes(state.nodes, next);
66
- removeFragments(state.fragments);
70
+ else if (item.text?.parentNode != null) replaceNodes([item.text], next);
71
+ } else replaceNodes(item.nodes, next);
72
+ removeFragments(item.fragments);
67
73
  return next;
68
74
  }
69
75
  function setReactiveValue(data, comment, reactive) {
70
- const item = data.items.find((item$1) => item$1.nodes.includes(comment));
71
- const state = { text: new Text() };
76
+ let item = data.items.find((item$1) => item$1.nodes?.includes(comment));
77
+ item ??= {};
78
+ item.text = new Text();
72
79
  data.mora.subscribers.add(reactive.subscribe((value) => {
73
- if (Array.isArray(value)) setReactiveValueForArray(state, comment, value);
74
- else setReactiveValueForSingle(state, comment, value);
75
- if (item != null) {
76
- item.fragments = state.fragments;
77
- item.nodes = [...state.nodes ?? [comment]];
78
- }
80
+ if (Array.isArray(value)) setReactiveValueForArray(item, comment, value);
81
+ else setReactiveValueForSingle(item, comment, value);
82
+ item.nodes = [...item.nodes ?? [comment]];
79
83
  }));
80
84
  }
81
- function setReactiveValueForArray(state, comment, value) {
82
- const result = setArray(state, comment, value);
83
- state.fragments = typeof result === "boolean" ? void 0 : result?.fragments;
84
- if (typeof result === "boolean") state.nodes = result ? [state.text] : void 0;
85
- else state.nodes = result?.nodes;
85
+ function setReactiveValueForArray(item, comment, value) {
86
+ const result = setArray(item, comment, value);
87
+ item.fragments = typeof result === "boolean" ? void 0 : result?.fragments;
88
+ if (typeof result === "boolean") if (result) item.nodes = item.text == null ? [] : [item.text];
89
+ else item.nodes = void 0;
90
+ else item.nodes = result?.nodes;
86
91
  }
87
- function setReactiveValueForSingle(state, comment, value) {
92
+ function setReactiveValueForSingle(item, comment, value) {
88
93
  const valueIsFragment = isFragment(value);
89
- state.fragments = valueIsFragment ? [value] : void 0;
90
- if (valueIsFragment || isChildNode(value)) state.nodes = setNodes(state, comment, createNodes(value));
91
- else state.nodes = setText(state, comment, value);
94
+ item.fragments = valueIsFragment ? [value] : void 0;
95
+ if (valueIsFragment || isChildNode(value)) item.nodes = setNodes(item, comment, createNodes(value));
96
+ else item.nodes = setText(item, comment, value);
92
97
  }
93
- function setText(state, comment, value) {
98
+ function setText(item, comment, value) {
94
99
  const isNullable = isNullableOrWhitespace(value);
95
- state.text.textContent = isNullable ? "" : getString(value);
100
+ if (item.text != null) item.text.textContent = isNullable ? "" : getString(value);
96
101
  let result = false;
97
- if (state.nodes != null) {
98
- replaceNodes(state.nodes, [isNullable ? comment : state.text]);
102
+ if (item.nodes != null) {
103
+ replaceText(item, comment, isNullable);
99
104
  result = !isNullable;
100
- } else if (isNullable && comment.parentNode == null) state.text.replaceWith(comment);
101
- else if (!isNullable && state.text.parentNode == null) {
102
- comment.replaceWith(state.text);
105
+ } else if (isNullable && comment.parentNode == null) item.text?.replaceWith(comment);
106
+ else if (!isNullable && item?.text?.parentNode == null) {
107
+ if (item.text != null) comment.replaceWith(item.text);
103
108
  result = true;
104
109
  }
105
- removeFragments(state.fragments);
106
- return result ? [state.text] : void 0;
110
+ removeFragments(item.fragments);
111
+ if (result) return item.text == null ? [] : [item.text];
107
112
  }
108
113
  export { setReactiveValue };
package/dist/parse.js CHANGED
@@ -10,9 +10,12 @@ function handleExpression(data, prefix, expression) {
10
10
  return isNullableOrWhitespace(expression) ? prefix : `${prefix}${expression}`;
11
11
  }
12
12
  function parse(data) {
13
+ if (data.template != null) return data.template;
13
14
  const { length } = data.strings;
14
- let template = "";
15
- for (let index = 0; index < length; index += 1) template += handleExpression(data, data.strings[index], data.expressions[index]);
16
- return template;
15
+ data.template = "";
16
+ for (let index = 0; index < length; index += 1) data.template += handleExpression(data, data.strings[index], data.expressions[index]);
17
+ data.expressions = [];
18
+ data.strings = [];
19
+ return data.template;
17
20
  }
18
21
  export { parse };
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "dependencies": {
7
7
  "@oscarpalmer/atoms": "^0.114",
8
8
  "@oscarpalmer/mora": "^0.23",
9
- "@oscarpalmer/toretto": "^0.24"
9
+ "@oscarpalmer/toretto": "^0.25"
10
10
  },
11
11
  "devDependencies": {
12
12
  "@biomejs/biome": "^2.3",
@@ -58,5 +58,5 @@
58
58
  },
59
59
  "type": "module",
60
60
  "types": "types/index.d.ts",
61
- "version": "0.14.0"
61
+ "version": "0.15.0"
62
62
  }
package/src/fragment.ts CHANGED
@@ -1,9 +1,11 @@
1
+ import {isPlainObject} from '@oscarpalmer/atoms/is';
1
2
  import {html} from '@oscarpalmer/toretto/html';
3
+ import {handleFragments} from './fragments';
4
+ import {isFragments} from './helpers';
2
5
  import {removeNodes} from './helpers/dom';
3
6
  import type {FragmentConfiguration, FragmentData} from './models';
4
7
  import {mapNodes} from './node';
5
8
  import {parse} from './parse';
6
- import { isPlainObject } from '@oscarpalmer/atoms';
7
9
 
8
10
  export class Fragment {
9
11
  readonly #data: FragmentData;
@@ -11,13 +13,20 @@ export class Fragment {
11
13
  readonly #configuration: Required<FragmentConfiguration> = {
12
14
  identifier: undefined,
13
15
  ignoreCache: false,
14
- }
16
+ };
15
17
 
18
+ /**
19
+ * Fragment identifier
20
+ */
16
21
  get identifier(): unknown {
17
22
  return this.#configuration.identifier;
18
23
  }
19
24
 
20
25
  constructor(strings: TemplateStringsArray, expressions: unknown[]) {
26
+ Object.defineProperty(this, '$fragment', {
27
+ value: true,
28
+ });
29
+
21
30
  this.#data = {
22
31
  expressions,
23
32
  strings,
@@ -28,19 +37,21 @@ export class Fragment {
28
37
  },
29
38
  values: [],
30
39
  };
31
-
32
- Object.defineProperty(this, '$fragment', {
33
- value: true,
34
- });
35
40
  }
36
41
 
37
42
  /**
38
- * Appends the fragment to the given element
43
+ * Append the fragment to the given element
44
+ * @param element Element to append to
39
45
  */
40
46
  appendTo(element: Element): void {
41
47
  element.append(...this.get());
42
48
  }
43
49
 
50
+ /**
51
+ * Configure the fragment
52
+ * @param configuration Configuration options
53
+ * @returns Fragment
54
+ */
44
55
  configure(configuration: FragmentConfiguration): Fragment {
45
56
  const actual = isPlainObject(configuration) ? configuration : {};
46
57
 
@@ -56,7 +67,8 @@ export class Fragment {
56
67
  }
57
68
 
58
69
  /**
59
- * Gets a list of the fragment's nodes
70
+ * Get a list of the fragment's nodes
71
+ * @returns List of nodes
60
72
  */
61
73
  get(): ChildNode[] {
62
74
  const data = this.#data;
@@ -65,13 +77,10 @@ export class Fragment {
65
77
  const parsed = parse(data);
66
78
 
67
79
  const templated = html(parsed, {
80
+ ignoreCache: this.#configuration.ignoreCache,
68
81
  sanitizeBooleanAttributes: false,
69
82
  });
70
83
 
71
- if (this.#configuration.ignoreCache) {
72
- html.remove(parsed);
73
- }
74
-
75
84
  data.items.splice(
76
85
  0,
77
86
  data.items.length,
@@ -84,7 +93,9 @@ export class Fragment {
84
93
  data,
85
94
  data.items.flatMap(
86
95
  item =>
87
- item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes ?? [],
96
+ item.fragments?.flatMap(fragment => fragment.get()) ??
97
+ item.nodes ??
98
+ [],
88
99
  ),
89
100
  );
90
101
  }
@@ -92,11 +103,21 @@ export class Fragment {
92
103
  return [
93
104
  ...data.items.flatMap(
94
105
  item =>
95
- item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes ?? [],
106
+ item.fragments?.flatMap(fragment => fragment.get()) ??
107
+ item.nodes ??
108
+ [],
96
109
  ),
97
110
  ];
98
111
  }
99
112
 
113
+ /**
114
+ * Set an identifier for the fragment
115
+ *
116
+ * _An identifier can be used to uniquely identify a fragment,
117
+ * which helps prevent re-rendering in certain scenarios._
118
+ * @param identifier Identifier
119
+ * @returns Fragment
120
+ */
100
121
  identify(identifier: unknown): Fragment {
101
122
  this.#configuration.identifier = identifier;
102
123
 
@@ -104,7 +125,7 @@ export class Fragment {
104
125
  }
105
126
 
106
127
  /**
107
- * Removes the fragment from the DOM
128
+ * Remove the fragment from the DOM
108
129
  */
109
130
  remove(): void {
110
131
  removeFragment(this.#data);
@@ -114,20 +135,34 @@ export class Fragment {
114
135
  function removeFragment(data: FragmentData): void {
115
136
  removeMora(data);
116
137
 
117
- const {length} = data.items;
138
+ let {length} = data.items;
118
139
 
119
140
  for (let index = 0; index < length; index += 1) {
120
141
  const {fragments, nodes} = data.items[index];
121
- const length = fragments?.length ?? 0;
122
-
123
- for (let index = 0; index < length; index += 1) {
124
- fragments?.[index]?.remove();
142
+ const fragmentsLength = fragments?.length ?? 0;
143
+
144
+ for (
145
+ let fragmentIndex = 0;
146
+ fragmentIndex < fragmentsLength;
147
+ fragmentIndex += 1
148
+ ) {
149
+ fragments?.[fragmentIndex]?.remove();
125
150
  }
126
151
 
127
152
  removeNodes(nodes ?? []);
128
153
  }
129
154
 
130
155
  data.items.length = 0;
156
+
157
+ length = data.values.length;
158
+
159
+ for (let index = 0; index < length; index += 1) {
160
+ const value = data.values[index];
161
+
162
+ if (isFragments(value)) {
163
+ handleFragments(value, true);
164
+ }
165
+ }
131
166
  }
132
167
 
133
168
  function removeMora(data: FragmentData): void {
@@ -0,0 +1,134 @@
1
+ import {getString} from '@oscarpalmer/atoms/string';
2
+ import {array, type ReactiveArray} from '@oscarpalmer/mora';
3
+ import type {Fragment} from './fragment';
4
+ import {isFragment, isFragments} from './helpers';
5
+ import type {FragmentsState} from './models';
6
+
7
+ export class Fragments {
8
+ readonly #state: FragmentsState;
9
+
10
+ /**
11
+ * Fragment items
12
+ */
13
+ get items(): ReactiveArray<Fragment> {
14
+ return this.#state.mapped;
15
+ }
16
+
17
+ constructor(
18
+ items: ReactiveArray<unknown>,
19
+ identify: (item: unknown) => unknown,
20
+ fragment: (item: unknown) => Fragment,
21
+ ) {
22
+ Object.defineProperty(this, '$fragments', {
23
+ value: true,
24
+ });
25
+
26
+ this.#state = {
27
+ fragment,
28
+ identify,
29
+ array: items,
30
+ instances: {},
31
+ mapped: array<Fragment>([]),
32
+ subscriber: undefined,
33
+ };
34
+
35
+ states.set(this, this.#state);
36
+
37
+ initializeFragments(this.#state);
38
+ }
39
+ }
40
+
41
+ export function handleFragments(
42
+ item: Fragments | FragmentsState,
43
+ remove: boolean,
44
+ ): void {
45
+ const state = isFragments(item) ? states.get(item) : item;
46
+
47
+ if (state != null) {
48
+ (remove ? removeFragments : initializeFragments)(state);
49
+ }
50
+ }
51
+
52
+ function handleItems(state: FragmentsState, items: unknown[]): void {
53
+ const keys = new Set<string>();
54
+ const mapped: Fragment[] = [];
55
+
56
+ const {length} = items;
57
+
58
+ for (let index = 0; index < length; index += 1) {
59
+ const item = items[index];
60
+ const identifier = state.identify(item);
61
+
62
+ if (identifier == null) {
63
+ throw new Error('Identifier cannot be null or undefined');
64
+ }
65
+
66
+ const key = getString(identifier);
67
+
68
+ if (keys.has(key)) {
69
+ throw new Error(`Duplicate identifier found: "${key}"`);
70
+ }
71
+
72
+ let instance = state.instances[key];
73
+
74
+ if (instance == null) {
75
+ instance = state.fragment(item);
76
+
77
+ if (!isFragment(instance)) {
78
+ throw new Error('Fragment function must return a Fragment instance');
79
+ }
80
+ }
81
+
82
+ instance.identify(key);
83
+
84
+ state.instances[key] = instance;
85
+
86
+ keys.add(key);
87
+
88
+ mapped.push(instance);
89
+ }
90
+
91
+ state.mapped.set(mapped);
92
+
93
+ updateFragments(state, keys);
94
+ }
95
+
96
+ export function initializeFragments(state: FragmentsState): void {
97
+ state.subscriber ??= state.array.subscribe(items => {
98
+ handleItems(state, items);
99
+ });
100
+ }
101
+
102
+ function removeFragments(state: FragmentsState): void {
103
+ state.subscriber?.();
104
+
105
+ updateFragments(state);
106
+
107
+ state.subscriber = undefined;
108
+ }
109
+
110
+ function updateFragments(state: FragmentsState, active?: Set<string>): void {
111
+ const next: Record<string, Fragment> = {};
112
+
113
+ const previous = {...state.instances};
114
+ const keys = Object.keys(previous);
115
+ const {length} = keys;
116
+
117
+ for (let index = 0; index < length; index += 1) {
118
+ const key = keys[index];
119
+
120
+ if (active?.has(key)) {
121
+ next[key] = previous[key];
122
+ } else {
123
+ previous[key].remove();
124
+ }
125
+ }
126
+
127
+ state.instances = next;
128
+
129
+ active?.clear();
130
+ }
131
+
132
+ //
133
+
134
+ const states: WeakMap<Fragments, FragmentsState> = new WeakMap();
@@ -1,4 +1,5 @@
1
1
  import type {Fragment} from '../fragment';
2
+ import type {Fragments} from '../fragments';
2
3
 
3
4
  export function compareArrays(
4
5
  first: unknown[],
@@ -27,3 +28,12 @@ export function isFragment(value: unknown): value is Fragment {
27
28
  value.$fragment === true
28
29
  );
29
30
  }
31
+
32
+ export function isFragments(value: unknown): value is Fragments {
33
+ return (
34
+ typeof value === 'object' &&
35
+ value != null &&
36
+ '$fragments' in value &&
37
+ value.$fragments === true
38
+ );
39
+ }
package/src/index.ts CHANGED
@@ -1,13 +1,27 @@
1
1
  import '@oscarpalmer/mora';
2
- import { Fragment } from './fragment';
2
+ import type {ReactiveArray} from '@oscarpalmer/mora';
3
+ import {Fragment} from './fragment';
4
+ import {Fragments} from './fragments';
5
+
6
+ export function fragments<Item>(
7
+ array: ReactiveArray<Item>,
8
+ identify: (item: Item) => unknown,
9
+ fragment: (item: Item) => Fragment,
10
+ ): Fragments {
11
+ return new Fragments(
12
+ array as ReactiveArray<unknown>,
13
+ identify as never,
14
+ fragment as never,
15
+ );
16
+ }
3
17
 
4
18
  export function html(
5
19
  strings: TemplateStringsArray,
6
20
  ...values: unknown[]
7
- ): Fragment {
21
+ ): unknown {
8
22
  return new Fragment(strings, values);
9
23
  }
10
24
 
11
25
  export * from '@oscarpalmer/mora';
12
- export type { Fragment } from './fragment';
13
-
26
+ export type {Fragment} from './fragment';
27
+ export type {Fragments} from './fragments';
package/src/models.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {Reactive} from '@oscarpalmer/mora';
1
+ import type {Reactive, ReactiveArray, Unsubscribe} from '@oscarpalmer/mora';
2
2
  import type {Fragment} from './fragment';
3
3
 
4
4
  export type FragmentConfiguration = {
@@ -21,6 +21,15 @@ export type FragmentItem = {
21
21
  text?: Text;
22
22
  };
23
23
 
24
+ export type FragmentsState = {
25
+ array: ReactiveArray<unknown>;
26
+ fragment: (item: unknown) => Fragment;
27
+ identify: (item: unknown) => unknown;
28
+ instances: Record<string, Fragment>;
29
+ mapped: ReactiveArray<Fragment>;
30
+ subscriber: Unsubscribe | undefined;
31
+ };
32
+
24
33
  type MoraData = {
25
34
  subscribers: Set<() => void>;
26
35
  values: Set<Reactive<unknown>>;
package/src/node/index.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {computed, isReactive} from '@oscarpalmer/mora';
2
+ import {computed, isReactive, Reactive} from '@oscarpalmer/mora';
3
3
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
4
4
  import {EXPRESSION_COMMENT_CONTENT} from '../constants';
5
- import {isFragment} from '../helpers';
5
+ import {handleFragments} from '../fragments';
6
+ import {isFragment, isFragments} from '../helpers';
6
7
  import {createNodes} from '../helpers/dom';
7
8
  import type {FragmentData} from '../models';
8
9
  import {mapAttributes} from './attribute';
@@ -45,6 +46,15 @@ function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
45
46
  setComputedValue(data, comment, value as GenericCallback);
46
47
  break;
47
48
 
49
+ case isFragments(value):
50
+ handleFragments(value, false);
51
+ setReactiveValue(
52
+ data,
53
+ comment,
54
+ value.items as Reactive<unknown[], unknown>,
55
+ );
56
+ break;
57
+
48
58
  case isReactive(value):
49
59
  setReactiveValue(data, comment, value);
50
60
  break;