@svadmin/ui 0.65.1 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/MetricStrip.svelte +149 -0
- package/dist/components/MetricStrip.svelte.d.ts +32 -0
- package/dist/components/ui/badge/badge.svelte +5 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +15 -0
- package/dist/components/ui/table/table-cell.svelte +14 -2
- package/dist/components/ui/table/table-cell.svelte.d.ts +4 -1
- package/dist/components/ui/table/table-head.svelte +14 -2
- package/dist/components/ui/table/table-head.svelte.d.ts +4 -1
- package/dist/components/ui/table/table-row.svelte +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Skeleton } from './ui/skeleton/index.js';
|
|
3
|
+
import { cn } from '../utils.js';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
|
|
6
|
+
export type MetricTone = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
|
7
|
+
|
|
8
|
+
export interface MetricStripItem {
|
|
9
|
+
id?: string;
|
|
10
|
+
label: string;
|
|
11
|
+
value: string | number;
|
|
12
|
+
tone?: MetricTone;
|
|
13
|
+
badge?: {
|
|
14
|
+
text: string;
|
|
15
|
+
tone?: 'default' | 'success' | 'warning' | 'danger' | 'info';
|
|
16
|
+
};
|
|
17
|
+
icon?: import('svelte').Component<{ class?: string }>;
|
|
18
|
+
href?: string;
|
|
19
|
+
trend?: {
|
|
20
|
+
value: number;
|
|
21
|
+
label?: string;
|
|
22
|
+
};
|
|
23
|
+
loading?: boolean;
|
|
24
|
+
class?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Props {
|
|
28
|
+
items?: MetricStripItem[];
|
|
29
|
+
columns?: 2 | 3 | 4 | 5 | 6 | 'auto';
|
|
30
|
+
ariaLabel?: string;
|
|
31
|
+
class?: string;
|
|
32
|
+
children?: Snippet;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let {
|
|
36
|
+
items = [],
|
|
37
|
+
columns = 'auto',
|
|
38
|
+
ariaLabel = 'Metrics overview',
|
|
39
|
+
class: className = '',
|
|
40
|
+
children,
|
|
41
|
+
}: Props = $props();
|
|
42
|
+
|
|
43
|
+
const columnClassMap = {
|
|
44
|
+
2: 'grid-cols-1 sm:grid-cols-2',
|
|
45
|
+
3: 'grid-cols-1 sm:grid-cols-3',
|
|
46
|
+
4: 'grid-cols-2 sm:grid-cols-4',
|
|
47
|
+
5: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5',
|
|
48
|
+
6: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',
|
|
49
|
+
auto: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const toneValueColorMap: Record<MetricTone, string> = {
|
|
53
|
+
default: 'text-foreground',
|
|
54
|
+
primary: 'text-primary',
|
|
55
|
+
success: 'text-success',
|
|
56
|
+
warning: 'text-warning',
|
|
57
|
+
danger: 'text-destructive',
|
|
58
|
+
info: 'text-info',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const badgeToneMap: Record<
|
|
62
|
+
NonNullable<NonNullable<MetricStripItem['badge']>['tone']>,
|
|
63
|
+
string
|
|
64
|
+
> = {
|
|
65
|
+
default: 'bg-muted text-muted-foreground border-border',
|
|
66
|
+
success: 'bg-success/10 text-success border-success/20',
|
|
67
|
+
warning: 'bg-warning/10 text-warning border-warning/20',
|
|
68
|
+
danger: 'bg-destructive/10 text-destructive border-destructive/20',
|
|
69
|
+
info: 'bg-info/10 text-info border-info/20',
|
|
70
|
+
};
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<div
|
|
74
|
+
role="region"
|
|
75
|
+
aria-label={ariaLabel}
|
|
76
|
+
class={cn(
|
|
77
|
+
'grid gap-px overflow-hidden rounded-lg border border-border bg-border shadow-xs',
|
|
78
|
+
columnClassMap[columns] || columnClassMap.auto,
|
|
79
|
+
className
|
|
80
|
+
)}
|
|
81
|
+
>
|
|
82
|
+
{#if children}
|
|
83
|
+
{@render children()}
|
|
84
|
+
{:else}
|
|
85
|
+
{#each items as item, index (item.id || index)}
|
|
86
|
+
{@const isInteractive = Boolean(item.href)}
|
|
87
|
+
<svelte:element
|
|
88
|
+
this={isInteractive ? 'a' : 'div'}
|
|
89
|
+
href={item.href}
|
|
90
|
+
class={cn(
|
|
91
|
+
'group relative flex flex-col justify-between gap-1 bg-card p-3.5 text-left transition-colors duration-150 sm:p-4',
|
|
92
|
+
isInteractive && 'cursor-pointer text-inherit no-underline hover:bg-muted/40',
|
|
93
|
+
item.class
|
|
94
|
+
)}
|
|
95
|
+
>
|
|
96
|
+
<div class="flex items-center justify-between gap-2">
|
|
97
|
+
<span class="truncate text-xs font-semibold text-muted-foreground">{item.label}</span>
|
|
98
|
+
{#if item.icon}
|
|
99
|
+
<span class="shrink-0 text-muted-foreground/70">
|
|
100
|
+
<item.icon class="h-3.5 w-3.5" />
|
|
101
|
+
</span>
|
|
102
|
+
{/if}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
{#if item.loading}
|
|
106
|
+
<div class="mt-1 flex items-center justify-between">
|
|
107
|
+
<Skeleton class="h-6 w-16" />
|
|
108
|
+
</div>
|
|
109
|
+
{:else}
|
|
110
|
+
<div class="mt-0.5 flex items-baseline justify-between gap-2">
|
|
111
|
+
<strong
|
|
112
|
+
class={cn(
|
|
113
|
+
'text-xl font-bold tracking-tight tabular-nums sm:text-2xl',
|
|
114
|
+
toneValueColorMap[item.tone || 'default']
|
|
115
|
+
)}
|
|
116
|
+
>
|
|
117
|
+
{item.value}
|
|
118
|
+
</strong>
|
|
119
|
+
|
|
120
|
+
{#if item.badge}
|
|
121
|
+
<span
|
|
122
|
+
class={cn(
|
|
123
|
+
'inline-flex items-center rounded-full border px-1.5 py-0.5 text-[11px] font-semibold',
|
|
124
|
+
badgeToneMap[item.badge.tone || 'default']
|
|
125
|
+
)}
|
|
126
|
+
>
|
|
127
|
+
{item.badge.text}
|
|
128
|
+
</span>
|
|
129
|
+
{:else if item.trend}
|
|
130
|
+
<span
|
|
131
|
+
class={cn(
|
|
132
|
+
'inline-flex items-center gap-0.5 text-xs font-medium tabular-nums',
|
|
133
|
+
item.trend.value >= 0 ? 'text-success' : 'text-destructive'
|
|
134
|
+
)}
|
|
135
|
+
aria-label="{item.trend.value >= 0 ? '上升' : '下降'} {Math.abs(item.trend.value)}%"
|
|
136
|
+
>
|
|
137
|
+
<span aria-hidden="true">{item.trend.value >= 0 ? '↑' : '↓'}</span>
|
|
138
|
+
<span>{Math.abs(item.trend.value)}%</span>
|
|
139
|
+
{#if item.trend.label}
|
|
140
|
+
<span class="ml-0.5 text-muted-foreground">{item.trend.label}</span>
|
|
141
|
+
{/if}
|
|
142
|
+
</span>
|
|
143
|
+
{/if}
|
|
144
|
+
</div>
|
|
145
|
+
{/if}
|
|
146
|
+
</svelte:element>
|
|
147
|
+
{/each}
|
|
148
|
+
{/if}
|
|
149
|
+
</div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type MetricTone = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
|
3
|
+
export interface MetricStripItem {
|
|
4
|
+
id?: string;
|
|
5
|
+
label: string;
|
|
6
|
+
value: string | number;
|
|
7
|
+
tone?: MetricTone;
|
|
8
|
+
badge?: {
|
|
9
|
+
text: string;
|
|
10
|
+
tone?: 'default' | 'success' | 'warning' | 'danger' | 'info';
|
|
11
|
+
};
|
|
12
|
+
icon?: import('svelte').Component<{
|
|
13
|
+
class?: string;
|
|
14
|
+
}>;
|
|
15
|
+
href?: string;
|
|
16
|
+
trend?: {
|
|
17
|
+
value: number;
|
|
18
|
+
label?: string;
|
|
19
|
+
};
|
|
20
|
+
loading?: boolean;
|
|
21
|
+
class?: string;
|
|
22
|
+
}
|
|
23
|
+
interface Props {
|
|
24
|
+
items?: MetricStripItem[];
|
|
25
|
+
columns?: 2 | 3 | 4 | 5 | 6 | 'auto';
|
|
26
|
+
ariaLabel?: string;
|
|
27
|
+
class?: string;
|
|
28
|
+
children?: Snippet;
|
|
29
|
+
}
|
|
30
|
+
declare const MetricStrip: import("svelte").Component<Props, {}, "">;
|
|
31
|
+
type MetricStrip = ReturnType<typeof MetricStrip>;
|
|
32
|
+
export default MetricStrip;
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
9
9
|
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
10
10
|
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
|
|
11
|
+
subtle: "bg-primary/10 text-primary border-primary/20 [a]:hover:bg-primary/15",
|
|
12
|
+
"subtle-success": "bg-success/10 text-success border-success/20 [a]:hover:bg-success/15",
|
|
13
|
+
"subtle-warning": "bg-warning/10 text-warning border-warning/20 [a]:hover:bg-warning/15",
|
|
14
|
+
"subtle-destructive": "bg-destructive/10 text-destructive border-destructive/20 [a]:hover:bg-destructive/15",
|
|
15
|
+
"subtle-pill": "rounded-full bg-muted/70 text-muted-foreground border-border/80 font-medium",
|
|
11
16
|
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
12
17
|
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
13
18
|
link: "text-primary underline-offset-4 hover:underline",
|
|
@@ -4,6 +4,11 @@ export declare const badgeVariants: import("tailwind-variants").TVReturnType<{
|
|
|
4
4
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80";
|
|
5
5
|
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80";
|
|
6
6
|
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20";
|
|
7
|
+
subtle: "bg-primary/10 text-primary border-primary/20 [a]:hover:bg-primary/15";
|
|
8
|
+
"subtle-success": "bg-success/10 text-success border-success/20 [a]:hover:bg-success/15";
|
|
9
|
+
"subtle-warning": "bg-warning/10 text-warning border-warning/20 [a]:hover:bg-warning/15";
|
|
10
|
+
"subtle-destructive": "bg-destructive/10 text-destructive border-destructive/20 [a]:hover:bg-destructive/15";
|
|
11
|
+
"subtle-pill": "rounded-full bg-muted/70 text-muted-foreground border-border/80 font-medium";
|
|
7
12
|
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground";
|
|
8
13
|
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50";
|
|
9
14
|
link: "text-primary underline-offset-4 hover:underline";
|
|
@@ -13,6 +18,11 @@ export declare const badgeVariants: import("tailwind-variants").TVReturnType<{
|
|
|
13
18
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80";
|
|
14
19
|
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80";
|
|
15
20
|
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20";
|
|
21
|
+
subtle: "bg-primary/10 text-primary border-primary/20 [a]:hover:bg-primary/15";
|
|
22
|
+
"subtle-success": "bg-success/10 text-success border-success/20 [a]:hover:bg-success/15";
|
|
23
|
+
"subtle-warning": "bg-warning/10 text-warning border-warning/20 [a]:hover:bg-warning/15";
|
|
24
|
+
"subtle-destructive": "bg-destructive/10 text-destructive border-destructive/20 [a]:hover:bg-destructive/15";
|
|
25
|
+
"subtle-pill": "rounded-full bg-muted/70 text-muted-foreground border-border/80 font-medium";
|
|
16
26
|
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground";
|
|
17
27
|
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50";
|
|
18
28
|
link: "text-primary underline-offset-4 hover:underline";
|
|
@@ -22,6 +32,11 @@ export declare const badgeVariants: import("tailwind-variants").TVReturnType<{
|
|
|
22
32
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80";
|
|
23
33
|
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80";
|
|
24
34
|
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20";
|
|
35
|
+
subtle: "bg-primary/10 text-primary border-primary/20 [a]:hover:bg-primary/15";
|
|
36
|
+
"subtle-success": "bg-success/10 text-success border-success/20 [a]:hover:bg-success/15";
|
|
37
|
+
"subtle-warning": "bg-warning/10 text-warning border-warning/20 [a]:hover:bg-warning/15";
|
|
38
|
+
"subtle-destructive": "bg-destructive/10 text-destructive border-destructive/20 [a]:hover:bg-destructive/15";
|
|
39
|
+
"subtle-pill": "rounded-full bg-muted/70 text-muted-foreground border-border/80 font-medium";
|
|
25
40
|
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground";
|
|
26
41
|
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50";
|
|
27
42
|
link: "text-primary underline-offset-4 hover:underline";
|
|
@@ -4,19 +4,31 @@
|
|
|
4
4
|
import type { HTMLTdAttributes } from "svelte/elements";
|
|
5
5
|
import type { TableDensity } from "./table.svelte";
|
|
6
6
|
|
|
7
|
+
type Props = WithElementRef<HTMLTdAttributes, HTMLTableCellElement> & {
|
|
8
|
+
sticky?: "left" | "right" | boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
7
11
|
let {
|
|
8
12
|
ref = $bindable(null),
|
|
13
|
+
sticky = false,
|
|
9
14
|
class: className,
|
|
10
15
|
children,
|
|
11
16
|
...restProps
|
|
12
|
-
}:
|
|
17
|
+
}: Props = $props();
|
|
13
18
|
|
|
14
19
|
const getDensity = getContext<() => TableDensity>("svadmin-table-density");
|
|
15
20
|
const densityClass = $derived(getDensity?.() === "compact"
|
|
16
21
|
? "h-8 px-2 py-1 text-xs"
|
|
17
22
|
: "p-2");
|
|
23
|
+
const stickyClass = $derived(
|
|
24
|
+
sticky === "right" || sticky === true
|
|
25
|
+
? "sticky right-0 z-10 bg-background shadow-[-1px_0_0_var(--border)] group-hover/row:bg-muted/50 group-data-[state=selected]/row:bg-muted"
|
|
26
|
+
: sticky === "left"
|
|
27
|
+
? "sticky left-0 z-10 bg-background shadow-[1px_0_0_var(--border)] group-hover/row:bg-muted/50 group-data-[state=selected]/row:bg-muted"
|
|
28
|
+
: ""
|
|
29
|
+
);
|
|
18
30
|
</script>
|
|
19
31
|
|
|
20
|
-
<td bind:this={ref} data-slot="table-cell" class={cn("align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", densityClass, className)} {...restProps}>
|
|
32
|
+
<td bind:this={ref} data-slot="table-cell" data-sticky={sticky ? (typeof sticky === "string" ? sticky : "right") : undefined} class={cn("align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", densityClass, stickyClass, className)} {...restProps}>
|
|
21
33
|
{@render children?.()}
|
|
22
34
|
</td>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type WithElementRef } from "../../../utils.js";
|
|
2
2
|
import type { HTMLTdAttributes } from "svelte/elements";
|
|
3
|
-
|
|
3
|
+
type Props = WithElementRef<HTMLTdAttributes, HTMLTableCellElement> & {
|
|
4
|
+
sticky?: "left" | "right" | boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const TableCell: import("svelte").Component<Props, {}, "ref">;
|
|
4
7
|
type TableCell = ReturnType<typeof TableCell>;
|
|
5
8
|
export default TableCell;
|
|
@@ -4,19 +4,31 @@
|
|
|
4
4
|
import type { HTMLThAttributes } from "svelte/elements";
|
|
5
5
|
import type { TableDensity } from "./table.svelte";
|
|
6
6
|
|
|
7
|
+
type Props = WithElementRef<HTMLThAttributes, HTMLTableCellElement> & {
|
|
8
|
+
sticky?: "left" | "right" | boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
7
11
|
let {
|
|
8
12
|
ref = $bindable(null),
|
|
13
|
+
sticky = false,
|
|
9
14
|
class: className,
|
|
10
15
|
children,
|
|
11
16
|
...restProps
|
|
12
|
-
}:
|
|
17
|
+
}: Props = $props();
|
|
13
18
|
|
|
14
19
|
const getDensity = getContext<() => TableDensity>("svadmin-table-density");
|
|
15
20
|
const densityClass = $derived(getDensity?.() === "compact"
|
|
16
21
|
? "h-8 px-2 py-1 text-xs"
|
|
17
22
|
: "h-9 px-2");
|
|
23
|
+
const stickyClass = $derived(
|
|
24
|
+
sticky === "right" || sticky === true
|
|
25
|
+
? "sticky right-0 z-20 bg-background shadow-[-1px_0_0_var(--border)]"
|
|
26
|
+
: sticky === "left"
|
|
27
|
+
? "sticky left-0 z-20 bg-background shadow-[1px_0_0_var(--border)]"
|
|
28
|
+
: ""
|
|
29
|
+
);
|
|
18
30
|
</script>
|
|
19
31
|
|
|
20
|
-
<th bind:this={ref} data-slot="table-head" class={cn("text-foreground text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0", densityClass, className)} {...restProps}>
|
|
32
|
+
<th bind:this={ref} data-slot="table-head" data-sticky={sticky ? (typeof sticky === "string" ? sticky : "right") : undefined} class={cn("text-foreground text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0", densityClass, stickyClass, className)} {...restProps}>
|
|
21
33
|
{@render children?.()}
|
|
22
34
|
</th>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type WithElementRef } from "../../../utils.js";
|
|
2
2
|
import type { HTMLThAttributes } from "svelte/elements";
|
|
3
|
-
|
|
3
|
+
type Props = WithElementRef<HTMLThAttributes, HTMLTableCellElement> & {
|
|
4
|
+
sticky?: "left" | "right" | boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const TableHead: import("svelte").Component<Props, {}, "ref">;
|
|
4
7
|
type TableHead = ReturnType<typeof TableHead>;
|
|
5
8
|
export default TableHead;
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
}: WithElementRef<HTMLAttributes<HTMLTableRowElement>> = $props();
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
|
-
<tr bind:this={ref} data-slot="table-row" class={cn("hover:bg-muted/50 data-[state=selected]:bg-muted transition-colors", className)} {...restProps}>
|
|
13
|
+
<tr bind:this={ref} data-slot="table-row" class={cn("group/row hover:bg-muted/50 data-[state=selected]:bg-muted transition-colors", className)} {...restProps}>
|
|
14
14
|
{@render children?.()}
|
|
15
15
|
</tr>
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
|
|
|
13
13
|
export { default as FieldRenderer } from './components/FieldRenderer.svelte';
|
|
14
14
|
export { default as EmptyState } from './components/EmptyState.svelte';
|
|
15
15
|
export { default as StatsCard } from './components/StatsCard.svelte';
|
|
16
|
+
export { default as MetricStrip } from './components/MetricStrip.svelte';
|
|
17
|
+
export type { MetricStripItem, MetricTone } from './components/MetricStrip.svelte';
|
|
16
18
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
17
19
|
export { default as ResourceOperationsPage } from './components/ResourceOperationsPage.svelte';
|
|
18
20
|
export { default as RecordDetailDrawer } from './components/RecordDetailDrawer.svelte';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
|
|
|
16
16
|
export { default as FieldRenderer } from './components/FieldRenderer.svelte';
|
|
17
17
|
export { default as EmptyState } from './components/EmptyState.svelte';
|
|
18
18
|
export { default as StatsCard } from './components/StatsCard.svelte';
|
|
19
|
+
export { default as MetricStrip } from './components/MetricStrip.svelte';
|
|
19
20
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
20
21
|
export { default as ResourceOperationsPage } from './components/ResourceOperationsPage.svelte';
|
|
21
22
|
export { default as RecordDetailDrawer } from './components/RecordDetailDrawer.svelte';
|