@svadmin/ui 0.29.1 → 0.29.3

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 (224) hide show
  1. package/package.json +20 -9
  2. package/src/README.md +0 -18
  3. package/src/actions.ts +0 -110
  4. package/src/app.css +0 -673
  5. package/src/component-registry.svelte.ts +0 -60
  6. package/src/components/AICommandBar.svelte +0 -180
  7. package/src/components/AboutSettings.svelte +0 -106
  8. package/src/components/AdminApp.svelte +0 -199
  9. package/src/components/AnimatedCounter.svelte +0 -46
  10. package/src/components/AnomalyBadge.svelte +0 -65
  11. package/src/components/AppearanceSettings.svelte +0 -183
  12. package/src/components/AuditLogDrawer.svelte +0 -93
  13. package/src/components/AuditLogViewer.svelte +0 -226
  14. package/src/components/Authenticated.svelte +0 -51
  15. package/src/components/AutoForm.svelte +0 -242
  16. package/src/components/AutoSaveIndicator.svelte +0 -35
  17. package/src/components/AutoTable.svelte +0 -839
  18. package/src/components/Breadcrumbs.svelte +0 -58
  19. package/src/components/CanAccess.svelte +0 -23
  20. package/src/components/CatchAllNavigate.svelte +0 -21
  21. package/src/components/ChatDialog.svelte +0 -535
  22. package/src/components/ComboboxField.svelte +0 -129
  23. package/src/components/CommandPalette.svelte +0 -135
  24. package/src/components/ConfigErrorScreen.svelte +0 -126
  25. package/src/components/ConfirmDialog.svelte +0 -53
  26. package/src/components/CopilotPanel.svelte +0 -163
  27. package/src/components/CreatePage.svelte +0 -39
  28. package/src/components/DevTools.svelte +0 -149
  29. package/src/components/DraggableGrid.svelte +0 -86
  30. package/src/components/DraggableHeader.svelte +0 -112
  31. package/src/components/DrawerForm.svelte +0 -35
  32. package/src/components/EditPage.svelte +0 -52
  33. package/src/components/EmptyState.svelte +0 -31
  34. package/src/components/ErrorBoundary.svelte +0 -49
  35. package/src/components/ErrorComponent.svelte +0 -36
  36. package/src/components/FieldRenderer.svelte +0 -357
  37. package/src/components/ForgotPasswordPage.svelte +0 -112
  38. package/src/components/Header.svelte +0 -94
  39. package/src/components/InferencerPanel.svelte +0 -192
  40. package/src/components/InfiniteList.svelte +0 -93
  41. package/src/components/InlineEdit.svelte +0 -88
  42. package/src/components/InsightCard.svelte +0 -98
  43. package/src/components/KeyboardShortcuts.svelte +0 -61
  44. package/src/components/Layout.svelte +0 -167
  45. package/src/components/ListPage.svelte +0 -71
  46. package/src/components/LiveIndicator.svelte +0 -58
  47. package/src/components/LoginPage.svelte +0 -163
  48. package/src/components/MarkdownRenderer.svelte +0 -127
  49. package/src/components/ModalForm.svelte +0 -38
  50. package/src/components/NavigateToResource.svelte +0 -20
  51. package/src/components/PageHeader.svelte +0 -39
  52. package/src/components/PageSkeleton.svelte +0 -71
  53. package/src/components/PasswordInput.svelte +0 -96
  54. package/src/components/PermissionMatrix.svelte +0 -185
  55. package/src/components/ProfilePage.svelte +0 -284
  56. package/src/components/RegisterPage.svelte +0 -112
  57. package/src/components/RolesSettings.svelte +0 -146
  58. package/src/components/SettingsPage.svelte +0 -113
  59. package/src/components/ShowPage.svelte +0 -73
  60. package/src/components/Sidebar.svelte +0 -417
  61. package/src/components/SidebarItem.svelte +0 -129
  62. package/src/components/SmartSuggest.svelte +0 -158
  63. package/src/components/StatsCard.svelte +0 -79
  64. package/src/components/StepsForm.svelte +0 -192
  65. package/src/components/TaskQueueDrawer.svelte +0 -158
  66. package/src/components/TenantSwitcher.svelte +0 -84
  67. package/src/components/ThemedTitle.svelte +0 -32
  68. package/src/components/Toast.svelte +0 -46
  69. package/src/components/TooltipButton.svelte +0 -42
  70. package/src/components/UndoableNotification.svelte +0 -69
  71. package/src/components/UpdatePasswordPage.svelte +0 -89
  72. package/src/components/VirtualTable.svelte +0 -76
  73. package/src/components/VoiceInput.svelte +0 -120
  74. package/src/components/buttons/CloneButton.svelte +0 -30
  75. package/src/components/buttons/CreateButton.svelte +0 -29
  76. package/src/components/buttons/DeleteButton.svelte +0 -59
  77. package/src/components/buttons/EditButton.svelte +0 -30
  78. package/src/components/buttons/ExportButton.svelte +0 -30
  79. package/src/components/buttons/ImportButton.svelte +0 -56
  80. package/src/components/buttons/ListButton.svelte +0 -23
  81. package/src/components/buttons/RefreshButton.svelte +0 -36
  82. package/src/components/buttons/SaveButton.svelte +0 -27
  83. package/src/components/buttons/ShowButton.svelte +0 -31
  84. package/src/components/buttons/index.ts +0 -10
  85. package/src/components/charts/BarChart.svelte +0 -69
  86. package/src/components/charts/LineChart.svelte +0 -115
  87. package/src/components/charts/PieChart.svelte +0 -95
  88. package/src/components/charts/index.ts +0 -3
  89. package/src/components/fieldComponentMap.ts +0 -79
  90. package/src/components/fields/ArrayField.svelte +0 -86
  91. package/src/components/fields/BooleanField.svelte +0 -13
  92. package/src/components/fields/DateField.svelte +0 -6
  93. package/src/components/fields/EmailField.svelte +0 -9
  94. package/src/components/fields/FileField.svelte +0 -14
  95. package/src/components/fields/ImageField.svelte +0 -9
  96. package/src/components/fields/JsonField.svelte +0 -72
  97. package/src/components/fields/MarkdownField.svelte +0 -9
  98. package/src/components/fields/MultiSelectField.svelte +0 -21
  99. package/src/components/fields/NumberField.svelte +0 -6
  100. package/src/components/fields/RelationField.svelte +0 -24
  101. package/src/components/fields/RichTextField.svelte +0 -35
  102. package/src/components/fields/SelectField.svelte +0 -14
  103. package/src/components/fields/TagField.svelte +0 -12
  104. package/src/components/fields/TextField.svelte +0 -11
  105. package/src/components/fields/UrlField.svelte +0 -14
  106. package/src/components/ui/alert/alert-description.svelte +0 -17
  107. package/src/components/ui/alert/alert-title.svelte +0 -17
  108. package/src/components/ui/alert/alert.svelte +0 -44
  109. package/src/components/ui/alert/index.ts +0 -15
  110. package/src/components/ui/alert-dialog/alert-dialog-content.svelte +0 -29
  111. package/src/components/ui/alert-dialog/alert-dialog-description.svelte +0 -16
  112. package/src/components/ui/alert-dialog/alert-dialog-footer.svelte +0 -11
  113. package/src/components/ui/alert-dialog/alert-dialog-header.svelte +0 -11
  114. package/src/components/ui/alert-dialog/alert-dialog-overlay.svelte +0 -16
  115. package/src/components/ui/alert-dialog/alert-dialog-portal.svelte +0 -6
  116. package/src/components/ui/alert-dialog/alert-dialog-title.svelte +0 -16
  117. package/src/components/ui/alert-dialog/index.ts +0 -20
  118. package/src/components/ui/avatar/avatar.svelte +0 -63
  119. package/src/components/ui/avatar/index.ts +0 -2
  120. package/src/components/ui/badge/badge.svelte +0 -49
  121. package/src/components/ui/badge/index.ts +0 -3
  122. package/src/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +0 -18
  123. package/src/components/ui/breadcrumb/breadcrumb-item.svelte +0 -15
  124. package/src/components/ui/breadcrumb/breadcrumb-link.svelte +0 -15
  125. package/src/components/ui/breadcrumb/breadcrumb-list.svelte +0 -15
  126. package/src/components/ui/breadcrumb/breadcrumb-page.svelte +0 -18
  127. package/src/components/ui/breadcrumb/breadcrumb-separator.svelte +0 -22
  128. package/src/components/ui/breadcrumb/breadcrumb.svelte +0 -11
  129. package/src/components/ui/breadcrumb/index.ts +0 -7
  130. package/src/components/ui/button/button.svelte +0 -90
  131. package/src/components/ui/button/index.ts +0 -18
  132. package/src/components/ui/card/card-action.svelte +0 -23
  133. package/src/components/ui/card/card-content.svelte +0 -20
  134. package/src/components/ui/card/card-description.svelte +0 -20
  135. package/src/components/ui/card/card-footer.svelte +0 -20
  136. package/src/components/ui/card/card-header.svelte +0 -23
  137. package/src/components/ui/card/card-title.svelte +0 -15
  138. package/src/components/ui/card/card.svelte +0 -22
  139. package/src/components/ui/card/index.ts +0 -25
  140. package/src/components/ui/checkbox/checkbox.svelte +0 -39
  141. package/src/components/ui/checkbox/index.ts +0 -6
  142. package/src/components/ui/collapsible/index.ts +0 -7
  143. package/src/components/ui/command/command-dialog.svelte +0 -31
  144. package/src/components/ui/command/index.ts +0 -2
  145. package/src/components/ui/context-menu/index.ts +0 -11
  146. package/src/components/ui/dialog/dialog-close.svelte +0 -11
  147. package/src/components/ui/dialog/dialog-content.svelte +0 -48
  148. package/src/components/ui/dialog/dialog-description.svelte +0 -17
  149. package/src/components/ui/dialog/dialog-footer.svelte +0 -32
  150. package/src/components/ui/dialog/dialog-header.svelte +0 -20
  151. package/src/components/ui/dialog/dialog-overlay.svelte +0 -17
  152. package/src/components/ui/dialog/dialog-portal.svelte +0 -7
  153. package/src/components/ui/dialog/dialog-title.svelte +0 -17
  154. package/src/components/ui/dialog/dialog-trigger.svelte +0 -11
  155. package/src/components/ui/dialog/dialog.svelte +0 -7
  156. package/src/components/ui/dialog/index.ts +0 -34
  157. package/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -43
  158. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -35
  159. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -36
  160. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -10
  161. package/src/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -27
  162. package/src/components/ui/dropdown-menu/dropdown-menu.svelte +0 -45
  163. package/src/components/ui/dropdown-menu/index.ts +0 -21
  164. package/src/components/ui/input/index.ts +0 -7
  165. package/src/components/ui/input/input.svelte +0 -48
  166. package/src/components/ui/label/index.ts +0 -1
  167. package/src/components/ui/label/label.svelte +0 -18
  168. package/src/components/ui/pagination/index.ts +0 -7
  169. package/src/components/ui/pagination/pagination-content.svelte +0 -11
  170. package/src/components/ui/pagination/pagination-ellipsis.svelte +0 -12
  171. package/src/components/ui/pagination/pagination-item.svelte +0 -11
  172. package/src/components/ui/pagination/pagination-link.svelte +0 -26
  173. package/src/components/ui/pagination/pagination-next.svelte +0 -22
  174. package/src/components/ui/pagination/pagination-previous.svelte +0 -22
  175. package/src/components/ui/pagination/pagination.svelte +0 -11
  176. package/src/components/ui/popover/index.ts +0 -12
  177. package/src/components/ui/popover/popover-content.svelte +0 -25
  178. package/src/components/ui/progress/index.ts +0 -1
  179. package/src/components/ui/progress/progress.svelte +0 -22
  180. package/src/components/ui/scroll-area/index.ts +0 -1
  181. package/src/components/ui/scroll-area/scroll-area.svelte +0 -20
  182. package/src/components/ui/select/index.ts +0 -2
  183. package/src/components/ui/select/select.svelte +0 -36
  184. package/src/components/ui/separator/index.ts +0 -7
  185. package/src/components/ui/separator/separator.svelte +0 -23
  186. package/src/components/ui/sheet/index.ts +0 -16
  187. package/src/components/ui/sheet/sheet-content.svelte +0 -28
  188. package/src/components/ui/sheet/sheet-description.svelte +0 -22
  189. package/src/components/ui/sheet/sheet-footer.svelte +0 -22
  190. package/src/components/ui/sheet/sheet-header.svelte +0 -22
  191. package/src/components/ui/sheet/sheet-title.svelte +0 -22
  192. package/src/components/ui/sheet/sheet.svelte +0 -78
  193. package/src/components/ui/skeleton/index.ts +0 -2
  194. package/src/components/ui/skeleton/skeleton.svelte +0 -21
  195. package/src/components/ui/switch/index.ts +0 -2
  196. package/src/components/ui/switch/switch.svelte +0 -49
  197. package/src/components/ui/table/index.ts +0 -28
  198. package/src/components/ui/table/table-body.svelte +0 -15
  199. package/src/components/ui/table/table-caption.svelte +0 -20
  200. package/src/components/ui/table/table-cell.svelte +0 -15
  201. package/src/components/ui/table/table-footer.svelte +0 -20
  202. package/src/components/ui/table/table-head.svelte +0 -15
  203. package/src/components/ui/table/table-header.svelte +0 -20
  204. package/src/components/ui/table/table-row.svelte +0 -15
  205. package/src/components/ui/table/table.svelte +0 -17
  206. package/src/components/ui/tabs/index.ts +0 -15
  207. package/src/components/ui/tabs/tabs-content.svelte +0 -30
  208. package/src/components/ui/tabs/tabs-list.svelte +0 -26
  209. package/src/components/ui/tabs/tabs-trigger.svelte +0 -37
  210. package/src/components/ui/tabs/tabs.svelte +0 -27
  211. package/src/components/ui/textarea/index.ts +0 -2
  212. package/src/components/ui/textarea/textarea.svelte +0 -24
  213. package/src/components/ui/tooltip/index.ts +0 -19
  214. package/src/components/ui/tooltip/tooltip-content.svelte +0 -52
  215. package/src/components/ui/tooltip/tooltip-portal.svelte +0 -7
  216. package/src/components/ui/tooltip/tooltip-provider.svelte +0 -7
  217. package/src/components/ui/tooltip/tooltip-trigger.svelte +0 -7
  218. package/src/components/ui/tooltip/tooltip.svelte +0 -10
  219. package/src/editor-config.svelte.ts +0 -23
  220. package/src/index.ts +0 -155
  221. package/src/markdown-deps.d.ts +0 -21
  222. package/src/router-state.svelte.ts +0 -65
  223. package/src/types.ts +0 -33
  224. package/src/utils.ts +0 -28
@@ -1,56 +0,0 @@
1
- <script lang="ts">
2
- import { useImport, useCan, t, type UseImportOptions } from '@svadmin/core';
3
- import { Button } from '../ui/button/index.js';
4
- import { Upload } from '@lucide/svelte';
5
-
6
- let { resource, hideText = false, onFinish, accessControl = { enabled: true, hideIfUnauthorized: true }, class: className = '' } = $props<{
7
- resource: string;
8
- hideText?: boolean;
9
- onFinish?: (result: { succeeded: unknown[]; errored: { request: unknown; error: unknown }[] }) => void;
10
- accessControl?: { enabled?: boolean; hideIfUnauthorized?: boolean };
11
- class?: string;
12
- }>();
13
-
14
- let fileInput: HTMLInputElement | undefined = $state();
15
-
16
- const importHook = useImport({
17
- get resource() { return resource; },
18
- onFinish: (result) => onFinish?.(result),
19
- });
20
-
21
- function triggerImport() {
22
- fileInput?.click();
23
- }
24
-
25
- function handleFileChange(e: Event) {
26
- const input = e.target as HTMLInputElement;
27
- const file = input.files?.[0];
28
- if (file) {
29
- importHook.handleChange({ file });
30
- input.value = ''; // reset for re-import
31
- }
32
- }
33
-
34
- const can = useCan(() => ({ resource, action: 'import', queryOptions: { enabled: accessControl?.enabled ?? true } }));
35
- const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
36
- </script>
37
-
38
- {#if !hidden}
39
- <input
40
- bind:this={fileInput}
41
- type="file"
42
- accept=".csv"
43
- style="display:none"
44
- onchange={handleFileChange}
45
- />
46
- <Button
47
- variant="outline"
48
- size={hideText ? 'icon' : 'sm'}
49
- class={className}
50
- disabled={importHook.isLoading || !can.allowed}
51
- onclick={triggerImport}
52
- >
53
- <Upload class="h-4 w-4" />
54
- {#if !hideText}<span class="ml-1">{t('common.import')}</span>{/if}
55
- </Button>
56
- {/if}
@@ -1,23 +0,0 @@
1
- <script lang="ts">
2
- import { useNavigation, t } from '@svadmin/core';
3
- import { Button } from '../ui/button/index.js';
4
- import { List } from '@lucide/svelte';
5
-
6
- let { resource, hideText = false, class: className = '' } = $props<{
7
- resource: string;
8
- hideText?: boolean;
9
- class?: string;
10
- }>();
11
-
12
- const nav = useNavigation();
13
- </script>
14
-
15
- <Button
16
- variant="outline"
17
- size={hideText ? 'icon' : 'sm'}
18
- class={className}
19
- onclick={() => nav.list(resource)}
20
- >
21
- <List class="h-4 w-4" />
22
- {#if !hideText}<span class="ml-1">{resource}</span>{/if}
23
- </Button>
@@ -1,36 +0,0 @@
1
- <script lang="ts">
2
- import { useInvalidate, t } from '@svadmin/core';
3
- import { Button } from '../ui/button/index.js';
4
- import { RefreshCw } from '@lucide/svelte';
5
-
6
- let { resource, hideText = false, class: className = '' } = $props<{
7
- resource: string;
8
- hideText?: boolean;
9
- class?: string;
10
- }>();
11
-
12
- const invalidate = useInvalidate();
13
- let spinning = $state(false);
14
- let spinTimer: ReturnType<typeof setTimeout> | undefined;
15
-
16
- $effect(() => {
17
- return () => { if (spinTimer) clearTimeout(spinTimer); };
18
- });
19
-
20
- function refresh() {
21
- spinning = true;
22
- invalidate({ resource, invalidates: ['list', 'many'] });
23
- if (spinTimer) clearTimeout(spinTimer);
24
- spinTimer = setTimeout(() => { spinning = false; }, 600);
25
- }
26
- </script>
27
-
28
- <Button
29
- variant="ghost"
30
- size={hideText ? 'icon' : 'sm'}
31
- class={className}
32
- onclick={refresh}
33
- >
34
- <RefreshCw class="h-4 w-4 {spinning ? 'animate-spin' : ''}" />
35
- {#if !hideText}<span class="ml-1">{t('common.retry')}</span>{/if}
36
- </Button>
@@ -1,27 +0,0 @@
1
- <script lang="ts">
2
- import { t } from '@svadmin/core';
3
- import { Button } from '../ui/button/index.js';
4
- import { Save, Loader2 } from '@lucide/svelte';
5
-
6
- let { loading = false, hideText = false, type = 'submit', class: className = '' } = $props<{
7
- loading?: boolean;
8
- hideText?: boolean;
9
- type?: 'submit' | 'button';
10
- class?: string;
11
- }>();
12
- </script>
13
-
14
- <Button
15
- {type}
16
- variant="default"
17
- size={hideText ? 'icon' : 'default'}
18
- class={className}
19
- disabled={loading}
20
- >
21
- {#if loading}
22
- <Loader2 class="h-4 w-4 animate-spin" />
23
- {:else}
24
- <Save class="h-4 w-4" />
25
- {/if}
26
- {#if !hideText}<span class="ml-1">{t('common.save')}</span>{/if}
27
- </Button>
@@ -1,31 +0,0 @@
1
- <script lang="ts">
2
- import { useNavigation, useCan, t } from '@svadmin/core';
3
- import { Button } from '../ui/button/index.js';
4
- import { Eye } from '@lucide/svelte';
5
-
6
- let { resource, recordItemId, hideText = false, accessControl = { enabled: true, hideIfUnauthorized: true }, class: className = '' } = $props<{
7
- resource: string;
8
- recordItemId: string | number;
9
- hideText?: boolean;
10
- accessControl?: { enabled?: boolean; hideIfUnauthorized?: boolean };
11
- class?: string;
12
- }>();
13
-
14
- const nav = useNavigation();
15
- const can = useCan(() => ({ resource, action: 'show', queryOptions: { enabled: accessControl?.enabled ?? true } }));
16
- const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
17
- </script>
18
-
19
- {#if !hidden}
20
-
21
- <Button
22
- variant="ghost"
23
- size={hideText ? 'icon' : 'sm'}
24
- class={className}
25
- disabled={!can.allowed}
26
- onclick={() => nav.show(resource, recordItemId)}
27
- >
28
- <Eye class="h-4 w-4" />
29
- {#if !hideText}<span class="ml-1">{t('common.detail')}</span>{/if}
30
- </Button>
31
- {/if}
@@ -1,10 +0,0 @@
1
- export { default as CreateButton } from './CreateButton.svelte';
2
- export { default as EditButton } from './EditButton.svelte';
3
- export { default as DeleteButton } from './DeleteButton.svelte';
4
- export { default as ShowButton } from './ShowButton.svelte';
5
- export { default as ListButton } from './ListButton.svelte';
6
- export { default as RefreshButton } from './RefreshButton.svelte';
7
- export { default as ExportButton } from './ExportButton.svelte';
8
- export { default as ImportButton } from './ImportButton.svelte';
9
- export { default as SaveButton } from './SaveButton.svelte';
10
- export { default as CloneButton } from './CloneButton.svelte';
@@ -1,69 +0,0 @@
1
- <script lang="ts">
2
- interface DataPoint {
3
- label: string;
4
- value: number;
5
- color?: string;
6
- }
7
-
8
- interface Props {
9
- data: DataPoint[];
10
- height?: number;
11
- /** Show value labels on top of bars */
12
- showValues?: boolean;
13
- class?: string;
14
- }
15
-
16
- let { data, height = 200, showValues = true, class: className = '' }: Props = $props();
17
-
18
- const maxValue = $derived(Math.max(...data.map(d => d.value), 1));
19
- const barWidth = $derived(Math.max(20, Math.min(60, 400 / data.length)));
20
- const gap = $derived(Math.max(4, barWidth * 0.3));
21
- const totalWidth = $derived(data.length * (barWidth + gap) - gap + 40);
22
- </script>
23
-
24
- <div class="w-full overflow-x-auto {className}">
25
- <svg viewBox="0 0 {totalWidth} {height + 30}" class="w-full" style="min-width: {Math.min(totalWidth, 300)}px;">
26
- <!-- Bars -->
27
- {#each data as point, i}
28
- {@const barHeight = (point.value / maxValue) * (height - 20)}
29
- {@const x = 20 + i * (barWidth + gap)}
30
- {@const y = height - barHeight}
31
-
32
- <!-- Bar with animation -->
33
- <rect
34
- {x}
35
- {y}
36
- width={barWidth}
37
- height={barHeight}
38
- rx="4"
39
- fill={point.color || 'var(--primary)'}
40
- opacity="0.85"
41
- class="transition-all duration-500 hover:opacity-100"
42
- >
43
- <animate attributeName="height" from="0" to={barHeight} dur="0.6s" fill="freeze" calcMode="spline" keySplines="0.16 1 0.3 1" />
44
- <animate attributeName="y" from={height} to={y} dur="0.6s" fill="freeze" calcMode="spline" keySplines="0.16 1 0.3 1" />
45
- </rect>
46
-
47
- <!-- Value label -->
48
- {#if showValues}
49
- <text
50
- x={x + barWidth / 2}
51
- y={y - 6}
52
- text-anchor="middle"
53
- fill="var(--muted-foreground)"
54
- font-size="11"
55
- font-weight="500"
56
- >{point.value}</text>
57
- {/if}
58
-
59
- <!-- X-axis label -->
60
- <text
61
- x={x + barWidth / 2}
62
- y={height + 16}
63
- text-anchor="middle"
64
- fill="var(--muted-foreground)"
65
- font-size="10"
66
- >{point.label}</text>
67
- {/each}
68
- </svg>
69
- </div>
@@ -1,115 +0,0 @@
1
- <script lang="ts">
2
- interface DataPoint {
3
- label: string;
4
- value: number;
5
- }
6
-
7
- interface Props {
8
- data: DataPoint[];
9
- height?: number;
10
- color?: string;
11
- /** Show dots at data points */
12
- showDots?: boolean;
13
- /** Fill area under the line */
14
- fill?: boolean;
15
- class?: string;
16
- }
17
-
18
- let { data, height = 200, color = 'var(--primary)', showDots = true, fill = true, class: className = '' }: Props = $props();
19
-
20
- const padding = { top: 20, right: 20, bottom: 30, left: 40 };
21
- const chartWidth = $derived(Math.max(data.length * 60, 300));
22
- const chartHeight = $derived(height - padding.top - padding.bottom);
23
-
24
- const maxValue = $derived(Math.max(...data.map(d => d.value), 1));
25
- const minValue = $derived(Math.min(...data.map(d => d.value), 0));
26
- const range = $derived(maxValue - minValue || 1);
27
-
28
- function getX(i: number): number {
29
- if (data.length <= 1) return padding.left + chartWidth / 2;
30
- return padding.left + (i / (data.length - 1)) * (chartWidth - padding.left - padding.right);
31
- }
32
-
33
- function getY(value: number): number {
34
- return padding.top + chartHeight - ((value - minValue) / range) * chartHeight;
35
- }
36
-
37
- const linePath = $derived(
38
- data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${getX(i)} ${getY(d.value)}`).join(' ')
39
- );
40
-
41
- const areaPath = $derived(
42
- linePath + ` L ${getX(data.length - 1)} ${padding.top + chartHeight} L ${getX(0)} ${padding.top + chartHeight} Z`
43
- );
44
- </script>
45
-
46
- <div class="w-full overflow-x-auto {className}">
47
- <svg viewBox="0 0 {chartWidth} {height}" class="w-full" style="min-width: {Math.min(chartWidth, 300)}px;">
48
- <!-- Grid lines -->
49
- {#each [0, 0.25, 0.5, 0.75, 1] as ratio}
50
- {@const y = padding.top + chartHeight * (1 - ratio)}
51
- <line
52
- x1={padding.left}
53
- y1={y}
54
- x2={chartWidth - padding.right}
55
- y2={y}
56
- stroke="var(--border)"
57
- stroke-width="1"
58
- stroke-dasharray={ratio === 0 ? 'none' : '4 4'}
59
- opacity="0.5"
60
- />
61
- <text x={padding.left - 8} y={y + 4} text-anchor="end" fill="var(--muted-foreground)" font-size="10">
62
- {Math.round(minValue + range * ratio)}
63
- </text>
64
- {/each}
65
-
66
- <!-- Area fill -->
67
- {#if fill && data.length > 1}
68
- <path
69
- d={areaPath}
70
- fill={color}
71
- opacity="0.08"
72
- />
73
- {/if}
74
-
75
- <!-- Line -->
76
- {#if data.length > 1}
77
- <path
78
- d={linePath}
79
- fill="none"
80
- stroke={color}
81
- stroke-width="2"
82
- stroke-linecap="round"
83
- stroke-linejoin="round"
84
- >
85
- <animate attributeName="stroke-dashoffset" from="2000" to="0" dur="1s" fill="freeze" />
86
- </path>
87
- {/if}
88
-
89
- <!-- Dots -->
90
- {#if showDots}
91
- {#each data as point, i}
92
- <circle
93
- cx={getX(i)}
94
- cy={getY(point.value)}
95
- r="4"
96
- fill="var(--background)"
97
- stroke={color}
98
- stroke-width="2"
99
- class="transition-all duration-200 hover:r-[6]"
100
- />
101
- {/each}
102
- {/if}
103
-
104
- <!-- X-axis labels -->
105
- {#each data as point, i}
106
- <text
107
- x={getX(i)}
108
- y={height - 4}
109
- text-anchor="middle"
110
- fill="var(--muted-foreground)"
111
- font-size="10"
112
- >{point.label}</text>
113
- {/each}
114
- </svg>
115
- </div>
@@ -1,95 +0,0 @@
1
- <script lang="ts">
2
- interface Slice {
3
- label: string;
4
- value: number;
5
- color: string;
6
- }
7
-
8
- interface Props {
9
- data: Slice[];
10
- size?: number;
11
- /** Donut hole ratio (0 = pie, 0.6 = donut) */
12
- donut?: number;
13
- class?: string;
14
- }
15
-
16
- let { data, size = 200, donut = 0.6, class: className = '' }: Props = $props();
17
-
18
- const total = $derived(data.reduce((sum, d) => sum + d.value, 0) || 1);
19
- const center = $derived(size / 2);
20
- const radius = $derived(size / 2 - 8);
21
- const innerRadius = $derived(radius * donut);
22
-
23
- function getArcPath(startAngle: number, endAngle: number, outer: number, inner: number): string {
24
- const startOuter = polarToCartesian(center, center, outer, endAngle);
25
- const endOuter = polarToCartesian(center, center, outer, startAngle);
26
- const startInner = polarToCartesian(center, center, inner, startAngle);
27
- const endInner = polarToCartesian(center, center, inner, endAngle);
28
- const largeArc = endAngle - startAngle > 180 ? 1 : 0;
29
-
30
- return [
31
- `M ${startOuter.x} ${startOuter.y}`,
32
- `A ${outer} ${outer} 0 ${largeArc} 0 ${endOuter.x} ${endOuter.y}`,
33
- inner > 0 ? `L ${startInner.x} ${startInner.y}` : `L ${center} ${center}`,
34
- inner > 0 ? `A ${inner} ${inner} 0 ${largeArc} 1 ${endInner.x} ${endInner.y}` : '',
35
- 'Z',
36
- ].join(' ');
37
- }
38
-
39
- function polarToCartesian(cx: number, cy: number, r: number, angleDeg: number) {
40
- const rad = ((angleDeg - 90) * Math.PI) / 180;
41
- return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
42
- }
43
-
44
- const slices = $derived.by(() => {
45
- let currentAngle = 0;
46
- return data.map(d => {
47
- const angle = (d.value / total) * 360;
48
- const slice = {
49
- ...d,
50
- startAngle: currentAngle,
51
- endAngle: currentAngle + angle,
52
- percentage: ((d.value / total) * 100).toFixed(1),
53
- };
54
- currentAngle += angle;
55
- return slice;
56
- });
57
- });
58
- </script>
59
-
60
- <div class="inline-flex flex-col items-center gap-3 {className}">
61
- <svg viewBox="0 0 {size} {size}" width={size} height={size}>
62
- {#each slices as slice, i}
63
- <path
64
- d={getArcPath(slice.startAngle, slice.endAngle, radius, innerRadius)}
65
- fill={slice.color}
66
- class="transition-all duration-200 hover:opacity-80"
67
- stroke="var(--background)"
68
- stroke-width="2"
69
- >
70
- <animate attributeName="opacity" from="0" to="1" dur="0.4s" begin="{i * 0.1}s" fill="freeze" />
71
- </path>
72
- {/each}
73
-
74
- <!-- Center label (donut only) -->
75
- {#if donut > 0}
76
- <text x={center} y={center - 6} text-anchor="middle" fill="var(--foreground)" font-size="20" font-weight="600">
77
- {total.toLocaleString()}
78
- </text>
79
- <text x={center} y={center + 12} text-anchor="middle" fill="var(--muted-foreground)" font-size="11">
80
- Total
81
- </text>
82
- {/if}
83
- </svg>
84
-
85
- <!-- Legend -->
86
- <div class="flex flex-wrap justify-center gap-x-4 gap-y-1">
87
- {#each slices as slice}
88
- <div class="flex items-center gap-1.5 text-xs">
89
- <span class="h-2.5 w-2.5 rounded-full" style="background-color: {slice.color};"></span>
90
- <span class="text-muted-foreground">{slice.label}</span>
91
- <span class="font-medium text-foreground">{slice.percentage}%</span>
92
- </div>
93
- {/each}
94
- </div>
95
- </div>
@@ -1,3 +0,0 @@
1
- export { default as BarChart } from './BarChart.svelte';
2
- export { default as LineChart } from './LineChart.svelte';
3
- export { default as PieChart } from './PieChart.svelte';
@@ -1,79 +0,0 @@
1
- // Shared field type → display component mapping
2
- // Used by both ShowPage and FieldRenderer to avoid duplicate if/else chains.
3
- // Components are lazily resolved by field type at runtime.
4
-
5
- import type { Component } from 'svelte';
6
-
7
- export interface FieldComponentMap {
8
- [fieldType: string]: Component<any>;
9
- }
10
-
11
- // Default display component map (read-only field display)
12
- // Dynamically imports components only when first used
13
- const displayCache = new Map<string, Component<any>>();
14
-
15
- // Pre-import all field display components (tree-shaken by bundler)
16
- import BooleanField from './fields/BooleanField.svelte';
17
- import ImageField from './fields/ImageField.svelte';
18
- import TagField from './fields/TagField.svelte';
19
- import DateField from './fields/DateField.svelte';
20
- import EmailField from './fields/EmailField.svelte';
21
- import UrlField from './fields/UrlField.svelte';
22
- import SelectField from './fields/SelectField.svelte';
23
- import MultiSelectField from './fields/MultiSelectField.svelte';
24
- import RelationField from './fields/RelationField.svelte';
25
- import JsonField from './fields/JsonField.svelte';
26
- import RichTextField from './fields/RichTextField.svelte';
27
- import NumberField from './fields/NumberField.svelte';
28
- import FileField from './fields/FileField.svelte';
29
-
30
- /**
31
- * Built-in display components for show/detail pages.
32
- * Keys match FieldDefinition.type values.
33
- */
34
- export const builtinDisplayComponents: FieldComponentMap = {
35
- boolean: BooleanField,
36
- image: ImageField,
37
- images: ImageField,
38
- tags: TagField,
39
- date: DateField,
40
- email: EmailField,
41
- url: UrlField,
42
- select: SelectField,
43
- multiselect: MultiSelectField,
44
- relation: RelationField,
45
- json: JsonField,
46
- richtext: RichTextField,
47
- textarea: RichTextField,
48
- number: NumberField,
49
- file: FileField,
50
- };
51
-
52
- // ─── Custom component registry ─────────────────────────────────
53
- // Users can register custom field display components at runtime
54
-
55
- const customDisplayComponents = new Map<string, Component<any>>();
56
-
57
- /**
58
- * Register a custom field display component for a given field type.
59
- * The component will be used by ShowPage and any other display contexts.
60
- */
61
- export function registerDisplayComponent(fieldType: string, component: Component<any>): void {
62
- customDisplayComponents.set(fieldType, component);
63
- }
64
-
65
- /**
66
- * Get the display component for a field type.
67
- * Custom components take precedence over built-in ones.
68
- * Returns undefined if no component is registered for the type.
69
- */
70
- export function getDisplayComponent(fieldType: string): Component<any> | undefined {
71
- return customDisplayComponents.get(fieldType) ?? builtinDisplayComponents[fieldType];
72
- }
73
-
74
- /**
75
- * Check if a field type has a display component (custom or built-in).
76
- */
77
- export function hasDisplayComponent(fieldType: string): boolean {
78
- return customDisplayComponents.has(fieldType) || fieldType in builtinDisplayComponents;
79
- }
@@ -1,86 +0,0 @@
1
- <script lang="ts">
2
- import type { FieldDefinition } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
4
- import { Button } from '../ui/button/index.js';
5
- import { Plus, Trash2 } from '@lucide/svelte';
6
- import TooltipButton from '../TooltipButton.svelte';
7
- import FieldRenderer from '../FieldRenderer.svelte';
8
-
9
- let { field, value, onchange } = $props<{
10
- field: FieldDefinition;
11
- value: unknown;
12
- onchange: (val: unknown) => void;
13
- }>();
14
-
15
- const arrayVal = $derived(Array.isArray(value) ? value : []);
16
- const subFields = $derived(field.subFields || []);
17
-
18
- function getDefaultForType(type: string): unknown {
19
- switch (type) {
20
- case 'text': case 'textarea': case 'richtext': case 'image': return '';
21
- case 'number': return 0;
22
- case 'boolean': return false;
23
- case 'tags': case 'images': case 'multiselect': case 'array': return [];
24
- case 'json': return {};
25
- default: return '';
26
- }
27
- }
28
-
29
- function handleAdd() {
30
- const newItem: Record<string, unknown> = {};
31
- for (const f of subFields) {
32
- newItem[f.key] = f.defaultValue ?? getDefaultForType(f.type);
33
- }
34
- onchange([...arrayVal, newItem]);
35
- }
36
-
37
- function handleRemove(index: number) {
38
- onchange(arrayVal.filter((_, i) => i !== index));
39
- }
40
-
41
- function handleChange(index: number, key: string, val: unknown) {
42
- const next = [...arrayVal];
43
- next[index] = { ...next[index], [key]: val };
44
- onchange(next);
45
- }
46
- </script>
47
-
48
- <div class="space-y-4 rounded-lg border border-dashed p-4">
49
- <div class="flex items-center justify-between">
50
- <div class="text-sm font-medium text-foreground">
51
- {field.label}
52
- {#if field.required}
53
- <span class="text-destructive">*</span>
54
- {/if}
55
- </div>
56
- <Button variant="outline" size="sm" type="button" onclick={handleAdd}>
57
- <Plus class="h-3.5 w-3.5 mr-1" /> {t('common.add') || 'Add'}
58
- </Button>
59
- </div>
60
-
61
- <div class="space-y-4 pt-2">
62
- {#each arrayVal as item, i}
63
- <div class="relative rounded-md border bg-card p-4 pt-6 shadow-sm">
64
- <div class="absolute right-2 top-2">
65
- <TooltipButton tooltip={t('common.remove') || 'Remove'} variant="ghost" size="icon" class="h-8 w-8 text-muted-foreground hover:text-destructive transition-colors" onclick={() => handleRemove(i)}>
66
- <Trash2 class="h-4 w-4" />
67
- </TooltipButton>
68
- </div>
69
- <div class="grid gap-5">
70
- {#each subFields as subField (subField.key)}
71
- <FieldRenderer
72
- field={subField}
73
- value={item[subField.key]}
74
- onchange={(val) => handleChange(i, subField.key, val)}
75
- />
76
- {/each}
77
- </div>
78
- </div>
79
- {/each}
80
- {#if arrayVal.length === 0}
81
- <div class="flex flex-col items-center justify-center py-6 text-sm text-muted-foreground bg-muted/10 rounded-md">
82
- <span>{t('common.noData') || 'No items added yet.'}</span>
83
- </div>
84
- {/if}
85
- </div>
86
- </div>
@@ -1,13 +0,0 @@
1
- <script lang="ts">
2
- interface Props { value: boolean | null | undefined; trueIcon?: string; falseIcon?: string }
3
- let { value, trueIcon = '✓', falseIcon = '✗' }: Props = $props();
4
- </script>
5
- {#if value}
6
- <span class="inline-flex items-center justify-center w-6 h-6 rounded-full text-sm bg-success/10 text-success">
7
- {trueIcon}
8
- </span>
9
- {:else}
10
- <span class="inline-flex items-center justify-center w-6 h-6 rounded-full text-sm bg-destructive/10 text-destructive">
11
- {falseIcon}
12
- </span>
13
- {/if}
@@ -1,6 +0,0 @@
1
- <script lang="ts">
2
- interface Props { value: string | number | Date | null | undefined; locale?: string; options?: Intl.DateTimeFormatOptions }
3
- let { value, locale = 'en-US', options = { dateStyle: 'medium' } }: Props = $props();
4
- const formatted = $derived(value != null ? new Intl.DateTimeFormat(locale, options).format(new Date(value as string | number | Date)) : '—');
5
- </script>
6
- <span class="field-date">{formatted}</span>
@@ -1,9 +0,0 @@
1
- <script lang="ts">
2
- interface Props { value: string | null | undefined }
3
- let { value }: Props = $props();
4
- </script>
5
- {#if value}
6
- <a href="mailto:{value}" class="text-primary hover:underline no-underline">{value}</a>
7
- {:else}
8
- <span class="text-muted-foreground">—</span>
9
- {/if}