@runwisp/ui 0.14.0 → 0.15.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 -7
- package/src/lib/actions/portal.ts +0 -3
- package/src/lib/components/CronInput.svelte +0 -4
- package/src/lib/components/DataGrid.svelte +1 -3
- package/src/lib/components/Drawer.svelte +0 -3
- package/src/lib/components/Dropdown.svelte +2 -6
- package/src/lib/components/Modal.svelte +0 -3
- package/src/lib/components/Select.svelte +2 -5
- package/src/lib/components/StatusIndicator.svelte +0 -3
- package/src/lib/components/Tabs.svelte +0 -2
- package/src/lib/components/ToastContainer.svelte +0 -5
- package/src/lib/components/Tooltip.svelte +5 -2
- package/src/lib/components/dashboard/RunDetailPanel.svelte +133 -226
- package/src/lib/components/dashboard/RunFilterPopover.svelte +14 -14
- package/src/lib/components/dashboard/RunsList.svelte +29 -29
- package/src/lib/components/dashboard/run-filters.test.ts +31 -33
- package/src/lib/components/dashboard/run-filters.ts +33 -33
- package/src/lib/components/dashboard/run-helpers.test.ts +42 -17
- package/src/lib/components/dashboard/run-helpers.ts +53 -17
- package/src/lib/components/dashboard/status-config.ts +9 -8
- package/src/lib/utils/error.ts +0 -3
- package/src/lib/utils/format.test.ts +2 -6
- package/src/lib/utils/format.ts +28 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runwisp/ui",
|
|
3
3
|
"description": "Svelte 5 UI component library for RunWisp dashboards and admin interfaces.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.15.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@chromatic-com/storybook": "^5.2.1",
|
|
51
51
|
"@eslint/js": "^10.0.1",
|
|
52
|
-
"@runwisp/eslint-config": "0.
|
|
53
|
-
"@runwisp/typescript-config": "0.
|
|
52
|
+
"@runwisp/eslint-config": "0.15.0",
|
|
53
|
+
"@runwisp/typescript-config": "0.15.0",
|
|
54
54
|
"@storybook/addon-a11y": "^10.5.5",
|
|
55
55
|
"@storybook/addon-docs": "^10.5.5",
|
|
56
56
|
"@storybook/addon-svelte-csf": "^5.1.2",
|
|
@@ -60,7 +60,6 @@
|
|
|
60
60
|
"@tailwindcss/forms": "^0.5.11",
|
|
61
61
|
"@tailwindcss/typography": "^0.5.20",
|
|
62
62
|
"@tailwindcss/vite": "^4.3.3",
|
|
63
|
-
"@types/dlv": "^1.1.5",
|
|
64
63
|
"@types/ms": "^2.1.0",
|
|
65
64
|
"@types/node": "^26.1.2",
|
|
66
65
|
"eslint": "^10.8.0",
|
|
@@ -94,13 +93,11 @@
|
|
|
94
93
|
"@fontsource-variable/geist-mono": "^5.3.0",
|
|
95
94
|
"@fontsource/tasa-orbiter": "^5.3.0",
|
|
96
95
|
"@lucide/svelte": "^1.28.0",
|
|
97
|
-
"@runwisp/common": "0.
|
|
96
|
+
"@runwisp/common": "0.15.0",
|
|
98
97
|
"@tanstack/svelte-virtual": "^3.13.35",
|
|
99
98
|
"ansi-to-html": "^0.7.2",
|
|
100
99
|
"cron-parser": "^5.7.0",
|
|
101
100
|
"cronstrue": "^3.24.0",
|
|
102
|
-
"date-fns": "^4.4.0",
|
|
103
|
-
"dlv": "^1.1.3",
|
|
104
101
|
"fuse.js": "^7.5.0",
|
|
105
102
|
"ms": "^2.1.3"
|
|
106
103
|
}
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
// but we want to catch it to show "Invalid cron expression".
|
|
43
43
|
const text = cronstrue.toString(value, { throwExceptionOnParseError: true });
|
|
44
44
|
|
|
45
|
-
// Calculate next run
|
|
46
45
|
let next: Date | undefined;
|
|
47
46
|
try {
|
|
48
47
|
const interval = CronExpressionParser.parse(value);
|
|
@@ -57,7 +56,6 @@
|
|
|
57
56
|
}
|
|
58
57
|
});
|
|
59
58
|
|
|
60
|
-
// Determine state for styling
|
|
61
59
|
let isError = $derived(!disabled && value && value.trim() !== "" && !explanation.isValid);
|
|
62
60
|
</script>
|
|
63
61
|
|
|
@@ -74,7 +72,6 @@
|
|
|
74
72
|
</div>
|
|
75
73
|
<div class="flex gap-4">
|
|
76
74
|
<div class="flex-1">
|
|
77
|
-
<!-- Pass connection-status-like classes effectively by using the component props or class -->
|
|
78
75
|
<Input
|
|
79
76
|
placeholder="* * * * *"
|
|
80
77
|
bind:value
|
|
@@ -83,7 +80,6 @@
|
|
|
83
80
|
/>
|
|
84
81
|
</div>
|
|
85
82
|
</div>
|
|
86
|
-
<!-- Live Feedback -->
|
|
87
83
|
<div
|
|
88
84
|
class="flex items-start gap-3 rounded-[4px] border p-3 {isError
|
|
89
85
|
? 'border-danger-soft-border bg-danger-soft'
|
|
@@ -55,8 +55,6 @@
|
|
|
55
55
|
import Pagination from "./Pagination.svelte";
|
|
56
56
|
import Fuse from "fuse.js";
|
|
57
57
|
|
|
58
|
-
import dlv from "dlv";
|
|
59
|
-
|
|
60
58
|
let {
|
|
61
59
|
columns,
|
|
62
60
|
data,
|
|
@@ -331,7 +329,7 @@
|
|
|
331
329
|
<!-- Raw field value: a token, so mono. Snippet-rendered
|
|
332
330
|
cells choose their own voice. -->
|
|
333
331
|
<span class="font-mono"
|
|
334
|
-
>{
|
|
332
|
+
>{row[column.key as keyof T] ?? "-"}</span
|
|
335
333
|
>
|
|
336
334
|
{/if}
|
|
337
335
|
</td>
|
|
@@ -110,7 +110,6 @@
|
|
|
110
110
|
aria-labelledby={title ? "drawer-title" : undefined}
|
|
111
111
|
transition:fly={{ ...flyParams[side], duration: 250 }}
|
|
112
112
|
>
|
|
113
|
-
<!-- Header -->
|
|
114
113
|
{#if header}
|
|
115
114
|
<div class="border-b border-outline px-6 py-4">
|
|
116
115
|
{@render header()}
|
|
@@ -141,14 +140,12 @@
|
|
|
141
140
|
</div>
|
|
142
141
|
{/if}
|
|
143
142
|
|
|
144
|
-
<!-- Body -->
|
|
145
143
|
{#if children}
|
|
146
144
|
<div class="flex-1 overflow-y-auto px-6 py-4">
|
|
147
145
|
{@render children()}
|
|
148
146
|
</div>
|
|
149
147
|
{/if}
|
|
150
148
|
|
|
151
|
-
<!-- Footer -->
|
|
152
149
|
{#if footer}
|
|
153
150
|
<div class="border-t border-outline bg-surface-sunken px-6 py-4">
|
|
154
151
|
{@render footer()}
|
|
@@ -56,11 +56,7 @@
|
|
|
56
56
|
|
|
57
57
|
void computePosition(triggerEl, menuEl, {
|
|
58
58
|
placement,
|
|
59
|
-
middleware: [
|
|
60
|
-
offset(4), // MENU_OFFSET
|
|
61
|
-
flip(),
|
|
62
|
-
shift({ padding: 8 }), // VIEWPORT_PADDING
|
|
63
|
-
],
|
|
59
|
+
middleware: [offset(4), flip(), shift({ padding: 8 })],
|
|
64
60
|
}).then(({ x, y, placement }) => {
|
|
65
61
|
if (menuEl) {
|
|
66
62
|
Object.assign(menuEl.style, {
|
|
@@ -69,7 +65,7 @@
|
|
|
69
65
|
position: "absolute",
|
|
70
66
|
});
|
|
71
67
|
|
|
72
|
-
//
|
|
68
|
+
// Flip transform origin vertically when the menu flips above the trigger
|
|
73
69
|
const isTop = placement.startsWith("top");
|
|
74
70
|
transformOrigin = `${isTop ? "bottom" : "top"} ${align}`;
|
|
75
71
|
}
|
|
@@ -89,7 +89,6 @@
|
|
|
89
89
|
aria-modal="true"
|
|
90
90
|
aria-labelledby={title ? "modal-title" : undefined}
|
|
91
91
|
>
|
|
92
|
-
<!-- Header -->
|
|
93
92
|
{#if header}
|
|
94
93
|
<div class="border-b border-outline px-6 py-4">
|
|
95
94
|
{@render header()}
|
|
@@ -123,14 +122,12 @@
|
|
|
123
122
|
</div>
|
|
124
123
|
{/if}
|
|
125
124
|
|
|
126
|
-
<!-- Body -->
|
|
127
125
|
{#if children}
|
|
128
126
|
<div class="flex-1 overflow-y-auto px-6 py-4">
|
|
129
127
|
{@render children()}
|
|
130
128
|
</div>
|
|
131
129
|
{/if}
|
|
132
130
|
|
|
133
|
-
<!-- Footer -->
|
|
134
131
|
{#if footer}
|
|
135
132
|
<div class="rounded-b-[4px] border-t border-outline bg-surface-sunken px-6 py-4">
|
|
136
133
|
{@render footer()}
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
id = `select-${generateUlid()}`,
|
|
54
54
|
name,
|
|
55
55
|
onchange,
|
|
56
|
-
onsearch,
|
|
56
|
+
onsearch,
|
|
57
57
|
}: Props & { onsearch?: (query: string) => void } = $props();
|
|
58
58
|
|
|
59
59
|
let isOpen = $state(false);
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
value = option.value;
|
|
157
157
|
onchange?.(value);
|
|
158
158
|
isOpen = false;
|
|
159
|
-
cleanupFloating?.();
|
|
159
|
+
cleanupFloating?.();
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
function handleKeydown(e: KeyboardEvent) {
|
|
@@ -294,12 +294,10 @@
|
|
|
294
294
|
</div>
|
|
295
295
|
</button>
|
|
296
296
|
|
|
297
|
-
<!-- Hidden input for form submission if needed -->
|
|
298
297
|
{#if name}
|
|
299
298
|
<input type="hidden" {name} {value} />
|
|
300
299
|
{/if}
|
|
301
300
|
|
|
302
|
-
<!-- Dropdown Menu -->
|
|
303
301
|
{#if isOpen}
|
|
304
302
|
<div use:portal bind:this={menuEl} class="z-[9999] min-w-[200px]">
|
|
305
303
|
<div
|
|
@@ -384,7 +382,6 @@
|
|
|
384
382
|
{/if}
|
|
385
383
|
</div>
|
|
386
384
|
|
|
387
|
-
<!-- Helper Component for items -->
|
|
388
385
|
{#snippet OptionItem({
|
|
389
386
|
option,
|
|
390
387
|
active,
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
class: className = "",
|
|
27
27
|
}: Props = $props();
|
|
28
28
|
|
|
29
|
-
// Status configuration lookup
|
|
30
29
|
const STATUS_CONFIG: Record<Status, { label: string; classes: string; iconColor: string }> = {
|
|
31
30
|
running: {
|
|
32
31
|
label: "Running",
|
|
@@ -65,7 +64,6 @@
|
|
|
65
64
|
},
|
|
66
65
|
};
|
|
67
66
|
|
|
68
|
-
// Size configuration lookup
|
|
69
67
|
const SIZE_CONFIG: Record<Size, { classes: string; iconSize: number }> = {
|
|
70
68
|
sm: { classes: "px-2 py-0.5 text-2xs gap-1.5", iconSize: 10 },
|
|
71
69
|
md: { classes: "px-2.5 py-0.5 text-xs gap-1.5", iconSize: 12 },
|
|
@@ -81,7 +79,6 @@
|
|
|
81
79
|
role="status"
|
|
82
80
|
in:fade={{ duration: 150 }}
|
|
83
81
|
>
|
|
84
|
-
<!-- Icon Container -->
|
|
85
82
|
<span class="flex shrink-0 items-center justify-center {config.iconColor}">
|
|
86
83
|
{#if status === "running" || pulse}
|
|
87
84
|
<svg
|
|
@@ -80,7 +80,6 @@
|
|
|
80
80
|
</script>
|
|
81
81
|
|
|
82
82
|
<div class={className}>
|
|
83
|
-
<!-- Tab list -->
|
|
84
83
|
<div
|
|
85
84
|
class="
|
|
86
85
|
{variant === 'underline' ? 'border-b border-outline' : ''}
|
|
@@ -133,7 +132,6 @@
|
|
|
133
132
|
</nav>
|
|
134
133
|
</div>
|
|
135
134
|
|
|
136
|
-
<!-- Tab panels -->
|
|
137
135
|
{#if children && renderedActiveTab}
|
|
138
136
|
<div
|
|
139
137
|
id="tabpanel-{renderedActiveTab}"
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
|
3
3
|
|
|
4
4
|
<script lang="ts">
|
|
5
|
-
/**
|
|
6
|
-
* Toast notification component
|
|
7
|
-
* Displays toast notifications for user feedback
|
|
8
|
-
*/
|
|
9
5
|
import { fly, fade } from "svelte/transition";
|
|
10
6
|
import { CircleCheck, CircleX, Info, TriangleAlert, X } from "@lucide/svelte";
|
|
11
7
|
import { toast, type Toast } from "../utils/toast.svelte.js";
|
|
@@ -50,7 +46,6 @@
|
|
|
50
46
|
}
|
|
51
47
|
</script>
|
|
52
48
|
|
|
53
|
-
<!-- Toast Container -->
|
|
54
49
|
<div
|
|
55
50
|
class="pointer-events-none fixed bottom-4 left-1/2 z-[100] flex -translate-x-1/2 flex-col gap-2"
|
|
56
51
|
role="region"
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
<div class="group relative inline-block {className}">
|
|
42
42
|
{@render children()}
|
|
43
43
|
|
|
44
|
+
<!-- Hover intent: 100ms before it appears, so sweeping the pointer across a
|
|
45
|
+
dense row doesn't flash a trail of bubbles. Still no fade — `duration-0`
|
|
46
|
+
keeps the appearance instant once the delay is served, per DESIGN.md. -->
|
|
44
47
|
<div
|
|
45
48
|
class="
|
|
46
49
|
absolute z-50 {positionClasses[position]}
|
|
@@ -48,8 +51,8 @@
|
|
|
48
51
|
border border-outline bg-surface-overlay px-2.5 py-1.5 font-sans
|
|
49
52
|
text-xs
|
|
50
53
|
{wide ? 'w-max max-w-xs whitespace-normal' : 'whitespace-nowrap'}
|
|
51
|
-
text-on-surface opacity-0
|
|
52
|
-
group-hover:opacity-100
|
|
54
|
+
text-on-surface opacity-0 transition-[opacity,visibility]
|
|
55
|
+
delay-100 duration-0 group-hover:visible group-hover:opacity-100
|
|
53
56
|
"
|
|
54
57
|
role="tooltip"
|
|
55
58
|
>
|