@neeloong/form 0.1.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/index.mjs ADDED
@@ -0,0 +1,3076 @@
1
+ /*!
2
+ * @neeloong/form v0.1.0
3
+ * (c) 2024-2025 Fierflame
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import { Signal } from 'signal-polyfill';
8
+ export { Signal } from 'signal-polyfill';
9
+
10
+ /** @import Store from './index.mjs' */
11
+
12
+ /**
13
+ *
14
+ * @param {Store} self
15
+ * @param {boolean?} [defState]
16
+ * @param {boolean | ((store: Store) => boolean) | null} [fn]
17
+ * @param {Signal.Computed<boolean>?} [parent]
18
+ * @returns {[Signal.State<boolean?>, Signal.Computed<boolean>]}
19
+ */
20
+ const createBooleanStates = (self, defState, fn, parent) => {
21
+
22
+ const selfState = new Signal.State(typeof defState === 'boolean' ? defState : null);
23
+ const scriptState = typeof fn === 'function' ? new Signal.Computed(() => fn(self)) : fn ? new Signal.Computed(() => true) : new Signal.Computed(() => false);
24
+
25
+ const getState = () => {
26
+ const s = selfState.get();
27
+ return s === null ? scriptState.get() : s;
28
+ };
29
+ const state = parent
30
+ ? new Signal.Computed(() => parent.get() || getState())
31
+ : new Signal.Computed(getState);
32
+
33
+ return [selfState, state];
34
+
35
+ };
36
+
37
+ /** @import { Schema } from '../types.mjs' */
38
+ /**
39
+ * @template [T=any]
40
+ */
41
+ class Store {
42
+ /**
43
+ * @param {Schema} schema
44
+ * @param {object} [options]
45
+ * @param {boolean} [options.new]
46
+ */
47
+ static create(schema, options = {}) {
48
+ return new ObjectStore({type: null, props: schema}, { ...options, parent: null });
49
+ }
50
+ #null = false;
51
+ get null() { return this.#null; }
52
+ /**
53
+ * @param {Schema.Field} schema
54
+ * @param {object} options
55
+ * @param {*} [options.parent]
56
+ * @param {*} [options.state]
57
+ * @param {number | string | null} [options.index]
58
+ * @param {number} [options.length]
59
+ * @param {boolean} [options.null]
60
+ * @param {boolean} [options.new]
61
+ * @param {boolean} [options.hidden]
62
+ * @param {boolean} [options.clearable]
63
+ * @param {boolean} [options.required]
64
+ * @param {boolean} [options.readonly]
65
+ * @param {boolean} [options.disabled]
66
+ * @param {((value: any) => any)?} [options.setValue]
67
+ * @param {((value: any) => any)?} [options.setState]
68
+ * @param {((value: any, state: any) => [value: any, state: any])?} [options.convert]
69
+ * @param {((value: T?, index: any) => void)?} [options.onUpdate]
70
+ * @param {((value: T?, index: any) => void)?} [options.onUpdateState]
71
+ */
72
+ constructor(schema, {
73
+ null: isNull, state,
74
+ setValue, setState, convert, onUpdate, onUpdateState,
75
+ index, length, new: isNew, parent: parentNode,
76
+ hidden, clearable, required, disabled, readonly,
77
+ }) {
78
+ this.schema = schema;
79
+ this.#state.set(typeof state === 'object' && state || {});
80
+ const parent = parentNode instanceof Store ? parentNode : null;
81
+ if (parent) {
82
+ this.#parent = parent;
83
+ this.#root = parent.#root;
84
+ // TODO: 事件向上冒泡
85
+ }
86
+
87
+ const selfNewState = new Signal.State(Boolean(isNew));
88
+ this.#selfNew = selfNewState;
89
+ /** @type {Signal.Computed<boolean>} */
90
+ const newState = parent
91
+ ? new Signal.Computed(() => parent.#new.get() || selfNewState.get())
92
+ : new Signal.Computed(() => selfNewState.get());
93
+
94
+ this.#new = newState;
95
+ const immutable = Boolean(schema.immutable);
96
+ const creatable = schema.creatable !== false;
97
+ this.#immutable = immutable;
98
+ this.#creatable = creatable;
99
+ this.#editable = new Signal.Computed(() => newState.get() ? creatable : !immutable);
100
+
101
+ [this.#selfHidden, this.#hidden] = createBooleanStates(this, hidden, schema.hidden, parent ? parent.#hidden : null);
102
+ [this.#selfClearable, this.#clearable] = createBooleanStates(this, clearable, schema.clearable, parent ? parent.#clearable : null);
103
+ [this.#selfRequired, this.#required] = createBooleanStates(this, required, schema.required, parent ? parent.#required : null);
104
+ [this.#selfDisabled, this.#disabled] = createBooleanStates(this, disabled, schema.disabled, parent ? parent.#disabled : null);
105
+ [this.#selfReadonly, this.#readonly] = createBooleanStates(this, readonly, schema.readonly, parent ? parent.#readonly : null);
106
+
107
+ if (isNull) {
108
+ this.#null = true;
109
+ return;
110
+ }
111
+ this.#onUpdate = onUpdate || null;
112
+ this.#onUpdateState = onUpdateState || null;
113
+ this.#setValue = typeof setValue === 'function' ? setValue : null;
114
+ this.#setState = typeof setState === 'function' ? setState : null;
115
+ this.#convert = typeof convert === 'function' ? convert : null;
116
+ this.#length.set(length || 0);
117
+ this.#index.set(index ?? '');
118
+
119
+ }
120
+ #destroyed = false;
121
+ /** @type {((value: any) => any)?} */
122
+ #setValue = null
123
+ /** @type {((value: any) => any)?} */
124
+ #setState = null
125
+ /** @type {((value: any, state: any) => [value: any, state: any])?} */
126
+ #convert = null
127
+ /** @type {((value: any, index: any) => void)?} */
128
+ #onUpdate = null
129
+ /** @type {((value: any, index: any) => void)?} */
130
+ #onUpdateState = null
131
+ /** @readonly @type {Store?} */
132
+ #parent = null;
133
+ /** @readonly @type {Store} */
134
+ #root = this;
135
+ get parent() { return this.#parent; }
136
+ get root() { return this.#root; }
137
+
138
+ #length = new Signal.State(0);
139
+ get length() { return this.#length.get(); }
140
+ set length(v) { this.#length.set(v); }
141
+ #index = new Signal.State(/** @type {string | number} */(''));
142
+ get index() { return this.#index.get(); }
143
+ set index(v) { this.#index.set(v); }
144
+ get no() {
145
+ if (this.#null) { return ''; }
146
+ const index = this.index;
147
+ return typeof index === 'number' ? index + 1 : index;
148
+ }
149
+
150
+ #creatable = true;
151
+ get creatable() { return this.#creatable; }
152
+ #immutable = false;
153
+ get immutable() { return this.#immutable; }
154
+
155
+ /** @readonly @type {Signal.Computed<boolean>} */
156
+ #new
157
+ /** @readonly @type {Signal.State<boolean>} */
158
+ #selfNew
159
+ /** @readonly @type {Signal.Computed<boolean>} */
160
+ #editable
161
+ get selfNew() { return this.#selfNew.get(); }
162
+ set selfNew(v) { this.#selfNew.set(Boolean(v)); }
163
+ get new() { return this.#new.get(); }
164
+ set new(v) { this.#selfNew.set(Boolean(v)); }
165
+ get editable() { return this.#editable.get(); }
166
+
167
+ /** @readonly @type {Signal.State<boolean?>} */
168
+ #selfHidden
169
+ /** @readonly @type {Signal.Computed<boolean>} */
170
+ #hidden
171
+ get selfHidden() { return this.#selfHidden.get(); }
172
+ set selfHidden(v) { this.#selfHidden.set(typeof v === 'boolean' ? v : null); }
173
+ get hidden() { return this.#hidden.get(); }
174
+ set hidden(v) { this.#selfHidden.set(typeof v === 'boolean' ? v : null); }
175
+
176
+ /** @readonly @type {Signal.State<boolean?>} */
177
+ #selfClearable
178
+ /** @readonly @type {Signal.Computed<boolean>} */
179
+ #clearable
180
+ get selfClearable() { return this.#selfClearable.get(); }
181
+ set selfClearable(v) { this.#selfClearable.set(typeof v === 'boolean' ? v : null); }
182
+ get clearable() { return this.#clearable.get(); }
183
+ set clearable(v) { this.#selfClearable.set(typeof v === 'boolean' ? v : null); }
184
+
185
+ /** @readonly @type {Signal.State<boolean?>} */
186
+ #selfRequired
187
+ /** @readonly @type {Signal.Computed<boolean>} */
188
+ #required
189
+ get selfRequired() { return this.#selfRequired.get(); }
190
+ set selfRequired(v) { this.#selfRequired.set(typeof v === 'boolean' ? v : null); }
191
+ get required() { return this.#required.get(); }
192
+ set required(v) { this.#selfRequired.set(typeof v === 'boolean' ? v : null); }
193
+
194
+ /** @readonly @type {Signal.State<boolean?>} */
195
+ #selfDisabled
196
+ /** @readonly @type {Signal.Computed<boolean>} */
197
+ #disabled
198
+ get selfDisabled() { return this.#selfDisabled.get(); }
199
+ set selfDisabled(v) { this.#selfDisabled.set(typeof v === 'boolean' ? v : null); }
200
+ get disabled() { return this.#disabled.get(); }
201
+ set disabled(v) { this.#selfDisabled.set(typeof v === 'boolean' ? v : null); }
202
+
203
+ /** @readonly @type {Signal.State<boolean?>} */
204
+ #selfReadonly
205
+ /** @readonly @type {Signal.Computed<boolean>} */
206
+ #readonly
207
+ get selfReadonly() { return this.#selfReadonly.get(); }
208
+ set selfReadonly(v) { this.#selfReadonly.set(typeof v === 'boolean' ? v : null); }
209
+ get readonly() { return this.#readonly.get(); }
210
+ set readonly(v) { this.#selfReadonly.set(typeof v === 'boolean' ? v : null); }
211
+
212
+
213
+
214
+
215
+
216
+ /** @returns {IterableIterator<[key: string | number, value: Store]>} */
217
+ *[Symbol.iterator]() {}
218
+ /**
219
+ *
220
+ * @param {string | number} key
221
+ * @returns {Store?}
222
+ */
223
+ child(key) { return null; }
224
+
225
+ #set = false;
226
+ /** @type {T?} */
227
+ #initValue = null;
228
+ #lastValue = this.#initValue;
229
+ #value = new Signal.State(this.#initValue);
230
+
231
+
232
+ #state = new Signal.State(/** @type {any} */(null));
233
+ #lastState = this.#state.get();
234
+
235
+
236
+ get changed() { return this.#value.get() === this.#lastValue; }
237
+ get saved() { return this.#value.get() === this.#initValue; }
238
+
239
+ get value() { return this.#value.get(); }
240
+ set value(v) {
241
+ if (this.#destroyed) { return; }
242
+ const val = this.#setValue?.(v) || v;
243
+ this.#value.set(val);
244
+ if (!this.#set) {
245
+ this.#set = true;
246
+ this.#initValue = v;
247
+ }
248
+ this.#onUpdate?.(this.#value.get(), this.#index.get());
249
+ this.#requestUpdate();
250
+ }
251
+
252
+ get state() { return this.#state.get(); }
253
+ set state(v) {
254
+ if (this.#destroyed) { return; }
255
+ const sta = this.#setState?.(v) || v;
256
+ this.#state.set(sta);
257
+ this.#set = true;
258
+ this.#onUpdateState?.(this.#state.get(), this.#index.get());
259
+ this.#requestUpdate();
260
+ }
261
+ #requestUpdate() {
262
+ if (this.#needUpdate) { return; }
263
+ this.#needUpdate = true;
264
+ queueMicrotask(() => {
265
+ const oldValue = this.#value.get();
266
+ const oldState = this.#state.get();
267
+ return this.#runUpdate(oldValue, oldState);
268
+ });
269
+ }
270
+
271
+
272
+ #needUpdate = false;
273
+ /**
274
+ *
275
+ * @param {T} value
276
+ * @param {*} state
277
+ * @returns
278
+ */
279
+ #toUpdate(value, state) {
280
+ if (this.#destroyed) { return value; }
281
+ const [val,sta] = this.#convert?.(value, state) || [value, state];
282
+ if(this.#value.get() === val && this.#state.get() === sta) { return [val,sta] }
283
+ this.#value.set(val);
284
+ this.#state.set(sta);
285
+ if (!this.#set) {
286
+ this.#set = true;
287
+ this.#initValue = val;
288
+ }
289
+ return this.#runUpdate(val, sta);
290
+ }
291
+ /**
292
+ *
293
+ * @param {*} val
294
+ * @param {*} sta
295
+ * @returns
296
+ */
297
+ #runUpdate(val, sta) {
298
+ if (this.#destroyed) { return [val, sta]; }
299
+ this.#needUpdate = false;
300
+ if (val && typeof val === 'object') {
301
+ /** @type {T} */
302
+ // @ts-ignore
303
+ let newValues = Array.isArray(val) ? [...val] : {...val};
304
+ let newStates = Array.isArray(val) ? Array.isArray(sta) ? [...sta] : [] : {...sta};
305
+ let updated = false;
306
+ for (const [key, field] of this) {
307
+ // @ts-ignore
308
+ const data = val[key];
309
+ const state = sta?.[key];
310
+ const [newData, newState] = field.#toUpdate(data, state);
311
+ if (data !== newData) {
312
+ // @ts-ignore
313
+ newValues[key] = newData;
314
+ updated = true;
315
+ }
316
+ if (state !== newState) {
317
+ newStates[key] = newState;
318
+ updated = true;
319
+ }
320
+ }
321
+ if (updated) {
322
+ val = newValues;
323
+ sta = newStates;
324
+ this.#value.set(val);
325
+ this.#state.set(newStates);
326
+ }
327
+ }
328
+ if (this.#lastValue === val && this.#lastState === sta) {
329
+ return [val, sta];
330
+ }
331
+ this.#lastValue = val;
332
+ this.#lastState = sta;
333
+ return [val, sta];
334
+
335
+ }
336
+
337
+
338
+
339
+ get destroyed() { return this.#destroyed; }
340
+ destroy() {
341
+ if (this.#destroyed) { return; }
342
+ this.#destroyed = true;
343
+ for (const [, field] of this) {
344
+ field.destroy();
345
+ }
346
+ }
347
+
348
+ }
349
+
350
+
351
+
352
+ class ObjectStore extends Store {
353
+ /** @type {Record<string, Store>} */
354
+ #children
355
+ *[Symbol.iterator]() {yield* Object.entries(this.#children);}
356
+ /**
357
+ *
358
+ * @param {string | number} key
359
+ * @returns {Store?}
360
+ */
361
+ child(key) { return this.#children[key] || null; }
362
+ /**
363
+ * @param {Schema.Field} schema
364
+ * @param {object} [options]
365
+ * @param {Store?} [options.parent]
366
+ * @param {string | number} [options.index]
367
+ * @param {boolean} [options.new]
368
+ * @param {(value: any, index: any) => void} [options.onUpdate]
369
+ * @param {(value: any, index: any) => void} [options.onUpdateState]
370
+ */
371
+ constructor(schema,{ parent, index, new: isNew, onUpdate, onUpdateState } = {}) {
372
+ super(schema, {
373
+ parent, index, new: isNew, onUpdate, onUpdateState,
374
+ setValue(v) {
375
+ if (typeof v !== 'object') { return {}; }
376
+ return v;
377
+ },
378
+ setState(v) {
379
+ if (typeof v !== 'object') { return {}; }
380
+ return v;
381
+ },
382
+ convert(v, state) {
383
+ return [
384
+ typeof v === 'object' ? v : {},
385
+ typeof state === 'object' ? state : {},
386
+ ]
387
+ },
388
+ });
389
+ const children = Object.create(null);
390
+ const childCommonOptions = {
391
+ parent: this,
392
+ /** @param {*} value @param {*} index */
393
+ onUpdate: (value, index) => {
394
+ this.value = {...this.value, [index]: value};
395
+ },
396
+ /** @param {*} state @param {*} index */
397
+ onUpdateState: (state, index) => {
398
+ this.state = {...this.state, [index]: state};
399
+ }
400
+ };
401
+
402
+ for (const [index, field] of Object.entries(schema.props || {})) {
403
+ let child;
404
+ if (typeof field.type === 'string') {
405
+ if (field.array) {
406
+ child = new ArrayStore(field, {...childCommonOptions, index});
407
+ } else {
408
+ child = new Store(field, {...childCommonOptions, index});
409
+ }
410
+ } else if (field.array) {
411
+ child = new ArrayStore(field, {...childCommonOptions, index});
412
+ } else {
413
+ child = new ObjectStore(field, { ...childCommonOptions, index});
414
+ }
415
+ children[index] = child;
416
+ }
417
+ this.#children = children;
418
+ }
419
+ }
420
+
421
+ /**
422
+ * @template [T=any]
423
+ * @extends {Store<(T | null)[]>}
424
+ */
425
+ class ArrayStore extends Store {
426
+ /** @type {(index: number, isNew?: boolean) => Store} */
427
+ #create = () => {throw new Error}
428
+ #children = new Signal.State(/** @type {Store[]} */([]));
429
+ get children() { return [...this.#children.get()]; }
430
+ *[Symbol.iterator]() { return yield*[...this.#children.get().entries()]; }
431
+ /**
432
+ *
433
+ * @param {string | number} key
434
+ * @returns {Store?}
435
+ */
436
+ child(key) {
437
+ const children = this.#children.get();
438
+ if (typeof key === 'number' && key < 0) {
439
+ return children[children.length + key] || null;
440
+ }
441
+ return children[Number(key)] || null;
442
+ }
443
+ /**
444
+ * @param {Schema.Field} schema
445
+ * @param {object} [options]
446
+ * @param {Store?} [options.parent]
447
+ * @param {string | number | null} [options.index]
448
+ * @param {boolean} [options.new]
449
+ * @param {(value: any, index: any) => void} [options.onUpdate]
450
+ * @param {(value: any, index: any) => void} [options.onUpdateState]
451
+ */
452
+ constructor(schema, { parent, onUpdate, onUpdateState, index, new: isNew} = {}) {
453
+ // @ts-ignore
454
+ const updateChildren = (list) => {
455
+ if (this.destroyed) { return; }
456
+ const length = Array.isArray(list) && list.length || 0;
457
+ const children = [...this.#children.get()];
458
+ const oldLength = children.length;
459
+ for (let i = children.length; i < length; i++) {
460
+ children.push(this.#create(i));
461
+ }
462
+ for (const schema of children.splice(length)) {
463
+ schema.destroy();
464
+ }
465
+ if (oldLength !== length) {
466
+ this.length = children.length;
467
+ this.#children.set(children);
468
+ }
469
+
470
+ };
471
+ super(schema, {
472
+ index, new: isNew, parent,
473
+ state: [],
474
+ setValue(v) { return Array.isArray(v) ? v : v == null ? [] : [v] },
475
+ setState(v) { return Array.isArray(v) ? v : v == null ? [] : [v] },
476
+ convert(v, state) {
477
+ const val = Array.isArray(v) ? v : v == null ? [] : [v];
478
+ updateChildren(val);
479
+ return [
480
+ val,
481
+ (Array.isArray(state) ? state : v == null ? [] : [state]),
482
+ ];
483
+ },
484
+ onUpdate:(value, index) => {
485
+ updateChildren(value);
486
+ onUpdate?.(value, index);
487
+ },
488
+ onUpdateState,
489
+ });
490
+ const childCommonOptions = {
491
+ parent: this,
492
+ /** @param {*} value @param {*} index */
493
+ onUpdate: (value, index) => {
494
+ const val = [...this.value || []];
495
+ if (val.length < index) {
496
+ val.length = index;
497
+ }
498
+ val[index] = value;
499
+ this.value = val;
500
+ },
501
+ /** @param {*} state @param {*} index */
502
+ onUpdateState: (state, index) => {
503
+ const sta = [...this.state || []];
504
+ if (sta.length < index) {
505
+ sta.length = index;
506
+ }
507
+ sta[index] = state;
508
+ this.state = sta;
509
+ },
510
+ };
511
+ if (typeof schema.type === 'string') {
512
+ this.#create = (index, isNew) => {
513
+ const child = new Store(schema, {...childCommonOptions, index, new: isNew });
514
+ child.index = index;
515
+ return child
516
+ };
517
+ } else if (!Array.isArray(schema.props)) {
518
+ this.#create = (index, isNew) => {
519
+ const child = new ObjectStore(schema, { ...childCommonOptions, index, new: isNew});
520
+ child.index = index;
521
+ return child
522
+ };
523
+ } else {
524
+ throw new Error();
525
+ }
526
+
527
+ }
528
+ /**
529
+ *
530
+ * @param {number} index
531
+ * @param {T} value
532
+ * @param {boolean} [isNew]
533
+ * @returns
534
+ */
535
+ insert(index, value, isNew) {
536
+ if (this.destroyed) { return false; }
537
+ const data = this.value;
538
+ if (!Array.isArray(data)) { return false; }
539
+ const children = [...this.#children.get()];
540
+ const insertIndex = Math.max(0, Math.min(Math.floor(index), children.length));
541
+ const item = this.#create(insertIndex, isNew);
542
+ item.new = true;
543
+ children.splice(insertIndex, 0, item);
544
+ for (let i = index + 1; i < children.length; i++) {
545
+ children[i].index = i;
546
+ }
547
+ const val = [...data];
548
+ val.splice(insertIndex, 0, value);
549
+ const state = this.state;
550
+ if (Array.isArray(state)) {
551
+ const sta = [...state];
552
+ sta.splice(insertIndex, 0, {});
553
+ this.state = sta;
554
+ }
555
+ this.value = val;
556
+ this.length = children.length;
557
+ this.#children.set(children);
558
+ return true;
559
+ }
560
+ /**
561
+ *
562
+ * @param {T} value
563
+ * @returns
564
+ */
565
+ add(value) {
566
+ return this.insert(this.#children.get().length, value);
567
+ }
568
+ /**
569
+ *
570
+ * @param {number} index
571
+ * @returns
572
+ */
573
+ remove(index) {
574
+ if (this.destroyed) { return; }
575
+ const data = this.value;
576
+ if (!Array.isArray(data)) { return; }
577
+ const children = [...this.#children.get()];
578
+ const removeIndex = Math.max(0, Math.min(Math.floor(index), children.length));
579
+ const [item] = children.splice(removeIndex, 1);
580
+ if (!item) { return; }
581
+ for (let i = index; i < children.length; i++) {
582
+ children[i].index = i;
583
+ }
584
+ item.destroy();
585
+ const val = [...data];
586
+ const [value] = val.splice(removeIndex, 1);
587
+ const state = this.state;
588
+ if (Array.isArray(state)) {
589
+ const sta = [...this.state];
590
+ sta.splice(removeIndex, 1);
591
+ this.state = sta;
592
+ }
593
+ this.value = val;
594
+ this.length = children.length;
595
+ this.#children.set(children);
596
+ return value;
597
+
598
+ }
599
+ /**
600
+ *
601
+ * @param {number} from
602
+ * @param {number} to
603
+ * @returns
604
+ */
605
+ move(from, to) {
606
+ if (this.destroyed) { return false; }
607
+ const data = this.value;
608
+ if (!Array.isArray(data)) { return false; }
609
+ const children = [...this.#children.get()];
610
+ const [item] = children.splice(from, 1);
611
+ if (!item) { return false; }
612
+ children.splice(to, 0, item);
613
+ let lft = Math.min(from, to);
614
+ let rgt = Math.max(from, to);
615
+ for (let i = lft; i <= rgt; i++) {
616
+ children[i].index = i;
617
+ }
618
+ const val = [...data];
619
+ const [value] = val.splice(from, 1);
620
+ val.splice(to, 0, value);
621
+ const state = this.state;
622
+ if (Array.isArray(state)) {
623
+ const sta = [...state];
624
+ const [value = {}] = sta.splice(from, 1);
625
+ if (to <= sta.length) {
626
+ sta.splice(to, 0, value);
627
+ } else {
628
+ sta[to] = value;
629
+ }
630
+ this.state = sta;
631
+ }
632
+ this.value = val;
633
+ this.#children.set(children);
634
+ return true;
635
+
636
+ }
637
+ /**
638
+ *
639
+ * @param {number} a
640
+ * @param {number} b
641
+ * @returns
642
+ */
643
+ exchange(a, b) {
644
+ if (this.destroyed) { return false; }
645
+ const data = this.value;
646
+ if (!Array.isArray(data)) { return false; }
647
+ const children = [...this.#children.get()];
648
+ const aItem = children[a];
649
+ const bItem = children[b];
650
+ if (!aItem || !bItem) { return false; }
651
+ children[b] = aItem;
652
+ children[a] = bItem;
653
+ aItem.index = b;
654
+ bItem.index = a;
655
+ const val = [...data];
656
+ const aValue = val[a];
657
+ const bValue = val[b];
658
+ val[b] = aValue;
659
+ val[a] = bValue;
660
+ const state = this.state;
661
+ if (Array.isArray(state)) {
662
+ const sta = [...state];
663
+ const aValue = sta[a];
664
+ const bValue = sta[b];
665
+ sta[b] = aValue;
666
+ sta[a] = bValue;
667
+ this.state = sta;
668
+ }
669
+ this.value = val;
670
+ this.#children.set(children);
671
+ return true;
672
+ }
673
+ }
674
+
675
+ const errors = {
676
+ CALC: 'no `creteCalc` option, no expression parsing support',
677
+ EVENT: 'no `creteEvent`, options, no event parsing support',
678
+ /** @param {string} endTag @param {string} startTag */
679
+ CLOSE: (endTag, startTag) => `end tag name: ${endTag} is not match the current start tagName: ${startTag}`,
680
+ /** @param {string} name */
681
+ UNCLOSE: (name) => `end tag name: ${name} maybe not complete`,
682
+ /** @param {string} char */
683
+ QUOTE: (char) => `attribute value no end \'${char}\' match`,
684
+ CLOSE_SYMBOL: `elements closed character '/' and '>' must be connected to`,
685
+ /** @param {string} endTag @param {string} [startTag] */
686
+ UNCOMPLETED: (endTag, startTag) => startTag
687
+ ? `end tag name: ${endTag} is not complete: ${startTag}`
688
+ : `end tag name: ${endTag} maybe not complete`,
689
+ /** @param {string} entity */
690
+ ENTITY: entity => `entity not found: ${entity}`,
691
+ /** @param {string} symbol */
692
+ SYMBOL: symbol => `unexpected symbol: ${symbol}`,
693
+ /** @param {string} tag */
694
+ TAG: tag => `invalid tagName: ${tag}`,
695
+ /** @param {string} attr */
696
+ ATTR: attr => `invalid attribute: ${attr}`,
697
+ EQUAL: 'attribute equal must after attrName',
698
+ ATTR_VALUE: 'attribute value must after "="',
699
+ EOF: `unexpected end of file`,
700
+ };
701
+ class ParseError extends Error {
702
+ /**
703
+ * @param {keyof typeof errors} code
704
+ * @param {...*} p
705
+ */
706
+ constructor(code, ...p) {
707
+ /** @type {*} */
708
+ const f = errors[code];
709
+ if (typeof f === 'function') {
710
+ super(f(...p));
711
+
712
+ } else {
713
+ super(f);
714
+ }
715
+ this.code = code;
716
+ }
717
+ }
718
+
719
+ /** @import * as Layout from './index.mjs' */
720
+
721
+ const attrPattern = /^(?<decorator>[:@!+*\.]|style:|样式:)?(?<name>-?[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_:\d\.]*)$/u;
722
+ const nameRegex = /^(?<name>[a-zA-Z$\p{Unified_Ideograph}_][\da-zA-Z$\p{Unified_Ideograph}_]*)?$/u;
723
+ /**
724
+ * @param {Layout.Node} node
725
+ * @param {Exclude<Layout.Options['creteCalc'], undefined>} creteCalc
726
+ * @param {Exclude<Layout.Options['creteEvent'], undefined>} creteEvent
727
+ */
728
+ function createAttributeAdder(node, creteCalc, creteEvent) {
729
+ const { attrs, directives, events, classes, styles, vars, aliases } = node;
730
+ /**
731
+ * @param {string} qName
732
+ * @param {string} value
733
+ */
734
+ function addAttribute(qName, value) {
735
+ const attr = attrPattern.exec(qName
736
+ .replace(/./g,'.')
737
+ .replace(/:/g,':')
738
+ .replace(/@/g,'@')
739
+ .replace(/+/g,'+')
740
+ .replace(/-/g,'-')
741
+ .replace(/[*×]/g,'*')
742
+ .replace(/!/g, '!'))?.groups;
743
+ if (!attr) { throw new ParseError('ATTR', qName); }
744
+ const { name } = attr;
745
+ const decorator = attr.decorator?.toLowerCase();
746
+ if (!decorator) {
747
+ attrs[name] = value;
748
+ } else if (decorator === ':') {
749
+ attrs[name] = nameRegex.test(value) ? {name: value} : creteCalc(value);
750
+ } else if (decorator === '.') {
751
+ classes[name] = !value ? true : nameRegex.test(value) ? value : creteCalc(value);
752
+ } else if (decorator === 'style:') {
753
+ styles[name] = nameRegex.test(value) ? value : creteCalc(value);
754
+ } else if (decorator === '@') {
755
+ events[name] = nameRegex.test(value) ? value : creteEvent(value);
756
+ } else if (decorator === '+') {
757
+ vars[name] = !value ? '' : nameRegex.test(value) ? value : creteCalc(value);
758
+ } else if (decorator === '*') {
759
+ aliases[name] = nameRegex.test(value) ? value : creteEvent(value);
760
+ } else if (decorator === '!') {
761
+ const key = name.toString();
762
+ switch (key) {
763
+ case 'fragment':
764
+ case 'else':
765
+ case 'enum':
766
+ directives[key] = true;
767
+ break;
768
+ case 'if':
769
+ case 'text':
770
+ case 'html':
771
+ directives[key] = nameRegex.test(value) ? value : creteCalc(value);
772
+ break;
773
+ case 'bind':
774
+ case 'value':
775
+ case 'comment':
776
+ directives[key] = value;
777
+ break;
778
+ }
779
+ }
780
+ }
781
+ return addAttribute;
782
+ }
783
+
784
+ /** @import * as Layout from './index.mjs' */
785
+
786
+ /**
787
+ * @param {string} name
788
+ * @param {string?} [is]
789
+ * @returns {Layout.Node}
790
+ *
791
+ */
792
+ function createElement(name, is) {
793
+ return {
794
+ name,
795
+ is,
796
+ children: [],
797
+ attrs: Object.create(null),
798
+ events: Object.create(null),
799
+ directives: Object.create(null),
800
+ classes: Object.create(null),
801
+ styles: Object.create(null),
802
+ vars: Object.create(null),
803
+ aliases: Object.create(null),
804
+ };
805
+ }
806
+
807
+ /** @type {Record<string, string>} */
808
+ var entityMap = {
809
+ lt: '<',
810
+ gt: '>',
811
+ amp: '&',
812
+ quot: '"',
813
+ apos: "'",
814
+
815
+ Agrave: 'À',
816
+ Aacute: 'Á',
817
+ Acirc: 'Â',
818
+ Atilde: 'Ã',
819
+ Auml: 'Ä',
820
+ Aring: 'Å',
821
+ AElig: 'Æ',
822
+ Ccedil: 'Ç',
823
+ Egrave: 'È',
824
+ Eacute: 'É',
825
+ Ecirc: 'Ê',
826
+ Euml: 'Ë',
827
+ Igrave: 'Ì',
828
+ Iacute: 'Í',
829
+ Icirc: 'Î',
830
+ Iuml: 'Ï',
831
+ ETH: 'Ð',
832
+ Ntilde: 'Ñ',
833
+ Ograve: 'Ò',
834
+ Oacute: 'Ó',
835
+ Ocirc: 'Ô',
836
+ Otilde: 'Õ',
837
+ Ouml: 'Ö',
838
+ Oslash: 'Ø',
839
+ Ugrave: 'Ù',
840
+ Uacute: 'Ú',
841
+ Ucirc: 'Û',
842
+ Uuml: 'Ü',
843
+ Yacute: 'Ý',
844
+ THORN: 'Þ',
845
+ szlig: 'ß',
846
+ agrave: 'à',
847
+ aacute: 'á',
848
+ acirc: 'â',
849
+ atilde: 'ã',
850
+ auml: 'ä',
851
+ aring: 'å',
852
+ aelig: 'æ',
853
+ ccedil: 'ç',
854
+ egrave: 'è',
855
+ eacute: 'é',
856
+ ecirc: 'ê',
857
+ euml: 'ë',
858
+ igrave: 'ì',
859
+ iacute: 'í',
860
+ icirc: 'î',
861
+ iuml: 'ï',
862
+ eth: 'ð',
863
+ ntilde: 'ñ',
864
+ ograve: 'ò',
865
+ oacute: 'ó',
866
+ ocirc: 'ô',
867
+ otilde: 'õ',
868
+ ouml: 'ö',
869
+ oslash: 'ø',
870
+ ugrave: 'ù',
871
+ uacute: 'ú',
872
+ ucirc: 'û',
873
+ uuml: 'ü',
874
+ yacute: 'ý',
875
+ thorn: 'þ',
876
+ yuml: 'ÿ',
877
+ nbsp: '\u00a0',
878
+ iexcl: '¡',
879
+ cent: '¢',
880
+ pound: '£',
881
+ curren: '¤',
882
+ yen: '¥',
883
+ brvbar: '¦',
884
+ sect: '§',
885
+ uml: '¨',
886
+ copy: '©',
887
+ ordf: 'ª',
888
+ laquo: '«',
889
+ not: '¬',
890
+ shy: '­­',
891
+ reg: '®',
892
+ macr: '¯',
893
+ deg: '°',
894
+ plusmn: '±',
895
+ sup2: '²',
896
+ sup3: '³',
897
+ acute: '´',
898
+ micro: 'µ',
899
+ para: '¶',
900
+ middot: '·',
901
+ cedil: '¸',
902
+ sup1: '¹',
903
+ ordm: 'º',
904
+ raquo: '»',
905
+ frac14: '¼',
906
+ frac12: '½',
907
+ frac34: '¾',
908
+ iquest: '¿',
909
+ times: '×',
910
+ divide: '÷',
911
+ forall: '∀',
912
+ part: '∂',
913
+ exist: '∃',
914
+ empty: '∅',
915
+ nabla: '∇',
916
+ isin: '∈',
917
+ notin: '∉',
918
+ ni: '∋',
919
+ prod: '∏',
920
+ sum: '∑',
921
+ minus: '−',
922
+ lowast: '∗',
923
+ radic: '√',
924
+ prop: '∝',
925
+ infin: '∞',
926
+ ang: '∠',
927
+ and: '∧',
928
+ or: '∨',
929
+ cap: '∩',
930
+ cup: '∪',
931
+ 'int': '∫',
932
+ there4: '∴',
933
+ sim: '∼',
934
+ cong: '≅',
935
+ asymp: '≈',
936
+ ne: '≠',
937
+ equiv: '≡',
938
+ le: '≤',
939
+ ge: '≥',
940
+ sub: '⊂',
941
+ sup: '⊃',
942
+ nsub: '⊄',
943
+ sube: '⊆',
944
+ supe: '⊇',
945
+ oplus: '⊕',
946
+ otimes: '⊗',
947
+ perp: '⊥',
948
+ sdot: '⋅',
949
+ Alpha: 'Α',
950
+ Beta: 'Β',
951
+ Gamma: 'Γ',
952
+ Delta: 'Δ',
953
+ Epsilon: 'Ε',
954
+ Zeta: 'Ζ',
955
+ Eta: 'Η',
956
+ Theta: 'Θ',
957
+ Iota: 'Ι',
958
+ Kappa: 'Κ',
959
+ Lambda: 'Λ',
960
+ Mu: 'Μ',
961
+ Nu: 'Ν',
962
+ Xi: 'Ξ',
963
+ Omicron: 'Ο',
964
+ Pi: 'Π',
965
+ Rho: 'Ρ',
966
+ Sigma: 'Σ',
967
+ Tau: 'Τ',
968
+ Upsilon: 'Υ',
969
+ Phi: 'Φ',
970
+ Chi: 'Χ',
971
+ Psi: 'Ψ',
972
+ Omega: 'Ω',
973
+ alpha: 'α',
974
+ beta: 'β',
975
+ gamma: 'γ',
976
+ delta: 'δ',
977
+ epsilon: 'ε',
978
+ zeta: 'ζ',
979
+ eta: 'η',
980
+ theta: 'θ',
981
+ iota: 'ι',
982
+ kappa: 'κ',
983
+ lambda: 'λ',
984
+ mu: 'μ',
985
+ nu: 'ν',
986
+ xi: 'ξ',
987
+ omicron: 'ο',
988
+ pi: 'π',
989
+ rho: 'ρ',
990
+ sigmaf: 'ς',
991
+ sigma: 'σ',
992
+ tau: 'τ',
993
+ upsilon: 'υ',
994
+ phi: 'φ',
995
+ chi: 'χ',
996
+ psi: 'ψ',
997
+ omega: 'ω',
998
+ thetasym: 'ϑ',
999
+ upsih: 'ϒ',
1000
+ piv: 'ϖ',
1001
+ OElig: 'Œ',
1002
+ oelig: 'œ',
1003
+ Scaron: 'Š',
1004
+ scaron: 'š',
1005
+ Yuml: 'Ÿ',
1006
+ fnof: 'ƒ',
1007
+ circ: 'ˆ',
1008
+ tilde: '˜',
1009
+ ensp: ' ',
1010
+ emsp: ' ',
1011
+ thinsp: ' ',
1012
+ zwnj: '‌',
1013
+ zwj: '‍',
1014
+ lrm: '‎',
1015
+ rlm: '‏',
1016
+ ndash: '–',
1017
+ mdash: '—',
1018
+ lsquo: '‘',
1019
+ rsquo: '’',
1020
+ sbquo: '‚',
1021
+ ldquo: '“',
1022
+ rdquo: '”',
1023
+ bdquo: '„',
1024
+ dagger: '†',
1025
+ Dagger: '‡',
1026
+ bull: '•',
1027
+ hellip: '…',
1028
+ permil: '‰',
1029
+ prime: '′',
1030
+ Prime: '″',
1031
+ lsaquo: '‹',
1032
+ rsaquo: '›',
1033
+ oline: '‾',
1034
+ euro: '€',
1035
+ trade: '™',
1036
+ larr: '←',
1037
+ uarr: '↑',
1038
+ rarr: '→',
1039
+ darr: '↓',
1040
+ harr: '↔',
1041
+ crarr: '↵',
1042
+ lceil: '⌈',
1043
+ rceil: '⌉',
1044
+ lfloor: '⌊',
1045
+ rfloor: '⌋',
1046
+ loz: '◊',
1047
+ spades: '♠',
1048
+ clubs: '♣',
1049
+ hearts: '♥',
1050
+ diams: '♦'
1051
+ };
1052
+
1053
+ /** @import * as Layout from './index.mjs' */
1054
+
1055
+ const tagNamePattern = /^(?<name>[\w\p{Unified_Ideograph}_][-\.\|:|d\w\p{Unified_Ideograph}_:]*)(?:|(?<is>[\w\p{Unified_Ideograph}_][-\.\|:|d\w\p{Unified_Ideograph}_]*))?$/u;
1056
+
1057
+ /**
1058
+ *
1059
+ * @param {string} c
1060
+ * @returns
1061
+ */
1062
+ function isSpace(c) {
1063
+ return c === '0x80' || c <= ' ';
1064
+ }
1065
+ /**
1066
+ *
1067
+ * @param {string} c
1068
+ * @returns
1069
+ */
1070
+ function isIdCode(c) {
1071
+ return c !== '=' && c !== '/' && c !== '>' && c && c !== '\'' && c !== '"' && !isSpace(c);
1072
+ }
1073
+
1074
+ /**
1075
+ *
1076
+ * @param {string} source
1077
+ * @param {number} elStartEnd
1078
+ * @param {string} name
1079
+ * @param {*} closeMap
1080
+ * @returns
1081
+ */
1082
+ function fixSelfClosed(source, elStartEnd, name, closeMap) {
1083
+ let pos = closeMap[name];
1084
+ if (pos == null) {
1085
+ pos = source.lastIndexOf('</' + name + '>');
1086
+ if (pos < elStartEnd) {
1087
+ pos = source.lastIndexOf('</' + name);
1088
+ }
1089
+ closeMap[name] = pos;
1090
+ }
1091
+ return pos < elStartEnd;
1092
+ }
1093
+
1094
+ /**
1095
+ * @param {ConstructorParameters<typeof ParseError>} p
1096
+ */
1097
+ function error(...p) {
1098
+ console.error(new ParseError(...p));
1099
+ }
1100
+
1101
+ /**
1102
+ *
1103
+ * @param {string} a
1104
+ * @returns {string}
1105
+ */
1106
+ function entityReplacer(a) {
1107
+ const k = a.slice(1, -1);
1108
+ if (k.charAt(0) === '#') {
1109
+ return String.fromCodePoint(parseInt(k.substring(1).replace('x', '0x')));
1110
+ }
1111
+ if (k in entityMap) { return entityMap[k]; }
1112
+ error('ENTITY', a);
1113
+ return a;
1114
+ }
1115
+ /**
1116
+ *
1117
+ * @param {string} source
1118
+ * @param {Layout.Options} [options]
1119
+ * @returns {(Layout.Node | string)[]}
1120
+ */
1121
+ function parse(source, {
1122
+ creteCalc = () => { throw new ParseError('CALC'); },
1123
+ creteEvent = () => { throw new ParseError('EVENT'); },
1124
+ simpleTag = new Set,
1125
+ } = {}) {
1126
+ /** @type {(Layout.Node | string)[]} */
1127
+ const children = [];
1128
+
1129
+ const doc = { children };
1130
+ /** @type {(Layout.Node | null)[]} */
1131
+ const stack = [];
1132
+ /** @type {Layout.Node?} */
1133
+ let currentNode = null;
1134
+ /** @type {typeof doc | Layout.Node} */
1135
+ let current = doc;
1136
+ function endElement() {
1137
+ currentNode = stack.pop() || null;
1138
+ current = currentNode || doc;
1139
+ }
1140
+ /**
1141
+ *
1142
+ * @param {string} chars
1143
+ * @returns
1144
+ */
1145
+ function characters(chars) {
1146
+ chars = chars.replace(/^\n|(?<=\n)\t+|\n\t*$/g, '');
1147
+ if (!chars) { return; }
1148
+ current.children.push(chars);
1149
+ }
1150
+
1151
+ let closeMap = {};
1152
+ let index = 0;
1153
+ /**
1154
+ *
1155
+ * @param {number} end
1156
+ */
1157
+ function appendText(end) {
1158
+ if (end <= index) { return; }
1159
+ const xt = source.substring(index, end).replace(/&#?\w+;/g, entityReplacer);
1160
+ characters(xt);
1161
+ index = end;
1162
+ }
1163
+ for (; ;) {
1164
+ const tagStart = source.indexOf('<', index);
1165
+ if (tagStart < 0) {
1166
+ const text = source.substring(index);
1167
+ if (!text.match(/^\s*$/)) {
1168
+ characters(text);
1169
+ }
1170
+ break;
1171
+ }
1172
+ if (tagStart > index) {
1173
+ appendText(tagStart);
1174
+ }
1175
+ if (source.charAt(tagStart + 1) === '/') {
1176
+ index = source.indexOf('>', tagStart + 3);
1177
+ let name = source.substring(tagStart + 2, index);
1178
+ if (index < 0) {
1179
+ name = source.substring(tagStart + 2).replace(/[\s<].*/, '');
1180
+ error('UNCOMPLETED', name, currentNode?.name);
1181
+ index = tagStart + 1 + name.length;
1182
+ } else if (name.match(/\s</)) {
1183
+ name = name.replace(/[\s<].*/, '');
1184
+ error('UNCOMPLETED', name);
1185
+ index = tagStart + 1 + name.length;
1186
+ }
1187
+ if (currentNode) {
1188
+ const currentName = currentNode.name;
1189
+ if (currentName === name) {
1190
+ endElement();
1191
+ } else if (currentName.toLowerCase() == name.toLowerCase()) {
1192
+ endElement();
1193
+ } else {
1194
+ throw new ParseError('CLOSE', name, currentNode.name);
1195
+ }
1196
+ }
1197
+ index++;
1198
+ continue;
1199
+ }
1200
+ index = tagStart + 1;
1201
+ /**
1202
+ *
1203
+ * @param {string} c
1204
+ * @returns
1205
+ */
1206
+ function getQuote(c) {
1207
+ let start = index + 1;
1208
+ index = source.indexOf(c, start);
1209
+ if (index < 0) { throw new ParseError('QUOTE', c); }
1210
+ const value = source.slice(start, index).replace(/&#?\w+;/g, entityReplacer);
1211
+ index++;
1212
+ return value;
1213
+ }
1214
+ function skipSpace() {
1215
+ let c = source.charAt(index);
1216
+ for (; c <= ' ' || c === '\u0080'; c = source.charAt(++index)) { }
1217
+ return c;
1218
+
1219
+ }
1220
+ function getId() {
1221
+ let start = index;
1222
+ let c = source.charAt(index);
1223
+ while (isIdCode(c)) {
1224
+ index++;
1225
+ c = source.charAt(index);
1226
+ }
1227
+ return source.slice(start, index);
1228
+ }
1229
+ let c = source.charAt(index);
1230
+ switch (c) {
1231
+ case '=': throw new ParseError('EQUAL');
1232
+ case '"': case '\'': throw new ParseError('ATTR_VALUE');
1233
+ case '>': case '/': throw new ParseError('SYMBOL', c);
1234
+ case '': throw new ParseError('EOF');
1235
+ }
1236
+ const name = getId();
1237
+ const tagRes = tagNamePattern.exec(name)?.groups;
1238
+ if (!tagRes) { throw new ParseError('TAG', name); }
1239
+ stack.push(currentNode);
1240
+ currentNode = createElement(tagRes.name, tagRes.is);
1241
+ current.children.push(currentNode);
1242
+ current = currentNode;
1243
+ const addAttribute = createAttributeAdder(currentNode, creteCalc, creteEvent);
1244
+
1245
+ let run = true;
1246
+ let closed = false;
1247
+ parseAttr: for (; run;) {
1248
+ let c = skipSpace();
1249
+ switch (c) {
1250
+ case '': error('EOF'); index++; break parseAttr;
1251
+ case '>': index++; break parseAttr;
1252
+ case '/': closed = true; break parseAttr;
1253
+ case '"': case '\'': throw new ParseError('ATTR_VALUE');
1254
+ case '=': throw new ParseError('SYMBOL', c);
1255
+ }
1256
+ const id = getId();
1257
+ if (!id) { error('EOF'); index++; break parseAttr; }
1258
+ c = skipSpace();
1259
+ switch (c) {
1260
+ case '': error('EOF'); index++; break parseAttr;
1261
+ case '>': addAttribute(id, ''); index++; break parseAttr;
1262
+ case '/': addAttribute(id, ''); closed = true; break parseAttr;
1263
+ case '=': index++; break;
1264
+ case '\'': case '"': addAttribute(id, getQuote(c)); continue;
1265
+ default: addAttribute(id, ''); continue;
1266
+ }
1267
+ c = skipSpace();
1268
+ switch (c) {
1269
+ case '': addAttribute(id, ''); error('EOF'); index++; break parseAttr;
1270
+ case '>': addAttribute(id, ''); index++; break parseAttr;
1271
+ case '/': addAttribute(id, ''); closed = true; break parseAttr;
1272
+ case '\'': case '"': addAttribute(id, getQuote(c)); continue;
1273
+ }
1274
+ addAttribute(id, getId());
1275
+ }
1276
+ if (closed) {
1277
+ while (true) {
1278
+ index++;
1279
+ const c = source.charAt(index);
1280
+ if (c === '/') { continue; }
1281
+ if (c <= ' ' || c === '\u0080') { continue; }
1282
+ break;
1283
+ }
1284
+ const c = source.charAt(index);
1285
+ switch (c) {
1286
+ case '=': throw new ParseError('EQUAL');
1287
+ case '"': case '\'': throw new ParseError('ATTR_VALUE'); // No known test case
1288
+ case '': error('EOF'); break;
1289
+ case '>': index++; break;
1290
+ default: throw new ParseError('CLOSE_SYMBOL');
1291
+ }
1292
+ endElement();
1293
+ } else if (simpleTag.has(name) || fixSelfClosed(source, index, name, closeMap)) {
1294
+ endElement();
1295
+ }
1296
+ }
1297
+ return children;
1298
+ }
1299
+
1300
+ /** @import * as Layout from './index.mjs' */
1301
+
1302
+ /**
1303
+ *
1304
+ * @param {*} c
1305
+ * @returns
1306
+ */
1307
+ function _xmlEncoder(c) {
1308
+ return c == '<' && '&lt;' ||
1309
+ c == '>' && '&gt;' ||
1310
+ c == '&' && '&amp;' ||
1311
+ c == '"' && '&quot;' ||
1312
+ '&#' + c.charCodeAt() + ';';
1313
+ }
1314
+
1315
+ /**
1316
+ *
1317
+ * @param {Layout.Node} node
1318
+ * @param {number} [level]
1319
+ * @returns {Iterable<string>}
1320
+ */
1321
+ function* nodeToString(node, level = 0) {
1322
+ const { attrs, events, directives, children, is, name, classes, styles, aliases, vars } = node;
1323
+ const pad = level > 0 ? ''.padEnd(level, '\t') : '';
1324
+
1325
+ yield pad;
1326
+ yield* ['<', name || '-'];
1327
+ if (is) { yield* ['|', is]; }
1328
+
1329
+ for (const [name, value] of Object.entries(directives)) {
1330
+ if (value === false || value == null) { continue; }
1331
+ const val = typeof value === 'function' ? String(value) : value;
1332
+ yield* [' !', name];
1333
+ if (val && typeof val === 'string') {
1334
+ yield* ['="', val.replace(/[<&"]/g, _xmlEncoder), '"'];
1335
+ }
1336
+ }
1337
+ for (const [name, value] of Object.entries(aliases)) {
1338
+ if (value == null) { continue; }
1339
+ const val = typeof value === 'function' ? String(value) : value;
1340
+ yield* [' *', name];
1341
+ if (val && typeof val === 'string') {
1342
+ yield* ['="', val.replace(/[<&"]/g, _xmlEncoder), '"'];
1343
+ }
1344
+ }
1345
+ for (const [name, value] of Object.entries(vars)) {
1346
+ if (value == null) { continue; }
1347
+ const val = typeof value === 'function' ? String(value) : value;
1348
+ yield* [' +', name];
1349
+ if (val && typeof val === 'string') {
1350
+ yield* ['="', val.replace(/[<&"]/g, _xmlEncoder), '"'];
1351
+ }
1352
+ }
1353
+ for (const [name, value] of Object.entries(attrs)) {
1354
+ if (value == null) { continue; }
1355
+ if (typeof value === 'string') {
1356
+ yield* [' ', name];
1357
+ if (value) {
1358
+ yield* ['="', value.replace(/[<&"]/g, _xmlEncoder), '"'];
1359
+ }
1360
+ continue;
1361
+ }
1362
+ const val = typeof value === 'function' ? String(value) : typeof value === 'object' ? value.name : value;
1363
+ if (val && typeof val === 'string') {
1364
+ yield* [' :', name, '="', val.replace(/[<&"]/g, _xmlEncoder) || '', '"'];
1365
+ }
1366
+ }
1367
+ for (const [name, value] of Object.entries(events)) {
1368
+ if (value == null) { continue; }
1369
+ const val = typeof value === 'function' ? String(value) : value;
1370
+ if (val && typeof val === 'string') {
1371
+ yield* [' @', name, '="', val.replace(/[<&"]/g, _xmlEncoder), '"'];
1372
+ }
1373
+ }
1374
+ for (const [name, value] of Object.entries(classes)) {
1375
+ if (value == null || value == false) { continue; }
1376
+ const val = typeof value === 'function' ? String(value) : value;
1377
+ yield* [' .', name];
1378
+ if (val && typeof val === 'string') {
1379
+ yield* [' .', name, '="', val.replace(/[<&"]/g, _xmlEncoder), '"'];
1380
+ }
1381
+ }
1382
+ for (const [name, value] of Object.entries(styles)) {
1383
+ if (value == null) { continue; }
1384
+ const val = typeof value === 'function' ? String(value) : value;
1385
+ if (val && typeof val === 'string') {
1386
+ yield* [' style:', name, '="', val.replace(/[<&"]/g, _xmlEncoder), '"'];
1387
+ }
1388
+ }
1389
+ if (!children.length) {
1390
+ yield '/>';
1391
+ if (level >= 0) { yield '\n'; }
1392
+ return;
1393
+ }
1394
+ if (children.length === 1) {
1395
+ const [child] = children;
1396
+ if (typeof child === 'string' && child.length < 80 && !child.includes('\n')) {
1397
+ yield '>';
1398
+ yield* [child.replace(/[<&\t]/g, _xmlEncoder).replace(/]]>/g, ']]&gt;')];
1399
+ yield* ['</', name, '>'];
1400
+ if (level >= 0) { yield '\n'; }
1401
+ return;
1402
+ }
1403
+ }
1404
+ yield '>';
1405
+ if (level >= 0) { yield '\n'; }
1406
+ yield* listToString(children, level >= 0 ? level + 1 : -1);
1407
+ yield* [pad, '</', name, '>'];
1408
+ if (level >= 0) { yield '\n'; }
1409
+
1410
+ }
1411
+ /**
1412
+ *
1413
+ * @param {(Layout.Node |string)[]} nodes
1414
+ * @param {number} [level]
1415
+ * @returns {Iterable<string>}
1416
+ */
1417
+ function* listToString(nodes, level = 0) {
1418
+ if (!nodes.length) { return ''; }
1419
+
1420
+ const pad = level > 0 ? ''.padEnd(level, '\t') : '';
1421
+ for (const child of nodes) {
1422
+ if (typeof child === 'string') {
1423
+ let text = child.replace(/[<&\t]/g, _xmlEncoder).replace(/]]>/g, ']]&gt;');
1424
+ if (pad) {
1425
+ text = text.replace(/(?<=^|\n)/g, pad);
1426
+ }
1427
+ yield text;
1428
+ if (level >= 0) { yield '\n'; }
1429
+ } else {
1430
+ yield* nodeToString(child, level);
1431
+ }
1432
+ }
1433
+ }
1434
+ /**
1435
+ *
1436
+ * @param {Layout.Node | (Layout.Node |string)[]} value
1437
+ * @param {boolean} [formable]
1438
+ * @returns {string}
1439
+ */
1440
+ function toString(value, formable) {
1441
+ const level = formable ? 0 : -1;
1442
+ if (Array.isArray(value)) { return [...listToString(value, level)].join(''); }
1443
+ return [nodeToString(value, level)].join();
1444
+
1445
+ }
1446
+
1447
+ /**
1448
+ * @typedef {object} Directives
1449
+ *
1450
+ * @property {boolean} [fragment]
1451
+ *
1452
+ * @property {string | Function} [if]
1453
+ * @property {boolean} [else]
1454
+ *
1455
+ * @property {string} [value] 值关联(关联为列表)
1456
+ * @property {boolean} [enum] 列表属性枚举
1457
+ *
1458
+ * @property {string} [bind]
1459
+ * @property {string | Function} [text]
1460
+ * @property {string | Function} [html]
1461
+ *
1462
+ * @property {string | Function} [comment] 注释
1463
+ */
1464
+
1465
+
1466
+
1467
+ /**
1468
+ * @typedef {object} Options
1469
+ * @property {(t: string) => (vars: Record<string, any>) => any} [options.creteCalc]
1470
+ * @property {(t: string) => ($event: any, vars: Record<string, any>) => any} [options.creteEvent]
1471
+ * @property {Set<string>} [options.simpleTag]
1472
+ */
1473
+ /**
1474
+ * @typedef {object} Node
1475
+ * @property {string} name
1476
+ * @property {string?} [is]
1477
+ * @property {string} [id]
1478
+ * @property {Record<string, string | {name: string} | ((global: any) => void)>} attrs
1479
+ * @property {Record<string, string | boolean | ((global: any) => void)>} classes
1480
+ * @property {Record<string, string | ((global: any) => void)>} styles
1481
+ * @property {Record<string, string | (($event: any, global: any) => void)>} events
1482
+ * @property {Record<string, string | ((global: any) => void)>} vars
1483
+ * @property {Record<string, string | Function>} aliases
1484
+ * @property {Directives} directives
1485
+ * @property {(Node | string)[]} children
1486
+ */
1487
+
1488
+ var index$1 = /*#__PURE__*/Object.freeze({
1489
+ __proto__: null,
1490
+ parse: parse,
1491
+ stringify: toString
1492
+ });
1493
+
1494
+ let needsEnqueue = true;
1495
+
1496
+ const w = new Signal.subtle.Watcher(() => {
1497
+ if (needsEnqueue) {
1498
+ needsEnqueue = false;
1499
+ queueMicrotask(processPending);
1500
+ }
1501
+ });
1502
+
1503
+ function processPending() {
1504
+ needsEnqueue = true;
1505
+
1506
+ for (const s of w.getPending()) {
1507
+ s.get();
1508
+ }
1509
+
1510
+ w.watch();
1511
+ }
1512
+
1513
+ /**
1514
+ * 创建可赋值计算值
1515
+ * @template T
1516
+ * @param {() => T} getter 取值方法
1517
+ * @param {(value: T) => void} callback 取值方法
1518
+ * @returns {() => void}
1519
+ */
1520
+ function watch(getter, callback) {
1521
+
1522
+ let run = false;
1523
+ /** @type {any} */
1524
+ let value;
1525
+ const computed = new Signal.Computed(() => {
1526
+ const val = getter();
1527
+ if (run && Object.is(val, value)) { return; }
1528
+ value = val;
1529
+ run = true;
1530
+ callback(val);
1531
+ });
1532
+
1533
+ w.watch(computed);
1534
+ computed.get();
1535
+
1536
+ return () => { w.unwatch(computed); };
1537
+ }
1538
+
1539
+ /** @typedef {{get(): any; set?(v: any): void; exec?: null; store?: Store; calc?: null; }} ValueDefine */
1540
+ /** @typedef {{get?: null; exec(...p: any[]): any; calc?: null}} ExecDefine */
1541
+ /** @typedef {{get?: null; calc(...p: any[]): any; exec?: null;}} CalcDefine */
1542
+ /**
1543
+ *
1544
+ * @param {Store} val
1545
+ * @param {string | number} [key]
1546
+ * @returns {Iterable<[string, ValueDefine | ExecDefine | CalcDefine]>}
1547
+ */
1548
+ function *toItem(val, key = '', sign = '$') {
1549
+ yield [`${key}`, {get: () => val.value, set: v => val.value = v, store: val}];
1550
+ yield [`${key}${sign}value`, {get: () => val.value, set: v => val.value = v}];
1551
+ yield [`${key}${sign}state`, {get: () => val.state, set: v => val.state = v}];
1552
+ yield [`${key}${sign}null`, {get: () => val.null}];
1553
+ yield [`${key}${sign}index`, {get: () => val.index}];
1554
+ yield [`${key}${sign}no`, {get: () => val.no}];
1555
+ yield [`${key}${sign}length`, {get: () => val.length}];
1556
+ yield [`${key}${sign}creatable`, {get: () => val.creatable}];
1557
+ yield [`${key}${sign}immutable`, {get: () => val.immutable}];
1558
+ yield [`${key}${sign}new`, {get: () => val.new}];
1559
+ yield [`${key}${sign}editable`, {get: () => val.editable}];
1560
+ yield [`${key}${sign}hidden`, {get: () => val.hidden}];
1561
+ yield [`${key}${sign}clearable`, {get: () => val.clearable}];
1562
+ yield [`${key}${sign}required`, {get: () => val.required}];
1563
+ yield [`${key}${sign}disabled`, {get: () => val.disabled}];
1564
+ yield [`${key}${sign}readonly`, {get: () => val.readonly}];
1565
+ if (!(val instanceof ArrayStore)) { return; }
1566
+ yield [`${key}${sign}insert`, {exec: (index, value) => val.insert(index, value)}];
1567
+ yield [`${key}${sign}add`, {exec: (v) => val.add(v)}];
1568
+ yield [`${key}${sign}remove`, {exec: (index) => val.remove(index)}];
1569
+ yield [`${key}${sign}move`, {exec: (from, to) => val.move(from, to)}];
1570
+ yield [`${key}${sign}exchange`, {exec: (a, b) => val.exchange(a, b)}];
1571
+ }
1572
+ /**
1573
+ *
1574
+ * @param {Store?} parent
1575
+ * @param {Store} val
1576
+ * @param {string | number} [key]
1577
+ * @returns {Iterable<[string, ValueDefine | ExecDefine | CalcDefine]>}
1578
+ */
1579
+ function *toParentItem(parent, val, key = '', sign = '$') {
1580
+ if (!(parent instanceof ArrayStore)) {
1581
+ yield [`${key}${sign}upMovable`, {get: () => false}];
1582
+ yield [`${key}${sign}downMovable`, {get: () => false}];
1583
+ return
1584
+ }
1585
+ yield [`${key}${sign}upMovable`, {get: () => {
1586
+ const s = val.index;
1587
+ if (typeof s !== 'number') { return false; }
1588
+ if (s <= 0) { return false; }
1589
+ return true;
1590
+ }}];
1591
+ yield [`${key}${sign}downMovable`, {get: () => {
1592
+ const s = val.index;
1593
+ if (typeof s !== 'number') { return false; }
1594
+ if (s >= parent.length - 1) { return false; }
1595
+ return true;
1596
+ }}];
1597
+ yield [`${key}${sign}remove`, {exec: () => parent.remove(Number(val.index))}];
1598
+ yield [`${key}${sign}upMove`, {exec: () => {
1599
+ const s = val.index;
1600
+ if (typeof s !== 'number') { return; }
1601
+ if (s <= 0) { return; }
1602
+ parent.move(s, s - 1);
1603
+ }}];
1604
+ yield [`${key}${sign}downMove`, {exec: () => {
1605
+ const s = val.index;
1606
+ if (typeof s !== 'number') { return; }
1607
+ if (s >= parent.length - 1) { return; }
1608
+ parent.move(s, s + 1);
1609
+ }}];
1610
+ }
1611
+ class Environment {
1612
+ /**
1613
+ * @param {string | Function} value
1614
+ */
1615
+ exec(value) {
1616
+ if (typeof value === 'string') {
1617
+ const item = this.#items[value];
1618
+ if (typeof item?.get !== 'function') { return }
1619
+ return item.get();
1620
+ }
1621
+ if (typeof value === 'function') {
1622
+ return value(this.getters);
1623
+ }
1624
+ }
1625
+ /**
1626
+ * @param {string | Function} value
1627
+ * @param {(value: any) => void} cb
1628
+ */
1629
+ watch(value, cb) { return watch(() => this.exec(value), cb); }
1630
+
1631
+ /**
1632
+ * @param {string} name
1633
+ * @param {string} type
1634
+ * @param {(value: any) => void} cb
1635
+ */
1636
+ bind(name, type, cb) {
1637
+ const item = this.#items[name];
1638
+ if (!item?.get) { return; }
1639
+ const { store } = item;
1640
+ if (!store) { return; }
1641
+ switch(type) {
1642
+ case 'value': return watch(() => store.value, cb);
1643
+ case 'state': return watch(() => store.state, cb);
1644
+ case 'required': return watch(() => store.required, cb);
1645
+ case 'clearable': return watch(() => store.clearable, cb);
1646
+ case 'hidden': return watch(() => store.hidden, cb);
1647
+ case 'disabled': return watch(() => store.disabled, cb);
1648
+ case 'readonly': return watch(() => store.readonly || !store.editable, cb);
1649
+ }
1650
+ }
1651
+ /**
1652
+ * @param {string} name
1653
+ * @returns {Record<string, ((cb: (value: any) => void) => () => void) | void> | void}
1654
+ */
1655
+ bindAll(name) {
1656
+ const item = this.#items[name];
1657
+ if (!item?.get) { return; }
1658
+ const { store } = item;
1659
+ if (!store) {
1660
+ const get = item.get;
1661
+ if (typeof get !== 'function') { return; }
1662
+ return { '$value': cb => watch(get, cb) }
1663
+ }
1664
+ return {
1665
+ '$value': cb => watch(() => store.value, cb),
1666
+ '$state': cb => watch(() => store.state, cb),
1667
+ '$required': cb => watch(() => store.required, cb),
1668
+ '$clearable': cb => watch(() => store.clearable, cb),
1669
+ '$hidden': cb => watch(() => store.hidden, cb),
1670
+ '$disabled': cb => watch(() => store.disabled, cb),
1671
+ '$readonly': cb => watch(() => store.readonly || !store.editable, cb),
1672
+ }
1673
+ }
1674
+ /**
1675
+ * @param {string} name
1676
+ * @param {string} type
1677
+ * @returns {((value: any) => void) | void}
1678
+ */
1679
+ bindSet(name, type) {
1680
+ const item = this.#items[name];
1681
+ if (!item?.get) { return; }
1682
+ const { store } = item;
1683
+ if (!store) { return; }
1684
+ switch(type) {
1685
+ case 'value': return v => {store.value = v; };
1686
+ case 'state': return v => {store.state = v; };
1687
+ }
1688
+ }
1689
+ /**
1690
+ * @param {string} name
1691
+ * @returns {Record<string, ((value: any) => void) | void> | void}
1692
+ */
1693
+ bindStateAllSet(name) {
1694
+ const item = this.#items[name];
1695
+ if (!item?.get) { return; }
1696
+ const { store } = item;
1697
+ if (!store) {
1698
+ const set = item.set;
1699
+ if (typeof set !== 'function') { return; }
1700
+ return { '$value': set }
1701
+ }
1702
+ return {
1703
+ '$value': v => {store.value = v; },
1704
+ '$state': v => {store.state = v; },
1705
+ }
1706
+ }
1707
+
1708
+ /**
1709
+ * @param {string | (($event: any, global: any) => any)} event
1710
+ * @returns {(($event: any, global: any) => any)?}
1711
+ */
1712
+ getEvent(event) {
1713
+ if (typeof event === 'function') { return event }
1714
+ const item = this.#items[event];
1715
+ if (!item) { return null }
1716
+ const {exec, calc} = item;
1717
+ if (typeof exec === 'function') { return exec }
1718
+ if (typeof calc === 'function') { return calc }
1719
+ return null
1720
+
1721
+ }
1722
+ /**
1723
+ *
1724
+ * @param {Environment | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }>?} [global]
1725
+ */
1726
+ constructor(global) {
1727
+ if (global instanceof Environment) {
1728
+ this.#global = global.#global;
1729
+ const schemaItems = this.#schemaItems;
1730
+ for (const [k, v] of Object.entries(global.#schemaItems)) {
1731
+ schemaItems[k] = v;
1732
+ }
1733
+ const explicit = this.#explicit;
1734
+ for (const [k, v] of Object.entries(global.#explicit)) {
1735
+ explicit[k] = v;
1736
+ }
1737
+ return;
1738
+ }
1739
+ const items = Object.create(null);
1740
+ this.#global = items;
1741
+ if (!global) { return }
1742
+ if (typeof global !== 'object') { return; }
1743
+ for (const [key, value] of Object.entries(global)) {
1744
+ if (!key || key.includes('$')) { continue; }
1745
+ if (!value || typeof value !== 'object') { return; }
1746
+ if (value instanceof Store) {
1747
+ for (const [k, v] of toItem(value, key)) {
1748
+ items[k] = v;
1749
+ }
1750
+ continue;
1751
+ }
1752
+ const {get,set,exec,calc} = value;
1753
+ if (typeof get === 'function') {
1754
+ items[key] = typeof set === 'function' ? {get,set} : {get};
1755
+ continue;
1756
+ }
1757
+ if (typeof calc === 'function') {
1758
+ items[key] = {calc};
1759
+ continue;
1760
+ }
1761
+ if (typeof exec === 'function') {
1762
+ items[key] = {exec};
1763
+ continue;
1764
+ }
1765
+ }
1766
+ }
1767
+ /** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>} */
1768
+ #global
1769
+ /** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>} */
1770
+ #schemaItems = Object.create(null);
1771
+ /** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>} */
1772
+ #explicit = Object.create(null);
1773
+ /** @type {Store?} */
1774
+ #store = null
1775
+ /** @type {Store?} */
1776
+ #parent = null
1777
+ /** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>?} */
1778
+ #allItems = null
1779
+ get #items() {
1780
+ const ai = this.#allItems;
1781
+ if (ai) { return ai; }
1782
+ /** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>} */
1783
+ const ais = Object.create(null, Object.getOwnPropertyDescriptors({
1784
+ ...this.#schemaItems,
1785
+ ...this.#global,
1786
+ ...this.#explicit,
1787
+ }));
1788
+ const store = this.#store;
1789
+ const parent = this.#parent;
1790
+ if (store) {
1791
+ for (const [key, item] of toItem(store)) {
1792
+ ais[key] = item;
1793
+ }
1794
+ for (const [key, item] of toParentItem(parent, store)) {
1795
+ ais[key] = item;
1796
+ }
1797
+ }
1798
+ this.#allItems = ais;
1799
+ return ais;
1800
+ }
1801
+ /**
1802
+ *
1803
+ * @param {Store} store
1804
+ * @param {Store} [parent]
1805
+ */
1806
+ setValue(store, parent) {
1807
+ const cloned = new Environment(this);
1808
+ cloned.#store = store;
1809
+ if (parent) { cloned.#parent = parent; }
1810
+ if (store instanceof ArrayStore) { return cloned; }
1811
+ const items = cloned.#schemaItems;
1812
+ for (const [name, val] of store) {
1813
+ for (const [b, x] of toItem(val, name)) {
1814
+ items[b] = x;
1815
+ }
1816
+ for (const [b, x] of toItem(val, name, '$$')) {
1817
+ items[b] = x;
1818
+ }
1819
+ }
1820
+ return cloned;
1821
+ }
1822
+ /**
1823
+ *
1824
+ * @param {Record<string, string | Function>} aliases
1825
+ * @param {Record<string, any>} vars
1826
+ */
1827
+ set(aliases, vars) {
1828
+ if (Object.keys(aliases).length + Object.keys(vars).length === 0) { return this; }
1829
+ const cloned = new Environment(this);
1830
+ cloned.#store = this.#store;
1831
+ cloned.#parent = this.#parent;
1832
+ const explicit = cloned.#explicit;
1833
+ const items = cloned.#items;
1834
+ for (const [key, name] of Object.entries(aliases)) {
1835
+ if (typeof name === 'function') {
1836
+ const getters = cloned.getters;
1837
+ cloned.#getters = null;
1838
+ const val = new Signal.Computed(() => name(getters));
1839
+ explicit[key] = items[key] = {
1840
+ get: () => { return val.get(); },
1841
+ };
1842
+ continue;
1843
+ }
1844
+ const item = items[name];
1845
+ if (!item) { continue; }
1846
+ if (!item.get || !item.store) {
1847
+ explicit[key] = items[key] = item;
1848
+ continue;
1849
+ }
1850
+ for (const [k, it] of toItem(item.store, key)) {
1851
+ explicit[k] = items[k] = it;
1852
+ }
1853
+ }
1854
+ for (const [k,v] of Object.entries(vars)) {
1855
+
1856
+ const val = new Signal.State(/** @type {any} */(null));
1857
+ if (typeof v === 'function') {
1858
+ const settable = cloned.settable;
1859
+ cloned.#settable = null;
1860
+ val.set(v(settable));
1861
+ } else if (v && typeof v === 'string') {
1862
+ const item = items[v];
1863
+ if (!item?.get) { continue }
1864
+ val.set(item.get());
1865
+ }
1866
+ explicit[k] = items[k] = {
1867
+ get: () => { return val.get(); },
1868
+ set: (v) => { val.set(v); },
1869
+ };
1870
+ }
1871
+ return cloned;
1872
+ }
1873
+
1874
+ /** @type {Record<string, any>?} */
1875
+ #all = null;
1876
+ get all() {
1877
+ const gt = this.#all;
1878
+ if (gt) { return gt; }
1879
+ /** @type {Record<string, any>} */
1880
+ const ngt = {};
1881
+ for (const [key, item] of Object.entries(this.#items)) {
1882
+ if (item.get) {
1883
+ Object.defineProperty(ngt, key, {
1884
+ get: item.get,
1885
+ set: item.set,
1886
+ configurable: true,
1887
+ enumerable: true,
1888
+ });
1889
+ } else if (item.calc) {
1890
+ Object.defineProperty(ngt, key, {
1891
+ value: item.calc,
1892
+ writable: false,
1893
+ configurable: true,
1894
+ enumerable: false,
1895
+ });
1896
+ } else {
1897
+ Object.defineProperty(ngt, key, {
1898
+ value: item.exec,
1899
+ writable: false,
1900
+ configurable: true,
1901
+ enumerable: false,
1902
+ });
1903
+ }
1904
+ }
1905
+ this.#all = ngt;
1906
+ return ngt;
1907
+ }
1908
+ /** @type {Record<string, any>?} */
1909
+ #settable = null;
1910
+ get settable() {
1911
+ const gt = this.#settable;
1912
+ if (gt) { return gt; }
1913
+ /** @type {Record<string, any>} */
1914
+ const ngt = {};
1915
+ for (const [key, item] of Object.entries(this.#items)) {
1916
+ if (item.get) {
1917
+ Object.defineProperty(ngt, key, {
1918
+ get: item.get,
1919
+ set: item.set,
1920
+ configurable: true,
1921
+ enumerable: true,
1922
+ });
1923
+ } else if (item.calc) {
1924
+ Object.defineProperty(ngt, key, {
1925
+ value: item.calc,
1926
+ writable: false,
1927
+ configurable: true,
1928
+ enumerable: false,
1929
+ });
1930
+ }
1931
+ }
1932
+ this.#settable = ngt;
1933
+ return ngt;
1934
+ }
1935
+ /** @type {Record<string, any>?} */
1936
+ #getters = null;
1937
+ get getters() {
1938
+ const gt = this.#getters;
1939
+ if (gt) { return gt; }
1940
+ /** @type {Record<string, any>} */
1941
+ const ngt = {};
1942
+ for (const [key, item] of Object.entries(this.#items)) {
1943
+ if (item.get) {
1944
+ Object.defineProperty(ngt, key, {
1945
+ get: item.get,
1946
+ configurable: true,
1947
+ enumerable: true,
1948
+ });
1949
+ } else if (item.calc) {
1950
+ Object.defineProperty(ngt, key, {
1951
+ value: item.calc,
1952
+ writable: false,
1953
+ configurable: true,
1954
+ enumerable: false,
1955
+ });
1956
+ }
1957
+ }
1958
+ this.#getters = ngt;
1959
+ return ngt;
1960
+ }
1961
+ }
1962
+
1963
+ /** @import { Component } from '../types.mjs' */
1964
+
1965
+ /**
1966
+ * @param {Component.Handler} handler
1967
+ * @param {Environment} envs
1968
+ * @param {Record<string, string | {name: string} | ((...any: any) => void)>} attrs
1969
+ * @param {Record<string, Component.Attr>} componentAttrs
1970
+ * @param {string?} [bindValue]
1971
+ */
1972
+ function bindAttrs(handler, envs, attrs, componentAttrs, bindValue) {
1973
+
1974
+ let bk = new Set();
1975
+ for (const [name, attr] of Object.entries(componentAttrs)) {
1976
+ const attrValue = attrs[name];
1977
+ if (name in attrs) {
1978
+ if (typeof attrValue !== 'function' && typeof attrValue !== 'object') {
1979
+ handler.set(name, attrValue);
1980
+ continue;
1981
+ }
1982
+ const attrSchema = typeof attrValue === 'function' ? attrValue : attrValue.name;
1983
+ if (attr.immutable) {
1984
+ handler.set(name, envs.exec(attrSchema));
1985
+ continue;
1986
+ }
1987
+ bk.add(envs.watch(attrSchema, v => handler.set(name, v)));
1988
+ continue;
1989
+ }
1990
+ const bind = attr.bind;
1991
+ if (!bindValue || !bind) {
1992
+ handler.set(name, attr.default);
1993
+ continue;
1994
+ }
1995
+ if (typeof bind === 'string') {
1996
+ const r = envs.bind(bindValue, bind, v => handler.set(name, v));
1997
+ if (r) {
1998
+ bk.add(r);
1999
+ } else {
2000
+ handler.set(name, attr.default);
2001
+ }
2002
+ continue;
2003
+ }
2004
+ if (!Array.isArray(bind)) {
2005
+ handler.set(name, attr.default);
2006
+ continue;
2007
+ }
2008
+ const [event, set, isState] = bind;
2009
+ if (!event || typeof set !== 'function') {
2010
+ continue;
2011
+ }
2012
+ if (!isState) {
2013
+ bk.add(envs.watch(bindValue, v => handler.set(name, v)));
2014
+ handler.addEvent(event, (...args) => { envs.all[bindValue] = set(...args);});
2015
+ continue;
2016
+ }
2017
+ const r = envs.bind(bindValue, 'state', v => handler.set(name, v));
2018
+ if (!r) { continue; }
2019
+ bk.add(r);
2020
+ const s = envs.bindSet(bindValue, 'state');
2021
+ if (!s) { continue; }
2022
+ handler.addEvent(event, (...args) => { s(set(...args));});
2023
+ }
2024
+ // TODO: 创建组件
2025
+ return ()=> {
2026
+ const list = bk;
2027
+ bk = new Set();
2028
+ for (const s of list) {
2029
+ s();
2030
+ }
2031
+ }
2032
+ }
2033
+
2034
+ /** @import { Component } from '../types.mjs' */
2035
+
2036
+ /**
2037
+ * @param {Component.Handler} handler
2038
+ * @param {Environment} envs
2039
+ * @param {Record<string, string | {name: string} | ((...any: any) => void)>} attrs
2040
+ * @param {string?} [bindValue]
2041
+ */
2042
+ function bindBaseAttrs(handler, envs, attrs, bindValue) {
2043
+ const tag = handler.tag;
2044
+ let bk = new Set();
2045
+ for (const [name, attr] of Object.entries(attrs)) {
2046
+ if (typeof attr !== 'function' && typeof attr !== 'object') {
2047
+ handler.set(name, attr);
2048
+ continue;
2049
+ }
2050
+ const attrSchema = typeof attr === 'function' ? attr : attr.name;
2051
+ if (typeof tag === 'string' && tag.toLocaleLowerCase() === 'input' && name.toLocaleLowerCase() === 'type') {
2052
+ const value = envs.exec(attrSchema);
2053
+ handler.set(name, value);
2054
+ continue;
2055
+ }
2056
+ bk.add(envs.watch(attrSchema, val => handler.set(name, val)));
2057
+ }
2058
+ if (bindValue) {
2059
+ for (const [key, effect] of Object.entries(envs.bindAll(bindValue) || {})) {
2060
+ if (typeof effect !== 'function') { continue; }
2061
+ bk.add(effect(val => handler.set(key, val)));
2062
+ }
2063
+ for (const [key, setter] of Object.entries(envs.bindStateAllSet(bindValue) || {})) {
2064
+ if (typeof setter !== 'function') { continue; }
2065
+ handler.addEvent(key, $event => setter($event));
2066
+ }
2067
+ }
2068
+
2069
+ return ()=> {
2070
+ const list = bk;
2071
+ bk = new Set();
2072
+ for (const s of list) {
2073
+ s();
2074
+ }
2075
+ }
2076
+ }
2077
+
2078
+ /**
2079
+ * @param {Node} node
2080
+ * @param {Environment} envs
2081
+ * @param {Record<string, string | boolean | ((...any: any) => void)>} classes
2082
+ */
2083
+ function bindClasses(node, classes, envs) {
2084
+ if (!(node instanceof Element)) {
2085
+ return () => {};
2086
+ }
2087
+
2088
+ /** @type {Set<() => void>?} */
2089
+ let bk = new Set();
2090
+ for (const [name, attr] of Object.entries(classes)) {
2091
+ if (!attr) { continue; }
2092
+ if (attr === true) {
2093
+ node.classList.add(name);
2094
+ continue;
2095
+ }
2096
+ bk.add(watch(() => Boolean(envs.exec(attr)), value => {
2097
+ if (value) {
2098
+ node.classList.add(name);
2099
+ } else {
2100
+ node.classList.remove(name);
2101
+ }
2102
+ }));
2103
+ }
2104
+ // TODO: 创建组件
2105
+ return ()=> {
2106
+ if (!bk) { return; }
2107
+ const list = bk;
2108
+ bk = null;
2109
+ for (const s of list) {
2110
+ s();
2111
+ }
2112
+ }
2113
+ }
2114
+
2115
+ /** @type {Record<string, string>} */
2116
+ const unit = {
2117
+ 'width': 'px',
2118
+ 'height': 'px',
2119
+ 'top': 'px',
2120
+ 'right': 'px',
2121
+ 'bottom': 'px',
2122
+ 'left': 'px',
2123
+ 'border': 'px',
2124
+ 'border-top': 'px',
2125
+ 'border-right': 'px',
2126
+ 'border-left': 'px',
2127
+ 'border-bottom': 'px',
2128
+ 'border-width': 'px',
2129
+ 'border-top-width': 'px',
2130
+ 'border-right-width': 'px',
2131
+ 'border-left-width': 'px',
2132
+ 'border-bottom-width': 'px',
2133
+ 'border-radius': 'px',
2134
+ 'border-top-left-radius': 'px',
2135
+ 'border-top-right-radius': 'px',
2136
+ 'border-bottom-left-radius': 'px',
2137
+ 'border-bottom-right-radius': 'px',
2138
+ 'padding': 'px',
2139
+ 'padding-top': 'px',
2140
+ 'padding-right': 'px',
2141
+ 'padding-left': 'px',
2142
+ 'padding-bottom': 'px',
2143
+ 'margin': 'px',
2144
+ 'margin-top': 'px',
2145
+ 'margin-right': 'px',
2146
+ 'margin-left': 'px',
2147
+ 'margin-bottom': 'px',
2148
+ };
2149
+ /**
2150
+ *
2151
+ * @param {string} name
2152
+ * @param {any} value
2153
+ * @returns {[string, 'important' | undefined]?}
2154
+ */
2155
+ function toStyle(name, value) {
2156
+ let important = Array.isArray(value) ? Boolean(value[1]) : false;
2157
+ let style = '';
2158
+ /** @typedef {[string, 'important' | undefined]} Result */
2159
+ if (typeof value === 'string') {
2160
+ style = value.replace(/!important\s*$/, '');
2161
+ if (!style) { return null; }
2162
+ important = style !== value;
2163
+ return [style, important ? 'important' : undefined];
2164
+ }
2165
+ const val = Array.isArray(value) ? value[0] : value;
2166
+ if (typeof val === 'number' || typeof val === 'bigint') {
2167
+ style = val && name in unit ? `${ val }${ unit[name] }` : `${ val }`;
2168
+ } else if (typeof val === 'string') {
2169
+ style = val;
2170
+ }
2171
+ if (!style) { return null; }
2172
+ return [style, important ? 'important' : undefined];
2173
+
2174
+
2175
+ }
2176
+
2177
+
2178
+ /**
2179
+ * @param {Element} node
2180
+ * @param {Environment} envs
2181
+ * @param {Record<string, string | ((...any: any) => void)>} classes
2182
+ */
2183
+ function bindStyles(node, classes, envs) {
2184
+ if (!(node instanceof HTMLElement) && !(node instanceof SVGElement)) {
2185
+ return () => {};
2186
+ }
2187
+
2188
+ /** @type {Set<() => void>?} */
2189
+ let bk = new Set();
2190
+ for (const [name, attr] of Object.entries(classes)) {
2191
+ bk.add(watch(() => toStyle(name, envs.exec(attr)), value => {
2192
+ if (value) {
2193
+ node.style.setProperty(name, ...value);
2194
+ } else {
2195
+ node.style.removeProperty(name);
2196
+ }
2197
+ }));
2198
+ }
2199
+ // TODO: 创建组件
2200
+ return ()=> {
2201
+ if (!bk) { return; }
2202
+ const list = bk;
2203
+ bk = null;
2204
+ for (const s of list) {
2205
+ s();
2206
+ }
2207
+ }
2208
+ }
2209
+
2210
+ /**
2211
+ * @template {Record<string, any[]>} T
2212
+ */
2213
+ class EventEmitter {
2214
+ /** @type {Map<keyof T, Set<(...p: any[]) => void>>} */
2215
+ #events = new Map()
2216
+ /**
2217
+ *
2218
+ * @template {keyof T} K
2219
+ * @param {K} event
2220
+ * @param {T[K]} p
2221
+ */
2222
+ emit(event, ...p) {
2223
+ const key = typeof event === 'number' ? String(event) : event;
2224
+ const events = this.#events;
2225
+ for (const d of [...events.get(key) || []]) {
2226
+ d(...p);
2227
+ }
2228
+ }
2229
+ /**
2230
+ *
2231
+ * @template {keyof T} K
2232
+ * @param {K} event
2233
+ * @param {(...p: T[K]) => void} listener
2234
+ * @returns {() => void}
2235
+ */
2236
+ listen(event, listener) {
2237
+ /** @type {any} */
2238
+ const fn = listener.bind(this);
2239
+ const events = this.#events;
2240
+ const key = typeof event === 'number' ? String(event) : event;
2241
+ let set = events.get(key);
2242
+ if (!set) {
2243
+ set = new Set();
2244
+ events.set(key, set);
2245
+ }
2246
+ set.add(fn);
2247
+ return () => { set?.delete(fn); }
2248
+
2249
+ }
2250
+ }
2251
+
2252
+ /** @import { Component } from '../types.mjs' */
2253
+ /** @import Environment from './Environment.mjs' */
2254
+
2255
+
2256
+ /** @type {Record<string, (evt: any, param: string[], global: any) => boolean | null | void>} */
2257
+ const eventFilters = {
2258
+ stop(evt) {
2259
+ if (evt instanceof Event) { evt.stopPropagation(); }
2260
+ },
2261
+ prevent(evt) {
2262
+ if (evt instanceof Event) { evt.preventDefault(); }
2263
+ },
2264
+ self(evt) {
2265
+ if (evt instanceof Event) { return evt.target === evt.currentTarget; }
2266
+ },
2267
+ enter(evt) {
2268
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Enter'; }
2269
+ },
2270
+ tab(evt) {
2271
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Tab'; }
2272
+ },
2273
+ esc(evt) {
2274
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Escape'; }
2275
+ },
2276
+ space(evt) {
2277
+ if (evt instanceof KeyboardEvent) { return evt.key === ' '; }
2278
+ },
2279
+ backspace(evt) {
2280
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Backspace'; }
2281
+ },
2282
+ delete(evt) {
2283
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Delete'; }
2284
+ },
2285
+ delBack(evt) {
2286
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Delete' || evt.key === 'Backspace'; }
2287
+ },
2288
+ 'del-back'(evt) {
2289
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Delete' || evt.key === 'Backspace'; }
2290
+ },
2291
+ insert(evt) {
2292
+ if (evt instanceof KeyboardEvent) { return evt.key === 'Insert'; }
2293
+ },
2294
+ repeat(evt) {
2295
+ if (evt instanceof KeyboardEvent) { return evt.repeat; }
2296
+ },
2297
+
2298
+ key(evt, param) {
2299
+ if (evt instanceof KeyboardEvent) {
2300
+ const key = evt.code.toLowerCase().replace(/-/g, '');
2301
+ for (const k of param) {
2302
+ if (key === k.toLowerCase().replace(/-/g, '')) { return true }
2303
+ }
2304
+ return false;
2305
+ }
2306
+ },
2307
+ main(evt) {
2308
+ if (evt instanceof MouseEvent) { return evt.button === 0; }
2309
+ },
2310
+ auxiliary(evt) {
2311
+ if (evt instanceof MouseEvent) { return evt.button === 1; }
2312
+ },
2313
+ secondary(evt) {
2314
+ if (evt instanceof MouseEvent) { return evt.button === 2; }
2315
+ },
2316
+ left(evt) {
2317
+ if (evt instanceof MouseEvent) { return evt.button === 0; }
2318
+ },
2319
+ middle(evt) {
2320
+ if (evt instanceof MouseEvent) { return evt.button === 1; }
2321
+ },
2322
+ right(evt) {
2323
+ if (evt instanceof MouseEvent) { return evt.button === 2; }
2324
+ },
2325
+ primary(evt) {
2326
+ if (evt instanceof PointerEvent) { return evt.isPrimary; }
2327
+ },
2328
+ mouse(evt) {
2329
+ if (evt instanceof PointerEvent) { return evt.pointerType === 'mouse'; }
2330
+ },
2331
+ pen(evt) {
2332
+ if (evt instanceof PointerEvent) { return evt.pointerType === 'pen'; }
2333
+ },
2334
+ touch(evt) {
2335
+ if (evt instanceof PointerEvent) { return evt.pointerType === 'touch'; }
2336
+ },
2337
+ pointer(evt, param) {
2338
+ if (evt instanceof PointerEvent) {
2339
+ const pointerType = evt.pointerType.toLowerCase().replace(/-/g, '');
2340
+ for (const k of param) {
2341
+ if (pointerType === k.toLowerCase().replace(/-/g, '')) { return true }
2342
+ }
2343
+ return false;
2344
+ }
2345
+ },
2346
+
2347
+ ctrl(evt) {
2348
+ if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
2349
+ return evt.ctrlKey;
2350
+ }
2351
+ },
2352
+ alt(evt) {
2353
+ if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
2354
+ return evt.altKey;
2355
+ }
2356
+ },
2357
+ shift(evt) {
2358
+ if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
2359
+ return evt.shiftKey;
2360
+ }
2361
+ },
2362
+ meta(evt) {
2363
+ if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
2364
+ return evt.metaKey;
2365
+ }
2366
+ },
2367
+ cmd(evt) {
2368
+ if (evt instanceof MouseEvent|| evt instanceof KeyboardEvent || evt instanceof TouchEvent) {
2369
+ return evt.ctrlKey || evt.metaKey;
2370
+ }
2371
+ },
2372
+ };
2373
+ /**
2374
+ *
2375
+ * @param {Component | string} component
2376
+ * @param {Environment} env
2377
+ * @returns
2378
+ */
2379
+ function createContext(component, env) {
2380
+ const tag = typeof component === 'string' ? component : component.tag;
2381
+ const { attrs, events } = typeof component !== 'string' && component || {attrs: null, events: null };
2382
+
2383
+ let destroyed = false;
2384
+ let init = false;
2385
+ const tagAttrs = Object.create(null);
2386
+
2387
+ /** @type {[string, ($event: any) => void, AddEventListenerOptions][]} */
2388
+ const allEvents = [];
2389
+ const stateEmitter = new EventEmitter();
2390
+ /** @type {EventEmitter<Record<string, [any, any, string]>>} */
2391
+ const attrEmitter = new EventEmitter();
2392
+ /** @type {Component.Context} */
2393
+ const context = {
2394
+ events: allEvents,
2395
+ props: attrs ? new Set(Object.entries(attrs).filter(([,a]) => a.isProp).map(([e]) => e)) : null,
2396
+ tagAttrs,
2397
+ watchAttr(name, fn) { return attrEmitter.listen(name, fn); },
2398
+ get destroyed() { return destroyed},
2399
+ get init() { return init},
2400
+ listen(name, listener) { return stateEmitter.listen(name, listener); },
2401
+ };
2402
+ /** @type {Component.Handler} */
2403
+ const handler = {
2404
+ tag,
2405
+ set(name, value) {
2406
+ if (attrs && !(name in attrs)) { return; }
2407
+ if (!(name in tagAttrs)) { tagAttrs[name] = void 0; }
2408
+ const old = tagAttrs[name];
2409
+ if (old === value) { return; }
2410
+ tagAttrs[name] = value;
2411
+ attrEmitter.emit(name, value, old, name);
2412
+ },
2413
+ addEvent(name, fn) {
2414
+ if (typeof fn !== 'function') { return; }
2415
+ const [e, ...fs] = name.split('.').filter(Boolean);
2416
+ const filters = events ? events[e].filters : {};
2417
+ if (!filters) { return; }
2418
+ /** @type {AddEventListenerOptions} */
2419
+ const options = {};
2420
+ /** @type {[($event: any, param: string[], env: any) => boolean | null | void, string[], boolean][]} */
2421
+ const filterFns = [];
2422
+ if (filters) for (let f = fs.shift();f;f = fs.shift()) {
2423
+ const paramIndex = f.indexOf(':');
2424
+ const noParamName = paramIndex >= 0 ? f.slice(0, paramIndex) : f;
2425
+ const param = paramIndex >= 0 ? f.slice(paramIndex + 1).split(':') : [];
2426
+ const filterName = noParamName.replace(/^-+/, '');
2427
+ const sub = (noParamName.length - filterName.length) % 2 === 1;
2428
+ let filter = filters[filterName] || filterName;
2429
+ switch(filter) {
2430
+ case 'once':
2431
+ options.once = !sub;
2432
+ break;
2433
+ case 'passive':
2434
+ options.passive = !sub;
2435
+ break;
2436
+ case 'capture':
2437
+ options.capture = !sub;
2438
+ break;
2439
+ default:
2440
+ if (typeof filter === 'string') {
2441
+ filter = eventFilters[filter];
2442
+ }
2443
+ }
2444
+ if (typeof filter !== 'function') { continue; }
2445
+ filterFns.push([filter, param, sub]);
2446
+ }
2447
+ allEvents.push([e, $event => {
2448
+ const global = env.all;
2449
+ for (const [filter, param, sub] of filterFns) {
2450
+ if (filter($event, param, global) === sub) { return}
2451
+ }
2452
+ fn($event, global);
2453
+ }, options]);
2454
+ },
2455
+ destroy() {
2456
+ if (destroyed) { return }
2457
+ destroyed = true;
2458
+ stateEmitter.emit('destroy');
2459
+ },
2460
+ init() {
2461
+ if (init) { return }
2462
+ init = true;
2463
+ stateEmitter.emit('init', {events: allEvents});
2464
+ },
2465
+
2466
+ };
2467
+ return { context, handler };
2468
+ }
2469
+
2470
+ /** @import { Component } from '../types.mjs' */
2471
+ /**
2472
+ *
2473
+ * @param {any} val
2474
+ * @returns
2475
+ */
2476
+ function toAttrValue(val) {
2477
+ if (typeof val === 'number') {
2478
+ return String(val);
2479
+ }
2480
+ if (typeof val === 'bigint') {
2481
+ return String(val);
2482
+ }
2483
+ if (typeof val === 'boolean') {
2484
+ return val ? '' : null;
2485
+ }
2486
+ if (typeof val === 'string') {
2487
+ return val;
2488
+ }
2489
+ if ((val ?? null) === null) {
2490
+ return null;
2491
+ }
2492
+ return String(val);
2493
+ }
2494
+ /**
2495
+ *
2496
+ * @param {any} val
2497
+ * @returns
2498
+ */
2499
+ function toText$1(val) {
2500
+ if ((val ?? null) === null) {
2501
+ return '';
2502
+ }
2503
+ return String(val);
2504
+ }
2505
+
2506
+
2507
+ /**
2508
+ *
2509
+ * @param {Element} el
2510
+ * @param {string} attr
2511
+ */
2512
+ function getAttrs(el, attr) {
2513
+ if (el instanceof HTMLInputElement && 'checked' === attr) {
2514
+ switch (el.type.toLowerCase()) {
2515
+ case 'checkbox':
2516
+ case 'radio':
2517
+ return Boolean;
2518
+ }
2519
+ }
2520
+ if ((
2521
+ el instanceof HTMLSelectElement
2522
+ || el instanceof HTMLInputElement
2523
+ || el instanceof HTMLTextAreaElement
2524
+ ) && 'value' === attr) {
2525
+ return toText$1;
2526
+ }
2527
+ if ((el instanceof HTMLDetailsElement) && 'open' === attr) {
2528
+ return Boolean;
2529
+ }
2530
+ if (el instanceof HTMLMediaElement) {
2531
+ if ('muted' === attr) {
2532
+ return Boolean;
2533
+ }
2534
+ if ('paused' === attr) {
2535
+ return Boolean;
2536
+ }
2537
+ if ('currentTime' === attr) {
2538
+ return true;
2539
+ }
2540
+ if ('playbackRate' === attr) {
2541
+ return true;
2542
+ }
2543
+ if ('volume' === attr) {
2544
+ return true;
2545
+ }
2546
+ }
2547
+ return false;
2548
+ }
2549
+
2550
+ /**
2551
+ * @typedef {object} TagBind
2552
+ * @property {Record<string, (value: any, el: any) => void>} attrs
2553
+ * @property {Record<string, [name: string, set: (event: any, el: any) => any]>} events
2554
+ */
2555
+ /** @type {Record<string, TagBind>} */
2556
+ const tagBindMap = {
2557
+ input: {
2558
+ attrs: {
2559
+ /** @param {any} v @param {HTMLInputElement} e */
2560
+ $disabled: (v, e) => {e.disabled = v;},
2561
+ /** @param {any} v @param {HTMLInputElement} e */
2562
+ $readonly: (v, e) => {e.readOnly = v;},
2563
+ /** @param {any} v @param {HTMLInputElement} e */
2564
+ $required: (v, e) => {e.required = v;},
2565
+ /** @param {any} v @param {HTMLInputElement} e */
2566
+ $value: (v, e) => {
2567
+ switch(e.type) {
2568
+ case 'checkbox':
2569
+ case 'radio':
2570
+ e.checked = Boolean(v);
2571
+ break;
2572
+ }
2573
+ e.value = toText$1(v);
2574
+ },
2575
+ },
2576
+ events: {
2577
+ $value: ['input', (v, e) => {
2578
+ switch(e.type) {
2579
+ case 'checkbox':
2580
+ case 'radio':
2581
+ return e.checked;
2582
+ case 'number':
2583
+ return Number(e.value);
2584
+ }
2585
+ return e.value;
2586
+ }],
2587
+ },
2588
+ },
2589
+ textarea: {
2590
+ attrs: {
2591
+ /** @param {any} v @param {HTMLInputElement} e */
2592
+ $disabled: (v, e) => {e.disabled = v;},
2593
+ /** @param {any} v @param {HTMLInputElement} e */
2594
+ $readonly: (v, e) => {e.readOnly = v;},
2595
+ /** @param {any} v @param {HTMLInputElement} e */
2596
+ $required: (v, e) => {e.required = v;},
2597
+ /** @param {any} v @param {HTMLInputElement} e */
2598
+ $value: (v, e) => { e.value = toText$1(v); },
2599
+ },
2600
+ events: {
2601
+ $value: ['input', (v, e) => { return e.value; }],
2602
+ },
2603
+ },
2604
+ select: {
2605
+ attrs: {
2606
+ /** @param {any} v @param {HTMLInputElement} e */
2607
+ $disabled: (v, e) => {e.disabled = v;},
2608
+ /** @param {any} v @param {HTMLInputElement} e */
2609
+ $readonly: (v, e) => {e.readOnly = v;},
2610
+ /** @param {any} v @param {HTMLInputElement} e */
2611
+ $required: (v, e) => {e.required = v;},
2612
+ /** @param {any} v @param {HTMLInputElement} e */
2613
+ $value: (v, e) => { e.value = toText$1(v); },
2614
+ },
2615
+ events: {
2616
+ $value: ['change', (v, e) => { return e.value; }],
2617
+ },
2618
+ },
2619
+ };
2620
+
2621
+ /**
2622
+ *
2623
+ * @param {Component.Context} context
2624
+ * @param {string} name
2625
+ * @param {string?} [is]
2626
+ */
2627
+ function createTagComponent (context, name, is) {
2628
+ const node = document.createElement(name, {is: is || undefined});
2629
+ const { watchAttr, props } = context;
2630
+
2631
+ context.listen('init', ({events})=> {
2632
+ const e = tagBindMap[name.toLowerCase()];
2633
+ const eAttrs = e?.attrs || {};
2634
+ const eEvents = e?.events || {};
2635
+ for (const [type, listener, options] of events) {
2636
+ if (type[0] === '$') {
2637
+ const e = eEvents[type];
2638
+ if (e) {
2639
+ const [evt, set] = e;
2640
+ node.addEventListener(evt, e => listener(set(e, node)), options);
2641
+ }
2642
+ continue;
2643
+ }
2644
+ node.addEventListener(type, listener, options);
2645
+ }
2646
+ if (props) {
2647
+ for (const [name, attr] of Object.entries(context.tagAttrs)) {
2648
+ watchAttr(name, v => {
2649
+ // @ts-ignore
2650
+ if (props.has(name)) { node[name] = v; } else {
2651
+ const val = toAttrValue(v);
2652
+ if (val == null) {
2653
+ node.removeAttribute(name);
2654
+ } else {
2655
+ node.setAttribute(name, val);
2656
+ }
2657
+ }
2658
+ });
2659
+ // @ts-ignore
2660
+ if (props.has(name)) { node[name] = attr; } else {
2661
+ const val = toAttrValue(attr);
2662
+ if (val !== null) {
2663
+ node.setAttribute(name, val);
2664
+ }
2665
+ }
2666
+ }
2667
+ return;
2668
+
2669
+ }
2670
+ for (const [name, attr] of Object.entries(context.tagAttrs)) {
2671
+ if (node instanceof HTMLInputElement && name.toLocaleLowerCase() === 'type') {
2672
+ const value = toAttrValue(attr);
2673
+ if (value !== null) {
2674
+ node.setAttribute(name, value);
2675
+ }
2676
+ continue;
2677
+ }
2678
+ if (name === '$hidden') {
2679
+ if (attr) { node.hidden = attr; }
2680
+ watchAttr(name, (val) => { node.hidden = val; });
2681
+ continue;
2682
+ }
2683
+
2684
+ if (name[0] === '$') {
2685
+ const e = eAttrs[name];
2686
+ if (e) {
2687
+ e(attr, node);
2688
+ watchAttr(name, (attr) => e(attr, node));
2689
+ }
2690
+ continue;
2691
+ }
2692
+ const prop = getAttrs(node, name);
2693
+ if (typeof prop === 'function') {
2694
+ // @ts-ignore
2695
+ node[name] = prop(attr);
2696
+ watchAttr(name, (attr) => {
2697
+ // @ts-ignore
2698
+ node[name] = prop(attr);
2699
+ });
2700
+ continue;
2701
+ }
2702
+ if (prop) {
2703
+ // @ts-ignore
2704
+ node[name] = attr;
2705
+ watchAttr(name, (attr) => {
2706
+ // @ts-ignore
2707
+ node[name] = attr;
2708
+ });
2709
+ continue;
2710
+ }
2711
+ let value = toAttrValue(attr);
2712
+ if (value !== null) {
2713
+ node.setAttribute(name, value);
2714
+ }
2715
+ watchAttr(name, (val) => {
2716
+ const value = toAttrValue(val);
2717
+ if (value === null) {
2718
+ node.removeAttribute(name);
2719
+ } else {
2720
+ node.setAttribute(name, value);
2721
+ }
2722
+ });
2723
+ }
2724
+ });
2725
+ return node;
2726
+ }
2727
+
2728
+ /** @import * as Layout from '../Layout/index.mjs' */
2729
+ /** @import Store, { ArrayStore } from '../Store/index.mjs' */
2730
+
2731
+ /**
2732
+ *
2733
+ * @param {Layout.Node} layout
2734
+ * @param {Element} parent
2735
+ * @param {Node?} next
2736
+ * @param {ArrayStore} store
2737
+ * @param {Environment} env
2738
+ * @param {(layout: Layout.Node, parent: Element, next: Node | null, store: Store, env: any) => () => void} renderItem
2739
+ */
2740
+ function renderArray(layout, parent, next, store, env, renderItem) {
2741
+ const start = parent.insertBefore(document.createComment(''), next);
2742
+ /** @type {Map<Store, [Comment, Comment, () => void]>} */
2743
+ let seMap = new Map();
2744
+ /** @param {Map<Store, [Comment, Comment, () => void]>} map */
2745
+ function destroyMap(map) {
2746
+ for (const [s, e, d] of map.values()) {
2747
+ d();
2748
+ s.remove();
2749
+ e.remove();
2750
+ }
2751
+
2752
+ }
2753
+ const childrenResult = watch(() => store.children, function render(children) {
2754
+ if (!start.parentNode) { return; }
2755
+ let nextNode = start.nextSibling;
2756
+ const oldSeMap = seMap;
2757
+ seMap = new Map();
2758
+ for (let child of children) {
2759
+ const old = oldSeMap.get(child);
2760
+ if (!old) {
2761
+ const ItemStart = parent.insertBefore(document.createComment(''), nextNode);
2762
+ const itemEnd = parent.insertBefore(document.createComment(''), nextNode);
2763
+ const d = renderItem(layout, parent, itemEnd, child, env.setValue(child, store));
2764
+ seMap.set(child, [ItemStart, itemEnd, d]);
2765
+ continue;
2766
+ }
2767
+ oldSeMap.delete(child);
2768
+ seMap.set(child, old);
2769
+ if (nextNode === old[0]) {
2770
+ nextNode = old[1].nextSibling;
2771
+ continue;
2772
+ }
2773
+ /** @type {Node?} */
2774
+ let c = old[0];
2775
+ while (c && c !== old[1]) {
2776
+ const o = c;
2777
+ c = c.nextSibling;
2778
+ parent.insertBefore(o, nextNode);
2779
+ }
2780
+ parent.insertBefore(old[1], nextNode);
2781
+ }
2782
+ destroyMap(oldSeMap);
2783
+ });
2784
+
2785
+ return () => {
2786
+ start.remove();
2787
+ childrenResult();
2788
+ };
2789
+ }
2790
+
2791
+ /** @import * as Layout from '../Layout/index.mjs' */
2792
+ /**
2793
+ *
2794
+ * @param {any} val
2795
+ * @returns
2796
+ */
2797
+ function toText(val) {
2798
+ if ((val ?? null) === null) {
2799
+ return '';
2800
+ }
2801
+ return String(val);
2802
+ }
2803
+
2804
+
2805
+ /**
2806
+ * @param {Element} parent
2807
+ * @param {Node?} next
2808
+ * @param {Environment} envs
2809
+ * @param {Layout.Directives} layout
2810
+ */
2811
+ function renderFillDirectives(parent, next, envs, { text, html }) {
2812
+ if (text != null) {
2813
+ const node = parent.insertBefore(document.createTextNode(''), next);
2814
+ const stop = envs.watch(text, val => node.textContent = toText(val));
2815
+ return () => {
2816
+ node.remove();
2817
+ stop();
2818
+ };
2819
+ }
2820
+ if (html == null) { return; }
2821
+ const start = parent.insertBefore(document.createComment(''), next);
2822
+ const end = parent.insertBefore(document.createComment(''), next);
2823
+ const div = document.createElement('div');
2824
+ /** @param {string} html */
2825
+ function add(html) {
2826
+ div.innerHTML = html;
2827
+ for (let node = div.firstChild; node; node = start.firstChild) {
2828
+ parent.insertBefore(node, end);
2829
+ }
2830
+ }
2831
+ function remove() {
2832
+ for (let node = start.nextSibling; node && node !== end; node = start.nextSibling) {
2833
+ node.remove();
2834
+ }
2835
+ }
2836
+ const result = envs.watch(html, val => {
2837
+ remove();
2838
+ add(toText(val));
2839
+ });
2840
+ return () => {
2841
+ result();
2842
+ remove();
2843
+ start.remove();
2844
+ end.remove();
2845
+ };
2846
+ }
2847
+
2848
+ /** @import * as Layout from '../Layout/index.mjs' */
2849
+
2850
+ /**
2851
+ * @param {(Layout.Node | string)[]} layouts
2852
+ * @param {Element} parent
2853
+ * @param {Node?} next
2854
+ * @param {Environment} envs
2855
+ * @param {(layout: Layout.Node) => () => void} renderItem
2856
+ * @returns {() => void}
2857
+ */
2858
+ function renderList(layouts, parent, next, envs, renderItem) {
2859
+
2860
+ /** @type {Set<() => void>?} */
2861
+ let bkList = new Set();
2862
+ /** @type {[string | Function | null, Layout.Node][]} */
2863
+ let ifList = [];
2864
+
2865
+ /** @param {[string | Function | null, Layout.Node][]} list */
2866
+ function renderIf(list) {
2867
+ if (!list.length || !bkList) { return; }
2868
+ const end = parent.insertBefore(document.createComment(''), next);
2869
+
2870
+
2871
+ let lastIndex = -1;
2872
+ let destroy = () => { };
2873
+ /**
2874
+ *
2875
+ * @param {number} index
2876
+ * @returns
2877
+ */
2878
+ function renderIndex(index) {
2879
+ const layout = list[index]?.[1];
2880
+ if (!layout) { return; }
2881
+ destroy = renderItem(layout);
2882
+ }
2883
+ bkList.add(() => {
2884
+ destroy();
2885
+ destroy = () => { };
2886
+ end.remove();
2887
+ });
2888
+ bkList.add(watch(
2889
+ () => list.findIndex(([ifv]) => ifv === null || envs.exec(ifv)),
2890
+ index => {
2891
+ if (index === lastIndex) { return; }
2892
+ lastIndex = index;
2893
+ destroy();
2894
+ destroy = () => { };
2895
+ renderIndex(lastIndex);
2896
+ },
2897
+ ));
2898
+ }
2899
+ for (const layout of layouts) {
2900
+ if (typeof layout === 'string') {
2901
+ renderIf(ifList);
2902
+ ifList = [];
2903
+ const node = document.createTextNode(layout);
2904
+ parent.insertBefore(node, next);
2905
+ bkList.add(() => node.remove());
2906
+ continue;
2907
+ }
2908
+ if (ifList.length && layout.directives.else) {
2909
+ const ifv = layout.directives.if || null;
2910
+ ifList.push([ifv, layout]);
2911
+ if (!ifv) {
2912
+ renderIf(ifList);
2913
+ ifList = [];
2914
+ }
2915
+ continue;
2916
+ }
2917
+ renderIf(ifList);
2918
+ ifList = [];
2919
+ const ifv = layout.directives.if;
2920
+ if (ifv) {
2921
+ ifList.push([ifv, layout]);
2922
+ continue;
2923
+ }
2924
+ bkList.add(
2925
+ renderItem(layout)
2926
+ );
2927
+ }
2928
+
2929
+ return () => {
2930
+ if (!bkList) { return; }
2931
+ const list = bkList;
2932
+ bkList = null;
2933
+ for (const s of list) {
2934
+ s();
2935
+ }
2936
+ };
2937
+ }
2938
+
2939
+ /** @import Store from '../Store/index.mjs' */
2940
+
2941
+ /**
2942
+ * @param {Layout.Node} layout
2943
+ * @param {Element} parent
2944
+ * @param {Node?} next
2945
+ * @param {Environment} env
2946
+ * @param {(layout: Layout.Node) => () => void} renderItem
2947
+ * @returns {() => void}
2948
+ */
2949
+ function renderFragment(layout, parent, next, env, renderItem) {
2950
+ return renderFillDirectives(parent, next, env, layout.directives) ||
2951
+ renderList(layout.children || [], parent, next, env, renderItem);
2952
+
2953
+ }
2954
+ /**
2955
+ * @param {Layout.Node} layout
2956
+ * @param {Element} parent
2957
+ * @param {Node?} next
2958
+ * @param {Store} store
2959
+ * @param {Environment} env
2960
+ * @param {string[]} componentPath
2961
+ * @param {((path: string[]) => Component?)?} [getComponent]
2962
+ */
2963
+ function renderItem(layout, parent, next, store, env, componentPath, getComponent) {
2964
+ env = env.set(layout.aliases, layout.vars);
2965
+ if (!layout.name || layout.directives.fragment) {
2966
+ return renderFragment(layout, parent, next, env, l => {
2967
+ return render(l, parent, next, store, env, componentPath, getComponent);
2968
+ });
2969
+ }
2970
+ const path = [...componentPath, layout.name];
2971
+ const component = getComponent?.(path);
2972
+ if (getComponent && !component) { return () => { }; }
2973
+ const { context, handler } = createContext(component ? component : layout.name, env);
2974
+
2975
+
2976
+ const componentAttrs = component?.attrs;
2977
+ const attrs = componentAttrs
2978
+ ? bindAttrs(handler, env, layout.attrs, componentAttrs, layout.directives.bind)
2979
+ : bindBaseAttrs(handler, env, layout.attrs, layout.directives.bind);
2980
+
2981
+ for (const [name, event] of Object.entries(layout.events)) {
2982
+ const fn = env.getEvent(event);
2983
+ if (fn) { handler.addEvent(name, fn); }
2984
+ }
2985
+
2986
+ const r = component ?
2987
+ typeof component.tag === 'function'
2988
+ ? component.tag(context)
2989
+ : createTagComponent(context, component.tag, component.is)
2990
+ : createTagComponent(context, layout.name, layout.is);
2991
+ const root = Array.isArray(r) ? r[0] : r;
2992
+ const slot = Array.isArray(r) && r[1] || root;
2993
+ parent.insertBefore(root, next);
2994
+ const children =
2995
+ renderFillDirectives(slot, null, env, layout.directives)
2996
+ || renderList(layout.children || [], slot, null, env, l => {
2997
+ return render(l, slot, null, store, env, componentPath, getComponent);
2998
+ });
2999
+
3000
+
3001
+ bindClasses(root, layout.classes, env);
3002
+ bindStyles(root, layout.styles, env);
3003
+
3004
+ handler.init();
3005
+ // TODO: 创建组件
3006
+ return () => {
3007
+ root.remove();
3008
+ handler.destroy();
3009
+ attrs();
3010
+ children();
3011
+ };
3012
+ }
3013
+ /**
3014
+ *
3015
+ * @param {Layout.Node} layout
3016
+ * @param {Element} parent
3017
+ * @param {Node?} next
3018
+ * @param {Store} store
3019
+ * @param {Environment} env
3020
+ * @param {string[]} componentPath
3021
+ * @param {((path: string[]) => Component?)?} [getComponent]
3022
+ * @returns {() => void}
3023
+ */
3024
+ function render(layout, parent, next, store, env, componentPath, getComponent) {
3025
+ const { directives } = layout;
3026
+ const { value } = directives;
3027
+ if (value) {
3028
+ const newStore = store.child(value);
3029
+ if (!newStore) { return () => {}; }
3030
+ store = newStore;
3031
+ env = env.setValue(store);
3032
+ }
3033
+ if (!directives.enum) {
3034
+ return renderItem(layout, parent, next, store, env, componentPath, getComponent);
3035
+ }
3036
+ if (!(store instanceof ArrayStore)) { return () => { }; }
3037
+ return renderArray(layout, parent, next, store, env, (a, b, c, store, env) => {
3038
+ return renderItem(a, b, c, store, env, componentPath, getComponent);
3039
+ });
3040
+ }
3041
+
3042
+ /**
3043
+ * @overload
3044
+ * @param {Store} store
3045
+ * @param {(Layout.Node | string)[]} layouts
3046
+ * @param {Element} parent
3047
+ * @param {Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }>} [global]
3048
+ * @param {(path: string[]) => Component?} [components]
3049
+ * @returns {() => void}
3050
+ */
3051
+ /**
3052
+ * @overload
3053
+ * @param {Store} store
3054
+ * @param {(Layout.Node | string)[]} layouts
3055
+ * @param {Element} parent
3056
+ * @param {((path: string[]) => Component?)?} [components]
3057
+ * @returns {() => void}
3058
+ */
3059
+ /**
3060
+ * @param {Store} store
3061
+ * @param {(Layout.Node | string)[]} layouts
3062
+ * @param {Element} parent
3063
+ * @param {((path: string[]) => Component?) | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt1]
3064
+ * @param {((path: string[]) => Component?) | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt2]
3065
+ */
3066
+ function index (store, layouts, parent, opt1, opt2) {
3067
+ const options = [opt1, opt2];
3068
+ const components = options.find(v => typeof v === 'function');
3069
+ const global = options.find(v => typeof v === 'object');
3070
+ const env = new Environment(global).setValue(store);
3071
+ return renderList(layouts, parent, null, env, l => {
3072
+ return render(l, parent, null, store, env, [], components);
3073
+ });
3074
+ }
3075
+
3076
+ export { index$1 as Layout, Store, index as render };