/
RangeCalendar.tsx
134 lines (115 loc) · 4.09 KB
/
RangeCalendar.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import dayjs from 'dayjs';
import React, { useState, forwardRef } from 'react';
import { useComponentDefaultProps } from '@mantine/core';
import { isSameDate } from '../../utils';
import { DayModifiers } from '../Month';
import { CalendarBase, CalendarBaseProps } from '../CalendarBase/CalendarBase';
export interface RangeCalendarProps
extends Omit<
CalendarBaseProps,
'value' | 'onChange' | 'isDateInRange' | 'isDateFirstInRange' | 'isDateLastInRange'
> {
/** Selected dates */
value: [Date | null, Date | null];
/** Called when selected date changes */
onChange(value: [Date, Date]): void;
/** Allow one date to be selected as range */
allowSingleDateInRange?: boolean;
}
const defaultProps: Partial<RangeCalendarProps> = {
__staticSelector: 'RangeCalendar',
allowSingleDateInRange: false,
amountOfMonths: 1,
};
export const RangeCalendar = forwardRef<HTMLDivElement, RangeCalendarProps>(
(props: RangeCalendarProps, ref) => {
const {
value,
onChange,
dayStyle,
onMouseLeave,
__staticSelector,
allowSingleDateInRange,
amountOfMonths,
paginateBy,
...others
} = useComponentDefaultProps('RangeCalendar', defaultProps, props);
const [hoveredDay, setHoveredDay] = useState<Date>(null);
const [pickedDate, setPickedDate] = useState<Date>(null);
const setRangeDate = (date: Date) => {
if (pickedDate instanceof Date) {
if (isSameDate(date, pickedDate) && !allowSingleDateInRange) {
setPickedDate(null);
setHoveredDay(null);
return null;
}
const result: [Date, Date] = [date, pickedDate];
result.sort((a, b) => a.getTime() - b.getTime());
onChange(result);
setPickedDate(null);
return null;
}
if (value[0] && isSameDate(date, value[0]) && !allowSingleDateInRange) {
setPickedDate(null);
setHoveredDay(null);
onChange([null, null]);
return null;
}
onChange([date, null]);
setPickedDate(date);
return null;
};
const handleMouseLeave = (event: React.MouseEvent<HTMLDivElement>) => {
typeof onMouseLeave === 'function' && onMouseLeave(event);
setHoveredDay(null);
};
const shouldHighlightDate = (date: Date, modifiers: DayModifiers) => {
if (pickedDate instanceof Date && hoveredDay instanceof Date) {
const result: [Date, Date] = [hoveredDay, pickedDate];
result.sort((a, b) => a.getTime() - b.getTime());
return (
!modifiers.selected &&
dayjs(date).subtract(1, 'day').isBefore(result[1]) &&
dayjs(date).add(1, 'day').isAfter(result[0])
);
}
return false;
};
const isPickedDateFirstInRange = (date: Date, modifiers: DayModifiers) => {
if (pickedDate instanceof Date && hoveredDay instanceof Date) {
const result: [Date, Date] = [hoveredDay, pickedDate];
result.sort((a, b) => a.getTime() - b.getTime());
return modifiers.selected && dayjs(date).isBefore(result[1]);
}
return false;
};
const isPickedDateLastInRange = (date: Date, modifiers: DayModifiers) => {
if (pickedDate instanceof Date && hoveredDay instanceof Date) {
const result: [Date, Date] = [hoveredDay, pickedDate];
result.sort((a, b) => a.getTime() - b.getTime());
return modifiers.selected && dayjs(date).isAfter(result[0]);
}
return false;
};
return (
<CalendarBase
dayStyle={dayStyle}
onMouseLeave={handleMouseLeave}
onDayMouseEnter={(date) => setHoveredDay(date)}
onChange={setRangeDate}
value={pickedDate}
range={value}
ref={ref}
__staticSelector={__staticSelector}
amountOfMonths={amountOfMonths}
paginateBy={paginateBy || amountOfMonths}
hideOutsideDates={amountOfMonths > 1}
isDateInRange={shouldHighlightDate}
isDateFirstInRange={isPickedDateFirstInRange}
isDateLastInRange={isPickedDateLastInRange}
{...others}
/>
);
}
);
RangeCalendar.displayName = '@mantine/dates/RangeCalendar';