@neeloong/form 0.24.0 → 0.26.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.full.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.24.0
2
+ * @neeloong/form v0.26.0
3
3
  * (c) 2024-2026 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -723,7 +723,8 @@
723
723
  let TypeStores = Object.create(null);
724
724
  /**
725
725
  * @template [M=any]
726
- * @param {Schema.Field<M>} schema
726
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
727
+ * @param {Schema.Field<M, S>} schema
727
728
  * @param {object} [options]
728
729
  * @param {Store?} [options.parent]
729
730
  * @param {string | number | null} [options.index]
@@ -877,6 +878,7 @@
877
878
  * 管理单个表单字段的状态和行为
878
879
  * @template [T=any]
879
880
  * @template [M=any]
881
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
880
882
  */
881
883
  class Store {
882
884
  /** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
@@ -919,7 +921,8 @@
919
921
  /**
920
922
  * 从数据结构模式创建存储
921
923
  * @template [M=any]
922
- * @param {Schema<M>} schema 数据结构模式
924
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
925
+ * @param {Schema<M, S>} schema 数据结构模式
923
926
  * @param {object} [options] 选项
924
927
  * @param {boolean} [options.new] 是否为新建环境
925
928
  */
@@ -943,9 +946,10 @@
943
946
  #ref = null;
944
947
  get ref() { return this.#ref || createRef(this); }
945
948
  /**
946
- * @param {Schema.Field<M>} schema 字段的 Schema 定义
949
+ * @param {Schema.Field<M, S>} schema 字段的 Schema 定义
947
950
  * @param {object} [options] 可选配置
948
951
  * @param {Store?} [options.parent]
952
+ * @param {Partial<S>?} [options.states]
949
953
  * @param {((store: Store, value?: any) => any) | object | number | string | boolean | null | undefined} [options.default]
950
954
  * @param {number | string | null} [options.index]
951
955
  * @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
@@ -980,7 +984,7 @@
980
984
  */
981
985
  constructor(schema, {
982
986
  null: isNull, ref, default: defaultValue,
983
- setValue, convert, onUpdate,
987
+ setValue, convert, onUpdate, states,
984
988
  validator, validators,
985
989
  index, size, new: isNew, parent: parentNode,
986
990
  hidden, clearable, required, disabled, readonly, removable,
@@ -1058,6 +1062,22 @@
1058
1062
 
1059
1063
  const validatorResult = createValidator(this, schema.validator, validator);
1060
1064
 
1065
+
1066
+ const schemaStates = schema.states;
1067
+ this.#states = schemaStates ? Object.defineProperties(Object.create(null),
1068
+ Object.fromEntries(
1069
+ Object.entries(schemaStates).map(([k, { get, set, toState }]) => {
1070
+ const state = new exports.Signal.State(toState?.(states?.[k]));
1071
+ const computed = new exports.Signal.Computed(() => get(this, state));
1072
+ return [k, {
1073
+ configurable: true,
1074
+ enumerable: true,
1075
+ get() { return computed.get(); },
1076
+ set(v) { return set?.(this, state, v); },
1077
+ }];
1078
+ })
1079
+ )) : null;
1080
+
1061
1081
  const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
1062
1082
  const [blurred, blurredResult, cancelBlur] = createAsyncValidator(this, schema.validators?.blur, validators?.blur);
1063
1083
  this.listen('change', () => { changed(); });
@@ -1092,8 +1112,11 @@
1092
1112
  this.listen(k, f);
1093
1113
  }
1094
1114
  }
1115
+ /** @type {S?} */
1116
+ #states;
1117
+ get states() { return this.#states; }
1095
1118
  /** @type {StoreLayout.Field<any>} */
1096
- #layout
1119
+ #layout;
1097
1120
  get layout() { return this.#layout; }
1098
1121
  #createDefault;
1099
1122
  /** @param {any} [value] @returns {any} */
@@ -1269,9 +1292,10 @@
1269
1292
  #selfMin;
1270
1293
  /** @readonly @type {Signal.Computed<number?>} */
1271
1294
  #min;
1295
+ /** @deprecated */
1272
1296
  get selfMin() { return this.#selfMin.get(); }
1273
1297
  set selfMin(v) { this.#selfMin.set(number(v)); }
1274
- /** 数值字段的最小值限制 */
1298
+ /** @deprecated 数值字段的最小值限制 */
1275
1299
  get min() { return this.#min.get(); }
1276
1300
  set min(v) { this.#selfMin.set(number(v)); }
1277
1301
 
@@ -1280,9 +1304,10 @@
1280
1304
  #selfMax;
1281
1305
  /** @readonly @type {Signal.Computed<number?>} */
1282
1306
  #max;
1307
+ /** @deprecated */
1283
1308
  get selfMax() { return this.#selfMax.get(); }
1284
1309
  set selfMax(v) { this.#selfMax.set(number(v)); }
1285
- /** 数值字段的最大值限制 */
1310
+ /** @deprecated 数值字段的最大值限制 */
1286
1311
  get max() { return this.#max.get(); }
1287
1312
  set max(v) { this.#selfMax.set(number(v)); }
1288
1313
 
@@ -1291,9 +1316,10 @@
1291
1316
  #selfStep;
1292
1317
  /** @readonly @type {Signal.Computed<number?>} */
1293
1318
  #step;
1319
+ /** @deprecated */
1294
1320
  get selfStep() { return this.#selfStep.get(); }
1295
1321
  set selfStep(v) { this.#selfStep.set(number(v)); }
1296
- /** 数值字段的步长 */
1322
+ /** @deprecated 数值字段的步长 */
1297
1323
  get step() { return this.#step.get(); }
1298
1324
  set step(v) { this.#selfStep.set(number(v)); }
1299
1325
 
@@ -1301,9 +1327,10 @@
1301
1327
  #selfMinLength;
1302
1328
  /** @readonly @type {Signal.Computed<number?>} */
1303
1329
  #minLength;
1330
+ /** @deprecated */
1304
1331
  get selfMinLength() { return this.#selfMinLength.get(); }
1305
1332
  set selfMinLength(v) { this.#selfMinLength.set(number(v)); }
1306
- /** 最小长度 */
1333
+ /** @deprecated 最小长度 */
1307
1334
  get minLength() { return this.#minLength.get(); }
1308
1335
  set minLength(v) { this.#selfMinLength.set(number(v)); }
1309
1336
 
@@ -1311,9 +1338,10 @@
1311
1338
  #selfMaxLength;
1312
1339
  /** @readonly @type {Signal.Computed<number?>} */
1313
1340
  #maxLength;
1341
+ /** @deprecated */
1314
1342
  get selfMaxLength() { return this.#selfMaxLength.get(); }
1315
1343
  set selfMaxLength(v) { this.#selfMaxLength.set(number(v)); }
1316
- /** 最大长度 */
1344
+ /** @deprecated 最大长度 */
1317
1345
  get maxLength() { return this.#maxLength.get(); }
1318
1346
  set maxLength(v) { this.#selfMaxLength.set(number(v)); }
1319
1347
 
@@ -1321,9 +1349,10 @@
1321
1349
  #selfPattern;
1322
1350
  /** @readonly @type {Signal.Computed<RegExp?>} */
1323
1351
  #pattern;
1352
+ /** @deprecated */
1324
1353
  get selfPattern() { return this.#selfPattern.get(); }
1325
1354
  set selfPattern(v) { this.#selfPattern.set(regex(v)); }
1326
- /** 模式 */
1355
+ /** @deprecated 模式 */
1327
1356
  get pattern() { return this.#pattern.get(); }
1328
1357
  set pattern(v) { this.#selfPattern.set(regex(v)); }
1329
1358
 
@@ -1332,9 +1361,10 @@
1332
1361
  #selfValues;
1333
1362
  /** @readonly @type {Signal.Computed<(Schema.Value.Group | Schema.Value)[] | null>} */
1334
1363
  #values;
1364
+ /** @deprecated */
1335
1365
  get selfValues() { return this.#selfValues.get(); }
1336
1366
  set selfValues(v) { this.#selfValues.set(values(v)); }
1337
- /** 可选值列表 */
1367
+ /** @deprecated 可选值列表 */
1338
1368
  get values() { return this.#values.get(); }
1339
1369
  set values(v) { this.#selfValues.set(values(v)); }
1340
1370
 
@@ -1369,6 +1399,40 @@
1369
1399
  #initValue = new exports.Signal.State(/** @type {T?} */(null));
1370
1400
  #value = new exports.Signal.State(this.#initValue.get());
1371
1401
 
1402
+ /**
1403
+ * @template [M=any]
1404
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1405
+ * @param {Schema<M, S>} schema 数据结构模式
1406
+ */
1407
+ bindObject(schema) {
1408
+ const bindStores = this.#bindStores;
1409
+ /** @type {Store[]} */
1410
+ const list = [];
1411
+ for (const [index, field] of Object.entries(schema)) {
1412
+ const bindStore = create(field, {
1413
+ index, parent: this,
1414
+ /** @param {*} value @param {*} currentIndex @param {Store} store */
1415
+ onUpdate: (value, currentIndex, store) => {
1416
+ if (index !== currentIndex) { return; }
1417
+ if (bindStores.has(store)) { return; }
1418
+ const val = this.#value ?? null;
1419
+ if (typeof val !== 'object' || Array.isArray(val)) { return }
1420
+ // @ts-ignore
1421
+ this.value = { ...val, [currentIndex]: value };
1422
+ },
1423
+ });
1424
+ list.push(bindStore);
1425
+ bindStores.set(bindStore, index);
1426
+ }
1427
+ this.#requestUpdate();
1428
+ return () => {
1429
+ for (const bindStore of list) {
1430
+ bindStores.delete(bindStore);
1431
+ }
1432
+ };
1433
+ }
1434
+ /** @type {Map<Store, string>} */
1435
+ #bindStores = new Map();
1372
1436
 
1373
1437
  /** 内容是否已改变 */
1374
1438
  get changed() { return !Object.is(this.#value.get(), this.#initValue.get()); }
@@ -1415,6 +1479,9 @@
1415
1479
  for (const [, field] of this) {
1416
1480
  field.#reset(null, false);
1417
1481
  }
1482
+ for (const [field] of this.#bindStores) {
1483
+ field.#reset(null, false);
1484
+ }
1418
1485
  this.#value.set(value);
1419
1486
  this.#initValue.set(value);
1420
1487
  this.#onUpdate?.(value, this.#index.get(), this);
@@ -1425,6 +1492,9 @@
1425
1492
  for (const [key, field] of this) {
1426
1493
  newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
1427
1494
  }
1495
+ for (const [field, key] of this.#bindStores) {
1496
+ newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
1497
+ }
1428
1498
  this.#value.set(newValues);
1429
1499
  this.#initValue.set(newValues);
1430
1500
  this.#onUpdate?.(newValues, this.#index.get(), this);
@@ -1466,6 +1536,15 @@
1466
1536
  newValues[key] = newData;
1467
1537
  updated = true;
1468
1538
  }
1539
+ for (const [field, key] of this.#bindStores) {
1540
+ // @ts-ignore
1541
+ const data = Object.hasOwn(val, key) ? val[key] : undefined;
1542
+ const newData = field.#toUpdate(data);
1543
+ if (Object.is(data, newData)) { continue; }
1544
+ // @ts-ignore
1545
+ newValues[key] = newData;
1546
+ updated = true;
1547
+ }
1469
1548
  if (updated) {
1470
1549
  val = newValues;
1471
1550
  initValue = val;
@@ -1482,7 +1561,7 @@
1482
1561
  /**
1483
1562
  * 异步校验
1484
1563
  * @overload
1485
- * @param {true} [path]
1564
+ * @param {true} [self]
1486
1565
  * @returns {Promise<string[] | null>}
1487
1566
  */
1488
1567
  /**
@@ -1505,6 +1584,7 @@
1505
1584
  });
1506
1585
  }
1507
1586
  const selfPath = Array.isArray(path) ? path : [];
1587
+ if (this.#hidden.get()) { return Promise.resolve([]); }
1508
1588
  const list = [this.validate(true).then(errors => {
1509
1589
  if (!errors?.length) { return []; }
1510
1590
  return [{ path: [...selfPath], store: /** @type {Store} */(this), errors }];
@@ -1512,6 +1592,9 @@
1512
1592
  for (const [key, field] of this) {
1513
1593
  list.push(field.validate([...selfPath, key]));
1514
1594
  }
1595
+ for (const [field, key] of this.#bindStores) {
1596
+ list.push(field.validate([...selfPath, key]));
1597
+ }
1515
1598
  return Promise.all(list).then(v => v.flat());
1516
1599
  }
1517
1600
  }
@@ -1521,7 +1604,8 @@
1521
1604
  /**
1522
1605
  * @template {Record<string, any>} [T=Record<string, any>]
1523
1606
  * @template [M=any]
1524
- * @extends {Store<T, M>}
1607
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1608
+ * @extends {Store<T, M, S>}
1525
1609
  */
1526
1610
  class ObjectStore extends Store {
1527
1611
  get kind() { return 'object'; }
@@ -1535,7 +1619,7 @@
1535
1619
  */
1536
1620
  child(key) { return this.#children[key] || null; }
1537
1621
  /**
1538
- * @param {Schema.Object<M> & Schema.Attr<M>} schema
1622
+ * @param {Schema.Object<M, S> & Schema.Attr<M, S>} schema
1539
1623
  * @param {object} [options]
1540
1624
  * @param {Store?} [options.parent]
1541
1625
  * @param {number | string | null} [options.index]
@@ -1587,7 +1671,8 @@
1587
1671
  /**
1588
1672
  * @template [T=any]
1589
1673
  * @template [M=any]
1590
- * @extends {Store<(T | null)[], M>}
1674
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1675
+ * @extends {Store<(T | null)[], M, S>}
1591
1676
  */
1592
1677
  class ArrayStore extends Store {
1593
1678
  /** @type {(index: number, isNew?: boolean) => Store} */
@@ -1610,7 +1695,7 @@
1610
1695
  }
1611
1696
  get kind() { return 'array'; }
1612
1697
  /**
1613
- * @param {Schema.Field<M>} schema
1698
+ * @param {Schema.Field<M, S>} schema
1614
1699
  * @param {object} [options]
1615
1700
  * @param {Store?} [options.parent]
1616
1701
  * @param {string | number | null} [options.index]
@@ -1679,6 +1764,8 @@
1679
1764
  return child;
1680
1765
  };
1681
1766
  }
1767
+ /** @returns {never} */
1768
+ bindObject() { throw new Error(`ArrayStore 不支持 bindObject()方法`); }
1682
1769
 
1683
1770
 
1684
1771
  /** @readonly @type {Signal.State<boolean?>} */
@@ -4796,7 +4883,7 @@
4796
4883
  const children = [];
4797
4884
  const childStores = [...store];
4798
4885
  const count = childStores.length;
4799
- /** @type {[string, Store<any, any>, number][]} */
4886
+ /** @type {[string, Store<any, any, any>, number][]} */
4800
4887
  const stores = sort
4801
4888
  ? childStores
4802
4889
  .map(([k,v]) => [k,v,env.setStore(v, store).exec(sort)])
@@ -5343,7 +5430,7 @@
5343
5430
  * @param {StoreLayout.Options?} options
5344
5431
  * @param {StoreLayout<T>} layout
5345
5432
  * @param {Node} [anchor]
5346
- * @param {(child?: Store<any, any> | undefined) => void} [dragenter]
5433
+ * @param {(child?: Store<any, any, any> | undefined) => void} [dragenter]
5347
5434
  * @returns {void}
5348
5435
  */
5349
5436
  function renderHtml(store, fieldRenderer, node, options, layout, anchor, dragenter) {
@@ -5356,27 +5443,49 @@
5356
5443
  const mode = node.getAttribute('mode') || '';
5357
5444
  const fieldStore = field ? store.child(field) : store;
5358
5445
  if (!fieldStore) { return; }
5359
- const fieldLayout = field
5360
- ? layout?.fields?.find(createFieldFilter(field)) || fieldStore.layout
5361
- : { ...layout, html: '' };
5446
+ /** @type {HTMLElement?} */
5447
+ let el = null;
5362
5448
  switch (mode) {
5363
5449
  case 'grid': {
5364
- const el = Form(store, fieldRenderer, fieldLayout, options);
5365
- if (el) { node.replaceWith(el); }
5366
- return;
5450
+ const fieldLayout = field
5451
+ ? layout?.fields?.find(createFieldFilter(field)) || fieldStore.layout
5452
+ : { ...layout, html: '' };
5453
+ el = Form(fieldStore, fieldRenderer, fieldLayout, options);
5454
+ break;
5455
+ }
5456
+ default: {
5457
+ el = fieldRenderer(fieldStore, layout.renderer, options);
5458
+ break;
5367
5459
  }
5368
5460
  }
5369
- const res = fieldRenderer(fieldStore, layout.renderer, options);
5370
- if (res) {
5371
- node.replaceWith(res);
5461
+ if (!el) {
5462
+ const value = node.getAttribute('placeholder') || '';
5463
+ node.replaceWith(document.createTextNode(value));
5372
5464
  return;
5373
5465
  }
5374
- const value = node.getAttribute('placeholder') || '';
5375
- node.replaceWith(document.createTextNode(value));
5466
+ const className = node.getAttribute('class') || '';
5467
+ if (className) {
5468
+ el.setAttribute('class', [
5469
+ el.getAttribute('class') || '',
5470
+ className,
5471
+ ].filter(Boolean).join(' '));
5472
+ }
5473
+ const style = node.getAttribute('style') || '';
5474
+ if (style) {
5475
+ el.setAttribute('style', [
5476
+ el.getAttribute('style') || '',
5477
+ style,
5478
+ ].filter(Boolean).join(' '));
5479
+ }
5480
+ node.replaceWith(el);
5376
5481
  return;
5377
5482
  }
5378
5483
  if (tagName === 'nl-form-button') {
5379
5484
  const button = document.createElement('button');
5485
+ const className = node.getAttribute('class') || '';
5486
+ const style = node.getAttribute('style') || '';
5487
+ if (className) { button.setAttribute('class', className); }
5488
+ if (style) { button.setAttribute('style', style); }
5380
5489
  button.classList.add('NeeloongForm-item-button');
5381
5490
  const click = node.getAttribute('click') || '';
5382
5491
  const call = options?.call;
@@ -5564,7 +5673,7 @@
5564
5673
  /**
5565
5674
  *
5566
5675
  * @template T
5567
- * @param {Store<any, any>} store
5676
+ * @param {Store<any, any, any>} store
5568
5677
  * @param {StoreLayout.Renderer<T>} fieldRenderer
5569
5678
  * @param {StoreLayout<T>} layout
5570
5679
  * @param {StoreLayout.Options?} options
@@ -5587,7 +5696,7 @@
5587
5696
  /**
5588
5697
  *
5589
5698
  * @template T
5590
- * @param {Store<any, any>} store
5699
+ * @param {Store<any, any, any>} store
5591
5700
  * @param {StoreLayout.Renderer<T>} fieldRenderer
5592
5701
  * @param {StoreLayout.Field<T>} layout
5593
5702
  * @param {object} option
@@ -5626,6 +5735,7 @@
5626
5735
  const body = root.appendChild(document.createElement('tr'));
5627
5736
  const main = body.appendChild(document.createElement('td'));
5628
5737
  main.colSpan = columns.length;
5738
+ main.appendChild(form);
5629
5739
  body.hidden = true;
5630
5740
  trigger = () => {
5631
5741
  if (body.hidden) {
@@ -5665,12 +5775,12 @@
5665
5775
  }
5666
5776
 
5667
5777
  for (const column of columns) {
5668
- const { actions, field, pattern } = column;
5778
+ const { actions, field, pattern, editable } = column;
5669
5779
  if (!actions?.length) {
5670
5780
  const td = head.appendChild(document.createElement('td'));
5671
5781
  const child = field && store.child(field);
5672
5782
  if (child) {
5673
- const el = FormFieldInline(child, fieldRenderer, column, options);
5783
+ const el = FormFieldInline(child, fieldRenderer, column, {...options, editable: options?.editable && (editable !== false)});
5674
5784
  if (el) { td.appendChild(el); }
5675
5785
  }
5676
5786
  continue;
@@ -5718,6 +5828,64 @@
5718
5828
  return root;
5719
5829
  }
5720
5830
 
5831
+ /** @import { ArrayStore } from '../Store/index.mjs' */
5832
+ /** @import { StoreLayout } from '../StoreLayout.types.mjs' */
5833
+
5834
+ /**
5835
+ *
5836
+ * @template T
5837
+ * @param {ArrayStore} store
5838
+ * @param {StoreLayout.Field<T>} layout
5839
+ * @param {StoreLayout.Action[]} actionOptions
5840
+ * @param {(fields: { field: string; width: any; label: any; editable?: boolean? }[]) => StoreLayout.Column<T>[]} createDefault
5841
+ * @returns {StoreLayout.Column<T>[]}
5842
+ */
5843
+ function getColumns(store, layout, actionOptions, createDefault) {
5844
+ const fieldList = Object.entries(store.type || {})
5845
+ .filter(([k, v]) => typeof v?.type !== 'object')
5846
+ .map(([field, { width, label }]) => ({ field, width, label }));
5847
+ const headerColumns = layout.columns;
5848
+ if (Array.isArray(headerColumns)) {
5849
+ const map = new Map(fieldList.map(v => [v.field, v]));
5850
+ /** @type {(StoreLayout.Column<T> | null)[]} */
5851
+ const allColumns = headerColumns.map(v => {
5852
+ if (!v) { return null; }
5853
+ if (typeof v === 'number') { return { placeholder: v }; }
5854
+ if (typeof v === 'string') { return map.get(v) || null; }
5855
+ if (typeof v !== 'object') { return null; }
5856
+ if (Array.isArray(v)) {
5857
+ /** @type {Set<StoreLayout.Action>} */
5858
+ const options = new Set(actionOptions);
5859
+ const actions = v.filter(v => options.delete(v));
5860
+ if (!actions) { return null; }
5861
+ return { actions };
5862
+ }
5863
+ const { action, actions, field, placeholder, pattern, width, label, editable } = v;
5864
+ if (field) {
5865
+ const define = map.get(field);
5866
+ if (define) {
5867
+ return { field, placeholder, width, label: label || define.label, editable };
5868
+ }
5869
+ }
5870
+ const options = new Set(actionOptions);
5871
+ const allActions = [action, actions].flat().filter(v => v && options.delete(v));
5872
+ if (allActions.length) {
5873
+ return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label, editable };
5874
+ }
5875
+ if (pattern) {
5876
+ return { pattern, placeholder, width, label, editable };
5877
+ }
5878
+ if (placeholder || width) {
5879
+ return { placeholder, width, label, editable };
5880
+ }
5881
+ return null;
5882
+ });
5883
+ const columns = /** @type {StoreLayout.Column<T>[]} */(allColumns.filter(Boolean));
5884
+ if (columns.length) { return columns; }
5885
+ }
5886
+ return createDefault(fieldList);
5887
+ }
5888
+
5721
5889
  /** @import { Store, ArrayStore } from '../Store/index.mjs' */
5722
5890
  /** @import { StoreLayout } from '../StoreLayout.types.mjs' */
5723
5891
 
@@ -5748,6 +5916,7 @@
5748
5916
  watch(() => !addable.get(), disabled => { button.disabled = disabled; }, true, signal);
5749
5917
  }
5750
5918
  }
5919
+
5751
5920
  /**
5752
5921
  *
5753
5922
  * @template T
@@ -5759,54 +5928,15 @@
5759
5928
  */
5760
5929
  function Table(store, fieldRenderer, layout, options) {
5761
5930
  if (options?.signal?.aborted) { return null; }
5762
- const headerColumns = layout.columns;
5763
- const fieldList = Object.entries(store.type || {})
5764
- .filter(([k, v]) => typeof v?.type !== 'object')
5765
- .map(([field, { width, label }]) => ({ field, width, label }));
5766
- /** @type {StoreLayout.Column<T>[]} */
5767
- let columns = [];
5768
- if (Array.isArray(headerColumns)) {
5769
- const map = new Map(fieldList.map(v => [v.field, v]));
5770
-
5771
- /** @type {(StoreLayout.Column<T> | null)[]} */
5772
- const allColumns = headerColumns.map(v => {
5773
- if (!v) { return null; }
5774
- if (typeof v === 'number') { return { placeholder: v }; }
5775
- if (typeof v === 'string') { return map.get(v) || null; }
5776
- if (typeof v !== 'object') { return null; }
5777
- if (Array.isArray(v)) {
5778
- /** @type {Set<StoreLayout.Action>} */
5779
- const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
5780
- const actions = v.filter(v => options.delete(v));
5781
- if (!actions) { return null; }
5782
- return { actions };
5783
- }
5784
- const { action, actions, field, placeholder, pattern, width, label } = v;
5785
- if (field) {
5786
- const define = map.get(field);
5787
- if (define) {
5788
- return { field, placeholder, width, label: label || define.label };
5789
- }
5790
- }
5791
- const options = new Set(['add', 'move', 'trigger', 'remove', 'serial']);
5792
- const allActions = [action, actions].flat().filter(v => v && options.delete(v));
5793
- if (allActions.length) {
5794
- return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label };
5795
- }
5796
- // if (pattern) {
5797
- // return { pattern, placeholder, width, label };
5798
- // }
5799
- return null;
5800
- });
5801
- columns = /** @type {StoreLayout.Column<T>[]} */(allColumns.filter(Boolean));
5802
-
5803
- }
5804
- if (!columns.length) {
5805
- columns = [
5931
+ const columns = getColumns(
5932
+ store,
5933
+ layout,
5934
+ ['add', 'move', 'trigger', 'remove', 'serial'],
5935
+ fields => [
5806
5936
  { actions: ['add', 'trigger', 'move', 'remove', 'serial'] },
5807
- ...fieldList.slice(0, 3),
5808
- ];
5809
- }
5937
+ ...fields.slice(0, 3),
5938
+ ],
5939
+ );
5810
5940
 
5811
5941
  const table = document.createElement('table');
5812
5942
  table.classList.add('NeeloongForm-table');
@@ -6051,8 +6181,8 @@
6051
6181
  /**
6052
6182
  *
6053
6183
  * @template T
6054
- * @param {Store<any, any>} store
6055
- * @param {Signal.State<Store<any, any>?>} currentStore
6184
+ * @param {Store<any, any, any>} store
6185
+ * @param {Signal.State<Store<any, any, any>?>} currentStore
6056
6186
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6057
6187
  * @param {StoreLayout.Field<T>} layout
6058
6188
  * @param {Signal.State<State>} state
@@ -6066,7 +6196,7 @@
6066
6196
  * @param {() => void} option.dragend
6067
6197
  * @param {{get(): boolean}} option.deletable
6068
6198
  * @param {() => void} option.addNode
6069
- * @param {(store: Store<any, any>) => () => void} option.createDetails
6199
+ * @param {(store: Store<any, any, any>) => () => void} option.createDetails
6070
6200
  * @param {StoreLayout.Options?} options
6071
6201
  * @returns {HTMLElement}
6072
6202
  */
@@ -6383,56 +6513,16 @@
6383
6513
  */
6384
6514
  function Tree(store, fieldRenderer, layout, options) {
6385
6515
  if (options?.signal?.aborted) { return null; }
6386
- const headerColumns = layout.columns;
6387
- const fieldList = Object.entries(store.type || {})
6388
- .filter(([k, v]) => typeof v?.type !== 'object')
6389
- .map(([field, { width, label }]) => ({ field, width, label }));
6390
- /** @type {StoreLayout.Column<T>[]} */
6391
- let columns = [];
6392
- if (Array.isArray(headerColumns)) {
6393
- const map = new Map(fieldList.map(v => [v.field, v]));
6394
- /** @type {(StoreLayout.Column<T> | null)[]} */
6395
- const allColumns = headerColumns.map(v => {
6396
- if (!v) { return null; }
6397
- if (typeof v === 'number') { return { placeholder: v }; }
6398
- if (typeof v === 'string') { return map.get(v) || null; }
6399
- if (typeof v !== 'object') { return null; }
6400
- if (Array.isArray(v)) {
6401
- /** @type {Set<StoreLayout.Action>} */
6402
- const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
6403
- const actions = v.filter(v => options.delete(v));
6404
- if (!actions) { return null; }
6405
- return { actions };
6406
- }
6407
- const { action, actions, field, placeholder, pattern, width, label } = v;
6408
- if (field) {
6409
- const define = map.get(field);
6410
- if (define) {
6411
- return { field, placeholder, width, label: label || define.label };
6412
- }
6413
- }
6414
- const options = new Set(['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse']);
6415
- const allActions = [action, actions].flat().filter(v => v && options.delete(v));
6416
- if (allActions.length) {
6417
- return { actions: /** @type {StoreLayout.Action[]} */(allActions), width, label };
6418
- }
6419
- if (pattern) {
6420
- return { pattern, placeholder, width, label };
6421
- }
6422
- if (placeholder || width) {
6423
- return { placeholder, width, label };
6424
- }
6425
- return null;
6426
- });
6427
- columns = /** @type {StoreLayout.Column<T>[]} */(allColumns.filter(Boolean));
6428
- }
6429
- if (!columns.length) {
6430
- columns = [
6516
+ const columns = getColumns(
6517
+ store,
6518
+ layout,
6519
+ ['add', 'move', 'trigger', 'remove', 'serial', 'open', 'collapse'],
6520
+ fields => [
6431
6521
  { actions: ['collapse', 'move'] },
6432
- fieldList[0],
6522
+ fields[0],
6433
6523
  { actions: ['add', 'remove'] },
6434
- ];
6435
- }
6524
+ ]
6525
+ );
6436
6526
 
6437
6527
 
6438
6528
  const root = document.createElement('div');
@@ -6508,10 +6598,10 @@
6508
6598
 
6509
6599
  /** @type {AbortController?} */
6510
6600
  let detailAbortController = null;
6511
- const detailsStore = new exports.Signal.State(/** @type{Store<any, any>?}*/(null));
6601
+ const detailsStore = new exports.Signal.State(/** @type{Store<any, any, any>?}*/(null));
6512
6602
  /**
6513
6603
  *
6514
- * @param {Store<any, any>} store
6604
+ * @param {Store<any, any, any>} store
6515
6605
  * @returns
6516
6606
  */
6517
6607
  function createDetails(store) {
@@ -6763,7 +6853,7 @@
6763
6853
  *
6764
6854
  * @template T
6765
6855
  * @param {string | ParentNode} html
6766
- * @param {Store<any, any>} store
6856
+ * @param {Store<any, any, any>} store
6767
6857
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6768
6858
  * @param {StoreLayout.Options?} options
6769
6859
  * @param {StoreLayout.Field<T>} layout
@@ -6795,7 +6885,7 @@
6795
6885
  /**
6796
6886
  *
6797
6887
  * @template T
6798
- * @param {Store<any, any>} store
6888
+ * @param {Store<any, any, any>} store
6799
6889
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6800
6890
  * @param {StoreLayout.Field<T>} layout
6801
6891
  * @param {StoreLayout.Options?} options
@@ -6829,7 +6919,7 @@
6829
6919
 
6830
6920
  /**
6831
6921
  *
6832
- * @param {Store<any, any>} store
6922
+ * @param {Store<any, any, any>} store
6833
6923
  * @param {StoreLayout.Button} layout
6834
6924
  * @param {StoreLayout.Options?} options
6835
6925
  * @returns {ParentNode}
@@ -6867,7 +6957,7 @@
6867
6957
  /**
6868
6958
  *
6869
6959
  * @template T
6870
- * @param {Store<any, any>} store
6960
+ * @param {Store<any, any, any>} store
6871
6961
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6872
6962
  * @param {StoreLayout.Html} layout
6873
6963
  * @param {StoreLayout.Options?} options
@@ -6889,7 +6979,7 @@
6889
6979
  /**
6890
6980
  *
6891
6981
  * @template T
6892
- * @param {Store<any, any>} store
6982
+ * @param {Store<any, any, any>} store
6893
6983
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6894
6984
  * @param {StoreLayout.Item<T>} item
6895
6985
  * @param {StoreLayout.Options?} options
@@ -6914,7 +7004,7 @@
6914
7004
  /**
6915
7005
  *
6916
7006
  * @template T
6917
- * @param {Store<any, any>} store
7007
+ * @param {Store<any, any, any>} store
6918
7008
  * @param {StoreLayout.Renderer<T>} fieldRenderer
6919
7009
  * @param {StoreLayout<T>} layout
6920
7010
  * @param {StoreLayout.Options?} options