@runwisp/ui 0.7.0 → 0.8.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/package.json +4 -12
- package/src/lib/components/Alert.svelte +4 -4
- package/src/lib/components/Avatar.svelte +8 -8
- package/src/lib/components/Badge.svelte +23 -23
- package/src/lib/components/CronInput.svelte +1 -1
- package/src/lib/components/Dropdown.svelte +16 -3
- package/src/lib/components/DurationInput.svelte +4 -2
- package/src/lib/components/EmptyState.svelte +1 -1
- package/src/lib/components/FormField.svelte +4 -2
- package/src/lib/components/Input.svelte +4 -2
- package/src/lib/components/LogConsole.svelte +22 -29
- package/src/lib/components/Progress.svelte +4 -4
- package/src/lib/components/RadioGroup.svelte +3 -1
- package/src/lib/components/Select.svelte +1 -1
- package/src/lib/components/Sparkline.svelte +11 -3
- package/src/lib/components/TaskCard.svelte +4 -4
- package/src/lib/components/Textarea.svelte +2 -2
- package/src/lib/components/ToastContainer.svelte +4 -4
- package/src/lib/components/Tooltip.svelte +11 -2
- package/src/lib/components/dashboard/RunDetailPanel.svelte +24 -17
- package/src/lib/components/dashboard/RunsList.svelte +19 -2
- package/src/lib/components/dashboard/cron-format.test.ts +67 -0
- package/src/lib/components/dashboard/cron-format.ts +71 -0
- package/src/lib/components/dashboard/status-config.test.ts +18 -0
- package/src/lib/components/dashboard/status-config.ts +19 -0
- package/src/lib/index.ts +1 -4
- package/src/lib/log-console/ansi.test.ts +50 -0
- package/src/lib/log-console/ansi.ts +39 -0
- package/src/lib/theme-tokens.css +33 -0
- package/src/lib/theme.css +6 -0
- package/src/lib/utils/format.test.ts +37 -1
- package/src/lib/utils/format.ts +8 -6
- package/src/stories/Avatar.stories.svelte +1 -1
- package/src/stories/Breadcrumb.stories.svelte +3 -3
- package/src/stories/Card.stories.svelte +21 -19
- package/src/stories/Checkbox.stories.svelte +2 -2
- package/src/stories/DataGrid.stories.svelte +1 -1
- package/src/stories/Dropdown.stories.svelte +3 -3
- package/src/stories/EmptyState.stories.svelte +1 -1
- package/src/stories/LogConsole.stories.svelte +75 -0
- package/src/stories/LogConsoleDemo.svelte +60 -0
- package/src/stories/Logo.stories.svelte +9 -9
- package/src/stories/Modal.stories.svelte +12 -10
- package/src/stories/Progress.stories.svelte +6 -6
- package/src/stories/Tabs.stories.svelte +16 -12
- package/src/stories/Toggle.stories.svelte +2 -2
- package/src/stories/Tooltip.stories.svelte +5 -5
- package/src/lib/utils/toast.ts +0 -60
package/src/lib/utils/format.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: PoppyCake, s.r.o.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { formatDistance } from "date-fns";
|
|
5
5
|
|
|
6
6
|
export function formatBytes(bytes: number): string {
|
|
7
7
|
const units: [string, ...string[]] = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
@@ -16,14 +16,16 @@ export function formatBytes(bytes: number): string {
|
|
|
16
16
|
return `${new Intl.NumberFormat("en", { maximumFractionDigits: digits }).format(value)} ${unit}`;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function formatRelativeTime(dateStr: string | Date): string {
|
|
20
|
-
return
|
|
19
|
+
export function formatRelativeTime(dateStr: string | Date, now: Date = new Date()): string {
|
|
20
|
+
return formatDistance(new Date(dateStr), now, { addSuffix: true });
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export function formatRelativeTimeWithAbsolute(
|
|
23
|
+
export function formatRelativeTimeWithAbsolute(
|
|
24
|
+
dateStr: string | Date,
|
|
25
|
+
now: Date = new Date(),
|
|
26
|
+
): string {
|
|
24
27
|
const date = new Date(dateStr);
|
|
25
|
-
const relative =
|
|
26
|
-
const now = new Date();
|
|
28
|
+
const relative = formatDistance(date, now, { addSuffix: true }).replace("about ", "");
|
|
27
29
|
const diffMs = Math.abs(now.getTime() - date.getTime());
|
|
28
30
|
const diffDays = diffMs / (1000 * 60 * 60 * 24);
|
|
29
31
|
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
<Avatar name="Charlie Brown" size="md" />
|
|
69
69
|
<Avatar name="Diana Ross" size="md" />
|
|
70
70
|
<div
|
|
71
|
-
class="flex h-10 w-10 items-center justify-center rounded-full bg-mist-200 text-sm font-medium text-
|
|
71
|
+
class="flex h-10 w-10 items-center justify-center rounded-full bg-mist-200 text-sm font-medium text-on-surface-muted ring-2 ring-white"
|
|
72
72
|
>
|
|
73
73
|
+5
|
|
74
74
|
</div>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
/>
|
|
50
50
|
|
|
51
51
|
<Story name="In Page Context" asChild>
|
|
52
|
-
<div class="rounded-xl border border-
|
|
52
|
+
<div class="rounded-xl border border-outline bg-white p-6">
|
|
53
53
|
<Breadcrumb
|
|
54
54
|
items={[
|
|
55
55
|
{ label: "Dashboard", href: "/" },
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
]}
|
|
59
59
|
class="mb-4"
|
|
60
60
|
/>
|
|
61
|
-
<h1 class="text-2xl font-semibold text-
|
|
62
|
-
<p class="mt-1 text-
|
|
61
|
+
<h1 class="text-2xl font-semibold text-on-surface">backup-database</h1>
|
|
62
|
+
<p class="mt-1 text-on-surface-muted">Daily database backup task</p>
|
|
63
63
|
</div>
|
|
64
64
|
</Story>
|
|
@@ -25,26 +25,28 @@
|
|
|
25
25
|
</script>
|
|
26
26
|
|
|
27
27
|
<Story name="Default" args={{}}>
|
|
28
|
-
<h3 class="mb-2 text-lg font-semibold text-
|
|
29
|
-
<p class="text-
|
|
28
|
+
<h3 class="mb-2 text-lg font-semibold text-on-surface">Card Title</h3>
|
|
29
|
+
<p class="text-on-surface-muted">
|
|
30
|
+
This is a basic card component with default padding and styling.
|
|
31
|
+
</p>
|
|
30
32
|
</Story>
|
|
31
33
|
|
|
32
34
|
<Story name="With Header and Footer" asChild>
|
|
33
35
|
<Card>
|
|
34
36
|
{#snippet header()}
|
|
35
37
|
<div class="flex items-center justify-between">
|
|
36
|
-
<h3 class="font-semibold text-
|
|
38
|
+
<h3 class="font-semibold text-on-surface">Task Configuration</h3>
|
|
37
39
|
<Badge variant="success" dot>Active</Badge>
|
|
38
40
|
</div>
|
|
39
41
|
{/snippet}
|
|
40
42
|
|
|
41
43
|
<div class="space-y-3">
|
|
42
|
-
<p class="text-sm text-
|
|
44
|
+
<p class="text-sm text-on-surface-muted">
|
|
43
45
|
Configure your scheduled task settings and execution parameters.
|
|
44
46
|
</p>
|
|
45
47
|
<div class="text-sm">
|
|
46
|
-
<span class="text-
|
|
47
|
-
<span class="ml-2 font-mono text-
|
|
48
|
+
<span class="text-on-surface-muted">Schedule:</span>
|
|
49
|
+
<span class="ml-2 font-mono text-on-surface">*/5 * * * *</span>
|
|
48
50
|
</div>
|
|
49
51
|
</div>
|
|
50
52
|
|
|
@@ -60,16 +62,16 @@
|
|
|
60
62
|
<Story name="Hoverable" asChild>
|
|
61
63
|
<div class="grid max-w-3xl grid-cols-3 gap-4">
|
|
62
64
|
<Card hover>
|
|
63
|
-
<h4 class="mb-1 font-semibold text-
|
|
64
|
-
<p class="text-2xl font-bold text-
|
|
65
|
+
<h4 class="mb-1 font-semibold text-on-surface">Total Tasks</h4>
|
|
66
|
+
<p class="text-2xl font-bold text-primary">156</p>
|
|
65
67
|
</Card>
|
|
66
68
|
<Card hover>
|
|
67
|
-
<h4 class="mb-1 font-semibold text-
|
|
68
|
-
<p class="text-2xl font-bold text-success-
|
|
69
|
+
<h4 class="mb-1 font-semibold text-on-surface">Active</h4>
|
|
70
|
+
<p class="text-2xl font-bold text-success-soft-text">142</p>
|
|
69
71
|
</Card>
|
|
70
72
|
<Card hover>
|
|
71
|
-
<h4 class="mb-1 font-semibold text-
|
|
72
|
-
<p class="text-2xl font-bold text-danger-
|
|
73
|
+
<h4 class="mb-1 font-semibold text-on-surface">Failed</h4>
|
|
74
|
+
<p class="text-2xl font-bold text-danger-soft-text">3</p>
|
|
73
75
|
</Card>
|
|
74
76
|
</div>
|
|
75
77
|
</Story>
|
|
@@ -77,13 +79,13 @@
|
|
|
77
79
|
<Story name="Different Padding" asChild>
|
|
78
80
|
<div class="max-w-md space-y-4">
|
|
79
81
|
<Card padding="sm">
|
|
80
|
-
<p class="text-sm text-
|
|
82
|
+
<p class="text-sm text-on-surface-muted">Small padding card</p>
|
|
81
83
|
</Card>
|
|
82
84
|
<Card padding="md">
|
|
83
|
-
<p class="text-sm text-
|
|
85
|
+
<p class="text-sm text-on-surface-muted">Medium padding card (default)</p>
|
|
84
86
|
</Card>
|
|
85
87
|
<Card padding="lg">
|
|
86
|
-
<p class="text-sm text-
|
|
88
|
+
<p class="text-sm text-on-surface-muted">Large padding card</p>
|
|
87
89
|
</Card>
|
|
88
90
|
</div>
|
|
89
91
|
</Story>
|
|
@@ -91,16 +93,16 @@
|
|
|
91
93
|
<Story name="Different Shadows" asChild>
|
|
92
94
|
<div class="flex gap-4">
|
|
93
95
|
<Card shadow="none">
|
|
94
|
-
<p class="text-sm text-
|
|
96
|
+
<p class="text-sm text-on-surface-muted">No shadow</p>
|
|
95
97
|
</Card>
|
|
96
98
|
<Card shadow="sm">
|
|
97
|
-
<p class="text-sm text-
|
|
99
|
+
<p class="text-sm text-on-surface-muted">Small shadow</p>
|
|
98
100
|
</Card>
|
|
99
101
|
<Card shadow="md">
|
|
100
|
-
<p class="text-sm text-
|
|
102
|
+
<p class="text-sm text-on-surface-muted">Medium shadow</p>
|
|
101
103
|
</Card>
|
|
102
104
|
<Card shadow="lg">
|
|
103
|
-
<p class="text-sm text-
|
|
105
|
+
<p class="text-sm text-on-surface-muted">Large shadow</p>
|
|
104
106
|
</Card>
|
|
105
107
|
</div>
|
|
106
108
|
</Story>
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
</Story>
|
|
54
54
|
|
|
55
55
|
<Story name="Settings Example" asChild>
|
|
56
|
-
<div class="max-w-md space-y-4 rounded-xl border border-
|
|
57
|
-
<h3 class="font-semibold text-
|
|
56
|
+
<div class="max-w-md space-y-4 rounded-xl border border-outline bg-white p-4">
|
|
57
|
+
<h3 class="font-semibold text-on-surface">Notification Preferences</h3>
|
|
58
58
|
<div class="space-y-3">
|
|
59
59
|
<Checkbox
|
|
60
60
|
label="Task completion alerts"
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
{/snippet}
|
|
82
82
|
|
|
83
83
|
{#snippet durationCell(/** @type {any} */ row)}
|
|
84
|
-
<span class="font-mono text-
|
|
84
|
+
<span class="font-mono text-on-surface-muted">{row.duration}</span>
|
|
85
85
|
{/snippet}
|
|
86
86
|
|
|
87
87
|
<Story name="Basic" args={{ columns, data: tasks }} />
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
/>
|
|
70
70
|
|
|
71
71
|
<Story name="In Context" asChild>
|
|
72
|
-
<div class="max-w-md rounded-xl border border-
|
|
72
|
+
<div class="max-w-md rounded-xl border border-outline bg-white p-4">
|
|
73
73
|
<div class="flex items-center justify-between">
|
|
74
74
|
<div>
|
|
75
|
-
<h3 class="font-semibold text-
|
|
76
|
-
<p class="text-sm text-
|
|
75
|
+
<h3 class="font-semibold text-on-surface">backup-database</h3>
|
|
76
|
+
<p class="text-sm text-on-surface-muted">Daily at 2:00 AM</p>
|
|
77
77
|
</div>
|
|
78
78
|
<Dropdown items={taskActions} />
|
|
79
79
|
</div>
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
</Story>
|
|
61
61
|
|
|
62
62
|
<Story name="In Card Context" asChild>
|
|
63
|
-
<div class="max-w-lg rounded-xl border border-
|
|
63
|
+
<div class="max-w-lg rounded-xl border border-outline bg-white p-6">
|
|
64
64
|
<EmptyState
|
|
65
65
|
icon={Calendar}
|
|
66
66
|
title="No upcoming executions"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<!-- SPDX-FileCopyrightText: PoppyCake, s.r.o. -->
|
|
2
|
+
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
|
3
|
+
|
|
4
|
+
<script module>
|
|
5
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
6
|
+
import LogConsole from "$lib/components/LogConsole.svelte";
|
|
7
|
+
import LogConsoleDemo from "./LogConsoleDemo.svelte";
|
|
8
|
+
|
|
9
|
+
const { Story } = defineMeta({
|
|
10
|
+
title: "Dashboard/LogConsole",
|
|
11
|
+
component: LogConsole,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
parameters: {
|
|
14
|
+
backgrounds: { default: "dark" },
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const ESC = "\x1b[";
|
|
19
|
+
|
|
20
|
+
const SAMPLE_LOG = [
|
|
21
|
+
`${ESC}90m2026-06-05T03:00:00+02:00${ESC}0m ${ESC}1mbackup-database${ESC}0m starting`,
|
|
22
|
+
`${ESC}36m[info]${ESC}0m connecting to postgres://localhost:5432/app`,
|
|
23
|
+
`${ESC}36m[info]${ESC}0m dumping schema ${ESC}1mpublic${ESC}0m (12 tables)`,
|
|
24
|
+
` ${ESC}32m✓${ESC}0m users ${ESC}90m(184,221 rows, 48 MB)${ESC}0m`,
|
|
25
|
+
` ${ESC}32m✓${ESC}0m sessions ${ESC}90m(902,114 rows, 210 MB)${ESC}0m`,
|
|
26
|
+
` ${ESC}32m✓${ESC}0m invoices ${ESC}90m(45,002 rows, 12 MB)${ESC}0m`,
|
|
27
|
+
`${ESC}33m[warn]${ESC}0m table ${ESC}1maudit_log${ESC}0m exceeds 1 GB — consider partitioning`,
|
|
28
|
+
` ${ESC}32m✓${ESC}0m audit_log ${ESC}90m(7,212,330 rows, 1.2 GB)${ESC}0m`,
|
|
29
|
+
`${ESC}31m[error]${ESC}0m checksum mismatch on chunk 7, retrying ${ESC}90m(attempt 1/3)${ESC}0m`,
|
|
30
|
+
` ${ESC}32m✓${ESC}0m retry succeeded`,
|
|
31
|
+
`${ESC}36m[info]${ESC}0m compressing with ${ESC}35mzstd -19${ESC}0m`,
|
|
32
|
+
`${ESC}36m[info]${ESC}0m uploading to ${ESC}34ms3://backups/app/2026-06-05.tar.zst${ESC}0m`,
|
|
33
|
+
`${ESC}32m[done]${ESC}0m backup completed in ${ESC}1m42.7s${ESC}0m ${ESC}90m(exit 0)${ESC}0m`,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const PALETTE_DEMO = [
|
|
37
|
+
"Standard foreground (30–37):",
|
|
38
|
+
`${ESC}30mblack${ESC}0m ${ESC}31mred${ESC}0m ${ESC}32mgreen${ESC}0m ${ESC}33myellow${ESC}0m ${ESC}34mblue${ESC}0m ${ESC}35mmagenta${ESC}0m ${ESC}36mcyan${ESC}0m ${ESC}37mwhite${ESC}0m`,
|
|
39
|
+
"",
|
|
40
|
+
"Bright foreground (90–97):",
|
|
41
|
+
`${ESC}90mblack${ESC}0m ${ESC}91mred${ESC}0m ${ESC}92mgreen${ESC}0m ${ESC}93myellow${ESC}0m ${ESC}94mblue${ESC}0m ${ESC}95mmagenta${ESC}0m ${ESC}96mcyan${ESC}0m ${ESC}97mwhite${ESC}0m`,
|
|
42
|
+
"",
|
|
43
|
+
"Bold + colour:",
|
|
44
|
+
`${ESC}1;31mbold red${ESC}0m ${ESC}1;32mbold green${ESC}0m ${ESC}1;34mbold blue${ESC}0m ${ESC}1;36mbold cyan${ESC}0m`,
|
|
45
|
+
"",
|
|
46
|
+
"Backgrounds (41–47):",
|
|
47
|
+
`${ESC}41m red ${ESC}0m ${ESC}42m green ${ESC}0m ${ESC}43m yellow ${ESC}0m ${ESC}44m blue ${ESC}0m ${ESC}45m magenta ${ESC}0m ${ESC}46m cyan ${ESC}0m`,
|
|
48
|
+
"",
|
|
49
|
+
"Underline + colour:",
|
|
50
|
+
`${ESC}4;36munderlined cyan link${ESC}0m and ${ESC}4;33munderlined yellow${ESC}0m`,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const STREAM_TICKS = [
|
|
54
|
+
`${ESC}36m[info]${ESC}0m processed batch ${ESC}1m#42${ESC}0m ${ESC}90m(1,024 records)${ESC}0m`,
|
|
55
|
+
`${ESC}32m✓${ESC}0m health check passed`,
|
|
56
|
+
`${ESC}33m[warn]${ESC}0m slow query detected ${ESC}90m(812ms)${ESC}0m`,
|
|
57
|
+
`${ESC}36m[info]${ESC}0m flushing write buffer`,
|
|
58
|
+
];
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<Story name="Sample Run Log" asChild>
|
|
62
|
+
<LogConsoleDemo lines={SAMPLE_LOG} />
|
|
63
|
+
</Story>
|
|
64
|
+
|
|
65
|
+
<Story name="ANSI Palette" asChild>
|
|
66
|
+
<LogConsoleDemo lines={PALETTE_DEMO} />
|
|
67
|
+
</Story>
|
|
68
|
+
|
|
69
|
+
<Story name="Streaming" asChild>
|
|
70
|
+
<LogConsoleDemo lines={SAMPLE_LOG.slice(0, 5)} finished={false} streamLines={STREAM_TICKS} />
|
|
71
|
+
</Story>
|
|
72
|
+
|
|
73
|
+
<Story name="Truncated" asChild>
|
|
74
|
+
<LogConsoleDemo lines={SAMPLE_LOG.slice(6)} firstAvailableLine={1500} />
|
|
75
|
+
</Story>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!-- SPDX-FileCopyrightText: PoppyCake, s.r.o. -->
|
|
2
|
+
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
|
3
|
+
|
|
4
|
+
<!-- Storybook-only harness: LogConsole is seeded by its parent calling
|
|
5
|
+
onStream(), so this wrapper feeds it a canned LogEvent on mount and,
|
|
6
|
+
optionally, keeps appending lines to demo live streaming. -->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { onMount } from "svelte";
|
|
9
|
+
import LogConsole from "$lib/components/LogConsole.svelte";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
lines: string[];
|
|
13
|
+
finished?: boolean;
|
|
14
|
+
firstAvailableLine?: number;
|
|
15
|
+
streamLines?: string[];
|
|
16
|
+
height?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
lines,
|
|
21
|
+
finished = true,
|
|
22
|
+
firstAvailableLine = 0,
|
|
23
|
+
streamLines = [],
|
|
24
|
+
height = "24rem",
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
|
|
27
|
+
let logConsole = $state<LogConsole | null>(null);
|
|
28
|
+
|
|
29
|
+
function toEvent(slice: Record<number, string>, total: number): void {
|
|
30
|
+
logConsole?.onStream({
|
|
31
|
+
lines: slice,
|
|
32
|
+
sizeLines: total,
|
|
33
|
+
sizeBytes: Object.values(slice).reduce((n, l) => n + l.length + 1, 0),
|
|
34
|
+
finished,
|
|
35
|
+
firstAvailableLine,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onMount(() => {
|
|
40
|
+
const slice: Record<number, string> = {};
|
|
41
|
+
lines.forEach((text, i) => {
|
|
42
|
+
slice[firstAvailableLine + i] = text;
|
|
43
|
+
});
|
|
44
|
+
toEvent(slice, firstAvailableLine + lines.length);
|
|
45
|
+
|
|
46
|
+
if (finished || streamLines.length === 0) return;
|
|
47
|
+
|
|
48
|
+
let next = firstAvailableLine + lines.length;
|
|
49
|
+
const timer = setInterval(() => {
|
|
50
|
+
const text = streamLines[(next - lines.length) % streamLines.length] ?? "";
|
|
51
|
+
toEvent({ [next]: text }, next + 1);
|
|
52
|
+
next += 1;
|
|
53
|
+
}, 700);
|
|
54
|
+
return () => clearInterval(timer);
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<div class="overflow-hidden rounded-xl border border-outline" style="height: {height};">
|
|
59
|
+
<LogConsole bind:this={logConsole} />
|
|
60
|
+
</div>
|
|
@@ -21,26 +21,26 @@
|
|
|
21
21
|
<Story name="Default" args={{}} />
|
|
22
22
|
|
|
23
23
|
<Story name="Sizes" asChild>
|
|
24
|
-
<div class="flex items-center gap-8 rounded-lg bg-
|
|
24
|
+
<div class="flex items-center gap-8 rounded-lg bg-surface-sunken p-8">
|
|
25
25
|
<div class="flex flex-col items-center gap-2">
|
|
26
26
|
<Logo size="xs" />
|
|
27
|
-
<span class="text-xs text-
|
|
27
|
+
<span class="text-xs text-on-surface-muted">XS</span>
|
|
28
28
|
</div>
|
|
29
29
|
<div class="flex flex-col items-center gap-2">
|
|
30
30
|
<Logo size="sm" />
|
|
31
|
-
<span class="text-xs text-
|
|
31
|
+
<span class="text-xs text-on-surface-muted">SM</span>
|
|
32
32
|
</div>
|
|
33
33
|
<div class="flex flex-col items-center gap-2">
|
|
34
34
|
<Logo size="md" />
|
|
35
|
-
<span class="text-xs text-
|
|
35
|
+
<span class="text-xs text-on-surface-muted">MD</span>
|
|
36
36
|
</div>
|
|
37
37
|
<div class="flex flex-col items-center gap-2">
|
|
38
38
|
<Logo size="lg" />
|
|
39
|
-
<span class="text-xs text-
|
|
39
|
+
<span class="text-xs text-on-surface-muted">LG</span>
|
|
40
40
|
</div>
|
|
41
41
|
<div class="flex flex-col items-center gap-2">
|
|
42
42
|
<Logo size="xl" />
|
|
43
|
-
<span class="text-xs text-
|
|
43
|
+
<span class="text-xs text-on-surface-muted">XL</span>
|
|
44
44
|
</div>
|
|
45
45
|
</div>
|
|
46
46
|
</Story>
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
</Story>
|
|
63
63
|
|
|
64
64
|
<Story name="With Custom Class" asChild>
|
|
65
|
-
<div class="flex items-center gap-4 rounded-lg bg-
|
|
66
|
-
<Logo size="lg" class="text-
|
|
67
|
-
<span class="text-lg font-semibold text-
|
|
65
|
+
<div class="flex items-center gap-4 rounded-lg bg-surface-sunken p-8">
|
|
66
|
+
<Logo size="lg" class="text-primary" />
|
|
67
|
+
<span class="text-lg font-semibold text-on-surface">Custom Color</span>
|
|
68
68
|
</div>
|
|
69
69
|
</Story>
|
|
70
70
|
|
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
title="Modal Title"
|
|
39
39
|
description="This is a description of the modal."
|
|
40
40
|
>
|
|
41
|
-
<p class="text-
|
|
41
|
+
<p class="text-on-surface-muted">
|
|
42
|
+
This is the modal content. You can put any content here.
|
|
43
|
+
</p>
|
|
42
44
|
</Modal>
|
|
43
45
|
</Story>
|
|
44
46
|
|
|
@@ -62,7 +64,7 @@
|
|
|
62
64
|
<Story name="Confirmation Dialog" asChild>
|
|
63
65
|
<Button variant="danger" onclick={() => (confirmOpen = true)}>Delete Task</Button>
|
|
64
66
|
<Modal bind:open={confirmOpen} title="Delete Task?" size="sm">
|
|
65
|
-
<p class="text-
|
|
67
|
+
<p class="text-on-surface-muted">
|
|
66
68
|
Are you sure you want to delete <strong>backup-database</strong>? This action cannot be
|
|
67
69
|
undone.
|
|
68
70
|
</p>
|
|
@@ -81,15 +83,15 @@
|
|
|
81
83
|
<div
|
|
82
84
|
class="max-h-96 overflow-auto rounded-lg bg-mist-900 p-4 font-mono text-sm text-mist-100"
|
|
83
85
|
>
|
|
84
|
-
<p class="text-
|
|
85
|
-
<p class="text-
|
|
86
|
-
<p class="text-
|
|
87
|
-
<p class="text-
|
|
88
|
-
<p class="text-
|
|
89
|
-
<p class="text-
|
|
90
|
-
<p class="text-
|
|
86
|
+
<p class="text-info">Starting task execution...</p>
|
|
87
|
+
<p class="text-on-surface-faint">Connecting to database...</p>
|
|
88
|
+
<p class="text-on-surface-faint">Connection established</p>
|
|
89
|
+
<p class="text-on-surface-faint">Running backup query...</p>
|
|
90
|
+
<p class="text-on-surface-faint">Processing 1,234 records...</p>
|
|
91
|
+
<p class="text-on-surface-faint">Compressing backup file...</p>
|
|
92
|
+
<p class="text-on-surface-faint">Uploading to storage...</p>
|
|
91
93
|
<p class="text-success-400">Task completed successfully</p>
|
|
92
|
-
<p class="text-
|
|
94
|
+
<p class="text-on-surface-muted">Total execution time: 90s</p>
|
|
93
95
|
</div>
|
|
94
96
|
{#snippet footer()}
|
|
95
97
|
<div class="flex justify-between">
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
</Story>
|
|
45
45
|
|
|
46
46
|
<Story name="Usage Example" asChild>
|
|
47
|
-
<div class="max-w-md space-y-4 rounded-xl border border-
|
|
48
|
-
<h3 class="font-semibold text-
|
|
47
|
+
<div class="max-w-md space-y-4 rounded-xl border border-outline bg-white p-4">
|
|
48
|
+
<h3 class="font-semibold text-on-surface">Resource Usage</h3>
|
|
49
49
|
<Progress value={45} label="CPU Usage" showLabel />
|
|
50
50
|
<Progress value={72} label="Memory" showLabel variant="warning" />
|
|
51
51
|
<Progress value={23} label="Storage" showLabel variant="success" />
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
</Story>
|
|
55
55
|
|
|
56
56
|
<Story name="Task Execution Progress" asChild>
|
|
57
|
-
<div class="max-w-md space-y-4 rounded-xl border border-
|
|
57
|
+
<div class="max-w-md space-y-4 rounded-xl border border-outline bg-white p-4">
|
|
58
58
|
<div class="flex items-center justify-between">
|
|
59
|
-
<h3 class="font-semibold text-
|
|
60
|
-
<span class="text-sm text-
|
|
59
|
+
<h3 class="font-semibold text-on-surface">backup-database</h3>
|
|
60
|
+
<span class="text-sm text-on-surface-muted">Running...</span>
|
|
61
61
|
</div>
|
|
62
62
|
<Progress value={67} showLabel />
|
|
63
|
-
<p class="text-sm text-
|
|
63
|
+
<p class="text-sm text-on-surface-muted">Processing 847 of 1,234 records</p>
|
|
64
64
|
</div>
|
|
65
65
|
</Story>
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
<Story name="Underline (Default)" asChild>
|
|
44
44
|
<Tabs tabs={basicTabs} variant="underline">
|
|
45
45
|
{#snippet children(activeTab)}
|
|
46
|
-
<div class="rounded-lg border border-
|
|
47
|
-
<p class="text-
|
|
46
|
+
<div class="rounded-lg border border-outline bg-white p-4">
|
|
47
|
+
<p class="text-on-surface-muted">Content for <strong>{activeTab}</strong> tab</p>
|
|
48
48
|
</div>
|
|
49
49
|
{/snippet}
|
|
50
50
|
</Tabs>
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
<Story name="Pills" asChild>
|
|
54
54
|
<Tabs tabs={basicTabs} variant="pills">
|
|
55
55
|
{#snippet children(activeTab)}
|
|
56
|
-
<div class="rounded-lg border border-
|
|
57
|
-
<p class="text-
|
|
56
|
+
<div class="rounded-lg border border-outline bg-white p-4">
|
|
57
|
+
<p class="text-on-surface-muted">Content for <strong>{activeTab}</strong> tab</p>
|
|
58
58
|
</div>
|
|
59
59
|
{/snippet}
|
|
60
60
|
</Tabs>
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
<Story name="Enclosed" asChild>
|
|
64
64
|
<Tabs tabs={basicTabs} variant="enclosed">
|
|
65
65
|
{#snippet children(activeTab)}
|
|
66
|
-
<div class="rounded-lg border border-
|
|
67
|
-
<p class="text-
|
|
66
|
+
<div class="rounded-lg border border-outline bg-white p-4">
|
|
67
|
+
<p class="text-on-surface-muted">Content for <strong>{activeTab}</strong> tab</p>
|
|
68
68
|
</div>
|
|
69
69
|
{/snippet}
|
|
70
70
|
</Tabs>
|
|
@@ -73,8 +73,10 @@
|
|
|
73
73
|
<Story name="With Icons and Badges" asChild>
|
|
74
74
|
<Tabs tabs={iconTabs} variant="underline">
|
|
75
75
|
{#snippet children(activeTab)}
|
|
76
|
-
<div class="rounded-lg border border-
|
|
77
|
-
<p class="text-
|
|
76
|
+
<div class="rounded-lg border border-outline bg-white p-4">
|
|
77
|
+
<p class="text-on-surface-muted">
|
|
78
|
+
You're viewing the <strong>{activeTab}</strong> section
|
|
79
|
+
</p>
|
|
78
80
|
</div>
|
|
79
81
|
{/snippet}
|
|
80
82
|
</Tabs>
|
|
@@ -84,8 +86,10 @@
|
|
|
84
86
|
<div class="max-w-md">
|
|
85
87
|
<Tabs tabs={basicTabs} variant="pills" fullWidth>
|
|
86
88
|
{#snippet children(activeTab)}
|
|
87
|
-
<div class="rounded-lg border border-
|
|
88
|
-
<p class="text-
|
|
89
|
+
<div class="rounded-lg border border-outline bg-white p-4">
|
|
90
|
+
<p class="text-on-surface-muted">
|
|
91
|
+
Content for <strong>{activeTab}</strong> tab
|
|
92
|
+
</p>
|
|
89
93
|
</div>
|
|
90
94
|
{/snippet}
|
|
91
95
|
</Tabs>
|
|
@@ -95,8 +99,8 @@
|
|
|
95
99
|
<Story name="With Disabled Tab" asChild>
|
|
96
100
|
<Tabs tabs={disabledTabs} variant="underline">
|
|
97
101
|
{#snippet children(activeTab)}
|
|
98
|
-
<div class="rounded-lg border border-
|
|
99
|
-
<p class="text-
|
|
102
|
+
<div class="rounded-lg border border-outline bg-white p-4">
|
|
103
|
+
<p class="text-on-surface-muted">Showing <strong>{activeTab}</strong> tasks</p>
|
|
100
104
|
</div>
|
|
101
105
|
{/snippet}
|
|
102
106
|
</Tabs>
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
</Story>
|
|
49
49
|
|
|
50
50
|
<Story name="Settings Panel" asChild>
|
|
51
|
-
<div class="max-w-md space-y-4 rounded-xl border border-
|
|
52
|
-
<h3 class="font-semibold text-
|
|
51
|
+
<div class="max-w-md space-y-4 rounded-xl border border-outline bg-white p-4">
|
|
52
|
+
<h3 class="font-semibold text-on-surface">Task Settings</h3>
|
|
53
53
|
<div class="space-y-4">
|
|
54
54
|
<Toggle
|
|
55
55
|
label="Enable task"
|
|
@@ -48,17 +48,17 @@
|
|
|
48
48
|
<Story name="On Icons" asChild>
|
|
49
49
|
<div class="flex items-center justify-center gap-6 py-8">
|
|
50
50
|
<Tooltip content="More information">
|
|
51
|
-
<button class="rounded-lg p-2 text-
|
|
51
|
+
<button class="rounded-lg p-2 text-on-surface-muted hover:bg-surface-sunken">
|
|
52
52
|
<Info size={20} />
|
|
53
53
|
</button>
|
|
54
54
|
</Tooltip>
|
|
55
55
|
<Tooltip content="Get help">
|
|
56
|
-
<button class="rounded-lg p-2 text-
|
|
56
|
+
<button class="rounded-lg p-2 text-on-surface-muted hover:bg-surface-sunken">
|
|
57
57
|
<CircleQuestionMark size={20} />
|
|
58
58
|
</button>
|
|
59
59
|
</Tooltip>
|
|
60
60
|
<Tooltip content="Settings">
|
|
61
|
-
<button class="rounded-lg p-2 text-
|
|
61
|
+
<button class="rounded-lg p-2 text-on-surface-muted hover:bg-surface-sunken">
|
|
62
62
|
<Settings size={20} />
|
|
63
63
|
</button>
|
|
64
64
|
</Tooltip>
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
|
|
68
68
|
<Story name="Usage Example" asChild>
|
|
69
69
|
<div class="flex items-center gap-2 py-8">
|
|
70
|
-
<span class="text-sm text-
|
|
70
|
+
<span class="text-sm text-on-surface-muted">Cron Expression</span>
|
|
71
71
|
<Tooltip content="Use standard cron syntax: minute hour day month weekday">
|
|
72
|
-
<button class="text-
|
|
72
|
+
<button class="text-on-surface-faint hover:text-on-surface-muted">
|
|
73
73
|
<CircleQuestionMark size={16} />
|
|
74
74
|
</button>
|
|
75
75
|
</Tooltip>
|
package/src/lib/utils/toast.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: PoppyCake, s.r.o.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
import { generateUlid } from "@runwisp/common";
|
|
5
|
-
|
|
6
|
-
export type ToastType = "success" | "error" | "warning" | "info";
|
|
7
|
-
|
|
8
|
-
export interface Toast {
|
|
9
|
-
id: string;
|
|
10
|
-
type: ToastType;
|
|
11
|
-
message: string;
|
|
12
|
-
duration?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const DEFAULT_DURATION = 5000;
|
|
16
|
-
|
|
17
|
-
class ToastStore {
|
|
18
|
-
items = $state<Toast[]>([]);
|
|
19
|
-
|
|
20
|
-
add(type: ToastType, message: string, duration = DEFAULT_DURATION): string {
|
|
21
|
-
const id = generateUlid();
|
|
22
|
-
const t: Toast = { id, type, message, duration };
|
|
23
|
-
|
|
24
|
-
this.items = [...this.items, t];
|
|
25
|
-
|
|
26
|
-
if (duration > 0) {
|
|
27
|
-
setTimeout(() => {
|
|
28
|
-
this.remove(id);
|
|
29
|
-
}, duration);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return id;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
remove(id: string) {
|
|
36
|
-
this.items = this.items.filter((t) => t.id !== id);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
success(message: string, duration?: number): string {
|
|
40
|
-
return this.add("success", message, duration);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
error(message: string, duration?: number): string {
|
|
44
|
-
return this.add("error", message, duration);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
warning(message: string, duration?: number): string {
|
|
48
|
-
return this.add("warning", message, duration);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
info(message: string, duration?: number): string {
|
|
52
|
-
return this.add("info", message, duration);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
clear() {
|
|
56
|
-
this.items = [];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const toast = new ToastStore();
|