@diwauhris/ui 1.7.38 → 1.7.40

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/lib/assets/ui.css CHANGED
@@ -250,6 +250,10 @@
250
250
  pointer-events: none;
251
251
  }
252
252
 
253
+ .pointer-events-auto {
254
+ pointer-events: auto;
255
+ }
256
+
253
257
  .\!visible {
254
258
  visibility: visible !important;
255
259
  }
@@ -1328,6 +1332,10 @@
1328
1332
  max-width: 96px;
1329
1333
  }
1330
1334
 
1335
+ .max-w-\[calc\(100vw-16px\)\] {
1336
+ max-width: calc(100vw - 16px);
1337
+ }
1338
+
1331
1339
  .max-w-lg {
1332
1340
  max-width: 32rem;
1333
1341
  }
@@ -2560,6 +2568,10 @@
2560
2568
  background-color: rgb(0 55 123 / 0.5);
2561
2569
  }
2562
2570
 
2571
+ .bg-brand-navy\/60 {
2572
+ background-color: rgb(0 55 123 / 0.6);
2573
+ }
2574
+
2563
2575
  .bg-brand-navy\/\[0\.03\] {
2564
2576
  background-color: rgb(0 55 123 / 0.03);
2565
2577
  }
@@ -4157,6 +4169,11 @@
4157
4169
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
4158
4170
  }
4159
4171
 
4172
+ .backdrop-blur {
4173
+ --tw-backdrop-blur: blur(8px);
4174
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
4175
+ }
4176
+
4160
4177
  .backdrop-blur-sm {
4161
4178
  --tw-backdrop-blur: blur(4px);
4162
4179
  backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
@@ -4232,6 +4249,14 @@
4232
4249
  transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
4233
4250
  }
4234
4251
 
4252
+ .will-change-\[opacity\] {
4253
+ will-change: opacity;
4254
+ }
4255
+
4256
+ .will-change-\[transform\2c opacity\] {
4257
+ will-change: transform,opacity;
4258
+ }
4259
+
4235
4260
  @keyframes enter {
4236
4261
  from {
4237
4262
  opacity: var(--tw-enter-opacity, 1);
package/lib/index.cjs CHANGED
@@ -2462,73 +2462,76 @@ function Modal({ isOpen, onClose, title, children, icon, subtitle, headerMeta, f
2462
2462
  onOpenChange: (isOpen) => {
2463
2463
  if (!isOpen && !preventClose) onClose();
2464
2464
  },
2465
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Portal, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Overlay, { className: "fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 duration-200" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Content, {
2466
- className: cn("fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2", "flex w-full flex-col", "rounded-lg border border-slate-200 bg-white shadow-xl", "max-h-[85vh] outline-none", maxWidth, "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0", "data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95", "duration-200"),
2467
- style: { padding: 0 },
2468
- onOpenAutoFocus,
2469
- onCloseAutoFocus,
2470
- onEscapeKeyDown: preventClose ? (e) => e.preventDefault() : onEscapeKeyDown,
2471
- onInteractOutside: (e) => {
2472
- if (e.target?.closest(".pis-datepicker-popover")) {
2473
- e.preventDefault();
2474
- return;
2475
- }
2476
- if (preventClose) {
2477
- e.preventDefault();
2478
- return;
2479
- }
2480
- onInteractOutside?.(e);
2481
- },
2482
- children: [
2483
- !preventClose && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Close, {
2484
- asChild: true,
2485
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2486
- type: "button",
2487
- "aria-label": closeLabel,
2488
- className: "absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
2489
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
2490
- size: 16,
2491
- "aria-hidden": "true"
2465
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Portal, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Overlay, { className: "fixed inset-0 z-50 bg-brand-navy/60 will-change-[opacity] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 duration-150 ease-out" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2466
+ className: "pointer-events-none fixed inset-0 z-50 flex items-center justify-center p-4",
2467
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Content, {
2468
+ className: cn("pointer-events-auto relative", "flex w-full flex-col", "rounded-lg border border-slate-200 bg-white shadow-xl", "max-h-[85vh] outline-none", maxWidth, "will-change-[transform,opacity]", "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0", "data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95", "duration-150 ease-out"),
2469
+ style: { padding: 0 },
2470
+ onOpenAutoFocus,
2471
+ onCloseAutoFocus,
2472
+ onEscapeKeyDown: preventClose ? (e) => e.preventDefault() : onEscapeKeyDown,
2473
+ onInteractOutside: (e) => {
2474
+ if (e.target?.closest(".pis-datepicker-popover")) {
2475
+ e.preventDefault();
2476
+ return;
2477
+ }
2478
+ if (preventClose) {
2479
+ e.preventDefault();
2480
+ return;
2481
+ }
2482
+ onInteractOutside?.(e);
2483
+ },
2484
+ children: [
2485
+ !preventClose && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Close, {
2486
+ asChild: true,
2487
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2488
+ type: "button",
2489
+ "aria-label": closeLabel,
2490
+ className: "absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
2491
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
2492
+ size: 16,
2493
+ "aria-hidden": "true"
2494
+ })
2492
2495
  })
2493
- })
2494
- }),
2495
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2496
- className: "px-6 pt-6 pb-4 border-b-2 border-slate-100",
2497
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2498
- className: "flex items-center gap-2.5",
2499
- children: [icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2500
- className: iconBadgeClass,
2501
- children: icon
2502
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Title, {
2503
- className: cn(typography.titleOverlay, danger ? "text-rose-700" : "text-slate-900"),
2504
- children: title
2505
- }), subtitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Description, {
2506
- className: "text-xs text-slate-500 mt-0.5",
2507
- children: subtitle
2508
- })] })]
2509
- }), headerMeta && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2510
- className: "mt-2",
2511
- children: headerMeta
2512
- })]
2513
- }),
2514
- onSubmit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2515
- onSubmit,
2516
- className: "flex flex-col min-h-0 flex-1",
2517
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2496
+ }),
2497
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2498
+ className: "px-6 pt-6 pb-4 border-b-2 border-slate-100",
2499
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2500
+ className: "flex items-center gap-2.5",
2501
+ children: [icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2502
+ className: iconBadgeClass,
2503
+ children: icon
2504
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Title, {
2505
+ className: cn(typography.titleOverlay, danger ? "text-rose-700" : "text-slate-900"),
2506
+ children: title
2507
+ }), subtitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Description, {
2508
+ className: "text-xs text-slate-500 mt-0.5",
2509
+ children: subtitle
2510
+ })] })]
2511
+ }), headerMeta && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2512
+ className: "mt-2",
2513
+ children: headerMeta
2514
+ })]
2515
+ }),
2516
+ onSubmit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2517
+ onSubmit,
2518
+ className: "flex flex-col min-h-0 flex-1",
2519
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2520
+ className: "flex-1 overflow-y-auto px-6 py-5",
2521
+ children: bodyContent
2522
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2523
+ className: "px-6 py-4 border-t border-slate-100",
2524
+ children: resolvedFooter
2525
+ })]
2526
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2518
2527
  className: "flex-1 overflow-y-auto px-6 py-5",
2519
2528
  children: bodyContent
2520
2529
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2521
2530
  className: "px-6 py-4 border-t border-slate-100",
2522
2531
  children: resolvedFooter
2523
- })]
2524
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2525
- className: "flex-1 overflow-y-auto px-6 py-5",
2526
- children: bodyContent
2527
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2528
- className: "px-6 py-4 border-t border-slate-100",
2529
- children: resolvedFooter
2530
- })] })
2531
- ]
2532
+ })] })
2533
+ ]
2534
+ })
2532
2535
  })] })
2533
2536
  });
2534
2537
  }
@@ -3989,26 +3992,44 @@ function SidebarItem({ href, onClick, active = false, icon, badge, disabled = fa
3989
3992
  */
3990
3993
  function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassName = "", panelClassName = "" }) {
3991
3994
  const ref = (0, react.useRef)(null);
3995
+ const panelRef = (0, react.useRef)(null);
3992
3996
  const triggerRef = (0, react.useRef)(null);
3993
3997
  const panelId = `${(0, react.useId)().replace(/:/g, "")}-panel`;
3994
3998
  const [panelStyle, setPanelStyle] = (0, react.useState)({});
3999
+ const [positioned, setPositioned] = (0, react.useState)(false);
3995
4000
  (0, react.useEffect)(() => {
3996
- if (!isOpen || !triggerRef.current) return;
4001
+ if (!isOpen || !triggerRef.current) {
4002
+ setPositioned(false);
4003
+ return;
4004
+ }
4005
+ const GAP = 4;
4006
+ const MARGIN = 8;
3997
4007
  const updatePosition = () => {
3998
4008
  if (!triggerRef.current) return;
3999
4009
  const rect = triggerRef.current.getBoundingClientRect();
4010
+ const panelHeight = panelRef.current?.offsetHeight ?? 280;
4011
+ const panelWidth = panelRef.current?.offsetWidth ?? Math.max(rect.width, 180);
4012
+ const spaceBelow = window.innerHeight - rect.bottom;
4013
+ const spaceAbove = rect.top;
4014
+ const top = spaceBelow >= panelHeight + GAP + MARGIN || spaceBelow >= spaceAbove ? Math.min(rect.bottom + GAP, window.innerHeight - panelHeight - MARGIN) : Math.max(rect.top - panelHeight - GAP, MARGIN);
4015
+ const left = Math.max(MARGIN, Math.min(rect.left, window.innerWidth - panelWidth - MARGIN));
4000
4016
  setPanelStyle({
4001
4017
  position: "fixed",
4002
- top: rect.bottom + 4,
4003
- left: rect.left,
4004
- zIndex: 9999,
4018
+ top: Math.max(MARGIN, top),
4019
+ left,
4020
+ zIndex: 1e3,
4005
4021
  minWidth: Math.max(rect.width, 180)
4006
4022
  });
4007
4023
  };
4008
4024
  updatePosition();
4025
+ const raf = requestAnimationFrame(() => {
4026
+ updatePosition();
4027
+ setPositioned(true);
4028
+ });
4009
4029
  window.addEventListener("scroll", updatePosition, true);
4010
4030
  window.addEventListener("resize", updatePosition);
4011
4031
  return () => {
4032
+ cancelAnimationFrame(raf);
4012
4033
  window.removeEventListener("scroll", updatePosition, true);
4013
4034
  window.removeEventListener("resize", updatePosition);
4014
4035
  };
@@ -4017,6 +4038,8 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
4017
4038
  if (!isOpen) return;
4018
4039
  const handlePointerDown = (e) => {
4019
4040
  const target = e.target;
4041
+ if (panelRef.current?.contains(target)) return;
4042
+ if (target.closest?.(".pis-datepicker-popover")) return;
4020
4043
  if (ref.current && !ref.current.contains(target)) {
4021
4044
  onClose();
4022
4045
  triggerRef.current?.focus();
@@ -4051,8 +4074,12 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
4051
4074
  children: trigger
4052
4075
  }), isOpen && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4053
4076
  id: panelId,
4054
- style: panelStyle,
4055
- className: cn("max-h-[280px] overflow-y-auto", "rounded-xl border border-slate-200 bg-white shadow-lg", panelClassName),
4077
+ ref: panelRef,
4078
+ style: {
4079
+ ...panelStyle,
4080
+ visibility: positioned ? "visible" : "hidden"
4081
+ },
4082
+ className: cn("w-max max-w-[calc(100vw-16px)] max-h-[280px] overflow-y-auto", "rounded-xl border border-slate-200 bg-white shadow-lg", panelClassName),
4056
4083
  children
4057
4084
  }), document.body)]
4058
4085
  });
package/lib/index.mjs CHANGED
@@ -2438,73 +2438,76 @@ function Modal({ isOpen, onClose, title, children, icon, subtitle, headerMeta, f
2438
2438
  onOpenChange: (isOpen) => {
2439
2439
  if (!isOpen && !preventClose) onClose();
2440
2440
  },
2441
- children: /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [/* @__PURE__ */ jsx(DialogPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 duration-200" }), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
2442
- className: cn("fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2", "flex w-full flex-col", "rounded-lg border border-slate-200 bg-white shadow-xl", "max-h-[85vh] outline-none", maxWidth, "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0", "data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95", "duration-200"),
2443
- style: { padding: 0 },
2444
- onOpenAutoFocus,
2445
- onCloseAutoFocus,
2446
- onEscapeKeyDown: preventClose ? (e) => e.preventDefault() : onEscapeKeyDown,
2447
- onInteractOutside: (e) => {
2448
- if (e.target?.closest(".pis-datepicker-popover")) {
2449
- e.preventDefault();
2450
- return;
2451
- }
2452
- if (preventClose) {
2453
- e.preventDefault();
2454
- return;
2455
- }
2456
- onInteractOutside?.(e);
2457
- },
2458
- children: [
2459
- !preventClose && /* @__PURE__ */ jsx(DialogPrimitive.Close, {
2460
- asChild: true,
2461
- children: /* @__PURE__ */ jsx("button", {
2462
- type: "button",
2463
- "aria-label": closeLabel,
2464
- className: "absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
2465
- children: /* @__PURE__ */ jsx(X, {
2466
- size: 16,
2467
- "aria-hidden": "true"
2441
+ children: /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [/* @__PURE__ */ jsx(DialogPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-brand-navy/60 will-change-[opacity] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 duration-150 ease-out" }), /* @__PURE__ */ jsx("div", {
2442
+ className: "pointer-events-none fixed inset-0 z-50 flex items-center justify-center p-4",
2443
+ children: /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
2444
+ className: cn("pointer-events-auto relative", "flex w-full flex-col", "rounded-lg border border-slate-200 bg-white shadow-xl", "max-h-[85vh] outline-none", maxWidth, "will-change-[transform,opacity]", "data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0", "data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95", "duration-150 ease-out"),
2445
+ style: { padding: 0 },
2446
+ onOpenAutoFocus,
2447
+ onCloseAutoFocus,
2448
+ onEscapeKeyDown: preventClose ? (e) => e.preventDefault() : onEscapeKeyDown,
2449
+ onInteractOutside: (e) => {
2450
+ if (e.target?.closest(".pis-datepicker-popover")) {
2451
+ e.preventDefault();
2452
+ return;
2453
+ }
2454
+ if (preventClose) {
2455
+ e.preventDefault();
2456
+ return;
2457
+ }
2458
+ onInteractOutside?.(e);
2459
+ },
2460
+ children: [
2461
+ !preventClose && /* @__PURE__ */ jsx(DialogPrimitive.Close, {
2462
+ asChild: true,
2463
+ children: /* @__PURE__ */ jsx("button", {
2464
+ type: "button",
2465
+ "aria-label": closeLabel,
2466
+ className: "absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
2467
+ children: /* @__PURE__ */ jsx(X, {
2468
+ size: 16,
2469
+ "aria-hidden": "true"
2470
+ })
2468
2471
  })
2469
- })
2470
- }),
2471
- /* @__PURE__ */ jsxs("div", {
2472
- className: "px-6 pt-6 pb-4 border-b-2 border-slate-100",
2473
- children: [/* @__PURE__ */ jsxs("div", {
2474
- className: "flex items-center gap-2.5",
2475
- children: [icon && /* @__PURE__ */ jsx("div", {
2476
- className: iconBadgeClass,
2477
- children: icon
2478
- }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DialogPrimitive.Title, {
2479
- className: cn(typography.titleOverlay, danger ? "text-rose-700" : "text-slate-900"),
2480
- children: title
2481
- }), subtitle && /* @__PURE__ */ jsx(DialogPrimitive.Description, {
2482
- className: "text-xs text-slate-500 mt-0.5",
2483
- children: subtitle
2484
- })] })]
2485
- }), headerMeta && /* @__PURE__ */ jsx("div", {
2486
- className: "mt-2",
2487
- children: headerMeta
2488
- })]
2489
- }),
2490
- onSubmit ? /* @__PURE__ */ jsxs("form", {
2491
- onSubmit,
2492
- className: "flex flex-col min-h-0 flex-1",
2493
- children: [/* @__PURE__ */ jsx("div", {
2472
+ }),
2473
+ /* @__PURE__ */ jsxs("div", {
2474
+ className: "px-6 pt-6 pb-4 border-b-2 border-slate-100",
2475
+ children: [/* @__PURE__ */ jsxs("div", {
2476
+ className: "flex items-center gap-2.5",
2477
+ children: [icon && /* @__PURE__ */ jsx("div", {
2478
+ className: iconBadgeClass,
2479
+ children: icon
2480
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DialogPrimitive.Title, {
2481
+ className: cn(typography.titleOverlay, danger ? "text-rose-700" : "text-slate-900"),
2482
+ children: title
2483
+ }), subtitle && /* @__PURE__ */ jsx(DialogPrimitive.Description, {
2484
+ className: "text-xs text-slate-500 mt-0.5",
2485
+ children: subtitle
2486
+ })] })]
2487
+ }), headerMeta && /* @__PURE__ */ jsx("div", {
2488
+ className: "mt-2",
2489
+ children: headerMeta
2490
+ })]
2491
+ }),
2492
+ onSubmit ? /* @__PURE__ */ jsxs("form", {
2493
+ onSubmit,
2494
+ className: "flex flex-col min-h-0 flex-1",
2495
+ children: [/* @__PURE__ */ jsx("div", {
2496
+ className: "flex-1 overflow-y-auto px-6 py-5",
2497
+ children: bodyContent
2498
+ }), /* @__PURE__ */ jsx("div", {
2499
+ className: "px-6 py-4 border-t border-slate-100",
2500
+ children: resolvedFooter
2501
+ })]
2502
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
2494
2503
  className: "flex-1 overflow-y-auto px-6 py-5",
2495
2504
  children: bodyContent
2496
2505
  }), /* @__PURE__ */ jsx("div", {
2497
2506
  className: "px-6 py-4 border-t border-slate-100",
2498
2507
  children: resolvedFooter
2499
- })]
2500
- }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
2501
- className: "flex-1 overflow-y-auto px-6 py-5",
2502
- children: bodyContent
2503
- }), /* @__PURE__ */ jsx("div", {
2504
- className: "px-6 py-4 border-t border-slate-100",
2505
- children: resolvedFooter
2506
- })] })
2507
- ]
2508
+ })] })
2509
+ ]
2510
+ })
2508
2511
  })] })
2509
2512
  });
2510
2513
  }
@@ -3965,26 +3968,44 @@ function SidebarItem({ href, onClick, active = false, icon, badge, disabled = fa
3965
3968
  */
3966
3969
  function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassName = "", panelClassName = "" }) {
3967
3970
  const ref = useRef(null);
3971
+ const panelRef = useRef(null);
3968
3972
  const triggerRef = useRef(null);
3969
3973
  const panelId = `${useId().replace(/:/g, "")}-panel`;
3970
3974
  const [panelStyle, setPanelStyle] = useState({});
3975
+ const [positioned, setPositioned] = useState(false);
3971
3976
  useEffect(() => {
3972
- if (!isOpen || !triggerRef.current) return;
3977
+ if (!isOpen || !triggerRef.current) {
3978
+ setPositioned(false);
3979
+ return;
3980
+ }
3981
+ const GAP = 4;
3982
+ const MARGIN = 8;
3973
3983
  const updatePosition = () => {
3974
3984
  if (!triggerRef.current) return;
3975
3985
  const rect = triggerRef.current.getBoundingClientRect();
3986
+ const panelHeight = panelRef.current?.offsetHeight ?? 280;
3987
+ const panelWidth = panelRef.current?.offsetWidth ?? Math.max(rect.width, 180);
3988
+ const spaceBelow = window.innerHeight - rect.bottom;
3989
+ const spaceAbove = rect.top;
3990
+ const top = spaceBelow >= panelHeight + GAP + MARGIN || spaceBelow >= spaceAbove ? Math.min(rect.bottom + GAP, window.innerHeight - panelHeight - MARGIN) : Math.max(rect.top - panelHeight - GAP, MARGIN);
3991
+ const left = Math.max(MARGIN, Math.min(rect.left, window.innerWidth - panelWidth - MARGIN));
3976
3992
  setPanelStyle({
3977
3993
  position: "fixed",
3978
- top: rect.bottom + 4,
3979
- left: rect.left,
3980
- zIndex: 9999,
3994
+ top: Math.max(MARGIN, top),
3995
+ left,
3996
+ zIndex: 1e3,
3981
3997
  minWidth: Math.max(rect.width, 180)
3982
3998
  });
3983
3999
  };
3984
4000
  updatePosition();
4001
+ const raf = requestAnimationFrame(() => {
4002
+ updatePosition();
4003
+ setPositioned(true);
4004
+ });
3985
4005
  window.addEventListener("scroll", updatePosition, true);
3986
4006
  window.addEventListener("resize", updatePosition);
3987
4007
  return () => {
4008
+ cancelAnimationFrame(raf);
3988
4009
  window.removeEventListener("scroll", updatePosition, true);
3989
4010
  window.removeEventListener("resize", updatePosition);
3990
4011
  };
@@ -3993,6 +4014,8 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
3993
4014
  if (!isOpen) return;
3994
4015
  const handlePointerDown = (e) => {
3995
4016
  const target = e.target;
4017
+ if (panelRef.current?.contains(target)) return;
4018
+ if (target.closest?.(".pis-datepicker-popover")) return;
3996
4019
  if (ref.current && !ref.current.contains(target)) {
3997
4020
  onClose();
3998
4021
  triggerRef.current?.focus();
@@ -4027,8 +4050,12 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
4027
4050
  children: trigger
4028
4051
  }), isOpen && createPortal(/* @__PURE__ */ jsx("div", {
4029
4052
  id: panelId,
4030
- style: panelStyle,
4031
- className: cn("max-h-[280px] overflow-y-auto", "rounded-xl border border-slate-200 bg-white shadow-lg", panelClassName),
4053
+ ref: panelRef,
4054
+ style: {
4055
+ ...panelStyle,
4056
+ visibility: positioned ? "visible" : "hidden"
4057
+ },
4058
+ className: cn("w-max max-w-[calc(100vw-16px)] max-h-[280px] overflow-y-auto", "rounded-xl border border-slate-200 bg-white shadow-lg", panelClassName),
4032
4059
  children
4033
4060
  }), document.body)]
4034
4061
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diwauhris/ui",
3
3
  "private": false,
4
- "version": "1.7.38",
4
+ "version": "1.7.40",
5
5
  "type": "module",
6
6
  "description": "DIWA UHRIS UI component library — React + Tailwind CSS",
7
7
  "keywords": [