@oscarpalmer/abydon 0.23.0 → 0.24.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 +1505 -1032
- package/dist/attribute/index.d.mts +4 -6
- package/dist/attribute/index.mjs +4 -5
- package/dist/attribute/value.d.mts +3 -5
- package/dist/attribute/value.mjs +2 -4
- package/dist/constants.d.mts +42 -38
- package/dist/constants.mjs +12 -7
- package/dist/fragment.d.mts +31 -57
- package/dist/fragment.mjs +83 -108
- package/dist/fragments.d.mts +28 -11
- package/dist/fragments.mjs +60 -33
- package/dist/helpers/dom.d.mts +4 -5
- package/dist/helpers/index.d.mts +8 -10
- package/dist/helpers/index.mjs +4 -2
- package/dist/index.d.mts +5 -47
- package/dist/index.mjs +4 -53
- package/dist/models.d.mts +84 -14
- package/dist/node/event.d.mts +2 -3
- package/dist/node/index.d.mts +3 -5
- package/dist/node/index.mjs +5 -8
- package/dist/node/value.d.mts +3 -5
- package/dist/node/value.mjs +8 -7
- package/dist/parse.d.mts +3 -5
- package/package.json +14 -14
- package/src/attribute/index.ts +13 -7
- package/src/attribute/value.ts +11 -7
- package/src/constants.ts +24 -8
- package/src/fragment.ts +154 -130
- package/src/fragments.ts +94 -45
- package/src/helpers/dom.ts +4 -0
- package/src/helpers/index.ts +12 -7
- package/src/index.ts +3 -67
- package/src/models.ts +97 -9
- package/src/node/event.ts +4 -0
- package/src/node/index.ts +17 -15
- package/src/node/value.ts +12 -10
- package/src/parse.ts +7 -3
package/src/fragments.ts
CHANGED
|
@@ -1,47 +1,92 @@
|
|
|
1
1
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
2
|
-
import {array, type ReactiveArray} from '@oscarpalmer/mora';
|
|
2
|
+
import {array, isReactiveArray, type ReactiveArray} from '@oscarpalmer/mora';
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
MESSAGE_FRAGMENTS_FRAGMENT_RESULT,
|
|
5
|
+
MESSAGE_FRAGMENTS_FRAGMENT_TYPE,
|
|
6
|
+
MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_DUPLICATE,
|
|
7
|
+
MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_TYPE,
|
|
8
|
+
MESSAGE_FRAGMENTS_IDENTIFIER_TYPE,
|
|
9
|
+
MESSAGE_FRAGMENTS_VALUE,
|
|
7
10
|
NAME_FRAGMENTS,
|
|
11
|
+
SYMBOL,
|
|
8
12
|
TEMPLATE_ITEM,
|
|
9
13
|
} from './constants';
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
import {isFragment} from './helpers';
|
|
15
|
+
import type {Fragment, Fragments, FragmentsState, InternalFragments} from './models';
|
|
16
|
+
|
|
17
|
+
// #region Instances
|
|
18
|
+
|
|
19
|
+
function Fragments(
|
|
20
|
+
this: any,
|
|
21
|
+
items: ReactiveArray<unknown>,
|
|
22
|
+
identify: (item: unknown) => unknown,
|
|
23
|
+
fragment: (item: unknown) => Fragment,
|
|
24
|
+
) {
|
|
25
|
+
this[SYMBOL] = {
|
|
26
|
+
fragment,
|
|
27
|
+
identify,
|
|
28
|
+
array: items,
|
|
29
|
+
instances: {},
|
|
30
|
+
mapped: array<Fragment>([]),
|
|
31
|
+
name: NAME_FRAGMENTS,
|
|
32
|
+
subscription: undefined,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
initializeFragments.call(this);
|
|
36
|
+
}
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
Fragments.prototype.remove = removeFragments;
|
|
39
|
+
|
|
40
|
+
// #endregion
|
|
41
|
+
|
|
42
|
+
// #region Functions
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create a _Fragments_ instance from a reactive array
|
|
46
|
+
*
|
|
47
|
+
* _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._
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const fruits = array(['Apple', 'Banana', 'Cherry']);
|
|
52
|
+
* const items = fragments(
|
|
53
|
+
* fruits,
|
|
54
|
+
* fruit => fruit, // Identifies a unique item
|
|
55
|
+
* fruit => html`<p>${fruit}</p>`, // Creates a Fragment from an item
|
|
56
|
+
* );
|
|
57
|
+
* html`${items}`.appendTo(document.body) // Renders '<p>Apple</p><p>Banana</p><p>Cherry</p>'
|
|
58
|
+
* fruits.push(['Date', 'Elderberry', 'Fig']); // Appends '<p>Date</p><p>Elderberry</p><p>Fig</p>'
|
|
59
|
+
* // without re-rendering the existing Fragments
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @param array Reactive array
|
|
63
|
+
* @param identify Function to identify item uniquely _(non-nullable)_
|
|
64
|
+
* @param fragment Function to create _Fragment_ from item
|
|
65
|
+
* @returns _Fragments_
|
|
66
|
+
*/
|
|
67
|
+
export function fragments<Item>(
|
|
68
|
+
array: ReactiveArray<Item>,
|
|
69
|
+
identify: (item: Item) => unknown,
|
|
70
|
+
fragment: (item: Item) => Fragment,
|
|
71
|
+
): Fragments {
|
|
72
|
+
if (!isReactiveArray(array)) {
|
|
73
|
+
throw new TypeError(MESSAGE_FRAGMENTS_VALUE);
|
|
74
|
+
}
|
|
34
75
|
|
|
35
|
-
|
|
76
|
+
if (typeof identify !== 'function') {
|
|
77
|
+
throw new TypeError(MESSAGE_FRAGMENTS_IDENTIFIER_TYPE);
|
|
78
|
+
}
|
|
36
79
|
|
|
37
|
-
|
|
80
|
+
if (typeof fragment !== 'function') {
|
|
81
|
+
throw new TypeError(MESSAGE_FRAGMENTS_FRAGMENT_TYPE);
|
|
38
82
|
}
|
|
39
|
-
}
|
|
40
83
|
|
|
41
|
-
|
|
42
|
-
|
|
84
|
+
// @ts-expect-error All good, no worries :-)
|
|
85
|
+
return new Fragments(array as ReactiveArray<unknown>, identify as never, fragment as never);
|
|
86
|
+
}
|
|
43
87
|
|
|
44
|
-
|
|
88
|
+
export function handleFragments(instance: Fragments, remove: boolean): void {
|
|
89
|
+
(remove ? removeFragments : initializeFragments).call(instance as InternalFragments);
|
|
45
90
|
}
|
|
46
91
|
|
|
47
92
|
function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
@@ -55,13 +100,13 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
|
55
100
|
const identifier = state.identify(item);
|
|
56
101
|
|
|
57
102
|
if (identifier == null) {
|
|
58
|
-
throw new TypeError(
|
|
103
|
+
throw new TypeError(MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_TYPE);
|
|
59
104
|
}
|
|
60
105
|
|
|
61
106
|
const key = getString(identifier);
|
|
62
107
|
|
|
63
108
|
if (keys.has(key)) {
|
|
64
|
-
throw new Error(
|
|
109
|
+
throw new Error(MESSAGE_FRAGMENTS_IDENTIFIER_RESULT_DUPLICATE.replace(TEMPLATE_ITEM, key));
|
|
65
110
|
}
|
|
66
111
|
|
|
67
112
|
let instance = state.instances[key];
|
|
@@ -70,7 +115,7 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
|
70
115
|
instance = state.fragment(item);
|
|
71
116
|
|
|
72
117
|
if (!isFragment(instance)) {
|
|
73
|
-
throw new Error(
|
|
118
|
+
throw new Error(MESSAGE_FRAGMENTS_FRAGMENT_RESULT);
|
|
74
119
|
}
|
|
75
120
|
}
|
|
76
121
|
|
|
@@ -90,18 +135,24 @@ function handleItems(state: FragmentsState, items: unknown[]): void {
|
|
|
90
135
|
updateFragments(state, keys);
|
|
91
136
|
}
|
|
92
137
|
|
|
93
|
-
function initializeFragments(
|
|
94
|
-
|
|
138
|
+
function initializeFragments(this: InternalFragments): void {
|
|
139
|
+
const state = this[SYMBOL];
|
|
140
|
+
|
|
141
|
+
state.subscription ??= state.array.subscribe(items => {
|
|
95
142
|
handleItems(state, items);
|
|
96
143
|
});
|
|
97
144
|
}
|
|
98
145
|
|
|
99
|
-
function removeFragments(
|
|
100
|
-
state
|
|
146
|
+
function removeFragments(this: InternalFragments): void {
|
|
147
|
+
const state = this[SYMBOL];
|
|
148
|
+
|
|
149
|
+
state.subscription?.unsubscribe();
|
|
150
|
+
|
|
151
|
+
state.mapped.set([]);
|
|
101
152
|
|
|
102
153
|
updateFragments(state);
|
|
103
154
|
|
|
104
|
-
state.
|
|
155
|
+
state.subscription = undefined;
|
|
105
156
|
}
|
|
106
157
|
|
|
107
158
|
function updateFragments(state: FragmentsState, active?: Set<string>): void {
|
|
@@ -116,7 +167,7 @@ function updateFragments(state: FragmentsState, active?: Set<string>): void {
|
|
|
116
167
|
|
|
117
168
|
if (active?.has(key)) {
|
|
118
169
|
next[key] = previous[key];
|
|
119
|
-
} else {
|
|
170
|
+
} else if (active == null) {
|
|
120
171
|
previous[key].remove();
|
|
121
172
|
}
|
|
122
173
|
}
|
|
@@ -126,6 +177,4 @@ function updateFragments(state: FragmentsState, active?: Set<string>): void {
|
|
|
126
177
|
active?.clear();
|
|
127
178
|
}
|
|
128
179
|
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
export const fragmentsStates: WeakMap<Fragments, FragmentsState> = new WeakMap();
|
|
180
|
+
// #endregion
|
package/src/helpers/dom.ts
CHANGED
|
@@ -2,6 +2,8 @@ import {getString} from '@oscarpalmer/atoms/string';
|
|
|
2
2
|
import {isChildNode} from '@oscarpalmer/toretto/is';
|
|
3
3
|
import {isFragment} from './index';
|
|
4
4
|
|
|
5
|
+
// #region Functions
|
|
6
|
+
|
|
5
7
|
export function createNodes(value: unknown): ChildNode[] {
|
|
6
8
|
if (isFragment(value)) {
|
|
7
9
|
return value.get() as ChildNode[];
|
|
@@ -33,3 +35,5 @@ export function replaceNodes(from: ChildNode[], to: ChildNode[]): ChildNode[] {
|
|
|
33
35
|
|
|
34
36
|
return to;
|
|
35
37
|
}
|
|
38
|
+
|
|
39
|
+
// #endregion
|
package/src/helpers/index.ts
CHANGED
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
ARRAY_COMPARISON_REMOVED,
|
|
7
7
|
NAME_FRAGMENT,
|
|
8
8
|
NAME_FRAGMENTS,
|
|
9
|
+
SYMBOL,
|
|
9
10
|
} from '../constants';
|
|
10
|
-
import type {Fragment} from '../
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
import type {Fragment, Fragments, FragmentState} from '../models';
|
|
12
|
+
|
|
13
|
+
// #region Functions
|
|
13
14
|
|
|
14
15
|
export function compareArrays(
|
|
15
16
|
first: unknown[],
|
|
@@ -31,6 +32,7 @@ export function compareArrays(
|
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Is the value a _Fragment_?
|
|
35
|
+
*
|
|
34
36
|
* @param value Value to check
|
|
35
37
|
* @returns `true` if the value is a _Fragment_, otherwise `false`
|
|
36
38
|
*/
|
|
@@ -40,6 +42,7 @@ export function isFragment(value: unknown): value is Fragment {
|
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* Is the value a _Fragments_ instance?
|
|
45
|
+
*
|
|
43
46
|
* @param value Value to check
|
|
44
47
|
* @returns `true` if the value is a _Fragments_ instance, otherwise `false`
|
|
45
48
|
*/
|
|
@@ -50,14 +53,14 @@ export function isFragments(value: unknown): value is Fragments {
|
|
|
50
53
|
function isNamed(value: unknown, name: string): boolean {
|
|
51
54
|
return (
|
|
52
55
|
typeof value === 'object' &&
|
|
53
|
-
value
|
|
54
|
-
|
|
55
|
-
(value as PlainObject)
|
|
56
|
+
value !== null &&
|
|
57
|
+
SYMBOL in value &&
|
|
58
|
+
(value[SYMBOL] as PlainObject).name === name
|
|
56
59
|
);
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
export function setComputedValue(
|
|
60
|
-
data:
|
|
63
|
+
data: FragmentState,
|
|
61
64
|
callback: GenericCallback,
|
|
62
65
|
after: (computation: Computed<unknown>) => void,
|
|
63
66
|
): void {
|
|
@@ -67,3 +70,5 @@ export function setComputedValue(
|
|
|
67
70
|
|
|
68
71
|
after(computation);
|
|
69
72
|
}
|
|
73
|
+
|
|
74
|
+
// #endregion
|
package/src/index.ts
CHANGED
|
@@ -1,71 +1,7 @@
|
|
|
1
1
|
import '@oscarpalmer/mora';
|
|
2
|
-
import {isArray, type ReactiveArray} from '@oscarpalmer/mora';
|
|
3
|
-
import {Fragment} from './fragment';
|
|
4
|
-
import {Fragments} from './fragments';
|
|
5
|
-
|
|
6
|
-
/**
|
|
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
|
-
*
|
|
24
|
-
* @param array Reactive array
|
|
25
|
-
* @param identify Function to identify item uniquely _(non-nullable)_
|
|
26
|
-
* @param fragment Function to create _Fragment_ from item
|
|
27
|
-
* @returns _Fragments_
|
|
28
|
-
*/
|
|
29
|
-
export function fragments<Item>(
|
|
30
|
-
array: ReactiveArray<Item>,
|
|
31
|
-
identify: (item: Item) => unknown,
|
|
32
|
-
fragment: (item: Item) => Fragment,
|
|
33
|
-
): Fragments {
|
|
34
|
-
if (!isArray(array)) {
|
|
35
|
-
throw new TypeError('Fragments array must be a reactive array');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (typeof identify !== 'function') {
|
|
39
|
-
throw new TypeError('Fragments identify must be a function');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (typeof fragment !== 'function') {
|
|
43
|
-
throw new TypeError('Fragments fragment must be a function');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return new Fragments(array as ReactiveArray<unknown>, identify as never, fragment as never);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
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_
|
|
63
|
-
*/
|
|
64
|
-
export function html(template: TemplateStringsArray, ...values: unknown[]): Fragment {
|
|
65
|
-
return new Fragment(template, values);
|
|
66
|
-
}
|
|
67
2
|
|
|
68
3
|
export * from '@oscarpalmer/mora';
|
|
69
|
-
export
|
|
70
|
-
export
|
|
4
|
+
export {fragment, html} from './fragment';
|
|
5
|
+
export {fragments} from './fragments';
|
|
71
6
|
export {isFragment, isFragments} from './helpers';
|
|
7
|
+
export type {Fragment, Fragments} from './models';
|
package/src/models.ts
CHANGED
|
@@ -1,5 +1,80 @@
|
|
|
1
|
-
import type {Reactive, ReactiveArray,
|
|
2
|
-
import type {
|
|
1
|
+
import type {Reactive, ReactiveArray, Subscription} from '@oscarpalmer/mora';
|
|
2
|
+
import type {SYMBOL} from './constants';
|
|
3
|
+
|
|
4
|
+
// #region Types
|
|
5
|
+
|
|
6
|
+
export type InternalFragment = {
|
|
7
|
+
[SYMBOL]: FragmentState;
|
|
8
|
+
} & Fragment;
|
|
9
|
+
|
|
10
|
+
export type InternalFragments = {
|
|
11
|
+
[SYMBOL]: FragmentsState;
|
|
12
|
+
} & Fragments;
|
|
13
|
+
|
|
14
|
+
export type Fragment = {
|
|
15
|
+
/**
|
|
16
|
+
* Is template caching enabled?
|
|
17
|
+
*/
|
|
18
|
+
get cache(): boolean;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Identifier for the _Fragment_
|
|
22
|
+
*
|
|
23
|
+
* _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
|
|
24
|
+
*/
|
|
25
|
+
get identifier(): unknown;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Insert the _Fragment_ after the given element
|
|
29
|
+
*
|
|
30
|
+
* @param element Element to insert after
|
|
31
|
+
*/
|
|
32
|
+
after(element: Element): void;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Append the _Fragment_ to the given element
|
|
36
|
+
*
|
|
37
|
+
* @param element Element to append to
|
|
38
|
+
*/
|
|
39
|
+
appendTo(element: Element): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Insert the _Fragment_ before the given element
|
|
43
|
+
*
|
|
44
|
+
* @param element Element to insert before
|
|
45
|
+
*/
|
|
46
|
+
before(element: Element): void;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Configure the _Fragment_
|
|
50
|
+
*
|
|
51
|
+
* @param configuration Configuration options
|
|
52
|
+
* @returns _Fragment_
|
|
53
|
+
*/
|
|
54
|
+
configure(configuration: FragmentConfiguration): Fragment;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get a list of the _Fragment_'s nodes
|
|
58
|
+
*
|
|
59
|
+
* @returns List of nodes
|
|
60
|
+
*/
|
|
61
|
+
get(): ChildNode[];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Prepend the _Fragment_ to the given element
|
|
65
|
+
*
|
|
66
|
+
* @param element Element to prepend to
|
|
67
|
+
*/
|
|
68
|
+
prependTo(element: Element): void;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Remove the _Fragment_ _(and all its descendants)_ from the _DOM_
|
|
72
|
+
*
|
|
73
|
+
* - _Any events, reactive values, and Fragments will also be cleaned up and removed_
|
|
74
|
+
* - _After being removed, the Fragment can be re-inserted into the DOM_
|
|
75
|
+
*/
|
|
76
|
+
remove(): void;
|
|
77
|
+
};
|
|
3
78
|
|
|
4
79
|
/**
|
|
5
80
|
* Configuration for a _Fragment_
|
|
@@ -17,19 +92,29 @@ export type FragmentConfiguration = {
|
|
|
17
92
|
identifier?: unknown;
|
|
18
93
|
};
|
|
19
94
|
|
|
20
|
-
export type
|
|
95
|
+
export type FragmentItem = {
|
|
96
|
+
fragments?: Fragment[];
|
|
97
|
+
nodes?: ChildNode[];
|
|
98
|
+
text?: Text;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export type FragmentState = {
|
|
102
|
+
cache: boolean;
|
|
21
103
|
expressions: unknown[];
|
|
104
|
+
identifier: unknown;
|
|
22
105
|
items: FragmentItem[];
|
|
23
106
|
mora: MoraData;
|
|
107
|
+
name: string;
|
|
24
108
|
strings: TemplateStringsArray | string[];
|
|
25
109
|
template?: string;
|
|
26
110
|
values: unknown[];
|
|
27
111
|
};
|
|
28
112
|
|
|
29
|
-
export type
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
113
|
+
export type Fragments = {
|
|
114
|
+
/**
|
|
115
|
+
* Remove the _Fragments_ _(and all its descendants)_ from the _DOM_, including any events, reactive values, and Fragments
|
|
116
|
+
*/
|
|
117
|
+
remove(): void;
|
|
33
118
|
};
|
|
34
119
|
|
|
35
120
|
export type FragmentsState = {
|
|
@@ -38,10 +123,13 @@ export type FragmentsState = {
|
|
|
38
123
|
identify: (item: unknown) => unknown;
|
|
39
124
|
instances: Record<string, Fragment>;
|
|
40
125
|
mapped: ReactiveArray<Fragment>;
|
|
41
|
-
|
|
126
|
+
name: string;
|
|
127
|
+
subscription: Subscription | undefined;
|
|
42
128
|
};
|
|
43
129
|
|
|
44
130
|
type MoraData = {
|
|
45
|
-
|
|
131
|
+
subscriptions: Set<Subscription>;
|
|
46
132
|
values: Set<Reactive<unknown>>;
|
|
47
133
|
};
|
|
134
|
+
|
|
135
|
+
// #endregion
|
package/src/node/event.ts
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
EXPRESSION_EVENT_OPTIONS_ONCE,
|
|
14
14
|
} from '../constants';
|
|
15
15
|
|
|
16
|
+
// #region Functions
|
|
17
|
+
|
|
16
18
|
function getOptions(options: string): AddEventListenerOptions {
|
|
17
19
|
const parts = options.split(EVENT_OPTIONS_DELIMITER);
|
|
18
20
|
|
|
@@ -53,3 +55,5 @@ export function mapEvent(element: HTMLElement | SVGElement, name: string, value:
|
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
}
|
|
58
|
+
|
|
59
|
+
// #endregion
|
package/src/node/index.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {isReactive
|
|
2
|
+
import {isReactive} from '@oscarpalmer/mora';
|
|
3
3
|
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
4
4
|
import {mapAttributes, mapAttributeValue} from '../attribute/index';
|
|
5
|
-
import {EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE} from '../constants';
|
|
6
|
-
import {
|
|
5
|
+
import {EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE, SYMBOL} from '../constants';
|
|
6
|
+
import {handleFragments} from '../fragments';
|
|
7
7
|
import {isFragment, isFragments, setComputedValue} from '../helpers';
|
|
8
8
|
import {createNodes} from '../helpers/dom';
|
|
9
|
-
import type {
|
|
9
|
+
import type {Fragments, FragmentState, InternalFragments} from '../models';
|
|
10
10
|
import {setReactiveValue} from './value';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// #region Functions
|
|
13
|
+
|
|
14
|
+
function mapNode(data: FragmentState, comment: Comment): void {
|
|
13
15
|
const matches = EXPRESSION_ABYDON_CONTENT.exec(comment.textContent);
|
|
14
16
|
const value = matches == null ? null : data.values[+matches[1]];
|
|
15
17
|
|
|
@@ -18,7 +20,7 @@ function mapNode(data: FragmentData, comment: Comment): void {
|
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
export function mapNodes(data:
|
|
23
|
+
export function mapNodes(data: FragmentState, nodes: ChildNode[]): void {
|
|
22
24
|
const {length} = nodes;
|
|
23
25
|
|
|
24
26
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -49,7 +51,7 @@ export function mapNodes(data: FragmentData, nodes: ChildNode[]): void {
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
function mapTextarea(data:
|
|
54
|
+
function mapTextarea(data: FragmentState, element: HTMLTextAreaElement): boolean {
|
|
53
55
|
const [, index] =
|
|
54
56
|
EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ??
|
|
55
57
|
EXPRESSION_TEXTAREA_VALUE.exec(element.value) ??
|
|
@@ -67,7 +69,7 @@ function mapTextarea(data: FragmentData, element: HTMLTextAreaElement): boolean
|
|
|
67
69
|
return true;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
function mapValue(data:
|
|
72
|
+
function mapValue(data: FragmentState, comment: Comment, value: unknown): void {
|
|
71
73
|
switch (true) {
|
|
72
74
|
case typeof value === 'function':
|
|
73
75
|
setComputedNode(data, comment, value as GenericCallback);
|
|
@@ -87,7 +89,7 @@ function mapValue(data: FragmentData, comment: Comment, value: unknown): void {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
function replaceComment(data:
|
|
92
|
+
function replaceComment(data: FragmentState, comment: Comment, value: unknown): void {
|
|
91
93
|
const item = data.items.find(item => item.nodes?.includes(comment));
|
|
92
94
|
const nodes = createNodes(value);
|
|
93
95
|
|
|
@@ -99,16 +101,16 @@ function replaceComment(data: FragmentData, comment: Comment, value: unknown): v
|
|
|
99
101
|
comment.replaceWith(...nodes);
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
function setComputedNode(data:
|
|
104
|
+
function setComputedNode(data: FragmentState, comment: Comment, callback: GenericCallback): void {
|
|
103
105
|
setComputedValue(data, callback, computation => {
|
|
104
106
|
setReactiveValue(data, comment, computation);
|
|
105
107
|
});
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
function setFragmentsNode(data:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
handleFragments(state, false);
|
|
110
|
+
function setFragmentsNode(data: FragmentState, comment: Comment, fragments: Fragments): void {
|
|
111
|
+
handleFragments(fragments, false);
|
|
112
112
|
|
|
113
|
-
setReactiveValue(data, comment,
|
|
113
|
+
setReactiveValue(data, comment, (fragments as InternalFragments)[SYMBOL].mapped);
|
|
114
114
|
}
|
|
115
|
+
|
|
116
|
+
// #endregion
|
package/src/node/value.ts
CHANGED
|
@@ -3,12 +3,11 @@ import {getString} from '@oscarpalmer/atoms/string';
|
|
|
3
3
|
import type {Reactive} from '@oscarpalmer/mora';
|
|
4
4
|
import {isChildNode} from '@oscarpalmer/toretto/is';
|
|
5
5
|
import {ARRAY_COMPARISON_ADDED, ARRAY_COMPARISON_REMOVED} from '../constants';
|
|
6
|
-
import type {Fragment} from '../fragment';
|
|
7
6
|
import {compareArrays, isFragment} from '../helpers';
|
|
8
7
|
import {createNodes, replaceNodes} from '../helpers/dom';
|
|
9
|
-
import type {
|
|
8
|
+
import type {Fragment, FragmentItem, FragmentState} from '../models';
|
|
10
9
|
|
|
11
|
-
//
|
|
10
|
+
// #region Types
|
|
12
11
|
|
|
13
12
|
type ArrayData = {
|
|
14
13
|
next: ArrayDataIdentifiers;
|
|
@@ -45,7 +44,9 @@ type ExtendedItems = {
|
|
|
45
44
|
next: Fragment[];
|
|
46
45
|
} & BaseItems;
|
|
47
46
|
|
|
48
|
-
//
|
|
47
|
+
// #endregion
|
|
48
|
+
|
|
49
|
+
// #region Functions
|
|
49
50
|
|
|
50
51
|
function addToArray(
|
|
51
52
|
identifiers: Identifiers,
|
|
@@ -183,6 +184,7 @@ function setArray(item: FragmentItem, comment: Comment, value: unknown[]): Parti
|
|
|
183
184
|
if (
|
|
184
185
|
template.empty ||
|
|
185
186
|
item.nodes == null ||
|
|
187
|
+
item.fragments == null ||
|
|
186
188
|
previous.array.some(identifier => identifier == null)
|
|
187
189
|
) {
|
|
188
190
|
const fragments = (item.fragments ?? []).slice();
|
|
@@ -208,7 +210,7 @@ function setArray(item: FragmentItem, comment: Comment, value: unknown[]): Parti
|
|
|
208
210
|
previous,
|
|
209
211
|
},
|
|
210
212
|
{
|
|
211
|
-
fragments: item.fragments
|
|
213
|
+
fragments: item.fragments,
|
|
212
214
|
templates: template.items,
|
|
213
215
|
},
|
|
214
216
|
item.nodes,
|
|
@@ -220,7 +222,7 @@ function setNodes(item: FragmentItem, comment: Comment, next: ChildNode[]): Chil
|
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
export function setReactiveValue(
|
|
223
|
-
data:
|
|
225
|
+
data: FragmentState,
|
|
224
226
|
comment: Comment,
|
|
225
227
|
reactive: Reactive<unknown>,
|
|
226
228
|
): void {
|
|
@@ -228,7 +230,7 @@ export function setReactiveValue(
|
|
|
228
230
|
|
|
229
231
|
item ??= {};
|
|
230
232
|
|
|
231
|
-
data.mora.
|
|
233
|
+
data.mora.subscriptions.add(
|
|
232
234
|
reactive.subscribe(value => {
|
|
233
235
|
if (Array.isArray(value)) {
|
|
234
236
|
setReactiveValueForArray(item, comment, value);
|
|
@@ -271,7 +273,7 @@ function setText(item: FragmentItem, comment: Comment, value?: unknown): ChildNo
|
|
|
271
273
|
|
|
272
274
|
const result = valueIsNullable ? [comment] : [item.text];
|
|
273
275
|
|
|
274
|
-
replaceNodes(item.nodes ?? [comment], result);
|
|
275
|
-
|
|
276
|
-
return result;
|
|
276
|
+
return replaceNodes(item.nodes ?? [comment], result);
|
|
277
277
|
}
|
|
278
|
+
|
|
279
|
+
// #endregion
|
package/src/parse.ts
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
EXPRESSION_EVENT_ATTRIBUTE,
|
|
5
5
|
WHITESPACE,
|
|
6
6
|
} from './constants';
|
|
7
|
-
import type {
|
|
7
|
+
import type {FragmentState} from './models';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// #region Functions
|
|
10
|
+
|
|
11
|
+
function handleExpression(data: FragmentState, prefix: string, expression: unknown): string {
|
|
10
12
|
if (Array.isArray(expression)) {
|
|
11
13
|
if (EXPRESSION_EVENT_ATTRIBUTE.test(prefix.split(WHITESPACE).at(-1)!)) {
|
|
12
14
|
return transformExpression(prefix, data.values.push(expression) - 1);
|
|
@@ -32,7 +34,7 @@ function handleExpression(data: FragmentData, prefix: string, expression: unknow
|
|
|
32
34
|
return asString.trim().length === 0 ? prefix : `${prefix}${asString}`;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
export function parse(data:
|
|
37
|
+
export function parse(data: FragmentState): string {
|
|
36
38
|
if (data.template != null) {
|
|
37
39
|
return data.template;
|
|
38
40
|
}
|
|
@@ -60,3 +62,5 @@ function transformAttribute(_: string, name: string, index: string): string {
|
|
|
60
62
|
function transformExpression(prefix: string, index: number): string {
|
|
61
63
|
return `${prefix}<!--abydon.${index}-->`;
|
|
62
64
|
}
|
|
65
|
+
|
|
66
|
+
// #endregion
|