Okami
Primitives React Components to build a Calendar

Project status
Okami is used in production, we consider evolving its API, but it works as expected!
Introduction
It's a set of Primitives React Components to build a Calendar. Handle the logic so you can focus on UI/UX. Similar to downshift or selectless.
We use composition to construct the calendar, which simply means:
- A monthly calendar is composed of weekly calendar.
- A weekly calendar is composed of daily calendar.
This allow to have a lot fo flexibility without repeating the logic and avoid complexity.
We strongly recommend to use okami with date-fns.
Install
yarn add okami
npm install --save okami
Basic Usage
For more examples, look at the storybook :)
import React from 'react'
import frLocale from 'date-fns/locale/fr'
import { Calendar, DailyCalendar, Navigation, HoursLabels } from 'okami'
import data from './stories/data'
const Container = props =>
<div style={{ display: 'flex', alignItems: 'stretch', justifyContent: 'center'}} {...props} />
const CalendarContainer = props =>
<div style={{ display: 'flex', alignItems: 'stretch', justifyContent: 'flex-start', width: '100%' }} {...props} />
export default props => (
<Calendar
data={data}
startingDay="monday"
dateFormat="ddd DD/MM"
hourFormat="HH"
startHour="PT3H"
endHour="PT22H"
locale={frLocale}
>
<DailyCalendar showNow {...props}>
{({calendar, dayEvents, rowHeight}) => (
<div style={{display: 'flex', flexDirection:'column'}}>
<div style={{display:'flex'}}>
<Navigation dateFormat="ddd DD MMMM">
{({next, prev, today, currentDate}) => (
<div style={{display:'flex'}}>
<button onClick={today}>Today</button>
<button onClick={prev}>Prev day</button>
<button onClick={next}>Next day</button>
<span>{currentDate}</span>
</div>
)}
</Navigation>
</div>
<Container>
<div style={{paddingTop: rowHeight * (dayEvents.length + 1)}}>
<HoursLabels renderChild={props => <span style={{height: rowHeight}} {...props} />} />
</div>
<CalendarContainer style={{flexDirection: 'column'}}>
{dayEvents.map(props => <Event {...props} />)}
{calendar.events.map(props => <Event {...props} />)}
</CalendarContainer>
</Container>
</div>
)}
</DailyCalendar>
</Calendar>
)
Documentation
Props Calendar
data | array | yes | - | List of events |
startHour | string | no | PT0H | Hour to start the calendar |
endHour | string | no | PT24H | Hour to end the calendar |
dateFormat | string | no | MM/DD/YYYY | Format of the date string |
hourFormat | string | no | HH:mm | Format of the hour string |
startingDay | string | no | sunday | Starting day of the week |
locale | object | no | en | Local from date-fns |
rowHeight | number | no | 30 | Height of a row |
To define property that will be use as duration like startHours or endHour.
We use ISO-8601 format for durations.
Every prop passed to Calendar can be overwritten as the sub component level.
<Calendar
data={data}
startingDay="monday"
dateFormat="ddd DD/MM"
hourFormat="HH"
startHour="PT3H"
endHour="PT22H"
>
<MonthlyCalendar>{...}</MonthlyCalendar>
<DailyCalendar dateFormat="DD-MM-YYYY">{...}</DailyCalendar>
</Calendar>
This will use the dateFormat from <Calendar> in <MonthlyCalendar> but <DailyCalendar> will use his custom dateFormat when it renders.
Daily Calendar
Render a day.
Props
start | Date | new Date() | Day to show |
showNow | boolean | false | Send props to show the time of day |
Child Callback Function
calendar | Object | events, label and date of the day |
start | Date | date of the day |
showNowProps | Object | if showNow is true, props to the showNow container (postion) |
hours | Array | Array of formatted string for hours labels |
rowHeight | Number | Height of the row |
dayEvents | Array | Array of the events that last more than a day of the all day |
calendar
dayEvents
key | event.id |
event | Object |
style | Object |
The style object is passed only if the getColumnProp is called and we have a ref available.
nextDay | Go to next day |
prevDay | Go to previous day |
gotoToday | Go to today |
dateLabel | get the date formatted |
getColumnProps | get the props for the container of the events |
dateLabel
Allow you to render the current day with a special format.
- dateFormat: use the convention from
date-fns format
getColumnProps
Allow to get the ref to your column element for calculation of the events position.
Weekly Calendar
Render a week.
Props
start | Date | new Date() | Day of the week to show |
showNow | boolean | false | Send props to show the time of day |
Child Callback Function
calendar | Array | list of day for the week |
end | Date | end of the week |
start | Date | start of the week |
showNowProps | Object | if showNow is true, props to the showNow container (postion) |
hours | Array | Array of formatted string for hours labels |
rowHeight | Number | Height of the row |
weekEvents | Array | Array of the events that last more than a day of the all week |
weekEvents
key | event.id |
event | Object |
style | Object |
The style object is passed only if the getContainerProps is called and we have a ref available.
nextWeek | Go to next week |
prevWeek | Go to previous week |
gotoToday | Go to today |
dateLabel | get the date formatted |
getContainerProps | get the props for the container of the events |
dateLabel
Allow you to render the current week with a special format.
- dateFormat: use the convention from
date-fns format
getContainerProps
Allow to get the ref to your column element for calculation of the events position.
Monthly Calendar
Render a month.
Props
start | Date | new Date() | Date of the curent month |
Child Callback Function
calendar | Array | List of first day of the week for the month |
end | Date | end of the month |
start | Date | start of the month |
weeks | Array | List of the first day of week for the month |
rowHeight | Number | Height of the row |
nextMonth | Go to next month |
prevMonth | Go to previous month |
gotoToday | Go to today |
dateLabel | get the date formatted |
dateLabel
Allow you to render the current month with a special format.
- dateFormat: use the convention from
date-fns format
Navigation
render the navigation for the calendar and the current label. The navigation and label are based on the type.
If you use this in a DailyCalendar, next() will jump to the next day but if you use it in MonthlyCalendar, it will jump to the next month.
You also get access to the function to toggle weekends on the calendar.
If you force showWeekend on a sub component, don't forget to update manually this props.
Props
dateFormat | string | format of the date |
Child Callback Function
type | string | Type of calendar ('monthly', 'weekly', 'daily') |
currentDate | string | Formatted date based on type |
next | Go to next based on type |
prev | Go to previous based on type |
today | Go to today |
toggleWeekend | show/hide weekend on calendar |
toggleWeekend
Let you toggle the weekend in the calendar.
If you don't pass a pamarater, it will toggle the prop. If you pass a boolean, it will force the value.
const showWeekend = true
toggleWeekend()
toggleWeekend(true)
toggleWeekend(true)
toggleWeekend(false)
[...]
DaysLabels / HoursLabels
render the days/hours labels for the calendar.
You have 3 options to render those components:
- Child Callback function : allow full control
renderChild props: pass a component that receive pre formatted props
- The default render that let you style the
div container.
Props
Child Callback Function
DaysLabels
weeks | array | Array of the formatted day labels |
HoursLabels
hours | array | Array of the formatted hour labels |
<HoursLabels>
{ hours => hours.map(h => <span>{h}</span>} }
</HoursLabels>
renderChild prop
props from the HoursLabels/DaysLabels are passed down to the container and the labels are rendered with the component from renderChild.
<HoursLabels renderChild={props => <span {...props} />} />
The default render function
props from the HoursLabels/DaysLabels are passed down to the container.
<HoursLabels />
This is the render function used :
<div {...props}>
{formattedDays.map((h, idx) => (
<div key={`day_label_${idx}`} style={{height: rowHeight}}>
{h}
</div>
))}
</div>
Data structure
data is an array of objects, the object requires a few properties :
id | ID | Identifier of the event |
title | string | Title of the event |
allDay | `boolean | Date` |
start | Date | Beggining of the event |
end | Date | End of the event |
allDay has 3 states :
true, the event is on multiple days
false, the event is no longer than a day
Date, the event last all day
if allDay is a boolean, you need to provide a start and end properties.
If you want to create an event for the all day, you just put the date in allDay.
{
id: 0,
title: 'Diner',
allDay: false,
start : 'Wed Sep 06 2017 12:07:52 GMT+0200 (CEST)',
end: 'Wed Sep 06 2017 14:07:52 GMT+0200 (CEST)'
}
{
id: 0,
title: 'Diner',
allDay: 'Wed Sep 06 2017 12:07:52 GMT+0200 (CEST)'
}
{
id: 0,
title: 'Diner',
allDay: true,
start : 'Wed Sep 06 2017 12:07:52 GMT+0200 (CEST)',
end: 'Wed Sep 08 2017 14:07:52 GMT+0200 (CEST)'
}
LICENSE
MIT