2.0.0 • Published 3 years ago

use-date-input v2.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

use-date-input

React hooks for building date input and datepicker components.

Installation

To use this hook you need to install date-fns.

yarn add date-fns
yarn add use-date-input

or with npm:

npm install date-fns
npm install use-date-input

useDateInput

useDateInput(props: UseDateInputProps): UseDateInput

Hook that manages state of masked date input.

Uses IMask for input masking and date-fns for date formatting and parsing.

Props

UseDateInputProps

NameTypeDescription
dateFormatstringdate-fns date format, see https://date-fns.org/v2.22.1/docs/format
maskBlocks?MaskedDateOptions"blocks"Blocks defining each date part, see https://imask.js.org/guide.html#masked-date for more info
onComplete?(value?: Date) => voidThis will execute when input satisfies given format (mask), usually this function should be used to update date value
value?DateDate value of input

Return value

UseDateInput

NameTypeDescription
inputValuestringValue of input element
onKeyPressKeyboardEventHandlerApply to input element if you wish to autocomplete date when user presses enter key. Parsing is done using dateFns parse with dateFormat truncated to the length of current inputValue and using current date as reference. For example if today is 07/08/2021 and input value is 2, on enter press, input value will become 02/08/2021
refRefObject<HTMLInputElement>Input element ref
resetValueOnDeleteChangeEventHandler<HTMLInputElement>Will call onComplete with undefined value if e.target.value is falsy. Use it to reset value when user deletes the input.
setInputValueDispatch<SetStateAction<string>>Sets inputValue, note that you don't need to set input value on input element change, this is done internally by using IMask's accept event

Usage

// define maskBlocks as constant or memoize in component
const maskBlocks = {
  dd: {
    mask: IMask.MaskedRange,
    from: 1,
    to: 31,
    maxLength: 2,
  },
  MM: {
    mask: IMask.MaskedRange,
    from: 1,
    to: 12,
    maxLength: 2,
  },
  yyyy: {
    mask: IMask.MaskedRange,
    from: 1900,
    to: 9999,
  },
};

const DateInput = () => {
  const [value, setValue] = useState<Date>();
  const { ref, inputValue, resetValueOnDelete } = useDateInput({
    value,
    dateFormat: "dd-MM-yyyy",
    maskBlocks,
    onComplete: setValue,
  });

  return <input ref={ref} value={inputValue} onChange={resetValueOnDelete} />;
};

useCalendar

useCalendar(props: UseCalendarProps): UseCalendar

Manages calendar state.

You can use this hook to render calendar view. Focused date state is used as reference to return calendar days.

Uses date-fns for date manipulation.

Props

UseCalendarProps

NameTypeDescription
maxDate?DateOnly dates before this date will be marked valid For best performance memoize this date, this will prevent recalculation of calendar days on each render.
minDate?DateOnly dates after this date will be marked valid. For best performance memoize this date, this will prevent recalculation of calendar days on each render.
startDate?DateIf provided calendar will start at the month of provided date
validate?(date: Date) => booleanValidation function, dates that fail this test will be marked invalid. For best performance memoize this function, this will prevent recalculation of calendar days on each render.
value?DateDatepicker value
weekStartsOn?0 1 2 3 4 5 6Day that a week starts on. 0 - sunday, 1 - monday ..., default is 0

Return value

UseCalendar

NameTypeDescription
daysUseDatepickerDay[]Returns all dates that can be seen in calendar view along with flags for each date that indicate if it is in the same month as focused date and if it is valid (available for selection). Use this function to render month view of calendar.
focusedDateDateFirst day of month that represents current calendar view
isSelected(date: Date) => booleanCompares given date with value.
monthsDate[]Dates representing each month in the same year as focusedDate. You can use this function render month selection for calendar view.
nextMonth() => voidAdds one month to focusedDate. Use it to move calendar view to next month.
nextYear() => voidAdds one year to focusedDate. Use it to move calendar view to next year.
previousMonth() => voidSubtracts one month from focusedDate. Use it to move calendar view to previous month.
previousYear() => voidAdds one month to focusedDate. Use it to move calendar view to next month.
setFocusedDateDispatch<SetStateAction<Date>>Changes focusedDate, make sure to only pass first day of the month dates. By changing focusedDate you are actually changing the reference point of calendar which means, once focused date is changed, days and months functions will use new focusedDate value as reference point.
years?Date[]Dates representing eachYear from minDate to maxDate. Returns undefined if minDate or maxDate is not defined

UseDatePickerDay

NameTypeDescription
dateDateRepresents calendar day
inMonthbooleanIndicates if this date is in currently viewed calendar month
isValidbooleanIf true this date can be selected according to provided min and max date and validate function

Usage

const Calendar = () => {
  const [value, setValue] = useState<Date>();
  const { previousYear, nextYear, focusedDate, previousMonth, nextMonth, days, isSelected } = useCalendar({ value });

  return (
    <div style={{ width: "420px" }}>
      <div style={{ width: "100%" }}>
        <button onClick={previousYear}>{"<"}</button>
        {format(focusedDate, "yyyy")}
        <button onClick={nextYear}>{">"}</button>
      </div>
      <div style={{ width: "100%" }}>
        <button onClick={previousMonth}>{"<"}</button>
        {format(focusedDate, "MMMM")}
        <button onClick={nextMonth}>{">"}</button>
      </div>
      <div style={{ display: "flex", flexWrap: "wrap" }}>
        {days.map(({ date, inMonth }) => (
          <button
            disabled={!inMonth}
            style={{ width: "60px", backgroundColor: isSelected(date) ? "aliceblue" : "initial" }}
            key={date.toDateString()}
            onClick={() => setValue(date)}>
            {format(date, "d")}
          </button>
        ))}
      </div>
    </div>
  );
};

useDatepicker

useDatepicker<T>(props: UseCalendarProps & UseDropdownProps): UseCalendar & UseDropdown<T>

Combines useCalendar and useDropdown hooks into one.

Props

NameTypeDescription
additionalRefs?RefObject<HTMLElement>[]These refs will be used when determining what constitutes a click outside of dropdown.
disabled?booleanIf true, open and close will do nothing
onClose?() => voidExecuted when close is called
onOpen?() => voidExecuted when open is called
maxDate?DateOnly dates before this date will be marked valid For best performance memoize this date, this will prevent recalculation of calendar days on each render.
minDate?DateOnly dates after this date will be marked valid. For best performance memoize this date, this will prevent recalculation of calendar days on each render.
startDate?DateIf provided calendar will start at the month of provided date
validate?(date: Date) => booleanValidation function, dates that fail this test will be marked invalid. For best performance memoize this function, this will prevent recalculation of calendar days on each render.
value?DateDatepicker value
weekStartsOn?0 1 2 3 4 5 6Day that a week starts on. 0 - sunday, 1 - monday ..., default is 0

Return value

NameTypeDescription
dropdownRefRefObject<T>Ref for dropdown element
isOpenbooleanDropdown state
open() => voidSets isOpen to true
close() => voidSets isOpen to false
daysUseDatepickerDay[]Returns all dates that can be seen in calendar view along with flags for each date that indicate if it is in the same month as focused date and if it is valid (available for selection). Use this function to render month view of calendar.
focusedDateDateFirst day of month that represents current calendar view
isSelected(date: Date) => booleanCompares given date with value.
monthsDate[]Dates representing each month in the same year as focusedDate. You can use this function render month selection for calendar view.
nextMonth() => voidAdds one month to focusedDate. Use it to move calendar view to next month.
nextYear() => voidAdds one year to focusedDate. Use it to move calendar view to next year.
previousMonth() => voidSubtracts one month from focusedDate. Use it to move calendar view to previous month.
previousYear() => voidAdds one month to focusedDate. Use it to move calendar view to next month.
setFocusedDateDispatch<SetStateAction<Date>>Changes focusedDate, make sure to only pass first day of the month dates. By changing focusedDate you are actually changing the reference point of calendar which means, once focused date is changed, days and months functions will use new focusedDate value as reference point.
years?Date[]Dates representing eachYear from minDate to maxDate. Returns undefined if minDate or maxDate is not defined

Usage

const DateInputWithDatepicker = () => {
  const [value, setValue] = useState<Date>();
  const { ref, inputValue, resetValueOnDelete } = useDateInput({
    value,
    dateFormat: "dd-MM-yyyy",
    maskBlocks,
    onComplete: setValue,
  });
  const {
    isOpen,
    open,
    days,
    previousYear,
    nextYear,
    previousMonth,
    nextMonth,
    focusedDate,
    dropdownRef,
    isSelected,
  } = useDatepicker<HTMLDivElement>({
    value,
  });

  return (
    <div ref={dropdownRef}>
      <input ref={ref} value={inputValue} onChange={resetValueOnDelete} onFocus={open} />
      {isOpen && (
        <div style={{ width: "420px" }}>
          <div style={{ width: "100%" }}>
            <button onClick={previousYear}>{"<"}</button>
            {format(focusedDate, "yyyy")}
            <button onClick={nextYear}>{">"}</button>
          </div>
          <div style={{ width: "100%" }}>
            <button onClick={previousMonth}>{"<"}</button>
            {format(focusedDate, "MMMM")}
            <button onClick={nextMonth}>{">"}</button>
          </div>
          <div style={{ display: "flex", flexWrap: "wrap" }}>
            {days.map(({ date, inMonth }) => (
              <button
                disabled={!inMonth}
                style={{ width: "60px", backgroundColor: isSelected(date) ? "aliceblue" : "initial" }}
                key={date.toDateString()}
                onClick={() => setValue(date)}>
                {format(date, "d")}
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};