@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/README.md CHANGED
@@ -199,11 +199,11 @@ Legend: **(+native)** = also extends the element's native HTML attributes.
199
199
  |---|---|
200
200
  | `Input` | `type`, `value` / `defaultValue`, `onChange`, `success`, `error`, `hint`, `disabled`, (+native input) (+override) |
201
201
  | `Label` | `htmlFor`, `children`, (+native label) |
202
- | `Select` | `options` (`{ value, label, icon? }[]`), `placeholder`, `defaultValue`, `onChange(value)`, `disabled` |
203
- | `MultiSelect` | `options`, `selected` / `defaultSelected`, `onChange`, `placeholder`, `disabled` |
202
+ | `Select` | `options` (`{ value, label, icon? }[]`), `placeholder`, `defaultValue`, `onChange(value)`, `disabled`, `portal?` (default `true`, prevents clipping inside tables/cards) |
203
+ | `MultiSelect` | `options`, `selected` / `defaultSelected`, `onChange`, `placeholder`, `disabled`, `portal?` (default `true`, prevents clipping inside tables/cards) |
204
204
  | `Checkbox` | `label`, `checked`, `onChange(checked)`, `disabled`, `checkedColor?` |
205
205
  | `Radio` / `RadioSm` | `id`, `name`, `value`, `checked`, `label`, `onChange(value)`, `disabled` |
206
- | `Switch` | `label`, `checked` / `defaultChecked`, `onChange(checked)`, `color` (blue/gray), `disabled` |
206
+ | `Switch` | `label?`, `checked` / `defaultChecked`, `onChange(checked)`, `color` (blue/gray), `disabled` |
207
207
  | `TextArea` | `value`, `onChange`, `rows`, `success`, `error`, `hint`, `disabled` |
208
208
  | `FileInput` | `onChange`, `accept`, `multiple`, (+native input) |
209
209
  | `PasswordInput` | password field with a show/hide eye toggle, (+native input attrs) |
package/dist/index.cjs CHANGED
@@ -632,6 +632,8 @@ var Input = ({
632
632
  style,
633
633
  ...rest
634
634
  }) => {
635
+ const placeholder = typeof rest.placeholder === "string" ? rest.placeholder.trim() : "";
636
+ const placeholderMinWidth = placeholder ? `${Math.min(Math.max(placeholder.length + 6, 18), 40)}ch` : void 0;
635
637
  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";
636
638
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
637
639
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -643,7 +645,11 @@ var Input = ({
643
645
  stateClass,
644
646
  className
645
647
  ),
646
- style: { ...styleOverride({ bgColor, textColor, borderColor }), ...style },
648
+ style: {
649
+ minWidth: placeholderMinWidth,
650
+ ...styleOverride({ bgColor, textColor, borderColor }),
651
+ ...style
652
+ },
647
653
  ...rest
648
654
  }
649
655
  ),
@@ -807,7 +813,7 @@ function DataDrivenTable({
807
813
  setSearch(e.target.value);
808
814
  setPage(1);
809
815
  },
810
- className: "max-w-[220px]"
816
+ className: "sm:w-auto"
811
817
  }
812
818
  )
813
819
  ] }),
@@ -2220,7 +2226,12 @@ var Switch = ({
2220
2226
  defaultChecked = false,
2221
2227
  disabled = false,
2222
2228
  onChange,
2223
- color = "blue"
2229
+ color = "blue",
2230
+ className,
2231
+ onClick,
2232
+ onKeyDown,
2233
+ "aria-label": ariaLabel,
2234
+ ...rest
2224
2235
  }) => {
2225
2236
  const isControlled = checked !== void 0;
2226
2237
  const [internalChecked, setInternalChecked] = React5.useState(defaultChecked);
@@ -2235,6 +2246,20 @@ var Switch = ({
2235
2246
  onChange(newCheckedState);
2236
2247
  }
2237
2248
  };
2249
+ const handleClick = (event) => {
2250
+ onClick?.(event);
2251
+ if (!event.defaultPrevented) {
2252
+ handleToggle();
2253
+ }
2254
+ };
2255
+ const handleKeyDown = (event) => {
2256
+ onKeyDown?.(event);
2257
+ if (event.defaultPrevented) return;
2258
+ if (event.key === " " || event.key === "Enter") {
2259
+ event.preventDefault();
2260
+ handleToggle();
2261
+ }
2262
+ };
2238
2263
  const offTrack = "bg-gray-200 ring-1 ring-inset ring-gray-300 dark:bg-white/10 dark:ring-gray-700";
2239
2264
  const switchColors = color === "blue" ? {
2240
2265
  background: isChecked ? "bg-brand-500 " : offTrack,
@@ -2245,14 +2270,23 @@ var Switch = ({
2245
2270
  // Gray version
2246
2271
  knob: isChecked ? "translate-x-full bg-white" : "translate-x-0 bg-white"
2247
2272
  };
2273
+ const hasLabel = label !== void 0 && label !== null && label !== "";
2248
2274
  return /* @__PURE__ */ jsxRuntime.jsxs(
2249
2275
  "label",
2250
2276
  {
2277
+ role: "switch",
2278
+ "aria-checked": isChecked,
2279
+ "aria-disabled": disabled || void 0,
2280
+ "aria-label": ariaLabel ?? (!hasLabel ? "Toggle switch" : void 0),
2281
+ tabIndex: disabled ? -1 : 0,
2251
2282
  className: chunkYERNSNT4_cjs.cn(
2252
2283
  "flex cursor-pointer select-none items-center gap-3 text-sm font-medium",
2253
- disabled ? "text-gray-400" : "text-gray-700 dark:text-gray-400"
2284
+ disabled ? "text-gray-400" : "text-gray-700 dark:text-gray-400",
2285
+ className
2254
2286
  ),
2255
- onClick: handleToggle,
2287
+ onClick: handleClick,
2288
+ onKeyDown: handleKeyDown,
2289
+ ...rest,
2256
2290
  children: [
2257
2291
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2258
2292
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2274,7 +2308,7 @@ var Switch = ({
2274
2308
  }
2275
2309
  )
2276
2310
  ] }),
2277
- label
2311
+ hasLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
2278
2312
  ]
2279
2313
  }
2280
2314
  );
@@ -2285,15 +2319,20 @@ var Select = ({
2285
2319
  onChange,
2286
2320
  className,
2287
2321
  defaultValue = "",
2288
- disabled = false
2322
+ disabled = false,
2323
+ portal = true
2289
2324
  }) => {
2290
2325
  const [selectedValue, setSelectedValue] = React5.useState(defaultValue);
2291
2326
  const [isOpen, setIsOpen] = React5.useState(false);
2292
2327
  const rootRef = React5.useRef(null);
2328
+ const triggerRef = React5.useRef(null);
2329
+ const menuRef = React5.useRef(null);
2330
+ const [menuPosition, setMenuPosition] = React5.useState();
2293
2331
  React5.useEffect(() => {
2294
2332
  if (!isOpen) return;
2295
2333
  const onPointerDown = (e) => {
2296
- if (rootRef.current && !rootRef.current.contains(e.target)) {
2334
+ const target = e.target;
2335
+ if (rootRef.current && !rootRef.current.contains(target) && !menuRef.current?.contains(target)) {
2297
2336
  setIsOpen(false);
2298
2337
  }
2299
2338
  };
@@ -2307,6 +2346,33 @@ var Select = ({
2307
2346
  document.removeEventListener("keydown", onKeyDown);
2308
2347
  };
2309
2348
  }, [isOpen]);
2349
+ React5.useLayoutEffect(() => {
2350
+ if (!isOpen || !portal) return;
2351
+ const updatePosition = () => {
2352
+ const trigger = triggerRef.current;
2353
+ const menu = menuRef.current;
2354
+ if (!trigger || !menu) return;
2355
+ const rect = trigger.getBoundingClientRect();
2356
+ const menuHeight = menu.offsetHeight;
2357
+ const viewportPadding = 8;
2358
+ const gap = 6;
2359
+ const width = rect.width;
2360
+ const fitsBelow = window.innerHeight - rect.bottom - gap >= menuHeight + viewportPadding;
2361
+ const top = fitsBelow ? rect.bottom + gap : Math.max(viewportPadding, rect.top - gap - menuHeight);
2362
+ const left = Math.min(
2363
+ Math.max(viewportPadding, rect.left),
2364
+ window.innerWidth - width - viewportPadding
2365
+ );
2366
+ setMenuPosition({ top, left, width });
2367
+ };
2368
+ updatePosition();
2369
+ window.addEventListener("resize", updatePosition);
2370
+ window.addEventListener("scroll", updatePosition, true);
2371
+ return () => {
2372
+ window.removeEventListener("resize", updatePosition);
2373
+ window.removeEventListener("scroll", updatePosition, true);
2374
+ };
2375
+ }, [isOpen, portal]);
2310
2376
  const toggleOpen = () => {
2311
2377
  if (!disabled) setIsOpen((prev) => !prev);
2312
2378
  };
@@ -2320,6 +2386,7 @@ var Select = ({
2320
2386
  /* @__PURE__ */ jsxRuntime.jsxs(
2321
2387
  "button",
2322
2388
  {
2389
+ ref: triggerRef,
2323
2390
  type: "button",
2324
2391
  onClick: toggleOpen,
2325
2392
  disabled,
@@ -2355,52 +2422,61 @@ var Select = ({
2355
2422
  ]
2356
2423
  }
2357
2424
  ),
2358
- isOpen && /* @__PURE__ */ jsxRuntime.jsx(
2359
- "ul",
2360
- {
2361
- role: "listbox",
2362
- className: chunkYERNSNT4_cjs.cn(
2363
- "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",
2364
- chunkTF3G3E72_cjs.layers.menu
2365
- ),
2366
- children: options.map((option) => {
2367
- const isSelected = option.value === selectedValue;
2368
- return /* @__PURE__ */ jsxRuntime.jsxs(
2369
- "li",
2370
- {
2371
- role: "option",
2372
- "aria-selected": isSelected,
2373
- onClick: () => handleSelect(option.value),
2374
- className: chunkYERNSNT4_cjs.cn(
2375
- "flex cursor-pointer items-center justify-between px-4 py-2 text-sm transition hover:bg-gray-50 dark:hover:bg-white/5",
2376
- 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"
2377
- ),
2378
- children: [
2379
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
2380
- option.icon && /* @__PURE__ */ jsxRuntime.jsx(
2381
- "span",
2425
+ isOpen && (() => {
2426
+ const menu = /* @__PURE__ */ jsxRuntime.jsx(
2427
+ "ul",
2428
+ {
2429
+ ref: menuRef,
2430
+ role: "listbox",
2431
+ className: chunkYERNSNT4_cjs.cn(
2432
+ "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",
2433
+ portal ? chunkYERNSNT4_cjs.cn("fixed", chunkTF3G3E72_cjs.layers.portal) : chunkYERNSNT4_cjs.cn("absolute left-0 top-full mt-1.5 w-full", chunkTF3G3E72_cjs.layers.menu)
2434
+ ),
2435
+ style: {
2436
+ ...portal && !menuPosition ? { visibility: "hidden" } : void 0,
2437
+ ...portal ? menuPosition : void 0
2438
+ },
2439
+ children: options.map((option) => {
2440
+ const isSelected = option.value === selectedValue;
2441
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2442
+ "li",
2443
+ {
2444
+ role: "option",
2445
+ "aria-selected": isSelected,
2446
+ onClick: () => handleSelect(option.value),
2447
+ className: chunkYERNSNT4_cjs.cn(
2448
+ "flex cursor-pointer items-center justify-between px-4 py-2 text-sm transition hover:bg-gray-50 dark:hover:bg-white/5",
2449
+ 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"
2450
+ ),
2451
+ children: [
2452
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
2453
+ option.icon && /* @__PURE__ */ jsxRuntime.jsx(
2454
+ "span",
2455
+ {
2456
+ "aria-hidden": "true",
2457
+ className: "flex h-5 w-5 shrink-0 items-center justify-center [&>svg]:size-4",
2458
+ children: option.icon
2459
+ }
2460
+ ),
2461
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: option.label })
2462
+ ] }),
2463
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx(
2464
+ lucideReact.Check,
2382
2465
  {
2383
- "aria-hidden": "true",
2384
- className: "flex h-5 w-5 shrink-0 items-center justify-center [&>svg]:size-4",
2385
- children: option.icon
2466
+ className: "ml-2 size-4 shrink-0 stroke-current",
2467
+ "aria-hidden": "true"
2386
2468
  }
2387
- ),
2388
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: option.label })
2389
- ] }),
2390
- isSelected && /* @__PURE__ */ jsxRuntime.jsx(
2391
- lucideReact.Check,
2392
- {
2393
- className: "ml-2 size-4 shrink-0 stroke-current",
2394
- "aria-hidden": "true"
2395
- }
2396
- )
2397
- ]
2398
- },
2399
- option.value
2400
- );
2401
- })
2402
- }
2403
- )
2469
+ )
2470
+ ]
2471
+ },
2472
+ option.value
2473
+ );
2474
+ })
2475
+ }
2476
+ );
2477
+ if (!portal || typeof document === "undefined") return menu;
2478
+ return reactDom.createPortal(menu, document.body);
2479
+ })()
2404
2480
  ] });
2405
2481
  };
2406
2482
  var MultiSelect = ({
@@ -2408,10 +2484,60 @@ var MultiSelect = ({
2408
2484
  options,
2409
2485
  defaultSelected = [],
2410
2486
  onChange,
2411
- disabled = false
2487
+ disabled = false,
2488
+ portal = true
2412
2489
  }) => {
2413
2490
  const [selectedOptions, setSelectedOptions] = React5.useState(defaultSelected);
2414
2491
  const [isOpen, setIsOpen] = React5.useState(false);
2492
+ const rootRef = React5.useRef(null);
2493
+ const triggerRef = React5.useRef(null);
2494
+ const menuRef = React5.useRef(null);
2495
+ const [menuPosition, setMenuPosition] = React5.useState();
2496
+ React5.useEffect(() => {
2497
+ if (!isOpen) return;
2498
+ const onPointerDown = (event) => {
2499
+ const target = event.target;
2500
+ if (rootRef.current && !rootRef.current.contains(target) && !menuRef.current?.contains(target)) {
2501
+ setIsOpen(false);
2502
+ }
2503
+ };
2504
+ const onKeyDown = (event) => {
2505
+ if (event.key === "Escape") setIsOpen(false);
2506
+ };
2507
+ document.addEventListener("mousedown", onPointerDown);
2508
+ document.addEventListener("keydown", onKeyDown);
2509
+ return () => {
2510
+ document.removeEventListener("mousedown", onPointerDown);
2511
+ document.removeEventListener("keydown", onKeyDown);
2512
+ };
2513
+ }, [isOpen]);
2514
+ React5.useLayoutEffect(() => {
2515
+ if (!isOpen || !portal) return;
2516
+ const updatePosition = () => {
2517
+ const trigger = triggerRef.current;
2518
+ const menu = menuRef.current;
2519
+ if (!trigger || !menu) return;
2520
+ const rect = trigger.getBoundingClientRect();
2521
+ const menuHeight = menu.offsetHeight;
2522
+ const viewportPadding = 8;
2523
+ const gap = 6;
2524
+ const width = rect.width;
2525
+ const fitsBelow = window.innerHeight - rect.bottom - gap >= menuHeight + viewportPadding;
2526
+ const top = fitsBelow ? rect.bottom + gap : Math.max(viewportPadding, rect.top - gap - menuHeight);
2527
+ const left = Math.min(
2528
+ Math.max(viewportPadding, rect.left),
2529
+ window.innerWidth - width - viewportPadding
2530
+ );
2531
+ setMenuPosition({ top, left, width });
2532
+ };
2533
+ updatePosition();
2534
+ window.addEventListener("resize", updatePosition);
2535
+ window.addEventListener("scroll", updatePosition, true);
2536
+ return () => {
2537
+ window.removeEventListener("resize", updatePosition);
2538
+ window.removeEventListener("scroll", updatePosition, true);
2539
+ };
2540
+ }, [isOpen, portal]);
2415
2541
  const toggleDropdown = () => {
2416
2542
  if (disabled) return;
2417
2543
  setIsOpen((prev) => !prev);
@@ -2429,94 +2555,110 @@ var MultiSelect = ({
2429
2555
  const selectedValuesText = selectedOptions.map(
2430
2556
  (value) => options.find((option) => option.value === value)?.text || ""
2431
2557
  );
2432
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
2558
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: "w-full", children: [
2433
2559
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400", children: label }),
2434
2560
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative inline-block w-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col items-center", children: [
2435
- /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: toggleDropdown, className: "w-full", children: /* @__PURE__ */ jsxRuntime.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: [
2436
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap flex-auto gap-2", children: selectedValuesText.length > 0 ? selectedValuesText.map((text, index) => /* @__PURE__ */ jsxRuntime.jsxs(
2437
- "div",
2438
- {
2439
- 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",
2440
- children: [
2441
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-initial max-w-full", children: text }),
2442
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row-reverse flex-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
2443
- "div",
2444
- {
2445
- onClick: () => removeOption(index, selectedOptions[index]),
2446
- className: "pl-2 text-gray-500 cursor-pointer group-hover:text-gray-400 dark:text-gray-400",
2447
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4", role: "button", "aria-hidden": "true" })
2448
- }
2449
- ) })
2450
- ]
2451
- },
2452
- index
2453
- )) : /* @__PURE__ */ jsxRuntime.jsx(
2454
- "input",
2455
- {
2456
- placeholder: "Select option",
2457
- 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",
2458
- readOnly: true,
2459
- value: "Select option"
2460
- }
2461
- ) }),
2462
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center py-1 pl-1 pr-1 w-7", children: /* @__PURE__ */ jsxRuntime.jsx(
2463
- "button",
2464
- {
2465
- type: "button",
2466
- onClick: toggleDropdown,
2467
- className: "w-5 h-5 text-gray-700 outline-hidden cursor-pointer focus:outline-hidden dark:text-gray-400",
2468
- children: /* @__PURE__ */ jsxRuntime.jsx(
2469
- lucideReact.ChevronDown,
2470
- {
2471
- className: `size-4 stroke-current ${isOpen ? "rotate-180" : ""}`,
2472
- "aria-hidden": "true"
2473
- }
2474
- )
2475
- }
2476
- ) })
2477
- ] }) }),
2478
- isOpen && /* @__PURE__ */ jsxRuntime.jsx(
2561
+ /* @__PURE__ */ jsxRuntime.jsx("div", { onClick: toggleDropdown, className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsxs(
2479
2562
  "div",
2480
2563
  {
2481
- className: chunkYERNSNT4_cjs.cn(
2482
- "absolute left-0 top-full max-h-select w-full overflow-y-auto rounded-lg bg-white shadow-sm dark:bg-gray-900",
2483
- chunkTF3G3E72_cjs.layers.menu
2484
- ),
2485
- onClick: (e) => e.stopPropagation(),
2486
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", children: options.map((option, index) => {
2487
- const isSelected = selectedOptions.includes(option.value);
2488
- return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
2564
+ ref: triggerRef,
2565
+ 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",
2566
+ children: [
2567
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap flex-auto gap-2", children: selectedValuesText.length > 0 ? selectedValuesText.map((text, index) => /* @__PURE__ */ jsxRuntime.jsxs(
2489
2568
  "div",
2490
2569
  {
2491
- className: `w-full cursor-pointer rounded-t border-b border-gray-200 hover:bg-brand-500/5 dark:border-gray-800`,
2492
- onClick: () => handleSelect(option.value),
2493
- children: /* @__PURE__ */ jsxRuntime.jsxs(
2494
- "div",
2570
+ 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",
2571
+ children: [
2572
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-initial max-w-full", children: text }),
2573
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row-reverse flex-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
2574
+ "div",
2575
+ {
2576
+ onClick: () => removeOption(index, selectedOptions[index]),
2577
+ className: "pl-2 text-gray-500 cursor-pointer group-hover:text-gray-400 dark:text-gray-400",
2578
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4", role: "button", "aria-hidden": "true" })
2579
+ }
2580
+ ) })
2581
+ ]
2582
+ },
2583
+ index
2584
+ )) : /* @__PURE__ */ jsxRuntime.jsx(
2585
+ "input",
2586
+ {
2587
+ placeholder: "Select option",
2588
+ 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",
2589
+ readOnly: true,
2590
+ value: "Select option"
2591
+ }
2592
+ ) }),
2593
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center py-1 pl-1 pr-1 w-7", children: /* @__PURE__ */ jsxRuntime.jsx(
2594
+ "button",
2595
+ {
2596
+ type: "button",
2597
+ onClick: toggleDropdown,
2598
+ className: "w-5 h-5 text-gray-700 outline-hidden cursor-pointer focus:outline-hidden dark:text-gray-400",
2599
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2600
+ lucideReact.ChevronDown,
2495
2601
  {
2496
- className: `relative flex w-full items-center justify-between p-2 pl-2 ${isSelected ? "bg-brand-500/10" : ""}`,
2497
- children: [
2498
- /* @__PURE__ */ jsxRuntime.jsx(
2499
- "div",
2500
- {
2501
- className: `mx-2 leading-6 ${isSelected ? "font-medium text-brand-600 dark:text-brand-400" : "text-gray-800 dark:text-white/90"}`,
2502
- children: option.text
2503
- }
2504
- ),
2505
- isSelected && /* @__PURE__ */ jsxRuntime.jsx(
2506
- lucideReact.Check,
2507
- {
2508
- className: "mr-2 size-4 shrink-0 stroke-current text-brand-500",
2509
- "aria-hidden": "true"
2510
- }
2511
- )
2512
- ]
2602
+ className: `size-4 stroke-current ${isOpen ? "rotate-180" : ""}`,
2603
+ "aria-hidden": "true"
2513
2604
  }
2514
2605
  )
2515
2606
  }
2516
- ) }, index);
2517
- }) })
2607
+ ) })
2608
+ ]
2518
2609
  }
2519
- )
2610
+ ) }),
2611
+ isOpen && (() => {
2612
+ const menu = /* @__PURE__ */ jsxRuntime.jsx(
2613
+ "div",
2614
+ {
2615
+ ref: menuRef,
2616
+ className: chunkYERNSNT4_cjs.cn(
2617
+ "max-h-select overflow-y-auto rounded-lg bg-white shadow-sm dark:bg-gray-900",
2618
+ portal ? chunkYERNSNT4_cjs.cn("fixed", chunkTF3G3E72_cjs.layers.portal) : chunkYERNSNT4_cjs.cn("absolute left-0 top-full w-full", chunkTF3G3E72_cjs.layers.menu)
2619
+ ),
2620
+ style: {
2621
+ ...portal && !menuPosition ? { visibility: "hidden" } : void 0,
2622
+ ...portal ? menuPosition : void 0
2623
+ },
2624
+ onClick: (e) => e.stopPropagation(),
2625
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", children: options.map((option, index) => {
2626
+ const isSelected = selectedOptions.includes(option.value);
2627
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
2628
+ "div",
2629
+ {
2630
+ className: `w-full cursor-pointer rounded-t border-b border-gray-200 hover:bg-brand-500/5 dark:border-gray-800`,
2631
+ onClick: () => handleSelect(option.value),
2632
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2633
+ "div",
2634
+ {
2635
+ className: `relative flex w-full items-center justify-between p-2 pl-2 ${isSelected ? "bg-brand-500/10" : ""}`,
2636
+ children: [
2637
+ /* @__PURE__ */ jsxRuntime.jsx(
2638
+ "div",
2639
+ {
2640
+ className: `mx-2 leading-6 ${isSelected ? "font-medium text-brand-600 dark:text-brand-400" : "text-gray-800 dark:text-white/90"}`,
2641
+ children: option.text
2642
+ }
2643
+ ),
2644
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx(
2645
+ lucideReact.Check,
2646
+ {
2647
+ className: "mr-2 size-4 shrink-0 stroke-current text-brand-500",
2648
+ "aria-hidden": "true"
2649
+ }
2650
+ )
2651
+ ]
2652
+ }
2653
+ )
2654
+ }
2655
+ ) }, index);
2656
+ }) })
2657
+ }
2658
+ );
2659
+ if (!portal || typeof document === "undefined") return menu;
2660
+ return reactDom.createPortal(menu, document.body);
2661
+ })()
2520
2662
  ] }) })
2521
2663
  ] });
2522
2664
  };