@diwauhris/ui 1.7.24 → 1.7.26
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 +39 -13
- package/lib/index.mjs +40 -14
- package/lib/types/ui-library/gallery/date-picker/DatePicker.d.ts +22 -2
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -1398,7 +1398,8 @@ function getScrollParent(el) {
|
|
|
1398
1398
|
if (["auto", "scroll"].includes(overflowY) || ["auto", "scroll"].includes(overflow)) return el;
|
|
1399
1399
|
return getScrollParent(el.parentElement);
|
|
1400
1400
|
}
|
|
1401
|
-
function computePopupStyle(rect, placement = "auto") {
|
|
1401
|
+
function computePopupStyle(rect, placement = "auto", measuredHeight) {
|
|
1402
|
+
const height = measuredHeight ?? CALENDAR_HEIGHT;
|
|
1402
1403
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
1403
1404
|
const spaceAbove = rect.top;
|
|
1404
1405
|
const isNarrow = window.innerWidth < 368;
|
|
@@ -1407,23 +1408,30 @@ function computePopupStyle(rect, placement = "auto") {
|
|
|
1407
1408
|
left: isNarrow ? 8 : Math.max(8, Math.min(rect.left, window.innerWidth - POPOVER_WIDTH - 8)),
|
|
1408
1409
|
width: isNarrow ? `calc(100vw - 16px)` : void 0
|
|
1409
1410
|
};
|
|
1410
|
-
if (placement === "below" ? true : placement === "above" ? false : spaceBelow >=
|
|
1411
|
-
else style.top = Math.max(rect.top -
|
|
1411
|
+
if (placement === "below" ? true : placement === "above" ? false : spaceBelow >= height || spaceBelow >= spaceAbove) style.top = Math.min(rect.bottom + 6, window.innerHeight - height - 8);
|
|
1412
|
+
else style.top = Math.max(rect.top - height - 6, 8);
|
|
1412
1413
|
return style;
|
|
1413
1414
|
}
|
|
1414
|
-
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 }) {
|
|
1415
|
-
const
|
|
1415
|
+
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
|
+
const isControlled = open !== void 0;
|
|
1417
|
+
const [uncontrolledOpen, setUncontrolledOpen] = (0, react.useState)(defaultOpen);
|
|
1418
|
+
const isOpen = isControlled ? open : uncontrolledOpen;
|
|
1416
1419
|
const [popupStyle, setPopupStyle] = (0, react.useState)({});
|
|
1420
|
+
const [positioned, setPositioned] = (0, react.useState)(false);
|
|
1417
1421
|
const wrapperRef = (0, react.useRef)(null);
|
|
1418
1422
|
const popoverRef = (0, react.useRef)(null);
|
|
1419
1423
|
const inputRef = (0, react.useRef)(null);
|
|
1420
1424
|
const internalId = (0, react.useId)();
|
|
1421
1425
|
const setIsOpen = (0, react.useCallback)((next) => {
|
|
1422
|
-
|
|
1426
|
+
if (isControlled) {
|
|
1427
|
+
onOpenChange?.(next);
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
setUncontrolledOpen((prev) => {
|
|
1423
1431
|
if (prev !== next) onOpenChange?.(next);
|
|
1424
1432
|
return next;
|
|
1425
1433
|
});
|
|
1426
|
-
}, [onOpenChange]);
|
|
1434
|
+
}, [isControlled, onOpenChange]);
|
|
1427
1435
|
const ctx = useFieldContext();
|
|
1428
1436
|
const inputId = id ?? ctx?.inputId ?? internalId;
|
|
1429
1437
|
const resolvedRequired = required ?? ctx?.required ?? false;
|
|
@@ -1438,7 +1446,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1438
1446
|
const displayValue = selectedDate ? (0, date_fns.format)(selectedDate, dateFormat) : "";
|
|
1439
1447
|
const skipNextOpen = (0, react.useRef)(false);
|
|
1440
1448
|
const openPopup = (0, react.useCallback)(() => {
|
|
1441
|
-
if (disabled) return;
|
|
1449
|
+
if (disabled || readOnly) return;
|
|
1442
1450
|
if (skipNextOpen.current) {
|
|
1443
1451
|
skipNextOpen.current = false;
|
|
1444
1452
|
return;
|
|
@@ -1449,14 +1457,27 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1449
1457
|
if (!wrapperRef.current) return;
|
|
1450
1458
|
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
|
|
1451
1459
|
setPopupStyle(style);
|
|
1460
|
+
setPositioned(false);
|
|
1452
1461
|
setIsOpen(true);
|
|
1453
1462
|
});
|
|
1454
1463
|
}, 50);
|
|
1455
1464
|
}, [
|
|
1456
1465
|
disabled,
|
|
1466
|
+
readOnly,
|
|
1457
1467
|
placement,
|
|
1458
1468
|
setIsOpen
|
|
1459
1469
|
]);
|
|
1470
|
+
(0, react.useLayoutEffect)(() => {
|
|
1471
|
+
if (!isOpen) {
|
|
1472
|
+
setPositioned(false);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
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]);
|
|
1460
1481
|
(0, react.useEffect)(() => {
|
|
1461
1482
|
function handleClickOutside(e) {
|
|
1462
1483
|
const target = e.target;
|
|
@@ -1528,11 +1549,12 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1528
1549
|
"aria-required": resolvedRequired ? "true" : void 0,
|
|
1529
1550
|
"aria-invalid": resolvedInvalid ? "true" : void 0,
|
|
1530
1551
|
"aria-describedby": describedBy,
|
|
1531
|
-
"aria-
|
|
1532
|
-
"aria-
|
|
1552
|
+
"aria-readonly": readOnly ? "true" : void 0,
|
|
1553
|
+
"aria-haspopup": readOnly ? void 0 : "dialog",
|
|
1554
|
+
"aria-expanded": readOnly ? void 0 : isOpen,
|
|
1533
1555
|
role: "combobox",
|
|
1534
1556
|
className: inputClasses
|
|
1535
|
-
}), clearable && value && !disabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1557
|
+
}), clearable && value && !disabled && !readOnly ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1536
1558
|
type: "button",
|
|
1537
1559
|
"aria-label": "Clear date",
|
|
1538
1560
|
onMouseDown: (e) => {
|
|
@@ -1551,10 +1573,13 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1551
1573
|
className: `absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none ${isOpen ? "text-brand-blue" : "text-slate-300"}`,
|
|
1552
1574
|
"aria-hidden": "true"
|
|
1553
1575
|
})]
|
|
1554
|
-
}), isOpen && !disabled && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1576
|
+
}), isOpen && !disabled && !readOnly && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1555
1577
|
ref: popoverRef,
|
|
1556
1578
|
className: "pis-datepicker-popover",
|
|
1557
|
-
style:
|
|
1579
|
+
style: {
|
|
1580
|
+
...popupStyle,
|
|
1581
|
+
visibility: positioned ? void 0 : "hidden"
|
|
1582
|
+
},
|
|
1558
1583
|
role: "dialog",
|
|
1559
1584
|
"aria-label": ariaLabel ? `${ariaLabel} calendar` : "Date picker",
|
|
1560
1585
|
"aria-modal": "false",
|
|
@@ -1571,6 +1596,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1571
1596
|
defaultActiveStartDate: selectedDate ?? minDate,
|
|
1572
1597
|
minDate,
|
|
1573
1598
|
maxDate,
|
|
1599
|
+
tileDisabled: isDateDisabled ? ({ date, view }) => view === "month" && isDateDisabled(date) : void 0,
|
|
1574
1600
|
locale,
|
|
1575
1601
|
defaultView: "month",
|
|
1576
1602
|
showNeighboringDecade: true,
|
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";
|
|
@@ -1374,7 +1374,8 @@ function getScrollParent(el) {
|
|
|
1374
1374
|
if (["auto", "scroll"].includes(overflowY) || ["auto", "scroll"].includes(overflow)) return el;
|
|
1375
1375
|
return getScrollParent(el.parentElement);
|
|
1376
1376
|
}
|
|
1377
|
-
function computePopupStyle(rect, placement = "auto") {
|
|
1377
|
+
function computePopupStyle(rect, placement = "auto", measuredHeight) {
|
|
1378
|
+
const height = measuredHeight ?? CALENDAR_HEIGHT;
|
|
1378
1379
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
1379
1380
|
const spaceAbove = rect.top;
|
|
1380
1381
|
const isNarrow = window.innerWidth < 368;
|
|
@@ -1383,23 +1384,30 @@ function computePopupStyle(rect, placement = "auto") {
|
|
|
1383
1384
|
left: isNarrow ? 8 : Math.max(8, Math.min(rect.left, window.innerWidth - POPOVER_WIDTH - 8)),
|
|
1384
1385
|
width: isNarrow ? `calc(100vw - 16px)` : void 0
|
|
1385
1386
|
};
|
|
1386
|
-
if (placement === "below" ? true : placement === "above" ? false : spaceBelow >=
|
|
1387
|
-
else style.top = Math.max(rect.top -
|
|
1387
|
+
if (placement === "below" ? true : placement === "above" ? false : spaceBelow >= height || spaceBelow >= spaceAbove) style.top = Math.min(rect.bottom + 6, window.innerHeight - height - 8);
|
|
1388
|
+
else style.top = Math.max(rect.top - height - 6, 8);
|
|
1388
1389
|
return style;
|
|
1389
1390
|
}
|
|
1390
|
-
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 }) {
|
|
1391
|
-
const
|
|
1391
|
+
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
|
+
const isControlled = open !== void 0;
|
|
1393
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
1394
|
+
const isOpen = isControlled ? open : uncontrolledOpen;
|
|
1392
1395
|
const [popupStyle, setPopupStyle] = useState({});
|
|
1396
|
+
const [positioned, setPositioned] = useState(false);
|
|
1393
1397
|
const wrapperRef = useRef(null);
|
|
1394
1398
|
const popoverRef = useRef(null);
|
|
1395
1399
|
const inputRef = useRef(null);
|
|
1396
1400
|
const internalId = useId();
|
|
1397
1401
|
const setIsOpen = useCallback((next) => {
|
|
1398
|
-
|
|
1402
|
+
if (isControlled) {
|
|
1403
|
+
onOpenChange?.(next);
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
setUncontrolledOpen((prev) => {
|
|
1399
1407
|
if (prev !== next) onOpenChange?.(next);
|
|
1400
1408
|
return next;
|
|
1401
1409
|
});
|
|
1402
|
-
}, [onOpenChange]);
|
|
1410
|
+
}, [isControlled, onOpenChange]);
|
|
1403
1411
|
const ctx = useFieldContext();
|
|
1404
1412
|
const inputId = id ?? ctx?.inputId ?? internalId;
|
|
1405
1413
|
const resolvedRequired = required ?? ctx?.required ?? false;
|
|
@@ -1414,7 +1422,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1414
1422
|
const displayValue = selectedDate ? format(selectedDate, dateFormat) : "";
|
|
1415
1423
|
const skipNextOpen = useRef(false);
|
|
1416
1424
|
const openPopup = useCallback(() => {
|
|
1417
|
-
if (disabled) return;
|
|
1425
|
+
if (disabled || readOnly) return;
|
|
1418
1426
|
if (skipNextOpen.current) {
|
|
1419
1427
|
skipNextOpen.current = false;
|
|
1420
1428
|
return;
|
|
@@ -1425,14 +1433,27 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1425
1433
|
if (!wrapperRef.current) return;
|
|
1426
1434
|
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
|
|
1427
1435
|
setPopupStyle(style);
|
|
1436
|
+
setPositioned(false);
|
|
1428
1437
|
setIsOpen(true);
|
|
1429
1438
|
});
|
|
1430
1439
|
}, 50);
|
|
1431
1440
|
}, [
|
|
1432
1441
|
disabled,
|
|
1442
|
+
readOnly,
|
|
1433
1443
|
placement,
|
|
1434
1444
|
setIsOpen
|
|
1435
1445
|
]);
|
|
1446
|
+
useLayoutEffect(() => {
|
|
1447
|
+
if (!isOpen) {
|
|
1448
|
+
setPositioned(false);
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
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]);
|
|
1436
1457
|
useEffect(() => {
|
|
1437
1458
|
function handleClickOutside(e) {
|
|
1438
1459
|
const target = e.target;
|
|
@@ -1504,11 +1525,12 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1504
1525
|
"aria-required": resolvedRequired ? "true" : void 0,
|
|
1505
1526
|
"aria-invalid": resolvedInvalid ? "true" : void 0,
|
|
1506
1527
|
"aria-describedby": describedBy,
|
|
1507
|
-
"aria-
|
|
1508
|
-
"aria-
|
|
1528
|
+
"aria-readonly": readOnly ? "true" : void 0,
|
|
1529
|
+
"aria-haspopup": readOnly ? void 0 : "dialog",
|
|
1530
|
+
"aria-expanded": readOnly ? void 0 : isOpen,
|
|
1509
1531
|
role: "combobox",
|
|
1510
1532
|
className: inputClasses
|
|
1511
|
-
}), clearable && value && !disabled ? /* @__PURE__ */ jsx("button", {
|
|
1533
|
+
}), clearable && value && !disabled && !readOnly ? /* @__PURE__ */ jsx("button", {
|
|
1512
1534
|
type: "button",
|
|
1513
1535
|
"aria-label": "Clear date",
|
|
1514
1536
|
onMouseDown: (e) => {
|
|
@@ -1527,10 +1549,13 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1527
1549
|
className: `absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none ${isOpen ? "text-brand-blue" : "text-slate-300"}`,
|
|
1528
1550
|
"aria-hidden": "true"
|
|
1529
1551
|
})]
|
|
1530
|
-
}), isOpen && !disabled && createPortal(/* @__PURE__ */ jsx("div", {
|
|
1552
|
+
}), isOpen && !disabled && !readOnly && createPortal(/* @__PURE__ */ jsx("div", {
|
|
1531
1553
|
ref: popoverRef,
|
|
1532
1554
|
className: "pis-datepicker-popover",
|
|
1533
|
-
style:
|
|
1555
|
+
style: {
|
|
1556
|
+
...popupStyle,
|
|
1557
|
+
visibility: positioned ? void 0 : "hidden"
|
|
1558
|
+
},
|
|
1534
1559
|
role: "dialog",
|
|
1535
1560
|
"aria-label": ariaLabel ? `${ariaLabel} calendar` : "Date picker",
|
|
1536
1561
|
"aria-modal": "false",
|
|
@@ -1547,6 +1572,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1547
1572
|
defaultActiveStartDate: selectedDate ?? minDate,
|
|
1548
1573
|
minDate,
|
|
1549
1574
|
maxDate,
|
|
1575
|
+
tileDisabled: isDateDisabled ? ({ date, view }) => view === "month" && isDateDisabled(date) : void 0,
|
|
1550
1576
|
locale,
|
|
1551
1577
|
defaultView: "month",
|
|
1552
1578
|
showNeighboringDecade: true,
|
|
@@ -32,7 +32,27 @@ interface DatePickerProps {
|
|
|
32
32
|
locale?: string;
|
|
33
33
|
/** Name forwarded to the trigger input for form association. */
|
|
34
34
|
name?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Per-day predicate to disable individual dates that min/maxDate can't express
|
|
37
|
+
* (weekends, holidays, already-booked days). Return true to disable the date.
|
|
38
|
+
* Only consulted on the day (month) view.
|
|
39
|
+
*/
|
|
40
|
+
isDateDisabled?: (date: Date) => boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Read-only: the value is shown but the calendar can't be opened and no date
|
|
43
|
+
* can be picked. Unlike `disabled`, it keeps normal (non-greyed) styling for
|
|
44
|
+
* view/detail screens.
|
|
45
|
+
*/
|
|
46
|
+
readOnly?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Controlled open state. When provided, the component no longer manages its
|
|
49
|
+
* own open/close — the parent owns it via `onOpenChange`. Leave undefined for
|
|
50
|
+
* the default uncontrolled behavior.
|
|
51
|
+
*/
|
|
52
|
+
open?: boolean;
|
|
53
|
+
/** Initial open state when uncontrolled. Ignored when `open` is provided. */
|
|
54
|
+
defaultOpen?: boolean;
|
|
35
55
|
}
|
|
36
|
-
export declare function computePopupStyle(rect: DOMRect, placement?: 'auto' | 'below' | 'above'): React.CSSProperties;
|
|
37
|
-
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, }: DatePickerProps): import("react").JSX.Element;
|
|
56
|
+
export declare function computePopupStyle(rect: DOMRect, placement?: 'auto' | 'below' | 'above', measuredHeight?: number): React.CSSProperties;
|
|
57
|
+
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;
|
|
38
58
|
export {};
|