@uxf/datepicker

npm size quality license

A set of React hooks to create an awesome datepicker.

Quick start

Main logic component

export const DatePicker: FC = () => {
    // prepare state to handle selected date
    const [selectedDate, setSelectedDate] = useState<OnDateChangeType>(null);

    const {
        activeMonths,
        firstDayOfWeek,
    } = useDatePicker({
        selectedDate,
        minBookingDate: new Date(),
        onDateChange: setSelectedDate,
    });

    // you can use DatePickerContext that helps you avoid passing callbacks throught components
    return (
        <DatePickerContext.Provider value={{ addPropsHere }}>
              {activeMonths.map(month => (
                    <DateRangePickerMonth
                        firstDayOfWeek={firstDayOfWeek}
                        key={`${month.year}-${month.month}`}
                        month={month.month}
                        year={month.year}
                    />
              ))}
        </DatePickerContext.Provider>
    );
};

Month component

export const DateRangePickerMonth: FC<UseMonthProps> = ({
    firstDayOfWeek,
    month,
    year,
}) => {
    const { days, monthLabel } = useMonth({
        year,
        month,
        firstDayOfWeek,
    });

    return (
        <div>
            <p>{monthLabel}</p>
            <div style={{ display: "grid" }}>
                {days.map((day, index) => {
                    return <DateRangePickerDay date={day.date} key={day.dayLabel + index} day={day.dayLabel} />;
                })}
            </div>
        </div>
    );
};

Day component

export const DateRangePickerDay: FC<{ day: string; date: Date }> = ({ day, date }) => {
    const dayRef = useRef(null);
    const {
        focusedDate,
        isDateFocused,
        isDateSelected,
        isDateHovered,
        isDateBlocked,
        onDateSelect,
        onDateFocus,
        onDateHover,
    } = useContext(DatePickerContext);

    const { disabledDate, isSelected, isWithinHoverRange, onClick, isToday } =
        useDay<HTMLDivElement>({
            date,
            dayRef,
            focusedDate,
            isDateFocused,
            isDateSelected,
            isDateHovered,
            isDateBlocked,
            onDateFocus,
            onDateSelect,
            onDateHover,
        });

    if (!day) {
        return <div />;
    }

    return (
        <button
            type="button"
            ref={dayRef}
            onClick={onClick}
            className= {clsx({
                "day__disabled": disabledDate,
                "day__selected": isSelected,
                "day__today": isToday,
            })}
        >
            {day}
        </button>
    );
};

API

useDatePicker()

UseDatePickerProps

| name | type | required | default | description | |---------------------|----------------------------------|----------|---------------|-------------| | firstDayOfWeek | FirstDayOfWeek | | 0 | | | initialVisibleMonth | Date | | | | | isDateBlocked | (date: Date) => boolean | | () => false | | | maxBookingDate | Date | | | | | minBookingDate | Date | | | | | numberOfMonths | number | | 1 | | | onDateChange | (data: OnDateChangeType): void | yes | | | | selectedDate | Date \| null | yes | | | | unavailableDates | Date[] | | [] | @deprecated | | datesConfig | DatesConfig[] | | [] | |

UseDatePickerReturnType

| name | type | description | |------------------------------|--------------------------------|-------------| | canGoToMonth | (month: Date) => boolean | | | canGoToNextMonth | boolean | | | canGoToNextYear | boolean | | | canGoToPrevMonth | boolean | | | canGoToPrevYear | boolean | | | canGoToYear | (month: Date) => boolean | | | goToDate | (date: Date) => void | | | goToNextMonths | () => void | | | goToNextMonthsByOneMonth | () => void | | | goToNextYear | () => void | | | goToPrevMonths | () => void | | | goToPrevMonthsByOneMonth | () => void | | | goToPrevYear | () => void | | | activeMonths | MonthType[] | | | firstDayOfWeek | FirstDayOfWeek | | | focusedDate | Date \| null | | | hoveredDate | Date \| null | | | isDateBlocked | (date: Date) => boolean | | | isDateFocused | (date: Date) => boolean | | | isDateHovered | (date: Date) => boolean | | | isDateSelected | (date: Date) => boolean | | | numberOfMonths | number | | | onDateFocus | (date: Date) => void | | | onDateHover | (date: Date \| null) => void | | | onDateSelect | (date: Date) => void | | | onResetDates | () => void | |

useDateRangePicker()

UseDateRangePickerProps

| name | type | required | default | description | |---------------------------|-----------------------------------|----------|---------------|-------------| | changeActiveMonthOnSelect | boolean | | false | | | endDate | Date \| null | yes | | | | exactMinBookingDays | boolean | | false | | | firstDayOfWeek | FirstDayOfWeek | | 0 | | | focusedInput | FocusedInput | yes | startDate | | | initialVisibleMonth | Date | | | | | isDateBlocked | (date: Date) => boolean | | () => false | | | maxBookingDate | Date | | | | | minBookingDate | Date | | | | | minBookingDates | number | | 1 | | | numberOfMonths | number | | 2 | | | onDatesChange | (data: OnDatesChangeType): void | yes | | | | startDate | Date \| null | yes | | | | unavailableDates | Date[] | | [] | @deprecated | | datesConfig | DatesConfig[] | | [] | |

UseDateRangePickerReturnType

| name | type | description | |------------------------------|--------------------------------|-------------| | canGoToMonth | (month: Date) => boolean | | | canGoToNextMonth | boolean | | | canGoToNextYear | boolean | | | canGoToPrevMonth | boolean | | | canGoToPrevYear | boolean | | | canGoToYear | (month: Date) => boolean | | | goToDate | (date: Date) => void | | | goToNextMonths | () => void | | | goToNextMonthsByOneMonth | () => void | | | goToNextYear | () => void | | | goToPrevMonths | () => void | | | goToPrevMonthsByOneMonth | () => void | | | goToPrevYear | () => void | | | activeMonths | MonthType[] | | | firstDayOfWeek | FirstDayOfWeek | | | focusedDate | Date \| null | | | hoveredDate | Date \| null | | | isDateBlocked | (date: Date) => boolean | | | isDateFocused | (date: Date) => boolean | | | isDateHovered | (date: Date) => boolean | | | isDateSelected | (date: Date) => boolean | | | isEndDate | (date: Date) => boolean | | | isStartDate | (date: Date) => boolean | | | numberOfMonths | number | | | onDateFocus | (date: Date) => void | | | onDateHover | (date: Date \| null) => void | | | onDateSelect | (date: Date) => void | | | onResetDates | () => void | |

useDecade()

UseDecadeProps

| name | type | required | default | description | |-------------------|--------------------------------------|----------|---------|-------------| | decadeLabelFormat | (start: Date, end: Date) => string | | | | | year | number | yes | | | | yearLabelFormat | 'date: Date) => string | | | |

UseDecadeReturnType

| name | type | description | |---------------|-------------------------------------------|-------------| | years | { yearLabel : string; date: Date }[] | | | decadeLabel | string | |

useYear()

UseYearProps

| name | type | required | default | description | |------------------|--------------------------|----------|---------|-------------| | year | number | yes | | | | yearLabelFormat | (date: Date) => string | | | | | monthLabelFormat | (date: Date) => string | | | |

UseYearReturnType

| name | type | description | |---------------|--------------------------------------------|-------------| | months | { monthLabel : string; date: Date }[] | | | yearLabel | string | |

useMonth()

UseMonthProps

| name | type | required | default | description | |--------------------|--------------------------|----------|---------|-------------| | year | number | yes | | | | month | number | yes | | | | firstDayOfWeek | FirstDayOfWeek | | 0 | | | dayLabelFormat | (date: Date) => string | | | | | weekdayLabelFormat | (date: Date) => string | | | | | monthLabelFormat | (date: Date) => string | | | |

UseMonthReturnType

| name | type | description | |---------------|--------------------------------------------------------------|-------------| | days | {currentMonth: boolean; dayLabel: string; date: Date })[] | | | monthLabel | string | | | weekdayLabels | string[] | |

useDay()

UseDayProps

| name | type | required | default | description | |------------------|---------------------------|----------|---------|-------------| | date | Date | yes | | | | dayRef | RefObject | | | | | focusedDate | Date \| null | yes | | | | isDateBlocked | (date: Date) => boolean | yes | | | | isDateFocused | (date: Date) => boolean | yes | | | | isDateHovered | (date: Date) => boolean | yes | | | | isDateSelected | (date: Date) => boolean | yes | | | | onDateFocus | (date: Date) => void | yes | | | | onDateHover | (date: Date) => void | yes | | | | onDateSelect | (date: Date) => void | yes | | | | unavailableDates | Date[] | | | @deprecated | | datesConfig | DatesConfig[] | | | |

UseDayReturnType

| name | type | description | |--------------------|------------------------------|-------------| | disabledDate | boolean | | | isFirstDisabled | boolean | | | isLastDisabled | boolean | | | disabledDate | boolean | | | isHovered | boolean | | | isSelected | boolean | | | isToday | boolean | | | isWithinHoverRange | boolean | | | onClick | () => void | | | onKeyDown | (e: KeyboardEvent) => void | | | onMouseEnter | () => void | | | tabIndex | number | | | flags | Flag[] | |

Known issues

  • keyboard navigation doesn't work for year and decade view
  • the demo needs a little more love!