@neeloong/form 0.25.0 → 0.27.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.25.0
2
+ * @neeloong/form v0.27.0
3
3
  * (c) 2024-2026 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -145,7 +145,8 @@ let ArrayStoreClass = null;
145
145
  let TypeStores = Object.create(null);
146
146
  /**
147
147
  * @template [M=any]
148
- * @param {Schema.Field<M>} schema
148
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
149
+ * @param {Schema.Field<M, S>} schema
149
150
  * @param {object} [options]
150
151
  * @param {Store?} [options.parent]
151
152
  * @param {string | number | null} [options.index]
@@ -299,6 +300,7 @@ function makeDefault(store, def) {
299
300
  * 管理单个表单字段的状态和行为
300
301
  * @template [T=any]
301
302
  * @template [M=any]
303
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
302
304
  */
303
305
  class Store {
304
306
  /** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
@@ -341,7 +343,8 @@ class Store {
341
343
  /**
342
344
  * 从数据结构模式创建存储
343
345
  * @template [M=any]
344
- * @param {Schema<M>} schema 数据结构模式
346
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
347
+ * @param {Schema<M, S>} schema 数据结构模式
345
348
  * @param {object} [options] 选项
346
349
  * @param {boolean} [options.new] 是否为新建环境
347
350
  */
@@ -365,9 +368,10 @@ class Store {
365
368
  #ref = null;
366
369
  get ref() { return this.#ref || createRef(this); }
367
370
  /**
368
- * @param {Schema.Field<M>} schema 字段的 Schema 定义
371
+ * @param {Schema.Field<M, S>} schema 字段的 Schema 定义
369
372
  * @param {object} [options] 可选配置
370
373
  * @param {Store?} [options.parent]
374
+ * @param {Partial<S>?} [options.states]
371
375
  * @param {((store: Store, value?: any) => any) | object | number | string | boolean | null | undefined} [options.default]
372
376
  * @param {number | string | null} [options.index]
373
377
  * @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
@@ -402,7 +406,7 @@ class Store {
402
406
  */
403
407
  constructor(schema, {
404
408
  null: isNull, ref, default: defaultValue,
405
- setValue, convert, onUpdate,
409
+ setValue, convert, onUpdate, states,
406
410
  validator, validators,
407
411
  index, size, new: isNew, parent: parentNode,
408
412
  hidden, clearable, required, disabled, readonly, removable,
@@ -480,6 +484,22 @@ class Store {
480
484
 
481
485
  const validatorResult = createValidator(this, schema.validator, validator);
482
486
 
487
+
488
+ const schemaStates = schema.states;
489
+ this.#states = schemaStates ? Object.defineProperties(Object.create(null),
490
+ Object.fromEntries(
491
+ Object.entries(schemaStates).map(([k, { get, set, toState }]) => {
492
+ const state = new Signal.State(toState?.(states?.[k]));
493
+ const computed = new Signal.Computed(() => get(this, state));
494
+ return [k, {
495
+ configurable: true,
496
+ enumerable: true,
497
+ get() { return computed.get(); },
498
+ set(v) { return set?.(this, state, v); },
499
+ }];
500
+ })
501
+ )) : null;
502
+
483
503
  const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
484
504
  const [blurred, blurredResult, cancelBlur] = createAsyncValidator(this, schema.validators?.blur, validators?.blur);
485
505
  this.listen('change', () => { changed(); });
@@ -514,8 +534,11 @@ class Store {
514
534
  this.listen(k, f);
515
535
  }
516
536
  }
537
+ /** @type {S?} */
538
+ #states;
539
+ get states() { return this.#states; }
517
540
  /** @type {StoreLayout.Field<any>} */
518
- #layout
541
+ #layout;
519
542
  get layout() { return this.#layout; }
520
543
  #createDefault;
521
544
  /** @param {any} [value] @returns {any} */
@@ -691,9 +714,10 @@ class Store {
691
714
  #selfMin;
692
715
  /** @readonly @type {Signal.Computed<number?>} */
693
716
  #min;
717
+ /** @deprecated */
694
718
  get selfMin() { return this.#selfMin.get(); }
695
719
  set selfMin(v) { this.#selfMin.set(number(v)); }
696
- /** 数值字段的最小值限制 */
720
+ /** @deprecated 数值字段的最小值限制 */
697
721
  get min() { return this.#min.get(); }
698
722
  set min(v) { this.#selfMin.set(number(v)); }
699
723
 
@@ -702,9 +726,10 @@ class Store {
702
726
  #selfMax;
703
727
  /** @readonly @type {Signal.Computed<number?>} */
704
728
  #max;
729
+ /** @deprecated */
705
730
  get selfMax() { return this.#selfMax.get(); }
706
731
  set selfMax(v) { this.#selfMax.set(number(v)); }
707
- /** 数值字段的最大值限制 */
732
+ /** @deprecated 数值字段的最大值限制 */
708
733
  get max() { return this.#max.get(); }
709
734
  set max(v) { this.#selfMax.set(number(v)); }
710
735
 
@@ -713,9 +738,10 @@ class Store {
713
738
  #selfStep;
714
739
  /** @readonly @type {Signal.Computed<number?>} */
715
740
  #step;
741
+ /** @deprecated */
716
742
  get selfStep() { return this.#selfStep.get(); }
717
743
  set selfStep(v) { this.#selfStep.set(number(v)); }
718
- /** 数值字段的步长 */
744
+ /** @deprecated 数值字段的步长 */
719
745
  get step() { return this.#step.get(); }
720
746
  set step(v) { this.#selfStep.set(number(v)); }
721
747
 
@@ -723,9 +749,10 @@ class Store {
723
749
  #selfMinLength;
724
750
  /** @readonly @type {Signal.Computed<number?>} */
725
751
  #minLength;
752
+ /** @deprecated */
726
753
  get selfMinLength() { return this.#selfMinLength.get(); }
727
754
  set selfMinLength(v) { this.#selfMinLength.set(number(v)); }
728
- /** 最小长度 */
755
+ /** @deprecated 最小长度 */
729
756
  get minLength() { return this.#minLength.get(); }
730
757
  set minLength(v) { this.#selfMinLength.set(number(v)); }
731
758
 
@@ -733,9 +760,10 @@ class Store {
733
760
  #selfMaxLength;
734
761
  /** @readonly @type {Signal.Computed<number?>} */
735
762
  #maxLength;
763
+ /** @deprecated */
736
764
  get selfMaxLength() { return this.#selfMaxLength.get(); }
737
765
  set selfMaxLength(v) { this.#selfMaxLength.set(number(v)); }
738
- /** 最大长度 */
766
+ /** @deprecated 最大长度 */
739
767
  get maxLength() { return this.#maxLength.get(); }
740
768
  set maxLength(v) { this.#selfMaxLength.set(number(v)); }
741
769
 
@@ -743,9 +771,10 @@ class Store {
743
771
  #selfPattern;
744
772
  /** @readonly @type {Signal.Computed<RegExp?>} */
745
773
  #pattern;
774
+ /** @deprecated */
746
775
  get selfPattern() { return this.#selfPattern.get(); }
747
776
  set selfPattern(v) { this.#selfPattern.set(regex(v)); }
748
- /** 模式 */
777
+ /** @deprecated 模式 */
749
778
  get pattern() { return this.#pattern.get(); }
750
779
  set pattern(v) { this.#selfPattern.set(regex(v)); }
751
780
 
@@ -754,9 +783,10 @@ class Store {
754
783
  #selfValues;
755
784
  /** @readonly @type {Signal.Computed<(Schema.Value.Group | Schema.Value)[] | null>} */
756
785
  #values;
786
+ /** @deprecated */
757
787
  get selfValues() { return this.#selfValues.get(); }
758
788
  set selfValues(v) { this.#selfValues.set(values(v)); }
759
- /** 可选值列表 */
789
+ /** @deprecated 可选值列表 */
760
790
  get values() { return this.#values.get(); }
761
791
  set values(v) { this.#selfValues.set(values(v)); }
762
792
 
@@ -791,6 +821,40 @@ class Store {
791
821
  #initValue = new Signal.State(/** @type {T?} */(null));
792
822
  #value = new Signal.State(this.#initValue.get());
793
823
 
824
+ /**
825
+ * @template [M=any]
826
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
827
+ * @param {Schema<M, S>} schema 数据结构模式
828
+ */
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
+ }
849
+ this.#requestUpdate();
850
+ return () => {
851
+ for (const bindStore of list) {
852
+ bindStores.delete(bindStore);
853
+ }
854
+ };
855
+ }
856
+ /** @type {Map<Store, string>} */
857
+ #bindStores = new Map();
794
858
 
795
859
  /** 内容是否已改变 */
796
860
  get changed() { return !Object.is(this.#value.get(), this.#initValue.get()); }
@@ -837,6 +901,9 @@ class Store {
837
901
  for (const [, field] of this) {
838
902
  field.#reset(null, false);
839
903
  }
904
+ for (const [field] of this.#bindStores) {
905
+ field.#reset(null, false);
906
+ }
840
907
  this.#value.set(value);
841
908
  this.#initValue.set(value);
842
909
  this.#onUpdate?.(value, this.#index.get(), this);
@@ -847,6 +914,9 @@ class Store {
847
914
  for (const [key, field] of this) {
848
915
  newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
849
916
  }
917
+ for (const [field, key] of this.#bindStores) {
918
+ newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
919
+ }
850
920
  this.#value.set(newValues);
851
921
  this.#initValue.set(newValues);
852
922
  this.#onUpdate?.(newValues, this.#index.get(), this);
@@ -888,6 +958,15 @@ class Store {
888
958
  newValues[key] = newData;
889
959
  updated = true;
890
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;
969
+ }
891
970
  if (updated) {
892
971
  val = newValues;
893
972
  initValue = val;
@@ -935,6 +1014,9 @@ class Store {
935
1014
  for (const [key, field] of this) {
936
1015
  list.push(field.validate([...selfPath, key]));
937
1016
  }
1017
+ for (const [field, key] of this.#bindStores) {
1018
+ list.push(field.validate([...selfPath, key]));
1019
+ }
938
1020
  return Promise.all(list).then(v => v.flat());
939
1021
  }
940
1022
  }
@@ -944,7 +1026,8 @@ class Store {
944
1026
  /**
945
1027
  * @template {Record<string, any>} [T=Record<string, any>]
946
1028
  * @template [M=any]
947
- * @extends {Store<T, M>}
1029
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1030
+ * @extends {Store<T, M, S>}
948
1031
  */
949
1032
  class ObjectStore extends Store {
950
1033
  get kind() { return 'object'; }
@@ -958,7 +1041,7 @@ class ObjectStore extends Store {
958
1041
  */
959
1042
  child(key) { return this.#children[key] || null; }
960
1043
  /**
961
- * @param {Schema.Object<M> & Schema.Attr<M>} schema
1044
+ * @param {Schema.Object<M, S> & Schema.Attr<M, S>} schema
962
1045
  * @param {object} [options]
963
1046
  * @param {Store?} [options.parent]
964
1047
  * @param {number | string | null} [options.index]
@@ -1010,7 +1093,8 @@ setObjectStore(ObjectStore);
1010
1093
  /**
1011
1094
  * @template [T=any]
1012
1095
  * @template [M=any]
1013
- * @extends {Store<(T | null)[], M>}
1096
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1097
+ * @extends {Store<(T | null)[], M, S>}
1014
1098
  */
1015
1099
  class ArrayStore extends Store {
1016
1100
  /** @type {(index: number, isNew?: boolean) => Store} */
@@ -1033,7 +1117,7 @@ class ArrayStore extends Store {
1033
1117
  }
1034
1118
  get kind() { return 'array'; }
1035
1119
  /**
1036
- * @param {Schema.Field<M>} schema
1120
+ * @param {Schema.Field<M, S>} schema
1037
1121
  * @param {object} [options]
1038
1122
  * @param {Store?} [options.parent]
1039
1123
  * @param {string | number | null} [options.index]
@@ -1102,6 +1186,8 @@ class ArrayStore extends Store {
1102
1186
  return child;
1103
1187
  };
1104
1188
  }
1189
+ /** @returns {never} */
1190
+ bindObject() { throw new Error(`ArrayStore 不支持 bindObject()方法`); }
1105
1191
 
1106
1192
 
1107
1193
  /** @readonly @type {Signal.State<boolean?>} */
@@ -4219,7 +4305,7 @@ function renderObject(parent, next, store, env, renderItem, sort) {
4219
4305
  const children = [];
4220
4306
  const childStores = [...store];
4221
4307
  const count = childStores.length;
4222
- /** @type {[string, Store<any, any>, number][]} */
4308
+ /** @type {[string, Store<any, any, any>, number][]} */
4223
4309
  const stores = sort
4224
4310
  ? childStores
4225
4311
  .map(([k,v]) => [k,v,env.setStore(v, store).exec(sort)])
@@ -4766,7 +4852,7 @@ function createFieldFilter(field) {
4766
4852
  * @param {StoreLayout.Options?} options
4767
4853
  * @param {StoreLayout<T>} layout
4768
4854
  * @param {Node} [anchor]
4769
- * @param {(child?: Store<any, any> | undefined) => void} [dragenter]
4855
+ * @param {(child?: Store<any, any, any> | undefined) => void} [dragenter]
4770
4856
  * @returns {void}
4771
4857
  */
4772
4858
  function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragenter) {
@@ -4777,29 +4863,53 @@ function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragent
4777
4863
  if (tagName === 'nl-form-field') {
4778
4864
  const field = node.getAttribute('name') || '';
4779
4865
  const mode = node.getAttribute('mode') || '';
4866
+ const renderer = node.getAttribute('renderer') || '';
4867
+ const editable = options?.editable && !node.hasAttribute('non-editable');
4780
4868
  const fieldStore = field ? store.child(field) : store;
4781
4869
  if (!fieldStore) { return; }
4782
- const fieldLayout = field
4783
- ? layout?.fields?.find(createFieldFilter(field)) || fieldStore.layout
4784
- : { ...layout, html: '' };
4870
+ /** @type {HTMLElement?} */
4871
+ let el = null;
4785
4872
  switch (mode) {
4786
4873
  case 'grid': {
4787
- const el = Form(store, fieldRenderer, fieldLayout, options);
4788
- if (el) { node.replaceWith(el); }
4789
- return;
4874
+ const fieldLayout = field
4875
+ ? layout?.fields?.find(createFieldFilter(field)) || fieldStore.layout
4876
+ : { ...layout, html: '' };
4877
+ el = Form(fieldStore, fieldRenderer, fieldLayout, {...options, editable});
4878
+ break;
4879
+ }
4880
+ default: {
4881
+ el = fieldRenderer(fieldStore, renderer || layout.renderer, {...options, editable});
4882
+ break;
4790
4883
  }
4791
4884
  }
4792
- const res = fieldRenderer(fieldStore, layout.renderer, options);
4793
- if (res) {
4794
- node.replaceWith(res);
4885
+ if (!el) {
4886
+ const value = node.getAttribute('placeholder') || '';
4887
+ node.replaceWith(document.createTextNode(value));
4795
4888
  return;
4796
4889
  }
4797
- const value = node.getAttribute('placeholder') || '';
4798
- node.replaceWith(document.createTextNode(value));
4890
+ const className = node.getAttribute('class') || '';
4891
+ if (className) {
4892
+ el.setAttribute('class', [
4893
+ el.getAttribute('class') || '',
4894
+ className,
4895
+ ].filter(Boolean).join(' '));
4896
+ }
4897
+ const style = node.getAttribute('style') || '';
4898
+ if (style) {
4899
+ el.setAttribute('style', [
4900
+ el.getAttribute('style') || '',
4901
+ style,
4902
+ ].filter(Boolean).join(' '));
4903
+ }
4904
+ node.replaceWith(el);
4799
4905
  return;
4800
4906
  }
4801
4907
  if (tagName === 'nl-form-button') {
4802
4908
  const button = document.createElement('button');
4909
+ const className = node.getAttribute('class') || '';
4910
+ const style = node.getAttribute('style') || '';
4911
+ if (className) { button.setAttribute('class', className); }
4912
+ if (style) { button.setAttribute('style', style); }
4803
4913
  button.classList.add('NeeloongForm-item-button');
4804
4914
  const click = node.getAttribute('click') || '';
4805
4915
  const call = options?.call;
@@ -4970,9 +5080,10 @@ function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragent
4970
5080
  /**
4971
5081
  *
4972
5082
  * @param {string | ParentNode | null} [html]
5083
+ * @param {function(string): string} [sanitize]
4973
5084
  * @returns {ParentNode}
4974
5085
  */
4975
- function getHtmlContent(html) {
5086
+ function getHtmlContent(html, sanitize) {
4976
5087
  if (!html) {
4977
5088
  return document.createElement('template').content;
4978
5089
  }
@@ -4980,14 +5091,14 @@ function getHtmlContent(html) {
4980
5091
  return /** @type {ParentNode} */(html.cloneNode(true));
4981
5092
  }
4982
5093
  const template = document.createElement('template');
4983
- template.innerHTML = html;
5094
+ template.innerHTML = sanitize ? sanitize(html) : html;
4984
5095
  return template.content;
4985
5096
  }
4986
5097
 
4987
5098
  /**
4988
5099
  *
4989
5100
  * @template T
4990
- * @param {Store<any, any>} store
5101
+ * @param {Store<any, any, any>} store
4991
5102
  * @param {StoreLayout.Renderer<T>} fieldRenderer
4992
5103
  * @param {StoreLayout<T>} layout
4993
5104
  * @param {StoreLayout.Options?} options
@@ -4997,7 +5108,7 @@ function FormFieldInline(store, fieldRenderer, layout, options) {
4997
5108
  if (options?.signal?.aborted) { return null; }
4998
5109
  const html = layout.html;
4999
5110
  if (html) {
5000
- const content = getHtmlContent(html);
5111
+ const content = getHtmlContent(html, options?.sanitizeHtml);
5001
5112
  renderHtml(store, fieldRenderer, content, options, layout);
5002
5113
  return content;
5003
5114
  }
@@ -5010,7 +5121,7 @@ function FormFieldInline(store, fieldRenderer, layout, options) {
5010
5121
  /**
5011
5122
  *
5012
5123
  * @template T
5013
- * @param {Store<any, any>} store
5124
+ * @param {Store<any, any, any>} store
5014
5125
  * @param {StoreLayout.Renderer<T>} fieldRenderer
5015
5126
  * @param {StoreLayout.Field<T>} layout
5016
5127
  * @param {object} option
@@ -5089,12 +5200,12 @@ function Line(store, fieldRenderer, layout, {
5089
5200
  }
5090
5201
 
5091
5202
  for (const column of columns) {
5092
- const { actions, field, pattern } = column;
5203
+ const { actions, field, pattern, editable } = column;
5093
5204
  if (!actions?.length) {
5094
5205
  const td = head.appendChild(document.createElement('td'));
5095
5206
  const child = field && store.child(field);
5096
5207
  if (child) {
5097
- const el = FormFieldInline(child, fieldRenderer, column, options);
5208
+ const el = FormFieldInline(child, fieldRenderer, column, {...options, editable: options?.editable && (editable !== false)});
5098
5209
  if (el) { td.appendChild(el); }
5099
5210
  }
5100
5211
  continue;
@@ -5151,7 +5262,7 @@ function Line(store, fieldRenderer, layout, {
5151
5262
  * @param {ArrayStore} store
5152
5263
  * @param {StoreLayout.Field<T>} layout
5153
5264
  * @param {StoreLayout.Action[]} actionOptions
5154
- * @param {(fields: { field: string; width: any; label: any; }[]) => StoreLayout.Column<T>[]} createDefault
5265
+ * @param {(fields: { field: string; width: any; label: any; editable?: boolean? }[]) => StoreLayout.Column<T>[]} createDefault
5155
5266
  * @returns {StoreLayout.Column<T>[]}
5156
5267
  */
5157
5268
  function getColumns(store, layout, actionOptions, createDefault) {
@@ -5174,23 +5285,23 @@ function getColumns(store, layout, actionOptions, createDefault) {
5174
5285
  if (!actions) { return null; }
5175
5286
  return { actions };
5176
5287
  }
5177
- const { action, actions, field, placeholder, pattern, width, label } = v;
5288
+ const { action, actions, field, placeholder, pattern, width, label, editable } = v;
5178
5289
  if (field) {
5179
5290
  const define = map.get(field);
5180
5291
  if (define) {
5181
- return { field, placeholder, width, label: label || define.label };
5292
+ return { field, placeholder, width, label: label || define.label, editable };
5182
5293
  }
5183
5294
  }
5184
5295
  const options = new Set(actionOptions);
5185
5296
  const allActions = [action, actions].flat().filter(v => v && options.delete(v));
5186
5297
  if (allActions.length) {
5187
- return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label };
5298
+ return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label, editable };
5188
5299
  }
5189
5300
  if (pattern) {
5190
- return { pattern, placeholder, width, label };
5301
+ return { pattern, placeholder, width, label, editable };
5191
5302
  }
5192
5303
  if (placeholder || width) {
5193
- return { placeholder, width, label };
5304
+ return { placeholder, width, label, editable };
5194
5305
  }
5195
5306
  return null;
5196
5307
  });
@@ -5495,8 +5606,8 @@ function createCell(signal, layout, values, defCell, blockOnly) {
5495
5606
  /**
5496
5607
  *
5497
5608
  * @template T
5498
- * @param {Store<any, any>} store
5499
- * @param {Signal.State<Store<any, any>?>} currentStore
5609
+ * @param {Store<any, any, any>} store
5610
+ * @param {Signal.State<Store<any, any, any>?>} currentStore
5500
5611
  * @param {StoreLayout.Renderer<T>} fieldRenderer
5501
5612
  * @param {StoreLayout.Field<T>} layout
5502
5613
  * @param {Signal.State<State>} state
@@ -5510,7 +5621,7 @@ function createCell(signal, layout, values, defCell, blockOnly) {
5510
5621
  * @param {() => void} option.dragend
5511
5622
  * @param {{get(): boolean}} option.deletable
5512
5623
  * @param {() => void} option.addNode
5513
- * @param {(store: Store<any, any>) => () => void} option.createDetails
5624
+ * @param {(store: Store<any, any, any>) => () => void} option.createDetails
5514
5625
  * @param {StoreLayout.Options?} options
5515
5626
  * @returns {HTMLElement}
5516
5627
  */
@@ -5912,10 +6023,10 @@ function Tree(store, fieldRenderer, layout, options) {
5912
6023
 
5913
6024
  /** @type {AbortController?} */
5914
6025
  let detailAbortController = null;
5915
- const detailsStore = new Signal.State(/** @type{Store<any, any>?}*/(null));
6026
+ const detailsStore = new Signal.State(/** @type{Store<any, any, any>?}*/(null));
5916
6027
  /**
5917
6028
  *
5918
- * @param {Store<any, any>} store
6029
+ * @param {Store<any, any, any>} store
5919
6030
  * @returns
5920
6031
  */
5921
6032
  function createDetails(store) {
@@ -6167,14 +6278,14 @@ function Tree(store, fieldRenderer, layout, options) {
6167
6278
  *
6168
6279
  * @template T
6169
6280
  * @param {string | ParentNode} html
6170
- * @param {Store<any, any>} store
6281
+ * @param {Store<any, any, any>} store
6171
6282
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6172
6283
  * @param {StoreLayout.Options?} options
6173
6284
  * @param {StoreLayout.Field<T>} layout
6174
6285
  * @returns {ParentNode}
6175
6286
  */
6176
6287
  function Html(html, store, fieldRenderer, options, layout) {
6177
- const htmlContent = getHtmlContent(html);
6288
+ const htmlContent = getHtmlContent(html, options?.sanitizeHtml);
6178
6289
  renderHtml(store, fieldRenderer, htmlContent, options, layout);
6179
6290
  return htmlContent;
6180
6291
  }
@@ -6199,7 +6310,7 @@ function renderArrayCell(arrayStyle, store, fieldRenderer, layout, options) {
6199
6310
  /**
6200
6311
  *
6201
6312
  * @template T
6202
- * @param {Store<any, any>} store
6313
+ * @param {Store<any, any, any>} store
6203
6314
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6204
6315
  * @param {StoreLayout.Field<T>} layout
6205
6316
  * @param {StoreLayout.Options?} options
@@ -6233,7 +6344,7 @@ function FormField(store, fieldRenderer, layout, options) {
6233
6344
 
6234
6345
  /**
6235
6346
  *
6236
- * @param {Store<any, any>} store
6347
+ * @param {Store<any, any, any>} store
6237
6348
  * @param {StoreLayout.Button} layout
6238
6349
  * @param {StoreLayout.Options?} options
6239
6350
  * @returns {ParentNode}
@@ -6271,7 +6382,7 @@ function FormButton(store, layout, options) {
6271
6382
  /**
6272
6383
  *
6273
6384
  * @template T
6274
- * @param {Store<any, any>} store
6385
+ * @param {Store<any, any, any>} store
6275
6386
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6276
6387
  * @param {StoreLayout.Html} layout
6277
6388
  * @param {StoreLayout.Options?} options
@@ -6281,7 +6392,7 @@ function FormHtml(store, fieldRenderer, layout, options) {
6281
6392
  const html = layout.html;
6282
6393
  if (!html) { return null; }
6283
6394
  const [root, content] = createCell(options?.signal, layout, store);
6284
- const htmlContent = getHtmlContent(html);
6395
+ const htmlContent = getHtmlContent(html, options?.sanitizeHtml);
6285
6396
  renderHtml(store, fieldRenderer, htmlContent, options, layout);
6286
6397
  content.appendChild(htmlContent);
6287
6398
  return root;
@@ -6293,7 +6404,7 @@ function FormHtml(store, fieldRenderer, layout, options) {
6293
6404
  /**
6294
6405
  *
6295
6406
  * @template T
6296
- * @param {Store<any, any>} store
6407
+ * @param {Store<any, any, any>} store
6297
6408
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6298
6409
  * @param {StoreLayout.Item<T>} item
6299
6410
  * @param {StoreLayout.Options?} options
@@ -6318,7 +6429,7 @@ function FormItem(store, fieldRenderer, item, options) {
6318
6429
  /**
6319
6430
  *
6320
6431
  * @template T
6321
- * @param {Store<any, any>} store
6432
+ * @param {Store<any, any, any>} store
6322
6433
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6323
6434
  * @param {StoreLayout<T>} layout
6324
6435
  * @param {StoreLayout.Options?} options
@@ -6367,7 +6478,7 @@ function renderStore(store, fieldRenderer, root, layout, options) {
6367
6478
  Form(store, fieldRenderer, storeLayout, options || null, root);
6368
6479
  return;
6369
6480
  }
6370
- const content = getHtmlContent(html);
6481
+ const content = getHtmlContent(html, options?.sanitizeHtml);
6371
6482
  renderHtml(store, fieldRenderer, content, options || null, storeLayout);
6372
6483
  root.appendChild(content);
6373
6484
  options?.signal?.addEventListener('abort', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neeloong/form",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "一个基于响应式数据绑定的表单渲染库",
5
5
  "keywords": [
6
6
  "from",