@hitachivantara/uikit-react-core
Version:
Core React components for the NEXT Design System.
345 lines (344 loc) • 11.7 kB
JavaScript
import { jsxs, jsx } from "react/jsx-runtime";
import { forwardRef, useRef, useEffect } from "react";
import { useForkRef } from "@mui/material/utils";
import { useDefaultProps } from "@hitachivantara/uikit-react-utils";
import { DEFAULT_LOCALE, isDate } from "../Calendar/utils.js";
import { HvLabelContainer } from "../FormElement/LabelContainer.js";
import { useControlled } from "../hooks/useControlled.js";
import { useLabels } from "../hooks/useLabels.js";
import { useUniqueId } from "../hooks/useUniqueId.js";
import { HvIcon } from "../icons.js";
import { setId } from "../utils/setId.js";
import { useSavedState } from "../utils/useSavedState.js";
import { useClasses } from "./DatePicker.styles.js";
import { staticClasses } from "./DatePicker.styles.js";
import useVisibleDate from "./useVisibleDate.js";
import { getDateLabel } from "./utils.js";
import { HvCalendar } from "../Calendar/Calendar.js";
import { HvButton } from "../Button/Button.js";
import { HvFormElement } from "../FormElement/FormElement.js";
import { HvBaseDropdown } from "../BaseDropdown/BaseDropdown.js";
import { isInvalid } from "../FormElement/utils.js";
import { HvWarningText } from "../FormElement/WarningText/WarningText.js";
import { HvActionBar } from "../ActionBar/ActionBar.js";
const DEFAULT_LABELS = {
/** Apply button label. */
applyLabel: "Apply",
/** Cancel button label. */
cancelLabel: "Cancel",
/** Clear button label. */
clearLabel: "Clear",
/** Invalid Date label. */
invalidDateLabel: "Invalid date"
};
const HvDatePicker = forwardRef(
function HvDatePicker2(props, ref) {
const {
classes: classesProp,
className,
id,
name,
required,
disabled,
readOnly,
label,
"aria-label": ariaLabel,
"aria-labelledby": ariaLabelledBy,
description,
"aria-describedby": ariaDescribedBy,
onChange,
onCancel,
onClear,
status,
statusMessage,
"aria-errormessage": ariaErrorMessage,
placeholder,
labels: labelsProp,
value,
startValue,
endValue,
expanded,
defaultExpanded,
onToggle,
rangeMode = false,
startAdornment,
horizontalPlacement = "right",
locale = DEFAULT_LOCALE,
showActions,
showClear,
disablePortal = true,
escapeWithReference = true,
dropdownProps = {},
calendarProps,
...others
} = useDefaultProps("HvDatePicker", props);
const { classes, cx } = useClasses(classesProp);
const labels = useLabels(DEFAULT_LABELS, labelsProp);
const elementId = useUniqueId(id);
const [validationState, setValidationState] = useControlled(
status,
"standBy"
);
const [validationMessage] = useControlled(statusMessage, "Required");
const [calendarOpen, setCalendarOpen] = useControlled(
expanded,
Boolean(defaultExpanded)
);
const [startDate, setStartDate, rollbackStartDate] = useSavedState(
rangeMode ? startValue : value
);
const [endDate, setEndDate, rollbackEndDate] = useSavedState(endValue);
const [visibleDate, dispatchAction] = useVisibleDate(startDate, endDate);
const focusTarget = useRef(null);
const { ref: refProp, ...otherDropdownProps } = dropdownProps;
const dropdownForkedRef = useForkRef(ref, refProp);
useEffect(() => {
setStartDate(rangeMode ? startValue : value, true);
setEndDate(endValue, true);
}, [value, startValue, endValue, rangeMode, setStartDate, setEndDate]);
const endDateIsSet = useRef(false);
endDateIsSet.current = endDate != null;
useEffect(() => {
if (startDate != null) {
dispatchAction({
type: "month_year",
target: endDateIsSet.current ? "left" : "best",
year: startDate.getFullYear(),
month: startDate.getMonth() + 1
});
}
}, [dispatchAction, startDate]);
useEffect(() => {
if (endDate != null) {
dispatchAction({
type: "month_year",
target: "right",
year: endDate.getFullYear(),
month: endDate.getMonth() + 1
});
}
}, [dispatchAction, endDate]);
const handleApply = () => {
setStartDate(startDate, true);
setEndDate(endDate ?? startDate, true);
onChange?.(startDate, endDate);
setValidationState(() => {
if (required && (!isDate(startDate) || rangeMode && !isDate(endDate))) {
return "invalid";
}
return "valid";
});
setCalendarOpen(false);
};
const handleCancel = () => {
rollbackStartDate();
rollbackEndDate();
onCancel?.();
setCalendarOpen(false);
};
const handleClear = () => {
setStartDate(void 0, false);
setEndDate(void 0, false);
onClear?.();
};
const handleCalendarClose = () => {
if (rangeMode || showActions) {
handleCancel();
}
};
const handleToggle = (evt, open) => {
if (evt === null) return;
onToggle?.(evt, open);
setCalendarOpen(open);
if (!open) handleCalendarClose();
};
const focusOnContainer = () => {
focusTarget.current?.focus();
};
const handleDateChange = (event, newDate) => {
if (!isDate(newDate)) return;
const autoSave = !showActions && !rangeMode;
if (rangeMode) {
if (!startDate || startDate && endDate || newDate < startDate) {
setStartDate(newDate);
setEndDate(void 0);
} else {
setEndDate(newDate);
}
} else {
setStartDate(newDate, autoSave);
}
if (autoSave) {
onChange?.(newDate);
setValidationState(() => {
if (required && !isDate(newDate)) {
return "invalid";
}
return "valid";
});
setCalendarOpen(false);
}
};
const handleInputDateChange = (event, newDate, position) => {
if (!isDate(newDate)) return;
if (!rangeMode) {
handleDateChange(event, newDate);
return;
}
if (position === "left") {
if (endDate) setStartDate(newDate > endDate ? endDate : newDate);
} else if (position === "right") {
if (!startDate) {
if (endDate) setStartDate(newDate > endDate ? endDate : newDate);
return;
}
setEndDate(newDate < startDate ? startDate : newDate);
}
};
const renderActions = () => /* @__PURE__ */ jsxs(HvActionBar, { className: cx({ [classes.actionContainer]: showClear }), children: [
showClear && /* @__PURE__ */ jsx("div", { className: classes.leftContainer, children: /* @__PURE__ */ jsx(
HvButton,
{
id: setId(id, "action", "clear"),
className: classes.action,
variant: "primaryGhost",
onClick: handleClear,
children: labels?.clearLabel
}
) }),
/* @__PURE__ */ jsxs("div", { className: classes.rightContainer, children: [
/* @__PURE__ */ jsx(
HvButton,
{
id: setId(id, "action", "apply"),
className: classes.action,
variant: "primaryGhost",
onClick: handleApply,
children: labels?.applyLabel
}
),
/* @__PURE__ */ jsx(
HvButton,
{
id: setId(id, "action", "cancel"),
className: classes.action,
variant: "primaryGhost",
onClick: handleCancel,
children: labels?.cancelLabel
}
)
] })
] });
const dateValue = rangeMode ? { startDate, endDate } : startDate;
const dateString = getDateLabel(dateValue, rangeMode, locale);
const canShowError = ariaErrorMessage == null && (status !== void 0 && statusMessage !== void 0 || status === void 0 && required);
const isStateInvalid = isInvalid(validationState);
let errorMessageId;
if (isStateInvalid) {
errorMessageId = canShowError ? setId(elementId, "error") : ariaErrorMessage;
}
return /* @__PURE__ */ jsxs(
HvFormElement,
{
id,
name,
value: dateValue,
status: validationState,
disabled,
required,
className: cx(classes.root, className),
readOnly,
...others,
children: [
/* @__PURE__ */ jsx(
HvLabelContainer,
{
label,
description,
labelId: setId(elementId, "label"),
descriptionId: setId(elementId, "description"),
classes: {
root: classes.labelContainer,
label: classes.label,
description: classes.description
}
}
),
/* @__PURE__ */ jsxs(
HvBaseDropdown,
{
ref: dropdownForkedRef,
role: "combobox",
classes: {
root: classes.dropdown,
panel: classes.panel,
header: cx({ [classes.dropdownHeaderInvalid]: isStateInvalid }),
headerOpen: classes.dropdownHeaderOpen,
placeholder: cx(classes.inputText, {
[classes.dateText]: dateString
}),
container: classes.container
},
readOnly,
disabled,
disablePortal,
variableWidth: true,
placement: horizontalPlacement,
expanded: calendarOpen,
onToggle: handleToggle,
onClickOutside: handleCalendarClose,
onContainerCreation: focusOnContainer,
placeholder: dateString || placeholder || "",
adornment: /* @__PURE__ */ jsx(HvIcon, { name: "Calendar", className: classes.icon }),
popperProps: {
modifiers: [
{ name: "preventOverflow", enabled: escapeWithReference }
]
},
"aria-haspopup": "dialog",
"aria-label": ariaLabel,
"aria-labelledby": [label && setId(elementId, "label"), ariaLabelledBy].join(" ").trim() || void 0,
"aria-invalid": isStateInvalid ? true : void 0,
"aria-errormessage": errorMessageId,
"aria-describedby": [description && setId(elementId, "description"), ariaDescribedBy].join(" ").trim() || void 0,
...otherDropdownProps,
children: [
/* @__PURE__ */ jsx("div", { ref: focusTarget, tabIndex: -1 }),
/* @__PURE__ */ jsx(
HvCalendar,
{
id: setId(id, "calendar"),
startAdornment,
onChange: handleDateChange,
onInputChange: handleInputDateChange,
onVisibleDateChange: (_event, type, month, target) => {
dispatchAction({ type, target, month });
},
locale,
...visibleDate,
...calendarProps,
invalidDateLabel: labels?.invalidDateLabel
}
),
(rangeMode || showActions) && renderActions()
]
}
),
canShowError && /* @__PURE__ */ jsx(
HvWarningText,
{
id: setId(elementId, "error"),
disableBorder: true,
className: cx(classes.error),
children: validationMessage
}
)
]
}
);
}
);
export {
HvDatePicker,
staticClasses as datePickerClasses
};