@oscarpalmer/abydon 0.20.0 → 0.22.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 (40) hide show
  1. package/README.md +2 -0
  2. package/dist/abydon.full.mjs +787 -276
  3. package/dist/attribute/index.d.mts +6 -0
  4. package/dist/{node/attribute → attribute}/index.mjs +10 -19
  5. package/dist/{node/attribute → attribute}/value.d.mts +2 -2
  6. package/dist/attribute/value.mjs +63 -0
  7. package/dist/constants.d.mts +7 -3
  8. package/dist/constants.mjs +12 -8
  9. package/dist/fragment.d.mts +45 -1
  10. package/dist/fragment.mjs +9 -3
  11. package/dist/fragments.d.mts +4 -6
  12. package/dist/fragments.mjs +5 -11
  13. package/dist/helpers/index.d.mts +11 -1
  14. package/dist/helpers/index.mjs +10 -0
  15. package/dist/index.d.mts +5 -4
  16. package/dist/index.mjs +8 -4
  17. package/dist/models.d.mts +43 -1
  18. package/dist/node/event.mjs +9 -4
  19. package/dist/node/index.d.mts +1 -1
  20. package/dist/node/index.mjs +19 -10
  21. package/dist/node/value.d.mts +1 -1
  22. package/dist/node/value.mjs +9 -12
  23. package/dist/parse.d.mts +1 -1
  24. package/dist/parse.mjs +6 -4
  25. package/package.json +8 -7
  26. package/src/{node/attribute → attribute}/index.ts +16 -33
  27. package/src/attribute/value.ts +140 -0
  28. package/src/constants.ts +16 -8
  29. package/src/fragment.ts +10 -5
  30. package/src/fragments.ts +4 -13
  31. package/src/helpers/index.ts +10 -0
  32. package/src/index.ts +16 -3
  33. package/src/node/event.ts +12 -5
  34. package/src/node/index.ts +31 -13
  35. package/src/node/value.ts +10 -25
  36. package/src/parse.ts +14 -4
  37. package/dist/fragment-9qTBAvtR.d.mts +0 -82
  38. package/dist/node/attribute/index.d.mts +0 -6
  39. package/dist/node/attribute/value.mjs +0 -65
  40. package/src/node/attribute/value.ts +0 -158
package/src/node/index.ts CHANGED
@@ -1,24 +1,25 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
+ import {getString} from '@oscarpalmer/atoms/string';
2
3
  import {
3
4
  computed,
4
5
  isComputed,
5
6
  isReactive,
6
7
  isSignal,
7
8
  type Computed,
8
- type Reactive,
9
+ type ReactiveArray,
9
10
  } from '@oscarpalmer/mora';
10
11
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
11
- import {EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE} from '../constants';
12
- import {handleFragments} from '../fragments';
12
+ import {mapAttributes} from '../attribute/index';
13
+ import {EVENT_ON_VALUE, EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE} from '../constants';
14
+ import {Fragments, fragmentsStates, handleFragments} from '../fragments';
13
15
  import {isFragment, isFragments} from '../helpers';
14
16
  import {createNodes} from '../helpers/dom';
15
17
  import type {FragmentData} from '../models';
16
- import {mapAttributes} from './attribute';
17
- import {setReactiveValue} from './value';
18
18
  import {mapEvent} from './event';
19
+ import {setReactiveValue} from './value';
19
20
 
20
21
  function mapNode(data: FragmentData, comment: Comment): void {
21
- const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent ?? '');
22
+ const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent);
22
23
  const value = matches == null ? null : data.values[+matches[1]];
23
24
 
24
25
  if (value != null) {
@@ -53,23 +54,33 @@ export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
53
54
  }
54
55
 
55
56
  function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): void {
56
- const [, index] = EXPRESSION_TEXTAREA_VALUE.exec(element.value) ?? [];
57
+ const [, index] =
58
+ EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ??
59
+ EXPRESSION_TEXTAREA_VALUE.exec(element.value) ??
60
+ [];
57
61
 
58
62
  if (index == null) {
59
63
  return;
60
64
  }
61
65
 
66
+ element.textContent = '';
62
67
  element.value = '';
63
68
 
64
69
  const value = data.values[Number.parseInt(index, 10)];
65
70
 
66
71
  if (isSignal(value)) {
67
- element.value = String(value.peek());
72
+ element.value = getString(value.peek());
68
73
 
69
- mapEvent(element, '@on', () => {
74
+ mapEvent(element, EVENT_ON_VALUE, () => {
70
75
  value.set(element.value);
71
76
  });
72
77
 
78
+ data.mora.subscribers.add(
79
+ value.subscribe(value => {
80
+ element.value = getString(value);
81
+ }),
82
+ );
83
+
73
84
  return;
74
85
  }
75
86
 
@@ -82,11 +93,11 @@ function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): void {
82
93
  }
83
94
 
84
95
  if (reactive == null) {
85
- element.value = String(value);
96
+ element.value = '';
86
97
  } else {
87
98
  data.mora.subscribers.add(
88
99
  reactive.subscribe(value => {
89
- element.value = String(value);
100
+ element.value = getString(value);
90
101
  }),
91
102
  );
92
103
  }
@@ -99,8 +110,7 @@ function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
99
110
  break;
100
111
 
101
112
  case isFragments(value):
102
- handleFragments(value, false);
103
- setReactiveValue(data, comment, value.items as Reactive<unknown[], unknown>);
113
+ setFragmentsValue(data, comment, value);
104
114
  break;
105
115
 
106
116
  case isReactive(value):
@@ -132,3 +142,11 @@ function setComputedValue(data: FragmentData, comment: Comment, callback: Generi
132
142
 
133
143
  setReactiveValue(data, comment, value);
134
144
  }
145
+
146
+ function setFragmentsValue(data: FragmentData, comment: Comment, fragments: Fragments): void {
147
+ const state = fragmentsStates.get(fragments)!;
148
+
149
+ handleFragments(state, false);
150
+
151
+ setReactiveValue(data, comment, state.mapped as ReactiveArray<unknown>);
152
+ }
package/src/node/value.ts CHANGED
@@ -113,11 +113,7 @@ function replaceText(item: FragmentItem, comment: Comment, isNullable: boolean):
113
113
  replaceNodes(item.nodes ?? [], to);
114
114
  }
115
115
 
116
- function setArray(
117
- item: FragmentItem,
118
- comment: Comment,
119
- value: unknown[],
120
- ): Partial<FragmentItem> | boolean {
116
+ function setArray(item: FragmentItem, comment: Comment, value: unknown[]): Partial<FragmentItem> {
121
117
  if (value.length === 0) {
122
118
  return {
123
119
  nodes: setText(item, comment, value),
@@ -129,7 +125,9 @@ function setArray(
129
125
  const next = templates.map(fragment => fragment.identifier) as unknown[];
130
126
  const previous = item.fragments?.map(fragment => fragment.identifier) ?? [];
131
127
 
132
- if (new Set(next).size !== templates.length) {
128
+ const nextSet = new Set(next);
129
+
130
+ if (nextSet.size !== templates.length) {
133
131
  templates = [];
134
132
  }
135
133
 
@@ -150,7 +148,7 @@ function setArray(
150
148
 
151
149
  return handleArray(
152
150
  {
153
- next: new Set(next),
151
+ next: nextSet,
154
152
  previous: new Set(previous),
155
153
  },
156
154
  {
@@ -163,11 +161,7 @@ function setArray(
163
161
 
164
162
  function setNodes(item: FragmentItem, comment: Comment, next: ChildNode[]): ChildNode[] {
165
163
  if (item.nodes == null) {
166
- if (comment.parentNode != null) {
167
- replaceNodes([comment], next);
168
- } else if (item.text?.parentNode != null) {
169
- replaceNodes([item.text], next);
170
- }
164
+ replaceNodes([comment], next);
171
165
  } else {
172
166
  replaceNodes(item.nodes, next);
173
167
  }
@@ -196,25 +190,16 @@ export function setReactiveValue(
196
190
  setReactiveValueForSingle(item, comment, value);
197
191
  }
198
192
 
199
- item.nodes = [...(item.nodes ?? [comment])];
193
+ item.nodes ??= [comment];
200
194
  }),
201
195
  );
202
196
  }
203
197
 
204
198
  function setReactiveValueForArray(item: FragmentItem, comment: Comment, value: unknown[]): void {
205
- const result = setArray(item, comment, value);
206
-
207
- item.fragments = typeof result === 'boolean' ? undefined : result?.fragments;
199
+ const {fragments, nodes} = setArray(item, comment, value);
208
200
 
209
- if (typeof result === 'boolean') {
210
- if (result) {
211
- item.nodes = item.text == null ? [] : [item.text];
212
- } else {
213
- item.nodes = undefined;
214
- }
215
- } else {
216
- item.nodes = result?.nodes;
217
- }
201
+ item.fragments = fragments;
202
+ item.nodes = nodes;
218
203
  }
219
204
 
220
205
  function setReactiveValueForSingle(item: FragmentItem, comment: Comment, value: unknown): void {
package/src/parse.ts CHANGED
@@ -1,9 +1,17 @@
1
- import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
2
- import {EXPRESSION_ABYDON_ATTRIBUTE_FULL} from './constants';
1
+ import {getString} from '@oscarpalmer/atoms/string';
2
+ import {
3
+ EXPRESSION_ABYDON_ATTRIBUTE_FULL,
4
+ EXPRESSION_EVENT_ATTRIBUTE,
5
+ WHITESPACE,
6
+ } from './constants';
3
7
  import type {FragmentData} from './models';
4
8
 
5
9
  function handleExpression(data: FragmentData, prefix: string, expression: unknown): string {
6
10
  if (Array.isArray(expression)) {
11
+ if (EXPRESSION_EVENT_ATTRIBUTE.test(prefix.split(WHITESPACE).at(-1)!)) {
12
+ return transformExpression(prefix, data.values.push(expression) - 1);
13
+ }
14
+
7
15
  const {length} = expression;
8
16
 
9
17
  let expressions = '';
@@ -19,7 +27,9 @@ function handleExpression(data: FragmentData, prefix: string, expression: unknow
19
27
  return transformExpression(prefix, data.values.push(expression) - 1);
20
28
  }
21
29
 
22
- return isNullableOrWhitespace(expression) ? prefix : `${prefix}${expression}`;
30
+ const asString = getString(expression);
31
+
32
+ return asString.trim().length === 0 ? prefix : `${prefix}${asString}`;
23
33
  }
24
34
 
25
35
  export function parse(data: FragmentData): string {
@@ -44,7 +54,7 @@ export function parse(data: FragmentData): string {
44
54
  }
45
55
 
46
56
  function transformAttribute(_: string, name: string, index: string): string {
47
- return `_${name}="@abydon.${index}@"`;
57
+ return `abydon-${name}="abydon.${index}"`;
48
58
  }
49
59
 
50
60
  function transformExpression(prefix: string, index: number): string {
@@ -1,82 +0,0 @@
1
- import { Reactive, ReactiveArray, Unsubscribe } from "@oscarpalmer/mora";
2
-
3
- //#region src/models.d.ts
4
- type FragmentConfiguration = {
5
- /**
6
- * Should the template be cached? _(defaults to `true`)_
7
- */
8
- cache?: boolean;
9
- /**
10
- * Identifier for the fragment
11
- *
12
- * _(An identifier can be used to uniquely identify a fragment,
13
- * which helps prevent re-rendering in certain scenarios)_
14
- */
15
- identifier?: unknown;
16
- };
17
- type FragmentData = {
18
- expressions: unknown[];
19
- items: FragmentItem[];
20
- mora: MoraData;
21
- strings: TemplateStringsArray;
22
- template?: string;
23
- values: unknown[];
24
- };
25
- type FragmentItem = {
26
- fragments?: Fragment[];
27
- nodes?: ChildNode[];
28
- text?: Text;
29
- };
30
- type FragmentsState = {
31
- array: ReactiveArray<unknown>;
32
- fragment: (item: unknown) => Fragment;
33
- identify: (item: unknown) => unknown;
34
- instances: Record<string, Fragment>;
35
- mapped: ReactiveArray<Fragment>;
36
- subscriber: Unsubscribe | undefined;
37
- };
38
- type MoraData = {
39
- subscribers: Set<() => void>;
40
- values: Set<Reactive<unknown>>;
41
- };
42
- //#endregion
43
- //#region src/fragment.d.ts
44
- declare class Fragment {
45
- #private;
46
- /**
47
- * Fragment identifier
48
- */
49
- get identifier(): unknown;
50
- constructor(strings: TemplateStringsArray, expressions: unknown[]);
51
- /**
52
- * Append the fragment to the given element
53
- * @param element Element to append to
54
- */
55
- appendTo(element: Element): void;
56
- /**
57
- * Configure the fragment
58
- * @param configuration Configuration options
59
- * @returns Fragment
60
- */
61
- configure(configuration: FragmentConfiguration): Fragment;
62
- /**
63
- * Get a list of the fragment's nodes
64
- * @returns List of nodes
65
- */
66
- get(): ChildNode[];
67
- /**
68
- * Set an identifier for the fragment
69
- *
70
- * _An identifier can be used to uniquely identify a fragment,
71
- * which helps prevent re-rendering in certain scenarios._
72
- * @param identifier Identifier
73
- * @returns Fragment
74
- */
75
- identify(identifier: unknown): Fragment;
76
- /**
77
- * Remove the fragment from the DOM
78
- */
79
- remove(): void;
80
- }
81
- //#endregion
82
- export { FragmentsState as a, FragmentItem as i, FragmentConfiguration as n, FragmentData as r, Fragment as t };
@@ -1,6 +0,0 @@
1
- import { r as FragmentData } from "../../fragment-9qTBAvtR.mjs";
2
-
3
- //#region src/node/attribute/index.d.ts
4
- declare function mapAttributes(data: FragmentData, element: HTMLElement | SVGElement): void;
5
- //#endregion
6
- export { mapAttributes };
@@ -1,65 +0,0 @@
1
- import { EVENT_CHANGE, EVENT_INPUT, EXPRESSION_ATTRIBUTE_CLASS, EXPRESSION_ATTRIBUTE_STYLE_FULL, EXPRESSION_ATTRIBUTE_STYLE_PREFIX, PROPERTY_CHECKED, PROPERTY_VALUE } from "../../constants.mjs";
2
- import { isReactive } from "@oscarpalmer/mora";
3
- import { isBooleanAttribute, setAttribute as setAttribute$1 } from "@oscarpalmer/toretto/attribute";
4
- //#region src/node/attribute/value.ts
5
- function getCallback(element, name) {
6
- if (isBooleanAttribute(name) && name in element) return name === "checked" ? updateChecked : updateProperty;
7
- return name === "value" ? updateValue : setAttribute$1;
8
- }
9
- function setAttribute(data, element, name, value) {
10
- switch (true) {
11
- case EXPRESSION_ATTRIBUTE_CLASS.test(name):
12
- setClasses(data, element, name, value);
13
- return;
14
- case EXPRESSION_ATTRIBUTE_STYLE_PREFIX.test(name):
15
- setStyle(data, element, name, value);
16
- return;
17
- default:
18
- setValue(data, element, name, value);
19
- break;
20
- }
21
- }
22
- function setClasses(data, element, name, value) {
23
- function update(value) {
24
- if (value === true) element.classList.add(...classes);
25
- else element.classList.remove(...classes);
26
- }
27
- const classes = name.slice(6).split(".");
28
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
29
- else update(value);
30
- }
31
- function setStyle(data, element, name, value) {
32
- const [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name) ?? [];
33
- if (property == null) return;
34
- function update(value) {
35
- if (value == null || value === false || value === true && unit == null) element.style.removeProperty(property);
36
- else element.style.setProperty(property, value === true ? unit : String(value));
37
- }
38
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
39
- else update(value);
40
- }
41
- function setValue(data, element, name, value) {
42
- const callback = getCallback(element, name);
43
- if (isReactive(value)) data.mora.subscribers.add(value.subscribe((next) => {
44
- callback(element, name, next);
45
- }));
46
- else callback(element, name, value);
47
- }
48
- function updateChecked(element, name, value) {
49
- updateElement(EVENT_CHANGE, PROPERTY_CHECKED, element, name, value, value === true);
50
- }
51
- function updateElement(event, property, element, name, value, next) {
52
- if (!(property in element) || element[property] === next) return;
53
- setAttribute$1(element, name, value);
54
- if (element[property] === next) return;
55
- element[property] = next;
56
- element.dispatchEvent(new Event(event, { bubbles: true }));
57
- }
58
- function updateProperty(element, name, value) {
59
- setAttribute$1(element, name, value === true);
60
- }
61
- function updateValue(element, name, value) {
62
- updateElement(element instanceof HTMLSelectElement ? EVENT_CHANGE : EVENT_INPUT, PROPERTY_VALUE, element, name, value, String(value));
63
- }
64
- //#endregion
65
- export { setAttribute };
@@ -1,158 +0,0 @@
1
- import type {PlainObject} from '@oscarpalmer/atoms/models';
2
- import {isReactive} from '@oscarpalmer/mora';
3
- import {
4
- isBooleanAttribute,
5
- setAttribute as setTorettoAttribute,
6
- } from '@oscarpalmer/toretto/attribute';
7
- import {
8
- ATTRIBUTE_CLASS_PREFIX_LENGTH,
9
- ATTRIBUTE_NAME_DELIMITER,
10
- EVENT_CHANGE,
11
- EVENT_INPUT,
12
- EXPRESSION_ATTRIBUTE_CLASS,
13
- EXPRESSION_ATTRIBUTE_STYLE_FULL,
14
- EXPRESSION_ATTRIBUTE_STYLE_PREFIX,
15
- PROPERTY_CHECKED,
16
- PROPERTY_VALUE,
17
- } from '../../constants';
18
- import type {FragmentData} from '../../models';
19
-
20
- function getCallback(element: HTMLElement | SVGElement, name: string) {
21
- if (isBooleanAttribute(name) && name in element) {
22
- return name === PROPERTY_CHECKED ? updateChecked : updateProperty;
23
- }
24
-
25
- return name === PROPERTY_VALUE ? updateValue : setTorettoAttribute;
26
- }
27
-
28
- export function setAttribute(
29
- data: FragmentData,
30
- element: HTMLElement | SVGElement,
31
- name: string,
32
- value: unknown,
33
- ): void {
34
- switch (true) {
35
- case EXPRESSION_ATTRIBUTE_CLASS.test(name):
36
- setClasses(data, element, name, value);
37
- return;
38
-
39
- case EXPRESSION_ATTRIBUTE_STYLE_PREFIX.test(name):
40
- setStyle(data, element, name, value);
41
- return;
42
-
43
- default:
44
- setValue(data, element, name, value);
45
- break;
46
- }
47
- }
48
-
49
- function setClasses(
50
- data: FragmentData,
51
- element: HTMLElement | SVGElement,
52
- name: string,
53
- value: unknown,
54
- ): void {
55
- function update(value: unknown): void {
56
- if (value === true) {
57
- element.classList.add(...classes);
58
- } else {
59
- element.classList.remove(...classes);
60
- }
61
- }
62
-
63
- const classes = name.slice(ATTRIBUTE_CLASS_PREFIX_LENGTH).split(ATTRIBUTE_NAME_DELIMITER);
64
-
65
- if (isReactive(value)) {
66
- data.mora.subscribers.add(value.subscribe(update));
67
- } else {
68
- update(value);
69
- }
70
- }
71
-
72
- function setStyle(
73
- data: FragmentData,
74
- element: HTMLElement | SVGElement,
75
- name: string,
76
- value: unknown,
77
- ): void {
78
- const [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name) ?? [];
79
-
80
- if (property == null) {
81
- return;
82
- }
83
-
84
- function update(value: unknown): void {
85
- if (value == null || value === false || (value === true && unit == null)) {
86
- element.style.removeProperty(property);
87
- } else {
88
- element.style.setProperty(property, value === true ? unit : String(value));
89
- }
90
- }
91
-
92
- if (isReactive(value)) {
93
- data.mora.subscribers.add(value.subscribe(update));
94
- } else {
95
- update(value);
96
- }
97
- }
98
-
99
- function setValue(
100
- data: FragmentData,
101
- element: HTMLElement | SVGElement,
102
- name: string,
103
- value: unknown,
104
- ): void {
105
- const callback = getCallback(element, name);
106
-
107
- if (isReactive(value)) {
108
- data.mora.subscribers.add(
109
- value.subscribe(next => {
110
- callback(element, name, next);
111
- }),
112
- );
113
- } else {
114
- callback(element, name, value);
115
- }
116
- }
117
-
118
- function updateChecked(element: HTMLElement | SVGElement, name: string, value: unknown): void {
119
- updateElement(EVENT_CHANGE, PROPERTY_CHECKED, element, name, value, value === true);
120
- }
121
-
122
- function updateElement(
123
- event: string,
124
- property: string,
125
- element: HTMLElement | SVGElement,
126
- name: string,
127
- value: unknown,
128
- next: unknown,
129
- ): void {
130
- if (!(property in element) || (element as unknown as PlainObject)[property] === next) {
131
- return;
132
- }
133
-
134
- setTorettoAttribute(element, name, value);
135
-
136
- if ((element as unknown as PlainObject)[property] === next) {
137
- return;
138
- }
139
-
140
- (element as unknown as PlainObject)[property] = next;
141
-
142
- element.dispatchEvent(new Event(event, {bubbles: true}));
143
- }
144
-
145
- function updateProperty(element: HTMLElement | SVGElement, name: string, value: unknown): void {
146
- setTorettoAttribute(element, name, value === true);
147
- }
148
-
149
- function updateValue(element: HTMLElement | SVGElement, name: string, value: unknown): void {
150
- updateElement(
151
- element instanceof HTMLSelectElement ? EVENT_CHANGE : EVENT_INPUT,
152
- PROPERTY_VALUE,
153
- element,
154
- name,
155
- value,
156
- String(value),
157
- );
158
- }