@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.mjs 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
  */
@@ -73,7 +73,7 @@ function toValueItem(v) {
73
73
 
74
74
  }
75
75
  /** @param {*} v */
76
- const values$1 = v => {
76
+ const values = v => {
77
77
  if (!v || !Array.isArray(v)) { return null;}
78
78
  const list = v.map(toValueItem).filter(valueFilter);
79
79
  if (!list.length) { return null; }
@@ -148,7 +148,8 @@ let ArrayStore$1 = null;
148
148
  /** @type {Record<string, {new(...p: ConstructorParameters<typeof Store>): Store}?>} */
149
149
  let TypeStores = Object.create(null);
150
150
  /**
151
- * @param {Schema.Field} schema
151
+ * @template [M=any]
152
+ * @param {Schema.Field<M>} schema
152
153
  * @param {object} [options]
153
154
  * @param {Store?} [options.parent]
154
155
  * @param {string | number | null} [options.index]
@@ -285,6 +286,7 @@ function merge(...v) {
285
286
  /**
286
287
  * 管理单个表单字段的状态和行为
287
288
  * @template [T=any]
289
+ * @template [M=any]
288
290
  */
289
291
  class Store {
290
292
  /** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
@@ -326,7 +328,8 @@ class Store {
326
328
  }
327
329
  /**
328
330
  * 从数据结构模式创建存储
329
- * @param {Schema} schema 数据结构模式
331
+ * @template [M=any]
332
+ * @param {Schema<M>} schema 数据结构模式
330
333
  * @param {object} [options] 选项
331
334
  * @param {boolean} [options.new] 是否为新建环境
332
335
  */
@@ -350,12 +353,12 @@ class Store {
350
353
  #ref = null;
351
354
  get ref() { return this.#ref || createRef(this); }
352
355
  /**
353
- * @param {Schema.Field} schema 字段的 Schema 定义
356
+ * @param {Schema.Field<M>} schema 字段的 Schema 定义
354
357
  * @param {object} [options] 可选配置
355
358
  * @param {*} [options.parent]
356
359
  * @param {*} [options.state]
357
360
  * @param {number | string | null} [options.index]
358
- * @param {number | Signal.State<number> | Signal.Computed<number>} [options.length]
361
+ * @param {number | Signal.State<number> | Signal.Computed<number>} [options.size]
359
362
  * @param {boolean} [options.null]
360
363
  * @param {boolean} [options.new]
361
364
  * @param {boolean} [options.hidden]
@@ -390,9 +393,9 @@ class Store {
390
393
  null: isNull, state, ref,
391
394
  setValue, setState, convert, onUpdate, onUpdateState,
392
395
  validator, validators,
393
- index, length, new: isNew, parent: parentNode,
396
+ index, size, new: isNew, parent: parentNode,
394
397
  hidden, clearable, required, disabled, readonly,
395
- label, description, placeholder, min, max, step, minLength, maxLength, pattern, values
398
+ label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
396
399
  } = {}) {
397
400
  this.schema = schema;
398
401
  this.#state.set(typeof state === 'object' && state || {});
@@ -455,7 +458,7 @@ class Store {
455
458
  [this.#selfMaxLength, this.#maxLength] = createState(this, number, maxLength, schema.maxLength);
456
459
  [this.#selfPattern, this.#pattern] = createState(this, regex, pattern, schema.pattern);
457
460
  // @ts-ignore
458
- [this.#selfValues, this.#values] = createState(this, values$1, values, schema.values);
461
+ [this.#selfValues, this.#values] = createState(this, values, values$1, schema.values);
459
462
 
460
463
  const validatorResult = createValidator(this, schema.validator, validator);
461
464
 
@@ -470,10 +473,10 @@ class Store {
470
473
  this.#cancelChange = cancelChange;
471
474
  this.#cancelBlur = cancelBlur;
472
475
 
473
- if (length instanceof Signal.State || length instanceof Signal.Computed) {
474
- this.#length = length;
476
+ if (size instanceof Signal.State || size instanceof Signal.Computed) {
477
+ this.#size = size;
475
478
  } else {
476
- this.#length = new Signal.State(length || 0);
479
+ this.#size = new Signal.State(size || 0);
477
480
  }
478
481
 
479
482
  if (isNull) {
@@ -511,7 +514,7 @@ class Store {
511
514
  #root = this;
512
515
  /** @readonly @type {any} */
513
516
  #type;
514
- /** @readonly @type {any} */
517
+ /** @readonly @type {M | void} */
515
518
  #meta;
516
519
  /** @readonly @type {any} */
517
520
  #component;
@@ -529,9 +532,9 @@ class Store {
529
532
  get component() { return this.#component; }
530
533
 
531
534
  /** @type {Signal.State<number> | Signal.Computed<number>} */
532
- #length;
535
+ #size;
533
536
  /** 长度信息 */
534
- get length() { return this.#length.get(); }
537
+ get size() { return this.#size.get(); }
535
538
  #index = new Signal.State(/** @type {string | number} */(''));
536
539
  /** 索引信息 */
537
540
  get index() { return this.#index.get(); }
@@ -713,10 +716,10 @@ class Store {
713
716
  /** @readonly @type {Signal.Computed<(Schema.Value.Group | Schema.Value)[] | null>} */
714
717
  #values
715
718
  get selfValues() { return this.#selfValues.get(); }
716
- set selfValues(v) { this.#selfValues.set(values$1(v)); }
719
+ set selfValues(v) { this.#selfValues.set(values(v)); }
717
720
  /** 可选值列表 */
718
721
  get values() { return this.#values.get(); }
719
- set values(v) { this.#selfValues.set(values$1(v)); }
722
+ set values(v) { this.#selfValues.set(values(v)); }
720
723
 
721
724
 
722
725
  /** @type {Signal.Computed<string[]>} */
@@ -918,7 +921,8 @@ class Store {
918
921
 
919
922
  /**
920
923
  * @template {Record<string, any>} [T=Record<string, any>]
921
- * @extends {Store<T>}
924
+ * @template [M=any]
925
+ * @extends {Store<T, M>}
922
926
  */
923
927
  class ObjectStore extends Store {
924
928
  get kind() { return 'object'; }
@@ -932,7 +936,7 @@ class ObjectStore extends Store {
932
936
  */
933
937
  child(key) { return this.#children[key] || null; }
934
938
  /**
935
- * @param {Schema.Object & Schema.Attr} schema
939
+ * @param {Schema.Object<M> & Schema.Attr<M>} schema
936
940
  * @param {object} [options]
937
941
  * @param {Store?} [options.parent]
938
942
  * @param {number | string | null} [options.index]
@@ -944,7 +948,7 @@ class ObjectStore extends Store {
944
948
  const childrenTypes = Object.entries(schema.type);
945
949
  super(schema, {
946
950
  parent, index, new: isNew, onUpdate, onUpdateState,
947
- length: childrenTypes.length,
951
+ size: childrenTypes.length,
948
952
  setValue(v) { return typeof v === 'object' ? v : null; },
949
953
  setState(v) { return typeof v === 'object' ? v : null; },
950
954
  convert(v, state) {
@@ -985,7 +989,8 @@ setObjectStore(ObjectStore);
985
989
 
986
990
  /**
987
991
  * @template [T=any]
988
- * @extends {Store<(T | null)[]>}
992
+ * @template [M=any]
993
+ * @extends {Store<(T | null)[], M>}
989
994
  */
990
995
  class ArrayStore extends Store {
991
996
  /** @type {(index: number, isNew?: boolean) => Store} */
@@ -1008,7 +1013,7 @@ class ArrayStore extends Store {
1008
1013
  }
1009
1014
  get kind() { return 'array'; }
1010
1015
  /**
1011
- * @param {Schema.Field} schema
1016
+ * @param {Schema.Field<M>} schema
1012
1017
  * @param {object} [options]
1013
1018
  * @param {Store?} [options.parent]
1014
1019
  * @param {string | number | null} [options.index]
@@ -1026,6 +1031,7 @@ class ArrayStore extends Store {
1026
1031
  for (let i = children.length; i < length; i++) {
1027
1032
  children.push(this.#create(i));
1028
1033
  }
1034
+ children.length = length;
1029
1035
  if (oldLength !== length) {
1030
1036
  childrenState.set(children);
1031
1037
  }
@@ -1033,7 +1039,7 @@ class ArrayStore extends Store {
1033
1039
  };
1034
1040
  super(schema, {
1035
1041
  index, new: isNew, parent,
1036
- length: new Signal.Computed(() => childrenState.get().length),
1042
+ size: new Signal.Computed(() => childrenState.get().length),
1037
1043
  state: [],
1038
1044
  setValue(v) { return Array.isArray(v) ? v : v == null ? null : [v] },
1039
1045
  setState(v) { return Array.isArray(v) ? v : v == null ? null : [v] },
@@ -1223,6 +1229,158 @@ class ArrayStore extends Store {
1223
1229
  // @ts-ignore
1224
1230
  setArrayStore(ArrayStore);
1225
1231
 
1232
+ /** @import * as Layout from './index.mjs' */
1233
+
1234
+ /** @import { OldNode } from './createElement.mjs' */
1235
+
1236
+ /**
1237
+ * @param {(OldNode | string)[]} [layouts]
1238
+ * @returns {Layout.Child[]}
1239
+ */
1240
+ function renderList(layouts) {
1241
+ if (!layouts?.length) { return []; }
1242
+ /** @type {Layout.Child[]} */
1243
+ const children = [];
1244
+ /** @type {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, OldNode][]} */
1245
+ let ifList = [];
1246
+ /** @type {Record<string, Layout.Template>} */
1247
+ let templates = Object.create(null);
1248
+ let hasTemplate = false;
1249
+ for (const layout of layouts) {
1250
+ if (typeof layout === 'string') { continue; }
1251
+ const name = layout.template;
1252
+ if (!name) { continue; }
1253
+ hasTemplate = true;
1254
+ const children = convertItem(layout);
1255
+ templates[name] = { params: layout.params, children: children ? [children] : [] };
1256
+ }
1257
+
1258
+ /** @param {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, OldNode][]} list */
1259
+ function renderIf(list) {
1260
+ if (!list.length) { return; }
1261
+ children.push({
1262
+ type: 'divergent',
1263
+ children: list.map(([a, b]) => {
1264
+ const child = convertItem(b);
1265
+ if (!child) { return [{ children: [] }, a]; }
1266
+ if (typeof child !== 'string' && child.type === 'fragment') {
1267
+ return [child, a];
1268
+ }
1269
+ return [{ children: [child] }, a];
1270
+ }),
1271
+ });
1272
+ }
1273
+ for (const layout of layouts) {
1274
+ if (typeof layout === 'string') {
1275
+ renderIf(ifList);
1276
+ ifList = [];
1277
+ children.push(layout);
1278
+ continue;
1279
+ }
1280
+ if (layout.template) {
1281
+ renderIf(ifList);
1282
+ ifList = [];
1283
+ continue;
1284
+ }
1285
+ if (ifList.length && layout.else) {
1286
+ const ifv = layout.if || null;
1287
+ ifList.push([ifv, layout]);
1288
+ if (!ifv) {
1289
+ renderIf(ifList);
1290
+ ifList = [];
1291
+ }
1292
+ continue;
1293
+ }
1294
+ renderIf(ifList);
1295
+ ifList = [];
1296
+ const ifv = layout.if;
1297
+ if (ifv) {
1298
+ ifList.push([ifv, layout]);
1299
+ continue;
1300
+ }
1301
+ const s = convertItem(layout);
1302
+ if (s)
1303
+ children.push(s);
1304
+ }
1305
+ renderIf(ifList);
1306
+ if (hasTemplate) {
1307
+ return [{ type: 'fragment', templates, children }];
1308
+ }
1309
+ return children;
1310
+ }
1311
+
1312
+
1313
+ /**
1314
+ * @param {OldNode} layout
1315
+ * @returns {Layout.Child | undefined}
1316
+ */
1317
+ function renderFillDirectives$1({ text, html }) {
1318
+ if (text != null) { return { type: 'content', value: text }; }
1319
+ if (html != null) { return { type: 'content', value: html, html: true }; }
1320
+ }
1321
+
1322
+ /**
1323
+ * @param {OldNode} layout
1324
+ * @returns {Layout.Child?}
1325
+ */
1326
+ function convertNode(layout) {
1327
+ const fragment = layout.fragment;
1328
+ if (fragment && typeof fragment === 'string') {
1329
+ return { type: 'template', template: fragment, attrs: layout.attrs, children: [] };
1330
+ }
1331
+ const child = renderFillDirectives$1(layout);
1332
+ const children = child ? [child] : renderList(layout.children);
1333
+ if (!layout.name || fragment) { return child || { type: 'fragment', children }; }
1334
+ return {
1335
+ name: layout.name,
1336
+ is: layout.is,
1337
+ attrs: layout.attrs,
1338
+ events: layout.events,
1339
+ classes: layout.classes,
1340
+ styles: layout.styles,
1341
+ enhancements: layout.enhancements,
1342
+ bind: layout.bind,
1343
+ comment: layout.comment,
1344
+ children,
1345
+ };
1346
+ }
1347
+ /**
1348
+ *
1349
+ * @param {OldNode} layout
1350
+ * @returns {Layout.Child?}
1351
+ */
1352
+ function convertItem(layout) {
1353
+ let child = convertNode(layout);
1354
+ /** @type {Layout.Variable[]?} */
1355
+ let vars = layout.vars;
1356
+ if (vars.length && child && typeof child !== 'string') {
1357
+ if (child.vars) {
1358
+ child.vars = [...vars, ...child.vars];
1359
+ } else if (child) {
1360
+ child.vars = vars;
1361
+ } else {
1362
+ child = { type: 'fragment', vars, children: [] };
1363
+ }
1364
+ vars = null;
1365
+ }
1366
+ const enumValue = layout.enum;
1367
+ const name = layout.value;
1368
+ if (enumValue) { child = { type: 'enum', value: enumValue, vars, children: child ? [child] : [] }; vars = null; }
1369
+ if (name) { child = { type: 'value', name, vars, children: child ? [child] : [] }; vars = null; }
1370
+ if (vars?.length) {
1371
+ child = { type: 'fragment', vars, children: child ? [child] : [] };
1372
+ }
1373
+ return child;
1374
+ }
1375
+
1376
+ /**
1377
+ * @param {(OldNode | string)[]} layouts 布局信息
1378
+ * @returns {Layout.Child[]}
1379
+ */
1380
+ function convert(layouts) {
1381
+ return renderList(layouts);
1382
+ }
1383
+
1226
1384
  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;
1227
1385
  /**
1228
1386
  *
@@ -1278,6 +1436,7 @@ class ParseError extends Error {
1278
1436
  }
1279
1437
 
1280
1438
  /** @import * as Layout from './index.mjs' */
1439
+ /** @import { OldNode } from './createElement.mjs' */
1281
1440
 
1282
1441
  const attrPattern = /^(?<decorator>[:@!+*\.?]|style:|样式:)?(?<name>-?[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_:\d\.]*)$/u;
1283
1442
  const enhancementPattern = /^~(?<enhancement>[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_\d\.]*)(?:(?<decorator>[:@!])(?<name>-?[\w\p{Unified_Ideograph}_][-\w\p{Unified_Ideograph}_:\d\.]*))?$/u;
@@ -1317,14 +1476,14 @@ function parse$1(value, createCalc) {
1317
1476
  }
1318
1477
 
1319
1478
  /**
1320
- * @param {Layout.Node} node
1479
+ * @param {OldNode} node
1321
1480
  * @param {Exclude<Layout.Options['createCalc'], undefined>} createCalc
1322
1481
  * @param {Exclude<Layout.Options['createInit'], undefined>} createInit
1323
1482
  * @param {Exclude<Layout.Options['createEvent'], undefined>} createEvent
1324
1483
  * @param {boolean} enableHTML
1325
1484
  */
1326
1485
  function createAttributeAdder(node, createCalc, createInit, createEvent, enableHTML) {
1327
- const { attrs, events, classes, styles, vars, aliases, params, enhancements } = node;
1486
+ const { attrs, events, classes, styles, vars, params, enhancements } = node;
1328
1487
  /**
1329
1488
  * @param {string} qName
1330
1489
  * @param {string} value
@@ -1379,11 +1538,11 @@ function createAttributeAdder(node, createCalc, createInit, createEvent, enableH
1379
1538
  return;
1380
1539
  }
1381
1540
  if (decorator === '+') {
1382
- vars[name] = !value ? {value: undefined} : parse$1(value, createInit);
1541
+ vars.push({...value ? parse$1(value, createInit) : {value: undefined}, variable: name, init: true});
1383
1542
  return;
1384
1543
  }
1385
1544
  if (decorator === '*') {
1386
- aliases[name] = parse$1(value, createCalc);
1545
+ vars.push({...parse$1(value, createCalc), variable: name, init: false });
1387
1546
  return;
1388
1547
  }
1389
1548
  if (decorator === '?') {
@@ -1420,10 +1579,40 @@ function createAttributeAdder(node, createCalc, createInit, createEvent, enableH
1420
1579
 
1421
1580
  /** @import * as Layout from './index.mjs' */
1422
1581
 
1582
+ /**
1583
+ * @typedef {object} OldNode 布局节点
1584
+ * @property {string} name 标签名
1585
+ * @property {string?} [is]
1586
+ * @property {string} [id]
1587
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} attrs 属性
1588
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} params 模板参数定义
1589
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} classes 类名
1590
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} styles 样式
1591
+ * @property {Record<string, Layout.Node.Name | Layout.Node.Event>} events 事件
1592
+ * @property {Layout.Variable[]} vars 局部变量/别名/计算名
1593
+ * @property {Record<string, Layout.Enhancement>} enhancements 增强
1594
+ *
1595
+ * @property {string} [template] 模板定义的名称
1596
+ * @property {boolean | string} [fragment] 是否为片段或模板调用
1597
+ *
1598
+ * @property {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [if] 分歧条件
1599
+ * @property {boolean} [else] 否定
1600
+ *
1601
+ * @property {string} [value] 值关联
1602
+ * @property {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} [enum] 列表属性枚举
1603
+ *
1604
+ * @property {boolean | string} [bind] 绑定内容
1605
+ * @property {Layout.Node.Name | Layout.Node.Value | Layout.Node.Calc} [text] 文本渲染
1606
+ * @property {Layout.Node.Name | Layout.Node.Value | Layout.Node.Calc} [html] HTML 渲染
1607
+ *
1608
+ * @property {string} [comment] 注释
1609
+ *
1610
+ * @property {(OldNode | string)[]} children 子元素
1611
+ */
1423
1612
  /**
1424
1613
  * @param {string} name
1425
1614
  * @param {string?} [is]
1426
- * @returns {Layout.Node}
1615
+ * @returns {OldNode}
1427
1616
  *
1428
1617
  */
1429
1618
  function createElement(name, is) {
@@ -1435,8 +1624,7 @@ function createElement(name, is) {
1435
1624
  events: Object.create(null),
1436
1625
  classes: Object.create(null),
1437
1626
  styles: Object.create(null),
1438
- vars: Object.create(null),
1439
- aliases: Object.create(null),
1627
+ vars: [],
1440
1628
  params: Object.create(null),
1441
1629
  enhancements: Object.create(null),
1442
1630
  };
@@ -1689,6 +1877,7 @@ var entityMap = {
1689
1877
  };
1690
1878
 
1691
1879
  /** @import * as Layout from './index.mjs' */
1880
+ /** @import { OldNode } from './createElement.mjs' */
1692
1881
 
1693
1882
  const tagNamePattern = /^(?<name>[\w\p{Unified_Ideograph}_][-\.\|:|d\w\p{Unified_Ideograph}_:]*)(?:|(?<is>[\w\p{Unified_Ideograph}_][-\.\|:|d\w\p{Unified_Ideograph}_]*))?$/u;
1694
1883
 
@@ -1754,7 +1943,7 @@ function entityReplacer(a) {
1754
1943
  * 解析模板内容
1755
1944
  * @param {string} source 输入源字符串
1756
1945
  * @param {Layout.Options} [options] 解析选项
1757
- * @returns {(Layout.Node | string)[]}
1946
+ * @returns {Layout.Child[]}
1758
1947
  */
1759
1948
  function parse(source, {
1760
1949
  createCalc = () => { throw new ParseError('CALC'); },
@@ -1763,15 +1952,15 @@ function parse(source, {
1763
1952
  simpleTag = new Set,
1764
1953
  enableHTML = false,
1765
1954
  } = {}) {
1766
- /** @type {(Layout.Node | string)[]} */
1955
+ /** @type {(OldNode | string)[]} */
1767
1956
  const children = [];
1768
1957
 
1769
1958
  const doc = { children };
1770
- /** @type {(Layout.Node | null)[]} */
1959
+ /** @type {(OldNode | null)[]} */
1771
1960
  const stack = [];
1772
- /** @type {Layout.Node?} */
1961
+ /** @type {OldNode?} */
1773
1962
  let currentNode = null;
1774
- /** @type {typeof doc | Layout.Node} */
1963
+ /** @type {typeof doc | OldNode} */
1775
1964
  let current = doc;
1776
1965
  function endElement() {
1777
1966
  currentNode = stack.pop() || null;
@@ -1947,229 +2136,156 @@ function parse(source, {
1947
2136
  endElement();
1948
2137
  }
1949
2138
  }
1950
- return children;
2139
+ return convert(children);
1951
2140
  }
1952
2141
 
1953
- /** @import * as Layout from './index.mjs' */
1954
-
1955
2142
  /**
1956
- *
1957
- * @param {*} c
1958
- * @returns
2143
+ * @typedef {object} Enhancement 增强信息
2144
+ * @property {Record<string, Node.Name | Node.Event>} events 事件
2145
+ * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2146
+ * @property {Node.Name | Node.Calc | Node.Value} [value] 主值
2147
+ * @property {boolean | string} [bind] 绑定信息
1959
2148
  */
1960
- function _xmlEncoder(c) {
1961
- return c == '<' && '&lt;' ||
1962
- c == '>' && '&gt;' ||
1963
- c == '&' && '&amp;' ||
1964
- c == '"' && '&quot;' ||
1965
- '&#' + c.charCodeAt() + ';';
1966
- }
1967
2149
 
1968
2150
  /**
1969
- * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Event | Layout.Node.Value} def
2151
+ * @typedef {object} Options 解析选项
2152
+ * @property {boolean} [enableHTML] 启用 `!html` 指令
2153
+ * @property {(t: string) => Calc} [createCalc] 创建计算属性的工厂函数
2154
+ * @property {(t: string) => Calc} [createInit] 创建变量初始化的工厂函数
2155
+ * @property {(t: string) => EventListener} [createEvent] 创建事件监听器的工厂函数
2156
+ * @property {Set<string>} [simpleTag] 简单标签的集合
2157
+ */
2158
+ /**
2159
+ * @template [T=any]
2160
+ * @typedef {{value: T; name?: undefined; calc?: undefined; event?: undefined}} Node.Value 基础值
1970
2161
  */
1971
- function toValue({name, calc, event, value}) {
1972
- if (value === true || value === undefined) { return ''; }
1973
- const val = typeof value === 'string' ? JSON.stringify(value) : name || calc || event || value;
1974
- return `="${toAttrValue$1(val)}"`;
1975
- }
1976
2162
  /**
1977
- * @param {string | Function | null} [value]
2163
+ * @typedef {{name: string; value?: undefined; calc?: undefined; event?: undefined}} Node.Name 名称
2164
+ */
2165
+ /**
2166
+ * @typedef {{event: EventListener; name?: undefined; calc?: undefined; value?: undefined}} Node.Event 事件
1978
2167
  */
1979
- function toAttrValue$1(value) {
1980
- return String(value).replace(/[<&"]/g, _xmlEncoder);
1981
- }
1982
-
1983
2168
  /**
1984
- * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Event | Layout.Node.Value>} values
1985
- * @param {string} [prefix]
1986
- * @param {boolean | null} [isName]
2169
+ * @typedef {{calc: Calc; name?: undefined; value?: undefined; event?: undefined}} Node.Calc 计算
1987
2170
  */
1988
- function *values(values, prefix, isName = false) {
1989
- if (prefix) {
1990
- for (const [key, {name, calc, event, value}] of Object.entries(values)) {
1991
- yield ` ${prefix}${key}`;
1992
- if (isName && name === key) { continue; }
1993
- const val = value && typeof value === 'string' ? JSON.stringify(value) : name || calc || event || value;
1994
- if (val == null) { continue; }
1995
- if (isName === false && val === true) { continue; }
1996
- yield `="${toAttrValue$1(val)}"`;
1997
- }
1998
- return;
1999
- }
2000
- for (const [key, attr] of Object.entries(values)) {
2001
- if (!attr) { continue; }
2002
- const {name, value, calc} = attr;
2003
- if (name === key) { yield ` :${key}`; continue; }
2004
- if (name || calc || typeof value !== 'string') {
2005
- yield ` :${key}="${toAttrValue$1(name || calc || value)}"`;
2006
- continue;
2007
- }
2008
- yield ` ${key}`;
2009
- if (value) {
2010
- yield `="${toAttrValue$1(value)}"`;
2011
- }
2012
- }
2013
- }
2014
-
2015
2171
 
2016
2172
  /**
2017
2173
  *
2018
- * @param {Layout.Node} node
2019
- * @param {number} [level]
2020
- * @returns {Iterable<string>}
2174
+ * @typedef {Divergent | Select | Enum | Content | CallTemplate | Fragment | Node | string} Child 分歧
2021
2175
  */
2022
- function* nodeToString(node, level = 0) {
2023
- const { children, is, name } = node;
2024
- const pad = level > 0 ? ''.padEnd(level, '\t') : '';
2025
-
2026
- yield pad;
2027
- yield* ['<', name || '-'];
2028
- if (is) { yield* ['|', is]; }
2029
- if (node.template) {
2030
- yield ` !template="${toAttrValue$1(node.template)}"`;
2031
- yield* values(node.params, '?', null);
2032
- }
2033
- if (node.fragment) { yield node.fragment === true ? ` !fragment` : ` !fragment="${toAttrValue$1(node.fragment)}"`; }
2034
- if (node.else) { yield ` !else`; }
2035
- if (node.if) { yield ` !if${toValue(node.if)}`; }
2036
- if (node.value) { yield ` !value="${toAttrValue$1(node.value)}"`; }
2037
- if (node.enum) { yield ` !enum${toValue(node.enum)}`; }
2038
- yield* values(node.aliases, '*', null);
2039
- yield* values(node.vars, '+', null);
2040
- if (node.bind) { yield node.bind === true ? ` !bind` : ` !bind="${toAttrValue$1(node.bind)}"`; }
2041
- yield* values(node.attrs);
2042
- yield* values(node.events, '@', true);
2043
- yield* values(node.classes, '.', true);
2044
- yield* values(node.styles, 'style:');
2045
- for (const [k, en] of Object.entries(node.enhancements)) {
2046
- if (en.bind) { yield en.bind === true ? ` ~${k}!bind` : ` ~${k}!bind="${toAttrValue$1(en.bind)}"`; }
2047
- if (en.value) { yield ` ~${k}${toValue(en.value)}`; }
2048
- yield* values(en.attrs, `~${k}:`, true);
2049
- yield* values(en.events, `~${k}@`, true);
2050
- }
2051
- if (node.text) { yield ` !text${toValue(node.text)}`; }
2052
- if (node.html) { yield ` !html${toValue(node.html)}`; }
2053
- if (node.comment) { yield ` !comment="${toAttrValue$1(node.comment)}"`; }
2054
- if (!children.length) {
2055
- yield '/>';
2056
- if (level >= 0) { yield '\n'; }
2057
- return;
2058
- }
2059
- if (children.length === 1) {
2060
- const [child] = children;
2061
- if (typeof child === 'string' && child.length < 80 && !child.includes('\n')) {
2062
- yield '>';
2063
- yield* [child.replace(/[<&\t]/g, _xmlEncoder).replace(/]]>/g, ']]&gt;')];
2064
- yield* ['</', name, '>'];
2065
- if (level >= 0) { yield '\n'; }
2066
- return;
2067
- }
2068
- }
2069
- yield '>';
2070
- if (level >= 0) { yield '\n'; }
2071
- yield* listToString(children, level >= 0 ? level + 1 : -1);
2072
- yield* [pad, '</', name, '>'];
2073
- if (level >= 0) { yield '\n'; }
2074
-
2075
- }
2076
2176
  /**
2077
2177
  *
2078
- * @param {(Layout.Node |string)[]} nodes
2079
- * @param {number} [level]
2080
- * @returns {Iterable<string>}
2178
+ * @template [T=unknown]
2179
+ * @typedef {object} Variable 变量定义
2180
+ * @property {string} variable
2181
+ * @property {string} [name]
2182
+ * @property {Calc} [calc]
2183
+ * @property {T} [value]
2184
+ * @property {boolean} [init] 是否普通变量
2185
+ * @property {string} [comment] 注释
2081
2186
  */
2082
- function* listToString(nodes, level = 0) {
2083
- if (!nodes.length) { return ''; }
2084
-
2085
- const pad = level > 0 ? ''.padEnd(level, '\t') : '';
2086
- for (const child of nodes) {
2087
- if (typeof child === 'string') {
2088
- let text = child.replace(/[<&\t]/g, _xmlEncoder).replace(/]]>/g, ']]&gt;');
2089
- if (pad) {
2090
- text = text.replace(/(?<=^|\n)/g, pad);
2091
- }
2092
- yield text;
2093
- if (level >= 0) { yield '\n'; }
2094
- } else {
2095
- yield* nodeToString(child, level);
2096
- }
2097
- }
2098
- }
2099
2187
  /**
2100
- * 将模板转为字符串
2101
- * @param {Layout.Node | (Layout.Node |string)[]} value 要转换的节点或节点数组
2102
- * @param {boolean} [formable] 是否对代码进行格式化
2103
- * @returns {string}
2188
+ * @typedef {object} Template
2189
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2190
+ * @property {Record<string, Template>} [templates]
2191
+ * @property {Record<string, Node.Name | Node.Calc | Node.Value>} params 模板参数定义
2192
+ * @property {Child[]} children 子元素
2193
+ * @property {string} [comment] 注释
2104
2194
  */
2105
- function stringify(value, formable) {
2106
- const level = formable ? 0 : -1;
2107
- if (Array.isArray(value)) { return [...listToString(value, level)].join(''); }
2108
- return [nodeToString(value, level)].join();
2109
-
2110
- }
2111
-
2112
2195
  /**
2113
- * @typedef {object} Enhancement 增强信息
2114
- * @property {Record<string, Node.Name | Node.Event>} events 事件
2115
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2116
- * @property {Node.Name | Node.Calc | Node.Value} [value] 主值
2117
- * @property {boolean | string} [bind] 绑定信息
2196
+ *
2197
+ * @typedef {object} DivergentChildren 分歧项
2198
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2199
+ * @property {Record<string, Template>} [templates]
2200
+ * @property {Child[]} children 子元素
2201
+ * @property {string} [comment] 注释
2118
2202
  */
2119
-
2120
2203
  /**
2121
- * @typedef {object} Options 解析选项
2122
- * @property {boolean} [enableHTML] 启用 `!html` 指令
2123
- * @property {(t: string) => Calc} [createCalc] 创建计算属性的工厂函数
2124
- * @property {(t: string) => Calc} [createInit] 创建变量初始化的工厂函数
2125
- * @property {(t: string) => EventListener} [createEvent] 创建事件监听器的工厂函数
2126
- * @property {Set<string>} [simpleTag] 简单标签的集合
2204
+ *
2205
+ * @typedef {object} Divergent 分歧
2206
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2207
+ * @property {Record<string, Template>} [templates]
2208
+ * @property {'divergent'} type
2209
+ * @property {[children: DivergentChildren, condition?: Node.Name | Node.Calc | Node.Value | null][]} children
2210
+ * @property {string} [comment] 注释
2127
2211
  */
2128
2212
  /**
2129
- * @template [T=any]
2130
- * @typedef {{value: T; name?: undefined; calc?: undefined; event?: undefined}} Node.Value 基础值
2213
+ *
2214
+ * @typedef {object} Select 选值
2215
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2216
+ * @property {Record<string, Template>} [templates]
2217
+ * @property {'value'} type
2218
+ * @property {string} name
2219
+ * @property {Child[]} children 子元素
2220
+ * @property {string} [comment] 注释
2131
2221
  */
2132
2222
  /**
2133
- * @typedef {{name: string; value?: undefined; calc?: undefined; event?: undefined}} Node.Name 名称
2223
+ *
2224
+ * @typedef {object} Enum 枚举
2225
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2226
+ * @property {Record<string, Template>} [templates]
2227
+ * @property {'enum'} type
2228
+ * @property {Node.Name | Node.Calc | Node.Value} value
2229
+ * @property {Child[]} children 子元素
2230
+ * @property {string} [comment] 注释
2134
2231
  */
2135
2232
  /**
2136
- * @typedef {{event: EventListener; name?: undefined; calc?: undefined; value?: undefined}} Node.Event 事件
2233
+ *
2234
+ * @typedef {object} Content 内容填充
2235
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2236
+ * @property {Record<string, Template>} [templates]
2237
+ * @property {'content'} type
2238
+ * @property {Node.Name | Node.Calc | Node.Value} value
2239
+ * @property {boolean} [html]
2240
+ * @property {string} [comment] 注释
2137
2241
  */
2138
2242
  /**
2139
- * @typedef {{calc: Calc; name?: undefined; value?: undefined; event?: undefined}} Node.Calc 计算
2243
+ *
2244
+ * @typedef {object} CallTemplate 模板调用
2245
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2246
+ * @property {Record<string, Template>} [templates]
2247
+ * @property {'template'} type
2248
+ * @property {string} template 模板名
2249
+ * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2250
+ * @property {boolean | string} [bind] 绑定内容
2251
+ * @property {Child[]} children 子元素
2252
+ * @property {string} [comment] 注释
2253
+ */
2254
+
2255
+ /**
2256
+ * @typedef {object} Fragment 片段
2257
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2258
+ * @property {Record<string, Template>} [templates]
2259
+ * @property {'fragment'} type
2260
+ * @property {Child[]} children 子元素
2261
+ * @property {string} [comment] 注释
2140
2262
  */
2263
+
2264
+
2141
2265
  /**
2142
2266
  * @typedef {object} Node 布局节点
2267
+ * @property {Variable[]?} [vars] 局部变量/别名/计算名
2268
+ * @property {Record<string, Template>} [templates]
2269
+ * @property {null} [type]
2270
+ *
2143
2271
  * @property {string} name 标签名
2144
2272
  * @property {string?} [is]
2145
2273
  * @property {string} [id]
2146
2274
  * @property {Record<string, Node.Name | Node.Calc | Node.Value>} attrs 属性
2147
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} params 模板参数定义
2148
2275
  * @property {Record<string, Node.Name | Node.Calc | Node.Value>} classes 类名
2149
2276
  * @property {Record<string, Node.Name | Node.Calc | Node.Value>} styles 样式
2150
2277
  * @property {Record<string, Node.Name | Node.Event>} events 事件
2151
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} vars 局部变量
2152
- * @property {Record<string, Node.Name | Node.Calc | Node.Value>} aliases 别名/计算名
2153
2278
  * @property {Record<string, Enhancement>} enhancements 增强
2154
2279
  *
2155
- * @property {string} [template] 模板定义的名称
2156
- * @property {boolean | string} [fragment] 是否为片段或模板调用
2157
- *
2158
- * @property {Node.Name | Node.Calc | Node.Value} [if] 分歧条件
2159
- * @property {boolean} [else] 否定
2160
- *
2161
- * @property {string} [value] 值关联
2162
- * @property {Node.Name | Node.Calc | Node.Value} [enum] 列表属性枚举
2163
2280
  *
2164
2281
  * @property {boolean | string} [bind] 绑定内容
2165
- * @property {Node.Name | Node.Value | Node.Calc} [text] 文本渲染
2166
- * @property {Node.Name | Node.Value | Node.Calc} [html] HTML 渲染
2167
2282
  *
2168
2283
  * @property {string} [comment] 注释
2169
2284
  *
2170
- * @property {(Node | string)[]} children 子元素
2285
+ * @property {Child[]} children 子元素
2171
2286
  */
2172
2287
 
2288
+
2173
2289
  /**
2174
2290
  * @callback Calc 计算函数
2175
2291
  * @param {Record<string, any>} env 上下文环境
@@ -2184,10 +2300,9 @@ function stringify(value, formable) {
2184
2300
  * @returns {void}
2185
2301
  */
2186
2302
 
2187
- var index$1 = /*#__PURE__*/Object.freeze({
2303
+ var index = /*#__PURE__*/Object.freeze({
2188
2304
  __proto__: null,
2189
- parse: parse,
2190
- stringify: stringify
2305
+ parse: parse
2191
2306
  });
2192
2307
 
2193
2308
  /**
@@ -2283,7 +2398,7 @@ const bindable = {
2283
2398
  null: true,
2284
2399
  index: true,
2285
2400
  no: true,
2286
- length: true,
2401
+ size: true,
2287
2402
 
2288
2403
  error: true,
2289
2404
  errors: true,
@@ -2342,7 +2457,7 @@ function *toParentItem(parent, val, key = '', sign = '$') {
2342
2457
  yield [`${key}${sign}downMovable`, {get: () => {
2343
2458
  const s = val.index;
2344
2459
  if (typeof s !== 'number') { return false; }
2345
- if (s >= parent.length - 1) { return false; }
2460
+ if (s >= parent.size - 1) { return false; }
2346
2461
  return true;
2347
2462
  }}];
2348
2463
  yield [`${key}${sign}remove`, {exec: () => parent.remove(Number(val.index))}];
@@ -2355,7 +2470,7 @@ function *toParentItem(parent, val, key = '', sign = '$') {
2355
2470
  yield [`${key}${sign}downMove`, {exec: () => {
2356
2471
  const s = val.index;
2357
2472
  if (typeof s !== 'number') { return; }
2358
- if (s >= parent.length - 1) { return; }
2473
+ if (s >= parent.size - 1) { return; }
2359
2474
  parent.move(s, s + 1);
2360
2475
  }}];
2361
2476
  }
@@ -2507,10 +2622,9 @@ class Environment {
2507
2622
  watch(value, cb) { return watch(() => this.exec(value), cb, true); }
2508
2623
 
2509
2624
  /**
2510
- * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null} [en]
2625
+ * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} en
2511
2626
  */
2512
2627
  enum(en) {
2513
- if (!en) { return true; }
2514
2628
  const {name, calc} = en;
2515
2629
  if (typeof calc === 'function') { return () => calc(this.getters); }
2516
2630
  if (typeof name === 'string') {
@@ -2696,17 +2810,16 @@ class Environment {
2696
2810
  const store = this.store;
2697
2811
  const parent = this.#parent;
2698
2812
  const object = this.#object;
2813
+ for (const [key, item] of toItem(store)) {
2814
+ ais[key] = item;
2815
+ }
2816
+ for (const [key, item] of toParentItem(parent, store)) {
2817
+ ais[key] = item;
2818
+ }
2699
2819
  if (object) {
2700
2820
  for (const k of Object.keys(object)) {
2701
2821
  ais[`$${k}`] = {get: () => object[k]};
2702
2822
  }
2703
- } else {
2704
- for (const [key, item] of toItem(store)) {
2705
- ais[key] = item;
2706
- }
2707
- for (const [key, item] of toParentItem(parent, store)) {
2708
- ais[key] = item;
2709
- }
2710
2823
  }
2711
2824
  this.#allItems = ais;
2712
2825
  return ais;
@@ -2715,11 +2828,13 @@ class Environment {
2715
2828
  *
2716
2829
  * @param {Store} store
2717
2830
  * @param {Store} parent
2831
+ * @param {Record<string, any>} [object]
2718
2832
  */
2719
- setStore(store, parent) {
2833
+ setStore(store, parent, object) {
2720
2834
  const cloned = new Environment(store, this);
2721
2835
  if (parent) { cloned.#parent = parent; }
2722
2836
  setStore(cloned.#schemaItems, store);
2837
+ if (object) { cloned.#object = object; }
2723
2838
  return cloned;
2724
2839
  }
2725
2840
  /**
@@ -2736,12 +2851,12 @@ class Environment {
2736
2851
  }
2737
2852
  /**
2738
2853
  *
2739
- * @param {Layout.Node} template
2740
- * @param {Layout.Node} source
2854
+ * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value<any>>} params
2855
+ * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value<any>>} attrs
2741
2856
  * @param {Environment} sourceEnv
2742
2857
  * @param {string | null | boolean} [bind]
2743
2858
  */
2744
- params({params}, {attrs}, sourceEnv, bind) {
2859
+ params(params, attrs, sourceEnv, bind) {
2745
2860
  /** @type {Store} */
2746
2861
  let store = this.store;
2747
2862
  if (bind === true) {
@@ -2820,22 +2935,39 @@ class Environment {
2820
2935
  }
2821
2936
  /**
2822
2937
  *
2823
- * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} aliases
2824
- * @param {Record<string, Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value>} vars
2938
+ * @param {Layout.Variable[]?} [vars]
2939
+ * @returns
2825
2940
  */
2826
- set(aliases, vars) {
2827
- if (Object.keys(aliases).length + Object.keys(vars).length === 0) { return this; }
2941
+ set(vars) {
2942
+ if (!vars?.length) { return this; }
2828
2943
  const cloned = new Environment(this.store, this);
2829
2944
  cloned.#parent = this.#parent;
2830
2945
  cloned.#object = this.#object;
2831
2946
  const explicit = cloned.#explicit;
2832
2947
  const items = cloned.#items;
2833
- for (const [key, {name, calc, value}] of Object.entries(aliases)) {
2948
+ for (const { variable, name, calc, value, init } of vars) {
2949
+ if (init) {
2950
+ const val = new Signal.State(/** @type {any} */(value));
2951
+ if (typeof calc === 'function') {
2952
+ const settable = cloned.settable;
2953
+ cloned.#settable = null;
2954
+ val.set(calc(settable));
2955
+ } else if (name) {
2956
+ const item = items[name];
2957
+ if (!item?.get) { continue; }
2958
+ val.set(item.get());
2959
+ }
2960
+ explicit[variable] = items[variable] = {
2961
+ get: () => { return val.get(); },
2962
+ set: (v) => { val.set(v); },
2963
+ };
2964
+ continue;
2965
+ }
2834
2966
  if (typeof calc === 'function') {
2835
2967
  const getters = cloned.getters;
2836
2968
  cloned.#getters = null;
2837
2969
  const val = new Signal.Computed(() => calc(getters));
2838
- explicit[key] = items[key] = {
2970
+ explicit[variable] = items[variable] = {
2839
2971
  get: () => { return val.get(); },
2840
2972
  };
2841
2973
  continue;
@@ -2844,34 +2976,17 @@ class Environment {
2844
2976
  const item = items[name];
2845
2977
  if (!item) { continue; }
2846
2978
  if (!item.get || !item.store) {
2847
- explicit[key] = items[key] = item;
2979
+ explicit[variable] = items[variable] = item;
2848
2980
  continue;
2849
2981
  }
2850
- for (const [k, it] of toItem(item.store, key)) {
2982
+ for (const [k, it] of toItem(item.store, variable)) {
2851
2983
  explicit[k] = items[k] = it;
2852
2984
  }
2853
2985
  continue;
2854
2986
  }
2855
- explicit[key] = items[key] = { get: () => { return value; } };
2987
+ explicit[variable] = items[variable] = { get: () => { return value; } };
2856
2988
  continue;
2857
2989
  }
2858
- for (const [k,{name, calc, value}] of Object.entries(vars)) {
2859
-
2860
- const val = new Signal.State(/** @type {any} */(value));
2861
- if (typeof calc === 'function') {
2862
- const settable = cloned.settable;
2863
- cloned.#settable = null;
2864
- val.set(calc(settable));
2865
- } else if (name) {
2866
- const item = items[name];
2867
- if (!item?.get) { continue }
2868
- val.set(item.get());
2869
- }
2870
- explicit[k] = items[k] = {
2871
- get: () => { return val.get(); },
2872
- set: (v) => { val.set(v); },
2873
- };
2874
- }
2875
2990
  return cloned;
2876
2991
  }
2877
2992
 
@@ -3854,17 +3969,24 @@ function renderArray(parent, next, store, env, renderItem) {
3854
3969
  }
3855
3970
 
3856
3971
  }
3972
+ const count = new Signal.State(0);
3857
3973
  const childrenResult = watch(() => store.children, function render(children) {
3858
3974
  if (!start.parentNode) { return; }
3859
3975
  let nextNode = start.nextSibling;
3860
3976
  const oldSeMap = seMap;
3861
3977
  seMap = new Map();
3978
+ count.set(children.length);
3862
3979
  for (let child of children) {
3863
3980
  const old = oldSeMap.get(child);
3864
3981
  if (!old) {
3865
3982
  const ItemStart = parent.insertBefore(document.createComment(''), nextNode);
3866
3983
  const itemEnd = parent.insertBefore(document.createComment(''), nextNode);
3867
- const d = renderItem(itemEnd, env.setStore(child, store));
3984
+ const d = renderItem(itemEnd, env.setStore(child, store, {
3985
+ get count() { return count.get() },
3986
+ get key() { return child.index; },
3987
+ get index() { return child.index; },
3988
+ get item() { return child.value; },
3989
+ }));
3868
3990
  seMap.set(child, [ItemStart, itemEnd, d]);
3869
3991
  continue;
3870
3992
  }
@@ -3911,18 +4033,18 @@ function toText(val) {
3911
4033
  * @param {Element} parent
3912
4034
  * @param {Node?} next
3913
4035
  * @param {Environment} envs
3914
- * @param {Layout.Node} layout
4036
+ * @param {Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value} value
4037
+ * @param {boolean} [isHtml]
3915
4038
  */
3916
- function renderFillDirectives(parent, next, envs, { text, html }) {
3917
- if (text != null) {
4039
+ function renderFillDirectives(parent, next, envs, value, isHtml) {
4040
+ if (!isHtml) {
3918
4041
  const node = parent.insertBefore(document.createTextNode(''), next);
3919
- const stop = envs.watch(text, val => node.textContent = toText(val));
4042
+ const stop = envs.watch(value, val => node.textContent = toText(val));
3920
4043
  return () => {
3921
4044
  node.remove();
3922
4045
  stop();
3923
4046
  };
3924
4047
  }
3925
- if (html == null) { return; }
3926
4048
  const start = parent.insertBefore(document.createComment(''), next);
3927
4049
  const end = parent.insertBefore(document.createComment(''), next);
3928
4050
  const div = document.createElement('div');
@@ -3938,7 +4060,7 @@ function renderFillDirectives(parent, next, envs, { text, html }) {
3938
4060
  node.remove();
3939
4061
  }
3940
4062
  }
3941
- const result = envs.watch(html, val => {
4063
+ const result = envs.watch(value, val => {
3942
4064
  remove();
3943
4065
  add(toText(val));
3944
4066
  });
@@ -3950,113 +4072,7 @@ function renderFillDirectives(parent, next, envs, { text, html }) {
3950
4072
  };
3951
4073
  }
3952
4074
 
3953
- /** @import * as Layout from '../Layout/index.mjs' */
3954
-
3955
- /**
3956
- * @param {(Layout.Node | string)[]} layouts
3957
- * @param {Element} parent
3958
- * @param {Node?} next
3959
- * @param {Environment} envs
3960
- * @param {Record<string, [Layout.Node, Environment]>} templates
3961
- * @param {(layout: Layout.Node, templates: Record<string, any>) => () => void} renderItem
3962
- * @returns {() => void}
3963
- */
3964
- function renderList(layouts, parent, next, envs, templates, renderItem) {
3965
-
3966
- /** @type {Set<() => void>?} */
3967
- let bkList = new Set();
3968
- /** @type {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, Layout.Node][]} */
3969
- let ifList = [];
3970
- /** @type {Record<string, [Layout.Node, Environment]>} */
3971
- let currentTemplates = Object.create(templates);
3972
- for (const layout of layouts) {
3973
- if (typeof layout === 'string') { continue; }
3974
- const name = layout.template;
3975
- if (!name) { continue; }
3976
- currentTemplates[name] = [layout, envs];
3977
- }
3978
-
3979
- /** @param {[Layout.Node.Name | Layout.Node.Calc | Layout.Node.Value | null, Layout.Node][]} list */
3980
- function renderIf(list) {
3981
- if (!list.length || !bkList) { return; }
3982
- const end = parent.insertBefore(document.createComment(''), next);
3983
-
3984
-
3985
- let lastIndex = -1;
3986
- let destroy = () => { };
3987
- /**
3988
- *
3989
- * @param {number} index
3990
- * @returns
3991
- */
3992
- function renderIndex(index) {
3993
- const layout = list[index]?.[1];
3994
- if (!layout) { return; }
3995
- destroy = renderItem(layout, currentTemplates);
3996
- }
3997
- bkList.add(() => {
3998
- destroy();
3999
- destroy = () => { };
4000
- end.remove();
4001
- });
4002
- bkList.add(watch(
4003
- () => list.findIndex(([ifv]) => !ifv || envs.exec(ifv)),
4004
- index => {
4005
- if (index === lastIndex) { return; }
4006
- lastIndex = index;
4007
- destroy();
4008
- destroy = () => { };
4009
- renderIndex(lastIndex);
4010
- }, true,
4011
- ));
4012
- }
4013
- for (const layout of layouts) {
4014
- if (typeof layout === 'string') {
4015
- renderIf(ifList);
4016
- ifList = [];
4017
- const node = document.createTextNode(layout);
4018
- parent.insertBefore(node, next);
4019
- bkList.add(() => node.remove());
4020
- continue;
4021
- }
4022
- if (layout.template) {
4023
- renderIf(ifList);
4024
- ifList = [];
4025
- continue;
4026
- }
4027
- if (ifList.length && layout.else) {
4028
- const ifv = layout.if || null;
4029
- ifList.push([ifv, layout]);
4030
- if (!ifv) {
4031
- renderIf(ifList);
4032
- ifList = [];
4033
- }
4034
- continue;
4035
- }
4036
- renderIf(ifList);
4037
- ifList = [];
4038
- const ifv = layout.if;
4039
- if (ifv) {
4040
- ifList.push([ifv, layout]);
4041
- continue;
4042
- }
4043
- bkList.add(
4044
- renderItem(layout, currentTemplates)
4045
- );
4046
- }
4047
- renderIf(ifList);
4048
-
4049
- return () => {
4050
- if (!bkList) { return; }
4051
- const list = bkList;
4052
- bkList = null;
4053
- for (const s of list) {
4054
- s();
4055
- }
4056
- };
4057
- }
4058
-
4059
- /** @import { ObjectStore } from '../Store/index.mjs' */
4075
+ /** @import { ObjectStore, Store } from '../Store/index.mjs' */
4060
4076
 
4061
4077
  /**
4062
4078
  *
@@ -4069,8 +4085,16 @@ function renderList(layouts, parent, next, envs, templates, renderItem) {
4069
4085
  function renderObject(parent, next, store, env, renderItem) {
4070
4086
  /** @type {(() => void)[]} */
4071
4087
  const children = [];
4072
- for (const [k, child] of [...store]) {
4073
- children.push(renderItem(next, env.setStore(child, store)));
4088
+ /** @type {[string, Store<any, any>, number][]} */
4089
+ const childStores = [...store].map(([k,v], i) => [k,v,i]);
4090
+ const count = childStores.length;
4091
+ for (const [key, child, index] of childStores) {
4092
+ children.push(renderItem(next, env.setStore(child, store, {
4093
+ get count() { return count; },
4094
+ get key() { return key; },
4095
+ get index() { return index; },
4096
+ get item() { return child.value; },
4097
+ })));
4074
4098
  }
4075
4099
 
4076
4100
  return () => {
@@ -4116,11 +4140,13 @@ function renderEnum(parent, next, getter, env, renderItem) {
4116
4140
  e.remove();
4117
4141
  }
4118
4142
  }
4143
+ const count = new Signal.State(0);
4119
4144
  const childrenResult = watch(() => list.get(), function render(children) {
4120
4145
  if (!start.parentNode) { return; }
4121
4146
  let nextNode = start.nextSibling;
4122
4147
  const oldSeMap = seMap;
4123
4148
  seMap = [];
4149
+ count.set(children.length);
4124
4150
  for (const [value, index, key] of children) {
4125
4151
  const index2 = oldSeMap.findIndex((v) => v[3] === key);
4126
4152
  const [old] = index2 >= 0 ? oldSeMap.splice(index2, 1) : [];
@@ -4130,8 +4156,9 @@ function renderEnum(parent, next, getter, env, renderItem) {
4130
4156
  const valueState = new Signal.State(value);
4131
4157
  const indexState = new Signal.State(index);
4132
4158
  const d = renderItem(itemEnd, env.setObject({
4159
+ get count() { return count.get() },
4133
4160
  get key() { return key; },
4134
- get value() { return valueState.get(); },
4161
+ get item() { return valueState.get(); },
4135
4162
  get index() { return indexState.get(); },
4136
4163
  }));
4137
4164
  seMap.push([ItemStart, itemEnd, d, key, valueState, indexState]);
@@ -4320,6 +4347,50 @@ function bindEnhancements(tag, enhancementDefine, env, enhancements, root, slot)
4320
4347
  };
4321
4348
  }
4322
4349
 
4350
+ /** @import * as Layout from '../Layout/index.mjs' */
4351
+
4352
+ /**
4353
+ * @param {Layout.Divergent} layout
4354
+ * @param {Element} parent
4355
+ * @param {Node?} next
4356
+ * @param {Environment} env
4357
+ * @param {(layout: Layout.Child[], vars?: Layout.Variable[]?, templates?: Record<string, any>) => () => void} renderItem
4358
+ * @returns {() => void}
4359
+ */
4360
+ function divergent(layout, parent, next, env, renderItem) {
4361
+ const children = layout.children;
4362
+ if (!children.length) { return () => { }; }
4363
+ const end = parent.insertBefore(document.createComment(''), next);
4364
+ /** @type {typeof children[0]?} */
4365
+ let last = null;
4366
+ let destroy = () => { };
4367
+ const stop = () => {
4368
+ destroy();
4369
+ destroy = () => { };
4370
+ end.remove();
4371
+ };
4372
+ const unwatch = watch(
4373
+ () => children.find(([, ifv]) => !ifv || env.exec(ifv)) || null,
4374
+ item => {
4375
+ if (item === last) { return; }
4376
+ last = item;
4377
+ destroy();
4378
+ destroy = () => { };
4379
+ const layout = item?.[0];
4380
+ if (!layout) { return; }
4381
+ destroy = renderItem(layout.children, layout.vars, layout.templates);
4382
+ }, true,
4383
+ );
4384
+
4385
+ let destroyed = false;
4386
+ return () => {
4387
+ if (destroyed) { return; }
4388
+ destroyed = true;
4389
+ unwatch();
4390
+ stop();
4391
+ };
4392
+ }
4393
+
4323
4394
  /** @import Store from '../Store/index.mjs' */
4324
4395
 
4325
4396
  /**
@@ -4327,37 +4398,19 @@ function bindEnhancements(tag, enhancementDefine, env, enhancements, root, slot)
4327
4398
  * @param {Element} parent
4328
4399
  * @param {Node?} next
4329
4400
  * @param {Environment} env
4330
- * @param {Record<string, [Layout.Node, Environment]>} templates
4401
+ * @param {Record<string, [Layout.Template, Environment]>} templates
4331
4402
  * @param {string[]} componentPath
4332
4403
  * @param {Record<string, Enhancement>} enhancements
4333
4404
  * @param {((store: Store, el: Element | Relatedness) => () => void)?} [relate]
4334
4405
  * @param {Component.Getter?} [getComponent]
4335
4406
  */
4336
- function renderItem(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
4337
- env = env.set(layout.aliases, layout.vars);
4407
+ function renderNode(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
4338
4408
  const bind = layout.bind;
4339
- const fragment = layout.fragment;
4340
- if (fragment && typeof fragment === 'string') {
4341
- const template = templates[fragment];
4342
- if (!template) { return () => {}; }
4343
- const [templateLayout, templateEnv] = template;
4344
- const newEnv = templateEnv.params(templateLayout, layout, env, bind);
4345
- return render(templateLayout, parent, next, newEnv, templates, componentPath, enhancements, relate, getComponent);
4346
- }
4347
- if (!layout.name || layout.fragment) {
4348
- return renderFillDirectives(parent, next, env, layout) ||
4349
- renderList(layout.children || [], parent, next, env, templates, (layout, templates) => {
4350
- return render(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
4351
- });
4352
- }
4353
4409
  const path = [...componentPath, layout.name];
4354
4410
  const component = getComponent?.(path);
4355
4411
  if (getComponent && !component) { return () => { }; }
4356
4412
  const { context, handler } = createContext(
4357
- component ? component : layout.name,
4358
- env,
4359
- env.getStore(bind),
4360
- relate
4413
+ component ? component : layout.name, env, env.getStore(bind), relate
4361
4414
  );
4362
4415
 
4363
4416
  const componentAttrs = component?.attrs;
@@ -4381,11 +4434,9 @@ function renderItem(layout, parent, next, env, templates, componentPath, enhance
4381
4434
  const root = Array.isArray(r) ? r[0] : r;
4382
4435
  const slot = Array.isArray(r) ? r[1] : root;
4383
4436
  parent.insertBefore(root, next);
4384
- const children = slot ?
4385
- renderFillDirectives(slot, null, env, layout)
4386
- || renderList(layout.children || [], slot, null, env, templates, (layout, templates) => {
4387
- return render(layout, slot, null, env, templates, componentPath, enhancements, relate, getComponent);
4388
- }) : () => {};
4437
+ const children = slot ?
4438
+ renderChildren(layout.children || [], slot, null, env, templates, componentPath, enhancements, relate, getComponent)
4439
+ : () => { };
4389
4440
 
4390
4441
 
4391
4442
  const classes = bindClasses(root, env, layout.classes, layout.attrs.class);
@@ -4393,7 +4444,7 @@ function renderItem(layout, parent, next, env, templates, componentPath, enhance
4393
4444
 
4394
4445
  handler.mount();
4395
4446
  const enhancement = bindEnhancements(handler.tag, layout.enhancements, env, enhancements, root, slot);
4396
-
4447
+
4397
4448
  return () => {
4398
4449
  enhancement();
4399
4450
  handler.destroy();
@@ -4405,43 +4456,117 @@ function renderItem(layout, parent, next, env, templates, componentPath, enhance
4405
4456
  styles();
4406
4457
  };
4407
4458
  }
4459
+
4408
4460
  /**
4409
4461
  *
4410
- * @param {Layout.Node} layout
4462
+ * @param {Environment} env
4463
+ * @param {Record<string, [Layout.Template, Environment]>} parentTemplates
4464
+ * @param {Record<string, Layout.Template>} [newTemplates]
4465
+ * @returns {Record<string, [Layout.Template, Environment]>}
4466
+ */
4467
+ function createTemplates(env, parentTemplates, newTemplates) {
4468
+ if (!newTemplates) { return parentTemplates; } const layOutTemplates = Object.entries(newTemplates);
4469
+ if (!layOutTemplates.length) { return parentTemplates; }
4470
+ /** @type {Record<string, [Layout.Template, Environment]>} */
4471
+ const templates = Object.create(parentTemplates);
4472
+ for (const [name, template] of layOutTemplates) {
4473
+ templates[name] = [template, env];
4474
+ }
4475
+ return templates;
4476
+ }
4477
+ /**
4478
+ *
4479
+ * @param {Layout.Child} layout
4411
4480
  * @param {Element} parent
4412
4481
  * @param {Node?} next
4413
- * @param {Environment} env
4414
- * @param {Record<string, [Layout.Node, Environment]>} templates
4482
+ * @param {Environment} parentEnv
4483
+ * @param {Record<string, [Layout.Template, Environment]>} parentTemplates
4415
4484
  * @param {string[]} componentPath
4416
4485
  * @param {Record<string, Enhancement>} enhancements
4417
4486
  * @param {((store: Store, el: Element | Relatedness) => () => void)?} [relate]
4418
4487
  * @param {Component.Getter?} [getComponent]
4419
4488
  * @returns {() => void}
4420
4489
  */
4421
- function render(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
4422
- const newEnv = env.child(layout.value);
4423
- if (!newEnv) { return () => {}; }
4424
- const list = newEnv.enum(layout.enum);
4425
- /** @type {(next: Node | null, env: any) => () => void} */
4426
- const r = (next, env) => renderItem(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
4427
- if (list === true) {
4428
- return r(next, newEnv);
4490
+ function renderChild(layout, parent, next, parentEnv, parentTemplates, componentPath, enhancements, relate, getComponent) {
4491
+ if (typeof layout === 'string') {
4492
+ const node = document.createTextNode(layout);
4493
+ parent.insertBefore(node, next);
4494
+ return () => node.remove();
4429
4495
  }
4430
- if (list instanceof ArrayStore) {
4431
- return renderArray(parent, next, list, newEnv, r);
4496
+ const env = parentEnv.set(layout.vars);
4497
+ const templates = createTemplates(env, parentTemplates, layout.templates);
4498
+ if (layout.type === 'divergent') {
4499
+ return divergent(layout, parent, next, env, (list, vars, templates2) => {
4500
+ const listEnv = env.set(vars);
4501
+ const templates = createTemplates(listEnv, parentTemplates, templates2);
4502
+ return renderChildren(list, parent, next, listEnv, templates, componentPath, enhancements, relate, getComponent);
4503
+ });
4432
4504
  }
4433
- if (list instanceof ObjectStore) {
4434
- return renderObject(parent, next, list, newEnv, r);
4505
+ if (layout.type === 'value') {
4506
+ const newEnv = env.child(layout.name);
4507
+ if (!newEnv) { return () => { }; }
4508
+ return renderChildren(layout.children, parent, next, newEnv, templates, componentPath, enhancements, relate, getComponent);
4435
4509
  }
4436
- if (typeof list === 'function') {
4437
- return renderEnum(parent, next, list, newEnv, r);
4510
+ if (layout.type === 'enum') {
4511
+ const list = env.enum(layout.value);
4512
+ /** @type {(next: Node | null, env: any) => () => void} */
4513
+ const r = (next, env) => renderChildren(layout.children, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
4514
+ if (list instanceof ArrayStore) {
4515
+ return renderArray(parent, next, list, env, r);
4516
+ }
4517
+ if (list instanceof ObjectStore) {
4518
+ return renderObject(parent, next, list, env, r);
4519
+ }
4520
+ if (typeof list === 'function') {
4521
+ return renderEnum(parent, next, list, env, r);
4522
+ }
4523
+ return () => { };
4438
4524
  }
4439
- return () => { };
4525
+ if (layout.type === 'content') {
4526
+ return renderFillDirectives(parent, next, env, layout.value, layout.html);
4527
+ }
4528
+ if (layout.type === 'template') {
4529
+ const template = templates[layout.template];
4530
+ if (!template) { return () => { }; }
4531
+ const [templateLayout, templateEnv] = template;
4532
+ const newEnv = templateEnv.params(templateLayout.params, layout.attrs, env, layout.bind);
4533
+ const subTemplates = createTemplates(newEnv, parentTemplates, templateLayout.templates);
4534
+ return renderChildren(templateLayout.children, parent, next, newEnv, subTemplates, componentPath, enhancements, relate, getComponent);
4535
+ }
4536
+ if (layout.type === 'fragment') {
4537
+ return renderChildren(layout.children, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
4538
+ }
4539
+ return renderNode(layout, parent, next, env, templates, componentPath, enhancements, relate, getComponent);
4540
+ }
4541
+
4542
+ /**
4543
+ *
4544
+ * @param {Layout.Child[]} layouts
4545
+ * @param {Element} parent
4546
+ * @param {Node?} next
4547
+ * @param {Environment} env
4548
+ * @param {Record<string, [Layout.Template, Environment]>} templates
4549
+ * @param {string[]} componentPath
4550
+ * @param {Record<string, Enhancement>} enhancements
4551
+ * @param {((store: Store, el: Element | Relatedness) => () => void)?} [relate]
4552
+ * @param {Component.Getter?} [getComponent]
4553
+ * @returns {() => void}
4554
+ */
4555
+ function renderChildren(layouts, parent, next, env, templates, componentPath, enhancements, relate, getComponent) {
4556
+ const list = layouts.map(v => renderChild(v, parent, next, env, templates, componentPath, enhancements, relate, getComponent));
4557
+ let removed = false;
4558
+ return () => {
4559
+ if (removed) { return; }
4560
+ removed = true;
4561
+ for (const k of list) {
4562
+ k();
4563
+ }
4564
+ };
4440
4565
  }
4441
4566
 
4442
4567
  /**
4443
4568
  * @param {Store} store 存储实例
4444
- * @param {(Layout.Node | string)[]} layouts 布局信息
4569
+ * @param {Layout.Child[]} layouts 布局信息
4445
4570
  * @param {Element} parent 渲染节点
4446
4571
  * @param {object} [options] 选项
4447
4572
  * @param {Record<string, Store | {get?(): any; set?(v: any): void; exec?(...p: any[]): any; calc?(...p: any[]): any }>} [options.global] 全局数据
@@ -4450,14 +4575,12 @@ function render(layout, parent, next, env, templates, componentPath, enhancement
4450
4575
  * @param {Record<string, Enhancement>} [options.enhancements] 增强信息
4451
4576
  * @returns {() => void}
4452
4577
  */
4453
- function index (store, layouts, parent, {component, global, relate, enhancements} = {}) {
4578
+ function render(store, layouts, parent, { component, global, relate, enhancements } = {}) {
4454
4579
  const env = new Environment(store, global);
4455
4580
  const templates = Object.create(null);
4456
4581
  const allEnhancements = enhancements || {};
4457
- const relateFn = typeof relate === 'function' ? relate : null;
4458
- return renderList(layouts, parent, null, env, templates, (layout, templates) => {
4459
- return render(layout, parent, null, env, templates, [], allEnhancements, relateFn, component);
4460
- });
4582
+ const relateFn = typeof relate === 'function' ? relate : null;
4583
+ return renderChildren(layouts, parent, null, env, templates, [], allEnhancements, relateFn, component);
4461
4584
  }
4462
4585
 
4463
- export { index$1 as Layout, Store, effect, index as render, watch };
4586
+ export { index as Layout, Store, effect, render, watch };