@oscarpalmer/abydon 0.7.0 → 0.9.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 +49 -0
- package/dist/helpers/dom.mjs +27 -0
- package/dist/helpers/index.mjs +15 -0
- package/dist/html.mjs +36 -0
- package/dist/index.js +398 -327
- package/dist/index.mjs +5 -0
- package/dist/models.mjs +0 -0
- package/dist/node/attribute/index.mjs +35 -0
- package/dist/node/attribute/value.mjs +89 -0
- package/dist/node/event.mjs +19 -0
- package/dist/node/index.mjs +55 -0
- package/dist/node/value.mjs +57 -0
- package/package.json +20 -9
- package/src/fragment.ts +68 -41
- package/src/helpers/dom.ts +8 -36
- package/src/helpers/index.ts +5 -8
- package/src/html.ts +4 -4
- package/src/index.ts +1 -1
- package/src/models.ts +9 -8
- package/src/node/attribute/index.ts +13 -34
- package/src/node/attribute/value.ts +23 -50
- package/src/node/event.ts +3 -3
- package/src/node/index.ts +27 -11
- package/src/node/value.ts +69 -30
- package/types/fragment.d.ts +17 -2
- package/types/helpers/dom.d.ts +3 -5
- package/types/helpers/index.d.ts +4 -3
- package/types/html.d.ts +2 -1
- package/types/index.d.cts +22 -0
- package/types/index.d.ts +1 -1
- package/types/models.d.ts +8 -8
- package/types/node/attribute/index.d.ts +2 -3
- package/types/node/attribute/value.d.ts +2 -2
- package/types/node/event.d.ts +2 -2
- package/types/node/index.d.ts +2 -2
- package/types/node/value.d.ts +2 -1
- package/.bun.ts +0 -32
- package/.gitattributes +0 -1
- package/biome.json +0 -22
- package/bun.lockb +0 -0
- package/tsconfig.json +0 -23
package/dist/index.mjs
ADDED
package/dist/models.mjs
ADDED
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/node/attribute/index.ts
|
|
2
|
+
import {isReactive} from "@oscarpalmer/sentinel";
|
|
3
|
+
import {mapEvent} from "../event";
|
|
4
|
+
import {setAttribute} from "./value";
|
|
5
|
+
function getValue(data, original) {
|
|
6
|
+
const matches = commentExpression.exec(original ?? "");
|
|
7
|
+
return matches == null ? original : data.values[+matches[1]];
|
|
8
|
+
}
|
|
9
|
+
function mapAttributes(data, element) {
|
|
10
|
+
const attributes = [...element.attributes];
|
|
11
|
+
const { length } = attributes;
|
|
12
|
+
for (let index = 0;index < length; index += 1) {
|
|
13
|
+
const { name, value: value2 } = attributes[index];
|
|
14
|
+
const actual = getValue(data, value2);
|
|
15
|
+
if (name.startsWith("@")) {
|
|
16
|
+
mapEvent(element, name, actual);
|
|
17
|
+
} else if (name.includes(".") || isReactive(actual)) {
|
|
18
|
+
mapValue(element, name, actual);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function mapValue(element, name, value2) {
|
|
23
|
+
switch (true) {
|
|
24
|
+
case typeof value2 === "function":
|
|
25
|
+
mapValue(element, name, value2());
|
|
26
|
+
return;
|
|
27
|
+
default:
|
|
28
|
+
setAttribute(element, name, value2);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
var commentExpression = /^<!--abydon\.(\d+)-->$/;
|
|
33
|
+
export {
|
|
34
|
+
mapAttributes
|
|
35
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/node/attribute/value.ts
|
|
2
|
+
import {getString} from "@oscarpalmer/atoms/string";
|
|
3
|
+
import {effect, isReactive} from "@oscarpalmer/sentinel";
|
|
4
|
+
import {
|
|
5
|
+
isBooleanAttribute,
|
|
6
|
+
setAttribute as setAttr
|
|
7
|
+
} from "@oscarpalmer/toretto/attribute";
|
|
8
|
+
function setAttribute(element, name, value) {
|
|
9
|
+
element.removeAttribute(name);
|
|
10
|
+
switch (true) {
|
|
11
|
+
case classPattern.test(name):
|
|
12
|
+
setClasses(element, name, value);
|
|
13
|
+
return;
|
|
14
|
+
case stylePattern.test(name):
|
|
15
|
+
setStyle(element, name, value);
|
|
16
|
+
return;
|
|
17
|
+
default:
|
|
18
|
+
setValue(element, name, value);
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function setClasses(element, name, value) {
|
|
23
|
+
function update(value2) {
|
|
24
|
+
if (/^(|true)$/.test(getString(value2))) {
|
|
25
|
+
element.classList.add(...classes);
|
|
26
|
+
} else {
|
|
27
|
+
element.classList.remove(...classes);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const classes = name.slice(6).split(".");
|
|
31
|
+
if (isReactive(value)) {
|
|
32
|
+
effect(() => {
|
|
33
|
+
update(value.get());
|
|
34
|
+
});
|
|
35
|
+
} else {
|
|
36
|
+
update(value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function setStyle(element, name, value) {
|
|
40
|
+
function update(value2) {
|
|
41
|
+
if (value2 == null || value2 === false || value2 === true && unit == null) {
|
|
42
|
+
element.style.removeProperty(property);
|
|
43
|
+
} else {
|
|
44
|
+
element.style.setProperty(property, value2 === true ? unit : getString(value2));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const [, property, unit] = /^\w+\.([a-z-]+)(?:\.(\w+))?$/i.exec(name) ?? [];
|
|
48
|
+
if (property == null) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (isReactive(value)) {
|
|
52
|
+
effect(() => {
|
|
53
|
+
update(value.get());
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
update(value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function setValue(element, name, value) {
|
|
60
|
+
const callback = isBooleanAttribute(name) && name in element ? name === "selected" ? updateSelected : updateProperty : setAttr;
|
|
61
|
+
if (isReactive(value)) {
|
|
62
|
+
effect(() => {
|
|
63
|
+
callback(element, name, value.get());
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
callback(element, name, value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function triggerSelectChange(select) {
|
|
70
|
+
if (select != null) {
|
|
71
|
+
select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function updateProperty(element, name, value) {
|
|
75
|
+
element[name] = /^(|true)$/.test(getString(value));
|
|
76
|
+
}
|
|
77
|
+
function updateSelected(element, name, value) {
|
|
78
|
+
const select = element.closest("select");
|
|
79
|
+
const options = [...select?.options ?? []];
|
|
80
|
+
if (select != null && options.includes(element)) {
|
|
81
|
+
triggerSelectChange(select);
|
|
82
|
+
}
|
|
83
|
+
updateProperty(element, name, value);
|
|
84
|
+
}
|
|
85
|
+
var classPattern = /^class\./;
|
|
86
|
+
var stylePattern = /^style\./;
|
|
87
|
+
export {
|
|
88
|
+
setAttribute
|
|
89
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/node/event.ts
|
|
2
|
+
function getOptions(options) {
|
|
3
|
+
const parts = options.split(":");
|
|
4
|
+
return {
|
|
5
|
+
capture: parts.includes("c") || parts.includes("capture"),
|
|
6
|
+
once: parts.includes("o") || parts.includes("once"),
|
|
7
|
+
passive: !parts.includes("a") && !parts.includes("active")
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function mapEvent(element, name, value) {
|
|
11
|
+
element.removeAttribute(name);
|
|
12
|
+
const [, type, options] = /^@(\w+)(?::([a-z:]+))?$/.exec(name) ?? [];
|
|
13
|
+
if (typeof value === "function" && type != null) {
|
|
14
|
+
element.addEventListener(type, value, getOptions(options ?? ""));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
mapEvent
|
|
19
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/node/index.ts
|
|
2
|
+
import {isReactive} from "@oscarpalmer/sentinel";
|
|
3
|
+
import {isFragment, isProperElement} from "../helpers";
|
|
4
|
+
import {createNodes} from "../helpers/dom";
|
|
5
|
+
import {mapAttributes} from "./attribute";
|
|
6
|
+
import {setReactiveNode} from "./value";
|
|
7
|
+
function mapNode(data, comment) {
|
|
8
|
+
const matches = commentExpression.exec(comment.textContent ?? "");
|
|
9
|
+
const value2 = matches == null ? null : data.values[+matches[1]];
|
|
10
|
+
if (value2 != null) {
|
|
11
|
+
mapValue(data, comment, value2);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function mapNodes(data, nodes) {
|
|
15
|
+
const { length } = nodes;
|
|
16
|
+
for (let index = 0;index < length; index += 1) {
|
|
17
|
+
const node = nodes[index];
|
|
18
|
+
if (node instanceof Comment) {
|
|
19
|
+
mapNode(data, node);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (isProperElement(node)) {
|
|
23
|
+
mapAttributes(data, node);
|
|
24
|
+
}
|
|
25
|
+
if (node.hasChildNodes()) {
|
|
26
|
+
mapNodes(data, [...node.childNodes]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function mapValue(data, comment, value2) {
|
|
31
|
+
switch (true) {
|
|
32
|
+
case typeof value2 === "function":
|
|
33
|
+
mapValue(data, comment, value2());
|
|
34
|
+
return;
|
|
35
|
+
case isReactive(value2):
|
|
36
|
+
setReactiveNode(data, comment, value2);
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
replaceComment(data, comment, value2);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function replaceComment(data, comment, value2) {
|
|
44
|
+
const item = data.items.find((item2) => item2.nodes.includes(comment));
|
|
45
|
+
const nodes = createNodes(value2);
|
|
46
|
+
if (item != null) {
|
|
47
|
+
item.fragment = isFragment(value2) ? value2 : undefined;
|
|
48
|
+
item.nodes = nodes;
|
|
49
|
+
}
|
|
50
|
+
comment.replaceWith(...nodes);
|
|
51
|
+
}
|
|
52
|
+
var commentExpression = /^abydon\.(\d+)$/;
|
|
53
|
+
export {
|
|
54
|
+
mapNodes
|
|
55
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/node/value.ts
|
|
2
|
+
import {isNullableOrWhitespace} from "@oscarpalmer/atoms/is";
|
|
3
|
+
import {getString} from "@oscarpalmer/atoms/string";
|
|
4
|
+
import {effect} from "@oscarpalmer/sentinel";
|
|
5
|
+
import {isFragment, isProperNode} from "../helpers";
|
|
6
|
+
import {createNodes, replaceNodes} from "../helpers/dom";
|
|
7
|
+
function setNodes(nodes, comment, text, value) {
|
|
8
|
+
const next = createNodes(value);
|
|
9
|
+
if (nodes == null) {
|
|
10
|
+
if (comment.parentNode != null) {
|
|
11
|
+
replaceNodes([comment], next);
|
|
12
|
+
} else if (text.parentNode != null) {
|
|
13
|
+
replaceNodes([text], next);
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
replaceNodes(nodes, next);
|
|
17
|
+
}
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
function setReactiveNode(data, comment, reactive) {
|
|
21
|
+
const item = data.items.find((item2) => item2.nodes.includes(comment));
|
|
22
|
+
const text = new Text;
|
|
23
|
+
let nodes;
|
|
24
|
+
effect(() => {
|
|
25
|
+
const value = reactive.get();
|
|
26
|
+
const valueIsFragment = isFragment(value);
|
|
27
|
+
if (valueIsFragment || isProperNode(value)) {
|
|
28
|
+
nodes = setNodes(nodes, comment, text, value);
|
|
29
|
+
} else {
|
|
30
|
+
nodes = setText(nodes, comment, text, value) ? [text] : undefined;
|
|
31
|
+
}
|
|
32
|
+
if (item != null) {
|
|
33
|
+
item.fragment = valueIsFragment ? value : undefined;
|
|
34
|
+
item.nodes = [...nodes ?? [comment]];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function setText(nodes, comment, text, value) {
|
|
39
|
+
const isNullable = isNullableOrWhitespace(value);
|
|
40
|
+
text.textContent = isNullable ? "" : getString(value);
|
|
41
|
+
if (nodes != null) {
|
|
42
|
+
replaceNodes(nodes, [isNullable ? comment : text]);
|
|
43
|
+
return !isNullable;
|
|
44
|
+
}
|
|
45
|
+
if (isNullable && comment.parentNode == null) {
|
|
46
|
+
text.replaceWith(comment);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (!isNullable && text.parentNode == null) {
|
|
50
|
+
comment.replaceWith(text);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
setReactiveNode
|
|
57
|
+
};
|
package/package.json
CHANGED
|
@@ -4,23 +4,31 @@
|
|
|
4
4
|
"url": "https://oscarpalmer.se"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@oscarpalmer/atoms": "^0.
|
|
8
|
-
"@oscarpalmer/sentinel": "^0.
|
|
7
|
+
"@oscarpalmer/atoms": "^0.71.0",
|
|
8
|
+
"@oscarpalmer/sentinel": "^0.23.0",
|
|
9
|
+
"@oscarpalmer/toretto": "0.12.1"
|
|
9
10
|
},
|
|
10
11
|
"devDependencies": {
|
|
11
12
|
"@biomejs/biome": "^1.8.3",
|
|
12
13
|
"@types/bun": "^1.1.6",
|
|
13
|
-
"bun": "^1.1.
|
|
14
|
+
"bun": "^1.1.24",
|
|
15
|
+
"dts-bundle-generator": "^9.5.1",
|
|
14
16
|
"typescript": "^5.5.4"
|
|
15
17
|
},
|
|
16
18
|
"exports": {
|
|
17
19
|
".": {
|
|
18
|
-
"types": "./types/index.d.ts",
|
|
19
20
|
"bun": "./src/index.ts",
|
|
20
|
-
"import":
|
|
21
|
-
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./types/index.d.ts",
|
|
23
|
+
"default": "./dist/index.mjs"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./types/index.d.cts",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
}
|
|
22
29
|
}
|
|
23
30
|
},
|
|
31
|
+
"files": ["dist/index.js", "dist/**/*.mjs", "src", "types"],
|
|
24
32
|
"license": "MIT",
|
|
25
33
|
"main": "dist/index.js",
|
|
26
34
|
"module": "dist/index.mjs",
|
|
@@ -32,9 +40,12 @@
|
|
|
32
40
|
"scripts": {
|
|
33
41
|
"build": "bun run clean && bunx bun ./.bun.ts && bunx bun ./.bun.ts --mjs && bun run types",
|
|
34
42
|
"clean": "rm -rf ./dist && rm -rf ./types && rm -rf ./tsconfig.tsbuildinfo",
|
|
35
|
-
"types": "
|
|
43
|
+
"types": "bun run types:cjs && bun run types:esm",
|
|
44
|
+
"types:cjs": "bunx dts-bundle-generator --out-file ./types/index.d.cts --no-check --silent ./src/index.ts",
|
|
45
|
+
"types:esm": "bunx tsc -p ./tsconfig.json",
|
|
36
46
|
"watch": "bun build ./src/index.ts --outfile ./dist/index.js --watch"
|
|
37
47
|
},
|
|
38
|
-
"
|
|
39
|
-
"
|
|
48
|
+
"type": "module",
|
|
49
|
+
"types": "types/index.d.ts",
|
|
50
|
+
"version": "0.9.0"
|
|
40
51
|
}
|
package/src/fragment.ts
CHANGED
|
@@ -1,51 +1,78 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {html} from '@oscarpalmer/toretto/html';
|
|
2
2
|
import {parse} from './html';
|
|
3
|
-
import type {
|
|
3
|
+
import type {FragmentData, ProperNode} from './models';
|
|
4
4
|
import {mapNodes} from './node';
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
): Fragment {
|
|
10
|
-
const data: FragmentData = {
|
|
11
|
-
expressions,
|
|
12
|
-
strings,
|
|
13
|
-
nodes: [],
|
|
14
|
-
values: [],
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const instance = Object.create({
|
|
18
|
-
appendTo(element: Element) {
|
|
19
|
-
element.append(...this.get());
|
|
20
|
-
},
|
|
21
|
-
get() {
|
|
22
|
-
if (data.nodes.length === 0) {
|
|
23
|
-
const parsed = parse(data);
|
|
24
|
-
const templated = createTemplate(parsed);
|
|
25
|
-
|
|
26
|
-
data.nodes.splice(0, data.nodes.length, ...getNodes(templated));
|
|
27
|
-
|
|
28
|
-
mapNodes(data, data.nodes);
|
|
29
|
-
}
|
|
6
|
+
export class Fragment {
|
|
7
|
+
private readonly $fragment = true;
|
|
8
|
+
private readonly data: FragmentData;
|
|
30
9
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
10
|
+
constructor(strings: TemplateStringsArray, expressions: unknown[]) {
|
|
11
|
+
this.data = {
|
|
12
|
+
expressions,
|
|
13
|
+
strings,
|
|
14
|
+
items: [],
|
|
15
|
+
values: [],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
35
18
|
|
|
36
|
-
|
|
37
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Appends the fragment to the given element
|
|
21
|
+
*/
|
|
22
|
+
appendTo(element: Element): void {
|
|
23
|
+
element.append(...this.get());
|
|
24
|
+
}
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Gets a list of the fragment's nodes
|
|
28
|
+
*/
|
|
29
|
+
get(): Node[] {
|
|
30
|
+
if (this.data.items.length === 0) {
|
|
31
|
+
const parsed = parse(this.data);
|
|
32
|
+
|
|
33
|
+
const templated = html(parsed, {
|
|
34
|
+
sanitiseBooleanAttributes: false,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.data.items.splice(
|
|
38
|
+
0,
|
|
39
|
+
this.data.items.length,
|
|
40
|
+
...templated.map(node => ({
|
|
41
|
+
nodes: [node as ProperNode],
|
|
42
|
+
})),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
mapNodes(
|
|
46
|
+
this.data,
|
|
47
|
+
this.data.items.flatMap(
|
|
48
|
+
item => item.fragment?.get() ?? item.nodes,
|
|
49
|
+
) as ProperNode[],
|
|
50
|
+
);
|
|
51
|
+
}
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
return [
|
|
54
|
+
...(this.data.items.flatMap(
|
|
55
|
+
item => item.fragment?.get() ?? item.nodes,
|
|
56
|
+
) as ProperNode[]),
|
|
57
|
+
];
|
|
58
|
+
}
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Removes the fragment from the DOM
|
|
62
|
+
*/
|
|
63
|
+
remove(): void {
|
|
64
|
+
const {length} = this.data.items;
|
|
65
|
+
|
|
66
|
+
for (let index = 0; index < length; index += 1) {
|
|
67
|
+
const item = this.data.items[index];
|
|
68
|
+
|
|
69
|
+
item.fragment?.remove();
|
|
70
|
+
|
|
71
|
+
for (const node of item.nodes) {
|
|
72
|
+
node.remove();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
49
75
|
|
|
50
|
-
|
|
76
|
+
this.data.items.splice(0, length);
|
|
77
|
+
}
|
|
51
78
|
}
|
package/src/helpers/dom.ts
CHANGED
|
@@ -1,48 +1,20 @@
|
|
|
1
1
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
2
|
-
import type {
|
|
3
|
-
import {isFragment,
|
|
4
|
-
|
|
5
|
-
export function createNodes(value: unknown): FragmentNode[] {
|
|
6
|
-
if (isFragmentNode(value)) {
|
|
7
|
-
return [value];
|
|
8
|
-
}
|
|
2
|
+
import type {ProperNode} from '../models';
|
|
3
|
+
import {isFragment, isProperNode} from './index';
|
|
9
4
|
|
|
5
|
+
export function createNodes(value: unknown): ProperNode[] {
|
|
10
6
|
if (isFragment(value)) {
|
|
11
|
-
return value.get();
|
|
7
|
+
return value.get() as ProperNode[];
|
|
12
8
|
}
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export function createTemplate(html: string): Node {
|
|
18
|
-
const template = document.createElement('template');
|
|
19
|
-
|
|
20
|
-
template.innerHTML = html;
|
|
21
|
-
|
|
22
|
-
const cloned = template.content.cloneNode(true);
|
|
23
|
-
|
|
24
|
-
const scripts = [
|
|
25
|
-
...(cloned instanceof Element ? cloned.querySelectorAll('script') : []),
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
const {length} = scripts;
|
|
29
|
-
|
|
30
|
-
for (let index = 0; index < length; index += 1) {
|
|
31
|
-
scripts[index].remove();
|
|
10
|
+
if (isProperNode(value)) {
|
|
11
|
+
return [value];
|
|
32
12
|
}
|
|
33
13
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return cloned;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function getNodes(node: Node): Node[] {
|
|
40
|
-
return /^documentfragment$/i.test(node.constructor.name)
|
|
41
|
-
? [...node.childNodes]
|
|
42
|
-
: [node];
|
|
14
|
+
return [new Text(getString(value))];
|
|
43
15
|
}
|
|
44
16
|
|
|
45
|
-
export function replaceNodes(from:
|
|
17
|
+
export function replaceNodes(from: ProperNode[], to: ProperNode[]): void {
|
|
46
18
|
const {length} = from;
|
|
47
19
|
|
|
48
20
|
for (let index = 0; index < length; index += 1) {
|
package/src/helpers/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {Fragment
|
|
1
|
+
import type {Fragment} from '../fragment';
|
|
2
|
+
import type {ProperElement, ProperNode} from '../models';
|
|
2
3
|
|
|
3
4
|
export function isFragment(value: unknown): value is Fragment {
|
|
4
5
|
return (
|
|
@@ -9,14 +10,10 @@ export function isFragment(value: unknown): value is Fragment {
|
|
|
9
10
|
);
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export function
|
|
13
|
+
export function isProperElement(value: unknown): value is ProperElement {
|
|
13
14
|
return value instanceof HTMLElement || value instanceof SVGElement;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function
|
|
17
|
-
return
|
|
18
|
-
value instanceof Comment ||
|
|
19
|
-
value instanceof Element ||
|
|
20
|
-
value instanceof Text
|
|
21
|
-
);
|
|
17
|
+
export function isProperNode(value: unknown): value is ProperNode {
|
|
18
|
+
return value instanceof CharacterData || value instanceof Element;
|
|
22
19
|
}
|
package/src/html.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
2
|
+
import {Fragment} from './fragment';
|
|
3
|
+
import type {FragmentData} from './models';
|
|
4
4
|
|
|
5
5
|
function handleExpression(
|
|
6
6
|
data: FragmentData,
|
|
@@ -36,7 +36,7 @@ export function html(
|
|
|
36
36
|
strings: TemplateStringsArray,
|
|
37
37
|
...values: unknown[]
|
|
38
38
|
): Fragment {
|
|
39
|
-
return
|
|
39
|
+
return new Fragment(strings, values);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function parse(data: FragmentData): string {
|
|
@@ -53,4 +53,4 @@ export function parse(data: FragmentData): string {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
return template;
|
|
56
|
-
}
|
|
56
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type {Fragment} from './
|
|
1
|
+
export type {Fragment} from './fragment';
|
|
2
2
|
export {html} from './html';
|
package/src/models.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
appendTo(element: Element): void;
|
|
3
|
-
get(): FragmentNode[];
|
|
4
|
-
remove(): void;
|
|
5
|
-
};
|
|
1
|
+
import type {Fragment} from './fragment';
|
|
6
2
|
|
|
7
3
|
export type FragmentData = {
|
|
8
4
|
expressions: unknown[];
|
|
9
|
-
|
|
5
|
+
items: FragmentItem[];
|
|
10
6
|
strings: TemplateStringsArray;
|
|
11
7
|
values: unknown[];
|
|
12
8
|
};
|
|
13
9
|
|
|
14
|
-
export type
|
|
10
|
+
export type FragmentItem = {
|
|
11
|
+
fragment?: Fragment;
|
|
12
|
+
nodes: ProperNode[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type ProperElement = HTMLElement | SVGElement;
|
|
15
16
|
|
|
16
|
-
export type
|
|
17
|
+
export type ProperNode = CharacterData | Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {isReactive} from '@oscarpalmer/sentinel';
|
|
2
|
-
import type {FragmentData,
|
|
2
|
+
import type {FragmentData, ProperElement} from '../../models';
|
|
3
3
|
import {mapEvent} from '../event';
|
|
4
4
|
import {setAttribute} from './value';
|
|
5
5
|
|
|
@@ -11,45 +11,24 @@ function getValue(data: FragmentData, original: string): unknown {
|
|
|
11
11
|
return matches == null ? original : data.values[+matches[1]];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
(/^(href|src|xlink:href)$/i.test(name) &&
|
|
18
|
-
/(data:text\/html|javascript:)/i.test(value))
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function mapAttributes(
|
|
23
|
-
data: FragmentData,
|
|
24
|
-
element: FragmentElement,
|
|
25
|
-
): void {
|
|
26
|
-
const attributes = [...element.attributes];
|
|
27
|
-
const {length} = attributes;
|
|
28
|
-
|
|
29
|
-
for (let index = 0; index < length; index += 1) {
|
|
30
|
-
const {name, value} = attributes[index];
|
|
14
|
+
export function mapAttributes(data: FragmentData, element: ProperElement): void {
|
|
15
|
+
const attributes = [...element.attributes];
|
|
16
|
+
const {length} = attributes;
|
|
31
17
|
|
|
32
|
-
|
|
33
|
-
|
|
18
|
+
for (let index = 0; index < length; index += 1) {
|
|
19
|
+
const {name, value} = attributes[index];
|
|
34
20
|
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const actual = getValue(data, value);
|
|
21
|
+
const actual = getValue(data, value);
|
|
39
22
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
23
|
+
if (name.startsWith('@')) {
|
|
24
|
+
mapEvent(element, name, actual);
|
|
25
|
+
} else if (name.includes('.') || isReactive(actual)) {
|
|
26
|
+
mapValue(element, name, actual);
|
|
27
|
+
}
|
|
44
28
|
}
|
|
45
29
|
}
|
|
46
|
-
}
|
|
47
30
|
|
|
48
|
-
function mapValue(
|
|
49
|
-
element: FragmentElement,
|
|
50
|
-
name: string,
|
|
51
|
-
value: unknown,
|
|
52
|
-
): void {
|
|
31
|
+
function mapValue(element: ProperElement, name: string, value: unknown): void {
|
|
53
32
|
switch (true) {
|
|
54
33
|
case typeof value === 'function':
|
|
55
34
|
mapValue(element, name, value());
|