@mantine/dates
Version:
Calendars, date and time pickers based on Mantine components
354 lines (353 loc) • 12 kB
JavaScript
"use client";
import { padTime } from "./utils/pad-time/pad-time.mjs";
import { SpinInput } from "../SpinInput/SpinInput.mjs";
import { TimePickerProvider } from "./TimePicker.context.mjs";
import { AmPmInput } from "./AmPmInput/AmPmInput.mjs";
import { AmPmControlsList } from "./TimeControlsList/AmPmControlsList.mjs";
import { TimeControlsList } from "./TimeControlsList/TimeControlsList.mjs";
import { TimePresets } from "./TimePresets/TimePresets.mjs";
import { clampTime } from "./utils/clamp-time/clamp-time.mjs";
import { getParsedTime } from "./utils/get-parsed-time/get-parsed-time.mjs";
import { getTimeString } from "./utils/get-time-string/get-time-string.mjs";
import { useTimePicker } from "./use-time-picker.mjs";
import TimePicker_module_default from "./TimePicker.module.mjs";
import { useRef, useState } from "react";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { CloseButton, InputBase, Popover, createVarsResolver, factory, getFontSize, useProps, useResolvedStylesApi, useStyles } from "@mantine/core";
import { useId, useMergedRef } from "@mantine/hooks";
//#region packages/@mantine/dates/src/components/TimePicker/TimePicker.tsx
const defaultProps = {
type: "time",
hoursStep: 1,
minutesStep: 1,
secondsStep: 1,
format: "24h",
amPmLabels: {
am: "AM",
pm: "PM"
},
pasteSplit: getParsedTime,
maxDropdownContentHeight: 200,
hoursPlaceholder: "--",
minutesPlaceholder: "--",
secondsPlaceholder: "--",
minHoursDigits: 2
};
const varsResolver = createVarsResolver((_theme, { size }) => ({ dropdown: { "--control-font-size": getFontSize(size) } }));
const DURATION_HOURS_MAX = Infinity;
const TimePicker = factory((_props) => {
const props = useProps([
"Input",
"InputWrapper",
"TimePicker"
], defaultProps, _props);
const { classNames, className, style, styles, unstyled, vars, onClick, type, format: _format, value, defaultValue, onChange, hoursStep, minutesStep, secondsStep, withSeconds, hoursInputLabel, minutesInputLabel, secondsInputLabel, amPmInputLabel, amPmLabels, clearable, clearSectionMode, onMouseDown, onFocusCapture, onBlurCapture, min, max, popoverProps, withDropdown, rightSection, onFocus, onBlur, clearButtonProps, hoursInputProps, minutesInputProps, secondsInputProps, amPmSelectProps, readOnly, disabled, size, name, form, hiddenInputProps, labelProps, pasteSplit, hoursRef, minutesRef, secondsRef, amPmRef, presets, closeDropdownOnPresetSelect, maxDropdownContentHeight, scrollAreaProps, attributes, reverseTimeControlsList, hoursPlaceholder, minutesPlaceholder, secondsPlaceholder, minHoursDigits, ...others } = props;
const isDuration = type === "duration";
const format = isDuration ? "24h" : _format;
const resolvedHoursPlaceholder = isDuration && hoursPlaceholder === "--" ? "-".repeat(minHoursDigits) : hoursPlaceholder;
const { resolvedClassNames, resolvedStyles } = useResolvedStylesApi({
classNames,
styles,
props
});
const getStyles = useStyles({
name: "TimePicker",
classes: TimePicker_module_default,
props,
className,
style,
classNames,
styles,
unstyled,
attributes,
vars,
varsResolver
});
const controller = useTimePicker({
value,
defaultValue,
onChange,
format,
amPmLabels,
withSeconds,
min,
max,
clearable,
disabled,
readOnly,
pasteSplit,
type
});
const _hoursRef = useMergedRef(controller.refs.hours, hoursRef);
const _minutesRef = useMergedRef(controller.refs.minutes, minutesRef);
const _secondsRef = useMergedRef(controller.refs.seconds, secondsRef);
const _amPmRef = useMergedRef(controller.refs.amPm, amPmRef);
const hoursInputId = useId();
const hasFocusRef = useRef(false);
const [dropdownOpened, setDropdownOpened] = useState(false);
const handleFocus = (event) => {
if (!hasFocusRef.current) {
hasFocusRef.current = true;
onFocus?.(event);
}
};
const handlePresetSelect = (timeString) => {
controller.setTimeString(timeString);
if (closeDropdownOnPresetSelect) {
setDropdownOpened(false);
popoverProps?.onChange?.(false);
}
};
const handleBlur = (event) => {
if (!event.currentTarget.contains(event.relatedTarget)) {
const computedValue = controller.values;
const timeString = getTimeString({
...computedValue,
format,
amPmLabels,
withSeconds: !!withSeconds
});
if (timeString.valid && (min || max)) {
const clamped = clampTime(timeString.value, min, max);
if (clamped.timeString !== timeString.value) controller.setTimeString(clamped.timeString);
}
hasFocusRef.current = false;
onBlur?.(event);
}
};
return /* @__PURE__ */ jsx(TimePickerProvider, {
value: {
getStyles,
scrollAreaProps,
maxDropdownContentHeight
},
children: /* @__PURE__ */ jsxs(Popover, {
opened: dropdownOpened,
transitionProps: { duration: 0 },
position: "bottom-start",
withRoles: false,
disabled: disabled || readOnly || !withDropdown || isDuration,
...popoverProps,
children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsxs(InputBase, {
component: "div",
size,
disabled,
onClick: (event) => {
onClick?.(event);
controller.focus("hours");
if (!disabled) setDropdownOpened(true);
},
onMouseDown: (event) => {
event.preventDefault();
onMouseDown?.(event);
},
onFocusCapture: (event) => {
setDropdownOpened(true);
onFocusCapture?.(event);
},
onBlurCapture: (event) => {
setDropdownOpened(false);
onBlurCapture?.(event);
},
rightSection,
__clearSection: /* @__PURE__ */ jsx(CloseButton, {
...clearButtonProps,
size,
onClick: (event) => {
controller.clear();
clearButtonProps?.onClick?.(event);
},
onMouseDown: (event) => {
event.preventDefault();
clearButtonProps?.onMouseDown?.(event);
}
}),
__clearable: controller.isClearable,
__clearSectionMode: clearSectionMode,
labelProps: {
htmlFor: hoursInputId,
...labelProps
},
style,
className,
classNames: resolvedClassNames,
styles: resolvedStyles,
attributes,
__staticSelector: "TimePicker",
...others,
children: [/* @__PURE__ */ jsx("div", {
...getStyles("fieldsRoot"),
dir: "ltr",
children: /* @__PURE__ */ jsxs("div", {
...getStyles("fieldsGroup"),
onBlur: handleBlur,
children: [
/* @__PURE__ */ jsx(SpinInput, {
id: hoursInputId,
...hoursInputProps,
...getStyles("field", {
className: hoursInputProps?.className,
style: isDuration ? {
width: `calc(${Math.max(minHoursDigits, padTime(controller.values.hours ?? 0).length)}ch + 0.3em)`,
...hoursInputProps?.style
} : hoursInputProps?.style
}),
value: controller.values.hours,
onChange: controller.setHours,
onNextInput: () => controller.focus("minutes"),
min: format === "12h" ? 1 : 0,
max: isDuration ? DURATION_HOURS_MAX : format === "12h" ? 12 : 23,
allowTemporaryZero: format === "12h",
disableAutoAdvance: isDuration,
focusable: true,
step: hoursStep,
ref: _hoursRef,
"aria-label": hoursInputLabel,
readOnly,
disabled,
onPaste: controller.onPaste,
onFocus: (event) => {
handleFocus(event);
hoursInputProps?.onFocus?.(event);
},
onBlur: (event) => {
const actualInputValue = event.currentTarget.value;
if (format === "12h" && (actualInputValue ? parseInt(actualInputValue, 10) : null) === 0) controller.setHours(12);
hoursInputProps?.onBlur?.(event);
},
placeholder: resolvedHoursPlaceholder
}),
/* @__PURE__ */ jsx("span", { children: ":" }),
/* @__PURE__ */ jsx(SpinInput, {
...minutesInputProps,
...getStyles("field", {
className: minutesInputProps?.className,
style: minutesInputProps?.style
}),
value: controller.values.minutes,
onChange: controller.setMinutes,
min: 0,
max: 59,
focusable: true,
step: minutesStep,
ref: _minutesRef,
onPreviousInput: () => controller.focus("hours"),
onNextInput: () => withSeconds ? controller.focus("seconds") : controller.focus("amPm"),
"aria-label": minutesInputLabel,
tabIndex: -1,
readOnly,
disabled,
onPaste: controller.onPaste,
onFocus: (event) => {
handleFocus(event);
minutesInputProps?.onFocus?.(event);
},
placeholder: minutesPlaceholder
}),
withSeconds && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", { children: ":" }), /* @__PURE__ */ jsx(SpinInput, {
...secondsInputProps,
...getStyles("field", {
className: secondsInputProps?.className,
style: secondsInputProps?.style
}),
value: controller.values.seconds,
onChange: controller.setSeconds,
min: 0,
max: 59,
focusable: true,
step: secondsStep,
ref: _secondsRef,
onPreviousInput: () => controller.focus("minutes"),
onNextInput: () => controller.focus("amPm"),
"aria-label": secondsInputLabel,
tabIndex: -1,
readOnly,
disabled,
onPaste: controller.onPaste,
onFocus: (event) => {
handleFocus(event);
secondsInputProps?.onFocus?.(event);
},
placeholder: secondsPlaceholder
})] }),
format === "12h" && !isDuration && /* @__PURE__ */ jsx(AmPmInput, {
...amPmSelectProps,
inputType: withDropdown ? "input" : "select",
labels: amPmLabels,
value: controller.values.amPm,
onChange: controller.setAmPm,
ref: _amPmRef,
"aria-label": amPmInputLabel,
onPreviousInput: () => withSeconds ? controller.focus("seconds") : controller.focus("minutes"),
readOnly,
disabled,
tabIndex: -1,
onPaste: controller.onPaste,
onFocus: (event) => {
handleFocus(event);
amPmSelectProps?.onFocus?.(event);
}
})
]
})
}), /* @__PURE__ */ jsx("input", {
type: "hidden",
name,
form,
value: controller.hiddenInputValue,
...hiddenInputProps
})]
}) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
...getStyles("dropdown"),
onMouseDown: (event) => event.preventDefault(),
children: presets ? /* @__PURE__ */ jsx(TimePresets, {
value: controller.hiddenInputValue,
onChange: handlePresetSelect,
format,
presets,
amPmLabels,
withSeconds: withSeconds || false
}) : /* @__PURE__ */ jsxs("div", {
...getStyles("controlsListGroup"),
children: [
/* @__PURE__ */ jsx(TimeControlsList, {
min: format === "12h" ? 1 : 0,
max: format === "12h" ? 12 : 23,
step: hoursStep,
value: controller.values.hours,
onSelect: controller.setHours,
reversed: reverseTimeControlsList
}),
/* @__PURE__ */ jsx(TimeControlsList, {
min: 0,
max: 59,
step: minutesStep,
value: controller.values.minutes,
onSelect: controller.setMinutes,
reversed: reverseTimeControlsList
}),
withSeconds && /* @__PURE__ */ jsx(TimeControlsList, {
min: 0,
max: 59,
step: secondsStep,
value: controller.values.seconds,
onSelect: controller.setSeconds,
reversed: reverseTimeControlsList
}),
format === "12h" && /* @__PURE__ */ jsx(AmPmControlsList, {
labels: amPmLabels,
value: controller.values.amPm,
onSelect: controller.setAmPm
})
]
})
})]
})
});
});
TimePicker.displayName = "@mantine/dates/TimePicker";
TimePicker.classes = TimePicker_module_default;
TimePicker.varsResolver = varsResolver;
//#endregion
export { TimePicker };
//# sourceMappingURL=TimePicker.mjs.map