@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 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.14.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.14.0",
53
- "@runwisp/typescript-config": "0.14.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.14.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
  }
@@ -1,9 +1,6 @@
1
1
  // SPDX-FileCopyrightText: PoppyCake, s.r.o.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- /**
5
- * Svelte Action for portaling an element to the body
6
- */
7
4
  export function portal(node: HTMLElement) {
8
5
  document.body.appendChild(node);
9
6
  return {
@@ -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
- >{dlv(row, column.key as string) ?? "-"}</span
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
- // Simple origin logic based on verify placement
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, // New prop
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?.(); // Cleanup immediately on select
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 group-hover:visible
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
  >