visit
The solution to all of these 👆 problems could be a headless-UI component like @rehookify/datepicker
Main features and pros:toLocaleDateString
and `toLocaleTimeString`
The main drawback is that you need to style everything by yourself. But, with modern CSS
layout techniques like grid
and flex
, it is an easy and fun thing to do. It means no drawbacks at all 😅
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={`${month}-${dpDay.day}`}>
<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={`${month}-${dpDay.day}`}>
<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 {
data: { weekDays, calendars },
propGetters: {
dayButton,
previousMonthButton,
nextMonthButton,
},
} = useDatePicker({
selectedDates,
onDatesChange,
});
// 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);
}
// selectedDates is an array of dates
// formatted with date.toLocaleDateString(locale, options)
return (
<section>
{selectedDates.length > 0 && <h1>{selectedDates[0]}</h1>}
<header>
<div>
<button {...previousMonthButton()}><</button>
<p>{month} {year}</p>
<button {...nextMonthButton()}>></button>
</div>
<ul>
{weekDays.map((day) => (
<li key={`${month}-${day}`}>{day}</li>
))}
</ul>
</header>
<ul>
{days.map((dpDay) => (
<li key={`${month}-${dpDay.day}`}>
<button
{...dayButton(dpDay, { onClick: onDayClick })}
>
{dpDay.day}
</button>
</li>
))}
</ul>
</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>
);
}