@oscarpalmer/abydon 0.9.0 → 0.11.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/fragment.mjs +15 -6
- package/dist/helpers/dom.mjs +23 -6
- package/dist/helpers/index.mjs +11 -1
- package/dist/index.js +370 -66
- package/dist/node/attribute/index.mjs +4 -4
- package/dist/node/attribute/value.mjs +16 -16
- package/dist/node/event.mjs +22 -2
- package/dist/node/index.mjs +4 -4
- package/dist/node/value.mjs +83 -17
- package/package.json +3 -3
- package/src/fragment.ts +26 -11
- package/src/helpers/dom.ts +29 -6
- package/src/helpers/index.ts +19 -0
- package/src/models.ts +3 -1
- package/src/node/attribute/index.ts +25 -17
- package/src/node/attribute/value.ts +16 -19
- package/src/node/event.ts +28 -6
- package/src/node/index.ts +5 -4
- package/src/node/value.ts +154 -22
- package/types/fragment.d.ts +5 -1
- package/types/helpers/dom.d.ts +1 -0
- package/types/helpers/index.d.ts +1 -0
- package/types/index.d.cts +6 -1
- package/types/models.d.ts +3 -1
- package/types/node/event.d.ts +1 -0
package/dist/node/event.mjs
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
// src/node/event.ts
|
|
2
|
+
function getController(element) {
|
|
3
|
+
let controller = controllers.get(element);
|
|
4
|
+
if (controller == null) {
|
|
5
|
+
controller = new AbortController;
|
|
6
|
+
controllers.set(element, controller);
|
|
7
|
+
}
|
|
8
|
+
return controller;
|
|
9
|
+
}
|
|
2
10
|
function getOptions(options) {
|
|
3
11
|
const parts = options.split(":");
|
|
4
12
|
return {
|
|
@@ -9,11 +17,23 @@ function getOptions(options) {
|
|
|
9
17
|
}
|
|
10
18
|
function mapEvent(element, name, value) {
|
|
11
19
|
element.removeAttribute(name);
|
|
12
|
-
const [, type, options] =
|
|
20
|
+
const [, type, options] = eventNamePattern.exec(name) ?? [];
|
|
13
21
|
if (typeof value === "function" && type != null) {
|
|
14
|
-
element.addEventListener(type, value,
|
|
22
|
+
element.addEventListener(type, value, {
|
|
23
|
+
...getOptions(options ?? ""),
|
|
24
|
+
signal: getController(element).signal
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function removeEvents(element) {
|
|
29
|
+
if (controllers.has(element)) {
|
|
30
|
+
controllers.get(element)?.abort();
|
|
31
|
+
controllers.delete(element);
|
|
15
32
|
}
|
|
16
33
|
}
|
|
34
|
+
var controllers = new WeakMap;
|
|
35
|
+
var eventNamePattern = /^@([\w-]+)(?::([a-z:]+))?$/i;
|
|
17
36
|
export {
|
|
37
|
+
removeEvents,
|
|
18
38
|
mapEvent
|
|
19
39
|
};
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/node/index.ts
|
|
2
|
-
import {isReactive} from "@oscarpalmer/sentinel";
|
|
2
|
+
import {computed, isReactive} from "@oscarpalmer/sentinel";
|
|
3
3
|
import {isFragment, isProperElement} from "../helpers";
|
|
4
4
|
import {createNodes} from "../helpers/dom";
|
|
5
5
|
import {mapAttributes} from "./attribute";
|
|
@@ -30,8 +30,8 @@ function mapNodes(data, nodes) {
|
|
|
30
30
|
function mapValue(data, comment, value2) {
|
|
31
31
|
switch (true) {
|
|
32
32
|
case typeof value2 === "function":
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
setReactiveNode(data, comment, computed(value2));
|
|
34
|
+
break;
|
|
35
35
|
case isReactive(value2):
|
|
36
36
|
setReactiveNode(data, comment, value2);
|
|
37
37
|
break;
|
|
@@ -44,7 +44,7 @@ function replaceComment(data, comment, value2) {
|
|
|
44
44
|
const item = data.items.find((item2) => item2.nodes.includes(comment));
|
|
45
45
|
const nodes = createNodes(value2);
|
|
46
46
|
if (item != null) {
|
|
47
|
-
item.
|
|
47
|
+
item.fragments = isFragment(value2) ? [value2] : undefined;
|
|
48
48
|
item.nodes = nodes;
|
|
49
49
|
}
|
|
50
50
|
comment.replaceWith(...nodes);
|
package/dist/node/value.mjs
CHANGED
|
@@ -2,10 +2,68 @@
|
|
|
2
2
|
import {isNullableOrWhitespace} from "@oscarpalmer/atoms/is";
|
|
3
3
|
import {getString} from "@oscarpalmer/atoms/string";
|
|
4
4
|
import {effect} from "@oscarpalmer/sentinel";
|
|
5
|
-
import {isFragment, isProperNode} from "../helpers";
|
|
5
|
+
import {compareArrays, isFragment, isProperNode} from "../helpers";
|
|
6
6
|
import {createNodes, replaceNodes} from "../helpers/dom";
|
|
7
|
-
function
|
|
8
|
-
|
|
7
|
+
function removeFragments(fragments) {
|
|
8
|
+
if (fragments != null) {
|
|
9
|
+
const { length } = fragments;
|
|
10
|
+
for (let index = 0;index < length; index += 1) {
|
|
11
|
+
fragments[index].remove();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function setArray(fragments, nodes, comment, text, value) {
|
|
16
|
+
if (value.length === 0) {
|
|
17
|
+
return {
|
|
18
|
+
nodes: setText(fragments, nodes, comment, text, value)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
let templates = value.filter((item) => isFragment(item) && item.identifier != null);
|
|
22
|
+
const identifiers = templates.map((fragment) => fragment.identifier);
|
|
23
|
+
const oldIdentifiers = fragments?.map((fragment) => fragment.identifier) ?? [];
|
|
24
|
+
if (new Set(identifiers).size !== templates.length) {
|
|
25
|
+
templates = [];
|
|
26
|
+
}
|
|
27
|
+
const noTemplates = templates.length === 0;
|
|
28
|
+
if (noTemplates || nodes == null || oldIdentifiers.some((identifier) => identifier == null)) {
|
|
29
|
+
return {
|
|
30
|
+
fragments: noTemplates ? undefined : templates,
|
|
31
|
+
nodes: setNodes(fragments, nodes, comment, text, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const next = templates.map((template) => fragments?.find((fragment) => fragment.identifier === template.identifier) ?? template);
|
|
35
|
+
const comparison = compareArrays(fragments ?? [], templates);
|
|
36
|
+
if (comparison !== "removed") {
|
|
37
|
+
let position = nodes[0];
|
|
38
|
+
const before = comparison === "added" && !oldIdentifiers.includes(templates[0].identifier);
|
|
39
|
+
const items = next.flatMap((fragment) => fragment.get().flatMap((node) => ({
|
|
40
|
+
identifier: fragment.identifier,
|
|
41
|
+
value: node
|
|
42
|
+
})));
|
|
43
|
+
const { length: length2 } = items;
|
|
44
|
+
for (let index = 0;index < length2; index += 1) {
|
|
45
|
+
const item = items[index];
|
|
46
|
+
if (comparison === "dissimilar" || !oldIdentifiers.includes(item.identifier)) {
|
|
47
|
+
if (index === 0 && before) {
|
|
48
|
+
position.before(item.value);
|
|
49
|
+
} else {
|
|
50
|
+
position.after(item.value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
position = item.value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const toRemove = fragments?.filter((fragment) => !identifiers.includes(fragment.identifier)) ?? [];
|
|
57
|
+
const { length } = toRemove;
|
|
58
|
+
for (let index = 0;index < length; index += 1) {
|
|
59
|
+
toRemove[index].remove();
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
fragments: next,
|
|
63
|
+
nodes: next.flatMap((fragment) => fragment.get())
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function setNodes(fragments, nodes, comment, text, next) {
|
|
9
67
|
if (nodes == null) {
|
|
10
68
|
if (comment.parentNode != null) {
|
|
11
69
|
replaceNodes([comment], next);
|
|
@@ -15,42 +73,50 @@ function setNodes(nodes, comment, text, value) {
|
|
|
15
73
|
} else {
|
|
16
74
|
replaceNodes(nodes, next);
|
|
17
75
|
}
|
|
76
|
+
removeFragments(fragments);
|
|
18
77
|
return next;
|
|
19
78
|
}
|
|
20
79
|
function setReactiveNode(data, comment, reactive) {
|
|
21
80
|
const item = data.items.find((item2) => item2.nodes.includes(comment));
|
|
22
81
|
const text = new Text;
|
|
82
|
+
let fragments;
|
|
23
83
|
let nodes;
|
|
24
84
|
effect(() => {
|
|
25
85
|
const value = reactive.get();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
const result = setArray(fragments, nodes, comment, text, value);
|
|
88
|
+
fragments = typeof result === "boolean" ? undefined : result?.fragments;
|
|
89
|
+
nodes = typeof result === "boolean" ? result ? [text] : undefined : result?.nodes;
|
|
29
90
|
} else {
|
|
30
|
-
|
|
91
|
+
const valueIsFragment = isFragment(value);
|
|
92
|
+
fragments = valueIsFragment ? [value] : undefined;
|
|
93
|
+
if (valueIsFragment || isProperNode(value)) {
|
|
94
|
+
nodes = setNodes(fragments, nodes, comment, text, createNodes(value));
|
|
95
|
+
} else {
|
|
96
|
+
nodes = setText(fragments, nodes, comment, text, value);
|
|
97
|
+
}
|
|
31
98
|
}
|
|
32
99
|
if (item != null) {
|
|
33
|
-
item.
|
|
100
|
+
item.fragments = fragments;
|
|
34
101
|
item.nodes = [...nodes ?? [comment]];
|
|
35
102
|
}
|
|
36
103
|
});
|
|
37
104
|
}
|
|
38
|
-
function setText(nodes, comment, text, value) {
|
|
105
|
+
function setText(fragments, nodes, comment, text, value) {
|
|
39
106
|
const isNullable = isNullableOrWhitespace(value);
|
|
40
107
|
text.textContent = isNullable ? "" : getString(value);
|
|
108
|
+
let result = false;
|
|
41
109
|
if (nodes != null) {
|
|
42
110
|
replaceNodes(nodes, [isNullable ? comment : text]);
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
if (isNullable && comment.parentNode == null) {
|
|
111
|
+
result = !isNullable;
|
|
112
|
+
} else if (isNullable && comment.parentNode == null) {
|
|
46
113
|
text.replaceWith(comment);
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
if (!isNullable && text.parentNode == null) {
|
|
114
|
+
} else if (!isNullable && text.parentNode == null) {
|
|
50
115
|
comment.replaceWith(text);
|
|
51
|
-
|
|
116
|
+
result = true;
|
|
52
117
|
}
|
|
53
|
-
|
|
118
|
+
removeFragments(fragments);
|
|
119
|
+
return result ? [text] : undefined;
|
|
54
120
|
}
|
|
55
121
|
export {
|
|
56
122
|
setReactiveNode
|
package/package.json
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@biomejs/biome": "^1.8.3",
|
|
13
|
-
"@types/bun": "^1.1.
|
|
14
|
-
"bun": "^1.1.
|
|
13
|
+
"@types/bun": "^1.1.8",
|
|
14
|
+
"bun": "^1.1.27",
|
|
15
15
|
"dts-bundle-generator": "^9.5.1",
|
|
16
16
|
"typescript": "^5.5.4"
|
|
17
17
|
},
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
},
|
|
48
48
|
"type": "module",
|
|
49
49
|
"types": "types/index.d.ts",
|
|
50
|
-
"version": "0.
|
|
50
|
+
"version": "0.11.0"
|
|
51
51
|
}
|
package/src/fragment.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import type {Key} from '@oscarpalmer/atoms/models';
|
|
1
2
|
import {html} from '@oscarpalmer/toretto/html';
|
|
2
3
|
import {parse} from './html';
|
|
3
4
|
import type {FragmentData, ProperNode} from './models';
|
|
4
5
|
import {mapNodes} from './node';
|
|
6
|
+
import {removeNodes} from './helpers/dom';
|
|
5
7
|
|
|
6
8
|
export class Fragment {
|
|
7
9
|
private readonly $fragment = true;
|
|
8
10
|
private readonly data: FragmentData;
|
|
9
11
|
|
|
12
|
+
get identifier(): Key | undefined {
|
|
13
|
+
return this.data.identifier;
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
constructor(strings: TemplateStringsArray, expressions: unknown[]) {
|
|
11
17
|
this.data = {
|
|
12
18
|
expressions,
|
|
@@ -26,7 +32,7 @@ export class Fragment {
|
|
|
26
32
|
/**
|
|
27
33
|
* Gets a list of the fragment's nodes
|
|
28
34
|
*/
|
|
29
|
-
get():
|
|
35
|
+
get(): ProperNode[] {
|
|
30
36
|
if (this.data.items.length === 0) {
|
|
31
37
|
const parsed = parse(this.data);
|
|
32
38
|
|
|
@@ -45,18 +51,26 @@ export class Fragment {
|
|
|
45
51
|
mapNodes(
|
|
46
52
|
this.data,
|
|
47
53
|
this.data.items.flatMap(
|
|
48
|
-
item =>
|
|
49
|
-
|
|
54
|
+
item =>
|
|
55
|
+
item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes,
|
|
56
|
+
),
|
|
50
57
|
);
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
return [
|
|
54
|
-
...
|
|
55
|
-
item =>
|
|
56
|
-
|
|
61
|
+
...this.data.items.flatMap(
|
|
62
|
+
item =>
|
|
63
|
+
item.fragments?.flatMap(fragment => fragment.get()) ?? item.nodes,
|
|
64
|
+
),
|
|
57
65
|
];
|
|
58
66
|
}
|
|
59
67
|
|
|
68
|
+
identify(identifier: Key): Fragment {
|
|
69
|
+
this.data.identifier = identifier;
|
|
70
|
+
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
60
74
|
/**
|
|
61
75
|
* Removes the fragment from the DOM
|
|
62
76
|
*/
|
|
@@ -64,13 +78,14 @@ export class Fragment {
|
|
|
64
78
|
const {length} = this.data.items;
|
|
65
79
|
|
|
66
80
|
for (let index = 0; index < length; index += 1) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
item.fragment?.remove();
|
|
81
|
+
const {fragments, nodes} = this.data.items[index];
|
|
82
|
+
const {length} = fragments ?? [];
|
|
70
83
|
|
|
71
|
-
for (
|
|
72
|
-
|
|
84
|
+
for (let index = 0; index < length; index += 1) {
|
|
85
|
+
fragments?.[index]?.remove();
|
|
73
86
|
}
|
|
87
|
+
|
|
88
|
+
removeNodes(nodes);
|
|
74
89
|
}
|
|
75
90
|
|
|
76
91
|
this.data.items.splice(0, length);
|
package/src/helpers/dom.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
2
2
|
import type {ProperNode} from '../models';
|
|
3
|
-
import {isFragment, isProperNode} from './index';
|
|
3
|
+
import {isFragment, isProperElement, isProperNode} from './index';
|
|
4
|
+
import {removeEvents} from '../node/event';
|
|
4
5
|
|
|
5
6
|
export function createNodes(value: unknown): ProperNode[] {
|
|
6
7
|
if (isFragment(value)) {
|
|
@@ -14,16 +15,38 @@ export function createNodes(value: unknown): ProperNode[] {
|
|
|
14
15
|
return [new Text(getString(value))];
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
export function removeNodes(nodes: ProperNode[]): void {
|
|
19
|
+
sanitiseNodes(nodes);
|
|
20
|
+
|
|
21
|
+
const {length} = nodes;
|
|
22
|
+
|
|
23
|
+
for (let index = 0; index < length; index += 1) {
|
|
24
|
+
nodes[index].remove();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
export function replaceNodes(from: ProperNode[], to: ProperNode[]): void {
|
|
29
|
+
from[0]?.replaceWith(...to);
|
|
30
|
+
|
|
18
31
|
const {length} = from;
|
|
19
32
|
|
|
33
|
+
for (let index = 1; index < length; index += 1) {
|
|
34
|
+
from[index].remove();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sanitiseNodes(nodes: ProperNode[]): void {
|
|
39
|
+
const {length} = nodes;
|
|
40
|
+
|
|
20
41
|
for (let index = 0; index < length; index += 1) {
|
|
21
|
-
const node =
|
|
42
|
+
const node = nodes[index];
|
|
43
|
+
|
|
44
|
+
if (isProperElement(node)) {
|
|
45
|
+
removeEvents(node);
|
|
46
|
+
}
|
|
22
47
|
|
|
23
|
-
if (
|
|
24
|
-
node.
|
|
25
|
-
} else {
|
|
26
|
-
node.remove();
|
|
48
|
+
if (node.hasChildNodes()) {
|
|
49
|
+
sanitiseNodes([...node.childNodes] as ProperNode[]);
|
|
27
50
|
}
|
|
28
51
|
}
|
|
29
52
|
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import type {Fragment} from '../fragment';
|
|
2
2
|
import type {ProperElement, ProperNode} from '../models';
|
|
3
3
|
|
|
4
|
+
export function compareArrays(
|
|
5
|
+
first: unknown[],
|
|
6
|
+
second: unknown[],
|
|
7
|
+
): 'added' | 'dissimilar' | 'removed' {
|
|
8
|
+
const firstIsLarger = first.length > second.length;
|
|
9
|
+
const from = firstIsLarger ? first : second;
|
|
10
|
+
const to = firstIsLarger ? second : first;
|
|
11
|
+
|
|
12
|
+
if (
|
|
13
|
+
!from
|
|
14
|
+
.filter(key => to.includes(key))
|
|
15
|
+
.every((key, index) => to[index] === key)
|
|
16
|
+
) {
|
|
17
|
+
return 'dissimilar';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return firstIsLarger ? 'removed' : 'added';
|
|
21
|
+
}
|
|
22
|
+
|
|
4
23
|
export function isFragment(value: unknown): value is Fragment {
|
|
5
24
|
return (
|
|
6
25
|
typeof value === 'object' &&
|
package/src/models.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import type {Key} from '@oscarpalmer/atoms/models';
|
|
1
2
|
import type {Fragment} from './fragment';
|
|
2
3
|
|
|
3
4
|
export type FragmentData = {
|
|
4
5
|
expressions: unknown[];
|
|
6
|
+
identifier?: Key;
|
|
5
7
|
items: FragmentItem[];
|
|
6
8
|
strings: TemplateStringsArray;
|
|
7
9
|
values: unknown[];
|
|
8
10
|
};
|
|
9
11
|
|
|
10
12
|
export type FragmentItem = {
|
|
11
|
-
|
|
13
|
+
fragments?: Fragment[];
|
|
12
14
|
nodes: ProperNode[];
|
|
13
15
|
};
|
|
14
16
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {computed, isReactive} from '@oscarpalmer/sentinel';
|
|
2
3
|
import type {FragmentData, ProperElement} from '../../models';
|
|
3
4
|
import {mapEvent} from '../event';
|
|
4
5
|
import {setAttribute} from './value';
|
|
@@ -11,28 +12,35 @@ function getValue(data: FragmentData, original: string): unknown {
|
|
|
11
12
|
return matches == null ? original : data.values[+matches[1]];
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export function mapAttributes(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
15
|
+
export function mapAttributes(
|
|
16
|
+
data: FragmentData,
|
|
17
|
+
element: ProperElement,
|
|
18
|
+
): void {
|
|
19
|
+
const attributes = [...element.attributes];
|
|
20
|
+
const {length} = attributes;
|
|
21
|
+
|
|
22
|
+
for (let index = 0; index < length; index += 1) {
|
|
23
|
+
const {name, value} = attributes[index];
|
|
24
|
+
|
|
25
|
+
const actual = getValue(data, value);
|
|
26
|
+
|
|
27
|
+
if (name.startsWith('@')) {
|
|
28
|
+
mapEvent(element, name, actual);
|
|
29
|
+
} else if (
|
|
30
|
+
name.includes('.') ||
|
|
31
|
+
typeof value === 'function' ||
|
|
32
|
+
isReactive(actual)
|
|
33
|
+
) {
|
|
34
|
+
mapValue(element, name, actual);
|
|
28
35
|
}
|
|
29
36
|
}
|
|
37
|
+
}
|
|
30
38
|
|
|
31
39
|
function mapValue(element: ProperElement, name: string, value: unknown): void {
|
|
32
40
|
switch (true) {
|
|
33
41
|
case typeof value === 'function':
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
setAttribute(element, name, computed(value as GenericCallback));
|
|
43
|
+
break;
|
|
36
44
|
|
|
37
45
|
default:
|
|
38
46
|
setAttribute(element, name, value);
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
} from '@oscarpalmer/toretto/attribute';
|
|
8
8
|
import type {ProperElement} from '../../models';
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const classExpression = /^class\./;
|
|
11
|
+
const styleFullExpression = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
|
|
12
|
+
const stylePrefixExpression = /^style\./;
|
|
12
13
|
|
|
13
14
|
export function setAttribute(
|
|
14
15
|
element: ProperElement,
|
|
@@ -18,11 +19,11 @@ export function setAttribute(
|
|
|
18
19
|
element.removeAttribute(name);
|
|
19
20
|
|
|
20
21
|
switch (true) {
|
|
21
|
-
case
|
|
22
|
+
case classExpression.test(name):
|
|
22
23
|
setClasses(element, name, value);
|
|
23
24
|
return;
|
|
24
25
|
|
|
25
|
-
case
|
|
26
|
+
case stylePrefixExpression.test(name):
|
|
26
27
|
setStyle(element, name, value);
|
|
27
28
|
return;
|
|
28
29
|
|
|
@@ -38,7 +39,7 @@ function setClasses(
|
|
|
38
39
|
value: unknown,
|
|
39
40
|
): void {
|
|
40
41
|
function update(value: unknown): void {
|
|
41
|
-
if (
|
|
42
|
+
if (value === true) {
|
|
42
43
|
element.classList.add(...classes);
|
|
43
44
|
} else {
|
|
44
45
|
element.classList.remove(...classes);
|
|
@@ -68,18 +69,16 @@ function setStyle(element: ProperElement, name: string, value: unknown): void {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
const [, property, unit] =
|
|
72
|
+
const [, property, unit] = styleFullExpression.exec(name) ?? [];
|
|
72
73
|
|
|
73
|
-
if (property
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
update(value
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
update(value);
|
|
74
|
+
if (property != null) {
|
|
75
|
+
if (isReactive(value)) {
|
|
76
|
+
effect(() => {
|
|
77
|
+
update(value.get());
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
update(value);
|
|
81
|
+
}
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -111,9 +110,7 @@ function updateProperty(
|
|
|
111
110
|
name: string,
|
|
112
111
|
value: unknown,
|
|
113
112
|
): void {
|
|
114
|
-
(element as unknown as PlainObject)[name] =
|
|
115
|
-
getString(value),
|
|
116
|
-
);
|
|
113
|
+
(element as unknown as PlainObject)[name] = value === true;
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
function updateSelected(
|
package/src/node/event.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type {ProperElement} from '../models';
|
|
2
2
|
|
|
3
|
+
const controllers = new WeakMap<ProperElement, AbortController>();
|
|
4
|
+
|
|
5
|
+
const eventNamePattern = /^@([\w-]+)(?::([a-z:]+))?$/i;
|
|
6
|
+
|
|
7
|
+
function getController(element: ProperElement): AbortController {
|
|
8
|
+
let controller = controllers.get(element);
|
|
9
|
+
|
|
10
|
+
if (controller == null) {
|
|
11
|
+
controller = new AbortController();
|
|
12
|
+
|
|
13
|
+
controllers.set(element, controller);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return controller;
|
|
17
|
+
}
|
|
18
|
+
|
|
3
19
|
function getOptions(options: string): AddEventListenerOptions {
|
|
4
20
|
const parts = options.split(':');
|
|
5
21
|
|
|
@@ -17,13 +33,19 @@ export function mapEvent(
|
|
|
17
33
|
): void {
|
|
18
34
|
element.removeAttribute(name);
|
|
19
35
|
|
|
20
|
-
const [, type, options] =
|
|
36
|
+
const [, type, options] = eventNamePattern.exec(name) ?? [];
|
|
21
37
|
|
|
22
38
|
if (typeof value === 'function' && type != null) {
|
|
23
|
-
element.addEventListener(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
element.addEventListener(type, value as EventListener, {
|
|
40
|
+
...getOptions(options ?? ''),
|
|
41
|
+
signal: getController(element).signal,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function removeEvents(element: ProperElement): void {
|
|
47
|
+
if (controllers.has(element)) {
|
|
48
|
+
controllers.get(element)?.abort();
|
|
49
|
+
controllers.delete(element);
|
|
28
50
|
}
|
|
29
51
|
}
|
package/src/node/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {computed, isReactive} from '@oscarpalmer/sentinel';
|
|
2
3
|
import {isFragment, isProperElement} from '../helpers';
|
|
3
4
|
import {createNodes} from '../helpers/dom';
|
|
4
5
|
import type {FragmentData, ProperNode} from '../models';
|
|
@@ -41,8 +42,8 @@ export function mapNodes(data: FragmentData, nodes: ProperNode[]): void {
|
|
|
41
42
|
function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
|
|
42
43
|
switch (true) {
|
|
43
44
|
case typeof value === 'function':
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
setReactiveNode(data, comment, computed(value as GenericCallback));
|
|
46
|
+
break;
|
|
46
47
|
|
|
47
48
|
case isReactive(value):
|
|
48
49
|
setReactiveNode(data, comment, value);
|
|
@@ -63,7 +64,7 @@ function replaceComment(
|
|
|
63
64
|
const nodes = createNodes(value);
|
|
64
65
|
|
|
65
66
|
if (item != null) {
|
|
66
|
-
item.
|
|
67
|
+
item.fragments = isFragment(value) ? [value] : undefined;
|
|
67
68
|
item.nodes = nodes;
|
|
68
69
|
}
|
|
69
70
|
|