@sarunyu/system-one 1.0.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.
Files changed (37) hide show
  1. package/README.md +11 -0
  2. package/dist/index.cjs +3672 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.js +3655 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/components/button.d.ts +16 -0
  7. package/dist/src/components/button.d.ts.map +1 -0
  8. package/dist/src/components/card.d.ts +18 -0
  9. package/dist/src/components/card.d.ts.map +1 -0
  10. package/dist/src/components/date-input.d.ts +33 -0
  11. package/dist/src/components/date-input.d.ts.map +1 -0
  12. package/dist/src/components/dropdown-multiple.d.ts +28 -0
  13. package/dist/src/components/dropdown-multiple.d.ts.map +1 -0
  14. package/dist/src/components/dropdown.d.ts +29 -0
  15. package/dist/src/components/dropdown.d.ts.map +1 -0
  16. package/dist/src/components/input.d.ts +15 -0
  17. package/dist/src/components/input.d.ts.map +1 -0
  18. package/dist/src/components/option-list.d.ts +19 -0
  19. package/dist/src/components/option-list.d.ts.map +1 -0
  20. package/dist/src/components/search-input.d.ts +11 -0
  21. package/dist/src/components/search-input.d.ts.map +1 -0
  22. package/dist/src/components/tab.d.ts +24 -0
  23. package/dist/src/components/tab.d.ts.map +1 -0
  24. package/dist/src/components/textarea.d.ts +14 -0
  25. package/dist/src/components/textarea.d.ts.map +1 -0
  26. package/dist/src/components/time-input.d.ts +36 -0
  27. package/dist/src/components/time-input.d.ts.map +1 -0
  28. package/dist/src/components/ui/drawer.d.ts +14 -0
  29. package/dist/src/components/ui/drawer.d.ts.map +1 -0
  30. package/dist/src/components/ui/use-mobile.d.ts +2 -0
  31. package/dist/src/components/ui/use-mobile.d.ts.map +1 -0
  32. package/dist/src/index.d.ts +25 -0
  33. package/dist/src/index.d.ts.map +1 -0
  34. package/dist/src/lib/utils.d.ts +3 -0
  35. package/dist/src/lib/utils.d.ts.map +1 -0
  36. package/dist/style.css +2 -0
  37. package/package.json +120 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,3672 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const clsx = require("clsx");
6
+ const tailwindMerge = require("tailwind-merge");
7
+ const react = require("@phosphor-icons/react");
8
+ const reactDayPicker = require("react-day-picker");
9
+ const Popover = require("@radix-ui/react-popover");
10
+ const lucideReact = require("lucide-react");
11
+ const vaul = require("vaul");
12
+ function _interopNamespaceDefault(e) {
13
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
14
+ if (e) {
15
+ for (const k in e) {
16
+ if (k !== "default") {
17
+ const d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: () => e[k]
21
+ });
22
+ }
23
+ }
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+ const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
29
+ const Popover__namespace = /* @__PURE__ */ _interopNamespaceDefault(Popover);
30
+ function cn(...inputs) {
31
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
32
+ }
33
+ const labelIconSizeClass = {
34
+ xs: "size-[13px]",
35
+ sm: "size-[15px]",
36
+ md: "size-[15px]",
37
+ lg: "size-[16px]",
38
+ xl: "size-[16px]"
39
+ };
40
+ const gapClass = {
41
+ xs: "gap-[4px]",
42
+ sm: "gap-[4px]",
43
+ md: "gap-[4px]",
44
+ lg: "gap-[4px]",
45
+ xl: "gap-[4px]"
46
+ };
47
+ const textSizeClass = {
48
+ xs: "text-[12px] leading-[18px]",
49
+ sm: "text-[14px] leading-[20px]",
50
+ md: "text-[14px] leading-[20px]",
51
+ lg: "text-[14px] leading-[20px]",
52
+ xl: "text-[14px] leading-[20px]"
53
+ };
54
+ const roundedLabelClass = {
55
+ xs: "rounded-[4px]",
56
+ sm: "rounded-[6px]",
57
+ md: "rounded-[6px]",
58
+ lg: "rounded-[8px]",
59
+ xl: "rounded-[8px]"
60
+ };
61
+ function getPaddingClasses(size, hasLeft, hasRight) {
62
+ const pyMap = {
63
+ xs: "py-[4px]",
64
+ sm: "py-[4px]",
65
+ md: "py-[6px]",
66
+ lg: "py-[8px]",
67
+ xl: "py-[10px]"
68
+ };
69
+ const pxMap = {
70
+ xs: { l: "pl-[6px]", r: "pr-[6px]" },
71
+ sm: { l: "pl-[8px]", r: "pr-[8px]" },
72
+ md: { l: "pl-[10px]", r: "pr-[10px]" },
73
+ lg: { l: "pl-[14px]", r: "pr-[14px]" },
74
+ xl: { l: "pl-[16px]", r: "pr-[16px]" }
75
+ };
76
+ const reducedMap = {
77
+ xs: { l: "pl-[6px]", r: "pr-[6px]" },
78
+ sm: { l: "pl-[6px]", r: "pr-[6px]" },
79
+ md: { l: "pl-[8px]", r: "pr-[8px]" },
80
+ lg: { l: "pl-[10px]", r: "pr-[10px]" },
81
+ xl: { l: "pl-[12px]", r: "pr-[12px]" }
82
+ };
83
+ return [
84
+ hasLeft ? reducedMap[size].l : pxMap[size].l,
85
+ hasRight ? reducedMap[size].r : pxMap[size].r,
86
+ pyMap[size]
87
+ ];
88
+ }
89
+ const iconSizeSpec = {
90
+ "icon-xs": { btn: "size-[26px]", icon: 16, rounded: "rounded-[4px]" },
91
+ "icon-sm": { btn: "size-[28px]", icon: 18, rounded: "rounded-[6px]" },
92
+ "icon-md": { btn: "size-[32px]", icon: 18, rounded: "rounded-[6px]" },
93
+ "icon-lg": { btn: "size-[36px]", icon: 20, rounded: "rounded-[8px]" },
94
+ "icon-xl": { btn: "size-[40px]", icon: 20, rounded: "rounded-[8px]" }
95
+ };
96
+ function getVariantClasses(variant, isDisabled) {
97
+ if (isDisabled) {
98
+ if (variant === "outline" || variant === "outline-black")
99
+ return "bg-disabled-bg text-disabled border border-border-disabled cursor-not-allowed";
100
+ return "bg-disabled-bg text-disabled cursor-not-allowed";
101
+ }
102
+ if (variant === "outline")
103
+ return "bg-white text-primary-action border border-border hover:bg-hover-bg active:bg-disabled-bg";
104
+ if (variant === "plain")
105
+ return "bg-transparent text-primary-action hover:bg-hover-bg active:bg-disabled-bg";
106
+ if (variant === "outline-black")
107
+ return "bg-white text-foreground border border-border hover:bg-hover-bg";
108
+ if (variant === "plain-black")
109
+ return "bg-transparent text-foreground hover:bg-hover-bg";
110
+ return "bg-primary-action text-white hover:bg-primary-action-hover active:bg-primary-action-active";
111
+ }
112
+ const Button = React.forwardRef(function Button2({
113
+ size = "md",
114
+ variant = "primary",
115
+ children,
116
+ leftIcon,
117
+ rightIcon,
118
+ disabled,
119
+ className = "",
120
+ // Destructure pointer events so we can merge with ghost-icon active handlers
121
+ onPointerDown,
122
+ onPointerUp,
123
+ onPointerLeave,
124
+ ...props
125
+ }, ref) {
126
+ const isDisabled = variant === "disabled" || !!disabled;
127
+ const isGhost = variant === "outline-black" || variant === "plain-black";
128
+ const isIconOnly = size.startsWith("icon-");
129
+ const [ghostPressed, setGhostPressed] = React.useState(false);
130
+ const needsGhostPress = isGhost && isIconOnly && !isDisabled;
131
+ const ghostPressStyle = needsGhostPress && ghostPressed ? { backgroundColor: "var(--disabled-bg)", color: "var(--disabled)" } : void 0;
132
+ const handlePointerDown = needsGhostPress ? (e) => {
133
+ setGhostPressed(true);
134
+ onPointerDown == null ? void 0 : onPointerDown(e);
135
+ } : onPointerDown;
136
+ const handlePointerUp = needsGhostPress ? (e) => {
137
+ setGhostPressed(false);
138
+ onPointerUp == null ? void 0 : onPointerUp(e);
139
+ } : onPointerUp;
140
+ const handlePointerLeave = needsGhostPress ? (e) => {
141
+ setGhostPressed(false);
142
+ onPointerLeave == null ? void 0 : onPointerLeave(e);
143
+ } : onPointerLeave;
144
+ const variantClasses = getVariantClasses(variant, isDisabled);
145
+ const cursorClass = isDisabled ? "cursor-not-allowed" : "cursor-pointer";
146
+ const baseClasses = "inline-flex items-center justify-center font-semibold whitespace-nowrap transition-colors duration-150 select-none";
147
+ if (isIconOnly) {
148
+ const spec = iconSizeSpec[size];
149
+ return /* @__PURE__ */ jsxRuntime.jsx(
150
+ "button",
151
+ {
152
+ ref,
153
+ className: cn(
154
+ baseClasses,
155
+ spec.rounded,
156
+ spec.btn,
157
+ "shrink-0",
158
+ variantClasses,
159
+ cursorClass,
160
+ className
161
+ ),
162
+ style: ghostPressStyle,
163
+ disabled: isDisabled,
164
+ "aria-label": "icon button",
165
+ onPointerDown: handlePointerDown,
166
+ onPointerUp: handlePointerUp,
167
+ onPointerLeave: handlePointerLeave,
168
+ ...props,
169
+ children: /* @__PURE__ */ jsxRuntime.jsx(
170
+ "span",
171
+ {
172
+ className: cn("flex items-center justify-center"),
173
+ "aria-hidden": "true",
174
+ style: { width: spec.icon, height: spec.icon },
175
+ children
176
+ }
177
+ )
178
+ }
179
+ );
180
+ }
181
+ const labelSize = size;
182
+ const hasLeft = Boolean(leftIcon);
183
+ const hasRight = Boolean(rightIcon);
184
+ const paddingParts = getPaddingClasses(labelSize, hasLeft, hasRight);
185
+ return /* @__PURE__ */ jsxRuntime.jsxs(
186
+ "button",
187
+ {
188
+ ref,
189
+ className: cn(
190
+ baseClasses,
191
+ roundedLabelClass[labelSize],
192
+ paddingParts[0],
193
+ paddingParts[1],
194
+ paddingParts[2],
195
+ hasLeft || hasRight ? gapClass[labelSize] : void 0,
196
+ variantClasses,
197
+ cursorClass,
198
+ textSizeClass[labelSize],
199
+ className
200
+ ),
201
+ disabled: isDisabled,
202
+ onPointerDown,
203
+ onPointerUp,
204
+ onPointerLeave,
205
+ ...props,
206
+ children: [
207
+ hasLeft && /* @__PURE__ */ jsxRuntime.jsx(
208
+ "span",
209
+ {
210
+ className: cn(
211
+ "flex items-center justify-center shrink-0 overflow-hidden",
212
+ labelIconSizeClass[labelSize]
213
+ ),
214
+ "aria-hidden": "true",
215
+ children: leftIcon
216
+ }
217
+ ),
218
+ children ?? "Button",
219
+ hasRight && /* @__PURE__ */ jsxRuntime.jsx(
220
+ "span",
221
+ {
222
+ className: cn(
223
+ "flex items-center justify-center shrink-0 overflow-hidden",
224
+ labelIconSizeClass[labelSize]
225
+ ),
226
+ "aria-hidden": "true",
227
+ children: rightIcon
228
+ }
229
+ )
230
+ ]
231
+ }
232
+ );
233
+ });
234
+ Button.displayName = "Button";
235
+ const imgBanner = "";
236
+ function LockIcon() {
237
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Lock, { size: 16, weight: "regular", color: "var(--subtle-text)" });
238
+ }
239
+ function CalendarIcon() {
240
+ return /* @__PURE__ */ jsxRuntime.jsx(react.CalendarBlank, { size: 16, weight: "regular", color: "var(--accent-orange)" });
241
+ }
242
+ function LocationIcon() {
243
+ return /* @__PURE__ */ jsxRuntime.jsx(react.MapPin, { size: 16, weight: "regular", color: "var(--subtle-text)" });
244
+ }
245
+ function AudienceIcon() {
246
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Users, { size: 16, weight: "regular", color: "var(--subtle-text)" });
247
+ }
248
+ const tagConfig = {
249
+ "not-registered": {
250
+ bg: "var(--disabled-bg)",
251
+ text: "var(--disabled)",
252
+ label: "ยังไม่ลงทะเบียน"
253
+ },
254
+ registered: {
255
+ bg: "var(--success-bg)",
256
+ text: "var(--success)",
257
+ label: "ลงทะเบียนแล้ว"
258
+ },
259
+ full: {
260
+ bg: "var(--error-bg)",
261
+ text: "var(--destructive)",
262
+ label: "เต็มแล้ว"
263
+ }
264
+ };
265
+ const Card = React.forwardRef(function Card2({
266
+ variant = "desktop",
267
+ title = "Lorem ipsum dolor sit amet consectetur. Lectus viverraasdasd",
268
+ date = "23 มิ.ย. 2567",
269
+ time = "08.30 - 12.00",
270
+ location = "ณ หอประชุมศาสตราจารย์สังเวียน อินทรวิชัย ชั้น 7 ตลาดหลักทรัพย์แห่งประเทศไทย",
271
+ showLocation = true,
272
+ showAudience = true,
273
+ count = "200/200",
274
+ locked = true,
275
+ tagStatus = "not-registered",
276
+ image,
277
+ className
278
+ }, ref) {
279
+ const widthClass = variant === "desktop" ? "w-[308px]" : variant === "tablet" ? "w-[224px]" : "w-[163px]";
280
+ const padding = variant === "desktop" ? "p-[16px]" : variant === "tablet" ? "p-[12px]" : "p-[10px]";
281
+ const titleGap = variant === "desktop" ? "gap-[6px]" : "gap-[4px]";
282
+ const bannerClass = variant === "desktop" ? "h-[173px]" : "aspect-video w-full";
283
+ const tag = tagConfig[tagStatus];
284
+ const bannerSrc = image ?? imgBanner;
285
+ return /* @__PURE__ */ jsxRuntime.jsxs(
286
+ "div",
287
+ {
288
+ ref,
289
+ className: cn(
290
+ "flex min-h-[120px] flex-col items-start overflow-clip rounded-[8px]",
291
+ "shadow-[0px_0px_2px_0px_rgba(102,102,102,0.16),0px_4px_8px_0px_rgba(102,102,102,0.12)]",
292
+ widthClass,
293
+ className
294
+ ),
295
+ children: [
296
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative w-full shrink-0 overflow-clip", bannerClass), children: [
297
+ /* @__PURE__ */ jsxRuntime.jsx(
298
+ "img",
299
+ {
300
+ alt: "event banner",
301
+ className: "pointer-events-none absolute inset-0 size-full object-cover",
302
+ src: bannerSrc
303
+ }
304
+ ),
305
+ locked && /* @__PURE__ */ jsxRuntime.jsxs(
306
+ "div",
307
+ {
308
+ className: cn(
309
+ "absolute left-[8px] top-[8px] flex items-center gap-[4px] rounded-[4px] bg-hover-bg px-[6px] py-[4px]"
310
+ ),
311
+ children: [
312
+ /* @__PURE__ */ jsxRuntime.jsx(
313
+ "div",
314
+ {
315
+ "aria-hidden": "true",
316
+ className: "pointer-events-none absolute inset-0 rounded-[4px] border border-solid border-border"
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsxRuntime.jsx(LockIcon, {}),
320
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-nowrap text-[12px] leading-[18px] text-subtle-text", children: "Lock" })
321
+ ]
322
+ }
323
+ )
324
+ ] }),
325
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full shrink-0 bg-white", padding), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex w-full flex-col items-start", titleGap), children: [
326
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full flex-col items-start gap-[4px]", children: [
327
+ /* @__PURE__ */ jsxRuntime.jsx(
328
+ "p",
329
+ {
330
+ className: cn(
331
+ "line-clamp-2 w-full overflow-hidden text-ellipsis text-[14px] leading-[20px] text-foreground"
332
+ ),
333
+ children: title
334
+ }
335
+ ),
336
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-[8px]", children: [
337
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-[22px] shrink-0 items-center gap-[4px]", children: [
338
+ /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, {}),
339
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-nowrap text-[12px] leading-[16px] text-accent-orange", children: date })
340
+ ] }),
341
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[14px] w-px shrink-0 bg-border" }),
342
+ /* @__PURE__ */ jsxRuntime.jsx(
343
+ "p",
344
+ {
345
+ className: cn(
346
+ "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap text-[12px] leading-[16px] text-subtle-text"
347
+ ),
348
+ children: time
349
+ }
350
+ )
351
+ ] }),
352
+ showLocation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-[22px] w-full items-center gap-[4px]", children: [
353
+ /* @__PURE__ */ jsxRuntime.jsx(LocationIcon, {}),
354
+ /* @__PURE__ */ jsxRuntime.jsx(
355
+ "p",
356
+ {
357
+ className: cn(
358
+ "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap text-[12px] leading-[16px] text-subtle-text"
359
+ ),
360
+ children: location
361
+ }
362
+ )
363
+ ] }),
364
+ showAudience && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-[22px] w-full items-center gap-[4px]", children: [
365
+ /* @__PURE__ */ jsxRuntime.jsx(AudienceIcon, {}),
366
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 whitespace-nowrap text-[12px] leading-[16px] text-subtle-text", children: "ผู้เข้าร่วม" }),
367
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 whitespace-nowrap text-[12px] leading-[16px] text-primary-action", children: count })
368
+ ] })
369
+ ] }),
370
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-start", children: /* @__PURE__ */ jsxRuntime.jsx(
371
+ "div",
372
+ {
373
+ className: "flex items-center justify-center gap-[2px] overflow-clip rounded-[4px] px-[8px] py-[4px]",
374
+ style: { backgroundColor: tag.bg },
375
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-nowrap text-[12px] leading-[16px]", style: { color: tag.text }, children: tag.label })
376
+ }
377
+ ) })
378
+ ] }) })
379
+ ]
380
+ }
381
+ );
382
+ });
383
+ Card.displayName = "Card";
384
+ const MOBILE_BREAKPOINT = 768;
385
+ function useIsMobile() {
386
+ const [isMobile, setIsMobile] = React__namespace.useState(
387
+ void 0
388
+ );
389
+ React__namespace.useEffect(() => {
390
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
391
+ const onChange = () => {
392
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
393
+ };
394
+ mql.addEventListener("change", onChange);
395
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
396
+ return () => mql.removeEventListener("change", onChange);
397
+ }, []);
398
+ return !!isMobile;
399
+ }
400
+ function Drawer({
401
+ ...props
402
+ }) {
403
+ return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Root, { "data-slot": "drawer", ...props });
404
+ }
405
+ function DrawerTrigger({
406
+ ...props
407
+ }) {
408
+ return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Trigger, { "data-slot": "drawer-trigger", ...props });
409
+ }
410
+ function DrawerPortal({
411
+ ...props
412
+ }) {
413
+ return /* @__PURE__ */ jsxRuntime.jsx(vaul.Drawer.Portal, { "data-slot": "drawer-portal", ...props });
414
+ }
415
+ function DrawerOverlay({
416
+ className,
417
+ ...props
418
+ }) {
419
+ return /* @__PURE__ */ jsxRuntime.jsx(
420
+ vaul.Drawer.Overlay,
421
+ {
422
+ "data-slot": "drawer-overlay",
423
+ className: cn(
424
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
425
+ className
426
+ ),
427
+ ...props
428
+ }
429
+ );
430
+ }
431
+ function DrawerContent({
432
+ className,
433
+ children,
434
+ ...props
435
+ }) {
436
+ return /* @__PURE__ */ jsxRuntime.jsxs(DrawerPortal, { "data-slot": "drawer-portal", children: [
437
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerOverlay, {}),
438
+ /* @__PURE__ */ jsxRuntime.jsxs(
439
+ vaul.Drawer.Content,
440
+ {
441
+ "data-slot": "drawer-content",
442
+ className: cn(
443
+ "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
444
+ "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
445
+ "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
446
+ "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
447
+ "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
448
+ className
449
+ ),
450
+ ...props,
451
+ children: [
452
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
453
+ children
454
+ ]
455
+ }
456
+ )
457
+ ] });
458
+ }
459
+ function DrawerTitle({
460
+ className,
461
+ ...props
462
+ }) {
463
+ return /* @__PURE__ */ jsxRuntime.jsx(
464
+ vaul.Drawer.Title,
465
+ {
466
+ "data-slot": "drawer-title",
467
+ className: cn("text-foreground font-semibold", className),
468
+ ...props
469
+ }
470
+ );
471
+ }
472
+ const THAI_MONTHS_SHORT = [
473
+ "ม.ค.",
474
+ "ก.พ.",
475
+ "มี.ค.",
476
+ "เม.ย.",
477
+ "พ.ค.",
478
+ "มิ.ย.",
479
+ "ก.ค.",
480
+ "ส.ค.",
481
+ "ก.ย.",
482
+ "ต.ค.",
483
+ "พ.ย.",
484
+ "ธ.ค."
485
+ ];
486
+ const THAI_MONTHS_FULL = [
487
+ "มกราคม",
488
+ "กุมภาพันธ์",
489
+ "มีนาคม",
490
+ "เมษายน",
491
+ "พฤษภาคม",
492
+ "มิถุนายน",
493
+ "กรกฎาคม",
494
+ "สิงหาคม",
495
+ "กันยายน",
496
+ "ตุลาคม",
497
+ "พฤศจิกายน",
498
+ "ธันวาคม"
499
+ ];
500
+ const THAI_WEEKDAYS = ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส"];
501
+ function formatThaiDate(date) {
502
+ return `${date.getDate()} ${THAI_MONTHS_SHORT[date.getMonth()]} ${date.getFullYear() + 543}`;
503
+ }
504
+ const DrawerRangeCtx = React.createContext(false);
505
+ const DisabledDatesCtx = React.createContext({});
506
+ const NAV_BTN_CLASS = cn(
507
+ "h-[28px] w-[28px] inline-flex items-center justify-center",
508
+ "rounded-[6px] bg-transparent border border-border cursor-pointer",
509
+ "text-muted-foreground hover:bg-disabled-bg transition-colors duration-100",
510
+ "p-0 outline-none"
511
+ );
512
+ function CustomCaption({
513
+ displayMonth,
514
+ displayIndex
515
+ }) {
516
+ const { goToMonth, previousMonth, nextMonth, displayMonths } = reactDayPicker.useNavigation();
517
+ const isDrawerRange = React.useContext(DrawerRangeCtx);
518
+ const { disabledYears = [] } = React.useContext(DisabledDatesCtx);
519
+ const [view, setView] = React.useState("days");
520
+ const [pickerYear, setPickerYear] = React.useState(
521
+ displayMonth.getFullYear()
522
+ );
523
+ const [yearRangeStart, setYearRangeStart] = React.useState(
524
+ Math.floor(displayMonth.getFullYear() / 12) * 12
525
+ );
526
+ const isFirst = displayMonths[0].getTime() === displayMonth.getTime();
527
+ const isLast = displayMonths[displayMonths.length - 1].getTime() === displayMonth.getTime();
528
+ const idx = displayIndex ?? 0;
529
+ const handlePrevYear = () => {
530
+ const d = new Date(displayMonth);
531
+ d.setFullYear(d.getFullYear() - 1);
532
+ goToMonth(d);
533
+ };
534
+ const handleNextYear = () => {
535
+ const d = new Date(displayMonth);
536
+ d.setFullYear(d.getFullYear() + 1);
537
+ goToMonth(d);
538
+ };
539
+ if (isDrawerRange && !isFirst) {
540
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center pt-3 mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
541
+ "button",
542
+ {
543
+ type: "button",
544
+ className: "text-[14px] leading-[20px] text-foreground font-medium hover:text-primary-action cursor-pointer bg-transparent border-0 outline-none transition-colors",
545
+ onClick: () => {
546
+ setPickerYear(displayMonth.getFullYear());
547
+ setView("months");
548
+ },
549
+ children: [
550
+ THAI_MONTHS_FULL[displayMonth.getMonth()],
551
+ " ",
552
+ displayMonth.getFullYear() + 543
553
+ ]
554
+ }
555
+ ) });
556
+ }
557
+ let headerText;
558
+ let onHeaderClick;
559
+ if (view === "years") {
560
+ headerText = `${yearRangeStart + 543} - ${yearRangeStart + 11 + 543}`;
561
+ onHeaderClick = () => setView("months");
562
+ } else if (view === "months") {
563
+ headerText = `${pickerYear + 543}`;
564
+ onHeaderClick = () => {
565
+ setYearRangeStart(Math.floor(pickerYear / 12) * 12);
566
+ setView("years");
567
+ };
568
+ } else {
569
+ headerText = `${THAI_MONTHS_FULL[displayMonth.getMonth()]} ${displayMonth.getFullYear() + 543}`;
570
+ onHeaderClick = () => {
571
+ setPickerYear(displayMonth.getFullYear());
572
+ setView("months");
573
+ };
574
+ }
575
+ let leftNav = null;
576
+ let rightNav = null;
577
+ if (view === "years") {
578
+ leftNav = /* @__PURE__ */ jsxRuntime.jsx(
579
+ "button",
580
+ {
581
+ type: "button",
582
+ className: NAV_BTN_CLASS,
583
+ onClick: () => setYearRangeStart((s) => s - 12),
584
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16, className: "text-primary-action" })
585
+ }
586
+ );
587
+ rightNav = /* @__PURE__ */ jsxRuntime.jsx(
588
+ "button",
589
+ {
590
+ type: "button",
591
+ className: NAV_BTN_CLASS,
592
+ onClick: () => setYearRangeStart((s) => s + 12),
593
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 16, className: "text-primary-action" })
594
+ }
595
+ );
596
+ } else if (view === "months") {
597
+ leftNav = /* @__PURE__ */ jsxRuntime.jsx(
598
+ "button",
599
+ {
600
+ type: "button",
601
+ className: NAV_BTN_CLASS,
602
+ onClick: () => setPickerYear((y) => y - 1),
603
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16, className: "text-primary-action" })
604
+ }
605
+ );
606
+ rightNav = /* @__PURE__ */ jsxRuntime.jsx(
607
+ "button",
608
+ {
609
+ type: "button",
610
+ className: NAV_BTN_CLASS,
611
+ onClick: () => setPickerYear((y) => y + 1),
612
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 16, className: "text-primary-action" })
613
+ }
614
+ );
615
+ } else if (isDrawerRange) {
616
+ leftNav = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
617
+ /* @__PURE__ */ jsxRuntime.jsx(
618
+ "button",
619
+ {
620
+ type: "button",
621
+ className: NAV_BTN_CLASS,
622
+ onClick: handlePrevYear,
623
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsLeft, { size: 16, className: "text-primary-action" })
624
+ }
625
+ ),
626
+ /* @__PURE__ */ jsxRuntime.jsx(
627
+ "button",
628
+ {
629
+ type: "button",
630
+ className: NAV_BTN_CLASS,
631
+ onClick: () => previousMonth && goToMonth(previousMonth),
632
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16, className: "text-primary-action" })
633
+ }
634
+ )
635
+ ] });
636
+ rightNav = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
637
+ /* @__PURE__ */ jsxRuntime.jsx(
638
+ "button",
639
+ {
640
+ type: "button",
641
+ className: NAV_BTN_CLASS,
642
+ onClick: () => nextMonth && goToMonth(nextMonth),
643
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 16, className: "text-primary-action" })
644
+ }
645
+ ),
646
+ /* @__PURE__ */ jsxRuntime.jsx(
647
+ "button",
648
+ {
649
+ type: "button",
650
+ className: NAV_BTN_CLASS,
651
+ onClick: handleNextYear,
652
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsRight, { size: 16, className: "text-primary-action" })
653
+ }
654
+ )
655
+ ] });
656
+ } else {
657
+ if (isFirst) {
658
+ leftNav = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
659
+ /* @__PURE__ */ jsxRuntime.jsx(
660
+ "button",
661
+ {
662
+ type: "button",
663
+ className: NAV_BTN_CLASS,
664
+ onClick: handlePrevYear,
665
+ children: /* @__PURE__ */ jsxRuntime.jsx(
666
+ lucideReact.ChevronsLeft,
667
+ {
668
+ size: 16,
669
+ className: "text-primary-action"
670
+ }
671
+ )
672
+ }
673
+ ),
674
+ /* @__PURE__ */ jsxRuntime.jsx(
675
+ "button",
676
+ {
677
+ type: "button",
678
+ className: NAV_BTN_CLASS,
679
+ onClick: () => previousMonth && goToMonth(previousMonth),
680
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16, className: "text-primary-action" })
681
+ }
682
+ )
683
+ ] });
684
+ }
685
+ if (isLast) {
686
+ rightNav = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
687
+ /* @__PURE__ */ jsxRuntime.jsx(
688
+ "button",
689
+ {
690
+ type: "button",
691
+ className: NAV_BTN_CLASS,
692
+ onClick: () => nextMonth && goToMonth(nextMonth),
693
+ children: /* @__PURE__ */ jsxRuntime.jsx(
694
+ lucideReact.ChevronRight,
695
+ {
696
+ size: 16,
697
+ className: "text-primary-action"
698
+ }
699
+ )
700
+ }
701
+ ),
702
+ /* @__PURE__ */ jsxRuntime.jsx(
703
+ "button",
704
+ {
705
+ type: "button",
706
+ className: NAV_BTN_CLASS,
707
+ onClick: handleNextYear,
708
+ children: /* @__PURE__ */ jsxRuntime.jsx(
709
+ lucideReact.ChevronsRight,
710
+ {
711
+ size: 16,
712
+ className: "text-primary-action"
713
+ }
714
+ )
715
+ }
716
+ )
717
+ ] });
718
+ }
719
+ }
720
+ const now = /* @__PURE__ */ new Date();
721
+ const gridBtnClass = (active, isToday, isItemDisabled = false) => cn(
722
+ "py-[10px] text-[13px] rounded-[6px] border-0 outline-none transition-colors",
723
+ isItemDisabled ? "bg-disabled-bg text-disabled cursor-not-allowed" : active ? "bg-primary-action text-white cursor-pointer" : isToday ? "bg-primary-action-muted text-primary-action cursor-pointer" : "bg-transparent text-foreground hover:bg-disabled-bg cursor-pointer"
724
+ );
725
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
726
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pt-1 mb-2", children: [
727
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1", children: leftNav }),
728
+ /* @__PURE__ */ jsxRuntime.jsx(
729
+ "button",
730
+ {
731
+ type: "button",
732
+ className: "text-[14px] leading-[20px] text-foreground font-medium hover:text-primary-action cursor-pointer bg-transparent border-0 outline-none transition-colors",
733
+ onClick: onHeaderClick,
734
+ children: headerText
735
+ }
736
+ ),
737
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1", children: rightNav })
738
+ ] }),
739
+ view === "months" && /* @__PURE__ */ jsxRuntime.jsx(
740
+ "div",
741
+ {
742
+ className: "absolute left-0 right-0 z-10 bg-white grid grid-cols-3 gap-1 p-2 content-center",
743
+ style: { top: "42px", bottom: "0" },
744
+ children: THAI_MONTHS_SHORT.map((name, i) => {
745
+ const isCurrent = pickerYear === displayMonth.getFullYear() && i === displayMonth.getMonth();
746
+ const isToday = pickerYear === now.getFullYear() && i === now.getMonth();
747
+ return /* @__PURE__ */ jsxRuntime.jsx(
748
+ "button",
749
+ {
750
+ type: "button",
751
+ onClick: () => {
752
+ goToMonth(new Date(pickerYear, i - idx));
753
+ setView("days");
754
+ },
755
+ className: gridBtnClass(isCurrent, isToday),
756
+ children: name
757
+ },
758
+ i
759
+ );
760
+ })
761
+ }
762
+ ),
763
+ view === "years" && /* @__PURE__ */ jsxRuntime.jsx(
764
+ "div",
765
+ {
766
+ className: "absolute left-0 right-0 z-10 bg-white grid grid-cols-3 gap-1 p-2 content-center",
767
+ style: { top: "42px", bottom: "0" },
768
+ children: Array.from({ length: 12 }, (_, i) => {
769
+ const year = yearRangeStart + i;
770
+ const isCurrent = year === displayMonth.getFullYear();
771
+ const isToday = year === now.getFullYear();
772
+ const isYearDisabled = disabledYears.includes(year);
773
+ return /* @__PURE__ */ jsxRuntime.jsx(
774
+ "button",
775
+ {
776
+ type: "button",
777
+ disabled: isYearDisabled,
778
+ onClick: () => {
779
+ if (isYearDisabled) return;
780
+ setPickerYear(year);
781
+ setView("months");
782
+ },
783
+ className: gridBtnClass(isCurrent, isToday, isYearDisabled),
784
+ children: year + 543
785
+ },
786
+ year
787
+ );
788
+ })
789
+ }
790
+ )
791
+ ] });
792
+ }
793
+ const DAY_PICKER_CLASSES = {
794
+ months: "flex flex-col sm:flex-row sm:gap-6",
795
+ month: "space-y-2 relative",
796
+ caption: "flex justify-center pt-1 relative items-center mb-2",
797
+ caption_label: "text-[14px] leading-[20px] text-foreground",
798
+ nav: "hidden",
799
+ nav_button: cn(
800
+ "h-[28px] w-[28px] inline-flex items-center justify-center",
801
+ "rounded-[6px] bg-transparent border border-border cursor-pointer",
802
+ "text-muted-foreground hover:bg-disabled-bg transition-colors duration-100",
803
+ "p-0 outline-none"
804
+ ),
805
+ nav_button_previous: "absolute left-1",
806
+ nav_button_next: "absolute right-1",
807
+ table: "w-full border-collapse",
808
+ head_row: "flex",
809
+ head_cell: "w-[36px] h-[32px] inline-flex items-center justify-center text-[12px] text-disabled font-normal",
810
+ row: "flex w-full mt-1",
811
+ cell: cn(
812
+ "h-[36px] w-[36px] text-center p-0 relative overflow-hidden",
813
+ "[&:has(.day-range-middle)]:bg-range-bg",
814
+ "[&:has(.day-range-start)]:bg-range-bg",
815
+ "[&:has(.day-range-end)]:bg-range-bg",
816
+ "[&:has(.day-range-start)]:rounded-l-[6px]",
817
+ "[&:has(.day-range-end)]:rounded-r-[6px]",
818
+ "[&:first-child:has(.day-range-middle)]:rounded-l-[6px]",
819
+ "[&:last-child:has(.day-range-middle)]:rounded-r-[6px]",
820
+ "[&:first-child:has(.day-range-end)]:rounded-l-[6px]",
821
+ "[&:last-child:has(.day-range-start)]:rounded-r-[6px]",
822
+ "focus-within:relative focus-within:z-20"
823
+ ),
824
+ day: cn(
825
+ "h-[36px] w-[36px] inline-flex items-center justify-center",
826
+ "text-[14px] text-foreground rounded-[6px]",
827
+ "border-0 bg-transparent cursor-pointer",
828
+ "hover:bg-disabled-bg transition-colors duration-100",
829
+ "outline-none aria-selected:opacity-100 p-0"
830
+ ),
831
+ day_range_start: "day-range-start !bg-primary-action !text-white !rounded-l-[6px] !rounded-r-none",
832
+ day_range_end: "day-range-end !bg-primary-action !text-white !rounded-r-[6px] !rounded-l-none",
833
+ day_selected: "!bg-primary-action text-white hover:!bg-primary-action focus:!bg-primary-action rounded-[6px]",
834
+ day_today: "[&:not([aria-selected=true])]:!bg-primary-action-light [&:not([aria-selected=true])]:text-foreground rounded-[6px]",
835
+ day_outside: "day-outside text-disabled opacity-50 aria-selected:bg-transparent aria-selected:opacity-30",
836
+ day_disabled: "text-disabled opacity-50 cursor-not-allowed",
837
+ day_range_middle: "day-range-middle !bg-range-bg !text-range-text !rounded-none",
838
+ day_hidden: "invisible"
839
+ };
840
+ const DRAWER_DAY_PICKER_CLASSES = {
841
+ ...DAY_PICKER_CLASSES,
842
+ months: "flex flex-col gap-6 w-full",
843
+ month: "space-y-2 relative w-full",
844
+ table: "w-full border-collapse table-fixed",
845
+ head_row: "flex w-full",
846
+ head_cell: "flex-1 h-[40px] inline-flex items-center justify-center text-[13px] text-disabled font-normal",
847
+ row: "flex w-full mt-1",
848
+ cell: cn(
849
+ "h-[44px] flex-1 text-center p-0 relative overflow-hidden",
850
+ "[&:has(.day-range-middle)]:bg-range-bg",
851
+ "[&:has(.day-range-start)]:bg-range-bg",
852
+ "[&:has(.day-range-end)]:bg-range-bg",
853
+ "[&:has(.day-range-start)]:rounded-l-[8px]",
854
+ "[&:has(.day-range-end)]:rounded-r-[8px]",
855
+ "[&:first-child:has(.day-range-middle)]:rounded-l-[8px]",
856
+ "[&:last-child:has(.day-range-middle)]:rounded-r-[8px]",
857
+ "[&:first-child:has(.day-range-end)]:rounded-l-[8px]",
858
+ "[&:last-child:has(.day-range-start)]:rounded-r-[8px]",
859
+ "focus-within:relative focus-within:z-20"
860
+ ),
861
+ day: cn(
862
+ "h-[44px] w-full inline-flex items-center justify-center",
863
+ "text-[16px] text-foreground rounded-[8px]",
864
+ "border-0 bg-transparent cursor-pointer",
865
+ "hover:bg-disabled-bg transition-colors duration-100",
866
+ "outline-none aria-selected:opacity-100 p-0"
867
+ )
868
+ };
869
+ const DATE_ITEM_H = 40;
870
+ const DATE_DRUM_H = 200;
871
+ const DATE_SPACER = (DATE_DRUM_H - DATE_ITEM_H) / 2;
872
+ const SCROLL_YEAR_START = 1950;
873
+ const SCROLL_YEAR_END = 2060;
874
+ const YEAR_ITEMS = Array.from(
875
+ { length: SCROLL_YEAR_END - SCROLL_YEAR_START + 1 },
876
+ (_, i) => String(SCROLL_YEAR_START + i)
877
+ );
878
+ function getDaysInMonth(year, month) {
879
+ return new Date(year, month, 0).getDate();
880
+ }
881
+ function dateToScrollValue(date) {
882
+ const d = date ?? /* @__PURE__ */ new Date();
883
+ return {
884
+ year: d.getFullYear(),
885
+ month: d.getMonth() + 1,
886
+ day: d.getDate()
887
+ };
888
+ }
889
+ function scrollValueToDate(v) {
890
+ return new Date(v.year, v.month - 1, v.day);
891
+ }
892
+ function DateScrollColumn({
893
+ items,
894
+ selectedIndex,
895
+ onChange,
896
+ flex = 1
897
+ }) {
898
+ const containerRef = React.useRef(null);
899
+ const debounceRef = React.useRef(void 0);
900
+ const [localIndex, setLocalIndex] = React.useState(selectedIndex);
901
+ const isUserScrolling = React.useRef(false);
902
+ React.useEffect(() => {
903
+ const el = containerRef.current;
904
+ if (el) {
905
+ el.scrollTop = selectedIndex * DATE_ITEM_H;
906
+ setLocalIndex(selectedIndex);
907
+ }
908
+ }, []);
909
+ React.useEffect(() => {
910
+ if (isUserScrolling.current) return;
911
+ const el = containerRef.current;
912
+ if (el) {
913
+ el.scrollTo({
914
+ top: selectedIndex * DATE_ITEM_H,
915
+ behavior: "smooth"
916
+ });
917
+ setLocalIndex(selectedIndex);
918
+ }
919
+ }, [selectedIndex]);
920
+ const handleScroll = React.useCallback(() => {
921
+ isUserScrolling.current = true;
922
+ const el = containerRef.current;
923
+ if (!el) return;
924
+ const rawIdx = el.scrollTop / DATE_ITEM_H;
925
+ const idx = Math.max(
926
+ 0,
927
+ Math.min(Math.round(rawIdx), items.length - 1)
928
+ );
929
+ setLocalIndex(idx);
930
+ clearTimeout(debounceRef.current);
931
+ debounceRef.current = setTimeout(() => {
932
+ isUserScrolling.current = false;
933
+ const finalIdx = Math.max(
934
+ 0,
935
+ Math.min(
936
+ Math.round(el.scrollTop / DATE_ITEM_H),
937
+ items.length - 1
938
+ )
939
+ );
940
+ el.scrollTo({
941
+ top: finalIdx * DATE_ITEM_H,
942
+ behavior: "smooth"
943
+ });
944
+ onChange(finalIdx);
945
+ setLocalIndex(finalIdx);
946
+ }, 120);
947
+ }, [items.length, onChange]);
948
+ const handleItemClick = React.useCallback(
949
+ (idx) => {
950
+ var _a;
951
+ setLocalIndex(idx);
952
+ onChange(idx);
953
+ (_a = containerRef.current) == null ? void 0 : _a.scrollTo({
954
+ top: idx * DATE_ITEM_H,
955
+ behavior: "smooth"
956
+ });
957
+ },
958
+ [onChange]
959
+ );
960
+ return /* @__PURE__ */ jsxRuntime.jsxs(
961
+ "div",
962
+ {
963
+ ref: containerRef,
964
+ className: "relative [&::-webkit-scrollbar]:hidden min-w-0",
965
+ style: {
966
+ flex,
967
+ height: DATE_DRUM_H,
968
+ overflowY: "scroll",
969
+ scrollSnapType: "y mandatory",
970
+ scrollbarWidth: "none"
971
+ },
972
+ onScroll: handleScroll,
973
+ children: [
974
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: DATE_SPACER, flexShrink: 0 } }),
975
+ items.map((label, idx) => {
976
+ const isSel = idx === localIndex;
977
+ return /* @__PURE__ */ jsxRuntime.jsx(
978
+ "div",
979
+ {
980
+ style: {
981
+ height: DATE_ITEM_H,
982
+ scrollSnapAlign: "center"
983
+ },
984
+ className: "flex items-center justify-center cursor-pointer select-none",
985
+ onClick: () => handleItemClick(idx),
986
+ children: /* @__PURE__ */ jsxRuntime.jsx(
987
+ "span",
988
+ {
989
+ style: {
990
+ fontSize: isSel ? 32 : 14,
991
+ lineHeight: 1,
992
+ color: isSel ? "var(--foreground)" : "var(--disabled)",
993
+ transition: "font-size 0.1s, color 0.1s",
994
+ whiteSpace: "nowrap"
995
+ },
996
+ children: label
997
+ }
998
+ )
999
+ },
1000
+ idx
1001
+ );
1002
+ }),
1003
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: DATE_SPACER, flexShrink: 0 } })
1004
+ ]
1005
+ }
1006
+ );
1007
+ }
1008
+ function ScrollDatePickerContent({
1009
+ value,
1010
+ onChange
1011
+ }) {
1012
+ const daysInMonth = getDaysInMonth(value.year, value.month);
1013
+ const dayItems = Array.from(
1014
+ { length: daysInMonth },
1015
+ (_, i) => String(i + 1).padStart(2, "0")
1016
+ );
1017
+ const monthIndex = value.month - 1;
1018
+ const dayIndex = Math.min(value.day, daysInMonth) - 1;
1019
+ const yearIndex = value.year - SCROLL_YEAR_START;
1020
+ const handleMonthChange = React.useCallback(
1021
+ (idx) => {
1022
+ const newMonth = idx + 1;
1023
+ const maxDay = getDaysInMonth(value.year, newMonth);
1024
+ onChange({
1025
+ ...value,
1026
+ month: newMonth,
1027
+ day: Math.min(value.day, maxDay)
1028
+ });
1029
+ },
1030
+ [value, onChange]
1031
+ );
1032
+ const handleDayChange = React.useCallback(
1033
+ (idx) => {
1034
+ onChange({ ...value, day: idx + 1 });
1035
+ },
1036
+ [value, onChange]
1037
+ );
1038
+ const handleYearChange = React.useCallback(
1039
+ (idx) => {
1040
+ const newYear = SCROLL_YEAR_START + idx;
1041
+ const maxDay = getDaysInMonth(newYear, value.month);
1042
+ onChange({
1043
+ ...value,
1044
+ year: newYear,
1045
+ day: Math.min(value.day, maxDay)
1046
+ });
1047
+ },
1048
+ [value, onChange]
1049
+ );
1050
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
1051
+ /* @__PURE__ */ jsxRuntime.jsx(
1052
+ "div",
1053
+ {
1054
+ className: "absolute left-0 right-0 rounded-[4px] bg-selected-light-bg pointer-events-none",
1055
+ style: {
1056
+ top: "50%",
1057
+ transform: "translateY(-50%)",
1058
+ height: DATE_ITEM_H,
1059
+ zIndex: 0
1060
+ }
1061
+ }
1062
+ ),
1063
+ /* @__PURE__ */ jsxRuntime.jsxs(
1064
+ "div",
1065
+ {
1066
+ className: "relative flex gap-[8px] items-center w-full min-w-[340px] px-4",
1067
+ style: { zIndex: 1 },
1068
+ children: [
1069
+ /* @__PURE__ */ jsxRuntime.jsx(
1070
+ DateScrollColumn,
1071
+ {
1072
+ items: THAI_MONTHS_FULL,
1073
+ selectedIndex: monthIndex,
1074
+ onChange: handleMonthChange,
1075
+ flex: 108
1076
+ }
1077
+ ),
1078
+ /* @__PURE__ */ jsxRuntime.jsx(
1079
+ DateScrollColumn,
1080
+ {
1081
+ items: dayItems,
1082
+ selectedIndex: dayIndex,
1083
+ onChange: handleDayChange,
1084
+ flex: 38
1085
+ }
1086
+ ),
1087
+ /* @__PURE__ */ jsxRuntime.jsx(
1088
+ DateScrollColumn,
1089
+ {
1090
+ items: YEAR_ITEMS,
1091
+ selectedIndex: yearIndex,
1092
+ onChange: handleYearChange,
1093
+ flex: 85
1094
+ }
1095
+ )
1096
+ ]
1097
+ }
1098
+ )
1099
+ ] });
1100
+ }
1101
+ const DateInput = React.forwardRef(
1102
+ function DateInput2({
1103
+ mode = "single",
1104
+ placeholder = "Text label",
1105
+ required = false,
1106
+ forceState,
1107
+ errorMessage = "Error message",
1108
+ helperText,
1109
+ pickerVariant = "calendar",
1110
+ value,
1111
+ onChange,
1112
+ dateRange,
1113
+ onRangeChange,
1114
+ disabledYears,
1115
+ className
1116
+ }, ref) {
1117
+ const [open, setOpen] = React.useState(false);
1118
+ const [internalDate, setInternalDate] = React.useState(void 0);
1119
+ const [internalRange, setInternalRange] = React.useState(void 0);
1120
+ const isMobile = useIsMobile();
1121
+ const [draftDate, setDraftDate] = React.useState(
1122
+ void 0
1123
+ );
1124
+ const [draftRange, setDraftRange] = React.useState(void 0);
1125
+ const [draftScrollValue, setDraftScrollValue] = React.useState(dateToScrollValue(void 0));
1126
+ const isStatic = Boolean(forceState);
1127
+ const isDisabled = forceState === "disabled";
1128
+ const currentDate = value !== void 0 ? value : internalDate;
1129
+ const currentRange = dateRange !== void 0 ? dateRange : internalRange;
1130
+ const state = forceState ?? (open ? "focus" : "default");
1131
+ const isError = state === "error";
1132
+ const isFocus = state === "focus";
1133
+ const isFilled = mode === "single" ? Boolean(currentDate) : Boolean(currentRange == null ? void 0 : currentRange.from);
1134
+ const bgClass = isDisabled ? "bg-disabled-bg" : "bg-white";
1135
+ const labelColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
1136
+ const valueColor = isDisabled ? "var(--disabled)" : "var(--foreground)";
1137
+ const iconColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
1138
+ const minusColor = isDisabled ? "var(--disabled)" : "var(--foreground)";
1139
+ const asteriskColorEmpty = isDisabled ? "var(--disabled)" : "var(--error-dark)";
1140
+ const asteriskColorFilled = isDisabled ? "var(--disabled)" : "var(--error-dark)";
1141
+ const borderInset = isFocus || isError ? "-1px" : "0px";
1142
+ const borderRad = isFocus || isError ? "9px" : "8px";
1143
+ const borderColor = isDisabled ? "var(--border-disabled)" : isError ? "var(--destructive)" : isFocus ? "var(--primary-action)" : "var(--border)";
1144
+ const showBelow = isError || Boolean(helperText);
1145
+ const leftText = isError ? errorMessage : helperText ?? "";
1146
+ const leftColor = isError ? "var(--error-dark)" : "var(--muted-foreground)";
1147
+ const handleDateSelect = (date) => {
1148
+ setDraftDate(date);
1149
+ };
1150
+ const handleRangeSelect = (range) => {
1151
+ setDraftRange(range);
1152
+ };
1153
+ const handleCancel = () => setOpen(false);
1154
+ const handleConfirm = () => {
1155
+ if (pickerVariant === "scroll") {
1156
+ const d = scrollValueToDate(draftScrollValue);
1157
+ if (value === void 0) setInternalDate(d);
1158
+ onChange == null ? void 0 : onChange(d);
1159
+ } else if (mode === "single") {
1160
+ if (value === void 0) setInternalDate(draftDate);
1161
+ onChange == null ? void 0 : onChange(draftDate);
1162
+ } else {
1163
+ if (dateRange === void 0) setInternalRange(draftRange);
1164
+ onRangeChange == null ? void 0 : onRangeChange(draftRange);
1165
+ }
1166
+ setOpen(false);
1167
+ };
1168
+ const renderContent = () => {
1169
+ if (isFilled) {
1170
+ const floatingLabel = required ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[2px] items-center relative shrink-0 w-full", children: [
1171
+ /* @__PURE__ */ jsxRuntime.jsx(
1172
+ "p",
1173
+ {
1174
+ className: "leading-[16px] not-italic relative shrink-0 text-[12px] whitespace-nowrap",
1175
+ style: { color: labelColor },
1176
+ children: placeholder
1177
+ }
1178
+ ),
1179
+ /* @__PURE__ */ jsxRuntime.jsx(
1180
+ "p",
1181
+ {
1182
+ className: "leading-[1.5] not-italic relative shrink-0 text-[9px] w-[7px]",
1183
+ style: { color: asteriskColorFilled },
1184
+ children: "*"
1185
+ }
1186
+ )
1187
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1188
+ "p",
1189
+ {
1190
+ className: "leading-[16px] not-italic relative shrink-0 text-[12px] w-full",
1191
+ style: { color: labelColor },
1192
+ children: placeholder
1193
+ }
1194
+ );
1195
+ const valueRow = mode === "single" && currentDate ? /* @__PURE__ */ jsxRuntime.jsx(
1196
+ "p",
1197
+ {
1198
+ className: "leading-[20px] not-italic relative shrink-0 text-[16px] w-full",
1199
+ style: { color: valueColor },
1200
+ children: formatThaiDate(currentDate)
1201
+ }
1202
+ ) : mode === "range" && (currentRange == null ? void 0 : currentRange.from) ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[8px] items-center relative shrink-0 w-full", children: [
1203
+ /* @__PURE__ */ jsxRuntime.jsx(
1204
+ "p",
1205
+ {
1206
+ className: "leading-[20px] not-italic relative shrink-0 text-[16px] whitespace-nowrap",
1207
+ style: { color: valueColor },
1208
+ children: formatThaiDate(currentRange.from)
1209
+ }
1210
+ ),
1211
+ /* @__PURE__ */ jsxRuntime.jsx(react.Minus, { size: 20, weight: "bold", color: minusColor }),
1212
+ /* @__PURE__ */ jsxRuntime.jsx(
1213
+ "p",
1214
+ {
1215
+ className: "leading-[20px] not-italic relative shrink-0 text-[16px] whitespace-nowrap",
1216
+ style: { color: valueColor },
1217
+ children: currentRange.to ? formatThaiDate(currentRange.to) : "..."
1218
+ }
1219
+ )
1220
+ ] }) : null;
1221
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "content-stretch flex flex-1 flex-col items-center justify-center min-h-px min-w-px relative", children: [
1222
+ floatingLabel,
1223
+ valueRow
1224
+ ] });
1225
+ }
1226
+ if (required) {
1227
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "content-stretch flex flex-1 min-w-px min-h-px gap-[2px] items-center relative", children: [
1228
+ /* @__PURE__ */ jsxRuntime.jsx(
1229
+ "p",
1230
+ {
1231
+ className: "leading-[20px] not-italic relative shrink-0 text-[16px] whitespace-nowrap",
1232
+ style: { color: labelColor },
1233
+ children: placeholder
1234
+ }
1235
+ ),
1236
+ /* @__PURE__ */ jsxRuntime.jsx(
1237
+ "p",
1238
+ {
1239
+ className: "font-normal h-full leading-[1.5] not-italic relative shrink-0 text-[12px] w-[7px]",
1240
+ style: { color: asteriskColorEmpty },
1241
+ children: "*"
1242
+ }
1243
+ )
1244
+ ] });
1245
+ }
1246
+ return /* @__PURE__ */ jsxRuntime.jsx(
1247
+ "p",
1248
+ {
1249
+ className: "flex-1 min-w-0 min-h-px text-[16px] leading-[20px] not-italic overflow-hidden text-ellipsis whitespace-nowrap relative",
1250
+ style: { color: labelColor },
1251
+ children: placeholder
1252
+ }
1253
+ );
1254
+ };
1255
+ const triggerInner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1256
+ /* @__PURE__ */ jsxRuntime.jsx(
1257
+ "div",
1258
+ {
1259
+ "aria-hidden": "true",
1260
+ className: "absolute pointer-events-none border border-solid",
1261
+ style: {
1262
+ inset: borderInset,
1263
+ borderRadius: borderRad,
1264
+ borderColor
1265
+ }
1266
+ }
1267
+ ),
1268
+ renderContent(),
1269
+ /* @__PURE__ */ jsxRuntime.jsx(
1270
+ "div",
1271
+ {
1272
+ className: cn(
1273
+ "flex flex-row items-center shrink-0",
1274
+ isFilled && "self-stretch"
1275
+ ),
1276
+ children: isFilled ? /* @__PURE__ */ jsxRuntime.jsx(react.CalendarBlank, { size: 22, weight: "regular", color: iconColor }) : /* @__PURE__ */ jsxRuntime.jsx(react.CalendarBlank, { size: 24, weight: "regular", color: iconColor })
1277
+ }
1278
+ )
1279
+ ] });
1280
+ const triggerBaseClasses = cn(
1281
+ "relative flex gap-[8px] items-center rounded-[8px]",
1282
+ bgClass,
1283
+ "px-[14px]",
1284
+ isFilled ? "py-[6px]" : "py-[12px]",
1285
+ "w-full"
1286
+ );
1287
+ const belowMessage = showBelow && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start px-[4px] text-[12px] leading-[16px]", children: /* @__PURE__ */ jsxRuntime.jsx(
1288
+ "span",
1289
+ {
1290
+ className: "flex-1 min-w-0",
1291
+ style: { color: leftColor },
1292
+ children: leftText
1293
+ }
1294
+ ) });
1295
+ if (isStatic) {
1296
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1297
+ "div",
1298
+ {
1299
+ ref,
1300
+ className: cn("flex flex-col gap-[4px] w-full", className),
1301
+ children: [
1302
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: triggerBaseClasses, children: triggerInner }),
1303
+ belowMessage
1304
+ ]
1305
+ }
1306
+ );
1307
+ }
1308
+ const isScrollVariant = pickerVariant === "scroll" && mode === "single";
1309
+ const pickerClasses = isMobile ? DRAWER_DAY_PICKER_CLASSES : DAY_PICKER_CLASSES;
1310
+ const pickerInner = isScrollVariant ? /* @__PURE__ */ jsxRuntime.jsx(
1311
+ ScrollDatePickerContent,
1312
+ {
1313
+ value: draftScrollValue,
1314
+ onChange: setDraftScrollValue
1315
+ }
1316
+ ) : mode === "single" ? /* @__PURE__ */ jsxRuntime.jsx(
1317
+ reactDayPicker.DayPicker,
1318
+ {
1319
+ mode: "single",
1320
+ selected: draftDate,
1321
+ onSelect: handleDateSelect,
1322
+ showOutsideDays: true,
1323
+ classNames: pickerClasses,
1324
+ formatters: {
1325
+ formatWeekdayName: (date) => THAI_WEEKDAYS[date.getDay()]
1326
+ },
1327
+ components: { Caption: CustomCaption }
1328
+ }
1329
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1330
+ reactDayPicker.DayPicker,
1331
+ {
1332
+ mode: "range",
1333
+ selected: draftRange,
1334
+ onSelect: handleRangeSelect,
1335
+ numberOfMonths: 2,
1336
+ showOutsideDays: true,
1337
+ classNames: pickerClasses,
1338
+ formatters: {
1339
+ formatWeekdayName: (date) => THAI_WEEKDAYS[date.getDay()]
1340
+ },
1341
+ components: { Caption: CustomCaption }
1342
+ }
1343
+ );
1344
+ const calendarContent = /* @__PURE__ */ jsxRuntime.jsx(DisabledDatesCtx.Provider, { value: { disabledYears }, children: pickerInner });
1345
+ const actionButtons = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[12px] items-center pt-[12px]", children: [
1346
+ /* @__PURE__ */ jsxRuntime.jsx(
1347
+ Button,
1348
+ {
1349
+ variant: "outline",
1350
+ size: "lg",
1351
+ className: "flex-1",
1352
+ onClick: handleCancel,
1353
+ children: "ยกเลิก"
1354
+ }
1355
+ ),
1356
+ /* @__PURE__ */ jsxRuntime.jsx(
1357
+ Button,
1358
+ {
1359
+ variant: "primary",
1360
+ size: "lg",
1361
+ className: "flex-1",
1362
+ onClick: handleConfirm,
1363
+ children: "ตกลง"
1364
+ }
1365
+ )
1366
+ ] });
1367
+ const triggerButton = /* @__PURE__ */ jsxRuntime.jsx(
1368
+ "button",
1369
+ {
1370
+ type: "button",
1371
+ disabled: isDisabled,
1372
+ className: cn(triggerBaseClasses, "text-left cursor-pointer disabled:cursor-default"),
1373
+ children: triggerInner
1374
+ }
1375
+ );
1376
+ const handleOpenChange = (o) => {
1377
+ if (!isDisabled) {
1378
+ if (o) {
1379
+ setDraftDate(currentDate);
1380
+ setDraftRange(currentRange);
1381
+ setDraftScrollValue(dateToScrollValue(currentDate));
1382
+ }
1383
+ setOpen(o);
1384
+ }
1385
+ };
1386
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1387
+ "div",
1388
+ {
1389
+ ref,
1390
+ className: cn("flex flex-col gap-[4px] w-full", className),
1391
+ children: [
1392
+ isMobile ? /* @__PURE__ */ jsxRuntime.jsxs(Drawer, { open, onOpenChange: handleOpenChange, children: [
1393
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerTrigger, { asChild: true, children: triggerButton }),
1394
+ /* @__PURE__ */ jsxRuntime.jsxs(DrawerContent, { children: [
1395
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { className: "sr-only", children: "เลือกวันที่" }),
1396
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-auto px-4 pt-2 pb-8 w-full", children: [
1397
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerRangeCtx.Provider, { value: mode === "range", children: calendarContent }),
1398
+ actionButtons
1399
+ ] })
1400
+ ] })
1401
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
1402
+ Popover__namespace.Root,
1403
+ {
1404
+ open,
1405
+ onOpenChange: handleOpenChange,
1406
+ children: [
1407
+ /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Trigger, { asChild: true, children: triggerButton }),
1408
+ /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
1409
+ Popover__namespace.Content,
1410
+ {
1411
+ align: "start",
1412
+ sideOffset: 4,
1413
+ className: "z-50 rounded-[8px] bg-white p-3 outline-none",
1414
+ style: {
1415
+ boxShadow: "0px 20px 25px -5px rgba(0,0,0,0.1), 0px 8px 10px -6px rgba(0,0,0,0.1)",
1416
+ border: "1px solid var(--border)"
1417
+ },
1418
+ onOpenAutoFocus: (e) => e.preventDefault(),
1419
+ children: [
1420
+ calendarContent,
1421
+ actionButtons
1422
+ ]
1423
+ }
1424
+ ) })
1425
+ ]
1426
+ }
1427
+ ),
1428
+ belowMessage
1429
+ ]
1430
+ }
1431
+ );
1432
+ }
1433
+ );
1434
+ DateInput.displayName = "DateInput";
1435
+ function ChevronDownIcon$1({ className }) {
1436
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 9L11 13L15 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1437
+ }
1438
+ function ChevronUpIcon$1({ className }) {
1439
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 13L11 9L15 13", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1440
+ }
1441
+ const Dropdown = React.forwardRef(
1442
+ ({
1443
+ placeholder = "Text label",
1444
+ label,
1445
+ required = false,
1446
+ forceState,
1447
+ errorMessage = "Error message",
1448
+ helperText,
1449
+ value,
1450
+ onChange,
1451
+ options = [],
1452
+ className
1453
+ }, ref) => {
1454
+ var _a;
1455
+ const [open, setOpen] = React.useState(false);
1456
+ const [internalValue, setInternalValue] = React.useState("");
1457
+ const [search, setSearch] = React.useState("");
1458
+ const containerRef = React.useRef(null);
1459
+ const inputRef = React.useRef(null);
1460
+ const controlled = value !== void 0;
1461
+ const currentValue = controlled ? value : internalValue;
1462
+ const isDisabled = forceState === "disabled";
1463
+ const selectedLabel = (_a = options.find((o) => o.value === currentValue)) == null ? void 0 : _a.label;
1464
+ const isFilled = Boolean(selectedLabel);
1465
+ const state = forceState ?? (open ? "focus" : "default");
1466
+ const isError = state === "error";
1467
+ const isFocus = state === "focus";
1468
+ const bg = isDisabled ? "bg-disabled-bg" : "bg-white";
1469
+ const labelColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
1470
+ const filledColor = isDisabled ? "var(--disabled)" : "var(--foreground)";
1471
+ const caretClassName = isDisabled ? "text-disabled" : "text-muted-foreground";
1472
+ const hasExternalLabel = Boolean(label);
1473
+ const borderInset = isFocus || isError ? "-1px" : "0px";
1474
+ const borderRad = isFocus || isError ? "9px" : "8px";
1475
+ const borderColor = isDisabled ? "var(--border-disabled)" : isError ? "var(--destructive)" : isFocus ? "var(--primary-action)" : "var(--border)";
1476
+ const showBelow = isError || Boolean(helperText);
1477
+ const leftText = isError ? errorMessage : helperText ?? "";
1478
+ const leftColor = isError ? "var(--destructive)" : "var(--muted-foreground)";
1479
+ const filteredOptions = search.trim() ? options.filter(
1480
+ (o) => o.label.toLowerCase().includes(search.trim().toLowerCase())
1481
+ ) : options;
1482
+ React.useEffect(() => {
1483
+ if (open && inputRef.current) {
1484
+ inputRef.current.focus();
1485
+ }
1486
+ }, [open]);
1487
+ React.useEffect(() => {
1488
+ if (!open) setSearch("");
1489
+ }, [open]);
1490
+ React.useEffect(() => {
1491
+ if (!open) return;
1492
+ const handler = (e) => {
1493
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
1494
+ setOpen(false);
1495
+ }
1496
+ };
1497
+ document.addEventListener("mousedown", handler);
1498
+ return () => document.removeEventListener("mousedown", handler);
1499
+ }, [open]);
1500
+ const handleSelect = (val) => {
1501
+ if (!controlled) setInternalValue(val);
1502
+ onChange == null ? void 0 : onChange(val);
1503
+ setOpen(false);
1504
+ };
1505
+ const handleToggle = () => {
1506
+ if (isDisabled) return;
1507
+ if (forceState) return;
1508
+ setOpen((prev) => !prev);
1509
+ };
1510
+ const handleInputKeyDown = (e) => {
1511
+ if (e.key === "Escape") {
1512
+ setOpen(false);
1513
+ } else if (e.key === "Enter" && filteredOptions.length === 1) {
1514
+ handleSelect(filteredOptions[0].value);
1515
+ }
1516
+ };
1517
+ const Asterisk = ({ color }) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "leading-[16px] not-italic text-[12px]", style: { color }, children: [
1518
+ " ",
1519
+ "*"
1520
+ ] });
1521
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1522
+ "div",
1523
+ {
1524
+ ref: (node) => {
1525
+ containerRef.current = node;
1526
+ if (typeof ref === "function") ref(node);
1527
+ else if (ref) ref.current = node;
1528
+ },
1529
+ className: cn("flex flex-col gap-[4px] w-full", className),
1530
+ children: [
1531
+ label && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative shrink-0 w-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start px-[4px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "leading-[20px] not-italic relative shrink-0 text-foreground text-[14px] font-bold whitespace-nowrap", children: label }) }) }),
1532
+ /* @__PURE__ */ jsxRuntime.jsxs(
1533
+ "div",
1534
+ {
1535
+ onClick: handleToggle,
1536
+ className: cn(
1537
+ "relative flex gap-[8px] items-center rounded-[8px] px-[14px]",
1538
+ bg,
1539
+ hasExternalLabel ? "h-[38px]" : isFilled && !open ? "py-[6px]" : "p-[14px]",
1540
+ !isDisabled && !forceState && "cursor-pointer"
1541
+ ),
1542
+ children: [
1543
+ /* @__PURE__ */ jsxRuntime.jsx(
1544
+ "div",
1545
+ {
1546
+ "aria-hidden": "true",
1547
+ className: "absolute pointer-events-none border border-solid",
1548
+ style: { inset: borderInset, borderRadius: borderRad, borderColor }
1549
+ }
1550
+ ),
1551
+ open && !forceState ? (
1552
+ /* ─── Open: search input ─── */
1553
+ hasExternalLabel ? (
1554
+ /* External label + open → single-line search input */
1555
+ /* @__PURE__ */ jsxRuntime.jsx(
1556
+ "input",
1557
+ {
1558
+ ref: inputRef,
1559
+ type: "text",
1560
+ value: search,
1561
+ onChange: (e) => setSearch(e.target.value),
1562
+ onKeyDown: handleInputKeyDown,
1563
+ placeholder: isFilled ? selectedLabel : placeholder + (required ? " *" : ""),
1564
+ className: "flex-1 min-w-0 min-h-[1px] text-[14px] leading-[20px] not-italic bg-transparent outline-none border-none p-0 m-0 placeholder:text-disabled",
1565
+ style: {
1566
+ color: filledColor,
1567
+ caretColor: "var(--caret-color)"
1568
+ },
1569
+ onClick: (e) => e.stopPropagation()
1570
+ }
1571
+ )
1572
+ ) : isFilled ? (
1573
+ /* Filled + open → floating label + search input */
1574
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start justify-center flex-1 min-w-0 min-h-[1px]", children: [
1575
+ /* @__PURE__ */ jsxRuntime.jsxs(
1576
+ "p",
1577
+ {
1578
+ className: "shrink-0 w-full leading-[16px] not-italic text-[12px]",
1579
+ style: { color: labelColor },
1580
+ children: [
1581
+ placeholder,
1582
+ required && /* @__PURE__ */ jsxRuntime.jsx(Asterisk, { color: isDisabled ? "var(--disabled)" : "var(--error-dark)" })
1583
+ ]
1584
+ }
1585
+ ),
1586
+ /* @__PURE__ */ jsxRuntime.jsx(
1587
+ "input",
1588
+ {
1589
+ ref: inputRef,
1590
+ type: "text",
1591
+ value: search,
1592
+ onChange: (e) => setSearch(e.target.value),
1593
+ onKeyDown: handleInputKeyDown,
1594
+ placeholder: selectedLabel,
1595
+ className: "w-full leading-[20px] not-italic text-[14px] min-w-0 bg-transparent outline-none border-none p-0 m-0 placeholder:text-disabled",
1596
+ style: {
1597
+ color: filledColor,
1598
+ caretColor: "var(--caret-color)"
1599
+ },
1600
+ onClick: (e) => e.stopPropagation()
1601
+ }
1602
+ )
1603
+ ] })
1604
+ ) : (
1605
+ /* Empty + open → inline search input */
1606
+ /* @__PURE__ */ jsxRuntime.jsx(
1607
+ "input",
1608
+ {
1609
+ ref: inputRef,
1610
+ type: "text",
1611
+ value: search,
1612
+ onChange: (e) => setSearch(e.target.value),
1613
+ onKeyDown: handleInputKeyDown,
1614
+ placeholder: placeholder + (required ? " *" : ""),
1615
+ className: "flex-1 min-w-0 min-h-[1px] text-[16px] leading-[20px] not-italic bg-transparent outline-none border-none p-0 m-0 placeholder:text-muted-foreground",
1616
+ style: {
1617
+ color: "var(--foreground)",
1618
+ caretColor: "var(--caret-color)"
1619
+ },
1620
+ onClick: (e) => e.stopPropagation()
1621
+ }
1622
+ )
1623
+ )
1624
+ ) : isFilled ? (
1625
+ /* ─── Closed + Filled ─── */
1626
+ hasExternalLabel ? (
1627
+ /* External label → single-line selected value */
1628
+ /* @__PURE__ */ jsxRuntime.jsxs(
1629
+ "p",
1630
+ {
1631
+ className: "flex-1 min-w-0 min-h-[1px] leading-[20px] not-italic text-[14px] overflow-hidden text-ellipsis whitespace-nowrap",
1632
+ style: { color: filledColor },
1633
+ children: [
1634
+ selectedLabel,
1635
+ required && /* @__PURE__ */ jsxRuntime.jsx(Asterisk, { color: isDisabled ? "var(--disabled)" : "var(--error-dark)" })
1636
+ ]
1637
+ }
1638
+ )
1639
+ ) : (
1640
+ /* Default → floating label + selected value */
1641
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start justify-center flex-1 min-w-0 min-h-[1px]", children: [
1642
+ /* @__PURE__ */ jsxRuntime.jsxs(
1643
+ "p",
1644
+ {
1645
+ className: "shrink-0 w-full leading-[16px] not-italic text-[12px]",
1646
+ style: { color: labelColor },
1647
+ children: [
1648
+ placeholder,
1649
+ required && /* @__PURE__ */ jsxRuntime.jsx(Asterisk, { color: isDisabled ? "var(--disabled)" : "var(--error-dark)" })
1650
+ ]
1651
+ }
1652
+ ),
1653
+ /* @__PURE__ */ jsxRuntime.jsx(
1654
+ "p",
1655
+ {
1656
+ className: "w-full leading-[20px] not-italic text-[14px] min-w-0 min-h-[1px]",
1657
+ style: { color: filledColor },
1658
+ children: selectedLabel
1659
+ }
1660
+ )
1661
+ ] })
1662
+ )
1663
+ ) : (
1664
+ /* ─── Closed + Empty: placeholder ─── */
1665
+ required ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 min-w-0 min-h-[1px] gap-[2px] items-center", children: [
1666
+ /* @__PURE__ */ jsxRuntime.jsx(
1667
+ "p",
1668
+ {
1669
+ className: "leading-[20px] not-italic text-[16px] whitespace-nowrap",
1670
+ style: { color: labelColor },
1671
+ children: placeholder
1672
+ }
1673
+ ),
1674
+ /* @__PURE__ */ jsxRuntime.jsx(
1675
+ "p",
1676
+ {
1677
+ className: "leading-[16px] not-italic text-[12px] w-[7px]",
1678
+ style: { color: isDisabled ? "var(--disabled)" : "var(--error-dark)" },
1679
+ children: "*"
1680
+ }
1681
+ )
1682
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1683
+ "p",
1684
+ {
1685
+ className: "flex-1 min-w-0 min-h-[1px] text-[16px] leading-[20px] not-italic overflow-hidden text-ellipsis whitespace-nowrap",
1686
+ style: { color: labelColor },
1687
+ children: placeholder
1688
+ }
1689
+ )
1690
+ ),
1691
+ isFocus ? /* @__PURE__ */ jsxRuntime.jsx(ChevronUpIcon$1, { className: cn("shrink-0", caretClassName) }) : /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon$1, { className: cn("shrink-0", caretClassName) })
1692
+ ]
1693
+ }
1694
+ ),
1695
+ open && !forceState && options.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1696
+ "div",
1697
+ {
1698
+ className: cn(
1699
+ "relative bg-white rounded-[8px] overflow-clip p-[8px] z-20 flex flex-col items-start",
1700
+ filteredOptions.length > 10 && "overflow-y-auto"
1701
+ ),
1702
+ style: {
1703
+ boxShadow: "0px 20px 25px -5px rgba(0,0,0,0.1), 0px 8px 10px -6px rgba(0,0,0,0.1)",
1704
+ ...filteredOptions.length > 10 ? { maxHeight: 10 * 48 + 16 } : {}
1705
+ },
1706
+ children: filteredOptions.length > 0 ? filteredOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
1707
+ "div",
1708
+ {
1709
+ onClick: () => handleSelect(opt.value),
1710
+ className: cn(
1711
+ "w-full shrink-0 rounded-[4px] cursor-pointer transition-colors duration-100",
1712
+ opt.value === currentValue ? "bg-primary-action-light" : "bg-white hover:bg-disabled-bg"
1713
+ ),
1714
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center p-[14px] relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
1715
+ "p",
1716
+ {
1717
+ className: cn(
1718
+ "flex-1 min-w-0 min-h-[1px] leading-[20px] not-italic overflow-hidden text-[14px] text-ellipsis whitespace-nowrap",
1719
+ opt.value === currentValue ? "text-primary-action" : "text-foreground"
1720
+ ),
1721
+ children: opt.label
1722
+ }
1723
+ ) }) })
1724
+ },
1725
+ opt.value
1726
+ )) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full shrink-0 bg-white", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center p-[14px] relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex-1 min-w-0 min-h-[1px] leading-[20px] not-italic text-[14px] text-disabled", children: "No results found" }) }) }) })
1727
+ }
1728
+ ),
1729
+ showBelow && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start px-[4px] text-[12px] leading-[16px]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 min-w-0", style: { color: leftColor }, children: leftText }) })
1730
+ ]
1731
+ }
1732
+ );
1733
+ }
1734
+ );
1735
+ Dropdown.displayName = "Dropdown";
1736
+ const TAG_GAP = 4;
1737
+ const MAX_COMPONENT_WIDTH = 343;
1738
+ function ChevronDownIcon({ className }) {
1739
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 9L11 13L15 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1740
+ }
1741
+ function ChevronUpIcon({ className }) {
1742
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 13L11 9L15 13", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1743
+ }
1744
+ function XIcon({ color = "var(--muted-foreground)" }) {
1745
+ return /* @__PURE__ */ jsxRuntime.jsx(
1746
+ "svg",
1747
+ {
1748
+ width: "12",
1749
+ height: "12",
1750
+ viewBox: "0 0 12 12",
1751
+ fill: "none",
1752
+ className: "shrink-0",
1753
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1754
+ "path",
1755
+ {
1756
+ d: "M9 3L3 9M3 3L9 9",
1757
+ stroke: color,
1758
+ strokeWidth: "1.5",
1759
+ strokeLinecap: "round",
1760
+ strokeLinejoin: "round"
1761
+ }
1762
+ )
1763
+ }
1764
+ );
1765
+ }
1766
+ function RemovableTag({
1767
+ label,
1768
+ disabled,
1769
+ onRemove,
1770
+ maxWidth
1771
+ }) {
1772
+ const textColor = disabled ? "var(--disabled)" : "var(--subtle-text)";
1773
+ const iconColor = disabled ? "var(--disabled)" : "var(--muted-foreground)";
1774
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1775
+ "div",
1776
+ {
1777
+ className: "bg-disabled-bg flex items-center overflow-hidden pl-[8px] pr-[8px] py-[4px] rounded-[4px] shrink-0 relative group",
1778
+ style: maxWidth !== void 0 ? { maxWidth, flexShrink: 1 } : void 0,
1779
+ children: [
1780
+ /* @__PURE__ */ jsxRuntime.jsxs(
1781
+ "p",
1782
+ {
1783
+ className: "leading-[16px] not-italic text-[12px] whitespace-nowrap overflow-hidden text-ellipsis pr-[4px]",
1784
+ style: { color: textColor },
1785
+ children: [
1786
+ label,
1787
+ " "
1788
+ ]
1789
+ }
1790
+ ),
1791
+ !disabled && onRemove && /* @__PURE__ */ jsxRuntime.jsx(
1792
+ "button",
1793
+ {
1794
+ type: "button",
1795
+ onClick: (e) => {
1796
+ e.stopPropagation();
1797
+ onRemove();
1798
+ },
1799
+ className: "absolute right-[4px] top-1/2 -translate-y-1/2 flex items-center justify-center rounded-[2px] p-[1px] cursor-pointer opacity-0 group-hover:opacity-100 transition-all duration-150 bg-[#e5e7eb]",
1800
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { color: iconColor })
1801
+ }
1802
+ )
1803
+ ]
1804
+ }
1805
+ );
1806
+ }
1807
+ function StaticTag({
1808
+ label,
1809
+ disabled,
1810
+ maxWidth
1811
+ }) {
1812
+ const textColor = disabled ? "var(--disabled)" : "var(--subtle-text)";
1813
+ return /* @__PURE__ */ jsxRuntime.jsx(
1814
+ "div",
1815
+ {
1816
+ className: "bg-disabled-bg flex items-center justify-center overflow-clip px-[8px] py-[4px] rounded-[4px] shrink-0",
1817
+ style: maxWidth !== void 0 ? { maxWidth, flexShrink: 1 } : void 0,
1818
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1819
+ "p",
1820
+ {
1821
+ className: "leading-[16px] not-italic text-[12px] whitespace-nowrap overflow-hidden text-ellipsis",
1822
+ style: { color: textColor },
1823
+ children: label
1824
+ }
1825
+ )
1826
+ }
1827
+ );
1828
+ }
1829
+ function OverflowBadge({
1830
+ count,
1831
+ disabled
1832
+ }) {
1833
+ const bg = disabled ? "bg-[#fafafa]" : "bg-selected-bg";
1834
+ const textColor = disabled ? "var(--disabled)" : "var(--primary-action)";
1835
+ return /* @__PURE__ */ jsxRuntime.jsx(
1836
+ "div",
1837
+ {
1838
+ className: cn(
1839
+ bg,
1840
+ "flex items-center justify-center overflow-clip px-[8px] py-[4px] rounded-[4px] shrink-0"
1841
+ ),
1842
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1843
+ "p",
1844
+ {
1845
+ className: "leading-[16px] not-italic text-[12px] whitespace-nowrap",
1846
+ style: { color: textColor },
1847
+ children: [
1848
+ "+",
1849
+ count
1850
+ ]
1851
+ }
1852
+ )
1853
+ }
1854
+ );
1855
+ }
1856
+ function CheckIcon$1(props) {
1857
+ return /* @__PURE__ */ jsxRuntime.jsx(
1858
+ "svg",
1859
+ {
1860
+ width: "10",
1861
+ height: "8",
1862
+ viewBox: "0 0 10 8",
1863
+ fill: "none",
1864
+ ...props,
1865
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1866
+ "path",
1867
+ {
1868
+ d: "M1 4L3.5 6.5L9 1",
1869
+ stroke: "currentColor",
1870
+ strokeWidth: "1.5",
1871
+ strokeLinecap: "round",
1872
+ strokeLinejoin: "round"
1873
+ }
1874
+ )
1875
+ }
1876
+ );
1877
+ }
1878
+ function useChipLayout(selectedOptions, measureRef, containerRef) {
1879
+ const [layout, setLayout] = React.useState({
1880
+ visibleCount: selectedOptions.length,
1881
+ lastTagMaxWidth: void 0
1882
+ });
1883
+ React.useLayoutEffect(() => {
1884
+ var _a, _b;
1885
+ const calculate = () => {
1886
+ var _a2, _b2;
1887
+ const total = selectedOptions.length;
1888
+ if (!measureRef.current || total === 0) {
1889
+ setLayout({
1890
+ visibleCount: total,
1891
+ lastTagMaxWidth: void 0
1892
+ });
1893
+ return;
1894
+ }
1895
+ const tagEls = Array.from(
1896
+ measureRef.current.children
1897
+ );
1898
+ const containerWidth = ((_a2 = containerRef.current) == null ? void 0 : _a2.offsetWidth) ?? MAX_COMPONENT_WIDTH;
1899
+ const RIGHT_SECTION_WIDTH = 60;
1900
+ const budget = containerWidth * 0.72 - RIGHT_SECTION_WIDTH;
1901
+ let used = 0;
1902
+ let count = 0;
1903
+ for (let i = 0; i < tagEls.length; i++) {
1904
+ const w = tagEls[i].offsetWidth;
1905
+ const gap = count > 0 ? TAG_GAP : 0;
1906
+ const nextUsed = used + gap + w;
1907
+ if (nextUsed <= budget) {
1908
+ used = nextUsed;
1909
+ count++;
1910
+ } else {
1911
+ count++;
1912
+ break;
1913
+ }
1914
+ }
1915
+ count = Math.max(1, count);
1916
+ const overflowCount = total - count;
1917
+ const isFullWidth = used >= budget * 0.98;
1918
+ let lastTagMaxWidth;
1919
+ if (overflowCount > 0 || isFullWidth) {
1920
+ const prevTagsWidth = tagEls.slice(0, count - 1).reduce(
1921
+ (sum, el, i) => sum + el.offsetWidth + (i > 0 ? TAG_GAP : 0),
1922
+ 0
1923
+ );
1924
+ const gapBeforeLast = count > 1 ? TAG_GAP : 0;
1925
+ const remaining = budget - prevTagsWidth - gapBeforeLast;
1926
+ ((_b2 = tagEls[count - 1]) == null ? void 0 : _b2.offsetWidth) ?? 0;
1927
+ if (remaining > 0) {
1928
+ lastTagMaxWidth = remaining;
1929
+ }
1930
+ }
1931
+ setLayout({ visibleCount: count, lastTagMaxWidth });
1932
+ };
1933
+ calculate();
1934
+ (_b = (_a = document.fonts) == null ? void 0 : _a.ready) == null ? void 0 : _b.then(calculate);
1935
+ }, [selectedOptions, measureRef, containerRef]);
1936
+ return layout;
1937
+ }
1938
+ const DropdownMultiple = React.forwardRef(
1939
+ function DropdownMultiple2({
1940
+ placeholder = "Placeholder",
1941
+ label,
1942
+ required = false,
1943
+ forceState,
1944
+ errorMessage = "Error message",
1945
+ helperText,
1946
+ value,
1947
+ onChange,
1948
+ options = [],
1949
+ className = ""
1950
+ }, ref) {
1951
+ const [open, setOpen] = React.useState(false);
1952
+ const [internalValue, setInternalValue] = React.useState([]);
1953
+ const [search, setSearch] = React.useState("");
1954
+ const containerRef = React.useRef(null);
1955
+ const inputRef = React.useRef(null);
1956
+ const measureRef = React.useRef(null);
1957
+ React.useImperativeHandle(ref, () => containerRef.current);
1958
+ const controlled = value !== void 0;
1959
+ const currentValue = controlled ? value : internalValue;
1960
+ const isDisabled = forceState === "disabled";
1961
+ const isStatic = Boolean(forceState);
1962
+ const isFilled = currentValue.length > 0;
1963
+ const state = forceState ?? (open ? "focus" : "default");
1964
+ const isError = state === "error";
1965
+ const isFocus = state === "focus";
1966
+ const bg = isDisabled ? "bg-disabled-bg" : "bg-white";
1967
+ const labelColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
1968
+ const caretColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
1969
+ const borderInset = isFocus || isError ? "-1px" : "0px";
1970
+ const borderRad = isFocus || isError ? "9px" : "8px";
1971
+ const borderColor = isDisabled ? "var(--border-disabled)" : isError ? "var(--destructive)" : isFocus ? "var(--primary-action)" : "var(--border)";
1972
+ const showBelow = isError || Boolean(helperText);
1973
+ const leftText = isError ? errorMessage : helperText ?? "";
1974
+ const leftColor = isError ? "var(--destructive)" : "var(--muted-foreground)";
1975
+ const selectedOptions = React.useMemo(
1976
+ () => currentValue.map((v) => options.find((o) => o.value === v)).filter(Boolean),
1977
+ [currentValue, options]
1978
+ );
1979
+ const { visibleCount, lastTagMaxWidth } = useChipLayout(
1980
+ selectedOptions,
1981
+ measureRef,
1982
+ containerRef
1983
+ );
1984
+ const visibleTags = selectedOptions.slice(0, visibleCount);
1985
+ const overflowCount = Math.max(
1986
+ 0,
1987
+ selectedOptions.length - visibleCount
1988
+ );
1989
+ const filteredOptions = React.useMemo(() => {
1990
+ if (!search.trim()) return options;
1991
+ const q = search.trim().toLowerCase();
1992
+ return options.filter(
1993
+ (o) => o.label.toLowerCase().includes(q)
1994
+ );
1995
+ }, [search, options]);
1996
+ React.useEffect(() => {
1997
+ if (open && inputRef.current) inputRef.current.focus();
1998
+ }, [open]);
1999
+ React.useEffect(() => {
2000
+ if (!open) setSearch("");
2001
+ }, [open]);
2002
+ React.useEffect(() => {
2003
+ if (!open) return;
2004
+ const handler = (e) => {
2005
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
2006
+ setOpen(false);
2007
+ }
2008
+ };
2009
+ document.addEventListener("mousedown", handler);
2010
+ return () => document.removeEventListener("mousedown", handler);
2011
+ }, [open]);
2012
+ const updateValue = React.useCallback(
2013
+ (next) => {
2014
+ if (!controlled) setInternalValue(next);
2015
+ onChange == null ? void 0 : onChange(next);
2016
+ },
2017
+ [controlled, onChange]
2018
+ );
2019
+ const handleToggleOption = React.useCallback(
2020
+ (val) => {
2021
+ const next = currentValue.includes(val) ? currentValue.filter((v) => v !== val) : [...currentValue, val];
2022
+ updateValue(next);
2023
+ },
2024
+ [currentValue, updateValue]
2025
+ );
2026
+ const handleRemoveTag = React.useCallback(
2027
+ (val) => {
2028
+ updateValue(currentValue.filter((v) => v !== val));
2029
+ },
2030
+ [currentValue, updateValue]
2031
+ );
2032
+ const handleTriggerClick = () => {
2033
+ var _a;
2034
+ if (isDisabled || isStatic) return;
2035
+ if (!open) setOpen(true);
2036
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
2037
+ };
2038
+ const handleInputKeyDown = (e) => {
2039
+ if (e.key === "Escape") setOpen(false);
2040
+ if (e.key === "Backspace" && search === "" && currentValue.length > 0) {
2041
+ updateValue(currentValue.slice(0, -1));
2042
+ }
2043
+ };
2044
+ const FloatingLabel = () => required ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[2px] items-center shrink-0 w-full", children: [
2045
+ /* @__PURE__ */ jsxRuntime.jsx(
2046
+ "p",
2047
+ {
2048
+ className: "shrink-0 leading-[16px] not-italic text-[12px]",
2049
+ style: { color: labelColor },
2050
+ children: placeholder
2051
+ }
2052
+ ),
2053
+ /* @__PURE__ */ jsxRuntime.jsx(
2054
+ "p",
2055
+ {
2056
+ className: "leading-[1.5] not-italic text-[9px] w-[7px]",
2057
+ style: {
2058
+ color: isDisabled ? "var(--disabled)" : "var(--error-dark)"
2059
+ },
2060
+ children: "*"
2061
+ }
2062
+ )
2063
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2064
+ "p",
2065
+ {
2066
+ className: "shrink-0 w-full leading-[16px] not-italic text-[12px]",
2067
+ style: { color: labelColor },
2068
+ children: placeholder
2069
+ }
2070
+ );
2071
+ const Placeholder = () => required ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 min-w-0 min-h-[1px] gap-[2px] items-center", children: [
2072
+ /* @__PURE__ */ jsxRuntime.jsx(
2073
+ "p",
2074
+ {
2075
+ className: "leading-[20px] not-italic text-[16px] whitespace-nowrap",
2076
+ style: { color: labelColor },
2077
+ children: placeholder
2078
+ }
2079
+ ),
2080
+ /* @__PURE__ */ jsxRuntime.jsx(
2081
+ "p",
2082
+ {
2083
+ className: "leading-[16px] not-italic text-[12px] w-[7px]",
2084
+ style: { color: isDisabled ? "var(--disabled)" : "var(--error-dark)" },
2085
+ children: "*"
2086
+ }
2087
+ )
2088
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2089
+ "p",
2090
+ {
2091
+ className: "flex-1 min-w-0 min-h-[1px] text-[16px] leading-[20px] not-italic overflow-hidden text-ellipsis whitespace-nowrap py-[7px]",
2092
+ style: { color: labelColor },
2093
+ children: placeholder
2094
+ }
2095
+ );
2096
+ const MeasurementLayer = () => /* @__PURE__ */ jsxRuntime.jsx(
2097
+ "div",
2098
+ {
2099
+ ref: measureRef,
2100
+ "aria-hidden": true,
2101
+ className: "absolute flex gap-[4px] items-center pointer-events-none",
2102
+ style: {
2103
+ visibility: "hidden",
2104
+ top: 0,
2105
+ left: 0,
2106
+ height: 0,
2107
+ overflow: "hidden"
2108
+ },
2109
+ children: selectedOptions.map(
2110
+ (opt) => isStatic ? /* @__PURE__ */ jsxRuntime.jsx(
2111
+ StaticTag,
2112
+ {
2113
+ label: opt.label,
2114
+ disabled: isDisabled
2115
+ },
2116
+ opt.value
2117
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2118
+ RemovableTag,
2119
+ {
2120
+ label: opt.label,
2121
+ disabled: isDisabled
2122
+ },
2123
+ opt.value
2124
+ )
2125
+ )
2126
+ }
2127
+ );
2128
+ const renderTriggerContent = () => {
2129
+ const hasExternalLabel2 = Boolean(label);
2130
+ if (isStatic) {
2131
+ if (isFilled) {
2132
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start flex-1 min-w-0 min-h-[1px] relative", children: [
2133
+ /* @__PURE__ */ jsxRuntime.jsx(MeasurementLayer, {}),
2134
+ " ",
2135
+ !hasExternalLabel2 && /* @__PURE__ */ jsxRuntime.jsx(FloatingLabel, {}),
2136
+ /* @__PURE__ */ jsxRuntime.jsx(
2137
+ "div",
2138
+ {
2139
+ className: "flex gap-[4px] items-center shrink-0 overflow-hidden",
2140
+ style: { maxWidth: "100%" },
2141
+ children: visibleTags.map((opt, i) => /* @__PURE__ */ jsxRuntime.jsx(
2142
+ StaticTag,
2143
+ {
2144
+ label: opt.label,
2145
+ disabled: isDisabled,
2146
+ maxWidth: i === visibleTags.length - 1 ? lastTagMaxWidth : void 0
2147
+ },
2148
+ opt.value
2149
+ ))
2150
+ }
2151
+ )
2152
+ ] });
2153
+ }
2154
+ if (!isFilled) {
2155
+ return /* @__PURE__ */ jsxRuntime.jsx(Placeholder, {});
2156
+ }
2157
+ return null;
2158
+ }
2159
+ if (isFilled) {
2160
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start flex-1 min-w-0 min-h-[1px] relative", children: [
2161
+ /* @__PURE__ */ jsxRuntime.jsx(MeasurementLayer, {}),
2162
+ !hasExternalLabel2 && /* @__PURE__ */ jsxRuntime.jsx(FloatingLabel, {}),
2163
+ /* @__PURE__ */ jsxRuntime.jsxs(
2164
+ "div",
2165
+ {
2166
+ className: "flex gap-[4px] items-center overflow-hidden",
2167
+ style: { maxWidth: "100%" },
2168
+ children: [
2169
+ visibleTags.map((opt, i) => /* @__PURE__ */ jsxRuntime.jsx(
2170
+ RemovableTag,
2171
+ {
2172
+ label: opt.label,
2173
+ onRemove: () => handleRemoveTag(opt.value),
2174
+ maxWidth: i === visibleTags.length - 1 ? lastTagMaxWidth : void 0
2175
+ },
2176
+ opt.value
2177
+ )),
2178
+ open && /* @__PURE__ */ jsxRuntime.jsx(
2179
+ "input",
2180
+ {
2181
+ ref: inputRef,
2182
+ type: "text",
2183
+ value: search,
2184
+ onChange: (e) => setSearch(e.target.value),
2185
+ onKeyDown: handleInputKeyDown,
2186
+ className: "flex-1 min-w-[40px] outline-none border-none bg-transparent text-[14px] leading-[20px]",
2187
+ style: {
2188
+ color: "var(--foreground)",
2189
+ caretColor: "var(--caret-color)"
2190
+ },
2191
+ onClick: (e) => e.stopPropagation()
2192
+ }
2193
+ )
2194
+ ]
2195
+ }
2196
+ )
2197
+ ] });
2198
+ }
2199
+ if (open) {
2200
+ return /* @__PURE__ */ jsxRuntime.jsx(
2201
+ "input",
2202
+ {
2203
+ ref: inputRef,
2204
+ type: "text",
2205
+ value: search,
2206
+ onChange: (e) => setSearch(e.target.value),
2207
+ onKeyDown: handleInputKeyDown,
2208
+ className: "flex-1 min-w-[40px] outline-none border-none bg-transparent text-[14px] leading-[20px]",
2209
+ style: {
2210
+ color: "var(--foreground)",
2211
+ caretColor: "var(--caret-color)"
2212
+ },
2213
+ onClick: (e) => e.stopPropagation()
2214
+ }
2215
+ );
2216
+ }
2217
+ if (!isFilled) {
2218
+ return /* @__PURE__ */ jsxRuntime.jsx(Placeholder, {});
2219
+ }
2220
+ return null;
2221
+ };
2222
+ const hasExternalLabel = Boolean(label);
2223
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2224
+ "div",
2225
+ {
2226
+ ref: containerRef,
2227
+ className: cn("flex flex-col gap-[4px] w-full max-w-[343px]", className),
2228
+ children: [
2229
+ label && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative shrink-0 w-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start px-[4px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
2230
+ "p",
2231
+ {
2232
+ className: "leading-[20px] not-italic relative shrink-0 text-foreground text-[14px] whitespace-nowrap",
2233
+ style: { fontWeight: 700 },
2234
+ children: label
2235
+ }
2236
+ ) }) }),
2237
+ /* @__PURE__ */ jsxRuntime.jsxs(
2238
+ "div",
2239
+ {
2240
+ onClick: handleTriggerClick,
2241
+ className: cn(
2242
+ "relative flex gap-[8px] items-center rounded-[8px]",
2243
+ bg,
2244
+ "px-[14px]",
2245
+ hasExternalLabel ? "h-[40px]" : "py-[4px] min-h-[48px]",
2246
+ !isDisabled && !isStatic && "cursor-pointer"
2247
+ ),
2248
+ children: [
2249
+ /* @__PURE__ */ jsxRuntime.jsx(
2250
+ "div",
2251
+ {
2252
+ "aria-hidden": "true",
2253
+ className: "absolute pointer-events-none border border-solid",
2254
+ style: {
2255
+ inset: borderInset,
2256
+ borderRadius: borderRad,
2257
+ borderColor
2258
+ }
2259
+ }
2260
+ ),
2261
+ renderTriggerContent(),
2262
+ isFilled && overflowCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2263
+ "div",
2264
+ {
2265
+ className: hasExternalLabel ? "self-center" : "self-end",
2266
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2267
+ OverflowBadge,
2268
+ {
2269
+ count: overflowCount,
2270
+ disabled: isDisabled
2271
+ }
2272
+ )
2273
+ }
2274
+ ),
2275
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { color: caretColor }, children: isFocus ? /* @__PURE__ */ jsxRuntime.jsx(ChevronUpIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, {}) })
2276
+ ]
2277
+ }
2278
+ ),
2279
+ open && !isStatic && options.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2280
+ "div",
2281
+ {
2282
+ className: "relative bg-white rounded-[8px] overflow-clip p-[8px] z-20 flex flex-col items-start",
2283
+ style: {
2284
+ boxShadow: "0px 20px 25px -5px rgba(0,0,0,0.1), 0px 8px 10px -6px rgba(0,0,0,0.1)"
2285
+ },
2286
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2287
+ "div",
2288
+ {
2289
+ className: cn(
2290
+ "w-full flex flex-col",
2291
+ filteredOptions.length > 10 && "overflow-y-auto"
2292
+ ),
2293
+ style: filteredOptions.length > 10 ? { maxHeight: 10 * 48 } : void 0,
2294
+ children: filteredOptions.length > 0 ? filteredOptions.map((opt) => {
2295
+ const isSelected = currentValue.includes(opt.value);
2296
+ return /* @__PURE__ */ jsxRuntime.jsx(
2297
+ "div",
2298
+ {
2299
+ onClick: (e) => {
2300
+ var _a;
2301
+ e.stopPropagation();
2302
+ handleToggleOption(opt.value);
2303
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
2304
+ },
2305
+ className: cn(
2306
+ "w-full shrink-0 rounded-[4px] cursor-pointer transition-colors duration-100",
2307
+ isSelected ? "bg-primary-action-light" : "bg-white hover:bg-disabled-bg"
2308
+ ),
2309
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-[8px] p-[14px] relative w-full", children: [
2310
+ /* @__PURE__ */ jsxRuntime.jsx(
2311
+ "p",
2312
+ {
2313
+ className: cn(
2314
+ "flex-1 min-w-0 min-h-[1px] leading-[20px] not-italic overflow-hidden text-[14px] text-ellipsis whitespace-nowrap",
2315
+ isSelected ? "text-primary-action" : "text-foreground"
2316
+ ),
2317
+ children: opt.label
2318
+ }
2319
+ ),
2320
+ /* @__PURE__ */ jsxRuntime.jsx(
2321
+ "div",
2322
+ {
2323
+ className: cn(
2324
+ "shrink-0 w-[16px] h-[16px] rounded-[3px] flex items-center justify-center transition-colors duration-100",
2325
+ "bg-transparent"
2326
+ ),
2327
+ children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon$1, { className: "text-primary-action" })
2328
+ }
2329
+ )
2330
+ ] }) })
2331
+ },
2332
+ opt.value
2333
+ );
2334
+ }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full shrink-0 bg-white", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center size-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center p-[14px] relative w-full", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex-1 min-w-0 min-h-[1px] leading-[20px] not-italic text-[14px] text-disabled", children: "No results found" }) }) }) })
2335
+ }
2336
+ )
2337
+ }
2338
+ ),
2339
+ showBelow && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start px-[4px] text-[12px] leading-[16px]", children: /* @__PURE__ */ jsxRuntime.jsx(
2340
+ "span",
2341
+ {
2342
+ className: "flex-1 min-w-0",
2343
+ style: { color: leftColor },
2344
+ children: leftText
2345
+ }
2346
+ ) })
2347
+ ]
2348
+ }
2349
+ );
2350
+ }
2351
+ );
2352
+ DropdownMultiple.displayName = "DropdownMultiple";
2353
+ const Input = React.forwardRef(function Input2({
2354
+ placeholder = "Text label",
2355
+ required = false,
2356
+ forceState,
2357
+ errorMessage = "Error message",
2358
+ value,
2359
+ onChange,
2360
+ rightIcon,
2361
+ unit,
2362
+ helperText,
2363
+ showCount = false,
2364
+ maxCount = 100,
2365
+ className = "",
2366
+ type = "text",
2367
+ style: inputStyleProp,
2368
+ onFocus: onFocusProp,
2369
+ onBlur: onBlurProp,
2370
+ disabled: _disabledProp,
2371
+ ...inputRest
2372
+ }, ref) {
2373
+ const [focused, setFocused] = React.useState(false);
2374
+ const [internalValue, setInternalValue] = React.useState("");
2375
+ const controlled = value !== void 0;
2376
+ const currentValue = controlled ? value : internalValue;
2377
+ const isDisabled = forceState === "disabled";
2378
+ const state = forceState ?? (focused ? "focus" : "default");
2379
+ const isError = state === "error";
2380
+ const isFocus = state === "focus";
2381
+ const isFilled = currentValue.length > 0;
2382
+ const bg = isDisabled ? "bg-disabled-bg" : "bg-white";
2383
+ const floatLabel = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
2384
+ const filledValue = isDisabled ? "var(--disabled)" : "var(--foreground)";
2385
+ const unitColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
2386
+ const borderInset = isFocus || isError ? "-1px" : "0px";
2387
+ const borderRad = isFocus || isError ? "9px" : "8px";
2388
+ const borderColor = isDisabled ? "var(--border-disabled)" : isError ? "var(--destructive)" : isFocus ? "var(--primary-action)" : "var(--border)";
2389
+ const hasRight = Boolean(rightIcon) || Boolean(unit);
2390
+ const padding = isFilled ? "px-[14px] py-[6px]" : hasRight ? "px-[14px] py-[12px]" : "p-[14px]";
2391
+ const charCount = currentValue.length;
2392
+ const showBelow = isError || Boolean(helperText) || showCount;
2393
+ const leftText = isError ? errorMessage : helperText ?? "";
2394
+ const leftColor = isError ? "var(--destructive)" : "var(--muted-foreground)";
2395
+ const handleChange = (e) => {
2396
+ if (!controlled) setInternalValue(e.target.value);
2397
+ onChange == null ? void 0 : onChange(e.target.value);
2398
+ };
2399
+ const handleFocus = (e) => {
2400
+ onFocusProp == null ? void 0 : onFocusProp(e);
2401
+ setFocused(true);
2402
+ };
2403
+ const handleBlur = (e) => {
2404
+ onBlurProp == null ? void 0 : onBlurProp(e);
2405
+ setFocused(false);
2406
+ };
2407
+ const containerFlex = isFilled ? !hasRight ? "flex-col items-start justify-center" : rightIcon ? "flex items-center gap-[8px]" : "flex items-end gap-[8px]" : cn("flex items-center", hasRight && "gap-[8px]");
2408
+ const inputCaretStyle = {
2409
+ caretColor: "var(--caret-color)"
2410
+ };
2411
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("flex flex-col gap-[4px] w-full", className), children: [
2412
+ /* @__PURE__ */ jsxRuntime.jsxs(
2413
+ "div",
2414
+ {
2415
+ className: cn(
2416
+ "relative rounded-[8px]",
2417
+ padding,
2418
+ bg,
2419
+ containerFlex
2420
+ ),
2421
+ children: [
2422
+ /* @__PURE__ */ jsxRuntime.jsx(
2423
+ "div",
2424
+ {
2425
+ "aria-hidden": "true",
2426
+ className: "absolute pointer-events-none border border-solid",
2427
+ style: {
2428
+ inset: borderInset,
2429
+ borderRadius: borderRad,
2430
+ borderColor
2431
+ }
2432
+ }
2433
+ ),
2434
+ /* @__PURE__ */ jsxRuntime.jsxs(
2435
+ "div",
2436
+ {
2437
+ className: cn(
2438
+ "flex flex-col min-w-0 min-h-[1px]",
2439
+ hasRight ? "flex-1 justify-center" : "w-full justify-center"
2440
+ ),
2441
+ children: [
2442
+ /* @__PURE__ */ jsxRuntime.jsxs(
2443
+ "p",
2444
+ {
2445
+ className: cn(
2446
+ "shrink-0 w-full not-italic",
2447
+ isFilled ? "leading-[16px] text-[12px]" : "text-[16px] leading-[20px] pointer-events-none"
2448
+ ),
2449
+ style: { color: floatLabel },
2450
+ children: [
2451
+ placeholder,
2452
+ required && !isFilled && /* @__PURE__ */ jsxRuntime.jsx(
2453
+ "span",
2454
+ {
2455
+ style: {
2456
+ color: isDisabled ? "var(--disabled)" : "var(--error-dark)"
2457
+ },
2458
+ children: " *"
2459
+ }
2460
+ )
2461
+ ]
2462
+ }
2463
+ ),
2464
+ !isDisabled && /* @__PURE__ */ jsxRuntime.jsx(
2465
+ "input",
2466
+ {
2467
+ ...inputRest,
2468
+ type,
2469
+ value: currentValue,
2470
+ onChange: handleChange,
2471
+ onFocus: handleFocus,
2472
+ onBlur: handleBlur,
2473
+ disabled: isDisabled,
2474
+ "aria-label": placeholder,
2475
+ className: cn(
2476
+ "w-full bg-transparent outline-none border-none min-w-0",
2477
+ isFilled ? "leading-[20px] not-italic text-[14px] p-0 m-0" : "absolute inset-0 h-full cursor-text text-[16px]"
2478
+ ),
2479
+ style: isFilled ? { ...inputStyleProp, color: filledValue, ...inputCaretStyle } : {
2480
+ ...inputStyleProp,
2481
+ color: "transparent",
2482
+ caretColor: isFocus ? "var(--caret-color)" : "transparent",
2483
+ padding: hasRight ? "12px 14px" : "14px"
2484
+ }
2485
+ }
2486
+ )
2487
+ ]
2488
+ }
2489
+ ),
2490
+ rightIcon && /* @__PURE__ */ jsxRuntime.jsx(
2491
+ "div",
2492
+ {
2493
+ className: cn(
2494
+ "shrink-0 flex items-center",
2495
+ isFilled && "self-stretch",
2496
+ !isFilled && "relative"
2497
+ ),
2498
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center size-[24px] overflow-hidden", children: rightIcon })
2499
+ }
2500
+ ),
2501
+ unit && !rightIcon && /* @__PURE__ */ jsxRuntime.jsx(
2502
+ "p",
2503
+ {
2504
+ className: cn(
2505
+ "shrink-0 whitespace-nowrap text-[16px]",
2506
+ !isFilled && "relative"
2507
+ ),
2508
+ style: {
2509
+ color: unitColor,
2510
+ lineHeight: "1.5",
2511
+ fontVariationSettings: "'wdth' 100"
2512
+ },
2513
+ children: unit
2514
+ }
2515
+ )
2516
+ ]
2517
+ }
2518
+ ),
2519
+ showBelow && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-[8px] px-[4px] text-[12px] leading-[16px]", children: [
2520
+ leftText ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 min-w-0", style: { color: leftColor }, children: leftText }) : showCount && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
2521
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs(
2522
+ "span",
2523
+ {
2524
+ className: "shrink-0 text-right whitespace-nowrap",
2525
+ style: { color: "var(--muted-foreground)" },
2526
+ children: [
2527
+ charCount,
2528
+ "/",
2529
+ maxCount
2530
+ ]
2531
+ }
2532
+ )
2533
+ ] })
2534
+ ] });
2535
+ });
2536
+ Input.displayName = "Input";
2537
+ function CheckIcon(props) {
2538
+ return /* @__PURE__ */ jsxRuntime.jsx(
2539
+ "svg",
2540
+ {
2541
+ width: "16",
2542
+ height: "11",
2543
+ viewBox: "0 0 16 11",
2544
+ fill: "none",
2545
+ ...props,
2546
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2547
+ "path",
2548
+ {
2549
+ d: "M1 5.5L5.5 10L15 1",
2550
+ stroke: "currentColor",
2551
+ strokeWidth: "1.5",
2552
+ strokeLinecap: "round",
2553
+ strokeLinejoin: "round"
2554
+ }
2555
+ )
2556
+ }
2557
+ );
2558
+ }
2559
+ const OptionList = React.forwardRef(
2560
+ function OptionList2({
2561
+ options,
2562
+ selectedValue,
2563
+ selectedValues,
2564
+ onSelect,
2565
+ onToggle,
2566
+ maxVisible = 10,
2567
+ emptyText = "No results found",
2568
+ className
2569
+ }, ref) {
2570
+ const isMulti = selectedValues !== void 0 || onToggle !== void 0;
2571
+ const isScrollable = options.length > maxVisible;
2572
+ const maxHeight = isScrollable ? maxVisible * 48 + 16 : void 0;
2573
+ const isSelected = (val) => isMulti ? (selectedValues ?? []).includes(val) : val === selectedValue;
2574
+ const handleClick = (opt) => {
2575
+ if (opt.disabled) return;
2576
+ if (isMulti) {
2577
+ onToggle == null ? void 0 : onToggle(opt.value);
2578
+ } else {
2579
+ onSelect == null ? void 0 : onSelect(opt.value);
2580
+ }
2581
+ };
2582
+ return /* @__PURE__ */ jsxRuntime.jsx(
2583
+ "div",
2584
+ {
2585
+ ref,
2586
+ className: cn(
2587
+ "flex flex-col items-start overflow-clip rounded-[8px] bg-white px-[8px] py-[8px]",
2588
+ className
2589
+ ),
2590
+ style: {
2591
+ boxShadow: "0px 20px 25px -5px rgba(0,0,0,0.1), 0px 8px 10px -6px rgba(0,0,0,0.1)",
2592
+ ...isScrollable ? { maxHeight, overflowY: "auto" } : {}
2593
+ },
2594
+ children: options.length > 0 ? options.map((opt) => {
2595
+ const selected = isSelected(opt.value);
2596
+ const disabled = opt.disabled === true;
2597
+ const rowBg = disabled ? "bg-disabled-bg" : selected ? "bg-selected-bg" : "bg-white hover:bg-selected-bg";
2598
+ return /* @__PURE__ */ jsxRuntime.jsx(
2599
+ "div",
2600
+ {
2601
+ onClick: () => handleClick(opt),
2602
+ className: cn(
2603
+ "w-full shrink-0 rounded-[4px] transition-colors duration-100",
2604
+ rowBg,
2605
+ disabled ? "cursor-default" : "cursor-pointer"
2606
+ ),
2607
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-[8px] p-[14px]", children: [
2608
+ opt.icon && /* @__PURE__ */ jsxRuntime.jsx(
2609
+ "span",
2610
+ {
2611
+ className: "flex size-[20px] shrink-0 items-center justify-center overflow-clip",
2612
+ style: {
2613
+ color: disabled ? "var(--disabled)" : "var(--muted-foreground)"
2614
+ },
2615
+ children: opt.icon
2616
+ }
2617
+ ),
2618
+ /* @__PURE__ */ jsxRuntime.jsx(
2619
+ "p",
2620
+ {
2621
+ className: "min-h-[1px] min-w-0 flex-1 overflow-hidden text-[14px] leading-[20px] text-ellipsis whitespace-nowrap not-italic",
2622
+ style: {
2623
+ color: disabled ? "var(--disabled)" : "var(--foreground)"
2624
+ },
2625
+ children: opt.label
2626
+ }
2627
+ ),
2628
+ selected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-[20px] shrink-0 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "text-primary-action" }) })
2629
+ ] })
2630
+ },
2631
+ opt.value
2632
+ );
2633
+ }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full shrink-0 bg-white", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center p-[14px]", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-h-[1px] min-w-0 flex-1 text-[14px] leading-[20px] not-italic text-disabled", children: emptyText }) }) })
2634
+ }
2635
+ );
2636
+ }
2637
+ );
2638
+ OptionList.displayName = "OptionList";
2639
+ const SearchInput = React.forwardRef(
2640
+ function SearchInput2({
2641
+ placeholder = "Placeholder",
2642
+ value,
2643
+ onChange,
2644
+ size = "lg",
2645
+ className,
2646
+ onClear
2647
+ }, ref) {
2648
+ const [focused, setFocused] = React.useState(false);
2649
+ const [internalValue, setInternalValue] = React.useState("");
2650
+ const inputRef = React.useRef(null);
2651
+ const controlled = value !== void 0;
2652
+ const currentValue = controlled ? value : internalValue;
2653
+ const isFilled = currentValue.length > 0;
2654
+ const iconSize = size === "lg" ? 24 : 22;
2655
+ const minHeight = size === "sm" ? "min-h-[32px]" : "";
2656
+ const padding = size === "sm" ? "px-[14px] py-[8px]" : "px-[14px] py-[12px]";
2657
+ const borderInset = focused ? "-1px" : "0px";
2658
+ const borderRad = focused ? "9px" : "8px";
2659
+ const borderColor = focused ? "var(--primary-action)" : "var(--border)";
2660
+ const handleChange = (next) => {
2661
+ if (!controlled) setInternalValue(next);
2662
+ onChange == null ? void 0 : onChange(next);
2663
+ };
2664
+ const handleClear = () => {
2665
+ var _a;
2666
+ if (!controlled) setInternalValue("");
2667
+ onChange == null ? void 0 : onChange("");
2668
+ onClear == null ? void 0 : onClear();
2669
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
2670
+ };
2671
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2672
+ "div",
2673
+ {
2674
+ ref,
2675
+ className: cn(
2676
+ "relative flex cursor-text items-center gap-[8px] rounded-[8px] bg-white",
2677
+ padding,
2678
+ minHeight,
2679
+ className
2680
+ ),
2681
+ onClick: () => {
2682
+ var _a;
2683
+ return (_a = inputRef.current) == null ? void 0 : _a.focus();
2684
+ },
2685
+ children: [
2686
+ /* @__PURE__ */ jsxRuntime.jsx(
2687
+ "div",
2688
+ {
2689
+ "aria-hidden": "true",
2690
+ className: "pointer-events-none absolute border border-solid",
2691
+ style: {
2692
+ inset: borderInset,
2693
+ borderRadius: borderRad,
2694
+ borderColor
2695
+ }
2696
+ }
2697
+ ),
2698
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex shrink-0 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
2699
+ react.MagnifyingGlass,
2700
+ {
2701
+ size: iconSize,
2702
+ color: "var(--muted-foreground)",
2703
+ weight: "regular"
2704
+ }
2705
+ ) }),
2706
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-w-0 flex-1", children: [
2707
+ !isFilled && /* @__PURE__ */ jsxRuntime.jsx(
2708
+ "p",
2709
+ {
2710
+ className: "pointer-events-none absolute inset-0 flex items-center text-[16px] leading-[20px] not-italic",
2711
+ style: { color: "var(--muted-foreground)" },
2712
+ children: placeholder
2713
+ }
2714
+ ),
2715
+ /* @__PURE__ */ jsxRuntime.jsx(
2716
+ "input",
2717
+ {
2718
+ ref: inputRef,
2719
+ type: "text",
2720
+ "aria-label": placeholder,
2721
+ value: currentValue,
2722
+ onChange: (e) => handleChange(e.target.value),
2723
+ onFocus: () => setFocused(true),
2724
+ onBlur: () => setFocused(false),
2725
+ className: "m-0 w-full border-none bg-transparent p-0 text-[16px] leading-[20px] outline-none",
2726
+ style: {
2727
+ color: isFilled ? "var(--foreground)" : "transparent",
2728
+ caretColor: "var(--caret-color)"
2729
+ }
2730
+ }
2731
+ )
2732
+ ] }),
2733
+ focused && isFilled && /* @__PURE__ */ jsxRuntime.jsx(
2734
+ "button",
2735
+ {
2736
+ type: "button",
2737
+ "aria-label": "Clear search",
2738
+ onMouseDown: (e) => e.preventDefault(),
2739
+ onClick: handleClear,
2740
+ className: "m-0 flex shrink-0 cursor-pointer items-center justify-center border-none bg-transparent p-0",
2741
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2742
+ react.X,
2743
+ {
2744
+ size: iconSize,
2745
+ color: "var(--muted-foreground)",
2746
+ weight: "regular"
2747
+ }
2748
+ )
2749
+ }
2750
+ )
2751
+ ]
2752
+ }
2753
+ );
2754
+ }
2755
+ );
2756
+ SearchInput.displayName = "SearchInput";
2757
+ const sizeClasses = {
2758
+ lg: {
2759
+ pad: "px-[12px] py-[10px]",
2760
+ text: "text-[14px]",
2761
+ leading: "leading-[20px]",
2762
+ font: "font-bold"
2763
+ },
2764
+ md: {
2765
+ pad: "px-[10px] py-[8px]",
2766
+ text: "text-[14px]",
2767
+ leading: "leading-[20px]",
2768
+ font: "font-bold"
2769
+ },
2770
+ sm: {
2771
+ pad: "px-[8px] py-[6px]",
2772
+ text: "text-[12px]",
2773
+ leading: "leading-[16px]",
2774
+ font: "font-semibold"
2775
+ }
2776
+ };
2777
+ const Tab = React.forwardRef(function Tab2({
2778
+ title = "Tab",
2779
+ size = "md",
2780
+ active = false,
2781
+ disabled = false,
2782
+ onClick,
2783
+ className
2784
+ }, ref) {
2785
+ const s = sizeClasses[size];
2786
+ const textColor = disabled ? "text-disabled" : active ? "text-primary-action" : "text-muted-foreground";
2787
+ const borderColor = active && !disabled ? "border-primary-action" : "border-border";
2788
+ const cursor = disabled ? "cursor-not-allowed" : "cursor-pointer";
2789
+ const hoverBg = !disabled && !active ? "hover:bg-hover-bg" : "";
2790
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2791
+ "div",
2792
+ {
2793
+ ref,
2794
+ role: "tab",
2795
+ "aria-selected": active,
2796
+ "aria-disabled": disabled,
2797
+ onClick: !disabled ? onClick : void 0,
2798
+ className: cn(
2799
+ "relative flex min-w-[80px] select-none items-center justify-center bg-white transition-colors duration-150",
2800
+ s.pad,
2801
+ cursor,
2802
+ hoverBg,
2803
+ className
2804
+ ),
2805
+ children: [
2806
+ /* @__PURE__ */ jsxRuntime.jsx(
2807
+ "div",
2808
+ {
2809
+ "aria-hidden": "true",
2810
+ className: cn(
2811
+ "pointer-events-none absolute inset-0 border-b-[1.5px] border-solid",
2812
+ borderColor
2813
+ )
2814
+ }
2815
+ ),
2816
+ /* @__PURE__ */ jsxRuntime.jsx(
2817
+ "span",
2818
+ {
2819
+ className: cn(
2820
+ "relative overflow-hidden text-ellipsis whitespace-nowrap text-center",
2821
+ s.text,
2822
+ s.leading,
2823
+ s.font,
2824
+ textColor
2825
+ ),
2826
+ children: title
2827
+ }
2828
+ )
2829
+ ]
2830
+ }
2831
+ );
2832
+ });
2833
+ Tab.displayName = "Tab";
2834
+ const TabGroup = React.forwardRef(
2835
+ function TabGroup2({ items, activeId, size = "md", onChange, className }, ref) {
2836
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, role: "tablist", className: cn("flex", className), children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
2837
+ Tab,
2838
+ {
2839
+ title: item.title,
2840
+ size,
2841
+ active: item.id === activeId,
2842
+ disabled: item.disabled,
2843
+ onClick: () => onChange == null ? void 0 : onChange(item.id)
2844
+ },
2845
+ item.id
2846
+ )) });
2847
+ }
2848
+ );
2849
+ TabGroup.displayName = "TabGroup";
2850
+ const TextArea = React.forwardRef(
2851
+ function TextArea2({
2852
+ placeholder = "Text label",
2853
+ required = false,
2854
+ forceState,
2855
+ errorMessage = "Error message",
2856
+ value,
2857
+ onChange,
2858
+ helperText,
2859
+ showCount = false,
2860
+ maxCount = 100,
2861
+ rows = 4,
2862
+ className,
2863
+ ...textareaProps
2864
+ }, ref) {
2865
+ const [focused, setFocused] = React.useState(false);
2866
+ const [internalValue, setInternalValue] = React.useState("");
2867
+ const controlled = value !== void 0;
2868
+ const currentValue = controlled ? value : internalValue;
2869
+ const isDisabled = forceState === "disabled";
2870
+ const state = forceState ?? (focused ? "focus" : "default");
2871
+ const isError = state === "error";
2872
+ const isFocus = state === "focus";
2873
+ const isFilled = currentValue.length > 0;
2874
+ const floatLabel = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
2875
+ const filledValue = isDisabled ? "var(--disabled)" : "var(--foreground)";
2876
+ const borderInset = isFocus || isError ? "-1px" : "0px";
2877
+ const borderRad = isFocus || isError ? "9px" : "8px";
2878
+ const borderColor = isDisabled ? "var(--border-disabled)" : isError ? "var(--destructive)" : isFocus ? "var(--primary-action)" : "var(--border)";
2879
+ const charCount = currentValue.length;
2880
+ const showBelow = isError || Boolean(helperText) || showCount;
2881
+ const leftText = isError ? errorMessage : helperText ?? "";
2882
+ const leftColor = isError ? "var(--destructive)" : "var(--muted-foreground)";
2883
+ const countColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
2884
+ const handleChange = (e) => {
2885
+ let newValue = e.target.value;
2886
+ if (showCount && newValue.length > maxCount) {
2887
+ newValue = newValue.slice(0, maxCount);
2888
+ }
2889
+ if (!controlled) setInternalValue(newValue);
2890
+ onChange == null ? void 0 : onChange(newValue);
2891
+ };
2892
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("flex w-full flex-col gap-[4px]", className), children: [
2893
+ /* @__PURE__ */ jsxRuntime.jsxs(
2894
+ "div",
2895
+ {
2896
+ className: cn(
2897
+ "relative h-[116px] rounded-[8px]",
2898
+ isDisabled ? "bg-disabled-bg" : "bg-white"
2899
+ ),
2900
+ children: [
2901
+ /* @__PURE__ */ jsxRuntime.jsx(
2902
+ "div",
2903
+ {
2904
+ "aria-hidden": "true",
2905
+ className: "pointer-events-none absolute border border-solid",
2906
+ style: { inset: borderInset, borderRadius: borderRad, borderColor }
2907
+ }
2908
+ ),
2909
+ isFilled ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full w-full flex-col items-start p-[14px] pb-[6px]", children: [
2910
+ /* @__PURE__ */ jsxRuntime.jsx(
2911
+ "p",
2912
+ {
2913
+ className: "w-full shrink-0 text-[12px] leading-[16px] not-italic",
2914
+ style: { color: floatLabel },
2915
+ children: placeholder
2916
+ }
2917
+ ),
2918
+ /* @__PURE__ */ jsxRuntime.jsx(
2919
+ "textarea",
2920
+ {
2921
+ ...textareaProps,
2922
+ value: currentValue,
2923
+ onChange: handleChange,
2924
+ onFocus: (e) => {
2925
+ var _a;
2926
+ (_a = textareaProps.onFocus) == null ? void 0 : _a.call(textareaProps, e);
2927
+ setFocused(true);
2928
+ },
2929
+ onBlur: (e) => {
2930
+ var _a;
2931
+ (_a = textareaProps.onBlur) == null ? void 0 : _a.call(textareaProps, e);
2932
+ setFocused(false);
2933
+ },
2934
+ disabled: isDisabled,
2935
+ rows,
2936
+ "aria-label": placeholder,
2937
+ className: cn(
2938
+ "m-0 min-w-0 flex-1 resize-none border-none bg-transparent p-0 text-[14px] leading-[20px] not-italic outline-none"
2939
+ ),
2940
+ style: {
2941
+ ...textareaProps.style,
2942
+ color: filledValue,
2943
+ caretColor: "var(--caret-color)"
2944
+ }
2945
+ }
2946
+ )
2947
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex size-full items-start p-[14px]", children: [
2948
+ /* @__PURE__ */ jsxRuntime.jsxs(
2949
+ "p",
2950
+ {
2951
+ className: "pointer-events-none relative h-full min-h-[1px] min-w-0 flex-1 text-[16px] leading-[20px] not-italic",
2952
+ style: { color: floatLabel },
2953
+ children: [
2954
+ placeholder,
2955
+ required && /* @__PURE__ */ jsxRuntime.jsxs(
2956
+ "span",
2957
+ {
2958
+ style: {
2959
+ color: isDisabled ? "var(--disabled)" : "var(--error-dark)"
2960
+ },
2961
+ children: [
2962
+ " ",
2963
+ "*"
2964
+ ]
2965
+ }
2966
+ )
2967
+ ]
2968
+ }
2969
+ ),
2970
+ !isDisabled && /* @__PURE__ */ jsxRuntime.jsx(
2971
+ "textarea",
2972
+ {
2973
+ ...textareaProps,
2974
+ value: currentValue,
2975
+ onChange: handleChange,
2976
+ onFocus: (e) => {
2977
+ var _a;
2978
+ (_a = textareaProps.onFocus) == null ? void 0 : _a.call(textareaProps, e);
2979
+ setFocused(true);
2980
+ },
2981
+ onBlur: (e) => {
2982
+ var _a;
2983
+ (_a = textareaProps.onBlur) == null ? void 0 : _a.call(textareaProps, e);
2984
+ setFocused(false);
2985
+ },
2986
+ rows,
2987
+ className: cn(
2988
+ "absolute inset-0 h-full w-full cursor-text resize-none border-none bg-transparent text-[16px] outline-none"
2989
+ ),
2990
+ style: {
2991
+ ...textareaProps.style,
2992
+ color: "transparent",
2993
+ caretColor: isFocus ? "var(--caret-color)" : "transparent",
2994
+ padding: "14px"
2995
+ },
2996
+ "aria-label": placeholder
2997
+ }
2998
+ )
2999
+ ] })
3000
+ ]
3001
+ }
3002
+ ),
3003
+ showBelow && /* @__PURE__ */ jsxRuntime.jsxs(
3004
+ "div",
3005
+ {
3006
+ className: "flex items-start gap-[8px] px-[4px] text-[12px] leading-[16px]",
3007
+ children: [
3008
+ leftText ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1", style: { color: leftColor }, children: leftText }) : showCount && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
3009
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs(
3010
+ "span",
3011
+ {
3012
+ className: "shrink-0 whitespace-nowrap text-right",
3013
+ style: { color: countColor },
3014
+ children: [
3015
+ charCount,
3016
+ "/",
3017
+ maxCount
3018
+ ]
3019
+ }
3020
+ )
3021
+ ]
3022
+ }
3023
+ )
3024
+ ] });
3025
+ }
3026
+ );
3027
+ TextArea.displayName = "TextArea";
3028
+ const HOURS = Array.from({ length: 24 }, (_, i) => i);
3029
+ const MINUTES = Array.from({ length: 60 }, (_, i) => i);
3030
+ const ITEM_H = 40;
3031
+ const DRUM_H = 248;
3032
+ const SPACER = (DRUM_H - ITEM_H) / 2;
3033
+ function generateHourlySlots() {
3034
+ return Array.from({ length: 24 }, (_, i) => ({
3035
+ start: { hour: i, minute: 0 },
3036
+ end: { hour: (i + 1) % 24, minute: 0 },
3037
+ label: `${pad2(i)}:00 - ${pad2((i + 1) % 24)}:00`
3038
+ }));
3039
+ }
3040
+ function slotMatches(slot, start, end) {
3041
+ return slot.start.hour === start.hour && slot.start.minute === start.minute && slot.end.hour === end.hour && slot.end.minute === end.minute;
3042
+ }
3043
+ function pad2(n) {
3044
+ return n.toString().padStart(2, "0");
3045
+ }
3046
+ function formatTime(v) {
3047
+ return `${pad2(v.hour)}:${pad2(v.minute)}`;
3048
+ }
3049
+ function ScrollColumn({
3050
+ items,
3051
+ value,
3052
+ onChange
3053
+ }) {
3054
+ const containerRef = React.useRef(null);
3055
+ const debounceRef = React.useRef(void 0);
3056
+ const [localValue, setLocalValue] = React.useState(value);
3057
+ const isUserScrolling = React.useRef(false);
3058
+ React.useEffect(() => {
3059
+ const el = containerRef.current;
3060
+ if (el) {
3061
+ el.scrollTop = items.indexOf(value) * ITEM_H;
3062
+ setLocalValue(value);
3063
+ }
3064
+ }, []);
3065
+ React.useEffect(() => {
3066
+ if (isUserScrolling.current) return;
3067
+ const el = containerRef.current;
3068
+ if (el) {
3069
+ const idx = items.indexOf(value);
3070
+ if (idx >= 0) {
3071
+ el.scrollTo({ top: idx * ITEM_H, behavior: "smooth" });
3072
+ setLocalValue(value);
3073
+ }
3074
+ }
3075
+ }, [value, items]);
3076
+ const handleScroll = React.useCallback(() => {
3077
+ isUserScrolling.current = true;
3078
+ const el = containerRef.current;
3079
+ if (!el) return;
3080
+ const rawIdx = el.scrollTop / ITEM_H;
3081
+ const idx = Math.max(
3082
+ 0,
3083
+ Math.min(Math.round(rawIdx), items.length - 1)
3084
+ );
3085
+ setLocalValue(items[idx]);
3086
+ clearTimeout(debounceRef.current);
3087
+ debounceRef.current = setTimeout(() => {
3088
+ isUserScrolling.current = false;
3089
+ const finalIdx = Math.max(
3090
+ 0,
3091
+ Math.min(
3092
+ Math.round(el.scrollTop / ITEM_H),
3093
+ items.length - 1
3094
+ )
3095
+ );
3096
+ const finalValue = items[finalIdx];
3097
+ el.scrollTo({
3098
+ top: finalIdx * ITEM_H,
3099
+ behavior: "smooth"
3100
+ });
3101
+ onChange(finalValue);
3102
+ setLocalValue(finalValue);
3103
+ }, 120);
3104
+ }, [items, onChange]);
3105
+ const handleItemClick = React.useCallback(
3106
+ (item) => {
3107
+ var _a;
3108
+ const idx = items.indexOf(item);
3109
+ setLocalValue(item);
3110
+ onChange(item);
3111
+ (_a = containerRef.current) == null ? void 0 : _a.scrollTo({
3112
+ top: idx * ITEM_H,
3113
+ behavior: "smooth"
3114
+ });
3115
+ },
3116
+ [items, onChange]
3117
+ );
3118
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3119
+ "div",
3120
+ {
3121
+ ref: containerRef,
3122
+ className: "relative [&::-webkit-scrollbar]:hidden",
3123
+ style: {
3124
+ width: 39,
3125
+ height: DRUM_H,
3126
+ overflowY: "scroll",
3127
+ scrollSnapType: "y mandatory",
3128
+ scrollbarWidth: "none"
3129
+ },
3130
+ onScroll: handleScroll,
3131
+ children: [
3132
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: SPACER, flexShrink: 0 } }),
3133
+ items.map((item) => {
3134
+ const isSel = item === localValue;
3135
+ return /* @__PURE__ */ jsxRuntime.jsx(
3136
+ "div",
3137
+ {
3138
+ style: {
3139
+ height: ITEM_H,
3140
+ scrollSnapAlign: "center"
3141
+ },
3142
+ className: "flex items-center justify-center cursor-pointer select-none",
3143
+ onClick: () => handleItemClick(item),
3144
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3145
+ "span",
3146
+ {
3147
+ style: {
3148
+ fontSize: isSel ? 32 : 14,
3149
+ lineHeight: 1,
3150
+ color: isSel ? "var(--foreground)" : "var(--disabled)",
3151
+ transition: "font-size 0.1s, color 0.1s"
3152
+ },
3153
+ children: pad2(item)
3154
+ }
3155
+ )
3156
+ },
3157
+ item
3158
+ );
3159
+ }),
3160
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: SPACER, flexShrink: 0 } })
3161
+ ]
3162
+ }
3163
+ );
3164
+ }
3165
+ const DEFAULT_SLOTS = generateHourlySlots();
3166
+ function RangeSlotPicker({
3167
+ slots = DEFAULT_SLOTS,
3168
+ startValue,
3169
+ endValue,
3170
+ onSelect
3171
+ }) {
3172
+ return /* @__PURE__ */ jsxRuntime.jsx(
3173
+ "div",
3174
+ {
3175
+ className: "overflow-y-auto [&::-webkit-scrollbar]:hidden",
3176
+ style: { maxHeight: 320, scrollbarWidth: "none", minWidth: 300 },
3177
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3178
+ "div",
3179
+ {
3180
+ className: "content-center grid grid-cols-2 gap-x-[20px] gap-y-[32px] items-center justify-between overflow-clip relative",
3181
+ style: { padding: "24px 40px" },
3182
+ children: slots.map((slot, idx) => {
3183
+ const isSelected = slotMatches(
3184
+ slot,
3185
+ startValue,
3186
+ endValue
3187
+ );
3188
+ return /* @__PURE__ */ jsxRuntime.jsx(
3189
+ "div",
3190
+ {
3191
+ onClick: () => onSelect(slot),
3192
+ className: cn(
3193
+ "content-stretch flex h-[25px] items-center justify-center relative rounded-[4px] shrink-0 cursor-pointer select-none transition-colors duration-100",
3194
+ isSelected ? "bg-primary-action" : "hover:bg-primary-action-light"
3195
+ ),
3196
+ style: { padding: "2px 8px" },
3197
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3198
+ "p",
3199
+ {
3200
+ className: "leading-[22px] relative shrink-0 text-[14px] text-center whitespace-nowrap",
3201
+ style: {
3202
+ fontVariationSettings: "'wdth' 100",
3203
+ color: isSelected ? "#ffffff" : "var(--foreground)"
3204
+ },
3205
+ children: slot.label
3206
+ }
3207
+ )
3208
+ },
3209
+ idx
3210
+ );
3211
+ })
3212
+ }
3213
+ )
3214
+ }
3215
+ );
3216
+ }
3217
+ function TimePickerContent({
3218
+ mode,
3219
+ singleValue,
3220
+ onSingleChange,
3221
+ startValue,
3222
+ endValue,
3223
+ onStartChange,
3224
+ onEndChange
3225
+ }) {
3226
+ if (mode === "range") {
3227
+ return /* @__PURE__ */ jsxRuntime.jsx(
3228
+ RangeSlotPicker,
3229
+ {
3230
+ startValue,
3231
+ endValue,
3232
+ onSelect: (slot) => {
3233
+ onStartChange(slot.start);
3234
+ onEndChange(slot.end);
3235
+ }
3236
+ }
3237
+ );
3238
+ }
3239
+ const handleHour = (h) => onSingleChange({ ...singleValue, hour: h });
3240
+ const handleMinute = (m) => onSingleChange({ ...singleValue, minute: m });
3241
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
3242
+ /* @__PURE__ */ jsxRuntime.jsx(
3243
+ "div",
3244
+ {
3245
+ className: "absolute left-0 right-0 rounded-[4px] bg-selected-light-bg pointer-events-none",
3246
+ style: {
3247
+ top: "50%",
3248
+ transform: "translateY(-50%)",
3249
+ height: ITEM_H,
3250
+ zIndex: 0
3251
+ }
3252
+ }
3253
+ ),
3254
+ /* @__PURE__ */ jsxRuntime.jsxs(
3255
+ "div",
3256
+ {
3257
+ className: "relative flex gap-[8px] items-center justify-center",
3258
+ style: { zIndex: 1 },
3259
+ children: [
3260
+ /* @__PURE__ */ jsxRuntime.jsx(
3261
+ ScrollColumn,
3262
+ {
3263
+ items: HOURS,
3264
+ value: singleValue.hour,
3265
+ onChange: handleHour
3266
+ }
3267
+ ),
3268
+ /* @__PURE__ */ jsxRuntime.jsx(
3269
+ "div",
3270
+ {
3271
+ className: "flex items-center justify-center shrink-0",
3272
+ style: { height: ITEM_H, width: 8 },
3273
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3274
+ "span",
3275
+ {
3276
+ style: {
3277
+ fontSize: 32,
3278
+ lineHeight: 1,
3279
+ color: "var(--foreground)"
3280
+ },
3281
+ children: ":"
3282
+ }
3283
+ )
3284
+ }
3285
+ ),
3286
+ /* @__PURE__ */ jsxRuntime.jsx(
3287
+ ScrollColumn,
3288
+ {
3289
+ items: MINUTES,
3290
+ value: singleValue.minute,
3291
+ onChange: handleMinute
3292
+ }
3293
+ )
3294
+ ]
3295
+ }
3296
+ )
3297
+ ] });
3298
+ }
3299
+ const TimeInput = React.forwardRef(
3300
+ ({
3301
+ mode = "single",
3302
+ placeholder = "Text label",
3303
+ required = false,
3304
+ forceState,
3305
+ errorMessage = "Error message",
3306
+ helperText,
3307
+ value,
3308
+ onChange,
3309
+ startTime,
3310
+ endTime,
3311
+ onStartChange,
3312
+ onEndChange,
3313
+ className
3314
+ }, ref) => {
3315
+ const [open, setOpen] = React.useState(false);
3316
+ const isMobile = useIsMobile();
3317
+ const [internalSingle, setInternalSingle] = React.useState(void 0);
3318
+ const [internalStart, setInternalStart] = React.useState({
3319
+ hour: 0,
3320
+ minute: 0
3321
+ });
3322
+ const [internalEnd, setInternalEnd] = React.useState({
3323
+ hour: 0,
3324
+ minute: 0
3325
+ });
3326
+ const [draftSingle, setDraftSingle] = React.useState({
3327
+ hour: 0,
3328
+ minute: 0
3329
+ });
3330
+ const [draftStart, setDraftStart] = React.useState({
3331
+ hour: 0,
3332
+ minute: 0
3333
+ });
3334
+ const [draftEnd, setDraftEnd] = React.useState({
3335
+ hour: 0,
3336
+ minute: 0
3337
+ });
3338
+ const isStatic = Boolean(forceState);
3339
+ const isDisabled = forceState === "disabled";
3340
+ const currentValue = value !== void 0 ? value : internalSingle;
3341
+ const currentStart = startTime !== void 0 ? startTime : internalStart;
3342
+ const currentEnd = endTime !== void 0 ? endTime : internalEnd;
3343
+ const state = forceState ?? (open ? "focus" : "default");
3344
+ const isError = state === "error";
3345
+ const isFocus = state === "focus";
3346
+ const isFilled = mode === "single" ? Boolean(currentValue) : true;
3347
+ const bgClass = isDisabled ? "bg-disabled-bg" : "bg-white";
3348
+ const labelColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
3349
+ const valueColor = isDisabled ? "var(--disabled)" : "var(--foreground)";
3350
+ const iconColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
3351
+ const minusColor = isDisabled ? "var(--disabled)" : "var(--muted-foreground)";
3352
+ const asteriskColor = isDisabled ? "var(--disabled)" : "var(--error-dark)";
3353
+ const asteriskSmall = isDisabled ? "var(--disabled)" : "var(--error-dark)";
3354
+ const borderInset = isFocus || isError ? "-1px" : "0px";
3355
+ const borderRad = isFocus || isError ? "9px" : "8px";
3356
+ const borderColor = isDisabled ? "var(--border-disabled)" : isError ? "var(--destructive)" : isFocus ? "var(--primary-action)" : "var(--border)";
3357
+ const showBelow = isError || Boolean(helperText);
3358
+ const leftText = isError ? errorMessage : helperText ?? "";
3359
+ const leftColor = isError ? "var(--error-dark)" : "var(--muted-foreground)";
3360
+ const commitSingle = React.useCallback(
3361
+ (v) => {
3362
+ if (value === void 0) setInternalSingle(v);
3363
+ onChange == null ? void 0 : onChange(v);
3364
+ },
3365
+ [value, onChange]
3366
+ );
3367
+ const commitStart = React.useCallback(
3368
+ (v) => {
3369
+ if (startTime === void 0) setInternalStart(v);
3370
+ onStartChange == null ? void 0 : onStartChange(v);
3371
+ },
3372
+ [startTime, onStartChange]
3373
+ );
3374
+ const commitEnd = React.useCallback(
3375
+ (v) => {
3376
+ if (endTime === void 0) setInternalEnd(v);
3377
+ onEndChange == null ? void 0 : onEndChange(v);
3378
+ },
3379
+ [endTime, onEndChange]
3380
+ );
3381
+ const renderContent = () => {
3382
+ if (isFilled && (mode === "range" || currentValue)) {
3383
+ const floatLabel = required ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[2px] items-center shrink-0 w-full", children: [
3384
+ /* @__PURE__ */ jsxRuntime.jsx(
3385
+ "p",
3386
+ {
3387
+ className: "leading-[16px] not-italic shrink-0 text-[12px] whitespace-nowrap",
3388
+ style: { color: labelColor },
3389
+ children: placeholder
3390
+ }
3391
+ ),
3392
+ /* @__PURE__ */ jsxRuntime.jsx(
3393
+ "p",
3394
+ {
3395
+ className: "leading-[1.5] not-italic shrink-0 text-[9px] w-[7px]",
3396
+ style: { color: asteriskSmall },
3397
+ children: "*"
3398
+ }
3399
+ )
3400
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
3401
+ "p",
3402
+ {
3403
+ className: "leading-[16px] not-italic shrink-0 text-[12px] w-full",
3404
+ style: { color: labelColor },
3405
+ children: placeholder
3406
+ }
3407
+ );
3408
+ const valueRow = mode === "single" && currentValue ? /* @__PURE__ */ jsxRuntime.jsx(
3409
+ "p",
3410
+ {
3411
+ className: "leading-[20px] not-italic shrink-0 text-[16px] w-full",
3412
+ style: { color: valueColor },
3413
+ children: formatTime(currentValue)
3414
+ }
3415
+ ) : mode === "range" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[8px] items-center shrink-0 w-full", children: [
3416
+ /* @__PURE__ */ jsxRuntime.jsx(
3417
+ "p",
3418
+ {
3419
+ className: "leading-[20px] not-italic shrink-0 text-[16px] whitespace-nowrap",
3420
+ style: { color: valueColor },
3421
+ children: formatTime(currentStart)
3422
+ }
3423
+ ),
3424
+ /* @__PURE__ */ jsxRuntime.jsx(
3425
+ react.Minus,
3426
+ {
3427
+ size: 20,
3428
+ color: minusColor,
3429
+ weight: "bold"
3430
+ }
3431
+ ),
3432
+ /* @__PURE__ */ jsxRuntime.jsx(
3433
+ "p",
3434
+ {
3435
+ className: "leading-[20px] not-italic shrink-0 text-[16px] whitespace-nowrap",
3436
+ style: { color: valueColor },
3437
+ children: formatTime(currentEnd)
3438
+ }
3439
+ )
3440
+ ] }) : null;
3441
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "content-stretch flex flex-1 flex-col items-center justify-center min-h-px min-w-px relative", children: [
3442
+ floatLabel,
3443
+ valueRow
3444
+ ] });
3445
+ }
3446
+ if (required) {
3447
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "content-stretch flex flex-1 min-w-px min-h-px gap-[2px] items-center relative", children: [
3448
+ /* @__PURE__ */ jsxRuntime.jsx(
3449
+ "p",
3450
+ {
3451
+ className: "leading-[20px] not-italic shrink-0 text-[16px] whitespace-nowrap",
3452
+ style: { color: labelColor },
3453
+ children: placeholder
3454
+ }
3455
+ ),
3456
+ /* @__PURE__ */ jsxRuntime.jsx(
3457
+ "p",
3458
+ {
3459
+ className: "font-normal h-full leading-[1.5] not-italic shrink-0 text-[12px] w-[7px]",
3460
+ style: { color: asteriskColor },
3461
+ children: "*"
3462
+ }
3463
+ )
3464
+ ] });
3465
+ }
3466
+ return /* @__PURE__ */ jsxRuntime.jsx(
3467
+ "p",
3468
+ {
3469
+ className: "flex-1 min-w-0 min-h-px text-[16px] leading-[20px] not-italic overflow-hidden text-ellipsis whitespace-nowrap relative",
3470
+ style: { color: labelColor },
3471
+ children: placeholder
3472
+ }
3473
+ );
3474
+ };
3475
+ const isTriggerFilled = isFilled && (mode === "range" || currentValue);
3476
+ const triggerPadding = isTriggerFilled ? "py-[6px]" : "py-[12px]";
3477
+ const triggerBase = cn(
3478
+ "relative flex gap-[8px] items-center rounded-[8px] px-[14px] w-full",
3479
+ bgClass,
3480
+ triggerPadding
3481
+ );
3482
+ const triggerInner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3483
+ /* @__PURE__ */ jsxRuntime.jsx(
3484
+ "div",
3485
+ {
3486
+ "aria-hidden": "true",
3487
+ className: "absolute pointer-events-none border border-solid",
3488
+ style: {
3489
+ inset: borderInset,
3490
+ borderRadius: borderRad,
3491
+ borderColor
3492
+ }
3493
+ }
3494
+ ),
3495
+ renderContent(),
3496
+ /* @__PURE__ */ jsxRuntime.jsx(
3497
+ "div",
3498
+ {
3499
+ className: cn(
3500
+ "flex flex-row items-center shrink-0",
3501
+ isTriggerFilled && "self-stretch"
3502
+ ),
3503
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3504
+ react.Clock,
3505
+ {
3506
+ size: isTriggerFilled ? 22 : 24,
3507
+ color: iconColor,
3508
+ weight: "regular"
3509
+ }
3510
+ )
3511
+ }
3512
+ )
3513
+ ] });
3514
+ const belowMsg = showBelow && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start px-[4px] text-[12px] leading-[16px]", children: /* @__PURE__ */ jsxRuntime.jsx(
3515
+ "span",
3516
+ {
3517
+ className: "flex-1 min-w-0",
3518
+ style: { color: leftColor },
3519
+ children: leftText
3520
+ }
3521
+ ) });
3522
+ if (isStatic) {
3523
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3524
+ "div",
3525
+ {
3526
+ ref,
3527
+ className: cn(
3528
+ "flex flex-col gap-[4px] w-full",
3529
+ className
3530
+ ),
3531
+ children: [
3532
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: triggerBase, children: triggerInner }),
3533
+ belowMsg
3534
+ ]
3535
+ }
3536
+ );
3537
+ }
3538
+ const handleOpenChange = (o) => {
3539
+ if (!isDisabled) {
3540
+ if (o) {
3541
+ setDraftSingle(
3542
+ currentValue ?? { hour: 0, minute: 0 }
3543
+ );
3544
+ setDraftStart(currentStart);
3545
+ setDraftEnd(currentEnd);
3546
+ }
3547
+ setOpen(o);
3548
+ }
3549
+ };
3550
+ const handleCancel = () => setOpen(false);
3551
+ const handleConfirm = () => {
3552
+ if (mode === "single") {
3553
+ commitSingle(draftSingle);
3554
+ } else {
3555
+ commitStart(draftStart);
3556
+ commitEnd(draftEnd);
3557
+ }
3558
+ setOpen(false);
3559
+ };
3560
+ const triggerButton = /* @__PURE__ */ jsxRuntime.jsx(
3561
+ "button",
3562
+ {
3563
+ type: "button",
3564
+ disabled: isDisabled,
3565
+ className: cn(
3566
+ triggerBase,
3567
+ "text-left cursor-pointer disabled:cursor-default"
3568
+ ),
3569
+ children: triggerInner
3570
+ }
3571
+ );
3572
+ const pickerContent = /* @__PURE__ */ jsxRuntime.jsx(
3573
+ TimePickerContent,
3574
+ {
3575
+ mode,
3576
+ singleValue: draftSingle,
3577
+ onSingleChange: setDraftSingle,
3578
+ startValue: draftStart,
3579
+ endValue: draftEnd,
3580
+ onStartChange: setDraftStart,
3581
+ onEndChange: setDraftEnd
3582
+ }
3583
+ );
3584
+ const actionButtons = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[12px] items-center pt-[12px]", children: [
3585
+ /* @__PURE__ */ jsxRuntime.jsx(
3586
+ Button,
3587
+ {
3588
+ variant: "outline",
3589
+ size: "lg",
3590
+ className: "flex-1",
3591
+ onClick: handleCancel,
3592
+ children: "ยกเลิก"
3593
+ }
3594
+ ),
3595
+ /* @__PURE__ */ jsxRuntime.jsx(
3596
+ Button,
3597
+ {
3598
+ variant: "primary",
3599
+ size: "lg",
3600
+ className: "flex-1",
3601
+ onClick: handleConfirm,
3602
+ children: "ตกลง"
3603
+ }
3604
+ )
3605
+ ] });
3606
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3607
+ "div",
3608
+ {
3609
+ ref,
3610
+ className: cn(
3611
+ "flex flex-col gap-[4px] w-full",
3612
+ className
3613
+ ),
3614
+ children: [
3615
+ isMobile ? /* @__PURE__ */ jsxRuntime.jsxs(Drawer, { open, onOpenChange: handleOpenChange, children: [
3616
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerTrigger, { asChild: true, children: triggerButton }),
3617
+ /* @__PURE__ */ jsxRuntime.jsxs(DrawerContent, { children: [
3618
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { className: "sr-only", children: "เลือกเวลา" }),
3619
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-auto px-4 pt-2 pb-8 w-full", children: [
3620
+ pickerContent,
3621
+ actionButtons
3622
+ ] })
3623
+ ] })
3624
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
3625
+ Popover__namespace.Root,
3626
+ {
3627
+ open,
3628
+ onOpenChange: handleOpenChange,
3629
+ children: [
3630
+ /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Trigger, { asChild: true, children: triggerButton }),
3631
+ /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
3632
+ Popover__namespace.Content,
3633
+ {
3634
+ align: "start",
3635
+ sideOffset: 4,
3636
+ className: "z-50 rounded-[8px] bg-white p-3 outline-none max-w-[340px]",
3637
+ style: {
3638
+ boxShadow: "0px 20px 25px -5px rgba(0,0,0,0.1), 0px 8px 10px -6px rgba(0,0,0,0.1)",
3639
+ border: "1px solid rgba(0,0,0,0.08)",
3640
+ minWidth: mode === "single" ? 327 : void 0
3641
+ },
3642
+ onOpenAutoFocus: (e) => e.preventDefault(),
3643
+ children: [
3644
+ pickerContent,
3645
+ actionButtons
3646
+ ]
3647
+ }
3648
+ ) })
3649
+ ]
3650
+ }
3651
+ ),
3652
+ belowMsg
3653
+ ]
3654
+ }
3655
+ );
3656
+ }
3657
+ );
3658
+ TimeInput.displayName = "TimeInput";
3659
+ exports.Button = Button;
3660
+ exports.Card = Card;
3661
+ exports.DateInput = DateInput;
3662
+ exports.Dropdown = Dropdown;
3663
+ exports.DropdownMultiple = DropdownMultiple;
3664
+ exports.Input = Input;
3665
+ exports.OptionList = OptionList;
3666
+ exports.SearchInput = SearchInput;
3667
+ exports.Tab = Tab;
3668
+ exports.TabGroup = TabGroup;
3669
+ exports.TextArea = TextArea;
3670
+ exports.TimeInput = TimeInput;
3671
+ exports.cn = cn;
3672
+ //# sourceMappingURL=index.cjs.map