UNPKG

@spaced-out/ui-design-system

Version:
230 lines (208 loc) 5.72 kB
// @flow strict import * as React from 'react'; import classify from '../../utils/classify'; import {UnstyledButton} from '../Button'; import css from './WeekdayPicker.module.css'; const DEFAULT_WEEKDAYS = [ {key: 'sun', label: 'S'}, {key: 'mon', label: 'M'}, {key: 'tue', label: 'T'}, {key: 'wed', label: 'W'}, {key: 'thu', label: 'T'}, {key: 'fri', label: 'F'}, {key: 'sat', label: 'S'}, ]; const WEEKEND_KEYS = ['sat', 'sun']; export const WEEKDAY_PICKER_SIZE = Object.freeze({ small: 'small', medium: 'medium', }); type ClassNames = $ReadOnly<{ wrapper?: string, button?: string, label?: string, helperText?: string, }>; type WeekdayPickerSize = $Values<typeof WEEKDAY_PICKER_SIZE>; type Weekday = { key: string, label: string, secondaryLabel?: string, }; export type WeekdayPickerProps = { size?: WeekdayPickerSize, selectedWeekDays?: Array<Weekday>, hiddenWeekDays?: Array<string>, disabledWeekDays?: Array<string>, customDayLabels?: Array<Weekday>, onChange?: ( selectedDays: Array<Weekday>, ?SyntheticEvent<HTMLElement>, ) => mixed, readOnly?: boolean, classNames?: ClassNames, showWeekends?: boolean, ariaLabel?: string, label?: React.Node, helperText?: React.Node, disableMultiSelect?: boolean, }; type DayButtonProps = { day: Weekday, isSelected: boolean, isDisabled: boolean, onClick?: ?(SyntheticEvent<HTMLElement>) => mixed, className?: string, size: WeekdayPickerSize, readOnly?: boolean, }; const DayButton = ({ day, isSelected, isDisabled, onClick, className, size, readOnly, }: DayButtonProps) => ( <UnstyledButton key={day.key} className={classify( css.weekdayButton, { [css.weekdayButtonMedium]: size === WEEKDAY_PICKER_SIZE.medium, [css.weekdayButtonSmall]: size === WEEKDAY_PICKER_SIZE.small, [css.weekdayButtonSelected]: isSelected, [css.weekdayButtonDisabled]: isDisabled, }, className, )} onClick={onClick} disabled={isDisabled} aria-label={`Select ${day.label}`} tabIndex={isDisabled || readOnly ? -1 : 0} role="checkbox" > {day.secondaryLabel && ( <div className={classify(css.secondaryLabel, { [css.secondaryLabelSelected]: isSelected, [css.secondaryLabelDisabled]: isDisabled, })} > {day.secondaryLabel} </div> )} {day.label} </UnstyledButton> ); export const WeekdayPicker: React$AbstractComponent< WeekdayPickerProps, HTMLDivElement, > = React.forwardRef<WeekdayPickerProps, HTMLDivElement>( ( { size = 'medium', selectedWeekDays = [], disabledWeekDays = [], hiddenWeekDays = [], customDayLabels = DEFAULT_WEEKDAYS, onChange, readOnly = false, classNames = {}, showWeekends = true, ariaLabel = 'Select weekdays', label, helperText, disableMultiSelect = false, }: WeekdayPickerProps, ref, ) => { const [selectedDays, setSelectedDays] = React.useState<Array<Weekday>>(selectedWeekDays); const handleDayToggle = ( day: Weekday, event: SyntheticEvent<HTMLElement>, ) => { if (readOnly || disabledWeekDays.includes(day.key)) { return; } let updatedSelectedDays = selectedDays; if (disableMultiSelect) { if (selectedDays.some((selected) => selected.key === day.key)) { updatedSelectedDays = []; } else { updatedSelectedDays = [day]; } } else { const isSelected = selectedDays.some( (selected) => selected.key === day.key, ); if (isSelected) { updatedSelectedDays = selectedDays.filter( (selected) => selected.key !== day.key, ); } else { updatedSelectedDays = [...selectedDays, day]; } } setSelectedDays(updatedSelectedDays); onChange?.(updatedSelectedDays, event); }; return ( <div data-testid="Weekday-Picker" ref={ref} className={classify(css.weekdayPickerContainer, classNames.wrapper, { [css.weekdayReadOnly]: readOnly, })} aria-label={ariaLabel} role="group" > {!!label && ( <div className={classify(css.weekdayLabel, classNames.label)}> {label} </div> )} <div className={css.weekdayPickerDays}> {customDayLabels.map((day) => { const isWeekend = WEEKEND_KEYS.includes(day.key); const isHidden = hiddenWeekDays.includes(day.key); if ((!showWeekends && isWeekend) || isHidden) { return null; } const isSelected = selectedDays.some( (selected) => selected.key === day.key, ); const isDisabled = disabledWeekDays.includes(day.key); return ( <DayButton key={day.key} day={day} isSelected={isSelected} isDisabled={isDisabled} onClick={(event) => { if (isDisabled) { event.preventDefault(); } else { handleDayToggle(day, event); } }} className={classNames.button} size={size} readOnly={readOnly} /> ); })} </div> {!!helperText && ( <div className={classify(css.weekdayHelperText, classNames.helperText)} > {helperText} </div> )} </div> ); }, );