@oscarpalmer/abydon 0.22.0 → 0.23.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/abydon.full.mjs +222 -160
- package/dist/attribute/index.d.mts +3 -2
- package/dist/attribute/index.mjs +21 -16
- package/dist/attribute/value.mjs +18 -28
- package/dist/constants.d.mts +4 -8
- package/dist/constants.mjs +6 -10
- package/dist/fragment.d.mts +27 -14
- package/dist/fragment.mjs +32 -16
- package/dist/fragments.d.mts +1 -2
- package/dist/fragments.mjs +2 -2
- package/dist/helpers/dom.d.mts +2 -3
- package/dist/helpers/dom.mjs +2 -4
- package/dist/helpers/index.d.mts +9 -5
- package/dist/helpers/index.mjs +11 -5
- package/dist/index.d.mts +33 -6
- package/dist/index.mjs +33 -6
- package/dist/models.d.mts +6 -4
- package/dist/node/index.mjs +18 -33
- package/dist/node/value.mjs +67 -50
- package/package.json +2 -2
- package/src/attribute/index.ts +41 -35
- package/src/attribute/value.ts +20 -53
- package/src/constants.ts +6 -14
- package/src/fragment.ts +34 -17
- package/src/fragments.ts +4 -2
- package/src/helpers/dom.ts +3 -5
- package/src/helpers/index.ts +19 -5
- package/src/index.ts +33 -6
- package/src/models.ts +6 -4
- package/src/node/index.ts +25 -63
- package/src/node/value.ts +117 -85
- package/src/parse.ts +1 -1
package/src/fragments.ts
CHANGED
|
@@ -74,7 +74,9 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
instance.
|
|
77
|
+
instance.configure({
|
|
78
|
+
identifier: key,
|
|
79
|
+
});
|
|
78
80
|
|
|
79
81
|
state.instances[key] = instance;
|
|
80
82
|
|
|
@@ -88,7 +90,7 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
|
88
90
|
updateFragments(state, keys);
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
function initializeFragments(state: FragmentsState): void {
|
|
92
94
|
state.subscriber ??= state.array.subscribe(items => {
|
|
93
95
|
handleItems(state, items);
|
|
94
96
|
});
|
package/src/helpers/dom.ts
CHANGED
|
@@ -14,10 +14,6 @@ export function createNodes(value: unknown): ChildNode[] {
|
|
|
14
14
|
return [new Text(getString(value))];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function isInputElement(node: Node): node is HTMLInputElement | HTMLSelectElement {
|
|
18
|
-
return node instanceof HTMLInputElement || node instanceof HTMLSelectElement;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
17
|
export function removeNodes(nodes: ChildNode[]): void {
|
|
22
18
|
const {length} = nodes;
|
|
23
19
|
|
|
@@ -26,7 +22,7 @@ export function removeNodes(nodes: ChildNode[]): void {
|
|
|
26
22
|
}
|
|
27
23
|
}
|
|
28
24
|
|
|
29
|
-
export function replaceNodes(from: ChildNode[], to: ChildNode[]):
|
|
25
|
+
export function replaceNodes(from: ChildNode[], to: ChildNode[]): ChildNode[] {
|
|
30
26
|
from[0]?.replaceWith(...to);
|
|
31
27
|
|
|
32
28
|
const {length} = from;
|
|
@@ -34,4 +30,6 @@ export function replaceNodes(from: ChildNode[], to: ChildNode[]): void {
|
|
|
34
30
|
for (let index = 1; index < length; index += 1) {
|
|
35
31
|
from[index].remove();
|
|
36
32
|
}
|
|
33
|
+
|
|
34
|
+
return to;
|
|
37
35
|
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
1
|
+
import type {GenericCallback, PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {computed, type Computed} from '@oscarpalmer/mora';
|
|
2
3
|
import {
|
|
3
4
|
ARRAY_COMPARISON_ADDED,
|
|
4
5
|
ARRAY_COMPARISON_DISSIMILAR,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
} from '../constants';
|
|
9
10
|
import type {Fragment} from '../fragment';
|
|
10
11
|
import type {Fragments} from '../fragments';
|
|
12
|
+
import type {FragmentData} from '../models';
|
|
11
13
|
|
|
12
14
|
export function compareArrays(
|
|
13
15
|
first: unknown[],
|
|
@@ -28,18 +30,18 @@ export function compareArrays(
|
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
|
-
* Is the value a
|
|
33
|
+
* Is the value a _Fragment_?
|
|
32
34
|
* @param value Value to check
|
|
33
|
-
* @returns `true` if the value is a
|
|
35
|
+
* @returns `true` if the value is a _Fragment_, otherwise `false`
|
|
34
36
|
*/
|
|
35
37
|
export function isFragment(value: unknown): value is Fragment {
|
|
36
38
|
return isNamed(value, NAME_FRAGMENT);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
|
-
* Is the value a
|
|
42
|
+
* Is the value a _Fragments_ instance?
|
|
41
43
|
* @param value Value to check
|
|
42
|
-
* @returns `true` if the value is a
|
|
44
|
+
* @returns `true` if the value is a _Fragments_ instance, otherwise `false`
|
|
43
45
|
*/
|
|
44
46
|
export function isFragments(value: unknown): value is Fragments {
|
|
45
47
|
return isNamed(value, NAME_FRAGMENTS);
|
|
@@ -53,3 +55,15 @@ function isNamed(value: unknown, name: string): boolean {
|
|
|
53
55
|
(value as PlainObject)[name] === true
|
|
54
56
|
);
|
|
55
57
|
}
|
|
58
|
+
|
|
59
|
+
export function setComputedValue(
|
|
60
|
+
data: FragmentData,
|
|
61
|
+
callback: GenericCallback,
|
|
62
|
+
after: (computation: Computed<unknown>) => void,
|
|
63
|
+
): void {
|
|
64
|
+
const computation = computed(callback);
|
|
65
|
+
|
|
66
|
+
data.mora.values.add(computation);
|
|
67
|
+
|
|
68
|
+
after(computation);
|
|
69
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,11 +4,27 @@ import {Fragment} from './fragment';
|
|
|
4
4
|
import {Fragments} from './fragments';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Create a
|
|
7
|
+
* Create a _Fragments_ instance from a reactive array
|
|
8
|
+
*
|
|
9
|
+
* _A Fragments instance can be used to efficiently render a list of items that may change over time, using unique identifiers to track each item, only adding, removing, or updating each related Fragment._
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const fruits = array(['Apple', 'Banana', 'Cherry']);
|
|
14
|
+
* const items = fragments(
|
|
15
|
+
* fruits,
|
|
16
|
+
* fruit => fruit, // Identifies a unique item
|
|
17
|
+
* fruit => html`<p>${fruit}</p>`, // Creates a Fragment from an item
|
|
18
|
+
* );
|
|
19
|
+
* html`${items}`.appendTo(document.body) // Renders '<p>Apple</p><p>Banana</p><p>Cherry</p>'
|
|
20
|
+
* fruits.push(['Date', 'Elderberry', 'Fig']); // Appends '<p>Date</p><p>Elderberry</p><p>Fig</p>'
|
|
21
|
+
* // without re-rendering the existing Fragments
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
8
24
|
* @param array Reactive array
|
|
9
|
-
* @param identify Function to identify item
|
|
10
|
-
* @param fragment Function to create
|
|
11
|
-
* @returns
|
|
25
|
+
* @param identify Function to identify item uniquely _(non-nullable)_
|
|
26
|
+
* @param fragment Function to create _Fragment_ from item
|
|
27
|
+
* @returns _Fragments_
|
|
12
28
|
*/
|
|
13
29
|
export function fragments<Item>(
|
|
14
30
|
array: ReactiveArray<Item>,
|
|
@@ -31,8 +47,19 @@ export function fragments<Item>(
|
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
/**
|
|
34
|
-
* Create a
|
|
35
|
-
*
|
|
50
|
+
* Create a _Fragment_ from a template
|
|
51
|
+
*
|
|
52
|
+
* _A Fragment can be used to efficiently render a template that may change over time, only updating the necessary parts of the DOM._
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const name = signal('World');
|
|
57
|
+
* const fragment = html`<p>Hello, ${name}!</p>`;
|
|
58
|
+
* fragment.appendTo(document.body); // Renders '<p>Hello, World!</p>'
|
|
59
|
+
* name.set('Alice'); // Replaces 'World' with 'Alice'
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @returns _Fragment_
|
|
36
63
|
*/
|
|
37
64
|
export function html(template: TemplateStringsArray, ...values: unknown[]): Fragment {
|
|
38
65
|
return new Fragment(template, values);
|
package/src/models.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import type {Reactive, ReactiveArray, Unsubscribe} from '@oscarpalmer/mora';
|
|
2
2
|
import type {Fragment} from './fragment';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for a _Fragment_
|
|
6
|
+
*/
|
|
4
7
|
export type FragmentConfiguration = {
|
|
5
8
|
/**
|
|
6
9
|
* Should the template be cached? _(defaults to `true`)_
|
|
7
10
|
*/
|
|
8
11
|
cache?: boolean;
|
|
9
12
|
/**
|
|
10
|
-
* Identifier for the
|
|
13
|
+
* Identifier for the _Fragment_
|
|
11
14
|
*
|
|
12
|
-
*
|
|
13
|
-
* which helps prevent re-rendering in certain scenarios)_
|
|
15
|
+
* _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
|
|
14
16
|
*/
|
|
15
17
|
identifier?: unknown;
|
|
16
18
|
};
|
|
@@ -19,7 +21,7 @@ export type FragmentData = {
|
|
|
19
21
|
expressions: unknown[];
|
|
20
22
|
items: FragmentItem[];
|
|
21
23
|
mora: MoraData;
|
|
22
|
-
strings: TemplateStringsArray;
|
|
24
|
+
strings: TemplateStringsArray | string[];
|
|
23
25
|
template?: string;
|
|
24
26
|
values: unknown[];
|
|
25
27
|
};
|
package/src/node/index.ts
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
computed,
|
|
5
|
-
isComputed,
|
|
6
|
-
isReactive,
|
|
7
|
-
isSignal,
|
|
8
|
-
type Computed,
|
|
9
|
-
type ReactiveArray,
|
|
10
|
-
} from '@oscarpalmer/mora';
|
|
2
|
+
import {isReactive, type ReactiveArray} from '@oscarpalmer/mora';
|
|
11
3
|
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
12
|
-
import {mapAttributes} from '../attribute/index';
|
|
13
|
-
import {
|
|
4
|
+
import {mapAttributes, mapAttributeValue} from '../attribute/index';
|
|
5
|
+
import {EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE} from '../constants';
|
|
14
6
|
import {Fragments, fragmentsStates, handleFragments} from '../fragments';
|
|
15
|
-
import {isFragment, isFragments} from '../helpers';
|
|
7
|
+
import {isFragment, isFragments, setComputedValue} from '../helpers';
|
|
16
8
|
import {createNodes} from '../helpers/dom';
|
|
17
9
|
import type {FragmentData} from '../models';
|
|
18
|
-
import {mapEvent} from './event';
|
|
19
10
|
import {setReactiveValue} from './value';
|
|
20
11
|
|
|
21
12
|
function mapNode(data: FragmentData, comment: Comment): void {
|
|
@@ -39,12 +30,17 @@ export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
|
|
|
39
30
|
continue;
|
|
40
31
|
}
|
|
41
32
|
|
|
42
|
-
if (node instanceof HTMLTextAreaElement) {
|
|
43
|
-
mapTextarea(data, node);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
33
|
if (isHTMLOrSVGElement(node)) {
|
|
47
|
-
|
|
34
|
+
let ignoreValueAttribute = false;
|
|
35
|
+
|
|
36
|
+
if (node instanceof HTMLTextAreaElement) {
|
|
37
|
+
// Textareas are a special case because their value can be set via the `value` property
|
|
38
|
+
// or the text content. In order to support both, we need to check for the presence of
|
|
39
|
+
// the expression in both places and map it accordingly.
|
|
40
|
+
ignoreValueAttribute = mapTextarea(data, node);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
mapAttributes(data, node, ignoreValueAttribute);
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
if (node.hasChildNodes()) {
|
|
@@ -53,64 +49,32 @@ export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
|
|
|
53
49
|
}
|
|
54
50
|
}
|
|
55
51
|
|
|
56
|
-
function mapTextarea(data: FragmentData, element: HTMLTextAreaElement):
|
|
52
|
+
function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): boolean {
|
|
57
53
|
const [, index] =
|
|
58
54
|
EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ??
|
|
59
55
|
EXPRESSION_TEXTAREA_VALUE.exec(element.value) ??
|
|
60
56
|
[];
|
|
61
57
|
|
|
62
58
|
if (index == null) {
|
|
63
|
-
return;
|
|
59
|
+
return false;
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
element.textContent = '';
|
|
67
63
|
element.value = '';
|
|
68
64
|
|
|
69
|
-
|
|
65
|
+
mapAttributeValue(data, element, 'value', data.values[Number.parseInt(index, 10)]);
|
|
70
66
|
|
|
71
|
-
|
|
72
|
-
element.value = getString(value.peek());
|
|
73
|
-
|
|
74
|
-
mapEvent(element, EVENT_ON_VALUE, () => {
|
|
75
|
-
value.set(element.value);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
data.mora.subscribers.add(
|
|
79
|
-
value.subscribe(value => {
|
|
80
|
-
element.value = getString(value);
|
|
81
|
-
}),
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
let reactive: Computed<unknown> | undefined;
|
|
88
|
-
|
|
89
|
-
if (typeof value === 'function') {
|
|
90
|
-
reactive = computed(value as GenericCallback);
|
|
91
|
-
} else if (isComputed(value)) {
|
|
92
|
-
reactive = value;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (reactive == null) {
|
|
96
|
-
element.value = '';
|
|
97
|
-
} else {
|
|
98
|
-
data.mora.subscribers.add(
|
|
99
|
-
reactive.subscribe(value => {
|
|
100
|
-
element.value = getString(value);
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
|
-
}
|
|
67
|
+
return true;
|
|
104
68
|
}
|
|
105
69
|
|
|
106
70
|
function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
|
|
107
71
|
switch (true) {
|
|
108
72
|
case typeof value === 'function':
|
|
109
|
-
|
|
73
|
+
setComputedNode(data, comment, value as GenericCallback);
|
|
110
74
|
break;
|
|
111
75
|
|
|
112
76
|
case isFragments(value):
|
|
113
|
-
|
|
77
|
+
setFragmentsNode(data, comment, value);
|
|
114
78
|
break;
|
|
115
79
|
|
|
116
80
|
case isReactive(value):
|
|
@@ -135,15 +99,13 @@ function replaceComment(data: FragmentData, comment: Comment, value: unknown): v
|
|
|
135
99
|
comment.replaceWith(...nodes);
|
|
136
100
|
}
|
|
137
101
|
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
setReactiveValue(data, comment, value);
|
|
102
|
+
function setComputedNode(data: FragmentData, comment: Comment, callback: GenericCallback): void {
|
|
103
|
+
setComputedValue(data, callback, computation => {
|
|
104
|
+
setReactiveValue(data, comment, computation);
|
|
105
|
+
});
|
|
144
106
|
}
|
|
145
107
|
|
|
146
|
-
function
|
|
108
|
+
function setFragmentsNode(data: FragmentData, comment: Comment, fragments: Fragments): void {
|
|
147
109
|
const state = fragmentsStates.get(fragments)!;
|
|
148
110
|
|
|
149
111
|
handleFragments(state, false);
|
package/src/node/value.ts
CHANGED
|
@@ -10,9 +10,30 @@ import type {FragmentData, FragmentItem} from '../models';
|
|
|
10
10
|
|
|
11
11
|
//
|
|
12
12
|
|
|
13
|
+
type ArrayData = {
|
|
14
|
+
next: ArrayDataIdentifiers;
|
|
15
|
+
previous: ArrayDataIdentifiers;
|
|
16
|
+
template: ArrayDataTemplate;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type ArrayDataIdentifiers = {
|
|
20
|
+
array: unknown[];
|
|
21
|
+
set: Set<unknown>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type ArrayDataTemplate = {
|
|
25
|
+
empty: boolean;
|
|
26
|
+
items: Fragment[];
|
|
27
|
+
};
|
|
28
|
+
|
|
13
29
|
type Identifiers = {
|
|
14
|
-
next:
|
|
15
|
-
previous:
|
|
30
|
+
next: IdentifiersValues;
|
|
31
|
+
previous: IdentifiersValues;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type IdentifiersValues = {
|
|
35
|
+
array: unknown[];
|
|
36
|
+
set: Set<unknown>;
|
|
16
37
|
};
|
|
17
38
|
|
|
18
39
|
type BaseItems = {
|
|
@@ -34,7 +55,7 @@ function addToArray(
|
|
|
34
55
|
): void {
|
|
35
56
|
let position = nodes[0];
|
|
36
57
|
|
|
37
|
-
const before = added && !identifiers.previous.has(items.templates[0].identifier);
|
|
58
|
+
const before = added && !identifiers.previous.set.has(items.templates[0].identifier);
|
|
38
59
|
|
|
39
60
|
const next = items.next.flatMap(fragment =>
|
|
40
61
|
fragment.get().flatMap(node => ({
|
|
@@ -48,7 +69,7 @@ function addToArray(
|
|
|
48
69
|
for (let index = 0; index < length; index += 1) {
|
|
49
70
|
const node = next[index];
|
|
50
71
|
|
|
51
|
-
if (!(added && identifiers.previous.has(node.identifier))) {
|
|
72
|
+
if (!(added && identifiers.previous.set.has(node.identifier))) {
|
|
52
73
|
if (index === 0 && before) {
|
|
53
74
|
position.before(node.value);
|
|
54
75
|
} else {
|
|
@@ -60,6 +81,46 @@ function addToArray(
|
|
|
60
81
|
}
|
|
61
82
|
}
|
|
62
83
|
|
|
84
|
+
function getArrayData(item: FragmentItem, values: unknown[]): ArrayData {
|
|
85
|
+
const {length} = values;
|
|
86
|
+
|
|
87
|
+
const next: unknown[] = [];
|
|
88
|
+
|
|
89
|
+
let templates: Fragment[] = [];
|
|
90
|
+
|
|
91
|
+
for (let index = 0; index < length; index += 1) {
|
|
92
|
+
const value = values[index];
|
|
93
|
+
|
|
94
|
+
if (isFragment(value) && value.identifier != null) {
|
|
95
|
+
next.push(value.identifier);
|
|
96
|
+
templates.push(value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const previous = item.fragments?.map(fragment => fragment.identifier) ?? [];
|
|
101
|
+
|
|
102
|
+
const nextSet = new Set(next);
|
|
103
|
+
|
|
104
|
+
if (nextSet.size !== templates.length) {
|
|
105
|
+
templates = [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
next: {
|
|
110
|
+
array: next,
|
|
111
|
+
set: nextSet,
|
|
112
|
+
},
|
|
113
|
+
previous: {
|
|
114
|
+
array: previous,
|
|
115
|
+
set: new Set(previous),
|
|
116
|
+
},
|
|
117
|
+
template: {
|
|
118
|
+
empty: templates.length === 0,
|
|
119
|
+
items: templates,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
63
124
|
function handleArray(
|
|
64
125
|
identifiers: Identifiers,
|
|
65
126
|
items: BaseItems,
|
|
@@ -67,17 +128,18 @@ function handleArray(
|
|
|
67
128
|
): Partial<FragmentItem> {
|
|
68
129
|
const next = items.templates.map(
|
|
69
130
|
template =>
|
|
70
|
-
items.fragments
|
|
131
|
+
items.fragments.find(fragment => fragment.identifier === template.identifier) ?? template,
|
|
71
132
|
);
|
|
72
133
|
|
|
73
|
-
const comparison = compareArrays(
|
|
134
|
+
const comparison = compareArrays(identifiers.previous.array, identifiers.next.array);
|
|
74
135
|
|
|
75
136
|
if (comparison !== ARRAY_COMPARISON_REMOVED) {
|
|
76
137
|
addToArray(identifiers, {...items, next}, nodes, comparison === ARRAY_COMPARISON_ADDED);
|
|
77
138
|
}
|
|
78
139
|
|
|
79
|
-
const toRemove =
|
|
80
|
-
|
|
140
|
+
const toRemove = items.fragments.filter(
|
|
141
|
+
fragment => !identifiers.next.set.has(fragment.identifier),
|
|
142
|
+
);
|
|
81
143
|
|
|
82
144
|
const {length} = toRemove;
|
|
83
145
|
|
|
@@ -91,84 +153,70 @@ function handleArray(
|
|
|
91
153
|
};
|
|
92
154
|
}
|
|
93
155
|
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
const {length} = fragments;
|
|
156
|
+
function removeArray(item: FragmentItem, comment: Comment): Partial<FragmentItem> {
|
|
157
|
+
const fragments = (item.fragments ?? []).slice();
|
|
97
158
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
159
|
+
const result = {
|
|
160
|
+
nodes: setText(item, comment),
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
removeFragmentsItems(fragments);
|
|
164
|
+
|
|
165
|
+
return result;
|
|
102
166
|
}
|
|
103
167
|
|
|
104
|
-
function
|
|
105
|
-
|
|
168
|
+
function removeFragmentsItems(fragments: Fragment[]): void {
|
|
169
|
+
const {length} = fragments;
|
|
106
170
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
} else {
|
|
110
|
-
to = item.text == null ? [] : [item.text];
|
|
171
|
+
for (let index = 0; index < length; index += 1) {
|
|
172
|
+
fragments[index].remove();
|
|
111
173
|
}
|
|
112
|
-
|
|
113
|
-
replaceNodes(item.nodes ?? [], to);
|
|
114
174
|
}
|
|
115
175
|
|
|
116
176
|
function setArray(item: FragmentItem, comment: Comment, value: unknown[]): Partial<FragmentItem> {
|
|
117
177
|
if (value.length === 0) {
|
|
118
|
-
return
|
|
119
|
-
nodes: setText(item, comment, value),
|
|
120
|
-
};
|
|
178
|
+
return removeArray(item, comment);
|
|
121
179
|
}
|
|
122
180
|
|
|
123
|
-
|
|
181
|
+
const {next, previous, template} = getArrayData(item, value);
|
|
124
182
|
|
|
125
|
-
|
|
126
|
-
|
|
183
|
+
if (
|
|
184
|
+
template.empty ||
|
|
185
|
+
item.nodes == null ||
|
|
186
|
+
previous.array.some(identifier => identifier == null)
|
|
187
|
+
) {
|
|
188
|
+
const fragments = (item.fragments ?? []).slice();
|
|
127
189
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const noTemplates = templates.length === 0;
|
|
135
|
-
|
|
136
|
-
if (noTemplates || item.nodes == null || previous.some(identifier => identifier == null)) {
|
|
137
|
-
return {
|
|
138
|
-
fragments: noTemplates ? undefined : templates,
|
|
139
|
-
nodes: setNodes(
|
|
140
|
-
item,
|
|
141
|
-
comment,
|
|
142
|
-
noTemplates
|
|
190
|
+
const result = {
|
|
191
|
+
fragments: template.empty ? undefined : template.items,
|
|
192
|
+
nodes: replaceNodes(
|
|
193
|
+
item.nodes ?? [comment],
|
|
194
|
+
template.empty
|
|
143
195
|
? value.flatMap(item => createNodes(item))
|
|
144
|
-
:
|
|
196
|
+
: template.items.flatMap(template => template.get()),
|
|
145
197
|
),
|
|
146
198
|
};
|
|
199
|
+
|
|
200
|
+
removeFragmentsItems(fragments);
|
|
201
|
+
|
|
202
|
+
return result;
|
|
147
203
|
}
|
|
148
204
|
|
|
149
205
|
return handleArray(
|
|
150
206
|
{
|
|
151
|
-
next
|
|
152
|
-
previous
|
|
207
|
+
next,
|
|
208
|
+
previous,
|
|
153
209
|
},
|
|
154
210
|
{
|
|
155
|
-
templates,
|
|
156
211
|
fragments: item.fragments ?? [],
|
|
212
|
+
templates: template.items,
|
|
157
213
|
},
|
|
158
214
|
item.nodes,
|
|
159
215
|
);
|
|
160
216
|
}
|
|
161
217
|
|
|
162
218
|
function setNodes(item: FragmentItem, comment: Comment, next: ChildNode[]): ChildNode[] {
|
|
163
|
-
|
|
164
|
-
replaceNodes([comment], next);
|
|
165
|
-
} else {
|
|
166
|
-
replaceNodes(item.nodes, next);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
removeFragments(item.fragments);
|
|
170
|
-
|
|
171
|
-
return next;
|
|
219
|
+
return replaceNodes(item.nodes ?? [comment], next);
|
|
172
220
|
}
|
|
173
221
|
|
|
174
222
|
export function setReactiveValue(
|
|
@@ -180,8 +228,6 @@ export function setReactiveValue(
|
|
|
180
228
|
|
|
181
229
|
item ??= {};
|
|
182
230
|
|
|
183
|
-
item.text = new Text();
|
|
184
|
-
|
|
185
231
|
data.mora.subscribers.add(
|
|
186
232
|
reactive.subscribe(value => {
|
|
187
233
|
if (Array.isArray(value)) {
|
|
@@ -189,8 +235,6 @@ export function setReactiveValue(
|
|
|
189
235
|
} else {
|
|
190
236
|
setReactiveValueForSingle(item, comment, value);
|
|
191
237
|
}
|
|
192
|
-
|
|
193
|
-
item.nodes ??= [comment];
|
|
194
238
|
}),
|
|
195
239
|
);
|
|
196
240
|
}
|
|
@@ -203,43 +247,31 @@ function setReactiveValueForArray(item: FragmentItem, comment: Comment, value: u
|
|
|
203
247
|
}
|
|
204
248
|
|
|
205
249
|
function setReactiveValueForSingle(item: FragmentItem, comment: Comment, value: unknown): void {
|
|
206
|
-
const
|
|
250
|
+
const fragments = (item.fragments ?? []).slice();
|
|
207
251
|
|
|
208
|
-
|
|
252
|
+
const valueIsFragment = isFragment(value);
|
|
209
253
|
|
|
210
254
|
if (valueIsFragment || isChildNode(value)) {
|
|
211
255
|
item.nodes = setNodes(item, comment, createNodes(value));
|
|
212
256
|
} else {
|
|
213
257
|
item.nodes = setText(item, comment, value);
|
|
214
258
|
}
|
|
215
|
-
}
|
|
216
259
|
|
|
217
|
-
|
|
218
|
-
const isNullable = isNullableOrWhitespace(value);
|
|
260
|
+
item.fragments = valueIsFragment ? [value] : undefined;
|
|
219
261
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
262
|
+
removeFragmentsItems(fragments);
|
|
263
|
+
}
|
|
223
264
|
|
|
224
|
-
|
|
265
|
+
function setText(item: FragmentItem, comment: Comment, value?: unknown): ChildNode[] {
|
|
266
|
+
const valueIsNullable = isNullableOrWhitespace(value);
|
|
225
267
|
|
|
226
|
-
|
|
227
|
-
replaceText(item, comment, isNullable);
|
|
268
|
+
item.text ??= new Text();
|
|
228
269
|
|
|
229
|
-
|
|
230
|
-
} else if (isNullable && comment.parentNode == null) {
|
|
231
|
-
item.text?.replaceWith(comment);
|
|
232
|
-
} else if (!isNullable && item?.text?.parentNode == null) {
|
|
233
|
-
if (item.text != null) {
|
|
234
|
-
comment.replaceWith(item.text);
|
|
235
|
-
}
|
|
270
|
+
item.text.textContent = valueIsNullable ? '' : getString(value);
|
|
236
271
|
|
|
237
|
-
|
|
238
|
-
}
|
|
272
|
+
const result = valueIsNullable ? [comment] : [item.text];
|
|
239
273
|
|
|
240
|
-
|
|
274
|
+
replaceNodes(item.nodes ?? [comment], result);
|
|
241
275
|
|
|
242
|
-
|
|
243
|
-
return item.text == null ? [] : [item.text];
|
|
244
|
-
}
|
|
276
|
+
return result;
|
|
245
277
|
}
|
package/src/parse.ts
CHANGED
|
@@ -48,7 +48,7 @@ export function parse(data: FragmentData): string {
|
|
|
48
48
|
data.template = data.template.replaceAll(EXPRESSION_ABYDON_ATTRIBUTE_FULL, transformAttribute);
|
|
49
49
|
|
|
50
50
|
data.expressions = [];
|
|
51
|
-
data.strings = []
|
|
51
|
+
data.strings = [];
|
|
52
52
|
|
|
53
53
|
return data.template;
|
|
54
54
|
}
|