@svadmin/ui 0.80.0 → 0.81.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/ConfigurationPage.svelte +75 -0
- package/dist/components/ConfigurationPage.svelte.d.ts +15 -0
- package/dist/components/EmptyState.svelte +21 -12
- package/dist/components/PagePattern.svelte +114 -0
- package/dist/components/PagePattern.svelte.d.ts +21 -0
- package/dist/components/content/DataState.svelte +18 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { definedOptions } from '@svadmin/core/options';
|
|
4
|
+
import ContentPageShell from './content/ContentPageShell.svelte';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
eyebrow?: string;
|
|
10
|
+
actions?: Snippet;
|
|
11
|
+
footer?: Snippet;
|
|
12
|
+
children: Snippet;
|
|
13
|
+
width?: 'narrow' | 'default' | 'wide';
|
|
14
|
+
density?: 'compact' | 'comfortable';
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
title,
|
|
20
|
+
description,
|
|
21
|
+
eyebrow,
|
|
22
|
+
actions,
|
|
23
|
+
footer,
|
|
24
|
+
children,
|
|
25
|
+
width = 'default',
|
|
26
|
+
density = 'comfortable',
|
|
27
|
+
class: className = '',
|
|
28
|
+
}: Props = $props();
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<ContentPageShell
|
|
32
|
+
{title}
|
|
33
|
+
{...definedOptions({ description, eyebrow, actions })}
|
|
34
|
+
{width}
|
|
35
|
+
{density}
|
|
36
|
+
class={className}
|
|
37
|
+
>
|
|
38
|
+
<div class="configuration-page__content">
|
|
39
|
+
{@render children()}
|
|
40
|
+
</div>
|
|
41
|
+
{#if footer}
|
|
42
|
+
<footer class="configuration-page__footer">
|
|
43
|
+
{@render footer()}
|
|
44
|
+
</footer>
|
|
45
|
+
{/if}
|
|
46
|
+
</ContentPageShell>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
.configuration-page__content {
|
|
50
|
+
display: grid;
|
|
51
|
+
gap: 24px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.configuration-page__footer {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-wrap: wrap;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: flex-end;
|
|
59
|
+
gap: 8px;
|
|
60
|
+
margin-top: 24px;
|
|
61
|
+
padding: 12px 0;
|
|
62
|
+
border-top: 1px solid var(--border);
|
|
63
|
+
background: var(--background);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@media (max-width: 640px) {
|
|
67
|
+
.configuration-page__footer {
|
|
68
|
+
justify-content: stretch;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.configuration-page__footer :global(button) {
|
|
72
|
+
flex: 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
eyebrow?: string;
|
|
6
|
+
actions?: Snippet;
|
|
7
|
+
footer?: Snippet;
|
|
8
|
+
children: Snippet;
|
|
9
|
+
width?: 'narrow' | 'default' | 'wide';
|
|
10
|
+
density?: 'compact' | 'comfortable';
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
declare const ConfigurationPage: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type ConfigurationPage = ReturnType<typeof ConfigurationPage>;
|
|
15
|
+
export default ConfigurationPage;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { scale } from 'svelte/transition';
|
|
3
2
|
import { PackageOpen } from '@lucide/svelte';
|
|
4
3
|
import { useTranslation } from '@svadmin/core';
|
|
5
4
|
|
|
@@ -17,19 +16,29 @@
|
|
|
17
16
|
</script>
|
|
18
17
|
|
|
19
18
|
<div
|
|
20
|
-
class="
|
|
21
|
-
in:scale={{ start: 0.95, duration: 200, opacity: 0 }}
|
|
19
|
+
class="empty-state"
|
|
22
20
|
role="status"
|
|
23
21
|
aria-live="polite"
|
|
24
22
|
>
|
|
25
|
-
<
|
|
26
|
-
<PackageOpen
|
|
23
|
+
<span class="empty-state__icon" aria-hidden="true">
|
|
24
|
+
<PackageOpen size={16} />
|
|
25
|
+
</span>
|
|
26
|
+
<div class="empty-state__body">
|
|
27
|
+
<h3>{title}</h3>
|
|
28
|
+
{#if description}<p>{description}</p>{/if}
|
|
29
|
+
{#if children}
|
|
30
|
+
<div class="empty-state__action">
|
|
31
|
+
{@render children()}
|
|
32
|
+
</div>
|
|
33
|
+
{/if}
|
|
27
34
|
</div>
|
|
28
|
-
<h3 class="svadmin-u-42536e69e639 svadmin-u-e83a7042bc91 svadmin-u-1d7f28b046ad">{title}</h3>
|
|
29
|
-
<p class="svadmin-u-fc7473ca09eb svadmin-u-bfa603190748 svadmin-u-50d0d216a2f8 svadmin-u-2472e9b81a97">{description}</p>
|
|
30
|
-
{#if children}
|
|
31
|
-
<div class="svadmin-u-31f2553311b6">
|
|
32
|
-
{@render children()}
|
|
33
|
-
</div>
|
|
34
|
-
{/if}
|
|
35
35
|
</div>
|
|
36
|
+
|
|
37
|
+
<style>
|
|
38
|
+
.empty-state { display: flex; align-items: flex-start; gap: 12px; padding: 16px; text-align: left; color: var(--foreground); }
|
|
39
|
+
.empty-state__icon { display: flex; align-items: center; justify-content: center; flex: 0 0 32px; height: 32px; border-radius: 6px; background: var(--muted); color: var(--muted-foreground); }
|
|
40
|
+
.empty-state__body { min-width: 0; flex: 1; overflow-wrap: anywhere; }
|
|
41
|
+
h3 { margin: 0; font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: 0; }
|
|
42
|
+
p { margin: 4px 0 0; font-size: 14px; line-height: 20px; color: var(--muted-foreground); }
|
|
43
|
+
.empty-state__action { margin-top: 12px; }
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { definedOptions } from '@svadmin/core/options';
|
|
4
|
+
import ContentPageShell from './content/ContentPageShell.svelte';
|
|
5
|
+
import WorkspaceLayout from './content/WorkspaceLayout.svelte';
|
|
6
|
+
|
|
7
|
+
export type PagePatternKind = 'list' | 'form' | 'detail' | 'workspace' | 'dashboard';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
kind: PagePatternKind;
|
|
11
|
+
title: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
eyebrow?: string;
|
|
14
|
+
actions?: Snippet;
|
|
15
|
+
toolbar?: Snippet;
|
|
16
|
+
metrics?: Snippet;
|
|
17
|
+
secondary?: Snippet;
|
|
18
|
+
children: Snippet;
|
|
19
|
+
footer?: Snippet;
|
|
20
|
+
width?: 'narrow' | 'default' | 'wide';
|
|
21
|
+
density?: 'compact' | 'comfortable';
|
|
22
|
+
pageId?: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
kind,
|
|
28
|
+
title,
|
|
29
|
+
description,
|
|
30
|
+
eyebrow,
|
|
31
|
+
actions,
|
|
32
|
+
toolbar,
|
|
33
|
+
metrics,
|
|
34
|
+
secondary,
|
|
35
|
+
children,
|
|
36
|
+
footer,
|
|
37
|
+
width,
|
|
38
|
+
density,
|
|
39
|
+
pageId,
|
|
40
|
+
class: className = '',
|
|
41
|
+
}: Props = $props();
|
|
42
|
+
|
|
43
|
+
const resolvedPageId = $derived(pageId ?? `pattern-${kind}`);
|
|
44
|
+
const resolvedWidth = $derived(width ?? (kind === 'form' ? 'narrow' : kind === 'detail' ? 'default' : 'wide'));
|
|
45
|
+
const resolvedDensity = $derived(density ?? (kind === 'list' || kind === 'workspace' ? 'compact' : 'comfortable'));
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<ContentPageShell
|
|
49
|
+
{title}
|
|
50
|
+
{...definedOptions({ description, eyebrow, actions })}
|
|
51
|
+
density={resolvedDensity}
|
|
52
|
+
width={resolvedWidth}
|
|
53
|
+
pageId={resolvedPageId}
|
|
54
|
+
class={className}
|
|
55
|
+
>
|
|
56
|
+
{#if metrics}
|
|
57
|
+
<div class="page-pattern__metrics" data-page-pattern-metrics>
|
|
58
|
+
{@render metrics()}
|
|
59
|
+
</div>
|
|
60
|
+
{/if}
|
|
61
|
+
|
|
62
|
+
{#if toolbar}
|
|
63
|
+
<div class="page-pattern__toolbar" data-page-pattern-toolbar>
|
|
64
|
+
{@render toolbar()}
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
67
|
+
|
|
68
|
+
{#if secondary}
|
|
69
|
+
<WorkspaceLayout primary={children} {...definedOptions({ secondary })} />
|
|
70
|
+
{:else}
|
|
71
|
+
<div class="page-pattern__content" data-page-pattern-content={kind} data-density={resolvedDensity}>
|
|
72
|
+
{@render children()}
|
|
73
|
+
</div>
|
|
74
|
+
{/if}
|
|
75
|
+
|
|
76
|
+
{#if footer}
|
|
77
|
+
<footer class="page-pattern__footer" data-page-pattern-footer>
|
|
78
|
+
{@render footer()}
|
|
79
|
+
</footer>
|
|
80
|
+
{/if}
|
|
81
|
+
</ContentPageShell>
|
|
82
|
+
|
|
83
|
+
<style>
|
|
84
|
+
.page-pattern__metrics {
|
|
85
|
+
display: grid;
|
|
86
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
|
|
87
|
+
gap: 16px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.page-pattern__toolbar,
|
|
91
|
+
.page-pattern__content {
|
|
92
|
+
min-width: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.page-pattern__content {
|
|
96
|
+
display: grid;
|
|
97
|
+
gap: 24px;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.page-pattern__content[data-density='compact'] {
|
|
101
|
+
gap: 16px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.page-pattern__footer {
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-wrap: wrap;
|
|
107
|
+
align-items: center;
|
|
108
|
+
justify-content: flex-end;
|
|
109
|
+
gap: 8px;
|
|
110
|
+
margin-top: 24px;
|
|
111
|
+
padding-top: 12px;
|
|
112
|
+
border-top: 1px solid var(--border);
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type PagePatternKind = 'list' | 'form' | 'detail' | 'workspace' | 'dashboard';
|
|
3
|
+
interface Props {
|
|
4
|
+
kind: PagePatternKind;
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
eyebrow?: string;
|
|
8
|
+
actions?: Snippet;
|
|
9
|
+
toolbar?: Snippet;
|
|
10
|
+
metrics?: Snippet;
|
|
11
|
+
secondary?: Snippet;
|
|
12
|
+
children: Snippet;
|
|
13
|
+
footer?: Snippet;
|
|
14
|
+
width?: 'narrow' | 'default' | 'wide';
|
|
15
|
+
density?: 'compact' | 'comfortable';
|
|
16
|
+
pageId?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const PagePattern: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type PagePattern = ReturnType<typeof PagePattern>;
|
|
21
|
+
export default PagePattern;
|
|
@@ -53,8 +53,23 @@
|
|
|
53
53
|
{#if action}<div class="svadmin-u-eccd13ef4f2f">{@render action()}</div>{/if}
|
|
54
54
|
</Alert.Root>
|
|
55
55
|
{:else}
|
|
56
|
-
<div class={'
|
|
57
|
-
<
|
|
58
|
-
|
|
56
|
+
<div class={'empty-state ' + className} role="status" aria-live="polite">
|
|
57
|
+
<span class="empty-state__icon" aria-hidden="true">
|
|
58
|
+
<Inbox size={16} />
|
|
59
|
+
</span>
|
|
60
|
+
<div class="empty-state__body">
|
|
61
|
+
<h3>{resolvedTitle}</h3>
|
|
62
|
+
{#if resolvedDescription}<p>{resolvedDescription}</p>{/if}
|
|
63
|
+
{#if action}<div class="empty-state__action">{@render action()}</div>{/if}
|
|
64
|
+
</div>
|
|
59
65
|
</div>
|
|
60
66
|
{/if}
|
|
67
|
+
|
|
68
|
+
<style>
|
|
69
|
+
.empty-state { display: flex; align-items: flex-start; gap: 12px; padding: 16px; text-align: left; color: var(--foreground); }
|
|
70
|
+
.empty-state__icon { display: flex; align-items: center; justify-content: center; flex: 0 0 32px; height: 32px; border-radius: 6px; background: var(--muted); color: var(--muted-foreground); }
|
|
71
|
+
.empty-state__body { min-width: 0; flex: 1; overflow-wrap: anywhere; }
|
|
72
|
+
.empty-state h3 { margin: 0; font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: 0; }
|
|
73
|
+
.empty-state p { margin: 4px 0 0; font-size: 14px; line-height: 20px; color: var(--muted-foreground); }
|
|
74
|
+
.empty-state__action { margin-top: 12px; }
|
|
75
|
+
</style>
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export { default as StatsCard } from './components/StatsCard.svelte';
|
|
|
36
36
|
export { default as MetricStrip } from './components/MetricStrip.svelte';
|
|
37
37
|
export type { MetricStripItem, MetricTone } from './components/MetricStrip.svelte';
|
|
38
38
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
39
|
+
export { default as PagePattern } from './components/PagePattern.svelte';
|
|
40
|
+
export type { PagePatternKind } from './components/PagePattern.svelte';
|
|
39
41
|
export { default as ResourceOperationsPage } from './components/ResourceOperationsPage.svelte';
|
|
40
42
|
export { default as RecordDetailDrawer } from './components/RecordDetailDrawer.svelte';
|
|
41
43
|
export { default as ResourceAccessGuard } from './components/ResourceAccessGuard.svelte';
|
|
@@ -276,6 +278,7 @@ export { default as ContentPageShell } from './components/content/ContentPageShe
|
|
|
276
278
|
export { default as DashboardPage } from './components/content/DashboardPage.svelte';
|
|
277
279
|
export { default as PageSection } from './components/content/PageSection.svelte';
|
|
278
280
|
export { default as ContentPageHeader } from './components/content/ContentPageHeader.svelte';
|
|
281
|
+
export { default as ConfigurationPage } from './components/ConfigurationPage.svelte';
|
|
279
282
|
export { default as SectionHeader } from './components/content/SectionHeader.svelte';
|
|
280
283
|
export { default as PageToolbar } from './components/content/PageToolbar.svelte';
|
|
281
284
|
export { default as WorkspaceLayout } from './components/content/WorkspaceLayout.svelte';
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ export { default as EmptyState } from './components/EmptyState.svelte';
|
|
|
30
30
|
export { default as StatsCard } from './components/StatsCard.svelte';
|
|
31
31
|
export { default as MetricStrip } from './components/MetricStrip.svelte';
|
|
32
32
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
33
|
+
export { default as PagePattern } from './components/PagePattern.svelte';
|
|
33
34
|
export { default as ResourceOperationsPage } from './components/ResourceOperationsPage.svelte';
|
|
34
35
|
export { default as RecordDetailDrawer } from './components/RecordDetailDrawer.svelte';
|
|
35
36
|
export { default as ResourceAccessGuard } from './components/ResourceAccessGuard.svelte';
|
|
@@ -245,6 +246,7 @@ export { default as ContentPageShell } from './components/content/ContentPageShe
|
|
|
245
246
|
export { default as DashboardPage } from './components/content/DashboardPage.svelte';
|
|
246
247
|
export { default as PageSection } from './components/content/PageSection.svelte';
|
|
247
248
|
export { default as ContentPageHeader } from './components/content/ContentPageHeader.svelte';
|
|
249
|
+
export { default as ConfigurationPage } from './components/ConfigurationPage.svelte';
|
|
248
250
|
export { default as SectionHeader } from './components/content/SectionHeader.svelte';
|
|
249
251
|
export { default as PageToolbar } from './components/content/PageToolbar.svelte';
|
|
250
252
|
export { default as WorkspaceLayout } from './components/content/WorkspaceLayout.svelte';
|