@granularjs/core 1.0.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/README.md +576 -0
- package/dist/granular.min.js +2 -0
- package/dist/granular.min.js.map +7 -0
- package/package.json +54 -0
- package/src/core/bootstrap.js +63 -0
- package/src/core/collections/observable-array.js +204 -0
- package/src/core/component/function-component.js +82 -0
- package/src/core/context.js +172 -0
- package/src/core/dom/dom.js +25 -0
- package/src/core/dom/element.js +725 -0
- package/src/core/dom/error-boundary.js +111 -0
- package/src/core/dom/input-format.js +82 -0
- package/src/core/dom/list.js +185 -0
- package/src/core/dom/portal.js +57 -0
- package/src/core/dom/tags.js +182 -0
- package/src/core/dom/virtual-list.js +242 -0
- package/src/core/dom/when.js +138 -0
- package/src/core/events/event-hub.js +97 -0
- package/src/core/forms/form.js +127 -0
- package/src/core/internal/symbols.js +5 -0
- package/src/core/network/websocket.js +165 -0
- package/src/core/query/query-client.js +529 -0
- package/src/core/reactivity/after-flush.js +20 -0
- package/src/core/reactivity/computed.js +51 -0
- package/src/core/reactivity/concat.js +89 -0
- package/src/core/reactivity/dirty-host.js +162 -0
- package/src/core/reactivity/observe.js +421 -0
- package/src/core/reactivity/persist.js +180 -0
- package/src/core/reactivity/resolve.js +8 -0
- package/src/core/reactivity/signal.js +97 -0
- package/src/core/reactivity/state.js +294 -0
- package/src/core/renderable/render-string.js +51 -0
- package/src/core/renderable/renderable.js +21 -0
- package/src/core/renderable/renderer.js +66 -0
- package/src/core/router/router.js +865 -0
- package/src/core/runtime.js +28 -0
- package/src/index.js +42 -0
- package/types/core/bootstrap.d.ts +11 -0
- package/types/core/collections/observable-array.d.ts +25 -0
- package/types/core/component/function-component.d.ts +14 -0
- package/types/core/context.d.ts +29 -0
- package/types/core/dom/dom.d.ts +13 -0
- package/types/core/dom/element.d.ts +10 -0
- package/types/core/dom/error-boundary.d.ts +8 -0
- package/types/core/dom/input-format.d.ts +6 -0
- package/types/core/dom/list.d.ts +8 -0
- package/types/core/dom/portal.d.ts +8 -0
- package/types/core/dom/tags.d.ts +114 -0
- package/types/core/dom/virtual-list.d.ts +8 -0
- package/types/core/dom/when.d.ts +13 -0
- package/types/core/events/event-hub.d.ts +48 -0
- package/types/core/forms/form.d.ts +9 -0
- package/types/core/internal/symbols.d.ts +4 -0
- package/types/core/network/websocket.d.ts +18 -0
- package/types/core/query/query-client.d.ts +73 -0
- package/types/core/reactivity/after-flush.d.ts +4 -0
- package/types/core/reactivity/computed.d.ts +1 -0
- package/types/core/reactivity/concat.d.ts +1 -0
- package/types/core/reactivity/dirty-host.d.ts +42 -0
- package/types/core/reactivity/observe.d.ts +10 -0
- package/types/core/reactivity/persist.d.ts +1 -0
- package/types/core/reactivity/resolve.d.ts +1 -0
- package/types/core/reactivity/signal.d.ts +11 -0
- package/types/core/reactivity/state.d.ts +14 -0
- package/types/core/renderable/render-string.d.ts +2 -0
- package/types/core/renderable/renderable.d.ts +15 -0
- package/types/core/renderable/renderer.d.ts +38 -0
- package/types/core/router/router.d.ts +57 -0
- package/types/core/runtime.d.ts +26 -0
- package/types/index.d.ts +2 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Renderable } from '../renderable/renderable.js';
|
|
2
|
+
import { Renderer } from '../renderable/renderer.js';
|
|
3
|
+
import { createComment, clearBetween } from './dom.js';
|
|
4
|
+
|
|
5
|
+
export class ErrorBoundaryNode extends Renderable {
|
|
6
|
+
#fallback;
|
|
7
|
+
#onError;
|
|
8
|
+
#child;
|
|
9
|
+
#start = null;
|
|
10
|
+
#end = null;
|
|
11
|
+
#mounted = false;
|
|
12
|
+
#mountedValues = [];
|
|
13
|
+
|
|
14
|
+
constructor(options, child) {
|
|
15
|
+
super();
|
|
16
|
+
this.#fallback = options?.fallback ?? null;
|
|
17
|
+
this.#onError = options?.onError ?? null;
|
|
18
|
+
this.#child = child;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
mountInto(parent, beforeNode) {
|
|
22
|
+
if (this.#mounted) return;
|
|
23
|
+
this.#mounted = true;
|
|
24
|
+
this.#start = createComment('zb:error:start', 'error');
|
|
25
|
+
this.#end = createComment('zb:error:end', 'error');
|
|
26
|
+
parent.insertBefore(this.#start, beforeNode);
|
|
27
|
+
parent.insertBefore(this.#end, beforeNode);
|
|
28
|
+
this.#renderSafe();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
unmount() {
|
|
32
|
+
if (!this.#mounted) return;
|
|
33
|
+
this.#mounted = false;
|
|
34
|
+
this.#cleanup();
|
|
35
|
+
if (this.#start && this.#end) {
|
|
36
|
+
clearBetween(this.#start, this.#end);
|
|
37
|
+
this.#start.remove();
|
|
38
|
+
this.#end.remove();
|
|
39
|
+
}
|
|
40
|
+
this.#start = null;
|
|
41
|
+
this.#end = null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#cleanup() {
|
|
45
|
+
for (const r of this.#mountedValues) Renderer.unmount(r);
|
|
46
|
+
this.#mountedValues = [];
|
|
47
|
+
if (this.#start && this.#end) clearBetween(this.#start, this.#end);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#renderValue(value) {
|
|
51
|
+
const values = Renderer.normalize(value);
|
|
52
|
+
this.#mountedValues = values;
|
|
53
|
+
for (const r of values) {
|
|
54
|
+
if (Renderer.isRenderable(r)) {
|
|
55
|
+
r.mountInto(this.#end.parentNode, this.#end);
|
|
56
|
+
} else if (Renderer.isDomNode(r)) {
|
|
57
|
+
this.#end.parentNode.insertBefore(r, this.#end);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#renderSafe() {
|
|
63
|
+
this.#cleanup();
|
|
64
|
+
try {
|
|
65
|
+
const value = typeof this.#child === 'function' ? this.#child() : this.#child;
|
|
66
|
+
this.#renderValue(value);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
this.#handleError(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#handleError(error) {
|
|
73
|
+
try {
|
|
74
|
+
if (typeof this.#onError === 'function') {
|
|
75
|
+
this.#onError(error, { phase: 'render' });
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// ignore errors from onError
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
if (this.#fallback) {
|
|
82
|
+
const value = typeof this.#fallback === 'function' ? this.#fallback(error) : this.#fallback;
|
|
83
|
+
this.#renderValue(value);
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// ignore errors from fallback
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
renderToString(render) {
|
|
91
|
+
try {
|
|
92
|
+
const value = typeof this.#child === 'function' ? this.#child() : this.#child;
|
|
93
|
+
return render(value);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (typeof this.#onError === 'function') {
|
|
96
|
+
try {
|
|
97
|
+
this.#onError(error, { phase: 'render' });
|
|
98
|
+
} catch {}
|
|
99
|
+
}
|
|
100
|
+
if (this.#fallback) {
|
|
101
|
+
const fallback = typeof this.#fallback === 'function' ? this.#fallback(error) : this.#fallback;
|
|
102
|
+
return render(fallback);
|
|
103
|
+
}
|
|
104
|
+
return '';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function ErrorBoundary(options, child) {
|
|
110
|
+
return new ErrorBoundaryNode(options, child);
|
|
111
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const tokenMatchers = {
|
|
2
|
+
d: (char) => /[0-9]/.test(char),
|
|
3
|
+
a: (char) => /[A-Za-z]/.test(char),
|
|
4
|
+
'*': (char) => /[A-Za-z0-9]/.test(char),
|
|
5
|
+
s: (char) => /[^A-Za-z0-9]/.test(char),
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const isToken = (char) => Object.prototype.hasOwnProperty.call(tokenMatchers, char);
|
|
9
|
+
|
|
10
|
+
function collectPatternValues(input, pattern) {
|
|
11
|
+
const values = [];
|
|
12
|
+
let patternIndex = 0;
|
|
13
|
+
for (const char of input) {
|
|
14
|
+
while (patternIndex < pattern.length && !isToken(pattern[patternIndex])) {
|
|
15
|
+
patternIndex += 1;
|
|
16
|
+
}
|
|
17
|
+
if (patternIndex >= pattern.length) break;
|
|
18
|
+
const token = pattern[patternIndex];
|
|
19
|
+
if (tokenMatchers[token]?.(char)) {
|
|
20
|
+
values.push(char);
|
|
21
|
+
patternIndex += 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return values;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function applyPattern(input, pattern) {
|
|
28
|
+
const values = collectPatternValues(input, pattern);
|
|
29
|
+
let visual = '';
|
|
30
|
+
let valueIndex = 0;
|
|
31
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
32
|
+
const char = pattern[i];
|
|
33
|
+
if (isToken(char)) {
|
|
34
|
+
if (valueIndex >= values.length) break;
|
|
35
|
+
visual += values[valueIndex];
|
|
36
|
+
valueIndex += 1;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (valueIndex === 0) continue;
|
|
40
|
+
if (valueIndex < values.length) visual += char;
|
|
41
|
+
}
|
|
42
|
+
return { raw: values.join(''), visual };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function normalizeInputFormat(format) {
|
|
46
|
+
if (format == null) return null;
|
|
47
|
+
if (typeof format === 'function') return { format, mode: 'both' };
|
|
48
|
+
if (typeof format === 'string') return { pattern: format, mode: 'both' };
|
|
49
|
+
if (typeof format === 'object') return { mode: 'both', ...format };
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function applyInputFormat(inputValue, format) {
|
|
54
|
+
const normalized = normalizeInputFormat(format);
|
|
55
|
+
const rawInput = String(inputValue ?? '');
|
|
56
|
+
if (!normalized) {
|
|
57
|
+
return { value: rawInput, visual: rawInput, raw: rawInput };
|
|
58
|
+
}
|
|
59
|
+
if (typeof normalized.format === 'function') {
|
|
60
|
+
let formatted = rawInput;
|
|
61
|
+
try {
|
|
62
|
+
formatted = normalized.format(rawInput);
|
|
63
|
+
} catch {}
|
|
64
|
+
if (formatted && typeof formatted === 'object') {
|
|
65
|
+
const value = formatted.value ?? formatted.visual ?? '';
|
|
66
|
+
const visual = formatted.visual ?? formatted.value ?? '';
|
|
67
|
+
const raw = formatted.raw ?? value ?? '';
|
|
68
|
+
return { value: String(value), visual: String(visual), raw: String(raw) };
|
|
69
|
+
}
|
|
70
|
+
return { value: String(formatted ?? ''), visual: String(formatted ?? ''), raw: String(formatted ?? '') };
|
|
71
|
+
}
|
|
72
|
+
if (normalized.pattern) {
|
|
73
|
+
const { raw, visual } = applyPattern(rawInput, String(normalized.pattern));
|
|
74
|
+
return { value: visual, visual, raw };
|
|
75
|
+
}
|
|
76
|
+
if (normalized.regex) {
|
|
77
|
+
const match = rawInput.match(normalized.regex);
|
|
78
|
+
const formatted = match ? match[0] : '';
|
|
79
|
+
return { value: formatted, visual: formatted, raw: formatted };
|
|
80
|
+
}
|
|
81
|
+
return { value: rawInput, visual: rawInput, raw: rawInput };
|
|
82
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Renderable } from '../renderable/renderable.js';
|
|
2
|
+
import { Renderer } from '../renderable/renderer.js';
|
|
3
|
+
import { isObservableArray } from '../collections/observable-array.js';
|
|
4
|
+
import { createComment, clearBetween } from './dom.js';
|
|
5
|
+
import { signal, setSignal, isSignal, readSignal, subscribeSignal } from '../reactivity/signal.js';
|
|
6
|
+
import { state, isState, isStatePath, readState, subscribeState } from '../reactivity/state.js';
|
|
7
|
+
|
|
8
|
+
export class ListNode extends Renderable {
|
|
9
|
+
#items;
|
|
10
|
+
#renderItem;
|
|
11
|
+
#start = null;
|
|
12
|
+
#end = null;
|
|
13
|
+
#mounted = false;
|
|
14
|
+
#unsub = null;
|
|
15
|
+
#itemRefs = [];
|
|
16
|
+
|
|
17
|
+
constructor(items, renderItem) {
|
|
18
|
+
super();
|
|
19
|
+
this.#items = items;
|
|
20
|
+
this.#renderItem = renderItem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
mountInto(parent, beforeNode) {
|
|
24
|
+
if (this.#mounted) return;
|
|
25
|
+
this.#mounted = true;
|
|
26
|
+
this.#start = createComment('zb:list:start', 'list');
|
|
27
|
+
this.#end = createComment('zb:list:end', 'list');
|
|
28
|
+
parent.insertBefore(this.#start, beforeNode);
|
|
29
|
+
parent.insertBefore(this.#end, beforeNode);
|
|
30
|
+
|
|
31
|
+
const initial = this.#readItems();
|
|
32
|
+
this.#mountAll(initial);
|
|
33
|
+
this.#wire();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
unmount() {
|
|
37
|
+
if (!this.#mounted) return;
|
|
38
|
+
this.#mounted = false;
|
|
39
|
+
if (this.#unsub) this.#unsub();
|
|
40
|
+
this.#unsub = null;
|
|
41
|
+
this.#cleanup();
|
|
42
|
+
if (this.#start && this.#end) {
|
|
43
|
+
clearBetween(this.#start, this.#end);
|
|
44
|
+
this.#start.remove();
|
|
45
|
+
this.#end.remove();
|
|
46
|
+
}
|
|
47
|
+
this.#start = null;
|
|
48
|
+
this.#end = null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
renderToString(render) {
|
|
52
|
+
const items = this.#readItems();
|
|
53
|
+
return items.map((item, index) => {
|
|
54
|
+
const itemState = state(item);
|
|
55
|
+
const indexSignal = signal(index);
|
|
56
|
+
return render(this.#renderItem(itemState, indexSignal));
|
|
57
|
+
}).join('');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#readItems() {
|
|
61
|
+
if (isObservableArray(this.#items)) return this.#items;
|
|
62
|
+
if (isSignal(this.#items)) return readSignal(this.#items) || [];
|
|
63
|
+
if (isState(this.#items) || isStatePath(this.#items)) return readState(this.#items) || [];
|
|
64
|
+
return Array.isArray(this.#items) ? this.#items : [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#wire() {
|
|
68
|
+
if (isObservableArray(this.#items)) {
|
|
69
|
+
this.#unsub = this.#items.subscribe((patch) => {
|
|
70
|
+
if (!this.#mounted) return;
|
|
71
|
+
if (patch.type === 'reset') {
|
|
72
|
+
this.#reset(patch.items);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (patch.type === 'insert') {
|
|
76
|
+
for (let i = 0; i < patch.items.length; i++) {
|
|
77
|
+
this.#insert(patch.index + i, patch.items[i]);
|
|
78
|
+
}
|
|
79
|
+
this.#updateIndices(patch.index + patch.items.length);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (patch.type === 'remove') {
|
|
83
|
+
this.#remove(patch.index, patch.count);
|
|
84
|
+
this.#updateIndices(patch.index);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (patch.type === 'set') {
|
|
88
|
+
this.#set(patch.index, patch.value);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (isSignal(this.#items)) {
|
|
95
|
+
this.#unsub = subscribeSignal(this.#items, () => {
|
|
96
|
+
this.#reset(this.#readItems());
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (isState(this.#items) || isStatePath(this.#items)) {
|
|
102
|
+
this.#unsub = subscribeState(this.#items, () => {
|
|
103
|
+
this.#reset(this.#readItems());
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#mountAll(items) {
|
|
109
|
+
this.#itemRefs = [];
|
|
110
|
+
for (let i = 0; i < items.length; i++) {
|
|
111
|
+
this.#insert(i, items[i]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#cleanup() {
|
|
116
|
+
for (const it of this.#itemRefs) {
|
|
117
|
+
for (const r of it.values) Renderer.unmount(r);
|
|
118
|
+
clearBetween(it.start, it.end);
|
|
119
|
+
it.start.remove();
|
|
120
|
+
it.end.remove();
|
|
121
|
+
}
|
|
122
|
+
this.#itemRefs = [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#reset(items) {
|
|
126
|
+
this.#cleanup();
|
|
127
|
+
this.#mountAll(items);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#insert(index, item) {
|
|
131
|
+
const refNode = index < this.#itemRefs.length ? this.#itemRefs[index].start : this.#end;
|
|
132
|
+
const itemStart = createComment('zb:item:start', 'item');
|
|
133
|
+
const itemEnd = createComment('zb:item:end', 'item');
|
|
134
|
+
const parent = this.#end.parentNode;
|
|
135
|
+
parent.insertBefore(itemStart, refNode);
|
|
136
|
+
parent.insertBefore(itemEnd, refNode);
|
|
137
|
+
const itemState = state(item);
|
|
138
|
+
const indexSignal = signal(index);
|
|
139
|
+
const rendered = this.#renderItem ? this.#renderItem(itemState, indexSignal) : item;
|
|
140
|
+
const values = Renderer.normalize(rendered);
|
|
141
|
+
for (const r of values) this.#mountRenderable(parent, r, itemEnd);
|
|
142
|
+
this.#itemRefs.splice(index, 0, { start: itemStart, end: itemEnd, values, state: itemState, index: indexSignal });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#remove(index, count) {
|
|
146
|
+
const removed = this.#itemRefs.splice(index, count);
|
|
147
|
+
for (const it of removed) {
|
|
148
|
+
for (const r of it.values) Renderer.unmount(r);
|
|
149
|
+
clearBetween(it.start, it.end);
|
|
150
|
+
it.start.remove();
|
|
151
|
+
it.end.remove();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#set(index, item) {
|
|
156
|
+
const ref = this.#itemRefs[index];
|
|
157
|
+
if (ref && ref.state) {
|
|
158
|
+
ref.state.set(item);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.#remove(index, 1);
|
|
162
|
+
this.#insert(index, item);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#updateIndices(fromIndex) {
|
|
166
|
+
for (let i = fromIndex; i < this.#itemRefs.length; i++) {
|
|
167
|
+
const ref = this.#itemRefs[i];
|
|
168
|
+
if (ref.index) setSignal(ref.index, i);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
#mountRenderable(parent, renderable, beforeNode) {
|
|
173
|
+
if (Renderer.isRenderable(renderable)) {
|
|
174
|
+
renderable.mountInto(parent, beforeNode);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (Renderer.isDomNode(renderable)) {
|
|
178
|
+
parent.insertBefore(renderable, beforeNode);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function list(items, renderItem) {
|
|
184
|
+
return new ListNode(items, renderItem);
|
|
185
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Renderable } from '../renderable/renderable.js';
|
|
2
|
+
import { Renderer } from '../renderable/renderer.js';
|
|
3
|
+
|
|
4
|
+
function resolveTarget(target) {
|
|
5
|
+
if (!target && typeof document !== 'undefined') return document.body;
|
|
6
|
+
if (typeof target === 'string') return document.querySelector(target);
|
|
7
|
+
return target;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class PortalNode extends Renderable {
|
|
11
|
+
#target;
|
|
12
|
+
#content;
|
|
13
|
+
#mounted = false;
|
|
14
|
+
#mountedValues = [];
|
|
15
|
+
|
|
16
|
+
constructor(target, content) {
|
|
17
|
+
super();
|
|
18
|
+
this.#target = target;
|
|
19
|
+
this.#content = content;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
mountInto(_parent, _beforeNode) {
|
|
23
|
+
if (this.#mounted) return;
|
|
24
|
+
this.#mounted = true;
|
|
25
|
+
const targetEl = resolveTarget(this.#target);
|
|
26
|
+
if (!targetEl) throw new Error('portal: target not found');
|
|
27
|
+
const value = typeof this.#content === 'function' ? this.#content() : this.#content;
|
|
28
|
+
const values = Renderer.normalize(value);
|
|
29
|
+
this.#mountedValues = values;
|
|
30
|
+
for (const r of values) {
|
|
31
|
+
if (Renderer.isRenderable(r)) {
|
|
32
|
+
r.mountInto(targetEl, null);
|
|
33
|
+
} else if (Renderer.isDomNode(r)) {
|
|
34
|
+
targetEl.appendChild(r);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
unmount() {
|
|
40
|
+
if (!this.#mounted) return;
|
|
41
|
+
this.#mounted = false;
|
|
42
|
+
for (const r of this.#mountedValues) Renderer.unmount(r);
|
|
43
|
+
this.#mountedValues = [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
renderToString(render) {
|
|
47
|
+
const value = typeof this.#content === 'function' ? this.#content() : this.#content;
|
|
48
|
+
return render(value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function portal(target, content) {
|
|
53
|
+
if (content === undefined) {
|
|
54
|
+
return new PortalNode(null, target);
|
|
55
|
+
}
|
|
56
|
+
return new PortalNode(target, content);
|
|
57
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { ElementNode } from './element.js';
|
|
2
|
+
import { Renderer } from '../renderable/renderer.js';
|
|
3
|
+
import { isObservableArray } from '../collections/observable-array.js';
|
|
4
|
+
import { isSignal } from '../reactivity/signal.js';
|
|
5
|
+
import { isComputed, isState, isStatePath } from '../reactivity/state.js';
|
|
6
|
+
|
|
7
|
+
const tags = [
|
|
8
|
+
'html', 'head', 'title', 'base', 'link', 'meta', 'style',
|
|
9
|
+
'body', 'article', 'section', 'nav', 'aside', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
10
|
+
'hgroup', 'header', 'footer', 'address', 'main', 'search',
|
|
11
|
+
'p', 'hr', 'pre', 'blockquote', 'ol', 'ul', 'li', 'dl', 'dt', 'dd',
|
|
12
|
+
'figure', 'figcaption', 'div', 'menu',
|
|
13
|
+
'a', 'em', 'strong', 'small', 's', 'cite', 'q', 'dfn', 'abbr', 'ruby', 'rt', 'rp',
|
|
14
|
+
'data', 'time', 'code', 'var', 'samp', 'kbd', 'sub', 'sup', 'i', 'b', 'u',
|
|
15
|
+
'mark', 'bdi', 'bdo', 'span', 'br', 'wbr',
|
|
16
|
+
'ins', 'del',
|
|
17
|
+
'picture', 'source', 'img', 'iframe', 'embed', 'object', 'param', 'video', 'audio',
|
|
18
|
+
'track', 'map', 'area',
|
|
19
|
+
'table', 'caption', 'colgroup', 'col', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'th',
|
|
20
|
+
'form', 'label', 'input', 'button', 'select', 'datalist', 'optgroup', 'option',
|
|
21
|
+
'textarea', 'output', 'progress', 'meter', 'fieldset', 'legend',
|
|
22
|
+
'details', 'summary', 'dialog',
|
|
23
|
+
'script', 'noscript', 'template', 'slot', 'canvas',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function toFactoryName(tag) {
|
|
27
|
+
let name = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|
28
|
+
if (name in globalThis) name = `Html${name}`;
|
|
29
|
+
return name;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createTag(tagName) {
|
|
33
|
+
return (...args) => {
|
|
34
|
+
const nextProps = {};
|
|
35
|
+
const nextChildren = [];
|
|
36
|
+
|
|
37
|
+
const isPropsObject = (value) =>
|
|
38
|
+
!!value &&
|
|
39
|
+
typeof value === 'object' &&
|
|
40
|
+
!Array.isArray(value) &&
|
|
41
|
+
!Renderer.isRenderable(value) &&
|
|
42
|
+
!Renderer.isDomNode(value) &&
|
|
43
|
+
!isObservableArray(value) &&
|
|
44
|
+
!isSignal(value) &&
|
|
45
|
+
!isState(value) &&
|
|
46
|
+
!isStatePath(value) &&
|
|
47
|
+
!isComputed(value);
|
|
48
|
+
|
|
49
|
+
for (const arg of args) {
|
|
50
|
+
if (isPropsObject(arg)) {
|
|
51
|
+
Object.assign(nextProps, arg);
|
|
52
|
+
} else {
|
|
53
|
+
nextChildren.push(arg);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return new ElementNode(tagName, nextProps, nextChildren);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const exported = {};
|
|
62
|
+
for (const tag of tags) {
|
|
63
|
+
const name = toFactoryName(tag);
|
|
64
|
+
exported[name] = createTag(tag);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const Elements = Object.freeze(exported);
|
|
68
|
+
export const {
|
|
69
|
+
Html,
|
|
70
|
+
Head,
|
|
71
|
+
Title,
|
|
72
|
+
Base,
|
|
73
|
+
Link,
|
|
74
|
+
Meta,
|
|
75
|
+
Style,
|
|
76
|
+
Body,
|
|
77
|
+
Article,
|
|
78
|
+
Section,
|
|
79
|
+
Nav,
|
|
80
|
+
Aside,
|
|
81
|
+
H1,
|
|
82
|
+
H2,
|
|
83
|
+
H3,
|
|
84
|
+
H4,
|
|
85
|
+
H5,
|
|
86
|
+
H6,
|
|
87
|
+
Hgroup,
|
|
88
|
+
Header,
|
|
89
|
+
Footer,
|
|
90
|
+
Address,
|
|
91
|
+
Main,
|
|
92
|
+
Search,
|
|
93
|
+
P,
|
|
94
|
+
Hr,
|
|
95
|
+
Pre,
|
|
96
|
+
Blockquote,
|
|
97
|
+
Ol,
|
|
98
|
+
Ul,
|
|
99
|
+
Li,
|
|
100
|
+
Dl,
|
|
101
|
+
Dt,
|
|
102
|
+
Dd,
|
|
103
|
+
Figure,
|
|
104
|
+
Figcaption,
|
|
105
|
+
Div,
|
|
106
|
+
Menu,
|
|
107
|
+
A,
|
|
108
|
+
Em,
|
|
109
|
+
Strong,
|
|
110
|
+
Small,
|
|
111
|
+
S,
|
|
112
|
+
Cite,
|
|
113
|
+
Q,
|
|
114
|
+
Dfn,
|
|
115
|
+
Abbr,
|
|
116
|
+
Ruby,
|
|
117
|
+
Rt,
|
|
118
|
+
Rp,
|
|
119
|
+
Data,
|
|
120
|
+
Time,
|
|
121
|
+
Code,
|
|
122
|
+
Var,
|
|
123
|
+
Samp,
|
|
124
|
+
Kbd,
|
|
125
|
+
Sub,
|
|
126
|
+
Sup,
|
|
127
|
+
I,
|
|
128
|
+
B,
|
|
129
|
+
U,
|
|
130
|
+
Mark,
|
|
131
|
+
Bdi,
|
|
132
|
+
Bdo,
|
|
133
|
+
Span,
|
|
134
|
+
Br,
|
|
135
|
+
Wbr,
|
|
136
|
+
Ins,
|
|
137
|
+
Del,
|
|
138
|
+
Picture,
|
|
139
|
+
Source,
|
|
140
|
+
Img,
|
|
141
|
+
Iframe,
|
|
142
|
+
Embed,
|
|
143
|
+
HtmlObject,
|
|
144
|
+
Param,
|
|
145
|
+
Video,
|
|
146
|
+
Audio,
|
|
147
|
+
Track,
|
|
148
|
+
Map,
|
|
149
|
+
Area,
|
|
150
|
+
Table,
|
|
151
|
+
Caption,
|
|
152
|
+
Colgroup,
|
|
153
|
+
Col,
|
|
154
|
+
Tbody,
|
|
155
|
+
Thead,
|
|
156
|
+
Tfoot,
|
|
157
|
+
Tr,
|
|
158
|
+
Td,
|
|
159
|
+
Th,
|
|
160
|
+
Form,
|
|
161
|
+
Label,
|
|
162
|
+
Input,
|
|
163
|
+
Button,
|
|
164
|
+
Select,
|
|
165
|
+
Datalist,
|
|
166
|
+
Optgroup,
|
|
167
|
+
Option,
|
|
168
|
+
Textarea,
|
|
169
|
+
Output,
|
|
170
|
+
Progress,
|
|
171
|
+
Meter,
|
|
172
|
+
Fieldset,
|
|
173
|
+
Legend,
|
|
174
|
+
Details,
|
|
175
|
+
Summary,
|
|
176
|
+
Dialog,
|
|
177
|
+
Script,
|
|
178
|
+
Noscript,
|
|
179
|
+
Template,
|
|
180
|
+
Slot,
|
|
181
|
+
Canvas,
|
|
182
|
+
} = exported;
|