@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
@@ -1,8 +1,10 @@
1
1
  <script lang="ts">
2
2
  import { Button } from './ui/button/index.js';
3
3
  import { Badge } from './ui/badge/index.js';
4
- import { Plus, GripVertical } from '@lucide/svelte';
4
+ import { Plus, GripVertical, RotateCw, ArrowUp, ArrowDown } from '@lucide/svelte';
5
5
  import { cn } from '../utils.js';
6
+ import { useTranslation } from '@svadmin/core/i18n';
7
+ import { onDestroy } from 'svelte';
6
8
 
7
9
  export interface KanbanCard {
8
10
  id: string;
@@ -25,9 +27,16 @@
25
27
  columns: KanbanColumn[];
26
28
  cards?: KanbanCard[];
27
29
  allowAddCard?: boolean;
28
- oncardmove?: (cardId: string, targetColumnId: string) => void;
30
+ oncardmove?: (cardId: string, targetColumnId: string, targetIndex: number) => void | Promise<void>;
31
+ canMoveCard?: (card: KanbanCard, targetColumnId: string) => boolean;
32
+ oncardmoveerror?: (error: unknown) => void;
29
33
  oncardclick?: (card: KanbanCard) => void;
30
- oncardadd?: (columnId: string, title: string) => void;
34
+ oncardadd?: (columnId: string, title: string) => KanbanCard | Promise<KanbanCard>;
35
+ scopeKey?: string | number;
36
+ loading?: boolean;
37
+ error?: string | undefined;
38
+ onRetry?: () => void;
39
+ ariaLabel?: string;
31
40
  class?: string;
32
41
  }
33
42
 
@@ -36,16 +45,80 @@
36
45
  cards = $bindable([]),
37
46
  allowAddCard = true,
38
47
  oncardmove,
48
+ canMoveCard,
49
+ oncardmoveerror,
39
50
  oncardclick,
40
51
  oncardadd,
52
+ scopeKey,
53
+ loading = false,
54
+ error,
55
+ onRetry,
56
+ ariaLabel,
41
57
  class: className = '',
42
58
  }: Props = $props();
43
59
 
44
60
  let draggedCardId = $state<string | null>(null);
45
61
  let addingColumnId = $state<string | null>(null);
46
62
  let newCardTitle = $state('');
63
+ let movingCardId = $state<string | null>(null);
64
+ let moveError = $state<string | null>(null);
65
+ let adding = $state(false);
66
+ let mounted = true;
67
+ const i18n = useTranslation();
68
+ const invalidData = $derived.by(() => {
69
+ if (!Array.isArray(columns) || !Array.isArray(cards)) return 'kanban.invalidData';
70
+ if (columns.length > 200 || cards.length > 1000) return 'kanban.limit';
71
+ if (columns.some(column => !column || !validText(column.id) || !validText(column.title))) return 'kanban.invalidData';
72
+ const columnIds = new Set(columns.map(column => column.id));
73
+ if (columnIds.size !== columns.length) return 'kanban.invalidData';
74
+ const cardIds = new Set<string>();
75
+ for (const card of cards) {
76
+ if (!validCard(card) || cardIds.has(card.id) || !columnIds.has(card.columnId)) return 'kanban.invalidData';
77
+ cardIds.add(card.id);
78
+ }
79
+ return '';
80
+ });
81
+ const displayError = $derived(error || (invalidData ? i18n.t(invalidData) : ''));
82
+ const fingerprint = $derived(invalidData ? '' : JSON.stringify([columns.map(column => ({ id: column.id, title: column.title })), cards.map(card => ({
83
+ id: card.id, title: card.title, columnId: card.columnId, description: card.description,
84
+ priority: card.priority, tags: card.tags, assignee: card.assignee?.name, dueDate: card.dueDate,
85
+ }))]));
86
+ const scope = $derived({ scopeKey, cards, columns, fingerprint, loading, error, oncardmove, oncardadd, canMoveCard, allowAddCard });
87
+ let operation = $state.raw<{ scope: typeof scope; preview?: KanbanCard[] }>();
88
+ const currentOperation = $derived(operation?.scope === scope);
89
+ const busy = $derived(currentOperation && (movingCardId !== null || adding));
90
+ const visibleCards = $derived(currentOperation && operation?.preview ? operation.preview : cards);
91
+
92
+ function validText(value: unknown): value is string {
93
+ return typeof value === 'string' && value.trim().length > 0 && value.length <= 1000;
94
+ }
95
+ function validCard(card: KanbanCard): boolean {
96
+ return !!card && validText(card.id) && validText(card.title) && validText(card.columnId)
97
+ && (card.description === undefined || (typeof card.description === 'string' && card.description.length <= 10_000))
98
+ && (card.tags === undefined || (Array.isArray(card.tags) && card.tags.length <= 100 && card.tags.every(validText)))
99
+ && (card.assignee === undefined || (!!card.assignee && validText(card.assignee.name)))
100
+ && (card.dueDate === undefined || typeof card.dueDate === 'string')
101
+ && (card.priority === undefined || ['low', 'medium', 'high', 'urgent'].includes(card.priority));
102
+ }
103
+ function moveAllowed(card: KanbanCard, targetColumn: string): boolean {
104
+ if (!oncardmove || loading || displayError || busy || !columns.some(column => column.id === targetColumn)) return false;
105
+ try { return canMoveCard === undefined || canMoveCard(card, targetColumn) === true; }
106
+ catch { return false; }
107
+ }
108
+ function active(origin: typeof scope): boolean {
109
+ return mounted && origin === scope;
110
+ }
111
+ $effect.pre(() => {
112
+ void scope;
113
+ draggedCardId = null;
114
+ addingColumnId = null;
115
+ newCardTitle = '';
116
+ moveError = null;
117
+ });
118
+ onDestroy(() => { mounted = false; });
47
119
 
48
120
  function handleDragStart(e: DragEvent, cardId: string) {
121
+ if (!oncardmove || busy || loading || displayError) { e.preventDefault(); return; }
49
122
  draggedCardId = cardId;
50
123
  if (e.dataTransfer) {
51
124
  e.dataTransfer.effectAllowed = 'move';
@@ -60,32 +133,65 @@
60
133
  }
61
134
  }
62
135
 
63
- function handleDrop(targetColId: string) {
64
- if (!draggedCardId) return;
65
- const card = cards.find((c) => c.id === draggedCardId);
66
- if (card && card.columnId !== targetColId) {
67
- cards = cards.map((c) =>
68
- c.id === draggedCardId ? { ...c, columnId: targetColId } : c
69
- );
70
- oncardmove?.(draggedCardId, targetColId);
136
+ async function moveCard(cardId: string, targetColId: string, targetIndex = Number.MAX_SAFE_INTEGER) {
137
+ const card = cards.find((c) => c.id === cardId);
138
+ if (!card || !moveAllowed(card, targetColId)) return;
139
+ const source = [...cards];
140
+ const without = source.filter(item => item.id !== card.id);
141
+ const targetCards = without.filter(item => item.columnId === targetColId);
142
+ const index = Math.min(Math.max(0, targetIndex), targetCards.length);
143
+ if (card.columnId === targetColId && source.filter(item => item.columnId === targetColId).findIndex(item => item.id === card.id) === index) return;
144
+ const destination = { ...card, columnId: targetColId };
145
+ const optimistic = [...without.filter(item => item.columnId !== targetColId), ...targetCards.slice(0, index), destination, ...targetCards.slice(index)];
146
+ const changed = source.length !== optimistic.length || source.some((item, i) => item.id !== optimistic[i]?.id || item.columnId !== optimistic[i]?.columnId);
147
+ if (!changed) return;
148
+ const origin = scope;
149
+ const current = { scope: origin, preview: optimistic };
150
+ operation = current;
151
+ adding = false;
152
+ movingCardId = card.id;
153
+ moveError = null;
154
+ try {
155
+ await origin.oncardmove?.(card.id, targetColId, index);
156
+ if (active(origin) && operation === current) cards = optimistic;
157
+ } catch (cause) {
158
+ if (active(origin) && operation === current) {
159
+ moveError = i18n.t('kanban.moveFailed');
160
+ try { oncardmoveerror?.(cause); } catch { /* 错误观察者不能再次触发写入。 */ }
161
+ }
162
+ } finally {
163
+ if (operation === current) { operation = undefined; movingCardId = null; }
71
164
  }
165
+ }
166
+
167
+ function handleDrop(targetColId: string, index?: number) {
168
+ const id = draggedCardId;
72
169
  draggedCardId = null;
170
+ if (id) void moveCard(id, targetColId, index);
73
171
  }
74
172
 
75
- function submitAddCard(columnId: string) {
76
- if (!newCardTitle.trim()) {
173
+ async function submitAddCard(columnId: string) {
174
+ if (!validText(newCardTitle) || !oncardadd || !allowAddCard || busy || loading || displayError || cards.length >= 1000) return;
175
+ const origin = scope;
176
+ const current = { scope: origin };
177
+ operation = current;
178
+ movingCardId = null;
179
+ adding = true;
180
+ moveError = null;
181
+ try {
182
+ const newCard = await origin.oncardadd?.(columnId, newCardTitle.trim());
183
+ if (!active(origin) || operation !== current) return;
184
+ if (!newCard || !validCard(newCard) || newCard.columnId !== columnId || cards.some(card => card.id === newCard.id)) {
185
+ throw new Error('Invalid card receipt');
186
+ }
187
+ cards = [...cards, newCard];
188
+ newCardTitle = '';
77
189
  addingColumnId = null;
78
- return;
190
+ } catch {
191
+ if (active(origin) && operation === current) moveError = i18n.t('kanban.addFailed');
192
+ } finally {
193
+ if (operation === current) { operation = undefined; adding = false; }
79
194
  }
80
- const newCard: KanbanCard = {
81
- id: `k_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
82
- title: newCardTitle.trim(),
83
- columnId,
84
- };
85
- cards = [...cards, newCard];
86
- oncardadd?.(columnId, newCardTitle.trim());
87
- newCardTitle = '';
88
- addingColumnId = null;
89
195
  }
90
196
 
91
197
  function getPriorityClass(priority?: string) {
@@ -102,15 +208,27 @@
102
208
  }
103
209
  </script>
104
210
 
105
- <div class={cn('svadmin-u-60fbb7713999 svadmin-u-0c3bc98565dd svadmin-u-1384f66f41d0 svadmin-u-9fcd8a13827e svadmin-u-359090c2d529 svadmin-u-7f6912283f11', className)}>
211
+ {#if moveError}<div role="alert">{moveError}</div>{/if}
212
+ <div role="region" aria-label={ariaLabel ?? i18n.t('kanban.label')} aria-busy={loading || busy}
213
+ class={cn('svadmin-u-60fbb7713999 svadmin-u-0c3bc98565dd svadmin-u-1384f66f41d0 svadmin-u-9fcd8a13827e svadmin-u-359090c2d529 svadmin-u-7f6912283f11', className)}>
214
+ {#if loading}
215
+ <div role="status">{i18n.t('common.loading')}</div>
216
+ {:else if displayError}
217
+ <div role="alert">{displayError}</div>
218
+ {#if error && onRetry}
219
+ <Button type="button" variant="ghost" size="sm" onclick={onRetry}><RotateCw aria-hidden="true" />{i18n.t('common.retry')}</Button>
220
+ {/if}
221
+ {:else if columns.length === 0}
222
+ <div role="status">{i18n.t('common.noData')}</div>
223
+ {:else}
106
224
  {#each columns as col (col.id)}
107
- {@const colCards = cards.filter((c) => c.columnId === col.id)}
225
+ {@const colCards = visibleCards.filter((c) => c.columnId === col.id)}
108
226
  <div
109
227
  role="region"
110
228
  aria-label={col.title}
111
229
  class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-92e13d146fd7 svadmin-u-012fbd121f37 svadmin-u-a217b4eaa918 svadmin-u-ca6bcd4b6f3f svadmin-u-22bb278694b4 svadmin-u-7f351d50b6e6 svadmin-u-cef5b893cf23 svadmin-u-a4805b5f5b63"
112
230
  ondragover={handleDragOver}
113
- ondrop={() => handleDrop(col.id)}
231
+ ondrop={(event) => { event.preventDefault(); handleDrop(col.id); }}
114
232
  >
115
233
  <!-- Column Header -->
116
234
  <div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-eb6e8b881acd svadmin-u-65fdbade2025 svadmin-u-591f378e24a1">
@@ -121,8 +239,12 @@
121
239
  </span>
122
240
  </div>
123
241
 
124
- {#if allowAddCard}
242
+ {#if allowAddCard && oncardadd}
125
243
  <Button
244
+ type="button"
245
+ disabled={busy || cards.length >= 1000}
246
+ aria-label={i18n.t('kanban.addTo', { column: col.title })}
247
+ title={i18n.t('kanban.addTo', { column: col.title })}
126
248
  variant="ghost"
127
249
  size="sm"
128
250
  class="svadmin-u-f6fe902450dc svadmin-u-7ec10f86d9b1 svadmin-u-8a539c7fe216 svadmin-u-bfa603190748 svadmin-u-ea7b2e9e070e"
@@ -138,22 +260,28 @@
138
260
 
139
261
  <!-- Column Card List -->
140
262
  <div class="svadmin-u-36e579c0b41c svadmin-u-92bf82f493b1 svadmin-u-7660b450905a svadmin-u-6f7e013d6499 svadmin-u-53ecdb131b58">
141
- {#each colCards as card (card.id)}
263
+ {#each colCards as card, cardIndex (card.id)}
142
264
  <div
143
- role="button"
144
- tabindex="0"
145
- draggable="true"
265
+ role="group"
266
+ aria-label={card.title}
267
+ aria-busy={busy && movingCardId === card.id}
268
+ draggable={!!oncardmove && !busy}
146
269
  class={cn(
147
270
  'group svadmin-u-d89972fe17d6 svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-77a2a20e90d4 svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-05faf5c801ff svadmin-u-cd0ad9a56558 svadmin-u-eb6e8b881acd svadmin-u-cef5b893cf23 svadmin-u-0fe7d7d814d0 svadmin-u-8d08385288a6 svadmin-u-d9bff91ef7ee svadmin-u-512e82e6a68f svadmin-u-ab1dd417ce7d svadmin-u-2eba0d65d059',
148
271
  draggedCardId === card.id ? 'svadmin-u-2a2db4667b27 svadmin-u-16b1efa5875e svadmin-u-2691847b25ed' : ''
149
272
  )}
150
273
  ondragstart={(e) => handleDragStart(e, card.id)}
151
- onclick={() => oncardclick?.(card)}
152
- onkeydown={(e) => { if (e.key === 'Enter') oncardclick?.(card); }}
274
+ ondragend={() => { draggedCardId = null; }}
275
+ ondragover={handleDragOver}
276
+ ondrop={(e) => { e.preventDefault(); e.stopPropagation(); handleDrop(col.id, cardIndex); }}
153
277
  >
154
278
  <div class="svadmin-u-60fbb7713999 svadmin-u-60541e1e26f8 svadmin-u-8ef2268efbbc svadmin-u-77a2a20e90d4">
155
- <div class="svadmin-u-2689f3958069 svadmin-u-d4108abe6359 svadmin-u-e25ca6535dea">{card.title}</div>
156
- <GripVertical class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c svadmin-u-52183a1cca53 svadmin-u-7065497e1ca0 svadmin-u-181f3d6c9821 svadmin-u-67d6184a0024 svadmin-u-012fbd121f37" />
279
+ {#if oncardclick}
280
+ <button type="button" disabled={busy} onclick={() => oncardclick?.(card)} class="svadmin-u-2689f3958069 svadmin-u-d4108abe6359 svadmin-u-e25ca6535dea">{card.title}</button>
281
+ {:else}
282
+ <div class="svadmin-u-2689f3958069 svadmin-u-d4108abe6359 svadmin-u-e25ca6535dea">{card.title}</div>
283
+ {/if}
284
+ {#if oncardmove}<GripVertical aria-hidden="true" class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c svadmin-u-52183a1cca53 svadmin-u-7065497e1ca0 svadmin-u-181f3d6c9821 svadmin-u-67d6184a0024 svadmin-u-012fbd121f37" />{/if}
157
285
  </div>
158
286
 
159
287
  {#if card.description}
@@ -167,7 +295,7 @@
167
295
  {card.priority}
168
296
  </Badge>
169
297
  {/if}
170
- {#each card.tags ?? [] as tag (tag)}
298
+ {#each card.tags ?? [] as tag, tagIndex (tagIndex)}
171
299
  <span class="svadmin-u-07389a777c1f svadmin-u-706701550477 svadmin-u-d8e0e382c67b svadmin-u-465609a240a8 svadmin-u-bfa603190748">#{tag}</span>
172
300
  {/each}
173
301
  </div>
@@ -181,18 +309,44 @@
181
309
  </div>
182
310
  {/if}
183
311
  </div>
312
+ {#if oncardmove}
313
+ <div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4">
314
+ <select aria-label={i18n.t('kanban.move', { card: card.title })} value={card.columnId} disabled={busy}
315
+ style="min-width: 0; max-width: 100%;"
316
+ onchange={(event) => {
317
+ const target = event.currentTarget.value;
318
+ event.currentTarget.value = card.columnId;
319
+ void moveCard(card.id, target);
320
+ }}>
321
+ {#each columns as destination (destination.id)}
322
+ <option value={destination.id} disabled={destination.id !== card.columnId && !moveAllowed(card, destination.id)}>{destination.title}</option>
323
+ {/each}
324
+ </select>
325
+ <Button type="button" variant="ghost" size="icon-sm" title={i18n.t('common.moveUp')}
326
+ aria-label={i18n.t('common.moveUp')} disabled={cardIndex === 0 || !moveAllowed(card, col.id)}
327
+ onclick={() => void moveCard(card.id, col.id, cardIndex - 1)}><ArrowUp aria-hidden="true" /></Button>
328
+ <Button type="button" variant="ghost" size="icon-sm" title={i18n.t('common.moveDown')}
329
+ aria-label={i18n.t('common.moveDown')} disabled={cardIndex === colCards.length - 1 || !moveAllowed(card, col.id)}
330
+ onclick={() => void moveCard(card.id, col.id, cardIndex + 1)}><ArrowDown aria-hidden="true" /></Button>
331
+ </div>
332
+ {/if}
184
333
  </div>
334
+ {:else}
335
+ <div role="status">{i18n.t('common.noData')}</div>
185
336
  {/each}
186
337
 
187
338
  {#if addingColumnId === col.id}
188
339
  <div class="svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-f5583b2907be svadmin-u-cd0ad9a56558 svadmin-u-9fe52d5d506c svadmin-u-6f7e013d6499 svadmin-u-cef5b893cf23">
189
340
  <textarea
190
341
  bind:value={newCardTitle}
191
- placeholder="Card title..."
342
+ aria-label={i18n.t('kanban.cardTitle')}
343
+ placeholder={i18n.t('kanban.cardTitle')}
344
+ maxlength={1000}
345
+ disabled={busy}
192
346
  rows={2}
193
347
  class="svadmin-u-6da6a3c3f741 svadmin-u-07389a777c1f svadmin-u-ca6bcd4b6f3f svadmin-u-e5795dad4d22 svadmin-u-e6f9e383a762 svadmin-u-cd009d7d208c svadmin-u-359090c2d529 svadmin-u-d4108abe6359 svadmin-u-9c24ab70af61 svadmin-u-f10f771f87e9 svadmin-u-3e94a98e1466 svadmin-u-9c1295a6914a svadmin-u-6aef32016896"
194
348
  onkeydown={(e) => {
195
- if (e.key === 'Enter' && !e.shiftKey) {
349
+ if (e.key === 'Enter' && !e.shiftKey && !e.isComposing && !e.repeat) {
196
350
  e.preventDefault();
197
351
  submitAddCard(col.id);
198
352
  } else if (e.key === 'Escape') {
@@ -201,12 +355,13 @@
201
355
  }}
202
356
  ></textarea>
203
357
  <div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-77c08e015d14 svadmin-u-58284b4ea568">
204
- <Button variant="ghost" size="sm" class="svadmin-u-f6fe902450dc svadmin-u-d058ca6de60f svadmin-u-d5eab218aa34" onclick={() => { addingColumnId = null; }}>Cancel</Button>
205
- <Button size="sm" class="svadmin-u-f6fe902450dc svadmin-u-d058ca6de60f svadmin-u-d5eab218aa34" onclick={() => submitAddCard(col.id)}>Add</Button>
358
+ <Button type="button" disabled={busy} variant="ghost" size="sm" class="svadmin-u-f6fe902450dc svadmin-u-d058ca6de60f svadmin-u-d5eab218aa34" onclick={() => { addingColumnId = null; }}>{i18n.t('common.cancel')}</Button>
359
+ <Button type="button" disabled={busy || !validText(newCardTitle)} size="sm" class="svadmin-u-f6fe902450dc svadmin-u-d058ca6de60f svadmin-u-d5eab218aa34" onclick={() => submitAddCard(col.id)}>{i18n.t('common.add')}</Button>
206
360
  </div>
207
361
  </div>
208
362
  {/if}
209
363
  </div>
210
364
  </div>
211
365
  {/each}
366
+ {/if}
212
367
  </div>
@@ -20,9 +20,16 @@ interface Props {
20
20
  columns: KanbanColumn[];
21
21
  cards?: KanbanCard[];
22
22
  allowAddCard?: boolean;
23
- oncardmove?: (cardId: string, targetColumnId: string) => void;
23
+ oncardmove?: (cardId: string, targetColumnId: string, targetIndex: number) => void | Promise<void>;
24
+ canMoveCard?: (card: KanbanCard, targetColumnId: string) => boolean;
25
+ oncardmoveerror?: (error: unknown) => void;
24
26
  oncardclick?: (card: KanbanCard) => void;
25
- oncardadd?: (columnId: string, title: string) => void;
27
+ oncardadd?: (columnId: string, title: string) => KanbanCard | Promise<KanbanCard>;
28
+ scopeKey?: string | number;
29
+ loading?: boolean;
30
+ error?: string | undefined;
31
+ onRetry?: () => void;
32
+ ariaLabel?: string;
26
33
  class?: string;
27
34
  }
28
35
  declare const KanbanBoard: import("svelte").Component<Props, {}, "cards">;
@@ -203,9 +203,9 @@
203
203
 
204
204
  <div
205
205
  class="svadmin-u-36e579c0b41c svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-2cd02d11d1af svadmin-u-0fe7d7d814d0 svadmin-u-7890552ecd63"
206
- class:md:ml-[252px]={!collapsed}
206
+ class:svadmin-sidebar-content-expanded={!collapsed}
207
207
  class:sidebar-content-expanded={!collapsed}
208
- class:md:ml-[70px]={collapsed}
208
+ class:svadmin-sidebar-content-collapsed={collapsed}
209
209
  class:sidebar-content-collapsed={collapsed}
210
210
  >
211
211
  <!-- Header with mobile hamburger -->
@@ -244,7 +244,8 @@
244
244
  <main id={mainContentId} tabindex="-1" data-svadmin-main class="svadmin-u-36e579c0b41c svadmin-u-92bf82f493b1 svadmin-u-2859c861d7de svadmin-u-f0faeb26d656 svadmin-u-c9b99cd93450 svadmin-u-cc06a6575385 svadmin-u-daf5dc5fac2b svadmin-u-e86fadb84483">
245
245
  <div class="svadmin-u-0e12dc7de920 svadmin-u-6da6a3c3f741 svadmin-u-9e3dc30c26a3">
246
246
  {#key getPath()}
247
- <div in:fly={{ x: 20, duration: 150 }} out:fade={{ duration: 80 }}>
247
+ <!-- 路由切换立即释放旧页面,避免退出期间继续读取已失效的路由派生状态。 -->
248
+ <div in:fly={{ x: 20, duration: 150 }}>
248
249
  {@render children()}
249
250
  </div>
250
251
  {/key}
@@ -0,0 +1,28 @@
1
+ <script module lang="ts">
2
+ import type { ComponentProps } from 'svelte';
3
+ import type AutoForm from './AutoForm.svelte';
4
+ export interface LazyAutoFormProps extends ComponentProps<typeof AutoForm> {
5
+ resourceName: ComponentProps<typeof AutoForm>['resourceName'];
6
+ }
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { useTranslation } from '@svadmin/core/i18n';
11
+
12
+ let props: LazyAutoFormProps = $props();
13
+ const i18n = useTranslation();
14
+ let page = $state.raw(import('./AutoForm.svelte'));
15
+ </script>
16
+
17
+ {#await page}
18
+ <p role="status" aria-live="polite">{i18n.t('common.loading')}</p>
19
+ {:then module}
20
+ <module.default {...props} />
21
+ {:catch}
22
+ <div role="alert">
23
+ <p>{i18n.t('common.operationFailed')}</p>
24
+ <button type="button" onclick={() => page = import('./AutoForm.svelte')}>
25
+ {i18n.t('common.retry')}
26
+ </button>
27
+ </div>
28
+ {/await}
@@ -0,0 +1,8 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import type AutoForm from './AutoForm.svelte';
3
+ export interface LazyAutoFormProps extends ComponentProps<typeof AutoForm> {
4
+ resourceName: ComponentProps<typeof AutoForm>['resourceName'];
5
+ }
6
+ declare const LazyAutoForm: import("svelte").Component<LazyAutoFormProps, {}, "">;
7
+ type LazyAutoForm = ReturnType<typeof LazyAutoForm>;
8
+ export default LazyAutoForm;
@@ -0,0 +1,28 @@
1
+ <script module lang="ts">
2
+ import type { ComponentProps } from 'svelte';
3
+ import type AutoTable from './AutoTable.svelte';
4
+ export interface LazyAutoTableProps extends ComponentProps<typeof AutoTable> {
5
+ resourceName: ComponentProps<typeof AutoTable>['resourceName'];
6
+ }
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { useTranslation } from '@svadmin/core/i18n';
11
+
12
+ let props: LazyAutoTableProps = $props();
13
+ const i18n = useTranslation();
14
+ let page = $state.raw(import('./AutoTable.svelte'));
15
+ </script>
16
+
17
+ {#await page}
18
+ <p role="status" aria-live="polite">{i18n.t('common.loading')}</p>
19
+ {:then module}
20
+ <module.default {...props} />
21
+ {:catch}
22
+ <div role="alert">
23
+ <p>{i18n.t('common.operationFailed')}</p>
24
+ <button type="button" onclick={() => page = import('./AutoTable.svelte')}>
25
+ {i18n.t('common.retry')}
26
+ </button>
27
+ </div>
28
+ {/await}
@@ -0,0 +1,8 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import type AutoTable from './AutoTable.svelte';
3
+ export interface LazyAutoTableProps extends ComponentProps<typeof AutoTable> {
4
+ resourceName: ComponentProps<typeof AutoTable>['resourceName'];
5
+ }
6
+ declare const LazyAutoTable: import("svelte").Component<LazyAutoTableProps, {}, "">;
7
+ type LazyAutoTable = ReturnType<typeof LazyAutoTable>;
8
+ export default LazyAutoTable;
@@ -0,0 +1,28 @@
1
+ <script module lang="ts">
2
+ import type { ComponentProps } from 'svelte';
3
+ import type SettingsPage from './SettingsPage.svelte';
4
+ export interface LazySettingsPageProps extends ComponentProps<typeof SettingsPage> {
5
+ preferencesProvider?: NonNullable<ComponentProps<typeof SettingsPage>['preferencesProvider']>;
6
+ }
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { useTranslation } from '@svadmin/core/i18n';
11
+
12
+ let props: LazySettingsPageProps = $props();
13
+ const i18n = useTranslation();
14
+ let page = $state.raw(import('./SettingsPage.svelte'));
15
+ </script>
16
+
17
+ {#await page}
18
+ <p role="status" aria-live="polite">{i18n.t('common.loading')}</p>
19
+ {:then module}
20
+ <module.default {...props} />
21
+ {:catch}
22
+ <div role="alert">
23
+ <p>{i18n.t('common.operationFailed')}</p>
24
+ <button type="button" onclick={() => page = import('./SettingsPage.svelte')}>
25
+ {i18n.t('common.retry')}
26
+ </button>
27
+ </div>
28
+ {/await}
@@ -0,0 +1,8 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import type SettingsPage from './SettingsPage.svelte';
3
+ export interface LazySettingsPageProps extends ComponentProps<typeof SettingsPage> {
4
+ preferencesProvider?: NonNullable<ComponentProps<typeof SettingsPage>['preferencesProvider']>;
5
+ }
6
+ declare const LazySettingsPage: import("svelte").Component<LazySettingsPageProps, {}, "">;
7
+ type LazySettingsPage = ReturnType<typeof LazySettingsPage>;
8
+ export default LazySettingsPage;
@@ -0,0 +1,28 @@
1
+ <script module lang="ts">
2
+ import type { ComponentProps } from 'svelte';
3
+ import type ShowPage from './ShowPage.svelte';
4
+ export interface LazyShowPageProps extends ComponentProps<typeof ShowPage> {
5
+ resourceName: ComponentProps<typeof ShowPage>['resourceName'];
6
+ }
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { useTranslation } from '@svadmin/core/i18n';
11
+
12
+ let props: LazyShowPageProps = $props();
13
+ const i18n = useTranslation();
14
+ let page = $state.raw(import('./ShowPage.svelte'));
15
+ </script>
16
+
17
+ {#await page}
18
+ <p role="status" aria-live="polite">{i18n.t('common.loading')}</p>
19
+ {:then module}
20
+ <module.default {...props} />
21
+ {:catch}
22
+ <div role="alert">
23
+ <p>{i18n.t('common.operationFailed')}</p>
24
+ <button type="button" onclick={() => page = import('./ShowPage.svelte')}>
25
+ {i18n.t('common.retry')}
26
+ </button>
27
+ </div>
28
+ {/await}
@@ -0,0 +1,8 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import type ShowPage from './ShowPage.svelte';
3
+ export interface LazyShowPageProps extends ComponentProps<typeof ShowPage> {
4
+ resourceName: ComponentProps<typeof ShowPage>['resourceName'];
5
+ }
6
+ declare const LazyShowPage: import("svelte").Component<LazyShowPageProps, {}, "">;
7
+ type LazyShowPage = ReturnType<typeof LazyShowPage>;
8
+ export default LazyShowPage;
@@ -2,7 +2,7 @@ declare const LazyTaskQueueDrawer: import("svelte").Component<{
2
2
  open?: boolean;
3
3
  taskProvider?: import("@svadmin/core").TaskProvider<import("@svadmin/core").TaskRecord>;
4
4
  title?: string;
5
- initialTab?: "dlq" | "tasks";
5
+ initialTab?: "tasks" | "dlq";
6
6
  }, {}, "open">;
7
7
  type LazyTaskQueueDrawer = ReturnType<typeof LazyTaskQueueDrawer>;
8
8
  export default LazyTaskQueueDrawer;
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import type { ResourceRendering } from '../rendering/index.js';
2
3
  import { definedOptions } from '@svadmin/core/options';
3
4
 
4
5
  import { getResource } from '@svadmin/core';
@@ -7,15 +8,18 @@
7
8
  import type { FieldDefinition } from '@svadmin/core';
8
9
  import PageHeader from './PageHeader.svelte';
9
10
  import AutoTable from './AutoTable.svelte';
11
+ import type { BatchSelection } from './table-contract';
10
12
  import CreateButton from './buttons/CreateButton.svelte';
11
13
 
12
14
  interface Props {
13
15
  resourceName: string;
16
+ rendering?: ResourceRendering | undefined;
14
17
  title?: string;
15
18
  canCreate?: boolean;
16
19
  density?: 'compact' | 'comfortable';
17
20
  /** Passthrough: enable row selection checkboxes */
18
21
  selectable?: boolean;
22
+ allowSelectAllMatching?: boolean;
19
23
  /** Passthrough: custom header actions */
20
24
  headerActions?: Snippet;
21
25
  /** Optional status tabs slot above filter/table */
@@ -23,7 +27,7 @@
23
27
  /** Optional filter toolbar slot between status tabs and table */
24
28
  filterToolbar?: Snippet;
25
29
  /** Passthrough: custom batch actions to render when rows are selected */
26
- batchActions?: Snippet<[{ selectedIds: (string | number)[] }]>;
30
+ batchActions?: Snippet<[{ selectedIds: (string | number)[]; selection: BatchSelection }]>;
27
31
  /** Passthrough: custom cell renderer per field */
28
32
  cellRenderer?: Snippet<[{ field: FieldDefinition; value: unknown; record: Record<string, unknown> }]>;
29
33
  /** Passthrough: custom row actions */
@@ -37,10 +41,12 @@
37
41
 
38
42
  let {
39
43
  resourceName,
44
+ rendering,
40
45
  title,
41
46
  canCreate,
42
47
  density = 'comfortable',
43
48
  selectable,
49
+ allowSelectAllMatching,
44
50
  headerActions,
45
51
  statusTabs,
46
52
  filterToolbar,
@@ -81,10 +87,12 @@
81
87
  {/if}
82
88
 
83
89
  <AutoTable
90
+ {rendering}
84
91
  {resourceName}
85
92
  showHeader={false}
86
93
  {density}
87
94
  {...definedOptions({ "selectable": selectable })}
95
+ {...definedOptions({ allowSelectAllMatching })}
88
96
  {...definedOptions({ batchActions })}
89
97
  {...definedOptions({ "defaultCellRenderer": cellRenderer })}
90
98
  {...definedOptions({ "rowActions": rowActions })}
@@ -1,12 +1,16 @@
1
+ import type { ResourceRendering } from '../rendering/index.js';
1
2
  import type { Snippet } from 'svelte';
2
3
  import type { FieldDefinition } from '@svadmin/core';
4
+ import type { BatchSelection } from './table-contract';
3
5
  interface Props {
4
6
  resourceName: string;
7
+ rendering?: ResourceRendering | undefined;
5
8
  title?: string;
6
9
  canCreate?: boolean;
7
10
  density?: 'compact' | 'comfortable';
8
11
  /** Passthrough: enable row selection checkboxes */
9
12
  selectable?: boolean;
13
+ allowSelectAllMatching?: boolean;
10
14
  /** Passthrough: custom header actions */
11
15
  headerActions?: Snippet;
12
16
  /** Optional status tabs slot above filter/table */
@@ -16,6 +20,7 @@ interface Props {
16
20
  /** Passthrough: custom batch actions to render when rows are selected */
17
21
  batchActions?: Snippet<[{
18
22
  selectedIds: (string | number)[];
23
+ selection: BatchSelection;
19
24
  }]>;
20
25
  /** Passthrough: custom cell renderer per field */
21
26
  cellRenderer?: Snippet<[{