@kayord/ui 2.1.8 → 2.1.10

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 (85) hide show
  1. package/dist/components/custom/combobox/Combobox.svelte.d.ts +1 -1
  2. package/dist/components/ui/accordion/accordion-root.svelte.d.ts +1 -1
  3. package/dist/components/ui/accordion/accordion.svelte.d.ts +1 -1
  4. package/dist/components/ui/audio-wave/audio-wave.svelte +58 -0
  5. package/dist/components/ui/audio-wave/audio-wave.svelte.d.ts +10 -0
  6. package/dist/components/ui/audio-wave/index.d.ts +2 -0
  7. package/dist/components/ui/audio-wave/index.js +2 -0
  8. package/dist/components/ui/calendar/calendar-month-select.svelte +5 -1
  9. package/dist/components/ui/calendar/calendar-year-select.svelte +5 -1
  10. package/dist/components/ui/calendar/calendar.svelte.d.ts +1 -1
  11. package/dist/components/ui/chip/chip-group.svelte +10 -0
  12. package/dist/components/ui/chip/chip-group.svelte.d.ts +8 -0
  13. package/dist/components/ui/chip/chip.svelte +132 -0
  14. package/dist/components/ui/chip/chip.svelte.d.ts +18 -0
  15. package/dist/components/ui/chip/index.d.ts +3 -0
  16. package/dist/components/ui/chip/index.js +3 -0
  17. package/dist/components/ui/color-picker/color-picker.svelte +474 -0
  18. package/dist/components/ui/color-picker/color-picker.svelte.d.ts +11 -0
  19. package/dist/components/ui/color-picker/index.d.ts +2 -0
  20. package/dist/components/ui/color-picker/index.js +2 -0
  21. package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
  22. package/dist/components/ui/command/command-input.svelte.d.ts +1 -1
  23. package/dist/components/ui/command/command.svelte.d.ts +1 -1
  24. package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +1 -1
  25. package/dist/components/ui/data-table/data-table.svelte.js +2 -3
  26. package/dist/components/ui/date-strip/ctx.d.ts +10 -0
  27. package/dist/components/ui/date-strip/ctx.js +8 -0
  28. package/dist/components/ui/date-strip/date-strip-item.svelte +52 -0
  29. package/dist/components/ui/date-strip/date-strip-item.svelte.d.ts +8 -0
  30. package/dist/components/ui/date-strip/date-strip.svelte +65 -0
  31. package/dist/components/ui/date-strip/date-strip.svelte.d.ts +15 -0
  32. package/dist/components/ui/date-strip/index.d.ts +3 -0
  33. package/dist/components/ui/date-strip/index.js +3 -0
  34. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +1 -1
  35. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
  36. package/dist/components/ui/dynamic-select/ctx.d.ts +15 -0
  37. package/dist/components/ui/dynamic-select/ctx.js +8 -0
  38. package/dist/components/ui/dynamic-select/dynamic-select-content.svelte +42 -0
  39. package/dist/components/ui/dynamic-select/dynamic-select-content.svelte.d.ts +7 -0
  40. package/dist/components/ui/dynamic-select/dynamic-select-item.svelte +16 -0
  41. package/dist/components/ui/dynamic-select/dynamic-select-item.svelte.d.ts +8 -0
  42. package/dist/components/ui/dynamic-select/dynamic-select-trigger.svelte +20 -0
  43. package/dist/components/ui/dynamic-select/dynamic-select-trigger.svelte.d.ts +8 -0
  44. package/dist/components/ui/dynamic-select/dynamic-select.svelte +37 -0
  45. package/dist/components/ui/dynamic-select/dynamic-select.svelte.d.ts +9 -0
  46. package/dist/components/ui/dynamic-select/index.d.ts +18 -0
  47. package/dist/components/ui/dynamic-select/index.js +7 -0
  48. package/dist/components/ui/field/field-error.svelte +1 -1
  49. package/dist/components/ui/index.d.ts +7 -0
  50. package/dist/components/ui/index.js +8 -0
  51. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  52. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +2 -2
  53. package/dist/components/ui/input-group/input-group-textarea.svelte.d.ts +1 -1
  54. package/dist/components/ui/input-otp/input-otp.svelte.d.ts +1 -1
  55. package/dist/components/ui/menubar/menubar-radio-group.svelte.d.ts +1 -1
  56. package/dist/components/ui/native-select/native-select.svelte.d.ts +1 -1
  57. package/dist/components/ui/radio-group/radio-group.svelte.d.ts +1 -1
  58. package/dist/components/ui/range-calendar/range-calendar.svelte.d.ts +1 -1
  59. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +2 -2
  60. package/dist/components/ui/slider/slider.svelte.d.ts +1 -1
  61. package/dist/components/ui/status-dot/index.d.ts +2 -0
  62. package/dist/components/ui/status-dot/index.js +2 -0
  63. package/dist/components/ui/status-dot/status-dot.svelte +56 -0
  64. package/dist/components/ui/status-dot/status-dot.svelte.d.ts +9 -0
  65. package/dist/components/ui/tabs/tabs.svelte.d.ts +1 -1
  66. package/dist/components/ui/textarea/textarea.svelte.d.ts +1 -1
  67. package/dist/components/ui/timeline/index.d.ts +8 -0
  68. package/dist/components/ui/timeline/index.js +8 -0
  69. package/dist/components/ui/timeline/timeline-content.svelte +16 -0
  70. package/dist/components/ui/timeline/timeline-content.svelte.d.ts +8 -0
  71. package/dist/components/ui/timeline/timeline-date.svelte +10 -0
  72. package/dist/components/ui/timeline/timeline-date.svelte.d.ts +8 -0
  73. package/dist/components/ui/timeline/timeline-description.svelte +10 -0
  74. package/dist/components/ui/timeline/timeline-description.svelte.d.ts +8 -0
  75. package/dist/components/ui/timeline/timeline-item.svelte +16 -0
  76. package/dist/components/ui/timeline/timeline-item.svelte.d.ts +8 -0
  77. package/dist/components/ui/timeline/timeline-separator.svelte +25 -0
  78. package/dist/components/ui/timeline/timeline-separator.svelte.d.ts +8 -0
  79. package/dist/components/ui/timeline/timeline-title.svelte +10 -0
  80. package/dist/components/ui/timeline/timeline-title.svelte.d.ts +8 -0
  81. package/dist/components/ui/timeline/timeline.svelte +17 -0
  82. package/dist/components/ui/timeline/timeline.svelte.d.ts +8 -0
  83. package/dist/components/ui/toggle-group/toggle-group-item.svelte.d.ts +1 -1
  84. package/dist/components/ui/toggle-group/toggle-group.svelte.d.ts +1 -1
  85. package/package.json +17 -12
@@ -0,0 +1,474 @@
1
+ <script lang="ts">
2
+ import { cn } from "../../../utils";
3
+ import { Button } from "../button";
4
+ import { ChevronDown } from "lucide-svelte";
5
+ import * as Popover from "../popover";
6
+ import * as Command from "../command";
7
+ import * as ButtonGroup from "../button-group";
8
+ import { Input } from "../input";
9
+
10
+ type ColorFormat = "hex" | "rgb" | "hsl" | "oklch";
11
+
12
+ let {
13
+ value = $bindable("#000000"),
14
+ class: className,
15
+ allowOpacity = false,
16
+ defaultFormat = "hex",
17
+ formats = ["hex", "rgb", "hsl", "oklch"],
18
+ }: {
19
+ value?: string;
20
+ class?: string;
21
+ allowOpacity?: boolean;
22
+ defaultFormat?: ColorFormat;
23
+ formats?: ColorFormat[];
24
+ } = $props();
25
+
26
+ let h = $state(0);
27
+ let s = $state(0);
28
+ let v = $state(0);
29
+ let a = $state(1);
30
+ let activeFormat = $state<ColorFormat>(defaultFormat);
31
+ let isDragging = $state(false);
32
+
33
+ let sbRef: HTMLDivElement | undefined = $state();
34
+ let hueRef: HTMLDivElement | undefined = $state();
35
+ let alphaRef: HTMLDivElement | undefined = $state();
36
+ let formatOpen = $state(false);
37
+
38
+ $effect(() => {
39
+ if (!isDragging) {
40
+ const parsed = parseColor(value);
41
+ if (parsed) {
42
+ const currentStr = formatOutput(h, s, v, a, activeFormat);
43
+ const parsedStr = formatOutput(parsed.h, parsed.s, parsed.v, parsed.a, activeFormat);
44
+
45
+ if (currentStr !== parsedStr) {
46
+ h = parsed.h;
47
+ s = parsed.s;
48
+ v = parsed.v;
49
+ a = parsed.a;
50
+ if (value.startsWith("rgb")) activeFormat = "rgb";
51
+ else if (value.startsWith("hsl")) activeFormat = "hsl";
52
+ else if (value.startsWith("oklch")) activeFormat = "oklch";
53
+ else activeFormat = "hex";
54
+ }
55
+ }
56
+ }
57
+ });
58
+
59
+ function updateExternal() {
60
+ value = formatOutput(h, s, v, a, activeFormat);
61
+ }
62
+
63
+ function setFormat(fmt: ColorFormat) {
64
+ activeFormat = fmt;
65
+ updateExternal();
66
+ formatOpen = false;
67
+ }
68
+
69
+ function parseColor(str: string) {
70
+ str = str.trim().toLowerCase();
71
+ if (str.startsWith("#")) {
72
+ let hex = str.replace("#", "");
73
+ let r = 0,
74
+ g = 0,
75
+ b = 0,
76
+ alpha = 1;
77
+ if (hex.length === 3) {
78
+ r = parseInt(hex[0] + hex[0], 16);
79
+ g = parseInt(hex[1] + hex[1], 16);
80
+ b = parseInt(hex[2] + hex[2], 16);
81
+ } else if (hex.length === 6) {
82
+ r = parseInt(hex.substring(0, 2), 16);
83
+ g = parseInt(hex.substring(2, 4), 16);
84
+ b = parseInt(hex.substring(4, 6), 16);
85
+ } else if (hex.length === 8) {
86
+ r = parseInt(hex.substring(0, 2), 16);
87
+ g = parseInt(hex.substring(2, 4), 16);
88
+ b = parseInt(hex.substring(4, 6), 16);
89
+ alpha = parseInt(hex.substring(6, 8), 16) / 255;
90
+ }
91
+ return { ...rgbToHsv(r, g, b), a: alpha };
92
+ }
93
+ if (str.startsWith("rgb")) {
94
+ const values = str.match(/[\d.]+/g)?.map(Number);
95
+ if (values && values.length >= 3) return { ...rgbToHsv(values[0], values[1], values[2]), a: values[3] ?? 1 };
96
+ }
97
+ if (str.startsWith("hsl")) {
98
+ const values = str.match(/[\d.]+/g)?.map(Number);
99
+ if (values && values.length >= 3) {
100
+ const sNorm = values[1] / 100,
101
+ lNorm = values[2] / 100;
102
+ let vNorm = lNorm + sNorm * Math.min(lNorm, 1 - lNorm);
103
+ let sHsv = vNorm === 0 ? 0 : 2 * (1 - lNorm / vNorm);
104
+ return { h: values[0], s: sHsv * 100, v: vNorm * 100, a: values[3] ?? 1 };
105
+ }
106
+ }
107
+ if (str.startsWith("oklch")) {
108
+ const values = str.match(/[\d.%]+/g)?.map((s) => (s.includes("%") ? parseFloat(s) / 100 : parseFloat(s)));
109
+ if (values && values.length >= 3) {
110
+ const rgb = oklchToRgb(values[0], values[1], values[2]);
111
+ return { ...rgbToHsv(rgb.r, rgb.g, rgb.b), a: values[3] ?? 1 };
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+
117
+ function formatOutput(h: number, s: number, v: number, a: number, format: ColorFormat): string {
118
+ if (format === "hex") return hsvToHex(h, s, v, a);
119
+ if (format === "rgb") return hsvToRgbString(h, s, v, a);
120
+ if (format === "hsl") return hsvToHslString(h, s, v, a);
121
+ if (format === "oklch") return hsvToOklchString(h, s, v, a);
122
+ return "";
123
+ }
124
+
125
+ function rgbToHsv(r: number, g: number, b: number) {
126
+ r /= 255;
127
+ g /= 255;
128
+ b /= 255;
129
+ const max = Math.max(r, g, b),
130
+ min = Math.min(r, g, b);
131
+ let h = 0,
132
+ s = 0,
133
+ v = max;
134
+ const d = max - min;
135
+ s = max === 0 ? 0 : d / max;
136
+ if (max !== min) {
137
+ switch (max) {
138
+ case r:
139
+ h = (g - b) / d + (g < b ? 6 : 0);
140
+ break;
141
+ case g:
142
+ h = (b - r) / d + 2;
143
+ break;
144
+ case b:
145
+ h = (r - g) / d + 4;
146
+ break;
147
+ }
148
+ h /= 6;
149
+ }
150
+ return { h: h * 360, s: s * 100, v: v * 100 };
151
+ }
152
+
153
+ function hsvToRgb(h: number, s: number, v: number) {
154
+ let sNorm = s / 100,
155
+ vNorm = v / 100;
156
+ let r = 0,
157
+ g = 0,
158
+ b = 0;
159
+ const i = Math.floor(h / 60),
160
+ f = h / 60 - i,
161
+ p = vNorm * (1 - sNorm),
162
+ q = vNorm * (1 - f * sNorm),
163
+ t = vNorm * (1 - (1 - f) * sNorm);
164
+ switch (i % 6) {
165
+ case 0:
166
+ r = vNorm;
167
+ g = t;
168
+ b = p;
169
+ break;
170
+ case 1:
171
+ r = q;
172
+ g = vNorm;
173
+ b = p;
174
+ break;
175
+ case 2:
176
+ r = p;
177
+ g = vNorm;
178
+ b = t;
179
+ break;
180
+ case 3:
181
+ r = p;
182
+ g = q;
183
+ b = vNorm;
184
+ break;
185
+ case 4:
186
+ r = t;
187
+ g = p;
188
+ b = vNorm;
189
+ break;
190
+ case 5:
191
+ r = vNorm;
192
+ g = p;
193
+ b = q;
194
+ break;
195
+ }
196
+ return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) };
197
+ }
198
+
199
+ function hsvToOklchString(h: number, s: number, v: number, a: number) {
200
+ const rgb = hsvToRgb(h, s, v);
201
+ const oklch = rgbToOklch(rgb.r, rgb.g, rgb.b);
202
+ const L = (oklch.l * 100).toFixed(1) + "%";
203
+ const C = oklch.c.toFixed(3);
204
+ const H = (oklch.h || 0).toFixed(1);
205
+
206
+ if (allowOpacity && a < 1) return `oklch(${L} ${C} ${H} / ${parseFloat(a.toFixed(2))})`;
207
+ return `oklch(${L} ${C} ${H})`;
208
+ }
209
+
210
+ function rgbToOklch(r: number, g: number, b: number) {
211
+ r /= 255;
212
+ g /= 255;
213
+ b /= 255;
214
+ r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
215
+ g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
216
+ b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
217
+
218
+ const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
219
+ const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
220
+ const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
221
+
222
+ const l_ = Math.cbrt(l),
223
+ m_ = Math.cbrt(m),
224
+ s_ = Math.cbrt(s);
225
+
226
+ const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;
227
+ const A = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;
228
+ const B = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;
229
+
230
+ const C = Math.sqrt(A * A + B * B);
231
+ let H = (Math.atan2(B, A) * 180) / Math.PI;
232
+ if (H < 0) H += 360;
233
+
234
+ return { l: L, c: C, h: H };
235
+ }
236
+
237
+ function oklchToRgb(l: number, c: number, h: number) {
238
+ const hRad = h * (Math.PI / 180);
239
+ const A = c * Math.cos(hRad);
240
+ const B = c * Math.sin(hRad);
241
+ const L = l;
242
+
243
+ const l_ = L + 0.3963377774 * A + 0.2158037573 * B;
244
+ const m_ = L - 0.1055613458 * A - 0.0638541728 * B;
245
+ const s_ = L - 0.0894841775 * A - 1.291485548 * B;
246
+
247
+ const lLin = l_ * l_ * l_;
248
+ const mLin = m_ * m_ * m_;
249
+ const sLin = s_ * s_ * s_;
250
+
251
+ let r = +4.0767416621 * lLin - 3.3077115913 * mLin + 0.2309699292 * sLin;
252
+ let g = -1.2684380046 * lLin + 2.6097574011 * mLin - 0.3413193965 * sLin;
253
+ let b = -0.0041960863 * lLin - 0.7034186147 * mLin + 1.707614701 * sLin;
254
+
255
+ r = r >= 0.0031308 ? 1.055 * Math.pow(r, 1.0 / 2.4) - 0.055 : 12.92 * r;
256
+ g = g >= 0.0031308 ? 1.055 * Math.pow(g, 1.0 / 2.4) - 0.055 : 12.92 * g;
257
+ b = b >= 0.0031308 ? 1.055 * Math.pow(b, 1.0 / 2.4) - 0.055 : 12.92 * b;
258
+
259
+ r = Math.min(Math.max(0, r), 1) * 255;
260
+ g = Math.min(Math.max(0, g), 1) * 255;
261
+ b = Math.min(Math.max(0, b), 1) * 255;
262
+ return { r, g, b };
263
+ }
264
+
265
+ function hsvToHex(h: number, s: number, v: number, a: number) {
266
+ const { r, g, b } = hsvToRgb(h, s, v);
267
+ const toHex = (x: number) => {
268
+ const hex = x.toString(16);
269
+ return hex.length === 1 ? "0" + hex : hex;
270
+ };
271
+ let hex = `#${toHex(r)}${toHex(g)}${toHex(b)}`;
272
+ if (allowOpacity && a < 1) hex += toHex(Math.round(a * 255));
273
+ return hex.toUpperCase();
274
+ }
275
+
276
+ function hsvToRgbString(h: number, s: number, v: number, a: number) {
277
+ const { r, g, b } = hsvToRgb(h, s, v);
278
+ if (allowOpacity && a < 1) return `rgba(${r}, ${g}, ${b}, ${parseFloat(a.toFixed(2))})`;
279
+ return `rgb(${r}, ${g}, ${b})`;
280
+ }
281
+
282
+ function hsvToHslString(h: number, s: number, v: number, a: number) {
283
+ const sNorm = s / 100,
284
+ vNorm = v / 100;
285
+ let l = ((2 - sNorm) * vNorm) / 2;
286
+ let sHsl = l && l < 1 ? (sNorm * vNorm) / (l < 0.5 ? l * 2 : 2 - l * 2) : sNorm;
287
+ if (allowOpacity && a < 1)
288
+ return `hsla(${Math.round(h)}, ${Math.round(sHsl * 100)}%, ${Math.round(l * 100)}%, ${parseFloat(a.toFixed(2))})`;
289
+ return `hsl(${Math.round(h)}, ${Math.round(sHsl * 100)}%, ${Math.round(l * 100)}%)`;
290
+ }
291
+
292
+ function handleDragStart(e: MouseEvent | TouchEvent, fn: (e: MouseEvent | TouchEvent) => void) {
293
+ isDragging = true;
294
+ fn(e);
295
+ const move = (e: MouseEvent | TouchEvent) => fn(e);
296
+ const stop = () => {
297
+ isDragging = false;
298
+ window.removeEventListener("mousemove", move);
299
+ window.removeEventListener("touchmove", move);
300
+ window.removeEventListener("mouseup", stop);
301
+ window.removeEventListener("touchend", stop);
302
+ };
303
+ window.addEventListener("mousemove", move);
304
+ window.addEventListener("touchmove", move);
305
+ window.addEventListener("mouseup", stop);
306
+ window.addEventListener("touchend", stop);
307
+ }
308
+
309
+ function handleSbChange(e: MouseEvent | TouchEvent) {
310
+ if (!sbRef) return;
311
+ const rect = sbRef.getBoundingClientRect();
312
+ const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
313
+ const clientY = "touches" in e ? e.touches[0].clientY : e.clientY;
314
+ const x = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
315
+ const y = Math.max(0, Math.min(1, (clientY - rect.top) / rect.height));
316
+ s = x * 100;
317
+ v = (1 - y) * 100;
318
+ updateExternal();
319
+ }
320
+
321
+ function handleHueChange(e: MouseEvent | TouchEvent) {
322
+ if (!hueRef) return;
323
+ const rect = hueRef.getBoundingClientRect();
324
+ const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
325
+ const x = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
326
+ h = x * 360;
327
+ updateExternal();
328
+ }
329
+
330
+ function handleAlphaChange(e: MouseEvent | TouchEvent) {
331
+ if (!alphaRef) return;
332
+ const rect = alphaRef.getBoundingClientRect();
333
+ const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
334
+ const x = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
335
+ a = Math.round(x * 100) / 100;
336
+ updateExternal();
337
+ }
338
+
339
+ function handleAlphaInput(e: Event & { currentTarget: HTMLInputElement }) {
340
+ let val = parseInt(e.currentTarget.value);
341
+ if (isNaN(val)) return;
342
+ val = Math.max(0, Math.min(100, val));
343
+ a = val / 100;
344
+ updateExternal();
345
+ }
346
+ </script>
347
+
348
+ <div class={cn("bg-popover flex w-[350px] flex-col gap-3 rounded-lg border p-3 shadow-sm", className)}>
349
+ <div
350
+ bind:this={sbRef}
351
+ class="relative h-56 w-full cursor-crosshair touch-none overflow-hidden rounded-md shadow-sm"
352
+ style:background-color={`hsl(${h}, 100%, 50%)`}
353
+ role="slider"
354
+ aria-label="Saturation and Brightness"
355
+ aria-valuenow={s}
356
+ tabindex="0"
357
+ onmousedown={(e) => handleDragStart(e, handleSbChange)}
358
+ ontouchstart={(e) => handleDragStart(e, handleSbChange)}
359
+ >
360
+ <div class="pointer-events-none absolute inset-0 bg-gradient-to-r from-white to-transparent" />
361
+ <div class="pointer-events-none absolute inset-0 bg-gradient-to-t from-black to-transparent" />
362
+ <div
363
+ class="pointer-events-none absolute h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm ring-1 ring-black/20"
364
+ style:left={`${s}%`}
365
+ style:top={`${100 - v}%`}
366
+ />
367
+ </div>
368
+
369
+ <div class="flex items-center gap-3">
370
+ <div
371
+ class="relative mt-1 h-8 w-8 shrink-0 overflow-hidden rounded-md border bg-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==')] shadow-sm"
372
+ >
373
+ <div class="absolute inset-0" style:background-color={hsvToHex(h, s, v, a)} />
374
+ </div>
375
+
376
+ <div class="flex flex-1 flex-col justify-center gap-3">
377
+ <div
378
+ bind:this={hueRef}
379
+ class="relative h-3 w-full cursor-pointer touch-none rounded-full shadow-sm ring-1 ring-black/5"
380
+ style:background={"linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)"}
381
+ role="slider"
382
+ aria-valuenow={h}
383
+ tabindex="0"
384
+ onmousedown={(e) => handleDragStart(e, handleHueChange)}
385
+ ontouchstart={(e) => handleDragStart(e, handleHueChange)}
386
+ >
387
+ <div
388
+ class="pointer-events-none absolute top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white"
389
+ style:left={`${(h / 360) * 100}%`}
390
+ />
391
+ </div>
392
+
393
+ {#if allowOpacity}
394
+ <div
395
+ bind:this={alphaRef}
396
+ class="relative h-3 w-full cursor-pointer touch-none rounded-full bg-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==')] shadow-sm ring-1 ring-black/5"
397
+ role="slider"
398
+ aria-valuenow={a}
399
+ tabindex="0"
400
+ onmousedown={(e) => handleDragStart(e, handleAlphaChange)}
401
+ ontouchstart={(e) => handleDragStart(e, handleAlphaChange)}
402
+ >
403
+ <div
404
+ class="absolute inset-0 rounded-full"
405
+ style:background={`linear-gradient(to right, transparent, ${hsvToHex(h, s, v, 1)})`}
406
+ />
407
+ <div
408
+ class="pointer-events-none absolute top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white"
409
+ style:left={`${a * 100}%`}
410
+ />
411
+ </div>
412
+ {/if}
413
+ </div>
414
+ </div>
415
+
416
+ <ButtonGroup.Root class="w-full">
417
+ {#if formats.length > 1}
418
+ <Popover.Root bind:open={formatOpen}>
419
+ <Popover.Trigger>
420
+ {#snippet child({ props })}
421
+ <Button {...props} variant="outline" class="h-9 max-w-[5rem] justify-between px-2 text-[10px]">
422
+ {activeFormat.toUpperCase()}
423
+ <ChevronDown class="h-3 w-3 opacity-50" />
424
+ </Button>
425
+ {/snippet}
426
+ </Popover.Trigger>
427
+ <Popover.Content class="w-[4.5rem] p-0" align="start">
428
+ <Command.Root>
429
+ <Command.List>
430
+ <Command.Group>
431
+ {#each ["hex", "rgb", "hsl", "oklch"] as fmt}
432
+ <Command.Item
433
+ value={fmt}
434
+ onSelect={() => setFormat(fmt as ColorFormat)}
435
+ class="flex h-7 justify-center text-[10px]"
436
+ >
437
+ {fmt.toUpperCase()}
438
+ </Command.Item>
439
+ {/each}
440
+ </Command.Group>
441
+ </Command.List>
442
+ </Command.Root>
443
+ </Popover.Content>
444
+ </Popover.Root>
445
+ {:else}
446
+ <Button variant="outline" class="h-9 max-w-[5rem] justify-between px-2 text-[10px]">
447
+ {activeFormat.toUpperCase()}
448
+ </Button>
449
+ {/if}
450
+ <Input
451
+ class="h-9 flex-1 font-mono text-[10px] uppercase"
452
+ {value}
453
+ oninput={(e) => {
454
+ const parsed = parseColor(e.currentTarget.value);
455
+ if (parsed) {
456
+ h = parsed.h;
457
+ s = parsed.s;
458
+ v = parsed.v;
459
+ a = parsed.a;
460
+ updateExternal();
461
+ }
462
+ }}
463
+ />
464
+
465
+ {#if allowOpacity}
466
+ <Input
467
+ class="h-9 max-w-[4.2rem] text-right font-mono text-[10px]"
468
+ value={Math.round(a * 100) + "%"}
469
+ oninput={handleAlphaInput}
470
+ maxlength={3}
471
+ />
472
+ {/if}
473
+ </ButtonGroup.Root>
474
+ </div>
@@ -0,0 +1,11 @@
1
+ type ColorFormat = "hex" | "rgb" | "hsl" | "oklch";
2
+ type $$ComponentProps = {
3
+ value?: string;
4
+ class?: string;
5
+ allowOpacity?: boolean;
6
+ defaultFormat?: ColorFormat;
7
+ formats?: ColorFormat[];
8
+ };
9
+ declare const ColorPicker: import("svelte").Component<$$ComponentProps, {}, "value">;
10
+ type ColorPicker = ReturnType<typeof ColorPicker>;
11
+ export default ColorPicker;
@@ -0,0 +1,2 @@
1
+ import ColorPicker from "./color-picker.svelte";
2
+ export { ColorPicker as Root };
@@ -0,0 +1,2 @@
1
+ import ColorPicker from "./color-picker.svelte";
2
+ export { ColorPicker as Root };
@@ -7,6 +7,6 @@ type $$ComponentProps = WithoutChildrenOrChild<DialogPrimitive.RootProps> & With
7
7
  title?: string;
8
8
  description?: string;
9
9
  };
10
- declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "open">;
10
+ declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "ref" | "value" | "open">;
11
11
  type CommandDialog = ReturnType<typeof CommandDialog>;
12
12
  export default CommandDialog;
@@ -1,4 +1,4 @@
1
1
  import { Command as CommandPrimitive } from "bits-ui";
2
- declare const CommandInput: import("svelte").Component<CommandPrimitive.InputProps, {}, "value" | "ref">;
2
+ declare const CommandInput: import("svelte").Component<CommandPrimitive.InputProps, {}, "ref" | "value">;
3
3
  type CommandInput = ReturnType<typeof CommandInput>;
4
4
  export default CommandInput;
@@ -3,6 +3,6 @@ export type CommandRootApi = CommandPrimitive.Root;
3
3
  type $$ComponentProps = CommandPrimitive.RootProps & {
4
4
  api?: CommandRootApi | null;
5
5
  };
6
- declare const Command: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "api">;
6
+ declare const Command: import("svelte").Component<$$ComponentProps, {}, "ref" | "value" | "api">;
7
7
  type Command = ReturnType<typeof Command>;
8
8
  export default Command;
@@ -1,4 +1,4 @@
1
1
  import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
2
- declare const ContextMenuRadioGroup: import("svelte").Component<ContextMenuPrimitive.RadioGroupProps, {}, "value" | "ref">;
2
+ declare const ContextMenuRadioGroup: import("svelte").Component<ContextMenuPrimitive.RadioGroupProps, {}, "ref" | "value">;
3
3
  type ContextMenuRadioGroup = ReturnType<typeof ContextMenuRadioGroup>;
4
4
  export default ContextMenuRadioGroup;
@@ -37,10 +37,9 @@ export function createSvelteTable(options) {
37
37
  const table = createTable(resolvedOptions);
38
38
  let state = $state(table.initialState);
39
39
  function updateOptions() {
40
- table.setOptions((prev) => {
41
- return mergeObjects(prev, options, {
40
+ table.setOptions(() => {
41
+ return mergeObjects(resolvedOptions, options, {
42
42
  state: mergeObjects(state, options.state || {}),
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
43
  onStateChange: (updater) => {
45
44
  if (updater instanceof Function)
46
45
  state = updater(state);
@@ -0,0 +1,10 @@
1
+ import type { DateValue } from "@internationalized/date";
2
+ type DateStripContext = {
3
+ selectedValue: () => DateValue | undefined;
4
+ onSelect: (date: DateValue) => void;
5
+ isDateDisabled: (date: DateValue) => boolean;
6
+ direction: () => "start" | "end";
7
+ };
8
+ export declare function setDateStripContext(props: DateStripContext): void;
9
+ export declare function getDateStripContext(): DateStripContext;
10
+ export {};
@@ -0,0 +1,8 @@
1
+ import { getContext, setContext } from "svelte";
2
+ const DATESTRIP_KEY = Symbol("datestrip");
3
+ export function setDateStripContext(props) {
4
+ setContext(DATESTRIP_KEY, props);
5
+ }
6
+ export function getDateStripContext() {
7
+ return getContext(DATESTRIP_KEY);
8
+ }
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { cn } from "../../../utils";
3
+ import { getDateStripContext } from "./ctx";
4
+ import { type DateValue, getLocalTimeZone, isToday } from "@internationalized/date";
5
+ import { DateFormatter } from "@internationalized/date";
6
+ import { Button } from "../button";
7
+
8
+ let {
9
+ date,
10
+ class: className,
11
+ }: {
12
+ date: DateValue;
13
+ class?: string;
14
+ } = $props();
15
+
16
+ const ctx = getDateStripContext();
17
+ const formatterMonth = new DateFormatter("en-US", { month: "short" });
18
+ const formatterDay = new DateFormatter("en-US", { day: "numeric" });
19
+
20
+ let isSelected = $derived(ctx.selectedValue()?.compare(date) === 0);
21
+ let isDisabled = $derived(ctx.isDateDisabled(date));
22
+ let direction = $derived(ctx.direction());
23
+
24
+ function handleClick() {
25
+ if (!isDisabled) {
26
+ ctx.onSelect(date);
27
+ }
28
+ }
29
+
30
+ const dateObj = $derived(date.toDate("UTC"));
31
+ </script>
32
+
33
+ <Button
34
+ variant="ghost"
35
+ onclick={handleClick}
36
+ class={cn(
37
+ "animate-in fade-in-5 flex flex-col items-center justify-center -space-y-1",
38
+ "h-12 w-12 shrink-0 rounded-lg",
39
+ direction === "end" ? "slide-in-from-right-12" : "slide-in-from-left-12",
40
+ isToday(date, getLocalTimeZone()) && "bg-accent",
41
+ isSelected && "bg-primary text-primary-foreground",
42
+ className
43
+ )}
44
+ disabled={isDisabled}
45
+ >
46
+ <span class="text-[8px] font-medium uppercase opacity-70">
47
+ {formatterMonth.format(dateObj)}
48
+ </span>
49
+ <span class="text-base leading-none font-bold">
50
+ {formatterDay.format(dateObj)}
51
+ </span>
52
+ </Button>
@@ -0,0 +1,8 @@
1
+ import { type DateValue } from "@internationalized/date";
2
+ type $$ComponentProps = {
3
+ date: DateValue;
4
+ class?: string;
5
+ };
6
+ declare const DateStripItem: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type DateStripItem = ReturnType<typeof DateStripItem>;
8
+ export default DateStripItem;
@@ -0,0 +1,65 @@
1
+ <script lang="ts">
2
+ import { cn } from "../../../utils";
3
+ import { setDateStripContext } from "./ctx";
4
+ import { Button } from "../button";
5
+ import { ChevronLeft, ChevronRight } from "lucide-svelte";
6
+ import type { Snippet } from "svelte";
7
+ import { type DateValue, getLocalTimeZone, today, startOfWeek } from "@internationalized/date";
8
+
9
+ let {
10
+ value = $bindable(),
11
+ class: className,
12
+ daysToShow = 5,
13
+ isDateDisabled = () => false,
14
+ onDateChange,
15
+ children,
16
+ }: {
17
+ value?: DateValue | undefined;
18
+ class?: string;
19
+ daysToShow?: number;
20
+ isDateDisabled?: (date: DateValue) => boolean;
21
+ onDateChange?: (date: DateValue) => void;
22
+ children: Snippet<[{ date: DateValue }]>;
23
+ } = $props();
24
+
25
+ let startDate = $state(startOfWeek(today(getLocalTimeZone()), "en-US"));
26
+ let slideDirection = $state<"start" | "end">("end");
27
+
28
+ const displayedDates = $derived(Array.from({ length: daysToShow }, (_, i) => startDate.add({ days: i })));
29
+
30
+ function handlePrev() {
31
+ slideDirection = "start";
32
+ startDate = startDate.add({ days: -daysToShow });
33
+ }
34
+
35
+ function handleNext() {
36
+ slideDirection = "end";
37
+ startDate = startDate.add({ days: daysToShow });
38
+ }
39
+
40
+ setDateStripContext({
41
+ selectedValue: () => value,
42
+ onSelect: (d) => {
43
+ value = d;
44
+ onDateChange?.(d);
45
+ },
46
+ isDateDisabled,
47
+ direction: () => slideDirection,
48
+ });
49
+ </script>
50
+
51
+ <div class={cn("bg-card flex items-center gap-2 rounded-xl border p-1 shadow-sm", className)}>
52
+ <Button variant="ghost" size="icon" class="h-7 w-7 shrink-0" onclick={handlePrev}>
53
+ <ChevronLeft class="h-4 w-4" />
54
+ </Button>
55
+
56
+ <div class="flex flex-1 justify-between gap-1 overflow-hidden">
57
+ {#each displayedDates as date (date.toString())}
58
+ {@render children({ date })}
59
+ {/each}
60
+ </div>
61
+
62
+ <Button variant="ghost" size="icon" class="h-7 w-7 shrink-0" onclick={handleNext}>
63
+ <ChevronRight class="h-4 w-4" />
64
+ </Button>
65
+ </div>