@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,10 +1,12 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
+
3
4
  import { getTheme, setTheme, getColorTheme, setColorTheme, getColorPresets } from '@svadmin/core';
4
- import { getLocale, setLocale, getAvailableLocales } from '@svadmin/core/i18n';
5
5
  import * as Card from './ui/card/index.js';
6
6
  import { Sun, Moon, Monitor, Check } from '@lucide/svelte';
7
7
 
8
+ const i18n = useTranslation();
9
+
8
10
  // Theme mode
9
11
  const themeOptions = [
10
12
  { value: 'light' as const, icon: Sun, label: 'settings.light' },
@@ -14,8 +16,8 @@
14
16
 
15
17
  let currentTheme = $derived(getTheme());
16
18
  let currentColor = $derived(getColorTheme());
17
- let currentLocale = $derived(getLocale());
18
- const availableLocales = getAvailableLocales();
19
+ let currentLocale = $derived(i18n.locale);
20
+ const availableLocales = i18n.getAvailableLocales();
19
21
  const presets = getColorPresets();
20
22
 
21
23
  // Sidebar density
@@ -55,14 +57,14 @@
55
57
 
56
58
  <div class="space-y-6">
57
59
  <div>
58
- <h2 class="text-xl font-semibold text-foreground">{t('settings.appearance')}</h2>
59
- <p class="text-sm text-muted-foreground mt-1">{t('settings.settingsDescription')}</p>
60
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('settings.appearance')}</h2>
61
+ <p class="text-sm text-muted-foreground mt-1">{i18n.t('settings.settingsDescription')}</p>
60
62
  </div>
61
63
 
62
64
  <!-- Theme Mode -->
63
65
  <Card.Card>
64
66
  <Card.CardHeader class="pb-3">
65
- <Card.CardTitle class="text-base">{t('settings.themeMode')}</Card.CardTitle>
67
+ <Card.CardTitle class="text-base">{i18n.t('settings.themeMode')}</Card.CardTitle>
66
68
  </Card.CardHeader>
67
69
  <Card.CardContent>
68
70
  <div class="grid grid-cols-3 gap-3">
@@ -85,7 +87,7 @@
85
87
  <opt.icon class="h-5 w-5" />
86
88
  </div>
87
89
  <span class="text-xs font-medium {active ? 'text-primary' : 'text-muted-foreground'}">
88
- {t(opt.label)}
90
+ {i18n.t(opt.label)}
89
91
  </span>
90
92
  </button>
91
93
  {/each}
@@ -96,7 +98,7 @@
96
98
  <!-- Color Accent -->
97
99
  <Card.Card>
98
100
  <Card.CardHeader class="pb-3">
99
- <Card.CardTitle class="text-base">{t('settings.colorAccent')}</Card.CardTitle>
101
+ <Card.CardTitle class="text-base">{i18n.t('settings.colorAccent')}</Card.CardTitle>
100
102
  </Card.CardHeader>
101
103
  <Card.CardContent>
102
104
  <div class="flex flex-wrap gap-3">
@@ -121,20 +123,20 @@
121
123
  <!-- Interface -->
122
124
  <Card.Card>
123
125
  <Card.CardHeader class="pb-3">
124
- <Card.CardTitle class="text-base">{t('settings.interface')}</Card.CardTitle>
126
+ <Card.CardTitle class="text-base">{i18n.t('settings.interface')}</Card.CardTitle>
125
127
  </Card.CardHeader>
126
128
  <Card.CardContent class="space-y-5">
127
129
  <!-- Language -->
128
130
  <div class="flex items-center justify-between">
129
131
  <div>
130
- <p class="text-sm font-medium text-foreground">{t('settings.language')}</p>
132
+ <p class="text-sm font-medium text-foreground">{i18n.t('settings.language')}</p>
131
133
  <p class="text-xs text-muted-foreground">{localeNames[currentLocale] ?? currentLocale}</p>
132
134
  </div>
133
135
  <select
134
136
  class="h-9 rounded-md border border-input bg-background px-3 text-sm ring-offset-background
135
137
  focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
136
138
  value={currentLocale}
137
- onchange={(e) => setLocale((e.target as HTMLSelectElement).value)}
139
+ onchange={(e) => i18n.setLocale((e.target as HTMLSelectElement).value)}
138
140
  >
139
141
  {#each availableLocales as loc, _i (_i)}
140
142
  <option value={loc}>{localeNames[loc] ?? loc}</option>
@@ -147,19 +149,19 @@
147
149
  <!-- Sidebar Density -->
148
150
  <div class="flex items-center justify-between">
149
151
  <div>
150
- <p class="text-sm font-medium text-foreground">{t('settings.sidebarDensity')}</p>
152
+ <p class="text-sm font-medium text-foreground">{i18n.t('settings.sidebarDensity')}</p>
151
153
  </div>
152
154
  <div class="flex rounded-lg border border-input overflow-hidden">
153
155
  <button
154
156
  class="px-3 py-1.5 text-xs font-medium transition-colors
155
157
  {density === 'compact' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
156
158
  onclick={() => setDensity('compact')}
157
- >{t('settings.compact')}</button>
159
+ >{i18n.t('settings.compact')}</button>
158
160
  <button
159
161
  class="px-3 py-1.5 text-xs font-medium transition-colors
160
162
  {density === 'standard' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
161
163
  onclick={() => setDensity('standard')}
162
- >{t('settings.standard')}</button>
164
+ >{i18n.t('settings.standard')}</button>
163
165
  </div>
164
166
  </div>
165
167
 
@@ -168,7 +170,7 @@
168
170
  <!-- Default Page Size -->
169
171
  <div class="flex items-center justify-between">
170
172
  <div>
171
- <p class="text-sm font-medium text-foreground">{t('settings.defaultPageSize')}</p>
173
+ <p class="text-sm font-medium text-foreground">{i18n.t('settings.defaultPageSize')}</p>
172
174
  </div>
173
175
  <div class="flex rounded-lg border border-input overflow-hidden">
174
176
  {#each pageSizeOptions as size, _i (_i)}
@@ -1,9 +1,12 @@
1
1
  <script lang="ts">
2
- import { getAuditLogProvider, t } from '@svadmin/core';
2
+ import { getAuditLogProvider, useTranslation } from '@svadmin/core';
3
+
3
4
  import type { AuditEntry } from '@svadmin/core';
4
5
  import * as Sheet from './ui/sheet/index.js';
5
6
  import { History, Loader2 } from '@lucide/svelte';
6
7
 
8
+ const i18n = useTranslation();
9
+
7
10
  let { open = $bindable(false), resource, recordId } = $props<{
8
11
  open: boolean;
9
12
  resource: string;
@@ -47,7 +50,7 @@
47
50
  <Sheet.Header>
48
51
  <Sheet.Title class="flex items-center gap-2">
49
52
  <History class="h-5 w-5" />
50
- {t('common.history') || 'History'}
53
+ {i18n.t('common.history') || 'History'}
51
54
  </Sheet.Title>
52
55
  <Sheet.Description>
53
56
  Audit log and revision history for this record.
@@ -63,7 +66,7 @@
63
66
  <div class="text-center py-10">
64
67
  <History class="h-8 w-8 mx-auto text-muted-foreground/30 mb-3" />
65
68
  <p class="text-sm text-muted-foreground">
66
- {t('common.noData') || 'No audit logs found'}
69
+ {i18n.t('common.noData') || 'No audit logs found'}
67
70
  </p>
68
71
  </div>
69
72
  {:else}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
+
3
4
  import { getAuthProvider } from '@svadmin/core';
4
5
  import type { AuditLog } from '@svadmin/core';
5
6
  import { toast } from '@svadmin/core/toast';
@@ -9,6 +10,8 @@
9
10
  import { Input } from './ui/input/index.js';
10
11
  import { Loader2, ChevronLeft, ChevronRight, Search, FileSearch, Eye, X } from '@lucide/svelte';
11
12
 
13
+ const i18n = useTranslation();
14
+
12
15
  const authProvider = getAuthProvider({ optional: true });
13
16
 
14
17
  let logs = $state<AuditLog[]>([]);
@@ -27,7 +30,7 @@
27
30
 
28
31
  async function loadLogs() {
29
32
  if (!authProvider?.getAuditLogs) {
30
- error = t('settings.auditNotSupported') ?? 'Audit logs are not supported by the current AuthProvider';
33
+ error = i18n.t('settings.auditNotSupported') ?? 'Audit logs are not supported by the current AuthProvider';
31
34
  loading = false;
32
35
  return;
33
36
  }
@@ -87,10 +90,10 @@
87
90
  <div class="mb-6">
88
91
  <h2 class="text-xl font-semibold text-foreground flex items-center gap-2">
89
92
  <FileSearch class="h-5 w-5 text-muted-foreground" />
90
- {t('settings.auditLogs') ?? 'Audit Logs'}
93
+ {i18n.t('settings.auditLogs') ?? 'Audit Logs'}
91
94
  </h2>
92
95
  <p class="text-sm text-muted-foreground mt-1">
93
- {t('settings.auditDescription') ?? 'Track and review all system operations for compliance and debugging.'}
96
+ {i18n.t('settings.auditDescription') ?? 'Track and review all system operations for compliance and debugging.'}
94
97
  </p>
95
98
  </div>
96
99
 
@@ -104,7 +107,7 @@
104
107
  <Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
105
108
  <Input
106
109
  bind:value={searchQuery}
107
- placeholder={t('settings.searchLogs') ?? 'Search by user, action, or resource...'}
110
+ placeholder={i18n.t('settings.searchLogs') ?? 'Search by user, action, or resource...'}
108
111
  class="pl-9"
109
112
  />
110
113
  </div>
@@ -114,19 +117,19 @@
114
117
  {#if loading}
115
118
  <div class="flex items-center justify-center h-48 text-muted-foreground">
116
119
  <Loader2 class="h-5 w-5 animate-spin mr-2" />
117
- {t('common.loading')}
120
+ {i18n.t('common.loading')}
118
121
  </div>
119
122
  {:else}
120
123
  <div class="overflow-auto">
121
124
  <Table.Root>
122
125
  <Table.Header>
123
126
  <Table.Row>
124
- <Table.Head class="w-[160px]">{t('settings.auditTime') ?? 'Time'}</Table.Head>
125
- <Table.Head>{t('settings.auditUser') ?? 'User'}</Table.Head>
126
- <Table.Head>{t('settings.auditAction') ?? 'Action'}</Table.Head>
127
- <Table.Head>{t('settings.auditResource') ?? 'Resource'}</Table.Head>
128
- <Table.Head class="w-[120px]">{t('settings.auditIp') ?? 'IP Address'}</Table.Head>
129
- <Table.Head class="w-[60px] text-center">{t('common.detail') ?? 'Detail'}</Table.Head>
127
+ <Table.Head class="w-[160px]">{i18n.t('settings.auditTime') ?? 'Time'}</Table.Head>
128
+ <Table.Head>{i18n.t('settings.auditUser') ?? 'User'}</Table.Head>
129
+ <Table.Head>{i18n.t('settings.auditAction') ?? 'Action'}</Table.Head>
130
+ <Table.Head>{i18n.t('settings.auditResource') ?? 'Resource'}</Table.Head>
131
+ <Table.Head class="w-[120px]">{i18n.t('settings.auditIp') ?? 'IP Address'}</Table.Head>
132
+ <Table.Head class="w-[60px] text-center">{i18n.t('common.detail') ?? 'Detail'}</Table.Head>
130
133
  </Table.Row>
131
134
  </Table.Header>
132
135
  <Table.Body>
@@ -152,7 +155,7 @@
152
155
  {:else}
153
156
  <Table.Row>
154
157
  <Table.Cell colspan={6} class="h-24 text-center text-muted-foreground">
155
- {t('common.noData')}
158
+ {i18n.t('common.noData')}
156
159
  </Table.Cell>
157
160
  </Table.Row>
158
161
  {/each}
@@ -164,7 +167,7 @@
164
167
 
165
168
  <!-- Pagination -->
166
169
  <div class="mt-4 flex items-center justify-between text-sm text-muted-foreground">
167
- <span>{total} {t('settings.auditTotal') ?? 'total records'}</span>
170
+ <span>{total} {i18n.t('settings.auditTotal') ?? 'total records'}</span>
168
171
  <div class="flex items-center gap-2">
169
172
  <Button
170
173
  variant="outline" size="sm"
@@ -199,7 +202,7 @@
199
202
  <div>
200
203
  <h2 class="text-lg font-bold text-foreground flex items-center gap-2">
201
204
  <FileSearch class="h-5 w-5 text-primary" />
202
- {t('settings.auditSnapshot') ?? 'Change Snapshot'}
205
+ {i18n.t('settings.auditSnapshot') ?? 'Change Snapshot'}
203
206
  </h2>
204
207
  <p class="text-xs text-muted-foreground mt-1">{formatDate(drawerLog.createdAt)} · {drawerLog.userName ?? drawerLog.userId}</p>
205
208
  </div>
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
- import { useIsAuthenticated, getRouterProvider } from '@svadmin/core';
2
+ import { captureAdminContext, useIsAuthenticated } from '@svadmin/core';
3
3
  import type { Snippet } from 'svelte';
4
- import { navigate } from '@svadmin/core';
5
4
 
6
5
  let {
7
6
  children,
@@ -17,13 +16,14 @@
17
16
  appendCurrentPathToQuery?: boolean;
18
17
  }>();
19
18
 
19
+ const adminContext = captureAdminContext();
20
20
  const isAuth = useIsAuthenticated();
21
21
 
22
22
  function triggerRedirect() {
23
23
  if (fallback) return;
24
24
  let url = redirectTo;
25
25
  if (appendCurrentPathToQuery && typeof window !== 'undefined') {
26
- const rp = getRouterProvider();
26
+ const rp = adminContext.routerProvider;
27
27
  let currentString = window.location.pathname + window.location.search;
28
28
  if (rp) {
29
29
  const parsed = rp.parse();
@@ -33,7 +33,7 @@
33
33
  const current = encodeURIComponent(currentString);
34
34
  url += url.includes('?') ? `&to=${current}` : `?to=${current}`;
35
35
  }
36
- navigate(url);
36
+ adminContext.navigate(url);
37
37
  }
38
38
 
39
39
  $effect(() => {
@@ -1,9 +1,9 @@
1
1
  <script lang="ts">
2
- import { useForm, getResource, deriveValidator } from '@svadmin/core';
2
+ import { captureAdminContext, useForm, getResource, deriveValidator } from '@svadmin/core';
3
3
  import { slide } from 'svelte/transition';
4
4
  import type { FieldDefinition } from '@svadmin/core';
5
- import { t } from '@svadmin/core/i18n';
6
- import { navigate } from '@svadmin/core/router';
5
+ import { useTranslation } from '@svadmin/core/i18n';
6
+
7
7
  import { Button } from './ui/button/index.js';
8
8
  import TooltipButton from './TooltipButton.svelte';
9
9
  import * as Card from './ui/card/index.js';
@@ -15,6 +15,8 @@
15
15
  import ConfirmDialog from './ConfirmDialog.svelte';
16
16
  import type { Snippet } from 'svelte';
17
17
 
18
+ const i18n = useTranslation();
19
+
18
20
  interface Props {
19
21
  resourceName: string;
20
22
  id?: string | number;
@@ -26,6 +28,7 @@
26
28
  }
27
29
 
28
30
  let { resourceName, id, mode = 'create', fieldRenderer, formActions, headerContent, onSuccess }: Props = $props();
31
+ const adminContext = captureAdminContext();
29
32
  const isReadonly = $derived(mode === 'show');
30
33
 
31
34
  // ─── Resource metadata ────────────────────────────────────────────
@@ -73,7 +76,7 @@
73
76
  return d;
74
77
  })());
75
78
 
76
- const validator = $derived(deriveValidator(formFields));
79
+ const validator = $derived(deriveValidator(formFields, { translate: i18n.t }));
77
80
 
78
81
  // ─── useForm: single source of truth for values, errors, tainted ──
79
82
  const form = useForm({
@@ -95,16 +98,16 @@
95
98
  await form.submit();
96
99
  onSuccess?.();
97
100
  } catch (e) {
98
- submitError = e instanceof Error ? e.message : t('common.operationFailed');
101
+ submitError = e instanceof Error ? e.message : i18n.t('common.operationFailed');
99
102
  }
100
103
  }
101
104
 
102
105
  const pageTitle = $derived(
103
106
  mode === 'create'
104
- ? `${t('common.create')}${resource.label}`
107
+ ? `${i18n.t('common.create')}${resource.label}`
105
108
  : mode === 'show'
106
- ? `${t('common.detail')}${resource.label}`
107
- : `${t('common.edit')}${resource.label}`
109
+ ? `${i18n.t('common.detail')}${resource.label}`
110
+ : `${i18n.t('common.edit')}${resource.label}`
108
111
  );
109
112
 
110
113
  // ─── Unsaved changes guard ────────────────────────────────────────
@@ -135,8 +138,8 @@
135
138
  <div class="space-y-6">
136
139
  <div class="flex items-center gap-4">
137
140
  <TooltipButton
138
- tooltip={t('common.back')}
139
- onclick={() => guardNavigate(() => navigate(`/${resourceName}`))}
141
+ tooltip={i18n.t('common.back')}
142
+ onclick={() => guardNavigate(() => adminContext.navigate(`/${resourceName}`))}
140
143
  >
141
144
  <ArrowLeft class="h-5 w-5" />
142
145
  </TooltipButton>
@@ -145,7 +148,7 @@
145
148
  {@render headerContent()}
146
149
  {/if}
147
150
  {#if form.isTainted()}
148
- <Badge variant="outline" class="border-warning/30 bg-warning/10 text-warning-foreground">{t('common.unsaved')}</Badge>
151
+ <Badge variant="outline" class="border-warning/30 bg-warning/10 text-warning-foreground">{i18n.t('common.unsaved')}</Badge>
149
152
  {/if}
150
153
  </div>
151
154
 
@@ -234,14 +237,14 @@
234
237
  {:else}
235
238
  <Save class="h-4 w-4" data-icon="inline-start" />
236
239
  {/if}
237
- {t('common.save')}
240
+ {i18n.t('common.save')}
238
241
  </Button>
239
242
  <Button
240
243
  type="button"
241
244
  variant="outline"
242
- onclick={() => guardNavigate(() => navigate(`/${resourceName}`))}
245
+ onclick={() => guardNavigate(() => adminContext.navigate(`/${resourceName}`))}
243
246
  >
244
- {t('common.cancel')}
247
+ {i18n.t('common.cancel')}
245
248
  </Button>
246
249
  {/if}
247
250
  </div>
@@ -251,8 +254,8 @@
251
254
 
252
255
  <ConfirmDialog
253
256
  open={confirmOpen}
254
- message={t('common.unsavedChanges')}
255
- confirmText={t('common.confirm')}
257
+ message={i18n.t('common.unsavedChanges')}
258
+ confirmText={i18n.t('common.confirm')}
256
259
  onconfirm={confirmNavigate}
257
260
  oncancel={cancelNavigate}
258
261
  />
@@ -1,7 +1,9 @@
1
1
  <script lang="ts">
2
2
  import { fade, fly } from 'svelte/transition';
3
3
  import { Loader2, Check, X, Cloud } from '@lucide/svelte';
4
- import { t } from '@svadmin/core/i18n';
4
+ import { useTranslation } from '@svadmin/core/i18n';
5
+
6
+ const i18n = useTranslation();
5
7
 
6
8
  let {
7
9
  status = 'idle',
@@ -19,16 +21,16 @@
19
21
  <div class="flex items-center gap-2" in:fly={{ y: -8, duration: 200 }} out:fade={{ duration: 150 }}>
20
22
  {#if status === 'idle'}
21
23
  <Cloud class="h-4 w-4" />
22
- <span>{t('autoSave.idle')}</span>
24
+ <span>{i18n.t('autoSave.idle')}</span>
23
25
  {:else if status === 'loading'}
24
26
  <Loader2 class="h-4 w-4 animate-spin text-primary" />
25
- <span>{t('autoSave.saving')}</span>
27
+ <span>{i18n.t('autoSave.saving')}</span>
26
28
  {:else if status === 'success'}
27
29
  <Check class="h-4 w-4 text-green-500" />
28
- <span class="text-green-500">{t('autoSave.saved')}</span>
30
+ <span class="text-green-500">{i18n.t('autoSave.saved')}</span>
29
31
  {:else if status === 'error'}
30
32
  <X class="h-4 w-4 text-destructive" />
31
- <span class="text-destructive" title={error}>{t('autoSave.error')}</span>
33
+ <span class="text-destructive" title={error}>{i18n.t('autoSave.error')}</span>
32
34
  {/if}
33
35
  </div>
34
36
  {/key}