@oscarpalmer/abydon 0.11.0 → 0.13.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.
Files changed (57) hide show
  1. package/dist/abydon.full.js +1247 -0
  2. package/dist/fragment.js +60 -0
  3. package/dist/helpers/dom.js +28 -0
  4. package/dist/helpers/index.js +11 -0
  5. package/dist/index.js +6 -885
  6. package/dist/node/attribute/index.js +34 -0
  7. package/dist/node/attribute/value.js +58 -0
  8. package/dist/node/event.js +32 -0
  9. package/dist/node/index.js +52 -0
  10. package/dist/node/value.js +93 -0
  11. package/dist/parse.js +21 -0
  12. package/package.json +41 -28
  13. package/src/fragment.ts +36 -15
  14. package/src/helpers/dom.ts +10 -10
  15. package/src/helpers/index.ts +0 -9
  16. package/src/index.ts +11 -1
  17. package/src/models.ts +7 -4
  18. package/src/node/attribute/index.ts +26 -7
  19. package/src/node/attribute/value.ts +31 -20
  20. package/src/node/event.ts +9 -9
  21. package/src/node/index.ts +22 -9
  22. package/src/node/value.ts +38 -37
  23. package/src/{html.ts → parse.ts} +5 -14
  24. package/types/fragment.d.cts +27 -0
  25. package/types/fragment.d.ts +1 -2
  26. package/types/helpers/dom.d.cts +7 -0
  27. package/types/helpers/dom.d.ts +3 -4
  28. package/types/helpers/index.d.cts +29 -0
  29. package/types/helpers/index.d.ts +0 -3
  30. package/types/index.d.cts +279 -4
  31. package/types/index.d.ts +4 -1
  32. package/types/models.d.cts +107 -0
  33. package/types/models.d.ts +8 -3
  34. package/types/node/attribute/index.d.cts +109 -0
  35. package/types/node/attribute/index.d.ts +3 -2
  36. package/types/node/attribute/value.d.cts +109 -0
  37. package/types/node/attribute/value.d.ts +3 -2
  38. package/types/node/event.d.cts +7 -0
  39. package/types/node/event.d.ts +3 -3
  40. package/types/node/index.d.cts +108 -0
  41. package/types/node/index.d.ts +2 -2
  42. package/types/node/value.d.cts +108 -0
  43. package/types/node/value.d.ts +2 -2
  44. package/types/parse.d.cts +108 -0
  45. package/types/parse.d.ts +2 -0
  46. package/dist/fragment.mjs +0 -58
  47. package/dist/helpers/dom.mjs +0 -44
  48. package/dist/helpers/index.mjs +0 -25
  49. package/dist/html.mjs +0 -36
  50. package/dist/index.mjs +0 -5
  51. package/dist/node/attribute/index.mjs +0 -35
  52. package/dist/node/attribute/value.mjs +0 -89
  53. package/dist/node/event.mjs +0 -39
  54. package/dist/node/index.mjs +0 -55
  55. package/dist/node/value.mjs +0 -123
  56. package/types/html.d.ts +0 -4
  57. /package/dist/{models.mjs → models.js} +0 -0
@@ -0,0 +1,34 @@
1
+ import { mapEvent } from "../event.js";
2
+ import { setAttribute } from "./value.js";
3
+ import { computed, isReactive } from "@oscarpalmer/mora";
4
+ var commentExpression = /^<!--abydon\.(\d+)-->$/;
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 } = attributes[index];
14
+ const actual = getValue(data, value);
15
+ if (name.startsWith("@")) mapEvent(element, name, actual);
16
+ else if (name.includes(".") || typeof value === "function" || isReactive(actual)) mapValue(data, element, name, actual);
17
+ }
18
+ }
19
+ function mapValue(data, element, name, value) {
20
+ switch (true) {
21
+ case typeof value === "function":
22
+ setComputedAttribute(data, element, name, value);
23
+ break;
24
+ default:
25
+ setAttribute(data, element, name, value);
26
+ break;
27
+ }
28
+ }
29
+ function setComputedAttribute(data, element, name, callback) {
30
+ const value = computed(callback);
31
+ data.mora.values.add(value);
32
+ setAttribute(data, element, name, value);
33
+ }
34
+ export { mapAttributes };
@@ -0,0 +1,58 @@
1
+ import { isReactive } from "@oscarpalmer/mora";
2
+ import { getString } from "@oscarpalmer/atoms/string";
3
+ import { isBooleanAttribute, setAttribute as setAttribute$1 } from "@oscarpalmer/toretto/attribute";
4
+ var classExpression = /^class\./;
5
+ var styleFullExpression = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
6
+ var stylePrefixExpression = /^style\./;
7
+ function setAttribute(data, element, name, value) {
8
+ element.removeAttribute(name);
9
+ switch (true) {
10
+ case classExpression.test(name):
11
+ setClasses(data, element, name, value);
12
+ return;
13
+ case stylePrefixExpression.test(name):
14
+ setStyle(data, element, name, value);
15
+ return;
16
+ default:
17
+ setValue(data, element, name, value);
18
+ break;
19
+ }
20
+ }
21
+ function setClasses(data, element, name, value) {
22
+ function update(value$1) {
23
+ if (value$1 === true) element.classList.add(...classes);
24
+ else element.classList.remove(...classes);
25
+ }
26
+ const classes = name.slice(6).split(".");
27
+ if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
28
+ else update(value);
29
+ }
30
+ function setStyle(data, element, name, value) {
31
+ function update(value$1) {
32
+ if (value$1 == null || value$1 === false || value$1 === true && unit == null) element.style.removeProperty(property);
33
+ else element.style.setProperty(property, value$1 === true ? unit : getString(value$1));
34
+ }
35
+ const [, property, unit] = styleFullExpression.exec(name) ?? [];
36
+ if (property != null) if (isReactive(value)) data.mora.subscribers.add(value.subscribe(update));
37
+ else update(value);
38
+ }
39
+ function setValue(data, element, name, value) {
40
+ const callback = isBooleanAttribute(name) && name in element ? name === "selected" ? updateSelected : updateProperty : setAttribute$1;
41
+ if (isReactive(value)) data.mora.subscribers.add(value.subscribe((next) => {
42
+ callback(element, name, next);
43
+ }));
44
+ else callback(element, name, value);
45
+ }
46
+ function triggerSelectChange(select) {
47
+ if (select != null) select.dispatchEvent(new Event("change", { bubbles: true }));
48
+ }
49
+ function updateProperty(element, name, value) {
50
+ element[name] = value === true;
51
+ }
52
+ function updateSelected(element, name, value) {
53
+ const select = element.closest("select");
54
+ const options = [...select?.options ?? []];
55
+ if (select != null && options.includes(element)) triggerSelectChange(select);
56
+ updateProperty(element, name, value);
57
+ }
58
+ export { setAttribute };
@@ -0,0 +1,32 @@
1
+ var controllers = /* @__PURE__ */ new WeakMap();
2
+ var eventNamePattern = /^@([\w-]+)(?::([a-z:]+))?$/i;
3
+ var reason = "Event removed as element was removed from document by Abydon";
4
+ function getController(element) {
5
+ let controller = controllers.get(element);
6
+ if (controller == null) {
7
+ controller = new AbortController();
8
+ controllers.set(element, controller);
9
+ }
10
+ return controller;
11
+ }
12
+ function getOptions(options) {
13
+ const parts = options.split(":");
14
+ return {
15
+ capture: parts.includes("c") || parts.includes("capture"),
16
+ once: parts.includes("o") || parts.includes("once"),
17
+ passive: !parts.includes("a") && !parts.includes("active")
18
+ };
19
+ }
20
+ function mapEvent(element, name, value) {
21
+ element.removeAttribute(name);
22
+ const [, type, options] = eventNamePattern.exec(name) ?? [];
23
+ if (typeof value === "function" && type != null) element.addEventListener(type, value, {
24
+ ...getOptions(options ?? ""),
25
+ signal: getController(element).signal
26
+ });
27
+ }
28
+ function removeEvents(element) {
29
+ controllers.get(element)?.abort(reason);
30
+ controllers.delete(element);
31
+ }
32
+ export { mapEvent, removeEvents };
@@ -0,0 +1,52 @@
1
+ import { isFragment } from "../helpers/index.js";
2
+ import { createNodes } from "../helpers/dom.js";
3
+ import { mapAttributes } from "./attribute/index.js";
4
+ import { setReactiveValue } from "./value.js";
5
+ import { computed, isReactive } from "@oscarpalmer/mora";
6
+ import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
7
+ var commentExpression = /^abydon\.(\d+)$/;
8
+ function mapNode(data, comment) {
9
+ const matches = commentExpression.exec(comment.textContent ?? "");
10
+ const value = matches == null ? null : data.values[+matches[1]];
11
+ if (value != null) mapValue(data, comment, value);
12
+ }
13
+ function mapNodes(data, nodes) {
14
+ const { length } = nodes;
15
+ for (let index = 0; index < length; index += 1) {
16
+ const node = nodes[index];
17
+ if (node instanceof Comment) {
18
+ mapNode(data, node);
19
+ continue;
20
+ }
21
+ if (isHTMLOrSVGElement(node)) mapAttributes(data, node);
22
+ if (node.hasChildNodes()) mapNodes(data, [...node.childNodes]);
23
+ }
24
+ }
25
+ function mapValue(data, comment, value) {
26
+ switch (true) {
27
+ case typeof value === "function":
28
+ setComputedValue(data, comment, value);
29
+ break;
30
+ case isReactive(value):
31
+ setReactiveValue(data, comment, value);
32
+ break;
33
+ default:
34
+ replaceComment(data, comment, value);
35
+ break;
36
+ }
37
+ }
38
+ function replaceComment(data, comment, value) {
39
+ const item = data.items.find((item$1) => item$1.nodes.includes(comment));
40
+ const nodes = createNodes(value);
41
+ if (item != null) {
42
+ item.fragments = isFragment(value) ? [value] : void 0;
43
+ item.nodes = nodes;
44
+ }
45
+ comment.replaceWith(...nodes);
46
+ }
47
+ function setComputedValue(data, comment, callback) {
48
+ const value = computed(callback);
49
+ data.mora.values.add(value);
50
+ setReactiveValue(data, comment, value);
51
+ }
52
+ export { mapNodes };
@@ -0,0 +1,93 @@
1
+ import { compareArrays, isFragment } from "../helpers/index.js";
2
+ import { createNodes, replaceNodes } from "../helpers/dom.js";
3
+ import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
4
+ import { getString } from "@oscarpalmer/atoms/string";
5
+ import { isChildNode } from "@oscarpalmer/toretto/is";
6
+ function removeFragments(fragments) {
7
+ if (fragments != null) {
8
+ const { length } = fragments;
9
+ for (let index = 0; index < length; index += 1) fragments[index].remove();
10
+ }
11
+ }
12
+ function setArray(fragments, nodes, comment, text, value) {
13
+ if (value.length === 0) return { nodes: setText(fragments, nodes, comment, text, value) };
14
+ let templates = value.filter((item) => isFragment(item) && item.identifier != null);
15
+ const identifiers = templates.map((fragment) => fragment.identifier);
16
+ const oldIdentifiers = fragments?.map((fragment) => fragment.identifier) ?? [];
17
+ if (new Set(identifiers).size !== templates.length) templates = [];
18
+ const noTemplates = templates.length === 0;
19
+ if (noTemplates || nodes == null || oldIdentifiers.some((identifier) => identifier == null)) return {
20
+ fragments: noTemplates ? void 0 : templates,
21
+ nodes: setNodes(fragments, nodes, comment, text, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
22
+ };
23
+ const next = templates.map((template) => fragments?.find((fragment) => fragment.identifier === template.identifier) ?? template);
24
+ const comparison = compareArrays(fragments ?? [], templates);
25
+ if (comparison !== "removed") {
26
+ let position = nodes[0];
27
+ const before = comparison === "added" && !oldIdentifiers.includes(templates[0].identifier);
28
+ const items = next.flatMap((fragment) => fragment.get().flatMap((node) => ({
29
+ identifier: fragment.identifier,
30
+ value: node
31
+ })));
32
+ const { length: length$1 } = items;
33
+ for (let index = 0; index < length$1; index += 1) {
34
+ const item = items[index];
35
+ if (comparison === "dissimilar" || !oldIdentifiers.includes(item.identifier)) if (index === 0 && before) position.before(item.value);
36
+ else position.after(item.value);
37
+ position = item.value;
38
+ }
39
+ }
40
+ const toRemove = fragments?.filter((fragment) => !identifiers.includes(fragment.identifier)) ?? [];
41
+ const { length } = toRemove;
42
+ for (let index = 0; index < length; index += 1) toRemove[index].remove();
43
+ return {
44
+ fragments: next,
45
+ nodes: next.flatMap((fragment) => fragment.get())
46
+ };
47
+ }
48
+ function setNodes(fragments, nodes, comment, text, next) {
49
+ if (nodes == null) {
50
+ if (comment.parentNode != null) replaceNodes([comment], next);
51
+ else if (text.parentNode != null) replaceNodes([text], next);
52
+ } else replaceNodes(nodes, next);
53
+ removeFragments(fragments);
54
+ return next;
55
+ }
56
+ function setReactiveValue(data, comment, reactive) {
57
+ const item = data.items.find((item$1) => item$1.nodes.includes(comment));
58
+ const text = new Text();
59
+ let fragments;
60
+ let nodes;
61
+ data.mora.subscribers.add(reactive.subscribe((value) => {
62
+ if (Array.isArray(value)) {
63
+ const result = setArray(fragments, nodes, comment, text, value);
64
+ fragments = typeof result === "boolean" ? void 0 : result?.fragments;
65
+ nodes = typeof result === "boolean" ? result ? [text] : void 0 : result?.nodes;
66
+ } else {
67
+ const valueIsFragment = isFragment(value);
68
+ fragments = valueIsFragment ? [value] : void 0;
69
+ if (valueIsFragment || isChildNode(value)) nodes = setNodes(fragments, nodes, comment, text, createNodes(value));
70
+ else nodes = setText(fragments, nodes, comment, text, value);
71
+ }
72
+ if (item != null) {
73
+ item.fragments = fragments;
74
+ item.nodes = [...nodes ?? [comment]];
75
+ }
76
+ }));
77
+ }
78
+ function setText(fragments, nodes, comment, text, value) {
79
+ const isNullable = isNullableOrWhitespace(value);
80
+ text.textContent = isNullable ? "" : getString(value);
81
+ let result = false;
82
+ if (nodes != null) {
83
+ replaceNodes(nodes, [isNullable ? comment : text]);
84
+ result = !isNullable;
85
+ } else if (isNullable && comment.parentNode == null) text.replaceWith(comment);
86
+ else if (!isNullable && text.parentNode == null) {
87
+ comment.replaceWith(text);
88
+ result = true;
89
+ }
90
+ removeFragments(fragments);
91
+ return result ? [text] : void 0;
92
+ }
93
+ export { setReactiveValue };
package/dist/parse.js ADDED
@@ -0,0 +1,21 @@
1
+ import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
2
+ function handleExpression(data, prefix, expression) {
3
+ if (Array.isArray(expression)) {
4
+ const { length } = expression;
5
+ let expressions = "";
6
+ for (let index = 0; index < length; index += 1) expressions += handleExpression(data, "", expression[index]);
7
+ return `${prefix}${expressions}`;
8
+ }
9
+ if (typeof expression === "function" || typeof expression === "object" && expression != null) {
10
+ const index = data.values.push(expression) - 1;
11
+ return `${prefix}<!--abydon.${index}-->`;
12
+ }
13
+ return isNullableOrWhitespace(expression) ? prefix : `${prefix}${expression}`;
14
+ }
15
+ function parse(data) {
16
+ const { length } = data.strings;
17
+ let template = "";
18
+ for (let index = 0; index < length; index += 1) template += handleExpression(data, data.strings[index], data.expressions[index]);
19
+ return template;
20
+ }
21
+ export { parse };
package/package.json CHANGED
@@ -4,48 +4,61 @@
4
4
  "url": "https://oscarpalmer.se"
5
5
  },
6
6
  "dependencies": {
7
- "@oscarpalmer/atoms": "^0.71.0",
8
- "@oscarpalmer/sentinel": "^0.23.0",
9
- "@oscarpalmer/toretto": "0.12.1"
7
+ "@oscarpalmer/atoms": "^0.108",
8
+ "@oscarpalmer/mora": "^0.21",
9
+ "@oscarpalmer/toretto": "^0.22"
10
10
  },
11
11
  "devDependencies": {
12
- "@biomejs/biome": "^1.8.3",
13
- "@types/bun": "^1.1.8",
14
- "bun": "^1.1.27",
15
- "dts-bundle-generator": "^9.5.1",
16
- "typescript": "^5.5.4"
12
+ "@biomejs/biome": "^2.2",
13
+ "@oscarpalmer/oui": "^0.17",
14
+ "@rollup/plugin-node-resolve": "^16",
15
+ "@rollup/plugin-typescript": "^12.1",
16
+ "@types/node": "^24.3",
17
+ "@vitest/coverage-istanbul": "^3.2",
18
+ "dts-bundle-generator": "^9.5",
19
+ "glob": "^11",
20
+ "jsdom": "^26.1",
21
+ "rollup": "^4.50",
22
+ "tslib": "^2.8",
23
+ "typescript": "^5.9",
24
+ "vite": "npm:rolldown-vite@latest",
25
+ "vitest": "^3.2"
17
26
  },
18
27
  "exports": {
28
+ "./package.json": "./package.json",
19
29
  ".": {
20
- "bun": "./src/index.ts",
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
- }
30
+ "types": "./types/index.d.ts",
31
+ "default": "./dist/index.js"
29
32
  }
30
33
  },
31
- "files": ["dist/index.js", "dist/**/*.mjs", "src", "types"],
34
+ "files": ["dist", "src", "types"],
35
+ "keywords": [
36
+ "components",
37
+ "dom",
38
+ "library",
39
+ "literals",
40
+ "reactive",
41
+ "signal",
42
+ "template",
43
+ "vanilla"
44
+ ],
32
45
  "license": "MIT",
33
- "main": "dist/index.js",
34
- "module": "dist/index.mjs",
46
+ "module": "dist/index.js",
35
47
  "name": "@oscarpalmer/abydon",
36
48
  "repository": {
37
49
  "type": "git",
38
50
  "url": "git+https://github.com/oscarpalmer/abydon.git"
39
51
  },
40
52
  "scripts": {
41
- "build": "bun run clean && bunx bun ./.bun.ts && bunx bun ./.bun.ts --mjs && bun run types",
42
- "clean": "rm -rf ./dist && rm -rf ./types && rm -rf ./tsconfig.tsbuildinfo",
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",
46
- "watch": "bun build ./src/index.ts --outfile ./dist/index.js --watch"
53
+ "build": "npm run clean && npx vite build && npm run rollup:build && npm run types",
54
+ "clean": "rm -rf ./dist && rm -rf ./types && rm -f ./tsconfig.tsbuildinfo",
55
+ "rollup:build": "npx rollup -c",
56
+ "rollup:watch": "npx rollup -c --watch",
57
+ "test": "npx vitest --coverage",
58
+ "types": "npx tsc && npx dts-bundle-generator --config ./dts.config.cts --silent",
59
+ "watch": "npx vite build --watch"
47
60
  },
48
61
  "type": "module",
49
- "types": "types/index.d.ts",
50
- "version": "0.11.0"
62
+ "types": "types/index.d.cts",
63
+ "version": "0.13.0"
51
64
  }
package/src/fragment.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type {Key} from '@oscarpalmer/atoms/models';
2
2
  import {html} from '@oscarpalmer/toretto/html';
3
- import {parse} from './html';
4
- import type {FragmentData, ProperNode} from './models';
5
- import {mapNodes} from './node';
6
3
  import {removeNodes} from './helpers/dom';
4
+ import type {FragmentData} from './models';
5
+ import {mapNodes} from './node';
6
+ import {parse} from './parse';
7
7
 
8
8
  export class Fragment {
9
9
  private readonly $fragment = true;
@@ -18,6 +18,10 @@ export class Fragment {
18
18
  expressions,
19
19
  strings,
20
20
  items: [],
21
+ mora: {
22
+ subscribers: new Set(),
23
+ values: new Set(),
24
+ },
21
25
  values: [],
22
26
  };
23
27
  }
@@ -32,19 +36,19 @@ export class Fragment {
32
36
  /**
33
37
  * Gets a list of the fragment's nodes
34
38
  */
35
- get(): ProperNode[] {
39
+ get(): ChildNode[] {
36
40
  if (this.data.items.length === 0) {
37
41
  const parsed = parse(this.data);
38
42
 
39
43
  const templated = html(parsed, {
40
- sanitiseBooleanAttributes: false,
44
+ sanitizeBooleanAttributes: false,
41
45
  });
42
46
 
43
47
  this.data.items.splice(
44
48
  0,
45
49
  this.data.items.length,
46
50
  ...templated.map(node => ({
47
- nodes: [node as ProperNode],
51
+ nodes: [node as ChildNode],
48
52
  })),
49
53
  );
50
54
 
@@ -75,19 +79,36 @@ export class Fragment {
75
79
  * Removes the fragment from the DOM
76
80
  */
77
81
  remove(): void {
78
- const {length} = this.data.items;
82
+ removeFragment(this.data);
83
+ }
84
+ }
79
85
 
80
- for (let index = 0; index < length; index += 1) {
81
- const {fragments, nodes} = this.data.items[index];
82
- const {length} = fragments ?? [];
86
+ function removeFragment(data: FragmentData): void {
87
+ removeMora(data);
88
+
89
+ const {length} = data.items;
83
90
 
84
- for (let index = 0; index < length; index += 1) {
85
- fragments?.[index]?.remove();
86
- }
91
+ for (let index = 0; index < length; index += 1) {
92
+ const {fragments, nodes} = data.items[index];
93
+ const {length} = fragments ?? [];
87
94
 
88
- removeNodes(nodes);
95
+ for (let index = 0; index < length; index += 1) {
96
+ fragments?.[index]?.remove();
89
97
  }
90
98
 
91
- this.data.items.splice(0, length);
99
+ removeNodes(nodes);
100
+ }
101
+
102
+ data.items.splice(0, length);
103
+ }
104
+
105
+ function removeMora(data: FragmentData): void {
106
+ const unsubscribers = [...data.mora.subscribers];
107
+
108
+ data.mora.subscribers.clear();
109
+ data.mora.values.clear();
110
+
111
+ for (const unsubscribe of unsubscribers) {
112
+ unsubscribe();
92
113
  }
93
114
  }
@@ -1,21 +1,21 @@
1
1
  import {getString} from '@oscarpalmer/atoms/string';
2
- import type {ProperNode} from '../models';
3
- import {isFragment, isProperElement, isProperNode} from './index';
2
+ import {isChildNode, isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
4
3
  import {removeEvents} from '../node/event';
4
+ import {isFragment} from './index';
5
5
 
6
- export function createNodes(value: unknown): ProperNode[] {
6
+ export function createNodes(value: unknown): ChildNode[] {
7
7
  if (isFragment(value)) {
8
- return value.get() as ProperNode[];
8
+ return value.get() as ChildNode[];
9
9
  }
10
10
 
11
- if (isProperNode(value)) {
11
+ if (isChildNode(value)) {
12
12
  return [value];
13
13
  }
14
14
 
15
15
  return [new Text(getString(value))];
16
16
  }
17
17
 
18
- export function removeNodes(nodes: ProperNode[]): void {
18
+ export function removeNodes(nodes: ChildNode[]): void {
19
19
  sanitiseNodes(nodes);
20
20
 
21
21
  const {length} = nodes;
@@ -25,7 +25,7 @@ export function removeNodes(nodes: ProperNode[]): void {
25
25
  }
26
26
  }
27
27
 
28
- export function replaceNodes(from: ProperNode[], to: ProperNode[]): void {
28
+ export function replaceNodes(from: ChildNode[], to: ChildNode[]): void {
29
29
  from[0]?.replaceWith(...to);
30
30
 
31
31
  const {length} = from;
@@ -35,18 +35,18 @@ export function replaceNodes(from: ProperNode[], to: ProperNode[]): void {
35
35
  }
36
36
  }
37
37
 
38
- function sanitiseNodes(nodes: ProperNode[]): void {
38
+ function sanitiseNodes(nodes: ChildNode[]): void {
39
39
  const {length} = nodes;
40
40
 
41
41
  for (let index = 0; index < length; index += 1) {
42
42
  const node = nodes[index];
43
43
 
44
- if (isProperElement(node)) {
44
+ if (isHTMLOrSVGElement(node)) {
45
45
  removeEvents(node);
46
46
  }
47
47
 
48
48
  if (node.hasChildNodes()) {
49
- sanitiseNodes([...node.childNodes] as ProperNode[]);
49
+ sanitiseNodes([...node.childNodes] as ChildNode[]);
50
50
  }
51
51
  }
52
52
  }
@@ -1,5 +1,4 @@
1
1
  import type {Fragment} from '../fragment';
2
- import type {ProperElement, ProperNode} from '../models';
3
2
 
4
3
  export function compareArrays(
5
4
  first: unknown[],
@@ -28,11 +27,3 @@ export function isFragment(value: unknown): value is Fragment {
28
27
  value.$fragment === true
29
28
  );
30
29
  }
31
-
32
- export function isProperElement(value: unknown): value is ProperElement {
33
- return value instanceof HTMLElement || value instanceof SVGElement;
34
- }
35
-
36
- export function isProperNode(value: unknown): value is ProperNode {
37
- return value instanceof CharacterData || value instanceof Element;
38
- }
package/src/index.ts CHANGED
@@ -1,2 +1,12 @@
1
+ import '@oscarpalmer/mora';
2
+ import {Fragment} from './fragment';
3
+
4
+ export function html(
5
+ strings: TemplateStringsArray,
6
+ ...values: unknown[]
7
+ ): Fragment {
8
+ return new Fragment(strings, values);
9
+ }
10
+
11
+ export * from '@oscarpalmer/mora';
1
12
  export type {Fragment} from './fragment';
2
- export {html} from './html';
package/src/models.ts CHANGED
@@ -1,19 +1,22 @@
1
1
  import type {Key} from '@oscarpalmer/atoms/models';
2
+ import type {Reactive} from '@oscarpalmer/mora';
2
3
  import type {Fragment} from './fragment';
3
4
 
4
5
  export type FragmentData = {
5
6
  expressions: unknown[];
6
7
  identifier?: Key;
7
8
  items: FragmentItem[];
9
+ mora: MoraData;
8
10
  strings: TemplateStringsArray;
9
11
  values: unknown[];
10
12
  };
11
13
 
12
14
  export type FragmentItem = {
13
15
  fragments?: Fragment[];
14
- nodes: ProperNode[];
16
+ nodes: ChildNode[];
15
17
  };
16
18
 
17
- export type ProperElement = HTMLElement | SVGElement;
18
-
19
- export type ProperNode = CharacterData | Element;
19
+ type MoraData = {
20
+ subscribers: Set<() => void>;
21
+ values: Set<Reactive<unknown>>;
22
+ };
@@ -1,6 +1,7 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {computed, isReactive} from '@oscarpalmer/sentinel';
3
- import type {FragmentData, ProperElement} from '../../models';
2
+ import {computed, isReactive} from '@oscarpalmer/mora';
3
+ import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
4
+ import type {FragmentData} from '../../models';
4
5
  import {mapEvent} from '../event';
5
6
  import {setAttribute} from './value';
6
7
 
@@ -14,7 +15,7 @@ function getValue(data: FragmentData, original: string): unknown {
14
15
 
15
16
  export function mapAttributes(
16
17
  data: FragmentData,
17
- element: ProperElement,
18
+ element: HTMLOrSVGElement,
18
19
  ): void {
19
20
  const attributes = [...element.attributes];
20
21
  const {length} = attributes;
@@ -31,19 +32,37 @@ export function mapAttributes(
31
32
  typeof value === 'function' ||
32
33
  isReactive(actual)
33
34
  ) {
34
- mapValue(element, name, actual);
35
+ mapValue(data, element, name, actual);
35
36
  }
36
37
  }
37
38
  }
38
39
 
39
- function mapValue(element: ProperElement, name: string, value: unknown): void {
40
+ function mapValue(
41
+ data: FragmentData,
42
+ element: HTMLOrSVGElement,
43
+ name: string,
44
+ value: unknown,
45
+ ): void {
40
46
  switch (true) {
41
47
  case typeof value === 'function':
42
- setAttribute(element, name, computed(value as GenericCallback));
48
+ setComputedAttribute(data, element, name, value as GenericCallback);
43
49
  break;
44
50
 
45
51
  default:
46
- setAttribute(element, name, value);
52
+ setAttribute(data, element, name, value);
47
53
  break;
48
54
  }
49
55
  }
56
+
57
+ function setComputedAttribute(
58
+ data: FragmentData,
59
+ element: HTMLOrSVGElement,
60
+ name: string,
61
+ callback: GenericCallback,
62
+ ): void {
63
+ const value = computed(callback);
64
+
65
+ data.mora.values.add(value);
66
+
67
+ setAttribute(data, element, name, value);
68
+ }