@svadmin/ui 0.70.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 (372) hide show
  1. package/README.md +40 -25
  2. package/THIRD_PARTY_NOTICES.md +52 -0
  3. package/dist/actions.js +1 -1
  4. package/dist/app.css +7804 -5188
  5. package/dist/app.theme.css +7738 -5173
  6. package/dist/class-properties.generated.d.ts +1 -0
  7. package/dist/class-properties.generated.js +2 -0
  8. package/dist/classnames.d.ts +8 -0
  9. package/dist/classnames.js +26 -0
  10. package/dist/component-registry.svelte.d.ts +33 -19
  11. package/dist/component-registry.svelte.js +6 -4
  12. package/dist/components/AdminApp.svelte +55 -53
  13. package/dist/components/AdminApp.svelte.d.ts +2 -2
  14. package/dist/components/ApiSettings.svelte +19 -1
  15. package/dist/components/AuditLogDrawer.svelte +14 -3
  16. package/dist/components/AutoForm.svelte +162 -50
  17. package/dist/components/AutoForm.svelte.d.ts +4 -0
  18. package/dist/components/AutoTable.svelte +599 -259
  19. package/dist/components/AutoTable.svelte.d.ts +6 -0
  20. package/dist/components/BoundRecordDetailDrawer.svelte +172 -0
  21. package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +11 -0
  22. package/dist/components/Breadcrumbs.svelte +7 -4
  23. package/dist/components/CancelTaskButton.svelte +9 -5
  24. package/dist/components/ColumnHeaderFilter.svelte +4 -3
  25. package/dist/components/ColumnSettings.svelte +1 -0
  26. package/dist/components/ComboboxField.svelte +118 -36
  27. package/dist/components/ComboboxField.svelte.d.ts +5 -4
  28. package/dist/components/CreatePage.svelte +7 -1
  29. package/dist/components/CreatePage.svelte.d.ts +2 -0
  30. package/dist/components/DateRangeInput.svelte +75 -0
  31. package/dist/components/DateRangeInput.svelte.d.ts +25 -0
  32. package/dist/components/DateTimeInput.svelte +65 -0
  33. package/dist/components/DateTimeInput.svelte.d.ts +19 -0
  34. package/dist/components/DetailDrawer.svelte +4 -0
  35. package/dist/components/DetailDrawer.svelte.d.ts +1 -0
  36. package/dist/components/DevTools.svelte +8 -21
  37. package/dist/components/DraggableGrid.svelte +1 -0
  38. package/dist/components/DraggableHeader.svelte +5 -9
  39. package/dist/components/DraggableHeader.svelte.d.ts +43 -18
  40. package/dist/components/DraggableRowTable.svelte +2 -2
  41. package/dist/components/DynamicFormList.svelte +17 -17
  42. package/dist/components/DynamicFormList.svelte.d.ts +2 -1
  43. package/dist/components/EditPage.svelte +7 -1
  44. package/dist/components/EditPage.svelte.d.ts +2 -0
  45. package/dist/components/EditableTable.svelte +4 -0
  46. package/dist/components/ErrorPage.svelte +3 -1
  47. package/dist/components/FieldDisplay.svelte +14 -0
  48. package/dist/components/FieldDisplay.svelte.d.ts +7 -0
  49. package/dist/components/FieldRenderer.svelte +96 -20
  50. package/dist/components/FileUpload.svelte +250 -0
  51. package/dist/components/FileUpload.svelte.d.ts +32 -0
  52. package/dist/components/FilterBuilder.svelte +236 -268
  53. package/dist/components/FilterBuilder.svelte.d.ts +3 -0
  54. package/dist/components/FilterDropdown.svelte.d.ts +1 -1
  55. package/dist/components/Header.svelte +5 -6
  56. package/dist/components/ImportWizard.svelte +182 -94
  57. package/dist/components/InfiniteList.svelte +22 -8
  58. package/dist/components/InfiniteList.svelte.d.ts +5 -3
  59. package/dist/components/InlineEdit.svelte +114 -44
  60. package/dist/components/JsonSchemaForm.svelte +256 -95
  61. package/dist/components/JsonSchemaForm.svelte.d.ts +12 -1
  62. package/dist/components/Layout.svelte +20 -17
  63. package/dist/components/LazyTaskQueueDrawer.svelte +26 -0
  64. package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +8 -0
  65. package/dist/components/ListPage.svelte +11 -6
  66. package/dist/components/ListPage.svelte.d.ts +2 -0
  67. package/dist/components/LoginPage.svelte +1 -1
  68. package/dist/components/MasterDetailView.svelte +3 -3
  69. package/dist/components/MediaLibraryModal.svelte +5 -2
  70. package/dist/components/MentionsInput.svelte +4 -3
  71. package/dist/components/MoneyInput.svelte +25 -0
  72. package/dist/components/MoneyInput.svelte.d.ts +20 -0
  73. package/dist/components/NavigateToResource.svelte +3 -2
  74. package/dist/components/NumberInput.svelte +25 -0
  75. package/dist/components/NumberInput.svelte.d.ts +19 -0
  76. package/dist/components/NumericInput.svelte +110 -0
  77. package/dist/components/NumericInput.svelte.d.ts +23 -0
  78. package/dist/components/PercentInput.svelte +25 -0
  79. package/dist/components/PercentInput.svelte.d.ts +20 -0
  80. package/dist/components/PermissionMatrix.svelte +4 -3
  81. package/dist/components/ProfilePage.svelte +2 -2
  82. package/dist/components/QuickEditDrawer.svelte +42 -0
  83. package/dist/components/QuickEditDrawer.svelte.d.ts +10 -0
  84. package/dist/components/RecordDetailDrawer.svelte +16 -108
  85. package/dist/components/RecordDetailDrawer.svelte.d.ts +2 -0
  86. package/dist/components/RecordRowActions.svelte +41 -0
  87. package/dist/components/RecordRowActions.svelte.d.ts +14 -0
  88. package/dist/components/ResourceAccessGuard.svelte +7 -5
  89. package/dist/components/ResourceOperationsPage.svelte +25 -19
  90. package/dist/components/ResourceOperationsPage.svelte.d.ts +5 -1
  91. package/dist/components/RetryTaskButton.svelte +9 -5
  92. package/dist/components/RowActions.svelte +5 -3
  93. package/dist/components/SensitiveDataMask.svelte +3 -3
  94. package/dist/components/SettingsPage.svelte +1 -1
  95. package/dist/components/ShowInferencer.svelte +4 -2
  96. package/dist/components/ShowInferencer.svelte.d.ts +2 -0
  97. package/dist/components/ShowPage.svelte +49 -39
  98. package/dist/components/ShowPage.svelte.d.ts +2 -0
  99. package/dist/components/Sidebar.svelte +15 -13
  100. package/dist/components/SplitPaneLayout.svelte +4 -2
  101. package/dist/components/SpreadsheetView.svelte +81 -286
  102. package/dist/components/StepsForm.svelte +166 -49
  103. package/dist/components/StepsForm.svelte.d.ts +1 -0
  104. package/dist/components/SvarAutoTable.svelte +39 -0
  105. package/dist/components/SvarAutoTable.svelte.d.ts +13 -0
  106. package/dist/components/SvarDataGrid.svelte +362 -0
  107. package/dist/components/SvarDataGrid.svelte.d.ts +57 -0
  108. package/dist/components/SvarGridCell.svelte +10 -0
  109. package/dist/components/SvarGridCell.svelte.d.ts +7 -0
  110. package/dist/components/SvarResourceRead.svelte +50 -0
  111. package/dist/components/SvarResourceRead.svelte.d.ts +23 -0
  112. package/dist/components/SvarResourceTable.svelte +63 -0
  113. package/dist/components/SvarResourceTable.svelte.d.ts +42 -0
  114. package/dist/components/SvarResourceTableView.svelte +364 -0
  115. package/dist/components/SvarResourceTableView.svelte.d.ts +4 -0
  116. package/dist/components/TaskDetails.svelte +20 -7
  117. package/dist/components/TaskList.svelte +19 -7
  118. package/dist/components/TaskQueueDrawer.svelte +92 -37
  119. package/dist/components/Toast.svelte +4 -2
  120. package/dist/components/TooltipButton.svelte +1 -1
  121. package/dist/components/TreeSelect.svelte +212 -30
  122. package/dist/components/TreeSelect.svelte.d.ts +3 -1
  123. package/dist/components/ValidatedField.svelte +27 -0
  124. package/dist/components/ValidatedField.svelte.d.ts +35 -0
  125. package/dist/components/account/ImportMembersPage.svelte +2 -2
  126. package/dist/components/account/SecurityLogPage.svelte +3 -1
  127. package/dist/components/account/SettingsEnterprisePage.svelte +7 -5
  128. package/dist/components/account/TeamMembersPage.svelte +1 -1
  129. package/dist/components/auto-table-grid.d.ts +26 -0
  130. package/dist/components/auto-table-grid.js +1 -0
  131. package/dist/components/buttons/CloneButton.svelte +6 -4
  132. package/dist/components/buttons/DeleteButton.svelte +95 -29
  133. package/dist/components/buttons/DeleteButton.svelte.d.ts +2 -0
  134. package/dist/components/buttons/EditButton.svelte +6 -4
  135. package/dist/components/buttons/ExportButton.svelte +22 -4
  136. package/dist/components/buttons/ImportButton.svelte +83 -38
  137. package/dist/components/buttons/ImportButton.svelte.d.ts +6 -10
  138. package/dist/components/buttons/RefreshButton.svelte +48 -8
  139. package/dist/components/buttons/ShowButton.svelte +6 -4
  140. package/dist/components/content/ApiKeyList.svelte +3 -1
  141. package/dist/components/content/ContentPageHeader.svelte +12 -9
  142. package/dist/components/content/ContentPageShell.svelte +8 -9
  143. package/dist/components/content/FileBrowser.svelte +35 -0
  144. package/dist/components/content/FileBrowser.svelte.d.ts +14 -0
  145. package/dist/components/content/FileList.svelte +3 -1
  146. package/dist/components/content/MemberList.svelte +3 -1
  147. package/dist/components/content/MetricBlock.svelte +10 -13
  148. package/dist/components/content/NetworkTable.svelte +6 -4
  149. package/dist/components/content/NetworkTable.svelte.d.ts +1 -0
  150. package/dist/components/content/PageToolbar.svelte +6 -10
  151. package/dist/components/content/SectionHeader.svelte +8 -13
  152. package/dist/components/content/SecurityEventTable.svelte +3 -1
  153. package/dist/components/content/SettingsFieldRow.svelte +9 -23
  154. package/dist/components/content/SettingsGroup.svelte +11 -27
  155. package/dist/components/content/StatusBadge.svelte +3 -8
  156. package/dist/components/content/StatusTabs.svelte +2 -2
  157. package/dist/components/content/WorkspaceLayout.svelte +8 -12
  158. package/dist/components/enterprise/filter-tree.d.ts +48 -0
  159. package/dist/components/enterprise/filter-tree.js +224 -0
  160. package/dist/components/enterprise/json-schema-form.d.ts +39 -0
  161. package/dist/components/enterprise/json-schema-form.js +195 -0
  162. package/dist/components/enterprise/recursive-schema-form.d.ts +30 -0
  163. package/dist/components/enterprise/recursive-schema-form.js +196 -0
  164. package/dist/components/enterprise/spreadsheet-formula.d.ts +12 -0
  165. package/dist/components/enterprise/spreadsheet-formula.js +245 -0
  166. package/dist/components/fieldComponentMap.d.ts +18 -13
  167. package/dist/components/fieldComponentMap.js +100 -47
  168. package/dist/components/fields/AvatarField.svelte +1 -1
  169. package/dist/components/fields/ChoiceField.svelte +16 -0
  170. package/dist/components/fields/ChoiceField.svelte.d.ts +10 -0
  171. package/dist/components/fields/CodeField.svelte +11 -12
  172. package/dist/components/fields/CodeField.svelte.d.ts +1 -1
  173. package/dist/components/fields/DateField.svelte +4 -3
  174. package/dist/components/fields/DateRangeField.svelte +2 -4
  175. package/dist/components/fields/ImageField.svelte +3 -1
  176. package/dist/components/fields/ImagesField.svelte +23 -0
  177. package/dist/components/fields/ImagesField.svelte.d.ts +6 -0
  178. package/dist/components/fields/JsonField.svelte +19 -13
  179. package/dist/components/fields/PasswordField.svelte +5 -0
  180. package/dist/components/fields/PasswordField.svelte.d.ts +6 -0
  181. package/dist/components/fields/date-field-value.d.ts +2 -0
  182. package/dist/components/fields/date-field-value.js +13 -0
  183. package/dist/components/fields/json-value.d.ts +11 -0
  184. package/dist/components/fields/json-value.js +28 -0
  185. package/dist/components/json-schema-form-state.d.ts +37 -0
  186. package/dist/components/json-schema-form-state.js +168 -0
  187. package/dist/components/profile/ActivityTimeline.svelte +2 -2
  188. package/dist/components/record-detail.svelte.d.ts +19 -0
  189. package/dist/components/record-detail.svelte.js +34 -0
  190. package/dist/components/saved-list-views.js +29 -29
  191. package/dist/components/svar-grid-cells.d.ts +14 -0
  192. package/dist/components/svar-grid-cells.js +2 -0
  193. package/dist/components/svar-grid-contract.d.ts +80 -0
  194. package/dist/components/svar-grid-contract.js +159 -0
  195. package/dist/components/svar-grid-interactions.d.ts +54 -0
  196. package/dist/components/svar-grid-interactions.js +174 -0
  197. package/dist/components/svar-grid-loading.d.ts +40 -0
  198. package/dist/components/svar-grid-loading.js +180 -0
  199. package/dist/components/svar-grid-operations.d.ts +37 -0
  200. package/dist/components/svar-grid-operations.js +186 -0
  201. package/dist/components/svar-grid-panes.d.ts +34 -0
  202. package/dist/components/svar-grid-panes.js +66 -0
  203. package/dist/components/svar-grid-resource-loading.d.ts +18 -0
  204. package/dist/components/svar-grid-resource-loading.js +68 -0
  205. package/dist/components/svar-grid-resource-session.svelte.d.ts +29 -0
  206. package/dist/components/svar-grid-resource-session.svelte.js +61 -0
  207. package/dist/components/table-contract.d.ts +13 -0
  208. package/dist/components/table-contract.js +42 -0
  209. package/dist/components/task-utils.js +4 -2
  210. package/dist/components/ui/alert/alert-variants.d.ts +2 -3
  211. package/dist/components/ui/alert/alert-variants.js +1 -2
  212. package/dist/components/ui/alert/alert.svelte +4 -5
  213. package/dist/components/ui/alert/alert.svelte.d.ts +3 -8
  214. package/dist/components/ui/alert/index.js +0 -2
  215. package/dist/components/ui/avatar/avatar-variants.d.ts +2 -3
  216. package/dist/components/ui/avatar/avatar-variants.js +1 -2
  217. package/dist/components/ui/avatar/avatar.svelte +5 -6
  218. package/dist/components/ui/avatar/avatar.svelte.d.ts +3 -8
  219. package/dist/components/ui/badge/badge-variants.d.ts +2 -3
  220. package/dist/components/ui/badge/badge-variants.js +3 -2
  221. package/dist/components/ui/badge/badge.svelte +4 -5
  222. package/dist/components/ui/badge/badge.svelte.d.ts +3 -8
  223. package/dist/components/ui/badge/index.js +0 -2
  224. package/dist/components/ui/button/button-variants.d.ts +2 -3
  225. package/dist/components/ui/button/button-variants.js +5 -3
  226. package/dist/components/ui/button/button.svelte +100 -44
  227. package/dist/components/ui/button/button.svelte.d.ts +8 -12
  228. package/dist/components/ui/button/index.js +0 -2
  229. package/dist/components/ui/command/command-dialog.svelte +3 -1
  230. package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
  231. package/dist/components/ui/command/command-group.svelte +4 -2
  232. package/dist/components/ui/command/command-item.svelte +4 -2
  233. package/dist/components/ui/command/command-separator.svelte +3 -1
  234. package/dist/components/ui/command/index.d.ts +5 -5
  235. package/dist/components/ui/input/input.svelte +41 -24
  236. package/dist/components/ui/input/input.svelte.d.ts +5 -4
  237. package/dist/components/ui/sheet/sheet.svelte +9 -3
  238. package/dist/components/ui/sheet/sheet.svelte.d.ts +1 -0
  239. package/dist/components/ui/textarea/textarea.svelte +2 -1
  240. package/dist/components/ui/tooltip/tooltip.svelte +4 -2
  241. package/dist/components.css +3079 -0
  242. package/dist/design-contract.d.ts +13 -0
  243. package/dist/design-contract.js +11 -0
  244. package/dist/design-system.d.ts +15 -0
  245. package/dist/design-system.js +7 -0
  246. package/dist/index.d.ts +15 -1
  247. package/dist/index.js +9 -0
  248. package/dist/inline-value.d.ts +5 -0
  249. package/dist/inline-value.js +24 -0
  250. package/dist/numeric-input.d.ts +2 -0
  251. package/dist/numeric-input.js +9 -0
  252. package/dist/page-chrome.css +12 -49
  253. package/dist/recipes.d.ts +4 -0
  254. package/dist/recipes.js +4 -0
  255. package/dist/rendering/index.d.ts +87 -0
  256. package/dist/rendering/index.js +117 -0
  257. package/dist/styled-system/css/conditions.js +36 -0
  258. package/dist/styled-system/css/css.d.ts +22 -0
  259. package/dist/styled-system/css/css.js +45 -0
  260. package/dist/styled-system/css/cva.d.ts +6 -0
  261. package/dist/styled-system/css/cva.js +87 -0
  262. package/dist/styled-system/css/cx.d.ts +5 -0
  263. package/dist/styled-system/css/cx.js +15 -0
  264. package/dist/styled-system/css/index.d.ts +5 -0
  265. package/dist/styled-system/css/index.js +4 -0
  266. package/dist/styled-system/css/sva.d.ts +4 -0
  267. package/dist/styled-system/css/sva.js +46 -0
  268. package/dist/styled-system/helpers.js +316 -0
  269. package/dist/styled-system/patterns/aspect-ratio.d.ts +20 -0
  270. package/dist/styled-system/patterns/aspect-ratio.js +38 -0
  271. package/dist/styled-system/patterns/bleed.d.ts +21 -0
  272. package/dist/styled-system/patterns/bleed.js +24 -0
  273. package/dist/styled-system/patterns/box.d.ts +20 -0
  274. package/dist/styled-system/patterns/box.js +15 -0
  275. package/dist/styled-system/patterns/center.d.ts +20 -0
  276. package/dist/styled-system/patterns/center.js +21 -0
  277. package/dist/styled-system/patterns/circle.d.ts +20 -0
  278. package/dist/styled-system/patterns/circle.js +25 -0
  279. package/dist/styled-system/patterns/container.d.ts +20 -0
  280. package/dist/styled-system/patterns/container.js +21 -0
  281. package/dist/styled-system/patterns/cq.d.ts +21 -0
  282. package/dist/styled-system/patterns/cq.js +21 -0
  283. package/dist/styled-system/patterns/divider.d.ts +22 -0
  284. package/dist/styled-system/patterns/divider.js +25 -0
  285. package/dist/styled-system/patterns/flex.d.ts +26 -0
  286. package/dist/styled-system/patterns/flex.js +26 -0
  287. package/dist/styled-system/patterns/float.d.ts +23 -0
  288. package/dist/styled-system/patterns/float.js +52 -0
  289. package/dist/styled-system/patterns/grid-item.d.ts +25 -0
  290. package/dist/styled-system/patterns/grid-item.js +25 -0
  291. package/dist/styled-system/patterns/grid.d.ts +24 -0
  292. package/dist/styled-system/patterns/grid.js +27 -0
  293. package/dist/styled-system/patterns/hstack.d.ts +21 -0
  294. package/dist/styled-system/patterns/hstack.js +24 -0
  295. package/dist/styled-system/patterns/index.d.ts +21 -0
  296. package/dist/styled-system/patterns/index.js +20 -0
  297. package/dist/styled-system/patterns/link-overlay.d.ts +20 -0
  298. package/dist/styled-system/patterns/link-overlay.js +24 -0
  299. package/dist/styled-system/patterns/spacer.d.ts +20 -0
  300. package/dist/styled-system/patterns/spacer.js +25 -0
  301. package/dist/styled-system/patterns/square.d.ts +20 -0
  302. package/dist/styled-system/patterns/square.js +24 -0
  303. package/dist/styled-system/patterns/stack.d.ts +23 -0
  304. package/dist/styled-system/patterns/stack.js +24 -0
  305. package/dist/styled-system/patterns/visually-hidden.d.ts +20 -0
  306. package/dist/styled-system/patterns/visually-hidden.js +18 -0
  307. package/dist/styled-system/patterns/vstack.d.ts +21 -0
  308. package/dist/styled-system/patterns/vstack.js +24 -0
  309. package/dist/styled-system/patterns/wrap.d.ts +24 -0
  310. package/dist/styled-system/patterns/wrap.js +25 -0
  311. package/dist/styled-system/recipes/content-header.d.ts +31 -0
  312. package/dist/styled-system/recipes/content-header.js +65 -0
  313. package/dist/styled-system/recipes/content-page.d.ts +34 -0
  314. package/dist/styled-system/recipes/content-page.js +43 -0
  315. package/dist/styled-system/recipes/create-recipe.js +82 -0
  316. package/dist/styled-system/recipes/index.d.ts +17 -0
  317. package/dist/styled-system/recipes/index.js +16 -0
  318. package/dist/styled-system/recipes/metric-block.d.ts +34 -0
  319. package/dist/styled-system/recipes/metric-block.js +76 -0
  320. package/dist/styled-system/recipes/product-list.d.ts +31 -0
  321. package/dist/styled-system/recipes/product-list.js +73 -0
  322. package/dist/styled-system/recipes/product-section.d.ts +31 -0
  323. package/dist/styled-system/recipes/product-section.js +49 -0
  324. package/dist/styled-system/recipes/product-settings-row.d.ts +34 -0
  325. package/dist/styled-system/recipes/product-settings-row.js +58 -0
  326. package/dist/styled-system/recipes/product-settings.d.ts +31 -0
  327. package/dist/styled-system/recipes/product-settings.js +57 -0
  328. package/dist/styled-system/recipes/product-status.d.ts +34 -0
  329. package/dist/styled-system/recipes/product-status.js +45 -0
  330. package/dist/styled-system/recipes/product-toolbar.d.ts +31 -0
  331. package/dist/styled-system/recipes/product-toolbar.js +41 -0
  332. package/dist/styled-system/recipes/product-workspace.d.ts +34 -0
  333. package/dist/styled-system/recipes/product-workspace.js +58 -0
  334. package/dist/styled-system/recipes/surface-metric.d.ts +38 -0
  335. package/dist/styled-system/recipes/surface-metric.js +63 -0
  336. package/dist/styled-system/recipes/surface-table.d.ts +34 -0
  337. package/dist/styled-system/recipes/surface-table.js +62 -0
  338. package/dist/styled-system/recipes/ui-badge.d.ts +31 -0
  339. package/dist/styled-system/recipes/ui-badge.js +38 -0
  340. package/dist/styled-system/recipes/ui-button.d.ts +32 -0
  341. package/dist/styled-system/recipes/ui-button.js +43 -0
  342. package/dist/styled-system/recipes/ui-input.d.ts +31 -0
  343. package/dist/styled-system/recipes/ui-input.js +49 -0
  344. package/dist/styled-system/recipes/ui-textarea.d.ts +31 -0
  345. package/dist/styled-system/recipes/ui-textarea.js +24 -0
  346. package/dist/styled-system/styles.css +1257 -0
  347. package/dist/styled-system/tokens/index.d.ts +9 -0
  348. package/dist/styled-system/tokens/index.js +268 -0
  349. package/dist/styled-system/tokens/tokens.d.ts +36 -0
  350. package/dist/styled-system/types/composition.d.ts +227 -0
  351. package/dist/styled-system/types/conditions.d.ts +236 -0
  352. package/dist/styled-system/types/csstype.d.ts +22570 -0
  353. package/dist/styled-system/types/index.d.ts +6 -0
  354. package/dist/styled-system/types/parts.d.ts +8 -0
  355. package/dist/styled-system/types/pattern.d.ts +78 -0
  356. package/dist/styled-system/types/prop-type.d.ts +223 -0
  357. package/dist/styled-system/types/recipe.d.ts +181 -0
  358. package/dist/styled-system/types/selectors.d.ts +59 -0
  359. package/dist/styled-system/types/static-css.d.ts +56 -0
  360. package/dist/styled-system/types/style-props.d.ts +8088 -0
  361. package/dist/styled-system/types/system-types.d.ts +151 -0
  362. package/dist/styles/aliases.css +49 -0
  363. package/dist/styles/compatibility.epilogue.css +384 -0
  364. package/dist/styles/compatibility.prelude.css +5513 -0
  365. package/dist/styles/conditional-states.css +28 -0
  366. package/dist/styles/conditional-utilities.css +22 -0
  367. package/dist/styles/recipes.css +1262 -0
  368. package/dist/types.d.ts +3 -3
  369. package/dist/utils.d.ts +1 -1
  370. package/dist/utils.js +1 -1
  371. package/package.json +38 -14
  372. package/styles-compatibility.json +12 -0
@@ -0,0 +1,364 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy, untrack } from 'svelte';
3
+ import { captureAdminContext, captureAuthSession, useResourceContract, useCan, useList, useUpdate, useDelete, getContractFormFields, type Filter, type Sort } from '@svadmin/core';
4
+ import { useTranslation } from '@svadmin/core/i18n';
5
+ import { snapshotPlainData, decodeBaseRecord } from '@svadmin/core/schema';
6
+ import SvarDataGrid from './SvarDataGrid.svelte';
7
+ import SvarResourceRead from './SvarResourceRead.svelte';
8
+ import { createSvarResourceReads } from './svar-grid-resource-session.svelte.js';
9
+ import { loadSvarResourceWindow, loadSvarResourceChildren, appendSvarResourcePage } from './svar-grid-resource-loading.js';
10
+ import { SvarLoadCancelled, type SvarWindowSource, type SvarChildrenLoader } from './svar-grid-loading.js';
11
+ import type { SvarSort, SvarTextFilter } from './svar-grid-contract.js';
12
+ import type { SvarInteractiveColumn, SvarCellEdit } from './svar-grid-interactions.js';
13
+ import { runSvarBatch, checkedSvarSelection, svarRecordIndex, svarRecordKey, prepareSvarExport, authorizeSvarExport, downloadSvarCsv, type SvarRecordId, type SvarBatchResult } from './svar-grid-operations.js';
14
+ import { readSavedListViews, serializeSavedListViews, savedListViewsStorageKey, type SavedListView, type SavedListViewState } from './saved-list-views.js';
15
+ import type { SvarResourceTableProps } from './SvarResourceTable.svelte';
16
+
17
+ let { Grid, Theme, resourceName, pageSize = 25, filters = [], initialSorters = [], height = 420,
18
+ freezeLeft = 0, freezeRight = 0, loadingMode = 'page', lazyTree, density = 'comfortable', childrenKey: providedChildrenKey, dataScopeKey = 0, disabled = false,
19
+ editable = false, selectable = false, batchUpdate = false, batchDelete = false, exportable = false,
20
+ savedViews = false, preferenceScopeKey, migrateAutoTableViews = false, deleteVariables, formats = {},
21
+ }: SvarResourceTableProps = $props();
22
+ const context = captureAdminContext();
23
+ const i18n = useTranslation();
24
+ const zh = $derived(i18n.locale.startsWith('zh'));
25
+ const binding = useResourceContract(() => resourceName);
26
+ const resource = $derived(context.getResource(resourceName));
27
+ const childrenKey = $derived(lazyTree ? providedChildrenKey ?? 'children' : providedChildrenKey);
28
+ const primaryKey = $derived(resource.primaryKey ?? 'id');
29
+ const permission = useCan(() => ({ resource: resourceName, action: 'list', meta: { svadminSvarScope: dataScopeKey } }));
30
+ const editPermission = useCan(() => ({ resource: resourceName, action: 'edit', meta: { svadminSvarScope: dataScopeKey }, queryOptions: { enabled: editable || batchUpdate } }));
31
+ const deletePermission = useCan(() => ({ resource: resourceName, action: 'delete', meta: { svadminSvarScope: dataScopeKey }, queryOptions: { enabled: batchDelete } }));
32
+ const exportPermission = useCan(() => ({ resource: resourceName, action: 'export', meta: { svadminSvarScope: dataScopeKey }, queryOptions: { enabled: exportable } }));
33
+ const writableFields = $derived.by(() => {
34
+ try { return new Set(getContractFormFields(binding.resource, 'edit')); }
35
+ catch { return new Set<string>(); }
36
+ });
37
+ const fields = $derived(resource.fields.filter(field => field.showInList !== false));
38
+ const editableFields = $derived(fields.filter(field => field.key !== primaryKey && field.key !== 'id'
39
+ && field.showInEdit !== false && writableFields.has(field.key) && ['text', 'number', 'email', 'url', 'textarea'].includes(field.type)));
40
+ let columnVisibility = $state<Record<string, boolean>>({});
41
+ let columnOrder = $state<string[]>([]);
42
+ const allColumns = $derived<SvarInteractiveColumn[]>(fields.map(field => {
43
+ const format = formats[field.key];
44
+ const canEdit = editable && resource.canEdit !== false && editPermission.allowed && editableFields.some(item => item.key === field.key);
45
+ return { key: field.key, label: field.label, sortable: field.sortable === true,
46
+ filterable: field.filterable === true && ['text', 'email', 'url', 'textarea'].includes(field.type),
47
+ ...(canEdit ? { editable: field.type === 'number' ? 'number' as const : 'text' as const } : {}),
48
+ ...(format === undefined ? {} : format) };
49
+ }));
50
+ const columns = $derived([...allColumns].sort((a, b) => {
51
+ const ai = columnOrder.indexOf(a.key), bi = columnOrder.indexOf(b.key);
52
+ return (ai < 0 ? allColumns.length : ai) - (bi < 0 ? allColumns.length : bi);
53
+ }).filter(column => columnVisibility[column.key] !== false));
54
+ let current = $state(1);
55
+ let viewPageSize = $state<number | undefined>(undefined);
56
+ const effectivePageSize = $derived(viewPageSize ?? pageSize);
57
+ let sorters = $state<Sort[]>(untrack(() => initialSorters.map(sort => ({ ...sort }))));
58
+ let textFilters = $state<SvarTextFilter[]>([]);
59
+ let viewFilters = $state<Filter[]>([]);
60
+ let viewSearch = $state('');
61
+ const validPageSize = $derived(Number.isSafeInteger(effectivePageSize) && effectivePageSize >= 1 && effectivePageSize <= 1000);
62
+ const searchable = $derived(resource.fields.filter(field => field.searchable));
63
+ const queryFilters = $derived<Filter[]>([...filters, ...viewFilters, ...textFilters,
64
+ ...(viewSearch && searchable.length ? [{ operator: 'or' as const, value: searchable.map(field => ({ field: field.key, operator: 'contains' as const, value: viewSearch })) }] : [])]);
65
+ const rootFilters = $derived<Filter[]>([...queryFilters, ...(lazyTree ? [{ field: lazyTree.parentField, operator: 'eq' as const, value: lazyTree.rootValue ?? null }] : [])]);
66
+ const validLoading = $derived(['page', 'window', 'infinite'].includes(loadingMode)
67
+ && !(loadingMode !== 'page' && childrenKey !== undefined)
68
+ && (!lazyTree || resource.fields.some(field => field.key === lazyTree.parentField)));
69
+ const queryPagination = $derived({ current, pageSize: effectivePageSize });
70
+ const query = useList({
71
+ get resource() { return binding.resource; }, get dataProviderName() { return binding.dataProviderName; },
72
+ get meta() { return { svadminSvarScope: dataScopeKey }; },
73
+ get queryOptions() { return { enabled: permission.allowed && validPageSize && validLoading && !disabled }; },
74
+ get pagination() { return queryPagination; }, get sorters() { return sorters; }, get filters() { return rootFilters; },
75
+ });
76
+ $effect.pre(() => { void pageSize; void filters; void loadingMode; void lazyTree; untrack(() => { current = 1; viewPageSize = undefined; }); });
77
+ const baseRecords = $derived(permission.allowed && !query.isError ? query.data?.data ?? [] : []);
78
+ let loadedRecords = $state.raw<readonly Record<string, unknown>[]>([]);
79
+ let infiniteRecords = $state.raw<readonly Record<string, unknown>[]>([]);
80
+ let refreshVersion = $state(0);
81
+ let dataVersion = $state(0);
82
+ const advanced = $derived(loadingMode !== 'page' || lazyTree !== undefined);
83
+ const records = $derived(advanced ? loadedRecords : baseRecords);
84
+ const gridItems = $derived(loadingMode === 'window' ? [] : loadingMode === 'infinite' ? infiniteRecords : baseRecords);
85
+ const total = $derived(permission.allowed ? query.data?.total ?? 0 : 0);
86
+ const pageCount = $derived(validPageSize ? Math.max(1, Math.ceil(total / effectivePageSize)) : 1);
87
+ const queryIdentity = $derived(JSON.stringify([resourceName, dataScopeKey, queryPagination, rootFilters, sorters, loadingMode, lazyTree]));
88
+ const error = $derived(query.isError ? (query.error instanceof Error ? query.error.message : zh ? '数据加载失败' : 'Data request failed') : undefined);
89
+ let selectedIds = $state<SvarRecordId[]>([]);
90
+ let busy = $state(false);
91
+ let feedback = $state('');
92
+ let result = $state.raw<SvarBatchResult>();
93
+ let confirmation = $state<'delete' | 'update' | undefined>();
94
+ let batchField = $state('');
95
+ let batchValue = $state('');
96
+ let alive = true;
97
+ let activeId = $state<SvarRecordId>(0);
98
+ let authorizedWrite = $state(false);
99
+ onDestroy(() => { alive = false; authorizedWrite = false; });
100
+ const operationScope = $derived({ resource: binding.resource, provider: context.getDataProviderForResource(resourceName),
101
+ auth: captureAuthSession(context.authProvider), access: context.accessControlProvider,
102
+ tenant: context.tenantCacheKey?.__svadminTenant, dataScopeKey, queryIdentity, refreshVersion, columns,
103
+ permission: permission.allowed, disabled, editable, batchUpdate, batchDelete, exportable });
104
+ const update = useUpdate({ get resource() { return binding.resource; }, get id() { return activeId; }, get enabled() { return alive && authorizedWrite && permission.allowed && !disabled; } });
105
+ const deletion = useDelete({ get resource() { return binding.resource; }, get id() { return activeId; }, get enabled() { return alive && authorizedWrite && permission.allowed && !disabled; } });
106
+ $effect.pre(() => { void queryIdentity; untrack(() => { selectedIds = []; confirmation = undefined; }); });
107
+
108
+ const reads = createSvarResourceReads({
109
+ scope: () => operationScope, current: scopeCurrent,
110
+ authorize: (scope, id) => allowed('list', id, scope),
111
+ resourceName: () => resourceName, dataScopeKey: () => dataScopeKey,
112
+ primaryKey: () => primaryKey, childrenKey: () => childrenKey,
113
+ });
114
+ $effect.pre(() => {
115
+ const root = baseRecords; void queryIdentity; void refreshVersion;
116
+ untrack(() => { infiniteRecords = root; loadedRecords = []; dataVersion++; if (advanced) selectedIds = []; });
117
+ });
118
+ const gridScope = $derived(`${queryIdentity}:${refreshVersion}:${dataVersion}`);
119
+ const windowSource = $derived.by((): SvarWindowSource | undefined => {
120
+ if (loadingMode !== 'window') return undefined;
121
+ // 每次分派捕获当前作用域,不在配置创建时固定暂态作用域。
122
+ void gridScope;
123
+ return { total, load: async ({ start, end, signal }) => {
124
+ const scope = operationScope;
125
+ return loadSvarResourceWindow({ start, end }, effectivePageSize,
126
+ pagination => reads.read({ scope, signal, pagination, filters: [...rootFilters], sorters: [...sorters] }), signal, primaryKey);
127
+ } };
128
+ });
129
+ const loadChildren: SvarChildrenLoader = async ({ id, signal }) => {
130
+ if (!lazyTree || !childrenKey) throw new Error('Lazy tree is not configured');
131
+ const scope = operationScope;
132
+ const branchFilters: Filter[] = [...queryFilters, { field: lazyTree.parentField, operator: 'eq', value: id }];
133
+ return loadSvarResourceChildren(effectivePageSize, lazyTree.maxChildren ?? 10_000,
134
+ pagination => reads.read({ scope, signal, pagination, filters: branchFilters, sorters: [...sorters], parentId: id }), signal, primaryKey, childrenKey);
135
+ };
136
+ async function loadMore({ signal }: { signal: AbortSignal }): Promise<void> {
137
+ const scope = operationScope, before = infiniteRecords;
138
+ if (loadingMode !== 'infinite' || before.length >= total) return;
139
+ if (before.length % effectivePageSize !== 0) throw new Error('Provider returned a truncated page; refresh');
140
+ const page = await reads.read({ scope, signal, pagination: { current: before.length / effectivePageSize + 1, pageSize: effectivePageSize }, filters: [...rootFilters], sorters: [...sorters] });
141
+ if (!scopeCurrent(scope) || signal.aborted || infiniteRecords !== before) throw new SvarLoadCancelled();
142
+ infiniteRecords = appendSvarResourcePage(before, page, total, primaryKey);
143
+ }
144
+ function loaded(next: readonly Record<string, unknown>[]): void {
145
+ loadedRecords = next;
146
+ if (!advanced) return;
147
+ const index = svarRecordIndex(next, primaryKey, childrenKey);
148
+ selectedIds = selectedIds.filter(id => index.has(svarRecordKey(id)));
149
+ }
150
+ function refresh(): void { reads.cancelAll(); refreshVersion++; void query.refetch(); }
151
+
152
+ function selection(next: SvarRecordId[]): void {
153
+ if (busy || disabled || !permission.allowed || query.isFetching) return;
154
+ try { selectedIds = checkedSvarSelection(next); feedback = ''; }
155
+ catch { feedback = zh ? '最多选择 100 条记录' : 'Select at most 100 records'; }
156
+ }
157
+ function selectPage(): void {
158
+ try {
159
+ const ids: SvarRecordId[] = [];
160
+ for (const record of svarRecordIndex(records, primaryKey, childrenKey).values()) {
161
+ const id: unknown = Object.getOwnPropertyDescriptor(record, primaryKey)?.value;
162
+ if (typeof id === 'string' || typeof id === 'number') ids.push(id);
163
+ }
164
+ selection(ids);
165
+ } catch { feedback = zh ? '记录标识无效' : 'Invalid record IDs'; }
166
+ }
167
+ function changeSort(next: SvarSort[]): void { if (!busy && !disabled && permission.allowed) { sorters = next; current = 1; } }
168
+ function changeFilter(next: SvarTextFilter[]): void { if (!busy && !disabled && permission.allowed) { textFilters = next; current = 1; } }
169
+ function scopeCurrent(scope: typeof operationScope): boolean {
170
+ return alive && operationScope === scope && scope.auth.available && scope.auth.isCurrent() && !disabled && permission.allowed;
171
+ }
172
+ async function allowed(action: 'edit' | 'delete' | 'export' | 'list', id: SvarRecordId | undefined, scope: typeof operationScope): Promise<boolean> {
173
+ if (!scopeCurrent(scope)) return false;
174
+ try {
175
+ const meta = context.getProviderMeta(resourceName, { svadminSvarScope: scope.dataScopeKey });
176
+ const decision: unknown = scope.access ? await scope.access.can({ resource: resourceName, action,
177
+ ...(id === undefined ? {} : { params: { id } }), ...(meta === undefined ? {} : { meta }),
178
+ }) : { can: true };
179
+ return scopeCurrent(scope) && decision !== null && typeof decision === 'object'
180
+ && Object.getOwnPropertyDescriptor(decision, 'can')?.value === true;
181
+ } catch { return false; }
182
+ }
183
+ async function execute(action: 'edit' | 'delete', ids: readonly SvarRecordId[], variables?: Record<string, unknown>): Promise<void> {
184
+ if (busy || disabled || !permission.allowed || query.isFetching || query.isError) throw new Error('Unavailable');
185
+ if (action === 'edit' && (!(editable || batchUpdate) || resource.canEdit === false)) throw new Error('Editing disabled');
186
+ if (action === 'delete' && (!batchDelete || resource.canDelete === false)) throw new Error('Deletion disabled');
187
+ const scope = operationScope;
188
+ const input = variables === undefined ? undefined : decodeBaseRecord(snapshotPlainData(variables));
189
+ if (input !== undefined && Object.keys(input).some(key => !editableFields.some(field => field.key === key))) throw new Error('Field is not writable');
190
+ const idsCopy = checkedSvarSelection(ids);
191
+ const index = svarRecordIndex(records, primaryKey, childrenKey);
192
+ if (idsCopy.some(id => !index.has(svarRecordKey(id)))) throw new Error('Selection is no longer loaded');
193
+ const removeInput = snapshotPlainData(deleteVariables ?? {});
194
+ const currentScope = () => scopeCurrent(scope);
195
+ busy = true; feedback = ''; result = undefined; confirmation = undefined;
196
+ try {
197
+ const outcome = await runSvarBatch({ ids: idsCopy, current: currentScope, authorize: id => allowed(action, id, scope),
198
+ async write(id) {
199
+ if (!currentScope()) throw new Error('Scope changed');
200
+ activeId = id; authorizedWrite = true;
201
+ try {
202
+ const options = { dataProviderName: binding.dataProviderName, meta: { svadminSvarScope: scope.dataScopeKey } };
203
+ if (action === 'edit') return await update.mutation.mutateAsync({ variables: input ?? {}, ...options });
204
+ return await deletion.mutation.mutateAsync({ variables: removeInput, ...options });
205
+ } finally { authorizedWrite = false; }
206
+ },
207
+ });
208
+ if (currentScope()) {
209
+ result = outcome;
210
+ const done = new Set(outcome.succeeded.map(svarRecordKey));
211
+ selectedIds = selectedIds.filter(id => !done.has(svarRecordKey(id)));
212
+ }
213
+ if (advanced && currentScope() && outcome.succeeded.length) refresh();
214
+ if (outcome.failed.length || outcome.cancelled) throw new Error('Operation incomplete');
215
+ } finally { if (alive) { busy = false; authorizedWrite = false; } }
216
+ }
217
+ async function editCell(edit: SvarCellEdit): Promise<void> { await execute('edit', [edit.id], { [edit.field]: edit.value }); }
218
+ async function confirmBatch(): Promise<void> {
219
+ const action = confirmation;
220
+ if (!action) return;
221
+ try {
222
+ if (action === 'delete') await execute('delete', selectedIds);
223
+ else {
224
+ const field = editableFields.find(field => field.key === batchField);
225
+ if (!field) throw new Error('Choose a writable field');
226
+ const value = field.type === 'number' ? Number(batchValue) : batchValue;
227
+ if (field.type === 'number' && (!batchValue.trim() || !Number.isFinite(value))) throw new Error('Invalid number');
228
+ await execute('edit', selectedIds, { [field.key]: value });
229
+ }
230
+ } catch { if (alive) feedback = zh ? '操作未全部完成,请检查结果并刷新后再重试' : 'Operation incomplete; inspect the results and refresh before retrying'; }
231
+ }
232
+ async function exportPage(): Promise<void> {
233
+ if (busy || !exportable || !permission.allowed || !exportPermission.allowed || disabled || query.isFetching || query.isError) return;
234
+ const scope = operationScope;
235
+ busy = true; feedback = '';
236
+ try {
237
+ const snapshot = prepareSvarExport(records, columns, primaryKey, childrenKey);
238
+ const csv = await authorizeSvarExport(snapshot, { current: () => scopeCurrent(scope), authorize: (action, id) => allowed(action, id, scope) });
239
+ if (scopeCurrent(scope)) downloadSvarCsv(csv, resourceName);
240
+ } catch { if (alive && operationScope === scope) feedback = zh ? '导出失败或没有导出权限' : 'Export failed or access denied'; }
241
+ finally { if (alive) busy = false; }
242
+ }
243
+
244
+ let views = $state<SavedListView[]>([]);
245
+ let viewName = $state('');
246
+ let activeView = $state('');
247
+ const allowedColumns = $derived(new Set(allColumns.map(column => column.key)));
248
+ const preferencesKey = $derived(preferenceScopeKey ? `svadmin-svar-views-v1:${JSON.stringify([resourceName, binding.dataProviderName, context.tenantCacheKey?.__svadminTenant, preferenceScopeKey])}` : undefined);
249
+ onMount(() => {
250
+ if (!savedViews || !preferencesKey) return;
251
+ try { views = readSavedListViews(localStorage.getItem(preferencesKey), allowedColumns); }
252
+ catch { feedback = zh ? '无法读取保存视图' : 'Saved views unavailable'; }
253
+ });
254
+ function persist(): void {
255
+ if (!preferencesKey) return;
256
+ try { localStorage.setItem(preferencesKey, serializeSavedListViews(views)); }
257
+ catch { feedback = zh ? '视图保存在本次会话中,浏览器存储不可用' : 'View is kept in memory; browser storage is unavailable'; }
258
+ }
259
+ function saveView(): void {
260
+ if (!savedViews || !viewName.trim() || views.length >= 25 || busy || disabled) return;
261
+ const state: SavedListViewState = { search: viewSearch, filters: [...viewFilters, ...textFilters], sorters: [...sorters],
262
+ pagination: { current: 1, pageSize: effectivePageSize }, columnVisibility: { ...columnVisibility }, columnOrder: [...columnOrder] };
263
+ const view = { id: crypto.randomUUID(), name: viewName.trim().slice(0, 60), state };
264
+ views = [...views, view]; activeView = view.id; viewName = ''; persist();
265
+ }
266
+ function applyView(id: string): void {
267
+ if (busy || disabled) return;
268
+ const view = views.find(view => view.id === id);
269
+ if (view && (view.state.sorters.some(sort => !allColumns.some(column => column.key === sort.field && column.sortable))
270
+ || !allColumns.some(column => view.state.columnVisibility[column.key] !== false)
271
+ || (view.state.search !== '' && searchable.length === 0))) {
272
+ feedback = zh ? '该视图与当前资源配置不兼容' : 'View is incompatible with the current resource'; return;
273
+ }
274
+ activeView = id; current = 1; textFilters = []; selectedIds = [];
275
+ if (!view) { viewFilters = []; viewSearch = ''; sorters = [...initialSorters]; columnVisibility = {}; columnOrder = []; viewPageSize = undefined; return; }
276
+ viewFilters = [...view.state.filters]; viewSearch = view.state.search; sorters = [...view.state.sorters];
277
+ columnVisibility = { ...view.state.columnVisibility }; columnOrder = [...view.state.columnOrder]; viewPageSize = view.state.pagination.pageSize;
278
+ }
279
+ function importViews(): void {
280
+ if (!migrateAutoTableViews || busy || disabled) return;
281
+ try {
282
+ const matcher = context.queryKeyMatcher(resourceName);
283
+ const oldKey = savedListViewsStorageKey({ resourceName, providerName: binding.dataProviderName, ...(matcher.tenant === undefined ? {} : { tenantIdentity: matcher.tenant }) });
284
+ const incoming = readSavedListViews(localStorage.getItem(oldKey), allowedColumns);
285
+ const existing = new Set(views.map(view => view.id));
286
+ views = [...views, ...incoming.filter(view => !existing.has(view.id))].slice(0, 25); persist();
287
+ } catch { feedback = zh ? '无法导入视图' : 'Unable to import views'; }
288
+ }
289
+ </script>
290
+
291
+ <section class="svar-resource-table" aria-label={resource.label}>
292
+ <header><h2>{resource.label}</h2>
293
+ <button type="button" disabled={!permission.allowed || !validPageSize || query.isFetching || busy || disabled} onclick={refresh}>{zh ? '刷新' : 'Refresh'}</button>
294
+ {#if exportable}<button type="button" disabled={!permission.allowed || !exportPermission.allowed || busy || disabled || query.isFetching || query.isError} onclick={exportPage}>{advanced ? (zh ? '导出已加载记录 CSV' : 'Export loaded CSV') : (zh ? '导出当前页 CSV' : 'Export page CSV')}</button>{/if}
295
+ </header>
296
+ {#if !validLoading}<p role="alert">{zh ? '高级加载配置无效;窗口和无限加载仅支持平面资源,异步树必须声明父字段' : 'Invalid loading configuration: window/infinite modes require flat resources; lazy trees require a declared parent field'}</p>
297
+ {:else if !validPageSize}<p role="alert">{zh ? '每页数量必须为 1–1000 的整数' : 'Page size must be an integer between 1 and 1000'}</p>
298
+ {:else if permission.isLoading}<p role="status">{zh ? '正在检查权限' : 'Checking access'}</p>
299
+ {:else if !permission.allowed}<p role="alert">{permission.reason ?? (zh ? '没有读取权限' : 'Read access denied')}</p>
300
+ {:else}
301
+ {#each [...reads.requests] as [id, request] (id)}<SvarResourceRead {request} />{/each}
302
+ {#if savedViews}
303
+ <div class="toolbar" aria-label={zh ? '保存视图' : 'Saved views'}>
304
+ <select aria-label={zh ? '当前视图' : 'Current view'} value={activeView} disabled={busy || disabled} onchange={event => applyView(event.currentTarget.value)}>
305
+ <option value="">{zh ? '默认视图' : 'Default view'}</option>{#each views as view (view.id)}<option value={view.id}>{view.name}</option>{/each}
306
+ </select>
307
+ <input aria-label={zh ? '视图名称' : 'View name'} bind:value={viewName} maxlength={60} disabled={busy || disabled} />
308
+ <button type="button" disabled={!viewName.trim() || busy || disabled || views.length >= 25} onclick={saveView}>{zh ? '保存视图' : 'Save view'}</button>
309
+ <button type="button" disabled={!activeView || busy || disabled} onclick={() => { views = views.filter(view => view.id !== activeView); activeView = ''; persist(); }}>{zh ? '删除视图' : 'Delete view'}</button>
310
+ {#if migrateAutoTableViews}<button type="button" disabled={busy || disabled} onclick={importViews}>{zh ? '导入 AutoTable 视图' : 'Import AutoTable views'}</button>{/if}
311
+ <details><summary>{zh ? '列设置' : 'Columns'}</summary>{#each allColumns as column (column.key)}
312
+ <label><input type="checkbox" checked={columnVisibility[column.key] !== false} disabled={busy || disabled || (columns.length === 1 && columnVisibility[column.key] !== false)} onchange={event => { columnVisibility = { ...columnVisibility, [column.key]: event.currentTarget.checked }; }} />{column.label}</label>
313
+ {/each}</details>
314
+ </div>
315
+ {/if}
316
+ {#if selectable || batchUpdate || batchDelete}
317
+ <div class="toolbar" aria-label={zh ? '批量操作' : 'Batch actions'}>
318
+ <button type="button" disabled={busy || disabled || query.isFetching || records.length === 0} onclick={selectPage}>{advanced ? (zh ? '选择已加载记录' : 'Select loaded') : (zh ? '选择当前页' : 'Select page')}</button>
319
+ <button type="button" disabled={busy || disabled || selectedIds.length === 0} onclick={() => { selectedIds = []; }}>{zh ? '清除选择' : 'Clear selection'}</button>
320
+ <span role="status">{zh ? '已选' : 'Selected'}: {selectedIds.length}</span>
321
+ {#if batchUpdate}<button type="button" disabled={!selectedIds.length || busy || disabled || query.isFetching || !editPermission.allowed || resource.canEdit === false} onclick={() => { confirmation = 'update'; }}>{zh ? '批量修改' : 'Update selected'}</button>{/if}
322
+ {#if batchDelete}<button type="button" disabled={!selectedIds.length || busy || disabled || query.isFetching || !deletePermission.allowed || resource.canDelete === false} onclick={() => { confirmation = 'delete'; }}>{zh ? '删除所选' : 'Delete selected'}</button>{/if}
323
+ </div>
324
+ {/if}
325
+ {#if confirmation}<div class="confirmation" role="group" aria-label={zh ? '确认批量操作' : 'Confirm batch action'}>
326
+ <p>{zh ? `确认对所选 ${selectedIds.length} 条记录执行操作?` : `Confirm operation on ${selectedIds.length} selected records?`}</p>
327
+ {#if confirmation === 'update'}
328
+ <select aria-label={zh ? '修改字段' : 'Update field'} bind:value={batchField}><option value="">{zh ? '选择字段' : 'Choose field'}</option>{#each editableFields as field (field.key)}<option value={field.key}>{field.label}</option>{/each}</select>
329
+ <input aria-label={zh ? '新值' : 'New value'} bind:value={batchValue} />
330
+ {/if}
331
+ <button type="button" disabled={busy || disabled || (confirmation === 'update' && !batchField)} onclick={confirmBatch}>{zh ? '确认执行' : 'Confirm action'}</button>
332
+ <button type="button" onclick={() => { confirmation = undefined; }}>{zh ? '取消' : 'Cancel'}</button>
333
+ </div>{/if}
334
+ {#if feedback}<p role="alert">{feedback}</p>{/if}
335
+ {#if result}<p role="status">{zh ? '成功 / 失败 / 未执行' : 'Succeeded / failed / skipped'}: {result.succeeded.length} / {result.failed.length} / {result.skipped.length}</p>{/if}
336
+ <SvarDataGrid {Grid} {Theme} items={gridItems} {columns} {primaryKey} {height} freezeRight={Math.min(freezeRight, Math.max(0, columns.length - 1))} freezeLeft={Math.min(freezeLeft, columns.length - Math.min(freezeRight, Math.max(0, columns.length - 1)))} {density}
337
+ queryMode="server" {sorters} filters={textFilters} onSortChange={changeSort} onFilterChange={changeFilter}
338
+ selectable={selectable || batchUpdate || batchDelete} {selectedIds} onSelectionChange={selection} {...advanced ? { onRecordsChange: loaded } : {}} scopeKey={gridScope}
339
+ {...windowSource ? { windowSource } : {}}
340
+ {...lazyTree ? { loadChildren, hasChildrenKey: lazyTree.hasChildrenKey ?? 'hasChildren' } : {}}
341
+ {...loadingMode === 'infinite' ? { onLoadMore: loadMore, hasMore: infiniteRecords.length < total } : {}}
342
+ {...editable ? { onCellEdit: editCell } : {}} disabled={disabled || busy} locale={i18n.locale}
343
+ loading={query.isLoading} {...error === undefined ? {} : { error }} {...childrenKey === undefined ? {} : { childrenKey }}
344
+ label={resource.label} loadingLabel={zh ? '正在加载' : 'Loading'} emptyLabel={zh ? '暂无记录' : 'No records'}
345
+ disabledLabel={zh ? '暂时不可操作' : 'Grid is disabled'} fallbackLabel={zh ? '静态预览(最多 20 行)' : 'Static preview (up to 20 rows)'} />
346
+ {#if loadingMode === 'page'}<footer aria-label={zh ? '分页' : 'Pagination'}>
347
+ <button type="button" disabled={current <= 1 || query.isFetching || busy || disabled} onclick={() => { current -= 1; }}>{zh ? '上一页' : 'Previous'}</button>
348
+ <span>{current} / {pageCount} · {total} {zh ? '条记录' : 'records'}</span>
349
+ <button type="button" disabled={current >= pageCount || query.isFetching || busy || disabled} onclick={() => { current += 1; }}>{zh ? '下一页' : 'Next'}</button>
350
+ </footer>{:else}<p role="status">{zh ? '已加载 / 总计' : 'Loaded / total'}: {records.length} / {total}</p>{/if}
351
+ {/if}
352
+ </section>
353
+
354
+ <style>
355
+ .svar-resource-table { min-width: 0; color: var(--foreground, CanvasText); }
356
+ header, footer, .toolbar, .confirmation { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; padding: 0.75rem 0; }
357
+ header, footer { justify-content: space-between; }
358
+ h2 { font-size: 1.125rem; margin: 0; }
359
+ button, input, select { max-width: 100%; font: inherit; color: inherit; background: var(--card, Canvas); border: 1px solid var(--border, GrayText); border-radius: var(--radius, 6px); padding: 0.375rem 0.75rem; }
360
+ button { cursor: pointer; } button:focus-visible, input:focus-visible, select:focus-visible { outline: 2px solid var(--ring, Highlight); outline-offset: 2px; }
361
+ button:disabled, input:disabled, select:disabled { opacity: 0.5; cursor: not-allowed; }
362
+ .confirmation { border-block: 1px solid var(--border, GrayText); } .confirmation p { width: 100%; margin: 0; }
363
+ details label { display: block; } [role='alert'] { color: var(--destructive, CanvasText); }
364
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { SvarResourceTableProps } from './SvarResourceTable.svelte';
2
+ declare const SvarResourceTableView: import("svelte").Component<SvarResourceTableProps, {}, "">;
3
+ type SvarResourceTableView = ReturnType<typeof SvarResourceTableView>;
4
+ export default SvarResourceTableView;
@@ -1,5 +1,8 @@
1
1
  <script lang="ts">
2
+ import { definedOptions } from '@svadmin/core/options';
3
+
2
4
  import { useTask, getTaskProvider } from '@svadmin/core';
5
+ import { decodeTaskRecord } from '@svadmin/core/schema';
3
6
  import { useTranslation } from '@svadmin/core/i18n';
4
7
 
5
8
  import type { TaskProvider, TaskRecord } from '@svadmin/core';
@@ -27,7 +30,7 @@
27
30
  let {
28
31
  task,
29
32
  taskId,
30
- taskProvider = getTaskProvider({ optional: true }) as TaskProvider<TaskRecord> | undefined,
33
+ taskProvider = getTaskProvider({ optional: true }) ?? undefined,
31
34
  title,
32
35
  useProviderData = !task && !!taskId,
33
36
  queryOptions,
@@ -59,13 +62,21 @@
59
62
  },
60
63
  });
61
64
 
62
- const resolvedTask = $derived(task ?? query.data);
65
+ const view = $derived.by(() => {
66
+ const value = task === undefined ? query.data : task;
67
+ try {
68
+ return { data: value === undefined ? undefined : decodeTaskRecord(value, taskId), invalid: false };
69
+ } catch {
70
+ return { data: undefined, invalid: true };
71
+ }
72
+ });
73
+ const resolvedTask = $derived(view.data);
63
74
  const resolvedTitle = $derived(title ?? i18n.t('task.detailsTitle'));
64
75
 
65
76
  function formatDate(value: unknown) {
66
- if (!value) return '—';
67
- const date = new Date(value as string | Date);
68
- if (Number.isNaN(date.getTime())) return String(value);
77
+ if (typeof value !== 'string' || !value) return '—';
78
+ const date = new Date(value);
79
+ if (Number.isNaN(date.getTime())) return '—';
69
80
  return date.toLocaleString();
70
81
  }
71
82
 
@@ -90,7 +101,9 @@
90
101
  </Card.Description>
91
102
  </Card.Header>
92
103
  <Card.Content class="svadmin-u-b43b4c086d9a svadmin-u-9335c39f6eff">
93
- {#if useProviderData && taskProvider && query.isLoading}
104
+ {#if view.invalid || (useProviderData && query.isError)}
105
+ <p role="alert">{i18n.t('validation.invalidFormat')}</p>
106
+ {:else if useProviderData && taskProvider && query.isLoading}
94
107
  <div class="svadmin-u-60fbb7713999 svadmin-u-aadad6871af8 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-bfa603190748">
95
108
  <Loader2 class="svadmin-u-d2347e8497a9 svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-afbdd13a380e" />
96
109
  {i18n.t('task.loadingDetail')}
@@ -102,7 +115,7 @@
102
115
  </div>
103
116
 
104
117
  {#if typeof resolveTaskProgress(resolvedTask) === 'number'}
105
- <TaskProgressBar value={resolveTaskProgress(resolvedTask)} />
118
+ <TaskProgressBar {...definedOptions({ "value": resolveTaskProgress(resolvedTask) })} />
106
119
  {/if}
107
120
 
108
121
  <div class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-e4d6f343b9ff">
@@ -1,5 +1,8 @@
1
1
  <script lang="ts">
2
+ import { definedOptions } from '@svadmin/core/options';
3
+
2
4
  import { useTaskList, getTaskProvider } from '@svadmin/core';
5
+ import { decodeTaskList } from '@svadmin/core/schema';
3
6
  import { useTranslation } from '@svadmin/core/i18n';
4
7
 
5
8
  import type { TaskProvider, TaskRecord } from '@svadmin/core';
@@ -24,7 +27,7 @@
24
27
 
25
28
  let {
26
29
  tasks,
27
- taskProvider = getTaskProvider({ optional: true }) as TaskProvider<TaskRecord> | undefined,
30
+ taskProvider = getTaskProvider({ optional: true }) ?? undefined,
28
31
  params,
29
32
  dlq = false,
30
33
  title,
@@ -70,14 +73,21 @@
70
73
  },
71
74
  });
72
75
 
73
- const resolvedTasks = $derived(tasks ?? query.data?.data ?? []);
76
+ const view = $derived.by(() => {
77
+ try {
78
+ return { data: decodeTaskList({ data: tasks === undefined ? query.data?.data ?? [] : tasks }).data, invalid: false };
79
+ } catch {
80
+ return { data: [], invalid: true };
81
+ }
82
+ });
83
+ const resolvedTasks = $derived(view.data);
74
84
  const resolvedTitle = $derived(title ?? (dlq ? i18n.t('task.dlqTitle') : i18n.t('task.listTitle')));
75
85
  const resolvedEmptyText = $derived(emptyText ?? (dlq ? i18n.t('task.noDlq') : i18n.t('task.noTasks')));
76
86
 
77
87
  function formatDate(value: unknown) {
78
- if (!value) return '—';
79
- const date = new Date(value as string | Date);
80
- if (Number.isNaN(date.getTime())) return String(value);
88
+ if (typeof value !== 'string' || !value) return '—';
89
+ const date = new Date(value);
90
+ if (Number.isNaN(date.getTime())) return '—';
81
91
  return date.toLocaleString();
82
92
  }
83
93
  </script>
@@ -94,7 +104,9 @@
94
104
  </Card.Description>
95
105
  </Card.Header>
96
106
  <Card.Content class="svadmin-u-9335c39f6eff">
97
- {#if useProviderData && taskProvider && query.isLoading}
107
+ {#if view.invalid || (useProviderData && query.isError)}
108
+ <p role="alert">{i18n.t('validation.invalidFormat')}</p>
109
+ {:else if useProviderData && taskProvider && query.isLoading}
98
110
  <div class="svadmin-u-60fbb7713999 svadmin-u-aadad6871af8 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-bfa603190748">
99
111
  <Loader2 class="svadmin-u-d2347e8497a9 svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-afbdd13a380e" />
100
112
  {i18n.t('task.loadingList')}
@@ -122,7 +134,7 @@
122
134
  <div class="svadmin-u-359090c2d529 svadmin-u-bfa603190748">{resolveTaskMessage(task)}</div>
123
135
  {/if}
124
136
  {#if showProgress && typeof resolveTaskProgress(task) === 'number'}
125
- <TaskProgressBar value={resolveTaskProgress(task)} />
137
+ <TaskProgressBar {...definedOptions({ "value": resolveTaskProgress(task) })} />
126
138
  {/if}
127
139
  </div>
128
140
  </Table.Cell>