@neeloong/form 0.27.0 → 0.28.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.27.0
2
+ * @neeloong/form v0.28.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$1 = null;
141
+ let ObjectStore$2 = 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$1) { Class = ObjectStore$1; }
166
+ if (ObjectStore$2) { Class = ObjectStore$2; }
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$1 = Class;
173
+ ObjectStore$2 = Class;
174
174
  }
175
175
 
176
176
  /** @param {{new(...p: ConstructorParameters<typeof Store>): ArrayStore}} Class */
@@ -292,6 +292,52 @@ function makeDefault(store, def) {
292
292
  return (value) => structuredClone(def(store, value));
293
293
  }
294
294
 
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
+
295
341
  /** @import { Ref } from './ref.mjs' */
296
342
  /** @import { Schema } from '../Schema.types.mjs' */
297
343
  /** @import { StoreLayout } from '../StoreLayout.types.mjs' */
@@ -303,6 +349,12 @@ function makeDefault(store, def) {
303
349
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
304
350
  */
305
351
  class Store {
352
+ /** @type {Store<T, M, S>?} */
353
+ #originStore = null;
354
+
355
+ /** @type {Schema.Field<M, S>} 字段的 Schema 定义 */
356
+ #schema;
357
+ get schema() { return this.#schema; }
306
358
  /** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
307
359
  #events = new Map();
308
360
  /**
@@ -310,8 +362,11 @@ class Store {
310
362
  * @template {keyof Schema.Events} K
311
363
  * @param {K} event
312
364
  * @param {Schema.Events[K]} value
365
+ * @returns {boolean}
313
366
  */
314
367
  emit(event, value) {
368
+ const originStore = this.#originStore;
369
+ if (originStore) { return originStore.emit(event, value); }
315
370
  const key = typeof event === 'number' ? String(event) : event;
316
371
  const events = this.#events;
317
372
  let canceled = false;
@@ -328,6 +383,8 @@ class Store {
328
383
  * @returns {() => void}
329
384
  */
330
385
  listen(event, listener) {
386
+ const originStore = this.#originStore;
387
+ if (originStore) { return originStore.listen(event, p => listener.call(this, p, this)); }
331
388
  const fn = listener.bind(this);
332
389
  const events = this.#events;
333
390
  const key = typeof event === 'number' ? String(event) : event;
@@ -368,7 +425,7 @@ class Store {
368
425
  #ref = null;
369
426
  get ref() { return this.#ref || createRef(this); }
370
427
  /**
371
- * @param {Schema.Field<M, S>} schema 字段的 Schema 定义
428
+ * @param {Schema.Field<M, S> | Store<T,M,S>} schema 字段的 Schema 定义
372
429
  * @param {object} [options] 可选配置
373
430
  * @param {Store?} [options.parent]
374
431
  * @param {Partial<S>?} [options.states]
@@ -412,7 +469,74 @@ class Store {
412
469
  hidden, clearable, required, disabled, readonly, removable,
413
470
  label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
414
471
  } = {}) {
415
- this.schema = schema;
472
+ 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;
537
+ return;
538
+ }
539
+ this.#schema = schema;
416
540
  const parent = parentNode instanceof Store ? parentNode : null;
417
541
  if (parent) {
418
542
  this.#parent = parent;
@@ -540,6 +664,7 @@ class Store {
540
664
  /** @type {StoreLayout.Field<any>} */
541
665
  #layout;
542
666
  get layout() { return this.#layout; }
667
+ /** @type {(value?: any) => unknown} */
543
668
  #createDefault;
544
669
  /** @param {any} [value] @returns {any} */
545
670
  createDefault(value) { return this.#createDefault(value); }
@@ -821,40 +946,26 @@ class Store {
821
946
  #initValue = new Signal.State(/** @type {T?} */(null));
822
947
  #value = new Signal.State(this.#initValue.get());
823
948
 
949
+ /** @type {Set<Store>} */
950
+ #subBindStores = new Set();
824
951
  /**
825
952
  * @template [M=any]
826
953
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
827
954
  * @param {Schema<M, S>} schema 数据结构模式
955
+ * @param {AbortSignal} [signal]
956
+ * @returns {BindObjectStore}
828
957
  */
829
- bindObject(schema) {
830
- const bindStores = this.#bindStores;
831
- /** @type {Store[]} */
832
- const list = [];
833
- for (const [index, field] of Object.entries(schema)) {
834
- const bindStore = create(field, {
835
- index, parent: this,
836
- /** @param {*} value @param {*} currentIndex @param {Store} store */
837
- onUpdate: (value, currentIndex, store) => {
838
- if (index !== currentIndex) { return; }
839
- if (bindStores.has(store)) { return; }
840
- const val = this.#value ?? null;
841
- if (typeof val !== 'object' || Array.isArray(val)) { return }
842
- // @ts-ignore
843
- this.value = { ...val, [currentIndex]: value };
844
- },
845
- });
846
- list.push(bindStore);
847
- bindStores.set(bindStore, index);
848
- }
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));
849
966
  this.#requestUpdate();
850
- return () => {
851
- for (const bindStore of list) {
852
- bindStores.delete(bindStore);
853
- }
854
- };
967
+ return store;
855
968
  }
856
- /** @type {Map<Store, string>} */
857
- #bindStores = new Map();
858
969
 
859
970
  /** 内容是否已改变 */
860
971
  get changed() { return !Object.is(this.#value.get(), this.#initValue.get()); }
@@ -862,6 +973,8 @@ class Store {
862
973
  /** 字段当前值 */
863
974
  get value() { return this.#value.get(); }
864
975
  set value(v) {
976
+ const originStore = this.#originStore;
977
+ if (originStore) { originStore.value = v; return; }
865
978
  const newValue = this.#setValue?.(v);
866
979
  const val = newValue === undefined ? v : newValue;
867
980
  this.#value.set(val);
@@ -882,6 +995,8 @@ class Store {
882
995
  }
883
996
  /** 重置数据 */
884
997
  reset(value = this.#set ? this.#initValue.get() : this.#createDefault(), isNew = this.#selfNew.get()) {
998
+ const originStore = this.#originStore;
999
+ if (originStore) { originStore.reset(value, isNew); return; }
885
1000
  this.#reset(value, Boolean(isNew));
886
1001
  }
887
1002
  /**
@@ -898,11 +1013,10 @@ class Store {
898
1013
  this.#cancelBlur();
899
1014
  this.#set = true;
900
1015
  if (!value || typeof value !== 'object') {
901
- for (const [, field] of this) {
902
- field.#reset(null, false);
903
- }
904
- for (const [field] of this.#bindStores) {
905
- field.#reset(null, false);
1016
+ for (const bind of [this, ...this.#subBindStores]) {
1017
+ for (const [, field] of bind) {
1018
+ field.#reset(null, false);
1019
+ }
906
1020
  }
907
1021
  this.#value.set(value);
908
1022
  this.#initValue.set(value);
@@ -911,11 +1025,10 @@ class Store {
911
1025
  }
912
1026
  /** @type {*} */
913
1027
  const newValues = Array.isArray(value) ? [...value] : { ...value };
914
- for (const [key, field] of this) {
915
- newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
916
- }
917
- for (const [field, key] of this.#bindStores) {
918
- newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
1028
+ for (const bind of [this, ...this.#subBindStores]) {
1029
+ for (const [key, field] of bind) {
1030
+ newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
1031
+ }
919
1032
  }
920
1033
  this.#value.set(newValues);
921
1034
  this.#initValue.set(newValues);
@@ -949,23 +1062,16 @@ class Store {
949
1062
  // @ts-ignore
950
1063
  let newValues = Array.isArray(val) ? [...val] : { ...val };
951
1064
  let updated = false;
952
- for (const [key, field] of this) {
953
- // @ts-ignore
954
- const data = Object.hasOwn(val, key) ? val[key] : undefined;
955
- const newData = field.#toUpdate(data);
956
- if (Object.is(data, newData)) { continue; }
957
- // @ts-ignore
958
- newValues[key] = newData;
959
- updated = true;
960
- }
961
- for (const [field, key] of this.#bindStores) {
962
- // @ts-ignore
963
- const data = Object.hasOwn(val, key) ? val[key] : undefined;
964
- const newData = field.#toUpdate(data);
965
- if (Object.is(data, newData)) { continue; }
966
- // @ts-ignore
967
- newValues[key] = newData;
968
- updated = true;
1065
+ for (const bind of [this,...this.#subBindStores]) {
1066
+ for (const [key, field] of bind) {
1067
+ // @ts-ignore
1068
+ const data = Object.hasOwn(val, key) ? val[key] : undefined;
1069
+ const newData = field.#toUpdate(data);
1070
+ if (Object.is(data, newData)) { continue; }
1071
+ // @ts-ignore
1072
+ newValues[key] = newData;
1073
+ updated = true;
1074
+ }
969
1075
  }
970
1076
  if (updated) {
971
1077
  val = newValues;
@@ -999,6 +1105,7 @@ class Store {
999
1105
  */
1000
1106
  validate(path) {
1001
1107
  if (path === true) {
1108
+ if (this.#originStore) { return Promise.resolve(null); }
1002
1109
  return Promise.all([this.#validatorResult.get(), this.#changed(), this.#blurred()])
1003
1110
  .then(v => {
1004
1111
  const errors = v.flat();
@@ -1006,7 +1113,7 @@ class Store {
1006
1113
  });
1007
1114
  }
1008
1115
  const selfPath = Array.isArray(path) ? path : [];
1009
- if (this.#hidden.get()) { return Promise.resolve([]); }
1116
+ if (!this.#originStore && this.#hidden.get()) { return Promise.resolve([]); }
1010
1117
  const list = [this.validate(true).then(errors => {
1011
1118
  if (!errors?.length) { return []; }
1012
1119
  return [{ path: [...selfPath], store: /** @type {Store} */(this), errors }];
@@ -1014,8 +1121,8 @@ class Store {
1014
1121
  for (const [key, field] of this) {
1015
1122
  list.push(field.validate([...selfPath, key]));
1016
1123
  }
1017
- for (const [field, key] of this.#bindStores) {
1018
- list.push(field.validate([...selfPath, key]));
1124
+ for (const sub of this.#subBindStores) {
1125
+ list.push(sub.validate(selfPath));
1019
1126
  }
1020
1127
  return Promise.all(list).then(v => v.flat());
1021
1128
  }
@@ -1029,7 +1136,7 @@ class Store {
1029
1136
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1030
1137
  * @extends {Store<T, M, S>}
1031
1138
  */
1032
- class ObjectStore extends Store {
1139
+ let ObjectStore$1 = class ObjectStore extends Store {
1033
1140
  get kind() { return 'object'; }
1034
1141
  /** @type {Record<string, Store>} */
1035
1142
  #children;
@@ -1082,9 +1189,9 @@ class ObjectStore extends Store {
1082
1189
  }
1083
1190
  this.#children = children;
1084
1191
  }
1085
- }
1192
+ };
1086
1193
  // @ts-ignore
1087
- setObjectStore(ObjectStore);
1194
+ setObjectStore(ObjectStore$1);
1088
1195
 
1089
1196
  /** @import { Schema } from '../Schema.types.mjs' */
1090
1197
 
@@ -4754,7 +4861,7 @@ function renderChild(layout, parent, next, parentEnv, parentTemplates, component
4754
4861
  if (list instanceof ArrayStore) {
4755
4862
  return renderArray(parent, next, list, env, r, layout.sort);
4756
4863
  }
4757
- if (list instanceof ObjectStore) {
4864
+ if (list instanceof ObjectStore$1) {
4758
4865
  return renderObject(parent, next, list, env, r, layout.sort);
4759
4866
  }
4760
4867
  if (typeof list === 'function') {
@@ -6486,4 +6593,4 @@ function renderStore(store, fieldRenderer, root, layout, options) {
6486
6593
  }, { once: true });
6487
6594
  }
6488
6595
 
6489
- export { ArrayStore, index as Layout, ObjectStore, Store, effect, render, renderStore, watch };
6596
+ export { ArrayStore, index as Layout, ObjectStore$1 as ObjectStore, Store, effect, render, renderStore, watch };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neeloong/form",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "一个基于响应式数据绑定的表单渲染库",
5
5
  "keywords": [
6
6
  "from",