Skip to content

Commit

Permalink
feat: enhance #date-picker key navigation to act more smart
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Jun 3, 2019
1 parent e7c47bb commit fa0a3b0
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export default class DatePickerCalendar extends PureComponent {
prevBtn={prevBtn}
onPrev={onPrev}
locale={locale}
onKeyDown={this.onKeyDownHandler}
/>
</div>
<label
Expand All @@ -206,6 +207,7 @@ export default class DatePickerCalendar extends PureComponent {
nextBtn={nextBtn}
onNext={onNext}
locale={locale}
onKeyDown={this.onKeyDownHandler}
/>
</div>
</div>
Expand Down Expand Up @@ -318,7 +320,15 @@ export default class DatePickerCalendar extends PureComponent {
}
}

const PrevButton = ({ nr, minDate, month, prevBtn, onPrev, locale }) => {
const PrevButton = ({
nr,
minDate,
month,
prevBtn,
onPrev,
locale,
onKeyDown
}) => {
if (!prevBtn) {
return <></>
}
Expand All @@ -335,6 +345,7 @@ const PrevButton = ({ nr, minDate, month, prevBtn, onPrev, locale }) => {
aria-label={title}
title={title}
onClick={onClick}
onKeyDown={onKeyDown}
/>
)
}
Expand All @@ -344,13 +355,23 @@ PrevButton.propTypes = {
month: PropTypes.object.isRequired,
locale: PropTypes.object.isRequired,
prevBtn: PropTypes.bool.isRequired,
onPrev: PropTypes.func.isRequired
onPrev: PropTypes.func.isRequired,
onKeyDown: PropTypes.func
}
PrevButton.defaultProps = {
minDate: null
minDate: null,
onKeyDown: null
}

const NextButton = ({ nr, maxDate, month, nextBtn, onNext, locale }) => {
const NextButton = ({
nr,
maxDate,
month,
nextBtn,
onNext,
locale,
onKeyDown
}) => {
if (!nextBtn) {
return <></>
}
Expand All @@ -368,6 +389,7 @@ const NextButton = ({ nr, maxDate, month, nextBtn, onNext, locale }) => {
aria-label={title}
title={title}
onClick={onClick}
onKeyDown={onKeyDown}
/>
)
)
Expand All @@ -378,10 +400,12 @@ NextButton.propTypes = {
month: PropTypes.object.isRequired,
locale: PropTypes.object.isRequired,
nextBtn: PropTypes.bool.isRequired,
onNext: PropTypes.func.isRequired
onNext: PropTypes.func.isRequired,
onKeyDown: PropTypes.func
}
NextButton.defaultProps = {
maxDate: null
maxDate: null,
onKeyDown: null
}

const onSelectRange = ({
Expand Down
147 changes: 107 additions & 40 deletions packages/dnb-ui-lib/src/components/date-picker/DatePickerRange.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import {
addDays,
addWeeks,
addMonths,
isSameMonth
isSameMonth,
isSameYear,
setDate,
lastDayOfMonth,
differenceInMonths
} from 'date-fns'
import DatePickerCalendar from './DatePickerCalendar'

Expand Down Expand Up @@ -68,14 +72,12 @@ export default class DatePickerRange extends PureComponent {
props.sync &&
((props.startDate &&
state.startDate &&
(props.startDate.getMonth() !== state.startDate.getMonth() ||
props.startDate.getFullYear() !==
state.startDate.getFullYear())) ||
(!isSameMonth(props.startDate, state.startDate) ||
!isSameYear(props.startDate, state.startDate))) ||
(props.endDate &&
state.endDate &&
(props.endDate.getMonth() !== state.endDate.getMonth() ||
props.endDate.getFullYear() !==
state.endDate.getFullYear())))
(!isSameMonth(props.endDate, state.endDate) ||
!isSameYear(props.endDate, state.endDate))))
) {
state.views = DatePickerRange.getViews(props)
}
Expand All @@ -85,6 +87,15 @@ export default class DatePickerRange extends PureComponent {
if (props.endDate) {
state.endDate = props.endDate
}
if (props.month) {
state.startMonth = props.month
}
if (props.startMonth) {
state.startMonth = props.startMonth
}
if (props.endMonth) {
state.endMonth = props.endMonth
}
}
state._listenForPropChanges = true
return state
Expand All @@ -94,42 +105,44 @@ export default class DatePickerRange extends PureComponent {
views: null,
startDate: null,
endDate: null,
startMonth: null,
endMonth: null,
_listenForPropChanges: true
}

constructor(props) {
super(props)
this.state.views = DatePickerRange.getViews(props)
this.state.views = DatePickerRange.getViews(props, props.range)
}

static getViews(props) {
static getViews(state, isRange) {
// fill the views with the calendar data getMonth()
return (Array.isArray(props.views)
? props.views
return (Array.isArray(state.views)
? state.views
: Array(
props.range
isRange
? 2 // set default range calendars
: props.views
: state.views
).fill(1)
).map((view, i) => ({
...view,
month: DatePickerRange.getMonth(i, props),
month: DatePickerRange.getMonth(i, state),
nr: i
}))
}

static getMonth(viewCount, props) {
if ((props.startMonth || props.startDate) && viewCount === 0) {
return props.startMonth || props.startDate
static getMonth(viewCount, state) {
if ((state.startMonth || state.startDate) && viewCount === 0) {
return state.startMonth || state.startDate
}
if ((props.endMonth || props.endDate) && viewCount === 1) {
return props.endMonth || props.endDate
if ((state.endMonth || state.endDate) && viewCount === 1) {
return state.endMonth || state.endDate
}
return addMonths(DatePickerRange.getFallbackMonth(props), viewCount)
return addMonths(DatePickerRange.getFallbackMonth(state), viewCount)
}

static getFallbackMonth(props) {
return props.month || props.startMonth || props.startDate || new Date()
static getFallbackMonth(state) {
return state.startMonth || state.startDate || new Date()
}

callOnChange(opts = {}) {
Expand Down Expand Up @@ -163,9 +176,15 @@ export default class DatePickerRange extends PureComponent {

onNext = ({ nr }) => {
const views = this.state.views.map(c => {
return this.props.link || c.nr === nr
? { ...c, month: addMonths(c.month, 1) }
: c
if (c.nr === nr) {
const month = addMonths(c.month, 1)
this.setState({
[`${nr === 0 ? 'start' : 'end'}Month`]: month,
_listenForPropChanges: false
})
return this.props.link || { ...c, month }
}
return this.props.link || c
})
this.setState({ views, _listenForPropChanges: false }, () => {
this.callOnNav()
Expand All @@ -174,9 +193,15 @@ export default class DatePickerRange extends PureComponent {

onPrev = ({ nr }) => {
const views = this.state.views.map(c => {
return this.props.link || c.nr === nr
? { ...c, month: subMonths(c.month, 1) }
: c
if (c.nr === nr) {
const month = subMonths(c.month, 1)
this.setState({
[`${nr === 0 ? 'start' : 'end'}Month`]: month,
_listenForPropChanges: false
})
return this.props.link || { ...c, month }
}
return this.props.link || c
})
this.setState({ views, _listenForPropChanges: false }, () => {
this.callOnNav()
Expand All @@ -197,6 +222,8 @@ export default class DatePickerRange extends PureComponent {
case 'down':
event.preventDefault()
break
default:
return
}

let type = nr === 0 ? 'start' : 'end'
Expand All @@ -221,24 +248,58 @@ export default class DatePickerRange extends PureComponent {
newDate = addWeeks(newDate, 1)
break
}
}

if (!newDate) {
} else {
// use the date picker month, if provided
newDate =
nr === 0
? this.props.month || this.props.startMonth || new Date()
: this.props.endMonth || addMonths(new Date(), 1)
this.state[`${type}Month`] ||
(this.props.range && nr === 1
? addMonths(new Date(), 1)
: new Date())
}

if (newDate !== this.state[`${type}Date`]) {
const state = {
[`${type}Date`]: newDate,
_listenForPropChanges: false
const state = { _listenForPropChanges: false }

const currentMonth = this.state[`${type}Month`]

if (
// in case we dont have a start/end date, then we use the current month date
(currentMonth && !this.state[`${type}Date`]) ||
// if we have a larger gap between the new date and the curent month in the calendar
(currentMonth &&
Math.abs(differenceInMonths(newDate, currentMonth)) > 1)
) {
if (!this.props.range) {
newDate = currentMonth
} else {
newDate =
nr === 0
? setDate(currentMonth, 1)
: lastDayOfMonth(currentMonth)
}
// if (nr === 1) {
// newDate = addMonths(newDate, 1)
// }
// only to make sure we navigate the calendar to the new date
} else if (
currentMonth &&
!isSameMonth(this.state[`${type}Date`], currentMonth)
) {
state[`${type}Month`] = newDate
}

state[`${type}Date`] = newDate

// set fallbacks
if (!this.props.range) {
state.endDate = newDate
} else if (this.props.range && nr === 0 && !this.state.endDate) {
state.endDate = addMonths(newDate, 1)
} else {
if (!this.state.startDate) {
state.startDate = newDate
}
if (!this.state.endDate) {
state.endDate = newDate
}
}

// make sure we stay on the same month
Expand All @@ -251,9 +312,15 @@ export default class DatePickerRange extends PureComponent {
}
}

// make sure we also navigate the view
if (this.props.sync) {
state.views = DatePickerRange.getViews({ ...this.props, ...state })
state.views = this.state.views
state.views[nr] = DatePickerRange.getViews(
{ ...this.state, ...state },
this.props.range
)[nr]
}

this.setState(state)
setTimeout(() => {
this.callOnChange()
Expand Down

0 comments on commit fa0a3b0

Please sign in to comment.