UNPKG

@utahdts/utah-design-system

Version:
156 lines (148 loc) 5.72 kB
import { add, format, isValid, parse } from 'date-fns'; import { useMemo } from 'react'; import { joinClassNames } from '../../util/joinClassNames'; import { ComboBox } from './ComboBox/ComboBox'; import { ComboBoxOption } from './ComboBox/ComboBoxOption'; import { TextInput } from './TextInput'; /** * @param {object} props * @param {boolean} [props.allowCustomEntry] can the user type in their own time that is not in the popup combobox list * @param {string} [props.className] * @param {string} [props.defaultValue] * @param {string} [props.errorMessage] * @param {boolean} [props.hasTimePopup] is there a popup from which the user can select the time? * @param {string} props.id * @param {import('react').Ref<HTMLDivElement>} [props.innerRef] * @param {boolean} [props.isClearable] should the clearable "X" icon be shown; is auto set to true if onClear is passed in * @param {boolean} [props.isDisabled] * @param {boolean} [props.isRequired] * @param {string} props.label * @param {string} [props.labelClassName] * @param {string} [props.name] * @param {(newValue: string) => void} [props.onChange] can be omitted to be uncontrolled * @param {() => void} [props.onClear] * @param {string} [props.placeholder] * @param {string} [props.timeFormat] use `date-fns` modifiers for formatting the time options * @param {number} [props.timeRangeIncrement] for popup, what increment (in minutes) for the options given to the user * @param {string} [props.timeRangeBegin] options in popup can start (inclusive) at a given time; format per `props.timeFormat` * @param {string} [props.timeRangeEnd] options in popup can end at the given time (inclusive); format per `props.timeFormat` * @param {string} [props.value] * @param {string} [props.wrapperClassName] * @returns {import('react').JSX.Element} */ export function TimeInput({ allowCustomEntry, className, defaultValue, errorMessage, hasTimePopup = true, id, innerRef, isClearable, isDisabled, isRequired, label, labelClassName, name, onChange, onClear, placeholder, timeFormat = 'h:mm aaa', timeRangeBegin, timeRangeEnd, timeRangeIncrement = 15, value, wrapperClassName, ...rest }) { const timeOptions = useMemo( () => { const defaultStartDate = new Date(new Date().setHours(0, 0, 0, 0)); const defaultEndDate = new Date(new Date().setHours(23, 59, 0, 0)); let optionsBeginDate = (timeRangeBegin && parse(timeRangeBegin, timeFormat, new Date())) || null; optionsBeginDate = (optionsBeginDate && isValid(optionsBeginDate)) ? optionsBeginDate : defaultStartDate; let optionsEndDate = (timeRangeEnd && parse(timeRangeEnd, timeFormat, new Date())) || null; optionsEndDate = (optionsEndDate && isValid(optionsEndDate)) ? optionsEndDate : defaultEndDate; const timeOptionsRet = []; for ( let loopDate = optionsBeginDate; loopDate.getTime() <= optionsEndDate.getTime(); loopDate = add(loopDate, { minutes: timeRangeIncrement }) ) { timeOptionsRet.push(format(loopDate, timeFormat)); } return timeOptionsRet; }, [timeRangeBegin, timeRangeEnd, timeRangeIncrement, timeFormat] ); const clockIcon = useMemo( () => ( <span className={joinClassNames('utds-icon-before-clock', 'time-input__clock-icon', isDisabled && 'time-input__clock-icon--is-disabled', !hasTimePopup && 'time-input__clock-icon--static')} aria-hidden="true" /> ), [isDisabled, hasTimePopup] ); return ( <div className={joinClassNames('time-input__wrapper', wrapperClassName)} ref={innerRef}> { hasTimePopup ? ( <ComboBox // COMMON PROPS: make sure these match with TextInput className={className} defaultValue={defaultValue} errorMessage={errorMessage} id={id} isClearable={isClearable} isDisabled={isDisabled} isRequired={isRequired} label={label} labelClassName={labelClassName} name={name || id} onClear={isClearable ? onClear : undefined} placeholder={placeholder} value={value} // END COMMON PROPS allowCustomEntry={allowCustomEntry} iconCallback={() => clockIcon} onChange={onChange} // eslint-disable-next-line react/jsx-props-no-spreading {...rest} > { timeOptions.map((timeOption) => ( <ComboBoxOption key={`time-input__${id}__${timeOption}`} label={timeOption} value={timeOption} /> )) } </ComboBox> ) : ( <TextInput // COMMON PROPS: make sure these match with ComboBox className={className} defaultValue={defaultValue} errorMessage={errorMessage} id={id} isClearable={isClearable} isDisabled={isDisabled} isRequired={isRequired} label={label} labelClassName={labelClassName} name={name || id} onClear={isClearable ? onClear : undefined} placeholder={placeholder} value={value} // END COMMON PROPS onChange={(e) => onChange?.(e.target.value)} rightContent={clockIcon} // eslint-disable-next-line react/jsx-props-no-spreading {...rest} /> ) } </div> ); }