@andreagiugni/tailwind-dashboard-ui 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -519,8 +519,8 @@ interface RadioSmProps {
519
519
  }
520
520
  declare const RadioSm: React.FC<RadioSmProps>;
521
521
 
522
- interface SwitchProps {
523
- label: string;
522
+ interface SwitchProps extends Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "onChange" | "color"> {
523
+ label?: React.ReactNode;
524
524
  checked?: boolean;
525
525
  defaultChecked?: boolean;
526
526
  disabled?: boolean;
@@ -541,6 +541,8 @@ interface SelectProps {
541
541
  className?: string;
542
542
  defaultValue?: string;
543
543
  disabled?: boolean;
544
+ /** Render menu under document.body to avoid clipping inside tables/cards. Default: true. */
545
+ portal?: boolean;
544
546
  }
545
547
  /**
546
548
  * A custom single-select dropdown (no native `<select>`), so the menu is fully
@@ -559,6 +561,8 @@ interface MultiSelectProps {
559
561
  defaultSelected?: string[];
560
562
  onChange?: (selected: string[]) => void;
561
563
  disabled?: boolean;
564
+ /** Render menu under document.body to avoid clipping inside tables/cards. Default: true. */
565
+ portal?: boolean;
562
566
  }
563
567
  declare const MultiSelect: React.FC<MultiSelectProps>;
564
568
 
package/dist/index.d.ts CHANGED
@@ -519,8 +519,8 @@ interface RadioSmProps {
519
519
  }
520
520
  declare const RadioSm: React.FC<RadioSmProps>;
521
521
 
522
- interface SwitchProps {
523
- label: string;
522
+ interface SwitchProps extends Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "onChange" | "color"> {
523
+ label?: React.ReactNode;
524
524
  checked?: boolean;
525
525
  defaultChecked?: boolean;
526
526
  disabled?: boolean;
@@ -541,6 +541,8 @@ interface SelectProps {
541
541
  className?: string;
542
542
  defaultValue?: string;
543
543
  disabled?: boolean;
544
+ /** Render menu under document.body to avoid clipping inside tables/cards. Default: true. */
545
+ portal?: boolean;
544
546
  }
545
547
  /**
546
548
  * A custom single-select dropdown (no native `<select>`), so the menu is fully
@@ -559,6 +561,8 @@ interface MultiSelectProps {
559
561
  defaultSelected?: string[];
560
562
  onChange?: (selected: string[]) => void;
561
563
  disabled?: boolean;
564
+ /** Render menu under document.body to avoid clipping inside tables/cards. Default: true. */
565
+ portal?: boolean;
562
566
  }
563
567
  declare const MultiSelect: React.FC<MultiSelectProps>;
564
568
 
package/dist/index.js CHANGED
@@ -626,6 +626,8 @@ var Input = ({
626
626
  style,
627
627
  ...rest
628
628
  }) => {
629
+ const placeholder = typeof rest.placeholder === "string" ? rest.placeholder.trim() : "";
630
+ const placeholderMinWidth = placeholder ? `${Math.min(Math.max(placeholder.length + 6, 18), 40)}ch` : void 0;
629
631
  const stateClass = disabled ? "text-gray-500 border-gray-300 cursor-not-allowed dark:bg-gray-800 dark:text-gray-400 dark:border-gray-700" : error ? "text-error-800 border-error-500 focus:ring-3 focus:ring-error-500/10 dark:text-error-400 dark:border-error-500" : success ? "text-success-500 border-success-400 focus:ring-success-500/10 focus:border-success-300 dark:text-success-400 dark:border-success-500" : "text-gray-800 border-gray-300 focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:text-white/90 dark:focus:border-brand-800";
630
632
  return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
631
633
  /* @__PURE__ */ jsx(
@@ -637,7 +639,11 @@ var Input = ({
637
639
  stateClass,
638
640
  className
639
641
  ),
640
- style: { ...styleOverride({ bgColor, textColor, borderColor }), ...style },
642
+ style: {
643
+ minWidth: placeholderMinWidth,
644
+ ...styleOverride({ bgColor, textColor, borderColor }),
645
+ ...style
646
+ },
641
647
  ...rest
642
648
  }
643
649
  ),
@@ -801,7 +807,7 @@ function DataDrivenTable({
801
807
  setSearch(e.target.value);
802
808
  setPage(1);
803
809
  },
804
- className: "max-w-[220px]"
810
+ className: "sm:w-auto"
805
811
  }
806
812
  )
807
813
  ] }),
@@ -2214,7 +2220,12 @@ var Switch = ({
2214
2220
  defaultChecked = false,
2215
2221
  disabled = false,
2216
2222
  onChange,
2217
- color = "blue"
2223
+ color = "blue",
2224
+ className,
2225
+ onClick,
2226
+ onKeyDown,
2227
+ "aria-label": ariaLabel,
2228
+ ...rest
2218
2229
  }) => {
2219
2230
  const isControlled = checked !== void 0;
2220
2231
  const [internalChecked, setInternalChecked] = useState(defaultChecked);
@@ -2229,6 +2240,20 @@ var Switch = ({
2229
2240
  onChange(newCheckedState);
2230
2241
  }
2231
2242
  };
2243
+ const handleClick = (event) => {
2244
+ onClick?.(event);
2245
+ if (!event.defaultPrevented) {
2246
+ handleToggle();
2247
+ }
2248
+ };
2249
+ const handleKeyDown = (event) => {
2250
+ onKeyDown?.(event);
2251
+ if (event.defaultPrevented) return;
2252
+ if (event.key === " " || event.key === "Enter") {
2253
+ event.preventDefault();
2254
+ handleToggle();
2255
+ }
2256
+ };
2232
2257
  const offTrack = "bg-gray-200 ring-1 ring-inset ring-gray-300 dark:bg-white/10 dark:ring-gray-700";
2233
2258
  const switchColors = color === "blue" ? {
2234
2259
  background: isChecked ? "bg-brand-500 " : offTrack,
@@ -2239,14 +2264,23 @@ var Switch = ({
2239
2264
  // Gray version
2240
2265
  knob: isChecked ? "translate-x-full bg-white" : "translate-x-0 bg-white"
2241
2266
  };
2267
+ const hasLabel = label !== void 0 && label !== null && label !== "";
2242
2268
  return /* @__PURE__ */ jsxs(
2243
2269
  "label",
2244
2270
  {
2271
+ role: "switch",
2272
+ "aria-checked": isChecked,
2273
+ "aria-disabled": disabled || void 0,
2274
+ "aria-label": ariaLabel ?? (!hasLabel ? "Toggle switch" : void 0),
2275
+ tabIndex: disabled ? -1 : 0,
2245
2276
  className: cn(
2246
2277
  "flex cursor-pointer select-none items-center gap-3 text-sm font-medium",
2247
- disabled ? "text-gray-400" : "text-gray-700 dark:text-gray-400"
2278
+ disabled ? "text-gray-400" : "text-gray-700 dark:text-gray-400",
2279
+ className
2248
2280
  ),
2249
- onClick: handleToggle,
2281
+ onClick: handleClick,
2282
+ onKeyDown: handleKeyDown,
2283
+ ...rest,
2250
2284
  children: [
2251
2285
  /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2252
2286
  /* @__PURE__ */ jsx(
@@ -2268,7 +2302,7 @@ var Switch = ({
2268
2302
  }
2269
2303
  )
2270
2304
  ] }),
2271
- label
2305
+ hasLabel && /* @__PURE__ */ jsx("span", { children: label })
2272
2306
  ]
2273
2307
  }
2274
2308
  );
@@ -2279,15 +2313,20 @@ var Select = ({
2279
2313
  onChange,
2280
2314
  className,
2281
2315
  defaultValue = "",
2282
- disabled = false
2316
+ disabled = false,
2317
+ portal = true
2283
2318
  }) => {
2284
2319
  const [selectedValue, setSelectedValue] = useState(defaultValue);
2285
2320
  const [isOpen, setIsOpen] = useState(false);
2286
2321
  const rootRef = useRef(null);
2322
+ const triggerRef = useRef(null);
2323
+ const menuRef = useRef(null);
2324
+ const [menuPosition, setMenuPosition] = useState();
2287
2325
  useEffect(() => {
2288
2326
  if (!isOpen) return;
2289
2327
  const onPointerDown = (e) => {
2290
- if (rootRef.current && !rootRef.current.contains(e.target)) {
2328
+ const target = e.target;
2329
+ if (rootRef.current && !rootRef.current.contains(target) && !menuRef.current?.contains(target)) {
2291
2330
  setIsOpen(false);
2292
2331
  }
2293
2332
  };
@@ -2301,6 +2340,33 @@ var Select = ({
2301
2340
  document.removeEventListener("keydown", onKeyDown);
2302
2341
  };
2303
2342
  }, [isOpen]);
2343
+ useLayoutEffect(() => {
2344
+ if (!isOpen || !portal) return;
2345
+ const updatePosition = () => {
2346
+ const trigger = triggerRef.current;
2347
+ const menu = menuRef.current;
2348
+ if (!trigger || !menu) return;
2349
+ const rect = trigger.getBoundingClientRect();
2350
+ const menuHeight = menu.offsetHeight;
2351
+ const viewportPadding = 8;
2352
+ const gap = 6;
2353
+ const width = rect.width;
2354
+ const fitsBelow = window.innerHeight - rect.bottom - gap >= menuHeight + viewportPadding;
2355
+ const top = fitsBelow ? rect.bottom + gap : Math.max(viewportPadding, rect.top - gap - menuHeight);
2356
+ const left = Math.min(
2357
+ Math.max(viewportPadding, rect.left),
2358
+ window.innerWidth - width - viewportPadding
2359
+ );
2360
+ setMenuPosition({ top, left, width });
2361
+ };
2362
+ updatePosition();
2363
+ window.addEventListener("resize", updatePosition);
2364
+ window.addEventListener("scroll", updatePosition, true);
2365
+ return () => {
2366
+ window.removeEventListener("resize", updatePosition);
2367
+ window.removeEventListener("scroll", updatePosition, true);
2368
+ };
2369
+ }, [isOpen, portal]);
2304
2370
  const toggleOpen = () => {
2305
2371
  if (!disabled) setIsOpen((prev) => !prev);
2306
2372
  };
@@ -2314,6 +2380,7 @@ var Select = ({
2314
2380
  /* @__PURE__ */ jsxs(
2315
2381
  "button",
2316
2382
  {
2383
+ ref: triggerRef,
2317
2384
  type: "button",
2318
2385
  onClick: toggleOpen,
2319
2386
  disabled,
@@ -2349,52 +2416,61 @@ var Select = ({
2349
2416
  ]
2350
2417
  }
2351
2418
  ),
2352
- isOpen && /* @__PURE__ */ jsx(
2353
- "ul",
2354
- {
2355
- role: "listbox",
2356
- className: cn(
2357
- "absolute left-0 top-full mt-1.5 max-h-60 w-full overflow-y-auto rounded-lg border border-gray-200 bg-white py-1 shadow-theme-lg dark:border-gray-800 dark:bg-gray-900",
2358
- layers.menu
2359
- ),
2360
- children: options.map((option) => {
2361
- const isSelected = option.value === selectedValue;
2362
- return /* @__PURE__ */ jsxs(
2363
- "li",
2364
- {
2365
- role: "option",
2366
- "aria-selected": isSelected,
2367
- onClick: () => handleSelect(option.value),
2368
- className: cn(
2369
- "flex cursor-pointer items-center justify-between px-4 py-2 text-sm transition hover:bg-gray-50 dark:hover:bg-white/5",
2370
- isSelected ? "bg-brand-50 font-medium text-brand-600 dark:bg-brand-500/15 dark:text-brand-400" : "text-gray-700 dark:text-gray-300"
2371
- ),
2372
- children: [
2373
- /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
2374
- option.icon && /* @__PURE__ */ jsx(
2375
- "span",
2419
+ isOpen && (() => {
2420
+ const menu = /* @__PURE__ */ jsx(
2421
+ "ul",
2422
+ {
2423
+ ref: menuRef,
2424
+ role: "listbox",
2425
+ className: cn(
2426
+ "max-h-60 overflow-y-auto rounded-lg border border-gray-200 bg-white py-1 shadow-theme-lg dark:border-gray-800 dark:bg-gray-900",
2427
+ portal ? cn("fixed", layers.portal) : cn("absolute left-0 top-full mt-1.5 w-full", layers.menu)
2428
+ ),
2429
+ style: {
2430
+ ...portal && !menuPosition ? { visibility: "hidden" } : void 0,
2431
+ ...portal ? menuPosition : void 0
2432
+ },
2433
+ children: options.map((option) => {
2434
+ const isSelected = option.value === selectedValue;
2435
+ return /* @__PURE__ */ jsxs(
2436
+ "li",
2437
+ {
2438
+ role: "option",
2439
+ "aria-selected": isSelected,
2440
+ onClick: () => handleSelect(option.value),
2441
+ className: cn(
2442
+ "flex cursor-pointer items-center justify-between px-4 py-2 text-sm transition hover:bg-gray-50 dark:hover:bg-white/5",
2443
+ isSelected ? "bg-brand-50 font-medium text-brand-600 dark:bg-brand-500/15 dark:text-brand-400" : "text-gray-700 dark:text-gray-300"
2444
+ ),
2445
+ children: [
2446
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
2447
+ option.icon && /* @__PURE__ */ jsx(
2448
+ "span",
2449
+ {
2450
+ "aria-hidden": "true",
2451
+ className: "flex h-5 w-5 shrink-0 items-center justify-center [&>svg]:size-4",
2452
+ children: option.icon
2453
+ }
2454
+ ),
2455
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label })
2456
+ ] }),
2457
+ isSelected && /* @__PURE__ */ jsx(
2458
+ Check,
2376
2459
  {
2377
- "aria-hidden": "true",
2378
- className: "flex h-5 w-5 shrink-0 items-center justify-center [&>svg]:size-4",
2379
- children: option.icon
2460
+ className: "ml-2 size-4 shrink-0 stroke-current",
2461
+ "aria-hidden": "true"
2380
2462
  }
2381
- ),
2382
- /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label })
2383
- ] }),
2384
- isSelected && /* @__PURE__ */ jsx(
2385
- Check,
2386
- {
2387
- className: "ml-2 size-4 shrink-0 stroke-current",
2388
- "aria-hidden": "true"
2389
- }
2390
- )
2391
- ]
2392
- },
2393
- option.value
2394
- );
2395
- })
2396
- }
2397
- )
2463
+ )
2464
+ ]
2465
+ },
2466
+ option.value
2467
+ );
2468
+ })
2469
+ }
2470
+ );
2471
+ if (!portal || typeof document === "undefined") return menu;
2472
+ return createPortal(menu, document.body);
2473
+ })()
2398
2474
  ] });
2399
2475
  };
2400
2476
  var MultiSelect = ({
@@ -2402,10 +2478,60 @@ var MultiSelect = ({
2402
2478
  options,
2403
2479
  defaultSelected = [],
2404
2480
  onChange,
2405
- disabled = false
2481
+ disabled = false,
2482
+ portal = true
2406
2483
  }) => {
2407
2484
  const [selectedOptions, setSelectedOptions] = useState(defaultSelected);
2408
2485
  const [isOpen, setIsOpen] = useState(false);
2486
+ const rootRef = useRef(null);
2487
+ const triggerRef = useRef(null);
2488
+ const menuRef = useRef(null);
2489
+ const [menuPosition, setMenuPosition] = useState();
2490
+ useEffect(() => {
2491
+ if (!isOpen) return;
2492
+ const onPointerDown = (event) => {
2493
+ const target = event.target;
2494
+ if (rootRef.current && !rootRef.current.contains(target) && !menuRef.current?.contains(target)) {
2495
+ setIsOpen(false);
2496
+ }
2497
+ };
2498
+ const onKeyDown = (event) => {
2499
+ if (event.key === "Escape") setIsOpen(false);
2500
+ };
2501
+ document.addEventListener("mousedown", onPointerDown);
2502
+ document.addEventListener("keydown", onKeyDown);
2503
+ return () => {
2504
+ document.removeEventListener("mousedown", onPointerDown);
2505
+ document.removeEventListener("keydown", onKeyDown);
2506
+ };
2507
+ }, [isOpen]);
2508
+ useLayoutEffect(() => {
2509
+ if (!isOpen || !portal) return;
2510
+ const updatePosition = () => {
2511
+ const trigger = triggerRef.current;
2512
+ const menu = menuRef.current;
2513
+ if (!trigger || !menu) return;
2514
+ const rect = trigger.getBoundingClientRect();
2515
+ const menuHeight = menu.offsetHeight;
2516
+ const viewportPadding = 8;
2517
+ const gap = 6;
2518
+ const width = rect.width;
2519
+ const fitsBelow = window.innerHeight - rect.bottom - gap >= menuHeight + viewportPadding;
2520
+ const top = fitsBelow ? rect.bottom + gap : Math.max(viewportPadding, rect.top - gap - menuHeight);
2521
+ const left = Math.min(
2522
+ Math.max(viewportPadding, rect.left),
2523
+ window.innerWidth - width - viewportPadding
2524
+ );
2525
+ setMenuPosition({ top, left, width });
2526
+ };
2527
+ updatePosition();
2528
+ window.addEventListener("resize", updatePosition);
2529
+ window.addEventListener("scroll", updatePosition, true);
2530
+ return () => {
2531
+ window.removeEventListener("resize", updatePosition);
2532
+ window.removeEventListener("scroll", updatePosition, true);
2533
+ };
2534
+ }, [isOpen, portal]);
2409
2535
  const toggleDropdown = () => {
2410
2536
  if (disabled) return;
2411
2537
  setIsOpen((prev) => !prev);
@@ -2423,94 +2549,110 @@ var MultiSelect = ({
2423
2549
  const selectedValuesText = selectedOptions.map(
2424
2550
  (value) => options.find((option) => option.value === value)?.text || ""
2425
2551
  );
2426
- return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
2552
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: "w-full", children: [
2427
2553
  /* @__PURE__ */ jsx("label", { className: "mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400", children: label }),
2428
2554
  /* @__PURE__ */ jsx("div", { className: "relative inline-block w-full", children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center", children: [
2429
- /* @__PURE__ */ jsx("div", { onClick: toggleDropdown, className: "w-full", children: /* @__PURE__ */ jsxs("div", { className: "mb-2 flex h-11 rounded-lg border border-gray-300 bg-white py-1.5 px-4 shadow-theme-xs outline-hidden transition focus:border-brand-300 focus:shadow-focus-ring dark:border-gray-700 dark:bg-gray-900 dark:focus:border-brand-300", children: [
2430
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap flex-auto gap-2", children: selectedValuesText.length > 0 ? selectedValuesText.map((text, index) => /* @__PURE__ */ jsxs(
2431
- "div",
2432
- {
2433
- className: "group flex items-center justify-center rounded-full border-[0.7px] border-transparent bg-gray-100 py-1 pl-2.5 pr-2 text-sm text-gray-800 hover:border-gray-200 dark:bg-gray-800 dark:text-white/90 dark:hover:border-gray-800",
2434
- children: [
2435
- /* @__PURE__ */ jsx("span", { className: "flex-initial max-w-full", children: text }),
2436
- /* @__PURE__ */ jsx("div", { className: "flex flex-row-reverse flex-auto", children: /* @__PURE__ */ jsx(
2437
- "div",
2438
- {
2439
- onClick: () => removeOption(index, selectedOptions[index]),
2440
- className: "pl-2 text-gray-500 cursor-pointer group-hover:text-gray-400 dark:text-gray-400",
2441
- children: /* @__PURE__ */ jsx(X, { className: "size-4", role: "button", "aria-hidden": "true" })
2442
- }
2443
- ) })
2444
- ]
2445
- },
2446
- index
2447
- )) : /* @__PURE__ */ jsx(
2448
- "input",
2449
- {
2450
- placeholder: "Select option",
2451
- className: "w-full h-full p-1 pr-2 text-sm bg-transparent border-0 outline-hidden appearance-none placeholder:text-gray-800 focus:border-0 focus:outline-hidden focus:ring-0 dark:placeholder:text-white/90",
2452
- readOnly: true,
2453
- value: "Select option"
2454
- }
2455
- ) }),
2456
- /* @__PURE__ */ jsx("div", { className: "flex items-center py-1 pl-1 pr-1 w-7", children: /* @__PURE__ */ jsx(
2457
- "button",
2458
- {
2459
- type: "button",
2460
- onClick: toggleDropdown,
2461
- className: "w-5 h-5 text-gray-700 outline-hidden cursor-pointer focus:outline-hidden dark:text-gray-400",
2462
- children: /* @__PURE__ */ jsx(
2463
- ChevronDown,
2464
- {
2465
- className: `size-4 stroke-current ${isOpen ? "rotate-180" : ""}`,
2466
- "aria-hidden": "true"
2467
- }
2468
- )
2469
- }
2470
- ) })
2471
- ] }) }),
2472
- isOpen && /* @__PURE__ */ jsx(
2555
+ /* @__PURE__ */ jsx("div", { onClick: toggleDropdown, className: "w-full", children: /* @__PURE__ */ jsxs(
2473
2556
  "div",
2474
2557
  {
2475
- className: cn(
2476
- "absolute left-0 top-full max-h-select w-full overflow-y-auto rounded-lg bg-white shadow-sm dark:bg-gray-900",
2477
- layers.menu
2478
- ),
2479
- onClick: (e) => e.stopPropagation(),
2480
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: options.map((option, index) => {
2481
- const isSelected = selectedOptions.includes(option.value);
2482
- return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
2558
+ ref: triggerRef,
2559
+ className: "mb-2 flex h-11 rounded-lg border border-gray-300 bg-white py-1.5 px-4 shadow-theme-xs outline-hidden transition focus:border-brand-300 focus:shadow-focus-ring dark:border-gray-700 dark:bg-gray-900 dark:focus:border-brand-300",
2560
+ children: [
2561
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap flex-auto gap-2", children: selectedValuesText.length > 0 ? selectedValuesText.map((text, index) => /* @__PURE__ */ jsxs(
2483
2562
  "div",
2484
2563
  {
2485
- className: `w-full cursor-pointer rounded-t border-b border-gray-200 hover:bg-brand-500/5 dark:border-gray-800`,
2486
- onClick: () => handleSelect(option.value),
2487
- children: /* @__PURE__ */ jsxs(
2488
- "div",
2564
+ className: "group flex items-center justify-center rounded-full border-[0.7px] border-transparent bg-gray-100 py-1 pl-2.5 pr-2 text-sm text-gray-800 hover:border-gray-200 dark:bg-gray-800 dark:text-white/90 dark:hover:border-gray-800",
2565
+ children: [
2566
+ /* @__PURE__ */ jsx("span", { className: "flex-initial max-w-full", children: text }),
2567
+ /* @__PURE__ */ jsx("div", { className: "flex flex-row-reverse flex-auto", children: /* @__PURE__ */ jsx(
2568
+ "div",
2569
+ {
2570
+ onClick: () => removeOption(index, selectedOptions[index]),
2571
+ className: "pl-2 text-gray-500 cursor-pointer group-hover:text-gray-400 dark:text-gray-400",
2572
+ children: /* @__PURE__ */ jsx(X, { className: "size-4", role: "button", "aria-hidden": "true" })
2573
+ }
2574
+ ) })
2575
+ ]
2576
+ },
2577
+ index
2578
+ )) : /* @__PURE__ */ jsx(
2579
+ "input",
2580
+ {
2581
+ placeholder: "Select option",
2582
+ className: "w-full h-full p-1 pr-2 text-sm bg-transparent border-0 outline-hidden appearance-none placeholder:text-gray-800 focus:border-0 focus:outline-hidden focus:ring-0 dark:placeholder:text-white/90",
2583
+ readOnly: true,
2584
+ value: "Select option"
2585
+ }
2586
+ ) }),
2587
+ /* @__PURE__ */ jsx("div", { className: "flex items-center py-1 pl-1 pr-1 w-7", children: /* @__PURE__ */ jsx(
2588
+ "button",
2589
+ {
2590
+ type: "button",
2591
+ onClick: toggleDropdown,
2592
+ className: "w-5 h-5 text-gray-700 outline-hidden cursor-pointer focus:outline-hidden dark:text-gray-400",
2593
+ children: /* @__PURE__ */ jsx(
2594
+ ChevronDown,
2489
2595
  {
2490
- className: `relative flex w-full items-center justify-between p-2 pl-2 ${isSelected ? "bg-brand-500/10" : ""}`,
2491
- children: [
2492
- /* @__PURE__ */ jsx(
2493
- "div",
2494
- {
2495
- className: `mx-2 leading-6 ${isSelected ? "font-medium text-brand-600 dark:text-brand-400" : "text-gray-800 dark:text-white/90"}`,
2496
- children: option.text
2497
- }
2498
- ),
2499
- isSelected && /* @__PURE__ */ jsx(
2500
- Check,
2501
- {
2502
- className: "mr-2 size-4 shrink-0 stroke-current text-brand-500",
2503
- "aria-hidden": "true"
2504
- }
2505
- )
2506
- ]
2596
+ className: `size-4 stroke-current ${isOpen ? "rotate-180" : ""}`,
2597
+ "aria-hidden": "true"
2507
2598
  }
2508
2599
  )
2509
2600
  }
2510
- ) }, index);
2511
- }) })
2601
+ ) })
2602
+ ]
2512
2603
  }
2513
- )
2604
+ ) }),
2605
+ isOpen && (() => {
2606
+ const menu = /* @__PURE__ */ jsx(
2607
+ "div",
2608
+ {
2609
+ ref: menuRef,
2610
+ className: cn(
2611
+ "max-h-select overflow-y-auto rounded-lg bg-white shadow-sm dark:bg-gray-900",
2612
+ portal ? cn("fixed", layers.portal) : cn("absolute left-0 top-full w-full", layers.menu)
2613
+ ),
2614
+ style: {
2615
+ ...portal && !menuPosition ? { visibility: "hidden" } : void 0,
2616
+ ...portal ? menuPosition : void 0
2617
+ },
2618
+ onClick: (e) => e.stopPropagation(),
2619
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: options.map((option, index) => {
2620
+ const isSelected = selectedOptions.includes(option.value);
2621
+ return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
2622
+ "div",
2623
+ {
2624
+ className: `w-full cursor-pointer rounded-t border-b border-gray-200 hover:bg-brand-500/5 dark:border-gray-800`,
2625
+ onClick: () => handleSelect(option.value),
2626
+ children: /* @__PURE__ */ jsxs(
2627
+ "div",
2628
+ {
2629
+ className: `relative flex w-full items-center justify-between p-2 pl-2 ${isSelected ? "bg-brand-500/10" : ""}`,
2630
+ children: [
2631
+ /* @__PURE__ */ jsx(
2632
+ "div",
2633
+ {
2634
+ className: `mx-2 leading-6 ${isSelected ? "font-medium text-brand-600 dark:text-brand-400" : "text-gray-800 dark:text-white/90"}`,
2635
+ children: option.text
2636
+ }
2637
+ ),
2638
+ isSelected && /* @__PURE__ */ jsx(
2639
+ Check,
2640
+ {
2641
+ className: "mr-2 size-4 shrink-0 stroke-current text-brand-500",
2642
+ "aria-hidden": "true"
2643
+ }
2644
+ )
2645
+ ]
2646
+ }
2647
+ )
2648
+ }
2649
+ ) }, index);
2650
+ }) })
2651
+ }
2652
+ );
2653
+ if (!portal || typeof document === "undefined") return menu;
2654
+ return createPortal(menu, document.body);
2655
+ })()
2514
2656
  ] }) })
2515
2657
  ] });
2516
2658
  };