@svadmin/ui 0.38.1 → 0.38.5

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 (144) hide show
  1. package/dist/app.css +10 -5
  2. package/dist/app.theme.css +1374 -0
  3. package/dist/components/AICommandBar.svelte +16 -15
  4. package/dist/components/AboutSettings.svelte +12 -11
  5. package/dist/components/AdminApp.svelte +144 -70
  6. package/dist/components/AdminApp.svelte.d.ts +6 -3
  7. package/dist/components/ApiSettings.svelte +19 -16
  8. package/dist/components/AppearanceSettings.svelte +18 -16
  9. package/dist/components/AuditLogDrawer.svelte +6 -3
  10. package/dist/components/AuditLogViewer.svelte +18 -15
  11. package/dist/components/Authenticated.svelte +4 -4
  12. package/dist/components/AutoForm.svelte +19 -16
  13. package/dist/components/AutoSaveIndicator.svelte +7 -5
  14. package/dist/components/AutoTable.svelte +85 -71
  15. package/dist/components/AutoTable.svelte.d.ts +1 -1
  16. package/dist/components/Breadcrumbs.svelte +17 -13
  17. package/dist/components/CancelTaskButton.svelte +5 -2
  18. package/dist/components/CatchAllNavigate.svelte +3 -3
  19. package/dist/components/ChatDialog.svelte +22 -19
  20. package/dist/components/ComboboxField.svelte +6 -4
  21. package/dist/components/CommandPalette.svelte +20 -20
  22. package/dist/components/ConfigErrorScreen.svelte +12 -10
  23. package/dist/components/ConfirmDialog.svelte +6 -4
  24. package/dist/components/CreatePage.svelte +4 -2
  25. package/dist/components/DevTools.svelte +11 -9
  26. package/dist/components/DrawerForm.svelte +5 -2
  27. package/dist/components/EditPage.svelte +8 -5
  28. package/dist/components/EmptyState.svelte +5 -3
  29. package/dist/components/ErrorBoundary.svelte +5 -3
  30. package/dist/components/ErrorComponent.svelte +6 -4
  31. package/dist/components/ErrorPage.svelte +12 -10
  32. package/dist/components/FieldRenderer.svelte +17 -15
  33. package/dist/components/ForgotPasswordPage.svelte +22 -19
  34. package/dist/components/Header.svelte +7 -4
  35. package/dist/components/InfiniteList.svelte +6 -3
  36. package/dist/components/IntegrationsSettings.svelte +9 -6
  37. package/dist/components/KeyboardShortcuts.svelte +4 -2
  38. package/dist/components/Layout.svelte +12 -13
  39. package/dist/components/LazyPage.svelte +47 -0
  40. package/dist/components/LazyPage.svelte.d.ts +14 -0
  41. package/dist/components/ListPage.svelte +8 -5
  42. package/dist/components/LoginPage.svelte +24 -22
  43. package/dist/components/MarkdownRenderer.svelte +89 -35
  44. package/dist/components/ModalForm.svelte +4 -2
  45. package/dist/components/NavigateToResource.svelte +3 -3
  46. package/dist/components/NotificationsSettings.svelte +26 -24
  47. package/dist/components/PasswordInput.svelte +4 -2
  48. package/dist/components/PermissionMatrix.svelte +15 -12
  49. package/dist/components/ProfilePage.svelte +23 -21
  50. package/dist/components/RegisterPage.svelte +21 -19
  51. package/dist/components/ResourceOperationsPage.svelte +3 -2
  52. package/dist/components/RetryTaskButton.svelte +5 -2
  53. package/dist/components/RolesSettings.svelte +10 -8
  54. package/dist/components/SecuritySettings.svelte +26 -23
  55. package/dist/components/SettingsPage.svelte +14 -11
  56. package/dist/components/ShowPage.svelte +9 -7
  57. package/dist/components/Sidebar.svelte +36 -28
  58. package/dist/components/SidebarItem.svelte +4 -4
  59. package/dist/components/SidebarItem.svelte.d.ts +1 -1
  60. package/dist/components/StepsForm.svelte +11 -8
  61. package/dist/components/TaskDetails.svelte +14 -11
  62. package/dist/components/TaskList.svelte +14 -11
  63. package/dist/components/TaskQueueDrawer.svelte +34 -32
  64. package/dist/components/TaskStatusBadge.svelte +5 -2
  65. package/dist/components/Toast.svelte +18 -4
  66. package/dist/components/TooltipButton.svelte +25 -3
  67. package/dist/components/TwoFactorAuthPage.svelte +26 -24
  68. package/dist/components/UndoableNotification.svelte +5 -2
  69. package/dist/components/UpdatePasswordPage.svelte +18 -15
  70. package/dist/components/VirtualTable.svelte +0 -4
  71. package/dist/components/account/CompanyProfilePage.svelte +13 -11
  72. package/dist/components/account/GetStartedPage.svelte +15 -13
  73. package/dist/components/account/ImportMembersPage.svelte +14 -12
  74. package/dist/components/account/MembersStarterPage.svelte +10 -8
  75. package/dist/components/account/SecurityLogPage.svelte +24 -22
  76. package/dist/components/account/SettingsEnterprisePage.svelte +6 -4
  77. package/dist/components/account/SettingsPlainPage.svelte +14 -12
  78. package/dist/components/account/SettingsSidebarPage.svelte +11 -9
  79. package/dist/components/account/TeamMembersPage.svelte +10 -8
  80. package/dist/components/buttons/CloneButton.svelte +4 -2
  81. package/dist/components/buttons/CreateButton.svelte +4 -2
  82. package/dist/components/buttons/DeleteButton.svelte +6 -4
  83. package/dist/components/buttons/EditButton.svelte +4 -2
  84. package/dist/components/buttons/ExportButton.svelte +4 -2
  85. package/dist/components/buttons/ImportButton.svelte +4 -2
  86. package/dist/components/buttons/RefreshButton.svelte +4 -2
  87. package/dist/components/buttons/SaveButton.svelte +4 -2
  88. package/dist/components/buttons/ShowButton.svelte +4 -2
  89. package/dist/components/fieldComponentMap.js +0 -3
  90. package/dist/components/fields/ArrayField.svelte +6 -4
  91. package/dist/components/fields/JsonField.svelte +5 -3
  92. package/dist/components/fields/RelationField.svelte +3 -3
  93. package/dist/components/fields/RichTextField.svelte +4 -2
  94. package/dist/components/network/TeamCrewTablePage.svelte +18 -16
  95. package/dist/components/network/UserCardsNFTPage.svelte +11 -9
  96. package/dist/components/profile/ActivityTimeline.svelte +10 -8
  97. package/dist/components/profile/ProfileCard.svelte +13 -11
  98. package/dist/components/profile/ProjectsGrid.svelte +7 -5
  99. package/dist/components/profile/PublicProfilePage.svelte +18 -16
  100. package/dist/components/profile/TeamsShowcase.svelte +7 -5
  101. package/dist/components/toast-host.svelte.d.ts +5 -0
  102. package/dist/components/toast-host.svelte.js +21 -0
  103. package/dist/components/ui/command/command-dialog.svelte +29 -13
  104. package/dist/components/ui/command/command-dialog.svelte.d.ts +4 -3
  105. package/dist/components/ui/command/command-empty.svelte +18 -0
  106. package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
  107. package/dist/components/ui/command/command-group.svelte +41 -0
  108. package/dist/components/ui/command/command-group.svelte.d.ts +8 -0
  109. package/dist/components/ui/command/command-input.svelte +23 -0
  110. package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
  111. package/dist/components/ui/command/command-item.svelte +43 -0
  112. package/dist/components/ui/command/command-item.svelte.d.ts +11 -0
  113. package/dist/components/ui/command/command-list.svelte +18 -0
  114. package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
  115. package/dist/components/ui/command/command-loading.svelte +12 -0
  116. package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
  117. package/dist/components/ui/command/command-separator.svelte +23 -0
  118. package/dist/components/ui/command/command-separator.svelte.d.ts +7 -0
  119. package/dist/components/ui/command/command.svelte +26 -0
  120. package/dist/components/ui/command/command.svelte.d.ts +4 -0
  121. package/dist/components/ui/command/index.d.ts +55 -2
  122. package/dist/components/ui/command/index.js +21 -2
  123. package/dist/components/ui/sheet/sheet-content.svelte +1 -4
  124. package/dist/components/ui/sheet/sheet-content.svelte.d.ts +1 -3
  125. package/dist/components/ui/tabs/tabs-content.svelte +6 -2
  126. package/dist/components/ui/tabs/tabs-context.svelte.d.ts +5 -0
  127. package/dist/components/ui/tabs/tabs-context.svelte.js +2 -0
  128. package/dist/components/ui/tabs/tabs-trigger.svelte +49 -4
  129. package/dist/components/ui/tabs/tabs.svelte +12 -1
  130. package/dist/components/ui/tooltip/index.d.ts +4 -1
  131. package/dist/components/ui/tooltip/index.js +3 -1
  132. package/dist/components/ui/tooltip/tooltip-content.svelte +308 -35
  133. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +42 -3
  134. package/dist/components/ui/tooltip/tooltip-context.svelte.d.ts +37 -0
  135. package/dist/components/ui/tooltip/tooltip-context.svelte.js +220 -0
  136. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.d.ts +11 -0
  137. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.js +93 -0
  138. package/dist/components/ui/tooltip/tooltip-trigger.svelte +56 -3
  139. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +23 -2
  140. package/dist/components/ui/tooltip/tooltip.svelte +63 -3
  141. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +23 -2
  142. package/dist/router-state.svelte.d.ts +13 -0
  143. package/dist/router-state.svelte.js +72 -37
  144. package/package.json +18 -12
@@ -1,9 +1,9 @@
1
1
  <script lang="ts">
2
- import { getResources, canAccessAsync } from '@svadmin/core';
2
+ import { captureAdminContext, getResources, canAccessAsync } from '@svadmin/core';
3
3
  import type { Identity, MenuItem } from '@svadmin/core';
4
- import { navigate } from '@svadmin/core/router';
5
4
  import { getPath } from '../router-state.svelte.js';
6
- import { t, getLocale, setLocale, getAvailableLocales } from '@svadmin/core/i18n';
5
+ import { useTranslation } from '@svadmin/core/i18n';
6
+
7
7
  import { toggleTheme, getResolvedTheme, getColorThemes, getColorTheme, setColorTheme } from '@svadmin/core';
8
8
  import { Button } from './ui/button/index.js';
9
9
  import TooltipButton from './TooltipButton.svelte';
@@ -22,6 +22,8 @@
22
22
  Calendar, Bell, Clock, Tag, Trash2, Shield
23
23
  } from '@lucide/svelte';
24
24
 
25
+ const i18n = useTranslation();
26
+
25
27
  let { collapsed, identity, title, onToggle, onLogout, menu, routeMode = 'auto' }: {
26
28
  collapsed: boolean;
27
29
  identity: Identity | null;
@@ -31,6 +33,7 @@
31
33
  menu?: MenuItem[];
32
34
  routeMode?: 'hash' | 'path' | 'auto';
33
35
  } = $props();
36
+ const adminContext = captureAdminContext();
34
37
 
35
38
  const effectiveRouteMode = $derived(
36
39
  routeMode === 'auto'
@@ -38,6 +41,11 @@
38
41
  : routeMode
39
42
  );
40
43
 
44
+ function formatSidebarLink(path: string): string {
45
+ if (adminContext.routerProvider) return adminContext.formatLink(path);
46
+ return effectiveRouteMode === 'hash' ? `#${path}` : path;
47
+ }
48
+
41
49
  const iconMap: Record<string, typeof LayoutDashboard> = {
42
50
  'dashboard': LayoutDashboard,
43
51
  'posts': FileText,
@@ -97,7 +105,7 @@
97
105
  let navItems = $state.raw<NavItem[]>([]);
98
106
 
99
107
  $effect(() => {
100
- getLocale();
108
+ const homeLabel = i18n.t('common.home');
101
109
  const currentResources = getResources();
102
110
  let cancelled = false;
103
111
 
@@ -110,7 +118,7 @@
110
118
  }
111
119
  })).then(results => {
112
120
  if (cancelled) return;
113
- const items: NavItem[] = [{ path: '/', label: t('common.home'), Icon: LayoutDashboard }];
121
+ const items: NavItem[] = [{ path: '/', label: homeLabel, Icon: LayoutDashboard }];
114
122
  for (const { r, can } of results) {
115
123
  if (can) {
116
124
  items.push({
@@ -128,14 +136,14 @@
128
136
  });
129
137
 
130
138
  function toggleLocale() {
131
- const locales = getAvailableLocales();
132
- const current = getLocale();
139
+ const locales = i18n.getAvailableLocales();
140
+ const current = i18n.locale;
133
141
  const idx = locales.indexOf(current);
134
142
  const next = locales[(idx + 1) % locales.length];
135
- setLocale(next);
143
+ i18n.setLocale(next);
136
144
  }
137
145
 
138
- const localeLabel = $derived(getLocale() === 'zh-CN' ? '中' : 'EN');
146
+ const localeLabel = $derived(i18n.locale === 'zh-CN' ? '中' : 'EN');
139
147
 
140
148
  const path = $derived(getPath());
141
149
 
@@ -210,12 +218,12 @@
210
218
  >
211
219
  <div class="flex h-[70px] items-center shrink-0" class:px-5={!collapsed} class:justify-center={collapsed}>
212
220
  {#if !collapsed}
213
- <a href="#/" class="flex items-center gap-2.5 group" onclick={(e) => { e.preventDefault(); navigate('/'); }}>
221
+ <a href={formatSidebarLink('/')} class="flex items-center gap-2.5 group" onclick={(e) => { e.preventDefault(); adminContext.navigate('/'); }}>
214
222
  <div class="w-[34px] h-[34px] rounded-lg bg-gradient-to-tr from-primary to-purple-500 shrink-0 transition-transform group-hover:scale-105" style="box-shadow: 0 4px 12px oklch(0.488 0.243 264.376 / 20%);"></div>
215
223
  <span class="font-semibold text-[15px] tracking-[-0.01em] text-sidebar-foreground">{title}</span>
216
224
  </a>
217
225
  {:else}
218
- <a href="#/" class="group" aria-label={title} onclick={(e) => { e.preventDefault(); navigate('/'); }}>
226
+ <a href={formatSidebarLink('/')} class="group" aria-label={title} onclick={(e) => { e.preventDefault(); adminContext.navigate('/'); }}>
219
227
  <div class="w-[34px] h-[34px] rounded-lg bg-gradient-to-tr from-primary to-purple-500 shrink-0 transition-transform group-hover:scale-105" style="box-shadow: 0 4px 12px oklch(0.488 0.243 264.376 / 20%);"></div>
220
228
  </a>
221
229
  {/if}
@@ -248,8 +256,8 @@
248
256
  {#each group.items as item, _i (_i)}
249
257
  {@const active = isActive(item.path)}
250
258
  <a
251
- href={effectiveRouteMode === 'hash' ? `#${item.path}` : item.path}
252
- onclick={(e) => { e.preventDefault(); navigate(item.path); }}
259
+ href={formatSidebarLink(item.path)}
260
+ onclick={(e) => { e.preventDefault(); adminContext.navigate(item.path); }}
253
261
  class="sidebar-menu-item flex items-center gap-2.5 rounded-md px-[10px] {pyClassGroupItem} text-[13px] font-medium transition-colors duration-150
254
262
  {active
255
263
  ? 'sidebar-menu-item-active bg-sidebar-accent text-primary'
@@ -271,8 +279,8 @@
271
279
  {#snippet child({ props }: { props: Record<string, unknown> })}
272
280
  <a
273
281
  {...props}
274
- href={effectiveRouteMode === 'hash' ? `#${item.path}` : item.path}
275
- onclick={(e) => { e.preventDefault(); navigate(item.path); }}
282
+ href={formatSidebarLink(item.path)}
283
+ onclick={(e) => { e.preventDefault(); adminContext.navigate(item.path); }}
276
284
  class="sidebar-menu-item flex items-center justify-center rounded-md px-2 {pyClass} transition-colors duration-150
277
285
  {active
278
286
  ? 'sidebar-menu-item-active bg-sidebar-accent text-primary'
@@ -288,8 +296,8 @@
288
296
  </Tooltip.Root>
289
297
  {:else}
290
298
  <a
291
- href={effectiveRouteMode === 'hash' ? `#${item.path}` : item.path}
292
- onclick={(e) => { e.preventDefault(); navigate(item.path); }}
299
+ href={formatSidebarLink(item.path)}
300
+ onclick={(e) => { e.preventDefault(); adminContext.navigate(item.path); }}
293
301
  class="sidebar-menu-item flex items-center gap-2.5 rounded-md px-[10px] {pyClass} text-[13px] font-medium transition-colors duration-150
294
302
  {active
295
303
  ? 'sidebar-menu-item-active bg-sidebar-accent text-primary'
@@ -315,7 +323,7 @@
315
323
  onclick={() => { if (!colorPickerOpen) colorPickerOpenedAt = Date.now(); colorPickerOpen = !colorPickerOpen; }}
316
324
  >
317
325
  <Palette class="h-3.5 w-3.5" />
318
- <span class="flex-1 text-left">{t('common.toggleTheme')}</span>
326
+ <span class="flex-1 text-left">{i18n.t('common.toggleTheme')}</span>
319
327
  <span
320
328
  class="h-3 w-3 rounded-full ring-1 ring-offset-1 ring-offset-sidebar"
321
329
  style="background-color: {getColorThemes().find(c => c.id === getColorTheme())?.color ?? '#6366f1'}; --tw-ring-color: {getColorThemes().find(c => c.id === getColorTheme())?.color ?? '#6366f1'}"
@@ -363,21 +371,21 @@
363
371
  </div>
364
372
  <div class="mt-1 flex items-center justify-between px-1">
365
373
  <div class="flex items-center gap-0.5">
366
- <TooltipButton tooltip={t('common.switchLanguage')} variant="ghost" size="icon-sm" onclick={toggleLocale} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
374
+ <TooltipButton tooltip={i18n.t('common.switchLanguage')} variant="ghost" size="icon-sm" onclick={toggleLocale} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
367
375
  <span class="text-[11px] font-semibold">{localeLabel}</span>
368
376
  </TooltipButton>
369
- <TooltipButton tooltip={t('common.toggleTheme')} variant="ghost" size="icon-sm" onclick={toggleTheme} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
377
+ <TooltipButton tooltip={i18n.t('common.toggleTheme')} variant="ghost" size="icon-sm" onclick={toggleTheme} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
370
378
  {#if getResolvedTheme() === 'dark'}
371
379
  <Sun class="h-3.5 w-3.5" />
372
380
  {:else}
373
381
  <Moon class="h-3.5 w-3.5" />
374
382
  {/if}
375
383
  </TooltipButton>
376
- <TooltipButton tooltip={t('settings.title')} variant="ghost" size="icon-sm" onclick={() => navigate('/settings')} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
384
+ <TooltipButton tooltip={i18n.t('settings.title')} variant="ghost" size="icon-sm" onclick={() => adminContext.navigate('/settings')} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
377
385
  <Settings class="h-3.5 w-3.5" />
378
386
  </TooltipButton>
379
387
  </div>
380
- <TooltipButton tooltip={t('common.toggleSidebar')} variant="ghost" size="icon-sm" onclick={onToggle} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
388
+ <TooltipButton tooltip={i18n.t('common.toggleSidebar')} variant="ghost" size="icon-sm" onclick={onToggle} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
381
389
  <ChevronLeft class="h-3.5 w-3.5" />
382
390
  </TooltipButton>
383
391
  </div>
@@ -392,7 +400,7 @@
392
400
  </Button>
393
401
  {/snippet}
394
402
  </Tooltip.Trigger>
395
- <Tooltip.Content side="right">{t('common.switchLanguage')}</Tooltip.Content>
403
+ <Tooltip.Content side="right">{i18n.t('common.switchLanguage')}</Tooltip.Content>
396
404
  </Tooltip.Root>
397
405
  <Tooltip.Root>
398
406
  <Tooltip.Trigger>
@@ -406,7 +414,7 @@
406
414
  </Button>
407
415
  {/snippet}
408
416
  </Tooltip.Trigger>
409
- <Tooltip.Content side="right">{t('common.toggleTheme')}</Tooltip.Content>
417
+ <Tooltip.Content side="right">{i18n.t('common.toggleTheme')}</Tooltip.Content>
410
418
  </Tooltip.Root>
411
419
  <Tooltip.Root>
412
420
  <Tooltip.Trigger>
@@ -416,7 +424,7 @@
416
424
  </Button>
417
425
  {/snippet}
418
426
  </Tooltip.Trigger>
419
- <Tooltip.Content side="right">{t('common.toggleSidebar')}</Tooltip.Content>
427
+ <Tooltip.Content side="right">{i18n.t('common.toggleSidebar')}</Tooltip.Content>
420
428
  </Tooltip.Root>
421
429
  <Tooltip.Root>
422
430
  <Tooltip.Trigger>
@@ -426,15 +434,15 @@
426
434
  </Button>
427
435
  {/snippet}
428
436
  </Tooltip.Trigger>
429
- <Tooltip.Content side="right">{t('common.logout')}</Tooltip.Content>
437
+ <Tooltip.Content side="right">{i18n.t('common.logout')}</Tooltip.Content>
430
438
  </Tooltip.Root>
431
439
  </div>
432
440
  {:else}
433
441
  <div class="flex justify-center gap-1 p-3">
434
- <TooltipButton tooltip={t('common.switchLanguage')} variant="ghost" size="icon" onclick={toggleLocale} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
442
+ <TooltipButton tooltip={i18n.t('common.switchLanguage')} variant="ghost" size="icon" onclick={toggleLocale} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
435
443
  <span class="text-[11px] font-semibold">{localeLabel}</span>
436
444
  </TooltipButton>
437
- <TooltipButton tooltip={t('common.toggleTheme')} variant="ghost" size="icon" onclick={toggleTheme} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
445
+ <TooltipButton tooltip={i18n.t('common.toggleTheme')} variant="ghost" size="icon" onclick={toggleTheme} class="h-8 w-8 rounded-md text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground">
438
446
  {#if getResolvedTheme() === 'dark'}
439
447
  <Sun class="h-3.5 w-3.5" />
440
448
  {:else}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import type { MenuItem } from '@svadmin/core';
2
+ import { captureAdminContext, type MenuItem } from '@svadmin/core';
3
3
  import * as Collapsible from './ui/collapsible/index.js';
4
4
  import * as Tooltip from './ui/tooltip/index.js';
5
5
  import { Badge } from './ui/badge/index.js';
@@ -12,14 +12,14 @@
12
12
  Circle,
13
13
  } from '@lucide/svelte';
14
14
 
15
- import { formatLink } from '@svadmin/core/router';
16
-
15
+
17
16
  let { item, currentPath, collapsed = false, depth = 0 }: {
18
17
  item: MenuItem;
19
18
  currentPath: string;
20
19
  collapsed?: boolean;
21
20
  depth?: number;
22
21
  } = $props();
22
+ const adminContext = captureAdminContext();
23
23
 
24
24
  const iconMap: Record<string, typeof LayoutDashboard> = {
25
25
  dashboard: LayoutDashboard,
@@ -90,7 +90,7 @@
90
90
  const childActive = $derived(hasActiveChild(item));
91
91
  const isExternal = $derived(item.target === '_blank' || item.href?.startsWith('http'));
92
92
 
93
- const finalHref = $derived(isExternal ? item.href : (item.href ? formatLink(item.href) : undefined));
93
+ const finalHref = $derived(isExternal ? item.href : (item.href ? adminContext.formatLink(item.href) : undefined));
94
94
 
95
95
  let isOpen = $state(false);
96
96
  $effect(() => { if (childActive) isOpen = true; });
@@ -1,4 +1,4 @@
1
- import type { MenuItem } from '@svadmin/core';
1
+ import { type MenuItem } from '@svadmin/core';
2
2
  import SidebarItem from './SidebarItem.svelte';
3
3
  type $$ComponentProps = {
4
4
  item: MenuItem;
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">
2
2
  import { fly } from 'svelte/transition';
3
- import { useStepsForm, deriveValidator } from '@svadmin/core';
3
+ import { captureAdminContext, useStepsForm, deriveValidator } from '@svadmin/core';
4
4
  import type { FieldDefinition } from '@svadmin/core';
5
5
  import { getResource } from '@svadmin/core';
6
- import { navigate } from '@svadmin/core/router';
7
- import { t } from '@svadmin/core/i18n';
6
+ import { useTranslation } from '@svadmin/core/i18n';
7
+
8
8
  import { Button } from './ui/button/index.js';
9
9
  import * as Card from './ui/card/index.js';
10
10
  import { Badge } from './ui/badge/index.js';
@@ -12,6 +12,8 @@
12
12
  import { Save, ArrowLeft, ArrowRight, Check, Loader2 } from '@lucide/svelte';
13
13
  import FieldRenderer from './FieldRenderer.svelte';
14
14
 
15
+ const i18n = useTranslation();
16
+
15
17
  interface StepDef {
16
18
  title: string;
17
19
  fields: string[];
@@ -25,6 +27,7 @@
25
27
  }
26
28
 
27
29
  let { resourceName, id, mode = 'create', steps }: Props = $props();
30
+ const adminContext = captureAdminContext();
28
31
 
29
32
  const resource = $derived(getResource(resourceName));
30
33
  const primaryKey = $derived(resource.primaryKey ?? 'id');
@@ -53,7 +56,7 @@
53
56
  return d;
54
57
  })());
55
58
 
56
- const validator = $derived(deriveValidator(flatStepsFields));
59
+ const validator = $derived(deriveValidator(flatStepsFields, { translate: i18n.t }));
57
60
  const stepsCount = $derived(steps.length);
58
61
 
59
62
  // ─── useStepsForm: form state + step navigation ──────────────────
@@ -147,7 +150,7 @@
147
150
  <Card.CardHeader>
148
151
  <Card.CardTitle>{steps[form.steps.currentStep]?.title ?? ''}</Card.CardTitle>
149
152
  <Badge variant="outline" class="w-fit">
150
- {t('common.step')} {form.steps.currentStep + 1} / {totalSteps}
153
+ {i18n.t('common.step')} {form.steps.currentStep + 1} / {totalSteps}
151
154
  </Badge>
152
155
  </Card.CardHeader>
153
156
  <Card.CardContent>
@@ -168,10 +171,10 @@
168
171
  <Button
169
172
  type="button"
170
173
  variant="outline"
171
- onclick={() => form.steps.currentStep === 0 ? navigate(`/${resourceName}`) : form.steps.prevStep()}
174
+ onclick={() => form.steps.currentStep === 0 ? adminContext.navigate(`/${resourceName}`) : form.steps.prevStep()}
172
175
  >
173
176
  <ArrowLeft class="h-4 w-4" data-icon="inline-start" />
174
- {form.steps.currentStep === 0 ? t('common.cancel') : t('common.back')}
177
+ {form.steps.currentStep === 0 ? i18n.t('common.cancel') : i18n.t('common.back')}
175
178
  </Button>
176
179
 
177
180
  <Button type="submit" disabled={form.submitting}>
@@ -182,7 +185,7 @@
182
185
  {:else}
183
186
  <ArrowRight class="h-4 w-4" data-icon="inline-start" />
184
187
  {/if}
185
- {isLastStep ? t('common.save') : t('common.next')}
188
+ {isLastStep ? i18n.t('common.save') : i18n.t('common.next')}
186
189
  </Button>
187
190
  </div>
188
191
  </form>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { useTask, getTaskProvider } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
+
4
5
  import type { TaskProvider, TaskRecord } from '@svadmin/core';
5
6
  import { Loader2 } from '@lucide/svelte';
6
7
  import * as Card from './ui/card/index.js';
@@ -21,6 +22,8 @@
21
22
  resolveTaskUpdatedAt,
22
23
  } from './task-utils.js';
23
24
 
25
+ const i18n = useTranslation();
26
+
24
27
  let {
25
28
  task,
26
29
  taskId,
@@ -57,7 +60,7 @@
57
60
  });
58
61
 
59
62
  const resolvedTask = $derived(task ?? query.data);
60
- const resolvedTitle = $derived(title ?? t('task.detailsTitle'));
63
+ const resolvedTitle = $derived(title ?? i18n.t('task.detailsTitle'));
61
64
 
62
65
  function formatDate(value: unknown) {
63
66
  if (!value) return '—';
@@ -82,7 +85,7 @@
82
85
  {#if resolvedTask}
83
86
  {resolveTaskTitle(resolvedTask)}
84
87
  {:else}
85
- {t('task.detailsSummary')}
88
+ {i18n.t('task.detailsSummary')}
86
89
  {/if}
87
90
  </Card.Description>
88
91
  </Card.Header>
@@ -90,7 +93,7 @@
90
93
  {#if useProviderData && taskProvider && query.isLoading}
91
94
  <div class="flex h-40 items-center justify-center text-muted-foreground">
92
95
  <Loader2 class="mr-2 h-4 w-4 animate-spin" />
93
- {t('task.loadingDetail')}
96
+ {i18n.t('task.loadingDetail')}
94
97
  </div>
95
98
  {:else if resolvedTask}
96
99
  <div class="flex flex-wrap items-center gap-2">
@@ -104,38 +107,38 @@
104
107
 
105
108
  <div class="grid gap-4 md:grid-cols-2">
106
109
  <div class="space-y-1">
107
- <div class="text-xs uppercase tracking-wide text-muted-foreground">{t('task.createdLabel')}</div>
110
+ <div class="text-xs uppercase tracking-wide text-muted-foreground">{i18n.t('task.createdLabel')}</div>
108
111
  <div class="text-sm">{formatDate(resolveTaskCreatedAt(resolvedTask))}</div>
109
112
  </div>
110
113
  <div class="space-y-1">
111
- <div class="text-xs uppercase tracking-wide text-muted-foreground">{t('task.updatedLabel')}</div>
114
+ <div class="text-xs uppercase tracking-wide text-muted-foreground">{i18n.t('task.updatedLabel')}</div>
112
115
  <div class="text-sm">{formatDate(resolveTaskUpdatedAt(resolvedTask))}</div>
113
116
  </div>
114
117
  </div>
115
118
 
116
119
  {#if resolveTaskMessage(resolvedTask)}
117
120
  <div class="space-y-1">
118
- <div class="text-xs uppercase tracking-wide text-muted-foreground">{t('task.messageLabel')}</div>
121
+ <div class="text-xs uppercase tracking-wide text-muted-foreground">{i18n.t('task.messageLabel')}</div>
119
122
  <div class="rounded-md border border-border/60 bg-muted/30 p-3 text-sm">{resolveTaskMessage(resolvedTask)}</div>
120
123
  </div>
121
124
  {/if}
122
125
 
123
126
  {#if resolveTaskError(resolvedTask)}
124
127
  <div class="space-y-1">
125
- <div class="text-xs uppercase tracking-wide text-destructive">{t('task.errorLabel')}</div>
128
+ <div class="text-xs uppercase tracking-wide text-destructive">{i18n.t('task.errorLabel')}</div>
126
129
  <pre class="overflow-auto rounded-md border border-destructive/20 bg-destructive/5 p-3 text-xs text-destructive">{toPrettyJson(resolveTaskError(resolvedTask))}</pre>
127
130
  </div>
128
131
  {/if}
129
132
 
130
133
  {#if resolveTaskResult(resolvedTask) !== undefined}
131
134
  <div class="space-y-1">
132
- <div class="text-xs uppercase tracking-wide text-muted-foreground">{t('task.resultLabel')}</div>
135
+ <div class="text-xs uppercase tracking-wide text-muted-foreground">{i18n.t('task.resultLabel')}</div>
133
136
  <pre class="overflow-auto rounded-md border border-border/60 bg-muted/20 p-3 text-xs">{toPrettyJson(resolveTaskResult(resolvedTask))}</pre>
134
137
  </div>
135
138
  {/if}
136
139
 
137
140
  <div class="space-y-1">
138
- <div class="text-xs uppercase tracking-wide text-muted-foreground">{t('task.payloadLabel')}</div>
141
+ <div class="text-xs uppercase tracking-wide text-muted-foreground">{i18n.t('task.payloadLabel')}</div>
139
142
  <pre class="overflow-auto rounded-md border border-border/60 bg-muted/20 p-3 text-xs">{toPrettyJson(resolvedTask.payload ?? resolvedTask)}</pre>
140
143
  </div>
141
144
 
@@ -151,7 +154,7 @@
151
154
  {/if}
152
155
  {:else}
153
156
  <div class="flex h-32 items-center justify-center text-sm text-muted-foreground">
154
- {t('task.noSelection')}
157
+ {i18n.t('task.noSelection')}
155
158
  </div>
156
159
  {/if}
157
160
  </Card.Content>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { useTaskList, getTaskProvider } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
+
4
5
  import type { TaskProvider, TaskRecord } from '@svadmin/core';
5
6
  import { Loader2 } from '@lucide/svelte';
6
7
  import * as Card from './ui/card/index.js';
@@ -19,6 +20,8 @@
19
20
  resolveTaskUpdatedAt,
20
21
  } from './task-utils.js';
21
22
 
23
+ const i18n = useTranslation();
24
+
22
25
  let {
23
26
  tasks,
24
27
  taskProvider = getTaskProvider({ optional: true }) as TaskProvider<TaskRecord> | undefined,
@@ -68,8 +71,8 @@
68
71
  });
69
72
 
70
73
  const resolvedTasks = $derived(tasks ?? query.data?.data ?? []);
71
- const resolvedTitle = $derived(title ?? (dlq ? t('task.dlqTitle') : t('task.listTitle')));
72
- const resolvedEmptyText = $derived(emptyText ?? (dlq ? t('task.noDlq') : t('task.noTasks')));
74
+ const resolvedTitle = $derived(title ?? (dlq ? i18n.t('task.dlqTitle') : i18n.t('task.listTitle')));
75
+ const resolvedEmptyText = $derived(emptyText ?? (dlq ? i18n.t('task.noDlq') : i18n.t('task.noTasks')));
73
76
 
74
77
  function formatDate(value: unknown) {
75
78
  if (!value) return '—';
@@ -84,9 +87,9 @@
84
87
  <Card.Title class="text-base">{resolvedTitle}</Card.Title>
85
88
  <Card.Description>
86
89
  {#if useProviderData && taskProvider}
87
- {t('task.count', { count: (query.data?.total ?? resolvedTasks.length) || 0 })}
90
+ {i18n.t('task.count', { count: (query.data?.total ?? resolvedTasks.length) || 0 })}
88
91
  {:else}
89
- {t('task.count', { count: resolvedTasks.length })}
92
+ {i18n.t('task.count', { count: resolvedTasks.length })}
90
93
  {/if}
91
94
  </Card.Description>
92
95
  </Card.Header>
@@ -94,18 +97,18 @@
94
97
  {#if useProviderData && taskProvider && query.isLoading}
95
98
  <div class="flex h-40 items-center justify-center text-muted-foreground">
96
99
  <Loader2 class="mr-2 h-4 w-4 animate-spin" />
97
- {t('task.loadingList')}
100
+ {i18n.t('task.loadingList')}
98
101
  </div>
99
102
  {:else}
100
103
  <div class="overflow-auto">
101
104
  <Table.Root>
102
105
  <Table.Header>
103
106
  <Table.Row>
104
- <Table.Head>{t('task.taskColumn')}</Table.Head>
105
- <Table.Head>{t('task.statusColumn')}</Table.Head>
106
- <Table.Head>{t('task.updatedColumn')}</Table.Head>
107
+ <Table.Head>{i18n.t('task.taskColumn')}</Table.Head>
108
+ <Table.Head>{i18n.t('task.statusColumn')}</Table.Head>
109
+ <Table.Head>{i18n.t('task.updatedColumn')}</Table.Head>
107
110
  {#if showActions}
108
- <Table.Head class="text-right">{t('task.actionsColumn')}</Table.Head>
111
+ <Table.Head class="text-right">{i18n.t('task.actionsColumn')}</Table.Head>
109
112
  {/if}
110
113
  </Table.Row>
111
114
  </Table.Header>
@@ -139,7 +142,7 @@
139
142
  <CancelTaskButton taskId={task.id} {taskProvider} />
140
143
  {/if}
141
144
  <Button variant="ghost" size="sm" onclick={(e) => { e.stopPropagation(); onSelect?.(task); }}>
142
- {t('task.detailsAction')}
145
+ {i18n.t('task.detailsAction')}
143
146
  </Button>
144
147
  </div>
145
148
  </Table.Cell>