@svadmin/ui 0.72.0 → 0.73.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.
Files changed (252) hide show
  1. package/README.md +40 -25
  2. package/THIRD_PARTY_NOTICES.md +52 -0
  3. package/dist/app.css +7694 -5117
  4. package/dist/app.theme.css +7645 -5119
  5. package/dist/class-properties.generated.d.ts +1 -0
  6. package/dist/class-properties.generated.js +2 -0
  7. package/dist/classnames.d.ts +8 -0
  8. package/dist/classnames.js +26 -0
  9. package/dist/components/AutoForm.svelte +20 -6
  10. package/dist/components/AutoForm.svelte.d.ts +2 -0
  11. package/dist/components/AutoTable.svelte +249 -9
  12. package/dist/components/AutoTable.svelte.d.ts +5 -0
  13. package/dist/components/BoundRecordDetailDrawer.svelte +8 -2
  14. package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +2 -0
  15. package/dist/components/ComboboxField.svelte +31 -9
  16. package/dist/components/ComboboxField.svelte.d.ts +3 -2
  17. package/dist/components/CreatePage.svelte +4 -0
  18. package/dist/components/CreatePage.svelte.d.ts +2 -0
  19. package/dist/components/DateRangeInput.svelte +75 -0
  20. package/dist/components/DateRangeInput.svelte.d.ts +25 -0
  21. package/dist/components/DateTimeInput.svelte +65 -0
  22. package/dist/components/DateTimeInput.svelte.d.ts +19 -0
  23. package/dist/components/DevTools.svelte +2 -2
  24. package/dist/components/EditPage.svelte +4 -0
  25. package/dist/components/EditPage.svelte.d.ts +2 -0
  26. package/dist/components/FieldRenderer.svelte +90 -18
  27. package/dist/components/FileUpload.svelte +250 -0
  28. package/dist/components/FileUpload.svelte.d.ts +32 -0
  29. package/dist/components/FilterBuilder.svelte +236 -279
  30. package/dist/components/FilterBuilder.svelte.d.ts +3 -0
  31. package/dist/components/ImportWizard.svelte +181 -94
  32. package/dist/components/JsonSchemaForm.svelte +255 -99
  33. package/dist/components/JsonSchemaForm.svelte.d.ts +12 -1
  34. package/dist/components/Layout.svelte +1 -2
  35. package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +1 -1
  36. package/dist/components/ListPage.svelte +4 -0
  37. package/dist/components/ListPage.svelte.d.ts +2 -0
  38. package/dist/components/LoginPage.svelte +1 -1
  39. package/dist/components/MoneyInput.svelte +25 -0
  40. package/dist/components/MoneyInput.svelte.d.ts +20 -0
  41. package/dist/components/NumberInput.svelte +25 -0
  42. package/dist/components/NumberInput.svelte.d.ts +19 -0
  43. package/dist/components/NumericInput.svelte +110 -0
  44. package/dist/components/NumericInput.svelte.d.ts +23 -0
  45. package/dist/components/PercentInput.svelte +25 -0
  46. package/dist/components/PercentInput.svelte.d.ts +20 -0
  47. package/dist/components/QuickEditDrawer.svelte +4 -1
  48. package/dist/components/QuickEditDrawer.svelte.d.ts +2 -0
  49. package/dist/components/RecordDetailDrawer.svelte +4 -2
  50. package/dist/components/RecordDetailDrawer.svelte.d.ts +2 -0
  51. package/dist/components/ResourceOperationsPage.svelte +25 -19
  52. package/dist/components/ResourceOperationsPage.svelte.d.ts +5 -1
  53. package/dist/components/ShowInferencer.svelte +4 -2
  54. package/dist/components/ShowInferencer.svelte.d.ts +2 -0
  55. package/dist/components/ShowPage.svelte +7 -3
  56. package/dist/components/ShowPage.svelte.d.ts +2 -0
  57. package/dist/components/Sidebar.svelte +5 -5
  58. package/dist/components/SpreadsheetView.svelte +81 -286
  59. package/dist/components/StepsForm.svelte +5 -5
  60. package/dist/components/SvarAutoTable.svelte +39 -0
  61. package/dist/components/SvarAutoTable.svelte.d.ts +13 -0
  62. package/dist/components/SvarDataGrid.svelte +362 -0
  63. package/dist/components/SvarDataGrid.svelte.d.ts +57 -0
  64. package/dist/components/SvarGridCell.svelte +10 -0
  65. package/dist/components/SvarGridCell.svelte.d.ts +7 -0
  66. package/dist/components/SvarResourceRead.svelte +50 -0
  67. package/dist/components/SvarResourceRead.svelte.d.ts +23 -0
  68. package/dist/components/SvarResourceTable.svelte +63 -0
  69. package/dist/components/SvarResourceTable.svelte.d.ts +42 -0
  70. package/dist/components/SvarResourceTableView.svelte +364 -0
  71. package/dist/components/SvarResourceTableView.svelte.d.ts +4 -0
  72. package/dist/components/TaskQueueDrawer.svelte +72 -16
  73. package/dist/components/TreeSelect.svelte +208 -28
  74. package/dist/components/TreeSelect.svelte.d.ts +3 -1
  75. package/dist/components/auto-table-grid.d.ts +26 -0
  76. package/dist/components/auto-table-grid.js +1 -0
  77. package/dist/components/content/ContentPageHeader.svelte +12 -9
  78. package/dist/components/content/ContentPageShell.svelte +6 -9
  79. package/dist/components/content/FileBrowser.svelte +35 -0
  80. package/dist/components/content/FileBrowser.svelte.d.ts +14 -0
  81. package/dist/components/content/MetricBlock.svelte +10 -13
  82. package/dist/components/content/NetworkTable.svelte +3 -3
  83. package/dist/components/content/NetworkTable.svelte.d.ts +1 -0
  84. package/dist/components/content/PageToolbar.svelte +6 -10
  85. package/dist/components/content/SectionHeader.svelte +8 -13
  86. package/dist/components/content/SettingsFieldRow.svelte +9 -23
  87. package/dist/components/content/SettingsGroup.svelte +11 -27
  88. package/dist/components/content/StatusBadge.svelte +3 -8
  89. package/dist/components/content/WorkspaceLayout.svelte +8 -12
  90. package/dist/components/enterprise/filter-tree.d.ts +48 -0
  91. package/dist/components/enterprise/filter-tree.js +224 -0
  92. package/dist/components/enterprise/json-schema-form.d.ts +39 -0
  93. package/dist/components/enterprise/json-schema-form.js +195 -0
  94. package/dist/components/enterprise/recursive-schema-form.d.ts +30 -0
  95. package/dist/components/enterprise/recursive-schema-form.js +196 -0
  96. package/dist/components/enterprise/spreadsheet-formula.d.ts +12 -0
  97. package/dist/components/enterprise/spreadsheet-formula.js +245 -0
  98. package/dist/components/fieldComponentMap.js +5 -0
  99. package/dist/components/fields/DateField.svelte +4 -3
  100. package/dist/components/fields/date-field-value.d.ts +2 -0
  101. package/dist/components/fields/date-field-value.js +13 -0
  102. package/dist/components/json-schema-form-state.d.ts +37 -0
  103. package/dist/components/json-schema-form-state.js +168 -0
  104. package/dist/components/record-detail.svelte.d.ts +3 -0
  105. package/dist/components/record-detail.svelte.js +10 -4
  106. package/dist/components/svar-grid-cells.d.ts +14 -0
  107. package/dist/components/svar-grid-cells.js +2 -0
  108. package/dist/components/svar-grid-contract.d.ts +80 -0
  109. package/dist/components/svar-grid-contract.js +159 -0
  110. package/dist/components/svar-grid-interactions.d.ts +54 -0
  111. package/dist/components/svar-grid-interactions.js +174 -0
  112. package/dist/components/svar-grid-loading.d.ts +40 -0
  113. package/dist/components/svar-grid-loading.js +180 -0
  114. package/dist/components/svar-grid-operations.d.ts +37 -0
  115. package/dist/components/svar-grid-operations.js +186 -0
  116. package/dist/components/svar-grid-panes.d.ts +34 -0
  117. package/dist/components/svar-grid-panes.js +66 -0
  118. package/dist/components/svar-grid-resource-loading.d.ts +18 -0
  119. package/dist/components/svar-grid-resource-loading.js +68 -0
  120. package/dist/components/svar-grid-resource-session.svelte.d.ts +29 -0
  121. package/dist/components/svar-grid-resource-session.svelte.js +61 -0
  122. package/dist/components/ui/badge/badge-variants.js +3 -1
  123. package/dist/components/ui/button/button-variants.js +5 -2
  124. package/dist/components/ui/input/input.svelte +1 -4
  125. package/dist/components/ui/textarea/textarea.svelte +2 -1
  126. package/dist/components.css +3079 -0
  127. package/dist/design-contract.d.ts +13 -0
  128. package/dist/design-contract.js +11 -0
  129. package/dist/design-system.d.ts +15 -0
  130. package/dist/design-system.js +7 -0
  131. package/dist/index.d.ts +14 -0
  132. package/dist/index.js +9 -0
  133. package/dist/page-chrome.css +12 -49
  134. package/dist/recipes.d.ts +4 -0
  135. package/dist/recipes.js +4 -0
  136. package/dist/rendering/index.d.ts +87 -0
  137. package/dist/rendering/index.js +117 -0
  138. package/dist/styled-system/css/conditions.js +36 -0
  139. package/dist/styled-system/css/css.d.ts +22 -0
  140. package/dist/styled-system/css/css.js +45 -0
  141. package/dist/styled-system/css/cva.d.ts +6 -0
  142. package/dist/styled-system/css/cva.js +87 -0
  143. package/dist/styled-system/css/cx.d.ts +5 -0
  144. package/dist/styled-system/css/cx.js +15 -0
  145. package/dist/styled-system/css/index.d.ts +5 -0
  146. package/dist/styled-system/css/index.js +4 -0
  147. package/dist/styled-system/css/sva.d.ts +4 -0
  148. package/dist/styled-system/css/sva.js +46 -0
  149. package/dist/styled-system/helpers.js +316 -0
  150. package/dist/styled-system/patterns/aspect-ratio.d.ts +20 -0
  151. package/dist/styled-system/patterns/aspect-ratio.js +38 -0
  152. package/dist/styled-system/patterns/bleed.d.ts +21 -0
  153. package/dist/styled-system/patterns/bleed.js +24 -0
  154. package/dist/styled-system/patterns/box.d.ts +20 -0
  155. package/dist/styled-system/patterns/box.js +15 -0
  156. package/dist/styled-system/patterns/center.d.ts +20 -0
  157. package/dist/styled-system/patterns/center.js +21 -0
  158. package/dist/styled-system/patterns/circle.d.ts +20 -0
  159. package/dist/styled-system/patterns/circle.js +25 -0
  160. package/dist/styled-system/patterns/container.d.ts +20 -0
  161. package/dist/styled-system/patterns/container.js +21 -0
  162. package/dist/styled-system/patterns/cq.d.ts +21 -0
  163. package/dist/styled-system/patterns/cq.js +21 -0
  164. package/dist/styled-system/patterns/divider.d.ts +22 -0
  165. package/dist/styled-system/patterns/divider.js +25 -0
  166. package/dist/styled-system/patterns/flex.d.ts +26 -0
  167. package/dist/styled-system/patterns/flex.js +26 -0
  168. package/dist/styled-system/patterns/float.d.ts +23 -0
  169. package/dist/styled-system/patterns/float.js +52 -0
  170. package/dist/styled-system/patterns/grid-item.d.ts +25 -0
  171. package/dist/styled-system/patterns/grid-item.js +25 -0
  172. package/dist/styled-system/patterns/grid.d.ts +24 -0
  173. package/dist/styled-system/patterns/grid.js +27 -0
  174. package/dist/styled-system/patterns/hstack.d.ts +21 -0
  175. package/dist/styled-system/patterns/hstack.js +24 -0
  176. package/dist/styled-system/patterns/index.d.ts +21 -0
  177. package/dist/styled-system/patterns/index.js +20 -0
  178. package/dist/styled-system/patterns/link-overlay.d.ts +20 -0
  179. package/dist/styled-system/patterns/link-overlay.js +24 -0
  180. package/dist/styled-system/patterns/spacer.d.ts +20 -0
  181. package/dist/styled-system/patterns/spacer.js +25 -0
  182. package/dist/styled-system/patterns/square.d.ts +20 -0
  183. package/dist/styled-system/patterns/square.js +24 -0
  184. package/dist/styled-system/patterns/stack.d.ts +23 -0
  185. package/dist/styled-system/patterns/stack.js +24 -0
  186. package/dist/styled-system/patterns/visually-hidden.d.ts +20 -0
  187. package/dist/styled-system/patterns/visually-hidden.js +18 -0
  188. package/dist/styled-system/patterns/vstack.d.ts +21 -0
  189. package/dist/styled-system/patterns/vstack.js +24 -0
  190. package/dist/styled-system/patterns/wrap.d.ts +24 -0
  191. package/dist/styled-system/patterns/wrap.js +25 -0
  192. package/dist/styled-system/recipes/content-header.d.ts +31 -0
  193. package/dist/styled-system/recipes/content-header.js +65 -0
  194. package/dist/styled-system/recipes/content-page.d.ts +34 -0
  195. package/dist/styled-system/recipes/content-page.js +43 -0
  196. package/dist/styled-system/recipes/create-recipe.js +82 -0
  197. package/dist/styled-system/recipes/index.d.ts +17 -0
  198. package/dist/styled-system/recipes/index.js +16 -0
  199. package/dist/styled-system/recipes/metric-block.d.ts +34 -0
  200. package/dist/styled-system/recipes/metric-block.js +76 -0
  201. package/dist/styled-system/recipes/product-list.d.ts +31 -0
  202. package/dist/styled-system/recipes/product-list.js +73 -0
  203. package/dist/styled-system/recipes/product-section.d.ts +31 -0
  204. package/dist/styled-system/recipes/product-section.js +49 -0
  205. package/dist/styled-system/recipes/product-settings-row.d.ts +34 -0
  206. package/dist/styled-system/recipes/product-settings-row.js +58 -0
  207. package/dist/styled-system/recipes/product-settings.d.ts +31 -0
  208. package/dist/styled-system/recipes/product-settings.js +57 -0
  209. package/dist/styled-system/recipes/product-status.d.ts +34 -0
  210. package/dist/styled-system/recipes/product-status.js +45 -0
  211. package/dist/styled-system/recipes/product-toolbar.d.ts +31 -0
  212. package/dist/styled-system/recipes/product-toolbar.js +41 -0
  213. package/dist/styled-system/recipes/product-workspace.d.ts +34 -0
  214. package/dist/styled-system/recipes/product-workspace.js +58 -0
  215. package/dist/styled-system/recipes/surface-metric.d.ts +38 -0
  216. package/dist/styled-system/recipes/surface-metric.js +63 -0
  217. package/dist/styled-system/recipes/surface-table.d.ts +34 -0
  218. package/dist/styled-system/recipes/surface-table.js +62 -0
  219. package/dist/styled-system/recipes/ui-badge.d.ts +31 -0
  220. package/dist/styled-system/recipes/ui-badge.js +38 -0
  221. package/dist/styled-system/recipes/ui-button.d.ts +32 -0
  222. package/dist/styled-system/recipes/ui-button.js +43 -0
  223. package/dist/styled-system/recipes/ui-input.d.ts +31 -0
  224. package/dist/styled-system/recipes/ui-input.js +49 -0
  225. package/dist/styled-system/recipes/ui-textarea.d.ts +31 -0
  226. package/dist/styled-system/recipes/ui-textarea.js +24 -0
  227. package/dist/styled-system/styles.css +1257 -0
  228. package/dist/styled-system/tokens/index.d.ts +9 -0
  229. package/dist/styled-system/tokens/index.js +268 -0
  230. package/dist/styled-system/tokens/tokens.d.ts +36 -0
  231. package/dist/styled-system/types/composition.d.ts +227 -0
  232. package/dist/styled-system/types/conditions.d.ts +236 -0
  233. package/dist/styled-system/types/csstype.d.ts +22570 -0
  234. package/dist/styled-system/types/index.d.ts +6 -0
  235. package/dist/styled-system/types/parts.d.ts +8 -0
  236. package/dist/styled-system/types/pattern.d.ts +78 -0
  237. package/dist/styled-system/types/prop-type.d.ts +223 -0
  238. package/dist/styled-system/types/recipe.d.ts +181 -0
  239. package/dist/styled-system/types/selectors.d.ts +59 -0
  240. package/dist/styled-system/types/static-css.d.ts +56 -0
  241. package/dist/styled-system/types/style-props.d.ts +8088 -0
  242. package/dist/styled-system/types/system-types.d.ts +151 -0
  243. package/dist/styles/aliases.css +49 -0
  244. package/dist/styles/compatibility.epilogue.css +384 -0
  245. package/dist/styles/compatibility.prelude.css +5513 -0
  246. package/dist/styles/conditional-states.css +28 -0
  247. package/dist/styles/conditional-utilities.css +22 -0
  248. package/dist/styles/recipes.css +1262 -0
  249. package/dist/utils.d.ts +1 -1
  250. package/dist/utils.js +1 -1
  251. package/package.json +31 -13
  252. package/styles-compatibility.json +12 -0
@@ -0,0 +1,168 @@
1
+ export const SCHEMA_FORM_LIMITS = Object.freeze({ depth: 32, nodes: 10_000, characters: 1_048_576 });
2
+ const unsafe = new Set(['__proto__', 'prototype', 'constructor']);
3
+ const record = (value) => value !== null
4
+ && typeof value === 'object' && !Array.isArray(value)
5
+ && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
6
+ function safeKey(key) {
7
+ if (unsafe.has(key))
8
+ throw new Error('Unsafe form field');
9
+ }
10
+ /** wire 模式仅省略对象的 undefined;数组的空洞/undefined 不能偷偷转成 null。 */
11
+ function copy(value, wire) {
12
+ const active = new Set();
13
+ let nodes = 0, characters = 0;
14
+ function visit(current, depth) {
15
+ if (++nodes > SCHEMA_FORM_LIMITS.nodes || depth > SCHEMA_FORM_LIMITS.depth)
16
+ throw new Error('Form value exceeds limits');
17
+ if (current === undefined && !wire)
18
+ return undefined;
19
+ if (current === null || typeof current === 'boolean')
20
+ return current;
21
+ if (typeof current === 'number' && Number.isFinite(current))
22
+ return current;
23
+ if (typeof current === 'string') {
24
+ characters += current.length;
25
+ if (characters > SCHEMA_FORM_LIMITS.characters)
26
+ throw new Error('Form text exceeds limits');
27
+ return current;
28
+ }
29
+ if ((!record(current) && !Array.isArray(current)) || active.has(current))
30
+ throw new Error('Expected acyclic plain form data');
31
+ active.add(current);
32
+ try {
33
+ if (Reflect.ownKeys(current).some(key => typeof key === 'symbol'))
34
+ throw new Error('Symbol values are not JSON fields');
35
+ if (Array.isArray(current)) {
36
+ if (current.length > SCHEMA_FORM_LIMITS.nodes)
37
+ throw new Error('Form array exceeds limits');
38
+ const result = [];
39
+ for (let index = 0; index < current.length; index++) {
40
+ const descriptor = Object.getOwnPropertyDescriptor(current, String(index));
41
+ if (!descriptor || !('value' in descriptor))
42
+ throw new Error('Sparse arrays and accessors are not form data');
43
+ result.push(visit(descriptor.value, depth + 1));
44
+ }
45
+ if (Object.keys(current).some(key => !/^(0|[1-9][0-9]*)$/u.test(key) || Number(key) >= current.length)) {
46
+ throw new Error('Array properties are not JSON fields');
47
+ }
48
+ return result;
49
+ }
50
+ const result = {};
51
+ for (const key of Object.keys(current)) {
52
+ safeKey(key);
53
+ characters += key.length;
54
+ if (characters > SCHEMA_FORM_LIMITS.characters)
55
+ throw new Error('Form text exceeds limits');
56
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
57
+ if (!descriptor || !('value' in descriptor))
58
+ throw new Error('Accessors are not form data');
59
+ if (!wire || descriptor.value !== undefined)
60
+ result[key] = visit(descriptor.value, depth + 1);
61
+ }
62
+ return result;
63
+ }
64
+ finally {
65
+ active.delete(current);
66
+ }
67
+ }
68
+ return visit(value, 0);
69
+ }
70
+ /** Svelte 代理也通过普通数据属性读取;回调永远收不到活跃草稿的引用。 */
71
+ export function schemaFormSnapshot(value) {
72
+ if (!record(value))
73
+ throw new Error('Form root must be an object');
74
+ return copy(value, true);
75
+ }
76
+ /** 默认值只填“缺失的键”;显式 undefined/null/false/0/空串都表示调用方的决定。 */
77
+ export function initializeSchemaForm(schema, value) {
78
+ if (!record(value))
79
+ throw new Error('Form root must be an object');
80
+ const active = new Set();
81
+ let nodes = 0;
82
+ function visit(node, current, present, required, depth) {
83
+ if (++nodes > SCHEMA_FORM_LIMITS.nodes || depth > SCHEMA_FORM_LIMITS.depth || !record(node) || active.has(node)) {
84
+ throw new Error('Form schema exceeds supported traversal limits');
85
+ }
86
+ active.add(node);
87
+ try {
88
+ if (!present) {
89
+ if (Object.hasOwn(node, 'default'))
90
+ current = copy(node.default, false);
91
+ else if (node.type === 'object' || node.properties)
92
+ current = {};
93
+ else if (node.type === 'array')
94
+ current = [];
95
+ else if (node.type === 'boolean' && required)
96
+ current = false;
97
+ }
98
+ if (record(current) && (node.type === 'object' || node.properties)) {
99
+ let result = current;
100
+ for (const [key, child] of Object.entries(node.properties ?? {})) {
101
+ safeKey(key);
102
+ const exists = Object.hasOwn(current, key);
103
+ const next = visit(child, exists ? current[key] : undefined, exists, node.required?.includes(key) ?? false, depth + 1);
104
+ if ((!exists && next !== undefined) || (exists && next !== current[key])) {
105
+ if (result === current)
106
+ result = { ...current };
107
+ result[key] = next;
108
+ }
109
+ }
110
+ return result;
111
+ }
112
+ if (Array.isArray(current) && node.type === 'array' && node.items) {
113
+ if (current.length > SCHEMA_FORM_LIMITS.nodes)
114
+ throw new Error('Form array exceeds limits');
115
+ const array = current;
116
+ const items = array.map(item => visit(node.items, item, true, true, depth + 1));
117
+ return items.some((item, index) => item !== array[index]) ? items : array;
118
+ }
119
+ return current;
120
+ }
121
+ finally {
122
+ active.delete(node);
123
+ }
124
+ }
125
+ return visit(schema, value, true, true, 0);
126
+ }
127
+ export function schemaFormArrayItem(schema) {
128
+ // 新增对象项也初始化其嵌套默认值及必填 false;数字留空,不猜测为 0。
129
+ const seed = Object.hasOwn(schema, 'default') ? copy(schema.default, false)
130
+ : schema.type === 'object' || schema.properties ? {}
131
+ : schema.type === 'array' ? [] : schema.type === 'boolean' ? false : schema.type === 'number' || schema.type === 'integer' ? undefined : '';
132
+ return initializeSchemaForm({ properties: { item: schema }, required: ['item'] }, { item: seed })['item'];
133
+ }
134
+ export function writeSchemaFormPath(value, path, nextValue) {
135
+ if (!path.length || path.length > SCHEMA_FORM_LIMITS.depth)
136
+ throw new Error('Invalid form path');
137
+ const next = copy(value, false);
138
+ let target = next;
139
+ for (const [index, key] of path.entries()) {
140
+ safeKey(key);
141
+ if (Array.isArray(target) && (!/^(0|[1-9][0-9]*)$/u.test(key) || Number(key) >= target.length))
142
+ throw new Error('Invalid array field');
143
+ if (index === path.length - 1) {
144
+ Reflect.set(target, key, copy(nextValue, false));
145
+ }
146
+ else {
147
+ let child = Object.hasOwn(target, key) ? Reflect.get(target, key) : undefined;
148
+ if (!record(child) && !Array.isArray(child)) {
149
+ child = {};
150
+ Reflect.set(target, key, child);
151
+ }
152
+ target = child;
153
+ }
154
+ }
155
+ return next;
156
+ }
157
+ /** option.value 是索引,不是 String(value):1 与 "1"、null 与 "null" 必须可区分。 */
158
+ export function schemaFormEnumIndex(choices, value) {
159
+ const index = choices.findIndex(choice => choice === value);
160
+ return index === -1 ? '' : String(index);
161
+ }
162
+ export function schemaFormEnumValue(choices, index) {
163
+ if (index === '')
164
+ return undefined;
165
+ if (!/^(0|[1-9][0-9]*)$/u.test(index) || !Object.hasOwn(choices, Number(index)))
166
+ throw new Error('Invalid form choice');
167
+ return choices[Number(index)];
168
+ }
@@ -1,13 +1,16 @@
1
+ import { type ResourceRendering } from '../rendering/index.js';
1
2
  /** Views mount this only when they have an explicit record ID. */
2
3
  export declare function useRecordDetail(options: () => {
3
4
  resourceName: string;
4
5
  id: string | number;
6
+ rendering?: ResourceRendering | undefined;
5
7
  }): {
6
8
  query: import("@tanstack/query-core").QueryObserverResult<import("@svadmin/core").GetOneResult<import("@svadmin/core").BaseRecord & {
7
9
  id: string | number;
8
10
  }>, unknown> & {
9
11
  overtime: import("@svadmin/core").OvertimeResult;
10
12
  };
13
+ readonly rendering: ResourceRendering | undefined;
11
14
  readonly resource: import("@svadmin/core").ResourceDefinition;
12
15
  readonly fields: import("@svadmin/core").FieldDefinition[];
13
16
  readonly canRead: boolean;
@@ -1,20 +1,26 @@
1
+ import { bindResourceRendering } from '../rendering/index.js';
1
2
  import { captureAdminContext, useOne, useCan, useResourceContract } from '@svadmin/core';
2
3
  /** Views mount this only when they have an explicit record ID. */
3
4
  export function useRecordDetail(options) {
4
5
  const context = captureAdminContext();
5
6
  const binding = useResourceContract(() => options().resourceName);
7
+ const rendering = $derived(bindResourceRendering(options().rendering, binding.resource));
6
8
  const resource = $derived(context.getResource(options().resourceName));
7
9
  const permission = useCan(() => ({
8
10
  resource: options().resourceName, action: 'show', id: options().id,
9
11
  queryOptions: { enabled: resource.canShow !== false },
10
12
  }));
11
13
  const canRead = $derived(resource.canShow !== false && permission.allowed === true);
12
- const query = useOne(() => ({
13
- resource: binding.resource, id: options().id, dataProviderName: binding.dataProviderName,
14
- queryOptions: { enabled: canRead },
15
- }));
14
+ const query = useOne(() => {
15
+ void rendering;
16
+ return {
17
+ resource: binding.resource, id: options().id, dataProviderName: binding.dataProviderName,
18
+ queryOptions: { enabled: canRead },
19
+ };
20
+ });
16
21
  return {
17
22
  query,
23
+ get rendering() { return rendering; },
18
24
  get resource() { return resource; },
19
25
  get fields() { return resource.fields.filter(field => field.showInShow !== false); },
20
26
  get canRead() { return canRead; },
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { SvarRecordId } from './svar-grid-operations.js';
3
+ export interface SvarCellContent {
4
+ readonly id: SvarRecordId;
5
+ readonly field: string;
6
+ readonly value: unknown;
7
+ readonly record: Record<string, unknown>;
8
+ }
9
+ export interface SvarCellContext {
10
+ readonly render: () => Snippet<[SvarCellContent]> | undefined;
11
+ readonly resolve: (row: unknown, column: unknown) => SvarCellContent | undefined;
12
+ }
13
+ /** 仅可信宿主可提供原生 snippet;不把引擎 api 或 exec 传给内容组件。 */
14
+ export declare const SVAR_CELL_CONTEXT: unique symbol;
@@ -0,0 +1,2 @@
1
+ /** 仅可信宿主可提供原生 snippet;不把引擎 api 或 exec 传给内容组件。 */
2
+ export const SVAR_CELL_CONTEXT = Symbol('svadmin.svar.cell');
@@ -0,0 +1,80 @@
1
+ /** SVAR 2.7 的窄适配契约。应用注入 Grid,未启用的页面无需安装或加载 SVAR。 */
2
+ export interface SvarColumn {
3
+ readonly key: string;
4
+ readonly label: string;
5
+ readonly width?: number;
6
+ readonly sortable?: boolean;
7
+ /** 首批内建列过滤只支持文本 contains;其他过滤通过资源 filters 传入。 */
8
+ readonly filterable?: boolean;
9
+ }
10
+ export interface SvarSort {
11
+ readonly field: string;
12
+ readonly order: 'asc' | 'desc';
13
+ }
14
+ export interface SvarTextFilter {
15
+ readonly field: string;
16
+ readonly operator: 'contains';
17
+ readonly value: string;
18
+ }
19
+ export interface SvarRow {
20
+ id: string;
21
+ data?: SvarRow[];
22
+ open?: boolean;
23
+ [key: string]: unknown;
24
+ }
25
+ export interface SvarEngineColumn {
26
+ id: string;
27
+ header: string | (string | {
28
+ filter: 'text';
29
+ })[];
30
+ width?: number;
31
+ flexgrow?: number;
32
+ sort: boolean;
33
+ resize: boolean;
34
+ treetoggle: boolean;
35
+ }
36
+ export interface SvarGridApi {
37
+ intercept(action: string, callback: (event: unknown) => false | undefined): void;
38
+ }
39
+ /** 仅使用已核对的上游属性;不暴露任意 editor、template 或事件执行入口。 */
40
+ export interface SvarGridEngineProps {
41
+ /** 上游声明带事件索引;适配层不直接转发任何事件 prop。 */
42
+ [event: `on${string}`]: never;
43
+ columns: SvarEngineColumn[];
44
+ data?: SvarRow[];
45
+ tree?: boolean;
46
+ split?: {
47
+ left?: number;
48
+ };
49
+ sizes?: {
50
+ rowHeight?: number;
51
+ headerHeight?: number;
52
+ columnWidth?: number;
53
+ };
54
+ sortMarks?: Record<string, {
55
+ order: 'asc' | 'desc';
56
+ index?: number;
57
+ }>;
58
+ filterValues?: Record<string, string>;
59
+ select?: boolean;
60
+ multiselect?: boolean;
61
+ reorder?: boolean;
62
+ draggableRows?: boolean;
63
+ undo?: boolean;
64
+ init?: (api: SvarGridApi) => void;
65
+ }
66
+ export declare function svarColumnId(key: string): string;
67
+ export declare function checkedSvarColumns(columns: readonly SvarColumn[]): SvarColumn[];
68
+ export declare function buildSvarColumns(columns: readonly SvarColumn[], tree: boolean): SvarEngineColumn[];
69
+ export declare function projectSvarRows(items: readonly Record<string, unknown>[], columns: readonly SvarColumn[], primaryKey?: string, childrenKey?: string): SvarRow[];
70
+ export declare function nextSvarSort(event: unknown, columns: readonly SvarColumn[], current: readonly SvarSort[]): SvarSort[] | undefined;
71
+ export declare function nextSvarFilter(event: unknown, columns: readonly SvarColumn[], current: readonly SvarTextFilter[]): SvarTextFilter[] | undefined;
72
+ export declare function svarSortMarks(sorters: readonly SvarSort[]): NonNullable<SvarGridEngineProps['sortMarks']>;
73
+ export declare function svarFilterValues(filters: readonly SvarTextFilter[]): Record<string, string>;
74
+ export declare const SVAR_BLOCKED_ACTIONS: readonly ["add-row", "delete-row", "update-row", "update-cell", "copy-row", "move-item", "open-editor", "editor", "undo", "redo", "export-data", "print"];
75
+ export declare function installSvarGuards(api: SvarGridApi, options: {
76
+ current: () => boolean;
77
+ server: () => boolean;
78
+ sort: (event: unknown) => void;
79
+ filter: (event: unknown) => void;
80
+ }): void;
@@ -0,0 +1,159 @@
1
+ const forbidden = new Set(['__proto__', 'prototype', 'constructor']);
2
+ function fieldName(value) {
3
+ return typeof value === 'string' && value.length > 0 && !forbidden.has(value);
4
+ }
5
+ export function svarColumnId(key) {
6
+ return `column:${encodeURIComponent(key)}`;
7
+ }
8
+ export function checkedSvarColumns(columns) {
9
+ if (!Array.isArray(columns) || columns.length === 0)
10
+ throw new Error('At least one grid column is required');
11
+ const ids = new Set();
12
+ return columns.map(column => {
13
+ if (!column || !fieldName(column.key) || typeof column.label !== 'string' || ids.has(column.key)) {
14
+ throw new Error('Invalid or duplicate grid column');
15
+ }
16
+ if (column.width !== undefined && (!Number.isFinite(column.width) || column.width < 40 || column.width > 4096)) {
17
+ throw new Error('Grid column width must be between 40 and 4096');
18
+ }
19
+ ids.add(column.key);
20
+ // 显式投影,拒绝将宿主对象上的 editor/template 等额外属性传入 Grid。
21
+ return {
22
+ key: column.key, label: column.label,
23
+ sortable: column.sortable === true, filterable: column.filterable === true,
24
+ ...(column.width === undefined ? {} : { width: column.width }),
25
+ };
26
+ });
27
+ }
28
+ export function buildSvarColumns(columns, tree) {
29
+ return columns.map((column, index) => ({
30
+ id: svarColumnId(column.key),
31
+ header: column.filterable ? [column.label, { filter: 'text' }] : column.label,
32
+ ...(column.width === undefined ? { flexgrow: 1 } : { width: column.width }),
33
+ sort: column.sortable === true, resize: true, treetoggle: tree && index === 0,
34
+ }));
35
+ }
36
+ function ownValue(record, key) {
37
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
38
+ if (!descriptor)
39
+ return undefined;
40
+ if (!('value' in descriptor))
41
+ throw new Error('Grid records must not contain accessors');
42
+ return descriptor.value;
43
+ }
44
+ function scalar(value) {
45
+ if (value == null)
46
+ return null;
47
+ if (typeof value === 'string' || typeof value === 'boolean')
48
+ return value;
49
+ if (typeof value === 'number' && Number.isFinite(value))
50
+ return value;
51
+ // 复杂字段需要独立格式化;不隐式调用用户对象的 toString/toJSON。
52
+ return '—';
53
+ }
54
+ export function projectSvarRows(items, columns, primaryKey = 'id', childrenKey) {
55
+ if (!Array.isArray(items) || !fieldName(primaryKey) || (childrenKey !== undefined && !fieldName(childrenKey))) {
56
+ throw new Error('Invalid grid data configuration');
57
+ }
58
+ const ids = new Set();
59
+ const ancestors = new Set();
60
+ function visit(rows, depth) {
61
+ if (depth > 64)
62
+ throw new Error('Grid tree is too deep');
63
+ return rows.map(record => {
64
+ if (!record || typeof record !== 'object' || Array.isArray(record) || ancestors.has(record)) {
65
+ throw new Error('Invalid or cyclic grid record');
66
+ }
67
+ const id = ownValue(record, primaryKey);
68
+ if (!(typeof id === 'string' && id.length > 0) && !(typeof id === 'number' && Number.isFinite(id))) {
69
+ throw new Error('Every grid record requires a stable string or finite number ID');
70
+ }
71
+ // SVAR 内部 ID 使用字符串;防止数字 1 与字符串 "1" 相互覆盖。
72
+ const key = `${typeof id === 'number' ? 'n' : 's'}:${id}`;
73
+ if (ids.has(key))
74
+ throw new Error('Duplicate grid record ID');
75
+ ids.add(key);
76
+ const row = { id: key };
77
+ for (const column of columns)
78
+ row[svarColumnId(column.key)] = scalar(ownValue(record, column.key));
79
+ if (childrenKey !== undefined) {
80
+ const children = ownValue(record, childrenKey);
81
+ if (children !== undefined && !Array.isArray(children))
82
+ throw new Error('Grid children must be an array');
83
+ if (Array.isArray(children) && children.length) {
84
+ ancestors.add(record);
85
+ row.data = visit(children, depth + 1);
86
+ ancestors.delete(record);
87
+ row.open = false;
88
+ }
89
+ }
90
+ return row;
91
+ });
92
+ }
93
+ return visit(items, 0);
94
+ }
95
+ function eventRecord(value) {
96
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
97
+ }
98
+ export function nextSvarSort(event, columns, current) {
99
+ const value = eventRecord(event);
100
+ if (!value || (value['order'] !== undefined && value['order'] !== 'asc' && value['order'] !== 'desc')
101
+ || (value['add'] !== undefined && typeof value['add'] !== 'boolean'))
102
+ return undefined;
103
+ const column = columns.find(column => svarColumnId(column.key) === value['key'] && column.sortable);
104
+ if (!column)
105
+ return undefined;
106
+ const existing = current.find(sort => sort.field === column.key);
107
+ const order = value['order'] ?? (existing?.order === 'asc' ? 'desc' : 'asc');
108
+ const next = value['add'] ? current.map(sort => ({ ...sort })) : [];
109
+ const index = next.findIndex(sort => sort.field === column.key);
110
+ const sort = { field: column.key, order };
111
+ if (index < 0)
112
+ next.push(sort);
113
+ else
114
+ next[index] = sort;
115
+ return next;
116
+ }
117
+ export function nextSvarFilter(event, columns, current) {
118
+ const value = eventRecord(event);
119
+ if (!value || (value['value'] != null && typeof value['value'] !== 'string'))
120
+ return undefined;
121
+ const column = columns.find(column => svarColumnId(column.key) === value['key'] && column.filterable);
122
+ if (!column)
123
+ return undefined;
124
+ const next = current.filter(filter => filter.field !== column.key).map(filter => ({ ...filter }));
125
+ if (typeof value['value'] === 'string' && value['value'] !== '') {
126
+ next.push({ field: column.key, operator: 'contains', value: value['value'] });
127
+ }
128
+ return next;
129
+ }
130
+ export function svarSortMarks(sorters) {
131
+ return Object.fromEntries(sorters.map((sort, index) => [svarColumnId(sort.field), { order: sort.order, index }]));
132
+ }
133
+ export function svarFilterValues(filters) {
134
+ return Object.fromEntries(filters.map(filter => [svarColumnId(filter.field), filter.value]));
135
+ }
136
+ export const SVAR_BLOCKED_ACTIONS = [
137
+ 'add-row', 'delete-row', 'update-row', 'update-cell', 'copy-row', 'move-item',
138
+ 'open-editor', 'editor', 'undo', 'redo', 'export-data', 'print',
139
+ ];
140
+ export function installSvarGuards(api, options) {
141
+ for (const action of SVAR_BLOCKED_ACTIONS)
142
+ api.intercept(action, () => false);
143
+ api.intercept('sort-rows', event => {
144
+ if (!options.current())
145
+ return false;
146
+ if (options.server()) {
147
+ options.sort(event);
148
+ return false;
149
+ }
150
+ });
151
+ api.intercept('filter-rows', event => {
152
+ if (!options.current())
153
+ return false;
154
+ if (options.server()) {
155
+ options.filter(event);
156
+ return false;
157
+ }
158
+ });
159
+ }
@@ -0,0 +1,54 @@
1
+ import type { SvarColumn, SvarEngineColumn, SvarGridApi, SvarGridEngineProps, SvarSort, SvarTextFilter } from './svar-grid-contract.js';
2
+ import { svarRecordKey, type SvarRecordId } from './svar-grid-operations.js';
3
+ export type SvarValueFormat = 'plain' | 'number' | 'currency' | 'percent' | 'date' | 'datetime' | 'boolean';
4
+ export interface SvarInteractiveColumn extends SvarColumn {
5
+ readonly editable?: 'text' | 'number';
6
+ readonly format?: SvarValueFormat;
7
+ readonly currency?: string;
8
+ }
9
+ export interface SvarCellEdit {
10
+ readonly id: SvarRecordId;
11
+ readonly field: string;
12
+ readonly value: string | number;
13
+ }
14
+ export interface SvarInteractiveApi extends SvarGridApi {
15
+ on(action: string, callback: (event: unknown) => void): void;
16
+ getState(): {
17
+ selectedRows?: unknown;
18
+ };
19
+ }
20
+ export interface SvarInteractiveEngineProps extends Omit<SvarGridEngineProps, 'init' | 'columns'> {
21
+ columns: (SvarEngineColumn & {
22
+ editor?: 'text';
23
+ template?: (value: unknown) => string;
24
+ })[];
25
+ selectedRows?: (string | number)[];
26
+ init?: (api: SvarInteractiveApi) => void;
27
+ }
28
+ export declare function checkedSvarInteractiveColumns(columns: readonly SvarInteractiveColumn[]): SvarInteractiveColumn[];
29
+ export declare function formatSvarValue(value: unknown, column: SvarInteractiveColumn, locale: string): string;
30
+ export declare function buildSvarInteractiveColumns(columns: readonly SvarInteractiveColumn[], tree: boolean, editable: boolean, locale: string): SvarInteractiveEngineProps['columns'];
31
+ export declare function resolveSvarCell(event: unknown, columns: readonly SvarInteractiveColumn[], index: Map<string, Record<string, unknown>>, primaryKey: string): {
32
+ id: SvarRecordId;
33
+ column: SvarInteractiveColumn;
34
+ } | undefined;
35
+ export declare function parseSvarCellEdit(event: unknown, columns: readonly SvarInteractiveColumn[], index: Map<string, Record<string, unknown>>, primaryKey: string): SvarCellEdit | undefined;
36
+ /** 引擎永远不能自行写业务数据;编辑只生成宿主请求,成功后由查询数据回填。 */
37
+ export declare function installSvarInteractions(api: SvarInteractiveApi, options: {
38
+ current: () => boolean;
39
+ columns: () => readonly SvarInteractiveColumn[];
40
+ items: () => readonly Record<string, unknown>[];
41
+ primaryKey: () => string;
42
+ childrenKey: () => string | undefined;
43
+ server: () => boolean;
44
+ sorters: () => readonly SvarSort[];
45
+ filters: () => readonly SvarTextFilter[];
46
+ sort: (sorters: SvarSort[]) => void;
47
+ filter: (filters: SvarTextFilter[]) => void;
48
+ editable: () => boolean;
49
+ edit: (edit: SvarCellEdit) => void;
50
+ selectable: () => boolean;
51
+ selection: (ids: SvarRecordId[]) => void;
52
+ error: (message: string) => void;
53
+ }): void;
54
+ export { svarRecordKey };