@diwauhris/ui 1.7.23 → 1.7.24
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
package/lib/index.cjs
CHANGED
|
@@ -1398,7 +1398,7 @@ 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") {
|
|
1402
1402
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
1403
1403
|
const spaceAbove = rect.top;
|
|
1404
1404
|
const isNarrow = window.innerWidth < 368;
|
|
@@ -1407,17 +1407,23 @@ function computePopupStyle(rect) {
|
|
|
1407
1407
|
left: isNarrow ? 8 : Math.max(8, Math.min(rect.left, window.innerWidth - POPOVER_WIDTH - 8)),
|
|
1408
1408
|
width: isNarrow ? `calc(100vw - 16px)` : void 0
|
|
1409
1409
|
};
|
|
1410
|
-
if (
|
|
1411
|
-
else style.top = rect.
|
|
1410
|
+
if (placement === "below" ? true : placement === "above" ? false : spaceBelow >= CALENDAR_HEIGHT || spaceBelow >= spaceAbove) style.top = Math.min(rect.bottom + 6, window.innerHeight - CALENDAR_HEIGHT - 8);
|
|
1411
|
+
else style.top = Math.max(rect.top - CALENDAR_HEIGHT - 6, 8);
|
|
1412
1412
|
return style;
|
|
1413
1413
|
}
|
|
1414
|
-
function DatePicker({ value, onChange, placeholder, className, disabled, error, minDate, maxDate, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, required }) {
|
|
1415
|
-
const [isOpen,
|
|
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 [isOpen, setIsOpenState] = (0, react.useState)(false);
|
|
1416
1416
|
const [popupStyle, setPopupStyle] = (0, react.useState)({});
|
|
1417
1417
|
const wrapperRef = (0, react.useRef)(null);
|
|
1418
1418
|
const popoverRef = (0, react.useRef)(null);
|
|
1419
1419
|
const inputRef = (0, react.useRef)(null);
|
|
1420
1420
|
const internalId = (0, react.useId)();
|
|
1421
|
+
const setIsOpen = (0, react.useCallback)((next) => {
|
|
1422
|
+
setIsOpenState((prev) => {
|
|
1423
|
+
if (prev !== next) onOpenChange?.(next);
|
|
1424
|
+
return next;
|
|
1425
|
+
});
|
|
1426
|
+
}, [onOpenChange]);
|
|
1421
1427
|
const ctx = useFieldContext();
|
|
1422
1428
|
const inputId = id ?? ctx?.inputId ?? internalId;
|
|
1423
1429
|
const resolvedRequired = required ?? ctx?.required ?? false;
|
|
@@ -1429,7 +1435,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1429
1435
|
const d2 = new Date(value);
|
|
1430
1436
|
return isNaN(d2.getTime()) ? void 0 : d2;
|
|
1431
1437
|
})() : void 0;
|
|
1432
|
-
const displayValue = selectedDate ? (0, date_fns.format)(selectedDate,
|
|
1438
|
+
const displayValue = selectedDate ? (0, date_fns.format)(selectedDate, dateFormat) : "";
|
|
1433
1439
|
const skipNextOpen = (0, react.useRef)(false);
|
|
1434
1440
|
const openPopup = (0, react.useCallback)(() => {
|
|
1435
1441
|
if (disabled) return;
|
|
@@ -1441,12 +1447,16 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1441
1447
|
if (!wrapperRef.current) return;
|
|
1442
1448
|
requestAnimationFrame(() => {
|
|
1443
1449
|
if (!wrapperRef.current) return;
|
|
1444
|
-
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect());
|
|
1450
|
+
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
|
|
1445
1451
|
setPopupStyle(style);
|
|
1446
1452
|
setIsOpen(true);
|
|
1447
1453
|
});
|
|
1448
1454
|
}, 50);
|
|
1449
|
-
}, [
|
|
1455
|
+
}, [
|
|
1456
|
+
disabled,
|
|
1457
|
+
placement,
|
|
1458
|
+
setIsOpen
|
|
1459
|
+
]);
|
|
1450
1460
|
(0, react.useEffect)(() => {
|
|
1451
1461
|
function handleClickOutside(e) {
|
|
1452
1462
|
const target = e.target;
|
|
@@ -1504,6 +1514,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1504
1514
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1505
1515
|
ref: inputRef,
|
|
1506
1516
|
id: inputId,
|
|
1517
|
+
name,
|
|
1507
1518
|
type: "text",
|
|
1508
1519
|
readOnly: true,
|
|
1509
1520
|
value: displayValue,
|
|
@@ -1521,7 +1532,21 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1521
1532
|
"aria-expanded": isOpen,
|
|
1522
1533
|
role: "combobox",
|
|
1523
1534
|
className: inputClasses
|
|
1524
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1535
|
+
}), clearable && value && !disabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1536
|
+
type: "button",
|
|
1537
|
+
"aria-label": "Clear date",
|
|
1538
|
+
onMouseDown: (e) => {
|
|
1539
|
+
e.preventDefault();
|
|
1540
|
+
e.stopPropagation();
|
|
1541
|
+
},
|
|
1542
|
+
onClick: () => {
|
|
1543
|
+
onChange("");
|
|
1544
|
+
setIsOpen(false);
|
|
1545
|
+
inputRef.current?.focus();
|
|
1546
|
+
},
|
|
1547
|
+
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",
|
|
1548
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { size: 14 })
|
|
1549
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CalendarDays, {
|
|
1525
1550
|
size: 16,
|
|
1526
1551
|
className: `absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none ${isOpen ? "text-brand-blue" : "text-slate-300"}`,
|
|
1527
1552
|
"aria-hidden": "true"
|
|
@@ -1546,6 +1571,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1546
1571
|
defaultActiveStartDate: selectedDate ?? minDate,
|
|
1547
1572
|
minDate,
|
|
1548
1573
|
maxDate,
|
|
1574
|
+
locale,
|
|
1549
1575
|
defaultView: "month",
|
|
1550
1576
|
showNeighboringDecade: true,
|
|
1551
1577
|
showNeighboringCentury: true,
|
package/lib/index.mjs
CHANGED
|
@@ -1374,7 +1374,7 @@ 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") {
|
|
1378
1378
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
1379
1379
|
const spaceAbove = rect.top;
|
|
1380
1380
|
const isNarrow = window.innerWidth < 368;
|
|
@@ -1383,17 +1383,23 @@ function computePopupStyle(rect) {
|
|
|
1383
1383
|
left: isNarrow ? 8 : Math.max(8, Math.min(rect.left, window.innerWidth - POPOVER_WIDTH - 8)),
|
|
1384
1384
|
width: isNarrow ? `calc(100vw - 16px)` : void 0
|
|
1385
1385
|
};
|
|
1386
|
-
if (
|
|
1387
|
-
else style.top = rect.
|
|
1386
|
+
if (placement === "below" ? true : placement === "above" ? false : spaceBelow >= CALENDAR_HEIGHT || spaceBelow >= spaceAbove) style.top = Math.min(rect.bottom + 6, window.innerHeight - CALENDAR_HEIGHT - 8);
|
|
1387
|
+
else style.top = Math.max(rect.top - CALENDAR_HEIGHT - 6, 8);
|
|
1388
1388
|
return style;
|
|
1389
1389
|
}
|
|
1390
|
-
function DatePicker({ value, onChange, placeholder, className, disabled, error, minDate, maxDate, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, required }) {
|
|
1391
|
-
const [isOpen,
|
|
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 [isOpen, setIsOpenState] = useState(false);
|
|
1392
1392
|
const [popupStyle, setPopupStyle] = useState({});
|
|
1393
1393
|
const wrapperRef = useRef(null);
|
|
1394
1394
|
const popoverRef = useRef(null);
|
|
1395
1395
|
const inputRef = useRef(null);
|
|
1396
1396
|
const internalId = useId();
|
|
1397
|
+
const setIsOpen = useCallback((next) => {
|
|
1398
|
+
setIsOpenState((prev) => {
|
|
1399
|
+
if (prev !== next) onOpenChange?.(next);
|
|
1400
|
+
return next;
|
|
1401
|
+
});
|
|
1402
|
+
}, [onOpenChange]);
|
|
1397
1403
|
const ctx = useFieldContext();
|
|
1398
1404
|
const inputId = id ?? ctx?.inputId ?? internalId;
|
|
1399
1405
|
const resolvedRequired = required ?? ctx?.required ?? false;
|
|
@@ -1405,7 +1411,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1405
1411
|
const d2 = new Date(value);
|
|
1406
1412
|
return isNaN(d2.getTime()) ? void 0 : d2;
|
|
1407
1413
|
})() : void 0;
|
|
1408
|
-
const displayValue = selectedDate ? format(selectedDate,
|
|
1414
|
+
const displayValue = selectedDate ? format(selectedDate, dateFormat) : "";
|
|
1409
1415
|
const skipNextOpen = useRef(false);
|
|
1410
1416
|
const openPopup = useCallback(() => {
|
|
1411
1417
|
if (disabled) return;
|
|
@@ -1417,12 +1423,16 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1417
1423
|
if (!wrapperRef.current) return;
|
|
1418
1424
|
requestAnimationFrame(() => {
|
|
1419
1425
|
if (!wrapperRef.current) return;
|
|
1420
|
-
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect());
|
|
1426
|
+
const style = computePopupStyle(wrapperRef.current.getBoundingClientRect(), placement);
|
|
1421
1427
|
setPopupStyle(style);
|
|
1422
1428
|
setIsOpen(true);
|
|
1423
1429
|
});
|
|
1424
1430
|
}, 50);
|
|
1425
|
-
}, [
|
|
1431
|
+
}, [
|
|
1432
|
+
disabled,
|
|
1433
|
+
placement,
|
|
1434
|
+
setIsOpen
|
|
1435
|
+
]);
|
|
1426
1436
|
useEffect(() => {
|
|
1427
1437
|
function handleClickOutside(e) {
|
|
1428
1438
|
const target = e.target;
|
|
@@ -1480,6 +1490,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1480
1490
|
children: [/* @__PURE__ */ jsx("input", {
|
|
1481
1491
|
ref: inputRef,
|
|
1482
1492
|
id: inputId,
|
|
1493
|
+
name,
|
|
1483
1494
|
type: "text",
|
|
1484
1495
|
readOnly: true,
|
|
1485
1496
|
value: displayValue,
|
|
@@ -1497,7 +1508,21 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1497
1508
|
"aria-expanded": isOpen,
|
|
1498
1509
|
role: "combobox",
|
|
1499
1510
|
className: inputClasses
|
|
1500
|
-
}), /* @__PURE__ */ jsx(
|
|
1511
|
+
}), clearable && value && !disabled ? /* @__PURE__ */ jsx("button", {
|
|
1512
|
+
type: "button",
|
|
1513
|
+
"aria-label": "Clear date",
|
|
1514
|
+
onMouseDown: (e) => {
|
|
1515
|
+
e.preventDefault();
|
|
1516
|
+
e.stopPropagation();
|
|
1517
|
+
},
|
|
1518
|
+
onClick: () => {
|
|
1519
|
+
onChange("");
|
|
1520
|
+
setIsOpen(false);
|
|
1521
|
+
inputRef.current?.focus();
|
|
1522
|
+
},
|
|
1523
|
+
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",
|
|
1524
|
+
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
1525
|
+
}) : /* @__PURE__ */ jsx(CalendarDays, {
|
|
1501
1526
|
size: 16,
|
|
1502
1527
|
className: `absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none ${isOpen ? "text-brand-blue" : "text-slate-300"}`,
|
|
1503
1528
|
"aria-hidden": "true"
|
|
@@ -1522,6 +1547,7 @@ function DatePicker({ value, onChange, placeholder, className, disabled, error,
|
|
|
1522
1547
|
defaultActiveStartDate: selectedDate ?? minDate,
|
|
1523
1548
|
minDate,
|
|
1524
1549
|
maxDate,
|
|
1550
|
+
locale,
|
|
1525
1551
|
defaultView: "month",
|
|
1526
1552
|
showNeighboringDecade: true,
|
|
1527
1553
|
showNeighboringCentury: true,
|
|
@@ -12,6 +12,27 @@ 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;
|
|
15
35
|
}
|
|
16
|
-
export declare function
|
|
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;
|
|
17
38
|
export {};
|