diff --git a/src/Calendar.js b/src/Calendar.js
index 3150f534..4ad9184c 100644
--- a/src/Calendar.js
+++ b/src/Calendar.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { useCallback, useState } from 'react'
import { bool, func, instanceOf, object, objectOf, string } from 'prop-types'
import { startOfMonth } from 'date-fns'
import { isSelectable, mergeModifiers } from './utils'
@@ -6,6 +6,8 @@ import useControllableState from './useControllableState'
import CalendarNavigation from './CalendarNavigation'
import CalendarWeekHeader from './CalendarWeekHeader'
import CalendarGrid from './CalendarGrid'
+import Popover from './Popover'
+import MonthPicker from './MonthPicker'
export default function Calendar({
locale,
@@ -18,15 +20,20 @@ export default function Calendar({
onDayHover,
onDayClick,
weekdayFormat,
- touchDragEnabled
+ touchDragEnabled,
+ monthModifiers,
+ monthModifiersClassNames
}) {
const [month, setMonth] = useControllableState(receivedMonth, onMonthChange, startOfMonth(new Date()))
+ const [show, setShow] = useState(true)
const modifiers = mergeModifiers(
{ disabled: date => !isSelectable(date, { minimumDate, maximumDate }) },
receivedModifiers
)
+ const handleToggle = useCallback(() => setShow(state => !state), [])
+
return (
-
+
+
+
+
{},
onClick: () => {}
}
diff --git a/src/CalendarMonth.js b/src/CalendarMonth.js
new file mode 100644
index 00000000..00e26932
--- /dev/null
+++ b/src/CalendarMonth.js
@@ -0,0 +1,84 @@
+import React from 'react'
+import { bool, instanceOf, func, number, object, objectOf, string } from 'prop-types'
+import { getMonth, format, getYear } from 'date-fns'
+import classNames from 'classnames'
+
+const defaultModifiersClassNames = {
+ today: '-today',
+ outside: '-outside',
+ wide: '-wide',
+ disabled: '-disabled',
+ selected: '-selected',
+ selectedStart: '-selected-start',
+ selectedMiddle: '-selected-middle',
+ selectedEnd: '-selected-end'
+}
+
+const isSameMonth = (date, actualDate) => {
+ return (getMonth(date) === getMonth(actualDate) && getYear(date) === getYear(actualDate))
+}
+
+export default function CalendarMonth({
+ date,
+ height,
+ locale,
+ modifiers: receivedModifiers,
+ modifiersClassNames: receivedModifiersClassNames,
+ onClick,
+ onHover,
+ showGrid,
+ actualDate
+}) {
+ const monthClassNames = {}
+ const modifiers = { today: isSameMonth(date, actualDate), ...receivedModifiers }
+ const modifiersClassNames = { ...defaultModifiersClassNames, ...receivedModifiersClassNames }
+
+ Object.keys(modifiers).forEach(name => {
+ monthClassNames[modifiersClassNames[name]] = modifiers[name]
+ })
+
+ const handleClick = event => {
+ onClick(date)
+ showGrid()
+ event.preventDefault()
+ }
+
+ const handleMouseEnter = () => {
+ onHover(date)
+ }
+
+ const handleMouseLeave = () => {
+ onHover(null)
+ }
+
+ return (
+
+ {format(date, 'LLLL', { locale })}
+
+ )
+}
+
+CalendarMonth.propTypes = {
+ date: instanceOf(Date).isRequired,
+ height: number.isRequired,
+ locale: object.isRequired,
+ modifiers: objectOf(bool),
+ modifiersClassNames: objectOf(string),
+ onHover: func,
+ onClick: func,
+ showGrid: func,
+ actualDate: instanceOf(Date)
+}
+
+CalendarMonth.defaultProps = {
+ modifiers: {},
+ onHover: () => { },
+ onClick: () => { }
+}
diff --git a/src/CalendarNavigation.js b/src/CalendarNavigation.js
index c077641c..0617384c 100644
--- a/src/CalendarNavigation.js
+++ b/src/CalendarNavigation.js
@@ -1,9 +1,9 @@
import React from 'react'
-import { func, instanceOf, object } from 'prop-types'
+import { bool, func, instanceOf, object } from 'prop-types'
import classNames from 'classnames'
-import { addMonths, getYear, startOfMonth, subMonths, format, isSameMonth } from 'date-fns'
+import { startOfMonth, format, isSameMonth, subMonths, addMonths } from 'date-fns'
-export default function CalendarNavigation({ locale, month, minimumDate, maximumDate, onMonthChange }) {
+export default function CalendarNavigation({ locale, month, minimumDate, maximumDate, onMonthChange, showMonthPicker, show }) {
const handlePrevious = event => {
onMonthChange(startOfMonth(subMonths(month, 1)))
event.preventDefault()
@@ -18,19 +18,22 @@ export default function CalendarNavigation({ locale, month, minimumDate, maximum
-
- {format(month, getYear(month) === getYear(new Date()) ? 'LLLL' : 'LLLL yyyy', { locale })}
+
+ {show
+ ? format(month, 'LLLL yyyy', { locale })
+ : format(month, 'yyyy', { locale })
+ }
@@ -95,7 +99,9 @@ DatePicker.propTypes = {
modifiers: objectOf(func),
modifiersClassNames: objectOf(string),
weekdayFormat: string,
- touchDragEnabled: bool
+ touchDragEnabled: bool,
+ monthModifiers: objectOf(func),
+ monthModifiersClassNames: objectOf(string)
}
DatePicker.defaultProps = {
diff --git a/src/DatePickerCalendar.js b/src/DatePickerCalendar.js
index aec742ae..6c6a9252 100644
--- a/src/DatePickerCalendar.js
+++ b/src/DatePickerCalendar.js
@@ -16,7 +16,9 @@ export default function DatePickerCalendar({
modifiers: receivedModifiers,
modifiersClassNames,
weekdayFormat,
- touchDragEnabled
+ touchDragEnabled,
+ monthModifiers,
+ monthModifiersClassNames
}) {
const isSelected = date => isSameDay(date, selectedDate) && isSelectable(date, { minimumDate, maximumDate })
const modifiers = mergeModifiers({ selected: isSelected, disabled: isSelected }, receivedModifiers)
@@ -38,6 +40,8 @@ export default function DatePickerCalendar({
modifiersClassNames={modifiersClassNames}
weekdayFormat={weekdayFormat}
touchDragEnabled={touchDragEnabled}
+ monthModifiers={monthModifiers}
+ monthModifiersClassNames={monthModifiersClassNames}
/>
)
}
@@ -53,5 +57,7 @@ DatePickerCalendar.propTypes = {
modifiers: objectOf(func),
modifiersClassNames: objectOf(string),
weekdayFormat: string,
- touchDragEnabled: bool
+ touchDragEnabled: bool,
+ monthModifiers: objectOf(func),
+ monthModifiersClassNames: objectOf(string)
}
diff --git a/src/MonthPicker.js b/src/MonthPicker.js
new file mode 100644
index 00000000..040c210e
--- /dev/null
+++ b/src/MonthPicker.js
@@ -0,0 +1,101 @@
+import React, { useEffect, useState } from 'react'
+import { bool, instanceOf, func, number, object, objectOf, string } from 'prop-types'
+import { lightFormat, eachMonthOfInterval, getYear } from 'date-fns'
+import CalendarMonth from './CalendarMonth'
+import useGrid from './useGrid'
+import getMonth from 'date-fns/getMonth'
+import classNames from 'classnames'
+import { ORIGIN_BOTTOM, ORIGIN_TOP } from './constants'
+
+const computeModifiers = (modifiers, date) => {
+ const computedModifiers = {}
+
+ Object.keys(modifiers).map(key => {
+ computedModifiers[key] = modifiers[key](date)
+ })
+
+ return computedModifiers
+}
+
+export default function MonthPicker({
+ locale,
+ modifiers,
+ actualDate,
+ modifiersClassNames,
+ onDayHover,
+ onClick,
+ transitionDuration,
+ touchDragEnabled,
+ showGrid
+}) {
+ const grid = useGrid({ locale, month: getMonth(actualDate), onMonthChange: onClick, transitionDuration, touchDragEnabled })
+ const { cellHeight, offset, containerElementRef, isWide, origin, transition } = grid
+ const [months, setMonths] = useState([])
+
+ useEffect(() => {
+ const allMonths = eachMonthOfInterval({
+ start: new Date(getYear(actualDate), 0, 1),
+ end: new Date(getYear(actualDate), 11, 1)
+ }).map(date => {
+ return (
+
+ )
+ })
+
+ setMonths(allMonths)
+ }, [actualDate, cellHeight, locale, modifiers, modifiersClassNames, onClick, onDayHover, showGrid, isWide])
+
+ return (
+ <>
+
+ >
+ )
+}
+
+MonthPicker.propTypes = {
+ locale: object.isRequired,
+ actualDate: instanceOf(Date).isRequired,
+ modifiers: objectOf(func),
+ modifiersClassNames: objectOf(string),
+ onDayHover: func,
+ transitionDuration: number.isRequired,
+ touchDragEnabled: bool,
+ onClick: func,
+ showGrid: func
+}
+
+MonthPicker.defaultProps = {
+ modifiers: {},
+ transitionDuration: 800,
+ touchDragEnabled: true
+}
diff --git a/src/style.scss b/src/style.scss
index 89928137..9ab954a1 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -22,6 +22,10 @@ $nice-dates-cell-width: calc(100% / 7);
&_current {
flex-grow: 1;
font-size: $nice-dates-font-size-big;
+ cursor: pointer;
+ &:hover {
+ font-weight: bolder;
+ }
}
&_previous, &_next {
@@ -103,6 +107,10 @@ $nice-dates-cell-width: calc(100% / 7);
transition: 300ms color;
}
+ &.-moving .nice-dates-month {
+ transition: 300ms color;
+ }
+
&.-origin-bottom {
top: auto;
bottom: 0;
@@ -119,6 +127,93 @@ $nice-dates-cell-width: calc(100% / 7);
}
}
+ &-month {
+ border-top: 1px solid transparent;
+ box-sizing: border-box;
+ color: $nice-dates-color-gray-dark;
+ cursor: pointer;
+ font-size: $nice-dates-font-size-big;
+ position: relative;
+ text-align: center;
+ margin-top: 20px;
+ width: calc(100% / 3);
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ transition: 150ms color;
+ will-change: color;
+ z-index: 3;
+
+ &:before, &:after {
+ border-radius: 999px;
+ bottom: 0;
+ box-sizing: border-box;
+ content: '';
+ display: block;
+ left: 0;
+ opacity: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+ }
+
+ &:before {
+ background-color: $nice-dates-color-accent;
+ z-index: 1;
+ }
+
+ &:after {
+ border: 2px solid $nice-dates-color-accent;
+ transform: scale(.95);
+ transition-duration: 150ms;
+ transition-property: transform, opacity;
+ z-index: 2;
+ }
+
+ &:hover {
+ &:after {
+ opacity: 1;
+ transform: scale(1);
+ }
+ }
+
+ &:not(.-disabled):hover {
+ &:after {
+ opacity: 1;
+ transform: scale(1);
+ }
+ }
+
+ &.-today {
+ font-weight: bolder;
+ }
+
+ &.-wide {
+ &:before, &:after {
+ left: 12.5%;
+ right: 12.5%;
+ }
+ }
+
+ @media (hover: none) {
+ &:after {
+ content: none;
+ }
+
+ &.-selected * {
+ color: #fff;
+ }
+ }
+
+ &.-disabled {
+ cursor: default;
+ pointer-events: none;
+ color: $nice-dates-color-gray-light;
+ }
+
+ }
+
&-day {
border-top: 1px solid transparent;
box-sizing: border-box;
@@ -271,8 +366,8 @@ $nice-dates-cell-width: calc(100% / 7);
&-popover {
background-color: #fff;
border-radius: 8px;
- box-shadow: 0 1px 8px rgba(#000, .12);
margin: 8px 0;
+ box-shadow: 0 1px 8px rgba(#000, .12);
max-width: 600px;
position: absolute;
transform-origin: top;
diff --git a/src/useGrid.js b/src/useGrid.js
index cf2b7bd7..f1af1c84 100644
--- a/src/useGrid.js
+++ b/src/useGrid.js
@@ -107,7 +107,7 @@ export default function useGrid({ locale, month: currentMonth, onMonthChange, tr
containerElement.classList.add('-transition')
clearTimeout(timeoutRef.current)
- if (Math.abs(differenceInCalendarMonths(currentMonth, lastCurrentMonth)) <= 3) {
+ if (Math.abs(differenceInCalendarMonths(currentMonth, lastCurrentMonth)) <= 12) {
dispatch({ type: 'transitionToCurrentMonth', currentMonth })
timeoutRef.current = setTimeout(() => {