@oscarpalmer/abydon 0.13.0 → 0.14.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.
@@ -16,7 +16,7 @@ export function createNodes(value: unknown): ChildNode[] {
16
16
  }
17
17
 
18
18
  export function removeNodes(nodes: ChildNode[]): void {
19
- sanitiseNodes(nodes);
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 sanitiseNodes(nodes: ChildNode[]): void {
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
- sanitiseNodes([...node.childNodes] as ChildNode[]);
49
+ sanitizeNodes([...node.childNodes] as ChildNode[]);
50
50
  }
51
51
  }
52
52
  }
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import '@oscarpalmer/mora';
2
- import {Fragment} from './fragment';
2
+ import { Fragment } from './fragment';
3
3
 
4
4
  export function html(
5
5
  strings: TemplateStringsArray,
@@ -9,4 +9,5 @@ export function html(
9
9
  }
10
10
 
11
11
  export * from '@oscarpalmer/mora';
12
- export type {Fragment} from './fragment';
12
+ export type { Fragment } from './fragment';
13
+
package/src/models.ts CHANGED
@@ -1,19 +1,24 @@
1
- import type {Key} from '@oscarpalmer/atoms/models';
2
1
  import type {Reactive} 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: ChildNode[];
20
+ nodes?: ChildNode[];
21
+ text?: Text;
17
22
  };
18
23
 
19
24
  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 = commentExpression.exec(original ?? '');
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
- if (name.startsWith('@')) {
29
- mapEvent(element, name, actual);
30
- } else if (
31
- name.includes('.') ||
32
- typeof value === 'function' ||
33
- isReactive(actual)
34
- ) {
35
- mapValue(data, element, name, actual);
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
- switch (true) {
47
- case typeof value === 'function':
48
- setComputedAttribute(data, element, name, value as GenericCallback);
49
- break;
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
 
@@ -6,12 +6,14 @@ import {
6
6
  setAttribute as setAttr,
7
7
  } from '@oscarpalmer/toretto/attribute';
8
8
  import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
9
+ import {
10
+ ATTRIBUTE_CLASS_PREFIX_LENGTH,
11
+ EXPRESSION_ATTRIBUTE_CLASS,
12
+ EXPRESSION_ATTRIBUTE_STYLE_FULL,
13
+ EXPRESSION_ATTRIBUTE_STYLE_PREFIX,
14
+ } from '../../constants';
9
15
  import type {FragmentData} from '../../models';
10
16
 
11
- const classExpression = /^class\./;
12
- const styleFullExpression = /^style\.([\w-]+)(?:\.([\w-]+))?$/;
13
- const stylePrefixExpression = /^style\./;
14
-
15
17
  export function setAttribute(
16
18
  data: FragmentData,
17
19
  element: HTMLOrSVGElement,
@@ -21,11 +23,11 @@ export function setAttribute(
21
23
  element.removeAttribute(name);
22
24
 
23
25
  switch (true) {
24
- case classExpression.test(name):
26
+ case EXPRESSION_ATTRIBUTE_CLASS.test(name):
25
27
  setClasses(data, element, name, value);
26
28
  return;
27
29
 
28
- case stylePrefixExpression.test(name):
30
+ case EXPRESSION_ATTRIBUTE_STYLE_PREFIX.test(name):
29
31
  setStyle(data, element, name, value);
30
32
  return;
31
33
 
@@ -49,7 +51,7 @@ function setClasses(
49
51
  }
50
52
  }
51
53
 
52
- const classes = name.slice(6).split('.');
54
+ const classes = name.slice(ATTRIBUTE_CLASS_PREFIX_LENGTH).split('.');
53
55
 
54
56
  if (isReactive(value)) {
55
57
  data.mora.subscribers.add(value.subscribe(update));
@@ -64,6 +66,12 @@ function setStyle(
64
66
  name: string,
65
67
  value: unknown,
66
68
  ): void {
69
+ const [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name) ?? [];
70
+
71
+ if (property == null) {
72
+ return;
73
+ }
74
+
67
75
  function update(value: unknown): void {
68
76
  if (value == null || value === false || (value === true && unit == null)) {
69
77
  element.style.removeProperty(property);
@@ -75,14 +83,10 @@ function setStyle(
75
83
  }
76
84
  }
77
85
 
78
- const [, property, unit] = styleFullExpression.exec(name) ?? [];
79
-
80
- if (property != null) {
81
- if (isReactive(value)) {
82
- data.mora.subscribers.add(value.subscribe(update));
83
- } else {
84
- update(value);
85
- }
86
+ if (isReactive(value)) {
87
+ data.mora.subscribers.add(value.subscribe(update));
88
+ } else {
89
+ update(value);
86
90
  }
87
91
  }
88
92
 
@@ -92,12 +96,17 @@ function setValue(
92
96
  name: string,
93
97
  value: unknown,
94
98
  ): void {
95
- const callback =
96
- isBooleanAttribute(name) && name in element
97
- ? name === 'selected'
98
- ? updateSelected
99
- : updateProperty
100
- : setAttr;
99
+ let callback: (
100
+ element: HTMLOrSVGElement,
101
+ name: string,
102
+ value: unknown,
103
+ ) => void;
104
+
105
+ if (isBooleanAttribute(name) && name in element) {
106
+ callback = name === 'selected' ? updateSelected : updateProperty;
107
+ } else {
108
+ callback = setAttr;
109
+ }
101
110
 
102
111
  if (isReactive(value)) {
103
112
  data.mora.subscribers.add(
@@ -110,12 +119,6 @@ function setValue(
110
119
  }
111
120
  }
112
121
 
113
- function triggerSelectChange(select: HTMLSelectElement): void {
114
- if (select != null) {
115
- select.dispatchEvent(new Event('change', {bubbles: true}));
116
- }
117
- }
118
-
119
122
  function updateProperty(
120
123
  element: HTMLOrSVGElement,
121
124
  name: string,
@@ -133,7 +136,7 @@ function updateSelected(
133
136
  const options = [...(select?.options ?? [])];
134
137
 
135
138
  if (select != null && options.includes(element as HTMLOptionElement)) {
136
- triggerSelectChange(select);
139
+ select.dispatchEvent(new Event('change', {bubbles: true}));
137
140
  }
138
141
 
139
142
  updateProperty(element, name, value);
package/src/node/event.ts CHANGED
@@ -1,18 +1,17 @@
1
1
  import type {HTMLOrSVGElement} from '@oscarpalmer/toretto/models';
2
-
3
- const controllers = new WeakMap<HTMLOrSVGElement, AbortController>();
4
-
5
- const eventNamePattern = /^@([\w-]+)(?::([a-z:]+))?$/i;
6
-
7
- const reason = 'Event removed as element was removed from document by Abydon';
2
+ import {
3
+ ABORT_CONTROLLERS,
4
+ EXPRESSION_EVENT_NAME,
5
+ REASON_EVENT_REMOVED,
6
+ } from '../constants';
8
7
 
9
8
  function getController(element: HTMLOrSVGElement): AbortController {
10
- let controller = controllers.get(element);
9
+ let controller = ABORT_CONTROLLERS.get(element);
11
10
 
12
11
  if (controller == null) {
13
12
  controller = new AbortController();
14
13
 
15
- controllers.set(element, controller);
14
+ ABORT_CONTROLLERS.set(element, controller);
16
15
  }
17
16
 
18
17
  return controller;
@@ -24,7 +23,7 @@ function getOptions(options: string): AddEventListenerOptions {
24
23
  return {
25
24
  capture: parts.includes('c') || parts.includes('capture'),
26
25
  once: parts.includes('o') || parts.includes('once'),
27
- passive: !parts.includes('a') && !parts.includes('active'),
26
+ passive: !(parts.includes('a') || parts.includes('active')),
28
27
  };
29
28
  }
30
29
 
@@ -35,7 +34,7 @@ export function mapEvent(
35
34
  ): void {
36
35
  element.removeAttribute(name);
37
36
 
38
- const [, type, options] = eventNamePattern.exec(name) ?? [];
37
+ const [, type, options] = EXPRESSION_EVENT_NAME.exec(name) ?? [];
39
38
 
40
39
  if (typeof value === 'function' && type != null) {
41
40
  element.addEventListener(type, value as EventListener, {
@@ -46,6 +45,6 @@ export function mapEvent(
46
45
  }
47
46
 
48
47
  export function removeEvents(element: HTMLOrSVGElement): void {
49
- controllers.get(element)?.abort(reason);
50
- controllers.delete(element);
48
+ ABORT_CONTROLLERS.get(element)?.abort(REASON_EVENT_REMOVED);
49
+ ABORT_CONTROLLERS.delete(element);
51
50
  }
package/src/node/index.ts CHANGED
@@ -1,16 +1,15 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
2
  import {computed, isReactive} from '@oscarpalmer/mora';
3
3
  import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
4
+ import {EXPRESSION_COMMENT_CONTENT} from '../constants';
4
5
  import {isFragment} from '../helpers';
5
6
  import {createNodes} from '../helpers/dom';
6
7
  import type {FragmentData} from '../models';
7
8
  import {mapAttributes} from './attribute';
8
9
  import {setReactiveValue} from './value';
9
10
 
10
- const commentExpression = /^abydon\.(\d+)$/;
11
-
12
11
  function mapNode(data: FragmentData, comment: Comment): void {
13
- const matches = commentExpression.exec(comment.textContent ?? '');
12
+ const matches = EXPRESSION_COMMENT_CONTENT.exec(comment.textContent ?? '');
14
13
  const value = matches == null ? null : data.values[+matches[1]];
15
14
 
16
15
  if (value != null) {
@@ -61,7 +60,7 @@ function replaceComment(
61
60
  comment: Comment,
62
61
  value: unknown,
63
62
  ): void {
64
- const item = data.items.find(item => item.nodes.includes(comment));
63
+ const item = data.items.find(item => item.nodes?.includes(comment));
65
64
  const nodes = createNodes(value);
66
65
 
67
66
  if (item != null) {