@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.
- package/README.md +2 -0
- package/dist/abydon.full.mjs +787 -276
- package/dist/attribute/index.d.mts +6 -0
- package/dist/{node/attribute → attribute}/index.mjs +10 -19
- package/dist/{node/attribute → attribute}/value.d.mts +2 -2
- package/dist/attribute/value.mjs +63 -0
- package/dist/constants.d.mts +7 -3
- package/dist/constants.mjs +12 -8
- package/dist/fragment.d.mts +45 -1
- package/dist/fragment.mjs +9 -3
- package/dist/fragments.d.mts +4 -6
- package/dist/fragments.mjs +5 -11
- package/dist/helpers/index.d.mts +11 -1
- package/dist/helpers/index.mjs +10 -0
- package/dist/index.d.mts +5 -4
- package/dist/index.mjs +8 -4
- package/dist/models.d.mts +43 -1
- package/dist/node/event.mjs +9 -4
- package/dist/node/index.d.mts +1 -1
- package/dist/node/index.mjs +19 -10
- package/dist/node/value.d.mts +1 -1
- package/dist/node/value.mjs +9 -12
- package/dist/parse.d.mts +1 -1
- package/dist/parse.mjs +6 -4
- package/package.json +8 -7
- package/src/{node/attribute → attribute}/index.ts +16 -33
- package/src/attribute/value.ts +140 -0
- package/src/constants.ts +16 -8
- package/src/fragment.ts +10 -5
- package/src/fragments.ts +4 -13
- package/src/helpers/index.ts +10 -0
- package/src/index.ts +16 -3
- package/src/node/event.ts +12 -5
- package/src/node/index.ts +31 -13
- package/src/node/value.ts +10 -25
- package/src/parse.ts +14 -4
- package/dist/fragment-9qTBAvtR.d.mts +0 -82
- package/dist/node/attribute/index.d.mts +0 -6
- package/dist/node/attribute/value.mjs +0 -65
- package/src/node/attribute/value.ts +0 -158
package/dist/node/value.mjs
CHANGED
|
@@ -52,14 +52,15 @@ function setArray(item, comment, value) {
|
|
|
52
52
|
let templates = value.filter((item) => isFragment(item) && item.identifier != null);
|
|
53
53
|
const next = templates.map((fragment) => fragment.identifier);
|
|
54
54
|
const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
|
|
55
|
-
|
|
55
|
+
const nextSet = new Set(next);
|
|
56
|
+
if (nextSet.size !== templates.length) templates = [];
|
|
56
57
|
const noTemplates = templates.length === 0;
|
|
57
58
|
if (noTemplates || item.nodes == null || previous.some((identifier) => identifier == null)) return {
|
|
58
59
|
fragments: noTemplates ? void 0 : templates,
|
|
59
60
|
nodes: setNodes(item, comment, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
|
|
60
61
|
};
|
|
61
62
|
return handleArray({
|
|
62
|
-
next:
|
|
63
|
+
next: nextSet,
|
|
63
64
|
previous: new Set(previous)
|
|
64
65
|
}, {
|
|
65
66
|
templates,
|
|
@@ -67,10 +68,8 @@ function setArray(item, comment, value) {
|
|
|
67
68
|
}, item.nodes);
|
|
68
69
|
}
|
|
69
70
|
function setNodes(item, comment, next) {
|
|
70
|
-
if (item.nodes == null)
|
|
71
|
-
|
|
72
|
-
else if (item.text?.parentNode != null) replaceNodes([item.text], next);
|
|
73
|
-
} else replaceNodes(item.nodes, next);
|
|
71
|
+
if (item.nodes == null) replaceNodes([comment], next);
|
|
72
|
+
else replaceNodes(item.nodes, next);
|
|
74
73
|
removeFragments(item.fragments);
|
|
75
74
|
return next;
|
|
76
75
|
}
|
|
@@ -81,15 +80,13 @@ function setReactiveValue(data, comment, reactive) {
|
|
|
81
80
|
data.mora.subscribers.add(reactive.subscribe((value) => {
|
|
82
81
|
if (Array.isArray(value)) setReactiveValueForArray(item, comment, value);
|
|
83
82
|
else setReactiveValueForSingle(item, comment, value);
|
|
84
|
-
item.nodes
|
|
83
|
+
item.nodes ??= [comment];
|
|
85
84
|
}));
|
|
86
85
|
}
|
|
87
86
|
function setReactiveValueForArray(item, comment, value) {
|
|
88
|
-
const
|
|
89
|
-
item.fragments =
|
|
90
|
-
|
|
91
|
-
else item.nodes = void 0;
|
|
92
|
-
else item.nodes = result?.nodes;
|
|
87
|
+
const { fragments, nodes } = setArray(item, comment, value);
|
|
88
|
+
item.fragments = fragments;
|
|
89
|
+
item.nodes = nodes;
|
|
93
90
|
}
|
|
94
91
|
function setReactiveValueForSingle(item, comment, value) {
|
|
95
92
|
const valueIsFragment = isFragment(value);
|
package/dist/parse.d.mts
CHANGED
package/dist/parse.mjs
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { EXPRESSION_ABYDON_ATTRIBUTE_FULL } from "./constants.mjs";
|
|
2
|
-
import {
|
|
1
|
+
import { EXPRESSION_ABYDON_ATTRIBUTE_FULL, EXPRESSION_EVENT_ATTRIBUTE, WHITESPACE } from "./constants.mjs";
|
|
2
|
+
import { getString } from "@oscarpalmer/atoms/string";
|
|
3
3
|
//#region src/parse.ts
|
|
4
4
|
function handleExpression(data, prefix, expression) {
|
|
5
5
|
if (Array.isArray(expression)) {
|
|
6
|
+
if (EXPRESSION_EVENT_ATTRIBUTE.test(prefix.split(WHITESPACE).at(-1))) return transformExpression(prefix, data.values.push(expression) - 1);
|
|
6
7
|
const { length } = expression;
|
|
7
8
|
let expressions = "";
|
|
8
9
|
for (let index = 0; index < length; index += 1) expressions += handleExpression(data, "", expression[index]);
|
|
9
10
|
return `${prefix}${expressions}`;
|
|
10
11
|
}
|
|
11
12
|
if (typeof expression === "function" || typeof expression === "object" && expression != null) return transformExpression(prefix, data.values.push(expression) - 1);
|
|
12
|
-
|
|
13
|
+
const asString = getString(expression);
|
|
14
|
+
return asString.trim().length === 0 ? prefix : `${prefix}${asString}`;
|
|
13
15
|
}
|
|
14
16
|
function parse(data) {
|
|
15
17
|
if (data.template != null) return data.template;
|
|
@@ -22,7 +24,7 @@ function parse(data) {
|
|
|
22
24
|
return data.template;
|
|
23
25
|
}
|
|
24
26
|
function transformAttribute(_, name, index) {
|
|
25
|
-
return `
|
|
27
|
+
return `abydon-${name}="abydon.${index}"`;
|
|
26
28
|
}
|
|
27
29
|
function transformExpression(prefix, index) {
|
|
28
30
|
return `${prefix}<!--abydon.${index}-->`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/abydon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"components",
|
|
6
6
|
"dom",
|
|
@@ -42,15 +42,16 @@
|
|
|
42
42
|
"test:leak": "npx vp test run --detect-async-leaks --coverage"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@oscarpalmer/atoms": "^0.
|
|
46
|
-
"@oscarpalmer/mora": "^0.
|
|
47
|
-
"@oscarpalmer/toretto": "^0.
|
|
45
|
+
"@oscarpalmer/atoms": "^0.185",
|
|
46
|
+
"@oscarpalmer/mora": "^0.29",
|
|
47
|
+
"@oscarpalmer/toretto": "^0.43"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@oscarpalmer/oui": "^0.
|
|
51
|
-
"@
|
|
50
|
+
"@oscarpalmer/oui": "^0.20",
|
|
51
|
+
"@oxlint/plugins": "^1.62",
|
|
52
|
+
"@types/node": "^25.6",
|
|
52
53
|
"@vitest/coverage-istanbul": "^4.1",
|
|
53
|
-
"jsdom": "^
|
|
54
|
+
"jsdom": "^29.1",
|
|
54
55
|
"tsdown": "^0.21",
|
|
55
56
|
"typescript": "^5.9",
|
|
56
57
|
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {
|
|
3
|
-
import {computed, isReactive, isSignal} from '@oscarpalmer/mora';
|
|
2
|
+
import {computed, isSignal} from '@oscarpalmer/mora';
|
|
4
3
|
import {
|
|
5
|
-
|
|
4
|
+
EVENT_ON_VALUE,
|
|
6
5
|
EXPRESSION_ABYDON_ATTRIBUTE_PREFIX,
|
|
7
6
|
EXPRESSION_ABYDON_CONTENT,
|
|
8
7
|
EXPRESSION_EVENT_PREFIX,
|
|
9
|
-
EXPRESSION_PERIOD,
|
|
10
8
|
INPUT_TYPE_CHECKBOX,
|
|
9
|
+
INPUT_TYPE_RADIO,
|
|
11
10
|
PROPERTY_CHECKED,
|
|
12
11
|
PROPERTY_VALUE,
|
|
13
|
-
} from '
|
|
14
|
-
import {isInputElement} from '
|
|
15
|
-
import type {FragmentData} from '
|
|
16
|
-
import {mapEvent} from '../event';
|
|
12
|
+
} from '../constants';
|
|
13
|
+
import {isInputElement} from '../helpers/dom';
|
|
14
|
+
import type {FragmentData} from '../models';
|
|
15
|
+
import {mapEvent} from '../node/event';
|
|
17
16
|
import {setAttribute} from './value';
|
|
18
17
|
|
|
19
18
|
function getValue(data: FragmentData, original: string): unknown {
|
|
20
|
-
const matches = EXPRESSION_ABYDON_CONTENT.exec(original
|
|
19
|
+
const matches = EXPRESSION_ABYDON_CONTENT.exec(original);
|
|
21
20
|
|
|
22
21
|
return matches == null ? original : data.values[+matches[1]];
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
export function mapAttributes(data: FragmentData, element: HTMLElement | SVGElement): void {
|
|
26
|
-
const attributes = [...element.attributes]
|
|
25
|
+
const attributes = [...element.attributes].sort((first, second) =>
|
|
26
|
+
first.name.localeCompare(second.name),
|
|
27
|
+
);
|
|
28
|
+
|
|
27
29
|
const {length} = attributes;
|
|
28
30
|
|
|
29
31
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -41,13 +43,8 @@ export function mapAttributes(data: FragmentData, element: HTMLElement | SVGElem
|
|
|
41
43
|
mapEvent(element, actualName, actualValue);
|
|
42
44
|
break;
|
|
43
45
|
|
|
44
|
-
case EXPRESSION_PERIOD.test(actualName):
|
|
45
|
-
case typeof actualValue === 'function':
|
|
46
|
-
case isReactive(actualValue):
|
|
47
|
-
mapValue(data, element, actualName, actualValue);
|
|
48
|
-
break;
|
|
49
|
-
|
|
50
46
|
default:
|
|
47
|
+
mapValue(data, element, actualName, actualValue);
|
|
51
48
|
break;
|
|
52
49
|
}
|
|
53
50
|
}
|
|
@@ -65,23 +62,9 @@ function mapValue(
|
|
|
65
62
|
setAttribute(data, element, name, value);
|
|
66
63
|
}
|
|
67
64
|
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let property: string | undefined;
|
|
73
|
-
|
|
74
|
-
if (name === PROPERTY_CHECKED && element.type === INPUT_TYPE_CHECKBOX) {
|
|
75
|
-
property = PROPERTY_CHECKED;
|
|
76
|
-
} else if (name === PROPERTY_VALUE) {
|
|
77
|
-
property = PROPERTY_VALUE;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (property != null) {
|
|
81
|
-
mapEvent(element, EVENT_ON_PREFIXED, () => {
|
|
82
|
-
const next = element[property as keyof typeof element];
|
|
83
|
-
|
|
84
|
-
value.set(parse(String(next)) ?? next);
|
|
65
|
+
if (isInputElement(element) && isSignal(value) && name in element) {
|
|
66
|
+
mapEvent(element, EVENT_ON_VALUE, () => {
|
|
67
|
+
value.set(element[name as keyof typeof element]);
|
|
85
68
|
});
|
|
86
69
|
}
|
|
87
70
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
2
|
+
import {getString} from '@oscarpalmer/atoms/string';
|
|
3
|
+
import {camelCase} from '@oscarpalmer/atoms/string/case';
|
|
4
|
+
import {isReactive} from '@oscarpalmer/mora';
|
|
5
|
+
import {setAttribute as setTorettoAttribute} from '@oscarpalmer/toretto/attribute';
|
|
6
|
+
import {setStyle} from '@oscarpalmer/toretto/style';
|
|
7
|
+
import {
|
|
8
|
+
ATTRIBUTE_CLASS_PREFIX_LENGTH,
|
|
9
|
+
ATTRIBUTE_NAME_DELIMITER,
|
|
10
|
+
EXPRESSION_ATTRIBUTE_CLASS,
|
|
11
|
+
EXPRESSION_ATTRIBUTE_STYLE_FULL,
|
|
12
|
+
EXPRESSION_ATTRIBUTE_STYLE_PREFIX,
|
|
13
|
+
EXPRESSION_ATTRIBUTE_STYLE_PROPERTY,
|
|
14
|
+
PROPERTY_CHECKED,
|
|
15
|
+
PROPERTY_VALUE,
|
|
16
|
+
VALUE_TRUE,
|
|
17
|
+
} from '../constants';
|
|
18
|
+
import type {FragmentData} from '../models';
|
|
19
|
+
|
|
20
|
+
function getStyleValue(value: unknown, unit: string, isProperty: boolean): string | undefined {
|
|
21
|
+
if (removeStyleValue(value, unit, isProperty)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return value === true || value === VALUE_TRUE ? unit : `${getString(value)}${unit ?? ''}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function removeStyleValue(value: unknown, unit: unknown, isProperty: boolean): boolean {
|
|
29
|
+
if (value == null || value === false || (isProperty && isNullableOrWhitespace(value))) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return unit == null && (value === true || value === VALUE_TRUE);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setAttribute(
|
|
37
|
+
data: FragmentData,
|
|
38
|
+
element: HTMLElement | SVGElement,
|
|
39
|
+
name: string,
|
|
40
|
+
value: unknown,
|
|
41
|
+
): void {
|
|
42
|
+
switch (true) {
|
|
43
|
+
case EXPRESSION_ATTRIBUTE_CLASS.test(name):
|
|
44
|
+
setClassValues(data, element, name, value);
|
|
45
|
+
return;
|
|
46
|
+
|
|
47
|
+
case EXPRESSION_ATTRIBUTE_STYLE_PREFIX.test(name):
|
|
48
|
+
setStyleValues(data, element, name, value);
|
|
49
|
+
return;
|
|
50
|
+
|
|
51
|
+
default:
|
|
52
|
+
setValue(data, element, name, value);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function setClassValues(
|
|
58
|
+
data: FragmentData,
|
|
59
|
+
element: HTMLElement | SVGElement,
|
|
60
|
+
name: string,
|
|
61
|
+
value: unknown,
|
|
62
|
+
): void {
|
|
63
|
+
function update(value: unknown): void {
|
|
64
|
+
if (value === true || value === VALUE_TRUE) {
|
|
65
|
+
element.classList.add(...classes);
|
|
66
|
+
} else {
|
|
67
|
+
element.classList.remove(...classes);
|
|
68
|
+
}
|
|
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
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function setStyleValues(
|
|
81
|
+
data: FragmentData,
|
|
82
|
+
element: HTMLElement | SVGElement,
|
|
83
|
+
name: string,
|
|
84
|
+
value: unknown,
|
|
85
|
+
): void {
|
|
86
|
+
let [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name)!;
|
|
87
|
+
|
|
88
|
+
const isProperty = EXPRESSION_ATTRIBUTE_STYLE_PROPERTY.test(name);
|
|
89
|
+
|
|
90
|
+
property = camelCase(property);
|
|
91
|
+
|
|
92
|
+
if (isProperty) {
|
|
93
|
+
property = `--${property}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function update(value: unknown): void {
|
|
97
|
+
setStyle(
|
|
98
|
+
element,
|
|
99
|
+
property as keyof CSSStyleDeclaration,
|
|
100
|
+
getStyleValue(value, unit, isProperty),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (isReactive(value)) {
|
|
105
|
+
data.mora.subscribers.add(value.subscribe(update));
|
|
106
|
+
} else {
|
|
107
|
+
update(value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function setValue(
|
|
112
|
+
data: FragmentData,
|
|
113
|
+
element: HTMLElement | SVGElement,
|
|
114
|
+
name: string,
|
|
115
|
+
value: unknown,
|
|
116
|
+
): void {
|
|
117
|
+
const isReactiveValue = isReactive(value);
|
|
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 {
|
|
128
|
+
setTorettoAttribute(element, name, value);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function unsetValue(element: HTMLElement | SVGElement, name: string, value: unknown): void {
|
|
133
|
+
if (name === PROPERTY_CHECKED) {
|
|
134
|
+
(element as HTMLInputElement).checked = !(value === true || value === VALUE_TRUE);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (name === PROPERTY_VALUE) {
|
|
138
|
+
(element as HTMLInputElement).value = (element as HTMLInputElement).value === '' ? '_' : '';
|
|
139
|
+
}
|
|
140
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -8,6 +8,8 @@ export const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
|
|
|
8
8
|
|
|
9
9
|
export const ATTRIBUTE_NAME_DELIMITER = '.';
|
|
10
10
|
|
|
11
|
+
export const CHANGE_INPUTS = new Set(['checkbox', 'radio']);
|
|
12
|
+
|
|
11
13
|
export const ERROR_FRAGMENT = 'Fragment function must return a Fragment instance';
|
|
12
14
|
|
|
13
15
|
export const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<id>'";
|
|
@@ -29,17 +31,15 @@ export const EVENT_DEFAULTS: Record<string, string> = {
|
|
|
29
31
|
TEXTAREA: EVENT_INPUT,
|
|
30
32
|
};
|
|
31
33
|
|
|
32
|
-
export const EVENT_ON_PREFIXED = '@on';
|
|
33
|
-
|
|
34
34
|
export const EVENT_ON_VALUE = 'on';
|
|
35
35
|
|
|
36
36
|
export const EVENT_OPTIONS_DELIMITER = ':';
|
|
37
37
|
|
|
38
|
-
export const EXPRESSION_ABYDON_ATTRIBUTE_FULL = /(@?[\w-]+)="<!--abydon.(\d+)-->"/g;
|
|
38
|
+
export const EXPRESSION_ABYDON_ATTRIBUTE_FULL = /(@?[\w-.]+(?::[a-z:]+)?)="<!--abydon.(\d+)-->"/g;
|
|
39
39
|
|
|
40
|
-
export const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX = /^
|
|
40
|
+
export const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX = /^abydon-/;
|
|
41
41
|
|
|
42
|
-
export const EXPRESSION_ABYDON_CONTENT =
|
|
42
|
+
export const EXPRESSION_ABYDON_CONTENT = /^abydon\.(\d+)$/;
|
|
43
43
|
|
|
44
44
|
export const EXPRESSION_ATTRIBUTE_CLASS = /^class\./;
|
|
45
45
|
|
|
@@ -47,9 +47,11 @@ 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_PROPERTY = /^style\.--/;
|
|
51
|
+
|
|
52
|
+
export const EXPRESSION_EVENT_ATTRIBUTE = /^@([\w-]+)(?::([a-z:]+))?="$/i;
|
|
51
53
|
|
|
52
|
-
export const EXPRESSION_EVENT_NAME =
|
|
54
|
+
export const EXPRESSION_EVENT_NAME = /^@?([\w-]+)(?::([a-z:]+))?$/i;
|
|
53
55
|
|
|
54
56
|
export const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
|
|
55
57
|
|
|
@@ -61,10 +63,12 @@ export const EXPRESSION_EVENT_PREFIX = /^@/;
|
|
|
61
63
|
|
|
62
64
|
export const EXPRESSION_PERIOD = /\./;
|
|
63
65
|
|
|
64
|
-
export const EXPRESSION_TEXTAREA_VALUE =
|
|
66
|
+
export const EXPRESSION_TEXTAREA_VALUE = /(?:<|<)!--abydon\.(\d+)--(?:>|>)/;
|
|
65
67
|
|
|
66
68
|
export const INPUT_TYPE_CHECKBOX = 'checkbox';
|
|
67
69
|
|
|
70
|
+
export const INPUT_TYPE_RADIO = 'radio';
|
|
71
|
+
|
|
68
72
|
export const NAME_FRAGMENT = '$fragment';
|
|
69
73
|
|
|
70
74
|
export const NAME_FRAGMENTS = '$fragments';
|
|
@@ -76,3 +80,7 @@ export const PROPERTY_IDENTIFIER = 'identifier';
|
|
|
76
80
|
export const PROPERTY_VALUE = 'value';
|
|
77
81
|
|
|
78
82
|
export const TEMPLATE_ITEM = '<>';
|
|
83
|
+
|
|
84
|
+
export const VALUE_TRUE = 'true';
|
|
85
|
+
|
|
86
|
+
export const WHITESPACE = /\s+/g;
|
package/src/fragment.ts
CHANGED
|
@@ -16,6 +16,13 @@ export class Fragment {
|
|
|
16
16
|
cache: true,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Is template caching enabled?
|
|
21
|
+
*/
|
|
22
|
+
get caching(): boolean {
|
|
23
|
+
return this.#configuration.cache;
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
/**
|
|
20
27
|
* Fragment identifier
|
|
21
28
|
*/
|
|
@@ -91,14 +98,12 @@ export class Fragment {
|
|
|
91
98
|
|
|
92
99
|
mapNodes(
|
|
93
100
|
data,
|
|
94
|
-
data.items.flatMap(
|
|
95
|
-
item => item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes ?? [],
|
|
96
|
-
),
|
|
101
|
+
data.items.flatMap(item => item.nodes!),
|
|
97
102
|
);
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
return data.items.flatMap(
|
|
101
|
-
item => item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes
|
|
106
|
+
item => item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes!,
|
|
102
107
|
);
|
|
103
108
|
}
|
|
104
109
|
|
|
@@ -137,7 +142,7 @@ function removeFragment(data: FragmentData): void {
|
|
|
137
142
|
fragments?.[fragmentIndex]?.remove();
|
|
138
143
|
}
|
|
139
144
|
|
|
140
|
-
removeNodes(nodes
|
|
145
|
+
removeNodes(nodes!);
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
data.items.length = 0;
|
package/src/fragments.ts
CHANGED
|
@@ -14,13 +14,6 @@ import type {FragmentsState} from './models';
|
|
|
14
14
|
export class Fragments {
|
|
15
15
|
readonly #state: FragmentsState;
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* Fragment items
|
|
19
|
-
*/
|
|
20
|
-
get items(): ReactiveArray<Fragment> {
|
|
21
|
-
return this.#state.mapped;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
17
|
constructor(
|
|
25
18
|
items: ReactiveArray<unknown>,
|
|
26
19
|
identify: (item: unknown) => unknown,
|
|
@@ -39,18 +32,16 @@ export class Fragments {
|
|
|
39
32
|
subscriber: undefined,
|
|
40
33
|
};
|
|
41
34
|
|
|
42
|
-
|
|
35
|
+
fragmentsStates.set(this, this.#state);
|
|
43
36
|
|
|
44
37
|
initializeFragments(this.#state);
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
40
|
|
|
48
41
|
export function handleFragments(item: Fragments | FragmentsState, remove: boolean): void {
|
|
49
|
-
const state = isFragments(item) ?
|
|
42
|
+
const state = isFragments(item) ? fragmentsStates.get(item) : item;
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
(remove ? removeFragments : initializeFragments)(state);
|
|
53
|
-
}
|
|
44
|
+
(remove ? removeFragments : initializeFragments)(state!);
|
|
54
45
|
}
|
|
55
46
|
|
|
56
47
|
function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
@@ -135,4 +126,4 @@ function updateFragments(state: FragmentsState, active?: Set<string>): void {
|
|
|
135
126
|
|
|
136
127
|
//
|
|
137
128
|
|
|
138
|
-
const
|
|
129
|
+
export const fragmentsStates: WeakMap<Fragments, FragmentsState> = new WeakMap();
|
package/src/helpers/index.ts
CHANGED
|
@@ -27,10 +27,20 @@ export function compareArrays(
|
|
|
27
27
|
return firstIsLarger ? ARRAY_COMPARISON_REMOVED : ARRAY_COMPARISON_ADDED;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Is the value a Fragment?
|
|
32
|
+
* @param value Value to check
|
|
33
|
+
* @returns `true` if the value is a Fragment, otherwise `false`
|
|
34
|
+
*/
|
|
30
35
|
export function isFragment(value: unknown): value is Fragment {
|
|
31
36
|
return isNamed(value, NAME_FRAGMENT);
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Is the value a Fragments?
|
|
41
|
+
* @param value Value to check
|
|
42
|
+
* @returns `true` if the value is a Fragments, otherwise `false`
|
|
43
|
+
*/
|
|
34
44
|
export function isFragments(value: unknown): value is Fragments {
|
|
35
45
|
return isNamed(value, NAME_FRAGMENTS);
|
|
36
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import '@oscarpalmer/mora';
|
|
2
|
-
import type
|
|
2
|
+
import {isArray, type ReactiveArray} from '@oscarpalmer/mora';
|
|
3
3
|
import {Fragment} from './fragment';
|
|
4
4
|
import {Fragments} from './fragments';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Create Fragments from a reactive array
|
|
7
|
+
* Create a Fragments from a reactive array
|
|
8
8
|
* @param array Reactive array
|
|
9
9
|
* @param identify Function to identify item
|
|
10
10
|
* @param fragment Function to create fragment from item
|
|
@@ -15,11 +15,23 @@ export function fragments<Item>(
|
|
|
15
15
|
identify: (item: Item) => unknown,
|
|
16
16
|
fragment: (item: Item) => Fragment,
|
|
17
17
|
): Fragments {
|
|
18
|
+
if (!isArray(array)) {
|
|
19
|
+
throw new TypeError('Fragments array must be a reactive array');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof identify !== 'function') {
|
|
23
|
+
throw new TypeError('Fragments identify must be a function');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof fragment !== 'function') {
|
|
27
|
+
throw new TypeError('Fragments fragment must be a function');
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
return new Fragments(array as ReactiveArray<unknown>, identify as never, fragment as never);
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
/**
|
|
22
|
-
* Create Fragment from a template
|
|
34
|
+
* Create a Fragment from a template
|
|
23
35
|
* @returns Fragment
|
|
24
36
|
*/
|
|
25
37
|
export function html(template: TemplateStringsArray, ...values: unknown[]): Fragment {
|
|
@@ -29,3 +41,4 @@ export function html(template: TemplateStringsArray, ...values: unknown[]): Frag
|
|
|
29
41
|
export * from '@oscarpalmer/mora';
|
|
30
42
|
export type {Fragment} from './fragment';
|
|
31
43
|
export type {Fragments} from './fragments';
|
|
44
|
+
export {isFragment, isFragments} from './helpers';
|
package/src/node/event.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {on} from '@oscarpalmer/toretto/event';
|
|
2
2
|
import {
|
|
3
|
+
CHANGE_INPUTS,
|
|
3
4
|
EVENT_CHANGE,
|
|
4
5
|
EVENT_DEFAULTS,
|
|
5
6
|
EVENT_INPUT,
|
|
6
7
|
EVENT_ON_VALUE,
|
|
7
8
|
EVENT_OPTIONS_DELIMITER,
|
|
8
9
|
EVENT_SUBMIT,
|
|
9
|
-
EXPRESSION_EVENT_CHANGE_TYPES,
|
|
10
10
|
EXPRESSION_EVENT_NAME,
|
|
11
11
|
EXPRESSION_EVENT_OPTIONS_ACTIVE,
|
|
12
12
|
EXPRESSION_EVENT_OPTIONS_CAPTURE,
|
|
@@ -29,7 +29,7 @@ function getType(element: HTMLElement | SVGElement, type: string): string {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
if (element instanceof HTMLInputElement) {
|
|
32
|
-
if (
|
|
32
|
+
if (CHANGE_INPUTS.has(element.type)) {
|
|
33
33
|
return EVENT_CHANGE;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -40,9 +40,16 @@ function getType(element: HTMLElement | SVGElement, type: string): string {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function mapEvent(element: HTMLElement | SVGElement, name: string, value: unknown): void {
|
|
43
|
-
const [, type, options] = EXPRESSION_EVENT_NAME.exec(name)
|
|
43
|
+
const [, type, options] = EXPRESSION_EVENT_NAME.exec(name)!;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const values = Array.isArray(value) ? value : [value];
|
|
46
|
+
const {length} = values;
|
|
47
|
+
|
|
48
|
+
for (let index = 0; index < length; index += 1) {
|
|
49
|
+
const item = values[index];
|
|
50
|
+
|
|
51
|
+
if (typeof item === 'function') {
|
|
52
|
+
on(element, getType(element, type), item as EventListener, getOptions(options ?? ''));
|
|
53
|
+
}
|
|
47
54
|
}
|
|
48
55
|
}
|