@oscarpalmer/abydon 0.13.0 → 0.15.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 +790 -763
- package/dist/constants.js +10 -0
- package/dist/fragment.js +38 -17
- package/dist/fragments.js +74 -0
- package/dist/helpers/dom.js +4 -4
- package/dist/helpers/index.js +4 -1
- package/dist/index.js +5 -1
- package/dist/node/attribute/index.js +19 -14
- package/dist/node/attribute/value.js +10 -12
- package/dist/node/event.js +7 -9
- package/dist/node/index.js +9 -4
- package/dist/node/value.js +84 -64
- package/dist/parse.js +7 -7
- package/package.json +14 -16
- package/src/constants.ts +20 -0
- package/src/fragment.ts +92 -29
- package/src/fragments.ts +134 -0
- package/src/helpers/dom.ts +3 -3
- package/src/helpers/index.ts +10 -0
- package/src/index.ts +16 -1
- package/src/models.ts +18 -4
- package/src/node/attribute/index.ts +24 -19
- package/src/node/attribute/value.ts +31 -28
- package/src/node/event.ts +11 -12
- package/src/node/index.ts +15 -6
- package/src/node/value.ts +186 -116
- package/src/parse.ts +10 -3
- package/types/constants.d.ts +9 -0
- package/types/fragment.d.ts +26 -8
- package/types/fragments.d.ts +13 -0
- package/types/helpers/index.d.ts +2 -0
- package/types/index.d.ts +5 -1
- package/types/models.d.ts +16 -4
- package/types/fragment.d.cts +0 -27
- package/types/helpers/dom.d.cts +0 -7
- package/types/helpers/index.d.cts +0 -29
- package/types/index.d.cts +0 -302
- package/types/models.d.cts +0 -107
- package/types/node/attribute/index.d.cts +0 -109
- package/types/node/attribute/value.d.cts +0 -109
- package/types/node/event.d.cts +0 -7
- package/types/node/index.d.cts +0 -108
- package/types/node/value.d.cts +0 -108
- package/types/parse.d.cts +0 -108
package/package.json
CHANGED
|
@@ -4,25 +4,24 @@
|
|
|
4
4
|
"url": "https://oscarpalmer.se"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@oscarpalmer/atoms": "^0.
|
|
8
|
-
"@oscarpalmer/mora": "^0.
|
|
9
|
-
"@oscarpalmer/toretto": "^0.
|
|
7
|
+
"@oscarpalmer/atoms": "^0.114",
|
|
8
|
+
"@oscarpalmer/mora": "^0.23",
|
|
9
|
+
"@oscarpalmer/toretto": "^0.25"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@biomejs/biome": "^2.
|
|
13
|
-
"@oscarpalmer/oui": "^0.
|
|
12
|
+
"@biomejs/biome": "^2.3",
|
|
13
|
+
"@oscarpalmer/oui": "^0.18",
|
|
14
14
|
"@rollup/plugin-node-resolve": "^16",
|
|
15
|
-
"@rollup/plugin-typescript": "^12.
|
|
16
|
-
"@types/node": "^24.
|
|
17
|
-
"@vitest/coverage-istanbul": "^
|
|
18
|
-
"dts-bundle-generator": "^9.5",
|
|
15
|
+
"@rollup/plugin-typescript": "^12.3",
|
|
16
|
+
"@types/node": "^24.9",
|
|
17
|
+
"@vitest/coverage-istanbul": "^4",
|
|
19
18
|
"glob": "^11",
|
|
20
|
-
"jsdom": "^
|
|
21
|
-
"rollup": "^4.
|
|
19
|
+
"jsdom": "^27.1",
|
|
20
|
+
"rollup": "^4.52",
|
|
22
21
|
"tslib": "^2.8",
|
|
23
22
|
"typescript": "^5.9",
|
|
24
23
|
"vite": "npm:rolldown-vite@latest",
|
|
25
|
-
"vitest": "^
|
|
24
|
+
"vitest": "^4"
|
|
26
25
|
},
|
|
27
26
|
"exports": {
|
|
28
27
|
"./package.json": "./package.json",
|
|
@@ -50,15 +49,14 @@
|
|
|
50
49
|
"url": "git+https://github.com/oscarpalmer/abydon.git"
|
|
51
50
|
},
|
|
52
51
|
"scripts": {
|
|
53
|
-
"build": "npm run clean && npx vite build && npm run rollup:build &&
|
|
52
|
+
"build": "npm run clean && npx vite build && npm run rollup:build && npx tsc",
|
|
54
53
|
"clean": "rm -rf ./dist && rm -rf ./types && rm -f ./tsconfig.tsbuildinfo",
|
|
55
54
|
"rollup:build": "npx rollup -c",
|
|
56
55
|
"rollup:watch": "npx rollup -c --watch",
|
|
57
56
|
"test": "npx vitest --coverage",
|
|
58
|
-
"types": "npx tsc && npx dts-bundle-generator --config ./dts.config.cts --silent",
|
|
59
57
|
"watch": "npx vite build --watch"
|
|
60
58
|
},
|
|
61
59
|
"type": "module",
|
|
62
|
-
"types": "types/index.d.
|
|
63
|
-
"version": "0.
|
|
60
|
+
"types": "types/index.d.ts",
|
|
61
|
+
"version": "0.15.0"
|
|
64
62
|
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const ABORT_CONTROLLERS: WeakMap<HTMLOrSVGElement, AbortController> =
|
|
2
|
+
new WeakMap();
|
|
3
|
+
|
|
4
|
+
export const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
|
|
5
|
+
|
|
6
|
+
export const EXPRESSION_ATTRIBUTE_CLASS = /^class\./;
|
|
7
|
+
|
|
8
|
+
export const EXPRESSION_ATTRIBUTE_STYLE_FULL =
|
|
9
|
+
/^style\.([\w-]+)(?:\.([\w-]+))?$/;
|
|
10
|
+
|
|
11
|
+
export const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
|
|
12
|
+
|
|
13
|
+
export const EXPRESSION_COMMENT_FULL = /^<!--abydon\.(\d+)-->$/;
|
|
14
|
+
|
|
15
|
+
export const EXPRESSION_COMMENT_CONTENT = /^abydon\.(\d+)$/;
|
|
16
|
+
|
|
17
|
+
export const EXPRESSION_EVENT_NAME = /^@([\w-]+)(?::([a-z:]+))?$/i;
|
|
18
|
+
|
|
19
|
+
export const REASON_EVENT_REMOVED =
|
|
20
|
+
'Event removed as element was removed from document by Abydon';
|
package/src/fragment.ts
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import {html} from '@oscarpalmer/toretto/html';
|
|
3
|
+
import {handleFragments} from './fragments';
|
|
4
|
+
import {isFragments} from './helpers';
|
|
3
5
|
import {removeNodes} from './helpers/dom';
|
|
4
|
-
import type {FragmentData} from './models';
|
|
6
|
+
import type {FragmentConfiguration, FragmentData} from './models';
|
|
5
7
|
import {mapNodes} from './node';
|
|
6
8
|
import {parse} from './parse';
|
|
7
9
|
|
|
8
10
|
export class Fragment {
|
|
9
|
-
|
|
10
|
-
private readonly data: FragmentData;
|
|
11
|
+
readonly #data: FragmentData;
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
readonly #configuration: Required<FragmentConfiguration> = {
|
|
14
|
+
identifier: undefined,
|
|
15
|
+
ignoreCache: false,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fragment identifier
|
|
20
|
+
*/
|
|
21
|
+
get identifier(): unknown {
|
|
22
|
+
return this.#configuration.identifier;
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
constructor(strings: TemplateStringsArray, expressions: unknown[]) {
|
|
17
|
-
this
|
|
26
|
+
Object.defineProperty(this, '$fragment', {
|
|
27
|
+
value: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.#data = {
|
|
18
31
|
expressions,
|
|
19
32
|
strings,
|
|
20
33
|
items: [],
|
|
@@ -27,79 +40,129 @@ export class Fragment {
|
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
/**
|
|
30
|
-
*
|
|
43
|
+
* Append the fragment to the given element
|
|
44
|
+
* @param element Element to append to
|
|
31
45
|
*/
|
|
32
46
|
appendTo(element: Element): void {
|
|
33
47
|
element.append(...this.get());
|
|
34
48
|
}
|
|
35
49
|
|
|
36
50
|
/**
|
|
37
|
-
*
|
|
51
|
+
* Configure the fragment
|
|
52
|
+
* @param configuration Configuration options
|
|
53
|
+
* @returns Fragment
|
|
54
|
+
*/
|
|
55
|
+
configure(configuration: FragmentConfiguration): Fragment {
|
|
56
|
+
const actual = isPlainObject(configuration) ? configuration : {};
|
|
57
|
+
|
|
58
|
+
if (actual.identifier !== undefined) {
|
|
59
|
+
this.#configuration.identifier = actual.identifier;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof actual.ignoreCache === 'boolean') {
|
|
63
|
+
this.#configuration.ignoreCache = actual.ignoreCache;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get a list of the fragment's nodes
|
|
71
|
+
* @returns List of nodes
|
|
38
72
|
*/
|
|
39
73
|
get(): ChildNode[] {
|
|
40
|
-
|
|
41
|
-
|
|
74
|
+
const data = this.#data;
|
|
75
|
+
|
|
76
|
+
if (data.items.length === 0) {
|
|
77
|
+
const parsed = parse(data);
|
|
42
78
|
|
|
43
79
|
const templated = html(parsed, {
|
|
80
|
+
ignoreCache: this.#configuration.ignoreCache,
|
|
44
81
|
sanitizeBooleanAttributes: false,
|
|
45
82
|
});
|
|
46
83
|
|
|
47
|
-
|
|
84
|
+
data.items.splice(
|
|
48
85
|
0,
|
|
49
|
-
|
|
86
|
+
data.items.length,
|
|
50
87
|
...templated.map(node => ({
|
|
51
88
|
nodes: [node as ChildNode],
|
|
52
89
|
})),
|
|
53
90
|
);
|
|
54
91
|
|
|
55
92
|
mapNodes(
|
|
56
|
-
|
|
57
|
-
|
|
93
|
+
data,
|
|
94
|
+
data.items.flatMap(
|
|
58
95
|
item =>
|
|
59
|
-
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
96
|
+
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
97
|
+
item.nodes ??
|
|
98
|
+
[],
|
|
60
99
|
),
|
|
61
100
|
);
|
|
62
101
|
}
|
|
63
102
|
|
|
64
103
|
return [
|
|
65
|
-
...
|
|
104
|
+
...data.items.flatMap(
|
|
66
105
|
item =>
|
|
67
|
-
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
106
|
+
item.fragments?.flatMap(fragment => fragment.get()) ??
|
|
107
|
+
item.nodes ??
|
|
108
|
+
[],
|
|
68
109
|
),
|
|
69
110
|
];
|
|
70
111
|
}
|
|
71
112
|
|
|
72
|
-
|
|
73
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Set an identifier for the fragment
|
|
115
|
+
*
|
|
116
|
+
* _An identifier can be used to uniquely identify a fragment,
|
|
117
|
+
* which helps prevent re-rendering in certain scenarios._
|
|
118
|
+
* @param identifier Identifier
|
|
119
|
+
* @returns Fragment
|
|
120
|
+
*/
|
|
121
|
+
identify(identifier: unknown): Fragment {
|
|
122
|
+
this.#configuration.identifier = identifier;
|
|
74
123
|
|
|
75
124
|
return this;
|
|
76
125
|
}
|
|
77
126
|
|
|
78
127
|
/**
|
|
79
|
-
*
|
|
128
|
+
* Remove the fragment from the DOM
|
|
80
129
|
*/
|
|
81
130
|
remove(): void {
|
|
82
|
-
removeFragment(this
|
|
131
|
+
removeFragment(this.#data);
|
|
83
132
|
}
|
|
84
133
|
}
|
|
85
134
|
|
|
86
135
|
function removeFragment(data: FragmentData): void {
|
|
87
136
|
removeMora(data);
|
|
88
137
|
|
|
89
|
-
|
|
138
|
+
let {length} = data.items;
|
|
90
139
|
|
|
91
140
|
for (let index = 0; index < length; index += 1) {
|
|
92
141
|
const {fragments, nodes} = data.items[index];
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
for (
|
|
96
|
-
|
|
142
|
+
const fragmentsLength = fragments?.length ?? 0;
|
|
143
|
+
|
|
144
|
+
for (
|
|
145
|
+
let fragmentIndex = 0;
|
|
146
|
+
fragmentIndex < fragmentsLength;
|
|
147
|
+
fragmentIndex += 1
|
|
148
|
+
) {
|
|
149
|
+
fragments?.[fragmentIndex]?.remove();
|
|
97
150
|
}
|
|
98
151
|
|
|
99
|
-
removeNodes(nodes);
|
|
152
|
+
removeNodes(nodes ?? []);
|
|
100
153
|
}
|
|
101
154
|
|
|
102
|
-
data.items.
|
|
155
|
+
data.items.length = 0;
|
|
156
|
+
|
|
157
|
+
length = data.values.length;
|
|
158
|
+
|
|
159
|
+
for (let index = 0; index < length; index += 1) {
|
|
160
|
+
const value = data.values[index];
|
|
161
|
+
|
|
162
|
+
if (isFragments(value)) {
|
|
163
|
+
handleFragments(value, true);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
103
166
|
}
|
|
104
167
|
|
|
105
168
|
function removeMora(data: FragmentData): void {
|
package/src/fragments.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {getString} from '@oscarpalmer/atoms/string';
|
|
2
|
+
import {array, type ReactiveArray} from '@oscarpalmer/mora';
|
|
3
|
+
import type {Fragment} from './fragment';
|
|
4
|
+
import {isFragment, isFragments} from './helpers';
|
|
5
|
+
import type {FragmentsState} from './models';
|
|
6
|
+
|
|
7
|
+
export class Fragments {
|
|
8
|
+
readonly #state: FragmentsState;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Fragment items
|
|
12
|
+
*/
|
|
13
|
+
get items(): ReactiveArray<Fragment> {
|
|
14
|
+
return this.#state.mapped;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
items: ReactiveArray<unknown>,
|
|
19
|
+
identify: (item: unknown) => unknown,
|
|
20
|
+
fragment: (item: unknown) => Fragment,
|
|
21
|
+
) {
|
|
22
|
+
Object.defineProperty(this, '$fragments', {
|
|
23
|
+
value: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
this.#state = {
|
|
27
|
+
fragment,
|
|
28
|
+
identify,
|
|
29
|
+
array: items,
|
|
30
|
+
instances: {},
|
|
31
|
+
mapped: array<Fragment>([]),
|
|
32
|
+
subscriber: undefined,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
states.set(this, this.#state);
|
|
36
|
+
|
|
37
|
+
initializeFragments(this.#state);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function handleFragments(
|
|
42
|
+
item: Fragments | FragmentsState,
|
|
43
|
+
remove: boolean,
|
|
44
|
+
): void {
|
|
45
|
+
const state = isFragments(item) ? states.get(item) : item;
|
|
46
|
+
|
|
47
|
+
if (state != null) {
|
|
48
|
+
(remove ? removeFragments : initializeFragments)(state);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
53
|
+
const keys = new Set<string>();
|
|
54
|
+
const mapped: Fragment[] = [];
|
|
55
|
+
|
|
56
|
+
const {length} = items;
|
|
57
|
+
|
|
58
|
+
for (let index = 0; index < length; index += 1) {
|
|
59
|
+
const item = items[index];
|
|
60
|
+
const identifier = state.identify(item);
|
|
61
|
+
|
|
62
|
+
if (identifier == null) {
|
|
63
|
+
throw new Error('Identifier cannot be null or undefined');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const key = getString(identifier);
|
|
67
|
+
|
|
68
|
+
if (keys.has(key)) {
|
|
69
|
+
throw new Error(`Duplicate identifier found: "${key}"`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let instance = state.instances[key];
|
|
73
|
+
|
|
74
|
+
if (instance == null) {
|
|
75
|
+
instance = state.fragment(item);
|
|
76
|
+
|
|
77
|
+
if (!isFragment(instance)) {
|
|
78
|
+
throw new Error('Fragment function must return a Fragment instance');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
instance.identify(key);
|
|
83
|
+
|
|
84
|
+
state.instances[key] = instance;
|
|
85
|
+
|
|
86
|
+
keys.add(key);
|
|
87
|
+
|
|
88
|
+
mapped.push(instance);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
state.mapped.set(mapped);
|
|
92
|
+
|
|
93
|
+
updateFragments(state, keys);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function initializeFragments(state: FragmentsState): void {
|
|
97
|
+
state.subscriber ??= state.array.subscribe(items => {
|
|
98
|
+
handleItems(state, items);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function removeFragments(state: FragmentsState): void {
|
|
103
|
+
state.subscriber?.();
|
|
104
|
+
|
|
105
|
+
updateFragments(state);
|
|
106
|
+
|
|
107
|
+
state.subscriber = undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function updateFragments(state: FragmentsState, active?: Set<string>): void {
|
|
111
|
+
const next: Record<string, Fragment> = {};
|
|
112
|
+
|
|
113
|
+
const previous = {...state.instances};
|
|
114
|
+
const keys = Object.keys(previous);
|
|
115
|
+
const {length} = keys;
|
|
116
|
+
|
|
117
|
+
for (let index = 0; index < length; index += 1) {
|
|
118
|
+
const key = keys[index];
|
|
119
|
+
|
|
120
|
+
if (active?.has(key)) {
|
|
121
|
+
next[key] = previous[key];
|
|
122
|
+
} else {
|
|
123
|
+
previous[key].remove();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
state.instances = next;
|
|
128
|
+
|
|
129
|
+
active?.clear();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//
|
|
133
|
+
|
|
134
|
+
const states: WeakMap<Fragments, FragmentsState> = new WeakMap();
|
package/src/helpers/dom.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function createNodes(value: unknown): ChildNode[] {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function removeNodes(nodes: ChildNode[]): void {
|
|
19
|
-
|
|
19
|
+
sanitizeNodes(nodes);
|
|
20
20
|
|
|
21
21
|
const {length} = nodes;
|
|
22
22
|
|
|
@@ -35,7 +35,7 @@ export function replaceNodes(from: ChildNode[], to: ChildNode[]): void {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function
|
|
38
|
+
function sanitizeNodes(nodes: ChildNode[]): void {
|
|
39
39
|
const {length} = nodes;
|
|
40
40
|
|
|
41
41
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -46,7 +46,7 @@ function sanitiseNodes(nodes: ChildNode[]): void {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
if (node.hasChildNodes()) {
|
|
49
|
-
|
|
49
|
+
sanitizeNodes([...node.childNodes] as ChildNode[]);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {Fragment} from '../fragment';
|
|
2
|
+
import type {Fragments} from '../fragments';
|
|
2
3
|
|
|
3
4
|
export function compareArrays(
|
|
4
5
|
first: unknown[],
|
|
@@ -27,3 +28,12 @@ export function isFragment(value: unknown): value is Fragment {
|
|
|
27
28
|
value.$fragment === true
|
|
28
29
|
);
|
|
29
30
|
}
|
|
31
|
+
|
|
32
|
+
export function isFragments(value: unknown): value is Fragments {
|
|
33
|
+
return (
|
|
34
|
+
typeof value === 'object' &&
|
|
35
|
+
value != null &&
|
|
36
|
+
'$fragments' in value &&
|
|
37
|
+
value.$fragments === true
|
|
38
|
+
);
|
|
39
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import '@oscarpalmer/mora';
|
|
2
|
+
import type {ReactiveArray} from '@oscarpalmer/mora';
|
|
2
3
|
import {Fragment} from './fragment';
|
|
4
|
+
import {Fragments} from './fragments';
|
|
5
|
+
|
|
6
|
+
export function fragments<Item>(
|
|
7
|
+
array: ReactiveArray<Item>,
|
|
8
|
+
identify: (item: Item) => unknown,
|
|
9
|
+
fragment: (item: Item) => Fragment,
|
|
10
|
+
): Fragments {
|
|
11
|
+
return new Fragments(
|
|
12
|
+
array as ReactiveArray<unknown>,
|
|
13
|
+
identify as never,
|
|
14
|
+
fragment as never,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
3
17
|
|
|
4
18
|
export function html(
|
|
5
19
|
strings: TemplateStringsArray,
|
|
6
20
|
...values: unknown[]
|
|
7
|
-
):
|
|
21
|
+
): unknown {
|
|
8
22
|
return new Fragment(strings, values);
|
|
9
23
|
}
|
|
10
24
|
|
|
11
25
|
export * from '@oscarpalmer/mora';
|
|
12
26
|
export type {Fragment} from './fragment';
|
|
27
|
+
export type {Fragments} from './fragments';
|
package/src/models.ts
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {Reactive} from '@oscarpalmer/mora';
|
|
1
|
+
import type {Reactive, ReactiveArray, Unsubscribe} from '@oscarpalmer/mora';
|
|
3
2
|
import type {Fragment} from './fragment';
|
|
4
3
|
|
|
4
|
+
export type FragmentConfiguration = {
|
|
5
|
+
identifier?: unknown;
|
|
6
|
+
ignoreCache?: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
5
9
|
export type FragmentData = {
|
|
6
10
|
expressions: unknown[];
|
|
7
|
-
identifier?: Key;
|
|
8
11
|
items: FragmentItem[];
|
|
9
12
|
mora: MoraData;
|
|
10
13
|
strings: TemplateStringsArray;
|
|
14
|
+
template?: string;
|
|
11
15
|
values: unknown[];
|
|
12
16
|
};
|
|
13
17
|
|
|
14
18
|
export type FragmentItem = {
|
|
15
19
|
fragments?: Fragment[];
|
|
16
|
-
nodes
|
|
20
|
+
nodes?: ChildNode[];
|
|
21
|
+
text?: Text;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type FragmentsState = {
|
|
25
|
+
array: ReactiveArray<unknown>;
|
|
26
|
+
fragment: (item: unknown) => Fragment;
|
|
27
|
+
identify: (item: unknown) => unknown;
|
|
28
|
+
instances: Record<string, Fragment>;
|
|
29
|
+
mapped: ReactiveArray<Fragment>;
|
|
30
|
+
subscriber: Unsubscribe | undefined;
|
|
17
31
|
};
|
|
18
32
|
|
|
19
33
|
type MoraData = {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {computed, isReactive} from '@oscarpalmer/mora';
|
|
3
|
+
import {isBooleanAttribute, setProperty} from '@oscarpalmer/toretto/attribute';
|
|
3
4
|
import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
|
|
5
|
+
import {EXPRESSION_COMMENT_FULL} from '../../constants';
|
|
4
6
|
import type {FragmentData} from '../../models';
|
|
5
7
|
import {mapEvent} from '../event';
|
|
6
8
|
import {setAttribute} from './value';
|
|
7
9
|
|
|
8
|
-
const commentExpression = /^<!--abydon\.(\d+)-->$/;
|
|
9
|
-
|
|
10
10
|
function getValue(data: FragmentData, original: string): unknown {
|
|
11
|
-
const matches =
|
|
11
|
+
const matches = EXPRESSION_COMMENT_FULL.exec(original ?? '');
|
|
12
12
|
|
|
13
13
|
return matches == null ? original : data.values[+matches[1]];
|
|
14
14
|
}
|
|
@@ -25,14 +25,23 @@ export function mapAttributes(
|
|
|
25
25
|
|
|
26
26
|
const actual = getValue(data, value);
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
switch (true) {
|
|
29
|
+
case name.startsWith('@'):
|
|
30
|
+
mapEvent(element, name, actual);
|
|
31
|
+
break;
|
|
32
|
+
|
|
33
|
+
case name.includes('.') ||
|
|
34
|
+
typeof actual === 'function' ||
|
|
35
|
+
isReactive(actual):
|
|
36
|
+
mapValue(data, element, name, actual);
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case isBooleanAttribute(name):
|
|
40
|
+
setProperty(element, name, value);
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
default:
|
|
44
|
+
break;
|
|
36
45
|
}
|
|
37
46
|
}
|
|
38
47
|
}
|
|
@@ -43,14 +52,10 @@ function mapValue(
|
|
|
43
52
|
name: string,
|
|
44
53
|
value: unknown,
|
|
45
54
|
): void {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
default:
|
|
52
|
-
setAttribute(data, element, name, value);
|
|
53
|
-
break;
|
|
55
|
+
if (typeof value === 'function') {
|
|
56
|
+
setComputedAttribute(data, element, name, value as GenericCallback);
|
|
57
|
+
} else {
|
|
58
|
+
setAttribute(data, element, name, value);
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|