react-day-picker 9.0.4 → 9.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/style.css CHANGED
@@ -49,7 +49,8 @@
49
49
  --rdp-week_number-height: var(--rdp-day-height); /* The height of the week number cells. */
50
50
  --rdp-week_number-opacity: 0.75; /* The opacity of the week number. */
51
51
  --rdp-week_number-width: var(--rdp-day-width); /* The width of the week number cells. */
52
-
52
+ --rdp-weeknumber-text-align: center; /* The text alignment of the weekday cells. */
53
+
53
54
  --rdp-weekday-font: 500 smaller var(--rdp-font-family); /* The font of the weekday. */
54
55
  --rdp-weekday-opacity: 0.75; /* The opacity of the weekday. */
55
56
  --rdp-weekday-padding: 0.5rem 0rem; /* The padding of the weekday. */
@@ -207,6 +208,10 @@
207
208
  max-width: fit-content;
208
209
  }
209
210
 
211
+ .rdp-month_grid {
212
+ border-collapse: collapse;
213
+ }
214
+
210
215
  .rdp-nav {
211
216
  position: absolute;
212
217
  inset-block-start: 0;
@@ -233,10 +238,7 @@
233
238
  width: var(--rdp-week_number-width);
234
239
  border: var(--rdp-week_number-border);
235
240
  border-radius: var(--rdp-week_number-border-radius);
236
- }
237
-
238
- .rdp-week_number_interactive {
239
- cursor: pointer;
241
+ text-align: var(--rdp-weeknumber-text-align);
240
242
  }
241
243
 
242
244
  /* DAY MODIFIERS */
@@ -296,6 +298,10 @@
296
298
  background-color: var(--rdp-range_end-date-background-color);
297
299
  }
298
300
 
301
+ .rdp-range_start.rdp-range_end {
302
+ background: revert;
303
+ }
304
+
299
305
  .rdp-focusable {
300
306
  cursor: pointer;
301
307
  }
@@ -49,7 +49,8 @@
49
49
  --rdp-week_number-height: var(--rdp-day-height); /* The height of the week number cells. */
50
50
  --rdp-week_number-opacity: 0.75; /* The opacity of the week number. */
51
51
  --rdp-week_number-width: var(--rdp-day-width); /* The width of the week number cells. */
52
-
52
+ --rdp-weeknumber-text-align: center; /* The text alignment of the weekday cells. */
53
+
53
54
  --rdp-weekday-font: 500 smaller var(--rdp-font-family); /* The font of the weekday. */
54
55
  --rdp-weekday-opacity: 0.75; /* The opacity of the weekday. */
55
56
  --rdp-weekday-padding: 0.5rem 0rem; /* The padding of the weekday. */
@@ -207,6 +208,10 @@
207
208
  max-width: fit-content;
208
209
  }
209
210
 
211
+ .month_grid {
212
+ border-collapse: collapse;
213
+ }
214
+
210
215
  .nav {
211
216
  position: absolute;
212
217
  inset-block-start: 0;
@@ -233,10 +238,7 @@
233
238
  width: var(--rdp-week_number-width);
234
239
  border: var(--rdp-week_number-border);
235
240
  border-radius: var(--rdp-week_number-border-radius);
236
- }
237
-
238
- .week_number_interactive {
239
- cursor: pointer;
241
+ text-align: var(--rdp-weeknumber-text-align);
240
242
  }
241
243
 
242
244
  /* DAY MODIFIERS */
@@ -262,7 +264,7 @@
262
264
 
263
265
  .hidden {
264
266
  visibility: hidden;
265
- color: var(end-range_start-color);
267
+ color: var(--rdp-range_start-color);
266
268
  }
267
269
 
268
270
  .range_start {
@@ -296,6 +298,10 @@
296
298
  background-color: var(--rdp-range_end-date-background-color);
297
299
  }
298
300
 
301
+ .range_start.range_end {
302
+ background: revert;
303
+ }
304
+
299
305
  .focusable {
300
306
  cursor: pointer;
301
307
  }
@@ -1,119 +1,117 @@
1
- import { addDays, subDays } from "date-fns";
1
+ import { addToRange } from "./addToRange";
2
2
 
3
- import type { DateRange } from "../types";
3
+ describe("addToRange", () => {
4
+ test("add a date to an undefined range", () => {
5
+ const date = new Date(2022, 0, 1);
6
+ const range = addToRange(date, undefined);
7
+ expect(range).toEqual({ from: date, to: date });
8
+ });
4
9
 
5
- import { addToRange } from "./addToRange";
10
+ test("add a date to an empty range", () => {
11
+ const date = new Date(2022, 0, 1);
12
+ const range = addToRange(date, { from: undefined, to: undefined });
13
+ expect(range).toEqual({ from: date, to: date });
14
+ });
6
15
 
7
- describe('when no "from" is the range', () => {
8
- const range = { from: undefined };
9
- const day = new Date();
10
- let result: DateRange | undefined;
11
- beforeAll(() => {
12
- result = addToRange(day, range);
16
+ test("add a date to an incomplete range with same start date", () => {
17
+ const date = new Date(2022, 0, 1);
18
+ const range = addToRange(date, { from: date, to: undefined });
19
+ expect(range).toEqual(undefined);
13
20
  });
14
- test('should set "from" as the given day', () => {
15
- expect(result).toEqual({ from: day, to: undefined });
21
+
22
+ test("add a date to an incomplete range with earlier date", () => {
23
+ const from = new Date(2022, 0, 1);
24
+ const earlierDate = new Date(2021, 11, 31);
25
+ const range = addToRange(earlierDate, { from: from, to: undefined });
26
+ expect(range).toEqual({ from: earlierDate, to: from });
16
27
  });
17
- });
18
28
 
19
- describe('when no "to" is the range', () => {
20
- const day = new Date();
21
- const range = { from: day, to: undefined };
22
- describe('and the day is the same as the "from" day', () => {
23
- let result: DateRange | undefined;
24
- beforeAll(() => {
25
- result = addToRange(day, range);
26
- });
27
- test("should reset the range", () => {
28
- expect(result).toEqual({ from: undefined, to: undefined });
29
- });
30
- });
31
- describe('and the day is before "from" day', () => {
32
- const day = subDays(range.from, 1);
33
- let result: DateRange | undefined;
34
- beforeAll(() => {
35
- result = addToRange(day, range);
36
- });
37
- test('should set the day as the "from" range', () => {
38
- expect(result).toEqual({ from: day, to: range.from });
39
- });
40
- });
41
- describe('and the day is after the "from" day', () => {
42
- const day = addDays(range.from, 1);
43
- let result: DateRange | undefined;
44
- beforeAll(() => {
45
- result = addToRange(day, range);
46
- });
47
- test('should set the day as the "to" date', () => {
48
- expect(result).toEqual({ from: range.from, to: day });
49
- });
29
+ test("add a date to an incomplete range with later date", () => {
30
+ const from = new Date(2022, 0, 1);
31
+ const date = new Date(2022, 0, 2);
32
+ const range = addToRange(date, { from: from, to: undefined });
33
+ expect(range).toEqual({ from: from, to: date });
50
34
  });
51
- });
52
35
 
53
- describe('when "from", "to" and "day" are the same', () => {
54
- const day = new Date();
55
- const range = { from: day, to: day };
56
- let result: DateRange | undefined;
57
- beforeAll(() => {
58
- result = addToRange(day, range);
36
+ test("add a date to a complete range with same start and end date", () => {
37
+ const date = new Date(2022, 0, 1);
38
+ const from = date;
39
+ const to = date;
40
+ const range = addToRange(date, { from, to }, 0, 0, false);
41
+ expect(range).toEqual(undefined);
59
42
  });
60
- test("should return an undefined range (reset)", () => {
61
- expect(result).toEqual({ from: undefined, to: undefined });
43
+
44
+ test("add a date to a complete range with same start date", () => {
45
+ const date = new Date(2022, 0, 1);
46
+ const to = new Date(2022, 0, 2);
47
+ const range = addToRange(date, { from: date, to: to });
48
+ expect(range).toEqual({ from: date, to: date });
62
49
  });
63
- });
64
50
 
65
- describe('when "to" and "day" are the same', () => {
66
- const from = new Date();
67
- const to = addDays(from, 4);
68
- const day = to;
69
- const range = { from, to };
70
- let result: DateRange | undefined;
71
- beforeAll(() => {
72
- result = addToRange(day, range);
51
+ test("add a date to a complete range with same end date", () => {
52
+ const date = new Date(2022, 0, 2);
53
+ const from = new Date(2022, 0, 1);
54
+ const range = addToRange(date, { from: from, to: date });
55
+ expect(range).toEqual({ from: date, to: date });
73
56
  });
74
- test('should set "to" to undefined', () => {
75
- expect(result).toEqual({ from: to, to: undefined });
57
+
58
+ test("add a date when inside the range", () => {
59
+ const date = new Date(2022, 0, 1);
60
+ const from = new Date(2021, 11, 31);
61
+ const to = new Date(2022, 0, 2);
62
+ const range = addToRange(date, { from, to });
63
+ expect(range).toEqual({ from, to: date });
76
64
  });
77
- });
78
65
 
79
- describe('when "from" and "day" are the same', () => {
80
- const from = new Date();
81
- const to = addDays(from, 4);
82
- const day = from;
83
- const range = { from, to };
84
- let result: DateRange | undefined;
85
- beforeAll(() => {
86
- result = addToRange(day, range);
66
+ test("add an earlier date to a complete range", () => {
67
+ const from = new Date(2022, 0, 1);
68
+ const to = new Date(2022, 0, 2);
69
+ const date = new Date(2021, 11, 31);
70
+ const range = addToRange(date, { from, to });
71
+ expect(range).toEqual({ from: date, to: to });
87
72
  });
88
- test("should reset the range", () => {
89
- expect(result).toEqual({ from: undefined, to: undefined });
73
+
74
+ test("add a later date to a complete range", () => {
75
+ const date = new Date(2022, 0, 2);
76
+ const from = new Date(2021, 11, 31);
77
+ const to = new Date(2022, 0, 1);
78
+ const range = addToRange(date, { from, to });
79
+ expect(range).toEqual({ from: from, to: date });
90
80
  });
91
- });
92
81
 
93
- describe('when "from" is after "day"', () => {
94
- const day = new Date();
95
- const from = addDays(day, 1);
96
- const to = addDays(from, 4);
97
- const range = { from, to };
98
- let result: DateRange | undefined;
99
- beforeAll(() => {
100
- result = addToRange(day, range);
82
+ test("add a date with min > 0", () => {
83
+ const date = new Date(2022, 0, 1);
84
+ const range = addToRange(date, undefined, 1, 0, false);
85
+ expect(range).toEqual({ from: date, to: undefined });
101
86
  });
102
- test('should set the day as "from"', () => {
103
- expect(result).toEqual({ from: day, to: range.to });
87
+
88
+ test("add a date with max > 0", () => {
89
+ const date = new Date(2022, 0, 1);
90
+ const range = addToRange(date, undefined, 0, 1, false);
91
+ expect(range).toEqual({ from: date, to: date });
92
+ });
93
+
94
+ test("add a date with required set to true", () => {
95
+ const date = new Date(2022, 0, 1);
96
+ const range = addToRange(date, undefined, 0, 0, true);
97
+ expect(range).toEqual({ from: date, to: date });
98
+ });
99
+
100
+ test("when exceeding max, set the start of the range", () => {
101
+ const from = new Date(2022, 0, 1);
102
+ const to = new Date(2022, 0, 2);
103
+ const date = new Date(2022, 0, 4);
104
+ const max = 2;
105
+ const range = addToRange(date, { from, to }, 0, max, false);
106
+ expect(range).toEqual({ from: date, to: undefined });
104
107
  });
105
- });
106
108
 
107
- describe('when "from" is before "day"', () => {
108
- const day = new Date();
109
- const from = subDays(day, 1);
110
- const to = addDays(from, 4);
111
- const range = { from, to };
112
- let result: DateRange | undefined;
113
- beforeAll(() => {
114
- result = addToRange(day, range);
115
- });
116
- test('should set the day as "to"', () => {
117
- expect(result).toEqual({ from: range.from, to: day });
109
+ test("when below min, set the start of the range", () => {
110
+ const from = new Date(2021, 11, 20);
111
+ const to = new Date(2022, 0, 2);
112
+ const date = new Date(2021, 11, 21);
113
+ const min = 5;
114
+ const range = addToRange(date, { from, to }, min, 0, false);
115
+ expect(range).toEqual({ from: date, to: undefined });
118
116
  });
119
117
  });
@@ -10,42 +10,78 @@ import type { DateRange, DateLib } from "../types/index.js";
10
10
  * @group Utilities
11
11
  */
12
12
  export function addToRange(
13
+ /** The date to add to the range. */
13
14
  date: Date,
14
- range: DateRange | undefined,
15
+ /** The range where to add `date`. */
16
+ initialRange: DateRange | undefined,
17
+ min = 0,
18
+ max = 0,
19
+ required = false,
15
20
  /** @ignore */
16
21
  dateLib: DateLib = defaultDateLib
17
- ): DateRange {
18
- const { from, to } = range || {};
22
+ ): DateRange | undefined {
23
+ const { from, to } = initialRange || {};
19
24
  const { isSameDay, isAfter, isBefore } = dateLib;
20
- if (from && to) {
21
- if (isSameDay(to, date) && isSameDay(from, date)) {
22
- return { from: undefined, to: undefined };
23
- }
24
- if (isSameDay(to, date)) {
25
- return { from: to, to: undefined };
26
- }
25
+
26
+ let range: DateRange | undefined;
27
+
28
+ if (!from && !to) {
29
+ // the range is empty, add the date
30
+ range = { from: date, to: min > 0 ? undefined : date };
31
+ } else if (from && !to) {
32
+ // adding date to an incomplete range
27
33
  if (isSameDay(from, date)) {
28
- return { from: undefined, to: undefined };
34
+ // adding a date equal to the start of the range
35
+ if (required) {
36
+ range = { from, to: undefined };
37
+ } else {
38
+ range = undefined;
39
+ }
40
+ } else if (isBefore(date, from)) {
41
+ // adding a date before the start of the range
42
+ range = { from: date, to: from };
43
+ } else {
44
+ // adding a date after the start of the range
45
+ range = { from, to: date };
29
46
  }
30
- if (isAfter(from, date)) {
31
- return { from: date, to };
47
+ } else if (from && to) {
48
+ // adding date to a complete range
49
+ if (isSameDay(from, date) && isSameDay(to, date)) {
50
+ // adding a date that is equal to both start and end of the range
51
+ if (required) {
52
+ range = { from, to };
53
+ } else {
54
+ range = undefined;
55
+ }
56
+ } else if (isSameDay(from, date)) {
57
+ // adding a date equal to the the start of the range
58
+ range = { from, to: min > 0 ? undefined : date };
59
+ } else if (isSameDay(to, date)) {
60
+ // adding a dare equal to the end of the range
61
+ range = { from: date, to: min > 0 ? undefined : date };
62
+ } else if (isBefore(date, from)) {
63
+ // adding a date before the start of the range
64
+ range = { from: date, to: to };
65
+ } else if (isAfter(date, from)) {
66
+ // adding a date after the start of the range
67
+ range = { from, to: date };
68
+ } else if (isAfter(date, to)) {
69
+ // adding a date after the end of the range
70
+ range = { from, to: date };
71
+ } else {
72
+ throw new Error("Invalid range");
32
73
  }
33
- return { from, to: date };
34
74
  }
35
- if (to) {
36
- if (isAfter(date, to)) {
37
- return { from: to, to: date };
38
- }
39
- return { from: date, to };
40
- }
41
- if (from) {
42
- if (isBefore(date, from)) {
43
- return { from: date, to: from };
44
- }
45
- if (isSameDay(date, from)) {
46
- return { from: undefined, to: undefined };
75
+
76
+ // check for min / max
77
+ if (range?.from && range?.to) {
78
+ const diff = dateLib.differenceInCalendarDays(range.to, range.from);
79
+ if (max > 0 && diff > max) {
80
+ range = { from: date, to: undefined };
81
+ } else if (min > 1 && diff < min) {
82
+ range = { from: date, to: undefined };
47
83
  }
48
- return { from, to: date };
49
84
  }
50
- return { from: date, to: undefined };
85
+
86
+ return range;
51
87
  }
@@ -40,23 +40,13 @@ When the `mode` prop is set to `"single"`, only one day can be selected.
40
40
  | `onSelect` | `(selected, triggerDate, modifiers, e) => void` | Event callback when a date is selected. |
41
41
  | `required` | `boolean` | Make the selection required. |
42
42
 
43
- The following code snippet will render a date picker with a single selected date. When a day is clicked, the `selectedDate` state is updated.
43
+ Use the `selected` and `onSelect` props to control the selected date:
44
44
 
45
45
  ```tsx
46
- import { useState } from "react";
47
-
48
- import { DayPicker } from "react-day-picker";
49
-
50
46
  export function App() {
51
- const initiallySelectedDate = new Date();
52
- const [selectedDate, setSelectedDate] = useState(initiallySelectedDate);
53
- return (
54
- <DayPicker
55
- mode="single"
56
- selected={selectedDate}
57
- onSelect={setSelectedDate}
58
- />
59
- );
47
+ const [selected, setSelected] = React.useState<Date | undefined>();
48
+
49
+ return <DayPicker mode="single" selected={selected} onSelect={setSelected} />;
60
50
  }
61
51
  ```
62
52
 
@@ -93,103 +83,89 @@ By setting the `mode` prop to `"multiple"`, DayPicker allows selecting multiple
93
83
  | `min` | `number` | The minimum dates that can be selected. |
94
84
  | `max` | `number` | The maximum dates that can be selected. |
95
85
 
96
- ### Min and Max Dates
97
-
98
- Use the `min` and `max` props to limit the number of selectable dates.
86
+ Use the `selected` and `onSelect` props to control the selected date:
99
87
 
100
88
  ```tsx
101
- import { addDays } from "date-fns";
102
- import { DayPicker } from "react-day-picker";
89
+ export function App() {
90
+ const [selected, setSelected] = React.useState<Date[] | undefined>();
103
91
 
104
- export function MultipleMinMax() {
105
- const defaultSelected = [new Date(), addDays(new Date(), 1)];
106
92
  return (
107
- <DayPicker
108
- defaultSelected={defaultSelected}
109
- mode="multiple"
110
- min={2}
111
- max={5}
112
- />
93
+ <DayPicker mode="multiple" selected={selected} onSelect={setSelected} />
113
94
  );
114
95
  }
115
96
  ```
116
97
 
98
+ ### Min and Max Dates
99
+
100
+ Use the `min` and `max` props to limit the number of selectable dates.
101
+
102
+ ```tsx
103
+ <DayPicker mode="multiple" min={2} max={5} />
104
+ ```
105
+
117
106
  <BrowserWindow>
118
107
  <Examples.MultipleMinMax />
119
108
  </BrowserWindow>
120
109
 
121
- ## Range Mode
110
+ ### Required Selection
122
111
 
123
- When the `mode` prop is set to `"range"`, DayPicker allows selecting a continuous range of dates.
112
+ By setting the `required` prop, DayPicker requires at least one date to be selected.
124
113
 
125
114
  ```tsx
126
- <DayPicker mode="range" />
115
+ <DayPicker mode="multiple" required selected={[new Date()]} />
127
116
  ```
128
117
 
129
- | Prop Name | Type | Description |
130
- | ---------- | ----------------------------------------------- | --------------------------------------- |
131
- | `selected` | [`DateRange`](../api/type-aliases/DateRange.md) | The selected range. |
132
- | `onSelect` | `(selected, triggerDate, modifiers, e) => void` | Event callback when a date is selected. |
133
- | `min` | `number` | The minimum dates that can be selected. |
134
- | `max` | `number` | The maximum dates that can be selected. |
135
-
136
- ```tsx
137
- import { addDays, format } from "date-fns";
138
- import { DateRange, DayPicker } from "react-day-picker";
118
+ <BrowserWindow>
119
+ <Examples.MultipleRequired />
120
+ </BrowserWindow>
139
121
 
140
- export function RangeExample() {
141
- const defaultMonth = new Date(2020, 5, 15);
122
+ ## Range Mode
142
123
 
143
- const defaultSelected: DateRange = {
144
- from: defaultMonth,
145
- to: addDays(defaultMonth, 4)
146
- };
147
- const [range, setRange] = React.useState<DateRange | undefined>(
148
- defaultSelected
149
- );
124
+ When the `mode` prop is set to `"range"`, DayPicker allows selecting a continuous range of dates.
150
125
 
151
- return (
152
- <DayPicker
153
- mode="range"
154
- defaultMonth={defaultMonth}
155
- selected={range}
156
- onSelect={setRange}
157
- />
158
- );
159
- }
126
+ ```tsx
127
+ <DayPicker mode="range" />
160
128
  ```
161
129
 
162
130
  <BrowserWindow>
163
131
  <Examples.Range />
164
132
  </BrowserWindow>
165
133
 
134
+ ### Range Mode Props
135
+
136
+ | Prop Name | Type | Description |
137
+ | ----------------- | ----------------------------------------------- | --------------------------------------- |
138
+ | `selected` | [`DateRange`](../api/type-aliases/DateRange.md) | The selected range. |
139
+ | `onSelect` | `(selected, triggerDate, modifiers, e) => void` | Event callback when a date is selected. |
140
+ | `required` | `boolean` | Make the selection required. |
141
+ | `min` | `number` | The minimum dates that can be selected. |
142
+ | `max` | `number` | The maximum dates that can be selected. |
143
+ | `excludeDisabled` | `boolean` | Exclude disabled dates from the range. |
144
+
166
145
  ### Min and Max Dates
167
146
 
168
- Also in range mode, you can set the `min` and `max` props to limit the number of selectable dates.
147
+ As default, a range can have length of 0 nights, thus the start date can be the same as the end date. Use the `min` prop to set the minimum number of nights. The range will stay "open" until at least `min` nights are selected.
148
+
149
+ Similarly, you can se the `max` prop to set the maximum number of nights.
169
150
 
170
151
  ```tsx
171
- import { useState } from "react";
152
+ <DayPicker mode="range" min={1} max={6} />
153
+ ```
172
154
 
173
- import { DateRange, DayPicker } from "react-day-picker";
155
+ <BrowserWindow bodyStyle={{ justifyContent: "start" }}>
156
+ <Examples.RangeMinMax />
157
+ </BrowserWindow>
174
158
 
175
- export function RangeMinMax() {
176
- const [range, setRange] = useState<DateRange | undefined>();
159
+ ### Required Ranges
177
160
 
178
- return (
179
- <DayPicker
180
- defaultMonth={new Date(2022, 8)}
181
- mode="range"
182
- min={3}
183
- max={9}
184
- selected={range}
185
- onSelect={setRange}
186
- />
187
- );
188
- }
161
+ By setting the `required` prop, a range must be selected.
162
+
163
+ ```tsx
164
+ <DayPicker mode="range" required />
189
165
  ```
190
166
 
191
167
  <BrowserWindow>
192
- <Examples.RangeMinMax />
168
+ <Examples.RangeRequired />
193
169
  </BrowserWindow>
194
170
 
195
171
  ## Disabling Dates