@oscarpalmer/abydon 0.14.0 → 0.16.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,11 +1,10 @@
1
1
  import type {PlainObject} from '@oscarpalmer/atoms/models';
2
- import {getString} from '@oscarpalmer/atoms/string';
3
2
  import {isReactive} from '@oscarpalmer/mora';
4
3
  import {
5
4
  isBooleanAttribute,
6
5
  setAttribute as setAttr,
6
+ setProperty,
7
7
  } from '@oscarpalmer/toretto/attribute';
8
- import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
9
8
  import {
10
9
  ATTRIBUTE_CLASS_PREFIX_LENGTH,
11
10
  EXPRESSION_ATTRIBUTE_CLASS,
@@ -14,14 +13,20 @@ import {
14
13
  } from '../../constants';
15
14
  import type {FragmentData} from '../../models';
16
15
 
16
+ function getCallback(element: HTMLElement | SVGElement, name: string) {
17
+ if (isBooleanAttribute(name) && name in element) {
18
+ return name === 'checked' ? updateChecked : updateProperty;
19
+ }
20
+
21
+ return name === 'value' ? updateValue : setAttr;
22
+ }
23
+
17
24
  export function setAttribute(
18
25
  data: FragmentData,
19
- element: HTMLOrSVGElement,
26
+ element: HTMLElement | SVGElement,
20
27
  name: string,
21
28
  value: unknown,
22
29
  ): void {
23
- element.removeAttribute(name);
24
-
25
30
  switch (true) {
26
31
  case EXPRESSION_ATTRIBUTE_CLASS.test(name):
27
32
  setClasses(data, element, name, value);
@@ -39,7 +44,7 @@ export function setAttribute(
39
44
 
40
45
  function setClasses(
41
46
  data: FragmentData,
42
- element: HTMLOrSVGElement,
47
+ element: HTMLElement | SVGElement,
43
48
  name: string,
44
49
  value: unknown,
45
50
  ): void {
@@ -62,7 +67,7 @@ function setClasses(
62
67
 
63
68
  function setStyle(
64
69
  data: FragmentData,
65
- element: HTMLOrSVGElement,
70
+ element: HTMLElement | SVGElement,
66
71
  name: string,
67
72
  value: unknown,
68
73
  ): void {
@@ -76,10 +81,7 @@ function setStyle(
76
81
  if (value == null || value === false || (value === true && unit == null)) {
77
82
  element.style.removeProperty(property);
78
83
  } else {
79
- element.style.setProperty(
80
- property,
81
- value === true ? unit : getString(value),
82
- );
84
+ element.style.setProperty(property, value === true ? unit : String(value));
83
85
  }
84
86
  }
85
87
 
@@ -92,21 +94,11 @@ function setStyle(
92
94
 
93
95
  function setValue(
94
96
  data: FragmentData,
95
- element: HTMLOrSVGElement,
97
+ element: HTMLElement | SVGElement,
96
98
  name: string,
97
99
  value: unknown,
98
100
  ): void {
99
- let callback: (
100
- element: HTMLOrSVGElement,
101
- name: string,
102
- value: unknown,
103
- ) => void;
104
-
105
- if (isBooleanAttribute(name) && name in element) {
106
- callback = name === 'selected' ? updateSelected : updateProperty;
107
- } else {
108
- callback = setAttr;
109
- }
101
+ const callback = getCallback(element, name);
110
102
 
111
103
  if (isReactive(value)) {
112
104
  data.mora.subscribers.add(
@@ -119,25 +111,40 @@ function setValue(
119
111
  }
120
112
  }
121
113
 
122
- function updateProperty(
123
- element: HTMLOrSVGElement,
124
- name: string,
125
- value: unknown,
126
- ): void {
127
- (element as unknown as PlainObject)[name] = value === true;
114
+ function updateChecked(element: HTMLElement | SVGElement, name: string, value: unknown): void {
115
+ updateElement('change', 'checked', element, name, value, value === true);
128
116
  }
129
117
 
130
- function updateSelected(
131
- element: HTMLOrSVGElement,
118
+ function updateElement(
119
+ event: string,
120
+ property: string,
121
+ element: HTMLElement | SVGElement,
132
122
  name: string,
133
123
  value: unknown,
124
+ next: unknown,
134
125
  ): void {
135
- const select = element.closest('select') as HTMLSelectElement;
136
- const options = [...(select?.options ?? [])];
137
-
138
- if (select != null && options.includes(element as HTMLOptionElement)) {
139
- select.dispatchEvent(new Event('change', {bubbles: true}));
126
+ if (!(property in element) || (element as unknown as PlainObject)[property] === next) {
127
+ return;
140
128
  }
141
129
 
142
- updateProperty(element, name, value);
130
+ setAttr(element, name, value);
131
+
132
+ (element as unknown as PlainObject)[property] = next;
133
+
134
+ element.dispatchEvent(new Event(event, {bubbles: true}));
135
+ }
136
+
137
+ function updateProperty(element: HTMLElement | SVGElement, name: string, value: unknown): void {
138
+ setProperty(element, name, value === true);
139
+ }
140
+
141
+ function updateValue(element: HTMLElement | SVGElement, name: string, value: unknown): void {
142
+ updateElement(
143
+ element instanceof HTMLSelectElement ? 'change' : 'input',
144
+ 'value',
145
+ element,
146
+ name,
147
+ value,
148
+ String(value),
149
+ );
143
150
  }
package/src/node/event.ts CHANGED
@@ -1,50 +1,43 @@
1
- import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
1
+ import {on} from '@oscarpalmer/toretto/event';
2
2
  import {
3
- ABORT_CONTROLLERS,
3
+ EVENT_DEFAULTS,
4
+ EXPRESSION_EVENT_CHANGE_TYPES,
4
5
  EXPRESSION_EVENT_NAME,
5
- REASON_EVENT_REMOVED,
6
+ EXPRESSION_EVENT_OPTIONS_ACTIVE,
7
+ EXPRESSION_EVENT_OPTIONS_CAPTURE,
8
+ EXPRESSION_EVENT_OPTIONS_ONCE,
6
9
  } from '../constants';
7
10
 
8
- function getController(element: HTMLOrSVGElement): AbortController {
9
- let controller = ABORT_CONTROLLERS.get(element);
10
-
11
- if (controller == null) {
12
- controller = new AbortController();
13
-
14
- ABORT_CONTROLLERS.set(element, controller);
15
- }
16
-
17
- return controller;
18
- }
19
-
20
11
  function getOptions(options: string): AddEventListenerOptions {
21
12
  const parts = options.split(':');
22
13
 
23
14
  return {
24
- capture: parts.includes('c') || parts.includes('capture'),
25
- once: parts.includes('o') || parts.includes('once'),
26
- passive: !(parts.includes('a') || parts.includes('active')),
15
+ capture: parts.some(part => EXPRESSION_EVENT_OPTIONS_CAPTURE.test(part)),
16
+ once: parts.some(part => EXPRESSION_EVENT_OPTIONS_ONCE.test(part)),
17
+ passive: !parts.some(part => EXPRESSION_EVENT_OPTIONS_ACTIVE.test(part)),
27
18
  };
28
19
  }
29
20
 
30
- export function mapEvent(
31
- element: HTMLOrSVGElement,
32
- name: string,
33
- value: unknown,
34
- ): void {
35
- element.removeAttribute(name);
21
+ function getType(element: HTMLElement | SVGElement, type: string): string {
22
+ if (type !== 'on') {
23
+ return type;
24
+ }
36
25
 
37
- const [, type, options] = EXPRESSION_EVENT_NAME.exec(name) ?? [];
26
+ if (element instanceof HTMLInputElement) {
27
+ if (EXPRESSION_EVENT_CHANGE_TYPES.test(element.type)) {
28
+ return 'change';
29
+ }
38
30
 
39
- if (typeof value === 'function' && type != null) {
40
- element.addEventListener(type, value as EventListener, {
41
- ...getOptions(options ?? ''),
42
- signal: getController(element).signal,
43
- });
31
+ return element.type === 'submit' ? 'submit' : 'input';
44
32
  }
33
+
34
+ return EVENT_DEFAULTS[element.tagName] ?? type;
45
35
  }
46
36
 
47
- export function removeEvents(element: HTMLOrSVGElement): void {
48
- ABORT_CONTROLLERS.get(element)?.abort(REASON_EVENT_REMOVED);
49
- ABORT_CONTROLLERS.delete(element);
37
+ export function mapEvent(element: HTMLElement | SVGElement, name: string, value: unknown): void {
38
+ const [, type, options] = EXPRESSION_EVENT_NAME.exec(name) ?? [];
39
+
40
+ if (type != null && typeof value === 'function') {
41
+ on(element, getType(element, type), value as EventListener, getOptions(options ?? ''));
42
+ }
50
43
  }
package/src/node/index.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {computed, isReactive} from '@oscarpalmer/mora';
2
+ import {computed, isReactive, type Reactive} from '@oscarpalmer/mora';
3
3
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
4
- import {EXPRESSION_COMMENT_CONTENT} from '../constants';
5
- import {isFragment} from '../helpers';
4
+ import {EXPRESSION_ABYDON_CONTENT} from '../constants';
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';
9
10
  import {setReactiveValue} from './value';
10
11
 
11
12
  function mapNode(data: FragmentData, comment: Comment): void {
12
- const matches = EXPRESSION_COMMENT_CONTENT.exec(comment.textContent ?? '');
13
+ const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent ?? '');
13
14
  const value = matches == null ? null : data.values[+matches[1]];
14
15
 
15
16
  if (value != null) {
@@ -45,6 +46,11 @@ 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(data, comment, value.items as Reactive<unknown[], unknown>);
52
+ break;
53
+
48
54
  case isReactive(value):
49
55
  setReactiveValue(data, comment, value);
50
56
  break;
@@ -55,11 +61,7 @@ function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
55
61
  }
56
62
  }
57
63
 
58
- function replaceComment(
59
- data: FragmentData,
60
- comment: Comment,
61
- value: unknown,
62
- ): void {
64
+ function replaceComment(data: FragmentData, comment: Comment, value: unknown): void {
63
65
  const item = data.items.find(item => item.nodes?.includes(comment));
64
66
  const nodes = createNodes(value);
65
67
 
@@ -71,11 +73,7 @@ function replaceComment(
71
73
  comment.replaceWith(...nodes);
72
74
  }
73
75
 
74
- function setComputedValue(
75
- data: FragmentData,
76
- comment: Comment,
77
- callback: GenericCallback,
78
- ): void {
76
+ function setComputedValue(data: FragmentData, comment: Comment, callback: GenericCallback): void {
79
77
  const value = computed(callback);
80
78
 
81
79
  data.mora.values.add(value);
package/src/node/value.ts CHANGED
@@ -10,8 +10,8 @@ import type {FragmentData, FragmentItem} from '../models';
10
10
  //
11
11
 
12
12
  type Identifiers = {
13
- next: unknown[];
14
- previous: (unknown | undefined)[];
13
+ next: Set<unknown>;
14
+ previous: Set<unknown>;
15
15
  };
16
16
 
17
17
  type BaseItems = {
@@ -33,8 +33,7 @@ function addToArray(
33
33
  ): void {
34
34
  let position = nodes[0];
35
35
 
36
- const before =
37
- added && !identifiers.previous.includes(items.templates[0].identifier);
36
+ const before = added && !identifiers.previous.has(items.templates[0].identifier);
38
37
 
39
38
  const next = items.next.flatMap(fragment =>
40
39
  fragment.get().flatMap(node => ({
@@ -48,7 +47,7 @@ function addToArray(
48
47
  for (let index = 0; index < length; index += 1) {
49
48
  const node = next[index];
50
49
 
51
- if (!(added && identifiers.previous.includes(node.identifier))) {
50
+ if (!(added && identifiers.previous.has(node.identifier))) {
52
51
  if (index === 0 && before) {
53
52
  position.before(node.value);
54
53
  } else {
@@ -67,9 +66,7 @@ function handleArray(
67
66
  ): Partial<FragmentItem> {
68
67
  const next = items.templates.map(
69
68
  template =>
70
- items.fragments?.find(
71
- fragment => fragment.identifier === template.identifier,
72
- ) ?? template,
69
+ items.fragments?.find(fragment => fragment.identifier === template.identifier) ?? template,
73
70
  );
74
71
 
75
72
  const comparison = compareArrays(items.fragments ?? [], items.templates);
@@ -79,9 +76,7 @@ function handleArray(
79
76
  }
80
77
 
81
78
  const toRemove =
82
- items.fragments?.filter(
83
- fragment => !identifiers.next.includes(fragment.identifier),
84
- ) ?? [];
79
+ items.fragments?.filter(fragment => !identifiers.next.has(fragment.identifier)) ?? [];
85
80
 
86
81
  const {length} = toRemove;
87
82
 
@@ -105,6 +100,18 @@ function removeFragments(fragments: Fragment[] | undefined): void {
105
100
  }
106
101
  }
107
102
 
103
+ function replaceText(item: FragmentItem, comment: Comment, isNullable: boolean): void {
104
+ let to: ChildNode[];
105
+
106
+ if (isNullable) {
107
+ to = [comment];
108
+ } else {
109
+ to = item.text == null ? [] : [item.text];
110
+ }
111
+
112
+ replaceNodes(item.nodes ?? [], to);
113
+ }
114
+
108
115
  function setArray(
109
116
  item: FragmentItem,
110
117
  comment: Comment,
@@ -116,9 +123,7 @@ function setArray(
116
123
  };
117
124
  }
118
125
 
119
- let templates = value.filter(
120
- item => isFragment(item) && item.identifier != null,
121
- ) as Fragment[];
126
+ let templates = value.filter(item => isFragment(item) && item.identifier != null) as Fragment[];
122
127
 
123
128
  const next = templates.map(fragment => fragment.identifier) as unknown[];
124
129
  const previous = item.fragments?.map(fragment => fragment.identifier) ?? [];
@@ -129,11 +134,7 @@ function setArray(
129
134
 
130
135
  const noTemplates = templates.length === 0;
131
136
 
132
- if (
133
- noTemplates ||
134
- item.nodes == null ||
135
- previous.some(identifier => identifier == null)
136
- ) {
137
+ if (noTemplates || item.nodes == null || previous.some(identifier => identifier == null)) {
137
138
  return {
138
139
  fragments: noTemplates ? undefined : templates,
139
140
  nodes: setNodes(
@@ -148,8 +149,8 @@ function setArray(
148
149
 
149
150
  return handleArray(
150
151
  {
151
- next,
152
- previous,
152
+ next: new Set(next),
153
+ previous: new Set(previous),
153
154
  },
154
155
  {
155
156
  templates,
@@ -159,11 +160,7 @@ function setArray(
159
160
  );
160
161
  }
161
162
 
162
- function setNodes(
163
- item: FragmentItem,
164
- comment: Comment,
165
- next: ChildNode[],
166
- ): ChildNode[] {
163
+ function setNodes(item: FragmentItem, comment: Comment, next: ChildNode[]): ChildNode[] {
167
164
  if (item.nodes == null) {
168
165
  if (comment.parentNode != null) {
169
166
  replaceNodes([comment], next);
@@ -203,27 +200,23 @@ export function setReactiveValue(
203
200
  );
204
201
  }
205
202
 
206
- function setReactiveValueForArray(
207
- item: FragmentItem,
208
- comment: Comment,
209
- value: unknown[],
210
- ): void {
203
+ function setReactiveValueForArray(item: FragmentItem, comment: Comment, value: unknown[]): void {
211
204
  const result = setArray(item, comment, value);
212
205
 
213
206
  item.fragments = typeof result === 'boolean' ? undefined : result?.fragments;
214
207
 
215
208
  if (typeof result === 'boolean') {
216
- item.nodes = result ? item.text == null ? [] : [item.text] : undefined;
209
+ if (result) {
210
+ item.nodes = item.text == null ? [] : [item.text];
211
+ } else {
212
+ item.nodes = undefined;
213
+ }
217
214
  } else {
218
215
  item.nodes = result?.nodes;
219
216
  }
220
217
  }
221
218
 
222
- function setReactiveValueForSingle(
223
- item: FragmentItem,
224
- comment: Comment,
225
- value: unknown,
226
- ): void {
219
+ function setReactiveValueForSingle(item: FragmentItem, comment: Comment, value: unknown): void {
227
220
  const valueIsFragment = isFragment(value);
228
221
 
229
222
  item.fragments = valueIsFragment ? [value] : undefined;
@@ -235,11 +228,7 @@ function setReactiveValueForSingle(
235
228
  }
236
229
  }
237
230
 
238
- function setText(
239
- item: FragmentItem,
240
- comment: Comment,
241
- value: unknown,
242
- ): ChildNode[] | undefined {
231
+ function setText(item: FragmentItem, comment: Comment, value: unknown): ChildNode[] | undefined {
243
232
  const isNullable = isNullableOrWhitespace(value);
244
233
 
245
234
  if (item.text != null) {
@@ -249,7 +238,7 @@ function setText(
249
238
  let result = false;
250
239
 
251
240
  if (item.nodes != null) {
252
- replaceNodes(item.nodes, isNullable ? [comment] : item.text == null ? [] : [item.text]);
241
+ replaceText(item, comment, isNullable);
253
242
 
254
243
  result = !isNullable;
255
244
  } else if (isNullable && comment.parentNode == null) {
@@ -264,5 +253,7 @@ function setText(
264
253
 
265
254
  removeFragments(item.fragments);
266
255
 
267
- return result ? item.text == null ? [] : [item.text] : undefined;
256
+ if (result) {
257
+ return item.text == null ? [] : [item.text];
258
+ }
268
259
  }
package/src/parse.ts CHANGED
@@ -1,11 +1,8 @@
1
1
  import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
2
+ import {EXPRESSION_ABYDON_ATTRIBUTE_FULL} from './constants';
2
3
  import type {FragmentData} from './models';
3
4
 
4
- function handleExpression(
5
- data: FragmentData,
6
- prefix: string,
7
- expression: unknown,
8
- ): string {
5
+ function handleExpression(data: FragmentData, prefix: string, expression: unknown): string {
9
6
  if (Array.isArray(expression)) {
10
7
  const {length} = expression;
11
8
 
@@ -18,13 +15,8 @@ function handleExpression(
18
15
  return `${prefix}${expressions}`;
19
16
  }
20
17
 
21
- if (
22
- typeof expression === 'function' ||
23
- (typeof expression === 'object' && expression != null)
24
- ) {
25
- const index = data.values.push(expression) - 1;
26
-
27
- return `${prefix}<!--abydon.${index}-->`;
18
+ if (typeof expression === 'function' || (typeof expression === 'object' && expression != null)) {
19
+ return transformExpression(prefix, data.values.push(expression) - 1);
28
20
  }
29
21
 
30
22
  return isNullableOrWhitespace(expression) ? prefix : `${prefix}${expression}`;
@@ -40,15 +32,21 @@ export function parse(data: FragmentData): string {
40
32
  data.template = '';
41
33
 
42
34
  for (let index = 0; index < length; index += 1) {
43
- data.template += handleExpression(
44
- data,
45
- data.strings[index],
46
- data.expressions[index],
47
- );
35
+ data.template += handleExpression(data, data.strings[index], data.expressions[index]);
48
36
  }
49
37
 
38
+ data.template = data.template.replaceAll(EXPRESSION_ABYDON_ATTRIBUTE_FULL, transformAttribute);
39
+
50
40
  data.expressions = [];
51
41
  data.strings = [] as never;
52
42
 
53
43
  return data.template;
54
44
  }
45
+
46
+ function transformAttribute(_: string, name: string, index: string): string {
47
+ return `_${name}="@abydon.${index}@"`;
48
+ }
49
+
50
+ function transformExpression(prefix: string, index: number): string {
51
+ return `${prefix}<!--abydon.${index}-->`;
52
+ }
@@ -1,9 +1,17 @@
1
- export declare const ABORT_CONTROLLERS: WeakMap<HTMLOrSVGElement, AbortController>;
2
1
  export declare const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
2
+ export declare const EVENT_DEFAULTS: Record<string, string>;
3
+ export declare const EXPRESSION_ABYDON_ATTRIBUTE_FULL: RegExp;
4
+ export declare const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX: RegExp;
5
+ export declare const EXPRESSION_ABYDON_CONTENT: RegExp;
3
6
  export declare const EXPRESSION_ATTRIBUTE_CLASS: RegExp;
4
7
  export declare const EXPRESSION_ATTRIBUTE_STYLE_FULL: RegExp;
5
8
  export declare const EXPRESSION_ATTRIBUTE_STYLE_PREFIX: RegExp;
6
- export declare const EXPRESSION_COMMENT_FULL: RegExp;
7
- export declare const EXPRESSION_COMMENT_CONTENT: RegExp;
9
+ export declare const EXPRESSION_EVENT_CHANGE_TYPES: RegExp;
8
10
  export declare const EXPRESSION_EVENT_NAME: RegExp;
9
- export declare const REASON_EVENT_REMOVED = "Event removed as element was removed from document by Abydon";
11
+ export declare const EXPRESSION_EVENT_OPTIONS_ACTIVE: RegExp;
12
+ export declare const EXPRESSION_EVENT_OPTIONS_CAPTURE: RegExp;
13
+ export declare const EXPRESSION_EVENT_OPTIONS_ONCE: RegExp;
14
+ export declare const EXPRESSION_EVENT_PREFIX: RegExp;
15
+ export declare const EXPRESSION_PERIOD: RegExp;
16
+ export declare const NAME_FRAGMENT = "$fragment";
17
+ export declare const NAME_FRAGMENTS = "$fragments";
@@ -1,18 +1,38 @@
1
+ import type { FragmentConfiguration } from './models';
1
2
  export declare class Fragment {
2
3
  #private;
4
+ /**
5
+ * Fragment identifier
6
+ */
3
7
  get identifier(): unknown;
4
8
  constructor(strings: TemplateStringsArray, expressions: unknown[]);
5
9
  /**
6
- * Appends the fragment to the given element
10
+ * Append the fragment to the given element
11
+ * @param element Element to append to
7
12
  */
8
13
  appendTo(element: Element): void;
9
14
  /**
10
- * Gets a list of the fragment's nodes
15
+ * Configure the fragment
16
+ * @param configuration Configuration options
17
+ * @returns Fragment
18
+ */
19
+ configure(configuration: FragmentConfiguration): Fragment;
20
+ /**
21
+ * Get a list of the fragment's nodes
22
+ * @returns List of nodes
11
23
  */
12
24
  get(): ChildNode[];
25
+ /**
26
+ * Set an identifier for the fragment
27
+ *
28
+ * _An identifier can be used to uniquely identify a fragment,
29
+ * which helps prevent re-rendering in certain scenarios._
30
+ * @param identifier Identifier
31
+ * @returns Fragment
32
+ */
13
33
  identify(identifier: unknown): Fragment;
14
34
  /**
15
- * Removes the fragment from the DOM
35
+ * Remove the fragment from the DOM
16
36
  */
17
37
  remove(): void;
18
38
  }
@@ -0,0 +1,13 @@
1
+ import { type ReactiveArray } from '@oscarpalmer/mora';
2
+ import type { Fragment } from './fragment';
3
+ import type { FragmentsState } from './models';
4
+ export declare class Fragments {
5
+ #private;
6
+ /**
7
+ * Fragment items
8
+ */
9
+ get items(): ReactiveArray<Fragment>;
10
+ constructor(items: ReactiveArray<unknown>, identify: (item: unknown) => unknown, fragment: (item: unknown) => Fragment);
11
+ }
12
+ export declare function handleFragments(item: Fragments | FragmentsState, remove: boolean): void;
13
+ export declare function initializeFragments(state: FragmentsState): void;
@@ -1,3 +1,5 @@
1
1
  import type { Fragment } from '../fragment';
2
+ import type { Fragments } from '../fragments';
2
3
  export declare function compareArrays(first: unknown[], second: unknown[]): 'added' | 'dissimilar' | 'removed';
3
4
  export declare function isFragment(value: unknown): value is Fragment;
5
+ export declare function isFragments(value: unknown): value is Fragments;
package/types/index.d.ts CHANGED
@@ -1,5 +1,20 @@
1
1
  import '@oscarpalmer/mora';
2
+ import type { ReactiveArray } from '@oscarpalmer/mora';
2
3
  import { Fragment } from './fragment';
3
- export declare function html(strings: TemplateStringsArray, ...values: unknown[]): Fragment;
4
+ import { Fragments } from './fragments';
5
+ /**
6
+ * Create Fragments from a reactive array
7
+ * @param array Reactive array
8
+ * @param identify Function to identify item
9
+ * @param fragment Function to create fragment from item
10
+ * @returns Fragments
11
+ */
12
+ export declare function fragments<Item>(array: ReactiveArray<Item>, identify: (item: Item) => unknown, fragment: (item: Item) => Fragment): Fragments;
13
+ /**
14
+ * Create Fragment from a template
15
+ * @returns Fragment
16
+ */
17
+ export declare function html(template: TemplateStringsArray, ...values: unknown[]): Fragment;
4
18
  export * from '@oscarpalmer/mora';
5
19
  export type { Fragment } from './fragment';
20
+ export type { Fragments } from './fragments';
package/types/models.d.ts CHANGED
@@ -1,16 +1,38 @@
1
- import type { Reactive } from '@oscarpalmer/mora';
1
+ import type { Reactive, ReactiveArray, Unsubscribe } from '@oscarpalmer/mora';
2
2
  import type { Fragment } from './fragment';
3
+ export type FragmentConfiguration = {
4
+ /**
5
+ * Should the template be cached? _(defaults to `true`)_
6
+ */
7
+ cache?: boolean;
8
+ /**
9
+ * Identifier for the fragment
10
+ *
11
+ * _(An identifier can be used to uniquely identify a fragment,
12
+ * which helps prevent re-rendering in certain scenarios)_
13
+ */
14
+ identifier?: unknown;
15
+ };
3
16
  export type FragmentData = {
4
17
  expressions: unknown[];
5
- identifier?: unknown;
6
18
  items: FragmentItem[];
7
19
  mora: MoraData;
8
20
  strings: TemplateStringsArray;
21
+ template?: string;
9
22
  values: unknown[];
10
23
  };
11
24
  export type FragmentItem = {
12
25
  fragments?: Fragment[];
13
- nodes: ChildNode[];
26
+ nodes?: ChildNode[];
27
+ text?: Text;
28
+ };
29
+ export type FragmentsState = {
30
+ array: ReactiveArray<unknown>;
31
+ fragment: (item: unknown) => Fragment;
32
+ identify: (item: unknown) => unknown;
33
+ instances: Record<string, Fragment>;
34
+ mapped: ReactiveArray<Fragment>;
35
+ subscriber: Unsubscribe | undefined;
14
36
  };
15
37
  type MoraData = {
16
38
  subscribers: Set<() => void>;
@@ -1,3 +1,2 @@
1
- import type { HTMLOrSVGElement } from '@oscarpalmer/toretto/models';
2
1
  import type { FragmentData } from '../../models';
3
- export declare function mapAttributes(data: FragmentData, element: HTMLOrSVGElement): void;
2
+ export declare function mapAttributes(data: FragmentData, element: HTMLElement | SVGElement): void;
@@ -1,3 +1,2 @@
1
- import type { HTMLOrSVGElement } from '@oscarpalmer/toretto/models';
2
1
  import type { FragmentData } from '../../models';
3
- export declare function setAttribute(data: FragmentData, element: HTMLOrSVGElement, name: string, value: unknown): void;
2
+ export declare function setAttribute(data: FragmentData, element: HTMLElement | SVGElement, name: string, value: unknown): void;