@birdnet/advanced-react-dates 0.0.0-stage → 1.0.0

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.
Files changed (267) hide show
  1. package/.babelrc +67 -0
  2. package/.eslintrc +66 -0
  3. package/.nvmrc +1 -0
  4. package/.nyc_output/4d8efcd220c2aec1f2b4ffd49d875001.json +1 -0
  5. package/.nyc_output/66d34779498a5d49bcf9d757dfc7fdb5.json +1 -0
  6. package/.nyc_output/8ca7e04140934515928bc7d4ae476b73.json +1 -0
  7. package/.nyc_output/afc270bb597bfc50e690436959c2ae54.json +1 -0
  8. package/.nycrc +19 -0
  9. package/.travis.yml +13 -0
  10. package/CHANGELOG.md +665 -0
  11. package/CLAUDE.md +56 -0
  12. package/INTHEWILD.md +9 -0
  13. package/LICENSE +21 -0
  14. package/README.md +421 -2
  15. package/constants.js +2 -0
  16. package/esm/components/CalendarDay.js +321 -0
  17. package/esm/components/CalendarIcon.js +11 -0
  18. package/esm/components/CalendarMonth.js +254 -0
  19. package/esm/components/CalendarMonthGrid.js +365 -0
  20. package/esm/components/CalendarWeek.js +11 -0
  21. package/esm/components/ChevronDown.js +11 -0
  22. package/esm/components/ChevronUp.js +11 -0
  23. package/esm/components/CloseButton.js +12 -0
  24. package/esm/components/CustomizableCalendarDay.js +341 -0
  25. package/esm/components/DateInput.js +344 -0
  26. package/esm/components/DateRangePicker.js +647 -0
  27. package/esm/components/DateRangePickerInput.js +341 -0
  28. package/esm/components/DateRangePickerInputController.js +348 -0
  29. package/esm/components/DayPicker.js +1167 -0
  30. package/esm/components/DayPickerKeyboardShortcuts.js +364 -0
  31. package/esm/components/DayPickerNavigation.js +314 -0
  32. package/esm/components/DayPickerRangeController.js +1202 -0
  33. package/esm/components/DayPickerSingleDateController.js +690 -0
  34. package/esm/components/KeyboardShortcutRow.js +69 -0
  35. package/esm/components/LeftArrow.js +11 -0
  36. package/esm/components/RightArrow.js +11 -0
  37. package/esm/components/SingleDatePicker.js +605 -0
  38. package/esm/components/SingleDatePickerInput.js +264 -0
  39. package/esm/components/SingleDatePickerInputController.js +260 -0
  40. package/esm/constants.js +28 -0
  41. package/esm/defaultPhrases.js +225 -0
  42. package/esm/index.js +20 -0
  43. package/esm/initialize.js +2 -0
  44. package/esm/shapes/AnchorDirectionShape.js +3 -0
  45. package/esm/shapes/CalendarInfoPositionShape.js +3 -0
  46. package/esm/shapes/DateRangePickerShape.js +101 -0
  47. package/esm/shapes/DayOfWeekShape.js +3 -0
  48. package/esm/shapes/DisabledShape.js +3 -0
  49. package/esm/shapes/FocusedInputShape.js +3 -0
  50. package/esm/shapes/IconPositionShape.js +3 -0
  51. package/esm/shapes/ModifiersShape.js +18 -0
  52. package/esm/shapes/NavPositionShape.js +3 -0
  53. package/esm/shapes/OpenDirectionShape.js +3 -0
  54. package/esm/shapes/OrientationShape.js +3 -0
  55. package/esm/shapes/ScrollableOrientationShape.js +3 -0
  56. package/esm/shapes/SingleDatePickerShape.js +90 -0
  57. package/esm/theme/DefaultTheme.js +176 -0
  58. package/esm/utils/calculateDimension.js +28 -0
  59. package/esm/utils/disableScroll.js +67 -0
  60. package/esm/utils/getActiveElement.js +3 -0
  61. package/esm/utils/getCalendarDaySettings.js +48 -0
  62. package/esm/utils/getCalendarMonthWeeks.js +37 -0
  63. package/esm/utils/getCalendarMonthWidth.js +4 -0
  64. package/esm/utils/getDetachedContainerStyles.js +36 -0
  65. package/esm/utils/getInputHeight.js +42 -0
  66. package/esm/utils/getNumberOfCalendarMonthWeeks.js +11 -0
  67. package/esm/utils/getPhrase.js +7 -0
  68. package/esm/utils/getPhrasePropTypes.js +9 -0
  69. package/esm/utils/getPooledMoment.js +8 -0
  70. package/esm/utils/getPreviousMonthMemoLast.js +9 -0
  71. package/esm/utils/getResponsiveContainerStyles.js +8 -0
  72. package/esm/utils/getSelectedDateOffset.js +8 -0
  73. package/esm/utils/getTransformStyles.js +8 -0
  74. package/esm/utils/getVisibleDays.js +42 -0
  75. package/esm/utils/isAfterDay.js +7 -0
  76. package/esm/utils/isBeforeDay.js +13 -0
  77. package/esm/utils/isDayVisible.js +38 -0
  78. package/esm/utils/isInclusivelyAfterDay.js +6 -0
  79. package/esm/utils/isInclusivelyBeforeDay.js +6 -0
  80. package/esm/utils/isNextDay.js +7 -0
  81. package/esm/utils/isNextMonth.js +6 -0
  82. package/esm/utils/isPrevMonth.js +6 -0
  83. package/esm/utils/isPreviousDay.js +7 -0
  84. package/esm/utils/isSameDay.js +7 -0
  85. package/esm/utils/isSameMonth.js +7 -0
  86. package/esm/utils/isTransitionEndSupported.js +3 -0
  87. package/esm/utils/modifiers.js +94 -0
  88. package/esm/utils/noflip.js +10 -0
  89. package/esm/utils/registerCSSInterfaceWithDefaultTheme.js +5 -0
  90. package/esm/utils/registerInterfaceWithDefaultTheme.js +6 -0
  91. package/esm/utils/toISODateString.js +12 -0
  92. package/esm/utils/toISOMonthString.js +12 -0
  93. package/esm/utils/toLocalizedDateString.js +8 -0
  94. package/esm/utils/toMomentObject.js +7 -0
  95. package/index.js +2 -0
  96. package/initialize.js +2 -0
  97. package/karma.conf.js +82 -0
  98. package/lib/components/CalendarDay.js +327 -0
  99. package/lib/components/CalendarIcon.js +18 -0
  100. package/lib/components/CalendarMonth.js +259 -0
  101. package/lib/components/CalendarMonthGrid.js +372 -0
  102. package/lib/components/CalendarWeek.js +18 -0
  103. package/lib/components/ChevronDown.js +18 -0
  104. package/lib/components/ChevronUp.js +18 -0
  105. package/lib/components/CloseButton.js +19 -0
  106. package/lib/components/CustomizableCalendarDay.js +347 -0
  107. package/lib/components/DateInput.js +351 -0
  108. package/lib/components/DateRangePicker.js +653 -0
  109. package/lib/components/DateRangePickerInput.js +348 -0
  110. package/lib/components/DateRangePickerInputController.js +354 -0
  111. package/lib/components/DayPicker.js +1175 -0
  112. package/lib/components/DayPickerKeyboardShortcuts.js +371 -0
  113. package/lib/components/DayPickerNavigation.js +321 -0
  114. package/lib/components/DayPickerRangeController.js +1208 -0
  115. package/lib/components/DayPickerSingleDateController.js +695 -0
  116. package/lib/components/KeyboardShortcutRow.js +76 -0
  117. package/lib/components/LeftArrow.js +18 -0
  118. package/lib/components/RightArrow.js +18 -0
  119. package/lib/components/SingleDatePicker.js +611 -0
  120. package/lib/components/SingleDatePickerInput.js +271 -0
  121. package/lib/components/SingleDatePickerInputController.js +266 -0
  122. package/lib/constants.js +34 -0
  123. package/lib/css/_datepicker.css +886 -0
  124. package/lib/defaultPhrases.js +231 -0
  125. package/lib/index.js +146 -0
  126. package/lib/initialize.js +5 -0
  127. package/lib/shapes/AnchorDirectionShape.js +10 -0
  128. package/lib/shapes/CalendarInfoPositionShape.js +10 -0
  129. package/lib/shapes/DateRangePickerShape.js +108 -0
  130. package/lib/shapes/DayOfWeekShape.js +10 -0
  131. package/lib/shapes/DisabledShape.js +10 -0
  132. package/lib/shapes/FocusedInputShape.js +10 -0
  133. package/lib/shapes/IconPositionShape.js +10 -0
  134. package/lib/shapes/ModifiersShape.js +25 -0
  135. package/lib/shapes/NavPositionShape.js +10 -0
  136. package/lib/shapes/OpenDirectionShape.js +10 -0
  137. package/lib/shapes/OrientationShape.js +10 -0
  138. package/lib/shapes/ScrollableOrientationShape.js +10 -0
  139. package/lib/shapes/SingleDatePickerShape.js +97 -0
  140. package/lib/theme/DefaultTheme.js +182 -0
  141. package/lib/utils/calculateDimension.js +34 -0
  142. package/lib/utils/disableScroll.js +75 -0
  143. package/lib/utils/getActiveElement.js +9 -0
  144. package/lib/utils/getCalendarDaySettings.js +55 -0
  145. package/lib/utils/getCalendarMonthWeeks.js +44 -0
  146. package/lib/utils/getCalendarMonthWidth.js +10 -0
  147. package/lib/utils/getDetachedContainerStyles.js +41 -0
  148. package/lib/utils/getInputHeight.js +48 -0
  149. package/lib/utils/getNumberOfCalendarMonthWeeks.js +18 -0
  150. package/lib/utils/getPhrase.js +13 -0
  151. package/lib/utils/getPhrasePropTypes.js +16 -0
  152. package/lib/utils/getPooledMoment.js +15 -0
  153. package/lib/utils/getPreviousMonthMemoLast.js +15 -0
  154. package/lib/utils/getResponsiveContainerStyles.js +15 -0
  155. package/lib/utils/getSelectedDateOffset.js +14 -0
  156. package/lib/utils/getTransformStyles.js +14 -0
  157. package/lib/utils/getVisibleDays.js +49 -0
  158. package/lib/utils/isAfterDay.js +14 -0
  159. package/lib/utils/isBeforeDay.js +20 -0
  160. package/lib/utils/isDayVisible.js +45 -0
  161. package/lib/utils/isInclusivelyAfterDay.js +13 -0
  162. package/lib/utils/isInclusivelyBeforeDay.js +13 -0
  163. package/lib/utils/isNextDay.js +14 -0
  164. package/lib/utils/isNextMonth.js +13 -0
  165. package/lib/utils/isPrevMonth.js +13 -0
  166. package/lib/utils/isPreviousDay.js +14 -0
  167. package/lib/utils/isSameDay.js +14 -0
  168. package/lib/utils/isSameMonth.js +14 -0
  169. package/lib/utils/isTransitionEndSupported.js +9 -0
  170. package/lib/utils/modifiers.js +102 -0
  171. package/lib/utils/noflip.js +16 -0
  172. package/lib/utils/registerCSSInterfaceWithDefaultTheme.js +12 -0
  173. package/lib/utils/registerInterfaceWithDefaultTheme.js +13 -0
  174. package/lib/utils/toISODateString.js +19 -0
  175. package/lib/utils/toISOMonthString.js +19 -0
  176. package/lib/utils/toLocalizedDateString.js +15 -0
  177. package/lib/utils/toMomentObject.js +14 -0
  178. package/package.json +141 -4
  179. package/scripts/.eslintrc +7 -0
  180. package/scripts/buildCSS.js +37 -0
  181. package/scripts/pure-component-fallback.js +70 -0
  182. package/scripts/renderAllComponents.jsx +25 -0
  183. package/src/components/CalendarDay.jsx +366 -0
  184. package/src/components/CalendarIcon.jsx +3 -0
  185. package/src/components/CalendarMonth.jsx +300 -0
  186. package/src/components/CalendarMonthGrid.jsx +427 -0
  187. package/src/components/CalendarWeek.jsx +17 -0
  188. package/src/components/ChevronDown.jsx +3 -0
  189. package/src/components/ChevronUp.jsx +3 -0
  190. package/src/components/CloseButton.jsx +3 -0
  191. package/src/components/CustomizableCalendarDay.jsx +382 -0
  192. package/src/components/DateInput.jsx +401 -0
  193. package/src/components/DateRangePicker.jsx +777 -0
  194. package/src/components/DateRangePickerInput.jsx +434 -0
  195. package/src/components/DateRangePickerInputController.jsx +379 -0
  196. package/src/components/DayPicker.jsx +1407 -0
  197. package/src/components/DayPickerKeyboardShortcuts.jsx +443 -0
  198. package/src/components/DayPickerNavigation.jsx +445 -0
  199. package/src/components/DayPickerRangeController.jsx +1409 -0
  200. package/src/components/DayPickerSingleDateController.jsx +769 -0
  201. package/src/components/KeyboardShortcutRow.jsx +93 -0
  202. package/src/components/LeftArrow.jsx +3 -0
  203. package/src/components/RightArrow.jsx +3 -0
  204. package/src/components/SingleDatePicker.jsx +731 -0
  205. package/src/components/SingleDatePickerInput.jsx +329 -0
  206. package/src/components/SingleDatePickerInputController.jsx +280 -0
  207. package/src/constants.js +37 -0
  208. package/src/defaultPhrases.js +215 -0
  209. package/src/index.js +20 -0
  210. package/src/initialize.js +3 -0
  211. package/src/shapes/AnchorDirectionShape.js +8 -0
  212. package/src/shapes/CalendarInfoPositionShape.js +15 -0
  213. package/src/shapes/DateRangePickerShape.js +111 -0
  214. package/src/shapes/DayOfWeekShape.js +5 -0
  215. package/src/shapes/DisabledShape.js +5 -0
  216. package/src/shapes/FocusedInputShape.js +8 -0
  217. package/src/shapes/IconPositionShape.js +8 -0
  218. package/src/shapes/ModifiersShape.js +20 -0
  219. package/src/shapes/NavPositionShape.js +8 -0
  220. package/src/shapes/OpenDirectionShape.js +8 -0
  221. package/src/shapes/OrientationShape.js +8 -0
  222. package/src/shapes/ScrollableOrientationShape.js +13 -0
  223. package/src/shapes/SingleDatePickerShape.js +100 -0
  224. package/src/svg/arrow-left.svg +1 -0
  225. package/src/svg/arrow-right.svg +1 -0
  226. package/src/svg/calendar.svg +1 -0
  227. package/src/svg/chevron-down.svg +1 -0
  228. package/src/svg/chevron-up.svg +1 -0
  229. package/src/svg/close.svg +1 -0
  230. package/src/theme/DefaultTheme.js +196 -0
  231. package/src/utils/calculateDimension.js +32 -0
  232. package/src/utils/disableScroll.js +65 -0
  233. package/src/utils/getActiveElement.js +3 -0
  234. package/src/utils/getCalendarDaySettings.js +61 -0
  235. package/src/utils/getCalendarMonthWeeks.js +49 -0
  236. package/src/utils/getCalendarMonthWidth.js +3 -0
  237. package/src/utils/getDetachedContainerStyles.js +39 -0
  238. package/src/utils/getInputHeight.js +67 -0
  239. package/src/utils/getNumberOfCalendarMonthWeeks.js +15 -0
  240. package/src/utils/getPhrase.jsx +9 -0
  241. package/src/utils/getPhrasePropTypes.js +9 -0
  242. package/src/utils/getPooledMoment.js +10 -0
  243. package/src/utils/getPreviousMonthMemoLast.js +11 -0
  244. package/src/utils/getResponsiveContainerStyles.js +18 -0
  245. package/src/utils/getSelectedDateOffset.js +6 -0
  246. package/src/utils/getTransformStyles.js +8 -0
  247. package/src/utils/getVisibleDays.js +54 -0
  248. package/src/utils/isAfterDay.js +9 -0
  249. package/src/utils/isBeforeDay.js +18 -0
  250. package/src/utils/isDayVisible.js +57 -0
  251. package/src/utils/isInclusivelyAfterDay.js +8 -0
  252. package/src/utils/isInclusivelyBeforeDay.js +8 -0
  253. package/src/utils/isNextDay.js +9 -0
  254. package/src/utils/isNextMonth.js +8 -0
  255. package/src/utils/isPrevMonth.js +8 -0
  256. package/src/utils/isPreviousDay.js +9 -0
  257. package/src/utils/isSameDay.js +10 -0
  258. package/src/utils/isSameMonth.js +9 -0
  259. package/src/utils/isTransitionEndSupported.js +3 -0
  260. package/src/utils/modifiers.js +116 -0
  261. package/src/utils/noflip.js +11 -0
  262. package/src/utils/registerCSSInterfaceWithDefaultTheme.js +7 -0
  263. package/src/utils/registerInterfaceWithDefaultTheme.js +7 -0
  264. package/src/utils/toISODateString.js +14 -0
  265. package/src/utils/toISOMonthString.js +14 -0
  266. package/src/utils/toLocalizedDateString.js +12 -0
  267. package/src/utils/toMomentObject.js +12 -0
@@ -0,0 +1,777 @@
1
+ import React from 'react';
2
+ import moment from 'moment';
3
+ import { withStyles, withStylesPropTypes } from 'react-with-styles';
4
+ import { Portal } from 'react-portal';
5
+ import { forbidExtraProps } from 'airbnb-prop-types';
6
+ import { addEventListener } from 'consolidated-events';
7
+ import isTouchDevice from 'is-touch-device';
8
+ import OutsideClickHandler from 'react-outside-click-handler';
9
+ import { darken } from 'color2k';
10
+
11
+ import DateRangePickerShape from '../shapes/DateRangePickerShape';
12
+ import { DateRangePickerPhrases } from '../defaultPhrases';
13
+
14
+ import getResponsiveContainerStyles from '../utils/getResponsiveContainerStyles';
15
+ import getDetachedContainerStyles from '../utils/getDetachedContainerStyles';
16
+ import getInputHeight from '../utils/getInputHeight';
17
+ import isInclusivelyAfterDay from '../utils/isInclusivelyAfterDay';
18
+ import disableScroll from '../utils/disableScroll';
19
+ import noflip from '../utils/noflip';
20
+
21
+ import DateRangePickerInputController from './DateRangePickerInputController';
22
+ import DayPickerRangeController from './DayPickerRangeController';
23
+ import CloseButton from './CloseButton';
24
+
25
+ import {
26
+ START_DATE,
27
+ END_DATE,
28
+ HORIZONTAL_ORIENTATION,
29
+ VERTICAL_ORIENTATION,
30
+ ANCHOR_LEFT,
31
+ ANCHOR_RIGHT,
32
+ OPEN_DOWN,
33
+ OPEN_UP,
34
+ DAY_SIZE,
35
+ ICON_BEFORE_POSITION,
36
+ INFO_POSITION_BOTTOM,
37
+ FANG_HEIGHT_PX,
38
+ DEFAULT_VERTICAL_SPACING,
39
+ NAV_POSITION_TOP,
40
+ } from '../constants';
41
+
42
+ const propTypes = forbidExtraProps({
43
+ ...withStylesPropTypes,
44
+ ...DateRangePickerShape,
45
+ });
46
+
47
+ const defaultProps = {
48
+ // required props for a functional interactive DateRangePicker
49
+ startDate: null,
50
+ endDate: null,
51
+ focusedInput: null,
52
+
53
+ // input related props
54
+ startDatePlaceholderText: 'Start Date',
55
+ endDatePlaceholderText: 'End Date',
56
+ startDateAriaLabel: undefined,
57
+ endDateAriaLabel: undefined,
58
+ startDateTitleText: undefined,
59
+ endDateTitleText: undefined,
60
+ startDateOffset: undefined,
61
+ endDateOffset: undefined,
62
+ disabled: false,
63
+ required: false,
64
+ readOnly: false,
65
+ screenReaderInputMessage: '',
66
+ showClearDates: false,
67
+ showDefaultInputIcon: false,
68
+ inputIconPosition: ICON_BEFORE_POSITION,
69
+ customInputIcon: null,
70
+ customArrowIcon: null,
71
+ customCloseIcon: null,
72
+ noBorder: false,
73
+ block: false,
74
+ small: false,
75
+ regular: false,
76
+ keepFocusOnInput: false,
77
+
78
+ // calendar presentation and interaction related props
79
+ renderMonthText: null,
80
+ renderWeekHeaderElement: null,
81
+ orientation: HORIZONTAL_ORIENTATION,
82
+ anchorDirection: ANCHOR_LEFT,
83
+ openDirection: OPEN_DOWN,
84
+ horizontalMargin: 0,
85
+ withPortal: false,
86
+ withFullScreenPortal: false,
87
+ appendToBody: false,
88
+ disableScroll: false,
89
+ initialVisibleMonth: null,
90
+ numberOfMonths: 2,
91
+ keepOpenOnDateSelect: false,
92
+ reopenPickerOnClearDates: false,
93
+ renderCalendarInfo: null,
94
+ calendarInfoPosition: INFO_POSITION_BOTTOM,
95
+ hideKeyboardShortcutsPanel: false,
96
+ daySize: DAY_SIZE,
97
+ isRTL: false,
98
+ firstDayOfWeek: null,
99
+ verticalHeight: null,
100
+ transitionDuration: undefined,
101
+ verticalSpacing: DEFAULT_VERTICAL_SPACING,
102
+ autoComplete: 'off',
103
+ horizontalMonthPadding: undefined,
104
+
105
+ // navigation related props
106
+ dayPickerNavigationInlineStyles: null,
107
+ navPosition: NAV_POSITION_TOP,
108
+ navPrev: null,
109
+ navNext: null,
110
+ renderNavPrevButton: null,
111
+ renderNavNextButton: null,
112
+
113
+ onPrevMonthClick() {},
114
+ onNextMonthClick() {},
115
+
116
+ onClose() {},
117
+
118
+ // day presentation and interaction related props
119
+ renderCalendarDay: undefined,
120
+ renderDayContents: null,
121
+ renderMonthElement: null,
122
+ minimumNights: 1,
123
+ enableOutsideDays: false,
124
+ isDayBlocked: () => false,
125
+ isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()),
126
+ isDayHighlighted: () => false,
127
+ minDate: undefined,
128
+ maxDate: undefined,
129
+
130
+ // internationalization
131
+ displayFormat: () => moment.localeData().longDateFormat('L'),
132
+ monthFormat: 'MMMM YYYY',
133
+ weekDayFormat: 'dd',
134
+ phrases: DateRangePickerPhrases,
135
+ dayAriaLabelFormat: undefined,
136
+ };
137
+
138
+ class DateRangePicker extends React.PureComponent {
139
+ constructor(props) {
140
+ super(props);
141
+ this.state = {
142
+ dayPickerContainerStyles: {},
143
+ isDateRangePickerInputFocused: false,
144
+ isDayPickerFocused: false,
145
+ showKeyboardShortcuts: false,
146
+ };
147
+
148
+ this.isTouchDevice = false;
149
+
150
+ this.onOutsideClick = this.onOutsideClick.bind(this);
151
+ this.onDateRangePickerInputFocus = this.onDateRangePickerInputFocus.bind(this);
152
+ this.onDayPickerFocus = this.onDayPickerFocus.bind(this);
153
+ this.onDayPickerFocusOut = this.onDayPickerFocusOut.bind(this);
154
+ this.onDayPickerBlur = this.onDayPickerBlur.bind(this);
155
+ this.showKeyboardShortcutsPanel = this.showKeyboardShortcutsPanel.bind(this);
156
+
157
+ this.responsivizePickerPosition = this.responsivizePickerPosition.bind(this);
158
+ this.disableScroll = this.disableScroll.bind(this);
159
+
160
+ this.setDayPickerContainerRef = this.setDayPickerContainerRef.bind(this);
161
+ this.setContainerRef = this.setContainerRef.bind(this);
162
+ }
163
+
164
+ componentDidMount() {
165
+ this.removeEventListener = addEventListener(
166
+ window,
167
+ 'resize',
168
+ this.responsivizePickerPosition,
169
+ { passive: true },
170
+ );
171
+ this.responsivizePickerPosition();
172
+ this.disableScroll();
173
+
174
+ const { focusedInput } = this.props;
175
+ if (focusedInput) {
176
+ this.setState({
177
+ isDateRangePickerInputFocused: true,
178
+ });
179
+ }
180
+
181
+ this.isTouchDevice = isTouchDevice();
182
+ }
183
+
184
+ componentDidUpdate(prevProps) {
185
+ const { focusedInput } = this.props;
186
+ if (!prevProps.focusedInput && focusedInput && this.isOpened()) {
187
+ // The date picker just changed from being closed to being open.
188
+ this.responsivizePickerPosition();
189
+ this.disableScroll();
190
+ } else if (prevProps.focusedInput && !focusedInput && !this.isOpened()) {
191
+ // The date picker just changed from being open to being closed.
192
+ if (this.enableScroll) this.enableScroll();
193
+ }
194
+ }
195
+
196
+ componentWillUnmount() {
197
+ this.removeDayPickerEventListeners();
198
+ if (this.removeEventListener) this.removeEventListener();
199
+ if (this.enableScroll) this.enableScroll();
200
+ }
201
+
202
+ onOutsideClick(event) {
203
+ const {
204
+ onFocusChange,
205
+ onClose,
206
+ startDate,
207
+ endDate,
208
+ appendToBody,
209
+ } = this.props;
210
+
211
+ if (!this.isOpened()) return;
212
+ if (appendToBody && this.dayPickerContainer.contains(event.target)) return;
213
+
214
+ this.setState({
215
+ isDateRangePickerInputFocused: false,
216
+ isDayPickerFocused: false,
217
+ showKeyboardShortcuts: false,
218
+ });
219
+
220
+ onFocusChange(null);
221
+ onClose({ startDate, endDate });
222
+ }
223
+
224
+ onDateRangePickerInputFocus(focusedInput) {
225
+ const {
226
+ onFocusChange,
227
+ readOnly,
228
+ withPortal,
229
+ withFullScreenPortal,
230
+ keepFocusOnInput,
231
+ } = this.props;
232
+
233
+ if (focusedInput) {
234
+ const withAnyPortal = withPortal || withFullScreenPortal;
235
+ const moveFocusToDayPicker = withAnyPortal
236
+ || (readOnly && !keepFocusOnInput)
237
+ || (this.isTouchDevice && !keepFocusOnInput);
238
+
239
+ if (moveFocusToDayPicker) {
240
+ this.onDayPickerFocus();
241
+ } else {
242
+ this.onDayPickerBlur();
243
+ }
244
+ }
245
+
246
+ onFocusChange(focusedInput);
247
+ }
248
+
249
+ onDayPickerFocus() {
250
+ const { focusedInput, onFocusChange } = this.props;
251
+ if (!focusedInput) onFocusChange(START_DATE);
252
+
253
+ this.setState({
254
+ isDateRangePickerInputFocused: false,
255
+ isDayPickerFocused: true,
256
+ showKeyboardShortcuts: false,
257
+ });
258
+ }
259
+
260
+ onDayPickerFocusOut(event) {
261
+ // In cases where **relatedTarget** is not null, it points to the right
262
+ // element here. However, in cases where it is null (such as clicking on a
263
+ // specific day) or it is **document.body** (IE11), the appropriate value is **event.target**.
264
+ //
265
+ // We handle both situations here by using the ` || ` operator to fallback
266
+ // to *event.target** when **relatedTarget** is not provided.
267
+ const relatedTarget = event.relatedTarget === document.body
268
+ ? event.target
269
+ : (event.relatedTarget || event.target);
270
+ if (this.dayPickerContainer.contains(relatedTarget)) return;
271
+ this.onOutsideClick(event);
272
+ }
273
+
274
+ onDayPickerBlur() {
275
+ this.setState({
276
+ isDateRangePickerInputFocused: true,
277
+ isDayPickerFocused: false,
278
+ showKeyboardShortcuts: false,
279
+ });
280
+ }
281
+
282
+ setDayPickerContainerRef(ref) {
283
+ if (ref === this.dayPickerContainer) return;
284
+ if (this.dayPickerContainer) this.removeDayPickerEventListeners();
285
+
286
+ this.dayPickerContainer = ref;
287
+ if (!ref) return;
288
+
289
+ this.addDayPickerEventListeners();
290
+ }
291
+
292
+ setContainerRef(ref) {
293
+ this.container = ref;
294
+ }
295
+
296
+ addDayPickerEventListeners() {
297
+ // NOTE: We are using a manual event listener here, because React doesn't
298
+ // provide FocusOut, while blur and keydown don't provide the information
299
+ // needed in order to know whether we have left focus or not.
300
+ //
301
+ // For reference, this issue is further described here:
302
+ // - https://github.com/facebook/react/issues/6410
303
+ this.removeDayPickerFocusOut = addEventListener(
304
+ this.dayPickerContainer,
305
+ 'focusout',
306
+ this.onDayPickerFocusOut,
307
+ );
308
+ }
309
+
310
+ removeDayPickerEventListeners() {
311
+ if (this.removeDayPickerFocusOut) this.removeDayPickerFocusOut();
312
+ }
313
+
314
+ isOpened() {
315
+ const { focusedInput } = this.props;
316
+ return focusedInput === START_DATE || focusedInput === END_DATE;
317
+ }
318
+
319
+ disableScroll() {
320
+ const { appendToBody, disableScroll: propDisableScroll } = this.props;
321
+ if (!appendToBody && !propDisableScroll) return;
322
+ if (!this.isOpened()) return;
323
+
324
+ // Disable scroll for every ancestor of this DateRangePicker up to the
325
+ // document level. This ensures the input and the picker never move. Other
326
+ // sibling elements or the picker itself can scroll.
327
+ this.enableScroll = disableScroll(this.container);
328
+ }
329
+
330
+ responsivizePickerPosition() {
331
+ // It's possible the portal props have been changed in response to window resizes
332
+ // So let's ensure we reset this back to the base state each time
333
+ const { dayPickerContainerStyles } = this.state;
334
+
335
+ if (Object.keys(dayPickerContainerStyles).length > 0) {
336
+ this.setState({ dayPickerContainerStyles: {} });
337
+ }
338
+
339
+ if (!this.isOpened()) {
340
+ return;
341
+ }
342
+
343
+ const {
344
+ openDirection,
345
+ anchorDirection,
346
+ horizontalMargin,
347
+ withPortal,
348
+ withFullScreenPortal,
349
+ appendToBody,
350
+ } = this.props;
351
+
352
+ const isAnchoredLeft = anchorDirection === ANCHOR_LEFT;
353
+ if (!withPortal && !withFullScreenPortal) {
354
+ const containerRect = this.dayPickerContainer.getBoundingClientRect();
355
+ const currentOffset = dayPickerContainerStyles[anchorDirection] || 0;
356
+ const containerEdge = isAnchoredLeft
357
+ ? containerRect[ANCHOR_RIGHT]
358
+ : containerRect[ANCHOR_LEFT];
359
+
360
+ this.setState({
361
+ dayPickerContainerStyles: {
362
+ ...getResponsiveContainerStyles(
363
+ anchorDirection,
364
+ currentOffset,
365
+ containerEdge,
366
+ horizontalMargin,
367
+ ),
368
+ ...(appendToBody && getDetachedContainerStyles(
369
+ openDirection,
370
+ anchorDirection,
371
+ this.container,
372
+ )),
373
+ },
374
+ });
375
+ }
376
+ }
377
+
378
+ showKeyboardShortcutsPanel() {
379
+ this.setState({
380
+ isDateRangePickerInputFocused: false,
381
+ isDayPickerFocused: true,
382
+ showKeyboardShortcuts: true,
383
+ });
384
+ }
385
+
386
+ maybeRenderDayPickerWithPortal() {
387
+ const { withPortal, withFullScreenPortal, appendToBody } = this.props;
388
+
389
+ if (!this.isOpened()) {
390
+ return null;
391
+ }
392
+
393
+ if (withPortal || withFullScreenPortal || appendToBody) {
394
+ return (
395
+ <Portal>
396
+ {this.renderDayPicker()}
397
+ </Portal>
398
+ );
399
+ }
400
+
401
+ return this.renderDayPicker();
402
+ }
403
+
404
+ renderDayPicker() {
405
+ const {
406
+ anchorDirection,
407
+ openDirection,
408
+ isDayBlocked,
409
+ isDayHighlighted,
410
+ isOutsideRange,
411
+ numberOfMonths,
412
+ orientation,
413
+ monthFormat,
414
+ renderMonthText,
415
+ renderWeekHeaderElement,
416
+ dayPickerNavigationInlineStyles,
417
+ navPosition,
418
+ navPrev,
419
+ navNext,
420
+ renderNavPrevButton,
421
+ renderNavNextButton,
422
+ onPrevMonthClick,
423
+ onNextMonthClick,
424
+ onDatesChange,
425
+ onFocusChange,
426
+ withPortal,
427
+ withFullScreenPortal,
428
+ daySize,
429
+ enableOutsideDays,
430
+ focusedInput,
431
+ startDate,
432
+ startDateOffset,
433
+ endDate,
434
+ endDateOffset,
435
+ minDate,
436
+ maxDate,
437
+ minimumNights,
438
+ keepOpenOnDateSelect,
439
+ renderCalendarDay,
440
+ renderDayContents,
441
+ renderCalendarInfo,
442
+ renderMonthElement,
443
+ calendarInfoPosition,
444
+ firstDayOfWeek,
445
+ initialVisibleMonth,
446
+ hideKeyboardShortcutsPanel,
447
+ customCloseIcon,
448
+ onClose,
449
+ phrases,
450
+ dayAriaLabelFormat,
451
+ isRTL,
452
+ weekDayFormat,
453
+ css,
454
+ styles,
455
+ verticalHeight,
456
+ noBorder,
457
+ transitionDuration,
458
+ verticalSpacing,
459
+ horizontalMonthPadding,
460
+ small,
461
+ disabled,
462
+ theme: { reactDates },
463
+ } = this.props;
464
+
465
+ const { dayPickerContainerStyles, isDayPickerFocused, showKeyboardShortcuts } = this.state;
466
+
467
+ const onOutsideClick = (!withFullScreenPortal && withPortal)
468
+ ? this.onOutsideClick
469
+ : undefined;
470
+ const initialVisibleMonthThunk = initialVisibleMonth || (
471
+ () => (startDate || endDate || moment())
472
+ );
473
+
474
+ const closeIcon = customCloseIcon || (
475
+ <CloseButton {...css(styles.DateRangePicker_closeButton_svg)} />
476
+ );
477
+
478
+ const inputHeight = getInputHeight(reactDates, small);
479
+
480
+ const withAnyPortal = withPortal || withFullScreenPortal;
481
+
482
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
483
+ /* eslint-disable jsx-a11y/click-events-have-key-events */
484
+ return (
485
+ <div
486
+ key="day-picker"
487
+ ref={this.setDayPickerContainerRef}
488
+ {...css(
489
+ styles.DateRangePicker_picker,
490
+ anchorDirection === ANCHOR_LEFT && styles.DateRangePicker_picker__directionLeft,
491
+ anchorDirection === ANCHOR_RIGHT && styles.DateRangePicker_picker__directionRight,
492
+ orientation === HORIZONTAL_ORIENTATION && styles.DateRangePicker_picker__horizontal,
493
+ orientation === VERTICAL_ORIENTATION && styles.DateRangePicker_picker__vertical,
494
+ !withAnyPortal && openDirection === OPEN_DOWN && {
495
+ top: inputHeight + verticalSpacing,
496
+ },
497
+ !withAnyPortal && openDirection === OPEN_UP && {
498
+ bottom: inputHeight + verticalSpacing,
499
+ },
500
+ withAnyPortal && styles.DateRangePicker_picker__portal,
501
+ withFullScreenPortal && styles.DateRangePicker_picker__fullScreenPortal,
502
+ isRTL && styles.DateRangePicker_picker__rtl,
503
+ dayPickerContainerStyles,
504
+ )}
505
+ onClick={onOutsideClick}
506
+ >
507
+ <DayPickerRangeController
508
+ orientation={orientation}
509
+ enableOutsideDays={enableOutsideDays}
510
+ numberOfMonths={numberOfMonths}
511
+ onPrevMonthClick={onPrevMonthClick}
512
+ onNextMonthClick={onNextMonthClick}
513
+ onDatesChange={onDatesChange}
514
+ onFocusChange={onFocusChange}
515
+ onClose={onClose}
516
+ focusedInput={focusedInput}
517
+ startDate={startDate}
518
+ startDateOffset={startDateOffset}
519
+ endDate={endDate}
520
+ endDateOffset={endDateOffset}
521
+ minDate={minDate}
522
+ maxDate={maxDate}
523
+ monthFormat={monthFormat}
524
+ renderMonthText={renderMonthText}
525
+ renderWeekHeaderElement={renderWeekHeaderElement}
526
+ withPortal={withAnyPortal}
527
+ daySize={daySize}
528
+ initialVisibleMonth={initialVisibleMonthThunk}
529
+ hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel}
530
+ dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles}
531
+ navPosition={navPosition}
532
+ navPrev={navPrev}
533
+ navNext={navNext}
534
+ renderNavPrevButton={renderNavPrevButton}
535
+ renderNavNextButton={renderNavNextButton}
536
+ minimumNights={minimumNights}
537
+ isOutsideRange={isOutsideRange}
538
+ isDayHighlighted={isDayHighlighted}
539
+ isDayBlocked={isDayBlocked}
540
+ keepOpenOnDateSelect={keepOpenOnDateSelect}
541
+ renderCalendarDay={renderCalendarDay}
542
+ renderDayContents={renderDayContents}
543
+ renderCalendarInfo={renderCalendarInfo}
544
+ renderMonthElement={renderMonthElement}
545
+ calendarInfoPosition={calendarInfoPosition}
546
+ isFocused={isDayPickerFocused}
547
+ showKeyboardShortcuts={showKeyboardShortcuts}
548
+ onBlur={this.onDayPickerBlur}
549
+ phrases={phrases}
550
+ dayAriaLabelFormat={dayAriaLabelFormat}
551
+ isRTL={isRTL}
552
+ firstDayOfWeek={firstDayOfWeek}
553
+ weekDayFormat={weekDayFormat}
554
+ verticalHeight={verticalHeight}
555
+ noBorder={noBorder}
556
+ transitionDuration={transitionDuration}
557
+ disabled={disabled}
558
+ horizontalMonthPadding={horizontalMonthPadding}
559
+ />
560
+
561
+ {withFullScreenPortal && (
562
+ <button
563
+ {...css(styles.DateRangePicker_closeButton)}
564
+ type="button"
565
+ onClick={this.onOutsideClick}
566
+ aria-label={phrases.closeDatePicker}
567
+ tabIndex="-1"
568
+ >
569
+ {closeIcon}
570
+ </button>
571
+ )}
572
+ </div>
573
+ );
574
+ /* eslint-enable jsx-a11y/no-static-element-interactions */
575
+ /* eslint-enable jsx-a11y/click-events-have-key-events */
576
+ }
577
+
578
+ render() {
579
+ const {
580
+ startDate,
581
+ startDateId,
582
+ startDatePlaceholderText,
583
+ startDateAriaLabel,
584
+ startDateTitleText,
585
+ endDate,
586
+ endDateId,
587
+ endDatePlaceholderText,
588
+ endDateAriaLabel,
589
+ endDateTitleText,
590
+ focusedInput,
591
+ screenReaderInputMessage,
592
+ showClearDates,
593
+ showDefaultInputIcon,
594
+ inputIconPosition,
595
+ customInputIcon,
596
+ customArrowIcon,
597
+ customCloseIcon,
598
+ disabled,
599
+ required,
600
+ readOnly,
601
+ autoComplete,
602
+ openDirection,
603
+ phrases,
604
+ isOutsideRange,
605
+ isDayBlocked,
606
+ minimumNights,
607
+ withPortal,
608
+ withFullScreenPortal,
609
+ displayFormat,
610
+ reopenPickerOnClearDates,
611
+ keepOpenOnDateSelect,
612
+ onDatesChange,
613
+ onClose,
614
+ isRTL,
615
+ noBorder,
616
+ block,
617
+ verticalSpacing,
618
+ small,
619
+ regular,
620
+ css,
621
+ styles,
622
+ } = this.props;
623
+
624
+ const { isDateRangePickerInputFocused } = this.state;
625
+
626
+ const enableOutsideClick = (!withPortal && !withFullScreenPortal);
627
+
628
+ const hideFang = verticalSpacing < FANG_HEIGHT_PX;
629
+
630
+ const input = (
631
+ <DateRangePickerInputController
632
+ startDate={startDate}
633
+ startDateId={startDateId}
634
+ startDatePlaceholderText={startDatePlaceholderText}
635
+ isStartDateFocused={focusedInput === START_DATE}
636
+ startDateAriaLabel={startDateAriaLabel}
637
+ startDateTitleText={startDateTitleText}
638
+ endDate={endDate}
639
+ endDateId={endDateId}
640
+ endDatePlaceholderText={endDatePlaceholderText}
641
+ isEndDateFocused={focusedInput === END_DATE}
642
+ endDateAriaLabel={endDateAriaLabel}
643
+ endDateTitleText={endDateTitleText}
644
+ displayFormat={displayFormat}
645
+ showClearDates={showClearDates}
646
+ showCaret={!withPortal && !withFullScreenPortal && !hideFang}
647
+ showDefaultInputIcon={showDefaultInputIcon}
648
+ inputIconPosition={inputIconPosition}
649
+ customInputIcon={customInputIcon}
650
+ customArrowIcon={customArrowIcon}
651
+ customCloseIcon={customCloseIcon}
652
+ disabled={disabled}
653
+ required={required}
654
+ readOnly={readOnly}
655
+ openDirection={openDirection}
656
+ reopenPickerOnClearDates={reopenPickerOnClearDates}
657
+ keepOpenOnDateSelect={keepOpenOnDateSelect}
658
+ isOutsideRange={isOutsideRange}
659
+ isDayBlocked={isDayBlocked}
660
+ minimumNights={minimumNights}
661
+ withFullScreenPortal={withFullScreenPortal}
662
+ onDatesChange={onDatesChange}
663
+ onFocusChange={this.onDateRangePickerInputFocus}
664
+ onKeyDownArrowDown={this.onDayPickerFocus}
665
+ onKeyDownQuestionMark={this.showKeyboardShortcutsPanel}
666
+ onClose={onClose}
667
+ phrases={phrases}
668
+ screenReaderMessage={screenReaderInputMessage}
669
+ isFocused={isDateRangePickerInputFocused}
670
+ isRTL={isRTL}
671
+ noBorder={noBorder}
672
+ block={block}
673
+ small={small}
674
+ regular={regular}
675
+ verticalSpacing={verticalSpacing}
676
+ autoComplete={autoComplete}
677
+ >
678
+ {this.maybeRenderDayPickerWithPortal()}
679
+ </DateRangePickerInputController>
680
+ );
681
+
682
+ return (
683
+ <div
684
+ ref={this.setContainerRef}
685
+ {...css(
686
+ styles.DateRangePicker,
687
+ block && styles.DateRangePicker__block,
688
+ )}
689
+ >
690
+ {enableOutsideClick && (
691
+ <OutsideClickHandler onOutsideClick={this.onOutsideClick}>
692
+ {input}
693
+ </OutsideClickHandler>
694
+ )}
695
+ {enableOutsideClick || input}
696
+ </div>
697
+ );
698
+ }
699
+ }
700
+
701
+ DateRangePicker.propTypes = propTypes;
702
+ DateRangePicker.defaultProps = defaultProps;
703
+
704
+ export { DateRangePicker as PureDateRangePicker };
705
+ export default withStyles(({ reactDates: { color, zIndex } }) => ({
706
+ DateRangePicker: {
707
+ position: 'relative',
708
+ display: 'inline-block',
709
+ },
710
+
711
+ DateRangePicker__block: {
712
+ display: 'block',
713
+ },
714
+
715
+ DateRangePicker_picker: {
716
+ zIndex: zIndex + 1,
717
+ backgroundColor: color.background,
718
+ position: 'absolute',
719
+ },
720
+
721
+ DateRangePicker_picker__rtl: {
722
+ direction: noflip('rtl'),
723
+ },
724
+
725
+ DateRangePicker_picker__directionLeft: {
726
+ left: noflip(0),
727
+ },
728
+
729
+ DateRangePicker_picker__directionRight: {
730
+ right: noflip(0),
731
+ },
732
+
733
+ DateRangePicker_picker__portal: {
734
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
735
+ position: 'fixed',
736
+ top: 0,
737
+ left: noflip(0),
738
+ height: '100%',
739
+ width: '100%',
740
+ },
741
+
742
+ DateRangePicker_picker__fullScreenPortal: {
743
+ backgroundColor: color.background,
744
+ },
745
+
746
+ DateRangePicker_closeButton: {
747
+ background: 'none',
748
+ border: 0,
749
+ color: 'inherit',
750
+ font: 'inherit',
751
+ lineHeight: 'normal',
752
+ overflow: 'visible',
753
+ cursor: 'pointer',
754
+
755
+ position: 'absolute',
756
+ top: 0,
757
+ right: noflip(0),
758
+ padding: 15,
759
+ zIndex: zIndex + 2,
760
+
761
+ ':hover': {
762
+ color: darken(color.core.grayLighter, 0.1),
763
+ textDecoration: 'none',
764
+ },
765
+
766
+ ':focus': {
767
+ color: darken(color.core.grayLighter, 0.1),
768
+ textDecoration: 'none',
769
+ },
770
+ },
771
+
772
+ DateRangePicker_closeButton_svg: {
773
+ height: 15,
774
+ width: 15,
775
+ fill: color.core.grayLighter,
776
+ },
777
+ }), { pureComponent: typeof React.PureComponent !== 'undefined' })(DateRangePicker);