Security News
npm Updates Search Experience with New Objective Sorting Options
npm has a revamped search experience with new, more transparent sorting optionsβRelevance, Downloads, Dependents, and Publish Date.
@rehookify/datepicker
Advanced tools
The ultimate tool to create a date, range and time picker in your React applications.
The ultimate tiny tool for creating date, range and time pickers in your React applications.
We have war at our home πΊπ¦
Help us in our struggle, π° United24, KOLO, Come Back Alive
.toLocaleString
, .toLocalTimeString
npm i -S @rehookify/datepicker
import { useState } from 'react';
import { useDatePickerState, useCalendars } from '@rehookify/datepicker';
const DatePicker = () => {
const [selectedDates, onDatesChange] = useState<Date[]>([]);
const dpState = useDatePickerState({
selectedDates,
onDatesChange,
dates: { toggle: true, mode: 'multiple' },
});
const { calendars, weekDays } = useCalendars(dpState);
const { month, year, days } = calendars[0];
return (
<section>
<header>
<div>
<p>{month} {year}</p>
</div>
<ul>
{weekDays.map((day) => (
<li key={`${month}-${day}`}>{day}</li>
))}
</ul>
</header>
<ul>
{days.map((dpDay) => (
<li key={dpDay.$date.toDateString()}>
<button>{dpDay.day}</button>
</li>
))}
</ul>
</section>
);
}
import { useState } from 'react';
import {
DatePickerStateProvider,
useContextCalendars,
useContextDaysPropGetters,
useContextTime,
useContextTimePropGetters,
} from '@rehookify/datepicker';
const DatePicker = () => {
const { calendars, weekDays } = useContextCalendars();
const { dayButton } = useContextDaysPropGetters();
const { year, month, days } = calendars[0];
return (
<main>
<header>
<div>
<p>{month} {year}</p>
</div>
<ul>
{weekDays.map((day) => (
<li key={`${month}-${day}`}>{day}</li>
))}
</ul>
</header>
<ul>
{days.map((dpDay) => (
<li key={dpDay.$date.toDateString()}>
<button {...dayButton(dpDay)}>{dpDay.day}</button>
</li>
))}
</ul>
</main>
)
}
const TimePicker = () => {
const { time } = useContextTime();
const { timeButton } = useContextTimePropGetters();
return (
<ul>
{time.map((t) => (
<li key={t.$date.toString()}>
<button {...timeButton(t)}>{t.time}</>
</li>
))}
</ul>
)
}
const App = () => {
const d = new Date();
const [selectedDates, onDatesChange] = useState<Date[]>([d]);
return (
<DatePickerStateProvider
config={{
selectedDates,
focusDate: d,
onDatesChange,
dates: { mode: 'multiple' },
}}
>
<section>
<DatePicker />
<TimePicker />
</section>
</DatePickerStateProvider>
);
}
import { MouseEvent, useState } from 'react';
import { useDatePicker } from '@rehookify/datepicker';
const DatePicker = () => {
const [selectedDates, onDatesChange] = useState<Date[]>([]);
const [offsetDate, onOffsetChange] = useState<Date>(new Date());
const {
data: { weekDays, calendars },
propGetters: {
dayButton,
addOffset,
subtractOffset,
},
} = useDatePicker({
selectedDates,
onDatesChange,
// we want to manipulate with offsetDate outside of the hook
offsetDate,
onOffsetChange,
});
// calendars[0] is always present, this is an initial calendar
const { year, month, days } = calendars[0];
const onDayClick = (evt: MouseEvent<HTMLElement>, date: Date) => {
// In case you need any action with evt
evt.stopPropagation();
// In case you need any additional action with date
console.log(date);
}
const moveOffsetToNewYear = () => {
onOffsetChange(new Date(2024, 0, 1));
}
// selectedDates is an array of dates
// formatted with date.toLocaleDateString(locale, options)
return (
<section>
{selectedDates.length > 0 && <h1>{selectedDates[0]}</h1>}
<header>
<div>
<button {...subtractOffset({ months: 1 })}><</button>
<p>{month} {year}</p>
<button {...appOffset({ months: 1 })}>></button>
</div>
<ul>
{weekDays.map((day) => (
<li key={`${month}-${day}`}>{day}</li>
))}
</ul>
</header>
<ul>
{days.map((dpDay) => (
<li key={dpDay.$date.toDateString()}>
<button
{...dayButton(dpDay, { onClick: onDayClick })}
>
{dpDay.day}
</button>
</li>
))}
</ul>
<button onClick={moveOffsetToNewYear}>New Year</button>
</section>
)
}
import { useState } from 'react';
import {
DatePickerProvider,
useDatePickerContext,
} from '@rehookify/datepicker';
const DatePicker = () => {
const {
data: { weekDays, calendars, years, months },
} = useDatePickerContext();
const { year, month, days } = calendars[0];
return (
<section>
<header>{month} {year}</header>
...
</section>
)
}
const App = () => {
const [selectedDates, onDatesChange] = useState<Date[]>([]);
return (
<DatePickerProvider
config={{
selectedDates,
onDatesChange,
dates: { mode: 'range' },
}}
>
<DatePicker />
</DatePickerProvider>
);
}
The state consists of two main parts: data and propGetters.
The data represents all entities that you could use in your date picker. It consists of calendars, weekDays, months, years, selectedDates and time
interface DPData {
calendars: Calendar[];
formattedDates: Date[];
months: CalendarMonth[];
selectedDates: Date[];
time: Time[];
weekDays: string[];
years: CalendarYears[];
}
calendars
are an array of objects with year, month and days properties. It always has at least one member - an initial calendar calendars[0]
. For calendars configuration π Calendar config
export type DPDayRange =
| 'in-range'
| 'range-start'
| 'range-end'
| 'range-start range-end'
| 'will-be-in-range'
| 'will-be-range-start'
| 'will-be-range-end'
| '';
interface DPDay {
$date: Date;
day: string;
disabled: boolean;
inCurrentMonth: boolean;
now: boolean;
range: DPDayRange;
selected: boolean;
}
interface DPCalendar {
days: DPDay[];
month: string;
year: string;
}
Weekdays are an array of day names [Mon
, Tue
, Wed
, ...]. The name format can be changed by locale.weekdays
property π Locale configuration
type DPWeekdays = string[]
Months are an array of objects with $date, active, disabled, month, now and selected properties. The month name format could be changed by locale.monthName
property π Locale configuration.
interface DPMonth {
$date: Date;
active: boolean;
disabled: boolean;
month: string;
now: boolean;
selected: boolean;
}
active
- shows that a user sees this month as current.
month
- month name e.g 'December'
now
- shows that this month is current in real life
selected
- shaws that we have a date selected for this month.
Years are an array of objects with $date, active, disabled, now, selected and year properties.
interface DPYear {
$date: Date;
active: boolean;
disabled: boolean;
now: boolean;
selected: boolean;
year: number;
}
active
- shows that a user sees this year as current.
now
- shows that this year is current in real life
selected
- shows that we have a date selected for this year.
year
- year value e.g 2023
An array of raw dates
type SelectedDates = Date[];
An array of formatted dates date.toLocaleDateString(locale, options)
π Locale configuration
type FormattedDates = string[];
Time is an array of objects with $date, disabled, now, selected and value properties. You can change time format with hour12
, hour
and minute
options π Locale configuration
export interface DPTime {
$date: Date;
disabled: boolean;
selected: boolean;
time: string;
}
time
- time value e.g 15:30
or 3:30 pm
A prop-getters is a pattern that allows you to get all the necessary pops and logic for your components. It gives you the possibility to pass additional configuration. @rehookify/datepicker
composes onClick
and calls it with event and date - onClick(event, date)
.
Each prop-getter accepts a configuration object to enhance the properties and functionality of the component.
export interface DPPropsGetterConfig extends Record<string, unknown> {
onClick?(evt?: MouseEvent<HTMLElement>, date?: Date): void;
disabled?: boolean;
}
Each prop-getter returns an object with properties:
export interface DPPropGetter extends Record<string, unknown> {
role: 'button';
tabIndex: number;
disabled?: boolean;
'aria-disabled'?: boolean;
'aria-selected'?: boolean;
onClick?(evt: MouseEvent<HTMLElement>): void;
}
dayButton
produces properties for calendar days and sets the selectedDates
state when a user clicks on a day.
Params:
day: DPDay
- you could get it from the calendars π #Calendarsprops?: DPPropsGetterConfig
Returns:
type DayButton = (day: DPDay, config?: DPPropsGetterConfig) => DPPropGetter;
βοΈ NOTE: onMouseMove
- appears only if dates mode is range
, it is not composable. π Dates configuration
monthButton
produces properties for calendar months and changes month when a user clicks on a month.
Returns:
type MonthButton = (month: DPMonth, config?: DPPropsGetterConfig) => DPPropGetter;
Params:
month: DPMonth
- you could get it from the months π Monthsprops?: DPMonthsPropGettersConfig
yearButton
produces properties for calendar years and changes the year when user clicks on a year.
Params:
year: DPYear
- you could get it from the years π Yearsprops?: DPPropsGetterConfig
Returns:
type YearButton = (year: DPYear, config?: DPPropsGetterConfig) => DPPropGetter;
nextYearsButton
moves years pagination one step forward.
Params:
props?: DPPropsGetterConfig
βοΈ NOTE: onClick
- callback function doesn't get date
as a second parameter.
previousYearsButton
moves years pagination one step backward.
Params:
props?: DPPropsGetterConfig
βοΈ NOTE: onClick
- callback function doesn't get date
as a second parameter.
timeButton
produces properties for time button and changes corresponding selectedDate
and focusDate
.
Params:
time: DPTime
- you could get it from the years π Timeprops?: DPPropsGetterConfig
βοΈ NOTE: onClick
- callback function doesn't get date
as a second parameter.
type SetOffset = (date: Date, config?: DPPropsGetterConfig) => DPPropGetter;
setOffset
moves offset to passed date if it is after than minDate and before maxDate.
Params:
date: Date
- JS date objectprops?: DPPropsGetterConfig
interface DPOffsetValue {
days?: number;
months?: number;
years?: number;
}
type AddOffset = (
offsetValue: DPOffsetValue,
config?: DPPropsGetterConfig,
) => DPPropGetter;
addOffset
- moves current offsetDate forward on the number of days, months and years.
Params:
offsetValue: DPOffsetValue
- JS object with number of days, months and yearsprops?: DPPropsGetterConfig
type SubtractOffset = (
offsetValue: DPOffsetValue,
config?: DPPropsGetterConfig,
) => DPPropGetter;
subtractOffset
- moves current offsetDate backward on the number of days, months and years.
Params:
offsetValue: DPOffsetValue
- JS object with number of days, months and yearsprops?: DPPropsGetterConfig
useDatePicker
, DatePickerProvider
, useDatePickerState
and DatePickerStateProvider
accepts same configuration object that consists of locale, calendar, dates and years
{
selectedDates: [],
focusDate: undefined,
onDatesChange: undefined,
dates: {
limit: undefined,
mode: 'single',
minDate: undefined,
maxDate: undefined,
selectedDates: [],
selectSameDate: false,
toggle: false,
},
calendar: {
mode: 'static',
offsets: [0],
},
exclude: {
day: [],
date: [],
},
locale: {
locale: 'en-GB',
day: '2-digit',
year: 'numeric',
weekday: 'short',
monthName: 'long',
hour: '2-digit',
minute: '2-digit',
hour12: undefined,
second: undefined,
},
time: {
interval: 30,
minTime: undefined,
maxTime: undefined,
},
years: {
mode: 'decade',
numberOfYears: 12;
step: 10,
},
}
selectedDates: Date[];
onDatesChange(d: Date[]): void;
focusDate?: Date | undefined;
offsetDate?: Date
onOffsetChange?(d: Date): void;
The date-picker is a controlled component that utilizes the selectedDates
property to create all entities and display the user's selection. If you don't provide a selectedDates
value, it will default to an empty array, but the selection won't be visible. Every time a date is selected, it will be passed to the onDatesChange
function.
A typical setup is to use the useState
hook to handle updates.
const [selectedDates, onDatesChange] = useState<Date[]>([]);
const { data } = useDatePicker({
selectedDates,
onDatesChange,
})
focusDate
is initial value for the time-picker, if it is undefined or not present in the selectedDates
array all time buttons will be disabled.
You can also pass an offsetDate and onOffsetChange function to control the offsetDate. It is really useful when you want to use the date-picker with input, or save offset in the multiple mode, or pair date selection with offset management.
If you will not pass either offsetDate or onOffsetChange, the date-picker will manage offsetDate by itself.
#### Locale configuration
Locale configuration consists of values compatible with `date.toLocaleString()`.
For more information about locale you can reed at [MDN doc](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString).
```ts
interface DPLocaleConfig {
locale?: Intl.LocalesArgument;
options?: Intl.DateTimeFormatOptions;
day?: Intl.DateTimeFormatOptions['day'];
year?: Intl.DateTimeFormatOptions['year'];
monthName?: Intl.DateTimeFormatOptions['month'];
weekday?: Intl.DateTimeFormatOptions['weekday'];
hour: Intl.DateTimeFormatOptions['hour'];
minute: Intl.DateTimeFormatOptions['minute'];
second?: Intl.DateTimeFormatOptions['second'];
hour12?: Intl.DateTimeFormatOptions['hour12'];
}
locale: UnicodeBCP47LocaleIdentifier | Locale | (UnicodeBCP47LocaleIdentifier | Locale)[] | undefined
- used to format all instances, a string with a BCP 47 language tag.options: Intl.DateTimeFormatOptions
it is left undefined to allow you to control how selectedDates
will formatted.day: "2-digit" | "numeric" | undefined
- defines the date's format in Calendarsyear: "numeric" | "2-digit" | undefined
- defines the year's format in YearsmonthName: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined
- defines the moths format in Monthsweekday: "long" | "short" | "narrow" | undefined
- defines weekday's format in Weekdayshour: "numeric" | "2-digit" | undefined
- defines hours format in Timeminute: "numeric" | "2-digit" | undefined
- defines minutes format in Timesecond: "numeric" | "2-digit" | undefined
- defines seconds format in Timehour12: boolean | undefined
- defines time format in general 12:12
or 12:12 pm
interface DPCalendarConfig {
mode?: 'static' | 'fluid';
offsets?: number[];
startDay: DPDayInteger;
}
mode: 'static' | 'fluid'
controls how calendar will look likeCalendars in static
mode have 6 rows by 7 days. This prevents UI from jumping while switching between months and years.
π February 2022 in static
mode:
30 31 01 02 03 04 05
06 07 08 09 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 01 02 03 04 05
06 07 08 09 10 11 12
Calendars in fluid
mode counts start and end offsets.
π February 2022 in fluid
mode:
30 31 01 02 03 04 05
06 07 08 09 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 01 02 03 04 05
offsets: number[]
- adds additional calendars to the Calendars;The first calendar is always [0]
- offsets comes next.
The values of offsets could be negative, -1
, this will add month before current.
offsets: [-1, 1]
gives you 3 calendars November, October, December
(today is November 2022).
startDay
- The day of the week that will be the first in the calendar. It accepts a number in the range of 0-6, where 0 represents Sunday and 6 represents Saturday.
// The days in JS Date object has numbers from 0 - Sun to 6 - Sat
type DPDayInteger = 0 | 1 | 2 | 3 | 4 | 5 | 6;
export interface DPExcludeConfig {
day?: DPDayInteger[];
date?: Date[];
}
day: DPDayInteger
- an array of days number from 0 to 6. If you will specify 0 and 6 all Sundays and Saturdays will be disabled.
date: Date[]
- an array of Dates that will be disabled
interface DPDatesUserConfig {
mode?: 'single' | 'multiple' | 'range';
minDate?: Date;
maxDate?: Date;
selectSameDate?: boolean;
toggle?: boolean;
limit?: number;
}
mode: 'single' | 'multiple' | 'range'
- defines how date picker behaves with dayssingle
- a user can pick only 1 date
multiple
- a user can pick unlimited number of dates until limit
is set
range
- a user can pick one dates range. selectedDates
will have 2 dates
minDate: Date
- all dates in prop-getters before the minDate
will be marked as disabled.βοΈ NOTE: if minDate > NOW
- initial calendar will show the month with minDate
maxDate: Date
- all dates in prop-getters after the maxDate
will be marked as disabled.βοΈ NOTE: if maxDate < NOW
- initial calendar will show the month with maxDate
selectSameDate: boolean
- it allows to select same date in the range
mode.
toggle: boolean
- allows a user to unselect dates.
limit: number
- number of dates that a user could select.
βοΈ NOTE: works only with mode: 'multiple'
export interface DPTimeLimit {
h: number;
m: number;
}
export interface DPTimeConfig {
interval: number;
minTime: DPTimeLimit;
maxTime: DPTimeLimit;
}
interval
- time segments value in minutes for example, interval 30 is 48 segments 2 for each hour
minTime
- all times in prop-getters before the minTime
will be marked as disabled
maxTime
- all times in prop-getters after the maxTime
will be marked as disabled
βοΈ NOTE: config will sort minTime
and maxTime
if both present.
type DPYearsMode = 'decade' | 'fluid';
interface DPYearsConfig {
mode: DPYearsMode,
numberOfYears: number;
step: number;
},
numberOfYears: number
- the number of years you want to show to a user.mode: 'decade' | 'fluid' | 'exact'
- it defines how current year will be centered;βοΈ NOTE: difference between decade
and fluid
mode
Years matrix for decade
mode.
It will count current decade (for 2022 is 2020-2029) and adds end of the previous and start of the next decade
2019 2020 2021
2022 2023 2024
2025 2026 2027
2028 2029 2030
Years matrix for fluid
mode.
It will place current year in the middle of the list -1 (we want to look at the future more) π
2017 2018 2019
2020 2021 2022
2023 2024 2025
2026 2027 2028
Years matrix for exact
mode.
It will place current year at the end of the list
2012 2013 2014
2015 2016 2017
2018 2019 2020
2021 2022 2023
step: number
- it defines step for previous/nextYearsButtonThe main aim of modular hooks is to safe bundle size of your app.
All entities are consists of 2 hooks: data, prop-getters and actions (for example useDays
and useDaysPropGetters
).
export interface DPReducerState {
focusDate?: Date;
rangeEnd: Date | null;
offsetDate: Date;
offsetYear: number;
}
export type DPReducerAction =
| DPSetFocusDate
| DPSetOffsetDate
| DPSetYearAction
| DPSetRangeEndAction;
export interface DPState {
dispatch: Dispatch<DPReducerAction>;
state: DPReducerState;
selectedDates: Date[];
offsetDate: Date;
config: DPConfig;
}
type UseDatePickerState = (config: DPUserConfig): DPState
Under the hook, it uses useReducer
to capture date-picker state and provides dispatch
for state manipulation.
Modular hooks use state and dispatch to derive their entities and update the date-picker.
DatePickerStateProvider
uses this hook and propagates state and dispatch through context.
type DatePickerStateProviderValue = DPState;
type DPUseCalendars = (state: DPState) => {
calendars: DPCalendar[];
weekDays: string[];
};
Basic entities to build UI without interactivity.
export type DPUseDays = (state: DPState) => {
selectedDates: Date[];
formattedDates: string[];
};
Set of data with raw and formatted dates
selectedDates
- π selecteDatesformattedDates
- π formattedDatesexport type DPUseDaysPropGetters = (state: DPState) => {
dayButton: (day: DPDay, config?: DPPropsGetterConfig) => DPPropGetter;
};
Prop-getter for dates selection.
dayButton
- propGetter π dayButtonexport type DPUseMonths = (state: DPState) => {
months: DPMonth[];
};
Months data.
months
- π monthsexport type DPUseMonthsPropGetters = (state: DPState) => {
monthButton: (month: DPMonth, config?: DPPropsGetterConfig) => DPPropGetter;
};
Prop-getters for month manipulation.
monthButton
- propGetter π monthButtonexport type DPUseTime = (state: DPState) => {
time: DPTime[];
};
export type DPUseDatePickerOffsetPropGetters = (state: DPState) => {
addOffset: (
offsetValue: DPOffsetValue,
config?: DPPropsGetterConfig,
) => DPPropGetter;
setOffset: (date: Date) => DPPropGetter;
subtractOffset: (
offsetValue: DPOffsetValue,
config?: DPPropsGetterConfig,
) => DPPropGetter;
};
Prop-getters for offset manipulation.
addOffset
- propGetter π addOffsetsetOffset
- propGetter π setOffsetsubtractOffset
- propGetter π subtractOffsetTime data.
time
- π Timeexport type DPUseTimePropGetter = (state: DPState) => {
timeButton: (time: DPTime, config?: DPPropsGetterConfig) => DPPropGetter;
};
Prop-getters for time manipulation.
timeButton
- propGetter π timeButtonexport type DPUseYears = (state: DPState) => {
years: DPYear[];
};
Years data.
years
- π yearsexport type DPUseYearsPropGetters = (state: DPState) => {
yearButton: (year: DPYear, config?: DPPropsGetterConfig) => DPPropGetter;
nextYearsButton: (config?: DPPropsGetterConfig) => DPPropGetter;
previousYearsButton: (config?: DPPropsGetterConfig) => DPPropGetter;
};
Prop-getters for years manipulation.
yearButton
- propGetter π yearButtonnextYearsButton
- propGetter π nextYearsButtonpreviousYearsButton
- propGetter π previousYearsButtonWe have set of context hooks that have similar API with regular one.
useContextCalendars
- π useCalendarsuseContextDays
- π useDayuseContextDaysPropsGetters
- π useDayPropGettersuseContextMonths
- π useMonthsuseContextMonthsPropGetters
- π useMonthsPropGettersuseContextTime
- π useTimeuseContextTimePropGetters
- π useTimePropGettersuseContextYears
- π useYearsuseContextYearsPropGetters
- π useYearsPropGettersuseContextDatePickerOffsetPropGetters
- π useDatePickerOffsetPropGettersThe main difference that they use context value from the DatePickerStateProvider
. You don't need to pass any parameters to them.
βοΈ NOTE: You can use them only inside DatePickerStateProvider
! π With modular context
FAQs
The ultimate tool to create a date, range and time picker in your React applications.
The npm package @rehookify/datepicker receives a total of 17,494 weekly downloads. As such, @rehookify/datepicker popularity was classified as popular.
We found that @rehookify/datepicker demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
npm has a revamped search experience with new, more transparent sorting optionsβRelevance, Downloads, Dependents, and Publish Date.
Security News
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.