Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add modifiers and expose styling opportunities for certain user states #1608

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0ae0478
Add after hovered start class
Mar 25, 2019
6f99bb4
Add no-selected-start-before-selected-end and before-hovered-end
Mar 25, 2019
98fd20b
Add selected-start-no-selected-end and selected-end-no-selected-start
Apr 1, 2019
d847a91
Fix broken test
Apr 14, 2019
953adfd
Eslint fixes
Apr 14, 2019
613eb1b
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 Apr 15, 2019
5b4e312
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 May 15, 2019
f25efca
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 May 30, 2019
dffe545
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 Jun 20, 2019
cc85dbe
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 Jul 1, 2019
0f62d1c
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 Sep 4, 2019
84946fe
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 Sep 9, 2019
37039a4
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 Sep 24, 2019
dd63dd6
Merge branch 'master' of github.com:airbnb/react-dates into add-after…
Oct 21, 2019
e80c45e
Fix tests
Oct 21, 2019
f07dbfa
Add tests for modifiers
Oct 21, 2019
8e04474
Merge branch 'master' into add-after-hovered-start-class
krissalvador27 Oct 22, 2019
71dc4d7
Fix tests + linting
Oct 22, 2019
35f50bb
Merge branch 'add-after-hovered-start-class' of github.com:krissalvad…
Oct 22, 2019
b8fab11
Update logic and tests for modifiers
Oct 29, 2019
e4427f3
Remove unused variable
Oct 30, 2019
5a78416
Merge updates from master
Oct 31, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/components/CalendarDay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,17 @@ class CalendarDay extends React.PureComponent {
modifiers.has('blocked-minimum-nights') && styles.CalendarDay__blocked_minimum_nights,
modifiers.has('blocked-calendar') && styles.CalendarDay__blocked_calendar,
hoveredSpan && styles.CalendarDay__hovered_span,
modifiers.has('after-hovered-start') && styles.CalendarDay__after_hovered_start,
modifiers.has('selected-span') && styles.CalendarDay__selected_span,
modifiers.has('selected-start') && styles.CalendarDay__selected_start,
modifiers.has('selected-end') && styles.CalendarDay__selected_end,
selected && !modifiers.has('selected-span') && styles.CalendarDay__selected,
modifiers.has('before-hovered-end') && styles.CalendarDay__before_hovered_end,
modifiers.has('no-selected-start-before-selected-end') && styles.CalendarDay__no_selected_start_before_selected_end,
modifiers.has('selected-start-in-hovered-span') && styles.CalendarDay__selected_start_in_hovered_span,
modifiers.has('selected-end-in-hovered-span') && styles.CalendarDay__selected_end_in_hovered_span,
modifiers.has('selected-start-no-selected-end') && styles.CalendarDay__selected_start_no_selected_end,
modifiers.has('selected-end-no-selected-start') && styles.CalendarDay__selected_end_no_selected_start,
isOutsideRange && styles.CalendarDay__blocked_out_of_range,
daySizeStyles,
)}
Expand Down Expand Up @@ -345,4 +352,11 @@ export default withStyles(({ reactDates: { color, font } }) => ({
CalendarDay__today: {},
CalendarDay__firstDayOfWeek: {},
CalendarDay__lastDayOfWeek: {},
CalendarDay__after_hovered_start: {},
CalendarDay__before_hovered_end: {},
CalendarDay__no_selected_start_before_selected_end: {},
CalendarDay__selected_start_in_hovered_span: {},
CalendarDay__selected_end_in_hovered_span: {},
CalendarDay__selected_start_no_selected_end: {},
CalendarDay__selected_end_no_selected_start: {},
}), { pureComponent: typeof React.PureComponent !== 'undefined' })(CalendarDay);
122 changes: 117 additions & 5 deletions src/components/DayPickerRangeController.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import isNextDay from '../utils/isNextDay';
import isSameDay from '../utils/isSameDay';
import isAfterDay from '../utils/isAfterDay';
import isBeforeDay from '../utils/isBeforeDay';
import isPreviousDay from '../utils/isPreviousDay';

import getVisibleDays from '../utils/getVisibleDays';
import isDayVisible from '../utils/isDayVisible';
Expand Down Expand Up @@ -222,6 +223,11 @@ export default class DayPickerRangeController extends React.PureComponent {
'last-day-of-week': (day) => this.isLastDayOfWeek(day),
'hovered-start-first-possible-end': (day, hoverDate) => this.isFirstPossibleEndDateForHoveredStartDate(day, hoverDate),
'hovered-start-blocked-minimum-nights': (day, hoverDate) => this.doesNotMeetMinNightsForHoveredStartDate(day, hoverDate),
'before-hovered-end': (day) => this.isDayBeforeHoveredEndDate(day),
'no-selected-start-before-selected-end': (day) => this.beforeSelectedEnd(day) && !props.startDate,
'selected-start-in-hovered-span': (day, hoverDate) => this.isStartDate(day) && isAfterDay(hoverDate, day),
'selected-start-no-selected-end': (day) => this.isStartDate(day) && !props.endDate,
'selected-end-no-selected-start': (day) => this.isEndDate(day) && !props.startDate,
};

const { currentMonth, visibleDays } = this.getStateForNewMonth(props);
Expand Down Expand Up @@ -341,12 +347,32 @@ export default class DayPickerRangeController extends React.PureComponent {
const startSpan = prevStartDate.clone().add(1, 'day');
const endSpan = prevStartDate.clone().add(prevMinimumNights + 1, 'days');
modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start');

if (!endDate || !prevEndDate) {
modifiers = this.deleteModifier(modifiers, prevStartDate, 'selected-start-no-selected-end');
}
}

if (!prevStartDate && endDate && startDate) {
modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-no-selected-start');
modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-in-hovered-span');

values(visibleDays).forEach((days) => {
Object.keys(days).forEach((day) => {
const momentObj = moment(day);
modifiers = this.deleteModifier(modifiers, momentObj, 'no-selected-start-before-selected-end');
});
});
}
}

if (didEndDateChange) {
modifiers = this.deleteModifier(modifiers, prevEndDate, 'selected-end');
modifiers = this.addModifier(modifiers, endDate, 'selected-end');

if (prevEndDate && (!startDate || !prevStartDate)) {
modifiers = this.deleteModifier(modifiers, prevEndDate, 'selected-end-no-selected-start');
}
}

if (didStartDateChange || didEndDateChange) {
Expand Down Expand Up @@ -374,6 +400,26 @@ export default class DayPickerRangeController extends React.PureComponent {
'selected-span',
);
}

if (startDate && !endDate) {
modifiers = this.addModifier(modifiers, startDate, 'selected-start-no-selected-end');
}

if (endDate && !startDate) {
modifiers = this.addModifier(modifiers, endDate, 'selected-end-no-selected-start');
}

if (!startDate && endDate) {
values(visibleDays).forEach((days) => {
Object.keys(days).forEach((day) => {
const momentObj = moment(day);

if (isBeforeDay(momentObj, endDate)) {
modifiers = this.addModifier(modifiers, momentObj, 'no-selected-start-before-selected-end');
}
});
});
}
}

if (!this.isTouchDevice && didStartDateChange && startDate && !endDate) {
Expand All @@ -382,6 +428,12 @@ export default class DayPickerRangeController extends React.PureComponent {
modifiers = this.addModifierToRange(modifiers, startSpan, endSpan, 'after-hovered-start');
}

if (!this.isTouchDevice && didEndDateChange && !startDate && endDate) {
const startSpan = endDate.clone().subtract(minimumNights, 'days');
const endSpan = endDate.clone();
modifiers = this.addModifierToRange(modifiers, startSpan, endSpan, 'before-hovered-end');
}

if (prevMinimumNights > 0) {
if (didFocusChange || didStartDateChange || minimumNights !== prevMinimumNights) {
const startSpan = prevStartDate || this.today;
Expand Down Expand Up @@ -653,9 +705,14 @@ export default class DayPickerRangeController extends React.PureComponent {
modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span');
}

if (isBeforeDay(day, startDate) || isSameDay(day, startDate)) {
modifiers = this.deleteModifier(modifiers, startDate, 'selected-start-in-hovered-span');
}

if (!this.isBlocked(day) && isAfterDay(day, startDate)) {
const endSpan = day.clone().add(1, 'day');
modifiers = this.addModifierToRange(modifiers, startDate, endSpan, 'hovered-span');
modifiers = this.addModifier(modifiers, startDate, 'selected-start-in-hovered-span');
}
}

Expand All @@ -664,8 +721,13 @@ export default class DayPickerRangeController extends React.PureComponent {
modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span');
}

if (isAfterDay(day, endDate) || isSameDay(day, endDate)) {
modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-in-hovered-span');
}

if (!this.isBlocked(day) && isBeforeDay(day, endDate)) {
modifiers = this.addModifierToRange(modifiers, day, endDate, 'hovered-span');
modifiers = this.addModifier(modifiers, endDate, 'selected-end-in-hovered-span');
}
}

Expand All @@ -686,6 +748,21 @@ export default class DayPickerRangeController extends React.PureComponent {
}
}

if (endDate) {
const startSpan = endDate.clone().subtract(minimumNights, 'days');
modifiers = this.deleteModifierFromRange(modifiers, startSpan, endDate, 'before-hovered-end');

if (isSameDay(day, endDate)) {
const newStartSpan = endDate.clone().subtract(minimumNights, 'days');
modifiers = this.addModifierToRange(
modifiers,
newStartSpan,
endDate,
'before-hovered-end',
);
}
}

if (hoverDate && !this.isBlocked(hoverDate)) {
const minNightsForPrevHoverDate = getMinNightsForHoverDate(hoverDate);
if (minNightsForPrevHoverDate > 0 && focusedInput === START_DATE) {
Expand Down Expand Up @@ -743,6 +820,7 @@ export default class DayPickerRangeController extends React.PureComponent {
minimumNights,
} = this.props;
const { hoverDate, visibleDays, dateOffset } = this.state;

if (this.isTouchDevice || !hoverDate) return;

let modifiers = {};
Expand All @@ -752,13 +830,25 @@ export default class DayPickerRangeController extends React.PureComponent {
modifiers = this.deleteModifierFromRange(modifiers, dateOffset.start, dateOffset.end, 'hovered-offset');
}

if (startDate && !endDate && isAfterDay(hoverDate, startDate)) {
const endSpan = hoverDate.clone().add(1, 'day');
modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span');
if (startDate && !endDate) {
if (isAfterDay(hoverDate, startDate)) {
const endSpan = hoverDate.clone().add(1, 'day');
modifiers = this.deleteModifierFromRange(modifiers, startDate, endSpan, 'hovered-span');
}

if (isAfterDay(day, startDate)) {
modifiers = this.deleteModifier(modifiers, startDate, 'selected-start-in-hovered-span');
}
}

if (!startDate && endDate && isAfterDay(endDate, hoverDate)) {
modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span');
if (!startDate && endDate) {
if (isAfterDay(endDate, hoverDate)) {
modifiers = this.deleteModifierFromRange(modifiers, hoverDate, endDate, 'hovered-span');
}

if (isBeforeDay(day, endDate)) {
modifiers = this.deleteModifier(modifiers, endDate, 'selected-end-in-hovered-span');
}
}

if (startDate && isSameDay(day, startDate)) {
Expand All @@ -767,6 +857,11 @@ export default class DayPickerRangeController extends React.PureComponent {
modifiers = this.deleteModifierFromRange(modifiers, startSpan, endSpan, 'after-hovered-start');
}

if (endDate && isSameDay(day, endDate)) {
const startSpan = endDate.clone().subtract(minimumNights, 'days');
modifiers = this.deleteModifierFromRange(modifiers, startSpan, endDate, 'before-hovered-end');
}

if (!this.isBlocked(hoverDate)) {
const minNightsForHoverDate = getMinNightsForHoverDate(hoverDate);
if (minNightsForHoverDate > 0 && focusedInput === START_DATE) {
Expand Down Expand Up @@ -1135,6 +1230,23 @@ export default class DayPickerRangeController extends React.PureComponent {
return isSameDay(day, firstAvailableEndDate);
}

beforeSelectedEnd(day) {
const { endDate } = this.props;
return isBeforeDay(day, endDate);
}

isDayBeforeHoveredEndDate(day) {
const { startDate, endDate, minimumNights } = this.props;
const { hoverDate } = this.state || {};

return !!endDate
&& !startDate
&& !this.isBlocked(day)
&& isPreviousDay(hoverDate, day)
&& minimumNights > 0
&& isSameDay(hoverDate, endDate);
}

render() {
const {
numberOfMonths,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/getCalendarDaySettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function shouldUseDefaultCursor(modifiers) {

function isHoveredSpan(modifiers) {
if (isSelected(modifiers)) return false;
return modifiers.has('hovered-span') || modifiers.has('after-hovered-start');
return modifiers.has('hovered-span') || modifiers.has('after-hovered-start') || modifiers.has('before-hovered-end');
}

function getAriaLabel(phrases, modifiers, day, ariaLabelFormat) {
Expand Down
9 changes: 9 additions & 0 deletions src/utils/isPreviousDay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import moment from 'moment';

import isSameDay from './isSameDay';

export default function isPreviousDay(a, b) {
if (!moment.isMoment(a) || !moment.isMoment(b)) return false;
const dayBefore = moment(a).subtract(1, 'day');
return isSameDay(dayBefore, b);
}
Loading