@runwisp/ui 0.10.0 → 0.11.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 -4
- package/src/lib/assets/runwisp-logo.svg +2 -2
- package/src/lib/components/LogConsole.svelte +518 -68
- package/src/lib/components/Popover.svelte +64 -31
- package/src/lib/components/dashboard/RunDetailPanel.svelte +753 -212
- package/src/lib/components/dashboard/RunFilterPopover.svelte +299 -0
- package/src/lib/components/dashboard/RunsList.svelte +418 -234
- package/src/lib/components/dashboard/run-filters.test.ts +299 -0
- package/src/lib/components/dashboard/run-filters.ts +384 -0
- package/src/lib/components/dashboard/run-helpers.test.ts +20 -0
- package/src/lib/components/dashboard/run-helpers.ts +15 -0
- package/src/lib/index.ts +20 -4
- package/src/lib/log-console/LogCache.svelte.ts +50 -1
- package/src/lib/log-console/types.ts +17 -0
- package/src/lib/theme-tokens.css +74 -62
- package/src/lib/theme.css +1 -0
- package/src/lib/utils/format.test.ts +38 -0
- package/src/lib/utils/format.ts +36 -0
- package/src/stories/assets/runwisp_c.svg +2 -2
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
<script lang="ts">
|
|
5
5
|
import type { Snippet } from "svelte";
|
|
6
6
|
import type { Placement } from "@floating-ui/dom";
|
|
7
|
-
import {
|
|
7
|
+
import { onDestroy } from "svelte";
|
|
8
8
|
import { computePosition, autoUpdate, flip, shift, offset } from "@floating-ui/dom";
|
|
9
|
-
import { scale } from "svelte/transition";
|
|
9
|
+
import { scale, fade } from "svelte/transition";
|
|
10
10
|
import { quintOut } from "svelte/easing";
|
|
11
11
|
import { portal } from "../actions/portal.js";
|
|
12
12
|
|
|
13
13
|
interface Props {
|
|
14
14
|
open?: boolean;
|
|
15
15
|
placement?: Placement;
|
|
16
|
+
// Opt-in: below the `md` breakpoint, render the content as a bottom
|
|
17
|
+
// sheet (full-width, pinned to the viewport bottom, dimmed scrim behind)
|
|
18
|
+
// instead of a floating popover. Leaves desktop behavior untouched.
|
|
19
|
+
mobileSheet?: boolean;
|
|
16
20
|
trigger?: Snippet;
|
|
17
21
|
children?: Snippet;
|
|
18
22
|
class?: string;
|
|
@@ -21,6 +25,7 @@
|
|
|
21
25
|
let {
|
|
22
26
|
open = $bindable(false),
|
|
23
27
|
placement = "bottom",
|
|
28
|
+
mobileSheet = false,
|
|
24
29
|
trigger,
|
|
25
30
|
children,
|
|
26
31
|
class: className = "",
|
|
@@ -30,20 +35,38 @@
|
|
|
30
35
|
let contentEl = $state<HTMLElement | null>(null);
|
|
31
36
|
let cleanupFloating = $state<(() => void) | null>(null);
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
// Track the `md` breakpoint only when sheet mode is requested, so the
|
|
39
|
+
// content can switch between floating (desktop) and bottom sheet (phone)
|
|
40
|
+
// live on resize. matchMedia is client-only; the effect never runs on SSR.
|
|
41
|
+
let isDesktop = $state(true);
|
|
42
|
+
$effect(() => {
|
|
43
|
+
if (!mobileSheet) return;
|
|
44
|
+
const mq = window.matchMedia("(min-width: 768px)");
|
|
45
|
+
isDesktop = mq.matches;
|
|
46
|
+
const onChange = (e: MediaQueryListEvent) => (isDesktop = e.matches);
|
|
47
|
+
mq.addEventListener("change", onChange);
|
|
48
|
+
return () => mq.removeEventListener("change", onChange);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const asSheet = $derived(mobileSheet && !isDesktop);
|
|
52
|
+
|
|
53
|
+
function teardown() {
|
|
54
|
+
cleanupFloating?.();
|
|
55
|
+
cleanupFloating = null;
|
|
56
|
+
}
|
|
35
57
|
|
|
58
|
+
function setupFloating() {
|
|
59
|
+
if (!triggerEl || !contentEl) return;
|
|
36
60
|
cleanupFloating = autoUpdate(triggerEl, contentEl, () => {
|
|
37
61
|
if (!triggerEl || !contentEl) return;
|
|
38
|
-
|
|
39
62
|
void computePosition(triggerEl, contentEl, {
|
|
40
63
|
placement,
|
|
41
64
|
middleware: [offset(8), flip(), shift({ padding: 8 })],
|
|
42
65
|
}).then(({ x, y }) => {
|
|
43
66
|
if (contentEl) {
|
|
44
67
|
Object.assign(contentEl.style, {
|
|
45
|
-
left: `${x}px`,
|
|
46
|
-
top: `${y}px`,
|
|
68
|
+
left: `${String(x)}px`,
|
|
69
|
+
top: `${String(y)}px`,
|
|
47
70
|
position: "absolute",
|
|
48
71
|
});
|
|
49
72
|
}
|
|
@@ -51,40 +74,40 @@
|
|
|
51
74
|
});
|
|
52
75
|
}
|
|
53
76
|
|
|
77
|
+
// Position the content while open: float it on desktop, or pin it as a
|
|
78
|
+
// sheet on phone (clearing any inline coords floating-ui left behind).
|
|
79
|
+
$effect(() => {
|
|
80
|
+
if (!open || !contentEl) {
|
|
81
|
+
teardown();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (asSheet) {
|
|
85
|
+
teardown();
|
|
86
|
+
contentEl.style.removeProperty("left");
|
|
87
|
+
contentEl.style.removeProperty("top");
|
|
88
|
+
contentEl.style.removeProperty("position");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
setupFloating();
|
|
92
|
+
return teardown;
|
|
93
|
+
});
|
|
94
|
+
|
|
54
95
|
function toggle() {
|
|
55
96
|
open = !open;
|
|
56
|
-
if (open) {
|
|
57
|
-
void tick().then(() => {
|
|
58
|
-
void setupFloating();
|
|
59
|
-
});
|
|
60
|
-
} else {
|
|
61
|
-
cleanupFloating?.();
|
|
62
|
-
cleanupFloating = null;
|
|
63
|
-
}
|
|
64
97
|
}
|
|
65
98
|
|
|
66
99
|
function handleOutsideClick(e: MouseEvent) {
|
|
67
100
|
const path = e.composedPath();
|
|
68
101
|
if (triggerEl && path.includes(triggerEl)) return;
|
|
69
102
|
if (contentEl && path.includes(contentEl)) return;
|
|
70
|
-
if (open)
|
|
71
|
-
open = false;
|
|
72
|
-
cleanupFloating?.();
|
|
73
|
-
cleanupFloating = null;
|
|
74
|
-
}
|
|
103
|
+
if (open) open = false;
|
|
75
104
|
}
|
|
76
105
|
|
|
77
106
|
function handleKeyDown(e: KeyboardEvent) {
|
|
78
|
-
if (e.key === "Escape" && open)
|
|
79
|
-
open = false;
|
|
80
|
-
cleanupFloating?.();
|
|
81
|
-
cleanupFloating = null;
|
|
82
|
-
}
|
|
107
|
+
if (e.key === "Escape" && open) open = false;
|
|
83
108
|
}
|
|
84
109
|
|
|
85
|
-
onDestroy(
|
|
86
|
-
cleanupFloating?.();
|
|
87
|
-
});
|
|
110
|
+
onDestroy(teardown);
|
|
88
111
|
</script>
|
|
89
112
|
|
|
90
113
|
<svelte:window onclick={handleOutsideClick} onkeydown={handleKeyDown} />
|
|
@@ -112,14 +135,24 @@
|
|
|
112
135
|
</div>
|
|
113
136
|
|
|
114
137
|
{#if open}
|
|
138
|
+
{#if asSheet}
|
|
139
|
+
<div
|
|
140
|
+
use:portal
|
|
141
|
+
transition:fade={{ duration: 120 }}
|
|
142
|
+
class="fixed inset-0 z-[9998] bg-on-surface/40"
|
|
143
|
+
aria-hidden="true"
|
|
144
|
+
></div>
|
|
145
|
+
{/if}
|
|
115
146
|
<div
|
|
116
147
|
use:portal
|
|
117
148
|
bind:this={contentEl}
|
|
118
149
|
transition:scale={{ duration: 150, start: 0.95, easing: quintOut }}
|
|
119
150
|
class="
|
|
120
|
-
z-[9999]
|
|
121
|
-
|
|
122
|
-
|
|
151
|
+
z-[9999] max-h-[80vh] overflow-y-auto
|
|
152
|
+
border border-outline bg-surface-overlay shadow-xl shadow-on-surface/5
|
|
153
|
+
{asSheet
|
|
154
|
+
? 'fixed inset-x-0 bottom-0 max-h-[85vh] rounded-t-2xl border-x-0 border-b-0 p-5'
|
|
155
|
+
: 'rounded-xl p-4'}
|
|
123
156
|
"
|
|
124
157
|
>
|
|
125
158
|
{#if children}
|