@oscarpalmer/abydon 0.15.0 → 0.17.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 +1443 -0
- package/dist/constants.d.mts +20 -0
- package/dist/constants.mjs +27 -0
- package/dist/fragment-9qTBAvtR.d.mts +82 -0
- package/dist/fragment.d.mts +2 -0
- package/dist/{fragment.js → fragment.mjs} +41 -14
- package/dist/fragments.d.mts +16 -0
- package/dist/{fragments.js → fragments.mjs} +10 -4
- package/dist/helpers/dom.d.mts +6 -0
- package/dist/helpers/{dom.js → dom.mjs} +4 -12
- package/dist/helpers/index.d.mts +9 -0
- package/dist/helpers/index.mjs +23 -0
- package/dist/index.d.mts +21 -0
- package/dist/index.mjs +24 -0
- package/dist/models.d.mts +2 -0
- package/dist/models.mjs +1 -0
- package/dist/node/attribute/index.d.mts +6 -0
- package/dist/node/attribute/index.mjs +41 -0
- package/dist/node/attribute/value.d.mts +6 -0
- package/dist/node/attribute/value.mjs +65 -0
- package/dist/node/event.d.mts +4 -0
- package/dist/node/event.mjs +25 -0
- package/dist/node/index.d.mts +6 -0
- package/dist/node/{index.js → index.mjs} +10 -8
- package/dist/node/value.d.mts +7 -0
- package/dist/node/{value.js → value.mjs} +12 -10
- package/dist/parse.d.mts +6 -0
- package/dist/{parse.js → parse.mjs} +11 -1
- package/package.json +42 -42
- package/src/constants.ts +30 -10
- package/src/fragment.ts +12 -24
- package/src/fragments.ts +3 -5
- package/src/helpers/dom.ts +1 -20
- package/src/helpers/index.ts +18 -15
- package/src/index.ts +14 -10
- package/src/models.ts +10 -1
- package/src/node/attribute/index.ts +22 -21
- package/src/node/attribute/value.ts +47 -37
- package/src/node/event.ts +26 -33
- package/src/node/index.ts +6 -18
- package/src/node/value.ts +15 -46
- package/src/parse.ts +15 -17
- package/dist/abydon.full.js +0 -1274
- package/dist/constants.js +0 -10
- package/dist/helpers/index.js +0 -14
- package/dist/index.js +0 -11
- package/dist/models.js +0 -0
- package/dist/node/attribute/index.js +0 -39
- package/dist/node/attribute/value.js +0 -56
- package/dist/node/event.js +0 -30
- package/types/constants.d.ts +0 -9
- package/types/fragment.d.ts +0 -38
- package/types/fragments.d.ts +0 -13
- package/types/helpers/dom.d.ts +0 -3
- package/types/helpers/index.d.ts +0 -5
- package/types/index.d.ts +0 -9
- package/types/models.d.ts +0 -32
- package/types/node/attribute/index.d.ts +0 -3
- package/types/node/attribute/value.d.ts +0 -3
- package/types/node/event.d.ts +0 -3
- package/types/node/index.d.ts +0 -2
- package/types/node/value.d.ts +0 -3
- package/types/parse.d.ts +0 -2
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { compareArrays, isFragment } from "../helpers/index.
|
|
2
|
-
import { createNodes, replaceNodes } from "../helpers/dom.
|
|
1
|
+
import { compareArrays, isFragment } from "../helpers/index.mjs";
|
|
2
|
+
import { createNodes, replaceNodes } from "../helpers/dom.mjs";
|
|
3
3
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
4
4
|
import { getString } from "@oscarpalmer/atoms/string";
|
|
5
5
|
import { isChildNode } from "@oscarpalmer/toretto/is";
|
|
6
|
+
//#region src/node/value.ts
|
|
6
7
|
function addToArray(identifiers, items, nodes, added) {
|
|
7
8
|
let position = nodes[0];
|
|
8
|
-
const before = added && !identifiers.previous.
|
|
9
|
+
const before = added && !identifiers.previous.has(items.templates[0].identifier);
|
|
9
10
|
const next = items.next.flatMap((fragment) => fragment.get().flatMap((node) => ({
|
|
10
11
|
identifier: fragment.identifier,
|
|
11
12
|
value: node
|
|
@@ -13,7 +14,7 @@ function addToArray(identifiers, items, nodes, added) {
|
|
|
13
14
|
const { length } = next;
|
|
14
15
|
for (let index = 0; index < length; index += 1) {
|
|
15
16
|
const node = next[index];
|
|
16
|
-
if (!(added && identifiers.previous.
|
|
17
|
+
if (!(added && identifiers.previous.has(node.identifier))) if (index === 0 && before) position.before(node.value);
|
|
17
18
|
else position.after(node.value);
|
|
18
19
|
position = node.value;
|
|
19
20
|
}
|
|
@@ -25,7 +26,7 @@ function handleArray(identifiers, items, nodes) {
|
|
|
25
26
|
...items,
|
|
26
27
|
next
|
|
27
28
|
}, nodes, comparison === "added");
|
|
28
|
-
const toRemove = items.fragments?.filter((fragment) => !identifiers.next.
|
|
29
|
+
const toRemove = items.fragments?.filter((fragment) => !identifiers.next.has(fragment.identifier)) ?? [];
|
|
29
30
|
const { length } = toRemove;
|
|
30
31
|
for (let index = 0; index < length; index += 1) toRemove[index].remove();
|
|
31
32
|
return {
|
|
@@ -47,18 +48,18 @@ function replaceText(item, comment, isNullable) {
|
|
|
47
48
|
}
|
|
48
49
|
function setArray(item, comment, value) {
|
|
49
50
|
if (value.length === 0) return { nodes: setText(item, comment, value) };
|
|
50
|
-
let templates = value.filter((item
|
|
51
|
+
let templates = value.filter((item) => isFragment(item) && item.identifier != null);
|
|
51
52
|
const next = templates.map((fragment) => fragment.identifier);
|
|
52
53
|
const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
|
|
53
54
|
if (new Set(next).size !== templates.length) templates = [];
|
|
54
55
|
const noTemplates = templates.length === 0;
|
|
55
56
|
if (noTemplates || item.nodes == null || previous.some((identifier) => identifier == null)) return {
|
|
56
57
|
fragments: noTemplates ? void 0 : templates,
|
|
57
|
-
nodes: setNodes(item, comment, noTemplates ? value.flatMap((item
|
|
58
|
+
nodes: setNodes(item, comment, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
|
|
58
59
|
};
|
|
59
60
|
return handleArray({
|
|
60
|
-
next,
|
|
61
|
-
previous
|
|
61
|
+
next: new Set(next),
|
|
62
|
+
previous: new Set(previous)
|
|
62
63
|
}, {
|
|
63
64
|
templates,
|
|
64
65
|
fragments: item.fragments ?? []
|
|
@@ -73,7 +74,7 @@ function setNodes(item, comment, next) {
|
|
|
73
74
|
return next;
|
|
74
75
|
}
|
|
75
76
|
function setReactiveValue(data, comment, reactive) {
|
|
76
|
-
let item = data.items.find((item
|
|
77
|
+
let item = data.items.find((item) => item.nodes?.includes(comment));
|
|
77
78
|
item ??= {};
|
|
78
79
|
item.text = new Text();
|
|
79
80
|
data.mora.subscribers.add(reactive.subscribe((value) => {
|
|
@@ -110,4 +111,5 @@ function setText(item, comment, value) {
|
|
|
110
111
|
removeFragments(item.fragments);
|
|
111
112
|
if (result) return item.text == null ? [] : [item.text];
|
|
112
113
|
}
|
|
114
|
+
//#endregion
|
|
113
115
|
export { setReactiveValue };
|
package/dist/parse.d.mts
ADDED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { EXPRESSION_ABYDON_ATTRIBUTE_FULL } from "./constants.mjs";
|
|
1
2
|
import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
|
|
3
|
+
//#region src/parse.ts
|
|
2
4
|
function handleExpression(data, prefix, expression) {
|
|
3
5
|
if (Array.isArray(expression)) {
|
|
4
6
|
const { length } = expression;
|
|
@@ -6,7 +8,7 @@ function handleExpression(data, prefix, expression) {
|
|
|
6
8
|
for (let index = 0; index < length; index += 1) expressions += handleExpression(data, "", expression[index]);
|
|
7
9
|
return `${prefix}${expressions}`;
|
|
8
10
|
}
|
|
9
|
-
if (typeof expression === "function" || typeof expression === "object" && expression != null) return
|
|
11
|
+
if (typeof expression === "function" || typeof expression === "object" && expression != null) return transformExpression(prefix, data.values.push(expression) - 1);
|
|
10
12
|
return isNullableOrWhitespace(expression) ? prefix : `${prefix}${expression}`;
|
|
11
13
|
}
|
|
12
14
|
function parse(data) {
|
|
@@ -14,8 +16,16 @@ function parse(data) {
|
|
|
14
16
|
const { length } = data.strings;
|
|
15
17
|
data.template = "";
|
|
16
18
|
for (let index = 0; index < length; index += 1) data.template += handleExpression(data, data.strings[index], data.expressions[index]);
|
|
19
|
+
data.template = data.template.replaceAll(EXPRESSION_ABYDON_ATTRIBUTE_FULL, transformAttribute);
|
|
17
20
|
data.expressions = [];
|
|
18
21
|
data.strings = [];
|
|
19
22
|
return data.template;
|
|
20
23
|
}
|
|
24
|
+
function transformAttribute(_, name, index) {
|
|
25
|
+
return `_${name}="@abydon.${index}@"`;
|
|
26
|
+
}
|
|
27
|
+
function transformExpression(prefix, index) {
|
|
28
|
+
return `${prefix}<!--abydon.${index}-->`;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
21
31
|
export { parse };
|
package/package.json
CHANGED
|
@@ -1,36 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"url": "https://oscarpalmer.se"
|
|
5
|
-
},
|
|
6
|
-
"dependencies": {
|
|
7
|
-
"@oscarpalmer/atoms": "^0.114",
|
|
8
|
-
"@oscarpalmer/mora": "^0.23",
|
|
9
|
-
"@oscarpalmer/toretto": "^0.25"
|
|
10
|
-
},
|
|
11
|
-
"devDependencies": {
|
|
12
|
-
"@biomejs/biome": "^2.3",
|
|
13
|
-
"@oscarpalmer/oui": "^0.18",
|
|
14
|
-
"@rollup/plugin-node-resolve": "^16",
|
|
15
|
-
"@rollup/plugin-typescript": "^12.3",
|
|
16
|
-
"@types/node": "^24.9",
|
|
17
|
-
"@vitest/coverage-istanbul": "^4",
|
|
18
|
-
"glob": "^11",
|
|
19
|
-
"jsdom": "^27.1",
|
|
20
|
-
"rollup": "^4.52",
|
|
21
|
-
"tslib": "^2.8",
|
|
22
|
-
"typescript": "^5.9",
|
|
23
|
-
"vite": "npm:rolldown-vite@latest",
|
|
24
|
-
"vitest": "^4"
|
|
25
|
-
},
|
|
26
|
-
"exports": {
|
|
27
|
-
"./package.json": "./package.json",
|
|
28
|
-
".": {
|
|
29
|
-
"types": "./types/index.d.ts",
|
|
30
|
-
"default": "./dist/index.js"
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"files": ["dist", "src", "types"],
|
|
2
|
+
"name": "@oscarpalmer/abydon",
|
|
3
|
+
"version": "0.17.0",
|
|
34
4
|
"keywords": [
|
|
35
5
|
"components",
|
|
36
6
|
"dom",
|
|
@@ -42,21 +12,51 @@
|
|
|
42
12
|
"vanilla"
|
|
43
13
|
],
|
|
44
14
|
"license": "MIT",
|
|
45
|
-
"
|
|
46
|
-
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Oscar Palmér",
|
|
17
|
+
"url": "https://oscarpalmer.se"
|
|
18
|
+
},
|
|
47
19
|
"repository": {
|
|
48
20
|
"type": "git",
|
|
49
21
|
"url": "git+https://github.com/oscarpalmer/abydon.git"
|
|
50
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"module": "./dist/index.mjs",
|
|
29
|
+
"types": "./dist/index.d.mts",
|
|
30
|
+
"exports": {
|
|
31
|
+
"./package.json": "./package.json",
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.mts",
|
|
34
|
+
"default": "./dist/index.mjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
51
37
|
"scripts": {
|
|
52
|
-
"build": "npm run
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"test": "npx
|
|
38
|
+
"build": "npm run tsdown:build && npx vp pack",
|
|
39
|
+
"tsdown:build": "npx tsdown -c ./tsdown.config.ts",
|
|
40
|
+
"tsdown:watch": "npx tsdown -c ./tsdown.config.ts --watch",
|
|
41
|
+
"test": "npx vp test run --coverage",
|
|
42
|
+
"test:leak": "npx vp test run --detect-async-leaks --coverage",
|
|
57
43
|
"watch": "npx vite build --watch"
|
|
58
44
|
},
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@oscarpalmer/atoms": "^0.165",
|
|
47
|
+
"@oscarpalmer/mora": "^0.26",
|
|
48
|
+
"@oscarpalmer/toretto": "^0.41"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@oscarpalmer/oui": "^0.19",
|
|
52
|
+
"@types/node": "^25.5",
|
|
53
|
+
"@vitest/coverage-istanbul": "^4.1",
|
|
54
|
+
"jsdom": "^28.1",
|
|
55
|
+
"tsdown": "^0.21",
|
|
56
|
+
"typescript": "^5.9",
|
|
57
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
58
|
+
"vite-plus": "latest",
|
|
59
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
|
60
|
+
},
|
|
61
|
+
"packageManager": "npm@11.11.1"
|
|
62
62
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,20 +1,40 @@
|
|
|
1
|
-
export const ABORT_CONTROLLERS: WeakMap<HTMLOrSVGElement, AbortController> =
|
|
2
|
-
new WeakMap();
|
|
3
|
-
|
|
4
1
|
export const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
|
|
5
2
|
|
|
3
|
+
export const EVENT_DEFAULTS: Record<string, string> = {
|
|
4
|
+
A: 'click',
|
|
5
|
+
BUTTON: 'click',
|
|
6
|
+
DETAILS: 'toggle',
|
|
7
|
+
FORM: 'submit',
|
|
8
|
+
SELECT: 'change',
|
|
9
|
+
TEXTAREA: 'input',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const EXPRESSION_ABYDON_ATTRIBUTE_FULL = /(@?\w+)="<!--abydon.(\d+)-->"/g;
|
|
13
|
+
|
|
14
|
+
export const EXPRESSION_ABYDON_ATTRIBUTE_PREFIX = /^_/;
|
|
15
|
+
|
|
16
|
+
export const EXPRESSION_ABYDON_CONTENT = /^@?abydon\.(\d+)@?$/;
|
|
17
|
+
|
|
6
18
|
export const EXPRESSION_ATTRIBUTE_CLASS = /^class\./;
|
|
7
19
|
|
|
8
|
-
export const EXPRESSION_ATTRIBUTE_STYLE_FULL =
|
|
9
|
-
/^style\.([\w-]+)(?:\.([\w-]+))?$/;
|
|
20
|
+
export const EXPRESSION_ATTRIBUTE_STYLE_FULL = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
|
|
10
21
|
|
|
11
22
|
export const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
|
|
12
23
|
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
-
export const EXPRESSION_COMMENT_CONTENT = /^abydon\.(\d+)$/;
|
|
24
|
+
export const EXPRESSION_EVENT_CHANGE_TYPES = /^(checkbox|radio)$/;
|
|
16
25
|
|
|
17
26
|
export const EXPRESSION_EVENT_NAME = /^@([\w-]+)(?::([a-z:]+))?$/i;
|
|
18
27
|
|
|
19
|
-
export const
|
|
20
|
-
|
|
28
|
+
export const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
|
|
29
|
+
|
|
30
|
+
export const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(apture)$/i;
|
|
31
|
+
|
|
32
|
+
export const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(nce)$/i;
|
|
33
|
+
|
|
34
|
+
export const EXPRESSION_EVENT_PREFIX = /^@/;
|
|
35
|
+
|
|
36
|
+
export const EXPRESSION_PERIOD = /\./;
|
|
37
|
+
|
|
38
|
+
export const NAME_FRAGMENT = '$fragment';
|
|
39
|
+
|
|
40
|
+
export const NAME_FRAGMENTS = '$fragments';
|
package/src/fragment.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import {html} from '@oscarpalmer/toretto/html';
|
|
3
|
+
import {NAME_FRAGMENT} from './constants';
|
|
3
4
|
import {handleFragments} from './fragments';
|
|
4
5
|
import {isFragments} from './helpers';
|
|
5
6
|
import {removeNodes} from './helpers/dom';
|
|
@@ -12,7 +13,7 @@ export class Fragment {
|
|
|
12
13
|
|
|
13
14
|
readonly #configuration: Required<FragmentConfiguration> = {
|
|
14
15
|
identifier: undefined,
|
|
15
|
-
|
|
16
|
+
cache: true,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -23,7 +24,7 @@ export class Fragment {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
constructor(strings: TemplateStringsArray, expressions: unknown[]) {
|
|
26
|
-
Object.defineProperty(this,
|
|
27
|
+
Object.defineProperty(this, NAME_FRAGMENT, {
|
|
27
28
|
value: true,
|
|
28
29
|
});
|
|
29
30
|
|
|
@@ -55,12 +56,12 @@ export class Fragment {
|
|
|
55
56
|
configure(configuration: FragmentConfiguration): Fragment {
|
|
56
57
|
const actual = isPlainObject(configuration) ? configuration : {};
|
|
57
58
|
|
|
58
|
-
if (
|
|
59
|
+
if ('identifier' in actual) {
|
|
59
60
|
this.#configuration.identifier = actual.identifier;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
if (typeof actual.
|
|
63
|
-
this.#configuration.
|
|
63
|
+
if (typeof actual.cache === 'boolean') {
|
|
64
|
+
this.#configuration.cache = actual.cache;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
return this;
|
|
@@ -77,8 +78,7 @@ export class Fragment {
|
|
|
77
78
|
const parsed = parse(data);
|
|
78
79
|
|
|
79
80
|
const templated = html(parsed, {
|
|
80
|
-
|
|
81
|
-
sanitizeBooleanAttributes: false,
|
|
81
|
+
cache: this.#configuration.cache,
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
data.items.splice(
|
|
@@ -92,22 +92,14 @@ export class Fragment {
|
|
|
92
92
|
mapNodes(
|
|
93
93
|
data,
|
|
94
94
|
data.items.flatMap(
|
|
95
|
-
item =>
|
|
96
|
-
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
97
|
-
item.nodes ??
|
|
98
|
-
[],
|
|
95
|
+
item => item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes ?? [],
|
|
99
96
|
),
|
|
100
97
|
);
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
107
|
-
item.nodes ??
|
|
108
|
-
[],
|
|
109
|
-
),
|
|
110
|
-
];
|
|
100
|
+
return data.items.flatMap(
|
|
101
|
+
item => item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes ?? [],
|
|
102
|
+
);
|
|
111
103
|
}
|
|
112
104
|
|
|
113
105
|
/**
|
|
@@ -141,11 +133,7 @@ function removeFragment(data: FragmentData): void {
|
|
|
141
133
|
const {fragments, nodes} = data.items[index];
|
|
142
134
|
const fragmentsLength = fragments?.length ?? 0;
|
|
143
135
|
|
|
144
|
-
for (
|
|
145
|
-
let fragmentIndex = 0;
|
|
146
|
-
fragmentIndex < fragmentsLength;
|
|
147
|
-
fragmentIndex += 1
|
|
148
|
-
) {
|
|
136
|
+
for (let fragmentIndex = 0; fragmentIndex < fragmentsLength; fragmentIndex += 1) {
|
|
149
137
|
fragments?.[fragmentIndex]?.remove();
|
|
150
138
|
}
|
|
151
139
|
|
package/src/fragments.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
2
2
|
import {array, type ReactiveArray} from '@oscarpalmer/mora';
|
|
3
|
+
import {NAME_FRAGMENTS} from './constants';
|
|
3
4
|
import type {Fragment} from './fragment';
|
|
4
5
|
import {isFragment, isFragments} from './helpers';
|
|
5
6
|
import type {FragmentsState} from './models';
|
|
@@ -19,7 +20,7 @@ export class Fragments {
|
|
|
19
20
|
identify: (item: unknown) => unknown,
|
|
20
21
|
fragment: (item: unknown) => Fragment,
|
|
21
22
|
) {
|
|
22
|
-
Object.defineProperty(this,
|
|
23
|
+
Object.defineProperty(this, NAME_FRAGMENTS, {
|
|
23
24
|
value: true,
|
|
24
25
|
});
|
|
25
26
|
|
|
@@ -38,10 +39,7 @@ export class Fragments {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
export function handleFragments(
|
|
42
|
-
item: Fragments | FragmentsState,
|
|
43
|
-
remove: boolean,
|
|
44
|
-
): void {
|
|
42
|
+
export function handleFragments(item: Fragments | FragmentsState, remove: boolean): void {
|
|
45
43
|
const state = isFragments(item) ? states.get(item) : item;
|
|
46
44
|
|
|
47
45
|
if (state != null) {
|
package/src/helpers/dom.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
2
|
-
import {isChildNode
|
|
3
|
-
import {removeEvents} from '../node/event';
|
|
2
|
+
import {isChildNode} from '@oscarpalmer/toretto/is';
|
|
4
3
|
import {isFragment} from './index';
|
|
5
4
|
|
|
6
5
|
export function createNodes(value: unknown): ChildNode[] {
|
|
@@ -16,8 +15,6 @@ export function createNodes(value: unknown): ChildNode[] {
|
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
export function removeNodes(nodes: ChildNode[]): void {
|
|
19
|
-
sanitizeNodes(nodes);
|
|
20
|
-
|
|
21
18
|
const {length} = nodes;
|
|
22
19
|
|
|
23
20
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -34,19 +31,3 @@ export function replaceNodes(from: ChildNode[], to: ChildNode[]): void {
|
|
|
34
31
|
from[index].remove();
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
|
-
|
|
38
|
-
function sanitizeNodes(nodes: ChildNode[]): void {
|
|
39
|
-
const {length} = nodes;
|
|
40
|
-
|
|
41
|
-
for (let index = 0; index < length; index += 1) {
|
|
42
|
-
const node = nodes[index];
|
|
43
|
-
|
|
44
|
-
if (isHTMLOrSVGElement(node)) {
|
|
45
|
-
removeEvents(node);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (node.hasChildNodes()) {
|
|
49
|
-
sanitizeNodes([...node.childNodes] as ChildNode[]);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {NAME_FRAGMENT, NAME_FRAGMENTS} from '../constants';
|
|
1
3
|
import type {Fragment} from '../fragment';
|
|
2
4
|
import type {Fragments} from '../fragments';
|
|
3
5
|
|
|
@@ -9,31 +11,32 @@ export function compareArrays(
|
|
|
9
11
|
const from = firstIsLarger ? first : second;
|
|
10
12
|
const to = firstIsLarger ? second : first;
|
|
11
13
|
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
.filter(key => to.includes(key))
|
|
15
|
-
.every((key, index) => to[index] === key)
|
|
16
|
-
) {
|
|
17
|
-
return 'dissimilar';
|
|
14
|
+
if (!from.filter(key => to.includes(key)).every((key, index) => to[index] === key)) {
|
|
15
|
+
return COMPARISON_DISSIMILAR;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
return firstIsLarger ?
|
|
18
|
+
return firstIsLarger ? COMPARISON_REMOVED : COMPARISON_ADDED;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export function isFragment(value: unknown): value is Fragment {
|
|
24
|
-
return (
|
|
25
|
-
typeof value === 'object' &&
|
|
26
|
-
value != null &&
|
|
27
|
-
'$fragment' in value &&
|
|
28
|
-
value.$fragment === true
|
|
29
|
-
);
|
|
22
|
+
return isNamed(value, NAME_FRAGMENT);
|
|
30
23
|
}
|
|
31
24
|
|
|
32
25
|
export function isFragments(value: unknown): value is Fragments {
|
|
26
|
+
return isNamed(value, NAME_FRAGMENTS);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isNamed(value: unknown, name: string): boolean {
|
|
33
30
|
return (
|
|
34
31
|
typeof value === 'object' &&
|
|
35
32
|
value != null &&
|
|
36
|
-
|
|
37
|
-
value
|
|
33
|
+
name in value &&
|
|
34
|
+
(value as PlainObject)[name] === true
|
|
38
35
|
);
|
|
39
36
|
}
|
|
37
|
+
|
|
38
|
+
const COMPARISON_ADDED = 'added';
|
|
39
|
+
|
|
40
|
+
const COMPARISON_DISSIMILAR = 'dissimilar';
|
|
41
|
+
|
|
42
|
+
const COMPARISON_REMOVED = 'removed';
|
package/src/index.ts
CHANGED
|
@@ -3,23 +3,27 @@ import type {ReactiveArray} from '@oscarpalmer/mora';
|
|
|
3
3
|
import {Fragment} from './fragment';
|
|
4
4
|
import {Fragments} from './fragments';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Create Fragments from a reactive array
|
|
8
|
+
* @param array Reactive array
|
|
9
|
+
* @param identify Function to identify item
|
|
10
|
+
* @param fragment Function to create fragment from item
|
|
11
|
+
* @returns Fragments
|
|
12
|
+
*/
|
|
6
13
|
export function fragments<Item>(
|
|
7
14
|
array: ReactiveArray<Item>,
|
|
8
15
|
identify: (item: Item) => unknown,
|
|
9
16
|
fragment: (item: Item) => Fragment,
|
|
10
17
|
): Fragments {
|
|
11
|
-
return new Fragments(
|
|
12
|
-
array as ReactiveArray<unknown>,
|
|
13
|
-
identify as never,
|
|
14
|
-
fragment as never,
|
|
15
|
-
);
|
|
18
|
+
return new Fragments(array as ReactiveArray<unknown>, identify as never, fragment as never);
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Create Fragment from a template
|
|
23
|
+
* @returns Fragment
|
|
24
|
+
*/
|
|
25
|
+
export function html(template: TemplateStringsArray, ...values: unknown[]): Fragment {
|
|
26
|
+
return new Fragment(template, values);
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
export * from '@oscarpalmer/mora';
|
package/src/models.ts
CHANGED
|
@@ -2,8 +2,17 @@ import type {Reactive, ReactiveArray, Unsubscribe} from '@oscarpalmer/mora';
|
|
|
2
2
|
import type {Fragment} from './fragment';
|
|
3
3
|
|
|
4
4
|
export 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
|
+
*/
|
|
5
15
|
identifier?: unknown;
|
|
6
|
-
ignoreCache?: boolean;
|
|
7
16
|
};
|
|
8
17
|
|
|
9
18
|
export type FragmentData = {
|
|
@@ -1,43 +1,44 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {computed, isReactive} from '@oscarpalmer/mora';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
EXPRESSION_ABYDON_ATTRIBUTE_PREFIX,
|
|
5
|
+
EXPRESSION_ABYDON_CONTENT,
|
|
6
|
+
EXPRESSION_EVENT_PREFIX,
|
|
7
|
+
EXPRESSION_PERIOD,
|
|
8
|
+
} from '../../constants';
|
|
6
9
|
import type {FragmentData} from '../../models';
|
|
7
10
|
import {mapEvent} from '../event';
|
|
8
11
|
import {setAttribute} from './value';
|
|
9
12
|
|
|
10
13
|
function getValue(data: FragmentData, original: string): unknown {
|
|
11
|
-
const matches =
|
|
14
|
+
const matches = EXPRESSION_ABYDON_CONTENT.exec(original ?? '');
|
|
12
15
|
|
|
13
16
|
return matches == null ? original : data.values[+matches[1]];
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
export function mapAttributes(
|
|
17
|
-
data: FragmentData,
|
|
18
|
-
element: HTMLOrSVGElement,
|
|
19
|
-
): void {
|
|
19
|
+
export function mapAttributes(data: FragmentData, element: HTMLElement | SVGElement): void {
|
|
20
20
|
const attributes = [...element.attributes];
|
|
21
21
|
const {length} = attributes;
|
|
22
22
|
|
|
23
23
|
for (let index = 0; index < length; index += 1) {
|
|
24
24
|
const {name, value} = attributes[index];
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const actualName = name.replace(EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, '');
|
|
27
|
+
const actualValue = getValue(data, value) as never;
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
break;
|
|
29
|
+
if (actualName !== name) {
|
|
30
|
+
element.removeAttribute(name);
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
mapValue(data, element, name, actual);
|
|
33
|
+
switch (true) {
|
|
34
|
+
case EXPRESSION_EVENT_PREFIX.test(actualName):
|
|
35
|
+
mapEvent(element, actualName, actualValue);
|
|
37
36
|
break;
|
|
38
37
|
|
|
39
|
-
case
|
|
40
|
-
|
|
38
|
+
case EXPRESSION_PERIOD.test(actualName):
|
|
39
|
+
case typeof actualValue === 'function':
|
|
40
|
+
case isReactive(actualValue):
|
|
41
|
+
mapValue(data, element, actualName, actualValue);
|
|
41
42
|
break;
|
|
42
43
|
|
|
43
44
|
default:
|
|
@@ -48,7 +49,7 @@ export function mapAttributes(
|
|
|
48
49
|
|
|
49
50
|
function mapValue(
|
|
50
51
|
data: FragmentData,
|
|
51
|
-
element:
|
|
52
|
+
element: HTMLElement | SVGElement,
|
|
52
53
|
name: string,
|
|
53
54
|
value: unknown,
|
|
54
55
|
): void {
|
|
@@ -61,7 +62,7 @@ function mapValue(
|
|
|
61
62
|
|
|
62
63
|
function setComputedAttribute(
|
|
63
64
|
data: FragmentData,
|
|
64
|
-
element:
|
|
65
|
+
element: HTMLElement | SVGElement,
|
|
65
66
|
name: string,
|
|
66
67
|
callback: GenericCallback,
|
|
67
68
|
): void {
|