@oscarpalmer/abydon 0.22.0 → 0.23.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.mjs +222 -160
- package/dist/attribute/index.d.mts +3 -2
- package/dist/attribute/index.mjs +21 -16
- package/dist/attribute/value.mjs +18 -28
- package/dist/constants.d.mts +4 -8
- package/dist/constants.mjs +6 -10
- package/dist/fragment.d.mts +27 -14
- package/dist/fragment.mjs +32 -16
- package/dist/fragments.d.mts +1 -2
- package/dist/fragments.mjs +2 -2
- package/dist/helpers/dom.d.mts +2 -3
- package/dist/helpers/dom.mjs +2 -4
- package/dist/helpers/index.d.mts +9 -5
- package/dist/helpers/index.mjs +11 -5
- package/dist/index.d.mts +33 -6
- package/dist/index.mjs +33 -6
- package/dist/models.d.mts +6 -4
- package/dist/node/index.mjs +18 -33
- package/dist/node/value.mjs +67 -50
- package/package.json +2 -2
- package/src/attribute/index.ts +41 -35
- package/src/attribute/value.ts +20 -53
- package/src/constants.ts +6 -14
- package/src/fragment.ts +34 -17
- package/src/fragments.ts +4 -2
- package/src/helpers/dom.ts +3 -5
- package/src/helpers/index.ts +19 -5
- package/src/index.ts +33 -6
- package/src/models.ts +6 -4
- package/src/node/index.ts +25 -63
- package/src/node/value.ts +117 -85
- package/src/parse.ts +1 -1
package/dist/node/index.mjs
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE } from "../constants.mjs";
|
|
2
|
-
import { isFragment, isFragments } from "../helpers/index.mjs";
|
|
2
|
+
import { isFragment, isFragments, setComputedValue } from "../helpers/index.mjs";
|
|
3
3
|
import { fragmentsStates, handleFragments } from "../fragments.mjs";
|
|
4
4
|
import { createNodes } from "../helpers/dom.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { mapAttributes } from "../attribute/index.mjs";
|
|
5
|
+
import { mapAttributeValue, mapAttributes } from "../attribute/index.mjs";
|
|
7
6
|
import { setReactiveValue } from "./value.mjs";
|
|
8
|
-
import {
|
|
9
|
-
import { computed, isComputed, isReactive, isSignal } from "@oscarpalmer/mora";
|
|
7
|
+
import { isReactive } from "@oscarpalmer/mora";
|
|
10
8
|
import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
|
|
11
9
|
//#region src/node/index.ts
|
|
12
10
|
function mapNode(data, comment) {
|
|
@@ -22,42 +20,29 @@ function mapNodes(data, nodes) {
|
|
|
22
20
|
mapNode(data, node);
|
|
23
21
|
continue;
|
|
24
22
|
}
|
|
25
|
-
if (node
|
|
26
|
-
|
|
23
|
+
if (isHTMLOrSVGElement(node)) {
|
|
24
|
+
let ignoreValueAttribute = false;
|
|
25
|
+
if (node instanceof HTMLTextAreaElement) ignoreValueAttribute = mapTextarea(data, node);
|
|
26
|
+
mapAttributes(data, node, ignoreValueAttribute);
|
|
27
|
+
}
|
|
27
28
|
if (node.hasChildNodes()) mapNodes(data, [...node.childNodes]);
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
function mapTextarea(data, element) {
|
|
31
32
|
const [, index] = EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ?? EXPRESSION_TEXTAREA_VALUE.exec(element.value) ?? [];
|
|
32
|
-
if (index == null) return;
|
|
33
|
+
if (index == null) return false;
|
|
33
34
|
element.textContent = "";
|
|
34
35
|
element.value = "";
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
element.value = getString(value.peek());
|
|
38
|
-
mapEvent(element, "on", () => {
|
|
39
|
-
value.set(element.value);
|
|
40
|
-
});
|
|
41
|
-
data.mora.subscribers.add(value.subscribe((value) => {
|
|
42
|
-
element.value = getString(value);
|
|
43
|
-
}));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
let reactive;
|
|
47
|
-
if (typeof value === "function") reactive = computed(value);
|
|
48
|
-
else if (isComputed(value)) reactive = value;
|
|
49
|
-
if (reactive == null) element.value = "";
|
|
50
|
-
else data.mora.subscribers.add(reactive.subscribe((value) => {
|
|
51
|
-
element.value = getString(value);
|
|
52
|
-
}));
|
|
36
|
+
mapAttributeValue(data, element, "value", data.values[Number.parseInt(index, 10)]);
|
|
37
|
+
return true;
|
|
53
38
|
}
|
|
54
39
|
function mapValue(data, comment, value) {
|
|
55
40
|
switch (true) {
|
|
56
41
|
case typeof value === "function":
|
|
57
|
-
|
|
42
|
+
setComputedNode(data, comment, value);
|
|
58
43
|
break;
|
|
59
44
|
case isFragments(value):
|
|
60
|
-
|
|
45
|
+
setFragmentsNode(data, comment, value);
|
|
61
46
|
break;
|
|
62
47
|
case isReactive(value):
|
|
63
48
|
setReactiveValue(data, comment, value);
|
|
@@ -76,12 +61,12 @@ function replaceComment(data, comment, value) {
|
|
|
76
61
|
}
|
|
77
62
|
comment.replaceWith(...nodes);
|
|
78
63
|
}
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
64
|
+
function setComputedNode(data, comment, callback) {
|
|
65
|
+
setComputedValue(data, callback, (computation) => {
|
|
66
|
+
setReactiveValue(data, comment, computation);
|
|
67
|
+
});
|
|
83
68
|
}
|
|
84
|
-
function
|
|
69
|
+
function setFragmentsNode(data, comment, fragments) {
|
|
85
70
|
const state = fragmentsStates.get(fragments);
|
|
86
71
|
handleFragments(state, false);
|
|
87
72
|
setReactiveValue(data, comment, state.mapped);
|
package/dist/node/value.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { isChildNode } from "@oscarpalmer/toretto/is";
|
|
|
7
7
|
//#region src/node/value.ts
|
|
8
8
|
function addToArray(identifiers, items, nodes, added) {
|
|
9
9
|
let position = nodes[0];
|
|
10
|
-
const before = added && !identifiers.previous.has(items.templates[0].identifier);
|
|
10
|
+
const before = added && !identifiers.previous.set.has(items.templates[0].identifier);
|
|
11
11
|
const next = items.next.flatMap((fragment) => fragment.get().flatMap((node) => ({
|
|
12
12
|
identifier: fragment.identifier,
|
|
13
13
|
value: node
|
|
@@ -15,19 +15,48 @@ function addToArray(identifiers, items, nodes, added) {
|
|
|
15
15
|
const { length } = next;
|
|
16
16
|
for (let index = 0; index < length; index += 1) {
|
|
17
17
|
const node = next[index];
|
|
18
|
-
if (!(added && identifiers.previous.has(node.identifier))) if (index === 0 && before) position.before(node.value);
|
|
18
|
+
if (!(added && identifiers.previous.set.has(node.identifier))) if (index === 0 && before) position.before(node.value);
|
|
19
19
|
else position.after(node.value);
|
|
20
20
|
position = node.value;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
function getArrayData(item, values) {
|
|
24
|
+
const { length } = values;
|
|
25
|
+
const next = [];
|
|
26
|
+
let templates = [];
|
|
27
|
+
for (let index = 0; index < length; index += 1) {
|
|
28
|
+
const value = values[index];
|
|
29
|
+
if (isFragment(value) && value.identifier != null) {
|
|
30
|
+
next.push(value.identifier);
|
|
31
|
+
templates.push(value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
|
|
35
|
+
const nextSet = new Set(next);
|
|
36
|
+
if (nextSet.size !== templates.length) templates = [];
|
|
37
|
+
return {
|
|
38
|
+
next: {
|
|
39
|
+
array: next,
|
|
40
|
+
set: nextSet
|
|
41
|
+
},
|
|
42
|
+
previous: {
|
|
43
|
+
array: previous,
|
|
44
|
+
set: new Set(previous)
|
|
45
|
+
},
|
|
46
|
+
template: {
|
|
47
|
+
empty: templates.length === 0,
|
|
48
|
+
items: templates
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
23
52
|
function handleArray(identifiers, items, nodes) {
|
|
24
|
-
const next = items.templates.map((template) => items.fragments
|
|
25
|
-
const comparison = compareArrays(
|
|
53
|
+
const next = items.templates.map((template) => items.fragments.find((fragment) => fragment.identifier === template.identifier) ?? template);
|
|
54
|
+
const comparison = compareArrays(identifiers.previous.array, identifiers.next.array);
|
|
26
55
|
if (comparison !== "removed") addToArray(identifiers, {
|
|
27
56
|
...items,
|
|
28
57
|
next
|
|
29
58
|
}, nodes, comparison === ARRAY_COMPARISON_ADDED);
|
|
30
|
-
const toRemove = items.fragments
|
|
59
|
+
const toRemove = items.fragments.filter((fragment) => !identifiers.next.set.has(fragment.identifier));
|
|
31
60
|
const { length } = toRemove;
|
|
32
61
|
for (let index = 0; index < length; index += 1) toRemove[index].remove();
|
|
33
62
|
return {
|
|
@@ -35,52 +64,45 @@ function handleArray(identifiers, items, nodes) {
|
|
|
35
64
|
nodes: next.flatMap((fragment) => fragment.get())
|
|
36
65
|
};
|
|
37
66
|
}
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
67
|
+
function removeArray(item, comment) {
|
|
68
|
+
const fragments = (item.fragments ?? []).slice();
|
|
69
|
+
const result = { nodes: setText(item, comment) };
|
|
70
|
+
removeFragmentsItems(fragments);
|
|
71
|
+
return result;
|
|
43
72
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
else to = item.text == null ? [] : [item.text];
|
|
48
|
-
replaceNodes(item.nodes ?? [], to);
|
|
73
|
+
function removeFragmentsItems(fragments) {
|
|
74
|
+
const { length } = fragments;
|
|
75
|
+
for (let index = 0; index < length; index += 1) fragments[index].remove();
|
|
49
76
|
}
|
|
50
77
|
function setArray(item, comment, value) {
|
|
51
|
-
if (value.length === 0) return
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
fragments
|
|
60
|
-
|
|
61
|
-
}
|
|
78
|
+
if (value.length === 0) return removeArray(item, comment);
|
|
79
|
+
const { next, previous, template } = getArrayData(item, value);
|
|
80
|
+
if (template.empty || item.nodes == null || previous.array.some((identifier) => identifier == null)) {
|
|
81
|
+
const fragments = (item.fragments ?? []).slice();
|
|
82
|
+
const result = {
|
|
83
|
+
fragments: template.empty ? void 0 : template.items,
|
|
84
|
+
nodes: replaceNodes(item.nodes ?? [comment], template.empty ? value.flatMap((item) => createNodes(item)) : template.items.flatMap((template) => template.get()))
|
|
85
|
+
};
|
|
86
|
+
removeFragmentsItems(fragments);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
62
89
|
return handleArray({
|
|
63
|
-
next
|
|
64
|
-
previous
|
|
90
|
+
next,
|
|
91
|
+
previous
|
|
65
92
|
}, {
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
fragments: item.fragments ?? [],
|
|
94
|
+
templates: template.items
|
|
68
95
|
}, item.nodes);
|
|
69
96
|
}
|
|
70
97
|
function setNodes(item, comment, next) {
|
|
71
|
-
|
|
72
|
-
else replaceNodes(item.nodes, next);
|
|
73
|
-
removeFragments(item.fragments);
|
|
74
|
-
return next;
|
|
98
|
+
return replaceNodes(item.nodes ?? [comment], next);
|
|
75
99
|
}
|
|
76
100
|
function setReactiveValue(data, comment, reactive) {
|
|
77
101
|
let item = data.items.find((item) => item.nodes?.includes(comment));
|
|
78
102
|
item ??= {};
|
|
79
|
-
item.text = new Text();
|
|
80
103
|
data.mora.subscribers.add(reactive.subscribe((value) => {
|
|
81
104
|
if (Array.isArray(value)) setReactiveValueForArray(item, comment, value);
|
|
82
105
|
else setReactiveValueForSingle(item, comment, value);
|
|
83
|
-
item.nodes ??= [comment];
|
|
84
106
|
}));
|
|
85
107
|
}
|
|
86
108
|
function setReactiveValueForArray(item, comment, value) {
|
|
@@ -89,25 +111,20 @@ function setReactiveValueForArray(item, comment, value) {
|
|
|
89
111
|
item.nodes = nodes;
|
|
90
112
|
}
|
|
91
113
|
function setReactiveValueForSingle(item, comment, value) {
|
|
114
|
+
const fragments = (item.fragments ?? []).slice();
|
|
92
115
|
const valueIsFragment = isFragment(value);
|
|
93
|
-
item.fragments = valueIsFragment ? [value] : void 0;
|
|
94
116
|
if (valueIsFragment || isChildNode(value)) item.nodes = setNodes(item, comment, createNodes(value));
|
|
95
117
|
else item.nodes = setText(item, comment, value);
|
|
118
|
+
item.fragments = valueIsFragment ? [value] : void 0;
|
|
119
|
+
removeFragmentsItems(fragments);
|
|
96
120
|
}
|
|
97
121
|
function setText(item, comment, value) {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
} else if (isNullable && comment.parentNode == null) item.text?.replaceWith(comment);
|
|
105
|
-
else if (!isNullable && item?.text?.parentNode == null) {
|
|
106
|
-
if (item.text != null) comment.replaceWith(item.text);
|
|
107
|
-
result = true;
|
|
108
|
-
}
|
|
109
|
-
removeFragments(item.fragments);
|
|
110
|
-
if (result) return item.text == null ? [] : [item.text];
|
|
122
|
+
const valueIsNullable = isNullableOrWhitespace(value);
|
|
123
|
+
item.text ??= new Text();
|
|
124
|
+
item.text.textContent = valueIsNullable ? "" : getString(value);
|
|
125
|
+
const result = valueIsNullable ? [comment] : [item.text];
|
|
126
|
+
replaceNodes(item.nodes ?? [comment], result);
|
|
127
|
+
return result;
|
|
111
128
|
}
|
|
112
129
|
//#endregion
|
|
113
130
|
export { setReactiveValue };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/abydon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"components",
|
|
6
6
|
"dom",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@oscarpalmer/atoms": "^0.185",
|
|
46
46
|
"@oscarpalmer/mora": "^0.29",
|
|
47
|
-
"@oscarpalmer/toretto": "^0.
|
|
47
|
+
"@oscarpalmer/toretto": "^0.45"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@oscarpalmer/oui": "^0.20",
|
package/src/attribute/index.ts
CHANGED
|
@@ -1,30 +1,53 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {
|
|
2
|
+
import {isSignal} from '@oscarpalmer/mora';
|
|
3
|
+
import {isInputElement} from '@oscarpalmer/toretto/is';
|
|
3
4
|
import {
|
|
4
5
|
EVENT_ON_VALUE,
|
|
5
6
|
EXPRESSION_ABYDON_ATTRIBUTE_PREFIX,
|
|
6
7
|
EXPRESSION_ABYDON_CONTENT,
|
|
7
8
|
EXPRESSION_EVENT_PREFIX,
|
|
8
|
-
INPUT_TYPE_CHECKBOX,
|
|
9
|
-
INPUT_TYPE_RADIO,
|
|
10
|
-
PROPERTY_CHECKED,
|
|
11
9
|
PROPERTY_VALUE,
|
|
12
10
|
} from '../constants';
|
|
13
|
-
import {
|
|
11
|
+
import {setComputedValue} from '../helpers';
|
|
14
12
|
import type {FragmentData} from '../models';
|
|
15
13
|
import {mapEvent} from '../node/event';
|
|
16
14
|
import {setAttribute} from './value';
|
|
17
15
|
|
|
16
|
+
function compareAttributes(first: Attr, second: Attr): number {
|
|
17
|
+
return first.name.localeCompare(second.name);
|
|
18
|
+
}
|
|
19
|
+
|
|
18
20
|
function getValue(data: FragmentData, original: string): unknown {
|
|
19
21
|
const matches = EXPRESSION_ABYDON_CONTENT.exec(original);
|
|
20
22
|
|
|
21
23
|
return matches == null ? original : data.values[+matches[1]];
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
export function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
export function mapAttributeValue(
|
|
27
|
+
data: FragmentData,
|
|
28
|
+
element: HTMLElement | SVGElement,
|
|
29
|
+
name: string,
|
|
30
|
+
value: unknown,
|
|
31
|
+
): void {
|
|
32
|
+
if (typeof value === 'function') {
|
|
33
|
+
setComputedAttribute(data, element, name, value as GenericCallback);
|
|
34
|
+
} else {
|
|
35
|
+
setAttribute(data, element, name, value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isInputElement(element) && isSignal(value) && name in element) {
|
|
39
|
+
mapEvent(element, EVENT_ON_VALUE, () => {
|
|
40
|
+
value.set(element[name as keyof typeof element]);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function mapAttributes(
|
|
46
|
+
data: FragmentData,
|
|
47
|
+
element: HTMLElement | SVGElement,
|
|
48
|
+
ignoreValue: boolean,
|
|
49
|
+
): void {
|
|
50
|
+
const attributes = [...element.attributes].sort(compareAttributes);
|
|
28
51
|
|
|
29
52
|
const {length} = attributes;
|
|
30
53
|
|
|
@@ -32,52 +55,35 @@ export function mapAttributes(data: FragmentData, element: HTMLElement | SVGElem
|
|
|
32
55
|
const {name, value} = attributes[index];
|
|
33
56
|
|
|
34
57
|
const actualName = name.replace(EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, '');
|
|
35
|
-
const actualValue = getValue(data, value)
|
|
58
|
+
const actualValue = getValue(data, value);
|
|
36
59
|
|
|
37
60
|
if (actualName !== name) {
|
|
38
61
|
element.removeAttribute(name);
|
|
39
62
|
}
|
|
40
63
|
|
|
64
|
+
if (actualName === PROPERTY_VALUE && ignoreValue) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
41
68
|
switch (true) {
|
|
42
69
|
case EXPRESSION_EVENT_PREFIX.test(actualName):
|
|
43
70
|
mapEvent(element, actualName, actualValue);
|
|
44
71
|
break;
|
|
45
72
|
|
|
46
73
|
default:
|
|
47
|
-
|
|
74
|
+
mapAttributeValue(data, element, actualName, actualValue);
|
|
48
75
|
break;
|
|
49
76
|
}
|
|
50
77
|
}
|
|
51
78
|
}
|
|
52
79
|
|
|
53
|
-
function mapValue(
|
|
54
|
-
data: FragmentData,
|
|
55
|
-
element: HTMLElement | SVGElement,
|
|
56
|
-
name: string,
|
|
57
|
-
value: unknown,
|
|
58
|
-
): void {
|
|
59
|
-
if (typeof value === 'function') {
|
|
60
|
-
setComputedAttribute(data, element, name, value as GenericCallback);
|
|
61
|
-
} else {
|
|
62
|
-
setAttribute(data, element, name, value);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (isInputElement(element) && isSignal(value) && name in element) {
|
|
66
|
-
mapEvent(element, EVENT_ON_VALUE, () => {
|
|
67
|
-
value.set(element[name as keyof typeof element]);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
80
|
function setComputedAttribute(
|
|
73
81
|
data: FragmentData,
|
|
74
82
|
element: HTMLElement | SVGElement,
|
|
75
83
|
name: string,
|
|
76
84
|
callback: GenericCallback,
|
|
77
85
|
): void {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
setAttribute(data, element, name, value);
|
|
86
|
+
setComputedValue(data, callback, computation => {
|
|
87
|
+
setAttribute(data, element, name, computation);
|
|
88
|
+
});
|
|
83
89
|
}
|
package/src/attribute/value.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
3
|
-
import {camelCase} from '@oscarpalmer/atoms/string/case';
|
|
4
3
|
import {isReactive} from '@oscarpalmer/mora';
|
|
5
4
|
import {setAttribute as setTorettoAttribute} from '@oscarpalmer/toretto/attribute';
|
|
6
5
|
import {setStyle} from '@oscarpalmer/toretto/style';
|
|
@@ -10,23 +9,21 @@ import {
|
|
|
10
9
|
EXPRESSION_ATTRIBUTE_CLASS,
|
|
11
10
|
EXPRESSION_ATTRIBUTE_STYLE_FULL,
|
|
12
11
|
EXPRESSION_ATTRIBUTE_STYLE_PREFIX,
|
|
13
|
-
|
|
14
|
-
PROPERTY_CHECKED,
|
|
15
|
-
PROPERTY_VALUE,
|
|
12
|
+
EXPRESSION_ATTRIBUTE_STYLE_VARIABLE,
|
|
16
13
|
VALUE_TRUE,
|
|
17
14
|
} from '../constants';
|
|
18
15
|
import type {FragmentData} from '../models';
|
|
19
16
|
|
|
20
|
-
function getStyleValue(value: unknown, unit: string,
|
|
21
|
-
if (removeStyleValue(value, unit,
|
|
17
|
+
function getStyleValue(value: unknown, unit: string, isVariable: boolean): string | undefined {
|
|
18
|
+
if (removeStyleValue(value, unit, isVariable)) {
|
|
22
19
|
return;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
return value === true || value === VALUE_TRUE ? unit : `${getString(value)}${unit ?? ''}`;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
function removeStyleValue(value: unknown, unit: unknown,
|
|
29
|
-
if (value == null || value === false || (
|
|
25
|
+
function removeStyleValue(value: unknown, unit: unknown, isVariable: boolean): boolean {
|
|
26
|
+
if (value == null || value === false || (isVariable && isNullableOrWhitespace(value))) {
|
|
30
27
|
return true;
|
|
31
28
|
}
|
|
32
29
|
|
|
@@ -60,21 +57,15 @@ function setClassValues(
|
|
|
60
57
|
name: string,
|
|
61
58
|
value: unknown,
|
|
62
59
|
): void {
|
|
63
|
-
|
|
60
|
+
const classes = name.slice(ATTRIBUTE_CLASS_PREFIX_LENGTH).split(ATTRIBUTE_NAME_DELIMITER);
|
|
61
|
+
|
|
62
|
+
updateValue(data, value, value => {
|
|
64
63
|
if (value === true || value === VALUE_TRUE) {
|
|
65
64
|
element.classList.add(...classes);
|
|
66
65
|
} else {
|
|
67
66
|
element.classList.remove(...classes);
|
|
68
67
|
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const classes = name.slice(ATTRIBUTE_CLASS_PREFIX_LENGTH).split(ATTRIBUTE_NAME_DELIMITER);
|
|
72
|
-
|
|
73
|
-
if (isReactive(value)) {
|
|
74
|
-
data.mora.subscribers.add(value.subscribe(update));
|
|
75
|
-
} else {
|
|
76
|
-
update(value);
|
|
77
|
-
}
|
|
68
|
+
});
|
|
78
69
|
}
|
|
79
70
|
|
|
80
71
|
function setStyleValues(
|
|
@@ -85,27 +76,15 @@ function setStyleValues(
|
|
|
85
76
|
): void {
|
|
86
77
|
let [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name)!;
|
|
87
78
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
property = camelCase(property);
|
|
79
|
+
const isVariable = EXPRESSION_ATTRIBUTE_STYLE_VARIABLE.test(name);
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
property = `--${property}`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function update(value: unknown): void {
|
|
81
|
+
updateValue(data, value, value => {
|
|
97
82
|
setStyle(
|
|
98
83
|
element,
|
|
99
84
|
property as keyof CSSStyleDeclaration,
|
|
100
|
-
getStyleValue(value, unit,
|
|
85
|
+
getStyleValue(value, unit, isVariable),
|
|
101
86
|
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (isReactive(value)) {
|
|
105
|
-
data.mora.subscribers.add(value.subscribe(update));
|
|
106
|
-
} else {
|
|
107
|
-
update(value);
|
|
108
|
-
}
|
|
87
|
+
});
|
|
109
88
|
}
|
|
110
89
|
|
|
111
90
|
function setValue(
|
|
@@ -114,27 +93,15 @@ function setValue(
|
|
|
114
93
|
name: string,
|
|
115
94
|
value: unknown,
|
|
116
95
|
): void {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
unsetValue(element, name, isReactiveValue ? value.peek() : value);
|
|
120
|
-
|
|
121
|
-
if (isReactiveValue) {
|
|
122
|
-
data.mora.subscribers.add(
|
|
123
|
-
value.subscribe(next => {
|
|
124
|
-
setTorettoAttribute(element, name, next);
|
|
125
|
-
}),
|
|
126
|
-
);
|
|
127
|
-
} else {
|
|
96
|
+
updateValue(data, value, value => {
|
|
128
97
|
setTorettoAttribute(element, name, value);
|
|
129
|
-
}
|
|
98
|
+
});
|
|
130
99
|
}
|
|
131
100
|
|
|
132
|
-
function
|
|
133
|
-
if (
|
|
134
|
-
(
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (name === PROPERTY_VALUE) {
|
|
138
|
-
(element as HTMLInputElement).value = (element as HTMLInputElement).value === '' ? '_' : '';
|
|
101
|
+
function updateValue(data: FragmentData, value: unknown, updater: (value: unknown) => void): void {
|
|
102
|
+
if (isReactive(value)) {
|
|
103
|
+
data.mora.subscribers.add(value.subscribe(updater));
|
|
104
|
+
} else {
|
|
105
|
+
updater(value);
|
|
139
106
|
}
|
|
140
107
|
}
|
package/src/constants.ts
CHANGED
|
@@ -4,7 +4,7 @@ export const ARRAY_COMPARISON_DISSIMILAR = 'dissimilar';
|
|
|
4
4
|
|
|
5
5
|
export const ARRAY_COMPARISON_REMOVED = 'removed';
|
|
6
6
|
|
|
7
|
-
export const ATTRIBUTE_CLASS_PREFIX_LENGTH =
|
|
7
|
+
export const ATTRIBUTE_CLASS_PREFIX_LENGTH = 'class.'.length;
|
|
8
8
|
|
|
9
9
|
export const ATTRIBUTE_NAME_DELIMITER = '.';
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ export const CHANGE_INPUTS = new Set(['checkbox', 'radio']);
|
|
|
12
12
|
|
|
13
13
|
export const ERROR_FRAGMENT = 'Fragment function must return a Fragment instance';
|
|
14
14
|
|
|
15
|
-
export const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '
|
|
15
|
+
export const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<>'";
|
|
16
16
|
|
|
17
17
|
export const ERROR_IDENTIFIER_TYPE = 'Identifier cannot be null or undefined';
|
|
18
18
|
|
|
@@ -47,34 +47,26 @@ export const EXPRESSION_ATTRIBUTE_STYLE_FULL = /^style\.([\w-]+)(?:\.([\w-]+))?$
|
|
|
47
47
|
|
|
48
48
|
export const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
|
|
49
49
|
|
|
50
|
-
export const
|
|
50
|
+
export const EXPRESSION_ATTRIBUTE_STYLE_VARIABLE = /^style\.--/;
|
|
51
51
|
|
|
52
52
|
export const EXPRESSION_EVENT_ATTRIBUTE = /^@([\w-]+)(?::([a-z:]+))?="$/i;
|
|
53
53
|
|
|
54
54
|
export const EXPRESSION_EVENT_NAME = /^@?([\w-]+)(?::([a-z:]+))?$/i;
|
|
55
55
|
|
|
56
|
-
export const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
|
|
56
|
+
export const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(?:ctive)$/i;
|
|
57
57
|
|
|
58
|
-
export const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(apture)$/i;
|
|
58
|
+
export const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(?:apture)$/i;
|
|
59
59
|
|
|
60
|
-
export const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(nce)$/i;
|
|
60
|
+
export const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(?:nce)$/i;
|
|
61
61
|
|
|
62
62
|
export const EXPRESSION_EVENT_PREFIX = /^@/;
|
|
63
63
|
|
|
64
|
-
export const EXPRESSION_PERIOD = /\./;
|
|
65
|
-
|
|
66
64
|
export const EXPRESSION_TEXTAREA_VALUE = /(?:<|<)!--abydon\.(\d+)--(?:>|>)/;
|
|
67
65
|
|
|
68
|
-
export const INPUT_TYPE_CHECKBOX = 'checkbox';
|
|
69
|
-
|
|
70
|
-
export const INPUT_TYPE_RADIO = 'radio';
|
|
71
|
-
|
|
72
66
|
export const NAME_FRAGMENT = '$fragment';
|
|
73
67
|
|
|
74
68
|
export const NAME_FRAGMENTS = '$fragments';
|
|
75
69
|
|
|
76
|
-
export const PROPERTY_CHECKED = 'checked';
|
|
77
|
-
|
|
78
70
|
export const PROPERTY_IDENTIFIER = 'identifier';
|
|
79
71
|
|
|
80
72
|
export const PROPERTY_VALUE = 'value';
|
package/src/fragment.ts
CHANGED
|
@@ -19,12 +19,14 @@ export class Fragment {
|
|
|
19
19
|
/**
|
|
20
20
|
* Is template caching enabled?
|
|
21
21
|
*/
|
|
22
|
-
get
|
|
22
|
+
get cache(): boolean {
|
|
23
23
|
return this.#configuration.cache;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Identifier for the _Fragment_
|
|
28
|
+
*
|
|
29
|
+
* _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
|
|
28
30
|
*/
|
|
29
31
|
get identifier(): unknown {
|
|
30
32
|
return this.#configuration.identifier;
|
|
@@ -48,7 +50,15 @@ export class Fragment {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/**
|
|
51
|
-
*
|
|
53
|
+
* Insert the _Fragment_ after the given element
|
|
54
|
+
* @param element Element to insert after
|
|
55
|
+
*/
|
|
56
|
+
after(element: Element): void {
|
|
57
|
+
element.after(...this.get());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Append the _Fragment_ to the given element
|
|
52
62
|
* @param element Element to append to
|
|
53
63
|
*/
|
|
54
64
|
appendTo(element: Element): void {
|
|
@@ -56,9 +66,19 @@ export class Fragment {
|
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
/**
|
|
59
|
-
*
|
|
69
|
+
* Insert the _Fragment_ before the given element
|
|
70
|
+
* @param element Element to insert before
|
|
71
|
+
*/
|
|
72
|
+
before(element: Element): void {
|
|
73
|
+
element.before(...this.get());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Configure the _Fragment_
|
|
78
|
+
*
|
|
79
|
+
* _Returns the Fragment instance for chaining_
|
|
60
80
|
* @param configuration Configuration options
|
|
61
|
-
* @returns
|
|
81
|
+
* @returns _Fragment_
|
|
62
82
|
*/
|
|
63
83
|
configure(configuration: FragmentConfiguration): Fragment {
|
|
64
84
|
const actual = isPlainObject(configuration) ? configuration : {};
|
|
@@ -75,7 +95,7 @@ export class Fragment {
|
|
|
75
95
|
}
|
|
76
96
|
|
|
77
97
|
/**
|
|
78
|
-
* Get a list of the
|
|
98
|
+
* Get a list of the _Fragment_'s nodes
|
|
79
99
|
* @returns List of nodes
|
|
80
100
|
*/
|
|
81
101
|
get(): ChildNode[] {
|
|
@@ -108,21 +128,18 @@ export class Fragment {
|
|
|
108
128
|
}
|
|
109
129
|
|
|
110
130
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* _An identifier can be used to uniquely identify a fragment,
|
|
114
|
-
* which helps prevent re-rendering in certain scenarios._
|
|
115
|
-
* @param identifier Identifier
|
|
116
|
-
* @returns Fragment
|
|
131
|
+
* Prepend the _Fragment_ to the given element
|
|
132
|
+
* @param element Element to prepend to
|
|
117
133
|
*/
|
|
118
|
-
|
|
119
|
-
this
|
|
120
|
-
|
|
121
|
-
return this;
|
|
134
|
+
prependTo(element: Element): void {
|
|
135
|
+
element.prepend(...this.get());
|
|
122
136
|
}
|
|
123
137
|
|
|
124
138
|
/**
|
|
125
|
-
* Remove the
|
|
139
|
+
* Remove the _Fragment_ _(and all its descendants)_ from the _DOM_
|
|
140
|
+
*
|
|
141
|
+
* - _Any events, reactive values, and Fragments will also be cleaned up and removed_
|
|
142
|
+
* - _After being removed, the Fragment can be re-inserted into the DOM_
|
|
126
143
|
*/
|
|
127
144
|
remove(): void {
|
|
128
145
|
removeFragment(this.#data);
|