@runwisp/ui 1.0.0 → 1.0.1
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 -4
- package/src/lib/components/Badge.svelte +51 -33
- package/src/lib/components/Tooltip.svelte +5 -2
- package/src/lib/components/dashboard/run-filters.test.ts +2 -2
- package/src/lib/components/dashboard/run-filters.ts +5 -5
- package/src/lib/components/dashboard/run-helpers.test.ts +1 -1
- package/src/lib/components/dashboard/run-helpers.ts +1 -1
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": "1.0.
|
|
4
|
+
"version": "1.0.1",
|
|
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": "1.0.
|
|
53
|
-
"@runwisp/typescript-config": "1.0.
|
|
52
|
+
"@runwisp/eslint-config": "1.0.1",
|
|
53
|
+
"@runwisp/typescript-config": "1.0.1",
|
|
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",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"@fontsource-variable/geist-mono": "^5.3.0",
|
|
94
94
|
"@fontsource/tasa-orbiter": "^5.3.0",
|
|
95
95
|
"@lucide/svelte": "^1.28.0",
|
|
96
|
-
"@runwisp/common": "1.0.
|
|
96
|
+
"@runwisp/common": "1.0.1",
|
|
97
97
|
"@tanstack/svelte-virtual": "^3.13.35",
|
|
98
98
|
"ansi-to-html": "^0.7.2",
|
|
99
99
|
"cron-parser": "^5.7.0",
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
<script lang="ts">
|
|
5
5
|
import type { Snippet } from "svelte";
|
|
6
|
+
import Tooltip from "./Tooltip.svelte";
|
|
6
7
|
|
|
7
8
|
type BadgeVariant = "default" | "primary" | "success" | "warning" | "danger" | "info";
|
|
8
9
|
type BadgeSize = "sm" | "md" | "lg";
|
|
10
|
+
type TooltipPosition = "top" | "bottom" | "left" | "right";
|
|
9
11
|
|
|
10
12
|
interface Props {
|
|
11
13
|
variant?: BadgeVariant;
|
|
@@ -13,6 +15,10 @@
|
|
|
13
15
|
dot?: boolean;
|
|
14
16
|
loading?: boolean;
|
|
15
17
|
outline?: boolean;
|
|
18
|
+
/** Shown in the styled hover tooltip (see {@link Tooltip}). */
|
|
19
|
+
tooltip?: string;
|
|
20
|
+
/** Side the tooltip opens on — pick one that has room, e.g. "bottom" near the top of the viewport. */
|
|
21
|
+
tooltipPosition?: TooltipPosition;
|
|
16
22
|
children?: Snippet;
|
|
17
23
|
class?: string;
|
|
18
24
|
}
|
|
@@ -23,6 +29,8 @@
|
|
|
23
29
|
dot = false,
|
|
24
30
|
loading = false,
|
|
25
31
|
outline = false,
|
|
32
|
+
tooltip,
|
|
33
|
+
tooltipPosition = "top",
|
|
26
34
|
children,
|
|
27
35
|
class: className = "",
|
|
28
36
|
}: Props = $props();
|
|
@@ -91,37 +99,47 @@
|
|
|
91
99
|
};
|
|
92
100
|
</script>
|
|
93
101
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
{#if
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
102
|
+
{#snippet chip(extraClass: string)}
|
|
103
|
+
<span
|
|
104
|
+
class="{baseClasses} {outline
|
|
105
|
+
? variantClasses[variant].outline
|
|
106
|
+
: variantClasses[variant].solid} {sizeClasses[size]} {extraClass}"
|
|
107
|
+
>
|
|
108
|
+
{#if dot}
|
|
109
|
+
{#if loading}
|
|
110
|
+
<svg
|
|
111
|
+
class="{spinnerSizeClasses[size]} animate-spin {indicatorTextColors[variant]}"
|
|
112
|
+
viewBox="0 0 24 24"
|
|
113
|
+
fill="none"
|
|
114
|
+
>
|
|
115
|
+
<circle
|
|
116
|
+
class="opacity-25"
|
|
117
|
+
cx="12"
|
|
118
|
+
cy="12"
|
|
119
|
+
r="10"
|
|
120
|
+
stroke="currentColor"
|
|
121
|
+
stroke-width="4"
|
|
122
|
+
></circle>
|
|
123
|
+
<path
|
|
124
|
+
class="opacity-75"
|
|
125
|
+
fill="currentColor"
|
|
126
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
127
|
+
></path>
|
|
128
|
+
</svg>
|
|
129
|
+
{:else}
|
|
130
|
+
<span class="h-1.5 w-1.5 rounded-full {dotColors[variant]}"></span>
|
|
131
|
+
{/if}
|
|
122
132
|
{/if}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
{#if children}
|
|
134
|
+
{@render children()}
|
|
135
|
+
{/if}
|
|
136
|
+
</span>
|
|
137
|
+
{/snippet}
|
|
138
|
+
|
|
139
|
+
{#if tooltip}
|
|
140
|
+
<Tooltip content={tooltip} position={tooltipPosition} class={className} wide>
|
|
141
|
+
{@render chip("")}
|
|
142
|
+
</Tooltip>
|
|
143
|
+
{:else}
|
|
144
|
+
{@render chip(className)}
|
|
145
|
+
{/if}
|
|
@@ -38,7 +38,10 @@
|
|
|
38
38
|
};
|
|
39
39
|
</script>
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
<!-- Named group: an unnamed `group` would also fire on any ancestor's own
|
|
42
|
+
`group-hover` (e.g. a hoverable card row this tooltip sits inside),
|
|
43
|
+
popping every tooltip in that row open at once. -->
|
|
44
|
+
<div class="group/tooltip relative inline-block {className}">
|
|
42
45
|
{@render children()}
|
|
43
46
|
|
|
44
47
|
<!-- Hover intent: 100ms before it appears, so sweeping the pointer across a
|
|
@@ -52,7 +55,7 @@
|
|
|
52
55
|
text-xs
|
|
53
56
|
{wide ? 'w-max max-w-xs whitespace-normal' : 'whitespace-nowrap'}
|
|
54
57
|
text-on-surface opacity-0 transition-[opacity,visibility]
|
|
55
|
-
delay-100 duration-0 group-hover:visible group-hover:opacity-100
|
|
58
|
+
delay-100 duration-0 group-hover/tooltip:visible group-hover/tooltip:opacity-100
|
|
56
59
|
"
|
|
57
60
|
role="tooltip"
|
|
58
61
|
>
|
|
@@ -274,9 +274,9 @@ describe("triggerDescription", () => {
|
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
describe("FILTERABLE_TRIGGERS", () => {
|
|
277
|
-
it("offers every trigger except
|
|
277
|
+
it("offers every trigger except station", () => {
|
|
278
278
|
expect(FILTERABLE_TRIGGERS).toEqual(["cron", "api", "ui", "cli", "service", "startup"]);
|
|
279
|
-
expect(FILTERABLE_TRIGGERS).not.toContain("
|
|
279
|
+
expect(FILTERABLE_TRIGGERS).not.toContain("station");
|
|
280
280
|
});
|
|
281
281
|
});
|
|
282
282
|
|
|
@@ -225,7 +225,7 @@ export function humanizeStatus(status: string): string {
|
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
227
|
* Descriptive label for a run's trigger source — fuller than the one-word row
|
|
228
|
-
* badge (`formatTriggeredByLabel`) for the "cron"/"
|
|
228
|
+
* badge (`formatTriggeredByLabel`) for the "cron"/"station"/"service"/"startup"
|
|
229
229
|
* sources, where the extra words disambiguate (e.g. "REST API" vs a bare
|
|
230
230
|
* "API"). "ui" and "cli" already read fine as their bare badge word, so those
|
|
231
231
|
* two match `formatTriggeredByLabel` exactly.
|
|
@@ -243,7 +243,7 @@ export function triggerDescription(trigger: string): string {
|
|
|
243
243
|
return "UI";
|
|
244
244
|
case "cli":
|
|
245
245
|
return "CLI";
|
|
246
|
-
case "
|
|
246
|
+
case "station":
|
|
247
247
|
return "Control plane";
|
|
248
248
|
case "service":
|
|
249
249
|
return "Service auto-start";
|
|
@@ -255,11 +255,11 @@ export function triggerDescription(trigger: string): string {
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
/**
|
|
258
|
-
* The triggers offered in the filter dropdown. `
|
|
259
|
-
* excluded for now — control-plane runs still carry the `
|
|
258
|
+
* The triggers offered in the filter dropdown. `station` is intentionally
|
|
259
|
+
* excluded for now — control-plane runs still carry the `station` trigger, but
|
|
260
260
|
* it isn't a selectable filter dimension.
|
|
261
261
|
*/
|
|
262
|
-
export const FILTERABLE_TRIGGERS: readonly Trigger[] = TRIGGERS.filter((t) => t !== "
|
|
262
|
+
export const FILTERABLE_TRIGGERS: readonly Trigger[] = TRIGGERS.filter((t) => t !== "station");
|
|
263
263
|
|
|
264
264
|
/** Chip label for the status dimension — names a whole bucket when it matches. */
|
|
265
265
|
export function statusChipLabel(statuses: string[]): string {
|
|
@@ -112,7 +112,7 @@ describe("formatTriggeredByLabel", () => {
|
|
|
112
112
|
expect(formatTriggeredByLabel("cron")).toBe("Cron");
|
|
113
113
|
expect(formatTriggeredByLabel("service")).toBe("Service");
|
|
114
114
|
expect(formatTriggeredByLabel("startup")).toBe("Startup");
|
|
115
|
-
expect(formatTriggeredByLabel("
|
|
115
|
+
expect(formatTriggeredByLabel("station")).toBe("Station");
|
|
116
116
|
});
|
|
117
117
|
});
|
|
118
118
|
|
|
@@ -90,7 +90,7 @@ export function formatTriggeredByLabel(triggeredBy: Run["triggeredBy"]): string
|
|
|
90
90
|
if (triggeredBy === "cron") return "Cron";
|
|
91
91
|
if (triggeredBy === "service") return "Service";
|
|
92
92
|
if (triggeredBy === "startup") return "Startup";
|
|
93
|
-
return "
|
|
93
|
+
return "Station";
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|