@neeloong/form 0.10.0 → 0.12.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.10.0
2
+ * @neeloong/form v0.12.0
3
3
  * (c) 2024-2025 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -651,7 +651,7 @@
651
651
 
652
652
  }
653
653
  /** @param {*} v */
654
- const values$1 = v => {
654
+ const values = v => {
655
655
  if (!v || !Array.isArray(v)) { return null;}
656
656
  const list = v.map(toValueItem).filter(valueFilter);
657
657
  if (!list.length) { return null; }
@@ -726,7 +726,8 @@
726
726
  /** @type {Record<string, {new(...p: ConstructorParameters<typeof Store>): Store}?>} */
727
727
  let TypeStores = Object.create(null);
728
728
  /**
729
- * @param {Schema.Field} schema
729
+ * @template [M=any]
730
+ * @param {Schema.Field<M>} schema
730
731
  * @param {object} [options]
731
732
  * @param {Store?} [options.parent]
732
733
  * @param {string | number | null} [options.index]
@@ -863,6 +864,7 @@
863
864
  /**
864
865
  * 管理单个表单字段的状态和行为
865
866
  * @template [T=any]
867
+ * @template [M=any]
866
868
  */
867
869
  class Store {
868
870
  /** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
@@ -904,7 +906,8 @@
904
906
  }
905
907
  /**
906
908
  * 从数据结构模式创建存储
907
- * @param {Schema} schema 数据结构模式
909
+ * @template [M=any]
910
+ * @param {Schema<M>} schema 数据结构模式
908
911
  * @param {object} [options] 选项
909
912
  * @param {boolean} [options.new] 是否为新建环境
910
913
  */
@@ -928,12 +931,12 @@
928
931
  #ref = null;
929
932
  get ref() { return this.#ref || createRef(this); }
930
933
  /**
931
- * @param {Schema.Field} schema 字段的 Schema 定义
934
+ * @param {Schema.Field<M>} schema 字段的 Schema 定义
932
935
  * @param {object} [options] 可选配置
933
936
  * @param {*} [options.parent]
934
937
  * @param {*} [options.state]
935
938
  * @param {number | string | null} [options.index]
936
- * @param {number | Signal.State<number> | Signal.Computed<number>} [options.length]
939
+ * @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
937
940
  * @param {boolean} [options.null]
938
941
  * @param {boolean} [options.new]
939
942
  * @param {boolean} [options.hidden]
@@ -968,9 +971,9 @@
968
971
  null: isNull, state, ref,
969
972
  setValue, setState, convert, onUpdate, onUpdateState,
970
973
  validator, validators,
971
- index, length, new: isNew, parent: parentNode,
974
+ index, size, new: isNew, parent: parentNode,
972
975
  hidden, clearable, required, disabled, readonly,
973
- label, description, placeholder, min, max, step, minLength, maxLength, pattern, values
976
+ label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
974
977
  } = {}) {
975
978
  this.schema = schema;
976
979
  this.#state.set(typeof state === 'object' && state || {});
@@ -1033,7 +1036,7 @@
1033
1036
  [this.#selfMaxLength, this.#maxLength] = createState(this, number, maxLength, schema.maxLength);
1034
1037
  [this.#selfPattern, this.#pattern] = createState(this, regex, pattern, schema.pattern);
1035
1038
  // @ts-ignore
1036
- [this.#selfValues, this.#values] = createState(this, values$1, values, schema.values);
1039
+ [this.#selfValues, this.#values] = createState(this, values, values$1, schema.values);
1037
1040
 
1038
1041
  const validatorResult = createValidator(this, schema.validator, validator);
1039
1042
 
@@ -1048,10 +1051,10 @@
1048
1051
  this.#cancelChange = cancelChange;
1049
1052
  this.#cancelBlur = cancelBlur;
1050
1053
 
1051
- if (length instanceof exports.Signal.State || length instanceof exports.Signal.Computed) {
1052
- this.#length = length;
1054
+ if (size instanceof exports.Signal.State || size instanceof exports.Signal.Computed) {
1055
+ this.#size = size;
1053
1056
  } else {
1054
- this.#length = new exports.Signal.State(length || 0);
1057
+ this.#size = new exports.Signal.State(size || 0);
1055
1058
  }
1056
1059
 
1057
1060
  if (isNull) {
@@ -1089,7 +1092,7 @@
1089
1092
  #root = this;
1090
1093
  /** @readonly @type {any} */
1091
1094
  #type;
1092
- /** @readonly @type {any} */
1095
+ /** @readonly @type {M | void} */
1093
1096
  #meta;
1094
1097
  /** @readonly @type {any} */
1095
1098
  #component;
@@ -1107,9 +1110,9 @@
1107
1110
  get component() { return this.#component; }
1108
1111
 
1109
1112
  /** @type {Signal.State<number> | Signal.Computed<number>} */
1110
- #length;
1113
+ #size;
1111
1114
  /** 长度信息 */
1112
- get length() { return this.#length.get(); }
1115
+ get size() { return this.#size.get(); }
1113
1116
  #index = new exports.Signal.State(/** @type {string | number} */(''));
1114
1117
  /** 索引信息 */
1115
1118
  get index() { return this.#index.get(); }
@@ -1291,10 +1294,10 @@
1291
1294
  /** @readonly @type {Signal.Computed<(Schema.Value.Group | Schema.Value)[] | null>} */
1292
1295
  #values
1293
1296
  get selfValues() { return this.#selfValues.get(); }
1294
- set selfValues(v) { this.#selfValues.set(values$1(v)); }
1297
+ set selfValues(v) { this.#selfValues.set(values(v)); }
1295
1298
  /** 可选值列表 */
1296
1299
  get values() { return this.#values.get(); }
1297
- set values(v) { this.#selfValues.set(values$1(v)); }
1300
+ set values(v) { this.#selfValues.set(values(v)); }
1298
1301
 
1299
1302
 
1300
1303
  /** @type {Signal.Computed<string[]>} */
@@ -1496,7 +1499,8 @@
1496
1499
 
1497
1500
  /**
1498
1501
  * @template {Record<string, any>} [T=Record<string, any>]
1499
- * @extends {Store<T>}
1502
+ * @template [M=any]
1503
+ * @extends {Store<T, M>}
1500
1504
  */
1501
1505
  class ObjectStore extends Store {
1502
1506
  get kind() { return 'object'; }
@@ -1510,7 +1514,7 @@
1510
1514
  */
1511
1515
  child(key) { return this.#children[key] || null; }
1512
1516
  /**
1513
- * @param {Schema.Object & Schema.Attr} schema
1517
+ * @param {Schema.Object<M> & Schema.Attr<M>} schema
1514
1518
  * @param {object} [options]
1515
1519
  * @param {Store?} [options.parent]
1516
1520
  * @param {number | string | null} [options.index]
@@ -1522,7 +1526,7 @@
1522
1526
  const childrenTypes = Object.entries(schema.type);
1523
1527
  super(schema, {
1524
1528
  parent, index, new: isNew, onUpdate, onUpdateState,
1525
- length: childrenTypes.length,
1529
+ size: childrenTypes.length,
1526
1530
  setValue(v) { return typeof v === 'object' ? v : null; },
1527
1531
  setState(v) { return typeof v === 'object' ? v : null; },
1528
1532
  convert(v, state) {
@@ -1563,7 +1567,8 @@
1563
1567
 
1564
1568
  /**
1565
1569
  * @template [T=any]
1566
- * @extends {Store<(T | null)[]>}
1570
+ * @template [M=any]
1571
+ * @extends {Store<(T | null)[], M>}
1567
1572
  */
1568
1573
  class ArrayStore extends Store {
1569
1574
  /** @type {(index: number, isNew?: boolean) => Store} */
@@ -1586,7 +1591,7 @@
1586
1591
  }
1587
1592
  get kind() { return 'array'; }
1588
1593
  /**
1589
- * @param {Schema.Field} schema
1594
+ * @param {Schema.Field<M>} schema
1590
1595
  * @param {object} [options]
1591
1596
  * @param {Store?} [options.parent]
1592
1597
  * @param {string | number | null} [options.index]
@@ -1604,6 +1609,7 @@
1604
1609
  for (let i = children.length; i < length; i++) {
1605
1610
  children.push(this.#create(i));
1606
1611
  }
1612
+ children.length = length;
1607
1613
  if (oldLength !== length) {
1608
1614
  childrenState.set(children);
1609
1615
  }
@@ -1611,7 +1617,7 @@
1611
1617
  };
1612
1618
  super(schema, {
1613
1619
  index, new: isNew, parent,
1614
- length: new exports.Signal.Computed(() => childrenState.get().length),
1620
+ size: new exports.Signal.Computed(() => childrenState.get().length),
1615
1621
  state: [],
1616
1622
  setValue(v) { return Array.isArray(v) ? v : v == null ? null : [v] },
1617
1623
  setState(v) { return Array.isArray(v) ? v : v == null ? null : [v] },
@@ -1801,6 +1807,158 @@
1801
1807
  // @ts-ignore
1802
1808
  setArrayStore(ArrayStore);
1803
1809
 
1810
+ /** @import * as Layout from './index.mjs' */
1811
+
1812
+ /** @import { OldNode } from './createElement.mjs' */
1813
+
1814
+ /**
1815
+ * @param {(OldNode | string)[]} [layouts]
1816
+ * @returns {Layout.Child[]}
1817
+ */
1818
+ function renderList(layouts) {
1819
+ if (!layouts?.length) { return []; }
1820
+ /** @type {Layout.Child[]} */
1821
+ const children = [];
1822
+ /** @type {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, OldNode][]} */
1823
+ let ifList = [];
1824
+ /** @type {Record<string, Layout.Template>} */
1825
+ let templates = Object.create(null);
1826
+ let hasTemplate = false;
1827
+ for (const layout of layouts) {
1828
+ if (typeof layout === 'string') { continue; }
1829
+ const name = layout.template;
1830
+ if (!name) { continue; }
1831
+ hasTemplate = true;
1832
+ const children = convertItem(layout);
1833
+ templates[name] = { params: layout.params, children: children ? [children] : [] };
1834
+ }
1835
+
1836
+ /** @param {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, OldNode][]} list */
1837
+ function renderIf(list) {
1838
+ if (!list.length) { return; }
1839
+ children.push({
1840
+ type: 'divergent',
1841
+ children: list.map(([a, b]) => {
1842
+ const child = convertItem(b);
1843
+ if (!child) { return [{ children: [] }, a]; }
1844
+ if (typeof child !== 'string' && child.type === 'fragment') {
1845
+ return [child, a];
1846
+ }
1847
+ return [{ children: [child] }, a];
1848
+ }),
1849
+ });
1850
+ }
1851
+ for (const layout of layouts) {
1852
+ if (typeof layout === 'string') {
1853
+ renderIf(ifList);
1854
+ ifList = [];
1855
+ children.push(layout);
1856
+ continue;
1857
+ }
1858
+ if (layout.template) {
1859
+ renderIf(ifList);
1860
+ ifList = [];
1861
+ continue;
1862
+ }
1863
+ if (ifList.length && layout.else) {
1864
+ const ifv = layout.if || null;
1865
+ ifList.push([ifv, layout]);
1866
+ if (!ifv) {
1867
+ renderIf(ifList);
1868
+ ifList = [];
1869
+ }
1870
+ continue;
1871
+ }
1872
+ renderIf(ifList);
1873
+ ifList = [];
1874
+ const ifv = layout.if;
1875
+ if (ifv) {
1876
+ ifList.push([ifv, layout]);
1877
+ continue;
1878
+ }
1879
+ const s = convertItem(layout);
1880
+ if (s)
1881
+ children.push(s);
1882
+ }
1883
+ renderIf(ifList);
1884
+ if (hasTemplate) {
1885
+ return [{ type: 'fragment', templates, children }];
1886
+ }
1887
+ return children;
1888
+ }
1889
+
1890
+
1891
+ /**
1892
+ * @param {OldNode} layout
1893
+ * @returns {Layout.Child | undefined}
1894
+ */
1895
+ function renderFillDirectives$1({ text, html }) {
1896
+ if (text != null) { return { type: 'content', value: text }; }
1897
+ if (html != null) { return { type: 'content', value: html, html: true }; }
1898
+ }
1899
+
1900
+ /**
1901
+ * @param {OldNode} layout
1902
+ * @returns {Layout.Child?}
1903
+ */
1904
+ function convertNode(layout) {
1905
+ const fragment = layout.fragment;
1906
+ if (fragment && typeof fragment === 'string') {
1907
+ return { type: 'template', template: fragment, attrs: layout.attrs, children: [] };
1908
+ }
1909
+ const child = renderFillDirectives$1(layout);
1910
+ const children = child ? [child] : renderList(layout.children);
1911
+ if (!layout.name || fragment) { return child || { type: 'fragment', children }; }
1912
+ return {
1913
+ name: layout.name,
1914
+ is: layout.is,
1915
+ attrs: layout.attrs,
1916
+ events: layout.events,
1917
+ classes: layout.classes,
1918
+ styles: layout.styles,
1919
+ enhancements: layout.enhancements,
1920
+ bind: layout.bind,
1921
+ comment: layout.comment,
1922
+ children,
1923
+ };
1924
+ }
1925
+ /**
1926
+ *
1927
+ * @param {OldNode} layout
1928
+ * @returns {Layout.Child?}
1929
+ */
1930
+ function convertItem(layout) {
1931
+ let child = convertNode(layout);
1932
+ /** @type {Layout.Variable[]?} */
1933
+ let vars = layout.vars;
1934
+ if (vars.length && child && typeof child !== 'string') {
1935
+ if (child.vars) {
1936
+ child.vars = [...vars, ...child.vars];
1937
+ } else if (child) {
1938
+ child.vars = vars;
1939
+ } else {
1940
+ child = { type: 'fragment', vars, children: [] };
1941
+ }
1942
+ vars = null;
1943
+ }
1944
+ const enumValue = layout.enum;
1945
+ const name = layout.value;
1946
+ if (enumValue) { child = { type: 'enum', value: enumValue, vars, children: child ? [child] : [] }; vars = null; }
1947
+ if (name) { child = { type: 'value', name, vars, children: child ? [child] : [] }; vars = null; }
1948
+ if (vars?.length) {
1949
+ child = { type: 'fragment', vars, children: child ? [child] : [] };
1950
+ }
1951
+ return child;
1952
+ }
1953
+
1954
+ /**
1955
+ * @param {(OldNode | string)[]} layouts 布局信息
1956
+ * @returns {Layout.Child[]}
1957
+ */
1958
+ function convert(layouts) {
1959
+ return renderList(layouts);
1960
+ }
1961
+
1804
1962
  const numRegex = /^([+-]?(\d(_?\d)*(\.(\d(_?\d)*)?)?|\.\d(_?\d)*)(?:e[+-]?\d(_?\d)*)|0(b[01](?:_?[01])*|o[0-7](?:_?[0-7])*|x[\dA-F](?:_?[\dA-F])*))$/is;
1805
1963
  /**
1806
1964
  *
@@ -1856,6 +2014,7 @@
1856
2014
  }
1857
2015
 
1858
2016
  /** @import * as Layout from './index.mjs' */
2017
+ /** @import { OldNode } from './createElement.mjs' */
1859
2018
 
1860
2019
  const attrPattern = /^(?<decorator>[:@!+*\.?]|style:|样式:)?(?<name>-?[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_:\d\.]*)$/u;
1861
2020
  const enhancementPattern = /^~(?<enhancement>[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_\d\.]*)(?:(?<decorator>[:@!])(?<name>-?[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_:\d\.]*))?$/u;
@@ -1895,14 +2054,14 @@
1895
2054
  }
1896
2055
 
1897
2056
  /**
1898
- * @param {Layout.Node} node
2057
+ * @param {OldNode} node
1899
2058
  * @param {Exclude<Layout.Options['createCalc'], undefined>} createCalc
1900
2059
  * @param {Exclude<Layout.Options['createInit'], undefined>} createInit
1901
2060
  * @param {Exclude<Layout.Options['createEvent'], undefined>} createEvent
1902
2061
  * @param {boolean} enableHTML
1903
2062
  */
1904
2063
  function createAttributeAdder(node, createCalc, createInit, createEvent, enableHTML) {
1905
- const { attrs, events, classes, styles, vars, aliases, params, enhancements } = node;
2064
+ const { attrs, events, classes, styles, vars, params, enhancements } = node;
1906
2065
  /**
1907
2066
  * @param {string} qName
1908
2067
  * @param {string} value
@@ -1957,11 +2116,11 @@
1957
2116
  return;
1958
2117
  }
1959
2118
  if (decorator === '+') {
1960
- vars[name] = !value ? {value: undefined} : parse$1(value, createInit);
2119
+ vars.push({...value ? parse$1(value, createInit) : {value: undefined}, variable: name, init: true});
1961
2120
  return;
1962
2121
  }
1963
2122
  if (decorator === '*') {
1964
- aliases[name] = parse$1(value, createCalc);
2123
+ vars.push({...parse$1(value, createCalc), variable: name, init: false });
1965
2124
  return;
1966
2125
  }
1967
2126
  if (decorator === '?') {
@@ -1998,10 +2157,40 @@
1998
2157
 
1999
2158
  /** @import * as Layout from './index.mjs' */
2000
2159
 
2160
+ /**
2161
+ * @typedef {object} OldNode 布局节点
2162
+ * @property {string} name 标签名
2163
+ * @property {string?} [is]
2164
+ * @property {string} [id]
2165
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} attrs 属性
2166
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} params 模板参数定义
2167
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} classes 类名
2168
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} styles 样式
2169
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Event>} events 事件
2170
+ * @property {Layout.Variable[]} vars 局部变量/别名/计算名
2171
+ * @property {Record<string, Layout.Enhancement>} enhancements 增强
2172
+ *
2173
+ * @property {string} [template] 模板定义的名称
2174
+ * @property {boolean | string} [fragment] 是否为片段或模板调用
2175
+ *
2176
+ * @property {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [if] 分歧条件
2177
+ * @property {boolean} [else] 否定
2178
+ *
2179
+ * @property {string} [value] 值关联
2180
+ * @property {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [enum] 列表属性枚举
2181
+ *
2182
+ * @property {boolean | string} [bind] 绑定内容
2183
+ * @property {Layout.Node.Name | Layout.Node.Value | Layout.Node.Calc} [text] 文本渲染
2184
+ * @property {Layout.Node.Name | Layout.Node.Value | Layout.Node.Calc} [html] HTML 渲染
2185
+ *
2186
+ * @property {string} [comment] 注释
2187
+ *
2188
+ * @property {(OldNode | string)[]} children 子元素
2189
+ */
2001
2190
  /**
2002
2191
  * @param {string} name
2003
2192
  * @param {string?} [is]
2004
- * @returns {Layout.Node}
2193
+ * @returns {OldNode}
2005
2194
  *
2006
2195
  */
2007
2196
  function createElement(name, is) {
@@ -2013,8 +2202,7 @@
2013
2202
  events: Object.create(null),
2014
2203
  classes: Object.create(null),
2015
2204
  styles: Object.create(null),
2016
- vars: Object.create(null),
2017
- aliases: Object.create(null),
2205
+ vars: [],
2018
2206
  params: Object.create(null),
2019
2207
  enhancements: Object.create(null),
2020
2208
  };
@@ -2267,6 +2455,7 @@
2267
2455
  };
2268
2456
 
2269
2457
  /** @import * as Layout from './index.mjs' */
2458
+ /** @import { OldNode } from './createElement.mjs' */
2270
2459
 
2271
2460
  const tagNamePattern = /^(?<name>[\w\p{Unified_Ideograph}_][-\.\|:|d\w\p{Unified_Ideograph}_:]*)(?:|(?<is>[\w\p{Unified_Ideograph}_][-\.\|:|d\w\p{Unified_Ideograph}_]*))?$/u;
2272
2461
 
@@ -2332,7 +2521,7 @@
2332
2521
  * 解析模板内容
2333
2522
  * @param {string} source 输入源字符串
2334
2523
  * @param {Layout.Options} [options] 解析选项
2335
- * @returns {(Layout.Node | string)[]}
2524
+ * @returns {Layout.Child[]}
2336
2525
  */
2337
2526
  function parse(source, {
2338
2527
  createCalc = () => { throw new ParseError('CALC'); },
@@ -2341,15 +2530,15 @@
2341
2530
  simpleTag = new Set,
2342
2531
  enableHTML = false,
2343
2532
  } = {}) {
2344
- /** @type {(Layout.Node | string)[]} */
2533
+ /** @type {(OldNode | string)[]} */
2345
2534
  const children = [];
2346
2535
 
2347
2536
  const doc = { children };
2348
- /** @type {(Layout.Node | null)[]} */
2537
+ /** @type {(OldNode | null)[]} */
2349
2538
  const stack = [];
2350
- /** @type {Layout.Node?} */
2539
+ /** @type {OldNode?} */
2351
2540
  let currentNode = null;
2352
- /** @type {typeof doc | Layout.Node} */
2541
+ /** @type {typeof doc | OldNode} */
2353
2542
  let current = doc;
2354
2543
  function endElement() {
2355
2544
  currentNode = stack.pop() || null;
@@ -2525,229 +2714,156 @@
2525
2714
  endElement();
2526
2715
  }
2527
2716
  }
2528
- return children;
2717
+ return convert(children);
2529
2718
  }
2530
2719
 
2531
- /** @import * as Layout from './index.mjs' */
2532
-
2533
2720
  /**
2534
- *
2535
- * @param {*} c
2536
- * @returns
2721
+ * @typedef {object} Enhancement 增强信息
2722
+ * @property {Record<string, Node.Name | Node.Event>} events 事件
2723
+ * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2724
+ * @property {Node.Name | Node.Calc | Node.Value} [value] 主值
2725
+ * @property {boolean | string} [bind] 绑定信息
2537
2726
  */
2538
- function _xmlEncoder(c) {
2539
- return c == '<' && '&lt;' ||
2540
- c == '>' && '&gt;' ||
2541
- c == '&' && '&amp;' ||
2542
- c == '"' && '&quot;' ||
2543
- '&#' + c.charCodeAt() + ';';
2544
- }
2545
2727
 
2546
2728
  /**
2547
- * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Event | Layout.Node.Value} def
2729
+ * @typedef {object} Options 解析选项
2730
+ * @property {boolean} [enableHTML] 启用 `!html` 指令
2731
+ * @property {(t: string) => Calc} [createCalc] 创建计算属性的工厂函数
2732
+ * @property {(t: string) => Calc} [createInit] 创建变量初始化的工厂函数
2733
+ * @property {(t: string) => EventListener} [createEvent] 创建事件监听器的工厂函数
2734
+ * @property {Set<string>} [simpleTag] 简单标签的集合
2735
+ */
2736
+ /**
2737
+ * @template [T=any]
2738
+ * @typedef {{value: T; name?: undefined; calc?: undefined; event?: undefined}} Node.Value 基础值
2548
2739
  */
2549
- function toValue({name, calc, event, value}) {
2550
- if (value === true || value === undefined) { return ''; }
2551
- const val = typeof value === 'string' ? JSON.stringify(value) : name || calc || event || value;
2552
- return `="${toAttrValue$1(val)}"`;
2553
- }
2554
2740
  /**
2555
- * @param {string | Function | null} [value]
2741
+ * @typedef {{name: string; value?: undefined; calc?: undefined; event?: undefined}} Node.Name 名称
2742
+ */
2743
+ /**
2744
+ * @typedef {{event: EventListener; name?: undefined; calc?: undefined; value?: undefined}} Node.Event 事件
2556
2745
  */
2557
- function toAttrValue$1(value) {
2558
- return String(value).replace(/[<&"]/g, _xmlEncoder);
2559
- }
2560
-
2561
2746
  /**
2562
- * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Event | Layout.Node.Value>} values
2563
- * @param {string} [prefix]
2564
- * @param {boolean | null} [isName]
2747
+ * @typedef {{calc: Calc; name?: undefined; value?: undefined; event?: undefined}} Node.Calc 计算
2565
2748
  */
2566
- function *values(values, prefix, isName = false) {
2567
- if (prefix) {
2568
- for (const [key, {name, calc, event, value}] of Object.entries(values)) {
2569
- yield ` ${prefix}${key}`;
2570
- if (isName && name === key) { continue; }
2571
- const val = value && typeof value === 'string' ? JSON.stringify(value) : name || calc || event || value;
2572
- if (val == null) { continue; }
2573
- if (isName === false && val === true) { continue; }
2574
- yield `="${toAttrValue$1(val)}"`;
2575
- }
2576
- return;
2577
- }
2578
- for (const [key, attr] of Object.entries(values)) {
2579
- if (!attr) { continue; }
2580
- const {name, value, calc} = attr;
2581
- if (name === key) { yield ` :${key}`; continue; }
2582
- if (name || calc || typeof value !== 'string') {
2583
- yield ` :${key}="${toAttrValue$1(name || calc || value)}"`;
2584
- continue;
2585
- }
2586
- yield ` ${key}`;
2587
- if (value) {
2588
- yield `="${toAttrValue$1(value)}"`;
2589
- }
2590
- }
2591
- }
2592
-
2593
2749
 
2594
2750
  /**
2595
2751
  *
2596
- * @param {Layout.Node} node
2597
- * @param {number} [level]
2598
- * @returns {Iterable<string>}
2752
+ * @typedef {Divergent | Select | Enum | Content | CallTemplate | Fragment | Node | string} Child 分歧
2599
2753
  */
2600
- function* nodeToString(node, level = 0) {
2601
- const { children, is, name } = node;
2602
- const pad = level > 0 ? ''.padEnd(level, '\t') : '';
2603
-
2604
- yield pad;
2605
- yield* ['<', name || '-'];
2606
- if (is) { yield* ['|', is]; }
2607
- if (node.template) {
2608
- yield ` !template="${toAttrValue$1(node.template)}"`;
2609
- yield* values(node.params, '?', null);
2610
- }
2611
- if (node.fragment) { yield node.fragment === true ? ` !fragment` : ` !fragment="${toAttrValue$1(node.fragment)}"`; }
2612
- if (node.else) { yield ` !else`; }
2613
- if (node.if) { yield ` !if${toValue(node.if)}`; }
2614
- if (node.value) { yield ` !value="${toAttrValue$1(node.value)}"`; }
2615
- if (node.enum) { yield ` !enum${toValue(node.enum)}`; }
2616
- yield* values(node.aliases, '*', null);
2617
- yield* values(node.vars, '+', null);
2618
- if (node.bind) { yield node.bind === true ? ` !bind` : ` !bind="${toAttrValue$1(node.bind)}"`; }
2619
- yield* values(node.attrs);
2620
- yield* values(node.events, '@', true);
2621
- yield* values(node.classes, '.', true);
2622
- yield* values(node.styles, 'style:');
2623
- for (const [k, en] of Object.entries(node.enhancements)) {
2624
- if (en.bind) { yield en.bind === true ? ` ~${k}!bind` : ` ~${k}!bind="${toAttrValue$1(en.bind)}"`; }
2625
- if (en.value) { yield ` ~${k}${toValue(en.value)}`; }
2626
- yield* values(en.attrs, `~${k}:`, true);
2627
- yield* values(en.events, `~${k}@`, true);
2628
- }
2629
- if (node.text) { yield ` !text${toValue(node.text)}`; }
2630
- if (node.html) { yield ` !html${toValue(node.html)}`; }
2631
- if (node.comment) { yield ` !comment="${toAttrValue$1(node.comment)}"`; }
2632
- if (!children.length) {
2633
- yield '/>';
2634
- if (level >= 0) { yield '\n'; }
2635
- return;
2636
- }
2637
- if (children.length === 1) {
2638
- const [child] = children;
2639
- if (typeof child === 'string' && child.length < 80 && !child.includes('\n')) {
2640
- yield '>';
2641
- yield* [child.replace(/[<&\t]/g, _xmlEncoder).replace(/]]>/g, ']]&gt;')];
2642
- yield* ['</', name, '>'];
2643
- if (level >= 0) { yield '\n'; }
2644
- return;
2645
- }
2646
- }
2647
- yield '>';
2648
- if (level >= 0) { yield '\n'; }
2649
- yield* listToString(children, level >= 0 ? level + 1 : -1);
2650
- yield* [pad, '</', name, '>'];
2651
- if (level >= 0) { yield '\n'; }
2652
-
2653
- }
2654
2754
  /**
2655
2755
  *
2656
- * @param {(Layout.Node |string)[]} nodes
2657
- * @param {number} [level]
2658
- * @returns {Iterable<string>}
2756
+ * @template [T=unknown]
2757
+ * @typedef {object} Variable 变量定义
2758
+ * @property {string} variable
2759
+ * @property {string} [name]
2760
+ * @property {Calc} [calc]
2761
+ * @property {T} [value]
2762
+ * @property {boolean} [init] 是否普通变量
2763
+ * @property {string} [comment] 注释
2659
2764
  */
2660
- function* listToString(nodes, level = 0) {
2661
- if (!nodes.length) { return ''; }
2662
-
2663
- const pad = level > 0 ? ''.padEnd(level, '\t') : '';
2664
- for (const child of nodes) {
2665
- if (typeof child === 'string') {
2666
- let text = child.replace(/[<&\t]/g, _xmlEncoder).replace(/]]>/g, ']]&gt;');
2667
- if (pad) {
2668
- text = text.replace(/(?<=^|\n)/g, pad);
2669
- }
2670
- yield text;
2671
- if (level >= 0) { yield '\n'; }
2672
- } else {
2673
- yield* nodeToString(child, level);
2674
- }
2675
- }
2676
- }
2677
2765
  /**
2678
- * 将模板转为字符串
2679
- * @param {Layout.Node | (Layout.Node |string)[]} value 要转换的节点或节点数组
2680
- * @param {boolean} [formable] 是否对代码进行格式化
2681
- * @returns {string}
2766
+ * @typedef {object} Template
2767
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2768
+ * @property {Record<string, Template>} [templates]
2769
+ * @property {Record<string, Node.Name | Node.Calc | Node.Value>} params 模板参数定义
2770
+ * @property {Child[]} children 子元素
2771
+ * @property {string} [comment] 注释
2682
2772
  */
2683
- function stringify(value, formable) {
2684
- const level = formable ? 0 : -1;
2685
- if (Array.isArray(value)) { return [...listToString(value, level)].join(''); }
2686
- return [nodeToString(value, level)].join();
2687
-
2688
- }
2689
-
2690
2773
  /**
2691
- * @typedef {object} Enhancement 增强信息
2692
- * @property {Record<string, Node.Name | Node.Event>} events 事件
2693
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2694
- * @property {Node.Name | Node.Calc | Node.Value} [value] 主值
2695
- * @property {boolean | string} [bind] 绑定信息
2774
+ *
2775
+ * @typedef {object} DivergentChildren 分歧项
2776
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2777
+ * @property {Record<string, Template>} [templates]
2778
+ * @property {Child[]} children 子元素
2779
+ * @property {string} [comment] 注释
2696
2780
  */
2697
-
2698
2781
  /**
2699
- * @typedef {object} Options 解析选项
2700
- * @property {boolean} [enableHTML] 启用 `!html` 指令
2701
- * @property {(t: string) => Calc} [createCalc] 创建计算属性的工厂函数
2702
- * @property {(t: string) => Calc} [createInit] 创建变量初始化的工厂函数
2703
- * @property {(t: string) => EventListener} [createEvent] 创建事件监听器的工厂函数
2704
- * @property {Set<string>} [simpleTag] 简单标签的集合
2782
+ *
2783
+ * @typedef {object} Divergent 分歧
2784
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2785
+ * @property {Record<string, Template>} [templates]
2786
+ * @property {'divergent'} type
2787
+ * @property {[children: DivergentChildren, condition?: Node.Name | Node.Calc | Node.Value | null][]} children
2788
+ * @property {string} [comment] 注释
2705
2789
  */
2706
2790
  /**
2707
- * @template [T=any]
2708
- * @typedef {{value: T; name?: undefined; calc?: undefined; event?: undefined}} Node.Value 基础值
2791
+ *
2792
+ * @typedef {object} Select 选值
2793
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2794
+ * @property {Record<string, Template>} [templates]
2795
+ * @property {'value'} type
2796
+ * @property {string} name
2797
+ * @property {Child[]} children 子元素
2798
+ * @property {string} [comment] 注释
2709
2799
  */
2710
2800
  /**
2711
- * @typedef {{name: string; value?: undefined; calc?: undefined; event?: undefined}} Node.Name 名称
2801
+ *
2802
+ * @typedef {object} Enum 枚举
2803
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2804
+ * @property {Record<string, Template>} [templates]
2805
+ * @property {'enum'} type
2806
+ * @property {Node.Name | Node.Calc | Node.Value} value
2807
+ * @property {Child[]} children 子元素
2808
+ * @property {string} [comment] 注释
2712
2809
  */
2713
2810
  /**
2714
- * @typedef {{event: EventListener; name?: undefined; calc?: undefined; value?: undefined}} Node.Event 事件
2811
+ *
2812
+ * @typedef {object} Content 内容填充
2813
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2814
+ * @property {Record<string, Template>} [templates]
2815
+ * @property {'content'} type
2816
+ * @property {Node.Name | Node.Calc | Node.Value} value
2817
+ * @property {boolean} [html]
2818
+ * @property {string} [comment] 注释
2715
2819
  */
2716
2820
  /**
2717
- * @typedef {{calc: Calc; name?: undefined; value?: undefined; event?: undefined}} Node.Calc 计算
2821
+ *
2822
+ * @typedef {object} CallTemplate 模板调用
2823
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2824
+ * @property {Record<string, Template>} [templates]
2825
+ * @property {'template'} type
2826
+ * @property {string} template 模板名
2827
+ * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2828
+ * @property {boolean | string} [bind] 绑定内容
2829
+ * @property {Child[]} children 子元素
2830
+ * @property {string} [comment] 注释
2831
+ */
2832
+
2833
+ /**
2834
+ * @typedef {object} Fragment 片段
2835
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2836
+ * @property {Record<string, Template>} [templates]
2837
+ * @property {'fragment'} type
2838
+ * @property {Child[]} children 子元素
2839
+ * @property {string} [comment] 注释
2718
2840
  */
2841
+
2842
+
2719
2843
  /**
2720
2844
  * @typedef {object} Node 布局节点
2845
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2846
+ * @property {Record<string, Template>} [templates]
2847
+ * @property {null} [type]
2848
+ *
2721
2849
  * @property {string} name 标签名
2722
2850
  * @property {string?} [is]
2723
2851
  * @property {string} [id]
2724
2852
  * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2725
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} params 模板参数定义
2726
2853
  * @property {Record<string, Node.Name | Node.Calc | Node.Value>} classes 类名
2727
2854
  * @property {Record<string, Node.Name | Node.Calc | Node.Value>} styles 样式
2728
2855
  * @property {Record<string, Node.Name | Node.Event>} events 事件
2729
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} vars 局部变量
2730
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} aliases 别名/计算名
2731
2856
  * @property {Record<string, Enhancement>} enhancements 增强
2732
2857
  *
2733
- * @property {string} [template] 模板定义的名称
2734
- * @property {boolean | string} [fragment] 是否为片段或模板调用
2735
- *
2736
- * @property {Node.Name | Node.Calc | Node.Value} [if] 分歧条件
2737
- * @property {boolean} [else] 否定
2738
- *
2739
- * @property {string} [value] 值关联
2740
- * @property {Node.Name | Node.Calc | Node.Value} [enum] 列表属性枚举
2741
2858
  *
2742
2859
  * @property {boolean | string} [bind] 绑定内容
2743
- * @property {Node.Name | Node.Value | Node.Calc} [text] 文本渲染
2744
- * @property {Node.Name | Node.Value | Node.Calc} [html] HTML 渲染
2745
2860
  *
2746
2861
  * @property {string} [comment] 注释
2747
2862
  *
2748
- * @property {(Node | string)[]} children 子元素
2863
+ * @property {Child[]} children 子元素
2749
2864
  */
2750
2865
 
2866
+
2751
2867
  /**
2752
2868
  * @callback Calc 计算函数
2753
2869
  * @param {Record<string, any>} env 上下文环境
@@ -2762,10 +2878,9 @@
2762
2878
  * @returns {void}
2763
2879
  */
2764
2880
 
2765
- var index$1 = /*#__PURE__*/Object.freeze({
2881
+ var index = /*#__PURE__*/Object.freeze({
2766
2882
  __proto__: null,
2767
- parse: parse,
2768
- stringify: stringify
2883
+ parse: parse
2769
2884
  });
2770
2885
 
2771
2886
  /**
@@ -2861,7 +2976,7 @@
2861
2976
  null: true,
2862
2977
  index: true,
2863
2978
  no: true,
2864
- length: true,
2979
+ size: true,
2865
2980
 
2866
2981
  error: true,
2867
2982
  errors: true,
@@ -2920,7 +3035,7 @@
2920
3035
  yield [`${key}${sign}downMovable`, {get: () => {
2921
3036
  const s = val.index;
2922
3037
  if (typeof s !== 'number') { return false; }
2923
- if (s >= parent.length - 1) { return false; }
3038
+ if (s >= parent.size - 1) { return false; }
2924
3039
  return true;
2925
3040
  }}];
2926
3041
  yield [`${key}${sign}remove`, {exec: () => parent.remove(Number(val.index))}];
@@ -2933,7 +3048,7 @@
2933
3048
  yield [`${key}${sign}downMove`, {exec: () => {
2934
3049
  const s = val.index;
2935
3050
  if (typeof s !== 'number') { return; }
2936
- if (s >= parent.length - 1) { return; }
3051
+ if (s >= parent.size - 1) { return; }
2937
3052
  parent.move(s, s + 1);
2938
3053
  }}];
2939
3054
  }
@@ -3085,10 +3200,9 @@
3085
3200
  watch(value, cb) { return watch(() => this.exec(value), cb, true); }
3086
3201
 
3087
3202
  /**
3088
- * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null} [en]
3203
+ * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} en
3089
3204
  */
3090
3205
  enum(en) {
3091
- if (!en) { return true; }
3092
3206
  const {name, calc} = en;
3093
3207
  if (typeof calc === 'function') { return () => calc(this.getters); }
3094
3208
  if (typeof name === 'string') {
@@ -3274,17 +3388,16 @@
3274
3388
  const store = this.store;
3275
3389
  const parent = this.#parent;
3276
3390
  const object = this.#object;
3391
+ for (const [key, item] of toItem(store)) {
3392
+ ais[key] = item;
3393
+ }
3394
+ for (const [key, item] of toParentItem(parent, store)) {
3395
+ ais[key] = item;
3396
+ }
3277
3397
  if (object) {
3278
3398
  for (const k of Object.keys(object)) {
3279
3399
  ais[`$${k}`] = {get: () => object[k]};
3280
3400
  }
3281
- } else {
3282
- for (const [key, item] of toItem(store)) {
3283
- ais[key] = item;
3284
- }
3285
- for (const [key, item] of toParentItem(parent, store)) {
3286
- ais[key] = item;
3287
- }
3288
3401
  }
3289
3402
  this.#allItems = ais;
3290
3403
  return ais;
@@ -3293,11 +3406,13 @@
3293
3406
  *
3294
3407
  * @param {Store} store
3295
3408
  * @param {Store} parent
3409
+ * @param {Record<string, any>} [object]
3296
3410
  */
3297
- setStore(store, parent) {
3411
+ setStore(store, parent, object) {
3298
3412
  const cloned = new Environment(store, this);
3299
3413
  if (parent) { cloned.#parent = parent; }
3300
3414
  setStore(cloned.#schemaItems, store);
3415
+ if (object) { cloned.#object = object; }
3301
3416
  return cloned;
3302
3417
  }
3303
3418
  /**
@@ -3314,12 +3429,12 @@
3314
3429
  }
3315
3430
  /**
3316
3431
  *
3317
- * @param {Layout.Node} template
3318
- * @param {Layout.Node} source
3432
+ * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value<any>>} params
3433
+ * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value<any>>} attrs
3319
3434
  * @param {Environment} sourceEnv
3320
3435
  * @param {string | null | boolean} [bind]
3321
3436
  */
3322
- params({params}, {attrs}, sourceEnv, bind) {
3437
+ params(params, attrs, sourceEnv, bind) {
3323
3438
  /** @type {Store} */
3324
3439
  let store = this.store;
3325
3440
  if (bind === true) {
@@ -3398,22 +3513,39 @@
3398
3513
  }
3399
3514
  /**
3400
3515
  *
3401
- * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} aliases
3402
- * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} vars
3516
+ * @param {Layout.Variable[]?} [vars]
3517
+ * @returns
3403
3518
  */
3404
- set(aliases, vars) {
3405
- if (Object.keys(aliases).length + Object.keys(vars).length === 0) { return this; }
3519
+ set(vars) {
3520
+ if (!vars?.length) { return this; }
3406
3521
  const cloned = new Environment(this.store, this);
3407
3522
  cloned.#parent = this.#parent;
3408
3523
  cloned.#object = this.#object;
3409
3524
  const explicit = cloned.#explicit;
3410
3525
  const items = cloned.#items;
3411
- for (const [key, {name, calc, value}] of Object.entries(aliases)) {
3526
+ for (const { variable, name, calc, value, init } of vars) {
3527
+ if (init) {
3528
+ const val = new exports.Signal.State(/** @type {any} */(value));
3529
+ if (typeof calc === 'function') {
3530
+ const settable = cloned.settable;
3531
+ cloned.#settable = null;
3532
+ val.set(calc(settable));
3533
+ } else if (name) {
3534
+ const item = items[name];
3535
+ if (!item?.get) { continue; }
3536
+ val.set(item.get());
3537
+ }
3538
+ explicit[variable] = items[variable] = {
3539
+ get: () => { return val.get(); },
3540
+ set: (v) => { val.set(v); },
3541
+ };
3542
+ continue;
3543
+ }
3412
3544
  if (typeof calc === 'function') {
3413
3545
  const getters = cloned.getters;
3414
3546
  cloned.#getters = null;
3415
3547
  const val = new exports.Signal.Computed(() => calc(getters));
3416
- explicit[key] = items[key] = {
3548
+ explicit[variable] = items[variable] = {
3417
3549
  get: () => { return val.get(); },
3418
3550
  };
3419
3551
  continue;
@@ -3422,34 +3554,17 @@
3422
3554
  const item = items[name];
3423
3555
  if (!item) { continue; }
3424
3556
  if (!item.get || !item.store) {
3425
- explicit[key] = items[key] = item;
3557
+ explicit[variable] = items[variable] = item;
3426
3558
  continue;
3427
3559
  }
3428
- for (const [k, it] of toItem(item.store, key)) {
3560
+ for (const [k, it] of toItem(item.store, variable)) {
3429
3561
  explicit[k] = items[k] = it;
3430
3562
  }
3431
3563
  continue;
3432
3564
  }
3433
- explicit[key] = items[key] = { get: () => { return value; } };
3565
+ explicit[variable] = items[variable] = { get: () => { return value; } };
3434
3566
  continue;
3435
3567
  }
3436
- for (const [k,{name, calc, value}] of Object.entries(vars)) {
3437
-
3438
- const val = new exports.Signal.State(/** @type {any} */(value));
3439
- if (typeof calc === 'function') {
3440
- const settable = cloned.settable;
3441
- cloned.#settable = null;
3442
- val.set(calc(settable));
3443
- } else if (name) {
3444
- const item = items[name];
3445
- if (!item?.get) { continue }
3446
- val.set(item.get());
3447
- }
3448
- explicit[k] = items[k] = {
3449
- get: () => { return val.get(); },
3450
- set: (v) => { val.set(v); },
3451
- };
3452
- }
3453
3568
  return cloned;
3454
3569
  }
3455
3570
 
@@ -4432,17 +4547,24 @@
4432
4547
  }
4433
4548
 
4434
4549
  }
4550
+ const count = new exports.Signal.State(0);
4435
4551
  const childrenResult = watch(() => store.children, function render(children) {
4436
4552
  if (!start.parentNode) { return; }
4437
4553
  let nextNode = start.nextSibling;
4438
4554
  const oldSeMap = seMap;
4439
4555
  seMap = new Map();
4556
+ count.set(children.length);
4440
4557
  for (let child of children) {
4441
4558
  const old = oldSeMap.get(child);
4442
4559
  if (!old) {
4443
4560
  const ItemStart = parent.insertBefore(document.createComment(''), nextNode);
4444
4561
  const itemEnd = parent.insertBefore(document.createComment(''), nextNode);
4445
- const d = renderItem(itemEnd, env.setStore(child, store));
4562
+ const d = renderItem(itemEnd, env.setStore(child, store, {
4563
+ get count() { return count.get() },
4564
+ get key() { return child.index; },
4565
+ get index() { return child.index; },
4566
+ get item() { return child.value; },
4567
+ }));
4446
4568
  seMap.set(child, [ItemStart, itemEnd, d]);
4447
4569
  continue;
4448
4570
  }
@@ -4489,18 +4611,18 @@
4489
4611
  * @param {Element} parent
4490
4612
  * @param {Node?} next
4491
4613
  * @param {Environment} envs
4492
- * @param {Layout.Node} layout
4614
+ * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} value
4615
+ * @param {boolean} [isHtml]
4493
4616
  */
4494
- function renderFillDirectives(parent, next, envs, { text, html }) {
4495
- if (text != null) {
4617
+ function renderFillDirectives(parent, next, envs, value, isHtml) {
4618
+ if (!isHtml) {
4496
4619
  const node = parent.insertBefore(document.createTextNode(''), next);
4497
- const stop = envs.watch(text, val => node.textContent = toText(val));
4620
+ const stop = envs.watch(value, val => node.textContent = toText(val));
4498
4621
  return () => {
4499
4622
  node.remove();
4500
4623
  stop();
4501
4624
  };
4502
4625
  }
4503
- if (html == null) { return; }
4504
4626
  const start = parent.insertBefore(document.createComment(''), next);
4505
4627
  const end = parent.insertBefore(document.createComment(''), next);
4506
4628
  const div = document.createElement('div');
@@ -4516,7 +4638,7 @@
4516
4638
  node.remove();
4517
4639
  }
4518
4640
  }
4519
- const result = envs.watch(html, val => {
4641
+ const result = envs.watch(value, val => {
4520
4642
  remove();
4521
4643
  add(toText(val));
4522
4644
  });
@@ -4528,113 +4650,7 @@
4528
4650
  };
4529
4651
  }
4530
4652
 
4531
- /** @import * as Layout from '../Layout/index.mjs' */
4532
-
4533
- /**
4534
- * @param {(Layout.Node | string)[]} layouts
4535
- * @param {Element} parent
4536
- * @param {Node?} next
4537
- * @param {Environment} envs
4538
- * @param {Record<string, [Layout.Node, Environment]>} templates
4539
- * @param {(layout: Layout.Node, templates: Record<string, any>) => () => void} renderItem
4540
- * @returns {() => void}
4541
- */
4542
- function renderList(layouts, parent, next, envs, templates, renderItem) {
4543
-
4544
- /** @type {Set<() => void>?} */
4545
- let bkList = new Set();
4546
- /** @type {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, Layout.Node][]} */
4547
- let ifList = [];
4548
- /** @type {Record<string, [Layout.Node, Environment]>} */
4549
- let currentTemplates = Object.create(templates);
4550
- for (const layout of layouts) {
4551
- if (typeof layout === 'string') { continue; }
4552
- const name = layout.template;
4553
- if (!name) { continue; }
4554
- currentTemplates[name] = [layout, envs];
4555
- }
4556
-
4557
- /** @param {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, Layout.Node][]} list */
4558
- function renderIf(list) {
4559
- if (!list.length || !bkList) { return; }
4560
- const end = parent.insertBefore(document.createComment(''), next);
4561
-
4562
-
4563
- let lastIndex = -1;
4564
- let destroy = () => { };
4565
- /**
4566
- *
4567
- * @param {number} index
4568
- * @returns
4569
- */
4570
- function renderIndex(index) {
4571
- const layout = list[index]?.[1];
4572
- if (!layout) { return; }
4573
- destroy = renderItem(layout, currentTemplates);
4574
- }
4575
- bkList.add(() => {
4576
- destroy();
4577
- destroy = () => { };
4578
- end.remove();
4579
- });
4580
- bkList.add(watch(
4581
- () => list.findIndex(([ifv]) => !ifv || envs.exec(ifv)),
4582
- index => {
4583
- if (index === lastIndex) { return; }
4584
- lastIndex = index;
4585
- destroy();
4586
- destroy = () => { };
4587
- renderIndex(lastIndex);
4588
- }, true,
4589
- ));
4590
- }
4591
- for (const layout of layouts) {
4592
- if (typeof layout === 'string') {
4593
- renderIf(ifList);
4594
- ifList = [];
4595
- const node = document.createTextNode(layout);
4596
- parent.insertBefore(node, next);
4597
- bkList.add(() => node.remove());
4598
- continue;
4599
- }
4600
- if (layout.template) {
4601
- renderIf(ifList);
4602
- ifList = [];
4603
- continue;
4604
- }
4605
- if (ifList.length && layout.else) {
4606
- const ifv = layout.if || null;
4607
- ifList.push([ifv, layout]);
4608
- if (!ifv) {
4609
- renderIf(ifList);
4610
- ifList = [];
4611
- }
4612
- continue;
4613
- }
4614
- renderIf(ifList);
4615
- ifList = [];
4616
- const ifv = layout.if;
4617
- if (ifv) {
4618
- ifList.push([ifv, layout]);
4619
- continue;
4620
- }
4621
- bkList.add(
4622
- renderItem(layout, currentTemplates)
4623
- );
4624
- }
4625
- renderIf(ifList);
4626
-
4627
- return () => {
4628
- if (!bkList) { return; }
4629
- const list = bkList;
4630
- bkList = null;
4631
- for (const s of list) {
4632
- s();
4633
- }
4634
- };
4635
- }
4636
-
4637
- /** @import { ObjectStore } from '../Store/index.mjs' */
4653
+ /** @import { ObjectStore, Store } from '../Store/index.mjs' */
4638
4654
 
4639
4655
  /**
4640
4656
  *
@@ -4647,8 +4663,16 @@
4647
4663
  function renderObject(parent, next, store, env, renderItem) {
4648
4664
  /** @type {(() => void)[]} */
4649
4665
  const children = [];
4650
- for (const [k, child] of [...store]) {
4651
- children.push(renderItem(next, env.setStore(child, store)));
4666
+ /** @type {[string, Store<any, any>, number][]} */
4667
+ const childStores = [...store].map(([k,v], i) => [k,v,i]);
4668
+ const count = childStores.length;
4669
+ for (const [key, child, index] of childStores) {
4670
+ children.push(renderItem(next, env.setStore(child, store, {
4671
+ get count() { return count; },
4672
+ get key() { return key; },
4673
+ get index() { return index; },
4674
+ get item() { return child.value; },
4675
+ })));
4652
4676
  }
4653
4677
 
4654
4678
  return () => {
@@ -4694,11 +4718,13 @@
4694
4718
  e.remove();
4695
4719
  }
4696
4720
  }
4721
+ const count = new exports.Signal.State(0);
4697
4722
  const childrenResult = watch(() => list.get(), function render(children) {
4698
4723
  if (!start.parentNode) { return; }
4699
4724
  let nextNode = start.nextSibling;
4700
4725
  const oldSeMap = seMap;
4701
4726
  seMap = [];
4727
+ count.set(children.length);
4702
4728
  for (const [value, index, key] of children) {
4703
4729
  const index2 = oldSeMap.findIndex((v) => v[3] === key);
4704
4730
  const [old] = index2 >= 0 ? oldSeMap.splice(index2, 1) : [];
@@ -4708,8 +4734,9 @@
4708
4734
  const valueState = new exports.Signal.State(value);
4709
4735
  const indexState = new exports.Signal.State(index);
4710
4736
  const d = renderItem(itemEnd, env.setObject({
4737
+ get count() { return count.get() },
4711
4738
  get key() { return key; },
4712
- get value() { return valueState.get(); },
4739
+ get item() { return valueState.get(); },
4713
4740
  get index() { return indexState.get(); },
4714
4741
  }));
4715
4742
  seMap.push([ItemStart, itemEnd, d, key, valueState, indexState]);
@@ -4898,6 +4925,50 @@
4898
4925
  };
4899
4926
  }
4900
4927
 
4928
+ /** @import * as Layout from '../Layout/index.mjs' */
4929
+
4930
+ /**
4931
+ * @param {Layout.Divergent} layout
4932
+ * @param {Element} parent
4933
+ * @param {Node?} next
4934
+ * @param {Environment} env
4935
+ * @param {(layout: Layout.Child[], vars?: Layout.Variable[]?, templates?: Record<string, any>) => () => void} renderItem
4936
+ * @returns {() => void}
4937
+ */
4938
+ function divergent(layout, parent, next, env, renderItem) {
4939
+ const children = layout.children;
4940
+ if (!children.length) { return () => { }; }
4941
+ const end = parent.insertBefore(document.createComment(''), next);
4942
+ /** @type {typeof children[0]?} */
4943
+ let last = null;
4944
+ let destroy = () => { };
4945
+ const stop = () => {
4946
+ destroy();
4947
+ destroy = () => { };
4948
+ end.remove();
4949
+ };
4950
+ const unwatch = watch(
4951
+ () => children.find(([, ifv]) => !ifv || env.exec(ifv)) || null,
4952
+ item => {
4953
+ if (item === last) { return; }
4954
+ last = item;
4955
+ destroy();
4956
+ destroy = () => { };
4957
+ const layout = item?.[0];
4958
+ if (!layout) { return; }
4959
+ destroy = renderItem(layout.children, layout.vars, layout.templates);
4960
+ }, true,
4961
+ );
4962
+
4963
+ let destroyed = false;
4964
+ return () => {
4965
+ if (destroyed) { return; }
4966
+ destroyed = true;
4967
+ unwatch();
4968
+ stop();
4969
+ };
4970
+ }
4971
+
4901
4972
  /** @import Store from '../Store/index.mjs' */
4902
4973
 
4903
4974
  /**
@@ -4905,37 +4976,19 @@
4905
4976
  * @param {Element} parent
4906
4977
  * @param {Node?} next
4907
4978
  * @param {Environment} env
4908
- * @param {Record<string, [Layout.Node, Environment]>} templates
4979
+ * @param {Record<string, [Layout.Template, Environment]>} templates
4909
4980
  * @param {string[]} componentPath
4910
4981
  * @param {Record<string, Enhancement>} enhancements
4911
4982
  * @param {((store: Store, el: Element | Relatedness) => () => void)?} [relate]
4912
4983
  * @param {Component.Getter?} [getComponent]
4913
4984
  */
4914
- function renderItem(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
4915
- env = env.set(layout.aliases, layout.vars);
4985
+ function renderNode(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
4916
4986
  const bind = layout.bind;
4917
- const fragment = layout.fragment;
4918
- if (fragment && typeof fragment === 'string') {
4919
- const template = templates[fragment];
4920
- if (!template) { return () => {}; }
4921
- const [templateLayout, templateEnv] = template;
4922
- const newEnv = templateEnv.params(templateLayout, layout, env, bind);
4923
- return render(templateLayout, parent, next, newEnv, templates, componentPath, enhancements, relate, getComponent);
4924
- }
4925
- if (!layout.name || layout.fragment) {
4926
- return renderFillDirectives(parent, next, env, layout) ||
4927
- renderList(layout.children || [], parent, next, env, templates, (layout, templates) => {
4928
- return render(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
4929
- });
4930
- }
4931
4987
  const path = [...componentPath, layout.name];
4932
4988
  const component = getComponent?.(path);
4933
4989
  if (getComponent && !component) { return () => { }; }
4934
4990
  const { context, handler } = createContext(
4935
- component ? component : layout.name,
4936
- env,
4937
- env.getStore(bind),
4938
- relate
4991
+ component ? component : layout.name, env, env.getStore(bind), relate
4939
4992
  );
4940
4993
 
4941
4994
  const componentAttrs = component?.attrs;
@@ -4959,11 +5012,9 @@
4959
5012
  const root = Array.isArray(r) ? r[0] : r;
4960
5013
  const slot = Array.isArray(r) ? r[1] : root;
4961
5014
  parent.insertBefore(root, next);
4962
- const children = slot ?
4963
- renderFillDirectives(slot, null, env, layout)
4964
- || renderList(layout.children || [], slot, null, env, templates, (layout, templates) => {
4965
- return render(layout, slot, null, env, templates, componentPath, enhancements, relate, getComponent);
4966
- }) : () => {};
5015
+ const children = slot ?
5016
+ renderChildren(layout.children || [], slot, null, env, templates, componentPath, enhancements, relate, getComponent)
5017
+ : () => { };
4967
5018
 
4968
5019
 
4969
5020
  const classes = bindClasses(root, env, layout.classes, layout.attrs.class);
@@ -4971,7 +5022,7 @@
4971
5022
 
4972
5023
  handler.mount();
4973
5024
  const enhancement = bindEnhancements(handler.tag, layout.enhancements, env, enhancements, root, slot);
4974
-
5025
+
4975
5026
  return () => {
4976
5027
  enhancement();
4977
5028
  handler.destroy();
@@ -4983,43 +5034,117 @@
4983
5034
  styles();
4984
5035
  };
4985
5036
  }
5037
+
4986
5038
  /**
4987
5039
  *
4988
- * @param {Layout.Node} layout
5040
+ * @param {Environment} env
5041
+ * @param {Record<string, [Layout.Template, Environment]>} parentTemplates
5042
+ * @param {Record<string, Layout.Template>} [newTemplates]
5043
+ * @returns {Record<string, [Layout.Template, Environment]>}
5044
+ */
5045
+ function createTemplates(env, parentTemplates, newTemplates) {
5046
+ if (!newTemplates) { return parentTemplates; } const layOutTemplates = Object.entries(newTemplates);
5047
+ if (!layOutTemplates.length) { return parentTemplates; }
5048
+ /** @type {Record<string, [Layout.Template, Environment]>} */
5049
+ const templates = Object.create(parentTemplates);
5050
+ for (const [name, template] of layOutTemplates) {
5051
+ templates[name] = [template, env];
5052
+ }
5053
+ return templates;
5054
+ }
5055
+ /**
5056
+ *
5057
+ * @param {Layout.Child} layout
4989
5058
  * @param {Element} parent
4990
5059
  * @param {Node?} next
4991
- * @param {Environment} env
4992
- * @param {Record<string, [Layout.Node, Environment]>} templates
5060
+ * @param {Environment} parentEnv
5061
+ * @param {Record<string, [Layout.Template, Environment]>} parentTemplates
4993
5062
  * @param {string[]} componentPath
4994
5063
  * @param {Record<string, Enhancement>} enhancements
4995
5064
  * @param {((store: Store, el: Element | Relatedness) => () => void)?} [relate]
4996
5065
  * @param {Component.Getter?} [getComponent]
4997
5066
  * @returns {() => void}
4998
5067
  */
4999
- function render(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
5000
- const newEnv = env.child(layout.value);
5001
- if (!newEnv) { return () => {}; }
5002
- const list = newEnv.enum(layout.enum);
5003
- /** @type {(next: Node | null, env: any) => () => void} */
5004
- const r = (next, env) => renderItem(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
5005
- if (list === true) {
5006
- return r(next, newEnv);
5068
+ function renderChild(layout, parent, next, parentEnv, parentTemplates, componentPath, enhancements, relate, getComponent) {
5069
+ if (typeof layout === 'string') {
5070
+ const node = document.createTextNode(layout);
5071
+ parent.insertBefore(node, next);
5072
+ return () => node.remove();
5007
5073
  }
5008
- if (list instanceof ArrayStore) {
5009
- return renderArray(parent, next, list, newEnv, r);
5074
+ const env = parentEnv.set(layout.vars);
5075
+ const templates = createTemplates(env, parentTemplates, layout.templates);
5076
+ if (layout.type === 'divergent') {
5077
+ return divergent(layout, parent, next, env, (list, vars, templates2) => {
5078
+ const listEnv = env.set(vars);
5079
+ const templates = createTemplates(listEnv, parentTemplates, templates2);
5080
+ return renderChildren(list, parent, next, listEnv, templates, componentPath, enhancements, relate, getComponent);
5081
+ });
5010
5082
  }
5011
- if (list instanceof ObjectStore) {
5012
- return renderObject(parent, next, list, newEnv, r);
5083
+ if (layout.type === 'value') {
5084
+ const newEnv = env.child(layout.name);
5085
+ if (!newEnv) { return () => { }; }
5086
+ return renderChildren(layout.children, parent, next, newEnv, templates, componentPath, enhancements, relate, getComponent);
5013
5087
  }
5014
- if (typeof list === 'function') {
5015
- return renderEnum(parent, next, list, newEnv, r);
5088
+ if (layout.type === 'enum') {
5089
+ const list = env.enum(layout.value);
5090
+ /** @type {(next: Node | null, env: any) => () => void} */
5091
+ const r = (next, env) => renderChildren(layout.children, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
5092
+ if (list instanceof ArrayStore) {
5093
+ return renderArray(parent, next, list, env, r);
5094
+ }
5095
+ if (list instanceof ObjectStore) {
5096
+ return renderObject(parent, next, list, env, r);
5097
+ }
5098
+ if (typeof list === 'function') {
5099
+ return renderEnum(parent, next, list, env, r);
5100
+ }
5101
+ return () => { };
5016
5102
  }
5017
- return () => { };
5103
+ if (layout.type === 'content') {
5104
+ return renderFillDirectives(parent, next, env, layout.value, layout.html);
5105
+ }
5106
+ if (layout.type === 'template') {
5107
+ const template = templates[layout.template];
5108
+ if (!template) { return () => { }; }
5109
+ const [templateLayout, templateEnv] = template;
5110
+ const newEnv = templateEnv.params(templateLayout.params, layout.attrs, env, layout.bind);
5111
+ const subTemplates = createTemplates(newEnv, parentTemplates, templateLayout.templates);
5112
+ return renderChildren(templateLayout.children, parent, next, newEnv, subTemplates, componentPath, enhancements, relate, getComponent);
5113
+ }
5114
+ if (layout.type === 'fragment') {
5115
+ return renderChildren(layout.children, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
5116
+ }
5117
+ return renderNode(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
5118
+ }
5119
+
5120
+ /**
5121
+ *
5122
+ * @param {Layout.Child[]} layouts
5123
+ * @param {Element} parent
5124
+ * @param {Node?} next
5125
+ * @param {Environment} env
5126
+ * @param {Record<string, [Layout.Template, Environment]>} templates
5127
+ * @param {string[]} componentPath
5128
+ * @param {Record<string, Enhancement>} enhancements
5129
+ * @param {((store: Store, el: Element | Relatedness) => () => void)?} [relate]
5130
+ * @param {Component.Getter?} [getComponent]
5131
+ * @returns {() => void}
5132
+ */
5133
+ function renderChildren(layouts, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
5134
+ const list = layouts.map(v => renderChild(v, parent, next, env, templates, componentPath, enhancements, relate, getComponent));
5135
+ let removed = false;
5136
+ return () => {
5137
+ if (removed) { return; }
5138
+ removed = true;
5139
+ for (const k of list) {
5140
+ k();
5141
+ }
5142
+ };
5018
5143
  }
5019
5144
 
5020
5145
  /**
5021
5146
  * @param {Store} store 存储实例
5022
- * @param {(Layout.Node | string)[]} layouts 布局信息
5147
+ * @param {Layout.Child[]} layouts 布局信息
5023
5148
  * @param {Element} parent 渲染节点
5024
5149
  * @param {object} [options] 选项
5025
5150
  * @param {Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }>} [options.global] 全局数据
@@ -5028,20 +5153,18 @@
5028
5153
  * @param {Record<string, Enhancement>} [options.enhancements] 增强信息
5029
5154
  * @returns {() => void}
5030
5155
  */
5031
- function index (store, layouts, parent, {component, global, relate, enhancements} = {}) {
5156
+ function render(store, layouts, parent, { component, global, relate, enhancements } = {}) {
5032
5157
  const env = new Environment(store, global);
5033
5158
  const templates = Object.create(null);
5034
5159
  const allEnhancements = enhancements || {};
5035
- const relateFn = typeof relate === 'function' ? relate : null;
5036
- return renderList(layouts, parent, null, env, templates, (layout, templates) => {
5037
- return render(layout, parent, null, env, templates, [], allEnhancements, relateFn, component);
5038
- });
5160
+ const relateFn = typeof relate === 'function' ? relate : null;
5161
+ return renderChildren(layouts, parent, null, env, templates, [], allEnhancements, relateFn, component);
5039
5162
  }
5040
5163
 
5041
- exports.Layout = index$1;
5164
+ exports.Layout = index;
5042
5165
  exports.Store = Store;
5043
5166
  exports.effect = effect;
5044
- exports.render = index;
5167
+ exports.render = render;
5045
5168
  exports.watch = watch;
5046
5169
 
5047
5170
  }));