@neeloong/form 0.20.0 → 0.21.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.d.mts +43 -25
- package/index.full.js +640 -676
- package/index.full.min.js +8 -8
- package/index.full.min.mjs +6 -6
- package/index.min.mjs +3 -3
- package/index.mjs +640 -676
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @neeloong/form v0.
|
|
3
|
-
* (c) 2024-
|
|
2
|
+
* @neeloong/form v0.21.0
|
|
3
|
+
* (c) 2024-2026 Fierflame
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -151,7 +151,6 @@ let TypeStores = Object.create(null);
|
|
|
151
151
|
* @param {string | number | null} [options.index]
|
|
152
152
|
* @param {boolean} [options.new]
|
|
153
153
|
* @param {(value: any, index: any, store: Store) => void} [options.onUpdate]
|
|
154
|
-
* @param {(value: any, index: any, store: Store) => void} [options.onUpdateState]
|
|
155
154
|
*/
|
|
156
155
|
function create(schema, options) {
|
|
157
156
|
const type = schema.type;
|
|
@@ -282,13 +281,14 @@ function merge(...v) {
|
|
|
282
281
|
*
|
|
283
282
|
* @template {Store} T
|
|
284
283
|
* @param {T} store
|
|
285
|
-
* @param {((store: T) => any) | any} def
|
|
284
|
+
* @param {((store: T, value?: any) => any) | any} def
|
|
285
|
+
* @returns {(value?: any) => unknown}
|
|
286
286
|
*/
|
|
287
287
|
function makeDefault(store, def) {
|
|
288
288
|
if (typeof def !== 'function') {
|
|
289
289
|
return () => structuredClone(def);
|
|
290
290
|
}
|
|
291
|
-
return () => structuredClone(def(store));
|
|
291
|
+
return (value) => structuredClone(def(store, value));
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
/** @import { Ref } from './ref.mjs' */
|
|
@@ -301,7 +301,7 @@ function makeDefault(store, def) {
|
|
|
301
301
|
*/
|
|
302
302
|
class Store {
|
|
303
303
|
/** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
|
|
304
|
-
#events = new Map()
|
|
304
|
+
#events = new Map();
|
|
305
305
|
/**
|
|
306
306
|
* 触发事件并通知监听器
|
|
307
307
|
* @template {keyof Schema.Events} K
|
|
@@ -334,7 +334,7 @@ class Store {
|
|
|
334
334
|
events.set(key, set);
|
|
335
335
|
}
|
|
336
336
|
set.add(fn);
|
|
337
|
-
return () => { set?.delete(fn); }
|
|
337
|
+
return () => { set?.delete(fn); };
|
|
338
338
|
|
|
339
339
|
}
|
|
340
340
|
/**
|
|
@@ -345,7 +345,7 @@ class Store {
|
|
|
345
345
|
* @param {boolean} [options.new] 是否为新建环境
|
|
346
346
|
*/
|
|
347
347
|
static create(schema, options = {}) {
|
|
348
|
-
return create({type: schema}, { ...options, parent: null });
|
|
348
|
+
return create({ type: schema }, { ...options, parent: null });
|
|
349
349
|
}
|
|
350
350
|
/**
|
|
351
351
|
* 设置自定义类型的存储类
|
|
@@ -367,8 +367,7 @@ class Store {
|
|
|
367
367
|
* @param {Schema.Field<M>} schema 字段的 Schema 定义
|
|
368
368
|
* @param {object} [options] 可选配置
|
|
369
369
|
* @param {Store?} [options.parent]
|
|
370
|
-
* @param {((store: Store) => any) |
|
|
371
|
-
* @param {*} [options.state]
|
|
370
|
+
* @param {((store: Store, value?: any) => any) | object | number | string | boolean | null | undefined} [options.default]
|
|
372
371
|
* @param {number | string | null} [options.index]
|
|
373
372
|
* @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
|
|
374
373
|
* @param {boolean} [options.null]
|
|
@@ -396,22 +395,19 @@ class Store {
|
|
|
396
395
|
* @param {Ref?} [options.ref]
|
|
397
396
|
*
|
|
398
397
|
* @param {((value: any) => any)?} [options.setValue]
|
|
399
|
-
* @param {((value: any) => any)?} [options.
|
|
400
|
-
* @param {((value: any, state: any) => [value: any, state: any])?} [options.convert]
|
|
398
|
+
* @param {((value: any) => any)?} [options.convert]
|
|
401
399
|
*
|
|
402
400
|
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
|
|
403
|
-
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdateState]
|
|
404
401
|
*/
|
|
405
402
|
constructor(schema, {
|
|
406
|
-
null: isNull,
|
|
407
|
-
setValue,
|
|
403
|
+
null: isNull, ref, default: defaultValue,
|
|
404
|
+
setValue, convert, onUpdate,
|
|
408
405
|
validator, validators,
|
|
409
406
|
index, size, new: isNew, parent: parentNode,
|
|
410
407
|
hidden, clearable, required, disabled, readonly, removable,
|
|
411
408
|
label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
|
|
412
409
|
} = {}) {
|
|
413
410
|
this.schema = schema;
|
|
414
|
-
this.#state.set(typeof state === 'object' && state || {});
|
|
415
411
|
const parent = parentNode instanceof Store ? parentNode : null;
|
|
416
412
|
if (parent) {
|
|
417
413
|
this.#parent = parent;
|
|
@@ -455,7 +451,7 @@ class Store {
|
|
|
455
451
|
const s = selfReadonly.get();
|
|
456
452
|
return s === null ? readonlyScript.get() : s;
|
|
457
453
|
};
|
|
458
|
-
const readonlyParent =
|
|
454
|
+
const readonlyParent = parent ? parent.#readonly : null;
|
|
459
455
|
this.#selfReadonly = selfReadonly;
|
|
460
456
|
this.#readonly = readonlyParent
|
|
461
457
|
? new Signal.Computed(() => readonlyParent.get() || getReadonly())
|
|
@@ -484,15 +480,15 @@ class Store {
|
|
|
484
480
|
|
|
485
481
|
const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
|
|
486
482
|
const [blurred, blurredResult, cancelBlur] = createAsyncValidator(this, schema.validators?.blur, validators?.blur);
|
|
487
|
-
this.listen('change', () => {changed();});
|
|
488
|
-
this.listen('blur', () => {blurred();});
|
|
483
|
+
this.listen('change', () => { changed(); });
|
|
484
|
+
this.listen('blur', () => { blurred(); });
|
|
489
485
|
this.#errors = merge(validatorResult, changedResult, blurredResult);
|
|
490
486
|
this.#validatorResult = validatorResult;
|
|
491
487
|
this.#changed = changed;
|
|
492
488
|
this.#blurred = blurred;
|
|
493
489
|
this.#cancelChange = cancelChange;
|
|
494
490
|
this.#cancelBlur = cancelBlur;
|
|
495
|
-
|
|
491
|
+
|
|
496
492
|
if (size instanceof Signal.State || size instanceof Signal.Computed) {
|
|
497
493
|
this.#size = size;
|
|
498
494
|
} else {
|
|
@@ -506,30 +502,25 @@ class Store {
|
|
|
506
502
|
}
|
|
507
503
|
this.#ref = ref || null;
|
|
508
504
|
this.#onUpdate = onUpdate || null;
|
|
509
|
-
this.#onUpdateState = onUpdateState || null;
|
|
510
505
|
this.#setValue = typeof setValue === 'function' ? setValue : null;
|
|
511
|
-
this.#setState = typeof setState === 'function' ? setState : null;
|
|
512
506
|
this.#convert = typeof convert === 'function' ? convert : null;
|
|
513
507
|
this.#index.set(index ?? '');
|
|
514
|
-
|
|
508
|
+
|
|
515
509
|
for (const [k, f] of Object.entries(schema.events || {})) {
|
|
516
510
|
if (typeof f !== 'function') { continue; }
|
|
517
511
|
// @ts-ignore
|
|
518
512
|
this.listen(k, f);
|
|
519
513
|
}
|
|
520
514
|
}
|
|
521
|
-
#createDefault
|
|
522
|
-
|
|
515
|
+
#createDefault;
|
|
516
|
+
/** @param {any} [value] @returns {any} */
|
|
517
|
+
createDefault(value) { return this.#createDefault(value); }
|
|
523
518
|
/** @type {((value: any) => any)?} */
|
|
524
|
-
#setValue = null
|
|
519
|
+
#setValue = null;
|
|
525
520
|
/** @type {((value: any) => any)?} */
|
|
526
|
-
#
|
|
527
|
-
/** @type {((value: any, state: any) => [value: any, state: any])?} */
|
|
528
|
-
#convert = null
|
|
521
|
+
#convert = null;
|
|
529
522
|
/** @type {((value: any, index: any, store: Store) => void)?} */
|
|
530
|
-
#onUpdate = null
|
|
531
|
-
/** @type {((value: any, index: any, store: Store) => void)?} */
|
|
532
|
-
#onUpdateState = null
|
|
523
|
+
#onUpdate = null;
|
|
533
524
|
/** @readonly @type {Store?} */
|
|
534
525
|
#parent = null;
|
|
535
526
|
/** @readonly @type {Store} */
|
|
@@ -541,7 +532,7 @@ class Store {
|
|
|
541
532
|
/** @readonly @type {any} */
|
|
542
533
|
#component;
|
|
543
534
|
/** @type {Signal.State<boolean>?} */
|
|
544
|
-
#selfLoading = null
|
|
535
|
+
#selfLoading = null;
|
|
545
536
|
/** @type {Signal.State<boolean>} */
|
|
546
537
|
#loading;
|
|
547
538
|
get loading() {
|
|
@@ -549,7 +540,7 @@ class Store {
|
|
|
549
540
|
}
|
|
550
541
|
set loading(loading) {
|
|
551
542
|
const s = this.#selfLoading;
|
|
552
|
-
if (!s) { return }
|
|
543
|
+
if (!s) { return; }
|
|
553
544
|
s.set(Boolean(loading));
|
|
554
545
|
}
|
|
555
546
|
/** 存储对象自身 */
|
|
@@ -588,9 +579,9 @@ class Store {
|
|
|
588
579
|
get immutable() { return this.#immutable; }
|
|
589
580
|
|
|
590
581
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
591
|
-
#new
|
|
582
|
+
#new;
|
|
592
583
|
/** @readonly @type {Signal.State<boolean>} */
|
|
593
|
-
#selfNew
|
|
584
|
+
#selfNew;
|
|
594
585
|
get selfNew() { return this.#selfNew.get(); }
|
|
595
586
|
set selfNew(v) { this.#selfNew.set(Boolean(v)); }
|
|
596
587
|
/** 是否新建项 */
|
|
@@ -598,9 +589,9 @@ class Store {
|
|
|
598
589
|
set new(v) { this.#selfNew.set(Boolean(v)); }
|
|
599
590
|
|
|
600
591
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
601
|
-
#selfHidden
|
|
592
|
+
#selfHidden;
|
|
602
593
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
603
|
-
#hidden
|
|
594
|
+
#hidden;
|
|
604
595
|
get selfHidden() { return this.#selfHidden.get(); }
|
|
605
596
|
set selfHidden(v) { this.#selfHidden.set(typeof v === 'boolean' ? v : null); }
|
|
606
597
|
/** 是否可隐藏 */
|
|
@@ -608,9 +599,9 @@ class Store {
|
|
|
608
599
|
set hidden(v) { this.#selfHidden.set(typeof v === 'boolean' ? v : null); }
|
|
609
600
|
|
|
610
601
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
611
|
-
#selfClearable
|
|
602
|
+
#selfClearable;
|
|
612
603
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
613
|
-
#clearable
|
|
604
|
+
#clearable;
|
|
614
605
|
get selfClearable() { return this.#selfClearable.get(); }
|
|
615
606
|
set selfClearable(v) { this.#selfClearable.set(typeof v === 'boolean' ? v : null); }
|
|
616
607
|
/** 是否可清除 */
|
|
@@ -618,9 +609,9 @@ class Store {
|
|
|
618
609
|
set clearable(v) { this.#selfClearable.set(typeof v === 'boolean' ? v : null); }
|
|
619
610
|
|
|
620
611
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
621
|
-
#selfRequired
|
|
612
|
+
#selfRequired;
|
|
622
613
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
623
|
-
#required
|
|
614
|
+
#required;
|
|
624
615
|
get selfRequired() { return this.#selfRequired.get(); }
|
|
625
616
|
set selfRequired(v) { this.#selfRequired.set(typeof v === 'boolean' ? v : null); }
|
|
626
617
|
/** 是否必填 */
|
|
@@ -628,9 +619,9 @@ class Store {
|
|
|
628
619
|
set required(v) { this.#selfRequired.set(typeof v === 'boolean' ? v : null); }
|
|
629
620
|
|
|
630
621
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
631
|
-
#selfDisabled
|
|
622
|
+
#selfDisabled;
|
|
632
623
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
633
|
-
#disabled
|
|
624
|
+
#disabled;
|
|
634
625
|
get selfDisabled() { return this.#selfDisabled.get(); }
|
|
635
626
|
set selfDisabled(v) { this.#selfDisabled.set(typeof v === 'boolean' ? v : null); }
|
|
636
627
|
/** 是否禁用字段 */
|
|
@@ -638,9 +629,9 @@ class Store {
|
|
|
638
629
|
set disabled(v) { this.#selfDisabled.set(typeof v === 'boolean' ? v : null); }
|
|
639
630
|
|
|
640
631
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
641
|
-
#selfReadonly
|
|
632
|
+
#selfReadonly;
|
|
642
633
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
643
|
-
#readonly
|
|
634
|
+
#readonly;
|
|
644
635
|
get selfReadonly() { return this.#selfReadonly.get(); }
|
|
645
636
|
set selfReadonly(v) { this.#selfReadonly.set(typeof v === 'boolean' ? v : null); }
|
|
646
637
|
/** 是否只读 */
|
|
@@ -649,9 +640,9 @@ class Store {
|
|
|
649
640
|
|
|
650
641
|
|
|
651
642
|
/** @readonly @type {Signal.State<boolean?>} */
|
|
652
|
-
#selfRemovable
|
|
643
|
+
#selfRemovable;
|
|
653
644
|
/** @readonly @type {Signal.Computed<boolean>} */
|
|
654
|
-
#removable
|
|
645
|
+
#removable;
|
|
655
646
|
get selfRemovable() { return this.#selfRemovable.get(); }
|
|
656
647
|
set selfRemovable(v) { this.#selfRemovable.set(typeof v === 'boolean' ? v : null); }
|
|
657
648
|
/** 是否只读 */
|
|
@@ -660,9 +651,9 @@ class Store {
|
|
|
660
651
|
|
|
661
652
|
|
|
662
653
|
/** @readonly @type {Signal.State<string?>} */
|
|
663
|
-
#selfLabel
|
|
654
|
+
#selfLabel;
|
|
664
655
|
/** @readonly @type {Signal.Computed<string?>} */
|
|
665
|
-
#label
|
|
656
|
+
#label;
|
|
666
657
|
get selfLabel() { return this.#selfLabel.get(); }
|
|
667
658
|
set selfLabel(v) { this.#selfLabel.set(string(v)); }
|
|
668
659
|
/** 字段的标签信息 */
|
|
@@ -671,9 +662,9 @@ class Store {
|
|
|
671
662
|
|
|
672
663
|
|
|
673
664
|
/** @readonly @type {Signal.State<string?>} */
|
|
674
|
-
#selfDescription
|
|
665
|
+
#selfDescription;
|
|
675
666
|
/** @readonly @type {Signal.Computed<string?>} */
|
|
676
|
-
#description
|
|
667
|
+
#description;
|
|
677
668
|
get selfDescription() { return this.#selfDescription.get(); }
|
|
678
669
|
set selfDescription(v) { this.#selfDescription.set(string(v)); }
|
|
679
670
|
/** 字段的描述信息 */
|
|
@@ -681,9 +672,9 @@ class Store {
|
|
|
681
672
|
set description(v) { this.#selfDescription.set(string(v)); }
|
|
682
673
|
|
|
683
674
|
/** @readonly @type {Signal.State<string?>} */
|
|
684
|
-
#selfPlaceholder
|
|
675
|
+
#selfPlaceholder;
|
|
685
676
|
/** @readonly @type {Signal.Computed<string?>} */
|
|
686
|
-
#placeholder
|
|
677
|
+
#placeholder;
|
|
687
678
|
get selfPlaceholder() { return this.#selfPlaceholder.get(); }
|
|
688
679
|
set selfPlaceholder(v) { this.#selfPlaceholder.set(string(v)); }
|
|
689
680
|
/** 字段的占位符信息 */
|
|
@@ -692,9 +683,9 @@ class Store {
|
|
|
692
683
|
|
|
693
684
|
|
|
694
685
|
/** @readonly @type {Signal.State<number?>} */
|
|
695
|
-
#selfMin
|
|
686
|
+
#selfMin;
|
|
696
687
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
697
|
-
#min
|
|
688
|
+
#min;
|
|
698
689
|
get selfMin() { return this.#selfMin.get(); }
|
|
699
690
|
set selfMin(v) { this.#selfMin.set(number(v)); }
|
|
700
691
|
/** 数值字段的最小值限制 */
|
|
@@ -703,9 +694,9 @@ class Store {
|
|
|
703
694
|
|
|
704
695
|
|
|
705
696
|
/** @readonly @type {Signal.State<number?>} */
|
|
706
|
-
#selfMax
|
|
697
|
+
#selfMax;
|
|
707
698
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
708
|
-
#max
|
|
699
|
+
#max;
|
|
709
700
|
get selfMax() { return this.#selfMax.get(); }
|
|
710
701
|
set selfMax(v) { this.#selfMax.set(number(v)); }
|
|
711
702
|
/** 数值字段的最大值限制 */
|
|
@@ -714,9 +705,9 @@ class Store {
|
|
|
714
705
|
|
|
715
706
|
|
|
716
707
|
/** @readonly @type {Signal.State<number?>} */
|
|
717
|
-
#selfStep
|
|
708
|
+
#selfStep;
|
|
718
709
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
719
|
-
#step
|
|
710
|
+
#step;
|
|
720
711
|
get selfStep() { return this.#selfStep.get(); }
|
|
721
712
|
set selfStep(v) { this.#selfStep.set(number(v)); }
|
|
722
713
|
/** 数值字段的步长 */
|
|
@@ -724,9 +715,9 @@ class Store {
|
|
|
724
715
|
set step(v) { this.#selfStep.set(number(v)); }
|
|
725
716
|
|
|
726
717
|
/** @readonly @type {Signal.State<number?>} */
|
|
727
|
-
#selfMinLength
|
|
718
|
+
#selfMinLength;
|
|
728
719
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
729
|
-
#minLength
|
|
720
|
+
#minLength;
|
|
730
721
|
get selfMinLength() { return this.#selfMinLength.get(); }
|
|
731
722
|
set selfMinLength(v) { this.#selfMinLength.set(number(v)); }
|
|
732
723
|
/** 最小长度 */
|
|
@@ -734,9 +725,9 @@ class Store {
|
|
|
734
725
|
set minLength(v) { this.#selfMinLength.set(number(v)); }
|
|
735
726
|
|
|
736
727
|
/** @readonly @type {Signal.State<number?>} */
|
|
737
|
-
#selfMaxLength
|
|
728
|
+
#selfMaxLength;
|
|
738
729
|
/** @readonly @type {Signal.Computed<number?>} */
|
|
739
|
-
#maxLength
|
|
730
|
+
#maxLength;
|
|
740
731
|
get selfMaxLength() { return this.#selfMaxLength.get(); }
|
|
741
732
|
set selfMaxLength(v) { this.#selfMaxLength.set(number(v)); }
|
|
742
733
|
/** 最大长度 */
|
|
@@ -744,9 +735,9 @@ class Store {
|
|
|
744
735
|
set maxLength(v) { this.#selfMaxLength.set(number(v)); }
|
|
745
736
|
|
|
746
737
|
/** @readonly @type {Signal.State<RegExp?>} */
|
|
747
|
-
#selfPattern
|
|
738
|
+
#selfPattern;
|
|
748
739
|
/** @readonly @type {Signal.Computed<RegExp?>} */
|
|
749
|
-
#pattern
|
|
740
|
+
#pattern;
|
|
750
741
|
get selfPattern() { return this.#selfPattern.get(); }
|
|
751
742
|
set selfPattern(v) { this.#selfPattern.set(regex(v)); }
|
|
752
743
|
/** 模式 */
|
|
@@ -755,9 +746,9 @@ class Store {
|
|
|
755
746
|
|
|
756
747
|
|
|
757
748
|
/** @readonly @type {Signal.State<(Schema.Value.Group | Schema.Value)[] | null>} */
|
|
758
|
-
#selfValues
|
|
749
|
+
#selfValues;
|
|
759
750
|
/** @readonly @type {Signal.Computed<(Schema.Value.Group | Schema.Value)[] | null>} */
|
|
760
|
-
#values
|
|
751
|
+
#values;
|
|
761
752
|
get selfValues() { return this.#selfValues.get(); }
|
|
762
753
|
set selfValues(v) { this.#selfValues.set(values(v)); }
|
|
763
754
|
/** 可选值列表 */
|
|
@@ -766,24 +757,24 @@ class Store {
|
|
|
766
757
|
|
|
767
758
|
|
|
768
759
|
/** @type {Signal.Computed<string[]>} */
|
|
769
|
-
#errors
|
|
760
|
+
#errors;
|
|
770
761
|
/** @type {Signal.Computed<string[]>} */
|
|
771
|
-
#validatorResult
|
|
762
|
+
#validatorResult;
|
|
772
763
|
/** @type {() => Promise<string[]>} */
|
|
773
|
-
#changed
|
|
764
|
+
#changed;
|
|
774
765
|
/** @type {() => Promise<string[]>} */
|
|
775
|
-
#blurred
|
|
766
|
+
#blurred;
|
|
776
767
|
/** @type {() => void} */
|
|
777
|
-
#cancelChange
|
|
768
|
+
#cancelChange;
|
|
778
769
|
/** @type {() => void} */
|
|
779
|
-
#cancelBlur
|
|
770
|
+
#cancelBlur;
|
|
780
771
|
/** 所有校验错误列表 */
|
|
781
772
|
get errors() { return this.#errors.get(); }
|
|
782
773
|
/** 字段校验错误信息 */
|
|
783
774
|
get error() { return this.#errors.get()[0]; }
|
|
784
775
|
|
|
785
776
|
/** @returns {IterableIterator<[key: string | number, value: Store]>} */
|
|
786
|
-
*[Symbol.iterator]() {}
|
|
777
|
+
*[Symbol.iterator]() { }
|
|
787
778
|
/**
|
|
788
779
|
* 获取子存储
|
|
789
780
|
* @param {string | number} key
|
|
@@ -795,11 +786,9 @@ class Store {
|
|
|
795
786
|
#initValue = new Signal.State(/** @type {T?} */(null));
|
|
796
787
|
#value = new Signal.State(this.#initValue.get());
|
|
797
788
|
|
|
798
|
-
|
|
799
|
-
#state = new Signal.State(/** @type {any} */(null));
|
|
800
789
|
|
|
801
790
|
/** 内容是否已改变 */
|
|
802
|
-
get changed() { return this.#value.get()
|
|
791
|
+
get changed() { return Object.is(this.#value.get(), this.#initValue.get()); }
|
|
803
792
|
|
|
804
793
|
/** 字段当前值 */
|
|
805
794
|
get value() { return this.#value.get(); }
|
|
@@ -814,34 +803,26 @@ class Store {
|
|
|
814
803
|
this.#requestUpdate();
|
|
815
804
|
}
|
|
816
805
|
|
|
817
|
-
/** 字段状态 */
|
|
818
|
-
get state() { return this.#state.get(); }
|
|
819
|
-
set state(v) {
|
|
820
|
-
const newState = this.#setState?.(v);
|
|
821
|
-
const sta = newState === undefined ? v : newState;
|
|
822
|
-
this.#state.set(sta);
|
|
823
|
-
this.#onUpdateState?.(sta, this.#index.get(), this);
|
|
824
|
-
this.#requestUpdate();
|
|
825
|
-
}
|
|
826
806
|
#requestUpdate() {
|
|
827
807
|
if (this.#needUpdate) { return; }
|
|
828
808
|
this.#needUpdate = true;
|
|
829
809
|
queueMicrotask(() => {
|
|
830
810
|
const oldValue = this.#value.get();
|
|
831
|
-
|
|
832
|
-
this.#runUpdate(oldValue, oldState);
|
|
811
|
+
this.#runUpdate(oldValue);
|
|
833
812
|
});
|
|
834
813
|
}
|
|
835
814
|
/** 重置数据 */
|
|
836
|
-
reset(value = this.#initValue.get()) {
|
|
837
|
-
this.#reset(value);
|
|
815
|
+
reset(value = this.#set ? this.#initValue.get() : this.#createDefault(), isNew = this.#selfNew.get()) {
|
|
816
|
+
this.#reset(value, Boolean(isNew));
|
|
838
817
|
}
|
|
839
818
|
/**
|
|
840
819
|
*
|
|
841
820
|
* @param {*} v
|
|
821
|
+
* @param {boolean} isNew
|
|
842
822
|
* @returns
|
|
843
823
|
*/
|
|
844
|
-
#reset(v) {
|
|
824
|
+
#reset(v, isNew) {
|
|
825
|
+
this.#selfNew.set(isNew);
|
|
845
826
|
const newValue = this.#setValue?.(v);
|
|
846
827
|
const value = newValue === undefined ? v : newValue;
|
|
847
828
|
this.#cancelChange();
|
|
@@ -849,16 +830,17 @@ class Store {
|
|
|
849
830
|
this.#set = true;
|
|
850
831
|
if (!value || typeof value !== 'object') {
|
|
851
832
|
for (const [, field] of this) {
|
|
852
|
-
field.#reset(null);
|
|
833
|
+
field.#reset(null, false);
|
|
853
834
|
}
|
|
854
835
|
this.#value.set(value);
|
|
855
836
|
this.#initValue.set(value);
|
|
837
|
+
this.#onUpdate?.(value, this.#index.get(), this);
|
|
856
838
|
return value;
|
|
857
839
|
}
|
|
858
840
|
/** @type {*} */
|
|
859
|
-
const newValues = Array.isArray(value) ? [...value] : {...value};
|
|
841
|
+
const newValues = Array.isArray(value) ? [...value] : { ...value };
|
|
860
842
|
for (const [key, field] of this) {
|
|
861
|
-
newValues[key] = field.#reset(newValues[key]);
|
|
843
|
+
newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
|
|
862
844
|
}
|
|
863
845
|
this.#value.set(newValues);
|
|
864
846
|
this.#initValue.set(newValues);
|
|
@@ -871,59 +853,48 @@ class Store {
|
|
|
871
853
|
/**
|
|
872
854
|
*
|
|
873
855
|
* @param {T} value
|
|
874
|
-
* @param {*} state
|
|
875
856
|
* @returns
|
|
876
857
|
*/
|
|
877
|
-
#toUpdate(value
|
|
878
|
-
|
|
879
|
-
if(
|
|
880
|
-
this.#value.
|
|
881
|
-
this.#
|
|
882
|
-
return this.#runUpdate(val, sta);
|
|
858
|
+
#toUpdate(value) {
|
|
859
|
+
let val = this.#convert?.(value) ?? value;
|
|
860
|
+
if (val === undefined) { val = value; }
|
|
861
|
+
if (Object.is(this.#value.get(), val)) { return val; }
|
|
862
|
+
return this.#runUpdate(val);
|
|
883
863
|
}
|
|
884
864
|
/**
|
|
885
865
|
*
|
|
886
866
|
* @param {*} val
|
|
887
|
-
* @param {*} sta
|
|
888
867
|
* @returns {[any, any]}
|
|
889
868
|
*/
|
|
890
|
-
#runUpdate(val
|
|
869
|
+
#runUpdate(val) {
|
|
891
870
|
this.#needUpdate = false;
|
|
892
871
|
let initValue = val;
|
|
893
872
|
if (val && typeof val === 'object') {
|
|
894
873
|
/** @type {T} */
|
|
895
874
|
// @ts-ignore
|
|
896
|
-
let newValues = Array.isArray(val) ? [...val] : {...val};
|
|
897
|
-
let newStates = Array.isArray(val) ? Array.isArray(sta) ? [...sta] : [] : {...sta};
|
|
875
|
+
let newValues = Array.isArray(val) ? [...val] : { ...val };
|
|
898
876
|
let updated = false;
|
|
899
877
|
for (const [key, field] of this) {
|
|
900
878
|
// @ts-ignore
|
|
901
|
-
const data = val[key];
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
updated = true;
|
|
908
|
-
}
|
|
909
|
-
if (state !== newState) {
|
|
910
|
-
newStates[key] = newState;
|
|
911
|
-
updated = true;
|
|
912
|
-
}
|
|
879
|
+
const data = Object.hasOwn(val, key) ? val[key] : undefined;
|
|
880
|
+
const newData = field.#toUpdate(data);
|
|
881
|
+
if (Object.is(data, newData)) { continue; }
|
|
882
|
+
// @ts-ignore
|
|
883
|
+
newValues[key] = newData;
|
|
884
|
+
updated = true;
|
|
913
885
|
}
|
|
914
886
|
if (updated) {
|
|
915
887
|
val = newValues;
|
|
916
|
-
sta = newStates;
|
|
917
888
|
initValue = val;
|
|
918
889
|
this.#value.set(val);
|
|
919
|
-
this.#state.set(newStates);
|
|
920
890
|
}
|
|
921
891
|
}
|
|
922
892
|
if (!this.#set) {
|
|
923
893
|
this.#set = true;
|
|
924
894
|
this.#initValue.set(initValue);
|
|
925
895
|
}
|
|
926
|
-
|
|
896
|
+
this.#value.set(val);
|
|
897
|
+
return val;
|
|
927
898
|
}
|
|
928
899
|
/**
|
|
929
900
|
* 异步校验
|
|
@@ -947,18 +918,18 @@ class Store {
|
|
|
947
918
|
return Promise.all([this.#validatorResult.get(), this.#changed(), this.#blurred()])
|
|
948
919
|
.then(v => {
|
|
949
920
|
const errors = v.flat();
|
|
950
|
-
return errors.length ? errors : null
|
|
921
|
+
return errors.length ? errors : null;
|
|
951
922
|
});
|
|
952
923
|
}
|
|
953
924
|
const selfPath = Array.isArray(path) ? path : [];
|
|
954
|
-
const list = [this.validate().then(errors => {
|
|
955
|
-
if (!errors?.length) {return [];}
|
|
956
|
-
return [{path: [...selfPath], store: /** @type {Store} */(this), errors}]
|
|
925
|
+
const list = [this.validate(true).then(errors => {
|
|
926
|
+
if (!errors?.length) { return []; }
|
|
927
|
+
return [{ path: [...selfPath], store: /** @type {Store} */(this), errors }];
|
|
957
928
|
})];
|
|
958
929
|
for (const [key, field] of this) {
|
|
959
930
|
list.push(field.validate([...selfPath, key]));
|
|
960
931
|
}
|
|
961
|
-
return Promise.all(list).then(v => v.flat())
|
|
932
|
+
return Promise.all(list).then(v => v.flat());
|
|
962
933
|
}
|
|
963
934
|
}
|
|
964
935
|
|
|
@@ -972,8 +943,8 @@ class Store {
|
|
|
972
943
|
class ObjectStore extends Store {
|
|
973
944
|
get kind() { return 'object'; }
|
|
974
945
|
/** @type {Record<string, Store>} */
|
|
975
|
-
#children
|
|
976
|
-
*[Symbol.iterator]() {yield* Object.entries(this.#children);}
|
|
946
|
+
#children;
|
|
947
|
+
*[Symbol.iterator]() { yield* Object.entries(this.#children); }
|
|
977
948
|
/**
|
|
978
949
|
*
|
|
979
950
|
* @param {string | number} key
|
|
@@ -987,26 +958,25 @@ class ObjectStore extends Store {
|
|
|
987
958
|
* @param {number | string | null} [options.index]
|
|
988
959
|
* @param {boolean} [options.new]
|
|
989
960
|
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
|
|
990
|
-
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdateState]
|
|
991
961
|
*/
|
|
992
|
-
constructor(schema,{ parent, index, new: isNew, onUpdate
|
|
962
|
+
constructor(schema, { parent, index, new: isNew, onUpdate } = {}) {
|
|
993
963
|
const childrenTypes = Object.entries(schema.type);
|
|
994
964
|
/** @type {Record<string, Store>} */
|
|
995
965
|
const children = Object.create(null);
|
|
996
966
|
super(schema, {
|
|
997
|
-
parent, index, new: isNew, onUpdate,
|
|
967
|
+
parent, index, new: isNew, onUpdate,
|
|
998
968
|
size: childrenTypes.length,
|
|
999
969
|
setValue(v) { return typeof v === 'object' ? v : null; },
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
return [
|
|
1003
|
-
typeof v === 'object' ? v : {},
|
|
1004
|
-
typeof state === 'object' ? state : {},
|
|
1005
|
-
]
|
|
970
|
+
convert(v) {
|
|
971
|
+
return typeof v === 'object' ? v : {};
|
|
1006
972
|
},
|
|
1007
|
-
default: schema.default ?? (() =>
|
|
1008
|
-
Object.entries(children)
|
|
1009
|
-
|
|
973
|
+
default: schema.default ?? ((store, value) => {
|
|
974
|
+
const list = Object.entries(children);
|
|
975
|
+
let obj = value;
|
|
976
|
+
if (!obj || typeof obj !== 'object') { obj = schema.default; }
|
|
977
|
+
if (!obj || typeof obj !== 'object') { obj = {}; }
|
|
978
|
+
return Object.fromEntries(list.map(([k, v]) => [k, v.createDefault(Object.hasOwn(obj, k) ? obj[k] : null)]));
|
|
979
|
+
}),
|
|
1010
980
|
});
|
|
1011
981
|
const childCommonOptions = {
|
|
1012
982
|
parent: this,
|
|
@@ -1014,17 +984,12 @@ class ObjectStore extends Store {
|
|
|
1014
984
|
onUpdate: (value, index, store) => {
|
|
1015
985
|
if (store !== this.#children[index]) { return; }
|
|
1016
986
|
// @ts-ignore
|
|
1017
|
-
this.value = {...this.value, [index]: value};
|
|
987
|
+
this.value = { ...this.value, [index]: value };
|
|
1018
988
|
},
|
|
1019
|
-
/** @param {*} state @param {*} index @param {Store} store */
|
|
1020
|
-
onUpdateState: (state, index, store) => {
|
|
1021
|
-
if (store !== this.#children[index]) { return; }
|
|
1022
|
-
this.state = {...this.state, [index]: state};
|
|
1023
|
-
}
|
|
1024
989
|
};
|
|
1025
990
|
|
|
1026
991
|
for (const [index, field] of childrenTypes) {
|
|
1027
|
-
children[index] = create(field, {...childCommonOptions, index});
|
|
992
|
+
children[index] = create(field, { ...childCommonOptions, index });
|
|
1028
993
|
}
|
|
1029
994
|
this.#children = children;
|
|
1030
995
|
}
|
|
@@ -1069,9 +1034,8 @@ class ArrayStore extends Store {
|
|
|
1069
1034
|
* @param {boolean} [options.new]
|
|
1070
1035
|
* @param {boolean} [options.addable]
|
|
1071
1036
|
* @param {(value: any, index: any, store: Store) => void} [options.onUpdate]
|
|
1072
|
-
* @param {(value: any, index: any, store: Store) => void} [options.onUpdateState]
|
|
1073
1037
|
*/
|
|
1074
|
-
constructor(schema, { parent, onUpdate,
|
|
1038
|
+
constructor(schema, { parent, onUpdate, index, new: isNew, addable } = {}) {
|
|
1075
1039
|
const childrenState = new Signal.State(/** @type {Store[]} */([]));
|
|
1076
1040
|
// @ts-ignore
|
|
1077
1041
|
const updateChildren = (list) => {
|
|
@@ -1090,22 +1054,16 @@ class ArrayStore extends Store {
|
|
|
1090
1054
|
super(schema, {
|
|
1091
1055
|
index, new: isNew, parent,
|
|
1092
1056
|
size: new Signal.Computed(() => childrenState.get().length),
|
|
1093
|
-
state: [],
|
|
1094
1057
|
setValue(v) { return Array.isArray(v) ? v : v == null ? null : [v]; },
|
|
1095
|
-
|
|
1096
|
-
convert(v, state) {
|
|
1058
|
+
convert(v) {
|
|
1097
1059
|
const val = Array.isArray(v) ? v : v == null ? null : [v];
|
|
1098
1060
|
updateChildren(val);
|
|
1099
|
-
return
|
|
1100
|
-
val,
|
|
1101
|
-
(Array.isArray(state) ? state : v == null ? [] : [state]),
|
|
1102
|
-
];
|
|
1061
|
+
return val;
|
|
1103
1062
|
},
|
|
1104
|
-
onUpdate: (value, index,
|
|
1063
|
+
onUpdate: (value, index, store) => {
|
|
1105
1064
|
updateChildren(value);
|
|
1106
|
-
onUpdate?.(value, index,
|
|
1065
|
+
onUpdate?.(value, index, store);
|
|
1107
1066
|
},
|
|
1108
|
-
onUpdateState,
|
|
1109
1067
|
default: schema.default ?? [],
|
|
1110
1068
|
});
|
|
1111
1069
|
|
|
@@ -1124,16 +1082,6 @@ class ArrayStore extends Store {
|
|
|
1124
1082
|
val[index] = value;
|
|
1125
1083
|
this.value = val;
|
|
1126
1084
|
},
|
|
1127
|
-
/** @param {*} state @param {*} index @param {Store} store */
|
|
1128
|
-
onUpdateState: (state, index, store) => {
|
|
1129
|
-
if (childrenState.get()[index] !== store) { return; }
|
|
1130
|
-
const sta = [...this.state || []];
|
|
1131
|
-
if (sta.length < index) {
|
|
1132
|
-
sta.length = index;
|
|
1133
|
-
}
|
|
1134
|
-
sta[index] = state;
|
|
1135
|
-
this.state = sta;
|
|
1136
|
-
},
|
|
1137
1085
|
};
|
|
1138
1086
|
this.#create = (index, isNew) => {
|
|
1139
1087
|
const child = create(schema, { ...childCommonOptions, index, new: isNew });
|
|
@@ -1175,13 +1123,7 @@ class ArrayStore extends Store {
|
|
|
1175
1123
|
children[i].index = i;
|
|
1176
1124
|
}
|
|
1177
1125
|
const val = [...data];
|
|
1178
|
-
val.splice(insertIndex, 0,
|
|
1179
|
-
const state = this.state;
|
|
1180
|
-
if (Array.isArray(state)) {
|
|
1181
|
-
const sta = [...state];
|
|
1182
|
-
sta.splice(insertIndex, 0, {});
|
|
1183
|
-
this.state = sta;
|
|
1184
|
-
}
|
|
1126
|
+
val.splice(insertIndex, 0, item.createDefault(value));
|
|
1185
1127
|
this.#children.set(children);
|
|
1186
1128
|
this.value = val;
|
|
1187
1129
|
return true;
|
|
@@ -1211,12 +1153,6 @@ class ArrayStore extends Store {
|
|
|
1211
1153
|
}
|
|
1212
1154
|
const val = [...data];
|
|
1213
1155
|
const [value] = val.splice(removeIndex, 1);
|
|
1214
|
-
const state = this.state;
|
|
1215
|
-
if (Array.isArray(state)) {
|
|
1216
|
-
const sta = [...this.state];
|
|
1217
|
-
sta.splice(removeIndex, 1);
|
|
1218
|
-
this.state = sta;
|
|
1219
|
-
}
|
|
1220
1156
|
this.#children.set(children);
|
|
1221
1157
|
this.value = val;
|
|
1222
1158
|
return value;
|
|
@@ -1256,16 +1192,6 @@ class ArrayStore extends Store {
|
|
|
1256
1192
|
while (toIndex > val.length) { val.push(null); }
|
|
1257
1193
|
val.splice(toIndex, 0, ...values);
|
|
1258
1194
|
|
|
1259
|
-
const state = this.state;
|
|
1260
|
-
if (Array.isArray(state)) {
|
|
1261
|
-
const sta = [...state];
|
|
1262
|
-
const states = sta.splice(from, len);
|
|
1263
|
-
for (let i = states.length; i < len; i++) { states.push({}); }
|
|
1264
|
-
while (toIndex > sta.length) { sta.push({}); }
|
|
1265
|
-
sta.splice(toIndex, 0, ...states);
|
|
1266
|
-
|
|
1267
|
-
this.state = sta;
|
|
1268
|
-
}
|
|
1269
1195
|
this.#children.set(children);
|
|
1270
1196
|
this.value = val;
|
|
1271
1197
|
return len;
|
|
@@ -1293,15 +1219,6 @@ class ArrayStore extends Store {
|
|
|
1293
1219
|
const bValue = val[b];
|
|
1294
1220
|
val[b] = aValue;
|
|
1295
1221
|
val[a] = bValue;
|
|
1296
|
-
const state = this.state;
|
|
1297
|
-
if (Array.isArray(state)) {
|
|
1298
|
-
const sta = [...state];
|
|
1299
|
-
const aValue = sta[a];
|
|
1300
|
-
const bValue = sta[b];
|
|
1301
|
-
sta[b] = aValue;
|
|
1302
|
-
sta[a] = bValue;
|
|
1303
|
-
this.state = sta;
|
|
1304
|
-
}
|
|
1305
1222
|
this.#children.set(children);
|
|
1306
1223
|
this.value = val;
|
|
1307
1224
|
return true;
|
|
@@ -2450,7 +2367,6 @@ const bindable = {
|
|
|
2450
2367
|
kind: true,
|
|
2451
2368
|
|
|
2452
2369
|
value: true,
|
|
2453
|
-
state: true,
|
|
2454
2370
|
|
|
2455
2371
|
store: true,
|
|
2456
2372
|
parent: true,
|
|
@@ -2508,7 +2424,8 @@ function *toItem(val, key = '', sign = '$') {
|
|
|
2508
2424
|
yield [`${key}${sign}${k}`, {get: () => val[k]}];
|
|
2509
2425
|
}
|
|
2510
2426
|
yield [`${key}${sign}value`, {get: () => val.value, set: v => val.value = v}];
|
|
2511
|
-
|
|
2427
|
+
/** @deprecated */
|
|
2428
|
+
yield [`${key}${sign}state`, {get: () => null, set: v => {}}];
|
|
2512
2429
|
yield [`${key}${sign}reset`, {exec: () => val.reset()}];
|
|
2513
2430
|
// @ts-ignore
|
|
2514
2431
|
yield [`${key}${sign}validate`, {exec: v => val.validate(v ? [] : null)}];
|
|
@@ -2796,6 +2713,8 @@ class Environment {
|
|
|
2796
2713
|
const res = Object.fromEntries([...bindableSet].map(v => [
|
|
2797
2714
|
`$${v}`, cb => watch(() => store[v], cb, true)
|
|
2798
2715
|
]));
|
|
2716
|
+
/** @deprecated */
|
|
2717
|
+
res.$$state = cb => watch(() => null, cb, true);
|
|
2799
2718
|
return res;
|
|
2800
2719
|
}
|
|
2801
2720
|
/**
|
|
@@ -2812,13 +2731,15 @@ class Environment {
|
|
|
2812
2731
|
}
|
|
2813
2732
|
/** @type {Record<string, {get(): any; set?(v: any): void}> | void} */
|
|
2814
2733
|
const res = Object.fromEntries([...bindableSet].map(v => [
|
|
2815
|
-
`$${v}`, v === 'value'
|
|
2734
|
+
`$${v}`, v === 'value' ? {
|
|
2816
2735
|
get: () => store[v],
|
|
2817
2736
|
set: (s)=>{store[v] = s;}
|
|
2818
2737
|
} : {
|
|
2819
2738
|
get: () => store[v],
|
|
2820
2739
|
}
|
|
2821
2740
|
]));
|
|
2741
|
+
/** @deprecated */
|
|
2742
|
+
res.$$state = { get: () => null, set: () => {} };
|
|
2822
2743
|
return res;
|
|
2823
2744
|
}
|
|
2824
2745
|
/**
|
|
@@ -2833,7 +2754,8 @@ class Environment {
|
|
|
2833
2754
|
if (!store) { return; }
|
|
2834
2755
|
switch(type) {
|
|
2835
2756
|
case 'value': return v => {store.value = v; };
|
|
2836
|
-
|
|
2757
|
+
/** @deprecated */
|
|
2758
|
+
case 'state': return v => { };
|
|
2837
2759
|
}
|
|
2838
2760
|
}
|
|
2839
2761
|
/**
|
|
@@ -2851,7 +2773,8 @@ class Environment {
|
|
|
2851
2773
|
}
|
|
2852
2774
|
return {
|
|
2853
2775
|
'$value': v => {store.value = v; },
|
|
2854
|
-
|
|
2776
|
+
/** @deprecated */
|
|
2777
|
+
'$state': v => { },
|
|
2855
2778
|
'$input': v => {store.emit('input', v); },
|
|
2856
2779
|
'$change': v => {store.emit('change', v); },
|
|
2857
2780
|
'$click': v => {store.emit('click', v); },
|
|
@@ -4746,431 +4669,85 @@ function render(store, layouts, parent, { component, global, relate, enhancement
|
|
|
4746
4669
|
/** @import { Store } from '../Store/index.mjs' */
|
|
4747
4670
|
/** @import { StoreLayout } from '../types.mjs' */
|
|
4748
4671
|
|
|
4672
|
+
|
|
4749
4673
|
/**
|
|
4750
4674
|
*
|
|
4751
|
-
* @param {
|
|
4752
|
-
* @param {StoreLayout.Renderer} fieldRenderer
|
|
4753
|
-
* @param {StoreLayout.Field?} layout
|
|
4754
|
-
* @param {object} option
|
|
4755
|
-
* @param {(string | StoreLayout.Action[])[]} option.columns
|
|
4756
|
-
* @param {() => void} option.remove
|
|
4757
|
-
* @param {() => void} option.dragenter
|
|
4758
|
-
* @param {() => void} option.dragstart
|
|
4759
|
-
* @param {() => void} option.dragend
|
|
4760
|
-
* @param {{get(): boolean}} option.deletable
|
|
4761
|
-
* @param {StoreLayout.Options?} options
|
|
4762
|
-
|
|
4763
|
-
* @returns {[HTMLTableSectionElement, () => void]}
|
|
4675
|
+
* @param {string} field
|
|
4764
4676
|
*/
|
|
4765
|
-
function
|
|
4766
|
-
columns,
|
|
4767
|
-
remove, dragenter, dragstart, dragend, deletable
|
|
4768
|
-
}, options) {
|
|
4769
|
-
const root = document.createElement('tbody');
|
|
4770
|
-
root.addEventListener('dragenter', () => {
|
|
4771
|
-
dragenter();
|
|
4772
|
-
});
|
|
4773
|
-
root.addEventListener('dragstart', (event) => {
|
|
4774
|
-
if (event.target !== event.currentTarget) { return; }
|
|
4775
|
-
dragstart();
|
|
4776
|
-
});
|
|
4777
|
-
root.addEventListener('dragend', dragend);
|
|
4778
|
-
const head = root.appendChild(document.createElement('tr'));
|
|
4779
|
-
|
|
4780
|
-
/** @type {(() => void)[]} */
|
|
4781
|
-
const destroyList = [];
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
let trigger = () => { };
|
|
4785
|
-
/** @type {HTMLButtonElement[]} */
|
|
4786
|
-
const triggerList = [];
|
|
4787
|
-
if (columns.find(v => Array.isArray(v) && v.includes('trigger'))) {
|
|
4788
|
-
const body = root.appendChild(document.createElement('tr'));
|
|
4789
|
-
const main = body.appendChild(document.createElement('td'));
|
|
4790
|
-
main.colSpan = columns.length;
|
|
4791
|
-
|
|
4792
|
-
const [form, destroy] = Form(store, fieldRenderer, layout, options);
|
|
4793
|
-
main.appendChild(form);
|
|
4794
|
-
destroyList.push(destroy);
|
|
4795
|
-
body.hidden = true;
|
|
4796
|
-
trigger = function click() {
|
|
4797
|
-
if (body.hidden) {
|
|
4798
|
-
body.hidden = false;
|
|
4799
|
-
for (const ext of triggerList) {
|
|
4800
|
-
ext.classList.remove('NeeloongForm-table-line-open');
|
|
4801
|
-
ext.classList.add('NeeloongForm-table-line-close');
|
|
4802
|
-
}
|
|
4803
|
-
} else {
|
|
4804
|
-
body.hidden = true;
|
|
4805
|
-
for (const ext of triggerList) {
|
|
4806
|
-
ext.classList.remove('NeeloongForm-table-line-close');
|
|
4807
|
-
ext.classList.add('NeeloongForm-table-line-open');
|
|
4808
|
-
}
|
|
4809
|
-
}
|
|
4810
|
-
};
|
|
4811
|
-
|
|
4812
|
-
}
|
|
4677
|
+
function createFieldFilter(field) {
|
|
4813
4678
|
|
|
4814
4679
|
/**
|
|
4815
4680
|
*
|
|
4816
|
-
* @param {
|
|
4681
|
+
* @param {StoreLayout.Item} v
|
|
4682
|
+
* @returns {v is StoreLayout.Field}
|
|
4817
4683
|
*/
|
|
4818
|
-
function pointerdown({ pointerId }) {
|
|
4819
|
-
root.draggable = true;
|
|
4820
|
-
/** @param {PointerEvent} event */
|
|
4821
|
-
function pointerup(event) {
|
|
4822
|
-
if (event.pointerId !== pointerId) { return; }
|
|
4823
|
-
if (!root) { return; }
|
|
4824
|
-
root.draggable = false;
|
|
4825
|
-
window.removeEventListener('pointerup', pointerup, { capture: true });
|
|
4826
|
-
window.removeEventListener('pointercancel', pointerup, { capture: true });
|
|
4827
|
-
}
|
|
4828
|
-
window.addEventListener('pointerup', pointerup, { capture: true });
|
|
4829
|
-
window.addEventListener('pointercancel', pointerup, { capture: true });
|
|
4830
|
-
|
|
4831
|
-
}
|
|
4832
4684
|
|
|
4833
|
-
|
|
4834
|
-
if (
|
|
4835
|
-
|
|
4836
|
-
const child = store.child(name);
|
|
4837
|
-
if (!child) { continue; }
|
|
4838
|
-
const [el, destroy] = FormField(child, fieldRenderer, null, options, true);
|
|
4839
|
-
destroyList.push(destroy);
|
|
4840
|
-
td.appendChild(el);
|
|
4841
|
-
continue;
|
|
4842
|
-
}
|
|
4843
|
-
const handle = head.appendChild(document.createElement('th'));
|
|
4844
|
-
handle.classList.add('NeeloongForm-table-line-handle');
|
|
4845
|
-
for (const k of name) {
|
|
4846
|
-
switch (k) {
|
|
4847
|
-
case 'trigger': {
|
|
4848
|
-
const ext = handle.appendChild(document.createElement('button'));
|
|
4849
|
-
ext.classList.add('NeeloongForm-table-line-open');
|
|
4850
|
-
triggerList.push(ext);
|
|
4851
|
-
ext.addEventListener('click', trigger);
|
|
4852
|
-
continue;
|
|
4853
|
-
}
|
|
4854
|
-
case 'move': {
|
|
4855
|
-
if (!options?.editable) { continue; }
|
|
4856
|
-
const move = handle.appendChild(document.createElement('button'));
|
|
4857
|
-
move.classList.add('NeeloongForm-table-move');
|
|
4858
|
-
move.addEventListener('pointerdown', pointerdown);
|
|
4859
|
-
destroyList.push(watch(() => store.readonly || store.disabled, disabled => {
|
|
4860
|
-
move.disabled = disabled;
|
|
4861
|
-
}, true));
|
|
4862
|
-
continue;
|
|
4863
|
-
}
|
|
4864
|
-
case 'remove': {
|
|
4865
|
-
if (!options?.editable) { continue; }
|
|
4866
|
-
const del = handle.appendChild(document.createElement('button'));
|
|
4867
|
-
del.classList.add('NeeloongForm-table-remove');
|
|
4868
|
-
del.addEventListener('click', remove);
|
|
4869
|
-
destroyList.push(watch(() => !deletable.get() || store.readonly || store.disabled, disabled => {
|
|
4870
|
-
del.disabled = disabled;
|
|
4871
|
-
}, true));
|
|
4872
|
-
continue;
|
|
4873
|
-
}
|
|
4874
|
-
case 'serial': {
|
|
4875
|
-
const serial = handle.appendChild(document.createElement('span'));
|
|
4876
|
-
serial.classList.add('NeeloongForm-table-serial');
|
|
4877
|
-
continue;
|
|
4878
|
-
}
|
|
4879
|
-
}
|
|
4880
|
-
}
|
|
4881
|
-
}
|
|
4685
|
+
return function (v) {
|
|
4686
|
+
if (v.type && v.type !== 'field') { return false; }
|
|
4687
|
+
return v.field === field;
|
|
4882
4688
|
|
|
4883
|
-
|
|
4884
|
-
for (const destroy of destroyList) {
|
|
4885
|
-
destroy();
|
|
4886
|
-
}
|
|
4887
|
-
}];
|
|
4689
|
+
};
|
|
4888
4690
|
}
|
|
4889
|
-
|
|
4890
|
-
/** @import { Store, ArrayStore } from '../Store/index.mjs' */
|
|
4891
|
-
/** @import { StoreLayout } from '../types.mjs' */
|
|
4892
|
-
|
|
4893
4691
|
/**
|
|
4894
4692
|
*
|
|
4895
|
-
* @param {
|
|
4896
|
-
* @param {
|
|
4897
|
-
* @param {
|
|
4898
|
-
* @param {
|
|
4899
|
-
* @param {
|
|
4693
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
4694
|
+
* @param {Store} store
|
|
4695
|
+
* @param {Node} node
|
|
4696
|
+
* @param {StoreLayout.Options?} options
|
|
4697
|
+
* @param {StoreLayout?} [layout]
|
|
4698
|
+
* @param {Node} [anchor]
|
|
4699
|
+
* @param {(child?: Store<any, any> | undefined) => void} [dragenter]
|
|
4900
4700
|
*/
|
|
4901
|
-
function
|
|
4902
|
-
const tr = parent.appendChild(document.createElement('tr'));
|
|
4701
|
+
function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragenter) {
|
|
4903
4702
|
/** @type {(() => void)[]} */
|
|
4904
4703
|
const destroyList = [];
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4704
|
+
if (node instanceof Element) {
|
|
4705
|
+
const tagName = node.tagName.toLowerCase();
|
|
4706
|
+
if (!node.parentNode) { return () => { }; }
|
|
4707
|
+
if (tagName === 'nl-form-field') {
|
|
4708
|
+
const field = node.getAttribute('name') || '';
|
|
4709
|
+
const mode = node.getAttribute('mode') || '';
|
|
4710
|
+
const fieldStore = field ? store.child(field) : store;
|
|
4711
|
+
const fieldLayout = field ? layout?.fields?.find(createFieldFilter(field)) || null : null;
|
|
4712
|
+
if (!fieldStore) { return () => { }; }
|
|
4713
|
+
switch (mode) {
|
|
4714
|
+
case 'grid': {
|
|
4715
|
+
const [el, destroy] = Form(store, fieldRenderer, fieldLayout, options);
|
|
4716
|
+
node.replaceWith(el);
|
|
4717
|
+
return destroy;
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
const component = fieldStore.component;
|
|
4721
|
+
if (component) {
|
|
4722
|
+
const res = fieldRenderer(fieldStore, component, options);
|
|
4723
|
+
if (res) {
|
|
4724
|
+
const [el, destroy] = res;
|
|
4725
|
+
node.replaceWith(el);
|
|
4726
|
+
return destroy;
|
|
4911
4727
|
}
|
|
4912
|
-
td.innerText = label;
|
|
4913
|
-
continue;
|
|
4914
|
-
}
|
|
4915
|
-
const th = tr.appendChild(document.createElement('th'));
|
|
4916
|
-
if (!editable) { continue; }
|
|
4917
|
-
for (const it of col) {
|
|
4918
|
-
switch (it) {
|
|
4919
|
-
case 'add':
|
|
4920
|
-
const button = th.appendChild(document.createElement('button'));
|
|
4921
|
-
button.addEventListener('click', add);
|
|
4922
|
-
button.classList.add('NeeloongForm-table-add');
|
|
4923
|
-
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
4924
|
-
continue;
|
|
4925
4728
|
}
|
|
4729
|
+
const value = node.getAttribute('placeholder') || '';
|
|
4730
|
+
node.replaceWith(document.createTextNode(value));
|
|
4731
|
+
return () => { };
|
|
4926
4732
|
}
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4733
|
+
if (tagName === 'nl-form-button') {
|
|
4734
|
+
const button = document.createElement('button');
|
|
4735
|
+
button.className = 'NeeloongForm-item-button';
|
|
4736
|
+
const click = node.getAttribute('click') || '';
|
|
4737
|
+
const call = options?.call;
|
|
4738
|
+
if (click && typeof call === 'function') {
|
|
4739
|
+
button.addEventListener('click', e => call(click, e, store, options));
|
|
4740
|
+
}
|
|
4741
|
+
for (const n of [...node.childNodes]) {
|
|
4742
|
+
button.appendChild(n);
|
|
4743
|
+
}
|
|
4744
|
+
node.replaceWith(button);
|
|
4745
|
+
return () => { };
|
|
4931
4746
|
}
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
* @param {ArrayStore} store
|
|
4937
|
-
* @param {StoreLayout.Renderer} fieldRenderer
|
|
4938
|
-
* @param {StoreLayout.Field?} layout
|
|
4939
|
-
* @param {StoreLayout.Options?} options
|
|
4940
|
-
* @returns {[HTMLTableElement, () => void]}
|
|
4941
|
-
*/
|
|
4942
|
-
function Table(store, fieldRenderer, layout, options) {
|
|
4943
|
-
const headerColumns = layout?.columns;
|
|
4944
|
-
const fieldList = Object.entries(store.type || {})
|
|
4945
|
-
.filter(([k, v]) => typeof v?.type !== 'object')
|
|
4946
|
-
.map(([field, {width, label}]) => ({field, width, label}));
|
|
4947
|
-
/** @type {({ field: string; width: any; label: any; } | StoreLayout.Action[])[]} */
|
|
4948
|
-
let columns = [];
|
|
4949
|
-
if (Array.isArray(headerColumns)) {
|
|
4950
|
-
const map = new Map(fieldList.map(v => [v.field, v]));
|
|
4951
|
-
columns = headerColumns.map(v => {
|
|
4952
|
-
if (typeof v === 'number') { return [] }
|
|
4953
|
-
if (typeof v === 'string') { return map.get(v) || [] }
|
|
4954
|
-
if (!Array.isArray(v)) { return []; }
|
|
4955
|
-
/** @type {Set<StoreLayout.Action>} */
|
|
4956
|
-
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial']);
|
|
4957
|
-
return v.filter(v => options.delete(v));
|
|
4958
|
-
}).filter(v => !Array.isArray(v) || v.length);
|
|
4959
|
-
}
|
|
4960
|
-
if (!columns.length) {
|
|
4961
|
-
columns = [['add', 'trigger', 'move', 'remove', 'serial']];
|
|
4962
|
-
}
|
|
4963
|
-
if (!columns.find(v => !Array.isArray(v))) {
|
|
4964
|
-
columns.push(...fieldList.slice(0, 3));
|
|
4965
|
-
}
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
const table = document.createElement('table');
|
|
4970
|
-
table.className = 'NeeloongForm-table';
|
|
4971
|
-
const thead = table.appendChild(document.createElement('thead'));
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
const addable = new Signal.Computed(() => store.addable);
|
|
4976
|
-
const deletable = { get: () => Boolean(options?.editable) };
|
|
4977
|
-
function add() {
|
|
4978
|
-
const data = {};
|
|
4979
|
-
store.add(data);
|
|
4980
|
-
|
|
4981
|
-
}
|
|
4982
|
-
/**
|
|
4983
|
-
*
|
|
4984
|
-
* @param {Store} child
|
|
4985
|
-
*/
|
|
4986
|
-
function remove(child) {
|
|
4987
|
-
store.remove(Number(child.index));
|
|
4988
|
-
}
|
|
4989
|
-
let dragRow = -1;
|
|
4990
|
-
/**
|
|
4991
|
-
*
|
|
4992
|
-
* @param {Store} [child]
|
|
4993
|
-
*/
|
|
4994
|
-
function dragenter(child) {
|
|
4995
|
-
if (dragRow < 0) { return; }
|
|
4996
|
-
const index = child ? Number(child.index) : store.children.length;
|
|
4997
|
-
if (index < 0 || dragRow < 0 || dragRow === index) { return; }
|
|
4998
|
-
if (store.move(dragRow, index)) {
|
|
4999
|
-
dragRow = index;
|
|
5000
|
-
}
|
|
5001
|
-
}
|
|
5002
|
-
/**
|
|
5003
|
-
*
|
|
5004
|
-
* @param {Store} child
|
|
5005
|
-
*/
|
|
5006
|
-
function dragstart(child) {
|
|
5007
|
-
dragRow = Number(child.index);
|
|
5008
|
-
|
|
5009
|
-
}
|
|
5010
|
-
function dragend() {
|
|
5011
|
-
dragRow = -1;
|
|
5012
|
-
|
|
5013
|
-
}
|
|
5014
|
-
/** @type {(() => void)[]} */
|
|
5015
|
-
const destroyList = [];
|
|
5016
|
-
destroyList.push(renderHead(thead, columns, add, addable, Boolean(options?.editable)));
|
|
5017
|
-
switch (layout?.tableFoot) {
|
|
5018
|
-
default:
|
|
5019
|
-
case 'header': {
|
|
5020
|
-
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5021
|
-
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5022
|
-
destroyList.push(renderHead(tfoot, columns, add, addable, Boolean(options?.editable)));
|
|
5023
|
-
break;
|
|
5024
|
-
}
|
|
5025
|
-
case 'add': {
|
|
5026
|
-
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5027
|
-
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5028
|
-
const tr = tfoot.appendChild(document.createElement('tr'));
|
|
5029
|
-
const th = tr.appendChild(document.createElement('th'));
|
|
5030
|
-
th.colSpan = columns.length;
|
|
5031
|
-
const button = th.appendChild(document.createElement('button'));
|
|
5032
|
-
button.addEventListener('click', add);
|
|
5033
|
-
button.classList.add('NeeloongForm-table-foot-add');
|
|
5034
|
-
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
5035
|
-
break;
|
|
5036
|
-
}
|
|
5037
|
-
case 'none':
|
|
5038
|
-
}
|
|
5039
|
-
const start = thead;
|
|
5040
|
-
/** @type {Map<Store, [HTMLTableSectionElement, () => void]>} */
|
|
5041
|
-
let seMap = new Map();
|
|
5042
|
-
/** @param {Map<Store, [tbody: HTMLTableSectionElement, destroy: () => void]>} map */
|
|
5043
|
-
function destroyMap(map) {
|
|
5044
|
-
for (const [el, destroy] of map.values()) {
|
|
5045
|
-
destroy();
|
|
5046
|
-
el.remove();
|
|
5047
|
-
}
|
|
5048
|
-
|
|
5049
|
-
}
|
|
5050
|
-
const columnNames = columns.map((v) => Array.isArray(v) ? v : v.field);
|
|
5051
|
-
const childrenResult = watch(() => store.children, function render(children) {
|
|
5052
|
-
let nextNode = thead.nextSibling;
|
|
5053
|
-
const oldSeMap = seMap;
|
|
5054
|
-
seMap = new Map();
|
|
5055
|
-
for (let child of children) {
|
|
5056
|
-
const old = oldSeMap.get(child);
|
|
5057
|
-
if (!old) {
|
|
5058
|
-
const [el, destroy] = Line(child, fieldRenderer, layout, {
|
|
5059
|
-
columns: columnNames,
|
|
5060
|
-
remove: remove.bind(null, child),
|
|
5061
|
-
dragenter: dragenter.bind(null, child),
|
|
5062
|
-
dragstart: dragstart.bind(null, child),
|
|
5063
|
-
dragend,
|
|
5064
|
-
deletable,
|
|
5065
|
-
}, options);
|
|
5066
|
-
table.insertBefore(el, nextNode);
|
|
5067
|
-
seMap.set(child, [el, destroy]);
|
|
5068
|
-
continue;
|
|
5069
|
-
}
|
|
5070
|
-
oldSeMap.delete(child);
|
|
5071
|
-
seMap.set(child, old);
|
|
5072
|
-
if (nextNode === old[0]) {
|
|
5073
|
-
nextNode = nextNode.nextSibling;
|
|
5074
|
-
continue;
|
|
5075
|
-
}
|
|
5076
|
-
table.insertBefore(old[0], nextNode);
|
|
5077
|
-
}
|
|
5078
|
-
destroyMap(oldSeMap);
|
|
5079
|
-
}, true);
|
|
5080
|
-
|
|
5081
|
-
return [table, () => {
|
|
5082
|
-
start.remove();
|
|
5083
|
-
thead.remove();
|
|
5084
|
-
destroyMap(seMap);
|
|
5085
|
-
childrenResult();
|
|
5086
|
-
for (const destroy of destroyList) {
|
|
5087
|
-
destroy();
|
|
5088
|
-
}
|
|
5089
|
-
}];
|
|
5090
|
-
}
|
|
5091
|
-
|
|
5092
|
-
/** @import { Store } from '../Store/index.mjs' */
|
|
5093
|
-
/** @import { StoreLayout } from '../types.mjs' */
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
/**
|
|
5097
|
-
*
|
|
5098
|
-
* @param {string} field
|
|
5099
|
-
*/
|
|
5100
|
-
function createFieldFilter(field) {
|
|
5101
|
-
|
|
5102
|
-
/**
|
|
5103
|
-
*
|
|
5104
|
-
* @param {StoreLayout.Item} v
|
|
5105
|
-
* @returns {v is StoreLayout.Field}
|
|
5106
|
-
*/
|
|
5107
|
-
|
|
5108
|
-
return function (v) {
|
|
5109
|
-
if (v.type && v.type !== 'field') { return false; }
|
|
5110
|
-
return v.field === field;
|
|
5111
|
-
|
|
5112
|
-
};
|
|
5113
|
-
}
|
|
5114
|
-
/**
|
|
5115
|
-
*
|
|
5116
|
-
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5117
|
-
* @param {Store} store
|
|
5118
|
-
* @param {Node} node
|
|
5119
|
-
* @param {StoreLayout.Options?} options
|
|
5120
|
-
* @param {StoreLayout?} [layout]
|
|
5121
|
-
* @param {Node} [anchor]
|
|
5122
|
-
* @param {(child?: Store<any, any> | undefined) => void} [dragenter]
|
|
5123
|
-
*/
|
|
5124
|
-
function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragenter) {
|
|
5125
|
-
/** @type {(() => void)[]} */
|
|
5126
|
-
const destroyList = [];
|
|
5127
|
-
if (node instanceof Element) {
|
|
5128
|
-
const tagName = node.tagName.toLowerCase();
|
|
5129
|
-
if (!node.parentNode) { return () => { }; }
|
|
5130
|
-
if (tagName === 'nl-form-field') {
|
|
5131
|
-
const field = node.getAttribute('name') || '';
|
|
5132
|
-
const mode = node.getAttribute('mode') || '';
|
|
5133
|
-
const fieldStore = field ? store.child(field) : store;
|
|
5134
|
-
const fieldLayout = field ? layout?.fields?.find(createFieldFilter(field)) || null : null;
|
|
5135
|
-
if (!fieldStore) { return () => { }; }
|
|
5136
|
-
switch (mode) {
|
|
5137
|
-
case 'grid': {
|
|
5138
|
-
const [el, destroy] = Form(store, fieldRenderer, fieldLayout, options);
|
|
5139
|
-
node.replaceWith(el);
|
|
5140
|
-
return destroy;
|
|
5141
|
-
}
|
|
5142
|
-
}
|
|
5143
|
-
const component = fieldStore.component;
|
|
5144
|
-
if (component) {
|
|
5145
|
-
const res = fieldRenderer(fieldStore, component, options);
|
|
5146
|
-
if (res) {
|
|
5147
|
-
const [el, destroy] = res;
|
|
5148
|
-
node.replaceWith(el);
|
|
5149
|
-
return destroy;
|
|
5150
|
-
}
|
|
5151
|
-
}
|
|
5152
|
-
const value = node.getAttribute('placeholder') || '';
|
|
5153
|
-
node.replaceWith(document.createTextNode(value));
|
|
5154
|
-
return () => { };
|
|
5155
|
-
}
|
|
5156
|
-
if (tagName === 'nl-form-button') {
|
|
5157
|
-
const button = document.createElement('button');
|
|
5158
|
-
button.className = 'NeeloongForm-item-button';
|
|
5159
|
-
const click = node.getAttribute('click') || '';
|
|
5160
|
-
const call = options?.call;
|
|
5161
|
-
if (click && typeof call === 'function') {
|
|
5162
|
-
button.addEventListener('click', e => call(click, e, store, options));
|
|
5163
|
-
}
|
|
5164
|
-
for (const n of [...node.childNodes]) {
|
|
5165
|
-
button.appendChild(n);
|
|
5166
|
-
}
|
|
5167
|
-
node.replaceWith(button);
|
|
5168
|
-
return () => { };
|
|
5169
|
-
}
|
|
5170
|
-
const name = node.getAttribute('nl-form-field');
|
|
5171
|
-
if (name) {
|
|
5172
|
-
const array = name.endsWith('[]');
|
|
5173
|
-
const field = array ? name.slice(0, name.length - 2) : name;
|
|
4747
|
+
const name = node.getAttribute('nl-form-field');
|
|
4748
|
+
if (name) {
|
|
4749
|
+
const array = name.endsWith('[]');
|
|
4750
|
+
const field = array ? name.slice(0, name.length - 2) : name;
|
|
5174
4751
|
|
|
5175
4752
|
const fieldStore = field ? store.child(field) : store;
|
|
5176
4753
|
if (!fieldStore) {
|
|
@@ -5306,34 +4883,406 @@ function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragent
|
|
|
5306
4883
|
}
|
|
5307
4884
|
}
|
|
5308
4885
|
}
|
|
5309
|
-
if (node instanceof Element || node instanceof DocumentFragment) {
|
|
5310
|
-
for (const n of [...node.children]) {
|
|
5311
|
-
destroyList.push(renderHtml(store, fieldRenderer, n, options, layout, anchor));
|
|
4886
|
+
if (node instanceof Element || node instanceof DocumentFragment) {
|
|
4887
|
+
for (const n of [...node.children]) {
|
|
4888
|
+
destroyList.push(renderHtml(store, fieldRenderer, n, options, layout, anchor));
|
|
4889
|
+
}
|
|
4890
|
+
}
|
|
4891
|
+
return () => {
|
|
4892
|
+
for (const destroy of destroyList) {
|
|
4893
|
+
destroy();
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
/**
|
|
4900
|
+
*
|
|
4901
|
+
* @param {string | ParentNode | null} [html]
|
|
4902
|
+
* @returns {ParentNode}
|
|
4903
|
+
*/
|
|
4904
|
+
function getHtmlContent(html) {
|
|
4905
|
+
if (!html) {
|
|
4906
|
+
return document.createElement('template').content;
|
|
4907
|
+
}
|
|
4908
|
+
if (typeof html !== 'string') {
|
|
4909
|
+
return /** @type {ParentNode} */(html.cloneNode(true));
|
|
4910
|
+
}
|
|
4911
|
+
const template = document.createElement('template');
|
|
4912
|
+
template.innerHTML = html;
|
|
4913
|
+
return template.content;
|
|
4914
|
+
}
|
|
4915
|
+
|
|
4916
|
+
/**
|
|
4917
|
+
*
|
|
4918
|
+
* @param {Store<any, any>} store
|
|
4919
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
4920
|
+
* @param {StoreLayout.Field?} layout
|
|
4921
|
+
* @param {StoreLayout.Options?} options
|
|
4922
|
+
* @returns {[ParentNode, () => void]}
|
|
4923
|
+
*/
|
|
4924
|
+
function FormFieldInline(store, fieldRenderer, layout, options) {
|
|
4925
|
+
const { component } = store;
|
|
4926
|
+
return component
|
|
4927
|
+
&& fieldRenderer(store, component, options)
|
|
4928
|
+
|| [document.createElement('div'), () => { }];
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4931
|
+
/** @import { Store } from '../Store/index.mjs' */
|
|
4932
|
+
/** @import { StoreLayout } from '../types.mjs' */
|
|
4933
|
+
|
|
4934
|
+
/**
|
|
4935
|
+
*
|
|
4936
|
+
* @param {Store<any, any>} store
|
|
4937
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
4938
|
+
* @param {StoreLayout.Field?} layout
|
|
4939
|
+
* @param {object} option
|
|
4940
|
+
* @param {StoreLayout.Column[]} option.columns
|
|
4941
|
+
* @param {() => void} option.remove
|
|
4942
|
+
* @param {() => void} option.dragenter
|
|
4943
|
+
* @param {() => void} option.dragstart
|
|
4944
|
+
* @param {() => void} option.dragend
|
|
4945
|
+
* @param {{get(): boolean}} option.deletable
|
|
4946
|
+
* @param {StoreLayout.Options?} options
|
|
4947
|
+
|
|
4948
|
+
* @returns {[HTMLTableSectionElement, () => void]}
|
|
4949
|
+
*/
|
|
4950
|
+
function Line(store, fieldRenderer, layout, {
|
|
4951
|
+
columns,
|
|
4952
|
+
remove, dragenter, dragstart, dragend, deletable
|
|
4953
|
+
}, options) {
|
|
4954
|
+
const root = document.createElement('tbody');
|
|
4955
|
+
root.addEventListener('dragenter', () => {
|
|
4956
|
+
dragenter();
|
|
4957
|
+
});
|
|
4958
|
+
root.addEventListener('dragstart', (event) => {
|
|
4959
|
+
if (event.target !== event.currentTarget) { return; }
|
|
4960
|
+
dragstart();
|
|
4961
|
+
});
|
|
4962
|
+
root.addEventListener('dragend', dragend);
|
|
4963
|
+
const head = root.appendChild(document.createElement('tr'));
|
|
4964
|
+
|
|
4965
|
+
/** @type {(() => void)[]} */
|
|
4966
|
+
const destroyList = [];
|
|
4967
|
+
|
|
4968
|
+
|
|
4969
|
+
let trigger = () => { };
|
|
4970
|
+
/** @type {HTMLButtonElement[]} */
|
|
4971
|
+
const triggerList = [];
|
|
4972
|
+
if (columns.find(v => v.actions?.includes('trigger'))) {
|
|
4973
|
+
const body = root.appendChild(document.createElement('tr'));
|
|
4974
|
+
const main = body.appendChild(document.createElement('td'));
|
|
4975
|
+
main.colSpan = columns.length;
|
|
4976
|
+
|
|
4977
|
+
const [form, destroy] = Form(store, fieldRenderer, layout, options);
|
|
4978
|
+
main.appendChild(form);
|
|
4979
|
+
destroyList.push(destroy);
|
|
4980
|
+
body.hidden = true;
|
|
4981
|
+
trigger = function click() {
|
|
4982
|
+
if (body.hidden) {
|
|
4983
|
+
body.hidden = false;
|
|
4984
|
+
for (const ext of triggerList) {
|
|
4985
|
+
ext.classList.remove('NeeloongForm-table-line-open');
|
|
4986
|
+
ext.classList.add('NeeloongForm-table-line-close');
|
|
4987
|
+
}
|
|
4988
|
+
} else {
|
|
4989
|
+
body.hidden = true;
|
|
4990
|
+
for (const ext of triggerList) {
|
|
4991
|
+
ext.classList.remove('NeeloongForm-table-line-close');
|
|
4992
|
+
ext.classList.add('NeeloongForm-table-line-open');
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
};
|
|
4996
|
+
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
/**
|
|
5000
|
+
*
|
|
5001
|
+
* @param {PointerEvent} event
|
|
5002
|
+
*/
|
|
5003
|
+
function pointerdown({ pointerId }) {
|
|
5004
|
+
root.draggable = true;
|
|
5005
|
+
/** @param {PointerEvent} event */
|
|
5006
|
+
function pointerup(event) {
|
|
5007
|
+
if (event.pointerId !== pointerId) { return; }
|
|
5008
|
+
if (!root) { return; }
|
|
5009
|
+
root.draggable = false;
|
|
5010
|
+
window.removeEventListener('pointerup', pointerup, { capture: true });
|
|
5011
|
+
window.removeEventListener('pointercancel', pointerup, { capture: true });
|
|
5012
|
+
}
|
|
5013
|
+
window.addEventListener('pointerup', pointerup, { capture: true });
|
|
5014
|
+
window.addEventListener('pointercancel', pointerup, { capture: true });
|
|
5015
|
+
|
|
5016
|
+
}
|
|
5017
|
+
|
|
5018
|
+
for (const name of columns) {
|
|
5019
|
+
const { actions, field, pattern } = name;
|
|
5020
|
+
if (!actions?.length) {
|
|
5021
|
+
const td = head.appendChild(document.createElement('td'));
|
|
5022
|
+
const child = field && store.child(field);
|
|
5023
|
+
if (child) {
|
|
5024
|
+
const [el, destroy] = FormFieldInline(child, fieldRenderer, null, options);
|
|
5025
|
+
destroyList.push(destroy);
|
|
5026
|
+
td.appendChild(el);
|
|
5027
|
+
}
|
|
5028
|
+
continue;
|
|
5029
|
+
}
|
|
5030
|
+
const handle = head.appendChild(document.createElement('th'));
|
|
5031
|
+
handle.classList.add('NeeloongForm-table-line-handle');
|
|
5032
|
+
for (const k of actions) {
|
|
5033
|
+
switch (k) {
|
|
5034
|
+
case 'trigger': {
|
|
5035
|
+
const ext = handle.appendChild(document.createElement('button'));
|
|
5036
|
+
ext.classList.add('NeeloongForm-table-line-open');
|
|
5037
|
+
triggerList.push(ext);
|
|
5038
|
+
ext.addEventListener('click', trigger);
|
|
5039
|
+
continue;
|
|
5040
|
+
}
|
|
5041
|
+
case 'move': {
|
|
5042
|
+
if (!options?.editable) { continue; }
|
|
5043
|
+
const move = handle.appendChild(document.createElement('button'));
|
|
5044
|
+
move.classList.add('NeeloongForm-table-move');
|
|
5045
|
+
move.addEventListener('pointerdown', pointerdown);
|
|
5046
|
+
destroyList.push(watch(() => store.readonly || store.disabled, disabled => {
|
|
5047
|
+
move.disabled = disabled;
|
|
5048
|
+
}, true));
|
|
5049
|
+
continue;
|
|
5050
|
+
}
|
|
5051
|
+
case 'remove': {
|
|
5052
|
+
if (!options?.editable) { continue; }
|
|
5053
|
+
const del = handle.appendChild(document.createElement('button'));
|
|
5054
|
+
del.classList.add('NeeloongForm-table-remove');
|
|
5055
|
+
del.addEventListener('click', remove);
|
|
5056
|
+
destroyList.push(watch(() => !deletable.get() || store.readonly || store.disabled, disabled => {
|
|
5057
|
+
del.disabled = disabled;
|
|
5058
|
+
}, true));
|
|
5059
|
+
continue;
|
|
5060
|
+
}
|
|
5061
|
+
case 'serial': {
|
|
5062
|
+
const serial = handle.appendChild(document.createElement('span'));
|
|
5063
|
+
serial.classList.add('NeeloongForm-table-serial');
|
|
5064
|
+
continue;
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
|
|
5070
|
+
return [root, () => {
|
|
5071
|
+
for (const destroy of destroyList) {
|
|
5072
|
+
destroy();
|
|
5073
|
+
}
|
|
5074
|
+
}];
|
|
5075
|
+
}
|
|
5076
|
+
|
|
5077
|
+
/** @import { Store, ArrayStore } from '../Store/index.mjs' */
|
|
5078
|
+
/** @import { StoreLayout } from '../types.mjs' */
|
|
5079
|
+
|
|
5080
|
+
/**
|
|
5081
|
+
*
|
|
5082
|
+
* @param {HTMLElement} parent
|
|
5083
|
+
* @param {StoreLayout.Column[]} columns
|
|
5084
|
+
* @param {() => any} add
|
|
5085
|
+
* @param {{get(): boolean}} addable
|
|
5086
|
+
* @param {boolean?} [editable]
|
|
5087
|
+
*/
|
|
5088
|
+
function renderHead(parent, columns, add, addable, editable) {
|
|
5089
|
+
const tr = parent.appendChild(document.createElement('tr'));
|
|
5090
|
+
/** @type {(() => void)[]} */
|
|
5091
|
+
const destroyList = [];
|
|
5092
|
+
for (const { action, actions, width, label } of columns) {
|
|
5093
|
+
const th = tr.appendChild(document.createElement('th'));
|
|
5094
|
+
if (width) { th.setAttribute('width', `${width}`); }
|
|
5095
|
+
if (![action, actions].flat().includes('add')) {
|
|
5096
|
+
th.innerText = label || '';
|
|
5097
|
+
continue;
|
|
5312
5098
|
}
|
|
5099
|
+
if (!editable) { continue; }
|
|
5100
|
+
const button = th.appendChild(document.createElement('button'));
|
|
5101
|
+
button.addEventListener('click', add);
|
|
5102
|
+
button.classList.add('NeeloongForm-table-add');
|
|
5103
|
+
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
5313
5104
|
}
|
|
5314
5105
|
return () => {
|
|
5315
5106
|
for (const destroy of destroyList) {
|
|
5316
5107
|
destroy();
|
|
5317
5108
|
}
|
|
5318
5109
|
};
|
|
5319
|
-
|
|
5320
5110
|
}
|
|
5321
|
-
|
|
5322
5111
|
/**
|
|
5323
|
-
*
|
|
5324
|
-
* @param {
|
|
5325
|
-
* @
|
|
5112
|
+
*
|
|
5113
|
+
* @param {ArrayStore} store
|
|
5114
|
+
* @param {StoreLayout.Renderer} fieldRenderer
|
|
5115
|
+
* @param {StoreLayout.Field?} layout
|
|
5116
|
+
* @param {StoreLayout.Options?} options
|
|
5117
|
+
* @returns {[HTMLTableElement, () => void]}
|
|
5326
5118
|
*/
|
|
5327
|
-
function
|
|
5328
|
-
|
|
5329
|
-
|
|
5119
|
+
function Table(store, fieldRenderer, layout, options) {
|
|
5120
|
+
const headerColumns = layout?.columns;
|
|
5121
|
+
const fieldList = Object.entries(store.type || {})
|
|
5122
|
+
.filter(([k, v]) => typeof v?.type !== 'object')
|
|
5123
|
+
.map(([field, { width, label }]) => ({ field, width, label }));
|
|
5124
|
+
/** @type {StoreLayout.Column[]} */
|
|
5125
|
+
let columns = [];
|
|
5126
|
+
if (Array.isArray(headerColumns)) {
|
|
5127
|
+
const map = new Map(fieldList.map(v => [v.field, v]));
|
|
5128
|
+
|
|
5129
|
+
/** @type {(StoreLayout.Column | null)[]} */
|
|
5130
|
+
const allColumns = headerColumns.map(v => {
|
|
5131
|
+
if (!v) { return null; }
|
|
5132
|
+
if (typeof v === 'number') { return { placeholder: v }; }
|
|
5133
|
+
if (typeof v === 'string') { return map.get(v) || null; }
|
|
5134
|
+
if (typeof v !== 'object') { return null; }
|
|
5135
|
+
if (Array.isArray(v)) {
|
|
5136
|
+
/** @type {Set<StoreLayout.Action>} */
|
|
5137
|
+
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
|
|
5138
|
+
const actions = v.filter(v => options.delete(v));
|
|
5139
|
+
if (!actions) { return null; }
|
|
5140
|
+
return { actions };
|
|
5141
|
+
}
|
|
5142
|
+
const { action, actions, field, placeholder, pattern, width, label } = v;
|
|
5143
|
+
if (field) {
|
|
5144
|
+
const define = map.get(field);
|
|
5145
|
+
if (define) {
|
|
5146
|
+
return { field, placeholder, width, label: label || define.label };
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial']);
|
|
5150
|
+
const allActions = [action, actions].flat().filter(v => v && options.delete(v));
|
|
5151
|
+
if (allActions.length) {
|
|
5152
|
+
return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label };
|
|
5153
|
+
}
|
|
5154
|
+
// if (pattern) {
|
|
5155
|
+
// return { pattern, placeholder, width, label };
|
|
5156
|
+
// }
|
|
5157
|
+
return null;
|
|
5158
|
+
});
|
|
5159
|
+
columns = /** @type {StoreLayout.Column[]} */(allColumns.filter(Boolean));
|
|
5160
|
+
|
|
5330
5161
|
}
|
|
5331
|
-
if (
|
|
5332
|
-
|
|
5162
|
+
if (!columns.length) {
|
|
5163
|
+
columns = [
|
|
5164
|
+
{ actions: ['add', 'trigger', 'move', 'remove', 'serial'] },
|
|
5165
|
+
...fieldList.slice(0, 3),
|
|
5166
|
+
];
|
|
5333
5167
|
}
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5168
|
+
|
|
5169
|
+
const table = document.createElement('table');
|
|
5170
|
+
table.className = 'NeeloongForm-table';
|
|
5171
|
+
const thead = table.appendChild(document.createElement('thead'));
|
|
5172
|
+
|
|
5173
|
+
const addable = new Signal.Computed(() => store.addable);
|
|
5174
|
+
const deletable = { get: () => Boolean(options?.editable) };
|
|
5175
|
+
function add() {
|
|
5176
|
+
const data = {};
|
|
5177
|
+
store.add(data);
|
|
5178
|
+
}
|
|
5179
|
+
/**
|
|
5180
|
+
*
|
|
5181
|
+
* @param {Store} child
|
|
5182
|
+
*/
|
|
5183
|
+
function remove(child) {
|
|
5184
|
+
store.remove(Number(child.index));
|
|
5185
|
+
}
|
|
5186
|
+
let dragRow = -1;
|
|
5187
|
+
/**
|
|
5188
|
+
*
|
|
5189
|
+
* @param {Store} [child]
|
|
5190
|
+
*/
|
|
5191
|
+
function dragenter(child) {
|
|
5192
|
+
if (dragRow < 0) { return; }
|
|
5193
|
+
const index = child ? Number(child.index) : store.children.length;
|
|
5194
|
+
if (index < 0 || dragRow < 0 || dragRow === index) { return; }
|
|
5195
|
+
if (store.move(dragRow, index)) {
|
|
5196
|
+
dragRow = index;
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
/**
|
|
5200
|
+
*
|
|
5201
|
+
* @param {Store} child
|
|
5202
|
+
*/
|
|
5203
|
+
function dragstart(child) {
|
|
5204
|
+
dragRow = Number(child.index);
|
|
5205
|
+
|
|
5206
|
+
}
|
|
5207
|
+
function dragend() {
|
|
5208
|
+
dragRow = -1;
|
|
5209
|
+
|
|
5210
|
+
}
|
|
5211
|
+
/** @type {(() => void)[]} */
|
|
5212
|
+
const destroyList = [];
|
|
5213
|
+
destroyList.push(renderHead(thead, columns, add, addable, Boolean(options?.editable)));
|
|
5214
|
+
switch (layout?.tableFoot) {
|
|
5215
|
+
default:
|
|
5216
|
+
case 'header': {
|
|
5217
|
+
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5218
|
+
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5219
|
+
destroyList.push(renderHead(tfoot, columns, add, addable, Boolean(options?.editable)));
|
|
5220
|
+
break;
|
|
5221
|
+
}
|
|
5222
|
+
case 'add': {
|
|
5223
|
+
const tfoot = table.appendChild(document.createElement('tfoot'));
|
|
5224
|
+
tfoot.addEventListener('dragenter', () => { dragenter(); });
|
|
5225
|
+
const tr = tfoot.appendChild(document.createElement('tr'));
|
|
5226
|
+
const th = tr.appendChild(document.createElement('th'));
|
|
5227
|
+
th.colSpan = columns.length;
|
|
5228
|
+
const button = th.appendChild(document.createElement('button'));
|
|
5229
|
+
button.addEventListener('click', add);
|
|
5230
|
+
button.classList.add('NeeloongForm-table-foot-add');
|
|
5231
|
+
destroyList.push(watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true));
|
|
5232
|
+
break;
|
|
5233
|
+
}
|
|
5234
|
+
case 'none':
|
|
5235
|
+
}
|
|
5236
|
+
const start = thead;
|
|
5237
|
+
/** @type {Map<Store, [HTMLTableSectionElement, () => void]>} */
|
|
5238
|
+
let seMap = new Map();
|
|
5239
|
+
/** @param {Map<Store, [tbody: HTMLTableSectionElement, destroy: () => void]>} map */
|
|
5240
|
+
function destroyMap(map) {
|
|
5241
|
+
for (const [el, destroy] of map.values()) {
|
|
5242
|
+
destroy();
|
|
5243
|
+
el.remove();
|
|
5244
|
+
}
|
|
5245
|
+
|
|
5246
|
+
}
|
|
5247
|
+
const childrenResult = watch(() => store.children, function render(children) {
|
|
5248
|
+
let nextNode = thead.nextSibling;
|
|
5249
|
+
const oldSeMap = seMap;
|
|
5250
|
+
seMap = new Map();
|
|
5251
|
+
for (let child of children) {
|
|
5252
|
+
const old = oldSeMap.get(child);
|
|
5253
|
+
if (!old) {
|
|
5254
|
+
const [el, destroy] = Line(child, fieldRenderer, layout, {
|
|
5255
|
+
columns,
|
|
5256
|
+
remove: remove.bind(null, child),
|
|
5257
|
+
dragenter: dragenter.bind(null, child),
|
|
5258
|
+
dragstart: dragstart.bind(null, child),
|
|
5259
|
+
dragend,
|
|
5260
|
+
deletable,
|
|
5261
|
+
}, options);
|
|
5262
|
+
table.insertBefore(el, nextNode);
|
|
5263
|
+
seMap.set(child, [el, destroy]);
|
|
5264
|
+
continue;
|
|
5265
|
+
}
|
|
5266
|
+
oldSeMap.delete(child);
|
|
5267
|
+
seMap.set(child, old);
|
|
5268
|
+
if (nextNode === old[0]) {
|
|
5269
|
+
nextNode = nextNode.nextSibling;
|
|
5270
|
+
continue;
|
|
5271
|
+
}
|
|
5272
|
+
table.insertBefore(old[0], nextNode);
|
|
5273
|
+
}
|
|
5274
|
+
destroyMap(oldSeMap);
|
|
5275
|
+
}, true);
|
|
5276
|
+
|
|
5277
|
+
return [table, () => {
|
|
5278
|
+
start.remove();
|
|
5279
|
+
thead.remove();
|
|
5280
|
+
destroyMap(seMap);
|
|
5281
|
+
childrenResult();
|
|
5282
|
+
for (const destroy of destroyList) {
|
|
5283
|
+
destroy();
|
|
5284
|
+
}
|
|
5285
|
+
}];
|
|
5337
5286
|
}
|
|
5338
5287
|
|
|
5339
5288
|
/** @import { StoreLayout } from '../types.mjs' */
|
|
@@ -5579,7 +5528,7 @@ function createCell(layout, values, defCell, blockOnly) {
|
|
|
5579
5528
|
* @param {StoreLayout.Field?} layout
|
|
5580
5529
|
* @param {State} initState
|
|
5581
5530
|
* @param {object} option
|
|
5582
|
-
* @param {
|
|
5531
|
+
* @param {StoreLayout.Column[]} option.columns
|
|
5583
5532
|
* @param {() => void} option.remove
|
|
5584
5533
|
* @param {(el: HTMLElement) => () => void} option.dragenter
|
|
5585
5534
|
* @param {() => void} option.dragstart
|
|
@@ -5674,38 +5623,40 @@ function TreeLine(
|
|
|
5674
5623
|
dropChildren.addEventListener('dragover', (e) => e.preventDefault());
|
|
5675
5624
|
dropFront.addEventListener('drop', () => drop());
|
|
5676
5625
|
dropChildren.addEventListener('drop', () => drop(true));
|
|
5677
|
-
destroyList.push(effect(() => {
|
|
5626
|
+
destroyList.push(effect(() => {
|
|
5678
5627
|
dropFront.hidden = dropChildren.hidden = !state.get().droppable;
|
|
5679
|
-
|
|
5628
|
+
}));
|
|
5680
5629
|
|
|
5681
|
-
let dragleave = () => {};
|
|
5630
|
+
let dragleave = () => { };
|
|
5682
5631
|
dropFront.addEventListener('dragenter', () => dragleave = dragenter(dropFront));
|
|
5683
5632
|
dropChildren.addEventListener('dragenter', () => dragleave = dragenter(dropChildren));
|
|
5684
5633
|
dropFront.addEventListener('dragleave', () => dragleave());
|
|
5685
5634
|
dropChildren.addEventListener('dragleave', () => dragleave());
|
|
5686
5635
|
|
|
5687
|
-
for (const
|
|
5688
|
-
if (
|
|
5689
|
-
const td = line.appendChild(document.createElement('div'));
|
|
5690
|
-
td.classList.add('NeeloongForm-tree-placeholder');
|
|
5691
|
-
td.style.flex = `${name}`;
|
|
5692
|
-
if (click) { td.addEventListener('click', click); }
|
|
5693
|
-
if (moveStart) { td.addEventListener('pointerdown', moveStart); }
|
|
5694
|
-
continue;
|
|
5695
|
-
}
|
|
5696
|
-
if (!Array.isArray(name)) {
|
|
5636
|
+
for (const { actions, pattern, placeholder, width, field } of columns) {
|
|
5637
|
+
if (!actions?.length) {
|
|
5697
5638
|
const td = line.appendChild(document.createElement('div'));
|
|
5698
5639
|
td.classList.add('NeeloongForm-tree-cell');
|
|
5699
|
-
const child = store.child(name);
|
|
5700
|
-
if (!child) { continue; }
|
|
5701
|
-
const [el, destroy] = FormField(child, fieldRenderer, null, { ...options, editable: false }, true);
|
|
5702
|
-
destroyList.push(destroy);
|
|
5703
|
-
td.appendChild(el);
|
|
5704
5640
|
if (click) { td.addEventListener('click', click); }
|
|
5705
5641
|
if (moveStart) { td.addEventListener('pointerdown', moveStart); }
|
|
5642
|
+
if (field) {
|
|
5643
|
+
const child = store.child(field);
|
|
5644
|
+
if (!child) { continue; }
|
|
5645
|
+
const [el, destroy] = FormFieldInline(child, fieldRenderer, null, { ...options, editable: false });
|
|
5646
|
+
destroyList.push(destroy);
|
|
5647
|
+
td.appendChild(el);
|
|
5648
|
+
continue;
|
|
5649
|
+
}
|
|
5650
|
+
if (typeof placeholder === 'number') {
|
|
5651
|
+
td.style.flex = `${placeholder}`;
|
|
5652
|
+
}
|
|
5653
|
+
if (typeof width === 'number') {
|
|
5654
|
+
td.style.width = `${width}px`;
|
|
5655
|
+
}
|
|
5706
5656
|
continue;
|
|
5657
|
+
|
|
5707
5658
|
}
|
|
5708
|
-
for (const k of
|
|
5659
|
+
for (const k of actions) {
|
|
5709
5660
|
switch (k) {
|
|
5710
5661
|
case 'trigger': {
|
|
5711
5662
|
const btn = line.appendChild(document.createElement('button'));
|
|
@@ -5912,28 +5863,54 @@ function Tree(store, fieldRenderer, layout, options) {
|
|
|
5912
5863
|
const fieldList = Object.entries(store.type || {})
|
|
5913
5864
|
.filter(([k, v]) => typeof v?.type !== 'object')
|
|
5914
5865
|
.map(([field, { width, label }]) => ({ field, width, label }));
|
|
5915
|
-
/** @type {
|
|
5866
|
+
/** @type {StoreLayout.Column[]} */
|
|
5916
5867
|
let columns = [];
|
|
5917
5868
|
if (Array.isArray(headerColumns)) {
|
|
5918
5869
|
const map = new Map(fieldList.map(v => [v.field, v]));
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
if (
|
|
5922
|
-
if (
|
|
5923
|
-
|
|
5870
|
+
/** @type {(StoreLayout.Column | null)[]} */
|
|
5871
|
+
const allColumns = headerColumns.map(v => {
|
|
5872
|
+
if (!v) { return null; }
|
|
5873
|
+
if (typeof v === 'number') { return { placeholder: v }; }
|
|
5874
|
+
if (typeof v === 'string') { return map.get(v) || null; }
|
|
5875
|
+
if (typeof v !== 'object') { return null; }
|
|
5876
|
+
if (Array.isArray(v)) {
|
|
5877
|
+
/** @type {Set<StoreLayout.Action>} */
|
|
5878
|
+
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
|
|
5879
|
+
const actions = v.filter(v => options.delete(v));
|
|
5880
|
+
if (!actions) { return null; }
|
|
5881
|
+
return { actions };
|
|
5882
|
+
}
|
|
5883
|
+
const { action, actions, field, placeholder, pattern, width, label } = v;
|
|
5884
|
+
if (field) {
|
|
5885
|
+
const define = map.get(field);
|
|
5886
|
+
if (define) {
|
|
5887
|
+
return { field, placeholder, width, label: label || define.label };
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5924
5890
|
const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
|
|
5925
|
-
|
|
5926
|
-
|
|
5891
|
+
const allActions = [action, actions].flat().filter(v => v && options.delete(v));
|
|
5892
|
+
if (allActions.length) {
|
|
5893
|
+
return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label };
|
|
5894
|
+
}
|
|
5895
|
+
if (pattern) {
|
|
5896
|
+
return { pattern, placeholder, width, label };
|
|
5897
|
+
}
|
|
5898
|
+
if (placeholder || width) {
|
|
5899
|
+
return { placeholder, width, label };
|
|
5900
|
+
}
|
|
5901
|
+
return null;
|
|
5902
|
+
});
|
|
5903
|
+
columns = /** @type {StoreLayout.Column[]} */(allColumns.filter(Boolean));
|
|
5927
5904
|
}
|
|
5928
5905
|
if (!columns.length) {
|
|
5929
|
-
columns = [
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5906
|
+
columns = [
|
|
5907
|
+
{ actions: ['collapse', 'move'] },
|
|
5908
|
+
fieldList[0],
|
|
5909
|
+
{ actions: ['add', 'remove'] },
|
|
5910
|
+
];
|
|
5933
5911
|
}
|
|
5934
5912
|
|
|
5935
5913
|
|
|
5936
|
-
|
|
5937
5914
|
const root = document.createElement('div');
|
|
5938
5915
|
root.className = 'NeeloongForm-tree';
|
|
5939
5916
|
const main = root.appendChild(document.createElement('div'));
|
|
@@ -6203,7 +6180,6 @@ function Tree(store, fieldRenderer, layout, options) {
|
|
|
6203
6180
|
}
|
|
6204
6181
|
/** @type {State[]} */
|
|
6205
6182
|
const states = [];
|
|
6206
|
-
const columnNames = columns.map((v) => Array.isArray(v) || typeof v === 'number' ? v : v.field);
|
|
6207
6183
|
const childrenResult = watch(() => store.children, function render(children) {
|
|
6208
6184
|
let nextNode = start.nextSibling;
|
|
6209
6185
|
const oldSeMap = seMap;
|
|
@@ -6220,7 +6196,7 @@ function Tree(store, fieldRenderer, layout, options) {
|
|
|
6220
6196
|
const old = oldSeMap.get(child);
|
|
6221
6197
|
if (!old) {
|
|
6222
6198
|
const [el, destroy, setState] = TreeLine(child, detailsStore, fieldRenderer, layout, state, {
|
|
6223
|
-
columns
|
|
6199
|
+
columns,
|
|
6224
6200
|
remove: remove.bind(null, child),
|
|
6225
6201
|
dragenter,
|
|
6226
6202
|
dragstart: dragstart.bind(null, child),
|
|
@@ -6291,22 +6267,10 @@ function getArrayCell(arrayStyle) {
|
|
|
6291
6267
|
* @param {StoreLayout.Renderer} fieldRenderer
|
|
6292
6268
|
* @param {StoreLayout.Field?} layout
|
|
6293
6269
|
* @param {StoreLayout.Options?} options
|
|
6294
|
-
* @param {boolean} [inline]
|
|
6295
6270
|
* @returns {[ParentNode, () => void]}
|
|
6296
6271
|
*/
|
|
6297
|
-
function FormField(store, fieldRenderer, layout, options
|
|
6272
|
+
function FormField(store, fieldRenderer, layout, options) {
|
|
6298
6273
|
const { type, component } = store;
|
|
6299
|
-
if (inline) {
|
|
6300
|
-
const html = layout?.inlineHtml;
|
|
6301
|
-
if (html) {
|
|
6302
|
-
const content = getHtmlContent(html);
|
|
6303
|
-
const destroy = renderHtml(store, fieldRenderer, content, options, layout);
|
|
6304
|
-
return [content, destroy];
|
|
6305
|
-
}
|
|
6306
|
-
return component
|
|
6307
|
-
&& fieldRenderer(store, component, options)
|
|
6308
|
-
|| [document.createElement('div'), () => { }];
|
|
6309
|
-
}
|
|
6310
6274
|
const isObject = type && typeof type === 'object';
|
|
6311
6275
|
const html = layout?.html;
|
|
6312
6276
|
/** @type {StoreLayout.Grid['cell']} */
|