@neeloong/form 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.28.0
2
+ * @neeloong/form v0.30.0
3
3
  * (c) 2024-2026 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -138,7 +138,7 @@ function createRef(store) {
138
138
 
139
139
 
140
140
  /** @type {{new(...p: ConstructorParameters<typeof Store>): Store}?} */
141
- let ObjectStore$2 = null;
141
+ let ObjectStore$1 = null;
142
142
  /** @type {{new(...p: ConstructorParameters<typeof Store>): ArrayStore}?} */
143
143
  let ArrayStoreClass = null;
144
144
  /** @type {Record<string, {new(...p: ConstructorParameters<typeof Store>): Store}?>} */
@@ -163,14 +163,14 @@ function create(schema, options) {
163
163
  const C = TypeStores[type];
164
164
  if (C) { Class = C; }
165
165
  } else if (type && typeof type === 'object') {
166
- if (ObjectStore$2) { Class = ObjectStore$2; }
166
+ if (ObjectStore$1) { Class = ObjectStore$1; }
167
167
  }
168
168
  return new Class(schema, options);
169
169
  }
170
170
 
171
171
  /** @param {{new(...p: ConstructorParameters<typeof Store>): Store}} Class */
172
172
  function setObjectStore(Class) {
173
- ObjectStore$2 = Class;
173
+ ObjectStore$1 = Class;
174
174
  }
175
175
 
176
176
  /** @param {{new(...p: ConstructorParameters<typeof Store>): ArrayStore}} Class */
@@ -201,79 +201,127 @@ function toResult(v) {
201
201
  /**
202
202
  *
203
203
  * @param {Store} store
204
- * @param {...Schema.Validator | undefined | null | (Schema.Validator | undefined | null)[]} validators
204
+ * @param {Schema.SyncValidator[]} syncValidators
205
205
  * @returns
206
206
  */
207
- function createValidator(store, ...validators) {
208
- const allValidators = validators.flat().filter(v => typeof v === 'function');
209
- if (!allValidators.length) {
210
- return new Signal.Computed(() => /** @type {string[]} */([]));
211
- }
207
+ function createSyncValidator(store, syncValidators) {
212
208
  return new Signal.Computed(() => {
213
209
  const results = [];
214
- for (const validator of allValidators) {
210
+ for (const item of syncValidators) {
215
211
  try {
216
- results.push(validator(store));
217
- } catch (e){
212
+ results.push(item(store));
213
+ } catch (e) {
218
214
  results.push(e);
219
215
  }
220
216
  }
221
217
  return results.flat().map(toResult).filter(Boolean);
222
- })
218
+ });
223
219
  }
220
+
224
221
  /**
225
222
  *
226
223
  * @param {Store} store
227
- * @param {...Schema.AsyncValidator | undefined | null | (Schema.AsyncValidator | undefined | null)[]} validators
228
- * @returns {[exec: () => Promise<string[]>, state: Signal.Computed<string[]>, stop: () => void]}
224
+ * @param {Map<string, Schema.AsyncValidator[]>} eventsValidators
225
+ * @returns {[Record<string, () => Promise<string[]>>, results: Signal.State<string[]>[], stop: () => void]}
229
226
  */
230
- function createAsyncValidator(store, ...validators) {
231
- const allValidators = validators.flat().filter(v => typeof v === 'function');
232
- if (!allValidators.length) {
233
- return [
234
- ()=>Promise.resolve([]),
235
- new Signal.Computed(() => /** @type {string[]} */([])),
236
- () => {}
237
- ];
238
- }
239
- const st = new Signal.State(/** @type {string[]} */([]));
240
- /**
241
- *
242
- * @param {Schema.AsyncValidator} validator
243
- * @param {AbortSignal} signal
244
- */
245
- async function run(validator, signal) {
246
- let results = [];
247
- try {
248
- results.push(await validator(store, signal));
249
- } catch (e){
250
- results.push(e);
227
+ function createEventsValidator(store, eventsValidators) {
228
+ /** @type {Record<string, () => Promise<string[]>>} */
229
+ const eventExecMap = {};
230
+ /** @type {(() => void)[]} */
231
+ const allCancels = [];
232
+ /** @type {Signal.State<string[]>[]} */
233
+ const results = [];
234
+ for (const [name, validators] of eventsValidators) {
235
+ const st = new Signal.State(/** @type {string[]} */([]));
236
+ /**
237
+ *
238
+ * @param {Schema.AsyncValidator} validator
239
+ * @param {AbortSignal} signal
240
+ */
241
+ async function run(validator, signal) {
242
+ let results = [];
243
+ try {
244
+ results.push(await validator(store, signal));
245
+ } catch (e) {
246
+ results.push(e);
247
+ }
248
+ const list = results.flat().map(toResult).filter(Boolean);
249
+ if (!signal.aborted && list.length) { st.set([...st.get(), ...list]); }
250
+ return list;
251
+ }
252
+ /** @type {AbortController?} */
253
+ let ac = null;
254
+ function exec() {
255
+ ac?.abort();
256
+ ac = new AbortController();
257
+ const signal = ac.signal;
258
+ st.set([]);
259
+
260
+ return Promise.all(validators.map(f => run(f, signal))).then(v => v.flat());
251
261
  }
252
- const list = results.flat().map(toResult).filter(Boolean);
253
- if (!signal.aborted && list.length) { st.set([...st.get(), ...list]); }
254
- return list;
262
+ eventExecMap[name] = exec;
263
+ allCancels.push(() => { ac?.abort(); st.set([]); });
264
+ results.push(st);
255
265
  }
256
- /** @type {AbortController?} */
257
- let ac = null;
258
- function exec() {
259
- ac?.abort();
260
- ac = new AbortController();
261
- const signal = ac.signal;
262
- st.set([]);
263
-
264
- return Promise.all(allValidators.map(f => run(f, signal))).then(v => v.flat());
265
- }
266
- return [exec, new Signal.Computed(() => st.get()), () => {ac?.abort(); st.set([]);}]
266
+
267
+ function stop() {
268
+ for (const c of allCancels) {
269
+ c();
270
+ }
271
+
272
+ }
273
+
274
+ return [eventExecMap, results, stop];
267
275
  }
268
276
 
269
277
  /**
270
278
  *
271
- * @param {...Signal.Computed<string[]>} v
272
- * @returns
279
+ * @param {Store} store
280
+ * @param {...Schema.Validator | undefined | null | (Schema.Validator | undefined | null)[]} validators
281
+ * @returns {[exec: () => Promise<string[]>, Record<string, () => Promise<string[]>>, state: Signal.Computed<string[]>, stop: () => void]}
273
282
  */
274
- function merge(...v) {
275
- const list = /** @type {Signal.Computed<string[]>[]} */(v.filter(Boolean));
276
- return new Signal.Computed(() => list.flatMap(v => v.get()));
283
+ function createValidator(store, ...validators) {
284
+
285
+ /** @type {Schema.SyncValidator[]} */
286
+ const syncValidators = [];
287
+ /** @type {Map<string, Schema.AsyncValidator[]>} */
288
+ const eventsValidators = new Map();
289
+ for (const v of validators.flat()) {
290
+ if (!v) { continue; }
291
+ if (typeof v === 'function') {
292
+ syncValidators.push(v);
293
+ continue;
294
+ }
295
+ const { event, validator } = v;
296
+ if (typeof validator !== 'function') { continue; }
297
+ if (typeof event !== 'string') {
298
+ syncValidators.push(validator);
299
+ continue;
300
+ }
301
+ const list = eventsValidators.get(event);
302
+ if (list) {
303
+ list.push(validator);
304
+ } else {
305
+ eventsValidators.set(event, [validator]);
306
+ }
307
+ }
308
+
309
+
310
+ const validatorResult = syncValidators.length
311
+ ? createSyncValidator(store, syncValidators)
312
+ : new Signal.Computed(() => /** @type {string[]} */([]));
313
+ if (!eventsValidators.size) {
314
+ return [() => Promise.resolve(validatorResult.get()), {}, validatorResult, () => {}];
315
+ }
316
+
317
+ const [eventExecMap, results, stop] = createEventsValidator(store, eventsValidators);
318
+
319
+ const errors = new Signal.Computed(() => [validatorResult, ...results].flatMap(v => v.get()));
320
+ const execAll = () => Promise.all([
321
+ validatorResult.get(),
322
+ ...Object.values(eventExecMap).map(exec => exec()),
323
+ ]).then(v => v.flat());
324
+ return [execAll, eventExecMap, errors, stop];
277
325
  }
278
326
 
279
327
  /** @import Store from './Store.mjs' */
@@ -292,52 +340,6 @@ function makeDefault(store, def) {
292
340
  return (value) => structuredClone(def(store, value));
293
341
  }
294
342
 
295
- /** @import { Schema } from '../Schema.types.mjs' */
296
-
297
- /**
298
- * @template [T=any]
299
- * @template [M=any]
300
- * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
301
- * @extends {Store<T, M, S>}
302
- */
303
- class BindObjectStore extends Store {
304
-
305
- get kind() { return 'object'; }
306
- /** @type {Record<string, Store>} */
307
- #children = Object.create(null);
308
- *[Symbol.iterator]() { yield* Object.entries(this.#children); }
309
- /**
310
- *
311
- * @param {string | number} key
312
- * @returns {Store?}
313
- */
314
- child(key) { return this.#children[key] || null; }
315
- /**
316
- * @param {Schema<any, Object.<string, Schema.State>>} schema 数据结构模式
317
- * @param {Store<T, M, S>} store
318
- */
319
- constructor(schema, store) {
320
- super(store);
321
- const children = this.#children;
322
- for (const [index, field] of Object.entries(schema)) {
323
- const bindStore = create(field, {
324
- index, parent: this,
325
- /** @param {*} value @param {*} index @param {Store} store */
326
- onUpdate: (value, index, store) => {
327
- if (store !== children[index]) { return; }
328
- const val = this.value ?? null;
329
- if (typeof val !== 'object' || Array.isArray(val)) { return; }
330
- // @ts-ignore
331
- this.value = { ...val, [index]: value };
332
- },
333
- });
334
- children[index] = bindStore;
335
- }
336
- }
337
- }
338
- // @ts-ignore
339
- setObjectStore(ObjectStore);
340
-
341
343
  /** @import { Ref } from './ref.mjs' */
342
344
  /** @import { Schema } from '../Schema.types.mjs' */
343
345
  /** @import { StoreLayout } from '../StoreLayout.types.mjs' */
@@ -378,10 +380,25 @@ class Store {
378
380
  /**
379
381
  * 监听事件
380
382
  * @template {keyof Schema.Events} K
383
+ * @overload
381
384
  * @param {K} event
382
385
  * @param {(this: this, p: Schema.Events[K], store: this) => void | boolean | null} listener
383
386
  * @returns {() => void}
384
387
  */
388
+ /**
389
+ * 监听事件
390
+ * @template {keyof Schema.Events} K
391
+ * @overload
392
+ * @param {string} event
393
+ * @param {(this: this, p: unknown, store: this) => void | boolean | null} listener
394
+ * @returns {() => void}
395
+ */
396
+ /**
397
+ * 监听事件
398
+ * @param {string} event
399
+ * @param {(this: this, p: unknown, store: this) => void | boolean | null} listener
400
+ * @returns {() => void}
401
+ */
385
402
  listen(event, listener) {
386
403
  const originStore = this.#originStore;
387
404
  if (originStore) { return originStore.listen(event, p => listener.call(this, p, this)); }
@@ -426,116 +443,87 @@ class Store {
426
443
  get ref() { return this.#ref || createRef(this); }
427
444
  /**
428
445
  * @param {Schema.Field<M, S> | Store<T,M,S>} schema 字段的 Schema 定义
429
- * @param {object} [options] 可选配置
430
- * @param {Store?} [options.parent]
431
- * @param {Partial<S>?} [options.states]
432
- * @param {((store: Store, value?: any) => any) | object | number | string | boolean | null | undefined} [options.default]
433
- * @param {number | string | null} [options.index]
434
- * @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
435
- * @param {boolean} [options.null]
436
- * @param {boolean} [options.new]
437
- * @param {boolean} [options.hidden]
438
- * @param {boolean} [options.clearable]
439
- * @param {boolean} [options.required]
440
- * @param {boolean} [options.disabled]
441
- * @param {boolean} [options.readonly]
442
- * @param {boolean} [options.removable]
443
- *
444
- * @param {string} [options.label] 字段标签
445
- * @param {string} [options.description] 字段描述
446
- * @param {string} [options.placeholder] 占位符
447
- * @param {number} [options.min] 日期、时间、数字的最小值
448
- * @param {number} [options.max] 日期、时间、数字的最大值
449
- * @param {number} [options.step] 日期、时间、数字的步长
450
- * @param {number} [options.minLength]
451
- * @param {number} [options.maxLength]
452
- * @param {RegExp} [options.pattern]
453
- * @param {(Schema.Value.Group | Schema.Value | string | number)[]} [options.values] 可选值
454
- * @param {Schema.Validator | Schema.Validator[] | null} [options.validator]
455
- * @param {{[k in keyof Schema.Events]?: Schema.AsyncValidator | Schema.AsyncValidator[] | null}} [options.validators]
456
- *
457
- * @param {Ref?} [options.ref]
458
- *
459
- * @param {((value: any) => any)?} [options.setValue]
460
- * @param {((value: any) => any)?} [options.convert]
461
- *
462
- * @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
446
+ * @param {StoreOptions | AbortSignal | null} [options] 可选配置
463
447
  */
464
- constructor(schema, {
465
- null: isNull, ref, default: defaultValue,
466
- setValue, convert, onUpdate, states,
467
- validator, validators,
468
- index, size, new: isNew, parent: parentNode,
469
- hidden, clearable, required, disabled, readonly, removable,
470
- label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
471
- } = {}) {
448
+ constructor(schema, options) {
472
449
  if (schema instanceof Store) {
473
- this.#originStore = schema;
474
- this.#schema = schema.#schema;
475
- this.#null = schema.#null;
476
- this.#ref = schema.#ref;
477
- this.#states = schema.#states;
478
- this.#layout = schema.#layout;
479
- this.#createDefault = schema.#createDefault;
480
- this.#setValue = schema.#setValue;
481
- this.#convert = schema.#convert;
482
- this.#onUpdate = schema.#onUpdate;
483
- this.#parent = schema.#parent;
484
- this.#root = schema.#root;
485
- this.#type = schema.#type;
486
- this.#meta = schema.#meta;
487
- this.#component = schema.#component;
488
- this.#selfLoading = schema.#selfLoading;
489
- this.#loading = schema.#loading;
490
- this.#size = schema.#size;
491
- this.#index = schema.#index;
492
- this.#creatable = schema.#creatable;
493
- this.#immutable = schema.#immutable;
494
- this.#new = schema.#new;
495
- this.#selfNew = schema.#selfNew;
496
- this.#selfHidden = schema.#selfHidden;
497
- this.#hidden = schema.#hidden;
498
- this.#selfClearable = schema.#selfClearable;
499
- this.#clearable = schema.#clearable;
500
- this.#selfRequired = schema.#selfRequired;
501
- this.#required = schema.#required;
502
- this.#selfDisabled = schema.#selfDisabled;
503
- this.#disabled = schema.#disabled;
504
- this.#selfReadonly = schema.#selfReadonly;
505
- this.#readonly = schema.#readonly;
506
- this.#selfRemovable = schema.#selfRemovable;
507
- this.#removable = schema.#removable;
508
- this.#selfLabel = schema.#selfLabel;
509
- this.#label = schema.#label;
510
- this.#selfDescription = schema.#selfDescription;
511
- this.#description = schema.#description;
512
- this.#selfPlaceholder = schema.#selfPlaceholder;
513
- this.#placeholder = schema.#placeholder;
514
- this.#selfMin = schema.#selfMin;
515
- this.#min = schema.#min;
516
- this.#selfMax = schema.#selfMax;
517
- this.#max = schema.#max;
518
- this.#selfStep = schema.#selfStep;
519
- this.#step = schema.#step;
520
- this.#selfMinLength = schema.#selfMinLength;
521
- this.#minLength = schema.#minLength;
522
- this.#selfMaxLength = schema.#selfMaxLength;
523
- this.#maxLength = schema.#maxLength;
524
- this.#selfPattern = schema.#selfPattern;
525
- this.#pattern = schema.#pattern;
526
- this.#selfValues = schema.#selfValues;
527
- this.#values = schema.#values;
528
- this.#errors = schema.#errors;
529
- this.#validatorResult = schema.#validatorResult;
530
- this.#changed = schema.#changed;
531
- this.#blurred = schema.#blurred;
532
- this.#cancelChange = schema.#cancelChange;
533
- this.#cancelBlur = schema.#cancelBlur;
534
- this.#set = schema.#set;
535
- this.#initValue = schema.#initValue;
536
- this.#value = schema.#value;
450
+ const store = schema.#originStore || schema;
451
+ this.#originStore = store;
452
+ this.#schema = store.#schema;
453
+ this.#null = store.#null;
454
+ this.#ref = store.#ref;
455
+ this.#states = store.#states;
456
+ this.#layout = store.#layout;
457
+ this.#createDefault = store.#createDefault;
458
+ this.#setValue = store.#setValue;
459
+ this.#convert = store.#convert;
460
+ this.#onUpdate = store.#onUpdate;
461
+ this.#parent = store.#parent;
462
+ this.#root = store.#root;
463
+ this.#type = store.#type;
464
+ this.#meta = store.#meta;
465
+ this.#component = store.#component;
466
+ this.#selfLoading = store.#selfLoading;
467
+ this.#loading = store.#loading;
468
+ this.#size = store.#size;
469
+ this.#index = store.#index;
470
+ this.#creatable = store.#creatable;
471
+ this.#immutable = store.#immutable;
472
+ this.#new = store.#new;
473
+ this.#selfNew = store.#selfNew;
474
+ this.#selfHidden = store.#selfHidden;
475
+ this.#hidden = store.#hidden;
476
+ this.#selfClearable = store.#selfClearable;
477
+ this.#clearable = store.#clearable;
478
+ this.#selfRequired = store.#selfRequired;
479
+ this.#required = store.#required;
480
+ this.#selfDisabled = store.#selfDisabled;
481
+ this.#disabled = store.#disabled;
482
+ this.#selfReadonly = store.#selfReadonly;
483
+ this.#readonly = store.#readonly;
484
+ this.#selfRemovable = store.#selfRemovable;
485
+ this.#removable = store.#removable;
486
+ this.#selfLabel = store.#selfLabel;
487
+ this.#label = store.#label;
488
+ this.#selfDescription = store.#selfDescription;
489
+ this.#description = store.#description;
490
+ this.#selfPlaceholder = store.#selfPlaceholder;
491
+ this.#placeholder = store.#placeholder;
492
+ this.#selfMin = store.#selfMin;
493
+ this.#min = store.#min;
494
+ this.#selfMax = store.#selfMax;
495
+ this.#max = store.#max;
496
+ this.#selfStep = store.#selfStep;
497
+ this.#step = store.#step;
498
+ this.#selfMinLength = store.#selfMinLength;
499
+ this.#minLength = store.#minLength;
500
+ this.#selfMaxLength = store.#selfMaxLength;
501
+ this.#maxLength = store.#maxLength;
502
+ this.#selfPattern = store.#selfPattern;
503
+ this.#pattern = store.#pattern;
504
+ this.#selfValues = store.#selfValues;
505
+ this.#values = store.#values;
506
+ this.#errors = store.#errors;
507
+ this.#execValidators = store.#execValidators;
508
+ this.#cancelEventValidator = store.#cancelEventValidator;
509
+ this.#set = store.#set;
510
+ this.#initValue = store.#initValue;
511
+ this.#value = store.#value;
512
+ const signal = options instanceof AbortSignal ? options : null;
513
+ if (signal?.aborted) { return; }
514
+ const subBindStores = store.#subBindStores;
515
+ subBindStores.add(this);
516
+ signal?.addEventListener('abort', () => subBindStores.delete(this));
517
+ store.#requestUpdate();
537
518
  return;
538
519
  }
520
+ const {
521
+ null: isNull, ref, default: defaultValue,
522
+ setValue, convert, onUpdate, states, validator,
523
+ index, size, new: isNew, parent: parentNode,
524
+ hidden, clearable, required, disabled, readonly, removable,
525
+ label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
526
+ } = !(options instanceof AbortSignal) && options || {};
539
527
  this.#schema = schema;
540
528
  const parent = parentNode instanceof Store ? parentNode : null;
541
529
  if (parent) {
@@ -606,9 +594,6 @@ class Store {
606
594
 
607
595
  [this.#selfRemovable, this.#removable] = createBooleanStates(this, removable, schema.removable ?? true);
608
596
 
609
- const validatorResult = createValidator(this, schema.validator, validator);
610
-
611
-
612
597
  const schemaStates = schema.states;
613
598
  this.#states = schemaStates ? Object.defineProperties(Object.create(null),
614
599
  Object.fromEntries(
@@ -624,16 +609,13 @@ class Store {
624
609
  })
625
610
  )) : null;
626
611
 
627
- const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
628
- const [blurred, blurredResult, cancelBlur] = createAsyncValidator(this, schema.validators?.blur, validators?.blur);
629
- this.listen('change', () => { changed(); });
630
- this.listen('blur', () => { blurred(); });
631
- this.#errors = merge(validatorResult, changedResult, blurredResult);
632
- this.#validatorResult = validatorResult;
633
- this.#changed = changed;
634
- this.#blurred = blurred;
635
- this.#cancelChange = cancelChange;
636
- this.#cancelBlur = cancelBlur;
612
+ const [execValidators, eventExecMap, errors, cancelEventValidator] = createValidator(this, schema.validator, validator);
613
+ for (const [name, exec] of Object.entries(eventExecMap)) {
614
+ this.listen(name, () => { exec(); });
615
+ }
616
+ this.#errors = errors;
617
+ this.#execValidators = execValidators;
618
+ this.#cancelEventValidator = cancelEventValidator;
637
619
 
638
620
  if (size instanceof Signal.State || size instanceof Signal.Computed) {
639
621
  this.#size = size;
@@ -918,16 +900,10 @@ class Store {
918
900
 
919
901
  /** @type {Signal.Computed<string[]>} */
920
902
  #errors;
921
- /** @type {Signal.Computed<string[]>} */
922
- #validatorResult;
923
- /** @type {() => Promise<string[]>} */
924
- #changed;
925
903
  /** @type {() => Promise<string[]>} */
926
- #blurred;
904
+ #execValidators;
927
905
  /** @type {() => void} */
928
- #cancelChange;
929
- /** @type {() => void} */
930
- #cancelBlur;
906
+ #cancelEventValidator;
931
907
  /** 所有校验错误列表 */
932
908
  get errors() { return this.#errors.get(); }
933
909
  /** 字段校验错误信息 */
@@ -948,24 +924,6 @@ class Store {
948
924
 
949
925
  /** @type {Set<Store>} */
950
926
  #subBindStores = new Set();
951
- /**
952
- * @template [M=any]
953
- * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
954
- * @param {Schema<M, S>} schema 数据结构模式
955
- * @param {AbortSignal} [signal]
956
- * @returns {BindObjectStore}
957
- */
958
- bindObject(schema, signal) {
959
- const originStore = this.#originStore;
960
- if (originStore) { return originStore.bindObject(schema, signal); }
961
- const store = new BindObjectStore(schema, this);
962
- if (signal?.aborted) { return store; }
963
- const subBindStores = this.#subBindStores;
964
- subBindStores.add(store);
965
- signal?.addEventListener('abort', () => subBindStores.delete(store));
966
- this.#requestUpdate();
967
- return store;
968
- }
969
927
 
970
928
  /** 内容是否已改变 */
971
929
  get changed() { return !Object.is(this.#value.get(), this.#initValue.get()); }
@@ -1009,8 +967,7 @@ class Store {
1009
967
  this.#selfNew.set(isNew);
1010
968
  const newValue = this.#setValue?.(v);
1011
969
  const value = newValue === undefined ? v : newValue;
1012
- this.#cancelChange();
1013
- this.#cancelBlur();
970
+ this.#cancelEventValidator();
1014
971
  this.#set = true;
1015
972
  if (!value || typeof value !== 'object') {
1016
973
  for (const bind of [this, ...this.#subBindStores]) {
@@ -1062,7 +1019,7 @@ class Store {
1062
1019
  // @ts-ignore
1063
1020
  let newValues = Array.isArray(val) ? [...val] : { ...val };
1064
1021
  let updated = false;
1065
- for (const bind of [this,...this.#subBindStores]) {
1022
+ for (const bind of [this, ...this.#subBindStores]) {
1066
1023
  for (const [key, field] of bind) {
1067
1024
  // @ts-ignore
1068
1025
  const data = Object.hasOwn(val, key) ? val[key] : undefined;
@@ -1106,11 +1063,7 @@ class Store {
1106
1063
  validate(path) {
1107
1064
  if (path === true) {
1108
1065
  if (this.#originStore) { return Promise.resolve(null); }
1109
- return Promise.all([this.#validatorResult.get(), this.#changed(), this.#blurred()])
1110
- .then(v => {
1111
- const errors = v.flat();
1112
- return errors.length ? errors : null;
1113
- });
1066
+ return this.#execValidators().then(errors => errors.length ? errors : null);
1114
1067
  }
1115
1068
  const selfPath = Array.isArray(path) ? path : [];
1116
1069
  if (!this.#originStore && this.#hidden.get()) { return Promise.resolve([]); }
@@ -1128,6 +1081,45 @@ class Store {
1128
1081
  }
1129
1082
  }
1130
1083
 
1084
+ /**
1085
+ * @template [T=any]
1086
+ * @template [M=any]
1087
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1088
+ * @typedef {object} StoreOptions
1089
+ * @property {Store?} [parent]
1090
+ * @property {Partial<S>?} [states]
1091
+ * @property {((store: Store, value?: any) => any) | object | number | string | boolean | null | undefined} [default]
1092
+ * @property {number | string | null} [index]
1093
+ * @property {number | Signal.State<number> | Signal.Computed<number>} [size]
1094
+ * @property {boolean} [null]
1095
+ * @property {boolean} [new]
1096
+ * @property {boolean} [hidden]
1097
+ * @property {boolean} [clearable]
1098
+ * @property {boolean} [required]
1099
+ * @property {boolean} [disabled]
1100
+ * @property {boolean} [readonly]
1101
+ * @property {boolean} [removable]
1102
+ *
1103
+ * @property {string} [label] 字段标签
1104
+ * @property {string} [description] 字段描述
1105
+ * @property {string} [placeholder] 占位符
1106
+ * @property {number} [min] 日期、时间、数字的最小值
1107
+ * @property {number} [max] 日期、时间、数字的最大值
1108
+ * @property {number} [step] 日期、时间、数字的步长
1109
+ * @property {number} [minLength]
1110
+ * @property {number} [maxLength]
1111
+ * @property {RegExp} [pattern]
1112
+ * @property {(Schema.Value.Group | Schema.Value | string | number)[]} [values] 可选值
1113
+ * @property {Schema.Validator | Schema.Validator[] | null} [validator]
1114
+ *
1115
+ * @property {Ref?} [ref]
1116
+ *
1117
+ * @property {((value: any) => any)?} [setValue]
1118
+ * @property {((value: any) => any)?} [convert]
1119
+ *
1120
+ * @property {((value: T?, index: any, store: Store) => void)?} [onUpdate]
1121
+ */
1122
+
1131
1123
  /** @import { Schema } from '../Schema.types.mjs' */
1132
1124
 
1133
1125
  /**
@@ -1136,7 +1128,7 @@ class Store {
1136
1128
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1137
1129
  * @extends {Store<T, M, S>}
1138
1130
  */
1139
- let ObjectStore$1 = class ObjectStore extends Store {
1131
+ class ObjectStore extends Store {
1140
1132
  get kind() { return 'object'; }
1141
1133
  /** @type {Record<string, Store>} */
1142
1134
  #children;
@@ -1189,9 +1181,9 @@ let ObjectStore$1 = class ObjectStore extends Store {
1189
1181
  }
1190
1182
  this.#children = children;
1191
1183
  }
1192
- };
1184
+ }
1193
1185
  // @ts-ignore
1194
- setObjectStore(ObjectStore$1);
1186
+ setObjectStore(ObjectStore);
1195
1187
 
1196
1188
  /** @import { Schema } from '../Schema.types.mjs' */
1197
1189
 
@@ -1433,6 +1425,51 @@ class ArrayStore extends Store {
1433
1425
  // @ts-ignore
1434
1426
  setArrayStore(ArrayStore);
1435
1427
 
1428
+ /** @import { Schema } from '../Schema.types.mjs' */
1429
+
1430
+ /**
1431
+ * @template [T=any]
1432
+ * @template [M=any]
1433
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1434
+ * @extends {Store<T, M, S>}
1435
+ */
1436
+ class BindObjectStore extends Store {
1437
+
1438
+ get kind() { return 'object'; }
1439
+ /** @type {Record<string, Store>} */
1440
+ #children = Object.create(null);
1441
+ *[Symbol.iterator]() { yield* Object.entries(this.#children); }
1442
+ /**
1443
+ *
1444
+ * @param {string | number} key
1445
+ * @returns {Store?}
1446
+ */
1447
+ child(key) { return this.#children[key] || null; }
1448
+ /**
1449
+ * @param {Schema<any, Object.<string, Schema.State>>} schema 数据结构模式
1450
+ * @param {Store<T, M, S>} store
1451
+ * @param {AbortSignal} [signal]
1452
+ */
1453
+ constructor(schema, store, signal) {
1454
+ super(store, signal);
1455
+ const children = this.#children;
1456
+ for (const [index, field] of Object.entries(schema)) {
1457
+ const bindStore = create(field, {
1458
+ index, parent: this,
1459
+ /** @param {*} value @param {*} index @param {Store} store */
1460
+ onUpdate: (value, index, store) => {
1461
+ if (store !== children[index]) { return; }
1462
+ const val = this.value ?? null;
1463
+ if (typeof val !== 'object' || Array.isArray(val)) { return; }
1464
+ // @ts-ignore
1465
+ this.value = { ...val, [index]: value };
1466
+ },
1467
+ });
1468
+ children[index] = bindStore;
1469
+ }
1470
+ }
1471
+ }
1472
+
1436
1473
  /** @import * as Layout from './index.mjs' */
1437
1474
 
1438
1475
  /** @import { OldNode } from './createElement.mjs' */
@@ -4861,7 +4898,7 @@ function renderChild(layout, parent, next, parentEnv, parentTemplates, component
4861
4898
  if (list instanceof ArrayStore) {
4862
4899
  return renderArray(parent, next, list, env, r, layout.sort);
4863
4900
  }
4864
- if (list instanceof ObjectStore$1) {
4901
+ if (list instanceof ObjectStore) {
4865
4902
  return renderObject(parent, next, list, env, r, layout.sort);
4866
4903
  }
4867
4904
  if (typeof list === 'function') {
@@ -6593,4 +6630,4 @@ function renderStore(store, fieldRenderer, root, layout, options) {
6593
6630
  }, { once: true });
6594
6631
  }
6595
6632
 
6596
- export { ArrayStore, index as Layout, ObjectStore$1 as ObjectStore, Store, effect, render, renderStore, watch };
6633
+ export { ArrayStore, BindObjectStore, index as Layout, ObjectStore, Store, effect, render, renderStore, watch };