@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.
- package/dist/abydon.full.js +814 -645
- package/dist/constants.js +20 -5
- package/dist/fragment.js +31 -12
- package/dist/fragments.js +75 -0
- package/dist/helpers/dom.js +1 -11
- package/dist/helpers/index.js +14 -4
- package/dist/index.js +7 -3
- package/dist/node/attribute/index.js +14 -14
- package/dist/node/attribute/value.js +19 -13
- package/dist/node/event.js +15 -22
- package/dist/node/index.js +9 -4
- package/dist/node/value.js +49 -44
- package/dist/parse.js +15 -4
- package/package.json +13 -15
- package/src/constants.ts +30 -10
- package/src/fragment.ts +53 -30
- package/src/fragments.ts +132 -0
- package/src/helpers/dom.ts +1 -20
- package/src/helpers/index.ts +22 -9
- package/src/index.ts +26 -8
- package/src/models.ts +20 -2
- package/src/node/attribute/index.ts +22 -21
- package/src/node/attribute/value.ts +44 -37
- package/src/node/event.ts +26 -33
- package/src/node/index.ts +12 -14
- package/src/node/value.ts +35 -44
- package/src/parse.ts +15 -17
- package/types/constants.d.ts +12 -4
- package/types/fragment.d.ts +23 -3
- package/types/fragments.d.ts +13 -0
- package/types/helpers/index.d.ts +2 -0
- package/types/index.d.ts +16 -1
- package/types/models.d.ts +25 -3
- package/types/node/attribute/index.d.ts +1 -2
- package/types/node/attribute/value.d.ts +1 -2
- package/types/node/event.d.ts +1 -3
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
97
|
+
element: HTMLElement | SVGElement,
|
|
96
98
|
name: string,
|
|
97
99
|
value: unknown,
|
|
98
100
|
): void {
|
|
99
|
-
|
|
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
|
|
123
|
-
element
|
|
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
|
|
131
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
|
1
|
+
import {on} from '@oscarpalmer/toretto/event';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
EVENT_DEFAULTS,
|
|
4
|
+
EXPRESSION_EVENT_CHANGE_TYPES,
|
|
4
5
|
EXPRESSION_EVENT_NAME,
|
|
5
|
-
|
|
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.
|
|
25
|
-
once: parts.
|
|
26
|
-
passive: !
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
26
|
+
if (element instanceof HTMLInputElement) {
|
|
27
|
+
if (EXPRESSION_EVENT_CHANGE_TYPES.test(element.type)) {
|
|
28
|
+
return 'change';
|
|
29
|
+
}
|
|
38
30
|
|
|
39
|
-
|
|
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
|
|
48
|
-
|
|
49
|
-
|
|
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 {
|
|
5
|
-
import {
|
|
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 =
|
|
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:
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/types/constants.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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";
|
package/types/fragment.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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;
|
package/types/helpers/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
2
|
+
export declare function setAttribute(data: FragmentData, element: HTMLElement | SVGElement, name: string, value: unknown): void;
|