@diwauhris/ui 1.7.27 → 1.7.28

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/index.cjs CHANGED
@@ -1408,16 +1408,20 @@ function computePopupStyle(rect, placement = "auto", measuredHeight) {
1408
1408
  left: isNarrow ? 8 : Math.max(8, Math.min(rect.left, window.innerWidth - POPOVER_WIDTH - 8)),
1409
1409
  width: isNarrow ? `calc(100vw - 16px)` : void 0
1410
1410
  };
1411
- if (placement === "below" ? true : placement === "above" ? false : spaceBelow >= height || spaceBelow >= spaceAbove) style.top = Math.min(rect.bottom + 6, window.innerHeight - height - 8);
1411
+ const placeBelow = placement === "below" ? true : placement === "above" ? false : spaceBelow >= height || spaceBelow >= spaceAbove;
1412
+ if (placeBelow) style.top = Math.min(rect.bottom + 6, window.innerHeight - height - 8);
1412
1413
  else style.top = Math.max(rect.top - height - 6, 8);
1413
- return style;
1414
+ return {
1415
+ style,
1416
+ placedAbove: !placeBelow
1417
+ };
1414
1418
  }
1415
1419
  function DatePicker({ value, onChange, placeholder, className, disabled, error, minDate, maxDate, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, required, dateFormat = "MMM d, yyyy", clearable = false, onOpenChange, placement = "auto", locale, name, isDateDisabled, readOnly = false, open, defaultOpen = false }) {
1416
1420
  const isControlled = open !== void 0;
1417
1421
  const [uncontrolledOpen, setUncontrolledOpen] = (0, react.useState)(defaultOpen);
1418
1422
  const isOpen = isControlled ? open : uncontrolledOpen;
1419
1423
  const [popupStyle, setPopupStyle] = (0, react.useState)({});
1420
- const [positioned, setPositioned] = (0, react.useState)(false);
1424
+ const placedAbove = (0, react.useRef)(false);
1421
1425
  const wrapperRef = (0, react.useRef)(null);
1422
1426
  const popoverRef = (0, react.useRef)(null);
1423
1427
  const inputRef = (0, react.useRef)(null);
@@ -1455,9 +1459,8 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1455
1459
  if (!wrapperRef.current) return;
1456
1460
  requestAnimationFrame(() => {
1457
1461
  if (!wrapperRef.current) return;
1458
- const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
1459
- setPopupStyle(style);
1460
- setPositioned(false);
1462
+ const result = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
1463
+ placedAbove.current = result.placedAbove;
1461
1464
  setIsOpen(true);
1462
1465
  });
1463
1466
  }, 50);
@@ -1467,34 +1470,43 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1467
1470
  placement,
1468
1471
  setIsOpen
1469
1472
  ]);
1470
- (0, react.useLayoutEffect)(() => {
1473
+ const [popoverReady, setPopoverReady] = (0, react.useState)(false);
1474
+ (0, react.useEffect)(() => {
1471
1475
  if (!isOpen) {
1472
- setPositioned(false);
1476
+ setPopoverReady(false);
1477
+ setPopupStyle({});
1473
1478
  return;
1474
1479
  }
1475
- if (!popoverRef.current || !wrapperRef.current) return;
1476
- const measuredHeight = popoverRef.current.offsetHeight;
1477
- const rect = wrapperRef.current.getBoundingClientRect();
1478
- setPopupStyle(computePopupStyle(rect, placement, measuredHeight));
1479
- setPositioned(true);
1480
- }, [isOpen, placement]);
1480
+ const frameId = requestAnimationFrame(() => {
1481
+ if (!popoverRef.current || !wrapperRef.current) return;
1482
+ const measuredHeight = popoverRef.current.offsetHeight;
1483
+ const result = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placedAbove.current ? "above" : "below", measuredHeight);
1484
+ setPopupStyle(result.style);
1485
+ setPopoverReady(true);
1486
+ });
1487
+ return () => cancelAnimationFrame(frameId);
1488
+ }, [isOpen]);
1481
1489
  (0, react.useEffect)(() => {
1490
+ if (!isOpen) return;
1482
1491
  function handleClickOutside(e) {
1483
1492
  const target = e.target;
1484
- if (wrapperRef.current && !wrapperRef.current.contains(target) && popoverRef.current && !popoverRef.current.contains(target)) setIsOpen(false);
1493
+ if (!popoverRef.current) return;
1494
+ const inWrapper = wrapperRef.current?.contains(target);
1495
+ const inPopover = popoverRef.current.contains(target);
1496
+ if (!inWrapper && !inPopover) setIsOpen(false);
1485
1497
  }
1486
1498
  document.addEventListener("mousedown", handleClickOutside);
1487
1499
  return () => document.removeEventListener("mousedown", handleClickOutside);
1488
- }, [setIsOpen]);
1500
+ }, [isOpen, setIsOpen]);
1489
1501
  (0, react.useEffect)(() => {
1490
- if (!isOpen || !positioned) return;
1502
+ if (!isOpen) return;
1491
1503
  let armed = false;
1492
1504
  const armTimer = window.setTimeout(() => {
1493
1505
  armed = true;
1494
- }, 150);
1506
+ }, 100);
1495
1507
  const close = () => setIsOpen(false);
1496
1508
  const onScroll = () => {
1497
- if (armed) setIsOpen(false);
1509
+ if (armed) close();
1498
1510
  };
1499
1511
  const scrollParent = getScrollParent(wrapperRef.current?.parentElement ?? null);
1500
1512
  if (scrollParent) scrollParent.addEventListener("scroll", onScroll, { passive: true });
@@ -1509,17 +1521,15 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1509
1521
  window.removeEventListener("scroll", onScroll, { capture: true });
1510
1522
  window.removeEventListener("resize", close);
1511
1523
  };
1512
- }, [
1513
- isOpen,
1514
- positioned,
1515
- setIsOpen
1516
- ]);
1524
+ }, [isOpen, setIsOpen]);
1517
1525
  (0, react.useEffect)(() => {
1518
1526
  if (!isOpen) return;
1519
1527
  function handleFocusOut(e) {
1520
1528
  if (!e.relatedTarget) return;
1521
1529
  const target = e.relatedTarget;
1522
- if (wrapperRef.current && !wrapperRef.current.contains(target) && popoverRef.current && !popoverRef.current.contains(target)) setIsOpen(false);
1530
+ const inWrapper = wrapperRef.current?.contains(target);
1531
+ const inPopover = popoverRef.current?.contains(target);
1532
+ if (!inWrapper && !inPopover) setIsOpen(false);
1523
1533
  }
1524
1534
  const wrapper = wrapperRef.current;
1525
1535
  wrapper?.addEventListener("focusout", handleFocusOut);
@@ -1587,8 +1597,11 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1587
1597
  })]
1588
1598
  }), isOpen && !disabled && !readOnly && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1589
1599
  ref: popoverRef,
1590
- className: cn("pis-datepicker-popover", !positioned && "pis-datepicker-popover--measuring"),
1591
- style: popupStyle,
1600
+ className: "pis-datepicker-popover",
1601
+ style: {
1602
+ ...popupStyle,
1603
+ visibility: popoverReady ? "visible" : "hidden"
1604
+ },
1592
1605
  role: "dialog",
1593
1606
  "aria-label": ariaLabel ? `${ariaLabel} calendar` : "Date picker",
1594
1607
  "aria-modal": "false",
package/lib/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { clsx } from "clsx";
2
2
  import { twMerge } from "tailwind-merge";
3
3
  import * as React$1 from "react";
4
- import React, { cloneElement, createContext, createElement, isValidElement, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
4
+ import React, { cloneElement, createContext, createElement, isValidElement, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { createPortal } from "react-dom";
7
7
  import { AlertCircle, AlertTriangle, Archive, ArchiveRestore, ArrowDown, Ban, CalendarDays, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, Clock, Command, File, Home, Info, Minus, RotateCcw, Search as Search$1, ShieldCheck, TrendingDown, TrendingUp, UploadCloud, X, ZoomIn, ZoomOut } from "lucide-react";
@@ -1384,16 +1384,20 @@ function computePopupStyle(rect, placement = "auto", measuredHeight) {
1384
1384
  left: isNarrow ? 8 : Math.max(8, Math.min(rect.left, window.innerWidth - POPOVER_WIDTH - 8)),
1385
1385
  width: isNarrow ? `calc(100vw - 16px)` : void 0
1386
1386
  };
1387
- if (placement === "below" ? true : placement === "above" ? false : spaceBelow >= height || spaceBelow >= spaceAbove) style.top = Math.min(rect.bottom + 6, window.innerHeight - height - 8);
1387
+ const placeBelow = placement === "below" ? true : placement === "above" ? false : spaceBelow >= height || spaceBelow >= spaceAbove;
1388
+ if (placeBelow) style.top = Math.min(rect.bottom + 6, window.innerHeight - height - 8);
1388
1389
  else style.top = Math.max(rect.top - height - 6, 8);
1389
- return style;
1390
+ return {
1391
+ style,
1392
+ placedAbove: !placeBelow
1393
+ };
1390
1394
  }
1391
1395
  function DatePicker({ value, onChange, placeholder, className, disabled, error, minDate, maxDate, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, required, dateFormat = "MMM d, yyyy", clearable = false, onOpenChange, placement = "auto", locale, name, isDateDisabled, readOnly = false, open, defaultOpen = false }) {
1392
1396
  const isControlled = open !== void 0;
1393
1397
  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
1394
1398
  const isOpen = isControlled ? open : uncontrolledOpen;
1395
1399
  const [popupStyle, setPopupStyle] = useState({});
1396
- const [positioned, setPositioned] = useState(false);
1400
+ const placedAbove = useRef(false);
1397
1401
  const wrapperRef = useRef(null);
1398
1402
  const popoverRef = useRef(null);
1399
1403
  const inputRef = useRef(null);
@@ -1431,9 +1435,8 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1431
1435
  if (!wrapperRef.current) return;
1432
1436
  requestAnimationFrame(() => {
1433
1437
  if (!wrapperRef.current) return;
1434
- const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
1435
- setPopupStyle(style);
1436
- setPositioned(false);
1438
+ const result = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
1439
+ placedAbove.current = result.placedAbove;
1437
1440
  setIsOpen(true);
1438
1441
  });
1439
1442
  }, 50);
@@ -1443,34 +1446,43 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1443
1446
  placement,
1444
1447
  setIsOpen
1445
1448
  ]);
1446
- useLayoutEffect(() => {
1449
+ const [popoverReady, setPopoverReady] = useState(false);
1450
+ useEffect(() => {
1447
1451
  if (!isOpen) {
1448
- setPositioned(false);
1452
+ setPopoverReady(false);
1453
+ setPopupStyle({});
1449
1454
  return;
1450
1455
  }
1451
- if (!popoverRef.current || !wrapperRef.current) return;
1452
- const measuredHeight = popoverRef.current.offsetHeight;
1453
- const rect = wrapperRef.current.getBoundingClientRect();
1454
- setPopupStyle(computePopupStyle(rect, placement, measuredHeight));
1455
- setPositioned(true);
1456
- }, [isOpen, placement]);
1456
+ const frameId = requestAnimationFrame(() => {
1457
+ if (!popoverRef.current || !wrapperRef.current) return;
1458
+ const measuredHeight = popoverRef.current.offsetHeight;
1459
+ const result = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placedAbove.current ? "above" : "below", measuredHeight);
1460
+ setPopupStyle(result.style);
1461
+ setPopoverReady(true);
1462
+ });
1463
+ return () => cancelAnimationFrame(frameId);
1464
+ }, [isOpen]);
1457
1465
  useEffect(() => {
1466
+ if (!isOpen) return;
1458
1467
  function handleClickOutside(e) {
1459
1468
  const target = e.target;
1460
- if (wrapperRef.current && !wrapperRef.current.contains(target) && popoverRef.current && !popoverRef.current.contains(target)) setIsOpen(false);
1469
+ if (!popoverRef.current) return;
1470
+ const inWrapper = wrapperRef.current?.contains(target);
1471
+ const inPopover = popoverRef.current.contains(target);
1472
+ if (!inWrapper && !inPopover) setIsOpen(false);
1461
1473
  }
1462
1474
  document.addEventListener("mousedown", handleClickOutside);
1463
1475
  return () => document.removeEventListener("mousedown", handleClickOutside);
1464
- }, [setIsOpen]);
1476
+ }, [isOpen, setIsOpen]);
1465
1477
  useEffect(() => {
1466
- if (!isOpen || !positioned) return;
1478
+ if (!isOpen) return;
1467
1479
  let armed = false;
1468
1480
  const armTimer = window.setTimeout(() => {
1469
1481
  armed = true;
1470
- }, 150);
1482
+ }, 100);
1471
1483
  const close = () => setIsOpen(false);
1472
1484
  const onScroll = () => {
1473
- if (armed) setIsOpen(false);
1485
+ if (armed) close();
1474
1486
  };
1475
1487
  const scrollParent = getScrollParent(wrapperRef.current?.parentElement ?? null);
1476
1488
  if (scrollParent) scrollParent.addEventListener("scroll", onScroll, { passive: true });
@@ -1485,17 +1497,15 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1485
1497
  window.removeEventListener("scroll", onScroll, { capture: true });
1486
1498
  window.removeEventListener("resize", close);
1487
1499
  };
1488
- }, [
1489
- isOpen,
1490
- positioned,
1491
- setIsOpen
1492
- ]);
1500
+ }, [isOpen, setIsOpen]);
1493
1501
  useEffect(() => {
1494
1502
  if (!isOpen) return;
1495
1503
  function handleFocusOut(e) {
1496
1504
  if (!e.relatedTarget) return;
1497
1505
  const target = e.relatedTarget;
1498
- if (wrapperRef.current && !wrapperRef.current.contains(target) && popoverRef.current && !popoverRef.current.contains(target)) setIsOpen(false);
1506
+ const inWrapper = wrapperRef.current?.contains(target);
1507
+ const inPopover = popoverRef.current?.contains(target);
1508
+ if (!inWrapper && !inPopover) setIsOpen(false);
1499
1509
  }
1500
1510
  const wrapper = wrapperRef.current;
1501
1511
  wrapper?.addEventListener("focusout", handleFocusOut);
@@ -1563,8 +1573,11 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1563
1573
  })]
1564
1574
  }), isOpen && !disabled && !readOnly && createPortal(/* @__PURE__ */ jsx("div", {
1565
1575
  ref: popoverRef,
1566
- className: cn("pis-datepicker-popover", !positioned && "pis-datepicker-popover--measuring"),
1567
- style: popupStyle,
1576
+ className: "pis-datepicker-popover",
1577
+ style: {
1578
+ ...popupStyle,
1579
+ visibility: popoverReady ? "visible" : "hidden"
1580
+ },
1568
1581
  role: "dialog",
1569
1582
  "aria-label": ariaLabel ? `${ariaLabel} calendar` : "Date picker",
1570
1583
  "aria-modal": "false",
@@ -53,6 +53,9 @@ interface DatePickerProps {
53
53
  /** Initial open state when uncontrolled. Ignored when `open` is provided. */
54
54
  defaultOpen?: boolean;
55
55
  }
56
- export declare function computePopupStyle(rect: DOMRect, placement?: 'auto' | 'below' | 'above', measuredHeight?: number): React.CSSProperties;
56
+ export declare function computePopupStyle(rect: DOMRect, placement?: 'auto' | 'below' | 'above', measuredHeight?: number): {
57
+ style: React.CSSProperties;
58
+ placedAbove: boolean;
59
+ };
57
60
  export declare function DatePicker({ value, onChange, placeholder, className, disabled, error, minDate, maxDate, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, required, dateFormat, clearable, onOpenChange, placement, locale, name, isDateDisabled, readOnly, open, defaultOpen, }: DatePickerProps): import("react").JSX.Element;
58
61
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diwauhris/ui",
3
3
  "private": false,
4
- "version": "1.7.27",
4
+ "version": "1.7.28",
5
5
  "type": "module",
6
6
  "description": "DIWA UHRIS UI component library — React + Tailwind CSS",
7
7
  "keywords": [