@diwauhris/ui 1.7.23 → 1.7.25
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 +4 -0
- package/lib/index.cjs +55 -13
- package/lib/index.mjs +55 -13
- package/lib/types/ui-library/gallery/date-picker/DatePicker.d.ts +42 -1
- package/package.json +1 -1
package/lib/assets/ui.css
CHANGED
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) {
|
|
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,17 +1408,29 @@ function computePopupStyle(rect) {
|
|
|
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 (
|
|
1411
|
-
else style.top = rect.
|
|
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 }) {
|
|
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)({});
|
|
1417
1420
|
const wrapperRef = (0, react.useRef)(null);
|
|
1418
1421
|
const popoverRef = (0, react.useRef)(null);
|
|
1419
1422
|
const inputRef = (0, react.useRef)(null);
|
|
1420
1423
|
const internalId = (0, react.useId)();
|
|
1424
|
+
const setIsOpen = (0, react.useCallback)((next) => {
|
|
1425
|
+
if (isControlled) {
|
|
1426
|
+
onOpenChange?.(next);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
setUncontrolledOpen((prev) => {
|
|
1430
|
+
if (prev !== next) onOpenChange?.(next);
|
|
1431
|
+
return next;
|
|
1432
|
+
});
|
|
1433
|
+
}, [isControlled, onOpenChange]);
|
|
1421
1434
|
const ctx = useFieldContext();
|
|
1422
1435
|
const inputId = id ?? ctx?.inputId ?? internalId;
|
|
1423
1436
|
const resolvedRequired = required ?? ctx?.required ?? false;
|
|
@@ -1429,10 +1442,10 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1429
1442
|
const d2 = new Date(value);
|
|
1430
1443
|
return isNaN(d2.getTime()) ? void 0 : d2;
|
|
1431
1444
|
})() : void 0;
|
|
1432
|
-
const displayValue = selectedDate ? (0, date_fns.format)(selectedDate,
|
|
1445
|
+
const displayValue = selectedDate ? (0, date_fns.format)(selectedDate, dateFormat) : "";
|
|
1433
1446
|
const skipNextOpen = (0, react.useRef)(false);
|
|
1434
1447
|
const openPopup = (0, react.useCallback)(() => {
|
|
1435
|
-
if (disabled) return;
|
|
1448
|
+
if (disabled || readOnly) return;
|
|
1436
1449
|
if (skipNextOpen.current) {
|
|
1437
1450
|
skipNextOpen.current = false;
|
|
1438
1451
|
return;
|
|
@@ -1441,12 +1454,23 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1441
1454
|
if (!wrapperRef.current) return;
|
|
1442
1455
|
requestAnimationFrame(() => {
|
|
1443
1456
|
if (!wrapperRef.current) return;
|
|
1444
|
-
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect());
|
|
1457
|
+
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
|
|
1445
1458
|
setPopupStyle(style);
|
|
1446
1459
|
setIsOpen(true);
|
|
1447
1460
|
});
|
|
1448
1461
|
}, 50);
|
|
1449
|
-
}, [
|
|
1462
|
+
}, [
|
|
1463
|
+
disabled,
|
|
1464
|
+
readOnly,
|
|
1465
|
+
placement,
|
|
1466
|
+
setIsOpen
|
|
1467
|
+
]);
|
|
1468
|
+
(0, react.useEffect)(() => {
|
|
1469
|
+
if (!isOpen || !popoverRef.current || !wrapperRef.current) return;
|
|
1470
|
+
const measuredHeight = popoverRef.current.offsetHeight;
|
|
1471
|
+
const rect = wrapperRef.current.getBoundingClientRect();
|
|
1472
|
+
setPopupStyle(computePopupStyle(rect, placement, measuredHeight));
|
|
1473
|
+
}, [isOpen, placement]);
|
|
1450
1474
|
(0, react.useEffect)(() => {
|
|
1451
1475
|
function handleClickOutside(e) {
|
|
1452
1476
|
const target = e.target;
|
|
@@ -1504,6 +1528,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1504
1528
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1505
1529
|
ref: inputRef,
|
|
1506
1530
|
id: inputId,
|
|
1531
|
+
name,
|
|
1507
1532
|
type: "text",
|
|
1508
1533
|
readOnly: true,
|
|
1509
1534
|
value: displayValue,
|
|
@@ -1517,16 +1542,31 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1517
1542
|
"aria-required": resolvedRequired ? "true" : void 0,
|
|
1518
1543
|
"aria-invalid": resolvedInvalid ? "true" : void 0,
|
|
1519
1544
|
"aria-describedby": describedBy,
|
|
1520
|
-
"aria-
|
|
1521
|
-
"aria-
|
|
1545
|
+
"aria-readonly": readOnly ? "true" : void 0,
|
|
1546
|
+
"aria-haspopup": readOnly ? void 0 : "dialog",
|
|
1547
|
+
"aria-expanded": readOnly ? void 0 : isOpen,
|
|
1522
1548
|
role: "combobox",
|
|
1523
1549
|
className: inputClasses
|
|
1524
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1550
|
+
}), clearable && value && !disabled && !readOnly ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1551
|
+
type: "button",
|
|
1552
|
+
"aria-label": "Clear date",
|
|
1553
|
+
onMouseDown: (e) => {
|
|
1554
|
+
e.preventDefault();
|
|
1555
|
+
e.stopPropagation();
|
|
1556
|
+
},
|
|
1557
|
+
onClick: () => {
|
|
1558
|
+
onChange("");
|
|
1559
|
+
setIsOpen(false);
|
|
1560
|
+
inputRef.current?.focus();
|
|
1561
|
+
},
|
|
1562
|
+
className: "absolute right-2.5 top-1/2 -translate-y-1/2 flex h-5 w-5 items-center justify-center rounded-full text-slate-300 transition hover:bg-slate-100 hover:text-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
1563
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { size: 14 })
|
|
1564
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CalendarDays, {
|
|
1525
1565
|
size: 16,
|
|
1526
1566
|
className: `absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none ${isOpen ? "text-brand-blue" : "text-slate-300"}`,
|
|
1527
1567
|
"aria-hidden": "true"
|
|
1528
1568
|
})]
|
|
1529
|
-
}), isOpen && !disabled && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1569
|
+
}), isOpen && !disabled && !readOnly && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1530
1570
|
ref: popoverRef,
|
|
1531
1571
|
className: "pis-datepicker-popover",
|
|
1532
1572
|
style: popupStyle,
|
|
@@ -1546,6 +1586,8 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1546
1586
|
defaultActiveStartDate: selectedDate ?? minDate,
|
|
1547
1587
|
minDate,
|
|
1548
1588
|
maxDate,
|
|
1589
|
+
tileDisabled: isDateDisabled ? ({ date, view }) => view === "month" && isDateDisabled(date) : void 0,
|
|
1590
|
+
locale,
|
|
1549
1591
|
defaultView: "month",
|
|
1550
1592
|
showNeighboringDecade: true,
|
|
1551
1593
|
showNeighboringCentury: true,
|
package/lib/index.mjs
CHANGED
|
@@ -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) {
|
|
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,17 +1384,29 @@ function computePopupStyle(rect) {
|
|
|
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 (
|
|
1387
|
-
else style.top = rect.
|
|
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 }) {
|
|
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({});
|
|
1393
1396
|
const wrapperRef = useRef(null);
|
|
1394
1397
|
const popoverRef = useRef(null);
|
|
1395
1398
|
const inputRef = useRef(null);
|
|
1396
1399
|
const internalId = useId();
|
|
1400
|
+
const setIsOpen = useCallback((next) => {
|
|
1401
|
+
if (isControlled) {
|
|
1402
|
+
onOpenChange?.(next);
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
setUncontrolledOpen((prev) => {
|
|
1406
|
+
if (prev !== next) onOpenChange?.(next);
|
|
1407
|
+
return next;
|
|
1408
|
+
});
|
|
1409
|
+
}, [isControlled, onOpenChange]);
|
|
1397
1410
|
const ctx = useFieldContext();
|
|
1398
1411
|
const inputId = id ?? ctx?.inputId ?? internalId;
|
|
1399
1412
|
const resolvedRequired = required ?? ctx?.required ?? false;
|
|
@@ -1405,10 +1418,10 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1405
1418
|
const d2 = new Date(value);
|
|
1406
1419
|
return isNaN(d2.getTime()) ? void 0 : d2;
|
|
1407
1420
|
})() : void 0;
|
|
1408
|
-
const displayValue = selectedDate ? format(selectedDate,
|
|
1421
|
+
const displayValue = selectedDate ? format(selectedDate, dateFormat) : "";
|
|
1409
1422
|
const skipNextOpen = useRef(false);
|
|
1410
1423
|
const openPopup = useCallback(() => {
|
|
1411
|
-
if (disabled) return;
|
|
1424
|
+
if (disabled || readOnly) return;
|
|
1412
1425
|
if (skipNextOpen.current) {
|
|
1413
1426
|
skipNextOpen.current = false;
|
|
1414
1427
|
return;
|
|
@@ -1417,12 +1430,23 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1417
1430
|
if (!wrapperRef.current) return;
|
|
1418
1431
|
requestAnimationFrame(() => {
|
|
1419
1432
|
if (!wrapperRef.current) return;
|
|
1420
|
-
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect());
|
|
1433
|
+
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
|
|
1421
1434
|
setPopupStyle(style);
|
|
1422
1435
|
setIsOpen(true);
|
|
1423
1436
|
});
|
|
1424
1437
|
}, 50);
|
|
1425
|
-
}, [
|
|
1438
|
+
}, [
|
|
1439
|
+
disabled,
|
|
1440
|
+
readOnly,
|
|
1441
|
+
placement,
|
|
1442
|
+
setIsOpen
|
|
1443
|
+
]);
|
|
1444
|
+
useEffect(() => {
|
|
1445
|
+
if (!isOpen || !popoverRef.current || !wrapperRef.current) return;
|
|
1446
|
+
const measuredHeight = popoverRef.current.offsetHeight;
|
|
1447
|
+
const rect = wrapperRef.current.getBoundingClientRect();
|
|
1448
|
+
setPopupStyle(computePopupStyle(rect, placement, measuredHeight));
|
|
1449
|
+
}, [isOpen, placement]);
|
|
1426
1450
|
useEffect(() => {
|
|
1427
1451
|
function handleClickOutside(e) {
|
|
1428
1452
|
const target = e.target;
|
|
@@ -1480,6 +1504,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1480
1504
|
children: [/* @__PURE__ */ jsx("input", {
|
|
1481
1505
|
ref: inputRef,
|
|
1482
1506
|
id: inputId,
|
|
1507
|
+
name,
|
|
1483
1508
|
type: "text",
|
|
1484
1509
|
readOnly: true,
|
|
1485
1510
|
value: displayValue,
|
|
@@ -1493,16 +1518,31 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1493
1518
|
"aria-required": resolvedRequired ? "true" : void 0,
|
|
1494
1519
|
"aria-invalid": resolvedInvalid ? "true" : void 0,
|
|
1495
1520
|
"aria-describedby": describedBy,
|
|
1496
|
-
"aria-
|
|
1497
|
-
"aria-
|
|
1521
|
+
"aria-readonly": readOnly ? "true" : void 0,
|
|
1522
|
+
"aria-haspopup": readOnly ? void 0 : "dialog",
|
|
1523
|
+
"aria-expanded": readOnly ? void 0 : isOpen,
|
|
1498
1524
|
role: "combobox",
|
|
1499
1525
|
className: inputClasses
|
|
1500
|
-
}), /* @__PURE__ */ jsx(
|
|
1526
|
+
}), clearable && value && !disabled && !readOnly ? /* @__PURE__ */ jsx("button", {
|
|
1527
|
+
type: "button",
|
|
1528
|
+
"aria-label": "Clear date",
|
|
1529
|
+
onMouseDown: (e) => {
|
|
1530
|
+
e.preventDefault();
|
|
1531
|
+
e.stopPropagation();
|
|
1532
|
+
},
|
|
1533
|
+
onClick: () => {
|
|
1534
|
+
onChange("");
|
|
1535
|
+
setIsOpen(false);
|
|
1536
|
+
inputRef.current?.focus();
|
|
1537
|
+
},
|
|
1538
|
+
className: "absolute right-2.5 top-1/2 -translate-y-1/2 flex h-5 w-5 items-center justify-center rounded-full text-slate-300 transition hover:bg-slate-100 hover:text-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
1539
|
+
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
1540
|
+
}) : /* @__PURE__ */ jsx(CalendarDays, {
|
|
1501
1541
|
size: 16,
|
|
1502
1542
|
className: `absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none ${isOpen ? "text-brand-blue" : "text-slate-300"}`,
|
|
1503
1543
|
"aria-hidden": "true"
|
|
1504
1544
|
})]
|
|
1505
|
-
}), isOpen && !disabled && createPortal(/* @__PURE__ */ jsx("div", {
|
|
1545
|
+
}), isOpen && !disabled && !readOnly && createPortal(/* @__PURE__ */ jsx("div", {
|
|
1506
1546
|
ref: popoverRef,
|
|
1507
1547
|
className: "pis-datepicker-popover",
|
|
1508
1548
|
style: popupStyle,
|
|
@@ -1522,6 +1562,8 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1522
1562
|
defaultActiveStartDate: selectedDate ?? minDate,
|
|
1523
1563
|
minDate,
|
|
1524
1564
|
maxDate,
|
|
1565
|
+
tileDisabled: isDateDisabled ? ({ date, view }) => view === "month" && isDateDisabled(date) : void 0,
|
|
1566
|
+
locale,
|
|
1525
1567
|
defaultView: "month",
|
|
1526
1568
|
showNeighboringDecade: true,
|
|
1527
1569
|
showNeighboringCentury: true,
|
|
@@ -12,6 +12,47 @@ interface DatePickerProps {
|
|
|
12
12
|
'aria-label'?: string;
|
|
13
13
|
'aria-labelledby'?: string;
|
|
14
14
|
required?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* date-fns format string for the text shown in the trigger.
|
|
17
|
+
* The committed `onChange` value is always ISO `yyyy-MM-dd` regardless of this.
|
|
18
|
+
* Default: 'MMM d, yyyy'.
|
|
19
|
+
*/
|
|
20
|
+
dateFormat?: string;
|
|
21
|
+
/** Show a clear (×) button that resets the value to '' when a date is set. */
|
|
22
|
+
clearable?: boolean;
|
|
23
|
+
/** Notified whenever the calendar popover opens or closes. */
|
|
24
|
+
onOpenChange?: (open: boolean) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Popover placement relative to the trigger.
|
|
27
|
+
* 'auto' (default) flips above/below based on viewport space;
|
|
28
|
+
* 'below'/'above' force a side (still clamped to stay on-screen).
|
|
29
|
+
*/
|
|
30
|
+
placement?: 'auto' | 'below' | 'above';
|
|
31
|
+
/** BCP-47 locale forwarded to the calendar (e.g. 'en-US', 'fr-FR'). */
|
|
32
|
+
locale?: string;
|
|
33
|
+
/** Name forwarded to the trigger input for form association. */
|
|
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;
|
|
15
55
|
}
|
|
16
|
-
export declare function
|
|
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;
|
|
17
58
|
export {};
|