@andreagiugni/tailwind-dashboard-ui 0.5.15 → 0.5.16

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
@@ -167,7 +167,7 @@ Legend: **(+native)** = also extends the element's native HTML attributes.
167
167
  | `Alert` | `variant` (success/error/warning/info), `title`, `message`, `showLink`, `linkHref`, `linkText`, `icon?` (override icon), (+native div) |
168
168
  | `Avatar` | `src`, `alt`, `size` (xsmall…xxlarge), `status` (online/offline/busy/none), `statusColor?`, (+native img) |
169
169
  | `AvatarText` | `children`, `className`, (+native div) |
170
- | `Dropdown` | `isOpen`, `onClose`, `children`, (+native div) |
170
+ | `Dropdown` | `isOpen`, `onClose`, `children`, `portal?` (default `true`, prevents clipping inside tables/overflow containers), (+native div) |
171
171
  | `DropdownItem` | `variant` (default/primary/outline, like Button), `icon?`, `bgColor`/`textColor`/`borderColor` overrides, `tag` (a/button), `href`, `onClick`, `onItemClick`, `baseClassName` (full override) |
172
172
  | `Modal` | `isOpen`, `onClose`, `title?` (wrapping header aligned with close button), `showCloseButton`, `isFullscreen`, `closeOnBackdrop?`, `closeOnEsc?`; **alert preset** → `variant` (`success`/`info`/`warning`/`danger`) + `title`, `description`, `actionText?`, `onAction?`; (+native div) |
173
173
  | `Table` (composable) | `TableHeader` / `TableBody` / `TableRow` / `TableCell` with `children`, `isHeader` (cell), (+native table elements; `onClick` / `onDoubleClick` on rows & cells) |
package/dist/index.cjs CHANGED
@@ -11,6 +11,7 @@ var chunkTF3G3E72_cjs = require('./chunk-TF3G3E72.cjs');
11
11
  var chunkYERNSNT4_cjs = require('./chunk-YERNSNT4.cjs');
12
12
  var jsxRuntime = require('react/jsx-runtime');
13
13
  var React5 = require('react');
14
+ var reactDom = require('react-dom');
14
15
  var flatpickr = require('flatpickr');
15
16
  require('flatpickr/dist/flatpickr.css');
16
17
 
@@ -420,9 +421,13 @@ var Dropdown = ({
420
421
  onClose,
421
422
  children,
422
423
  className,
424
+ portal = true,
425
+ style,
423
426
  ...rest
424
427
  }) => {
425
428
  const dropdownRef = React5.useRef(null);
429
+ const anchorRef = React5.useRef(null);
430
+ const [position, setPosition] = React5.useState();
426
431
  React5.useEffect(() => {
427
432
  const handleClickOutside = (event) => {
428
433
  if (dropdownRef.current && !dropdownRef.current.contains(event.target) && !event.target.closest(".dropdown-toggle")) {
@@ -434,20 +439,57 @@ var Dropdown = ({
434
439
  document.removeEventListener("mousedown", handleClickOutside);
435
440
  };
436
441
  }, [onClose]);
442
+ React5.useLayoutEffect(() => {
443
+ if (!isOpen || !portal) return;
444
+ const updatePosition = () => {
445
+ const anchor = anchorRef.current;
446
+ const menu2 = dropdownRef.current;
447
+ const trigger = anchor?.parentElement?.querySelector(".dropdown-toggle");
448
+ if (!anchor || !menu2) return;
449
+ const triggerRect = (trigger ?? anchor).getBoundingClientRect();
450
+ const menuRect = menu2.getBoundingClientRect();
451
+ const gap = 8;
452
+ const viewportPadding = 8;
453
+ const fitsBelow = triggerRect.bottom + gap + menuRect.height <= window.innerHeight - viewportPadding;
454
+ const top = fitsBelow ? triggerRect.bottom + gap : Math.max(viewportPadding, triggerRect.top - gap - menuRect.height);
455
+ const left = Math.min(
456
+ window.innerWidth - viewportPadding - menuRect.width,
457
+ Math.max(viewportPadding, triggerRect.right - menuRect.width)
458
+ );
459
+ setPosition({ top, left });
460
+ };
461
+ updatePosition();
462
+ window.addEventListener("resize", updatePosition);
463
+ window.addEventListener("scroll", updatePosition, true);
464
+ return () => {
465
+ window.removeEventListener("resize", updatePosition);
466
+ window.removeEventListener("scroll", updatePosition, true);
467
+ };
468
+ }, [isOpen, portal]);
437
469
  if (!isOpen) return null;
438
- return /* @__PURE__ */ jsxRuntime.jsx(
470
+ const menu = /* @__PURE__ */ jsxRuntime.jsx(
439
471
  "div",
440
472
  {
441
473
  ref: dropdownRef,
442
474
  className: chunkYERNSNT4_cjs.cn(
443
- "absolute right-0 mt-2 rounded-xl border border-gray-200 bg-white shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark",
444
- chunkTF3G3E72_cjs.layers.menu,
475
+ "rounded-xl border border-gray-200 bg-white shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark",
476
+ portal ? chunkYERNSNT4_cjs.cn("fixed", chunkTF3G3E72_cjs.layers.portal) : chunkYERNSNT4_cjs.cn("absolute right-0 mt-2", chunkTF3G3E72_cjs.layers.menu),
445
477
  className
446
478
  ),
479
+ style: {
480
+ ...portal && !position ? { visibility: "hidden" } : void 0,
481
+ ...portal ? position : void 0,
482
+ ...style
483
+ },
447
484
  ...rest,
448
485
  children
449
486
  }
450
487
  );
488
+ if (!portal || typeof document === "undefined") return menu;
489
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
490
+ /* @__PURE__ */ jsxRuntime.jsx("span", { ref: anchorRef, "aria-hidden": "true", className: "pointer-events-none absolute" }),
491
+ reactDom.createPortal(menu, document.body)
492
+ ] });
451
493
  };
452
494
  var variantBase = "flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm font-medium transition";
453
495
  var variantClasses3 = {