@svadmin/ui 0.29.0 → 0.29.2
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.
- package/package.json +20 -9
- package/src/README.md +0 -18
- package/src/actions.ts +0 -110
- package/src/app.css +0 -519
- package/src/component-registry.svelte.ts +0 -60
- package/src/components/AICommandBar.svelte +0 -180
- package/src/components/AboutSettings.svelte +0 -106
- package/src/components/AdminApp.svelte +0 -199
- package/src/components/AnimatedCounter.svelte +0 -46
- package/src/components/AnomalyBadge.svelte +0 -65
- package/src/components/AppearanceSettings.svelte +0 -183
- package/src/components/AuditLogDrawer.svelte +0 -93
- package/src/components/AuditLogViewer.svelte +0 -226
- package/src/components/Authenticated.svelte +0 -51
- package/src/components/AutoForm.svelte +0 -242
- package/src/components/AutoSaveIndicator.svelte +0 -35
- package/src/components/AutoTable.svelte +0 -839
- package/src/components/Breadcrumbs.svelte +0 -58
- package/src/components/CanAccess.svelte +0 -23
- package/src/components/CatchAllNavigate.svelte +0 -21
- package/src/components/ChatDialog.svelte +0 -535
- package/src/components/ComboboxField.svelte +0 -129
- package/src/components/CommandPalette.svelte +0 -135
- package/src/components/ConfigErrorScreen.svelte +0 -126
- package/src/components/ConfirmDialog.svelte +0 -53
- package/src/components/CopilotPanel.svelte +0 -163
- package/src/components/CreatePage.svelte +0 -39
- package/src/components/DevTools.svelte +0 -149
- package/src/components/DraggableGrid.svelte +0 -86
- package/src/components/DraggableHeader.svelte +0 -112
- package/src/components/DrawerForm.svelte +0 -35
- package/src/components/EditPage.svelte +0 -52
- package/src/components/EmptyState.svelte +0 -31
- package/src/components/ErrorBoundary.svelte +0 -49
- package/src/components/ErrorComponent.svelte +0 -36
- package/src/components/FieldRenderer.svelte +0 -357
- package/src/components/ForgotPasswordPage.svelte +0 -112
- package/src/components/Header.svelte +0 -94
- package/src/components/InferencerPanel.svelte +0 -192
- package/src/components/InfiniteList.svelte +0 -93
- package/src/components/InlineEdit.svelte +0 -88
- package/src/components/InsightCard.svelte +0 -98
- package/src/components/KeyboardShortcuts.svelte +0 -61
- package/src/components/Layout.svelte +0 -167
- package/src/components/ListPage.svelte +0 -71
- package/src/components/LiveIndicator.svelte +0 -58
- package/src/components/LoginPage.svelte +0 -163
- package/src/components/MarkdownRenderer.svelte +0 -127
- package/src/components/ModalForm.svelte +0 -38
- package/src/components/NavigateToResource.svelte +0 -20
- package/src/components/PageHeader.svelte +0 -39
- package/src/components/PageSkeleton.svelte +0 -71
- package/src/components/PasswordInput.svelte +0 -96
- package/src/components/PermissionMatrix.svelte +0 -185
- package/src/components/ProfilePage.svelte +0 -284
- package/src/components/RegisterPage.svelte +0 -112
- package/src/components/RolesSettings.svelte +0 -146
- package/src/components/SettingsPage.svelte +0 -113
- package/src/components/ShowPage.svelte +0 -73
- package/src/components/Sidebar.svelte +0 -417
- package/src/components/SidebarItem.svelte +0 -129
- package/src/components/SmartSuggest.svelte +0 -158
- package/src/components/StatsCard.svelte +0 -79
- package/src/components/StepsForm.svelte +0 -192
- package/src/components/TaskQueueDrawer.svelte +0 -158
- package/src/components/TenantSwitcher.svelte +0 -84
- package/src/components/ThemedTitle.svelte +0 -32
- package/src/components/Toast.svelte +0 -46
- package/src/components/TooltipButton.svelte +0 -42
- package/src/components/UndoableNotification.svelte +0 -69
- package/src/components/UpdatePasswordPage.svelte +0 -89
- package/src/components/VirtualTable.svelte +0 -76
- package/src/components/VoiceInput.svelte +0 -120
- package/src/components/buttons/CloneButton.svelte +0 -30
- package/src/components/buttons/CreateButton.svelte +0 -29
- package/src/components/buttons/DeleteButton.svelte +0 -59
- package/src/components/buttons/EditButton.svelte +0 -30
- package/src/components/buttons/ExportButton.svelte +0 -30
- package/src/components/buttons/ImportButton.svelte +0 -56
- package/src/components/buttons/ListButton.svelte +0 -23
- package/src/components/buttons/RefreshButton.svelte +0 -36
- package/src/components/buttons/SaveButton.svelte +0 -27
- package/src/components/buttons/ShowButton.svelte +0 -31
- package/src/components/buttons/index.ts +0 -10
- package/src/components/charts/BarChart.svelte +0 -69
- package/src/components/charts/LineChart.svelte +0 -115
- package/src/components/charts/PieChart.svelte +0 -95
- package/src/components/charts/index.ts +0 -3
- package/src/components/fieldComponentMap.ts +0 -79
- package/src/components/fields/ArrayField.svelte +0 -86
- package/src/components/fields/BooleanField.svelte +0 -13
- package/src/components/fields/DateField.svelte +0 -6
- package/src/components/fields/EmailField.svelte +0 -9
- package/src/components/fields/FileField.svelte +0 -14
- package/src/components/fields/ImageField.svelte +0 -9
- package/src/components/fields/JsonField.svelte +0 -72
- package/src/components/fields/MarkdownField.svelte +0 -9
- package/src/components/fields/MultiSelectField.svelte +0 -21
- package/src/components/fields/NumberField.svelte +0 -6
- package/src/components/fields/RelationField.svelte +0 -24
- package/src/components/fields/RichTextField.svelte +0 -35
- package/src/components/fields/SelectField.svelte +0 -14
- package/src/components/fields/TagField.svelte +0 -12
- package/src/components/fields/TextField.svelte +0 -11
- package/src/components/fields/UrlField.svelte +0 -14
- package/src/components/ui/alert/alert-description.svelte +0 -17
- package/src/components/ui/alert/alert-title.svelte +0 -17
- package/src/components/ui/alert/alert.svelte +0 -44
- package/src/components/ui/alert/index.ts +0 -15
- package/src/components/ui/alert-dialog/alert-dialog-content.svelte +0 -29
- package/src/components/ui/alert-dialog/alert-dialog-description.svelte +0 -16
- package/src/components/ui/alert-dialog/alert-dialog-footer.svelte +0 -11
- package/src/components/ui/alert-dialog/alert-dialog-header.svelte +0 -11
- package/src/components/ui/alert-dialog/alert-dialog-overlay.svelte +0 -16
- package/src/components/ui/alert-dialog/alert-dialog-portal.svelte +0 -6
- package/src/components/ui/alert-dialog/alert-dialog-title.svelte +0 -16
- package/src/components/ui/alert-dialog/index.ts +0 -20
- package/src/components/ui/avatar/avatar.svelte +0 -63
- package/src/components/ui/avatar/index.ts +0 -2
- package/src/components/ui/badge/badge.svelte +0 -49
- package/src/components/ui/badge/index.ts +0 -3
- package/src/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +0 -18
- package/src/components/ui/breadcrumb/breadcrumb-item.svelte +0 -15
- package/src/components/ui/breadcrumb/breadcrumb-link.svelte +0 -15
- package/src/components/ui/breadcrumb/breadcrumb-list.svelte +0 -15
- package/src/components/ui/breadcrumb/breadcrumb-page.svelte +0 -18
- package/src/components/ui/breadcrumb/breadcrumb-separator.svelte +0 -22
- package/src/components/ui/breadcrumb/breadcrumb.svelte +0 -11
- package/src/components/ui/breadcrumb/index.ts +0 -7
- package/src/components/ui/button/button.svelte +0 -90
- package/src/components/ui/button/index.ts +0 -18
- package/src/components/ui/card/card-action.svelte +0 -23
- package/src/components/ui/card/card-content.svelte +0 -20
- package/src/components/ui/card/card-description.svelte +0 -20
- package/src/components/ui/card/card-footer.svelte +0 -20
- package/src/components/ui/card/card-header.svelte +0 -23
- package/src/components/ui/card/card-title.svelte +0 -15
- package/src/components/ui/card/card.svelte +0 -22
- package/src/components/ui/card/index.ts +0 -25
- package/src/components/ui/checkbox/checkbox.svelte +0 -39
- package/src/components/ui/checkbox/index.ts +0 -6
- package/src/components/ui/collapsible/index.ts +0 -7
- package/src/components/ui/command/command-dialog.svelte +0 -31
- package/src/components/ui/command/index.ts +0 -2
- package/src/components/ui/context-menu/index.ts +0 -11
- package/src/components/ui/dialog/dialog-close.svelte +0 -11
- package/src/components/ui/dialog/dialog-content.svelte +0 -48
- package/src/components/ui/dialog/dialog-description.svelte +0 -17
- package/src/components/ui/dialog/dialog-footer.svelte +0 -32
- package/src/components/ui/dialog/dialog-header.svelte +0 -20
- package/src/components/ui/dialog/dialog-overlay.svelte +0 -17
- package/src/components/ui/dialog/dialog-portal.svelte +0 -7
- package/src/components/ui/dialog/dialog-title.svelte +0 -17
- package/src/components/ui/dialog/dialog-trigger.svelte +0 -11
- package/src/components/ui/dialog/dialog.svelte +0 -7
- package/src/components/ui/dialog/index.ts +0 -34
- package/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -43
- package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -35
- package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -36
- package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -10
- package/src/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -27
- package/src/components/ui/dropdown-menu/dropdown-menu.svelte +0 -45
- package/src/components/ui/dropdown-menu/index.ts +0 -21
- package/src/components/ui/input/index.ts +0 -7
- package/src/components/ui/input/input.svelte +0 -48
- package/src/components/ui/label/index.ts +0 -1
- package/src/components/ui/label/label.svelte +0 -18
- package/src/components/ui/pagination/index.ts +0 -7
- package/src/components/ui/pagination/pagination-content.svelte +0 -11
- package/src/components/ui/pagination/pagination-ellipsis.svelte +0 -12
- package/src/components/ui/pagination/pagination-item.svelte +0 -11
- package/src/components/ui/pagination/pagination-link.svelte +0 -26
- package/src/components/ui/pagination/pagination-next.svelte +0 -22
- package/src/components/ui/pagination/pagination-previous.svelte +0 -22
- package/src/components/ui/pagination/pagination.svelte +0 -11
- package/src/components/ui/popover/index.ts +0 -12
- package/src/components/ui/popover/popover-content.svelte +0 -25
- package/src/components/ui/progress/index.ts +0 -1
- package/src/components/ui/progress/progress.svelte +0 -22
- package/src/components/ui/scroll-area/index.ts +0 -1
- package/src/components/ui/scroll-area/scroll-area.svelte +0 -20
- package/src/components/ui/select/index.ts +0 -2
- package/src/components/ui/select/select.svelte +0 -36
- package/src/components/ui/separator/index.ts +0 -7
- package/src/components/ui/separator/separator.svelte +0 -23
- package/src/components/ui/sheet/index.ts +0 -16
- package/src/components/ui/sheet/sheet-content.svelte +0 -28
- package/src/components/ui/sheet/sheet-description.svelte +0 -22
- package/src/components/ui/sheet/sheet-footer.svelte +0 -22
- package/src/components/ui/sheet/sheet-header.svelte +0 -22
- package/src/components/ui/sheet/sheet-title.svelte +0 -22
- package/src/components/ui/sheet/sheet.svelte +0 -78
- package/src/components/ui/skeleton/index.ts +0 -2
- package/src/components/ui/skeleton/skeleton.svelte +0 -21
- package/src/components/ui/switch/index.ts +0 -2
- package/src/components/ui/switch/switch.svelte +0 -49
- package/src/components/ui/table/index.ts +0 -28
- package/src/components/ui/table/table-body.svelte +0 -15
- package/src/components/ui/table/table-caption.svelte +0 -20
- package/src/components/ui/table/table-cell.svelte +0 -15
- package/src/components/ui/table/table-footer.svelte +0 -20
- package/src/components/ui/table/table-head.svelte +0 -15
- package/src/components/ui/table/table-header.svelte +0 -20
- package/src/components/ui/table/table-row.svelte +0 -15
- package/src/components/ui/table/table.svelte +0 -17
- package/src/components/ui/tabs/index.ts +0 -15
- package/src/components/ui/tabs/tabs-content.svelte +0 -30
- package/src/components/ui/tabs/tabs-list.svelte +0 -26
- package/src/components/ui/tabs/tabs-trigger.svelte +0 -37
- package/src/components/ui/tabs/tabs.svelte +0 -27
- package/src/components/ui/textarea/index.ts +0 -2
- package/src/components/ui/textarea/textarea.svelte +0 -24
- package/src/components/ui/tooltip/index.ts +0 -19
- package/src/components/ui/tooltip/tooltip-content.svelte +0 -52
- package/src/components/ui/tooltip/tooltip-portal.svelte +0 -7
- package/src/components/ui/tooltip/tooltip-provider.svelte +0 -7
- package/src/components/ui/tooltip/tooltip-trigger.svelte +0 -7
- package/src/components/ui/tooltip/tooltip.svelte +0 -10
- package/src/editor-config.svelte.ts +0 -23
- package/src/index.ts +0 -155
- package/src/markdown-deps.d.ts +0 -21
- package/src/router-state.svelte.ts +0 -65
- package/src/types.ts +0 -33
- package/src/utils.ts +0 -28
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { fly, scale } from 'svelte/transition';
|
|
3
|
-
import { getResources } from '@svadmin/core';
|
|
4
|
-
import { getTheme, getColorTheme } from '@svadmin/core';
|
|
5
|
-
import { getLocale, t } from '@svadmin/core/i18n';
|
|
6
|
-
import { currentPath } from '@svadmin/core/router';
|
|
7
|
-
import { Button } from './ui/button/index.js';
|
|
8
|
-
import TooltipButton from './TooltipButton.svelte';
|
|
9
|
-
import * as Tabs from './ui/tabs/index.js';
|
|
10
|
-
import { Badge } from './ui/badge/index.js';
|
|
11
|
-
import { ScrollArea } from './ui/scroll-area/index.js';
|
|
12
|
-
import { Separator } from './ui/separator/index.js';
|
|
13
|
-
import { X, Bug, ChevronDown, ChevronUp, Wand2 } from '@lucide/svelte';
|
|
14
|
-
import InferencerPanel from './InferencerPanel.svelte';
|
|
15
|
-
|
|
16
|
-
let visible = $state(false);
|
|
17
|
-
let collapsed = $state(false);
|
|
18
|
-
|
|
19
|
-
function toggle() {
|
|
20
|
-
visible = !visible;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function handleKeydown(e: KeyboardEvent) {
|
|
24
|
-
if (e.ctrlKey && e.shiftKey && e.key === 'D') {
|
|
25
|
-
e.preventDefault();
|
|
26
|
-
toggle();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const resources = $derived.by(() => { try { return getResources(); } catch { return []; } });
|
|
31
|
-
const path = $derived(currentPath());
|
|
32
|
-
const theme = $derived(getTheme());
|
|
33
|
-
const colorTheme = $derived(getColorTheme());
|
|
34
|
-
const locale = $derived(getLocale());
|
|
35
|
-
|
|
36
|
-
// Auto-trace state changes in dev mode via Svelte 5 $inspect
|
|
37
|
-
if ((import.meta as any).env?.DEV) {
|
|
38
|
-
$inspect({ path, theme, colorTheme, locale, resourceCount: resources.length });
|
|
39
|
-
}
|
|
40
|
-
</script>
|
|
41
|
-
|
|
42
|
-
<svelte:window onkeydown={handleKeydown} />
|
|
43
|
-
|
|
44
|
-
{#if (import.meta as any).env?.DEV}
|
|
45
|
-
{#if visible}
|
|
46
|
-
<div
|
|
47
|
-
class="fixed bottom-0 right-4 z-[9999] w-[420px] max-w-[95vw] rounded-t-xl border border-b-0 bg-card shadow-2xl text-[0.8125rem] overflow-hidden"
|
|
48
|
-
class:w-auto={collapsed} class:min-w-[200px]={collapsed}
|
|
49
|
-
transition:fly={{ y: 400, duration: 300 }}
|
|
50
|
-
>
|
|
51
|
-
<!-- Header -->
|
|
52
|
-
<div class="flex items-center justify-between px-3 py-2 bg-muted border-b">
|
|
53
|
-
<div class="flex items-center gap-1.5 font-semibold text-xs uppercase tracking-wider text-foreground">
|
|
54
|
-
<Bug class="h-4 w-4" />
|
|
55
|
-
<span>svadmin DevTools</span>
|
|
56
|
-
</div>
|
|
57
|
-
<div class="flex gap-1">
|
|
58
|
-
<TooltipButton tooltip={collapsed ? t('common.expand') : t('common.collapse')} variant="ghost" size="icon" class="h-6 w-6" onclick={() => collapsed = !collapsed}>
|
|
59
|
-
{#if collapsed}
|
|
60
|
-
<ChevronUp class="h-3.5 w-3.5" />
|
|
61
|
-
{:else}
|
|
62
|
-
<ChevronDown class="h-3.5 w-3.5" />
|
|
63
|
-
{/if}
|
|
64
|
-
</TooltipButton>
|
|
65
|
-
<TooltipButton tooltip={t('common.close')} variant="ghost" size="icon" class="h-6 w-6" onclick={toggle}>
|
|
66
|
-
<X class="h-3.5 w-3.5" />
|
|
67
|
-
</TooltipButton>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
{#if !collapsed}
|
|
72
|
-
<Tabs.Root value="state" class="w-full">
|
|
73
|
-
<Tabs.List class="grid w-full grid-cols-2 h-8 rounded-none border-b">
|
|
74
|
-
<Tabs.Trigger value="state" class="text-xs data-[state=active]:shadow-none rounded-none border-b-2 border-transparent data-[state=active]:border-primary">State</Tabs.Trigger>
|
|
75
|
-
<Tabs.Trigger value="inferencer" class="text-xs data-[state=active]:shadow-none rounded-none border-b-2 border-transparent data-[state=active]:border-primary">
|
|
76
|
-
<Wand2 class="h-3 w-3 mr-1" /> Inferencer
|
|
77
|
-
</Tabs.Trigger>
|
|
78
|
-
</Tabs.List>
|
|
79
|
-
<Tabs.Content value="state" class="p-0">
|
|
80
|
-
<ScrollArea class="max-h-[400px]">
|
|
81
|
-
<div class="p-2 space-y-1">
|
|
82
|
-
<!-- Router -->
|
|
83
|
-
<div class="py-1">
|
|
84
|
-
<h4 class="text-[0.6875rem] font-bold uppercase tracking-widest text-muted-foreground mb-1 px-1">Router</h4>
|
|
85
|
-
<div class="flex items-center justify-between px-1 py-0.5 rounded hover:bg-muted/50">
|
|
86
|
-
<span class="text-xs text-foreground">Path</span>
|
|
87
|
-
<Badge variant="secondary" class="font-mono text-[0.6875rem]">{path}</Badge>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<Separator />
|
|
92
|
-
|
|
93
|
-
<!-- Theme -->
|
|
94
|
-
<div class="py-1">
|
|
95
|
-
<h4 class="text-[0.6875rem] font-bold uppercase tracking-widest text-muted-foreground mb-1 px-1">Theme</h4>
|
|
96
|
-
<div class="flex items-center justify-between px-1 py-0.5 rounded hover:bg-muted/50">
|
|
97
|
-
<span class="text-xs text-foreground">Mode</span>
|
|
98
|
-
<Badge variant="secondary" class="font-mono text-[0.6875rem]">{theme}</Badge>
|
|
99
|
-
</div>
|
|
100
|
-
<div class="flex items-center justify-between px-1 py-0.5 rounded hover:bg-muted/50">
|
|
101
|
-
<span class="text-xs text-foreground">Color</span>
|
|
102
|
-
<Badge variant="secondary" class="font-mono text-[0.6875rem]">{colorTheme}</Badge>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<Separator />
|
|
107
|
-
|
|
108
|
-
<!-- i18n -->
|
|
109
|
-
<div class="py-1">
|
|
110
|
-
<h4 class="text-[0.6875rem] font-bold uppercase tracking-widest text-muted-foreground mb-1 px-1">i18n</h4>
|
|
111
|
-
<div class="flex items-center justify-between px-1 py-0.5 rounded hover:bg-muted/50">
|
|
112
|
-
<span class="text-xs text-foreground">Locale</span>
|
|
113
|
-
<Badge variant="secondary" class="font-mono text-[0.6875rem]">{locale}</Badge>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
|
|
117
|
-
<Separator />
|
|
118
|
-
|
|
119
|
-
<!-- Resources -->
|
|
120
|
-
<div class="py-1">
|
|
121
|
-
<h4 class="text-[0.6875rem] font-bold uppercase tracking-widest text-muted-foreground mb-1 px-1">Resources ({resources.length})</h4>
|
|
122
|
-
{#each resources as r}
|
|
123
|
-
<div class="flex items-center justify-between px-1 py-0.5 rounded hover:bg-muted/50">
|
|
124
|
-
<span class="text-xs text-foreground">{r.name}</span>
|
|
125
|
-
<Badge variant="outline" class="font-mono text-[0.6875rem]">{r.fields.length} fields</Badge>
|
|
126
|
-
</div>
|
|
127
|
-
{/each}
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
</ScrollArea>
|
|
131
|
-
</Tabs.Content>
|
|
132
|
-
<Tabs.Content value="inferencer" class="p-0">
|
|
133
|
-
<InferencerPanel />
|
|
134
|
-
</Tabs.Content>
|
|
135
|
-
</Tabs.Root>
|
|
136
|
-
{/if}
|
|
137
|
-
</div>
|
|
138
|
-
{:else}
|
|
139
|
-
<TooltipButton
|
|
140
|
-
tooltip={t('devtools.title')}
|
|
141
|
-
variant="default"
|
|
142
|
-
size="icon"
|
|
143
|
-
class="fixed bottom-4 right-4 z-[9999] h-9 w-9 rounded-full shadow-lg opacity-60 hover:opacity-100 hover:scale-110 transition-all"
|
|
144
|
-
onclick={toggle}
|
|
145
|
-
>
|
|
146
|
-
<Bug class="h-4 w-4" />
|
|
147
|
-
</TooltipButton>
|
|
148
|
-
{/if}
|
|
149
|
-
{/if}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
import type { GridModule } from '../types.js';
|
|
4
|
-
|
|
5
|
-
let {
|
|
6
|
-
modules = $bindable<GridModule[]>([]),
|
|
7
|
-
onOrderSave,
|
|
8
|
-
renderItem,
|
|
9
|
-
class: className = '',
|
|
10
|
-
columns = 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
|
11
|
-
gap = 'gap-4',
|
|
12
|
-
}: {
|
|
13
|
-
modules?: GridModule[];
|
|
14
|
-
onOrderSave?: (orderedIds: string[]) => void;
|
|
15
|
-
renderItem: Snippet<[{ module: GridModule; index: number; dragging: boolean }]>;
|
|
16
|
-
class?: string;
|
|
17
|
-
columns?: string;
|
|
18
|
-
gap?: string;
|
|
19
|
-
} = $props();
|
|
20
|
-
|
|
21
|
-
let draggingId = $state<string | null>(null);
|
|
22
|
-
let dragOverId = $state<string | null>(null);
|
|
23
|
-
|
|
24
|
-
function handleDragStart(e: DragEvent, id: string) {
|
|
25
|
-
draggingId = id;
|
|
26
|
-
if (e.dataTransfer) {
|
|
27
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
28
|
-
e.dataTransfer.setData('text/plain', id);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function handleDragOver(e: DragEvent, id: string) {
|
|
33
|
-
e.preventDefault();
|
|
34
|
-
if (draggingId && draggingId !== id) {
|
|
35
|
-
dragOverId = id;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function handleDrop(e: DragEvent, targetId: string) {
|
|
40
|
-
e.preventDefault();
|
|
41
|
-
if (!draggingId || draggingId === targetId) {
|
|
42
|
-
dragOverId = null;
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const sourceIdx = modules.findIndex(m => m.id === draggingId);
|
|
47
|
-
const targetIdx = modules.findIndex(m => m.id === targetId);
|
|
48
|
-
|
|
49
|
-
if (sourceIdx === -1 || targetIdx === -1) {
|
|
50
|
-
dragOverId = null;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const newModules = [...modules];
|
|
55
|
-
const [dragged] = newModules.splice(sourceIdx, 1);
|
|
56
|
-
newModules.splice(targetIdx, 0, dragged);
|
|
57
|
-
|
|
58
|
-
modules = newModules;
|
|
59
|
-
dragOverId = null;
|
|
60
|
-
onOrderSave?.(modules.map(m => m.id));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function handleDragEnd() {
|
|
64
|
-
draggingId = null;
|
|
65
|
-
dragOverId = null;
|
|
66
|
-
}
|
|
67
|
-
</script>
|
|
68
|
-
|
|
69
|
-
<div class="grid {columns} {gap} {className}">
|
|
70
|
-
{#each modules as mod, i (mod.id)}
|
|
71
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
72
|
-
<div
|
|
73
|
-
draggable="true"
|
|
74
|
-
ondragstart={(e) => handleDragStart(e, mod.id)}
|
|
75
|
-
ondragover={(e) => handleDragOver(e, mod.id)}
|
|
76
|
-
ondrop={(e) => handleDrop(e, mod.id)}
|
|
77
|
-
ondragend={handleDragEnd}
|
|
78
|
-
ondragleave={() => { if (dragOverId === mod.id) dragOverId = null; }}
|
|
79
|
-
class="transition-all duration-200
|
|
80
|
-
{draggingId === mod.id ? 'opacity-40 scale-[0.97] cursor-grabbing' : 'cursor-grab'}
|
|
81
|
-
{dragOverId === mod.id ? 'ring-2 ring-primary/40 ring-offset-2 ring-offset-background rounded-lg scale-[1.02]' : ''}"
|
|
82
|
-
>
|
|
83
|
-
{@render renderItem({ module: mod, index: i, dragging: draggingId === mod.id })}
|
|
84
|
-
</div>
|
|
85
|
-
{/each}
|
|
86
|
-
</div>
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* DraggableHeader — Wraps table header cells with HTML5 Drag and Drop
|
|
4
|
-
* to enable column reorder. Persists order to localStorage.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* <DraggableHeader columns={columns} resourceName="posts" onReorder={handleReorder}>
|
|
8
|
-
* {#snippet header(column, index)}
|
|
9
|
-
* <th>...</th>
|
|
10
|
-
* {/snippet}
|
|
11
|
-
* </DraggableHeader>
|
|
12
|
-
*/
|
|
13
|
-
import type { Snippet } from 'svelte';
|
|
14
|
-
|
|
15
|
-
interface Column {
|
|
16
|
-
id: string;
|
|
17
|
-
[key: string]: unknown;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface Props {
|
|
21
|
-
columns: Column[];
|
|
22
|
-
resourceName: string;
|
|
23
|
-
onReorder: (newOrder: Column[]) => void;
|
|
24
|
-
header: Snippet<[Column, number, Record<string, any>]>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let { columns, resourceName, onReorder, header }: Props = $props();
|
|
28
|
-
|
|
29
|
-
let dragIndex = $state<number | null>(null);
|
|
30
|
-
let dropIndex = $state<number | null>(null);
|
|
31
|
-
|
|
32
|
-
// Restore saved order from localStorage
|
|
33
|
-
$effect(() => {
|
|
34
|
-
const storageKey = `svadmin-colorder-${resourceName}`;
|
|
35
|
-
if (typeof localStorage === 'undefined') return;
|
|
36
|
-
try {
|
|
37
|
-
const saved = localStorage.getItem(storageKey);
|
|
38
|
-
if (saved) {
|
|
39
|
-
const savedOrder: string[] = JSON.parse(saved);
|
|
40
|
-
const reordered = savedOrder
|
|
41
|
-
.map(id => columns.find(c => c.id === id))
|
|
42
|
-
.filter((c): c is Column => c !== undefined);
|
|
43
|
-
// Add any new columns not in saved order
|
|
44
|
-
const remaining = columns.filter(c => !savedOrder.includes(c.id));
|
|
45
|
-
if (reordered.length > 0) {
|
|
46
|
-
onReorder([...reordered, ...remaining]);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} catch { /* ignore */ }
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
function handleDragStart(e: DragEvent, index: number) {
|
|
53
|
-
dragIndex = index;
|
|
54
|
-
if (e.dataTransfer) {
|
|
55
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
56
|
-
e.dataTransfer.setData('text/plain', String(index));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function handleDragOver(e: DragEvent, index: number) {
|
|
61
|
-
e.preventDefault();
|
|
62
|
-
dropIndex = index;
|
|
63
|
-
if (e.dataTransfer) {
|
|
64
|
-
e.dataTransfer.dropEffect = 'move';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function handleDrop(e: DragEvent, index: number) {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
if (dragIndex === null || dragIndex === index) {
|
|
71
|
-
dragIndex = null;
|
|
72
|
-
dropIndex = null;
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const newColumns = [...columns];
|
|
77
|
-
const [moved] = newColumns.splice(dragIndex, 1);
|
|
78
|
-
newColumns.splice(index, 0, moved);
|
|
79
|
-
|
|
80
|
-
// Persist to localStorage
|
|
81
|
-
const storageKey = `svadmin-colorder-${resourceName}`;
|
|
82
|
-
if (typeof localStorage !== 'undefined') {
|
|
83
|
-
try {
|
|
84
|
-
localStorage.setItem(storageKey, JSON.stringify(newColumns.map(c => c.id)));
|
|
85
|
-
} catch { /* ignore */ }
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
onReorder(newColumns);
|
|
89
|
-
dragIndex = null;
|
|
90
|
-
dropIndex = null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function handleDragEnd() {
|
|
94
|
-
dragIndex = null;
|
|
95
|
-
dropIndex = null;
|
|
96
|
-
}
|
|
97
|
-
</script>
|
|
98
|
-
|
|
99
|
-
<tr class="hover:bg-muted/50 data-[state=selected]:bg-muted transition-colors bg-muted/50">
|
|
100
|
-
{#each columns as column, index}
|
|
101
|
-
{@render header(column, index, {
|
|
102
|
-
draggable: true,
|
|
103
|
-
ondragstart: (e: DragEvent) => handleDragStart(e, index),
|
|
104
|
-
ondragover: (e: DragEvent) => handleDragOver(e, index),
|
|
105
|
-
ondrop: (e: DragEvent) => handleDrop(e, index),
|
|
106
|
-
ondragend: handleDragEnd,
|
|
107
|
-
'data-dragging': dragIndex === index,
|
|
108
|
-
'data-drop-target': dropIndex === index && dropIndex !== dragIndex,
|
|
109
|
-
class: `cursor-grab active:cursor-grabbing select-none transition-opacity ${dragIndex === index ? 'opacity-50' : ''} ${dropIndex === index && dropIndex !== dragIndex ? 'border-l-2 border-primary' : ''}`
|
|
110
|
-
})}
|
|
111
|
-
{/each}
|
|
112
|
-
</tr>
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getResource } from '@svadmin/core';
|
|
3
|
-
import { t } from '@svadmin/core/i18n';
|
|
4
|
-
import * as Sheet from './ui/sheet/index.js';
|
|
5
|
-
import AutoForm from './AutoForm.svelte';
|
|
6
|
-
|
|
7
|
-
let { resourceName, mode = 'create', id, open = $bindable(false), side = 'right', onSuccess } = $props<{
|
|
8
|
-
resourceName: string;
|
|
9
|
-
mode?: 'create' | 'edit';
|
|
10
|
-
id?: string | number;
|
|
11
|
-
open: boolean;
|
|
12
|
-
side?: 'left' | 'right';
|
|
13
|
-
onSuccess?: () => void;
|
|
14
|
-
}>();
|
|
15
|
-
|
|
16
|
-
const resource = $derived(getResource(resourceName));
|
|
17
|
-
|
|
18
|
-
function handleClose() {
|
|
19
|
-
open = false;
|
|
20
|
-
onSuccess?.();
|
|
21
|
-
}
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<Sheet.Root bind:open {side}>
|
|
25
|
-
<Sheet.Content>
|
|
26
|
-
<Sheet.Header>
|
|
27
|
-
<Sheet.Title>
|
|
28
|
-
{mode === 'create' ? `${t('common.create')}${resource.label}` : `${t('common.edit')}${resource.label}`}
|
|
29
|
-
</Sheet.Title>
|
|
30
|
-
</Sheet.Header>
|
|
31
|
-
<div class="flex-1 overflow-y-auto">
|
|
32
|
-
<AutoForm {resourceName} {mode} {id} />
|
|
33
|
-
</div>
|
|
34
|
-
</Sheet.Content>
|
|
35
|
-
</Sheet.Root>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getResource, useDelete } from '@svadmin/core';
|
|
3
|
-
import { navigate } from '@svadmin/core/router';
|
|
4
|
-
import { t } from '@svadmin/core/i18n';
|
|
5
|
-
import type { Snippet } from 'svelte';
|
|
6
|
-
import PageHeader from './PageHeader.svelte';
|
|
7
|
-
import AutoForm from './AutoForm.svelte';
|
|
8
|
-
import ListButton from './buttons/ListButton.svelte';
|
|
9
|
-
import ShowButton from './buttons/ShowButton.svelte';
|
|
10
|
-
import RefreshButton from './buttons/RefreshButton.svelte';
|
|
11
|
-
import DeleteButton from './buttons/DeleteButton.svelte';
|
|
12
|
-
|
|
13
|
-
interface Props {
|
|
14
|
-
resourceName: string;
|
|
15
|
-
id: string | number;
|
|
16
|
-
title?: string;
|
|
17
|
-
canDelete?: boolean;
|
|
18
|
-
headerActions?: Snippet;
|
|
19
|
-
class?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let {
|
|
23
|
-
resourceName,
|
|
24
|
-
id,
|
|
25
|
-
title,
|
|
26
|
-
canDelete,
|
|
27
|
-
headerActions,
|
|
28
|
-
class: className = '',
|
|
29
|
-
}: Props = $props();
|
|
30
|
-
|
|
31
|
-
const resource = $derived(getResource(resourceName));
|
|
32
|
-
const pageTitle = $derived(title ?? `${t('common.edit')} ${resource.label} #${id}`);
|
|
33
|
-
const showDelete = $derived(canDelete ?? resource.canDelete !== false);
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
<div class="space-y-6 {className}">
|
|
37
|
-
<PageHeader title={pageTitle}>
|
|
38
|
-
{#snippet actions()}
|
|
39
|
-
<ListButton resource={resourceName} hideText />
|
|
40
|
-
<ShowButton resource={resourceName} recordItemId={id} hideText />
|
|
41
|
-
<RefreshButton resource={resourceName} hideText />
|
|
42
|
-
{#if showDelete !== false}
|
|
43
|
-
<DeleteButton resource={resourceName} recordItemId={id} hideText onSuccess={() => navigate(`/${resourceName}`)} />
|
|
44
|
-
{/if}
|
|
45
|
-
{#if headerActions}
|
|
46
|
-
{@render headerActions()}
|
|
47
|
-
{/if}
|
|
48
|
-
{/snippet}
|
|
49
|
-
</PageHeader>
|
|
50
|
-
|
|
51
|
-
<AutoForm {resourceName} mode="edit" {id} />
|
|
52
|
-
</div>
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { fade, scale } from 'svelte/transition';
|
|
3
|
-
import { PackageOpen } from '@lucide/svelte';
|
|
4
|
-
import { t } from '@svadmin/core';
|
|
5
|
-
|
|
6
|
-
let {
|
|
7
|
-
title = t('empty.title'),
|
|
8
|
-
description = t('empty.description'),
|
|
9
|
-
children,
|
|
10
|
-
} = $props<{
|
|
11
|
-
title?: string;
|
|
12
|
-
description?: string;
|
|
13
|
-
children?: import('svelte').Snippet;
|
|
14
|
-
}>();
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
<div
|
|
18
|
-
class="flex flex-col items-center justify-center p-8 text-center min-h-[300px] bg-card/50 rounded-lg shadow-sm"
|
|
19
|
-
in:scale={{ start: 0.95, duration: 250, opacity: 0 }}
|
|
20
|
-
>
|
|
21
|
-
<div class="h-20 w-20 rounded-full bg-muted flex items-center justify-center mb-4">
|
|
22
|
-
<PackageOpen class="h-10 w-10 text-muted-foreground" />
|
|
23
|
-
</div>
|
|
24
|
-
<h3 class="text-lg font-semibold tracking-tight">{title}</h3>
|
|
25
|
-
<p class="text-sm text-muted-foreground mt-2 max-w-sm">{description}</p>
|
|
26
|
-
{#if children}
|
|
27
|
-
<div class="mt-6">
|
|
28
|
-
{@render children()}
|
|
29
|
-
</div>
|
|
30
|
-
{/if}
|
|
31
|
-
</div>
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
import { AlertTriangle } from '@lucide/svelte';
|
|
4
|
-
import { Button } from './ui/button/index.js';
|
|
5
|
-
import * as Alert from './ui/alert/index.js';
|
|
6
|
-
import { t } from '@svadmin/core/i18n';
|
|
7
|
-
|
|
8
|
-
let {
|
|
9
|
-
children,
|
|
10
|
-
fallback,
|
|
11
|
-
}: {
|
|
12
|
-
children: Snippet;
|
|
13
|
-
/** Optional custom error UI snippet */
|
|
14
|
-
fallback?: Snippet<[{ error: Error; reset: () => void }]>;
|
|
15
|
-
} = $props();
|
|
16
|
-
|
|
17
|
-
function handleError(error: unknown) {
|
|
18
|
-
console.error('[svadmin] Uncaught error:', error);
|
|
19
|
-
}
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<svelte:boundary onerror={handleError}>
|
|
23
|
-
{@render children()}
|
|
24
|
-
|
|
25
|
-
{#snippet failed(error, reset)}
|
|
26
|
-
{#if fallback}
|
|
27
|
-
{@render fallback({ error: error instanceof Error ? error : new Error(String(error)), reset })}
|
|
28
|
-
{:else}
|
|
29
|
-
<div class="flex h-screen items-center justify-center bg-background">
|
|
30
|
-
<div class="max-w-md w-full px-4">
|
|
31
|
-
<Alert.Root variant="destructive" class="space-y-4">
|
|
32
|
-
<div class="flex items-start gap-3">
|
|
33
|
-
<AlertTriangle class="h-5 w-5 mt-0.5 shrink-0" />
|
|
34
|
-
<div class="space-y-2">
|
|
35
|
-
<Alert.Title>{t('common.error')}</Alert.Title>
|
|
36
|
-
<Alert.Description class="text-sm">
|
|
37
|
-
{error instanceof Error ? error.message : String(error)}
|
|
38
|
-
</Alert.Description>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
<Button variant="outline" size="sm" onclick={reset} class="w-full">
|
|
42
|
-
{t('common.retry')}
|
|
43
|
-
</Button>
|
|
44
|
-
</Alert.Root>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
{/if}
|
|
48
|
-
{/snippet}
|
|
49
|
-
</svelte:boundary>
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { AlertTriangle, RefreshCw } from '@lucide/svelte';
|
|
3
|
-
import { t } from '@svadmin/core';
|
|
4
|
-
import { Button } from './ui/button';
|
|
5
|
-
import * as Alert from './ui/alert/index.js';
|
|
6
|
-
|
|
7
|
-
let {
|
|
8
|
-
error,
|
|
9
|
-
title = t('error.title'),
|
|
10
|
-
description = t('error.description'),
|
|
11
|
-
retry,
|
|
12
|
-
} = $props<{
|
|
13
|
-
error?: Error | null;
|
|
14
|
-
title?: string;
|
|
15
|
-
description?: string;
|
|
16
|
-
retry?: () => void;
|
|
17
|
-
}>();
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<div class="flex flex-col items-center justify-center p-8 text-center min-h-[300px]">
|
|
21
|
-
<Alert.Root variant="destructive" class="max-w-md text-left">
|
|
22
|
-
<AlertTriangle class="h-5 w-5" />
|
|
23
|
-
<Alert.Title>{title}</Alert.Title>
|
|
24
|
-
<Alert.Description>
|
|
25
|
-
{error?.message || description}
|
|
26
|
-
</Alert.Description>
|
|
27
|
-
{#if retry}
|
|
28
|
-
<div class="mt-4">
|
|
29
|
-
<Button variant="outline" size="sm" onclick={retry}>
|
|
30
|
-
<RefreshCw class="h-3 w-3" data-icon="inline-start" />
|
|
31
|
-
{t('buttons.retry')}
|
|
32
|
-
</Button>
|
|
33
|
-
</div>
|
|
34
|
-
{/if}
|
|
35
|
-
</Alert.Root>
|
|
36
|
-
</div>
|