zabi-components 5.0.19 → 5.0.20

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.
Files changed (52) hide show
  1. package/dist/atoms/Button.svelte +6 -3
  2. package/dist/atoms/Button.svelte.d.ts +1 -0
  3. package/dist/atoms/Card.svelte +23 -47
  4. package/dist/atoms/CardContent.svelte +12 -5
  5. package/dist/atoms/CardContent.svelte.d.ts +2 -0
  6. package/dist/atoms/CardHeader.svelte +22 -5
  7. package/dist/atoms/CardHeader.svelte.d.ts +3 -0
  8. package/dist/atoms/ColorPicker.svelte +271 -7
  9. package/dist/atoms/Input.svelte +7 -6
  10. package/dist/atoms/Input.svelte.d.ts +3 -1
  11. package/dist/atoms/Progress.svelte +1 -3
  12. package/dist/atoms/Select.svelte +17 -22
  13. package/dist/atoms/Skeleton.svelte +12 -2
  14. package/dist/atoms/Skeleton.svelte.d.ts +1 -0
  15. package/dist/atoms/Tooltip.svelte +15 -17
  16. package/dist/atoms/index.d.ts +0 -2
  17. package/dist/atoms/index.js +0 -2
  18. package/dist/components/atoms/index.d.ts +0 -2
  19. package/dist/components/atoms/index.d.ts.map +1 -1
  20. package/dist/components/molecules/index.d.ts +7 -0
  21. package/dist/components/molecules/index.d.ts.map +1 -1
  22. package/dist/molecules/Dropdown.svelte +101 -14
  23. package/dist/molecules/Dropdown.svelte.d.ts +7 -0
  24. package/dist/molecules/ImageUpload.svelte +4 -4
  25. package/dist/molecules/Modal.svelte +42 -26
  26. package/dist/molecules/Modal.svelte.d.ts +1 -0
  27. package/dist/molecules/NavigationMenu.svelte +158 -0
  28. package/dist/molecules/NavigationMenu.svelte.d.ts +25 -0
  29. package/dist/molecules/NavigationMenuContent.svelte +83 -0
  30. package/dist/molecules/NavigationMenuContent.svelte.d.ts +9 -0
  31. package/dist/molecules/NavigationMenuItem.svelte +36 -0
  32. package/dist/molecules/NavigationMenuItem.svelte.d.ts +9 -0
  33. package/dist/molecules/NavigationMenuLink.svelte +53 -0
  34. package/dist/molecules/NavigationMenuLink.svelte.d.ts +10 -0
  35. package/dist/molecules/NavigationMenuList.svelte +31 -0
  36. package/dist/molecules/NavigationMenuList.svelte.d.ts +8 -0
  37. package/dist/molecules/NavigationMenuTrigger.svelte +81 -0
  38. package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +9 -0
  39. package/dist/molecules/Sidebar.svelte +324 -0
  40. package/dist/molecules/Sidebar.svelte.d.ts +30 -0
  41. package/dist/molecules/SlideUp.svelte +2 -2
  42. package/dist/molecules/index.d.ts +7 -0
  43. package/dist/molecules/index.js +7 -0
  44. package/dist/zabi-components-colors.css +2 -2
  45. package/dist/zabi-components-theme-only.css +2 -2
  46. package/dist/zabi-components-theme.css +2 -2
  47. package/dist/zabi-components.css +192 -27
  48. package/package.json +1 -1
  49. package/dist/atoms/CardDescription.svelte +0 -24
  50. package/dist/atoms/CardDescription.svelte.d.ts +0 -8
  51. package/dist/atoms/CardTitle.svelte +0 -33
  52. package/dist/atoms/CardTitle.svelte.d.ts +0 -9
@@ -8,6 +8,7 @@
8
8
  disabled?: boolean;
9
9
  type?: "button" | "submit" | "reset";
10
10
  text?: string;
11
+ isFullWidth?: boolean;
11
12
  onclick?: (event: MouseEvent) => void;
12
13
  children?: Snippet;
13
14
  }
@@ -18,6 +19,7 @@
18
19
  disabled = false,
19
20
  type = "button",
20
21
  text = "",
22
+ isFullWidth = false,
21
23
  onclick,
22
24
  children,
23
25
  ...restProps
@@ -75,10 +77,11 @@
75
77
 
76
78
  const buttonClasses = $derived(() => {
77
79
  const sizeStyles = sizeClass();
78
- const baseClasses =
79
- "inline-flex items-center justify-center transition-all duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none focus:outline-none";
80
+ const flexClass = isFullWidth ? "flex" : "inline-flex";
81
+ const widthClass = isFullWidth ? "w-full" : "";
82
+ const baseClasses = `${flexClass} items-center justify-center transition-all duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none focus:outline-none`;
80
83
 
81
- return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.font} ${sizeStyles.leading} ${sizeStyles.tracking} ${sizeStyles.radius} ${sizeStyles.gap} ${variantClass()}`.trim();
84
+ return `${baseClasses} ${widthClass} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.font} ${sizeStyles.leading} ${sizeStyles.tracking} ${sizeStyles.radius} ${sizeStyles.gap} ${variantClass()}`.trim();
82
85
  });
83
86
  </script>
84
87
 
@@ -6,6 +6,7 @@ interface Props {
6
6
  disabled?: boolean;
7
7
  type?: "button" | "submit" | "reset";
8
8
  text?: string;
9
+ isFullWidth?: boolean;
9
10
  onclick?: (event: MouseEvent) => void;
10
11
  children?: Snippet;
11
12
  }
@@ -1,6 +1,8 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
3
  import type { CardVariant, SizeVariant } from "../../types/variants.js";
4
+ import CardHeader from "./CardHeader.svelte";
5
+ import CardContent from "./CardContent.svelte";
4
6
 
5
7
  interface Props {
6
8
  title?: string;
@@ -42,11 +44,12 @@
42
44
  }
43
45
  });
44
46
 
45
- const useOldAPI = $derived(title !== "" || description !== "" || image !== "");
46
-
47
+ const useOldAPI = $derived(
48
+ title !== "" || description !== "" || image !== "",
49
+ );
50
+
47
51
  const cardClasses = $derived(() => {
48
- const baseClasses =
49
- `rounded-lg transition-all duration-200 ${variantClasses()}`;
52
+ const baseClasses = `rounded-lg transition-all duration-200 ${variantClasses()}`;
50
53
  const interactiveClasses = onclick
51
54
  ? variant === "elevated"
52
55
  ? "cursor-pointer hover:shadow-xl hover:bg-card-hover"
@@ -63,26 +66,6 @@
63
66
  return `${baseClasses} ${interactiveClasses} ${widthClasses} ${minWidthClass} ${paddingClass}`.trim();
64
67
  });
65
68
 
66
- const titleClasses = $derived(() => {
67
- if (size === "sm") {
68
- return "text-lg font-medium mb-2 text-headline";
69
- } else if (size === "lg") {
70
- return "text-2xl font-medium mb-4 text-headline";
71
- } else {
72
- return "text-xl font-medium mb-3 text-headline";
73
- }
74
- });
75
-
76
- const descriptionClasses = $derived(() => {
77
- if (size === "sm") {
78
- return "text-xs text-description mb-3";
79
- } else if (size === "lg") {
80
- return "text-base text-description mb-5";
81
- } else {
82
- return "text-sm text-description mb-4";
83
- }
84
- });
85
-
86
69
  function handleKeydown(event: KeyboardEvent) {
87
70
  if (onclick && (event.key === "Enter" || event.key === " ")) {
88
71
  event.preventDefault();
@@ -91,44 +74,37 @@
91
74
  }
92
75
 
93
76
  const cardRole = $derived(onclick ? "button" : undefined);
94
- const cardTabIndex = $derived(onclick ? 0 : undefined);
95
77
  const cardAriaLabel = $derived(onclick && title ? title : undefined);
96
78
  </script>
97
79
 
98
- <div
99
- class={cardClasses()}
100
- {onclick}
80
+ <div
81
+ class={cardClasses()}
82
+ {onclick}
101
83
  role={cardRole}
102
- tabindex={cardTabIndex}
84
+ {...onclick ? { tabindex: 0 } : {}}
103
85
  aria-label={cardAriaLabel}
104
- onkeydown={handleKeydown}
86
+ onkeydown={onclick ? handleKeydown : undefined}
105
87
  {...restProps}
106
88
  >
107
89
  {#if useOldAPI}
108
90
  <!-- Old API: Using title/description/image props -->
109
- {#if image}
110
- <img
111
- src={image}
112
- alt={title}
113
- class="w-full h-48 object-cover rounded-lg mb-4"
114
- />
115
- {/if}
116
-
117
- {#if title}
118
- <h3 class={titleClasses()}>{title}</h3>
91
+ {#if title || description}
92
+ <CardHeader {title} {description} />
119
93
  {/if}
120
94
 
121
- {#if description}
122
- <p class={descriptionClasses()}>{description}</p>
123
- {/if}
124
-
125
- {#if children}
126
- {@render children()}
95
+ {#if image || children}
96
+ <CardContent {image} imageAlt={title}>
97
+ {#if children}
98
+ {@render children()}
99
+ {/if}
100
+ </CardContent>
127
101
  {/if}
128
102
  {:else}
129
103
  <!-- New API: Using compound components (CardHeader, CardContent, CardFooter) -->
130
104
  {#if children}
131
- {@render children()}
105
+ <CardContent className="pt-6">
106
+ {@render children()}
107
+ </CardContent>
132
108
  {/if}
133
109
  {/if}
134
110
  </div>
@@ -2,23 +2,30 @@
2
2
  import type { Snippet } from "svelte";
3
3
 
4
4
  interface Props {
5
+ image?: string;
6
+ imageAlt?: string;
5
7
  className?: string;
6
8
  children?: Snippet;
7
9
  }
8
10
 
9
11
  let {
12
+ image = "",
13
+ imageAlt = "",
10
14
  className = "",
11
15
  children,
12
16
  ...restProps
13
17
  }: Props = $props();
14
18
  </script>
15
19
 
16
- <div
17
- class="p-6 pt-0 {className}"
18
- {...restProps}
19
- >
20
+ <div class="p-6 pt-0 {className}" {...restProps}>
21
+ {#if image}
22
+ <img
23
+ src={image}
24
+ alt={imageAlt}
25
+ class="w-full h-48 object-cover rounded-lg mb-4"
26
+ />
27
+ {/if}
20
28
  {#if children}
21
29
  {@render children()}
22
30
  {/if}
23
31
  </div>
24
-
@@ -1,5 +1,7 @@
1
1
  import type { Snippet } from "svelte";
2
2
  interface Props {
3
+ image?: string;
4
+ imageAlt?: string;
3
5
  className?: string;
4
6
  children?: Snippet;
5
7
  }
@@ -2,23 +2,40 @@
2
2
  import type { Snippet } from "svelte";
3
3
 
4
4
  interface Props {
5
+ title?: string;
6
+ description?: string;
7
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
5
8
  className?: string;
6
9
  children?: Snippet;
7
10
  }
8
11
 
9
12
  let {
13
+ title = "",
14
+ description = "",
15
+ level = 3,
10
16
  className = "",
11
17
  children,
12
18
  ...restProps
13
19
  }: Props = $props();
20
+
21
+ const headingTag = $derived(`h${level}`);
22
+ const headingClasses = $derived(() => {
23
+ return "text-2xl font-semibold leading-none tracking-tight text-headline";
24
+ });
14
25
  </script>
15
26
 
16
- <header
17
- class="flex flex-col space-y-1.5 p-6 {className}"
18
- {...restProps}
19
- >
27
+ <header class="flex flex-col space-y-1.5 p-6 {className}" {...restProps}>
28
+ {#if title}
29
+ <svelte:element this={headingTag} class={headingClasses()}>
30
+ {title}
31
+ </svelte:element>
32
+ {/if}
20
33
  {#if children}
21
34
  {@render children()}
22
35
  {/if}
36
+ {#if description}
37
+ <p class="text-sm text-description">
38
+ {description}
39
+ </p>
40
+ {/if}
23
41
  </header>
24
-
@@ -1,5 +1,8 @@
1
1
  import type { Snippet } from "svelte";
2
2
  interface Props {
3
+ title?: string;
4
+ description?: string;
5
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
3
6
  className?: string;
4
7
  children?: Snippet;
5
8
  }
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import Input from "./Input.svelte";
3
+ import { onMount, onDestroy } from "svelte";
3
4
 
4
5
  interface Props {
5
6
  value?: string;
@@ -18,11 +19,83 @@
18
19
  ...restProps
19
20
  }: Props = $props();
20
21
 
22
+ let isOpen = $state(false);
23
+ let pickerContainer: HTMLDivElement;
24
+ let colorMap: HTMLCanvasElement;
25
+ let colorMapContainer: HTMLDivElement;
26
+ let hueSlider: HTMLInputElement;
27
+ let isDragging = $state(false);
28
+
29
+ // HSL values (0-360, 0-100, 0-100)
30
+ let hue = $state(0);
31
+ let saturation = $state(100);
32
+ let lightness = $state(50);
33
+
21
34
  function isValidHex(hex: string): boolean {
22
35
  const hexPattern = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
23
36
  return hexPattern.test(hex);
24
37
  }
25
38
 
39
+ function hexToHsl(hex: string): [number, number, number] {
40
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
41
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
42
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
43
+
44
+ const max = Math.max(r, g, b);
45
+ const min = Math.min(r, g, b);
46
+ let h = 0;
47
+ let s = 0;
48
+ const l = (max + min) / 2;
49
+
50
+ if (max !== min) {
51
+ const d = max - min;
52
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
53
+ switch (max) {
54
+ case r:
55
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
56
+ break;
57
+ case g:
58
+ h = ((b - r) / d + 2) / 6;
59
+ break;
60
+ case b:
61
+ h = ((r - g) / d + 4) / 6;
62
+ break;
63
+ }
64
+ }
65
+
66
+ return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
67
+ }
68
+
69
+ function hslToHex(h: number, s: number, l: number): string {
70
+ l /= 100;
71
+ const a = (s * Math.min(l, 1 - l)) / 100;
72
+ const f = (n: number) => {
73
+ const k = (n + h / 30) % 12;
74
+ const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
75
+ return Math.round(255 * color)
76
+ .toString(16)
77
+ .padStart(2, "0");
78
+ };
79
+ return `#${f(0)}${f(8)}${f(4)}`;
80
+ }
81
+
82
+ function updateFromHex(hex: string) {
83
+ if (isValidHex(hex)) {
84
+ const [h, s, l] = hexToHsl(hex);
85
+ hue = h;
86
+ saturation = s;
87
+ lightness = l;
88
+ }
89
+ }
90
+
91
+ function updateHexFromHsl() {
92
+ const hex = hslToHex(hue, saturation, lightness);
93
+ value = hex;
94
+ if (onchange) {
95
+ onchange(new Event("change"));
96
+ }
97
+ }
98
+
26
99
  const variant = $derived(() => {
27
100
  if (value && !isValidHex(value)) {
28
101
  return "error";
@@ -39,6 +112,7 @@
39
112
 
40
113
  function handleInput(event: Event) {
41
114
  if (onchange) onchange(event);
115
+ updateFromHex(value);
42
116
  }
43
117
 
44
118
  function handleBlur(event: Event) {
@@ -51,9 +125,134 @@
51
125
 
52
126
  if (hexValue === "" || isValidHex(hexValue)) {
53
127
  value = hexValue;
128
+ updateFromHex(hexValue);
54
129
  if (onchange) onchange(event);
55
130
  }
56
131
  }
132
+
133
+ function togglePicker() {
134
+ if (!disabled) {
135
+ isOpen = !isOpen;
136
+ if (isOpen && value && isValidHex(value)) {
137
+ updateFromHex(value);
138
+ }
139
+ }
140
+ }
141
+
142
+ function drawColorMap() {
143
+ if (!colorMap) return;
144
+ const ctx = colorMap.getContext("2d");
145
+ if (!ctx) return;
146
+
147
+ const width = colorMap.width;
148
+ const height = colorMap.height;
149
+
150
+ // Draw saturation/lightness gradient
151
+ for (let x = 0; x < width; x++) {
152
+ for (let y = 0; y < height; y++) {
153
+ const s = (x / width) * 100;
154
+ const l = 100 - (y / height) * 100;
155
+ const hex = hslToHex(hue, s, l);
156
+ ctx.fillStyle = hex;
157
+ ctx.fillRect(x, y, 1, 1);
158
+ }
159
+ }
160
+ }
161
+
162
+ function handleColorMapClick(event: MouseEvent) {
163
+ if (!colorMapContainer || !colorMap) return;
164
+ const rect = colorMapContainer.getBoundingClientRect();
165
+ const x = event.clientX - rect.left;
166
+ const y = event.clientY - rect.top;
167
+
168
+ saturation = Math.max(0, Math.min(100, (x / rect.width) * 100));
169
+ lightness = Math.max(0, Math.min(100, 100 - (y / rect.height) * 100));
170
+
171
+ updateHexFromHsl();
172
+ drawColorMap();
173
+ }
174
+
175
+ function handleColorMapDrag(event: MouseEvent) {
176
+ if (isDragging) {
177
+ handleColorMapClick(event);
178
+ }
179
+ }
180
+
181
+ function handleHueChange(event: Event) {
182
+ const target = event.target as HTMLInputElement;
183
+ hue = parseInt(target.value);
184
+ updateHexFromHsl();
185
+ drawColorMap();
186
+ }
187
+
188
+ $effect(() => {
189
+ if (isOpen && colorMap) {
190
+ // Use setTimeout to ensure canvas is rendered
191
+ setTimeout(() => {
192
+ drawColorMap();
193
+ }, 0);
194
+ }
195
+ });
196
+
197
+ $effect(() => {
198
+ if (hue !== undefined && colorMap && isOpen) {
199
+ drawColorMap();
200
+ }
201
+ });
202
+
203
+ $effect(() => {
204
+ if (value && isValidHex(value) && !isDragging) {
205
+ updateFromHex(value);
206
+ }
207
+ });
208
+
209
+ function handleClickOutside(event: MouseEvent) {
210
+ if (
211
+ pickerContainer &&
212
+ !pickerContainer.contains(event.target as Node) &&
213
+ !(event.target as HTMLElement).closest(
214
+ '[aria-label="Open color picker"]',
215
+ )
216
+ ) {
217
+ isOpen = false;
218
+ }
219
+ }
220
+
221
+ onMount(() => {
222
+ if (value && isValidHex(value)) {
223
+ updateFromHex(value);
224
+ }
225
+ window.addEventListener("mousedown", handleClickOutside);
226
+ window.addEventListener("mouseup", () => {
227
+ isDragging = false;
228
+ });
229
+ window.addEventListener("mousemove", handleColorMapDrag);
230
+ });
231
+
232
+ onDestroy(() => {
233
+ window.removeEventListener("mousedown", handleClickOutside);
234
+ window.removeEventListener("mouseup", () => {});
235
+ window.removeEventListener("mousemove", handleColorMapDrag);
236
+ });
237
+
238
+ const displayColor = $derived(() => {
239
+ if (value && isValidHex(value)) {
240
+ return value;
241
+ }
242
+ return placeholder;
243
+ });
244
+
245
+ const currentHslColor = $derived(() => {
246
+ return `hsl(${hue}, 100%, 50%)`;
247
+ });
248
+
249
+ const pickerIndicatorX = $derived(() => {
250
+ return `${(saturation / 100) * 100}%`;
251
+ });
252
+
253
+ const pickerIndicatorY = $derived(() => {
254
+ return `${100 - (lightness / 100) * 100}%`;
255
+ });
57
256
  </script>
58
257
 
59
258
  <div {...restProps}>
@@ -71,12 +270,77 @@
71
270
  aria-label="Hex color input"
72
271
  />
73
272
  </div>
74
- {#if value && isValidHex(value)}
75
- <div
76
- class="w-8 h-8 rounded border-2 border-base-300 shrink-0 mt-6"
77
- style="background-color: {value};"
78
- aria-label="Color preview"
79
- ></div>
80
- {/if}
273
+ <div class="relative shrink-0 mt-6">
274
+ <button
275
+ type="button"
276
+ onclick={togglePicker}
277
+ {disabled}
278
+ class="w-11 h-11 rounded-lg border-2 border-card shrink-0 cursor-pointer hover:ring-2 hover:ring-brand-500 transition-colors disabled:cursor-not-allowed disabled:opacity-50"
279
+ style="background-color: {displayColor()};"
280
+ aria-label="Open color picker"
281
+ aria-expanded={isOpen}
282
+ ></button>
283
+
284
+ {#if isOpen}
285
+ <div
286
+ bind:this={pickerContainer}
287
+ class="absolute top-12 right-0 z-50 bg-input rounded-2xl shadow-lg p-4 w-80"
288
+ role="dialog"
289
+ aria-label="Color picker"
290
+ >
291
+ <div class="space-y-4">
292
+ <!-- Color Map -->
293
+ <div
294
+ bind:this={colorMapContainer}
295
+ class="relative w-full h-48 rounded-xl overflow-hidden cursor-crosshair"
296
+ onmousedown={(e) => {
297
+ isDragging = true;
298
+ handleColorMapClick(e);
299
+ }}
300
+ role="button"
301
+ tabindex="0"
302
+ >
303
+ <canvas
304
+ bind:this={colorMap}
305
+ width={256}
306
+ height={192}
307
+ class="w-full h-full"
308
+ ></canvas>
309
+ <!-- Color indicator -->
310
+ <div
311
+ class="absolute w-4 h-4 border-2 border-white rounded-full shadow-lg pointer-events-none transform -translate-x-1/2 -translate-y-1/2"
312
+ style="left: {pickerIndicatorX()}; top: {pickerIndicatorY()};"
313
+ ></div>
314
+ </div>
315
+
316
+ <!-- Hue Slider -->
317
+ <div class="space-y-2">
318
+ <div
319
+ class="relative h-6 rounded-lg overflow-hidden"
320
+ >
321
+ <div
322
+ class="absolute inset-0"
323
+ style="background: linear-gradient(to right, hsl(0,100%,50%), hsl(60,100%,50%), hsl(120,100%,50%), hsl(180,100%,50%), hsl(240,100%,50%), hsl(300,100%,50%), hsl(360,100%,50%));"
324
+ ></div>
325
+ <input
326
+ bind:this={hueSlider}
327
+ type="range"
328
+ min="0"
329
+ max="360"
330
+ value={hue}
331
+ oninput={handleHueChange}
332
+ class="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
333
+ aria-label="Hue slider"
334
+ />
335
+ <div
336
+ class="absolute top-0 bottom-0 w-3 bg-card shadow-md pointer-events-none my-0.25 rounded-full border border-base-950"
337
+ style="left: {(hue / 360) * 100}%;"
338
+ ></div>
339
+ </div>
340
+ </div>
341
+ </div>
342
+ </div>
343
+ {/if}
344
+ </div>
81
345
  </div>
82
346
  </div>
@@ -21,10 +21,12 @@
21
21
  variant?: SemanticVariant;
22
22
  message?: string;
23
23
  oninput?: (event: Event) => void;
24
+ onblur?: (event: Event) => void;
25
+ "aria-label"?: string;
24
26
  }
25
27
 
26
28
  let {
27
- value = "",
29
+ value = $bindable(""),
28
30
  type = "text",
29
31
  name = "",
30
32
  label = "",
@@ -34,6 +36,7 @@
34
36
  variant = "default",
35
37
  message = "",
36
38
  oninput,
39
+ onblur,
37
40
  ...restProps
38
41
  }: Props = $props();
39
42
 
@@ -74,7 +77,7 @@
74
77
  const inputClasses = $derived(() => {
75
78
  const sizeStyles = sizeClass();
76
79
  const baseClasses =
77
- "w-full bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed";
80
+ "w-full min-w-48 bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed";
78
81
 
79
82
  return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
80
83
  });
@@ -102,9 +105,6 @@
102
105
  });
103
106
 
104
107
  function handleInput(event: Event) {
105
- const target = event.target as HTMLInputElement;
106
- value = target.value;
107
-
108
108
  if (oninput) {
109
109
  oninput(event);
110
110
  }
@@ -119,11 +119,12 @@
119
119
  id={inputId}
120
120
  {type}
121
121
  {name}
122
- {value}
122
+ bind:value
123
123
  {placeholder}
124
124
  {disabled}
125
125
  class={inputClasses()}
126
126
  oninput={handleInput}
127
+ {onblur}
127
128
  aria-invalid={variant === "error" ? "true" : undefined}
128
129
  aria-describedby={message ? `${inputId}-message` : undefined}
129
130
  {...restProps}
@@ -10,7 +10,9 @@ interface Props {
10
10
  variant?: SemanticVariant;
11
11
  message?: string;
12
12
  oninput?: (event: Event) => void;
13
+ onblur?: (event: Event) => void;
14
+ "aria-label"?: string;
13
15
  }
14
- declare const Input: import("svelte").Component<Props, {}, "">;
16
+ declare const Input: import("svelte").Component<Props, {}, "value">;
15
17
  type Input = ReturnType<typeof Input>;
16
18
  export default Input;
@@ -41,9 +41,7 @@
41
41
 
42
42
  <div
43
43
  id={progressId}
44
- class="w-full bg-base-200 rounded-full overflow-hidden {sizeClasses[
45
- size
46
- ]}"
44
+ class="w-full bg-input rounded-full overflow-hidden {sizeClasses[size]}"
47
45
  role="progressbar"
48
46
  aria-valuenow={value}
49
47
  aria-valuemin="0"