@diwauhris/ui 1.7.25 → 1.7.27

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
@@ -4442,6 +4442,12 @@ body {
4442
4442
  animation: datepickerIn 0.2s cubic-bezier(0.16, 1, 0.3, 1);
4443
4443
  overflow: hidden;
4444
4444
  }
4445
+ /* Hidden during initial measurement — no animation until final position is set */
4446
+ .pis-datepicker-popover--measuring {
4447
+ opacity: 0;
4448
+ pointer-events: none;
4449
+ animation: none;
4450
+ }
4445
4451
  @keyframes datepickerIn {
4446
4452
  from { opacity: 0; transform: translateY(6px) scale(0.97); }
4447
4453
  to { opacity: 1; transform: translateY(0) scale(1); }
package/lib/index.cjs CHANGED
@@ -1417,6 +1417,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1417
1417
  const [uncontrolledOpen, setUncontrolledOpen] = (0, react.useState)(defaultOpen);
1418
1418
  const isOpen = isControlled ? open : uncontrolledOpen;
1419
1419
  const [popupStyle, setPopupStyle] = (0, react.useState)({});
1420
+ const [positioned, setPositioned] = (0, react.useState)(false);
1420
1421
  const wrapperRef = (0, react.useRef)(null);
1421
1422
  const popoverRef = (0, react.useRef)(null);
1422
1423
  const inputRef = (0, react.useRef)(null);
@@ -1456,6 +1457,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1456
1457
  if (!wrapperRef.current) return;
1457
1458
  const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
1458
1459
  setPopupStyle(style);
1460
+ setPositioned(false);
1459
1461
  setIsOpen(true);
1460
1462
  });
1461
1463
  }, 50);
@@ -1465,11 +1467,16 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1465
1467
  placement,
1466
1468
  setIsOpen
1467
1469
  ]);
1468
- (0, react.useEffect)(() => {
1469
- if (!isOpen || !popoverRef.current || !wrapperRef.current) return;
1470
+ (0, react.useLayoutEffect)(() => {
1471
+ if (!isOpen) {
1472
+ setPositioned(false);
1473
+ return;
1474
+ }
1475
+ if (!popoverRef.current || !wrapperRef.current) return;
1470
1476
  const measuredHeight = popoverRef.current.offsetHeight;
1471
1477
  const rect = wrapperRef.current.getBoundingClientRect();
1472
1478
  setPopupStyle(computePopupStyle(rect, placement, measuredHeight));
1479
+ setPositioned(true);
1473
1480
  }, [isOpen, placement]);
1474
1481
  (0, react.useEffect)(() => {
1475
1482
  function handleClickOutside(e) {
@@ -1478,23 +1485,35 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1478
1485
  }
1479
1486
  document.addEventListener("mousedown", handleClickOutside);
1480
1487
  return () => document.removeEventListener("mousedown", handleClickOutside);
1481
- }, []);
1488
+ }, [setIsOpen]);
1482
1489
  (0, react.useEffect)(() => {
1483
- if (!isOpen) return;
1490
+ if (!isOpen || !positioned) return;
1491
+ let armed = false;
1492
+ const armTimer = window.setTimeout(() => {
1493
+ armed = true;
1494
+ }, 150);
1484
1495
  const close = () => setIsOpen(false);
1496
+ const onScroll = () => {
1497
+ if (armed) setIsOpen(false);
1498
+ };
1485
1499
  const scrollParent = getScrollParent(wrapperRef.current?.parentElement ?? null);
1486
- if (scrollParent) scrollParent.addEventListener("scroll", close, { passive: true });
1487
- window.addEventListener("scroll", close, {
1500
+ if (scrollParent) scrollParent.addEventListener("scroll", onScroll, { passive: true });
1501
+ window.addEventListener("scroll", onScroll, {
1488
1502
  capture: true,
1489
1503
  passive: true
1490
1504
  });
1491
1505
  window.addEventListener("resize", close);
1492
1506
  return () => {
1493
- if (scrollParent) scrollParent.removeEventListener("scroll", close);
1494
- window.removeEventListener("scroll", close, { capture: true });
1507
+ window.clearTimeout(armTimer);
1508
+ if (scrollParent) scrollParent.removeEventListener("scroll", onScroll);
1509
+ window.removeEventListener("scroll", onScroll, { capture: true });
1495
1510
  window.removeEventListener("resize", close);
1496
1511
  };
1497
- }, [isOpen]);
1512
+ }, [
1513
+ isOpen,
1514
+ positioned,
1515
+ setIsOpen
1516
+ ]);
1498
1517
  (0, react.useEffect)(() => {
1499
1518
  if (!isOpen) return;
1500
1519
  function handleFocusOut(e) {
@@ -1505,7 +1524,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1505
1524
  const wrapper = wrapperRef.current;
1506
1525
  wrapper?.addEventListener("focusout", handleFocusOut);
1507
1526
  return () => wrapper?.removeEventListener("focusout", handleFocusOut);
1508
- }, [isOpen]);
1527
+ }, [isOpen, setIsOpen]);
1509
1528
  function handleKeyDown(e) {
1510
1529
  if (e.key === "Escape") {
1511
1530
  e.preventDefault();
@@ -1568,7 +1587,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1568
1587
  })]
1569
1588
  }), isOpen && !disabled && !readOnly && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1570
1589
  ref: popoverRef,
1571
- className: "pis-datepicker-popover",
1590
+ className: cn("pis-datepicker-popover", !positioned && "pis-datepicker-popover--measuring"),
1572
1591
  style: popupStyle,
1573
1592
  role: "dialog",
1574
1593
  "aria-label": ariaLabel ? `${ariaLabel} calendar` : "Date picker",
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, useMemo, useRef, useState } from "react";
4
+ import React, { cloneElement, createContext, createElement, isValidElement, use, useCallback, useContext, useEffect, useId, useLayoutEffect, 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";
@@ -1393,6 +1393,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1393
1393
  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
1394
1394
  const isOpen = isControlled ? open : uncontrolledOpen;
1395
1395
  const [popupStyle, setPopupStyle] = useState({});
1396
+ const [positioned, setPositioned] = useState(false);
1396
1397
  const wrapperRef = useRef(null);
1397
1398
  const popoverRef = useRef(null);
1398
1399
  const inputRef = useRef(null);
@@ -1432,6 +1433,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1432
1433
  if (!wrapperRef.current) return;
1433
1434
  const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
1434
1435
  setPopupStyle(style);
1436
+ setPositioned(false);
1435
1437
  setIsOpen(true);
1436
1438
  });
1437
1439
  }, 50);
@@ -1441,11 +1443,16 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1441
1443
  placement,
1442
1444
  setIsOpen
1443
1445
  ]);
1444
- useEffect(() => {
1445
- if (!isOpen || !popoverRef.current || !wrapperRef.current) return;
1446
+ useLayoutEffect(() => {
1447
+ if (!isOpen) {
1448
+ setPositioned(false);
1449
+ return;
1450
+ }
1451
+ if (!popoverRef.current || !wrapperRef.current) return;
1446
1452
  const measuredHeight = popoverRef.current.offsetHeight;
1447
1453
  const rect = wrapperRef.current.getBoundingClientRect();
1448
1454
  setPopupStyle(computePopupStyle(rect, placement, measuredHeight));
1455
+ setPositioned(true);
1449
1456
  }, [isOpen, placement]);
1450
1457
  useEffect(() => {
1451
1458
  function handleClickOutside(e) {
@@ -1454,23 +1461,35 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1454
1461
  }
1455
1462
  document.addEventListener("mousedown", handleClickOutside);
1456
1463
  return () => document.removeEventListener("mousedown", handleClickOutside);
1457
- }, []);
1464
+ }, [setIsOpen]);
1458
1465
  useEffect(() => {
1459
- if (!isOpen) return;
1466
+ if (!isOpen || !positioned) return;
1467
+ let armed = false;
1468
+ const armTimer = window.setTimeout(() => {
1469
+ armed = true;
1470
+ }, 150);
1460
1471
  const close = () => setIsOpen(false);
1472
+ const onScroll = () => {
1473
+ if (armed) setIsOpen(false);
1474
+ };
1461
1475
  const scrollParent = getScrollParent(wrapperRef.current?.parentElement ?? null);
1462
- if (scrollParent) scrollParent.addEventListener("scroll", close, { passive: true });
1463
- window.addEventListener("scroll", close, {
1476
+ if (scrollParent) scrollParent.addEventListener("scroll", onScroll, { passive: true });
1477
+ window.addEventListener("scroll", onScroll, {
1464
1478
  capture: true,
1465
1479
  passive: true
1466
1480
  });
1467
1481
  window.addEventListener("resize", close);
1468
1482
  return () => {
1469
- if (scrollParent) scrollParent.removeEventListener("scroll", close);
1470
- window.removeEventListener("scroll", close, { capture: true });
1483
+ window.clearTimeout(armTimer);
1484
+ if (scrollParent) scrollParent.removeEventListener("scroll", onScroll);
1485
+ window.removeEventListener("scroll", onScroll, { capture: true });
1471
1486
  window.removeEventListener("resize", close);
1472
1487
  };
1473
- }, [isOpen]);
1488
+ }, [
1489
+ isOpen,
1490
+ positioned,
1491
+ setIsOpen
1492
+ ]);
1474
1493
  useEffect(() => {
1475
1494
  if (!isOpen) return;
1476
1495
  function handleFocusOut(e) {
@@ -1481,7 +1500,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1481
1500
  const wrapper = wrapperRef.current;
1482
1501
  wrapper?.addEventListener("focusout", handleFocusOut);
1483
1502
  return () => wrapper?.removeEventListener("focusout", handleFocusOut);
1484
- }, [isOpen]);
1503
+ }, [isOpen, setIsOpen]);
1485
1504
  function handleKeyDown(e) {
1486
1505
  if (e.key === "Escape") {
1487
1506
  e.preventDefault();
@@ -1544,7 +1563,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
1544
1563
  })]
1545
1564
  }), isOpen && !disabled && !readOnly && createPortal(/* @__PURE__ */ jsx("div", {
1546
1565
  ref: popoverRef,
1547
- className: "pis-datepicker-popover",
1566
+ className: cn("pis-datepicker-popover", !positioned && "pis-datepicker-popover--measuring"),
1548
1567
  style: popupStyle,
1549
1568
  role: "dialog",
1550
1569
  "aria-label": ariaLabel ? `${ariaLabel} calendar` : "Date picker",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diwauhris/ui",
3
3
  "private": false,
4
- "version": "1.7.25",
4
+ "version": "1.7.27",
5
5
  "type": "module",
6
6
  "description": "DIWA UHRIS UI component library — React + Tailwind CSS",
7
7
  "keywords": [