@neeloong/form 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/index.d.mts +113 -62
- package/index.js +463 -172
- package/index.min.js +8 -8
- package/index.min.mjs +6 -6
- package/index.mjs +463 -172
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @neeloong/form v0.
|
|
2
|
+
* @neeloong/form v0.8.0
|
|
3
3
|
* (c) 2024-2025 Fierflame
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
@@ -591,7 +591,7 @@
|
|
|
591
591
|
*
|
|
592
592
|
* @param {Store} self
|
|
593
593
|
* @param {boolean?} [defState]
|
|
594
|
-
* @param {boolean | ((store: Store
|
|
594
|
+
* @param {boolean | ((store: Store) => boolean?) | null} [fn]
|
|
595
595
|
* @param {Signal.Computed<boolean>?} [parent]
|
|
596
596
|
* @returns {[Signal.State<boolean?>, Signal.Computed<boolean>]}
|
|
597
597
|
*/
|
|
@@ -601,7 +601,7 @@
|
|
|
601
601
|
/** @type {Signal.Computed<boolean>} */
|
|
602
602
|
let scriptState;
|
|
603
603
|
if (typeof fn === 'function') {
|
|
604
|
-
scriptState = new exports.Signal.Computed(() => Boolean(fn(self
|
|
604
|
+
scriptState = new exports.Signal.Computed(() => Boolean(fn(self)));
|
|
605
605
|
} else {
|
|
606
606
|
const def = Boolean(fn);
|
|
607
607
|
scriptState = new exports.Signal.Computed(() => def);
|
|
@@ -624,6 +624,8 @@
|
|
|
624
624
|
const string = v => typeof v === 'string' && v || null;
|
|
625
625
|
/** @param {*} v */
|
|
626
626
|
const number = v => typeof v === 'number' && v || null;
|
|
627
|
+
/** @param {*} v */
|
|
628
|
+
const regex = v =>v instanceof RegExp ? v : null;
|
|
627
629
|
|
|
628
630
|
/** @type {(v: Schema.Value | Schema.Value.Group | null) => v is Schema.Value | Schema.Value.Group} */
|
|
629
631
|
const valueFilter = /** @type {*} */(Boolean);
|
|
@@ -663,7 +665,7 @@
|
|
|
663
665
|
* @param {Store} self
|
|
664
666
|
* @param {(v: any) => any?} toValue
|
|
665
667
|
* @param {T?} [defState]
|
|
666
|
-
* @param {T | ((store: Store
|
|
668
|
+
* @param {T | ((store: Store) => T?) | null} [fn]
|
|
667
669
|
* @returns {[Signal.State<T?>, Signal.Computed<T?>]}
|
|
668
670
|
*/
|
|
669
671
|
function createState(self, toValue, defState, fn) {
|
|
@@ -672,8 +674,8 @@
|
|
|
672
674
|
/** @type {Signal.Computed<T>} */
|
|
673
675
|
let scriptState;
|
|
674
676
|
if (typeof fn === 'function') {
|
|
675
|
-
const f = /** @type {(store: Store
|
|
676
|
-
scriptState = new exports.Signal.Computed(() => toValue(f(self
|
|
677
|
+
const f = /** @type {(store: Store) => T?} */(fn);
|
|
678
|
+
scriptState = new exports.Signal.Computed(() => toValue(f(self)));
|
|
677
679
|
} else {
|
|
678
680
|
const def = toValue(fn);
|
|
679
681
|
scriptState = new exports.Signal.Computed(() => def);
|
|
@@ -688,7 +690,176 @@
|
|
|
688
690
|
|
|
689
691
|
}
|
|
690
692
|
|
|
693
|
+
const ref = Symbol();
|
|
694
|
+
/** @import Store from './index.mjs' */
|
|
695
|
+
/** @typedef {{ [ref]: Store; [k: string]: Ref | undefined;}} Ref */
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* @param {Store} store
|
|
699
|
+
*/
|
|
700
|
+
function createRef(store) {
|
|
701
|
+
|
|
702
|
+
/** @type{Ref} */
|
|
703
|
+
const r = { [ref]: store };
|
|
704
|
+
for (const [k, f] of store) {
|
|
705
|
+
Object.defineProperty(r, k, {
|
|
706
|
+
get() { return f.ref; },
|
|
707
|
+
configurable: false,
|
|
708
|
+
enumerable: true,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
Object.defineProperty(r, ref, {
|
|
712
|
+
get() { return store; },
|
|
713
|
+
configurable: false,
|
|
714
|
+
enumerable: true,
|
|
715
|
+
});
|
|
716
|
+
return r;
|
|
717
|
+
}
|
|
718
|
+
|
|
691
719
|
/** @import { Schema } from '../types.mjs' */
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
/** @type {{new(...p: ConstructorParameters<typeof Store>): Store}?} */
|
|
723
|
+
let ObjectStore$1 = null;
|
|
724
|
+
/** @type {{new(...p: ConstructorParameters<typeof Store>): Store}?} */
|
|
725
|
+
let ArrayStore$1 = null;
|
|
726
|
+
/** @type {Record<string, {new(...p: ConstructorParameters<typeof Store>): Store}?>} */
|
|
727
|
+
let TypeStores = Object.create(null);
|
|
728
|
+
/**
|
|
729
|
+
* @param {Schema.Field} schema
|
|
730
|
+
* @param {object} [options]
|
|
731
|
+
* @param {Store?} [options.parent]
|
|
732
|
+
* @param {string | number | null} [options.index]
|
|
733
|
+
* @param {boolean} [options.new]
|
|
734
|
+
* @param {(value: any, index: any, store: Store) => void} [options.onUpdate]
|
|
735
|
+
* @param {(value: any, index: any, store: Store) => void} [options.onUpdateState]
|
|
736
|
+
*/
|
|
737
|
+
function create(schema, options) {
|
|
738
|
+
const type = schema.type;
|
|
739
|
+
/** @type {{new(...p: ConstructorParameters<typeof Store>): Store}} */
|
|
740
|
+
let Class = Store;
|
|
741
|
+
if (schema.array && !(ArrayStore$1 && options?.parent instanceof ArrayStore$1)) {
|
|
742
|
+
if (ArrayStore$1) { Class = ArrayStore$1; }
|
|
743
|
+
} else if (typeof type === 'string') {
|
|
744
|
+
const C = TypeStores[type];
|
|
745
|
+
if (C) { Class = C; }
|
|
746
|
+
} else if (type && typeof type === 'object') {
|
|
747
|
+
if (ObjectStore$1) { Class = ObjectStore$1; }
|
|
748
|
+
}
|
|
749
|
+
return new Class(schema, options);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/** @param {{new(...p: ConstructorParameters<typeof Store>): Store}} Class */
|
|
753
|
+
function setObjectStore(Class) {
|
|
754
|
+
ObjectStore$1 = Class;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/** @param {{new(...p: ConstructorParameters<typeof Store>): Store}} Class */
|
|
758
|
+
function setArrayStore(Class) {
|
|
759
|
+
ArrayStore$1 = Class;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* @param {string} type
|
|
763
|
+
* @param {{new(...p: ConstructorParameters<typeof Store>): Store}} Class
|
|
764
|
+
*/
|
|
765
|
+
function setStore$1(type, Class) {
|
|
766
|
+
TypeStores[type] = Class;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/** @import Store from './Store.mjs' */
|
|
770
|
+
/** @import { AsyncValidator, Validator } from '../types.mjs' */
|
|
771
|
+
/**
|
|
772
|
+
*
|
|
773
|
+
* @param {*} v
|
|
774
|
+
*/
|
|
775
|
+
function toResult(v) {
|
|
776
|
+
if (!v) { return ''; }
|
|
777
|
+
if (v instanceof Error) { return v.message; }
|
|
778
|
+
if (typeof v !== 'string') { return ''; }
|
|
779
|
+
return v;
|
|
780
|
+
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
*
|
|
784
|
+
* @param {Store} store
|
|
785
|
+
* @param {...Validator | undefined | null | (Validator | undefined | null)[]} validators
|
|
786
|
+
* @returns
|
|
787
|
+
*/
|
|
788
|
+
function createValidator(store, ...validators) {
|
|
789
|
+
const allValidators = validators.flat().filter(v => typeof v === 'function');
|
|
790
|
+
if (!allValidators.length) {
|
|
791
|
+
return new exports.Signal.Computed(() => /** @type {string[]} */([]));
|
|
792
|
+
}
|
|
793
|
+
return new exports.Signal.Computed(() => {
|
|
794
|
+
const results = [];
|
|
795
|
+
for (const validator of allValidators) {
|
|
796
|
+
try {
|
|
797
|
+
results.push(validator(store));
|
|
798
|
+
} catch (e){
|
|
799
|
+
results.push(e);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return results.flat().map(toResult).filter(Boolean);
|
|
803
|
+
})
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
*
|
|
807
|
+
* @param {Store} store
|
|
808
|
+
* @param {...AsyncValidator | undefined | null | (AsyncValidator | undefined | null)[]} validators
|
|
809
|
+
* @returns {[exec: () => Promise<string[]>, state: Signal.Computed<string[]>, stop: () => void]}
|
|
810
|
+
*/
|
|
811
|
+
function createAsyncValidator(store, ...validators) {
|
|
812
|
+
const allValidators = validators.flat().filter(v => typeof v === 'function');
|
|
813
|
+
if (!allValidators.length) {
|
|
814
|
+
return [
|
|
815
|
+
()=>Promise.resolve([]),
|
|
816
|
+
new exports.Signal.Computed(() => /** @type {string[]} */([])),
|
|
817
|
+
() => {}
|
|
818
|
+
];
|
|
819
|
+
}
|
|
820
|
+
const st = new exports.Signal.State(/** @type {string[]} */([]));
|
|
821
|
+
/**
|
|
822
|
+
*
|
|
823
|
+
* @param {AsyncValidator} validator
|
|
824
|
+
* @param {AbortSignal} signal
|
|
825
|
+
*/
|
|
826
|
+
async function run(validator, signal) {
|
|
827
|
+
let results = [];
|
|
828
|
+
try {
|
|
829
|
+
results.push(await validator(store, signal));
|
|
830
|
+
} catch (e){
|
|
831
|
+
results.push(e);
|
|
832
|
+
}
|
|
833
|
+
const list = results.flat().map(toResult).filter(Boolean);
|
|
834
|
+
if (!signal.aborted && list.length) { st.set([...st.get(), ...list]); }
|
|
835
|
+
return list;
|
|
836
|
+
}
|
|
837
|
+
/** @type {AbortController?} */
|
|
838
|
+
let ac = null;
|
|
839
|
+
function exec() {
|
|
840
|
+
ac?.abort();
|
|
841
|
+
ac = new AbortController();
|
|
842
|
+
const signal = ac.signal;
|
|
843
|
+
st.set([]);
|
|
844
|
+
|
|
845
|
+
return Promise.all(allValidators.map(f => run(f, signal))).then(v => v.flat());
|
|
846
|
+
}
|
|
847
|
+
return [exec, new exports.Signal.Computed(() => st.get()), () => {ac?.abort(); st.set([]);}]
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
*
|
|
852
|
+
* @param {...Signal.Computed<string[]>} v
|
|
853
|
+
* @returns
|
|
854
|
+
*/
|
|
855
|
+
function merge(...v) {
|
|
856
|
+
const list = /** @type {Signal.Computed<string[]>[]} */(v.filter(Boolean));
|
|
857
|
+
return new exports.Signal.Computed(() => list.flatMap(v => v.get()));
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/** @import { Ref } from './ref.mjs' */
|
|
861
|
+
/** @import { AsyncValidator, Schema, Validator } from '../types.mjs' */
|
|
862
|
+
|
|
692
863
|
/**
|
|
693
864
|
* @template [T=any]
|
|
694
865
|
*/
|
|
@@ -736,14 +907,24 @@
|
|
|
736
907
|
* @param {boolean} [options.new]
|
|
737
908
|
*/
|
|
738
909
|
static create(schema, options = {}) {
|
|
739
|
-
return
|
|
910
|
+
return create({type: schema}, { ...options, parent: null });
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* @param {string} type
|
|
914
|
+
* @param {{new(...p: ConstructorParameters<typeof Store>): Store}} Class
|
|
915
|
+
*/
|
|
916
|
+
static setStore(type, Class) {
|
|
917
|
+
return setStore$1(type, Class);
|
|
740
918
|
}
|
|
741
919
|
#null = false;
|
|
742
920
|
get null() { return this.#null; }
|
|
743
921
|
get kind() { return ''; }
|
|
922
|
+
/** @type {Ref?} */
|
|
923
|
+
#ref = null;
|
|
924
|
+
get ref() { return this.#ref || createRef(this); }
|
|
744
925
|
/**
|
|
745
926
|
* @param {Schema.Field} schema
|
|
746
|
-
* @param {object} options
|
|
927
|
+
* @param {object} [options]
|
|
747
928
|
* @param {*} [options.parent]
|
|
748
929
|
* @param {*} [options.state]
|
|
749
930
|
* @param {number | string | null} [options.index]
|
|
@@ -762,21 +943,30 @@
|
|
|
762
943
|
* @param {number} [options.min] 日期、时间、数字的最小值
|
|
763
944
|
* @param {number} [options.max] 日期、时间、数字的最大值
|
|
764
945
|
* @param {number} [options.step] 日期、时间、数字的步长
|
|
946
|
+
* @param {number} [options.minLength]
|
|
947
|
+
* @param {number} [options.maxLength]
|
|
948
|
+
* @param {RegExp} [options.pattern]
|
|
765
949
|
* @param {(Schema.Value.Group | Schema.Value | string | number)[]} [options.values] 可选值
|
|
950
|
+
* @param {Validator | Validator[] | null} [options.validator]
|
|
951
|
+
* @param {{[k in keyof Schema.Events]?: AsyncValidator | AsyncValidator[] | null}} [options.validators]
|
|
952
|
+
*
|
|
953
|
+
* @param {Ref?} [options.ref]
|
|
766
954
|
*
|
|
767
955
|
* @param {((value: any) => any)?} [options.setValue]
|
|
768
956
|
* @param {((value: any) => any)?} [options.setState]
|
|
769
957
|
* @param {((value: any, state: any) => [value: any, state: any])?} [options.convert]
|
|
770
|
-
*
|
|
771
|
-
* @param {((value: T?, index: any) => void)?} [options.
|
|
958
|
+
*
|
|
959
|
+
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
|
|
960
|
+
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdateState]
|
|
772
961
|
*/
|
|
773
962
|
constructor(schema, {
|
|
774
|
-
null: isNull, state,
|
|
963
|
+
null: isNull, state, ref,
|
|
775
964
|
setValue, setState, convert, onUpdate, onUpdateState,
|
|
965
|
+
validator, validators,
|
|
776
966
|
index, length, new: isNew, parent: parentNode,
|
|
777
967
|
hidden, clearable, required, disabled, readonly,
|
|
778
|
-
label, description, placeholder, min, max, step, values: values$1
|
|
779
|
-
}) {
|
|
968
|
+
label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
|
|
969
|
+
} = {}) {
|
|
780
970
|
this.schema = schema;
|
|
781
971
|
this.#state.set(typeof state === 'object' && state || {});
|
|
782
972
|
const parent = parentNode instanceof Store ? parentNode : null;
|
|
@@ -807,7 +997,7 @@
|
|
|
807
997
|
/** @type {Signal.Computed<boolean>} */
|
|
808
998
|
let readonlyScript;
|
|
809
999
|
if (typeof readonlyFn === 'function') {
|
|
810
|
-
readonlyScript = new exports.Signal.Computed(() => Boolean(readonlyFn(this
|
|
1000
|
+
readonlyScript = new exports.Signal.Computed(() => Boolean(readonlyFn(this)));
|
|
811
1001
|
} else {
|
|
812
1002
|
const def = Boolean(readonlyFn);
|
|
813
1003
|
readonlyScript = new exports.Signal.Computed(() => def);
|
|
@@ -834,9 +1024,25 @@
|
|
|
834
1024
|
[this.#selfMin, this.#min] = createState(this, number, min, schema.min);
|
|
835
1025
|
[this.#selfMax, this.#max] = createState(this, number, max, schema.max);
|
|
836
1026
|
[this.#selfStep, this.#step] = createState(this, number, step, schema.step);
|
|
1027
|
+
[this.#selfMinLength, this.#minLength] = createState(this, number, minLength, schema.minLength);
|
|
1028
|
+
[this.#selfMaxLength, this.#maxLength] = createState(this, number, maxLength, schema.maxLength);
|
|
1029
|
+
[this.#selfPattern, this.#pattern] = createState(this, regex, pattern, schema.pattern);
|
|
837
1030
|
// @ts-ignore
|
|
838
1031
|
[this.#selfValues, this.#values] = createState(this, values, values$1, schema.values);
|
|
839
1032
|
|
|
1033
|
+
const validatorResult = createValidator(this, schema.validator, validator);
|
|
1034
|
+
|
|
1035
|
+
const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
|
|
1036
|
+
const [blurred, blurredResult, cancelBlur] = createAsyncValidator(this, schema.validators?.blur, validators?.blur);
|
|
1037
|
+
this.listen('change', () => {changed();});
|
|
1038
|
+
this.listen('blur', () => {blurred();});
|
|
1039
|
+
this.#errors = merge(validatorResult, changedResult, blurredResult);
|
|
1040
|
+
this.#validatorResult = validatorResult;
|
|
1041
|
+
this.#changed = changed;
|
|
1042
|
+
this.#blurred = blurred;
|
|
1043
|
+
this.#cancelChange = cancelChange;
|
|
1044
|
+
this.#cancelBlur = cancelBlur;
|
|
1045
|
+
|
|
840
1046
|
if (length instanceof exports.Signal.State || length instanceof exports.Signal.Computed) {
|
|
841
1047
|
this.#length = length;
|
|
842
1048
|
} else {
|
|
@@ -845,8 +1051,10 @@
|
|
|
845
1051
|
|
|
846
1052
|
if (isNull) {
|
|
847
1053
|
this.#null = true;
|
|
1054
|
+
this.#ref = createRef(this);
|
|
848
1055
|
return;
|
|
849
1056
|
}
|
|
1057
|
+
this.#ref = ref || null;
|
|
850
1058
|
this.#onUpdate = onUpdate || null;
|
|
851
1059
|
this.#onUpdateState = onUpdateState || null;
|
|
852
1060
|
this.#setValue = typeof setValue === 'function' ? setValue : null;
|
|
@@ -859,19 +1067,16 @@
|
|
|
859
1067
|
// @ts-ignore
|
|
860
1068
|
this.listen(k, f);
|
|
861
1069
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
1070
|
}
|
|
865
|
-
#destroyed = false;
|
|
866
1071
|
/** @type {((value: any) => any)?} */
|
|
867
1072
|
#setValue = null
|
|
868
1073
|
/** @type {((value: any) => any)?} */
|
|
869
1074
|
#setState = null
|
|
870
1075
|
/** @type {((value: any, state: any) => [value: any, state: any])?} */
|
|
871
1076
|
#convert = null
|
|
872
|
-
/** @type {((value: any, index: any) => void)?} */
|
|
1077
|
+
/** @type {((value: any, index: any, store: Store) => void)?} */
|
|
873
1078
|
#onUpdate = null
|
|
874
|
-
/** @type {((value: any, index: any) => void)?} */
|
|
1079
|
+
/** @type {((value: any, index: any, store: Store) => void)?} */
|
|
875
1080
|
#onUpdateState = null
|
|
876
1081
|
/** @readonly @type {Store?} */
|
|
877
1082
|
#parent = null;
|
|
@@ -1022,6 +1227,33 @@
|
|
|
1022
1227
|
get step() { return this.#step.get(); }
|
|
1023
1228
|
set step(v) { this.#selfStep.set(number(v)); }
|
|
1024
1229
|
|
|
1230
|
+
/** @readonly @type {Signal.State<number?>} */
|
|
1231
|
+
#selfMinLength
|
|
1232
|
+
/** @readonly @type {Signal.Computed<number?>} */
|
|
1233
|
+
#minLength
|
|
1234
|
+
get selfMinLength() { return this.#selfMinLength.get(); }
|
|
1235
|
+
set selfMinLength(v) { this.#selfMinLength.set(number(v)); }
|
|
1236
|
+
get minLength() { return this.#minLength.get(); }
|
|
1237
|
+
set minLength(v) { this.#selfMinLength.set(number(v)); }
|
|
1238
|
+
|
|
1239
|
+
/** @readonly @type {Signal.State<number?>} */
|
|
1240
|
+
#selfMaxLength
|
|
1241
|
+
/** @readonly @type {Signal.Computed<number?>} */
|
|
1242
|
+
#maxLength
|
|
1243
|
+
get selfMaxLength() { return this.#selfMaxLength.get(); }
|
|
1244
|
+
set selfMaxLength(v) { this.#selfMaxLength.set(number(v)); }
|
|
1245
|
+
get maxLength() { return this.#maxLength.get(); }
|
|
1246
|
+
set maxLength(v) { this.#selfMaxLength.set(number(v)); }
|
|
1247
|
+
|
|
1248
|
+
/** @readonly @type {Signal.State<RegExp?>} */
|
|
1249
|
+
#selfPattern
|
|
1250
|
+
/** @readonly @type {Signal.Computed<RegExp?>} */
|
|
1251
|
+
#pattern
|
|
1252
|
+
get selfPattern() { return this.#selfPattern.get(); }
|
|
1253
|
+
set selfPattern(v) { this.#selfPattern.set(regex(v)); }
|
|
1254
|
+
get pattern() { return this.#pattern.get(); }
|
|
1255
|
+
set pattern(v) { this.#selfPattern.set(regex(v)); }
|
|
1256
|
+
|
|
1025
1257
|
|
|
1026
1258
|
/** @readonly @type {Signal.State<(Schema.Value.Group | Schema.Value)[] | null>} */
|
|
1027
1259
|
#selfValues
|
|
@@ -1033,8 +1265,20 @@
|
|
|
1033
1265
|
set values(v) { this.#selfValues.set(values(v)); }
|
|
1034
1266
|
|
|
1035
1267
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1268
|
+
/** @type {Signal.Computed<string[]>} */
|
|
1269
|
+
#errors
|
|
1270
|
+
/** @type {Signal.Computed<string[]>} */
|
|
1271
|
+
#validatorResult
|
|
1272
|
+
/** @type {() => Promise<string[]>} */
|
|
1273
|
+
#changed
|
|
1274
|
+
/** @type {() => Promise<string[]>} */
|
|
1275
|
+
#blurred
|
|
1276
|
+
/** @type {() => void} */
|
|
1277
|
+
#cancelChange
|
|
1278
|
+
/** @type {() => void} */
|
|
1279
|
+
#cancelBlur
|
|
1280
|
+
get errors() { return this.#errors.get(); }
|
|
1281
|
+
get error() { return this.#errors.get()[0]; }
|
|
1038
1282
|
|
|
1039
1283
|
/** @returns {IterableIterator<[key: string | number, value: Store]>} */
|
|
1040
1284
|
*[Symbol.iterator]() {}
|
|
@@ -1046,39 +1290,32 @@
|
|
|
1046
1290
|
child(key) { return null; }
|
|
1047
1291
|
|
|
1048
1292
|
#set = false;
|
|
1049
|
-
/** @type {T?} */
|
|
1050
|
-
#
|
|
1051
|
-
#lastValue = this.#initValue;
|
|
1052
|
-
#value = new exports.Signal.State(this.#initValue);
|
|
1293
|
+
#initValue = new exports.Signal.State(/** @type {T?} */(null));
|
|
1294
|
+
#value = new exports.Signal.State(this.#initValue.get());
|
|
1053
1295
|
|
|
1054
1296
|
|
|
1055
1297
|
#state = new exports.Signal.State(/** @type {any} */(null));
|
|
1056
|
-
#lastState = this.#state.get();
|
|
1057
|
-
|
|
1058
1298
|
|
|
1059
|
-
get changed() { return this.#value.get() === this.#
|
|
1060
|
-
get saved() { return this.#value.get() === this.#initValue; }
|
|
1299
|
+
get changed() { return this.#value.get() === this.#initValue.get(); }
|
|
1061
1300
|
|
|
1062
1301
|
get value() { return this.#value.get(); }
|
|
1063
1302
|
set value(v) {
|
|
1064
|
-
|
|
1065
|
-
const val =
|
|
1303
|
+
const newValue = this.#setValue?.(v);
|
|
1304
|
+
const val = newValue === undefined ? v : newValue;
|
|
1066
1305
|
this.#value.set(val);
|
|
1067
1306
|
if (!this.#set) {
|
|
1068
|
-
this.#set
|
|
1069
|
-
this.#initValue = v;
|
|
1307
|
+
this.#initValue.set(val);
|
|
1070
1308
|
}
|
|
1071
|
-
this.#onUpdate?.(this.#
|
|
1309
|
+
this.#onUpdate?.(val, this.#index.get(), this);
|
|
1072
1310
|
this.#requestUpdate();
|
|
1073
1311
|
}
|
|
1074
1312
|
|
|
1075
1313
|
get state() { return this.#state.get(); }
|
|
1076
1314
|
set state(v) {
|
|
1077
|
-
|
|
1078
|
-
const sta =
|
|
1315
|
+
const newState = this.#setState?.(v);
|
|
1316
|
+
const sta = newState === undefined ? v : newState;
|
|
1079
1317
|
this.#state.set(sta);
|
|
1080
|
-
this.#
|
|
1081
|
-
this.#onUpdateState?.(this.#state.get(), this.#index.get());
|
|
1318
|
+
this.#onUpdateState?.(sta, this.#index.get(), this);
|
|
1082
1319
|
this.#requestUpdate();
|
|
1083
1320
|
}
|
|
1084
1321
|
#requestUpdate() {
|
|
@@ -1087,9 +1324,40 @@
|
|
|
1087
1324
|
queueMicrotask(() => {
|
|
1088
1325
|
const oldValue = this.#value.get();
|
|
1089
1326
|
const oldState = this.#state.get();
|
|
1090
|
-
|
|
1327
|
+
this.#runUpdate(oldValue, oldState);
|
|
1091
1328
|
});
|
|
1092
1329
|
}
|
|
1330
|
+
reset(value = this.#initValue.get()) {
|
|
1331
|
+
this.#reset(value);
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
*
|
|
1335
|
+
* @param {*} v
|
|
1336
|
+
* @returns
|
|
1337
|
+
*/
|
|
1338
|
+
#reset(v) {
|
|
1339
|
+
const newValue = this.#setValue?.(v);
|
|
1340
|
+
const value = newValue === undefined ? v : newValue;
|
|
1341
|
+
this.#cancelChange();
|
|
1342
|
+
this.#cancelBlur();
|
|
1343
|
+
this.#set = true;
|
|
1344
|
+
if (!value || typeof value !== 'object') {
|
|
1345
|
+
for (const [, field] of this) {
|
|
1346
|
+
field.#reset(null);
|
|
1347
|
+
}
|
|
1348
|
+
this.#value.set(value);
|
|
1349
|
+
this.#initValue.set(value);
|
|
1350
|
+
return value;
|
|
1351
|
+
}
|
|
1352
|
+
/** @type {*} */
|
|
1353
|
+
const newValues = Array.isArray(value) ? [...value] : {...value};
|
|
1354
|
+
for (const [key, field] of this) {
|
|
1355
|
+
newValues[key] = field.#reset(newValues[key]);
|
|
1356
|
+
}
|
|
1357
|
+
this.#value.set(newValues);
|
|
1358
|
+
this.#initValue.set(newValues);
|
|
1359
|
+
return newValues;
|
|
1360
|
+
}
|
|
1093
1361
|
|
|
1094
1362
|
|
|
1095
1363
|
#needUpdate = false;
|
|
@@ -1100,26 +1368,21 @@
|
|
|
1100
1368
|
* @returns
|
|
1101
1369
|
*/
|
|
1102
1370
|
#toUpdate(value, state) {
|
|
1103
|
-
if (this.#destroyed) { return value; }
|
|
1104
1371
|
const [val,sta] = this.#convert?.(value, state) || [value, state];
|
|
1105
1372
|
if(this.#value.get() === val && this.#state.get() === sta) { return [val,sta] }
|
|
1106
1373
|
this.#value.set(val);
|
|
1107
1374
|
this.#state.set(sta);
|
|
1108
|
-
if (!this.#set) {
|
|
1109
|
-
this.#set = true;
|
|
1110
|
-
this.#initValue = val;
|
|
1111
|
-
}
|
|
1112
1375
|
return this.#runUpdate(val, sta);
|
|
1113
1376
|
}
|
|
1114
1377
|
/**
|
|
1115
1378
|
*
|
|
1116
1379
|
* @param {*} val
|
|
1117
1380
|
* @param {*} sta
|
|
1118
|
-
* @returns
|
|
1381
|
+
* @returns {[any, any]}
|
|
1119
1382
|
*/
|
|
1120
1383
|
#runUpdate(val, sta) {
|
|
1121
|
-
if (this.#destroyed) { return [val, sta]; }
|
|
1122
1384
|
this.#needUpdate = false;
|
|
1385
|
+
let initValue = val;
|
|
1123
1386
|
if (val && typeof val === 'object') {
|
|
1124
1387
|
/** @type {T} */
|
|
1125
1388
|
// @ts-ignore
|
|
@@ -1144,34 +1407,58 @@
|
|
|
1144
1407
|
if (updated) {
|
|
1145
1408
|
val = newValues;
|
|
1146
1409
|
sta = newStates;
|
|
1410
|
+
initValue = val;
|
|
1147
1411
|
this.#value.set(val);
|
|
1148
1412
|
this.#state.set(newStates);
|
|
1149
1413
|
}
|
|
1150
1414
|
}
|
|
1151
|
-
if (this.#
|
|
1152
|
-
|
|
1415
|
+
if (!this.#set) {
|
|
1416
|
+
this.#set = true;
|
|
1417
|
+
this.#initValue.set(initValue);
|
|
1153
1418
|
}
|
|
1154
|
-
this.#lastValue = val;
|
|
1155
|
-
this.#lastState = sta;
|
|
1156
1419
|
return [val, sta];
|
|
1157
|
-
|
|
1158
1420
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1421
|
+
/**
|
|
1422
|
+
*
|
|
1423
|
+
* @overload
|
|
1424
|
+
* @param {null} [path]
|
|
1425
|
+
* @returns {Promise<string[] | null>}
|
|
1426
|
+
*/
|
|
1427
|
+
/**
|
|
1428
|
+
* @overload
|
|
1429
|
+
* @param {(string | number)[]} path
|
|
1430
|
+
* @returns {Promise<{ path: (string | number)[]; store: Store; errors: string[]}[]>}
|
|
1431
|
+
*/
|
|
1432
|
+
/**
|
|
1433
|
+
*
|
|
1434
|
+
* @param {(string | number)[]?} [path]
|
|
1435
|
+
* @returns {Promise<string[] | { path: (string | number)[]; store: Store; errors: string[] | null;}[] | null>}
|
|
1436
|
+
*/
|
|
1437
|
+
validate(path) {
|
|
1438
|
+
if (!Array.isArray(path)) {
|
|
1439
|
+
return Promise.all([this.#validatorResult.get(), this.#changed(), this.#blurred()])
|
|
1440
|
+
.then(v => {
|
|
1441
|
+
const errors = v.flat();
|
|
1442
|
+
return errors.length ? errors : null
|
|
1443
|
+
});
|
|
1168
1444
|
}
|
|
1445
|
+
const list = [this.validate().then(errors => {
|
|
1446
|
+
if (!errors?.length) {return [];}
|
|
1447
|
+
return [{path: [...path], store: /** @type {Store} */(this), errors}]
|
|
1448
|
+
})];
|
|
1449
|
+
for (const [key, field] of this) {
|
|
1450
|
+
list.push(field.validate([...path, key]));
|
|
1451
|
+
}
|
|
1452
|
+
return Promise.all(list).then(v => v.flat())
|
|
1169
1453
|
}
|
|
1170
|
-
|
|
1171
1454
|
}
|
|
1172
1455
|
|
|
1456
|
+
/** @import { Schema } from '../types.mjs' */
|
|
1173
1457
|
|
|
1174
|
-
|
|
1458
|
+
/**
|
|
1459
|
+
* @template {Record<string, any>} [T=Record<string, any>]
|
|
1460
|
+
* @extends {Store<T>}
|
|
1461
|
+
*/
|
|
1175
1462
|
class ObjectStore extends Store {
|
|
1176
1463
|
get kind() { return 'object'; }
|
|
1177
1464
|
/** @type {Record<string, Store>} */
|
|
@@ -1187,24 +1474,18 @@
|
|
|
1187
1474
|
* @param {Schema.Object & Schema.Attr} schema
|
|
1188
1475
|
* @param {object} [options]
|
|
1189
1476
|
* @param {Store?} [options.parent]
|
|
1190
|
-
* @param {string |
|
|
1477
|
+
* @param {number | string | null} [options.index]
|
|
1191
1478
|
* @param {boolean} [options.new]
|
|
1192
|
-
* @param {(value: any,
|
|
1193
|
-
* @param {(value: any,
|
|
1479
|
+
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
|
|
1480
|
+
* @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdateState]
|
|
1194
1481
|
*/
|
|
1195
1482
|
constructor(schema,{ parent, index, new: isNew, onUpdate, onUpdateState } = {}) {
|
|
1196
1483
|
const childrenTypes = Object.entries(schema.type);
|
|
1197
1484
|
super(schema, {
|
|
1198
1485
|
parent, index, new: isNew, onUpdate, onUpdateState,
|
|
1199
1486
|
length: childrenTypes.length,
|
|
1200
|
-
setValue(v) {
|
|
1201
|
-
|
|
1202
|
-
return v;
|
|
1203
|
-
},
|
|
1204
|
-
setState(v) {
|
|
1205
|
-
if (typeof v !== 'object') { return {}; }
|
|
1206
|
-
return v;
|
|
1207
|
-
},
|
|
1487
|
+
setValue(v) { return typeof v === 'object' ? v : null; },
|
|
1488
|
+
setState(v) { return typeof v === 'object' ? v : null; },
|
|
1208
1489
|
convert(v, state) {
|
|
1209
1490
|
return [
|
|
1210
1491
|
typeof v === 'object' ? v : {},
|
|
@@ -1215,34 +1496,31 @@
|
|
|
1215
1496
|
const children = Object.create(null);
|
|
1216
1497
|
const childCommonOptions = {
|
|
1217
1498
|
parent: this,
|
|
1218
|
-
/** @param {*} value @param {*} index */
|
|
1219
|
-
onUpdate: (value, index) => {
|
|
1499
|
+
/** @param {*} value @param {*} index @param {Store} store */
|
|
1500
|
+
onUpdate: (value, index, store) => {
|
|
1501
|
+
if (store !== this.#children[index]) { return; }
|
|
1502
|
+
// @ts-ignore
|
|
1220
1503
|
this.value = {...this.value, [index]: value};
|
|
1221
1504
|
},
|
|
1222
|
-
/** @param {*} state @param {*} index */
|
|
1223
|
-
onUpdateState: (state, index) => {
|
|
1505
|
+
/** @param {*} state @param {*} index @param {Store} store */
|
|
1506
|
+
onUpdateState: (state, index, store) => {
|
|
1507
|
+
if (store !== this.#children[index]) { return; }
|
|
1224
1508
|
this.state = {...this.state, [index]: state};
|
|
1225
1509
|
}
|
|
1226
1510
|
};
|
|
1227
1511
|
|
|
1228
1512
|
for (const [index, field] of childrenTypes) {
|
|
1229
|
-
|
|
1230
|
-
if (typeof field.type === 'string') {
|
|
1231
|
-
if (field.array) {
|
|
1232
|
-
child = new ArrayStore(field, {...childCommonOptions, index});
|
|
1233
|
-
} else {
|
|
1234
|
-
child = new Store(field, {...childCommonOptions, index});
|
|
1235
|
-
}
|
|
1236
|
-
} else if (field.array) {
|
|
1237
|
-
child = new ArrayStore(field, {...childCommonOptions, index});
|
|
1238
|
-
} else {
|
|
1239
|
-
child = new ObjectStore(/**@type {*}*/(field), { ...childCommonOptions, index});
|
|
1240
|
-
}
|
|
1241
|
-
children[index] = child;
|
|
1513
|
+
children[index] = create(field, {...childCommonOptions, index});
|
|
1242
1514
|
}
|
|
1243
1515
|
this.#children = children;
|
|
1244
1516
|
}
|
|
1245
1517
|
}
|
|
1518
|
+
// @ts-ignore
|
|
1519
|
+
setObjectStore(ObjectStore);
|
|
1520
|
+
|
|
1521
|
+
/** @import { Schema } from '../types.mjs' */
|
|
1522
|
+
|
|
1523
|
+
|
|
1246
1524
|
|
|
1247
1525
|
/**
|
|
1248
1526
|
* @template [T=any]
|
|
@@ -1274,23 +1552,19 @@
|
|
|
1274
1552
|
* @param {Store?} [options.parent]
|
|
1275
1553
|
* @param {string | number | null} [options.index]
|
|
1276
1554
|
* @param {boolean} [options.new]
|
|
1277
|
-
* @param {(value: any, index: any) => void} [options.onUpdate]
|
|
1278
|
-
* @param {(value: any, index: any) => void} [options.onUpdateState]
|
|
1555
|
+
* @param {(value: any, index: any, store: Store) => void} [options.onUpdate]
|
|
1556
|
+
* @param {(value: any, index: any, store: Store) => void} [options.onUpdateState]
|
|
1279
1557
|
*/
|
|
1280
|
-
constructor(schema,
|
|
1558
|
+
constructor(schema, { parent, onUpdate, onUpdateState, index, new: isNew} = {}) {
|
|
1281
1559
|
const childrenState = new exports.Signal.State(/** @type {Store[]} */([]));
|
|
1282
1560
|
// @ts-ignore
|
|
1283
1561
|
const updateChildren = (list) => {
|
|
1284
|
-
if (this.destroyed) { return; }
|
|
1285
1562
|
const length = Array.isArray(list) && list.length || 0;
|
|
1286
1563
|
const children = [...childrenState.get()];
|
|
1287
1564
|
const oldLength = children.length;
|
|
1288
1565
|
for (let i = children.length; i < length; i++) {
|
|
1289
1566
|
children.push(this.#create(i));
|
|
1290
1567
|
}
|
|
1291
|
-
for (const schema of children.splice(length)) {
|
|
1292
|
-
schema.destroy();
|
|
1293
|
-
}
|
|
1294
1568
|
if (oldLength !== length) {
|
|
1295
1569
|
childrenState.set(children);
|
|
1296
1570
|
}
|
|
@@ -1300,27 +1574,28 @@
|
|
|
1300
1574
|
index, new: isNew, parent,
|
|
1301
1575
|
length: new exports.Signal.Computed(() => childrenState.get().length),
|
|
1302
1576
|
state: [],
|
|
1303
|
-
setValue(v) { return Array.isArray(v) ? v : v == null ?
|
|
1304
|
-
setState(v) { return Array.isArray(v) ? v : v == null ?
|
|
1577
|
+
setValue(v) { return Array.isArray(v) ? v : v == null ? null : [v] },
|
|
1578
|
+
setState(v) { return Array.isArray(v) ? v : v == null ? null : [v] },
|
|
1305
1579
|
convert(v, state) {
|
|
1306
|
-
const val = Array.isArray(v) ? v : v == null ?
|
|
1580
|
+
const val = Array.isArray(v) ? v : v == null ? null : [v];
|
|
1307
1581
|
updateChildren(val);
|
|
1308
1582
|
return [
|
|
1309
1583
|
val,
|
|
1310
1584
|
(Array.isArray(state) ? state : v == null ? [] : [state]),
|
|
1311
1585
|
];
|
|
1312
1586
|
},
|
|
1313
|
-
onUpdate:(value, index) => {
|
|
1587
|
+
onUpdate:(value, index, state) => {
|
|
1314
1588
|
updateChildren(value);
|
|
1315
|
-
onUpdate?.(value, index);
|
|
1589
|
+
onUpdate?.(value, index, state);
|
|
1316
1590
|
},
|
|
1317
1591
|
onUpdateState,
|
|
1318
1592
|
});
|
|
1319
1593
|
this.#children = childrenState;
|
|
1320
1594
|
const childCommonOptions = {
|
|
1321
1595
|
parent: this,
|
|
1322
|
-
/** @param {*} value @param {*} index */
|
|
1323
|
-
onUpdate: (value, index) => {
|
|
1596
|
+
/** @param {*} value @param {*} index @param {Store} store */
|
|
1597
|
+
onUpdate: (value, index, store) => {
|
|
1598
|
+
if (childrenState.get()[index] !== store) { return;}
|
|
1324
1599
|
const val = [...this.value || []];
|
|
1325
1600
|
if (val.length < index) {
|
|
1326
1601
|
val.length = index;
|
|
@@ -1328,8 +1603,9 @@
|
|
|
1328
1603
|
val[index] = value;
|
|
1329
1604
|
this.value = val;
|
|
1330
1605
|
},
|
|
1331
|
-
/** @param {*} state @param {*} index */
|
|
1332
|
-
onUpdateState: (state, index) => {
|
|
1606
|
+
/** @param {*} state @param {*} index @param {Store} store */
|
|
1607
|
+
onUpdateState: (state, index, store) => {
|
|
1608
|
+
if (childrenState.get()[index] !== store) { return;}
|
|
1333
1609
|
const sta = [...this.state || []];
|
|
1334
1610
|
if (sta.length < index) {
|
|
1335
1611
|
sta.length = index;
|
|
@@ -1338,33 +1614,21 @@
|
|
|
1338
1614
|
this.state = sta;
|
|
1339
1615
|
},
|
|
1340
1616
|
};
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
};
|
|
1347
|
-
} else if (schema.type && typeof schema.type === 'object' && !Array.isArray(schema.type)) {
|
|
1348
|
-
this.#create = (index, isNew) => {
|
|
1349
|
-
const child = new ObjectStore(/** @type {*} */(schema), { ...childCommonOptions, index, new: isNew});
|
|
1350
|
-
child.index = index;
|
|
1351
|
-
return child
|
|
1352
|
-
};
|
|
1353
|
-
} else {
|
|
1354
|
-
throw new Error();
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1617
|
+
this.#create = (index, isNew) => {
|
|
1618
|
+
const child = create(schema, {...childCommonOptions, index, new: isNew });
|
|
1619
|
+
child.index = index;
|
|
1620
|
+
return child
|
|
1621
|
+
};
|
|
1357
1622
|
}
|
|
1358
1623
|
/**
|
|
1359
1624
|
*
|
|
1360
1625
|
* @param {number} index
|
|
1361
|
-
* @param {T} value
|
|
1626
|
+
* @param {T?} [value]
|
|
1362
1627
|
* @param {boolean} [isNew]
|
|
1363
1628
|
* @returns
|
|
1364
1629
|
*/
|
|
1365
|
-
insert(index, value, isNew) {
|
|
1366
|
-
|
|
1367
|
-
const data = this.value;
|
|
1630
|
+
insert(index, value = null, isNew) {
|
|
1631
|
+
const data = this.value || [];
|
|
1368
1632
|
if (!Array.isArray(data)) { return false; }
|
|
1369
1633
|
const children = [...this.#children.get()];
|
|
1370
1634
|
const insertIndex = Math.max(0, Math.min(Math.floor(index), children.length));
|
|
@@ -1382,16 +1646,16 @@
|
|
|
1382
1646
|
sta.splice(insertIndex, 0, {});
|
|
1383
1647
|
this.state = sta;
|
|
1384
1648
|
}
|
|
1385
|
-
this.value = val;
|
|
1386
1649
|
this.#children.set(children);
|
|
1650
|
+
this.value = val;
|
|
1387
1651
|
return true;
|
|
1388
1652
|
}
|
|
1389
1653
|
/**
|
|
1390
1654
|
*
|
|
1391
|
-
* @param {T} value
|
|
1655
|
+
* @param {T?} [value]
|
|
1392
1656
|
* @returns
|
|
1393
1657
|
*/
|
|
1394
|
-
add(value) {
|
|
1658
|
+
add(value = null) {
|
|
1395
1659
|
return this.insert(this.#children.get().length, value);
|
|
1396
1660
|
}
|
|
1397
1661
|
/**
|
|
@@ -1400,7 +1664,6 @@
|
|
|
1400
1664
|
* @returns
|
|
1401
1665
|
*/
|
|
1402
1666
|
remove(index) {
|
|
1403
|
-
if (this.destroyed) { return; }
|
|
1404
1667
|
const data = this.value;
|
|
1405
1668
|
if (!Array.isArray(data)) { return; }
|
|
1406
1669
|
const children = [...this.#children.get()];
|
|
@@ -1410,7 +1673,6 @@
|
|
|
1410
1673
|
for (let i = index; i < children.length; i++) {
|
|
1411
1674
|
children[i].index = i;
|
|
1412
1675
|
}
|
|
1413
|
-
item.destroy();
|
|
1414
1676
|
const val = [...data];
|
|
1415
1677
|
const [value] = val.splice(removeIndex, 1);
|
|
1416
1678
|
const state = this.state;
|
|
@@ -1419,8 +1681,8 @@
|
|
|
1419
1681
|
sta.splice(removeIndex, 1);
|
|
1420
1682
|
this.state = sta;
|
|
1421
1683
|
}
|
|
1422
|
-
this.value = val;
|
|
1423
1684
|
this.#children.set(children);
|
|
1685
|
+
this.value = val;
|
|
1424
1686
|
return value;
|
|
1425
1687
|
|
|
1426
1688
|
}
|
|
@@ -1431,7 +1693,6 @@
|
|
|
1431
1693
|
* @returns
|
|
1432
1694
|
*/
|
|
1433
1695
|
move(from, to) {
|
|
1434
|
-
if (this.destroyed) { return false; }
|
|
1435
1696
|
const data = this.value;
|
|
1436
1697
|
if (!Array.isArray(data)) { return false; }
|
|
1437
1698
|
const children = [...this.#children.get()];
|
|
@@ -1457,8 +1718,8 @@
|
|
|
1457
1718
|
}
|
|
1458
1719
|
this.state = sta;
|
|
1459
1720
|
}
|
|
1460
|
-
this.value = val;
|
|
1461
1721
|
this.#children.set(children);
|
|
1722
|
+
this.value = val;
|
|
1462
1723
|
return true;
|
|
1463
1724
|
|
|
1464
1725
|
}
|
|
@@ -1469,7 +1730,6 @@
|
|
|
1469
1730
|
* @returns
|
|
1470
1731
|
*/
|
|
1471
1732
|
exchange(a, b) {
|
|
1472
|
-
if (this.destroyed) { return false; }
|
|
1473
1733
|
const data = this.value;
|
|
1474
1734
|
if (!Array.isArray(data)) { return false; }
|
|
1475
1735
|
const children = [...this.#children.get()];
|
|
@@ -1494,11 +1754,13 @@
|
|
|
1494
1754
|
sta[a] = bValue;
|
|
1495
1755
|
this.state = sta;
|
|
1496
1756
|
}
|
|
1497
|
-
this.value = val;
|
|
1498
1757
|
this.#children.set(children);
|
|
1758
|
+
this.value = val;
|
|
1499
1759
|
return true;
|
|
1500
1760
|
}
|
|
1501
1761
|
}
|
|
1762
|
+
// @ts-ignore
|
|
1763
|
+
setArrayStore(ArrayStore);
|
|
1502
1764
|
|
|
1503
1765
|
const errors = {
|
|
1504
1766
|
CALC: 'no `createCalc` option, no expression parsing support',
|
|
@@ -2410,6 +2672,7 @@
|
|
|
2410
2672
|
store: true,
|
|
2411
2673
|
parent: true,
|
|
2412
2674
|
root: true,
|
|
2675
|
+
ref: true,
|
|
2413
2676
|
|
|
2414
2677
|
schema: true,
|
|
2415
2678
|
|
|
@@ -2417,6 +2680,7 @@
|
|
|
2417
2680
|
readonly: true,
|
|
2418
2681
|
creatable: true,
|
|
2419
2682
|
immutable: true,
|
|
2683
|
+
changed: true,
|
|
2420
2684
|
|
|
2421
2685
|
required: true,
|
|
2422
2686
|
clearable: true,
|
|
@@ -2429,12 +2693,18 @@
|
|
|
2429
2693
|
min: true,
|
|
2430
2694
|
max: true,
|
|
2431
2695
|
step: true,
|
|
2696
|
+
minLength: true,
|
|
2697
|
+
maxLength: true,
|
|
2698
|
+
pattern: true,
|
|
2432
2699
|
values: true,
|
|
2433
2700
|
|
|
2434
2701
|
null: true,
|
|
2435
2702
|
index: true,
|
|
2436
2703
|
no: true,
|
|
2437
2704
|
length: true,
|
|
2705
|
+
|
|
2706
|
+
error: true,
|
|
2707
|
+
errors: true,
|
|
2438
2708
|
};
|
|
2439
2709
|
/** @type {Set<keyof typeof bindable>} */
|
|
2440
2710
|
// @ts-ignore
|
|
@@ -2455,6 +2725,9 @@
|
|
|
2455
2725
|
}
|
|
2456
2726
|
yield [`${key}${sign}value`, {get: () => val.value, set: v => val.value = v}];
|
|
2457
2727
|
yield [`${key}${sign}state`, {get: () => val.state, set: v => val.state = v}];
|
|
2728
|
+
yield [`${key}${sign}reset`, {exec: () => val.reset()}];
|
|
2729
|
+
// @ts-ignore
|
|
2730
|
+
yield [`${key}${sign}validate`, {exec: v => val.validate(v ? [] : null)}];
|
|
2458
2731
|
if (!(val instanceof ArrayStore)) { return; }
|
|
2459
2732
|
yield [`${key}${sign}insert`, {exec: (index, value) => val.insert(index, value)}];
|
|
2460
2733
|
yield [`${key}${sign}add`, {exec: (v) => val.add(v)}];
|
|
@@ -2527,6 +2800,17 @@
|
|
|
2527
2800
|
/** @import { ValueDefine, ExecDefine, CalcDefine } from './index.mjs' */
|
|
2528
2801
|
|
|
2529
2802
|
|
|
2803
|
+
/** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>} */
|
|
2804
|
+
const items = {
|
|
2805
|
+
value$: { calc(r) { return r?.[ref].value; } },
|
|
2806
|
+
state$: { calc(r) { return r?.[ref].state; } },
|
|
2807
|
+
};
|
|
2808
|
+
|
|
2809
|
+
var globals = Object.getOwnPropertyDescriptors(items);
|
|
2810
|
+
|
|
2811
|
+
/** @import { ValueDefine, ExecDefine, CalcDefine } from './index.mjs' */
|
|
2812
|
+
|
|
2813
|
+
|
|
2530
2814
|
/**
|
|
2531
2815
|
*
|
|
2532
2816
|
* @param {string} key
|
|
@@ -2544,7 +2828,7 @@
|
|
|
2544
2828
|
*/
|
|
2545
2829
|
function toGlobal(global) {
|
|
2546
2830
|
/** @type {Record<string, ValueDefine | ExecDefine | CalcDefine>} */
|
|
2547
|
-
const items = Object.create(null);
|
|
2831
|
+
const items = Object.create(null, globals);
|
|
2548
2832
|
if (!global || typeof global !== 'object') { return items; }
|
|
2549
2833
|
for (const [key, value] of Object.entries(global)) {
|
|
2550
2834
|
if (!testKey(key)) { continue; }
|
|
@@ -2714,6 +2998,9 @@
|
|
|
2714
2998
|
'$click': v => {store.emit('click', v); },
|
|
2715
2999
|
'$focus': v => {store.emit('focus', v); },
|
|
2716
3000
|
'$blur': v => {store.emit('blur', v); },
|
|
3001
|
+
'$reset': v => {store.reset(); },
|
|
3002
|
+
// @ts-ignore
|
|
3003
|
+
'$validate': v => {store.validate(v ? [] : null); },
|
|
2717
3004
|
}
|
|
2718
3005
|
}
|
|
2719
3006
|
|
|
@@ -3324,6 +3611,7 @@
|
|
|
3324
3611
|
}
|
|
3325
3612
|
|
|
3326
3613
|
/** @import { Component } from '../types.mjs' */
|
|
3614
|
+
/** @import Store from '../Store/index.mjs' */
|
|
3327
3615
|
/** @import Environment from './Environment/index.mjs' */
|
|
3328
3616
|
|
|
3329
3617
|
|
|
@@ -3448,9 +3736,10 @@
|
|
|
3448
3736
|
*
|
|
3449
3737
|
* @param {Component | string} component
|
|
3450
3738
|
* @param {Environment} env
|
|
3739
|
+
* @param {((store: Store, el: Element) => () => void)?} [relate]
|
|
3451
3740
|
* @returns
|
|
3452
3741
|
*/
|
|
3453
|
-
function createContext(component, env) {
|
|
3742
|
+
function createContext(component, env, relate) {
|
|
3454
3743
|
const tag = typeof component === 'string' ? component : component.tag;
|
|
3455
3744
|
const { attrs, events } = typeof component !== 'string' && component || { attrs: null, events: null };
|
|
3456
3745
|
|
|
@@ -3462,7 +3751,8 @@
|
|
|
3462
3751
|
const attrStates = Object.create(null);
|
|
3463
3752
|
const tagAttrs = Object.create(null);
|
|
3464
3753
|
|
|
3465
|
-
|
|
3754
|
+
/** @type {Set<() => void>} */
|
|
3755
|
+
const cancelFns = new Set();
|
|
3466
3756
|
|
|
3467
3757
|
/** @type {[string, ($event: any) => void, AddEventListenerOptions][]} */
|
|
3468
3758
|
const allEvents = [];
|
|
@@ -3482,13 +3772,28 @@
|
|
|
3482
3772
|
old = v;
|
|
3483
3773
|
fn(v, o, name);
|
|
3484
3774
|
}, true);
|
|
3485
|
-
|
|
3775
|
+
cancelFns.add(w);
|
|
3486
3776
|
|
|
3487
3777
|
return () => {
|
|
3488
|
-
|
|
3778
|
+
cancelFns.delete(w);
|
|
3489
3779
|
w();
|
|
3490
3780
|
};
|
|
3491
3781
|
},
|
|
3782
|
+
relate(el) {
|
|
3783
|
+
if (!relate || destroyed) { return () => { }; }
|
|
3784
|
+
try {
|
|
3785
|
+
const w = relate(env.store, el);
|
|
3786
|
+
if (typeof w !== 'function') { return () => { }; }
|
|
3787
|
+
cancelFns.add(w);
|
|
3788
|
+
return () => {
|
|
3789
|
+
cancelFns.delete(w);
|
|
3790
|
+
w();
|
|
3791
|
+
};
|
|
3792
|
+
} catch {
|
|
3793
|
+
return () => { };
|
|
3794
|
+
}
|
|
3795
|
+
|
|
3796
|
+
},
|
|
3492
3797
|
get destroyed() { return destroyedState.get(); },
|
|
3493
3798
|
get init() { return mountedState.get(); },
|
|
3494
3799
|
listen(name, listener) { return stateEmitter.listen(name, listener); },
|
|
@@ -3557,7 +3862,7 @@
|
|
|
3557
3862
|
if (destroyed) { return; }
|
|
3558
3863
|
destroyed = true;
|
|
3559
3864
|
destroyedState.set(true);
|
|
3560
|
-
for (const w of
|
|
3865
|
+
for (const w of cancelFns) {
|
|
3561
3866
|
w();
|
|
3562
3867
|
}
|
|
3563
3868
|
stateEmitter.emit('destroy');
|
|
@@ -4207,9 +4512,10 @@
|
|
|
4207
4512
|
* @param {Environment} env
|
|
4208
4513
|
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
4209
4514
|
* @param {string[]} componentPath
|
|
4515
|
+
* @param {((store: Store, el: Element) => () => void)?} [relate]
|
|
4210
4516
|
* @param {Component.Getter?} [getComponent]
|
|
4211
4517
|
*/
|
|
4212
|
-
function renderItem(layout, parent, next, env, templates, componentPath, getComponent) {
|
|
4518
|
+
function renderItem(layout, parent, next, env, templates, componentPath, relate, getComponent) {
|
|
4213
4519
|
env = env.set(layout.aliases, layout.vars);
|
|
4214
4520
|
const bind = layout.directives.bind;
|
|
4215
4521
|
const fragment = layout.directives.fragment;
|
|
@@ -4218,18 +4524,18 @@
|
|
|
4218
4524
|
if (!template) { return () => {}; }
|
|
4219
4525
|
const [templateLayout, templateEnv] = template;
|
|
4220
4526
|
const newEnv = templateEnv.params(templateLayout, layout, env, bind);
|
|
4221
|
-
return render(templateLayout, parent, next, newEnv, templates, componentPath, getComponent);
|
|
4527
|
+
return render(templateLayout, parent, next, newEnv, templates, componentPath, relate, getComponent);
|
|
4222
4528
|
}
|
|
4223
4529
|
if (!layout.name || layout.directives.fragment) {
|
|
4224
4530
|
return renderFillDirectives(parent, next, env, layout.directives) ||
|
|
4225
4531
|
renderList(layout.children || [], parent, next, env, templates, (layout, templates) => {
|
|
4226
|
-
return render(layout, parent, next, env, templates, componentPath, getComponent);
|
|
4532
|
+
return render(layout, parent, next, env, templates, componentPath, relate, getComponent);
|
|
4227
4533
|
});
|
|
4228
4534
|
}
|
|
4229
4535
|
const path = [...componentPath, layout.name];
|
|
4230
4536
|
const component = getComponent?.(path);
|
|
4231
4537
|
if (getComponent && !component) { return () => { }; }
|
|
4232
|
-
const { context, handler } = createContext(component ? component : layout.name, env);
|
|
4538
|
+
const { context, handler } = createContext(component ? component : layout.name, env, relate);
|
|
4233
4539
|
|
|
4234
4540
|
|
|
4235
4541
|
const componentAttrs = component?.attrs;
|
|
@@ -4256,7 +4562,7 @@
|
|
|
4256
4562
|
const children = slot ?
|
|
4257
4563
|
renderFillDirectives(slot, null, env, layout.directives)
|
|
4258
4564
|
|| renderList(layout.children || [], slot, null, env, templates, (layout, templates) => {
|
|
4259
|
-
return render(layout, slot, null, env, templates, componentPath, getComponent);
|
|
4565
|
+
return render(layout, slot, null, env, templates, componentPath, relate, getComponent);
|
|
4260
4566
|
}) : () => {};
|
|
4261
4567
|
|
|
4262
4568
|
|
|
@@ -4281,16 +4587,17 @@
|
|
|
4281
4587
|
* @param {Environment} env
|
|
4282
4588
|
* @param {Record<string, [Layout.Node, Environment]>} templates
|
|
4283
4589
|
* @param {string[]} componentPath
|
|
4590
|
+
* @param {((store: Store, el: Element) => () => void)?} [relate]
|
|
4284
4591
|
* @param {Component.Getter?} [getComponent]
|
|
4285
4592
|
* @returns {() => void}
|
|
4286
4593
|
*/
|
|
4287
|
-
function render(layout, parent, next, env, templates, componentPath, getComponent) {
|
|
4594
|
+
function render(layout, parent, next, env, templates, componentPath, relate, getComponent) {
|
|
4288
4595
|
const { directives } = layout;
|
|
4289
4596
|
const newEnv = env.child(directives.value);
|
|
4290
4597
|
if (!newEnv) { return () => {}; }
|
|
4291
4598
|
const list = newEnv.enum(directives.enum);
|
|
4292
4599
|
/** @type {(next: Node | null, env: any) => () => void} */
|
|
4293
|
-
const r = (next, env) => renderItem(layout, parent, next, env, templates, componentPath, getComponent);
|
|
4600
|
+
const r = (next, env) => renderItem(layout, parent, next, env, templates, componentPath, relate, getComponent);
|
|
4294
4601
|
if (list === true) {
|
|
4295
4602
|
return r(next, newEnv);
|
|
4296
4603
|
}
|
|
@@ -4307,37 +4614,21 @@
|
|
|
4307
4614
|
}
|
|
4308
4615
|
|
|
4309
4616
|
/**
|
|
4310
|
-
* @overload
|
|
4311
4617
|
* @param {Store} store
|
|
4312
4618
|
* @param {(Layout.Node | string)[]} layouts
|
|
4313
4619
|
* @param {Element} parent
|
|
4314
|
-
* @param {
|
|
4315
|
-
* @param {(
|
|
4620
|
+
* @param {object} [options]
|
|
4621
|
+
* @param {Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }>} [options.global]
|
|
4622
|
+
* @param {(path: string[]) => Component?} [options.component]
|
|
4623
|
+
* @param {(store: Store, el: Element) => () => void} [options.relate]
|
|
4316
4624
|
* @returns {() => void}
|
|
4317
4625
|
*/
|
|
4318
|
-
|
|
4319
|
-
* @overload
|
|
4320
|
-
* @param {Store} store
|
|
4321
|
-
* @param {(Layout.Node | string)[]} layouts
|
|
4322
|
-
* @param {Element} parent
|
|
4323
|
-
* @param {Component.Getter?} [components]
|
|
4324
|
-
* @returns {() => void}
|
|
4325
|
-
*/
|
|
4326
|
-
/**
|
|
4327
|
-
* @param {Store} store
|
|
4328
|
-
* @param {(Layout.Node | string)[]} layouts
|
|
4329
|
-
* @param {Element} parent
|
|
4330
|
-
* @param {Component.Getter | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt1]
|
|
4331
|
-
* @param {Component.Getter | Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }> | null} [opt2]
|
|
4332
|
-
*/
|
|
4333
|
-
function index (store, layouts, parent, opt1, opt2) {
|
|
4334
|
-
const options = [opt1, opt2];
|
|
4335
|
-
const components = options.find(v => typeof v === 'function');
|
|
4336
|
-
const global = options.find(v => typeof v === 'object');
|
|
4626
|
+
function index (store, layouts, parent, {component, global, relate} = {}) {
|
|
4337
4627
|
const env = new Environment(store, global);
|
|
4338
4628
|
const templates = Object.create(null);
|
|
4629
|
+
const relateFn = typeof relate === 'function' ? relate : null;
|
|
4339
4630
|
return renderList(layouts, parent, null, env, templates, (layout, templates) => {
|
|
4340
|
-
return render(layout, parent, null, env, templates, [],
|
|
4631
|
+
return render(layout, parent, null, env, templates, [], relateFn, component);
|
|
4341
4632
|
});
|
|
4342
4633
|
}
|
|
4343
4634
|
|