@svadmin/ui 0.72.0 → 0.74.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 (276) hide show
  1. package/README.md +62 -22
  2. package/THIRD_PARTY_NOTICES.md +52 -0
  3. package/dist/app.css +8488 -6297
  4. package/dist/app.theme.css +8514 -6374
  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/component-registry.d.ts +14977 -0
  10. package/dist/component-registry.js +216 -0
  11. package/dist/components/AboutSettings.svelte +7 -1
  12. package/dist/components/AboutSettings.svelte.d.ts +4 -1
  13. package/dist/components/ActivityFeed.svelte +9 -3
  14. package/dist/components/ActivityFeed.svelte.d.ts +3 -0
  15. package/dist/components/AdminApp.svelte +19 -10
  16. package/dist/components/AdminApp.svelte.d.ts +6 -0
  17. package/dist/components/ApiSettings.svelte +41 -5
  18. package/dist/components/ApprovalCenter.svelte +283 -0
  19. package/dist/components/ApprovalCenter.svelte.d.ts +10 -0
  20. package/dist/components/AuditLogViewer.svelte +3 -1
  21. package/dist/components/AutoForm.svelte +53 -9
  22. package/dist/components/AutoForm.svelte.d.ts +3 -0
  23. package/dist/components/AutoTable.svelte +846 -43
  24. package/dist/components/AutoTable.svelte.d.ts +22 -1
  25. package/dist/components/BoundRecordDetailDrawer.svelte +17 -2
  26. package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +4 -0
  27. package/dist/components/Breadcrumbs.svelte +11 -1
  28. package/dist/components/CancelTaskButton.svelte +12 -42
  29. package/dist/components/ColorPicker.svelte +73 -0
  30. package/dist/components/ColorPicker.svelte.d.ts +15 -0
  31. package/dist/components/ComboboxField.svelte +143 -34
  32. package/dist/components/ComboboxField.svelte.d.ts +4 -2
  33. package/dist/components/CreatePage.svelte +4 -0
  34. package/dist/components/CreatePage.svelte.d.ts +2 -0
  35. package/dist/components/DashboardView.svelte +99 -0
  36. package/dist/components/DashboardView.svelte.d.ts +14 -0
  37. package/dist/components/DatePicker.svelte +10 -0
  38. package/dist/components/DatePicker.svelte.d.ts +6 -0
  39. package/dist/components/DateRangeInput.svelte +155 -0
  40. package/dist/components/DateRangeInput.svelte.d.ts +35 -0
  41. package/dist/components/DateRangePicker.svelte +10 -0
  42. package/dist/components/DateRangePicker.svelte.d.ts +6 -0
  43. package/dist/components/DateTimeInput.svelte +134 -0
  44. package/dist/components/DateTimeInput.svelte.d.ts +27 -0
  45. package/dist/components/DateTimePicker.svelte +10 -0
  46. package/dist/components/DateTimePicker.svelte.d.ts +6 -0
  47. package/dist/components/DevTools.svelte +36 -28
  48. package/dist/components/DevframePanel.svelte +123 -0
  49. package/dist/components/DevframePanel.svelte.d.ts +3 -0
  50. package/dist/components/EditPage.svelte +4 -0
  51. package/dist/components/EditPage.svelte.d.ts +2 -0
  52. package/dist/components/ErrorSummary.svelte +52 -0
  53. package/dist/components/ErrorSummary.svelte.d.ts +14 -0
  54. package/dist/components/FieldRenderer.svelte +172 -99
  55. package/dist/components/FileUpload.svelte +412 -0
  56. package/dist/components/FileUpload.svelte.d.ts +43 -0
  57. package/dist/components/FilterBuilder.svelte +244 -265
  58. package/dist/components/FilterBuilder.svelte.d.ts +3 -0
  59. package/dist/components/FilterCollectionInput.svelte +80 -0
  60. package/dist/components/FilterCollectionInput.svelte.d.ts +10 -0
  61. package/dist/components/GanttChart.svelte +66 -14
  62. package/dist/components/GanttChart.svelte.d.ts +6 -0
  63. package/dist/components/Header.svelte +18 -2
  64. package/dist/components/ImportWizard.svelte +509 -100
  65. package/dist/components/ImportWizard.svelte.d.ts +11 -0
  66. package/dist/components/JsonSchemaForm.svelte +322 -98
  67. package/dist/components/JsonSchemaForm.svelte.d.ts +18 -1
  68. package/dist/components/KanbanBoard.svelte +195 -40
  69. package/dist/components/KanbanBoard.svelte.d.ts +9 -2
  70. package/dist/components/Layout.svelte +4 -3
  71. package/dist/components/LazyAutoForm.svelte +28 -0
  72. package/dist/components/LazyAutoForm.svelte.d.ts +8 -0
  73. package/dist/components/LazyAutoTable.svelte +28 -0
  74. package/dist/components/LazyAutoTable.svelte.d.ts +8 -0
  75. package/dist/components/LazySettingsPage.svelte +28 -0
  76. package/dist/components/LazySettingsPage.svelte.d.ts +8 -0
  77. package/dist/components/LazyShowPage.svelte +28 -0
  78. package/dist/components/LazyShowPage.svelte.d.ts +8 -0
  79. package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +1 -1
  80. package/dist/components/ListPage.svelte +9 -1
  81. package/dist/components/ListPage.svelte.d.ts +5 -0
  82. package/dist/components/LoginPage.svelte +2 -2
  83. package/dist/components/MoneyInput.svelte +25 -0
  84. package/dist/components/MoneyInput.svelte.d.ts +20 -0
  85. package/dist/components/MultiTabKeepAlive.svelte +11 -6
  86. package/dist/components/NotificationsSettings.svelte +69 -2
  87. package/dist/components/NotificationsSettings.svelte.d.ts +25 -1
  88. package/dist/components/NumberInput.svelte +25 -0
  89. package/dist/components/NumberInput.svelte.d.ts +19 -0
  90. package/dist/components/NumericInput.svelte +110 -0
  91. package/dist/components/NumericInput.svelte.d.ts +23 -0
  92. package/dist/components/PercentInput.svelte +25 -0
  93. package/dist/components/PercentInput.svelte.d.ts +20 -0
  94. package/dist/components/PermissionMatrix.svelte +20 -1
  95. package/dist/components/PivotExportButton.svelte +157 -0
  96. package/dist/components/PivotExportButton.svelte.d.ts +16 -0
  97. package/dist/components/PivotTable.svelte +229 -74
  98. package/dist/components/PivotTable.svelte.d.ts +19 -2
  99. package/dist/components/QuickEditDrawer.svelte +4 -1
  100. package/dist/components/QuickEditDrawer.svelte.d.ts +2 -0
  101. package/dist/components/RadioGroup.svelte +121 -0
  102. package/dist/components/RadioGroup.svelte.d.ts +43 -0
  103. package/dist/components/RangeSlider.svelte +127 -0
  104. package/dist/components/RangeSlider.svelte.d.ts +22 -0
  105. package/dist/components/Rate.svelte +121 -0
  106. package/dist/components/Rate.svelte.d.ts +16 -0
  107. package/dist/components/RecordDetailDrawer.svelte +6 -2
  108. package/dist/components/RecordDetailDrawer.svelte.d.ts +4 -0
  109. package/dist/components/RelationPicker.svelte +341 -0
  110. package/dist/components/RelationPicker.svelte.d.ts +26 -0
  111. package/dist/components/ResourceOperationsPage.svelte +25 -19
  112. package/dist/components/ResourceOperationsPage.svelte.d.ts +5 -1
  113. package/dist/components/RetryTaskButton.svelte +12 -42
  114. package/dist/components/SecuritySettings.svelte +29 -5
  115. package/dist/components/SegmentedControl.svelte +145 -0
  116. package/dist/components/SegmentedControl.svelte.d.ts +38 -0
  117. package/dist/components/SettingsPage.svelte +3 -1
  118. package/dist/components/SettingsPage.svelte.d.ts +1 -0
  119. package/dist/components/ShowInferencer.svelte +4 -2
  120. package/dist/components/ShowInferencer.svelte.d.ts +2 -0
  121. package/dist/components/ShowPage.svelte +25 -6
  122. package/dist/components/ShowPage.svelte.d.ts +5 -0
  123. package/dist/components/Sidebar.svelte +5 -5
  124. package/dist/components/Slider.svelte +100 -0
  125. package/dist/components/Slider.svelte.d.ts +19 -0
  126. package/dist/components/SpreadsheetView.svelte +1037 -124
  127. package/dist/components/SpreadsheetView.svelte.d.ts +19 -0
  128. package/dist/components/StepsForm.svelte +5 -5
  129. package/dist/components/SvarAutoTable.svelte +39 -0
  130. package/dist/components/SvarAutoTable.svelte.d.ts +13 -0
  131. package/dist/components/SvarDataGrid.svelte +362 -0
  132. package/dist/components/SvarDataGrid.svelte.d.ts +57 -0
  133. package/dist/components/SvarGridCell.svelte +10 -0
  134. package/dist/components/SvarGridCell.svelte.d.ts +7 -0
  135. package/dist/components/SvarResourceRead.svelte +50 -0
  136. package/dist/components/SvarResourceRead.svelte.d.ts +23 -0
  137. package/dist/components/SvarResourceTable.svelte +63 -0
  138. package/dist/components/SvarResourceTable.svelte.d.ts +42 -0
  139. package/dist/components/SvarResourceTableView.svelte +364 -0
  140. package/dist/components/SvarResourceTableView.svelte.d.ts +4 -0
  141. package/dist/components/TagsInput.svelte +178 -0
  142. package/dist/components/TagsInput.svelte.d.ts +17 -0
  143. package/dist/components/TaskQueueDrawer.svelte +72 -16
  144. package/dist/components/TimePicker.svelte +10 -0
  145. package/dist/components/TimePicker.svelte.d.ts +6 -0
  146. package/dist/components/TreeSelect.svelte +440 -46
  147. package/dist/components/TreeSelect.svelte.d.ts +6 -1
  148. package/dist/components/TreeTable.svelte +484 -82
  149. package/dist/components/TreeTable.svelte.d.ts +24 -1
  150. package/dist/components/TwoFactorAuthPage.svelte +85 -48
  151. package/dist/components/TwoFactorAuthPage.svelte.d.ts +12 -1
  152. package/dist/components/VirtualTable.svelte +116 -20
  153. package/dist/components/VirtualTable.svelte.d.ts +5 -0
  154. package/dist/components/account/CompanyProfilePage.svelte +74 -38
  155. package/dist/components/account/GetStartedPage.svelte +2 -1
  156. package/dist/components/account/ImportMembersPage.svelte +22 -167
  157. package/dist/components/account/ImportMembersPage.svelte.d.ts +4 -1
  158. package/dist/components/account/MemberDirectory.svelte +156 -0
  159. package/dist/components/account/MemberDirectory.svelte.d.ts +19 -0
  160. package/dist/components/account/MembersStarterPage.svelte +9 -55
  161. package/dist/components/account/MembersStarterPage.svelte.d.ts +5 -1
  162. package/dist/components/account/SecurityLogPage.svelte +4 -1
  163. package/dist/components/account/SettingsPlainPage.svelte +7 -38
  164. package/dist/components/account/SettingsPlainPage.svelte.d.ts +4 -1
  165. package/dist/components/account/SettingsSidebarPage.svelte +7 -20
  166. package/dist/components/account/TeamMembersPage.svelte +4 -20
  167. package/dist/components/account/TeamMembersPage.svelte.d.ts +5 -1
  168. package/dist/components/account/UserProfilePage.svelte +3 -25
  169. package/dist/components/account/UserProfilePage.svelte.d.ts +17 -2
  170. package/dist/components/auto-table-grid.d.ts +26 -0
  171. package/dist/components/auto-table-grid.js +1 -0
  172. package/dist/components/buttons/ExportButton.svelte +56 -12
  173. package/dist/components/buttons/ExportButton.svelte.d.ts +11 -0
  174. package/dist/components/content/ContentPageHeader.svelte +12 -9
  175. package/dist/components/content/ContentPageShell.svelte +6 -9
  176. package/dist/components/content/FileBrowser.svelte +35 -0
  177. package/dist/components/content/FileBrowser.svelte.d.ts +14 -0
  178. package/dist/components/content/MetricBlock.svelte +10 -13
  179. package/dist/components/content/NetworkTable.svelte +3 -3
  180. package/dist/components/content/NetworkTable.svelte.d.ts +1 -0
  181. package/dist/components/content/PageToolbar.svelte +6 -10
  182. package/dist/components/content/ReferenceNotice.svelte +5 -0
  183. package/dist/components/content/ReferenceNotice.svelte.d.ts +18 -0
  184. package/dist/components/content/SectionHeader.svelte +8 -13
  185. package/dist/components/content/SettingsFieldRow.svelte +9 -23
  186. package/dist/components/content/SettingsGroup.svelte +11 -27
  187. package/dist/components/content/StatusBadge.svelte +3 -8
  188. package/dist/components/content/WorkspaceLayout.svelte +8 -12
  189. package/dist/components/enterprise/filter-tree.d.ts +48 -0
  190. package/dist/components/enterprise/filter-tree.js +224 -0
  191. package/dist/components/enterprise/json-schema-form.d.ts +39 -0
  192. package/dist/components/enterprise/json-schema-form.js +195 -0
  193. package/dist/components/enterprise/recursive-schema-form.d.ts +30 -0
  194. package/dist/components/enterprise/recursive-schema-form.js +196 -0
  195. package/dist/components/enterprise/spreadsheet-formula.d.ts +12 -0
  196. package/dist/components/enterprise/spreadsheet-formula.js +245 -0
  197. package/dist/components/export-reference.d.ts +2 -0
  198. package/dist/components/export-reference.js +9 -0
  199. package/dist/components/fieldComponentMap.js +2 -0
  200. package/dist/components/fields/DateField.svelte +12 -12
  201. package/dist/components/fields/date-display.d.ts +6 -0
  202. package/dist/components/fields/date-display.js +24 -0
  203. package/dist/components/fields/date-field-value.d.ts +2 -0
  204. package/dist/components/fields/date-field-value.js +13 -0
  205. package/dist/components/json-schema-form-state.d.ts +37 -0
  206. package/dist/components/json-schema-form-state.js +168 -0
  207. package/dist/components/network/TeamCrewTablePage.svelte +6 -1
  208. package/dist/components/network/UserCardsNFTPage.svelte +2 -0
  209. package/dist/components/profile/PublicProfilePage.svelte +2 -0
  210. package/dist/components/record-detail.svelte.d.ts +3 -0
  211. package/dist/components/record-detail.svelte.js +10 -4
  212. package/dist/components/saved-list-views.d.ts +74 -1
  213. package/dist/components/saved-list-views.js +191 -3
  214. package/dist/components/svar-grid-cells.d.ts +14 -0
  215. package/dist/components/svar-grid-cells.js +2 -0
  216. package/dist/components/svar-grid-contract.d.ts +80 -0
  217. package/dist/components/svar-grid-contract.js +159 -0
  218. package/dist/components/svar-grid-interactions.d.ts +54 -0
  219. package/dist/components/svar-grid-interactions.js +174 -0
  220. package/dist/components/svar-grid-loading.d.ts +40 -0
  221. package/dist/components/svar-grid-loading.js +180 -0
  222. package/dist/components/svar-grid-operations.d.ts +37 -0
  223. package/dist/components/svar-grid-operations.js +186 -0
  224. package/dist/components/svar-grid-panes.d.ts +34 -0
  225. package/dist/components/svar-grid-panes.js +66 -0
  226. package/dist/components/svar-grid-resource-loading.d.ts +18 -0
  227. package/dist/components/svar-grid-resource-loading.js +68 -0
  228. package/dist/components/svar-grid-resource-session.svelte.d.ts +29 -0
  229. package/dist/components/svar-grid-resource-session.svelte.js +61 -0
  230. package/dist/components/table-contract.d.ts +12 -1
  231. package/dist/components/task-action.svelte.d.ts +13 -0
  232. package/dist/components/task-action.svelte.js +73 -0
  233. package/dist/components/ui/DesignSystemShowcase.svelte +164 -0
  234. package/dist/components/ui/DesignSystemShowcase.svelte.d.ts +3 -0
  235. package/dist/components/ui/badge/badge-variants.d.ts +3 -2
  236. package/dist/components/ui/badge/badge-variants.js +2 -1
  237. package/dist/components/ui/badge/badge.svelte +3 -0
  238. package/dist/components/ui/badge/badge.svelte.d.ts +1 -0
  239. package/dist/components/ui/button/button-variants.d.ts +5 -4
  240. package/dist/components/ui/button/button-variants.js +7 -2
  241. package/dist/components/ui/checkbox/checkbox.svelte +5 -1
  242. package/dist/components/ui/checkbox/checkbox.svelte.d.ts +5 -1
  243. package/dist/components/ui/input/input.svelte +8 -6
  244. package/dist/components/ui/input/input.svelte.d.ts +4 -1
  245. package/dist/components/ui/select/select.svelte +4 -1
  246. package/dist/components/ui/select/select.svelte.d.ts +2 -1
  247. package/dist/components/ui/textarea/textarea.svelte +2 -1
  248. package/dist/components.css +3110 -0
  249. package/dist/date-input-policy.d.ts +4 -0
  250. package/dist/date-input-policy.js +35 -0
  251. package/dist/design-contract.d.ts +13 -0
  252. package/dist/design-contract.js +11 -0
  253. package/dist/design-system-components.d.ts +100 -0
  254. package/dist/design-system-components.js +101 -0
  255. package/dist/design-system.d.ts +15 -0
  256. package/dist/design-system.js +7 -0
  257. package/dist/devtools-bridge.d.ts +80 -0
  258. package/dist/devtools-bridge.js +92 -0
  259. package/dist/index.d.ts +50 -1
  260. package/dist/index.js +32 -2
  261. package/dist/page-chrome.css +12 -49
  262. package/dist/recipes.d.ts +960 -0
  263. package/dist/recipes.js +264 -0
  264. package/dist/rendering/index.d.ts +87 -0
  265. package/dist/rendering/index.js +117 -0
  266. package/dist/styles/aliases.css +49 -0
  267. package/dist/styles/compatibility.epilogue.css +384 -0
  268. package/dist/styles/compatibility.prelude.css +5513 -0
  269. package/dist/styles/conditional-states.css +28 -0
  270. package/dist/styles/conditional-utilities.css +22 -0
  271. package/dist/tree-table-model.d.ts +24 -0
  272. package/dist/tree-table-model.js +111 -0
  273. package/dist/utils.d.ts +1 -1
  274. package/dist/utils.js +1 -1
  275. package/package.json +40 -10
  276. package/styles-compatibility.json +12 -0
@@ -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 };
@@ -0,0 +1,174 @@
1
+ import { checkedSvarColumns, buildSvarColumns, svarColumnId, nextSvarSort, nextSvarFilter, SVAR_BLOCKED_ACTIONS } from './svar-grid-contract.js';
2
+ import { svarRecordIndex, svarRecordKey } from './svar-grid-operations.js';
3
+ export function checkedSvarInteractiveColumns(columns) {
4
+ const checked = checkedSvarColumns(columns);
5
+ const formats = new Set(['plain', 'number', 'currency', 'percent', 'date', 'datetime', 'boolean']);
6
+ return checked.map((column, index) => {
7
+ const source = columns[index];
8
+ if (!source)
9
+ throw new Error('Missing column');
10
+ if (source.editable !== undefined && source.editable !== 'text' && source.editable !== 'number')
11
+ throw new Error('Invalid editor');
12
+ if (source.format !== undefined && !formats.has(source.format))
13
+ throw new Error('Invalid value format');
14
+ if (source.currency !== undefined && !/^[A-Z]{3}$/u.test(source.currency))
15
+ throw new Error('Invalid currency');
16
+ return { ...column,
17
+ ...(source.editable === undefined ? {} : { editable: source.editable }),
18
+ ...(source.format === undefined ? {} : { format: source.format }),
19
+ ...(source.currency === undefined ? {} : { currency: source.currency }),
20
+ };
21
+ });
22
+ }
23
+ export function formatSvarValue(value, column, locale) {
24
+ if (value == null)
25
+ return '—';
26
+ const format = column.format ?? 'plain';
27
+ if (format === 'boolean')
28
+ return value === true ? (locale.startsWith('zh') ? '是' : 'Yes') : value === false ? (locale.startsWith('zh') ? '否' : 'No') : '—';
29
+ if (format === 'number' || format === 'currency' || format === 'percent') {
30
+ if (typeof value !== 'number' || !Number.isFinite(value))
31
+ return '—';
32
+ const options = format === 'currency'
33
+ ? { style: 'currency', currency: column.currency ?? 'USD' }
34
+ : format === 'percent' ? { style: 'percent', maximumFractionDigits: 2 } : {};
35
+ return new Intl.NumberFormat(locale, options).format(value);
36
+ }
37
+ if (format === 'date' || format === 'datetime') {
38
+ if (typeof value !== 'string' && typeof value !== 'number')
39
+ return '—';
40
+ const date = new Date(value);
41
+ if (!Number.isFinite(date.getTime()))
42
+ return '—';
43
+ return new Intl.DateTimeFormat(locale, format === 'date' ? { dateStyle: 'medium', timeZone: 'UTC' } : { dateStyle: 'medium', timeStyle: 'short', timeZone: 'UTC' }).format(date);
44
+ }
45
+ return typeof value === 'string' || typeof value === 'boolean' || (typeof value === 'number' && Number.isFinite(value)) ? String(value) : '—';
46
+ }
47
+ export function buildSvarInteractiveColumns(columns, tree, editable, locale) {
48
+ return buildSvarColumns(columns, tree).map((column, index) => {
49
+ const source = columns[index];
50
+ if (!source)
51
+ throw new Error('Missing column');
52
+ return { ...column,
53
+ ...(source.editable && editable ? { editor: 'text' } : {}),
54
+ ...(source.format ? { template: (value) => formatSvarValue(value, source, locale) } : {}),
55
+ };
56
+ });
57
+ }
58
+ function record(value) {
59
+ return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
60
+ }
61
+ export function resolveSvarCell(event, columns, index, primaryKey) {
62
+ const ev = record(event);
63
+ if (!ev || typeof ev['id'] !== 'string')
64
+ return;
65
+ const source = index.get(ev['id']);
66
+ const column = columns.find(item => svarColumnId(item.key) === ev['column'] && item.editable && item.key !== primaryKey && item.key !== 'id');
67
+ const id = source ? Object.getOwnPropertyDescriptor(source, primaryKey)?.value : undefined;
68
+ if (!column || (typeof id !== 'string' && typeof id !== 'number'))
69
+ return;
70
+ return { id, column };
71
+ }
72
+ export function parseSvarCellEdit(event, columns, index, primaryKey) {
73
+ const ev = record(event);
74
+ const cell = resolveSvarCell(event, columns, index, primaryKey);
75
+ if (!cell || !ev)
76
+ return;
77
+ const raw = ev['value'];
78
+ let value;
79
+ if (cell.column.editable === 'number') {
80
+ if (typeof raw !== 'string' && typeof raw !== 'number')
81
+ throw new Error('Invalid number');
82
+ if (typeof raw === 'string' && !raw.trim())
83
+ throw new Error('A number is required');
84
+ value = Number(raw);
85
+ if (!Number.isFinite(value))
86
+ throw new Error('Invalid number');
87
+ }
88
+ else {
89
+ if (typeof raw !== 'string')
90
+ throw new Error('Text is required');
91
+ value = raw;
92
+ }
93
+ return { id: cell.id, field: cell.column.key, value };
94
+ }
95
+ /** 引擎永远不能自行写业务数据;编辑只生成宿主请求,成功后由查询数据回填。 */
96
+ export function installSvarInteractions(api, options) {
97
+ const index = () => svarRecordIndex(options.items(), options.primaryKey(), options.childrenKey());
98
+ for (const action of SVAR_BLOCKED_ACTIONS) {
99
+ if (!['open-editor', 'editor', 'update-cell'].includes(action))
100
+ api.intercept(action, () => false);
101
+ }
102
+ api.intercept('open-editor', event => {
103
+ if (!options.current() || !options.editable())
104
+ return false;
105
+ try {
106
+ if (!resolveSvarCell(event, options.columns(), index(), options.primaryKey()))
107
+ return false;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ });
113
+ api.intercept('editor', () => { if (!options.current() || !options.editable())
114
+ return false; });
115
+ api.intercept('update-cell', event => {
116
+ if (options.current() && options.editable()) {
117
+ try {
118
+ const edit = parseSvarCellEdit(event, options.columns(), index(), options.primaryKey());
119
+ if (edit)
120
+ options.edit(edit);
121
+ }
122
+ catch (failure) {
123
+ options.error(failure instanceof Error ? failure.message : 'Invalid edit');
124
+ }
125
+ }
126
+ return false;
127
+ });
128
+ api.intercept('select-row', () => { if (!options.current() || !options.selectable())
129
+ return false; });
130
+ api.on('select-row', () => {
131
+ if (!options.current() || !options.selectable())
132
+ return;
133
+ const selected = api.getState().selectedRows;
134
+ if (!Array.isArray(selected))
135
+ return;
136
+ try {
137
+ const records = index();
138
+ const ids = selected.flatMap(key => {
139
+ const source = typeof key === 'string' ? records.get(key) : undefined;
140
+ const id = source ? Object.getOwnPropertyDescriptor(source, options.primaryKey())?.value : undefined;
141
+ return typeof id === 'string' || typeof id === 'number' ? [id] : [];
142
+ });
143
+ if (ids.length > 100) {
144
+ options.error('Select at most 100 records');
145
+ return;
146
+ }
147
+ options.selection(ids);
148
+ }
149
+ catch {
150
+ options.selection([]);
151
+ }
152
+ });
153
+ api.intercept('sort-rows', event => {
154
+ if (!options.current())
155
+ return false;
156
+ if (options.server()) {
157
+ const next = nextSvarSort(event, options.columns(), options.sorters());
158
+ if (next)
159
+ options.sort(next);
160
+ return false;
161
+ }
162
+ });
163
+ api.intercept('filter-rows', event => {
164
+ if (!options.current())
165
+ return false;
166
+ if (options.server()) {
167
+ const next = nextSvarFilter(event, options.columns(), options.filters());
168
+ if (next)
169
+ options.filter(next);
170
+ return false;
171
+ }
172
+ });
173
+ }
174
+ export { svarRecordKey };
@@ -0,0 +1,40 @@
1
+ import { type SvarColumn, type SvarRow } from './svar-grid-contract.js';
2
+ import { type SvarRecordId } from './svar-grid-operations.js';
3
+ export interface SvarRange {
4
+ readonly start: number;
5
+ readonly end: number;
6
+ }
7
+ export interface SvarWindowPage {
8
+ readonly data: readonly Record<string, unknown>[];
9
+ readonly total: number;
10
+ }
11
+ export interface SvarWindowSource {
12
+ readonly total: number;
13
+ /** 可信宿主负责数据契约和后端授权,范围使用从零开始、右端不含的索引。 */
14
+ readonly load: (request: SvarRange & {
15
+ readonly signal: AbortSignal;
16
+ }) => Promise<SvarWindowPage>;
17
+ }
18
+ export type SvarChildrenLoader = (request: {
19
+ readonly id: SvarRecordId;
20
+ readonly signal: AbortSignal;
21
+ }) => Promise<readonly Record<string, unknown>[]>;
22
+ export declare class SvarLoadCancelled extends Error {
23
+ constructor();
24
+ }
25
+ /** 单个网格的有界缓存:同键请求去重,AbortSignal 加请求所有者双重隔离。 */
26
+ export declare function createSvarLoadCache<T>(copy: (value: T) => T, maxReady?: number, maxPending?: number): {
27
+ request: (key: string, load: (signal: AbortSignal) => Promise<T>) => Promise<T>;
28
+ clear: () => void;
29
+ invalidate(key: string): void;
30
+ abortPendingExcept(key: string): void;
31
+ dispose(): void;
32
+ };
33
+ export declare function snapshotSvarRecords(items: readonly Record<string, unknown>[], primaryKey?: string, childrenKey?: string): Record<string, unknown>[];
34
+ export declare function checkedSvarRange(event: unknown, total: number): SvarRange;
35
+ export declare function snapshotSvarWindow(page: SvarWindowPage, range: SvarRange, primaryKey?: string): SvarWindowPage;
36
+ export declare function svarRowId(record: Record<string, unknown>, primaryKey: string): SvarRecordId;
37
+ /** 合并已加载分支但不修改 Provider 记录;最后全树验证,拒绝跨分支重复 ID。 */
38
+ export declare function mergeSvarBranches(items: readonly Record<string, unknown>[], branches: ReadonlyMap<string, readonly Record<string, unknown>[]>, primaryKey: string, childrenKey: string): Record<string, unknown>[];
39
+ /** 占位子节点永远不能被选中或编辑;真正展开前由适配器拦截并加载。 */
40
+ export declare function projectSvarLazyRows(records: readonly Record<string, unknown>[], columns: readonly SvarColumn[], primaryKey: string, childrenKey: string, hasChildrenKey: string, loaded: ReadonlyMap<string, readonly Record<string, unknown>[]>, opened: ReadonlySet<string>): SvarRow[];