UNPKG

rsuite

Version:

A suite of react components

250 lines (244 loc) 8.34 kB
'use client'; import _extends from "@babel/runtime/helpers/esm/extends"; import React, { useRef, useMemo } from 'react'; import Input from "../Input/index.js"; import useDateInputState from "./hooks/useDateInputState.js"; import useKeyboardInputEvent from "./hooks/useKeyboardInputEvent.js"; import useIsFocused from "./hooks/useIsFocused.js"; import useFieldCursor from "./hooks/useFieldCursor.js"; import useSelectedState from "./hooks/useSelectedState.js"; import { useControlled, useEventCallback, useCustom } from "../internals/hooks/index.js"; import { forwardRef, mergeRefs } from "../internals/utils/index.js"; import { isValid } from "../internals/utils/date/index.js"; import { getInputSelectedState, validateDateTime, useInputSelection } from "./utils.js"; /** * The DateInput component lets users select a date with the keyboard. * @version 5.58.0 * @see https://rsuitejs.com/components/date-input/ */ const DateInput = forwardRef((props, ref) => { const { propsWithDefaults, parseDate, getLocale } = useCustom('DateInput', props); const { dateLocale, shortDateFormat } = getLocale('DateTimeFormats'); const { format: formatStr = shortDateFormat, value: valueProp, defaultValue, placeholder, onChange, onKeyDown, onBlur, onFocus, onPaste, ...rest } = propsWithDefaults; const inputRef = useRef(null); const { selectedState, setSelectedState } = useSelectedState(); const [value, setValue, isControlled] = useControlled(valueProp, defaultValue); const { dateField, setDateOffset, setDateField, setNewDate, getDateField, toDateString, isEmptyValue } = useDateInputState({ formatStr, locale: dateLocale, date: value, isControlledDate: isControlled }); const { isMoveCursor, isResetValue, increment, reset } = useFieldCursor(formatStr, valueProp); const dateString = toDateString(); const keyPressOptions = useMemo(() => ({ formatStr, localize: dateLocale.localize, selectedMonth: dateField.month, dateString }), [dateField, dateString, formatStr, dateLocale]); const setSelectionRange = useInputSelection(inputRef); const handleChange = useEventCallback((value, event) => { onChange === null || onChange === void 0 || onChange(value, event); setValue(value); }); const handleClear = useEventCallback(event => { handleChange(null, event); setNewDate(null); setSelectionRange(0, 0); reset(); }); const onSegmentChange = useEventCallback((event, nextDirection) => { const input = event.target; const key = event.key; const direction = nextDirection || (key === 'ArrowRight' ? 'right' : 'left'); const state = getInputSelectedState({ ...keyPressOptions, input, direction }); setSelectedState(state); setSelectionRange(state.selectionStart, state.selectionEnd); // If the selected field changes, reset the input state if (selectedState.selectedPattern !== state.selectedPattern) { reset(); } }); const onSegmentValueChange = useEventCallback(event => { const input = event.target; const key = event.key; const offset = key === 'ArrowUp' ? 1 : -1; const state = getInputSelectedState({ ...keyPressOptions, input, valueOffset: offset }); setSelectedState(state); setDateOffset(state.selectedPattern, offset, date => handleChange(date, event)); setSelectionRange(state.selectionStart, state.selectionEnd); }); const onSegmentValueChangeWithNumericKeys = useEventCallback(event => { const input = event.target; const key = event.key; const isFunctionKey = key.startsWith('F') && !isNaN(Number(key.slice(1))); if (isFunctionKey) { return; } const pattern = selectedState.selectedPattern; if (!pattern) { return; } const field = getDateField(pattern); const value = parseInt(key, 10); const padValue = parseInt(`${field.value || ''}${key}`, 10); let newValue = value; if (validateDateTime(field.name, padValue) && !isResetValue()) { // Check if the value entered by the user is a valid date newValue = padValue; } setDateField(pattern, newValue, date => handleChange(date, event)); // The currently selected month will be retained as a parameter of getInputSelectedState, // but if the user enters a month, the month value will be replaced with the value entered by the user. const selectedMonth = pattern === 'M' ? newValue : dateField.month; const nextState = getInputSelectedState({ ...keyPressOptions, input, selectedMonth }); setSelectedState(nextState); setSelectionRange(nextState.selectionStart, nextState.selectionEnd); increment(); // If the field is full value, move the cursor to the next field if (isMoveCursor(newValue, pattern) && input.selectionEnd !== input.value.length) { onSegmentChange(event, 'right'); } }); const onSegmentValueRemove = useEventCallback(event => { const input = event.target; const value = input.value; // If the text is all selected, clear the value if (input.selectionStart === 0 && value && input.selectionEnd === value.length) { handleClear(event); } else if (selectedState.selectedPattern) { const nextState = getInputSelectedState({ ...keyPressOptions, input, valueOffset: null }); setSelectedState(nextState); setSelectionRange(nextState.selectionStart, nextState.selectionEnd); setDateField(selectedState.selectedPattern, null, date => handleChange(date, event)); reset(); } }); const onAmPmToggle = useEventCallback(event => { const input = event.target; const key = event.key.toLowerCase(); // Only handle 'a' or 'p' keys when the selected pattern is 'a' (AM/PM) if (selectedState.selectedPattern === 'a' && (key === 'a' || key === 'p')) { const currentHour = dateField.hour || 0; const isAM = currentHour < 12; const isPM = currentHour >= 12; // Toggle AM/PM based on the key pressed // 'a' key -> set to AM, 'p' key -> set to PM if (key === 'a' && isPM || key === 'p' && isAM) { const state = getInputSelectedState({ ...keyPressOptions, input }); setSelectedState(state); setDateOffset('a', 1, date => handleChange(date, event)); setSelectionRange(state.selectionStart, state.selectionEnd); } } }); const handleClick = useEventCallback(event => { const input = event.target; const state = getInputSelectedState({ ...keyPressOptions, input }); setSelectedState(state); setSelectionRange(state.selectionStart, state.selectionEnd); if (selectedState.selectedPattern !== state.selectedPattern) { reset(); } }); const handlePaste = useEventCallback(event => { var _event$clipboardData; event.preventDefault(); const pasteText = (_event$clipboardData = event.clipboardData) === null || _event$clipboardData === void 0 ? void 0 : _event$clipboardData.getData('text'); const nextDate = parseDate(pasteText, formatStr); if (isValid(nextDate)) { handleChange(nextDate, event); setNewDate(nextDate); } onPaste === null || onPaste === void 0 || onPaste(event); }); const onKeyboardInput = useKeyboardInputEvent({ onSegmentChange, onSegmentValueChange, onSegmentValueChangeWithNumericKeys, onSegmentValueRemove, onAmPmToggle, onKeyDown }); const [focused, focusEventProps] = useIsFocused({ onBlur, onFocus }); const renderedValue = useMemo(() => { if (!isEmptyValue()) { return dateString; } return !focused ? '' : dateString; }, [dateString, focused, isEmptyValue]); return /*#__PURE__*/React.createElement(Input, _extends({ inputMode: focused ? 'numeric' : 'text', autoComplete: "off", autoCorrect: "off", spellCheck: false, ref: mergeRefs(inputRef, ref), onKeyDown: onKeyboardInput, onClick: handleClick, onPaste: handlePaste, value: renderedValue, placeholder: placeholder || formatStr }, focusEventProps, rest)); }); DateInput.displayName = 'DateInput'; export default DateInput;