@neeloong/form 0.12.0 → 0.13.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/README.md CHANGED
@@ -175,6 +175,13 @@ render(store, layouts, app);
175
175
  <li !enum="items" !text="$item"></li> <!-- items 是数组 -->
176
176
  </ul>
177
177
  ```
178
+ #### 自定义排序
179
+
180
+ ```html
181
+ <ul>
182
+ <li !enum="object" !sort="$key" !text="$item"></li> <!-- object 是对象,将按照其键排序,渲染其值 -->
183
+ </ul>
184
+ ```
178
185
 
179
186
  #### 嵌套循环
180
187
 
@@ -284,7 +291,7 @@ render(store, layouts, app);
284
291
  1. 模板定义: `!template`
285
292
  1. 条件: `!if` `!else`
286
293
  1. 子属性: `!value`
287
- 1. 枚举: `!enum`
294
+ 1. 枚举: `!enum` `!sort`
288
295
  1. 别名、计算名与显式变量: `*别名` `*计算名` `+变量`
289
296
  1. 片段与模板调用: `!fragment`
290
297
  1. 属性与事件: `:绑定属性` `@事件` `普通属性` `!bind`
@@ -325,7 +332,9 @@ render(store, layouts, app);
325
332
  - `$kind` 只读 字段类别
326
333
  - `$error` 只读 字段校验错误信息
327
334
  - `$errors` 只读 所有校验错误列表
328
- 1. 数组字段扩展隐式函数(只在事件中可用)
335
+ - `$addable` 只读 是否可以为当前数组增加项目,非数组上下文总是为 `false`
336
+ - `$removable` 只读 是否可以将当前项从数组中移除,非数组成员上下文总是为 `false`
337
+ 1. 字段扩展隐式函数(只在事件中可用)
329
338
  - `$reset()` 重置数据
330
339
  - `$validate()` 触发当前项的异步校验
331
340
  - `$validate(true)` 触发当前项及其后代的异步校验
package/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.12.0
2
+ * @neeloong/form v0.13.0
3
3
  * (c) 2024-2025 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -253,6 +253,7 @@ type Enum = {
253
253
  templates?: Record<string, Template> | undefined;
254
254
  type: "enum";
255
255
  value: Node.Name | Node.Calc | Node.Value;
256
+ sort?: Node.Name | Node.Calc | Node.Value<any> | undefined;
256
257
  /**
257
258
  * 子元素
258
259
  */
@@ -653,6 +654,14 @@ declare namespace Schema {
653
654
  * 模式规则
654
655
  */
655
656
  pattern?: RegExp | ((store: Store) => RegExp | null) | null | undefined;
657
+ /**
658
+ * 数组内是否可添加
659
+ */
660
+ addable?: boolean | ((store: Store) => boolean) | null | undefined;
661
+ /**
662
+ * 数组内是否可移除
663
+ */
664
+ removable?: boolean | ((store: Store) => boolean) | null | undefined;
656
665
  /**
657
666
  * 可选值
658
667
  */
@@ -804,7 +813,7 @@ declare class Store<T = any, M = any> {
804
813
  /**
805
814
  * @param {Schema.Field<M>} schema 字段的 Schema 定义
806
815
  * @param {object} [options] 可选配置
807
- * @param {*} [options.parent]
816
+ * @param {Store?} [options.parent]
808
817
  * @param {*} [options.state]
809
818
  * @param {number | string | null} [options.index]
810
819
  * @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
@@ -813,8 +822,9 @@ declare class Store<T = any, M = any> {
813
822
  * @param {boolean} [options.hidden]
814
823
  * @param {boolean} [options.clearable]
815
824
  * @param {boolean} [options.required]
816
- * @param {boolean} [options.readonly]
817
825
  * @param {boolean} [options.disabled]
826
+ * @param {boolean} [options.readonly]
827
+ * @param {boolean} [options.removable]
818
828
  *
819
829
  * @param {string} [options.label] 字段标签
820
830
  * @param {string} [options.description] 字段描述
@@ -838,8 +848,8 @@ declare class Store<T = any, M = any> {
838
848
  * @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
839
849
  * @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdateState]
840
850
  */
841
- constructor(schema: Schema.Field<M>, { null: isNull, state, ref, setValue, setState, convert, onUpdate, onUpdateState, validator, validators, index, size, new: isNew, parent: parentNode, hidden, clearable, required, disabled, readonly, label, description, placeholder, min, max, step, minLength, maxLength, pattern, values }?: {
842
- parent?: any;
851
+ constructor(schema: Schema.Field<M>, { null: isNull, state, ref, setValue, setState, convert, onUpdate, onUpdateState, validator, validators, index, size, new: isNew, parent: parentNode, hidden, clearable, required, disabled, readonly, removable, label, description, placeholder, min, max, step, minLength, maxLength, pattern, values }?: {
852
+ parent?: Store<any, any> | null | undefined;
843
853
  state?: any;
844
854
  index?: string | number | null | undefined;
845
855
  size?: number | Signal.State<number> | Signal.Computed<number> | undefined;
@@ -848,8 +858,9 @@ declare class Store<T = any, M = any> {
848
858
  hidden?: boolean | undefined;
849
859
  clearable?: boolean | undefined;
850
860
  required?: boolean | undefined;
851
- readonly?: boolean | undefined;
852
861
  disabled?: boolean | undefined;
862
+ readonly?: boolean | undefined;
863
+ removable?: boolean | undefined;
853
864
  label?: string | undefined;
854
865
  description?: string | undefined;
855
866
  placeholder?: string | undefined;
@@ -964,6 +975,11 @@ declare class Store<T = any, M = any> {
964
975
  set readonly(v: boolean);
965
976
  /** 是否只读 */
966
977
  get readonly(): boolean;
978
+ set selfRemovable(v: boolean | null);
979
+ get selfRemovable(): boolean | null;
980
+ set removable(v: boolean);
981
+ /** 是否只读 */
982
+ get removable(): boolean;
967
983
  set selfLabel(v: string | null);
968
984
  get selfLabel(): string | null;
969
985
  set label(v: string | null);
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.12.0
2
+ * @neeloong/form v0.13.0
3
3
  * (c) 2024-2025 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -595,17 +595,12 @@
595
595
  * @param {Signal.Computed<boolean>?} [parent]
596
596
  * @returns {[Signal.State<boolean?>, Signal.Computed<boolean>]}
597
597
  */
598
- const createBooleanStates = (self, defState, fn, parent) => {
598
+ function createBooleanStates(self, defState, fn, parent) {
599
599
 
600
600
  const selfState = new exports.Signal.State(typeof defState === 'boolean' ? defState : null);
601
- /** @type {Signal.Computed<boolean>} */
602
- let scriptState;
603
- if (typeof fn === 'function') {
604
- scriptState = new exports.Signal.Computed(() => Boolean(fn(self)));
605
- } else {
606
- const def = Boolean(fn);
607
- scriptState = new exports.Signal.Computed(() => def);
608
- }
601
+ const scriptState = typeof fn === 'function'
602
+ ? new exports.Signal.Computed(() => Boolean(fn(self)))
603
+ : new exports.Signal.State(Boolean(fn));
609
604
 
610
605
  const getState = () => {
611
606
  const s = selfState.get();
@@ -617,7 +612,7 @@
617
612
 
618
613
  return [selfState, state];
619
614
 
620
- };
615
+ }
621
616
 
622
617
  /** @import { Schema } from '../types.mjs' */
623
618
  /** @param {*} v */
@@ -717,12 +712,13 @@
717
712
  }
718
713
 
719
714
  /** @import { Schema } from '../types.mjs' */
715
+ /** @import ArrayStore from './ArrayStore.mjs' */
720
716
 
721
717
 
722
718
  /** @type {{new(...p: ConstructorParameters<typeof Store>): Store}?} */
723
719
  let ObjectStore$1 = null;
724
- /** @type {{new(...p: ConstructorParameters<typeof Store>): Store}?} */
725
- let ArrayStore$1 = null;
720
+ /** @type {{new(...p: ConstructorParameters<typeof Store>): ArrayStore}?} */
721
+ let ArrayStoreClass = null;
726
722
  /** @type {Record<string, {new(...p: ConstructorParameters<typeof Store>): Store}?>} */
727
723
  let TypeStores = Object.create(null);
728
724
  /**
@@ -739,8 +735,8 @@
739
735
  const type = schema.type;
740
736
  /** @type {{new(...p: ConstructorParameters<typeof Store>): Store}} */
741
737
  let Class = Store;
742
- if (schema.array && !(ArrayStore$1 && options?.parent instanceof ArrayStore$1)) {
743
- if (ArrayStore$1) { Class = ArrayStore$1; }
738
+ if (schema.array && !(ArrayStoreClass && options?.parent instanceof ArrayStoreClass)) {
739
+ if (ArrayStoreClass) { Class = ArrayStoreClass; }
744
740
  } else if (typeof type === 'string') {
745
741
  const C = TypeStores[type];
746
742
  if (C) { Class = C; }
@@ -755,9 +751,9 @@
755
751
  ObjectStore$1 = Class;
756
752
  }
757
753
 
758
- /** @param {{new(...p: ConstructorParameters<typeof Store>): Store}} Class */
754
+ /** @param {{new(...p: ConstructorParameters<typeof Store>): ArrayStore}} Class */
759
755
  function setArrayStore(Class) {
760
- ArrayStore$1 = Class;
756
+ ArrayStoreClass = Class;
761
757
  }
762
758
  /**
763
759
  * @param {string} type
@@ -933,7 +929,7 @@
933
929
  /**
934
930
  * @param {Schema.Field<M>} schema 字段的 Schema 定义
935
931
  * @param {object} [options] 可选配置
936
- * @param {*} [options.parent]
932
+ * @param {Store?} [options.parent]
937
933
  * @param {*} [options.state]
938
934
  * @param {number | string | null} [options.index]
939
935
  * @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
@@ -942,8 +938,9 @@
942
938
  * @param {boolean} [options.hidden]
943
939
  * @param {boolean} [options.clearable]
944
940
  * @param {boolean} [options.required]
945
- * @param {boolean} [options.readonly]
946
941
  * @param {boolean} [options.disabled]
942
+ * @param {boolean} [options.readonly]
943
+ * @param {boolean} [options.removable]
947
944
  *
948
945
  * @param {string} [options.label] 字段标签
949
946
  * @param {string} [options.description] 字段描述
@@ -972,7 +969,7 @@
972
969
  setValue, setState, convert, onUpdate, onUpdateState,
973
970
  validator, validators,
974
971
  index, size, new: isNew, parent: parentNode,
975
- hidden, clearable, required, disabled, readonly,
972
+ hidden, clearable, required, disabled, readonly, removable,
976
973
  label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
977
974
  } = {}) {
978
975
  this.schema = schema;
@@ -1038,6 +1035,8 @@
1038
1035
  // @ts-ignore
1039
1036
  [this.#selfValues, this.#values] = createState(this, values, values$1, schema.values);
1040
1037
 
1038
+ [this.#selfRemovable, this.#removable] = createBooleanStates(this, removable, schema.removable ?? true);
1039
+
1041
1040
  const validatorResult = createValidator(this, schema.validator, validator);
1042
1041
 
1043
1042
  const [changed, changedResult, cancelChange] = createAsyncValidator(this, schema.validators?.change, validators?.change);
@@ -1192,6 +1191,15 @@
1192
1191
  set readonly(v) { this.#selfReadonly.set(typeof v === 'boolean' ? v : null); }
1193
1192
 
1194
1193
 
1194
+ /** @readonly @type {Signal.State<boolean?>} */
1195
+ #selfRemovable
1196
+ /** @readonly @type {Signal.Computed<boolean>} */
1197
+ #removable
1198
+ get selfRemovable() { return this.#selfRemovable.get(); }
1199
+ set selfRemovable(v) { this.#selfRemovable.set(typeof v === 'boolean' ? v : null); }
1200
+ /** 是否只读 */
1201
+ get removable() { return this.#removable.get(); }
1202
+ set removable(v) { this.#selfRemovable.set(typeof v === 'boolean' ? v : null); }
1195
1203
 
1196
1204
 
1197
1205
  /** @readonly @type {Signal.State<string?>} */
@@ -1397,6 +1405,7 @@
1397
1405
  }
1398
1406
  this.#value.set(newValues);
1399
1407
  this.#initValue.set(newValues);
1408
+ this.#onUpdate?.(newValues, this.#index.get(), this);
1400
1409
  return newValues;
1401
1410
  }
1402
1411
 
@@ -1596,10 +1605,11 @@
1596
1605
  * @param {Store?} [options.parent]
1597
1606
  * @param {string | number | null} [options.index]
1598
1607
  * @param {boolean} [options.new]
1608
+ * @param {boolean} [options.addable]
1599
1609
  * @param {(value: any, index: any, store: Store) => void} [options.onUpdate]
1600
1610
  * @param {(value: any, index: any, store: Store) => void} [options.onUpdateState]
1601
1611
  */
1602
- constructor(schema, { parent, onUpdate, onUpdateState, index, new: isNew} = {}) {
1612
+ constructor(schema, { parent, onUpdate, onUpdateState, index, new: isNew, addable} = {}) {
1603
1613
  const childrenState = new exports.Signal.State(/** @type {Store[]} */([]));
1604
1614
  // @ts-ignore
1605
1615
  const updateChildren = (list) => {
@@ -1635,6 +1645,9 @@
1635
1645
  },
1636
1646
  onUpdateState,
1637
1647
  });
1648
+
1649
+ [this.#selfAddable, this.#addable] = createBooleanStates(this, addable, schema.addable ?? true);
1650
+
1638
1651
  this.#children = childrenState;
1639
1652
  const childCommonOptions = {
1640
1653
  parent: this,
@@ -1665,6 +1678,20 @@
1665
1678
  return child
1666
1679
  };
1667
1680
  }
1681
+
1682
+
1683
+ /** @readonly @type {Signal.State<boolean?>} */
1684
+ #selfAddable
1685
+ /** @readonly @type {Signal.Computed<boolean>} */
1686
+ #addable
1687
+ get selfAddable() { return this.#selfAddable.get(); }
1688
+ set selfAddable(v) { this.#selfAddable.set(typeof v === 'boolean' ? v : null); }
1689
+ /** 是否禁用字段 */
1690
+ get addable() { return this.#addable.get(); }
1691
+ set addable(v) { this.#selfAddable.set(typeof v === 'boolean' ? v : null); }
1692
+
1693
+
1694
+
1668
1695
  /**
1669
1696
  *
1670
1697
  * @param {number} index
@@ -1673,6 +1700,7 @@
1673
1700
  * @returns
1674
1701
  */
1675
1702
  insert(index, value = null, isNew) {
1703
+ if (!this.addable) { return false; }
1676
1704
  const data = this.value || [];
1677
1705
  if (!Array.isArray(data)) { return false; }
1678
1706
  const children = [...this.#children.get()];
@@ -1943,7 +1971,7 @@
1943
1971
  }
1944
1972
  const enumValue = layout.enum;
1945
1973
  const name = layout.value;
1946
- if (enumValue) { child = { type: 'enum', value: enumValue, vars, children: child ? [child] : [] }; vars = null; }
1974
+ if (enumValue) { child = { type: 'enum', value: enumValue, sort: layout.sort, vars, children: child ? [child] : [] }; vars = null; }
1947
1975
  if (name) { child = { type: 'value', name, vars, children: child ? [child] : [] }; vars = null; }
1948
1976
  if (vars?.length) {
1949
1977
  child = { type: 'fragment', vars, children: child ? [child] : [] };
@@ -2137,6 +2165,9 @@
2137
2165
  case 'enum':
2138
2166
  node.enum = value ? parse$1(value, createCalc) : {value: true};
2139
2167
  break;
2168
+ case 'sort':
2169
+ node.sort = value ? parse$1(value, createCalc) : {value: true};
2170
+ break;
2140
2171
  case 'if':
2141
2172
  node.if = parse$1(value, createCalc);
2142
2173
  break;
@@ -2178,6 +2209,7 @@
2178
2209
  *
2179
2210
  * @property {string} [value] 值关联
2180
2211
  * @property {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [enum] 列表属性枚举
2212
+ * @property {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [sort]
2181
2213
  *
2182
2214
  * @property {boolean | string} [bind] 绑定内容
2183
2215
  * @property {Layout.Node.Name | Layout.Node.Value | Layout.Node.Calc} [text] 文本渲染
@@ -2804,6 +2836,7 @@
2804
2836
  * @property {Record<string, Template>} [templates]
2805
2837
  * @property {'enum'} type
2806
2838
  * @property {Node.Name | Node.Calc | Node.Value} value
2839
+ * @property {Node.Name | Node.Calc | Node.Value} [sort]
2807
2840
  * @property {Child[]} children 子元素
2808
2841
  * @property {string} [comment] 注释
2809
2842
  */
@@ -3003,7 +3036,11 @@
3003
3036
  yield [`${key}${sign}reset`, {exec: () => val.reset()}];
3004
3037
  // @ts-ignore
3005
3038
  yield [`${key}${sign}validate`, {exec: v => val.validate(v ? [] : null)}];
3006
- if (!(val instanceof ArrayStore)) { return; }
3039
+ if (!(val instanceof ArrayStore)) {
3040
+ yield [`${key}${sign}addable`, {get: () => false}];
3041
+ return;
3042
+ }
3043
+ yield [`${key}${sign}addable`, {get: () => val.addable}];
3007
3044
  yield [`${key}${sign}insert`, {exec: (index, value) => val.insert(index, value)}];
3008
3045
  yield [`${key}${sign}add`, {exec: (v) => val.add(v)}];
3009
3046
  yield [`${key}${sign}remove`, {exec: (index) => val.remove(index)}];
@@ -3022,30 +3059,53 @@
3022
3059
  */
3023
3060
  function *toParentItem(parent, val, key = '', sign = '$') {
3024
3061
  if (!(parent instanceof ArrayStore)) {
3062
+ yield [`${key}${sign}removable`, {get: () => false}];
3025
3063
  yield [`${key}${sign}upMovable`, {get: () => false}];
3026
3064
  yield [`${key}${sign}downMovable`, {get: () => false}];
3065
+ yield [`${key}${sign}remove`, {exec: () => {}}];
3066
+ yield [`${key}${sign}upMove`, {exec: () => {}}];
3067
+ yield [`${key}${sign}downMove`, {exec: () => {}}];
3027
3068
  return
3028
3069
  }
3070
+ yield [`${key}${sign}removable`, {get: () => {
3071
+ if (parent.readonly) { return false; }
3072
+ if (parent.disabled) { return false; }
3073
+ if (!val.removable) { return false; }
3074
+ return true;
3075
+ }}];
3029
3076
  yield [`${key}${sign}upMovable`, {get: () => {
3077
+ if (parent.readonly) { return false; }
3078
+ if (parent.disabled) { return false; }
3030
3079
  const s = val.index;
3031
3080
  if (typeof s !== 'number') { return false; }
3032
3081
  if (s <= 0) { return false; }
3033
3082
  return true;
3034
3083
  }}];
3035
3084
  yield [`${key}${sign}downMovable`, {get: () => {
3085
+ if (parent.readonly) { return false; }
3086
+ if (parent.disabled) { return false; }
3036
3087
  const s = val.index;
3037
3088
  if (typeof s !== 'number') { return false; }
3038
3089
  if (s >= parent.size - 1) { return false; }
3039
3090
  return true;
3040
3091
  }}];
3041
- yield [`${key}${sign}remove`, {exec: () => parent.remove(Number(val.index))}];
3092
+ yield [`${key}${sign}remove`, {exec: () => {
3093
+ if (parent.readonly) { return; }
3094
+ if (parent.disabled) { return; }
3095
+ if (!val.removable) { return; }
3096
+ parent.remove(Number(val.index));
3097
+ }}];
3042
3098
  yield [`${key}${sign}upMove`, {exec: () => {
3099
+ if (parent.readonly) { return; }
3100
+ if (parent.disabled) { return; }
3043
3101
  const s = val.index;
3044
3102
  if (typeof s !== 'number') { return; }
3045
3103
  if (s <= 0) { return; }
3046
3104
  parent.move(s, s - 1);
3047
3105
  }}];
3048
3106
  yield [`${key}${sign}downMove`, {exec: () => {
3107
+ if (parent.readonly) { return; }
3108
+ if (parent.disabled) { return; }
3049
3109
  const s = val.index;
3050
3110
  if (typeof s !== 'number') { return; }
3051
3111
  if (s >= parent.size - 1) { return; }
@@ -4524,6 +4584,7 @@
4524
4584
  return node;
4525
4585
  }
4526
4586
 
4587
+ /** @import * as Layout from '../Layout/index.mjs' */
4527
4588
  /** @import Store, { ArrayStore } from '../Store/index.mjs' */
4528
4589
 
4529
4590
  /**
@@ -4533,8 +4594,9 @@
4533
4594
  * @param {ArrayStore} store
4534
4595
  * @param {Environment} env
4535
4596
  * @param {(next: Node | null, env: any) => () => void} renderItem
4597
+ * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [sort]
4536
4598
  */
4537
- function renderArray(parent, next, store, env, renderItem) {
4599
+ function renderArray(parent, next, store, env, renderItem, sort) {
4538
4600
  const start = parent.insertBefore(document.createComment(''), next);
4539
4601
  /** @type {Map<Store, [Comment, Comment, () => void]>} */
4540
4602
  let seMap = new Map();
@@ -4650,7 +4712,26 @@
4650
4712
  };
4651
4713
  }
4652
4714
 
4715
+ /**
4716
+ *
4717
+ * @param {any} a
4718
+ * @param {any} b
4719
+ * @returns
4720
+ */
4721
+ function compare(a, b) {
4722
+ if (typeof a === 'bigint' && typeof b === 'bigint') {
4723
+ return Number(a - b);
4724
+ }
4725
+ if ((typeof a === 'number' || typeof a === 'bigint') && (typeof b === 'number' || typeof b === 'bigint')) {
4726
+ return Number(a) - Number(b);
4727
+ }
4728
+ const sa = String(a);
4729
+ const sb = String(b);
4730
+ return sa > sb ? 1 : sa < sb ? -1 : 0;
4731
+ }
4732
+
4653
4733
  /** @import { ObjectStore, Store } from '../Store/index.mjs' */
4734
+ /** @import * as Layout from '../Layout/index.mjs' */
4654
4735
 
4655
4736
  /**
4656
4737
  *
@@ -4659,14 +4740,21 @@
4659
4740
  * @param {ObjectStore} store
4660
4741
  * @param {Environment} env
4661
4742
  * @param {(next: Node | null, env: any) => () => void} renderItem
4743
+ * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [sort]
4662
4744
  */
4663
- function renderObject(parent, next, store, env, renderItem) {
4745
+ function renderObject(parent, next, store, env, renderItem, sort) {
4664
4746
  /** @type {(() => void)[]} */
4665
4747
  const children = [];
4666
- /** @type {[string, Store<any, any>, number][]} */
4667
- const childStores = [...store].map(([k,v], i) => [k,v,i]);
4748
+ const childStores = [...store];
4668
4749
  const count = childStores.length;
4669
- for (const [key, child, index] of childStores) {
4750
+ /** @type {[string, Store<any, any>, number][]} */
4751
+ const stores = sort
4752
+ ? childStores
4753
+ .map(([k,v]) => [k,v,env.setStore(v, store).exec(sort)])
4754
+ .sort(([,,a], [,,b]) => compare(a, b))
4755
+ .map(([k,v], i) => [k,v,i])
4756
+ : childStores.map(([k,v], i) => [k,v,i]);
4757
+ for (const [key, child, index] of stores) {
4670
4758
  children.push(renderItem(next, env.setStore(child, store, {
4671
4759
  get count() { return count; },
4672
4760
  get key() { return key; },
@@ -4682,6 +4770,8 @@
4682
4770
  };
4683
4771
  }
4684
4772
 
4773
+ /** @import * as Layout from '../Layout/index.mjs' */
4774
+
4685
4775
  /**
4686
4776
  *
4687
4777
  * @param {Element} parent
@@ -4689,24 +4779,38 @@
4689
4779
  * @param {() => any} getter
4690
4780
  * @param {Environment} env
4691
4781
  * @param {(next: Node | null, env: any) => () => void} renderItem
4782
+ * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [sort]
4692
4783
  */
4693
- function renderEnum(parent, next, getter, env, renderItem) {
4784
+ function renderEnum(parent, next, getter, env, renderItem, sort) {
4694
4785
 
4695
- /** @type {Signal.Computed<[value: any, index: number, kKey: any][]>} */
4786
+ /** @type {Signal.Computed<[value: any, kKey: any][]>} */
4696
4787
  const list = new exports.Signal.Computed(() => {
4697
4788
  const values = getter();
4698
4789
  if (typeof values === 'number') {
4699
4790
  const n = Math.floor(values);
4700
4791
  if (!n) { return []; }
4701
- return Array(n).fill(0).map((_, i) => [i + 1, i, i]);
4792
+ return Array(n).fill(0).map((_, i) => [i + 1, i]);
4702
4793
  }
4703
4794
  if (!values || typeof values !== 'object') { return []; }
4704
4795
  if (Array.isArray(values)) {
4705
- return values.map((v, i) => [v, i, i]);
4796
+ return values.map((v, i) => [v, i]);
4706
4797
  }
4707
4798
  // TODO: 转列表
4708
- return Object.entries(values).map(([k,v], i) => [v, i, k]);
4799
+ return Object.entries(values).map(([k,v]) => [v, k]);
4709
4800
  });
4801
+ /** @type {Signal.Computed<[value: any, index: number, kKey: any][]>} */
4802
+ const slotted = sort ? new exports.Signal.Computed(() => {
4803
+ const values = list.get();
4804
+ return values
4805
+ .map(([k,v], i) => [v, k, env.setObject({
4806
+ get count() { return values.length },
4807
+ get key() { return k; },
4808
+ get item() { return v; },
4809
+ get index() { return i; },
4810
+ }).exec(sort)])
4811
+ .sort(([,,a], [,,b]) => compare(a, b))
4812
+ .map(([k, v], i) => [v, i, k]);
4813
+ }) : new exports.Signal.Computed(() => list.get().map(([k,v], i) => [v, i, k]));
4710
4814
  const start = parent.insertBefore(document.createComment(''), next);
4711
4815
  /** @type {[Comment, Comment, () => void, key: any, Signal.State<any>, Signal.State<any>][]} */
4712
4816
  let seMap = [];
@@ -4719,7 +4823,7 @@
4719
4823
  }
4720
4824
  }
4721
4825
  const count = new exports.Signal.State(0);
4722
- const childrenResult = watch(() => list.get(), function render(children) {
4826
+ const childrenResult = watch(() => slotted.get(), function render(children) {
4723
4827
  if (!start.parentNode) { return; }
4724
4828
  let nextNode = start.nextSibling;
4725
4829
  const oldSeMap = seMap;
@@ -5090,13 +5194,13 @@
5090
5194
  /** @type {(next: Node | null, env: any) => () => void} */
5091
5195
  const r = (next, env) => renderChildren(layout.children, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
5092
5196
  if (list instanceof ArrayStore) {
5093
- return renderArray(parent, next, list, env, r);
5197
+ return renderArray(parent, next, list, env, r, layout.sort);
5094
5198
  }
5095
5199
  if (list instanceof ObjectStore) {
5096
- return renderObject(parent, next, list, env, r);
5200
+ return renderObject(parent, next, list, env, r, layout.sort);
5097
5201
  }
5098
5202
  if (typeof list === 'function') {
5099
- return renderEnum(parent, next, list, env, r);
5203
+ return renderEnum(parent, next, list, env, r, layout.sort);
5100
5204
  }
5101
5205
  return () => { };
5102
5206
  }