diff --git a/README.md b/README.md index 569c983..b67e9b4 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Example with property Range enabled | `untilDate` | `Moment` | set a date to where the user can select to | | `months` | `number` | the number of months you want to show default: 1 | | `onChange` | `function` | pass a custom function to handle the changes detected in the calendar : (fromDate, untilDate) => console.log(fromDate, untilDate) | +| `displayRanges` | `array` | pass an array of arrays. Each of those arrays are a range, consisting of a from date and until date. These only display ## Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. @@ -46,4 +47,4 @@ Pull requests are welcome. For major changes, please open an issue first to disc Please make sure to update tests as appropriate. ## License -[GPL-3.0-or-later](https://choosealicense.com/licenses/gpl-3.0/) \ No newline at end of file +[GPL-3.0-or-later](https://choosealicense.com/licenses/gpl-3.0/) diff --git a/src/DateTimeRangePicker/DatePicker/DatePicker.scss b/src/DateTimeRangePicker/DatePicker/DatePicker.scss index f2ab0a7..4653c85 100644 --- a/src/DateTimeRangePicker/DatePicker/DatePicker.scss +++ b/src/DateTimeRangePicker/DatePicker/DatePicker.scss @@ -2,6 +2,7 @@ $color-highlight: #428be3; $color-background-day: rgba(black, .1); $color-other-month: rgba(black, .4); $color-disabled: rgba(black, .4); +$color-already-selected: rgba(red, .4); .date-time-range-picker .date-time-range-picker-dates{ position: relative; @@ -93,6 +94,31 @@ $color-disabled: rgba(black, .4); color: white; } + &.already-selected { + background-color: $color-already-selected; + margin: .3em 0; + padding: 0 .3em; + border-radius: 0; + + &.active-from-date:not(.active-from-date-reverse) { + border-top-left-radius: 1.5em; + border-bottom-left-radius: 1.5em; + margin-left: .3em; + padding-left: 0; + } + + &.active-until-date { + border-top-right-radius: 1.5em; + border-bottom-right-radius: 1.5em; + margin-right: .3em; + padding-right: 0; + } + + &.active { + background-color: $color-already-selected; + } + } + &.in-range { background-color: rgba($color-highlight, .5); margin: .3em 0; diff --git a/src/DateTimeRangePicker/DatePicker/DatePicker.tsx b/src/DateTimeRangePicker/DatePicker/DatePicker.tsx index a140ffc..9e5ca7b 100644 --- a/src/DateTimeRangePicker/DatePicker/DatePicker.tsx +++ b/src/DateTimeRangePicker/DatePicker/DatePicker.tsx @@ -9,11 +9,13 @@ import Header from './Header/Header' const DatePicker: React.FunctionComponent = ( { range = false, + displayRanges, fromDate, untilDate, months = 1, onFromDateChanged, - onUntilDateChanged + onUntilDateChanged, + isDisabled = false } ) => { const [currentDate, setCurrentDate] = React.useState(fromDate ? fromDate.clone() : moment()) @@ -46,11 +48,14 @@ const DatePicker: React.FunctionComponent = ( hoverDate={hoverDate} onDaySelected={ (selectedDate: Moment) => { - if (!fromDate || (fromDate && untilDate)) { - onFromDateChanged(selectedDate.clone()) - onUntilDateChanged(range ? undefined : selectedDate.clone()) + if (isDisabled) { return } + if (!fromDate || (fromDate && untilDate)) { + onFromDateChanged(selectedDate.clone()) + onUntilDateChanged(range ? undefined : selectedDate.clone()) + return + } if (selectedDate.isBefore(fromDate)) { onFromDateChanged(selectedDate.clone()) @@ -64,9 +69,13 @@ const DatePicker: React.FunctionComponent = ( } onDayHover={ (onHoverDate: Moment | undefined) => { + if (isDisabled) { + return + } setHoverDate(onHoverDate) } } + displayRanges={displayRanges} /> ) } diff --git a/src/DateTimeRangePicker/DatePicker/IProps.ts b/src/DateTimeRangePicker/DatePicker/IProps.ts index 54c5e2d..58cb569 100644 --- a/src/DateTimeRangePicker/DatePicker/IProps.ts +++ b/src/DateTimeRangePicker/DatePicker/IProps.ts @@ -6,7 +6,9 @@ interface IProps { untilDate?: moment.Moment, months: number, onFromDateChanged: (date: Moment | undefined) => void - onUntilDateChanged: (date: Moment | undefined) => void + onUntilDateChanged: (date: Moment | undefined) => void, + displayRanges: [], + isDisabled?: boolean } export default IProps diff --git a/src/DateTimeRangePicker/DatePicker/Month/IProps.ts b/src/DateTimeRangePicker/DatePicker/Month/IProps.ts index d6a747e..411ccd2 100644 --- a/src/DateTimeRangePicker/DatePicker/Month/IProps.ts +++ b/src/DateTimeRangePicker/DatePicker/Month/IProps.ts @@ -10,6 +10,7 @@ interface IProps { showDaysNextMonth: boolean, onDaySelected: (date: Moment) => void, onDayHover: (date?: Moment | undefined) => void + displayRanges: [] } export default IProps diff --git a/src/DateTimeRangePicker/DatePicker/Month/Month.tsx b/src/DateTimeRangePicker/DatePicker/Month/Month.tsx index 7841053..24925f9 100644 --- a/src/DateTimeRangePicker/DatePicker/Month/Month.tsx +++ b/src/DateTimeRangePicker/DatePicker/Month/Month.tsx @@ -5,12 +5,13 @@ import IProps from './IProps' const Month: React.FunctionComponent = ( { + displayRanges, month, year, fromDate, untilDate, hoverDate, - showDaysPreviousMonth , + showDaysPreviousMonth, showDaysNextMonth, onDaySelected, onDayHover @@ -29,12 +30,29 @@ const Month: React.FunctionComponent = ( return dates.map((buttonDate: Moment) => { const classNames = [] - if (! buttonDate.isSame(date, 'month')) { - if (buttonDate.isBefore(date) && ! showDaysPreviousMonth) { + + if (displayRanges.length > 0) { + displayRanges.forEach((displayRange: Moment[]) => { + displayRange.forEach((displayRangeDate: Moment) => { + if (displayRangeDate.isSame(buttonDate, 'day')) { + classNames.push('already-selected') + if (displayRange[0].isSame(displayRangeDate)) { + classNames.push('active-from-date') + + } else if (displayRange[displayRange.length - 1].isSame(displayRangeDate)) { + classNames.push('active-until-date') + } + } + }) + }) + } + + if (!buttonDate.isSame(date, 'month')) { + if (buttonDate.isBefore(date) && !showDaysPreviousMonth) { return } - if (buttonDate.isAfter(date) && ! showDaysNextMonth) { + if (buttonDate.isAfter(date) && !showDaysNextMonth) { return } @@ -61,7 +79,7 @@ const Month: React.FunctionComponent = ( if ( fromDate - && ! untilDate + && !untilDate && hoverDate && ( buttonDate.isBetween(fromDate, hoverDate, 'day', '[]') @@ -71,7 +89,7 @@ const Month: React.FunctionComponent = ( classNames.push('hover-range') } - if (hoverDate && buttonDate.isSame(hoverDate, 'day')) { + if (hoverDate && buttonDate.isSame(hoverDate, 'day')) { classNames.push('hover') if (fromDate && !untilDate) { diff --git a/src/DateTimeRangePicker/DatePicker/Month/__tests__/Month.test.tsx b/src/DateTimeRangePicker/DatePicker/Month/__tests__/Month.test.tsx index 5c5406c..da9f4c5 100644 --- a/src/DateTimeRangePicker/DatePicker/Month/__tests__/Month.test.tsx +++ b/src/DateTimeRangePicker/DatePicker/Month/__tests__/Month.test.tsx @@ -14,6 +14,7 @@ describe('Date/time range picker month', () => { showDaysNextMonth={true} onDaySelected={() => undefined} onDayHover={() => undefined} + displayRanges={[]} /> ) @@ -31,6 +32,7 @@ describe('Date/time range picker month', () => { showDaysNextMonth={true} onDaySelected={onDaySelectedMock} onDayHover={() => undefined} + displayRanges={[]} /> ) @@ -50,6 +52,7 @@ describe('Date/time range picker month', () => { showDaysNextMonth={true} onDaySelected={() => undefined} onDayHover={onHoverMock} + displayRanges={[]} /> ) @@ -69,6 +72,7 @@ describe('Date/time range picker month', () => { hoverDate={moment('2020-05-10')} onDaySelected={() => undefined} onDayHover={() => undefined} + displayRanges={[]} /> ) @@ -86,6 +90,7 @@ describe('Date/time range picker month', () => { untilDate={moment('2020-05-15')} onDaySelected={() => undefined} onDayHover={() => undefined} + displayRanges={[]} /> ) diff --git a/src/DateTimeRangePicker/DateTimeRangePicker.tsx b/src/DateTimeRangePicker/DateTimeRangePicker.tsx index 8677036..43bec1a 100644 --- a/src/DateTimeRangePicker/DateTimeRangePicker.tsx +++ b/src/DateTimeRangePicker/DateTimeRangePicker.tsx @@ -1,5 +1,5 @@ import React, {useEffect, useState} from 'react' -import moment, {Moment} from 'moment' +import {Moment} from 'moment' import './DateTimeRangePicker.scss' import IProps from './IProps' @@ -11,12 +11,14 @@ const DateTimeRangePicker: React.FunctionComponent = ( { date = true, range = false, + displayRanges = [], time = false, inline = true, fromDate, untilDate, months = 1, - onChange + onChange, + isDisabled = false } ) => { const [currentFromDate, setCurrentFromDate] = useState(fromDate ? fromDate.clone() : undefined) @@ -31,23 +33,23 @@ const DateTimeRangePicker: React.FunctionComponent = ( useEffect( () => { - if (! isMounted) { + if (!isMounted) { setIsMounted(true) return } - if (! onChange) { + if (!onChange) { return } const newFromDate = Utils.getDateTime(date, time, currentFromDate, currentFromTime) - if (! newFromDate) { + if (!newFromDate) { return } const newUntilDate = Utils.getDateTime(date, time, currentUntilDate, currentUntilTime) - if (! newUntilDate) { + if (!newUntilDate) { onChange(newFromDate, newFromDate) return } @@ -60,6 +62,7 @@ const DateTimeRangePicker: React.FunctionComponent = ( {date ? = ( setCurrentUntilDate(changedDate) } } + isDisabled={isDisabled} /> : null } @@ -88,11 +92,12 @@ const DateTimeRangePicker: React.FunctionComponent = ( onTimeChanged={ (changedTime: Moment) => { setCurrentFromTime(changedTime) - if (! range) { + if (!range) { setCurrentUntilTime(changedTime) } } } + isDisabled={isDisabled} /> { @@ -107,6 +112,7 @@ const DateTimeRangePicker: React.FunctionComponent = ( setCurrentUntilTime(changedTime) } } + isDisabled={isDisabled} /> : null diff --git a/src/DateTimeRangePicker/IProps.ts b/src/DateTimeRangePicker/IProps.ts index 2dbd86e..129aaa3 100644 --- a/src/DateTimeRangePicker/IProps.ts +++ b/src/DateTimeRangePicker/IProps.ts @@ -9,6 +9,8 @@ interface IProps { untilDate?: Moment, months?: number, onChange?: (fromDateTime: Moment, untilDateTime?: Moment) => void + displayRanges?: [], + isDisabled?: boolean } export default IProps diff --git a/src/DateTimeRangePicker/TimePicker/IProps.ts b/src/DateTimeRangePicker/TimePicker/IProps.ts index fb01c62..c55f890 100644 --- a/src/DateTimeRangePicker/TimePicker/IProps.ts +++ b/src/DateTimeRangePicker/TimePicker/IProps.ts @@ -3,7 +3,8 @@ import {Moment} from 'moment' interface IProps { time?: Moment, step: number, - onTimeChanged: (time: Moment) => void + onTimeChanged: (time: Moment) => void, + isDisabled?: boolean } export default IProps diff --git a/src/DateTimeRangePicker/TimePicker/TimePicker.tsx b/src/DateTimeRangePicker/TimePicker/TimePicker.tsx index 09c38ec..3837415 100644 --- a/src/DateTimeRangePicker/TimePicker/TimePicker.tsx +++ b/src/DateTimeRangePicker/TimePicker/TimePicker.tsx @@ -5,7 +5,7 @@ import moment from 'moment' import IProps from './IProps' import './TimePicker.scss' -const TimePicker: FunctionComponent = ({ step = 15, onTimeChanged, time}) => { +const TimePicker: FunctionComponent = ({step = 15, onTimeChanged, time, isDisabled}) => { const [currentValue, setCurrentValue] = useState(time ? time.format('HH:mm') : undefined) const [isOpen, setIsOpen] = useState(false) const dropdownRef = useRef() @@ -13,10 +13,14 @@ const TimePicker: FunctionComponent = ({ step = 15, onTimeChanged, time} let inputRef: HTMLInputElement const onChange = (e: SyntheticEvent) => { + if (isDisabled) { + return + } + setCurrentValue(e.currentTarget.value) const changedTime = moment(e.currentTarget.value, 'HH:mm') - if (! changedTime.isValid()) { + if (!changedTime.isValid()) { return } @@ -30,6 +34,10 @@ const TimePicker: FunctionComponent = ({ step = 15, onTimeChanged, time} } const onBlur = (e: SyntheticEvent) => { + if (isDisabled) { + return + } + setCurrentValue(e.currentTarget.value) setTimeout( () => setIsOpen(false), @@ -37,7 +45,7 @@ const TimePicker: FunctionComponent = ({ step = 15, onTimeChanged, time} ) const changedTime = moment(e.currentTarget.value, 'HH:mm') - if (! changedTime.isValid()) { + if (!changedTime.isValid()) { return } @@ -48,6 +56,10 @@ const TimePicker: FunctionComponent = ({ step = 15, onTimeChanged, time} } const onSelect = (timeValue: string) => { + if (isDisabled) { + return + } + setCurrentValue(timeValue) const changedTime = moment(timeValue, 'HH:mm') @@ -64,7 +76,7 @@ const TimePicker: FunctionComponent = ({ step = 15, onTimeChanged, time} const isActive = (timeValue: string): boolean => { const currentTime = moment(currentValue, 'HH:mm') - if (! currentTime.isValid()) { + if (!currentTime.isValid()) { return false } @@ -73,37 +85,44 @@ const TimePicker: FunctionComponent = ({ step = 15, onTimeChanged, time} return currentTime.format('HH:mm') === timeValue } - return
- setIsOpen(true)} - onBlur={onBlur} - value={currentValue} - className={'time-picker-input'} - onKeyDown={ - (e: KeyboardEvent | any) => { - if (e.key === 'Enter' && inputRef) { - inputRef.blur() - } - } - } - htmlRef={(element: HTMLInputElement) => inputRef = element} - /> -
+ return ( +
{ - timeValues.map((timeValue, index) => { - return
onSelect(timeValue)} - > - {timeValue} + <> + setIsOpen(true)} + onBlur={onBlur} + value={currentValue} + className={'time-picker-input'} + onKeyDown={ + (e: KeyboardEvent | any) => { + if (e.key === 'Enter' && inputRef) { + inputRef.blur() + } + } + } + htmlRef={(element: HTMLInputElement) => inputRef = element} + disabled={isDisabled} + /> +
+ { + timeValues.map((timeValue, index) => { + return
onSelect(timeValue)} + > + {timeValue} +
+ }) + }
- }) + }
-
+ ) } export default TimePicker diff --git a/src/DateTimeRangePicker/TimePicker/__tests__/TimePicker.test.tsx b/src/DateTimeRangePicker/TimePicker/__tests__/TimePicker.test.tsx index 42c384d..cd2ec83 100644 --- a/src/DateTimeRangePicker/TimePicker/__tests__/TimePicker.test.tsx +++ b/src/DateTimeRangePicker/TimePicker/__tests__/TimePicker.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { shallow } from 'enzyme' +import {shallow} from 'enzyme' import moment from 'moment' import TimePicker from '../TimePicker' @@ -18,7 +18,7 @@ describe('Date/time range picker month', () => { const onTimeChangedMock = jest.fn() const component = shallow( - + ) component.find('.time-picker-dropdown div').at(8).simulate('click') @@ -29,13 +29,13 @@ describe('Date/time range picker month', () => { const onTimeChangedMock = jest.fn() const component = shallow( - + ) component.find('.time-picker-input').simulate('focus') expect(component.find('.open').length).toEqual(1) - component.find('.time-picker-input').simulate('blur', {currentTarget: { value: '14:40' }}) + component.find('.time-picker-input').simulate('blur', {currentTarget: {value: '14:40'}}) expect(onTimeChangedMock).toBeCalledTimes(1) expect(onTimeChangedMock.mock.calls[0][0].format('HH:mm')).toEqual('14:40') diff --git a/src/DateTimeRangePicker/__tests__/DateTimeRangePicker.test.tsx b/src/DateTimeRangePicker/__tests__/DateTimeRangePicker.test.tsx index 6b4c3d2..8d2c4d2 100644 --- a/src/DateTimeRangePicker/__tests__/DateTimeRangePicker.test.tsx +++ b/src/DateTimeRangePicker/__tests__/DateTimeRangePicker.test.tsx @@ -14,6 +14,7 @@ describe('DateTimeRangePicker', () => { time={false} inline={true} months={2} + displayRanges={[]} /> ) diff --git a/src/DateTimeRangePicker/__tests__/__snapshots__/DateTimeRangePicker.test.tsx.snap b/src/DateTimeRangePicker/__tests__/__snapshots__/DateTimeRangePicker.test.tsx.snap deleted file mode 100644 index bd7974f..0000000 --- a/src/DateTimeRangePicker/__tests__/__snapshots__/DateTimeRangePicker.test.tsx.snap +++ /dev/null @@ -1,16 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DateTimeRangePicker should render correctly 1`] = ` -
- -
-`; diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..6b6499c --- /dev/null +++ b/src/index.html @@ -0,0 +1,10 @@ + + + + + $Title$ + + +$END$ + +