@avenue-ticketing/ui 0.3.0 → 0.5.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 (74) hide show
  1. package/dist/react/avatar.d.ts +42 -0
  2. package/dist/react/avatar.js +159 -0
  3. package/dist/react/avatar.js.map +1 -0
  4. package/dist/react/badge.d.ts +12 -0
  5. package/dist/react/badge.js +35 -1
  6. package/dist/react/badge.js.map +1 -1
  7. package/dist/react/button.d.ts +1 -1
  8. package/dist/react/button.js +3 -3
  9. package/dist/react/button.js.map +1 -1
  10. package/dist/react/calendar.d.ts +13 -0
  11. package/dist/react/calendar.js +4639 -0
  12. package/dist/react/calendar.js.map +1 -0
  13. package/dist/react/card.d.ts +11 -0
  14. package/dist/react/card.js +113 -0
  15. package/dist/react/card.js.map +1 -0
  16. package/dist/react/checkbox.d.ts +11 -0
  17. package/dist/react/checkbox.js +131 -0
  18. package/dist/react/checkbox.js.map +1 -0
  19. package/dist/react/datetime-picker.d.ts +21 -0
  20. package/dist/react/datetime-picker.js +6124 -0
  21. package/dist/react/datetime-picker.js.map +1 -0
  22. package/dist/react/dialog.d.ts +7 -0
  23. package/dist/react/dialog.js +211 -90
  24. package/dist/react/dialog.js.map +1 -1
  25. package/dist/react/dropdown.d.ts +126 -0
  26. package/dist/react/dropdown.js +1269 -0
  27. package/dist/react/dropdown.js.map +1 -0
  28. package/dist/react/input.d.ts +7 -0
  29. package/dist/react/input.js +15 -2
  30. package/dist/react/input.js.map +1 -1
  31. package/dist/react/pagination.d.ts +28 -0
  32. package/dist/react/pagination.js +262 -0
  33. package/dist/react/pagination.js.map +1 -0
  34. package/dist/react/popover.d.ts +76 -0
  35. package/dist/react/popover.js +564 -0
  36. package/dist/react/popover.js.map +1 -0
  37. package/dist/react/scroll-header.js +13 -1
  38. package/dist/react/scroll-header.js.map +1 -1
  39. package/dist/react/scroll-wheel.d.ts +45 -0
  40. package/dist/react/scroll-wheel.js +557 -0
  41. package/dist/react/scroll-wheel.js.map +1 -0
  42. package/dist/react/select.d.ts +62 -0
  43. package/dist/react/select.js +889 -0
  44. package/dist/react/select.js.map +1 -0
  45. package/dist/react/sheet.js +1 -1
  46. package/dist/react/sheet.js.map +1 -1
  47. package/dist/react/switch.d.ts +38 -0
  48. package/dist/react/switch.js +117 -0
  49. package/dist/react/switch.js.map +1 -0
  50. package/dist/react/table-pagination.d.ts +15 -0
  51. package/dist/react/table-pagination.js +1153 -0
  52. package/dist/react/table-pagination.js.map +1 -0
  53. package/dist/react/table-view/column-menu.d.ts +15 -0
  54. package/dist/react/table-view/column-menu.js +918 -0
  55. package/dist/react/table-view/column-menu.js.map +1 -0
  56. package/dist/react/table-view/index.d.ts +70 -0
  57. package/dist/react/table-view/index.js +2155 -0
  58. package/dist/react/table-view/index.js.map +1 -0
  59. package/dist/react/table.d.ts +86 -0
  60. package/dist/react/table.js +414 -0
  61. package/dist/react/table.js.map +1 -0
  62. package/dist/react/tabs.d.ts +9 -3
  63. package/dist/react/tabs.js +217 -57
  64. package/dist/react/tabs.js.map +1 -1
  65. package/dist/react/textarea.d.ts +6 -0
  66. package/dist/react/textarea.js +33 -0
  67. package/dist/react/textarea.js.map +1 -0
  68. package/dist/react/time-picker.d.ts +22 -0
  69. package/dist/react/time-picker.js +856 -0
  70. package/dist/react/time-picker.js.map +1 -0
  71. package/dist/react/tooltip.d.ts +45 -0
  72. package/dist/react/tooltip.js +540 -0
  73. package/dist/react/tooltip.js.map +1 -0
  74. package/package.json +1 -1
@@ -0,0 +1,1269 @@
1
+ import * as React3 from 'react';
2
+ import React3__default, { useState, useCallback, useRef, useLayoutEffect, useMemo, useEffect } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { X, ChevronDown, Check, ChevronRight } from 'lucide-react';
5
+ import { clsx } from 'clsx';
6
+ import { twMerge } from 'tailwind-merge';
7
+ import { jsx, jsxs } from 'react/jsx-runtime';
8
+
9
+ function cn(...inputs) {
10
+ return twMerge(clsx(inputs));
11
+ }
12
+ var sizeClass = {
13
+ xs: "h-8 min-h-8 gap-2 px-4 text-sm has-[>svg]:px-3 [&_svg:not([class*='size-'])]:size-3",
14
+ default: "h-10 min-h-10 gap-2 px-5 text-sm has-[>svg]:px-4 [&_svg:not([class*='size-'])]:size-4",
15
+ lg: "h-11 min-h-11 gap-2 px-6 text-base has-[>svg]:px-5 [&_svg:not([class*='size-'])]:size-5"
16
+ };
17
+ var iconOnlySizeClass = {
18
+ xs: "size-8 min-h-8 min-w-8 gap-0 p-0 [&_svg:not([class*='size-'])]:size-3",
19
+ default: "size-10 min-h-10 min-w-10 gap-0 p-0 [&_svg:not([class*='size-'])]:size-4",
20
+ lg: "size-11 min-h-11 min-w-11 gap-0 p-0 [&_svg:not([class*='size-'])]:size-5"
21
+ };
22
+ var roundedClass = {
23
+ full: "rounded-full",
24
+ lg: "rounded-lg",
25
+ md: "rounded-md"
26
+ };
27
+ var variantClass = {
28
+ primary: "bg-primary text-background border border-transparent hover:bg-primary/90 active:bg-primary/85",
29
+ secondary: "bg-background text-primary border border-primary/10 hover:bg-primary/5",
30
+ destructive: "bg-background text-red-500 border border-red-500/25 hover:bg-red-500/5",
31
+ success: "bg-background text-green-500 border border-green-500/25 hover:bg-green-500/5"
32
+ };
33
+ var Button = React3.forwardRef(
34
+ ({
35
+ className,
36
+ type = "button",
37
+ variant = "secondary",
38
+ rounded: roundedProp,
39
+ size = "default",
40
+ iconOnly = false,
41
+ disabled,
42
+ ...props
43
+ }, ref) => {
44
+ const rounded = roundedProp ?? (iconOnly ? "md" : "full");
45
+ return /* @__PURE__ */ jsx(
46
+ "button",
47
+ {
48
+ type,
49
+ disabled,
50
+ "data-slot": "button",
51
+ "data-icon-only": iconOnly ? "" : void 0,
52
+ className: cn(
53
+ "inline-flex shrink-0 cursor-pointer items-center justify-center whitespace-nowrap outline-none scale-100 transition-[color,background-color,box-shadow,transform] duration-150 ease-out active:scale-[0.98] active:duration-100 active:ease-linear [&_svg]:pointer-events-none [&_svg]:shrink-0",
54
+ "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
55
+ "focus-visible:border-ring font-medium lg:tracking-wide focus-visible:ring-ring/50 focus-visible:ring-[3px]",
56
+ iconOnly ? iconOnlySizeClass[size] : sizeClass[size],
57
+ roundedClass[rounded],
58
+ variantClass[variant],
59
+ className
60
+ ),
61
+ ref,
62
+ ...props
63
+ }
64
+ );
65
+ }
66
+ );
67
+ Button.displayName = "Button";
68
+ var CHECKBOX_TICK_DELAY_MS = 60;
69
+ var CHECKBOX_TICK_DRAW_MS = 100;
70
+ function CheckboxAnimatedCheckMark() {
71
+ const lineRef = useRef(null);
72
+ useLayoutEffect(() => {
73
+ const poly = lineRef.current;
74
+ if (!poly || typeof poly.getTotalLength !== "function") return;
75
+ const len = poly.getTotalLength();
76
+ if (len <= 0) return;
77
+ poly.style.strokeDasharray = `${len}`;
78
+ poly.style.strokeDashoffset = `${len}`;
79
+ if (typeof poly.animate !== "function") {
80
+ poly.style.strokeDashoffset = "0";
81
+ return;
82
+ }
83
+ const anim = poly.animate(
84
+ [{ strokeDashoffset: len }, { strokeDashoffset: 0 }],
85
+ {
86
+ duration: CHECKBOX_TICK_DRAW_MS,
87
+ delay: CHECKBOX_TICK_DELAY_MS,
88
+ easing: "cubic-bezier(0.45, 0, 0.2, 1)",
89
+ fill: "forwards"
90
+ }
91
+ );
92
+ return () => anim.cancel();
93
+ }, []);
94
+ return /* @__PURE__ */ jsx(
95
+ "svg",
96
+ {
97
+ className: "size-4 shrink-0 overflow-visible",
98
+ viewBox: "0 0 24 24",
99
+ "aria-hidden": true,
100
+ children: /* @__PURE__ */ jsx(
101
+ "polyline",
102
+ {
103
+ ref: lineRef,
104
+ points: "4 12 9 17 20 6",
105
+ fill: "none",
106
+ stroke: "currentColor",
107
+ strokeWidth: "3",
108
+ strokeLinecap: "round",
109
+ strokeLinejoin: "round"
110
+ }
111
+ )
112
+ }
113
+ );
114
+ }
115
+ var Checkbox = React3.forwardRef(
116
+ ({
117
+ checked: checkedProp,
118
+ defaultChecked = false,
119
+ onCheckedChange,
120
+ indeterminate = false,
121
+ disabled = false,
122
+ className,
123
+ onClick,
124
+ ...rest
125
+ }, ref) => {
126
+ const isControlled = checkedProp !== void 0;
127
+ const [uncontrolledChecked, setUncontrolledChecked] = useState(
128
+ defaultChecked
129
+ );
130
+ const checked = isControlled ? checkedProp : uncontrolledChecked;
131
+ const ariaChecked = indeterminate ? "mixed" : checked ? true : false;
132
+ return /* @__PURE__ */ jsx(
133
+ "button",
134
+ {
135
+ ref,
136
+ type: "button",
137
+ role: "checkbox",
138
+ "data-slot": "checkbox",
139
+ "data-state": indeterminate ? "indeterminate" : checked ? "checked" : "unchecked",
140
+ disabled,
141
+ "aria-checked": ariaChecked,
142
+ className: cn(
143
+ "ring-offset-background focus-visible:ring-primary/40 inline-flex shrink-0 items-center justify-center rounded-md border border-transparent p-0 outline-none select-none",
144
+ "focus-visible:ring-2 focus-visible:ring-offset-2",
145
+ "disabled:cursor-not-allowed",
146
+ className
147
+ ),
148
+ onClick: (e) => {
149
+ onClick?.(e);
150
+ if (e.defaultPrevented) return;
151
+ if (disabled) return;
152
+ if (isControlled) {
153
+ onCheckedChange?.(!checked);
154
+ return;
155
+ }
156
+ setUncontrolledChecked((prev) => {
157
+ const next = !prev;
158
+ onCheckedChange?.(next);
159
+ return next;
160
+ });
161
+ },
162
+ ...rest,
163
+ children: /* @__PURE__ */ jsx("span", { className: "pointer-events-none", children: /* @__PURE__ */ jsx(
164
+ "span",
165
+ {
166
+ "aria-hidden": true,
167
+ className: cn(
168
+ "flex size-5 shrink-0 items-center justify-center rounded-[4px] border",
169
+ !disabled && (indeterminate ? "border-primary bg-primary text-background" : checked ? "border-primary bg-primary text-background" : "border-primary/20 bg-background"),
170
+ disabled && (indeterminate ? "border-primary/40 bg-primary/45 text-primary-foreground" : checked ? "border-primary/40 bg-primary/45 text-primary-foreground" : "border-primary/10 bg-muted/25")
171
+ ),
172
+ children: indeterminate ? /* @__PURE__ */ jsx(
173
+ "span",
174
+ {
175
+ className: "size-2.5 shrink-0 rounded-[2px] border border-primary/20 bg-background",
176
+ "aria-hidden": true
177
+ }
178
+ ) : checked ? /* @__PURE__ */ jsx(CheckboxAnimatedCheckMark, {}) : null
179
+ }
180
+ ) })
181
+ }
182
+ );
183
+ }
184
+ );
185
+ Checkbox.displayName = "Checkbox";
186
+ var DROPDOWN_PANEL_OPEN_EASING = "cubic-bezier(0,0.55,0.45,1)";
187
+ var DROPDOWN_PANEL_CLOSE_EASING = "cubic-bezier(0.55,0,1,0.45)";
188
+ var DROPDOWN_MENU_MIN_WIDTH_PX = 192;
189
+ var DROPDOWN_MOBILE_SHEET_MAX_PX = 1024;
190
+ var DROPDOWN_MOBILE_SHEET_MOTION_MS = 175;
191
+ var DROPDOWN_MOBILE_SHEET_ENTRY_EASING = "cubic-bezier(0.85, 0, 0.15, 1)";
192
+ var DROPDOWN_MOBILE_SHEET_EXIT_EASING = "cubic-bezier(0.85, 0, 1, 0.15)";
193
+ var DROPDOWN_MOBILE_SHEET_SLIDE_ENTRANCE_OFFSET_DEFAULT_PX = 120;
194
+ function resolveDropdownMobileSheet(mobileOptions) {
195
+ return {
196
+ sheet: mobileOptions?.sheet ?? true,
197
+ title: mobileOptions?.title,
198
+ sheetExtraClassName: mobileOptions?.className,
199
+ contentClassName: mobileOptions?.contentClassName
200
+ };
201
+ }
202
+ var DROPDOWN_SUB_CONTENT_ATTR = "data-dropdown-sub-content";
203
+ var DROPDOWN_SHEET_MENU_TEXT = "max-[1024px]:text-base";
204
+ var DROPDOWN_SHEET_MENU_SHORTCUT = "max-[1024px]:text-sm";
205
+ var DROPDOWN_SHEET_MENU_LABEL_TEXT = "max-[1024px]:text-sm";
206
+ var DROPDOWN_CONTENT_ORIGIN = {
207
+ bottom: "top center",
208
+ top: "bottom center",
209
+ left: "right center",
210
+ right: "left center"
211
+ };
212
+ var DROPDOWN_CONTENT_HIDDEN = {
213
+ bottom: "translateY(-4px) scale(0.97)",
214
+ top: "translateY(4px) scale(0.97)",
215
+ left: "translateX(4px) scale(0.97)",
216
+ right: "translateX(-4px) scale(0.97)"
217
+ };
218
+ var SUB_CONTENT_ORIGIN = {
219
+ bottom: "top center",
220
+ top: "bottom center",
221
+ left: "right top",
222
+ right: "left top"
223
+ };
224
+ function computePos(trigger, menu, side, align, offset, pad) {
225
+ const tr = trigger.getBoundingClientRect();
226
+ const mr = menu.getBoundingClientRect();
227
+ const vw = window.innerWidth;
228
+ const vh = window.innerHeight;
229
+ const sx = window.scrollX;
230
+ const sy = window.scrollY;
231
+ let top = 0;
232
+ let left = 0;
233
+ let effectiveSide = side;
234
+ const calc = (s) => {
235
+ switch (s) {
236
+ case "bottom":
237
+ top = tr.bottom + sy + offset;
238
+ if (align === "start") left = tr.left + sx;
239
+ else if (align === "end") left = tr.right + sx - mr.width;
240
+ else left = tr.left + sx + tr.width / 2 - mr.width / 2;
241
+ break;
242
+ case "top":
243
+ top = tr.top + sy - mr.height - offset;
244
+ if (align === "start") left = tr.left + sx;
245
+ else if (align === "end") left = tr.right + sx - mr.width;
246
+ else left = tr.left + sx + tr.width / 2 - mr.width / 2;
247
+ break;
248
+ case "right":
249
+ left = tr.right + sx + offset;
250
+ if (align === "start") top = tr.top + sy;
251
+ else if (align === "end") top = tr.bottom + sy - mr.height;
252
+ else top = tr.top + sy + tr.height / 2 - mr.height / 2;
253
+ break;
254
+ case "left":
255
+ left = tr.left + sx - mr.width - offset;
256
+ if (align === "start") top = tr.top + sy;
257
+ else if (align === "end") top = tr.bottom + sy - mr.height;
258
+ else top = tr.top + sy + tr.height / 2 - mr.height / 2;
259
+ break;
260
+ }
261
+ };
262
+ calc(side);
263
+ if (side === "bottom" && top + mr.height > vh + sy - pad) {
264
+ const ft = tr.top + sy - mr.height - offset;
265
+ if (ft >= sy + pad) {
266
+ effectiveSide = "top";
267
+ top = ft;
268
+ }
269
+ } else if (side === "top" && top < sy + pad) {
270
+ const ft = tr.bottom + sy + offset;
271
+ if (ft + mr.height <= vh + sy - pad) {
272
+ effectiveSide = "bottom";
273
+ top = ft;
274
+ }
275
+ } else if (side === "right" && left + mr.width > vw + sx - pad) {
276
+ const fl = tr.left + sx - mr.width - offset;
277
+ if (fl >= sx + pad) {
278
+ effectiveSide = "left";
279
+ left = fl;
280
+ }
281
+ } else if (side === "left" && left < sx + pad) {
282
+ const fl = tr.right + sx + offset;
283
+ if (fl + mr.width <= vw + sx - pad) {
284
+ effectiveSide = "right";
285
+ left = fl;
286
+ }
287
+ }
288
+ left = Math.max(sx + pad, Math.min(left, vw + sx - mr.width - pad));
289
+ top = Math.max(sy + pad, Math.min(top, vh + sy - mr.height - pad));
290
+ return { top, left, side: effectiveSide };
291
+ }
292
+ function useIsMobile(breakpoint = 1025) {
293
+ const [isMobile, setIsMobile] = useState(false);
294
+ useEffect(() => {
295
+ const mq = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
296
+ setIsMobile(mq.matches);
297
+ const handler = (e) => setIsMobile(e.matches);
298
+ mq.addEventListener("change", handler);
299
+ return () => mq.removeEventListener("change", handler);
300
+ }, [breakpoint]);
301
+ return isMobile;
302
+ }
303
+ var DropdownContext = React3__default.createContext(void 0);
304
+ function useDropdown() {
305
+ const ctx = React3__default.useContext(DropdownContext);
306
+ if (!ctx)
307
+ throw new Error("Dropdown components must be used within <Dropdown />");
308
+ return ctx;
309
+ }
310
+ var SubContext = React3__default.createContext(void 0);
311
+ var Dropdown = ({
312
+ children,
313
+ open: controlledOpen,
314
+ onOpenChange
315
+ }) => {
316
+ const [internalOpen, setInternalOpen] = useState(false);
317
+ const isControlled = controlledOpen !== void 0;
318
+ const open = isControlled ? controlledOpen : internalOpen;
319
+ const triggerRef = useRef(null);
320
+ const [radioValues, setRadioValues] = useState({});
321
+ const setOpen = useCallback(
322
+ (v) => {
323
+ if (!isControlled) setInternalOpen(v);
324
+ onOpenChange?.(v);
325
+ },
326
+ [isControlled, onOpenChange]
327
+ );
328
+ const setRadioValue = useCallback((group, value) => {
329
+ setRadioValues((prev) => ({ ...prev, [group]: value }));
330
+ }, []);
331
+ const ctx = useMemo(
332
+ () => ({ open, setOpen, triggerRef, radioValues, setRadioValue }),
333
+ [open, setOpen, radioValues, setRadioValue]
334
+ );
335
+ return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: ctx, children });
336
+ };
337
+ function mergeTriggerRef(triggerRef, node, childRef) {
338
+ triggerRef.current = node;
339
+ if (typeof childRef === "function") childRef(node);
340
+ else if (childRef && typeof childRef === "object")
341
+ childRef.current = node;
342
+ }
343
+ var DropdownTrigger = React3__default.forwardRef(
344
+ ({
345
+ children,
346
+ asChild,
347
+ className,
348
+ onClick: onClickProp,
349
+ disabled,
350
+ ...buttonProps
351
+ }, ref) => {
352
+ const { open, setOpen, triggerRef } = useDropdown();
353
+ const handleClick = useCallback(
354
+ (e) => {
355
+ onClickProp?.(e);
356
+ e.stopPropagation();
357
+ if (disabled) return;
358
+ setOpen(!open);
359
+ },
360
+ [disabled, onClickProp, open, setOpen]
361
+ );
362
+ const setButtonRef = useCallback(
363
+ (el) => {
364
+ triggerRef.current = el;
365
+ if (typeof ref === "function") ref(el);
366
+ else if (ref) ref.current = el;
367
+ },
368
+ [ref, triggerRef]
369
+ );
370
+ if (asChild && React3__default.isValidElement(children)) {
371
+ const child = children;
372
+ return React3__default.cloneElement(child, {
373
+ ref: (node) => {
374
+ mergeTriggerRef(
375
+ triggerRef,
376
+ node,
377
+ child.ref
378
+ );
379
+ },
380
+ className: cn("group", child.props.className),
381
+ onClick: (e) => {
382
+ child.props.onClick?.(e);
383
+ handleClick(e);
384
+ },
385
+ "aria-expanded": open,
386
+ "aria-haspopup": "menu",
387
+ "data-state": open ? "open" : "closed",
388
+ ...disabled !== void 0 ? {
389
+ disabled
390
+ } : {}
391
+ });
392
+ }
393
+ return /* @__PURE__ */ jsx(
394
+ Button,
395
+ {
396
+ ref: setButtonRef,
397
+ type: "button",
398
+ ...buttonProps,
399
+ disabled,
400
+ className: cn("group", className),
401
+ "aria-expanded": open,
402
+ "aria-haspopup": "menu",
403
+ "data-state": open ? "open" : "closed",
404
+ onClick: handleClick,
405
+ children
406
+ }
407
+ );
408
+ }
409
+ );
410
+ DropdownTrigger.displayName = "DropdownTrigger";
411
+ function DropdownChevron({
412
+ className,
413
+ ...props
414
+ }) {
415
+ const { open } = useDropdown();
416
+ return /* @__PURE__ */ jsx(
417
+ ChevronDown,
418
+ {
419
+ "aria-hidden": true,
420
+ className: cn("size-4 shrink-0", open && "rotate-180", className),
421
+ ...props
422
+ }
423
+ );
424
+ }
425
+ var DropdownMobileClose = React3__default.forwardRef(({ className, type = "button", ...props }, ref) => {
426
+ return /* @__PURE__ */ jsxs(
427
+ "button",
428
+ {
429
+ ref,
430
+ type,
431
+ className: cn(
432
+ "z-100 flex size-12 shrink-0 cursor-pointer items-center justify-center rounded-full transition-all hover:bg-secondary-background active:scale-[0.96]",
433
+ className
434
+ ),
435
+ ...props,
436
+ children: [
437
+ /* @__PURE__ */ jsx(X, { className: "size-5.5" }),
438
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
439
+ ]
440
+ }
441
+ );
442
+ });
443
+ DropdownMobileClose.displayName = "DropdownMobileClose";
444
+ function DropdownMobileBottomSheetPortal({
445
+ open,
446
+ isAnimating,
447
+ slideEntrance,
448
+ slideOffsetPx,
449
+ sheetTitle,
450
+ sheetExtraClassName,
451
+ contentClassName,
452
+ onRequestClose,
453
+ menuRef,
454
+ portalZClassName = "z-50",
455
+ children,
456
+ className,
457
+ style,
458
+ ...panelProps
459
+ }) {
460
+ const sheetMotion = open ? DROPDOWN_MOBILE_SHEET_ENTRY_EASING : DROPDOWN_MOBILE_SHEET_EXIT_EASING;
461
+ const sheetHiddenTransform = slideEntrance ? `translateY(${slideOffsetPx}px)` : "translateY(100%)";
462
+ return createPortal(
463
+ /* @__PURE__ */ jsxs(
464
+ "div",
465
+ {
466
+ className: cn(
467
+ "fixed inset-0 flex items-end justify-center p-0",
468
+ portalZClassName
469
+ ),
470
+ children: [
471
+ /* @__PURE__ */ jsx(
472
+ "div",
473
+ {
474
+ className: cn(
475
+ "fixed inset-0 bg-black/40 dark:bg-black/60",
476
+ isAnimating ? "opacity-100" : "opacity-0"
477
+ ),
478
+ style: {
479
+ transitionProperty: "opacity",
480
+ transitionDuration: `${DROPDOWN_MOBILE_SHEET_MOTION_MS}ms`,
481
+ transitionTimingFunction: sheetMotion
482
+ },
483
+ onClick: onRequestClose
484
+ }
485
+ ),
486
+ /* @__PURE__ */ jsxs(
487
+ "div",
488
+ {
489
+ ...panelProps,
490
+ ref: menuRef,
491
+ className: cn(
492
+ "bg-background border-primary/10 relative z-10 flex w-full max-h-[min(90dvh,calc(100dvh-env(safe-area-inset-bottom,0px)))] flex-col overflow-hidden shadow-2xl outline-none",
493
+ "rounded-t-2xl rounded-b-none border-x-0 border-b-0 border-t",
494
+ sheetExtraClassName,
495
+ className
496
+ ),
497
+ style: {
498
+ transform: isAnimating ? "translateY(0)" : sheetHiddenTransform,
499
+ opacity: isAnimating ? 1 : 0,
500
+ transitionProperty: "transform, opacity",
501
+ transitionDuration: `${DROPDOWN_MOBILE_SHEET_MOTION_MS}ms`,
502
+ transitionTimingFunction: sheetMotion,
503
+ ...style
504
+ },
505
+ children: [
506
+ /* @__PURE__ */ jsxs(
507
+ "div",
508
+ {
509
+ className: cn(
510
+ "flex w-full shrink-0 items-center py-2 pl-4 pr-2",
511
+ sheetTitle ? "justify-between gap-3" : "justify-end"
512
+ ),
513
+ children: [
514
+ sheetTitle ? /* @__PURE__ */ jsx("p", { className: "text-foreground min-w-0 flex-1 truncate text-base font-semibold", children: sheetTitle }) : null,
515
+ /* @__PURE__ */ jsx(
516
+ DropdownMobileClose,
517
+ {
518
+ onClick: (e) => {
519
+ e.stopPropagation();
520
+ onRequestClose();
521
+ }
522
+ }
523
+ )
524
+ ]
525
+ }
526
+ ),
527
+ /* @__PURE__ */ jsx(
528
+ "div",
529
+ {
530
+ className: cn(
531
+ "min-h-0 flex-1 overflow-y-auto pb-[calc(5rem+env(safe-area-inset-bottom,0px))]",
532
+ contentClassName
533
+ ),
534
+ children
535
+ }
536
+ )
537
+ ]
538
+ }
539
+ )
540
+ ]
541
+ }
542
+ ),
543
+ document.body
544
+ );
545
+ }
546
+ var DropdownContent = ({
547
+ children,
548
+ side = "bottom",
549
+ align = "start",
550
+ offset = 10,
551
+ duration = 80,
552
+ viewportPadding = 8,
553
+ closeOnEscape = true,
554
+ minWidth = "trigger",
555
+ loop = true,
556
+ mobileOptions,
557
+ slideEntrance = true,
558
+ slideEntranceOffsetPx: slideEntranceOffsetPxProp,
559
+ className,
560
+ style,
561
+ ...props
562
+ }) => {
563
+ const isMobile = useIsMobile(DROPDOWN_MOBILE_SHEET_MAX_PX + 1);
564
+ const { open, setOpen, triggerRef } = useDropdown();
565
+ const [shouldRender, setShouldRender] = useState(false);
566
+ const [isAnimating, setIsAnimating] = useState(false);
567
+ const [pos, setPos] = useState({ top: -9999, left: -9999, side });
568
+ const [triggerW, setTriggerW] = useState(0);
569
+ const menuRef = useRef(null);
570
+ const resolvedMobile = useMemo(
571
+ () => resolveDropdownMobileSheet(mobileOptions),
572
+ [mobileOptions]
573
+ );
574
+ const slideOffsetPx = useMemo(
575
+ () => slideEntranceOffsetPxProp ?? DROPDOWN_MOBILE_SHEET_SLIDE_ENTRANCE_OFFSET_DEFAULT_PX,
576
+ [slideEntranceOffsetPxProp]
577
+ );
578
+ const closeDuration = isMobile && resolvedMobile.sheet ? DROPDOWN_MOBILE_SHEET_MOTION_MS : duration;
579
+ const closeMenu = useCallback(() => setOpen(false), [setOpen]);
580
+ useEffect(() => {
581
+ if (open) {
582
+ setShouldRender(true);
583
+ } else {
584
+ setIsAnimating(false);
585
+ const timer = setTimeout(() => setShouldRender(false), closeDuration);
586
+ return () => clearTimeout(timer);
587
+ }
588
+ }, [open, closeDuration]);
589
+ useEffect(() => {
590
+ if (!shouldRender || !open) return;
591
+ let raf2 = 0;
592
+ const raf1 = requestAnimationFrame(() => {
593
+ raf2 = requestAnimationFrame(() => setIsAnimating(true));
594
+ });
595
+ return () => {
596
+ cancelAnimationFrame(raf1);
597
+ if (raf2) cancelAnimationFrame(raf2);
598
+ };
599
+ }, [shouldRender, open]);
600
+ useLayoutEffect(() => {
601
+ if (!shouldRender || !triggerRef.current || !menuRef.current) return;
602
+ const update = () => {
603
+ if (!triggerRef.current || !menuRef.current) return;
604
+ setTriggerW(triggerRef.current.getBoundingClientRect().width);
605
+ setPos(
606
+ computePos(
607
+ triggerRef.current,
608
+ menuRef.current,
609
+ side,
610
+ align,
611
+ offset,
612
+ viewportPadding
613
+ )
614
+ );
615
+ };
616
+ update();
617
+ window.addEventListener("resize", update);
618
+ window.addEventListener("scroll", update, true);
619
+ return () => {
620
+ window.removeEventListener("resize", update);
621
+ window.removeEventListener("scroll", update, true);
622
+ };
623
+ }, [shouldRender, side, align, offset, viewportPadding]);
624
+ useEffect(() => {
625
+ if (isAnimating && menuRef.current) {
626
+ menuRef.current.focus();
627
+ }
628
+ }, [isAnimating]);
629
+ useEffect(() => {
630
+ if (!open) return;
631
+ const handler = (e) => {
632
+ const t = e.target;
633
+ if (menuRef.current?.contains(t) || triggerRef.current?.contains(t))
634
+ return;
635
+ const el = e.target instanceof Element ? e.target : null;
636
+ if (el?.closest?.(`[${DROPDOWN_SUB_CONTENT_ATTR}]`)) return;
637
+ setOpen(false);
638
+ };
639
+ document.addEventListener("mousedown", handler);
640
+ return () => document.removeEventListener("mousedown", handler);
641
+ }, [open, setOpen, triggerRef]);
642
+ useEffect(() => {
643
+ if (!open) return;
644
+ const handler = (e) => {
645
+ const menu = menuRef.current;
646
+ if (!menu) return;
647
+ const focusedEl = document.activeElement;
648
+ if (focusedEl && !menu.contains(focusedEl)) return;
649
+ const items = getItems(menu);
650
+ const idx = items.indexOf(focusedEl);
651
+ switch (e.key) {
652
+ case "Escape":
653
+ if (closeOnEscape) {
654
+ e.preventDefault();
655
+ setOpen(false);
656
+ triggerRef.current?.focus();
657
+ }
658
+ break;
659
+ case "ArrowDown":
660
+ e.preventDefault();
661
+ if (items.length === 0) break;
662
+ if (idx === -1 || idx === items.length - 1 && loop)
663
+ items[0]?.focus();
664
+ else if (idx < items.length - 1) items[idx + 1]?.focus();
665
+ break;
666
+ case "ArrowUp":
667
+ e.preventDefault();
668
+ if (items.length === 0) break;
669
+ if (idx <= 0 && loop) items[items.length - 1]?.focus();
670
+ else if (idx > 0) items[idx - 1]?.focus();
671
+ break;
672
+ case "Home":
673
+ e.preventDefault();
674
+ items[0]?.focus();
675
+ break;
676
+ case "End":
677
+ e.preventDefault();
678
+ items[items.length - 1]?.focus();
679
+ break;
680
+ case "Tab":
681
+ setOpen(false);
682
+ break;
683
+ }
684
+ };
685
+ window.addEventListener("keydown", handler);
686
+ return () => window.removeEventListener("keydown", handler);
687
+ }, [open, closeOnEscape, loop, setOpen, triggerRef]);
688
+ useEffect(() => {
689
+ if (open && isMobile && resolvedMobile.sheet) {
690
+ document.body.style.overflow = "hidden";
691
+ }
692
+ return () => {
693
+ document.body.style.overflow = "";
694
+ };
695
+ }, [open, isMobile, resolvedMobile.sheet]);
696
+ if (!shouldRender || typeof document === "undefined") return null;
697
+ if (isMobile && resolvedMobile.sheet) {
698
+ return /* @__PURE__ */ jsx(
699
+ DropdownMobileBottomSheetPortal,
700
+ {
701
+ ...props,
702
+ open,
703
+ isAnimating,
704
+ slideEntrance,
705
+ slideOffsetPx,
706
+ sheetTitle: resolvedMobile.title,
707
+ sheetExtraClassName: resolvedMobile.sheetExtraClassName,
708
+ contentClassName: resolvedMobile.contentClassName,
709
+ onRequestClose: closeMenu,
710
+ menuRef,
711
+ portalZClassName: "z-50",
712
+ className,
713
+ style,
714
+ role: "menu",
715
+ "aria-orientation": "vertical",
716
+ tabIndex: -1,
717
+ children
718
+ }
719
+ );
720
+ }
721
+ const resolvedMinW = minWidth === "trigger" ? Math.max(triggerW, DROPDOWN_MENU_MIN_WIDTH_PX) : minWidth;
722
+ return createPortal(
723
+ /* @__PURE__ */ jsx(
724
+ "div",
725
+ {
726
+ ...props,
727
+ ref: menuRef,
728
+ role: "menu",
729
+ "aria-orientation": "vertical",
730
+ tabIndex: -1,
731
+ className: cn(
732
+ "bg-background border-primary/10 absolute z-50 overflow-hidden rounded-xl border py-1.5 shadow-xl outline-none",
733
+ className
734
+ ),
735
+ style: {
736
+ position: "absolute",
737
+ top: pos.top,
738
+ left: pos.left,
739
+ minWidth: resolvedMinW,
740
+ transformOrigin: DROPDOWN_CONTENT_ORIGIN[pos.side],
741
+ transform: isAnimating ? "none" : DROPDOWN_CONTENT_HIDDEN[pos.side],
742
+ opacity: isAnimating ? 1 : 0,
743
+ transitionProperty: "opacity, transform",
744
+ transitionDuration: `${duration}ms`,
745
+ transitionTimingFunction: isAnimating ? DROPDOWN_PANEL_OPEN_EASING : DROPDOWN_PANEL_CLOSE_EASING,
746
+ ...style
747
+ },
748
+ children
749
+ }
750
+ ),
751
+ document.body
752
+ );
753
+ };
754
+ function getItems(menu) {
755
+ return Array.from(
756
+ menu.querySelectorAll(
757
+ '[role="menuitem"]:not([aria-disabled="true"]),[role="menuitemcheckbox"]:not([aria-disabled="true"]),[role="menuitemradio"]:not([aria-disabled="true"])'
758
+ )
759
+ );
760
+ }
761
+ var DropdownItem = ({
762
+ children,
763
+ disabled = false,
764
+ destructive = false,
765
+ icon,
766
+ shortcut,
767
+ closeOnSelect = true,
768
+ inset = false,
769
+ className,
770
+ onClick,
771
+ ...props
772
+ }) => {
773
+ const { setOpen } = useDropdown();
774
+ const handleClick = (e) => {
775
+ if (disabled) return;
776
+ onClick?.(e);
777
+ if (closeOnSelect) setOpen(false);
778
+ };
779
+ const handleKeyDown = (e) => {
780
+ if (e.key === "Enter" || e.key === " ") {
781
+ e.preventDefault();
782
+ handleClick(e);
783
+ }
784
+ };
785
+ return /* @__PURE__ */ jsxs(
786
+ "div",
787
+ {
788
+ ...props,
789
+ role: "menuitem",
790
+ tabIndex: disabled ? void 0 : -1,
791
+ "aria-disabled": disabled,
792
+ onClick: handleClick,
793
+ onKeyDown: handleKeyDown,
794
+ className: cn(
795
+ "relative mx-1.5 flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors duration-0 outline-none select-none",
796
+ DROPDOWN_SHEET_MENU_TEXT,
797
+ inset && "pl-9",
798
+ !disabled && "cursor-pointer",
799
+ disabled && "lg:cursor-not-allowed",
800
+ !disabled && !destructive && "text-foreground hover:bg-primary/8 focus-visible:bg-primary/8 dark:hover:bg-primary/4 dark:focus-visible:bg-primary/4",
801
+ !disabled && destructive && "text-destructive hover:bg-destructive/10 focus-visible:bg-destructive/10 dark:text-destructive-foreground dark:hover:bg-destructive-foreground/18 dark:focus-visible:bg-destructive-foreground/18",
802
+ disabled && !destructive && "text-foreground/45 dark:text-foreground/50",
803
+ disabled && destructive && "bg-destructive/5 text-destructive/75 dark:bg-destructive-foreground/12 dark:text-destructive-foreground/78",
804
+ className
805
+ ),
806
+ children: [
807
+ icon && /* @__PURE__ */ jsx("span", { className: "flex size-4 shrink-0 items-center justify-center", children: icon }),
808
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children }),
809
+ shortcut ? /* @__PURE__ */ jsx(
810
+ "span",
811
+ {
812
+ className: cn(
813
+ "ml-auto text-xs tracking-widest",
814
+ DROPDOWN_SHEET_MENU_SHORTCUT,
815
+ destructive ? "text-destructive/70 dark:text-destructive-foreground/80" : "opacity-40"
816
+ ),
817
+ children: shortcut
818
+ }
819
+ ) : null
820
+ ]
821
+ }
822
+ );
823
+ };
824
+ var DropdownSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx(
825
+ "div",
826
+ {
827
+ role: "separator",
828
+ className: cn("border-primary/10 my-1.5 border-t", className),
829
+ ...props
830
+ }
831
+ );
832
+ var DropdownLabel = ({ className, inset, ...props }) => /* @__PURE__ */ jsx(
833
+ "div",
834
+ {
835
+ className: cn(
836
+ "text-primary/50 px-4 py-2 text-xs font-medium",
837
+ DROPDOWN_SHEET_MENU_LABEL_TEXT,
838
+ inset && "pl-9",
839
+ className
840
+ ),
841
+ ...props
842
+ }
843
+ );
844
+ var DropdownCheckboxItem = ({
845
+ children,
846
+ checked = false,
847
+ onCheckedChange,
848
+ disabled = false,
849
+ shortcut,
850
+ closeOnSelect = false,
851
+ className,
852
+ onClick,
853
+ ...props
854
+ }) => {
855
+ const { setOpen } = useDropdown();
856
+ const handleClick = (e) => {
857
+ if (disabled) return;
858
+ onCheckedChange?.(!checked);
859
+ onClick?.(e);
860
+ if (closeOnSelect) setOpen(false);
861
+ };
862
+ const handleKeyDown = (e) => {
863
+ if (e.key === "Enter" || e.key === " ") {
864
+ e.preventDefault();
865
+ handleClick(e);
866
+ }
867
+ };
868
+ return /* @__PURE__ */ jsxs(
869
+ "div",
870
+ {
871
+ ...props,
872
+ role: "menuitemcheckbox",
873
+ "aria-checked": checked,
874
+ tabIndex: disabled ? void 0 : -1,
875
+ "aria-disabled": disabled,
876
+ onClick: handleClick,
877
+ onKeyDown: handleKeyDown,
878
+ className: cn(
879
+ "mx-1.5 flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors duration-0 outline-none select-none",
880
+ DROPDOWN_SHEET_MENU_TEXT,
881
+ !disabled && "cursor-pointer text-foreground hover:bg-primary/8 focus-visible:bg-primary/8 dark:hover:bg-primary/4 dark:focus-visible:bg-primary/4",
882
+ disabled && "lg:cursor-not-allowed text-foreground/45 dark:text-foreground/50",
883
+ className
884
+ ),
885
+ children: [
886
+ /* @__PURE__ */ jsx("span", { className: "mr-1 inline-flex shrink-0", "aria-hidden": true, children: /* @__PURE__ */ jsx(
887
+ Checkbox,
888
+ {
889
+ checked,
890
+ disabled,
891
+ tabIndex: -1,
892
+ className: "pointer-events-none"
893
+ }
894
+ ) }),
895
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1", children }),
896
+ shortcut && /* @__PURE__ */ jsx(
897
+ "span",
898
+ {
899
+ className: cn(
900
+ "ml-auto text-xs tracking-widest opacity-40",
901
+ DROPDOWN_SHEET_MENU_SHORTCUT
902
+ ),
903
+ children: shortcut
904
+ }
905
+ )
906
+ ]
907
+ }
908
+ );
909
+ };
910
+ var DropdownRadioGroup = ({ children, value, onValueChange, group }) => {
911
+ const { setRadioValue, radioValues } = useDropdown();
912
+ const resolvedValue = value ?? radioValues[group] ?? "";
913
+ const handleValueChange = useCallback(
914
+ (v) => {
915
+ setRadioValue(group, v);
916
+ onValueChange?.(v);
917
+ },
918
+ [group, onValueChange, setRadioValue]
919
+ );
920
+ const radioCtx = useMemo(
921
+ () => ({
922
+ group,
923
+ value: resolvedValue,
924
+ onValueChange: handleValueChange
925
+ }),
926
+ [group, resolvedValue, handleValueChange]
927
+ );
928
+ return /* @__PURE__ */ jsx(RadioGroupContext.Provider, { value: radioCtx, children });
929
+ };
930
+ var RadioGroupContext = React3__default.createContext(
931
+ void 0
932
+ );
933
+ function useRadioGroup() {
934
+ const ctx = React3__default.useContext(RadioGroupContext);
935
+ if (!ctx)
936
+ throw new Error("DropdownRadioItem must be inside DropdownRadioGroup");
937
+ return ctx;
938
+ }
939
+ var DropdownRadioItem = ({
940
+ children,
941
+ value,
942
+ disabled = false,
943
+ shortcut,
944
+ closeOnSelect = false,
945
+ className,
946
+ onClick,
947
+ ...props
948
+ }) => {
949
+ const { setOpen } = useDropdown();
950
+ const { value: groupValue, onValueChange } = useRadioGroup();
951
+ const checked = groupValue === value;
952
+ const handleClick = (e) => {
953
+ if (disabled) return;
954
+ onValueChange(value);
955
+ onClick?.(e);
956
+ if (closeOnSelect) setOpen(false);
957
+ };
958
+ const handleKeyDown = (e) => {
959
+ if (e.key === "Enter" || e.key === " ") {
960
+ e.preventDefault();
961
+ handleClick(e);
962
+ }
963
+ };
964
+ return /* @__PURE__ */ jsxs(
965
+ "div",
966
+ {
967
+ ...props,
968
+ role: "menuitemradio",
969
+ "aria-checked": checked,
970
+ tabIndex: disabled ? void 0 : -1,
971
+ "aria-disabled": disabled,
972
+ onClick: handleClick,
973
+ onKeyDown: handleKeyDown,
974
+ className: cn(
975
+ "relative mx-1.5 flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors duration-0 outline-none select-none",
976
+ DROPDOWN_SHEET_MENU_TEXT,
977
+ !disabled && "cursor-pointer text-foreground hover:bg-primary/8 focus-visible:bg-primary/8 dark:hover:bg-primary/4 dark:focus-visible:bg-primary/4",
978
+ disabled && "lg:cursor-not-allowed text-foreground/45 dark:text-foreground/50",
979
+ className
980
+ ),
981
+ children: [
982
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1", children }),
983
+ shortcut ? /* @__PURE__ */ jsx(
984
+ "span",
985
+ {
986
+ className: cn(
987
+ "shrink-0 text-xs tracking-widest opacity-40",
988
+ DROPDOWN_SHEET_MENU_SHORTCUT
989
+ ),
990
+ children: shortcut
991
+ }
992
+ ) : null,
993
+ /* @__PURE__ */ jsx(
994
+ "span",
995
+ {
996
+ className: "flex size-4 shrink-0 items-center justify-center",
997
+ "aria-hidden": true,
998
+ children: checked ? /* @__PURE__ */ jsx(Check, { className: "size-3.5", strokeWidth: 2.5 }) : null
999
+ }
1000
+ )
1001
+ ]
1002
+ }
1003
+ );
1004
+ };
1005
+ var DropdownSub = ({
1006
+ children
1007
+ }) => {
1008
+ const [open, setOpen] = useState(false);
1009
+ const triggerRef = useRef(null);
1010
+ const subCtx = useMemo(
1011
+ () => ({ open, setOpen, triggerRef }),
1012
+ [open, setOpen]
1013
+ );
1014
+ return /* @__PURE__ */ jsx(SubContext.Provider, { value: subCtx, children });
1015
+ };
1016
+ var DropdownSubTrigger = ({ children, icon, inset, disabled = false, className, ...props }) => {
1017
+ const sub = React3__default.useContext(SubContext);
1018
+ if (!sub) throw new Error("DropdownSubTrigger must be inside DropdownSub");
1019
+ const { open, setOpen, triggerRef } = sub;
1020
+ const isMobile = useIsMobile(DROPDOWN_MOBILE_SHEET_MAX_PX + 1);
1021
+ return /* @__PURE__ */ jsxs(
1022
+ "div",
1023
+ {
1024
+ ...props,
1025
+ ref: (el) => {
1026
+ triggerRef.current = el;
1027
+ },
1028
+ role: "menuitem",
1029
+ "aria-haspopup": "menu",
1030
+ "aria-expanded": open,
1031
+ tabIndex: disabled ? void 0 : -1,
1032
+ "aria-disabled": disabled,
1033
+ onMouseEnter: () => {
1034
+ if (disabled || isMobile) return;
1035
+ setOpen(true);
1036
+ },
1037
+ onMouseLeave: () => {
1038
+ if (isMobile) return;
1039
+ setOpen(false);
1040
+ },
1041
+ onKeyDown: (e) => {
1042
+ if (disabled) return;
1043
+ if (e.key === "ArrowRight" || e.key === "Enter") {
1044
+ e.preventDefault();
1045
+ setOpen(true);
1046
+ }
1047
+ },
1048
+ onClick: () => {
1049
+ if (disabled) return;
1050
+ setOpen(!open);
1051
+ },
1052
+ className: cn(
1053
+ "relative mx-1.5 flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors duration-0 outline-none select-none",
1054
+ DROPDOWN_SHEET_MENU_TEXT,
1055
+ inset && "pl-9",
1056
+ !disabled && "cursor-pointer text-foreground hover:bg-primary/8 focus-visible:bg-primary/8 dark:hover:bg-primary/4 dark:focus-visible:bg-primary/4",
1057
+ disabled && "lg:cursor-not-allowed text-foreground/45 dark:text-foreground/50",
1058
+ className
1059
+ ),
1060
+ children: [
1061
+ icon && /* @__PURE__ */ jsx("span", { className: "flex size-4 shrink-0 items-center justify-center", children: icon }),
1062
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children }),
1063
+ /* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto size-4 opacity-50" })
1064
+ ]
1065
+ }
1066
+ );
1067
+ };
1068
+ var DropdownSubContent = ({
1069
+ children,
1070
+ duration = 80,
1071
+ viewportPadding = 8,
1072
+ mobileOptions,
1073
+ slideEntrance = true,
1074
+ slideEntranceOffsetPx: slideEntranceOffsetPxProp,
1075
+ className,
1076
+ style,
1077
+ onKeyDown: onKeyDownProp,
1078
+ ...props
1079
+ }) => {
1080
+ const sub = React3__default.useContext(SubContext);
1081
+ if (!sub) throw new Error("DropdownSubContent must be inside DropdownSub");
1082
+ const { open, setOpen, triggerRef } = sub;
1083
+ const isMobile = useIsMobile(DROPDOWN_MOBILE_SHEET_MAX_PX + 1);
1084
+ const slideOffsetPx = useMemo(
1085
+ () => slideEntranceOffsetPxProp ?? DROPDOWN_MOBILE_SHEET_SLIDE_ENTRANCE_OFFSET_DEFAULT_PX,
1086
+ [slideEntranceOffsetPxProp]
1087
+ );
1088
+ const resolvedMobile = useMemo(
1089
+ () => resolveDropdownMobileSheet(mobileOptions),
1090
+ [mobileOptions]
1091
+ );
1092
+ const [shouldRender, setShouldRender] = useState(false);
1093
+ const [isAnimating, setIsAnimating] = useState(false);
1094
+ const [pos, setPos] = useState({
1095
+ top: -9999,
1096
+ left: -9999,
1097
+ side: "right"
1098
+ });
1099
+ const menuRef = useRef(null);
1100
+ const closeDuration = isMobile && resolvedMobile.sheet ? DROPDOWN_MOBILE_SHEET_MOTION_MS : duration;
1101
+ const closeSub = useCallback(() => setOpen(false), [setOpen]);
1102
+ useEffect(() => {
1103
+ if (open) {
1104
+ setShouldRender(true);
1105
+ } else {
1106
+ setIsAnimating(false);
1107
+ const timer = setTimeout(() => setShouldRender(false), closeDuration);
1108
+ return () => clearTimeout(timer);
1109
+ }
1110
+ }, [open, closeDuration]);
1111
+ useEffect(() => {
1112
+ if (!shouldRender || !open) return;
1113
+ let raf2 = 0;
1114
+ const raf1 = requestAnimationFrame(() => {
1115
+ raf2 = requestAnimationFrame(() => setIsAnimating(true));
1116
+ });
1117
+ return () => {
1118
+ cancelAnimationFrame(raf1);
1119
+ if (raf2) cancelAnimationFrame(raf2);
1120
+ };
1121
+ }, [shouldRender, open]);
1122
+ useLayoutEffect(() => {
1123
+ if (!shouldRender || !triggerRef.current || !menuRef.current) return;
1124
+ if (isMobile && resolvedMobile.sheet) return;
1125
+ const update = () => {
1126
+ if (!triggerRef.current || !menuRef.current) return;
1127
+ setPos(
1128
+ computePos(
1129
+ triggerRef.current,
1130
+ menuRef.current,
1131
+ "right",
1132
+ "start",
1133
+ -8,
1134
+ viewportPadding
1135
+ )
1136
+ );
1137
+ };
1138
+ update();
1139
+ window.addEventListener("resize", update);
1140
+ window.addEventListener("scroll", update, true);
1141
+ return () => {
1142
+ window.removeEventListener("resize", update);
1143
+ window.removeEventListener("scroll", update, true);
1144
+ };
1145
+ }, [shouldRender, viewportPadding, isMobile, resolvedMobile.sheet]);
1146
+ useEffect(() => {
1147
+ if (isAnimating && menuRef.current) {
1148
+ menuRef.current.focus();
1149
+ }
1150
+ }, [isAnimating]);
1151
+ useEffect(() => {
1152
+ if (!open) return;
1153
+ const handler = (e) => {
1154
+ const t = e.target;
1155
+ if (menuRef.current?.contains(t) || triggerRef.current?.contains(t))
1156
+ return;
1157
+ const el = e.target instanceof Element ? e.target : null;
1158
+ const subPanel = el?.closest?.(`[${DROPDOWN_SUB_CONTENT_ATTR}]`);
1159
+ if (subPanel && subPanel !== menuRef.current) return;
1160
+ setOpen(false);
1161
+ };
1162
+ document.addEventListener("mousedown", handler);
1163
+ return () => document.removeEventListener("mousedown", handler);
1164
+ }, [open, setOpen, triggerRef]);
1165
+ const handleSubMenuKeyDown = useCallback(
1166
+ (e) => {
1167
+ onKeyDownProp?.(e);
1168
+ if (e.defaultPrevented) return;
1169
+ const menu = menuRef.current;
1170
+ if (!menu) return;
1171
+ const items = getItems(menu);
1172
+ const focused = document.activeElement;
1173
+ const idx = items.indexOf(focused);
1174
+ switch (e.key) {
1175
+ case "ArrowLeft":
1176
+ case "Escape":
1177
+ e.preventDefault();
1178
+ setOpen(false);
1179
+ triggerRef.current?.focus();
1180
+ break;
1181
+ case "ArrowDown":
1182
+ e.preventDefault();
1183
+ if (items.length === 0) break;
1184
+ if (idx === -1 || idx === items.length - 1) items[0]?.focus();
1185
+ else items[idx + 1]?.focus();
1186
+ break;
1187
+ case "ArrowUp":
1188
+ e.preventDefault();
1189
+ if (items.length === 0) break;
1190
+ if (idx <= 0) items[items.length - 1]?.focus();
1191
+ else items[idx - 1]?.focus();
1192
+ break;
1193
+ case "Home":
1194
+ e.preventDefault();
1195
+ items[0]?.focus();
1196
+ break;
1197
+ case "End":
1198
+ e.preventDefault();
1199
+ items[items.length - 1]?.focus();
1200
+ break;
1201
+ }
1202
+ },
1203
+ [onKeyDownProp, setOpen, triggerRef]
1204
+ );
1205
+ if (!shouldRender || typeof document === "undefined") return null;
1206
+ if (isMobile && resolvedMobile.sheet) {
1207
+ return /* @__PURE__ */ jsx(
1208
+ DropdownMobileBottomSheetPortal,
1209
+ {
1210
+ ...props,
1211
+ open,
1212
+ isAnimating,
1213
+ slideEntrance,
1214
+ slideOffsetPx,
1215
+ sheetTitle: resolvedMobile.title,
1216
+ sheetExtraClassName: resolvedMobile.sheetExtraClassName,
1217
+ contentClassName: resolvedMobile.contentClassName,
1218
+ onRequestClose: closeSub,
1219
+ menuRef,
1220
+ portalZClassName: "z-[70]",
1221
+ className,
1222
+ style,
1223
+ role: "menu",
1224
+ tabIndex: -1,
1225
+ onKeyDown: handleSubMenuKeyDown,
1226
+ "data-dropdown-sub-content": "",
1227
+ children
1228
+ }
1229
+ );
1230
+ }
1231
+ return createPortal(
1232
+ /* @__PURE__ */ jsx(
1233
+ "div",
1234
+ {
1235
+ ...props,
1236
+ ref: menuRef,
1237
+ role: "menu",
1238
+ tabIndex: -1,
1239
+ onMouseEnter: () => setOpen(true),
1240
+ onMouseLeave: () => setOpen(false),
1241
+ onKeyDown: handleSubMenuKeyDown,
1242
+ "data-dropdown-sub-content": "",
1243
+ className: cn(
1244
+ "bg-background border-primary/10 absolute z-60 overflow-hidden rounded-xl border py-1.5 shadow-xl outline-none",
1245
+ className
1246
+ ),
1247
+ style: {
1248
+ position: "absolute",
1249
+ top: pos.top,
1250
+ left: pos.left,
1251
+ minWidth: DROPDOWN_MENU_MIN_WIDTH_PX,
1252
+ transformOrigin: SUB_CONTENT_ORIGIN[pos.side],
1253
+ transform: isAnimating ? "none" : DROPDOWN_CONTENT_HIDDEN[pos.side],
1254
+ opacity: isAnimating ? 1 : 0,
1255
+ transitionProperty: "opacity, transform",
1256
+ transitionDuration: `${duration}ms`,
1257
+ transitionTimingFunction: isAnimating ? DROPDOWN_PANEL_OPEN_EASING : DROPDOWN_PANEL_CLOSE_EASING,
1258
+ ...style
1259
+ },
1260
+ children
1261
+ }
1262
+ ),
1263
+ document.body
1264
+ );
1265
+ };
1266
+
1267
+ export { Dropdown, DropdownCheckboxItem, DropdownChevron, DropdownContent, DropdownItem, DropdownLabel, DropdownMobileClose, DropdownRadioGroup, DropdownRadioItem, DropdownSeparator, DropdownSub, DropdownSubContent, DropdownSubTrigger, DropdownTrigger };
1268
+ //# sourceMappingURL=dropdown.js.map
1269
+ //# sourceMappingURL=dropdown.js.map