Dayzed
Primitives to build simple, flexible, WAI-ARIA compliant React date-picker components.
Install / Use
/learn @deseretdigital/DayzedREADME
dayzed
Primitives to build simple, flexible, WAI-ARIA compliant React date-picker components.
[![version][version-badge]][package] [![MIT License][license-badge]][license]
[![Supports React and Preact][react-badge]][react] [![size][size-badge]][unpkg-dist] [![gzip size][gzip-badge]][unpkg-dist] [![module formats: umd, cjs, and es][module-formats-badge]][unpkg-dist]
The problem
You need a date-picker in your application that is accessible, can fit a number of use cases (single date, multi date, range), and has styling and layout that can be easily extended.
This solution
This is a component that focuses on controlling user interactions so you can focus on creating beautiful, accessible, and useful date-pickers. It uses a custom [Hook][react-hooks] or a [render function as children][render-function-as-children]. This means you are responsible for rendering everything, but props are provided by the Hook or render function, through a pattern called [prop getters][prop-getters], which can be used to help enhance what you are rendering.
This differs from other solutions which render things for their use case and then expose many options to allow for extensibility resulting in a bigger API that is less flexible as well as making the implementation more complicated and harder to contribute to.
Table of Contents
<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->- Installation
- Usage
- Props
- Control Props
- Custom Hook
- Render Prop Function
- Inspiration and Thanks!
- Other Solutions
- Contributors
- LICENSE
Installation
This module is distributed via [npm][npm] which is bundled with [node][node] and
should be installed as one of your project's dependencies:
npm install --save dayzed
Or, you can install this module through the [yarn][yarn] package manager.
yarn add dayzed
This package also depends on
react@>=16.8.0andprop-types. Please make sure you have those installed as well.
Note also this library supports
preact@>=10out of the box. If you are usingpreactthen use the corresponding module in thepreact/distfolder. You can evenimport Dayzed from 'dayzed/preact'orimport { useDayzed } from 'dayzed/preact'
Usage
import React from 'react';
import Dayzed, { useDayzed } from 'dayzed';
const monthNamesShort = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
];
const weekdayNamesShort = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
function Calendar({ calendars, getBackProps, getForwardProps, getDateProps }) {
if (calendars.length) {
return (
<div style={{ maxWidth: 800, margin: '0 auto', textAlign: 'center' }}>
<div>
<button {...getBackProps({ calendars })}>Back</button>
<button {...getForwardProps({ calendars })}>Next</button>
</div>
{calendars.map(calendar => (
<div
key={`${calendar.month}${calendar.year}`}
style={{
display: 'inline-block',
width: '50%',
padding: '0 10px 30px',
boxSizing: 'border-box'
}}
>
<div>
{monthNamesShort[calendar.month]} {calendar.year}
</div>
{weekdayNamesShort.map(weekday => (
<div
key={`${calendar.month}${calendar.year}${weekday}`}
style={{
display: 'inline-block',
width: 'calc(100% / 7)',
border: 'none',
background: 'transparent'
}}
>
{weekday}
</div>
))}
{calendar.weeks.map((week, weekIndex) =>
week.map((dateObj, index) => {
let key = `${calendar.month}${calendar.year}${weekIndex}${index}`;
if (!dateObj) {
return (
<div
key={key}
style={{
display: 'inline-block',
width: 'calc(100% / 7)',
border: 'none',
background: 'transparent'
}}
/>
);
}
let { date, selected, selectable, today } = dateObj;
let background = today ? 'cornflowerblue' : '';
background = selected ? 'purple' : background;
background = !selectable ? 'teal' : background;
return (
<button
style={{
display: 'inline-block',
width: 'calc(100% / 7)',
border: 'none',
background
}}
key={key}
{...getDateProps({ dateObj })}
>
{selectable ? date.getDate() : 'X'}
</button>
);
})
)}
</div>
))}
</div>
);
}
return null;
}
/*----------- Render Prop -----------*/
class Datepicker extends React.Component {
render() {
return (
<Dayzed
onDateSelected={this.props.onDateSelected}
selected={this.props.selected}
render={dayzedData => <Calendar {...dayzedData} />}
/>
);
}
}
///////////////////////////////////////
// OR
///////////////////////////////////////
/*----------- Custom Hook -----------*/
function Datepicker(props) {
let dayzedData = useDayzed(props);
return <Calendar {...dayzedData} />;
}
class Single extends React.Component {
state = { selectedDate: null };
_handleOnDateSelected = ({ selected, selectable, date }) => {
this.setState(state => ({ selectedDate: date }));
};
render() {
let { selectedDate } = this.state;
return (
<div>
<Datepicker
selected={this.state.selectedDate}
onDateSelected={this._handleOnDateSelected}
/>
{this.state.selectedDate && (
<div style={{ paddingTop: 20, textAlign: 'center' }}>
<p>Selected:</p>
<p>{`${selectedDate.toLocaleDateString()}`}</p>
</div>
)}
</div>
);
}
}
export default Single;
Props
date
date| defaults tonew Date()
Used to calculate what month to display on initial render.
maxDate
date| optional
Used to calculate the maximum month to render.
minDate
date| optional
Used to calculate the minimum month to render.
monthsToDisplay
number| defaults to1
Number of months returned, based off the date and offset props.
firstDayOfWeek
number| defaults to0
First day of the week with possible values 0-6 (Sunday to Saturday). Defaults to Sunday.
showOutsideDays
boolean| defaults to false
Flag to fill front and back weeks with dates from adjacent months.
selected
any| optional
An array of Dates or a single Date that has been selected.
onDateSelected
function(selectedDate: Date, event: Event)| required
Called when the user selects a date.
selectedDate: The date that was just selected.event: The event fired when the date was selected.
render
function({})| required
This is called with an object. Read more about the properties of this object in the section "Render Prop Function".
offset
number| control prop (read more about this in the "Control Props" section below) - defaults to0if not controlled.
Number off months to offset from the date prop.
onOffsetChanged
function(offset: number)| control prop (read more about this in the "Control Props" section below)
Called when the user selects to go forward or back. This function is
required if offset is being provided as a prop.
offset: The number of months offset.
Control Props
dayzed manages its own offset state internally and calls your
onOffsetChanged handler when the offset changes. Your render prop function
(read more below) can be used to manipulate this state from within the render
function and can likely support many of your use cases.
However, if more control is needed, you can pass offset as a prop (as
indicated above) and that state becomes controlled. As soon as
this.props.offset !== undefined, internally, dayzed will determine its state
based on your prop's value rather than its own internal state. You will be
required to keep the state up to date (this is where the onOffsetChanged
handler comes in really handy), but you can also control the state from
anywhere, be that state from other components, redux, react-router, or
anywhere else.
Note: This is very similar to how normal controlled components work elsewhere in react (like
<input />). If you want to learn more about this concept, you can learn about that from this the ["Controlled Components" lecture][controlled-components-lecture]
Custom Hook
You can either use the cus
