@neeloong/form 0.1.0 → 0.3.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/LICENSE +201 -0
- package/README.md +219 -0
- package/index.d.mts +102 -11
- package/index.js +526 -77
- package/index.min.js +8 -8
- package/index.min.mjs +7 -7
- package/index.mjs +526 -77
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @neeloong/form v0.
|
|
2
|
+
* @neeloong/form v0.3.0
|
|
3
3
|
* (c) 2024-2025 Fierflame
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
@@ -13,14 +13,21 @@ export { Signal } from 'signal-polyfill';
|
|
|
13
13
|
*
|
|
14
14
|
* @param {Store} self
|
|
15
15
|
* @param {boolean?} [defState]
|
|
16
|
-
* @param {boolean | ((store: Store) => boolean) | null} [fn]
|
|
16
|
+
* @param {boolean | ((store: Store, root: Store) => boolean?) | null} [fn]
|
|
17
17
|
* @param {Signal.Computed<boolean>?} [parent]
|
|
18
18
|
* @returns {[Signal.State<boolean?>, Signal.Computed<boolean>]}
|
|
19
19
|
*/
|
|
20
20
|
const createBooleanStates = (self, defState, fn, parent) => {
|
|
21
21
|
|
|
22
22
|
const selfState = new Signal.State(typeof defState === 'boolean' ? defState : null);
|
|
23
|
-
|
|
23
|
+
/** @type {Signal.Computed<boolean>} */
|
|
24
|
+
let scriptState;
|
|
25
|
+
if (typeof fn === 'function') {
|
|
26
|
+
scriptState = new Signal.Computed(() => Boolean(fn(self, self.root)));
|
|
27
|
+
} else {
|
|
28
|
+
const def = Boolean(fn);
|
|
29
|
+
scriptState = new Signal.Computed(() => def);
|
|
30
|
+
}
|
|
24
31
|
|
|
25
32
|
const getState = () => {
|
|
26
33
|
const s = selfState.get();
|
|
@@ -34,6 +41,75 @@ const createBooleanStates = (self, defState, fn, parent) => {
|
|
|
34
41
|
|
|
35
42
|
};
|
|
36
43
|
|
|
44
|
+
/** @import { Schema } from '../types.mjs' */
|
|
45
|
+
/** @param {*} v */
|
|
46
|
+
const string = v => typeof v === 'string' && v || null;
|
|
47
|
+
/** @param {*} v */
|
|
48
|
+
const number = v => typeof v === 'number' && v || null;
|
|
49
|
+
|
|
50
|
+
/** @type {(v: Schema.Value | Schema.Value.Group | null) => v is Schema.Value | Schema.Value.Group} */
|
|
51
|
+
const valueFilter = /** @type {*} */(Boolean);
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
* @param {*} v
|
|
55
|
+
* @returns {Schema.Value | Schema.Value.Group | null}
|
|
56
|
+
*/
|
|
57
|
+
function toValueItem(v) {
|
|
58
|
+
if (typeof v === 'number' || typeof v === 'string') {
|
|
59
|
+
return /** @type {Schema.Value} */({ label: v, value: v});
|
|
60
|
+
}
|
|
61
|
+
if (!v || typeof v !== 'object') { return null; }
|
|
62
|
+
const {children, label, value} = v;
|
|
63
|
+
const list = Array.isArray(children) ? children.map(toValueItem).filter(valueFilter) : [];
|
|
64
|
+
if (list.length) {
|
|
65
|
+
return {children: list, label, value}
|
|
66
|
+
}
|
|
67
|
+
if (typeof value === 'number' || typeof value === 'string') {
|
|
68
|
+
return {label, value};
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
/** @param {*} v */
|
|
74
|
+
const values = v => {
|
|
75
|
+
if (!v || !Array.isArray(v)) { return null;}
|
|
76
|
+
const list = v.map(toValueItem).filter(valueFilter);
|
|
77
|
+
if (!list.length) { return null; }
|
|
78
|
+
return list;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** @import Store from './index.mjs' */
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @template T
|
|
85
|
+
* @param {Store} self
|
|
86
|
+
* @param {(v: any) => any?} toValue
|
|
87
|
+
* @param {T?} [defState]
|
|
88
|
+
* @param {T | ((store: Store, root: Store) => T?) | null} [fn]
|
|
89
|
+
* @returns {[Signal.State<T?>, Signal.Computed<T?>]}
|
|
90
|
+
*/
|
|
91
|
+
function createState(self, toValue, defState, fn) {
|
|
92
|
+
|
|
93
|
+
const selfState = new Signal.State(toValue(defState));
|
|
94
|
+
/** @type {Signal.Computed<T>} */
|
|
95
|
+
let scriptState;
|
|
96
|
+
if (typeof fn === 'function') {
|
|
97
|
+
const f = /** @type {(store: Store, root: Store) => T?} */(fn);
|
|
98
|
+
scriptState = new Signal.Computed(() => toValue(f(self, self.root)));
|
|
99
|
+
} else {
|
|
100
|
+
const def = toValue(fn);
|
|
101
|
+
scriptState = new Signal.Computed(() => def);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const state = new Signal.Computed(() => {
|
|
105
|
+
const s = selfState.get();
|
|
106
|
+
return s === null ? scriptState.get() : s;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return [selfState, state];
|
|
110
|
+
|
|
111
|
+
}
|
|
112
|
+
|
|
37
113
|
/** @import { Schema } from '../types.mjs' */
|
|
38
114
|
/**
|
|
39
115
|
* @template [T=any]
|
|
@@ -63,6 +139,15 @@ class Store {
|
|
|
63
139
|
* @param {boolean} [options.required]
|
|
64
140
|
* @param {boolean} [options.readonly]
|
|
65
141
|
* @param {boolean} [options.disabled]
|
|
142
|
+
*
|
|
143
|
+
* @param {string} [options.label] 字段标签
|
|
144
|
+
* @param {string} [options.description] 字段描述
|
|
145
|
+
* @param {string} [options.placeholder] 占位符
|
|
146
|
+
* @param {number} [options.min] 日期、时间、数字的最小值
|
|
147
|
+
* @param {number} [options.max] 日期、时间、数字的最大值
|
|
148
|
+
* @param {number} [options.step] 日期、时间、数字的步长
|
|
149
|
+
* @param {(Schema.Value.Group | Schema.Value | string | number)[]} [options.values] 可选值
|
|
150
|
+
*
|
|
66
151
|
* @param {((value: any) => any)?} [options.setValue]
|
|
67
152
|
* @param {((value: any) => any)?} [options.setState]
|
|
68
153
|
* @param {((value: any, state: any) => [value: any, state: any])?} [options.convert]
|
|
@@ -74,6 +159,7 @@ class Store {
|
|
|
74
159
|
setValue, setState, convert, onUpdate, onUpdateState,
|
|
75
160
|
index, length, new: isNew, parent: parentNode,
|
|
76
161
|
hidden, clearable, required, disabled, readonly,
|
|
162
|
+
label, description, placeholder, min, max, step, values: values$1
|
|
77
163
|
}) {
|
|
78
164
|
this.schema = schema;
|
|
79
165
|
this.#state.set(typeof state === 'object' && state || {});
|
|
@@ -96,13 +182,41 @@ class Store {
|
|
|
96
182
|
const creatable = schema.creatable !== false;
|
|
97
183
|
this.#immutable = immutable;
|
|
98
184
|
this.#creatable = creatable;
|
|
99
|
-
|
|
185
|
+
|
|
186
|
+
const readonlyFn = schema.readonly;
|
|
187
|
+
const selfReadonly = new Signal.State(typeof readonly === 'boolean' ? readonly : null);
|
|
188
|
+
/** @type {Signal.Computed<boolean>} */
|
|
189
|
+
let readonlyScript;
|
|
190
|
+
if (typeof readonlyFn === 'function') {
|
|
191
|
+
readonlyScript = new Signal.Computed(() => Boolean(readonlyFn(this, this.root)));
|
|
192
|
+
} else {
|
|
193
|
+
const def = Boolean(readonlyFn);
|
|
194
|
+
readonlyScript = new Signal.Computed(() => def);
|
|
195
|
+
}
|
|
196
|
+
const getReadonly = () => {
|
|
197
|
+
if (newState.get() ? !creatable : immutable) { return true; }
|
|
198
|
+
const s = selfReadonly.get();
|
|
199
|
+
return s === null ? readonlyScript.get() : s;
|
|
200
|
+
};
|
|
201
|
+
const readonlyParent = parent ? parent.#readonly : null;
|
|
202
|
+
this.#selfReadonly = selfReadonly;
|
|
203
|
+
this.#readonly = readonlyParent
|
|
204
|
+
? new Signal.Computed(() => readonlyParent.get() || getReadonly())
|
|
205
|
+
: new Signal.Computed(getReadonly);
|
|
100
206
|
|
|
101
207
|
[this.#selfHidden, this.#hidden] = createBooleanStates(this, hidden, schema.hidden, parent ? parent.#hidden : null);
|
|
102
208
|
[this.#selfClearable, this.#clearable] = createBooleanStates(this, clearable, schema.clearable, parent ? parent.#clearable : null);
|
|
103
209
|
[this.#selfRequired, this.#required] = createBooleanStates(this, required, schema.required, parent ? parent.#required : null);
|
|
104
210
|
[this.#selfDisabled, this.#disabled] = createBooleanStates(this, disabled, schema.disabled, parent ? parent.#disabled : null);
|
|
105
|
-
|
|
211
|
+
|
|
212
|
+
[this.#selfLabel, this.#label] = createState(this, string, label, schema.label);
|
|
213
|
+
[this.#selfDescription, this.#description] = createState(this, string, description, schema.description);
|
|
214
|
+
[this.#selfPlaceholder, this.#placeholder] = createState(this, string, placeholder, schema.placeholder);
|
|
215
|
+
[this.#selfMin, this.#min] = createState(this, number, min, schema.min);
|
|
216
|
+
[this.#selfMax, this.#max] = createState(this, number, max, schema.max);
|
|
217
|
+
[this.#selfStep, this.#step] = createState(this, number, step, schema.step);
|
|
218
|
+
// @ts-ignore
|
|
219
|
+
[this.#selfValues, this.#values] = createState(this, values, values$1, schema.values);
|
|
106
220
|
|
|
107
221
|
if (isNull) {
|
|
108
222
|
this.#null = true;
|
|
@@ -156,13 +270,10 @@ class Store {
|
|
|
156
270
|
#new
|
|
157
271
|
/** @readonly @type {Signal.State<boolean>} */
|
|
158
272
|
#selfNew
|
|
159
|
-
/** @readonly @type {Signal.Computed<boolean>} */
|
|
160
|
-
#editable
|
|
161
273
|
get selfNew() { return this.#selfNew.get(); }
|
|
162
274
|
set selfNew(v) { this.#selfNew.set(Boolean(v)); }
|
|
163
275
|
get new() { return this.#new.get(); }
|
|
164
276
|
set new(v) { this.#selfNew.set(Boolean(v)); }
|
|
165
|
-
get editable() { return this.#editable.get(); }
|
|
166
277
|
|
|
167
278
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
168
279
|
#selfHidden
|
|
@@ -212,6 +323,77 @@ class Store {
|
|
|
212
323
|
|
|
213
324
|
|
|
214
325
|
|
|
326
|
+
/** @readonly @type {Signal.State<string?>} */
|
|
327
|
+
#selfLabel
|
|
328
|
+
/** @readonly @type {Signal.Computed<string?>} */
|
|
329
|
+
#label
|
|
330
|
+
get selfLabel() { return this.#selfLabel.get(); }
|
|
331
|
+
set selfLabel(v) { this.#selfLabel.set(string(v)); }
|
|
332
|
+
get label() { return this.#label.get(); }
|
|
333
|
+
set label(v) { this.#selfLabel.set(string(v)); }
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
/** @readonly @type {Signal.State<string?>} */
|
|
337
|
+
#selfDescription
|
|
338
|
+
/** @readonly @type {Signal.Computed<string?>} */
|
|
339
|
+
#description
|
|
340
|
+
get selfDescription() { return this.#selfDescription.get(); }
|
|
341
|
+
set selfDescription(v) { this.#selfDescription.set(string(v)); }
|
|
342
|
+
get description() { return this.#description.get(); }
|
|
343
|
+
set description(v) { this.#selfDescription.set(string(v)); }
|
|
344
|
+
|
|
345
|
+
/** @readonly @type {Signal.State<string?>} */
|
|
346
|
+
#selfPlaceholder
|
|
347
|
+
/** @readonly @type {Signal.Computed<string?>} */
|
|
348
|
+
#placeholder
|
|
349
|
+
get selfPlaceholder() { return this.#selfPlaceholder.get(); }
|
|
350
|
+
set selfPlaceholder(v) { this.#selfPlaceholder.set(string(v)); }
|
|
351
|
+
get placeholder() { return this.#placeholder.get(); }
|
|
352
|
+
set placeholder(v) { this.#selfPlaceholder.set(string(v)); }
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
/** @readonly @type {Signal.State<number?>} */
|
|
356
|
+
#selfMin
|
|
357
|
+
/** @readonly @type {Signal.Computed<number?>} */
|
|
358
|
+
#min
|
|
359
|
+
get selfMin() { return this.#selfMin.get(); }
|
|
360
|
+
set selfMin(v) { this.#selfMin.set(number(v)); }
|
|
361
|
+
get min() { return this.#min.get(); }
|
|
362
|
+
set min(v) { this.#selfMin.set(number(v)); }
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
/** @readonly @type {Signal.State<number?>} */
|
|
366
|
+
#selfMax
|
|
367
|
+
/** @readonly @type {Signal.Computed<number?>} */
|
|
368
|
+
#max
|
|
369
|
+
get selfMax() { return this.#selfMax.get(); }
|
|
370
|
+
set selfMax(v) { this.#selfMax.set(number(v)); }
|
|
371
|
+
get max() { return this.#max.get(); }
|
|
372
|
+
set max(v) { this.#selfMax.set(number(v)); }
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
/** @readonly @type {Signal.State<number?>} */
|
|
376
|
+
#selfStep
|
|
377
|
+
/** @readonly @type {Signal.Computed<number?>} */
|
|
378
|
+
#step
|
|
379
|
+
get selfStep() { return this.#selfStep.get(); }
|
|
380
|
+
set selfStep(v) { this.#selfStep.set(number(v)); }
|
|
381
|
+
get step() { return this.#step.get(); }
|
|
382
|
+
set step(v) { this.#selfStep.set(number(v)); }
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
/** @readonly @type {Signal.State<(Schema.Value.Group | Schema.Value)[] | null>} */
|
|
386
|
+
#selfValues
|
|
387
|
+
/** @readonly @type {Signal.Computed<(Schema.Value.Group | Schema.Value)[] | null>} */
|
|
388
|
+
#values
|
|
389
|
+
get selfValues() { return this.#selfValues.get(); }
|
|
390
|
+
set selfValues(v) { this.#selfValues.set(values(v)); }
|
|
391
|
+
get values() { return this.#values.get(); }
|
|
392
|
+
set values(v) { this.#selfValues.set(values(v)); }
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
|
|
215
397
|
|
|
216
398
|
/** @returns {IterableIterator<[key: string | number, value: Store]>} */
|
|
217
399
|
*[Symbol.iterator]() {}
|
|
@@ -718,7 +900,7 @@ class ParseError extends Error {
|
|
|
718
900
|
|
|
719
901
|
/** @import * as Layout from './index.mjs' */
|
|
720
902
|
|
|
721
|
-
const attrPattern = /^(?<decorator>[
|
|
903
|
+
const attrPattern = /^(?<decorator>[:@!+*\.?]|style:|样式:)?(?<name>-?[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_:\d\.]*)$/u;
|
|
722
904
|
const nameRegex = /^(?<name>[a-zA-Z$\p{Unified_Ideograph}_][\da-zA-Z$\p{Unified_Ideograph}_]*)?$/u;
|
|
723
905
|
/**
|
|
724
906
|
* @param {Layout.Node} node
|
|
@@ -726,7 +908,7 @@ const nameRegex = /^(?<name>[a-zA-Z$\p{Unified_Ideograph}_][\da-zA-Z$\p{Unified_
|
|
|
726
908
|
* @param {Exclude<Layout.Options['creteEvent'], undefined>} creteEvent
|
|
727
909
|
*/
|
|
728
910
|
function createAttributeAdder(node, creteCalc, creteEvent) {
|
|
729
|
-
const { attrs, directives, events, classes, styles, vars, aliases } = node;
|
|
911
|
+
const { attrs, directives, events, classes, styles, vars, aliases, params } = node;
|
|
730
912
|
/**
|
|
731
913
|
* @param {string} qName
|
|
732
914
|
* @param {string} value
|
|
@@ -756,20 +938,23 @@ function createAttributeAdder(node, creteCalc, creteEvent) {
|
|
|
756
938
|
} else if (decorator === '+') {
|
|
757
939
|
vars[name] = !value ? '' : nameRegex.test(value) ? value : creteCalc(value);
|
|
758
940
|
} else if (decorator === '*') {
|
|
759
|
-
aliases[name] = nameRegex.test(value) ? value :
|
|
941
|
+
aliases[name] = nameRegex.test(value) ? value : creteCalc(value);
|
|
942
|
+
} else if (decorator === '?') {
|
|
943
|
+
params[name] = nameRegex.test(value) ? value : creteCalc(value);
|
|
760
944
|
} else if (decorator === '!') {
|
|
761
945
|
const key = name.toString();
|
|
762
946
|
switch (key) {
|
|
763
|
-
case 'fragment':
|
|
764
|
-
case 'else':
|
|
947
|
+
case 'fragment': directives.fragment = value || true; break;
|
|
948
|
+
case 'else': directives.else = true; break;
|
|
765
949
|
case 'enum':
|
|
766
|
-
directives
|
|
950
|
+
directives.enum = value ? nameRegex.test(value) ? value : creteCalc(value) : true;
|
|
767
951
|
break;
|
|
768
952
|
case 'if':
|
|
769
953
|
case 'text':
|
|
770
954
|
case 'html':
|
|
771
955
|
directives[key] = nameRegex.test(value) ? value : creteCalc(value);
|
|
772
956
|
break;
|
|
957
|
+
case 'template':
|
|
773
958
|
case 'bind':
|
|
774
959
|
case 'value':
|
|
775
960
|
case 'comment':
|
|
@@ -801,6 +986,7 @@ function createElement(name, is) {
|
|
|
801
986
|
styles: Object.create(null),
|
|
802
987
|
vars: Object.create(null),
|
|
803
988
|
aliases: Object.create(null),
|
|
989
|
+
params: Object.create(null),
|
|
804
990
|
};
|
|
805
991
|
}
|
|
806
992
|
|
|
@@ -1447,13 +1633,15 @@ function toString(value, formable) {
|
|
|
1447
1633
|
/**
|
|
1448
1634
|
* @typedef {object} Directives
|
|
1449
1635
|
*
|
|
1450
|
-
* @property {
|
|
1636
|
+
* @property {string} [template]
|
|
1637
|
+
*
|
|
1638
|
+
* @property {boolean | string} [fragment]
|
|
1451
1639
|
*
|
|
1452
1640
|
* @property {string | Function} [if]
|
|
1453
1641
|
* @property {boolean} [else]
|
|
1454
1642
|
*
|
|
1455
1643
|
* @property {string} [value] 值关联(关联为列表)
|
|
1456
|
-
* @property {boolean} [enum] 列表属性枚举
|
|
1644
|
+
* @property {boolean | string | Function} [enum] 列表属性枚举
|
|
1457
1645
|
*
|
|
1458
1646
|
* @property {string} [bind]
|
|
1459
1647
|
* @property {string | Function} [text]
|
|
@@ -1476,6 +1664,7 @@ function toString(value, formable) {
|
|
|
1476
1664
|
* @property {string?} [is]
|
|
1477
1665
|
* @property {string} [id]
|
|
1478
1666
|
* @property {Record<string, string | {name: string} | ((global: any) => void)>} attrs
|
|
1667
|
+
* @property {Record<string, string | ((global: any) => void)>} params
|
|
1479
1668
|
* @property {Record<string, string | boolean | ((global: any) => void)>} classes
|
|
1480
1669
|
* @property {Record<string, string | ((global: any) => void)>} styles
|
|
1481
1670
|
* @property {Record<string, string | (($event: any, global: any) => void)>} events
|
|
@@ -1536,6 +1725,28 @@ function watch(getter, callback) {
|
|
|
1536
1725
|
return () => { w.unwatch(computed); };
|
|
1537
1726
|
}
|
|
1538
1727
|
|
|
1728
|
+
/** @import * as Layout from '../Layout/index.mjs' */
|
|
1729
|
+
|
|
1730
|
+
const bindable = {
|
|
1731
|
+
new: true,
|
|
1732
|
+
readonly: true,
|
|
1733
|
+
|
|
1734
|
+
required: true,
|
|
1735
|
+
clearable: true,
|
|
1736
|
+
hidden: true,
|
|
1737
|
+
disabled: true,
|
|
1738
|
+
|
|
1739
|
+
label: true,
|
|
1740
|
+
description: true,
|
|
1741
|
+
placeholder: true,
|
|
1742
|
+
min: true,
|
|
1743
|
+
max: true,
|
|
1744
|
+
step: true,
|
|
1745
|
+
values: true,
|
|
1746
|
+
};
|
|
1747
|
+
/** @type {Set<keyof typeof bindable>} */
|
|
1748
|
+
// @ts-ignore
|
|
1749
|
+
const bindableSet = new Set(Object.keys(bindable));
|
|
1539
1750
|
/** @typedef {{get(): any; set?(v: any): void; exec?: null; store?: Store; calc?: null; }} ValueDefine */
|
|
1540
1751
|
/** @typedef {{get?: null; exec(...p: any[]): any; calc?: null}} ExecDefine */
|
|
1541
1752
|
/** @typedef {{get?: null; calc(...p: any[]): any; exec?: null;}} CalcDefine */
|
|
@@ -1555,13 +1766,12 @@ function *toItem(val, key = '', sign = '$') {
|
|
|
1555
1766
|
yield [`${key}${sign}length`, {get: () => val.length}];
|
|
1556
1767
|
yield [`${key}${sign}creatable`, {get: () => val.creatable}];
|
|
1557
1768
|
yield [`${key}${sign}immutable`, {get: () => val.immutable}];
|
|
1558
|
-
|
|
1559
|
-
yield [`${key}${sign}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
yield [`${key}${sign}readonly`, {get: () => val.readonly}];
|
|
1769
|
+
|
|
1770
|
+
yield [`${key}${sign}schema`, {get: () => val.schema}];
|
|
1771
|
+
|
|
1772
|
+
for (const k of bindableSet) {
|
|
1773
|
+
yield [`${key}${sign}${k}`, {get: () => val[k]}];
|
|
1774
|
+
}
|
|
1565
1775
|
if (!(val instanceof ArrayStore)) { return; }
|
|
1566
1776
|
yield [`${key}${sign}insert`, {exec: (index, value) => val.insert(index, value)}];
|
|
1567
1777
|
yield [`${key}${sign}add`, {exec: (v) => val.add(v)}];
|
|
@@ -1628,6 +1838,21 @@ class Environment {
|
|
|
1628
1838
|
*/
|
|
1629
1839
|
watch(value, cb) { return watch(() => this.exec(value), cb); }
|
|
1630
1840
|
|
|
1841
|
+
/**
|
|
1842
|
+
* @param {string | Function} name
|
|
1843
|
+
*/
|
|
1844
|
+
enum(name) {
|
|
1845
|
+
if (typeof name === 'function') {
|
|
1846
|
+
return () => name(this.getters);
|
|
1847
|
+
}
|
|
1848
|
+
if (typeof name !== 'string') { return null; }
|
|
1849
|
+
const item = this.#items[name];
|
|
1850
|
+
if (typeof item?.get !== 'function') { return null }
|
|
1851
|
+
const store = item.store;
|
|
1852
|
+
return store instanceof Store ? store : item.get;
|
|
1853
|
+
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1631
1856
|
/**
|
|
1632
1857
|
* @param {string} name
|
|
1633
1858
|
* @param {string} type
|
|
@@ -1641,11 +1866,11 @@ class Environment {
|
|
|
1641
1866
|
switch(type) {
|
|
1642
1867
|
case 'value': return watch(() => store.value, cb);
|
|
1643
1868
|
case 'state': return watch(() => store.state, cb);
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1869
|
+
}
|
|
1870
|
+
// @ts-ignore
|
|
1871
|
+
if (bindableSet.has(type)) {
|
|
1872
|
+
// @ts-ignore
|
|
1873
|
+
return watch(() => store[type], cb);
|
|
1649
1874
|
}
|
|
1650
1875
|
}
|
|
1651
1876
|
/**
|
|
@@ -1661,15 +1886,13 @@ class Environment {
|
|
|
1661
1886
|
if (typeof get !== 'function') { return; }
|
|
1662
1887
|
return { '$value': cb => watch(get, cb) }
|
|
1663
1888
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
'$readonly': cb => watch(() => store.readonly || !store.editable, cb),
|
|
1672
|
-
}
|
|
1889
|
+
/** @type {Record<string, ((cb: (value: any) => void) => () => void) | void> | void} */
|
|
1890
|
+
const res = Object.fromEntries([...bindableSet].map(v => [
|
|
1891
|
+
`$${v}`, cb => watch(() => store[v], cb)
|
|
1892
|
+
]));
|
|
1893
|
+
res.$value = cb => watch(() => store.value, cb);
|
|
1894
|
+
res.$state = cb => watch(() => store.state, cb);
|
|
1895
|
+
return res;
|
|
1673
1896
|
}
|
|
1674
1897
|
/**
|
|
1675
1898
|
* @param {string} name
|
|
@@ -1772,6 +1995,8 @@ class Environment {
|
|
|
1772
1995
|
#explicit = Object.create(null);
|
|
1773
1996
|
/** @type {Store?} */
|
|
1774
1997
|
#store = null
|
|
1998
|
+
/** @type {Record<string, any>?} */
|
|
1999
|
+
#object = null
|
|
1775
2000
|
/** @type {Store?} */
|
|
1776
2001
|
#parent = null
|
|
1777
2002
|
/** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>?} */
|
|
@@ -1787,6 +2012,7 @@ class Environment {
|
|
|
1787
2012
|
}));
|
|
1788
2013
|
const store = this.#store;
|
|
1789
2014
|
const parent = this.#parent;
|
|
2015
|
+
const object = this.#object;
|
|
1790
2016
|
if (store) {
|
|
1791
2017
|
for (const [key, item] of toItem(store)) {
|
|
1792
2018
|
ais[key] = item;
|
|
@@ -1795,6 +2021,11 @@ class Environment {
|
|
|
1795
2021
|
ais[key] = item;
|
|
1796
2022
|
}
|
|
1797
2023
|
}
|
|
2024
|
+
if (object) {
|
|
2025
|
+
for (const k of Object.keys(object)) {
|
|
2026
|
+
ais[`$${k}`] = {get: () => object[k]};
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
1798
2029
|
this.#allItems = ais;
|
|
1799
2030
|
return ais;
|
|
1800
2031
|
}
|
|
@@ -1803,7 +2034,7 @@ class Environment {
|
|
|
1803
2034
|
* @param {Store} store
|
|
1804
2035
|
* @param {Store} [parent]
|
|
1805
2036
|
*/
|
|
1806
|
-
|
|
2037
|
+
setStore(store, parent) {
|
|
1807
2038
|
const cloned = new Environment(this);
|
|
1808
2039
|
cloned.#store = store;
|
|
1809
2040
|
if (parent) { cloned.#parent = parent; }
|
|
@@ -1819,6 +2050,73 @@ class Environment {
|
|
|
1819
2050
|
}
|
|
1820
2051
|
return cloned;
|
|
1821
2052
|
}
|
|
2053
|
+
/**
|
|
2054
|
+
*
|
|
2055
|
+
* @param {Layout.Node} template
|
|
2056
|
+
* @param {Layout.Node} source
|
|
2057
|
+
* @param {Environment} sourceEnv
|
|
2058
|
+
*/
|
|
2059
|
+
params({params}, {attrs}, sourceEnv) {
|
|
2060
|
+
if (Object.keys(params).length === 0) { return this; }
|
|
2061
|
+
const cloned = new Environment(this);
|
|
2062
|
+
cloned.#store = this.#store;
|
|
2063
|
+
cloned.#parent = this.#parent;
|
|
2064
|
+
cloned.#object = this.#object;
|
|
2065
|
+
const explicit = cloned.#explicit;
|
|
2066
|
+
const items = cloned.#items;
|
|
2067
|
+
for (const [key, param] of Object.entries(params)) {
|
|
2068
|
+
const attr = key in attrs ? attrs[key] : null;
|
|
2069
|
+
if (typeof attr === 'string') {
|
|
2070
|
+
explicit[key] = items[key] = {get: () => attr};
|
|
2071
|
+
} else if (attr && typeof attr === 'object') {
|
|
2072
|
+
const item = sourceEnv.#items[attr.name];
|
|
2073
|
+
if (!item?.get) { continue; }
|
|
2074
|
+
if (!item.store) {
|
|
2075
|
+
explicit[key] = items[key] = item;
|
|
2076
|
+
continue;
|
|
2077
|
+
}
|
|
2078
|
+
for (const [k, it] of toItem(item.store, key)) {
|
|
2079
|
+
explicit[k] = items[k] = it;
|
|
2080
|
+
}
|
|
2081
|
+
continue;
|
|
2082
|
+
|
|
2083
|
+
} else if (typeof attr === 'function') {
|
|
2084
|
+
const val = new Signal.Computed(() => attr(sourceEnv.getters));
|
|
2085
|
+
explicit[key] = items[key] = {
|
|
2086
|
+
get: () => { return val.get(); },
|
|
2087
|
+
};
|
|
2088
|
+
|
|
2089
|
+
continue;
|
|
2090
|
+
} else if (typeof param === 'function') {
|
|
2091
|
+
const getters = cloned.getters;
|
|
2092
|
+
cloned.#getters = null;
|
|
2093
|
+
const val = new Signal.Computed(() => param(getters));
|
|
2094
|
+
explicit[key] = items[key] = {
|
|
2095
|
+
get: () => { return val.get(); },
|
|
2096
|
+
};
|
|
2097
|
+
continue;
|
|
2098
|
+
|
|
2099
|
+
} else {
|
|
2100
|
+
const item = items[param];
|
|
2101
|
+
if (!item?.get) { continue; }
|
|
2102
|
+
if (!item.store) {
|
|
2103
|
+
explicit[key] = items[key] = item;
|
|
2104
|
+
continue;
|
|
2105
|
+
}
|
|
2106
|
+
for (const [k, it] of toItem(item.store, key)) {
|
|
2107
|
+
explicit[k] = items[k] = it;
|
|
2108
|
+
}
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return cloned;
|
|
2113
|
+
}
|
|
2114
|
+
/** @param {Record<string, any>} object */
|
|
2115
|
+
setObject(object) {
|
|
2116
|
+
const cloned = new Environment(this);
|
|
2117
|
+
cloned.#object = object;
|
|
2118
|
+
return cloned;
|
|
2119
|
+
}
|
|
1822
2120
|
/**
|
|
1823
2121
|
*
|
|
1824
2122
|
* @param {Record<string, string | Function>} aliases
|
|
@@ -1829,6 +2127,7 @@ class Environment {
|
|
|
1829
2127
|
const cloned = new Environment(this);
|
|
1830
2128
|
cloned.#store = this.#store;
|
|
1831
2129
|
cloned.#parent = this.#parent;
|
|
2130
|
+
cloned.#object = this.#object;
|
|
1832
2131
|
const explicit = cloned.#explicit;
|
|
1833
2132
|
const items = cloned.#items;
|
|
1834
2133
|
for (const [key, name] of Object.entries(aliases)) {
|
|
@@ -2556,6 +2855,14 @@ function getAttrs(el, attr) {
|
|
|
2556
2855
|
const tagBindMap = {
|
|
2557
2856
|
input: {
|
|
2558
2857
|
attrs: {
|
|
2858
|
+
/** @param {any} v @param {HTMLInputElement} e */
|
|
2859
|
+
$min: (v, e) => {e.min = v;},
|
|
2860
|
+
/** @param {any} v @param {HTMLInputElement} e */
|
|
2861
|
+
$max: (v, e) => {e.max = v;},
|
|
2862
|
+
/** @param {any} v @param {HTMLInputElement} e */
|
|
2863
|
+
$step: (v, e) => {e.step = v;},
|
|
2864
|
+
/** @param {any} v @param {HTMLInputElement} e */
|
|
2865
|
+
$placeholder: (v, e) => {e.placeholder = v;},
|
|
2559
2866
|
/** @param {any} v @param {HTMLInputElement} e */
|
|
2560
2867
|
$disabled: (v, e) => {e.disabled = v;},
|
|
2561
2868
|
/** @param {any} v @param {HTMLInputElement} e */
|
|
@@ -2588,13 +2895,15 @@ const tagBindMap = {
|
|
|
2588
2895
|
},
|
|
2589
2896
|
textarea: {
|
|
2590
2897
|
attrs: {
|
|
2591
|
-
/** @param {any} v @param {
|
|
2898
|
+
/** @param {any} v @param {HTMLTextAreaElement} e */
|
|
2899
|
+
$placeholder: (v, e) => {e.placeholder = v;},
|
|
2900
|
+
/** @param {any} v @param {HTMLTextAreaElement} e */
|
|
2592
2901
|
$disabled: (v, e) => {e.disabled = v;},
|
|
2593
|
-
/** @param {any} v @param {
|
|
2902
|
+
/** @param {any} v @param {HTMLTextAreaElement} e */
|
|
2594
2903
|
$readonly: (v, e) => {e.readOnly = v;},
|
|
2595
|
-
/** @param {any} v @param {
|
|
2904
|
+
/** @param {any} v @param {HTMLTextAreaElement} e */
|
|
2596
2905
|
$required: (v, e) => {e.required = v;},
|
|
2597
|
-
/** @param {any} v @param {
|
|
2906
|
+
/** @param {any} v @param {HTMLTextAreaElement} e */
|
|
2598
2907
|
$value: (v, e) => { e.value = toText$1(v); },
|
|
2599
2908
|
},
|
|
2600
2909
|
events: {
|
|
@@ -2603,13 +2912,11 @@ const tagBindMap = {
|
|
|
2603
2912
|
},
|
|
2604
2913
|
select: {
|
|
2605
2914
|
attrs: {
|
|
2606
|
-
/** @param {any} v @param {
|
|
2915
|
+
/** @param {any} v @param {HTMLSelectElement} e */
|
|
2607
2916
|
$disabled: (v, e) => {e.disabled = v;},
|
|
2608
|
-
/** @param {any} v @param {
|
|
2609
|
-
$readonly: (v, e) => {e.readOnly = v;},
|
|
2610
|
-
/** @param {any} v @param {HTMLInputElement} e */
|
|
2917
|
+
/** @param {any} v @param {HTMLSelectElement} e */
|
|
2611
2918
|
$required: (v, e) => {e.required = v;},
|
|
2612
|
-
/** @param {any} v @param {
|
|
2919
|
+
/** @param {any} v @param {HTMLSelectElement} e */
|
|
2613
2920
|
$value: (v, e) => { e.value = toText$1(v); },
|
|
2614
2921
|
},
|
|
2615
2922
|
events: {
|
|
@@ -2760,7 +3067,7 @@ function renderArray(layout, parent, next, store, env, renderItem) {
|
|
|
2760
3067
|
if (!old) {
|
|
2761
3068
|
const ItemStart = parent.insertBefore(document.createComment(''), nextNode);
|
|
2762
3069
|
const itemEnd = parent.insertBefore(document.createComment(''), nextNode);
|
|
2763
|
-
const d = renderItem(layout, parent, itemEnd, child, env.
|
|
3070
|
+
const d = renderItem(layout, parent, itemEnd, child, env.setStore(child, store));
|
|
2764
3071
|
seMap.set(child, [ItemStart, itemEnd, d]);
|
|
2765
3072
|
continue;
|
|
2766
3073
|
}
|
|
@@ -2784,6 +3091,7 @@ function renderArray(layout, parent, next, store, env, renderItem) {
|
|
|
2784
3091
|
|
|
2785
3092
|
return () => {
|
|
2786
3093
|
start.remove();
|
|
3094
|
+
destroyMap(seMap);
|
|
2787
3095
|
childrenResult();
|
|
2788
3096
|
};
|
|
2789
3097
|
}
|
|
@@ -2852,15 +3160,24 @@ function renderFillDirectives(parent, next, envs, { text, html }) {
|
|
|
2852
3160
|
* @param {Element} parent
|
|
2853
3161
|
* @param {Node?} next
|
|
2854
3162
|
* @param {Environment} envs
|
|
2855
|
-
* @param {
|
|
3163
|
+
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
3164
|
+
* @param {(layout: Layout.Node, templates: Record<string, any>) => () => void} renderItem
|
|
2856
3165
|
* @returns {() => void}
|
|
2857
3166
|
*/
|
|
2858
|
-
function renderList(layouts, parent, next, envs, renderItem) {
|
|
3167
|
+
function renderList(layouts, parent, next, envs, templates, renderItem) {
|
|
2859
3168
|
|
|
2860
3169
|
/** @type {Set<() => void>?} */
|
|
2861
3170
|
let bkList = new Set();
|
|
2862
3171
|
/** @type {[string | Function | null, Layout.Node][]} */
|
|
2863
3172
|
let ifList = [];
|
|
3173
|
+
/** @type {Record<string, [Layout.Node, Environment]>} */
|
|
3174
|
+
let currentTemplates = Object.create(templates);
|
|
3175
|
+
for (const layout of layouts) {
|
|
3176
|
+
if (typeof layout === 'string') { continue; }
|
|
3177
|
+
const name = layout.directives.template;
|
|
3178
|
+
if (!name) { continue; }
|
|
3179
|
+
currentTemplates[name] = [layout, envs];
|
|
3180
|
+
}
|
|
2864
3181
|
|
|
2865
3182
|
/** @param {[string | Function | null, Layout.Node][]} list */
|
|
2866
3183
|
function renderIf(list) {
|
|
@@ -2878,7 +3195,7 @@ function renderList(layouts, parent, next, envs, renderItem) {
|
|
|
2878
3195
|
function renderIndex(index) {
|
|
2879
3196
|
const layout = list[index]?.[1];
|
|
2880
3197
|
if (!layout) { return; }
|
|
2881
|
-
destroy = renderItem(layout);
|
|
3198
|
+
destroy = renderItem(layout, currentTemplates);
|
|
2882
3199
|
}
|
|
2883
3200
|
bkList.add(() => {
|
|
2884
3201
|
destroy();
|
|
@@ -2905,6 +3222,11 @@ function renderList(layouts, parent, next, envs, renderItem) {
|
|
|
2905
3222
|
bkList.add(() => node.remove());
|
|
2906
3223
|
continue;
|
|
2907
3224
|
}
|
|
3225
|
+
if (layout.directives.template) {
|
|
3226
|
+
renderIf(ifList);
|
|
3227
|
+
ifList = [];
|
|
3228
|
+
continue;
|
|
3229
|
+
}
|
|
2908
3230
|
if (ifList.length && layout.directives.else) {
|
|
2909
3231
|
const ifv = layout.directives.if || null;
|
|
2910
3232
|
ifList.push([ifv, layout]);
|
|
@@ -2922,7 +3244,7 @@ function renderList(layouts, parent, next, envs, renderItem) {
|
|
|
2922
3244
|
continue;
|
|
2923
3245
|
}
|
|
2924
3246
|
bkList.add(
|
|
2925
|
-
renderItem(layout)
|
|
3247
|
+
renderItem(layout, currentTemplates)
|
|
2926
3248
|
);
|
|
2927
3249
|
}
|
|
2928
3250
|
|
|
@@ -2936,36 +3258,147 @@ function renderList(layouts, parent, next, envs, renderItem) {
|
|
|
2936
3258
|
};
|
|
2937
3259
|
}
|
|
2938
3260
|
|
|
3261
|
+
/** @import * as Layout from '../Layout/index.mjs' */
|
|
3262
|
+
/** @import Store, { ObjectStore } from '../Store/index.mjs' */
|
|
3263
|
+
|
|
3264
|
+
/**
|
|
3265
|
+
*
|
|
3266
|
+
* @param {Layout.Node} layout
|
|
3267
|
+
* @param {Element} parent
|
|
3268
|
+
* @param {Node?} next
|
|
3269
|
+
* @param {ObjectStore} store
|
|
3270
|
+
* @param {Environment} env
|
|
3271
|
+
* @param {(layout: Layout.Node, parent: Element, next: Node | null, store: Store, env: any) => () => void} renderItem
|
|
3272
|
+
*/
|
|
3273
|
+
function renderObject(layout, parent, next, store, env, renderItem) {
|
|
3274
|
+
/** @type {(() => void)[]} */
|
|
3275
|
+
const children = [];
|
|
3276
|
+
for (const [k, child] of [...store]) {
|
|
3277
|
+
children.push(renderItem(layout, parent, next, child, env.setStore(child, store)));
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
return () => {
|
|
3281
|
+
for (const d of children) {
|
|
3282
|
+
d();
|
|
3283
|
+
}
|
|
3284
|
+
};
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
/** @import * as Layout from '../Layout/index.mjs' */
|
|
2939
3288
|
/** @import Store from '../Store/index.mjs' */
|
|
2940
3289
|
|
|
2941
3290
|
/**
|
|
3291
|
+
*
|
|
2942
3292
|
* @param {Layout.Node} layout
|
|
2943
3293
|
* @param {Element} parent
|
|
2944
3294
|
* @param {Node?} next
|
|
3295
|
+
* @param {Store} store
|
|
3296
|
+
* @param {() => any} getter
|
|
2945
3297
|
* @param {Environment} env
|
|
2946
|
-
* @param {(layout: Layout.Node) => () => void} renderItem
|
|
2947
|
-
* @returns {() => void}
|
|
3298
|
+
* @param {(layout: Layout.Node, parent: Element, next: Node | null, store: Store, env: any) => () => void} renderItem
|
|
2948
3299
|
*/
|
|
2949
|
-
function
|
|
2950
|
-
|
|
2951
|
-
|
|
3300
|
+
function renderEnum(layout, parent, next, store, getter, env, renderItem) {
|
|
3301
|
+
|
|
3302
|
+
/** @type {Signal.Computed<[value: any, index: number, kKey: any][]>} */
|
|
3303
|
+
const list = new Signal.Computed(() => {
|
|
3304
|
+
const values = getter();
|
|
3305
|
+
if (typeof values === 'number') {
|
|
3306
|
+
const n = Math.floor(values);
|
|
3307
|
+
if (!n) { return []; }
|
|
3308
|
+
return Array(n).fill(0).map((_, i) => [i + 1, i, i]);
|
|
3309
|
+
}
|
|
3310
|
+
if (!values || typeof values !== 'object') { return []; }
|
|
3311
|
+
if (Array.isArray(values)) {
|
|
3312
|
+
return values.map((v, i) => [v, i, i]);
|
|
3313
|
+
}
|
|
3314
|
+
// TODO: 转列表
|
|
3315
|
+
return Object.entries(values).map(([k,v], i) => [v, i, k]);
|
|
3316
|
+
});
|
|
3317
|
+
const start = parent.insertBefore(document.createComment(''), next);
|
|
3318
|
+
/** @type {[Comment, Comment, () => void, key: any, Signal.State<any>, Signal.State<any>][]} */
|
|
3319
|
+
let seMap = [];
|
|
3320
|
+
/** @param {[Comment, Comment, () => void, key: any, Signal.State<any>, Signal.State<any>][]} map */
|
|
3321
|
+
function destroyMap(map) {
|
|
3322
|
+
for (const [s, e, d] of map) {
|
|
3323
|
+
d();
|
|
3324
|
+
s.remove();
|
|
3325
|
+
e.remove();
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
const childrenResult = watch(() => list.get(), function render(children) {
|
|
3329
|
+
if (!start.parentNode) { return; }
|
|
3330
|
+
let nextNode = start.nextSibling;
|
|
3331
|
+
const oldSeMap = seMap;
|
|
3332
|
+
seMap = [];
|
|
3333
|
+
for (const [value, index, key] of children) {
|
|
3334
|
+
const index2 = oldSeMap.findIndex((v) => v[3] === key);
|
|
3335
|
+
const [old] = index2 >= 0 ? oldSeMap.splice(index2, 1) : [];
|
|
3336
|
+
if (!old) {
|
|
3337
|
+
const ItemStart = parent.insertBefore(document.createComment(''), nextNode);
|
|
3338
|
+
const itemEnd = parent.insertBefore(document.createComment(''), nextNode);
|
|
3339
|
+
const valueState = new Signal.State(value);
|
|
3340
|
+
const indexState = new Signal.State(index);
|
|
3341
|
+
const d = renderItem(layout, parent, itemEnd, store, env.setObject({
|
|
3342
|
+
get key() { return key; },
|
|
3343
|
+
get value() { return valueState.get(); },
|
|
3344
|
+
get index() { return indexState.get(); },
|
|
3345
|
+
}));
|
|
3346
|
+
seMap.push([ItemStart, itemEnd, d, key, valueState, indexState]);
|
|
3347
|
+
continue;
|
|
3348
|
+
}
|
|
3349
|
+
seMap.push(old);
|
|
3350
|
+
old[4].set(value);
|
|
3351
|
+
old[5].set(index);
|
|
3352
|
+
if (nextNode === old[0]) {
|
|
3353
|
+
nextNode = old[1].nextSibling;
|
|
3354
|
+
continue;
|
|
3355
|
+
}
|
|
3356
|
+
/** @type {Node?} */
|
|
3357
|
+
let c = old[0];
|
|
3358
|
+
while (c && c !== old[1]) {
|
|
3359
|
+
const o = c;
|
|
3360
|
+
c = c.nextSibling;
|
|
3361
|
+
parent.insertBefore(o, nextNode);
|
|
3362
|
+
}
|
|
3363
|
+
parent.insertBefore(old[1], nextNode);
|
|
3364
|
+
}
|
|
3365
|
+
destroyMap(oldSeMap);
|
|
3366
|
+
});
|
|
2952
3367
|
|
|
3368
|
+
return () => {
|
|
3369
|
+
start.remove();
|
|
3370
|
+
destroyMap(seMap);
|
|
3371
|
+
childrenResult();
|
|
3372
|
+
};
|
|
2953
3373
|
}
|
|
3374
|
+
|
|
3375
|
+
/** @import Store from '../Store/index.mjs' */
|
|
3376
|
+
|
|
2954
3377
|
/**
|
|
2955
3378
|
* @param {Layout.Node} layout
|
|
2956
3379
|
* @param {Element} parent
|
|
2957
3380
|
* @param {Node?} next
|
|
2958
3381
|
* @param {Store} store
|
|
2959
3382
|
* @param {Environment} env
|
|
3383
|
+
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
2960
3384
|
* @param {string[]} componentPath
|
|
2961
3385
|
* @param {((path: string[]) => Component?)?} [getComponent]
|
|
2962
3386
|
*/
|
|
2963
|
-
function renderItem(layout, parent, next, store, env, componentPath, getComponent) {
|
|
3387
|
+
function renderItem(layout, parent, next, store, env, templates, componentPath, getComponent) {
|
|
2964
3388
|
env = env.set(layout.aliases, layout.vars);
|
|
3389
|
+
const fragment = layout.directives.fragment;
|
|
3390
|
+
if (fragment && typeof fragment === 'string') {
|
|
3391
|
+
const template = templates[fragment];
|
|
3392
|
+
if (!template) { return () => {}; }
|
|
3393
|
+
const [templateLayout, templateEnv] = template;
|
|
3394
|
+
const newEnv = templateEnv.params(templateLayout, layout, env);
|
|
3395
|
+
return render(templateLayout, parent, next, store, newEnv, templates, componentPath, getComponent);
|
|
3396
|
+
}
|
|
2965
3397
|
if (!layout.name || layout.directives.fragment) {
|
|
2966
|
-
return
|
|
2967
|
-
|
|
2968
|
-
|
|
3398
|
+
return renderFillDirectives(parent, next, env, layout.directives) ||
|
|
3399
|
+
renderList(layout.children || [], parent, next, env, templates, (layout, templates) => {
|
|
3400
|
+
return render(layout, parent, next, store, env, templates, componentPath, getComponent);
|
|
3401
|
+
});
|
|
2969
3402
|
}
|
|
2970
3403
|
const path = [...componentPath, layout.name];
|
|
2971
3404
|
const component = getComponent?.(path);
|
|
@@ -2989,13 +3422,13 @@ function renderItem(layout, parent, next, store, env, componentPath, getComponen
|
|
|
2989
3422
|
: createTagComponent(context, component.tag, component.is)
|
|
2990
3423
|
: createTagComponent(context, layout.name, layout.is);
|
|
2991
3424
|
const root = Array.isArray(r) ? r[0] : r;
|
|
2992
|
-
const slot = Array.isArray(r)
|
|
3425
|
+
const slot = Array.isArray(r) ? r[1] : root;
|
|
2993
3426
|
parent.insertBefore(root, next);
|
|
2994
|
-
const children =
|
|
3427
|
+
const children = slot ?
|
|
2995
3428
|
renderFillDirectives(slot, null, env, layout.directives)
|
|
2996
|
-
|| renderList(layout.children || [], slot, null, env,
|
|
2997
|
-
return render(
|
|
2998
|
-
});
|
|
3429
|
+
|| renderList(layout.children || [], slot, null, env, templates, (layout, templates) => {
|
|
3430
|
+
return render(layout, slot, null, store, env, templates, componentPath, getComponent);
|
|
3431
|
+
}) : () => {};
|
|
2999
3432
|
|
|
3000
3433
|
|
|
3001
3434
|
bindClasses(root, layout.classes, env);
|
|
@@ -3017,26 +3450,41 @@ function renderItem(layout, parent, next, store, env, componentPath, getComponen
|
|
|
3017
3450
|
* @param {Node?} next
|
|
3018
3451
|
* @param {Store} store
|
|
3019
3452
|
* @param {Environment} env
|
|
3453
|
+
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
3020
3454
|
* @param {string[]} componentPath
|
|
3021
3455
|
* @param {((path: string[]) => Component?)?} [getComponent]
|
|
3022
3456
|
* @returns {() => void}
|
|
3023
3457
|
*/
|
|
3024
|
-
function render(layout, parent, next, store, env, componentPath, getComponent) {
|
|
3458
|
+
function render(layout, parent, next, store, env, templates, componentPath, getComponent) {
|
|
3025
3459
|
const { directives } = layout;
|
|
3026
3460
|
const { value } = directives;
|
|
3027
3461
|
if (value) {
|
|
3028
3462
|
const newStore = store.child(value);
|
|
3029
3463
|
if (!newStore) { return () => {}; }
|
|
3030
3464
|
store = newStore;
|
|
3031
|
-
env = env.
|
|
3465
|
+
env = env.setStore(store);
|
|
3032
3466
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3467
|
+
const enumValue = directives.enum;
|
|
3468
|
+
if (!enumValue) {
|
|
3469
|
+
return renderItem(layout, parent, next, store, env, templates, componentPath, getComponent);
|
|
3035
3470
|
}
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
return
|
|
3039
|
-
|
|
3471
|
+
const newStore = enumValue === true ? store : env.enum(enumValue);
|
|
3472
|
+
if (newStore instanceof ArrayStore) {
|
|
3473
|
+
return renderArray(layout, parent, next, newStore, env, (a, b, c, store, env) => {
|
|
3474
|
+
return renderItem(a, b, c, store, env, templates, componentPath, getComponent);
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
if (newStore instanceof ObjectStore) {
|
|
3478
|
+
return renderObject(layout, parent, next, newStore, env, (a, b, c, store, env) => {
|
|
3479
|
+
return renderItem(a, b, c, store, env, templates, componentPath, getComponent);
|
|
3480
|
+
});
|
|
3481
|
+
}
|
|
3482
|
+
if (typeof newStore === 'function') {
|
|
3483
|
+
return renderEnum(layout, parent, next, store, newStore, env, (a, b, c, store, env) => {
|
|
3484
|
+
return renderItem(a, b, c, store, env, templates, componentPath, getComponent);
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3487
|
+
return () => { };
|
|
3040
3488
|
}
|
|
3041
3489
|
|
|
3042
3490
|
/**
|
|
@@ -3067,9 +3515,10 @@ function index (store, layouts, parent, opt1, opt2) {
|
|
|
3067
3515
|
const options = [opt1, opt2];
|
|
3068
3516
|
const components = options.find(v => typeof v === 'function');
|
|
3069
3517
|
const global = options.find(v => typeof v === 'object');
|
|
3070
|
-
const env = new Environment(global).
|
|
3071
|
-
|
|
3072
|
-
|
|
3518
|
+
const env = new Environment(global).setStore(store);
|
|
3519
|
+
const templates = Object.create(null);
|
|
3520
|
+
return renderList(layouts, parent, null, env, templates, (layout, templates) => {
|
|
3521
|
+
return render(layout, parent, null, store, env, templates, [], components);
|
|
3073
3522
|
});
|
|
3074
3523
|
}
|
|
3075
3524
|
|