@oscarpalmer/abydon 0.8.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.
@@ -1,26 +1,17 @@
1
- import {closest} from '@oscarpalmer/atoms/element';
2
1
  import type {PlainObject} from '@oscarpalmer/atoms/models';
3
2
  import {getString} from '@oscarpalmer/atoms/string';
4
3
  import {effect, isReactive} from '@oscarpalmer/sentinel';
5
- import type {FragmentElement} from '../../models';
6
-
7
- const booleanAttributes = new Set([
8
- 'checked',
9
- 'disabled',
10
- 'hidden',
11
- 'inert',
12
- 'multiple',
13
- 'open',
14
- 'readOnly',
15
- 'required',
16
- 'selected',
17
- ]);
4
+ import {
5
+ isBooleanAttribute,
6
+ setAttribute as setAttr,
7
+ } from '@oscarpalmer/toretto/attribute';
8
+ import type {ProperElement} from '../../models';
18
9
 
19
10
  const classPattern = /^class\./;
20
11
  const stylePattern = /^style\./;
21
12
 
22
13
  export function setAttribute(
23
- element: FragmentElement,
14
+ element: ProperElement,
24
15
  name: string,
25
16
  value: unknown,
26
17
  ): void {
@@ -42,7 +33,7 @@ export function setAttribute(
42
33
  }
43
34
 
44
35
  function setClasses(
45
- element: FragmentElement,
36
+ element: ProperElement,
46
37
  name: string,
47
38
  value: unknown,
48
39
  ): void {
@@ -65,11 +56,7 @@ function setClasses(
65
56
  }
66
57
  }
67
58
 
68
- function setStyle(
69
- element: FragmentElement,
70
- name: string,
71
- value: unknown,
72
- ): void {
59
+ function setStyle(element: ProperElement, name: string, value: unknown): void {
73
60
  function update(value: unknown): void {
74
61
  if (value == null || value === false || (value === true && unit == null)) {
75
62
  element.style.removeProperty(property);
@@ -96,17 +83,13 @@ function setStyle(
96
83
  }
97
84
  }
98
85
 
99
- function setValue(
100
- element: FragmentElement,
101
- name: string,
102
- value: unknown,
103
- ): void {
86
+ function setValue(element: ProperElement, name: string, value: unknown): void {
104
87
  const callback =
105
- booleanAttributes.has(name) && name in element
88
+ isBooleanAttribute(name) && name in element
106
89
  ? name === 'selected'
107
- ? updateSelected(element)
90
+ ? updateSelected
108
91
  : updateProperty
109
- : updateAttribute;
92
+ : setAttr;
110
93
 
111
94
  if (isReactive(value)) {
112
95
  effect(() => {
@@ -123,20 +106,8 @@ function triggerSelectChange(select: HTMLSelectElement): void {
123
106
  }
124
107
  }
125
108
 
126
- function updateAttribute(
127
- element: FragmentElement,
128
- name: string,
129
- value: unknown,
130
- ): void {
131
- if (value == null) {
132
- element.removeAttribute(name);
133
- } else {
134
- element.setAttribute(name, getString(value));
135
- }
136
- }
137
-
138
109
  function updateProperty(
139
- element: FragmentElement,
110
+ element: ProperElement,
140
111
  name: string,
141
112
  value: unknown,
142
113
  ): void {
@@ -145,15 +116,17 @@ function updateProperty(
145
116
  );
146
117
  }
147
118
 
148
- function updateSelected(element: FragmentElement) {
149
- const select = closest(element, 'select')[0] as HTMLSelectElement;
119
+ function updateSelected(
120
+ element: ProperElement,
121
+ name: string,
122
+ value: unknown,
123
+ ): void {
124
+ const select = element.closest('select') as HTMLSelectElement;
150
125
  const options = [...(select?.options ?? [])];
151
126
 
152
- return (element: FragmentElement, name: string, value: unknown): void => {
153
- if (select != null && options.includes(element as HTMLOptionElement)) {
154
- triggerSelectChange(select);
155
- }
127
+ if (select != null && options.includes(element as HTMLOptionElement)) {
128
+ triggerSelectChange(select);
129
+ }
156
130
 
157
- updateProperty(element, name, value);
158
- };
131
+ updateProperty(element, name, value);
159
132
  }
package/src/node/event.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {FragmentElement} from '../models';
1
+ import type {ProperElement} from '../models';
2
2
 
3
3
  function getOptions(options: string): AddEventListenerOptions {
4
4
  const parts = options.split(':');
@@ -11,7 +11,7 @@ function getOptions(options: string): AddEventListenerOptions {
11
11
  }
12
12
 
13
13
  export function mapEvent(
14
- element: FragmentElement,
14
+ element: ProperElement,
15
15
  name: string,
16
16
  value: unknown,
17
17
  ): void {
package/src/node/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import {isReactive} from '@oscarpalmer/sentinel';
2
- import {isFragmentElement} from '../helpers';
2
+ import {isFragment, isProperElement} from '../helpers';
3
3
  import {createNodes} from '../helpers/dom';
4
- import type {FragmentData} from '../models';
4
+ import type {FragmentData, ProperNode} from '../models';
5
5
  import {mapAttributes} from './attribute';
6
6
  import {setReactiveNode} from './value';
7
7
 
@@ -12,11 +12,11 @@ function mapNode(data: FragmentData, comment: Comment): void {
12
12
  const value = matches == null ? null : data.values[+matches[1]];
13
13
 
14
14
  if (value != null) {
15
- mapValue(comment, value);
15
+ mapValue(data, comment, value);
16
16
  }
17
17
  }
18
18
 
19
- export function mapNodes(data: FragmentData, nodes: Node[]): void {
19
+ export function mapNodes(data: FragmentData, nodes: ProperNode[]): void {
20
20
  const {length} = nodes;
21
21
 
22
22
  for (let index = 0; index < length; index += 1) {
@@ -28,28 +28,44 @@ export function mapNodes(data: FragmentData, nodes: Node[]): void {
28
28
  continue;
29
29
  }
30
30
 
31
- if (isFragmentElement(node)) {
31
+ if (isProperElement(node)) {
32
32
  mapAttributes(data, node);
33
33
  }
34
34
 
35
35
  if (node.hasChildNodes()) {
36
- mapNodes(data, [...node.childNodes]);
36
+ mapNodes(data, [...node.childNodes] as ProperNode[]);
37
37
  }
38
38
  }
39
39
  }
40
40
 
41
- function mapValue(comment: Comment, value: unknown): void {
41
+ function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
42
42
  switch (true) {
43
43
  case typeof value === 'function':
44
- mapValue(comment, value());
44
+ mapValue(data, comment, value());
45
45
  return;
46
46
 
47
47
  case isReactive(value):
48
- setReactiveNode(comment, value);
48
+ setReactiveNode(data, comment, value);
49
49
  break;
50
50
 
51
51
  default:
52
- comment.replaceWith(...createNodes(value));
52
+ replaceComment(data, comment, value);
53
53
  break;
54
54
  }
55
55
  }
56
+
57
+ function replaceComment(
58
+ data: FragmentData,
59
+ comment: Comment,
60
+ value: unknown,
61
+ ): void {
62
+ const item = data.items.find(item => item.nodes.includes(comment));
63
+ const nodes = createNodes(value);
64
+
65
+ if (item != null) {
66
+ item.fragment = isFragment(value) ? value : undefined;
67
+ item.nodes = nodes;
68
+ }
69
+
70
+ comment.replaceWith(...nodes);
71
+ }
package/src/node/value.ts CHANGED
@@ -1,48 +1,87 @@
1
1
  import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
2
2
  import {getString} from '@oscarpalmer/atoms/string';
3
3
  import {type Reactive, effect} from '@oscarpalmer/sentinel';
4
- import {isFragment, isFragmentNode} from '../helpers';
4
+ import type {Fragment} from '../fragment';
5
+ import {isFragment, isProperNode} from '../helpers';
5
6
  import {createNodes, replaceNodes} from '../helpers/dom';
6
- import type {FragmentNode} from '../models';
7
+ import type {FragmentData, ProperNode} from '../models';
7
8
 
8
- export function setReactiveNode(comment: Comment, reactive: Reactive): void {
9
+ function setNodes(
10
+ nodes: ProperNode[] | undefined,
11
+ comment: Comment,
12
+ text: Text,
13
+ value: Fragment | ProperNode,
14
+ ): ProperNode[] {
15
+ const next = createNodes(value);
16
+
17
+ if (nodes == null) {
18
+ if (comment.parentNode != null) {
19
+ replaceNodes([comment], next);
20
+ } else if (text.parentNode != null) {
21
+ replaceNodes([text], next);
22
+ }
23
+ } else {
24
+ replaceNodes(nodes, next);
25
+ }
26
+
27
+ return next;
28
+ }
29
+
30
+ export function setReactiveNode(
31
+ data: FragmentData,
32
+ comment: Comment,
33
+ reactive: Reactive,
34
+ ): void {
35
+ const item = data.items.find(item => item.nodes.includes(comment));
9
36
  const text = new Text();
10
37
 
11
- let nodes: FragmentNode[] | undefined;
38
+ let nodes: ProperNode[] | undefined;
12
39
 
13
40
  effect(() => {
14
41
  const value = reactive.get();
15
42
 
16
- if (isFragment(value) || isFragmentNode(value)) {
17
- const next = createNodes(value);
18
-
19
- if (nodes == null) {
20
- if (comment.parentNode != null) {
21
- replaceNodes([comment], next);
22
- } else if (text.parentNode != null) {
23
- replaceNodes([text], next);
24
- }
25
- } else {
26
- replaceNodes(nodes, next);
27
- }
43
+ const valueIsFragment = isFragment(value);
28
44
 
29
- nodes = next;
45
+ if (valueIsFragment || isProperNode(value)) {
46
+ nodes = setNodes(nodes, comment, text, value);
47
+ } else {
48
+ nodes = setText(nodes, comment, text, value) ? [text] : undefined;
49
+ }
30
50
 
31
- return;
51
+ if (item != null) {
52
+ item.fragment = valueIsFragment ? value : undefined;
53
+ item.nodes = [...(nodes ?? [comment])];
32
54
  }
55
+ });
56
+ }
33
57
 
34
- const isNullable = isNullableOrWhitespace(value);
58
+ function setText(
59
+ nodes: ProperNode[] | undefined,
60
+ comment: Comment,
61
+ text: Text,
62
+ value: unknown,
63
+ ): boolean {
64
+ const isNullable = isNullableOrWhitespace(value);
35
65
 
36
- text.textContent = getString(value);
66
+ text.textContent = isNullable ? '' : getString(value);
37
67
 
38
- if (nodes != null) {
39
- replaceNodes(nodes, [isNullable ? comment : text]);
40
- } else if (isNullable && comment.parentNode == null) {
41
- text.replaceWith(comment);
42
- } else if (!isNullable && text.parentNode == null) {
43
- comment.replaceWith(text);
44
- }
68
+ if (nodes != null) {
69
+ replaceNodes(nodes, [isNullable ? comment : text]);
45
70
 
46
- nodes = undefined;
47
- });
71
+ return !isNullable;
72
+ }
73
+
74
+ if (isNullable && comment.parentNode == null) {
75
+ text.replaceWith(comment);
76
+
77
+ return false;
78
+ }
79
+
80
+ if (!isNullable && text.parentNode == null) {
81
+ comment.replaceWith(text);
82
+
83
+ return true;
84
+ }
85
+
86
+ return false;
48
87
  }
@@ -1,5 +1,3 @@
1
- import type { FragmentNode } from '../models';
2
- export declare function createNodes(value: unknown): FragmentNode[];
3
- export declare function createTemplate(html: string): Node;
4
- export declare function getNodes(node: Node): Node[];
5
- export declare function replaceNodes(from: FragmentNode[], to: FragmentNode[]): void;
1
+ import type { ProperNode } from '../models';
2
+ export declare function createNodes(value: unknown): ProperNode[];
3
+ export declare function replaceNodes(from: ProperNode[], to: ProperNode[]): void;
@@ -1,5 +1,5 @@
1
1
  import type { Fragment } from '../fragment';
2
- import type { FragmentElement, FragmentNode } from '../models';
2
+ import type { ProperElement, ProperNode } from '../models';
3
3
  export declare function isFragment(value: unknown): value is Fragment;
4
- export declare function isFragmentElement(value: unknown): value is FragmentElement;
5
- export declare function isFragmentNode(value: unknown): value is FragmentNode;
4
+ export declare function isProperElement(value: unknown): value is ProperElement;
5
+ export declare function isProperNode(value: unknown): value is ProperNode;
package/types/models.d.ts CHANGED
@@ -1,8 +1,13 @@
1
+ import type { Fragment } from './fragment';
1
2
  export type FragmentData = {
2
3
  expressions: unknown[];
3
- nodes: Node[];
4
+ items: FragmentItem[];
4
5
  strings: TemplateStringsArray;
5
6
  values: unknown[];
6
7
  };
7
- export type FragmentElement = HTMLElement | SVGElement;
8
- export type FragmentNode = Comment | Element | Text;
8
+ export type FragmentItem = {
9
+ fragment?: Fragment;
10
+ nodes: ProperNode[];
11
+ };
12
+ export type ProperElement = HTMLElement | SVGElement;
13
+ export type ProperNode = CharacterData | Element;
@@ -1,3 +1,2 @@
1
- import type { FragmentData, FragmentElement } from '../../models';
2
- export declare function isBadAttribute(name: string, value: string): boolean;
3
- export declare function mapAttributes(data: FragmentData, element: FragmentElement): void;
1
+ import type { FragmentData, ProperElement } from '../../models';
2
+ export declare function mapAttributes(data: FragmentData, element: ProperElement): void;
@@ -1,2 +1,2 @@
1
- import type { FragmentElement } from '../../models';
2
- export declare function setAttribute(element: FragmentElement, name: string, value: unknown): void;
1
+ import type { ProperElement } from '../../models';
2
+ export declare function setAttribute(element: ProperElement, name: string, value: unknown): void;
@@ -1,2 +1,2 @@
1
- import type { FragmentElement } from '../models';
2
- export declare function mapEvent(element: FragmentElement, name: string, value: unknown): void;
1
+ import type { ProperElement } from '../models';
2
+ export declare function mapEvent(element: ProperElement, name: string, value: unknown): void;
@@ -1,2 +1,2 @@
1
- import type { FragmentData } from '../models';
2
- export declare function mapNodes(data: FragmentData, nodes: Node[]): void;
1
+ import type { FragmentData, ProperNode } from '../models';
2
+ export declare function mapNodes(data: FragmentData, nodes: ProperNode[]): void;
@@ -1,2 +1,3 @@
1
1
  import { type Reactive } from '@oscarpalmer/sentinel';
2
- export declare function setReactiveNode(comment: Comment, reactive: Reactive): void;
2
+ import type { FragmentData } from '../models';
3
+ export declare function setReactiveNode(data: FragmentData, comment: Comment, reactive: Reactive): void;