@shwfed/config 2.10.0 → 2.10.1

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.
Files changed (97) hide show
  1. package/dist/mcp.mjs +4125 -3183
  2. package/dist/module.json +1 -1
  3. package/dist/preview/assets/{FieldGroup.vue_vue_type_script_setup_true_lang-CCaOWk_7.js → FieldGroup.vue_vue_type_script_setup_true_lang-CuFusz5A.js} +1 -1
  4. package/dist/preview/assets/{badge-D9_7atSJ.js → badge-D5FPHSix.js} +1 -1
  5. package/dist/preview/assets/{config-B2d8SiPi.js → config-BHPiQ1lB.js} +1 -1
  6. package/dist/preview/assets/{config-DYxMKhCU.js → config-BWC-Zw21.js} +1 -1
  7. package/dist/preview/assets/{config-CNKb25Qo.js → config-Bg94Z7XN.js} +1 -1
  8. package/dist/preview/assets/{config-CQrqVV1U.js → config-BqSL4UAL.js} +1 -1
  9. package/dist/preview/assets/{config-Bk2VSNeu.js → config-CeRBpZbE.js} +1 -1
  10. package/dist/preview/assets/{config-C9WPOoA7.js → config-CfjcFb_E.js} +1 -1
  11. package/dist/preview/assets/{config-DZlaJUlF.js → config-DNUKa3lN.js} +1 -1
  12. package/dist/preview/assets/{config-BLEovXei.js → config-DVjZmomc.js} +1 -1
  13. package/dist/preview/assets/{config-DyPl6K2G.js → config-Djv6EQBY.js} +1 -1
  14. package/dist/preview/assets/{config-DWA385pD.js → config-PNpa6ENz.js} +1 -1
  15. package/dist/preview/assets/{config-C8lCItmz.js → config-ucxtM3wX.js} +1 -1
  16. package/dist/preview/assets/{config-Bf5Vckj3.js → config-urZuasV7.js} +1 -1
  17. package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-CFzQ7icU.js → definition.vue_vue_type_script_setup_true_lang-BzryfjdG.js} +1 -1
  18. package/dist/preview/assets/index-BE9O1XgB.js +1 -0
  19. package/dist/preview/assets/{index-CXOEVGFP.js → index-DCRQGu0g.js} +1 -1
  20. package/dist/preview/assets/index-yvElpEK3.js +743 -0
  21. package/dist/preview/assets/{item-DCVX69_o.js → item-CTkdtkvl.js} +1 -1
  22. package/dist/preview/assets/{runtime-r1wbrr4k.js → runtime-3rajYvjp.js} +1 -1
  23. package/dist/preview/assets/{runtime-BNk4EliL.js → runtime-4A3oiig9.js} +1 -1
  24. package/dist/preview/assets/{runtime-Cbc5NH57.js → runtime-BOGZFWxF.js} +1 -1
  25. package/dist/preview/assets/{runtime-BO-KY3T_.js → runtime-BqroTX7H.js} +1 -1
  26. package/dist/preview/assets/{runtime-O6MNC3GA.js → runtime-CBBae0-H.js} +1 -1
  27. package/dist/preview/assets/{runtime-DEWGIyvr.js → runtime-CBuV3vwL.js} +1 -1
  28. package/dist/preview/assets/{runtime-DJ9ElxWB.js → runtime-CI8yzwXd.js} +1 -1
  29. package/dist/preview/assets/{runtime-DSfMvph3.js → runtime-CcyhgOum.js} +1 -1
  30. package/dist/preview/assets/{runtime-BD1A-g1h.js → runtime-CkQ-mNoH.js} +1 -1
  31. package/dist/preview/assets/{runtime-BsNSI1XP.js → runtime-DmxKfudS.js} +1 -1
  32. package/dist/preview/assets/{schema-meta-ovcuERKg.js → schema-meta-BFzIzGiN.js} +1 -1
  33. package/dist/preview/index.html +1 -1
  34. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/runtime.vue +5 -1
  35. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/runtime.vue +16 -3
  36. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/runtime.vue +5 -1
  37. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/runtime.vue +5 -1
  38. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.text-input/runtime.vue +17 -3
  39. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/runtime.vue +5 -1
  40. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/runtime.vue +5 -1
  41. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi/runtime.vue +5 -1
  42. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/runtime.vue +5 -1
  43. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/runtime.vue +5 -1
  44. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi/runtime.vue +5 -1
  45. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/runtime.vue +5 -1
  46. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single/runtime.vue +5 -1
  47. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/runtime.vue +5 -1
  48. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/runtime.vue +5 -1
  49. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/runtime.vue +5 -1
  50. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/runtime.vue +5 -1
  51. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/runtime.vue +5 -1
  52. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/runtime.vue +5 -1
  53. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/runtime.vue +5 -1
  54. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.d.vue.ts +179 -0
  55. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.vue +942 -0
  56. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.vue.d.ts +179 -0
  57. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/runtime.d.vue.ts +9 -0
  58. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/runtime.vue +499 -0
  59. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/runtime.vue.d.ts +9 -0
  60. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/schema.d.ts +223 -0
  61. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/schema.js +276 -0
  62. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.d.vue.ts +179 -0
  63. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.vue +942 -0
  64. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.vue.d.ts +179 -0
  65. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/runtime.d.vue.ts +9 -0
  66. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/runtime.vue +438 -0
  67. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/runtime.vue.d.ts +9 -0
  68. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/schema.d.ts +223 -0
  69. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/schema.js +276 -0
  70. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +123 -0
  71. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue +646 -0
  72. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +123 -0
  73. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/runtime.d.vue.ts +9 -0
  74. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/runtime.vue +500 -0
  75. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/runtime.vue.d.ts +9 -0
  76. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/schema.d.ts +83 -0
  77. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/schema.js +179 -0
  78. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +121 -0
  79. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue +577 -0
  80. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +121 -0
  81. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/runtime.d.vue.ts +9 -0
  82. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/runtime.vue +465 -0
  83. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/runtime.vue.d.ts +9 -0
  84. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/schema.d.ts +80 -0
  85. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/schema.js +171 -0
  86. package/dist/runtime/components/table/columns/2026-06-14/combobox-migrate.d.ts +18 -0
  87. package/dist/runtime/components/table/columns/2026-06-14/combobox-migrate.js +36 -0
  88. package/dist/runtime/components/table/columns/2026-06-14/tree-combobox-shared.d.ts +35 -0
  89. package/dist/runtime/components/table/columns/2026-06-14/tree-combobox-shared.js +31 -0
  90. package/dist/runtime/components/table/index.d.vue.ts +2 -0
  91. package/dist/runtime/components/table/index.vue +24 -0
  92. package/dist/runtime/components/table/index.vue.d.ts +2 -0
  93. package/dist/runtime/components/table/utils/shared.d.ts +1 -0
  94. package/dist/runtime/components/table/utils/shared.js +7 -13
  95. package/package.json +1 -1
  96. package/dist/preview/assets/index-7BE56IYF.js +0 -739
  97. package/dist/preview/assets/index-Bwv0Yz_L.js +0 -1
@@ -0,0 +1,171 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { getProperty } from "dot-prop";
3
+ import { Locale } from "../../../../../share/locale.js";
4
+ import { Expression, HttpRequestResult, LocaleMarkdown } from "../../../../../share/expression.js";
5
+ import {
6
+ CelRowAccess,
7
+ derivedRowField,
8
+ editableColumnFields,
9
+ editableHeader,
10
+ registerRowVariablesIfAbsent
11
+ } from "../../../utils/shared.js";
12
+ import { localeLabelToExpression } from "../combobox-migrate.js";
13
+ export const type = "com.shwfed.table.column.tree-combobox-single";
14
+ export const compatibilityDate = "2026-06-14";
15
+ export const metadata = {
16
+ name: "\u4E0B\u62C9\u6811\uFF08\u5355\u9009\uFF09",
17
+ icon: "fluent:tree-evergreen-20-regular",
18
+ // Publishes its resolved node (backend record) to the per-row `selections` registry (runtime.vue), readable via `selections["<id>"]`.
19
+ selection: true
20
+ };
21
+ const isListLike = (t) => t === "dyn" || t.startsWith("list") || t.startsWith("optional");
22
+ const isKeyType = (t) => t === "string" || t === "number" || t === "dyn";
23
+ function withRowAndNode(configure) {
24
+ return (env) => {
25
+ configure(env);
26
+ registerRowVariablesIfAbsent(env);
27
+ env.registerVariable("node", "dyn", { description: "`\u6570\u636E\u6E90` \u8FD4\u56DE\u7684\u8282\u70B9\uFF1B\u7531\u6811\u9010\u5C42\u5411\u4E0B\u8BFB\u53D6" });
28
+ };
29
+ }
30
+ export function schema(configure) {
31
+ const CelBool = CelRowAccess(configure, { resultType: "bool" });
32
+ const CelNodeKey = Expression({ configure: withRowAndNode(configure), resultType: isKeyType });
33
+ const CelNodeChildren = Expression({ configure: withRowAndNode(configure), resultType: isListLike });
34
+ const CelNodeBool = Expression({ configure: withRowAndNode(configure), resultType: "bool" });
35
+ const CelKeywords = Expression({ configure: withRowAndNode(configure), resultType: isListLike });
36
+ const CelNodeLabel = Expression({ configure: withRowAndNode(configure), resultType: "dyn" });
37
+ const NodeLocaleMd = LocaleMarkdown({ configure: withRowAndNode(configure) });
38
+ const dataSourceConfigure = (env) => {
39
+ configure(env);
40
+ registerRowVariablesIfAbsent(env);
41
+ };
42
+ const CelDataSourceRequest = Expression({
43
+ configure: dataSourceConfigure,
44
+ resultType: HttpRequestResult
45
+ });
46
+ const CelDataSourceData = Expression({
47
+ configure: (env) => {
48
+ dataSourceConfigure(env);
49
+ env.registerVariable("json", "optional<dyn>", {
50
+ description: "HTTP \u54CD\u5E94\u4F53\uFF08\u5DF2\u89E3\u6790 JSON\uFF09\uFF1B\u672A\u914D\u7F6E `request` \u65F6\u4E3A `none`"
51
+ });
52
+ },
53
+ resultType: (t) => t.startsWith("list") || t === "dyn" || t.startsWith("optional")
54
+ });
55
+ const TreeDataSource = Schema.Struct({
56
+ request: Schema.optional(CelDataSourceRequest).annotations({
57
+ title: "\u8BF7\u6C42",
58
+ description: "\u53EF\u9009\u7684 HTTP \u8BF7\u6C42\u8868\u8FBE\u5F0F\uFF1B\u672A\u914D\u7F6E\u65F6\u6570\u636E\u4ECE `data` \u8868\u8FBE\u5F0F\u4E2D\u8BFB\u53D6"
59
+ }),
60
+ data: CelDataSourceData.annotations({
61
+ title: "\u6570\u636E",
62
+ description: "\u8FD4\u56DE\u884C\u6570\u636E\u5217\u8868\u7684 CEL \u8868\u8FBE\u5F0F\uFF1B\u914D\u7F6E\u4E86 `request` \u65F6\u53EF\u901A\u8FC7 `json` \u5F15\u7528\u54CD\u5E94\u4F53"
63
+ })
64
+ }).annotations({ title: "DataSource", description: "\u6570\u636E\u6E90\u914D\u7F6E" });
65
+ return Schema.Struct({
66
+ type: Schema.Literal(type),
67
+ compatibilityDate: Schema.Literal(compatibilityDate),
68
+ ...editableColumnFields(),
69
+ placeholder: Schema.optional(Locale.annotations({
70
+ title: "\u5360\u4F4D\u7B26",
71
+ description: "\u672A\u9009\u4E2D\u4EFB\u4F55\u9009\u9879\u65F6\u8F93\u5165\u6846\u4E2D\u7684\u5360\u4F4D\u6587\u672C"
72
+ })),
73
+ hidden: Schema.optional(CelBool.annotations({
74
+ title: "\u9690\u85CF\u6761\u4EF6",
75
+ description: "\u8FD4\u56DE `true` \u65F6\u8BE5\u884C\u7684\u4E0B\u62C9\u6811\u4E0D\u6E32\u67D3\uFF08\u5176\u4F59\u884C\u4E0D\u53D7\u5F71\u54CD\uFF09"
76
+ })),
77
+ disabled: Schema.optional(CelBool.annotations({
78
+ title: "\u7981\u7528\u6761\u4EF6",
79
+ description: "\u8FD4\u56DE `true` \u65F6\u4E0B\u62C9\u6811\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u9009\u62E9"
80
+ })),
81
+ readonly: Schema.optional(CelBool.annotations({
82
+ title: "\u53EA\u8BFB\u6761\u4EF6",
83
+ description: "\u8FD4\u56DE `true` \u65F6\u4EC5\u4EE5\u7EAF\u6587\u672C\u5C55\u793A\u5F53\u524D\u9009\u9879\u7684\u6807\u7B7E"
84
+ })),
85
+ derived: derivedRowField(configure, "dyn"),
86
+ dataSource: TreeDataSource.annotations({
87
+ title: "\u6570\u636E\u6E90",
88
+ description: "\u8FD4\u56DE\u5B8C\u6574\u6811\uFF08\u6839\u8282\u70B9\u5217\u8868\uFF09\u7684\u6570\u636E\u6E90\uFF1B\u901A\u8FC7 `\u8282\u70B9\u5B50\u7EA7` \u8868\u8FBE\u5F0F\u9010\u5C42\u5411\u4E0B\u8BFB\u53D6\uFF1B\u53EF\u8BBF\u95EE `row`\u3001`index`"
89
+ }),
90
+ nodeKey: CelNodeKey.annotations({
91
+ title: "\u8282\u70B9 ID",
92
+ description: "\u4E3A\u6BCF\u4E2A\u8282\u70B9\u8BA1\u7B97\u7A33\u5B9A\u552F\u4E00 ID \u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\u3001`row`\u3001`index`\uFF1B\u8FD0\u884C\u65F6\u4EE5\u5B57\u7B26\u4E32\u5F62\u5F0F\u5B58\u50A8"
93
+ }),
94
+ nodeChildren: CelNodeChildren.annotations({
95
+ title: "\u8282\u70B9\u5B50\u7EA7",
96
+ description: "\u8FD4\u56DE\u5F53\u524D\u8282\u70B9\u5B50\u7EA7\u5217\u8868\u7684 CEL \u8868\u8FBE\u5F0F\uFF1B\u8FD4\u56DE `none`/`null` \u6216\u7A7A\u6570\u7EC4\u65F6\u8BE5\u8282\u70B9\u89C6\u4E3A\u53F6\u5B50\uFF08\u7EC8\u7AEF\u8282\u70B9\uFF09"
97
+ }),
98
+ nodeLabel: CelNodeLabel.annotations({
99
+ title: "\u8282\u70B9\u6807\u7B7E",
100
+ description: '\u8FD4\u56DE\u8282\u70B9\u5C55\u793A\u6587\u672C\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\u3001`row`\u3001`index`\uFF1B\u4F8B\uFF1A`node.label` \u6216 `string(node.code) + " " + node.name`'
101
+ }),
102
+ nodeTooltip: Schema.optional(NodeLocaleMd.annotations({
103
+ title: "\u8282\u70B9\u63D0\u793A",
104
+ description: "\u9F20\u6807\u60AC\u505C\u5728\u8282\u70B9\u4E0A\u65F6\u5C55\u793A\u7684\u672C\u5730\u5316\u63D0\u793A"
105
+ })),
106
+ keywords: Schema.optional(CelKeywords.annotations({
107
+ title: "\u989D\u5916\u5173\u952E\u8BCD",
108
+ description: "\u8FD4\u56DE\u5B57\u7B26\u4E32\u5217\u8868\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u8BBF\u95EE `node`\u3001`row`\u3001`index`\uFF1B\u641C\u7D22\u65F6\u53EA\u8981 `\u8282\u70B9\u6807\u7B7E` \u6216 \u4EFB\u4E00\u5173\u952E\u8BCD \u5305\u542B\u67E5\u8BE2\u8BCD\uFF08\u4E0D\u533A\u5206\u5927\u5C0F\u5199\uFF09\u5373\u547D\u4E2D"
109
+ })),
110
+ nodeSelectable: Schema.optional(CelNodeBool.annotations({
111
+ title: "\u8282\u70B9\u53EF\u9009\u6761\u4EF6",
112
+ description: "\u8FD4\u56DE `true` \u8868\u793A\u8BE5\u8282\u70B9\u53EF\u88AB\u9009\u4E2D\uFF1B\u9ED8\u8BA4\u5168\u90E8\u53EF\u9009"
113
+ })),
114
+ expandAll: Schema.optionalWith(Schema.Boolean, { default: () => false }).annotations({
115
+ title: "\u9ED8\u8BA4\u5168\u90E8\u5C55\u5F00",
116
+ description: "\u5F00\u542F\u540E\u6302\u8F7D\u65F6\u5C55\u5F00\u6240\u6709\u975E\u53F6\u5B50\u8282\u70B9"
117
+ })
118
+ }).annotations({ title: "TreeComboboxSingleRenderer", description: "\u4E0B\u62C9\u6811\u5355\u9009\u6E32\u67D3\u5668\uFF08\u53EF\u7F16\u8F91\uFF09" });
119
+ }
120
+ export function defaults() {
121
+ return {
122
+ title: [{ locale: "zh", message: "" }],
123
+ binding: "",
124
+ size: 200,
125
+ dataSource: { data: "[]" },
126
+ nodeKey: "string(node.id)",
127
+ nodeChildren: "has(node.children) ? node.children : null",
128
+ nodeLabel: "node.label",
129
+ expandAll: false
130
+ };
131
+ }
132
+ export const migrateFrom = [
133
+ { type: "com.shwfed.table.column.tree-combobox-single", compatibilityDate: "2026-05-28" }
134
+ ];
135
+ export const migrate = (prev) => Effect.try({
136
+ try: () => {
137
+ if (!prev || typeof prev !== "object") {
138
+ throw new Error("\u4E0B\u62C9\u6811\uFF08\u5355\u9009\uFF09\u8FC1\u79FB\u5931\u8D25\uFF1A\u539F\u503C\u4E0D\u662F\u5BF9\u8C61");
139
+ }
140
+ const src = prev;
141
+ const srcType = src.type;
142
+ const srcDate = src.compatibilityDate;
143
+ if (srcType === "com.shwfed.table.column.tree-combobox-single" && srcDate === "2026-05-28") {
144
+ const { nodeLabel, type: _t, compatibilityDate: _c, ...rest } = src;
145
+ return {
146
+ ...rest,
147
+ type,
148
+ compatibilityDate,
149
+ nodeLabel: localeLabelToExpression(nodeLabel)
150
+ };
151
+ }
152
+ throw new Error(`\u4E0B\u62C9\u6811\uFF08\u5355\u9009\uFF09\u8FC1\u79FB\u5931\u8D25\uFF1A\u672A\u8BC6\u522B\u7684\u6765\u6E90 ${String(srcType)}@${String(srcDate)}`);
153
+ },
154
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
155
+ });
156
+ export function toColumnDef(value, { getLocaleText }) {
157
+ return {
158
+ header: editableHeader(getLocaleText(value.title)),
159
+ accessorFn: (row) => {
160
+ if (!row || typeof row !== "object") return void 0;
161
+ return getProperty(row, value.binding);
162
+ },
163
+ enableSorting: value.enableSorting ?? false,
164
+ sortingFn: "basic",
165
+ size: value.size,
166
+ meta: {
167
+ grow: value.grow ?? false,
168
+ tooltip: getLocaleText(value.tooltip)
169
+ }
170
+ };
171
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Rewrite a `{{ }}`-interpolated template into a single CEL expression.
3
+ *
4
+ * - `''` / no content → `''` (valid empty-string literal)
5
+ * - whole template is one `{{ expr }}` → `expr` (kept bare; runtime coerces to string)
6
+ * - mixed literal + interpolation → `"lit" + string(expr) + …`
7
+ *
8
+ * Interpolated segments are wrapped in `string(...)` in the mixed case because
9
+ * CEL `+` requires matching operand types and the literal segments are strings;
10
+ * the old runtime stringified every interpolation, so this preserves behavior.
11
+ */
12
+ export declare function templateToExpression(template: string): string;
13
+ /**
14
+ * Collapse a legacy label (a Locale list of `{{ }}` templates) into the single
15
+ * CEL expression the 2026-06-14 schema expects. Picks the zh entry (or the
16
+ * first), then rewrites its template. Anything unrecognized → `''`.
17
+ */
18
+ export declare function localeLabelToExpression(label: unknown): string;
@@ -0,0 +1,36 @@
1
+ const INTERPOLATION_RE = /\{\{(.*?)\}\}/gs;
2
+ function celString(text) {
3
+ const body = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
4
+ return `"${body}"`;
5
+ }
6
+ export function templateToExpression(template) {
7
+ const trimmed = template.trim();
8
+ if (trimmed.length === 0) return `''`;
9
+ const sole = /^\{\{([\s\S]*)\}\}$/.exec(trimmed);
10
+ if (sole && !sole[1].includes("{{")) {
11
+ const expr = sole[1].trim();
12
+ return expr.length > 0 ? expr : `''`;
13
+ }
14
+ const parts = [];
15
+ let last = 0;
16
+ let match;
17
+ INTERPOLATION_RE.lastIndex = 0;
18
+ while ((match = INTERPOLATION_RE.exec(template)) !== null) {
19
+ if (match.index > last) parts.push(celString(template.slice(last, match.index)));
20
+ const expr = match[1].trim();
21
+ if (expr.length > 0) parts.push(`string(${expr})`);
22
+ last = match.index + match[0].length;
23
+ }
24
+ if (last < template.length) parts.push(celString(template.slice(last)));
25
+ if (parts.length === 0) return `''`;
26
+ return parts.join(" + ");
27
+ }
28
+ export function localeLabelToExpression(label) {
29
+ if (!Array.isArray(label)) return `''`;
30
+ const zh = label.find(
31
+ (item) => !!item && typeof item === "object" && item.locale === "zh"
32
+ );
33
+ const chosen = zh ?? label[0];
34
+ const message = chosen && typeof chosen.message === "string" ? chosen.message : "";
35
+ return templateToExpression(message);
36
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Wrapping + adapter helpers shared by `tree-combobox-single` and
3
+ * `tree-combobox-multi`. The whole tree comes from `dataSource` in one
4
+ * shot, so wrapping is a pure walk over CEL-derived children: each raw
5
+ * node either has children (`branch`) or it doesn't (`terminal`).
6
+ */
7
+ export type WrappedNode = {
8
+ readonly kind: 'branch';
9
+ readonly raw: unknown;
10
+ readonly key: string;
11
+ readonly children: WrappedNode[];
12
+ } | {
13
+ readonly kind: 'terminal';
14
+ readonly raw: unknown;
15
+ readonly key: string;
16
+ };
17
+ export interface WrapStructureContext {
18
+ /** Stringified node id; one per raw node. Reuses `nodeKey` CEL. */
19
+ nodeKey: (raw: unknown) => string;
20
+ /**
21
+ * Normalised children array. `undefined`/`null`/empty all collapse to
22
+ * `undefined` here — the wrapping logic interprets that as
23
+ * "this node has no children" → `terminal`.
24
+ */
25
+ nodeChildren: (raw: unknown) => unknown[] | undefined;
26
+ }
27
+ export declare function wrapStructural(raw: unknown, ctx: WrapStructureContext): WrappedNode;
28
+ export declare function wrapStructuralAll(raws: unknown[], ctx: WrapStructureContext): WrappedNode[];
29
+ /** Synchronous `getChildren` adapter for ui/tree. */
30
+ export declare function getWrappedChildren(w: WrappedNode): WrappedNode[] | undefined;
31
+ /**
32
+ * Find the path from root to a node whose `key` matches. Returns `null`
33
+ * if the key isn't present in `roots`.
34
+ */
35
+ export declare function findWrappedPath(roots: WrappedNode[], key: string): WrappedNode[] | null;
@@ -0,0 +1,31 @@
1
+ export function wrapStructural(raw, ctx) {
2
+ const kids = ctx.nodeChildren(raw);
3
+ if (!kids || kids.length === 0) {
4
+ return { kind: "terminal", raw, key: ctx.nodeKey(raw) };
5
+ }
6
+ return {
7
+ kind: "branch",
8
+ raw,
9
+ key: ctx.nodeKey(raw),
10
+ children: kids.map((c) => wrapStructural(c, ctx))
11
+ };
12
+ }
13
+ export function wrapStructuralAll(raws, ctx) {
14
+ return raws.map((r) => wrapStructural(r, ctx));
15
+ }
16
+ export function getWrappedChildren(w) {
17
+ return w.kind === "branch" ? w.children : void 0;
18
+ }
19
+ export function findWrappedPath(roots, key) {
20
+ const trail = [];
21
+ function walk(siblings) {
22
+ for (const n of siblings) {
23
+ trail.push(n);
24
+ if (n.key === key) return true;
25
+ if (n.kind === "branch" && n.children.length > 0 && walk(n.children)) return true;
26
+ trail.pop();
27
+ }
28
+ return false;
29
+ }
30
+ return walk(roots) ? trail.slice() : null;
31
+ }
@@ -6,6 +6,8 @@ declare module '@tanstack/vue-table' {
6
6
  interface ColumnMeta<TData extends import('@tanstack/vue-table').RowData, TValue> {
7
7
  tooltip?: string;
8
8
  grow?: boolean;
9
+ editable?: boolean;
10
+ readonlyExpr?: string;
9
11
  }
10
12
  }
11
13
  declare const _default: typeof __VLS_export;
@@ -32,6 +32,8 @@ import { provideSharedFetchLayer } from "./utils/shared-fetch";
32
32
  import { provideEventTarget } from "../../share/event-bus";
33
33
  import { usePersistedQuery } from "../../share/use-persisted-query";
34
34
  import { findColumn } from "./utils/resolve";
35
+ import { isEditableHeader } from "./utils/shared";
36
+ import { useFormReadonly } from "../form/utils/readonly";
35
37
  import { interpolateMarkdown } from "./utils/runtime";
36
38
  import { carrySymbolId, createRowKeyResolver } from "./utils/row-key";
37
39
  import { provideReorderContext } from "./utils/reorder";
@@ -98,6 +100,15 @@ const { t, locale } = useI18n({
98
100
  const inheritedContext = injectCELContext();
99
101
  const containerRef = ref(null);
100
102
  const appliedInitialStateKey = ref("");
103
+ const formReadonly = useFormReadonly();
104
+ function isHeaderReadonly(readonlyExpr) {
105
+ if (readonlyExpr == null) return formReadonly.value;
106
+ try {
107
+ return Effect.runSync($cel(readonlyExpr, celBindings(inheritedContext))) === true;
108
+ } catch {
109
+ return false;
110
+ }
111
+ }
101
112
  function getLocaleText(value) {
102
113
  return getLocalizedText(value, locale.value);
103
114
  }
@@ -116,8 +127,11 @@ function translateColumn(column) {
116
127
  throw new Error(`[shwfed-table] no column registered for ${column.type}@${column.compatibilityDate}`);
117
128
  }
118
129
  const contribution = entry.toColumnDef(column, { getLocaleText, $cel, inheritedContext: celBindings(inheritedContext) });
130
+ const editable = isEditableHeader(contribution.header);
131
+ const readonlyExpr = editable ? column.readonly : void 0;
119
132
  return {
120
133
  ...contribution,
134
+ meta: { ...contribution.meta, editable, readonlyExpr },
121
135
  id: column.id,
122
136
  // `ctx` is the functional-component props object Vue mutates in place on
123
137
  // every update, so its identity stays stable across data refetches.
@@ -744,6 +758,16 @@ export { TableConfig, createTableConfig, getColumnTechnicalKey } from "./schema"
744
758
  :props="header.getContext()"
745
759
  />
746
760
 
761
+ <!-- Edit affordance: marks an editable column, hidden when
762
+ the column reads as readonly (form-wide or its own
763
+ `readonly` condition). -->
764
+ <Icon
765
+ v-if="!header.isPlaceholder && header.column.columnDef.meta?.editable && !isHeaderReadonly(header.column.columnDef.meta?.readonlyExpr)"
766
+ icon="fluent:edit-20-regular"
767
+ class="size-3 shrink-0 text-zinc-400"
768
+ aria-hidden="true"
769
+ />
770
+
747
771
  <!-- Sort toggle -->
748
772
  <Tooltip
749
773
  v-if="!header.isPlaceholder && header.column.getCanSort()"
@@ -6,6 +6,8 @@ declare module '@tanstack/vue-table' {
6
6
  interface ColumnMeta<TData extends import('@tanstack/vue-table').RowData, TValue> {
7
7
  tooltip?: string;
8
8
  grow?: boolean;
9
+ editable?: boolean;
10
+ readonlyExpr?: string;
9
11
  }
10
12
  }
11
13
  declare const _default: typeof __VLS_export;
@@ -54,6 +54,7 @@ export declare function editableColumnFields(): {
54
54
  groupId: Schema.optional<typeof Schema.UUID>;
55
55
  };
56
56
  export declare function editableHeader(titleText: string | undefined): () => VNode;
57
+ export declare function isEditableHeader(header: unknown): boolean;
57
58
  export declare function registerRowVariablesIfAbsent(env: Environment): void;
58
59
  export declare function CelRowAccess(configure: (env: Environment) => void, options?: {
59
60
  resultType?: ResultType;
@@ -1,5 +1,4 @@
1
1
  import { Schema } from "effect";
2
- import { Icon } from "@iconify/vue";
3
2
  import { h } from "vue";
4
3
  import { SELECTIONS_VAR } from "../../../utils/cel-context.js";
5
4
  import { Expression, LocaleMarkdown } from "../../../share/expression.js";
@@ -76,19 +75,14 @@ export function editableColumnFields() {
76
75
  tooltip: Schema.optional(Locale.annotations({ title: "\u63D0\u793A", description: "\u5217\u6807\u9898\u60AC\u6D6E\u63D0\u793A\uFF0C\u652F\u6301 Markdown" }))
77
76
  };
78
77
  }
78
+ const EDITABLE_HEADER_MARKER = Symbol("shwfed-editable-header");
79
79
  export function editableHeader(titleText) {
80
- return () => h(
81
- "span",
82
- { class: "inline-flex items-center gap-1" },
83
- [
84
- titleText ?? "",
85
- h(Icon, {
86
- "icon": "fluent:edit-20-regular",
87
- "class": "size-3 text-zinc-400",
88
- "aria-hidden": "true"
89
- })
90
- ]
91
- );
80
+ const render = () => h("span", { class: "inline-flex items-center" }, titleText ?? "");
81
+ render[EDITABLE_HEADER_MARKER] = true;
82
+ return render;
83
+ }
84
+ export function isEditableHeader(header) {
85
+ return typeof header === "function" && header[EDITABLE_HEADER_MARKER] === true;
92
86
  }
93
87
  export function registerRowVariablesIfAbsent(env) {
94
88
  const declared = new Set(env.getDefinitions().variables.map((v) => v.name));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/config",
3
- "version": "2.10.0",
3
+ "version": "2.10.1",
4
4
  "description": "Configurable UI for SHWFED",
5
5
  "type": "module",
6
6
  "publishConfig": {