@neeloong/form 0.29.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.29.0
2
+ * @neeloong/form v0.30.0
3
3
  * (c) 2024-2026 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -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
251
  }
252
- const list = results.flat().map(toResult).filter(Boolean);
253
- if (!signal.aborted && list.length) { st.set([...st.get(), ...list]); }
254
- return list;
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());
261
+ }
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' */
@@ -332,10 +380,25 @@ class Store {
332
380
  /**
333
381
  * 监听事件
334
382
  * @template {keyof Schema.Events} K
383
+ * @overload
335
384
  * @param {K} event
336
385
  * @param {(this: this, p: Schema.Events[K], store: this) => void | boolean | null} listener
337
386
  * @returns {() => void}
338
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
+ */
339
402
  listen(event, listener) {
340
403
  const originStore = this.#originStore;
341
404
  if (originStore) { return originStore.listen(event, p => listener.call(this, p, this)); }
@@ -385,73 +448,70 @@ class Store {
385
448
  constructor(schema, options) {
386
449
  if (schema instanceof Store) {
387
450
  const store = schema.#originStore || schema;
388
- this.#originStore= store;
389
- this.#schema= store.#schema;
390
- this.#null= store.#null;
391
- this.#ref= store.#ref;
392
- this.#states= store.#states;
393
- this.#layout= store.#layout;
394
- this.#createDefault= store.#createDefault;
395
- this.#setValue= store.#setValue;
396
- this.#convert= store.#convert;
397
- this.#onUpdate= store.#onUpdate;
398
- this.#parent= store.#parent;
399
- this.#root= store.#root;
400
- this.#type= store.#type;
401
- this.#meta= store.#meta;
402
- this.#component= store.#component;
403
- this.#selfLoading= store.#selfLoading;
404
- this.#loading= store.#loading;
405
- this.#size= store.#size;
406
- this.#index= store.#index;
407
- this.#creatable= store.#creatable;
408
- this.#immutable= store.#immutable;
409
- this.#new= store.#new;
410
- this.#selfNew= store.#selfNew;
411
- this.#selfHidden= store.#selfHidden;
412
- this.#hidden= store.#hidden;
413
- this.#selfClearable= store.#selfClearable;
414
- this.#clearable= store.#clearable;
415
- this.#selfRequired= store.#selfRequired;
416
- this.#required= store.#required;
417
- this.#selfDisabled= store.#selfDisabled;
418
- this.#disabled= store.#disabled;
419
- this.#selfReadonly= store.#selfReadonly;
420
- this.#readonly= store.#readonly;
421
- this.#selfRemovable= store.#selfRemovable;
422
- this.#removable= store.#removable;
423
- this.#selfLabel= store.#selfLabel;
424
- this.#label= store.#label;
425
- this.#selfDescription= store.#selfDescription;
426
- this.#description= store.#description;
427
- this.#selfPlaceholder= store.#selfPlaceholder;
428
- this.#placeholder= store.#placeholder;
429
- this.#selfMin= store.#selfMin;
430
- this.#min= store.#min;
431
- this.#selfMax= store.#selfMax;
432
- this.#max= store.#max;
433
- this.#selfStep= store.#selfStep;
434
- this.#step= store.#step;
435
- this.#selfMinLength= store.#selfMinLength;
436
- this.#minLength= store.#minLength;
437
- this.#selfMaxLength= store.#selfMaxLength;
438
- this.#maxLength= store.#maxLength;
439
- this.#selfPattern= store.#selfPattern;
440
- this.#pattern= store.#pattern;
441
- this.#selfValues= store.#selfValues;
442
- this.#values= store.#values;
443
- this.#errors= store.#errors;
444
- this.#validatorResult= store.#validatorResult;
445
- this.#changed= store.#changed;
446
- this.#blurred= store.#blurred;
447
- this.#cancelChange= store.#cancelChange;
448
- this.#cancelBlur= store.#cancelBlur;
449
- this.#set= store.#set;
450
- this.#initValue= store.#initValue;
451
- this.#value= store.#value;
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;
452
512
  const signal = options instanceof AbortSignal ? options : null;
453
513
  if (signal?.aborted) { return; }
454
- const subBindStores= store.#subBindStores;
514
+ const subBindStores = store.#subBindStores;
455
515
  subBindStores.add(this);
456
516
  signal?.addEventListener('abort', () => subBindStores.delete(this));
457
517
  store.#requestUpdate();
@@ -459,8 +519,7 @@ class Store {
459
519
  }
460
520
  const {
461
521
  null: isNull, ref, default: defaultValue,
462
- setValue, convert, onUpdate, states,
463
- validator, validators,
522
+ setValue, convert, onUpdate, states, validator,
464
523
  index, size, new: isNew, parent: parentNode,
465
524
  hidden, clearable, required, disabled, readonly, removable,
466
525
  label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
@@ -535,9 +594,6 @@ class Store {
535
594
 
536
595
  [this.#selfRemovable, this.#removable] = createBooleanStates(this, removable, schema.removable ?? true);
537
596
 
538
- const validatorResult = createValidator(this, schema.validator, validator);
539
-
540
-
541
597
  const schemaStates = schema.states;
542
598
  this.#states = schemaStates ? Object.defineProperties(Object.create(null),
543
599
  Object.fromEntries(
@@ -553,16 +609,13 @@ class Store {
553
609
  })
554
610
  )) : null;
555
611
 
556
- const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
557
- const [blurred, blurredResult, cancelBlur] = createAsyncValidator(this, schema.validators?.blur, validators?.blur);
558
- this.listen('change', () => { changed(); });
559
- this.listen('blur', () => { blurred(); });
560
- this.#errors = merge(validatorResult, changedResult, blurredResult);
561
- this.#validatorResult = validatorResult;
562
- this.#changed = changed;
563
- this.#blurred = blurred;
564
- this.#cancelChange = cancelChange;
565
- 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;
566
619
 
567
620
  if (size instanceof Signal.State || size instanceof Signal.Computed) {
568
621
  this.#size = size;
@@ -847,16 +900,10 @@ class Store {
847
900
 
848
901
  /** @type {Signal.Computed<string[]>} */
849
902
  #errors;
850
- /** @type {Signal.Computed<string[]>} */
851
- #validatorResult;
852
903
  /** @type {() => Promise<string[]>} */
853
- #changed;
854
- /** @type {() => Promise<string[]>} */
855
- #blurred;
856
- /** @type {() => void} */
857
- #cancelChange;
904
+ #execValidators;
858
905
  /** @type {() => void} */
859
- #cancelBlur;
906
+ #cancelEventValidator;
860
907
  /** 所有校验错误列表 */
861
908
  get errors() { return this.#errors.get(); }
862
909
  /** 字段校验错误信息 */
@@ -920,8 +967,7 @@ class Store {
920
967
  this.#selfNew.set(isNew);
921
968
  const newValue = this.#setValue?.(v);
922
969
  const value = newValue === undefined ? v : newValue;
923
- this.#cancelChange();
924
- this.#cancelBlur();
970
+ this.#cancelEventValidator();
925
971
  this.#set = true;
926
972
  if (!value || typeof value !== 'object') {
927
973
  for (const bind of [this, ...this.#subBindStores]) {
@@ -1017,11 +1063,7 @@ class Store {
1017
1063
  validate(path) {
1018
1064
  if (path === true) {
1019
1065
  if (this.#originStore) { return Promise.resolve(null); }
1020
- return Promise.all([this.#validatorResult.get(), this.#changed(), this.#blurred()])
1021
- .then(v => {
1022
- const errors = v.flat();
1023
- return errors.length ? errors : null;
1024
- });
1066
+ return this.#execValidators().then(errors => errors.length ? errors : null);
1025
1067
  }
1026
1068
  const selfPath = Array.isArray(path) ? path : [];
1027
1069
  if (!this.#originStore && this.#hidden.get()) { return Promise.resolve([]); }
@@ -1069,7 +1111,6 @@ class Store {
1069
1111
  * @property {RegExp} [pattern]
1070
1112
  * @property {(Schema.Value.Group | Schema.Value | string | number)[]} [values] 可选值
1071
1113
  * @property {Schema.Validator | Schema.Validator[] | null} [validator]
1072
- * @property {{[k in keyof Schema.Events]?: Schema.AsyncValidator | Schema.AsyncValidator[] | null}} [validators]
1073
1114
  *
1074
1115
  * @property {Ref?} [ref]
1075
1116
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neeloong/form",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "一个基于响应式数据绑定的表单渲染库",
5
5
  "keywords": [
6
6
  "from",