UNPKG

@mantine/dates

Version:

Calendars, date and time pickers based on Mantine components

420 lines (417 loc) 14.1 kB
'use client'; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { useRef, useState } from 'react'; import { createVarsResolver, getFontSize, factory, useProps, useResolvedStylesApi, useStyles, Popover, InputBase, CloseButton } from '@mantine/core'; import { useMergedRef, useId } from '@mantine/hooks'; import { SpinInput } from '../SpinInput/SpinInput.mjs'; import { AmPmInput } from './AmPmInput/AmPmInput.mjs'; import { AmPmControlsList } from './TimeControlsList/AmPmControlsList.mjs'; import { TimeControlsList } from './TimeControlsList/TimeControlsList.mjs'; import { TimePickerProvider } from './TimePicker.context.mjs'; import { TimePresets } from './TimePresets/TimePresets.mjs'; import { useTimePicker } from './use-time-picker.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 classes from './TimePicker.module.css.mjs'; const defaultProps = { hoursStep: 1, minutesStep: 1, secondsStep: 1, format: "24h", amPmLabels: { am: "AM", pm: "PM" }, pasteSplit: getParsedTime, maxDropdownContentHeight: 200, hoursPlaceholder: "--", minutesPlaceholder: "--", secondsPlaceholder: "--" }; const varsResolver = createVarsResolver((_theme, { size }) => ({ dropdown: { "--control-font-size": getFontSize(size) } })); const TimePicker = factory((_props, ref) => { const props = useProps("TimePicker", defaultProps, _props); const { classNames, className, style, styles, unstyled, vars, onClick, format, value, defaultValue, onChange, hoursStep, minutesStep, secondsStep, withSeconds, hoursInputLabel, minutesInputLabel, secondsInputLabel, amPmInputLabel, amPmLabels, clearable, 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, maxDropdownContentHeight, scrollAreaProps, attributes, reverseTimeControlsList, hoursPlaceholder, minutesPlaceholder, secondsPlaceholder, ...others } = props; const { resolvedClassNames, resolvedStyles } = useResolvedStylesApi({ classNames, styles, props }); const getStyles = useStyles({ name: "TimePicker", classes, props, className, style, classNames, styles, unstyled, attributes, vars, varsResolver }); const controller = useTimePicker({ value, defaultValue, onChange, format, amPmLabels, withSeconds, min, max, clearable, disabled, readOnly, pasteSplit }); 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 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, ...popoverProps, children: [ /* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsxs( InputBase, { component: "div", size, disabled, ref, onClick: (event) => { onClick?.(event); controller.focus("hours"); }, onMouseDown: (event) => { event.preventDefault(); onMouseDown?.(event); }, onFocusCapture: (event) => { setDropdownOpened(true); onFocusCapture?.(event); }, onBlurCapture: (event) => { setDropdownOpened(false); onBlurCapture?.(event); }, rightSection: rightSection || controller.isClearable && /* @__PURE__ */ jsx( CloseButton, { ...clearButtonProps, size, onClick: (event) => { controller.clear(); clearButtonProps?.onClick?.(event); }, onMouseDown: (event) => { event.preventDefault(); clearButtonProps?.onMouseDown?.(event); } } ), labelProps: { htmlFor: hoursInputId, ...labelProps }, style, className, classNames: resolvedClassNames, styles: resolvedStyles, __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: hoursInputProps?.style }), value: controller.values.hours, onChange: controller.setHours, onNextInput: () => controller.focus("minutes"), min: format === "12h" ? 1 : 0, max: format === "12h" ? 12 : 23, allowTemporaryZero: format === "12h", 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; const numericValue = actualInputValue ? parseInt(actualInputValue, 10) : null; if (format === "12h" && numericValue === 0) { controller.setHours(12); } hoursInputProps?.onBlur?.(event); }, placeholder: hoursPlaceholder } ), /* @__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" && /* @__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: controller.setTimeString, 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 = classes; export { TimePicker }; //# sourceMappingURL=TimePicker.mjs.map