@dorsk/tsumikit 0.6.0 → 0.7.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.
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Empty / placeholder state — the "nothing here yet" panel every unworked
|
|
3
|
+
// settings section, empty list, and zero-result table reaches for. Centered
|
|
4
|
+
// stack of:
|
|
5
|
+
// • a tinted icon chip (when `icon`/`iconChildren` is set)
|
|
6
|
+
// • a title
|
|
7
|
+
// • a muted, max-width-limited description
|
|
8
|
+
// • an optional action area (slot via `action`, or a built-in button when
|
|
9
|
+
// `actionLabel`+`onAction` are passed)
|
|
10
|
+
// Everything is token-driven. `compact` tightens spacing for inline/table use.
|
|
11
|
+
import type { Snippet } from 'svelte';
|
|
12
|
+
import Button from '../atoms/Button.svelte';
|
|
13
|
+
import Icon, { type IconName } from '../atoms/Icon.svelte';
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
icon,
|
|
19
|
+
// Tints the icon chip with a semantic hue; neutral falls back to accent.
|
|
20
|
+
tone = 'neutral',
|
|
21
|
+
// Built-in primary action button. Provide both to render it; for anything
|
|
22
|
+
// richer (multiple buttons, links) use the `action` snippet instead.
|
|
23
|
+
actionLabel,
|
|
24
|
+
onAction,
|
|
25
|
+
// Tighter padding/sizing for inline use (empty table body, narrow panels).
|
|
26
|
+
compact = false,
|
|
27
|
+
class: klass = '',
|
|
28
|
+
// Raw SVG markup for a custom glyph — passed straight to `Icon`.
|
|
29
|
+
iconChildren,
|
|
30
|
+
// Custom action area (overrides the built-in button when present).
|
|
31
|
+
action,
|
|
32
|
+
...rest
|
|
33
|
+
}: {
|
|
34
|
+
title: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
icon?: IconName;
|
|
37
|
+
tone?: 'neutral' | 'ok' | 'warn' | 'danger' | 'info';
|
|
38
|
+
actionLabel?: string;
|
|
39
|
+
onAction?: () => void;
|
|
40
|
+
compact?: boolean;
|
|
41
|
+
class?: string;
|
|
42
|
+
iconChildren?: Snippet;
|
|
43
|
+
action?: Snippet;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
} = $props();
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<div class="empty empty-{tone} {compact ? 'empty-compact' : ''} {klass}" {...rest}>
|
|
49
|
+
{#if icon || iconChildren}
|
|
50
|
+
<span class="empty-chip" aria-hidden="true">
|
|
51
|
+
<Icon name={icon}>{@render iconChildren?.()}</Icon>
|
|
52
|
+
</span>
|
|
53
|
+
{/if}
|
|
54
|
+
<p class="empty-title">{title}</p>
|
|
55
|
+
{#if description}<p class="empty-desc">{description}</p>{/if}
|
|
56
|
+
{#if action}
|
|
57
|
+
<div class="empty-action">{@render action()}</div>
|
|
58
|
+
{:else if actionLabel && onAction}
|
|
59
|
+
<div class="empty-action">
|
|
60
|
+
<Button variant="primary" onclick={onAction}>{actionLabel}</Button>
|
|
61
|
+
</div>
|
|
62
|
+
{/if}
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<style>
|
|
66
|
+
.empty {
|
|
67
|
+
/* The tone hue the chip pulls from; neutral falls back to accent. */
|
|
68
|
+
--empty-tone: var(--accent);
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
text-align: center;
|
|
74
|
+
gap: var(--sp-3);
|
|
75
|
+
padding: var(--sp-8) var(--sp-5);
|
|
76
|
+
}
|
|
77
|
+
:global(.empty-ok) {
|
|
78
|
+
--empty-tone: var(--ok);
|
|
79
|
+
}
|
|
80
|
+
:global(.empty-warn) {
|
|
81
|
+
--empty-tone: var(--warn);
|
|
82
|
+
}
|
|
83
|
+
:global(.empty-danger) {
|
|
84
|
+
--empty-tone: var(--danger);
|
|
85
|
+
}
|
|
86
|
+
:global(.empty-info) {
|
|
87
|
+
--empty-tone: var(--info);
|
|
88
|
+
}
|
|
89
|
+
.empty-compact {
|
|
90
|
+
gap: var(--sp-2);
|
|
91
|
+
padding: var(--sp-4) var(--sp-3);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.empty-chip {
|
|
95
|
+
display: inline-flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
flex: none;
|
|
99
|
+
width: 3rem;
|
|
100
|
+
height: 3rem;
|
|
101
|
+
border-radius: var(--r-lg);
|
|
102
|
+
font-size: 1.5rem;
|
|
103
|
+
color: var(--empty-tone);
|
|
104
|
+
background: color-mix(in srgb, var(--empty-tone) 14%, transparent);
|
|
105
|
+
border: 1px solid color-mix(in srgb, var(--empty-tone) 30%, transparent);
|
|
106
|
+
}
|
|
107
|
+
.empty-compact .empty-chip {
|
|
108
|
+
width: 2.25rem;
|
|
109
|
+
height: 2.25rem;
|
|
110
|
+
font-size: 1.125rem;
|
|
111
|
+
}
|
|
112
|
+
.empty-title {
|
|
113
|
+
margin: 0;
|
|
114
|
+
font-size: var(--fs-md);
|
|
115
|
+
font-weight: var(--fw-semibold);
|
|
116
|
+
color: var(--text);
|
|
117
|
+
line-height: 1.3;
|
|
118
|
+
}
|
|
119
|
+
.empty-desc {
|
|
120
|
+
margin: 0;
|
|
121
|
+
max-width: 32ch;
|
|
122
|
+
font-size: var(--fs-sm);
|
|
123
|
+
color: var(--text-muted);
|
|
124
|
+
line-height: 1.5;
|
|
125
|
+
}
|
|
126
|
+
.empty-action {
|
|
127
|
+
margin-top: var(--sp-1);
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type IconName } from '../atoms/Icon.svelte';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
icon?: IconName;
|
|
7
|
+
tone?: 'neutral' | 'ok' | 'warn' | 'danger' | 'info';
|
|
8
|
+
actionLabel?: string;
|
|
9
|
+
onAction?: () => void;
|
|
10
|
+
compact?: boolean;
|
|
11
|
+
class?: string;
|
|
12
|
+
iconChildren?: Snippet;
|
|
13
|
+
action?: Snippet;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
16
|
+
declare const EmptyState: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
17
|
+
type EmptyState = ReturnType<typeof EmptyState>;
|
|
18
|
+
export default EmptyState;
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export { type AccordionItem, default as Accordion, } from './components/molecule
|
|
|
27
27
|
export { default as CodeBlock } from './components/molecules/CodeBlock.svelte';
|
|
28
28
|
export { default as CopyButton } from './components/molecules/CopyButton.svelte';
|
|
29
29
|
export { default as Dropzone } from './components/molecules/Dropzone.svelte';
|
|
30
|
+
export { default as EmptyState } from './components/molecules/EmptyState.svelte';
|
|
30
31
|
export { default as Field } from './components/molecules/Field.svelte';
|
|
31
32
|
export { default as FileButton } from './components/molecules/FileButton.svelte';
|
|
32
33
|
export { default as FontScalePicker } from './components/molecules/FontScalePicker.svelte';
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ export { default as Accordion, } from './components/molecules/Accordion.svelte';
|
|
|
34
34
|
export { default as CodeBlock } from './components/molecules/CodeBlock.svelte';
|
|
35
35
|
export { default as CopyButton } from './components/molecules/CopyButton.svelte';
|
|
36
36
|
export { default as Dropzone } from './components/molecules/Dropzone.svelte';
|
|
37
|
+
export { default as EmptyState } from './components/molecules/EmptyState.svelte';
|
|
37
38
|
// ---- molecules ----
|
|
38
39
|
export { default as Field } from './components/molecules/Field.svelte';
|
|
39
40
|
export { default as FileButton } from './components/molecules/FileButton.svelte';
|
package/package.json
CHANGED