@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,731 @@
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 SingleDatePickerShape from '../shapes/SingleDatePickerShape';
12
+ import { SingleDatePickerPhrases } 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 SingleDatePickerInputController from './SingleDatePickerInputController';
22
+ import DayPickerSingleDateController from './DayPickerSingleDateController';
23
+ import CloseButton from './CloseButton';
24
+
25
+ import {
26
+ HORIZONTAL_ORIENTATION,
27
+ VERTICAL_ORIENTATION,
28
+ ANCHOR_LEFT,
29
+ ANCHOR_RIGHT,
30
+ OPEN_DOWN,
31
+ OPEN_UP,
32
+ DAY_SIZE,
33
+ ICON_BEFORE_POSITION,
34
+ INFO_POSITION_BOTTOM,
35
+ FANG_HEIGHT_PX,
36
+ DEFAULT_VERTICAL_SPACING,
37
+ NAV_POSITION_TOP,
38
+ } from '../constants';
39
+
40
+ const propTypes = forbidExtraProps({
41
+ ...withStylesPropTypes,
42
+ ...SingleDatePickerShape,
43
+ });
44
+
45
+ const defaultProps = {
46
+ // required props for a functional interactive SingleDatePicker
47
+ date: null,
48
+ focused: false,
49
+ minDate: null,
50
+ maxDate: null,
51
+
52
+ // input related props
53
+ id: 'date',
54
+ placeholder: 'Date',
55
+ ariaLabel: undefined,
56
+ autoComplete: 'off',
57
+ titleText: undefined,
58
+ disabled: false,
59
+ required: false,
60
+ readOnly: false,
61
+ screenReaderInputMessage: '',
62
+ showClearDate: false,
63
+ showDefaultInputIcon: false,
64
+ inputIconPosition: ICON_BEFORE_POSITION,
65
+ customInputIcon: null,
66
+ customCloseIcon: null,
67
+ noBorder: false,
68
+ block: false,
69
+ small: false,
70
+ regular: false,
71
+ verticalSpacing: DEFAULT_VERTICAL_SPACING,
72
+ keepFocusOnInput: false,
73
+
74
+ // calendar presentation and interaction related props
75
+ orientation: HORIZONTAL_ORIENTATION,
76
+ anchorDirection: ANCHOR_LEFT,
77
+ openDirection: OPEN_DOWN,
78
+ horizontalMargin: 0,
79
+ withPortal: false,
80
+ withFullScreenPortal: false,
81
+ appendToBody: false,
82
+ disableScroll: false,
83
+ initialVisibleMonth: null,
84
+ firstDayOfWeek: null,
85
+ numberOfMonths: 2,
86
+ keepOpenOnDateSelect: false,
87
+ reopenPickerOnClearDate: false,
88
+ renderCalendarInfo: null,
89
+ calendarInfoPosition: INFO_POSITION_BOTTOM,
90
+ hideKeyboardShortcutsPanel: false,
91
+ daySize: DAY_SIZE,
92
+ isRTL: false,
93
+ verticalHeight: null,
94
+ transitionDuration: undefined,
95
+ horizontalMonthPadding: 13,
96
+
97
+ // navigation related props
98
+ dayPickerNavigationInlineStyles: null,
99
+ navPosition: NAV_POSITION_TOP,
100
+ navPrev: null,
101
+ navNext: null,
102
+ renderNavPrevButton: null,
103
+ renderNavNextButton: null,
104
+
105
+ onPrevMonthClick() {},
106
+ onNextMonthClick() {},
107
+ onClose() {},
108
+
109
+ // month presentation and interaction related props
110
+ renderMonthText: null,
111
+ renderWeekHeaderElement: null,
112
+
113
+ // day presentation and interaction related props
114
+ renderCalendarDay: undefined,
115
+ renderDayContents: null,
116
+ renderMonthElement: null,
117
+ enableOutsideDays: false,
118
+ isDayBlocked: () => false,
119
+ isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()),
120
+ isDayHighlighted: () => {},
121
+
122
+ // internationalization props
123
+ displayFormat: () => moment.localeData().longDateFormat('L'),
124
+ monthFormat: 'MMMM YYYY',
125
+ weekDayFormat: 'dd',
126
+ phrases: SingleDatePickerPhrases,
127
+ dayAriaLabelFormat: undefined,
128
+ };
129
+
130
+ class SingleDatePicker extends React.PureComponent {
131
+ constructor(props) {
132
+ super(props);
133
+
134
+ this.isTouchDevice = false;
135
+
136
+ this.state = {
137
+ dayPickerContainerStyles: {},
138
+ isDayPickerFocused: false,
139
+ isInputFocused: false,
140
+ showKeyboardShortcuts: false,
141
+ };
142
+
143
+ this.onFocusOut = this.onFocusOut.bind(this);
144
+ this.onOutsideClick = this.onOutsideClick.bind(this);
145
+ this.onInputFocus = this.onInputFocus.bind(this);
146
+ this.onDayPickerFocus = this.onDayPickerFocus.bind(this);
147
+ this.onDayPickerBlur = this.onDayPickerBlur.bind(this);
148
+ this.showKeyboardShortcutsPanel = this.showKeyboardShortcutsPanel.bind(this);
149
+
150
+ this.responsivizePickerPosition = this.responsivizePickerPosition.bind(this);
151
+ this.disableScroll = this.disableScroll.bind(this);
152
+
153
+ this.setDayPickerContainerRef = this.setDayPickerContainerRef.bind(this);
154
+ this.setContainerRef = this.setContainerRef.bind(this);
155
+ }
156
+
157
+ /* istanbul ignore next */
158
+ componentDidMount() {
159
+ this.removeResizeEventListener = addEventListener(
160
+ window,
161
+ 'resize',
162
+ this.responsivizePickerPosition,
163
+ { passive: true },
164
+ );
165
+
166
+ this.responsivizePickerPosition();
167
+ this.disableScroll();
168
+
169
+ const { focused } = this.props;
170
+
171
+ if (focused) {
172
+ this.setState({
173
+ isInputFocused: true,
174
+ });
175
+ }
176
+
177
+ this.isTouchDevice = isTouchDevice();
178
+ }
179
+
180
+ componentDidUpdate(prevProps) {
181
+ const { focused } = this.props;
182
+ if (!prevProps.focused && focused) {
183
+ this.responsivizePickerPosition();
184
+ this.disableScroll();
185
+ } else if (prevProps.focused && !focused) {
186
+ if (this.enableScroll) this.enableScroll();
187
+ }
188
+ }
189
+
190
+ /* istanbul ignore next */
191
+ componentWillUnmount() {
192
+ if (this.removeResizeEventListener) this.removeResizeEventListener();
193
+ if (this.removeFocusOutEventListener) this.removeFocusOutEventListener();
194
+ if (this.enableScroll) this.enableScroll();
195
+ }
196
+
197
+ onOutsideClick(event) {
198
+ const {
199
+ focused,
200
+ onFocusChange,
201
+ onClose,
202
+ date,
203
+ appendToBody,
204
+ } = this.props;
205
+
206
+ if (!focused) return;
207
+ if (appendToBody && this.dayPickerContainer.contains(event.target)) return;
208
+
209
+ this.setState({
210
+ isInputFocused: false,
211
+ isDayPickerFocused: false,
212
+ showKeyboardShortcuts: false,
213
+ });
214
+
215
+ onFocusChange({ focused: false });
216
+ onClose({ date });
217
+ }
218
+
219
+ onInputFocus({ focused }) {
220
+ const {
221
+ onFocusChange,
222
+ readOnly,
223
+ withPortal,
224
+ withFullScreenPortal,
225
+ keepFocusOnInput,
226
+ } = this.props;
227
+
228
+ if (focused) {
229
+ const withAnyPortal = withPortal || withFullScreenPortal;
230
+ const moveFocusToDayPicker = withAnyPortal
231
+ || (readOnly && !keepFocusOnInput)
232
+ || (this.isTouchDevice && !keepFocusOnInput);
233
+
234
+ if (moveFocusToDayPicker) {
235
+ this.onDayPickerFocus();
236
+ } else {
237
+ this.onDayPickerBlur();
238
+ }
239
+ }
240
+
241
+ onFocusChange({ focused });
242
+ }
243
+
244
+ onDayPickerFocus() {
245
+ this.setState({
246
+ isInputFocused: false,
247
+ isDayPickerFocused: true,
248
+ showKeyboardShortcuts: false,
249
+ });
250
+ }
251
+
252
+ onDayPickerBlur() {
253
+ this.setState({
254
+ isInputFocused: true,
255
+ isDayPickerFocused: false,
256
+ showKeyboardShortcuts: false,
257
+ });
258
+ }
259
+
260
+ onFocusOut(e) {
261
+ const { onFocusChange } = this.props;
262
+ // In cases where **relatedTarget** is not null, it points to the right
263
+ // element here. However, in cases where it is null (such as clicking on a
264
+ // specific day) or it is **document.body** (IE11), the appropriate value is **event.target**.
265
+ //
266
+ // We handle both situations here by using the ` || ` operator to fallback
267
+ // to *event.target** when **relatedTarget** is not provided.
268
+ const relatedTarget = e.relatedTarget === document.body
269
+ ? e.target
270
+ : (e.relatedTarget || e.target);
271
+ if (this.dayPickerContainer.contains(relatedTarget)) return;
272
+ onFocusChange({ focused: false });
273
+ }
274
+
275
+ setDayPickerContainerRef(ref) {
276
+ if (ref === this.dayPickerContainer) return;
277
+ this.removeEventListeners();
278
+
279
+ this.dayPickerContainer = ref;
280
+ if (!ref) return;
281
+
282
+ this.addEventListeners();
283
+ }
284
+
285
+ setContainerRef(ref) {
286
+ this.container = ref;
287
+ }
288
+
289
+ addEventListeners() {
290
+ // We manually set event because React has not implemented onFocusIn/onFocusOut.
291
+ // Keep an eye on https://github.com/facebook/react/issues/6410 for updates
292
+ // We use "blur w/ useCapture param" vs "onfocusout" for FF browser support
293
+ this.removeFocusOutEventListener = addEventListener(
294
+ this.dayPickerContainer,
295
+ 'focusout',
296
+ this.onFocusOut,
297
+ );
298
+ }
299
+
300
+ removeEventListeners() {
301
+ if (this.removeFocusOutEventListener) this.removeFocusOutEventListener();
302
+ }
303
+
304
+ disableScroll() {
305
+ const { appendToBody, disableScroll: propDisableScroll, focused } = this.props;
306
+ if (!appendToBody && !propDisableScroll) return;
307
+ if (!focused) return;
308
+
309
+ // Disable scroll for every ancestor of this <SingleDatePicker> up to the
310
+ // document level. This ensures the input and the picker never move. Other
311
+ // sibling elements or the picker itself can scroll.
312
+ this.enableScroll = disableScroll(this.container);
313
+ }
314
+
315
+ /* istanbul ignore next */
316
+ responsivizePickerPosition() {
317
+ // It's possible the portal props have been changed in response to window resizes
318
+ // So let's ensure we reset this back to the base state each time
319
+ this.setState({ dayPickerContainerStyles: {} });
320
+
321
+ const {
322
+ openDirection,
323
+ anchorDirection,
324
+ horizontalMargin,
325
+ withPortal,
326
+ withFullScreenPortal,
327
+ appendToBody,
328
+ focused,
329
+ } = this.props;
330
+
331
+ const { dayPickerContainerStyles } = this.state;
332
+
333
+ if (!focused) {
334
+ return;
335
+ }
336
+
337
+ const isAnchoredLeft = anchorDirection === ANCHOR_LEFT;
338
+
339
+ if (!withPortal && !withFullScreenPortal) {
340
+ const containerRect = this.dayPickerContainer.getBoundingClientRect();
341
+ const currentOffset = dayPickerContainerStyles[anchorDirection] || 0;
342
+ const containerEdge = isAnchoredLeft
343
+ ? containerRect[ANCHOR_RIGHT]
344
+ : containerRect[ANCHOR_LEFT];
345
+
346
+ this.setState({
347
+ dayPickerContainerStyles: {
348
+ ...getResponsiveContainerStyles(
349
+ anchorDirection,
350
+ currentOffset,
351
+ containerEdge,
352
+ horizontalMargin,
353
+ ),
354
+ ...(appendToBody && getDetachedContainerStyles(
355
+ openDirection,
356
+ anchorDirection,
357
+ this.container,
358
+ )),
359
+ },
360
+ });
361
+ }
362
+ }
363
+
364
+ showKeyboardShortcutsPanel() {
365
+ this.setState({
366
+ isInputFocused: false,
367
+ isDayPickerFocused: true,
368
+ showKeyboardShortcuts: true,
369
+ });
370
+ }
371
+
372
+ maybeRenderDayPickerWithPortal() {
373
+ const {
374
+ focused,
375
+ withPortal,
376
+ withFullScreenPortal,
377
+ appendToBody,
378
+ } = this.props;
379
+
380
+ if (!focused) {
381
+ return null;
382
+ }
383
+
384
+ if (withPortal || withFullScreenPortal || appendToBody) {
385
+ return (
386
+ <Portal>
387
+ {this.renderDayPicker()}
388
+ </Portal>
389
+ );
390
+ }
391
+
392
+ return this.renderDayPicker();
393
+ }
394
+
395
+ renderDayPicker() {
396
+ const {
397
+ anchorDirection,
398
+ openDirection,
399
+ onDateChange,
400
+ date,
401
+ minDate,
402
+ maxDate,
403
+ onFocusChange,
404
+ focused,
405
+ enableOutsideDays,
406
+ numberOfMonths,
407
+ orientation,
408
+ monthFormat,
409
+ dayPickerNavigationInlineStyles,
410
+ navPosition,
411
+ navPrev,
412
+ navNext,
413
+ renderNavPrevButton,
414
+ renderNavNextButton,
415
+ onPrevMonthClick,
416
+ onNextMonthClick,
417
+ onClose,
418
+ withPortal,
419
+ withFullScreenPortal,
420
+ keepOpenOnDateSelect,
421
+ initialVisibleMonth,
422
+ renderMonthText,
423
+ renderWeekHeaderElement,
424
+ renderCalendarDay,
425
+ renderDayContents,
426
+ renderCalendarInfo,
427
+ renderMonthElement,
428
+ calendarInfoPosition,
429
+ hideKeyboardShortcutsPanel,
430
+ firstDayOfWeek,
431
+ customCloseIcon,
432
+ phrases,
433
+ dayAriaLabelFormat,
434
+ daySize,
435
+ isRTL,
436
+ isOutsideRange,
437
+ isDayBlocked,
438
+ isDayHighlighted,
439
+ weekDayFormat,
440
+ css,
441
+ styles,
442
+ verticalHeight,
443
+ transitionDuration,
444
+ verticalSpacing,
445
+ horizontalMonthPadding,
446
+ small,
447
+ theme: { reactDates },
448
+ } = this.props;
449
+ const { dayPickerContainerStyles, isDayPickerFocused, showKeyboardShortcuts } = this.state;
450
+
451
+ const onOutsideClick = (!withFullScreenPortal && withPortal) ? this.onOutsideClick : undefined;
452
+ const closeIcon = customCloseIcon || (<CloseButton />);
453
+
454
+ const inputHeight = getInputHeight(reactDates, small);
455
+
456
+ const withAnyPortal = withPortal || withFullScreenPortal;
457
+
458
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
459
+ /* eslint-disable jsx-a11y/click-events-have-key-events */
460
+ return (
461
+ <div
462
+ ref={this.setDayPickerContainerRef}
463
+ {...css(
464
+ styles.SingleDatePicker_picker,
465
+ anchorDirection === ANCHOR_LEFT && styles.SingleDatePicker_picker__directionLeft,
466
+ anchorDirection === ANCHOR_RIGHT && styles.SingleDatePicker_picker__directionRight,
467
+ openDirection === OPEN_DOWN && styles.SingleDatePicker_picker__openDown,
468
+ openDirection === OPEN_UP && styles.SingleDatePicker_picker__openUp,
469
+ !withAnyPortal && openDirection === OPEN_DOWN && {
470
+ top: inputHeight + verticalSpacing,
471
+ },
472
+ !withAnyPortal && openDirection === OPEN_UP && {
473
+ bottom: inputHeight + verticalSpacing,
474
+ },
475
+ orientation === HORIZONTAL_ORIENTATION && styles.SingleDatePicker_picker__horizontal,
476
+ orientation === VERTICAL_ORIENTATION && styles.SingleDatePicker_picker__vertical,
477
+ withAnyPortal && styles.SingleDatePicker_picker__portal,
478
+ withFullScreenPortal && styles.SingleDatePicker_picker__fullScreenPortal,
479
+ isRTL && styles.SingleDatePicker_picker__rtl,
480
+ dayPickerContainerStyles,
481
+ )}
482
+ onClick={onOutsideClick}
483
+ >
484
+ <DayPickerSingleDateController
485
+ date={date}
486
+ minDate={minDate}
487
+ maxDate={maxDate}
488
+ onDateChange={onDateChange}
489
+ onFocusChange={onFocusChange}
490
+ orientation={orientation}
491
+ enableOutsideDays={enableOutsideDays}
492
+ numberOfMonths={numberOfMonths}
493
+ monthFormat={monthFormat}
494
+ withPortal={withAnyPortal}
495
+ focused={focused}
496
+ keepOpenOnDateSelect={keepOpenOnDateSelect}
497
+ hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel}
498
+ initialVisibleMonth={initialVisibleMonth}
499
+ dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles}
500
+ navPosition={navPosition}
501
+ navPrev={navPrev}
502
+ navNext={navNext}
503
+ renderNavPrevButton={renderNavPrevButton}
504
+ renderNavNextButton={renderNavNextButton}
505
+ onPrevMonthClick={onPrevMonthClick}
506
+ onNextMonthClick={onNextMonthClick}
507
+ onClose={onClose}
508
+ renderMonthText={renderMonthText}
509
+ renderWeekHeaderElement={renderWeekHeaderElement}
510
+ renderCalendarDay={renderCalendarDay}
511
+ renderDayContents={renderDayContents}
512
+ renderCalendarInfo={renderCalendarInfo}
513
+ renderMonthElement={renderMonthElement}
514
+ calendarInfoPosition={calendarInfoPosition}
515
+ isFocused={isDayPickerFocused}
516
+ showKeyboardShortcuts={showKeyboardShortcuts}
517
+ onBlur={this.onDayPickerBlur}
518
+ phrases={phrases}
519
+ dayAriaLabelFormat={dayAriaLabelFormat}
520
+ daySize={daySize}
521
+ isRTL={isRTL}
522
+ isOutsideRange={isOutsideRange}
523
+ isDayBlocked={isDayBlocked}
524
+ isDayHighlighted={isDayHighlighted}
525
+ firstDayOfWeek={firstDayOfWeek}
526
+ weekDayFormat={weekDayFormat}
527
+ verticalHeight={verticalHeight}
528
+ transitionDuration={transitionDuration}
529
+ horizontalMonthPadding={horizontalMonthPadding}
530
+ />
531
+
532
+ {withFullScreenPortal && (
533
+ <button
534
+ {...css(styles.SingleDatePicker_closeButton)}
535
+ aria-label={phrases.closeDatePicker}
536
+ type="button"
537
+ onClick={this.onOutsideClick}
538
+ >
539
+ <div {...css(styles.SingleDatePicker_closeButton_svg)}>
540
+ {closeIcon}
541
+ </div>
542
+ </button>
543
+ )}
544
+ </div>
545
+ );
546
+ /* eslint-enable jsx-a11y/no-static-element-interactions */
547
+ /* eslint-enable jsx-a11y/click-events-have-key-events */
548
+ }
549
+
550
+ render() {
551
+ const {
552
+ id,
553
+ placeholder,
554
+ ariaLabel,
555
+ autoComplete,
556
+ titleText,
557
+ disabled,
558
+ focused,
559
+ required,
560
+ readOnly,
561
+ openDirection,
562
+ showClearDate,
563
+ showDefaultInputIcon,
564
+ inputIconPosition,
565
+ customCloseIcon,
566
+ customInputIcon,
567
+ date,
568
+ onDateChange,
569
+ displayFormat,
570
+ phrases,
571
+ withPortal,
572
+ withFullScreenPortal,
573
+ screenReaderInputMessage,
574
+ isRTL,
575
+ noBorder,
576
+ block,
577
+ small,
578
+ regular,
579
+ verticalSpacing,
580
+ reopenPickerOnClearDate,
581
+ keepOpenOnDateSelect,
582
+ css,
583
+ styles,
584
+ isOutsideRange,
585
+ isDayBlocked,
586
+ } = this.props;
587
+
588
+ const { isInputFocused } = this.state;
589
+
590
+ const enableOutsideClick = (!withPortal && !withFullScreenPortal);
591
+
592
+ const hideFang = verticalSpacing < FANG_HEIGHT_PX;
593
+
594
+ const input = (
595
+ <SingleDatePickerInputController
596
+ id={id}
597
+ placeholder={placeholder}
598
+ ariaLabel={ariaLabel}
599
+ autoComplete={autoComplete}
600
+ titleText={titleText}
601
+ focused={focused}
602
+ isFocused={isInputFocused}
603
+ disabled={disabled}
604
+ required={required}
605
+ readOnly={readOnly}
606
+ openDirection={openDirection}
607
+ showCaret={!withPortal && !withFullScreenPortal && !hideFang}
608
+ showClearDate={showClearDate}
609
+ showDefaultInputIcon={showDefaultInputIcon}
610
+ inputIconPosition={inputIconPosition}
611
+ isOutsideRange={isOutsideRange}
612
+ isDayBlocked={isDayBlocked}
613
+ customCloseIcon={customCloseIcon}
614
+ customInputIcon={customInputIcon}
615
+ date={date}
616
+ onDateChange={onDateChange}
617
+ displayFormat={displayFormat}
618
+ onFocusChange={this.onInputFocus}
619
+ onKeyDownArrowDown={this.onDayPickerFocus}
620
+ onKeyDownQuestionMark={this.showKeyboardShortcutsPanel}
621
+ screenReaderMessage={screenReaderInputMessage}
622
+ phrases={phrases}
623
+ isRTL={isRTL}
624
+ noBorder={noBorder}
625
+ block={block}
626
+ small={small}
627
+ regular={regular}
628
+ verticalSpacing={verticalSpacing}
629
+ reopenPickerOnClearDate={reopenPickerOnClearDate}
630
+ keepOpenOnDateSelect={keepOpenOnDateSelect}
631
+ >
632
+ {this.maybeRenderDayPickerWithPortal()}
633
+ </SingleDatePickerInputController>
634
+ );
635
+
636
+ return (
637
+ <div
638
+ ref={this.setContainerRef}
639
+ {...css(
640
+ styles.SingleDatePicker,
641
+ block && styles.SingleDatePicker__block,
642
+ )}
643
+ >
644
+ {enableOutsideClick && (
645
+ <OutsideClickHandler onOutsideClick={this.onOutsideClick}>
646
+ {input}
647
+ </OutsideClickHandler>
648
+ )}
649
+ {enableOutsideClick || input}
650
+ </div>
651
+ );
652
+ }
653
+ }
654
+
655
+ SingleDatePicker.propTypes = propTypes;
656
+ SingleDatePicker.defaultProps = defaultProps;
657
+
658
+ export { SingleDatePicker as PureSingleDatePicker };
659
+ export default withStyles(({ reactDates: { color, zIndex } }) => ({
660
+ SingleDatePicker: {
661
+ position: 'relative',
662
+ display: 'inline-block',
663
+ },
664
+
665
+ SingleDatePicker__block: {
666
+ display: 'block',
667
+ },
668
+
669
+ SingleDatePicker_picker: {
670
+ zIndex: zIndex + 1,
671
+ backgroundColor: color.background,
672
+ position: 'absolute',
673
+ },
674
+
675
+ SingleDatePicker_picker__rtl: {
676
+ direction: noflip('rtl'),
677
+ },
678
+
679
+ SingleDatePicker_picker__directionLeft: {
680
+ left: noflip(0),
681
+ },
682
+
683
+ SingleDatePicker_picker__directionRight: {
684
+ right: noflip(0),
685
+ },
686
+
687
+ SingleDatePicker_picker__portal: {
688
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
689
+ position: 'fixed',
690
+ top: 0,
691
+ left: noflip(0),
692
+ height: '100%',
693
+ width: '100%',
694
+ },
695
+
696
+ SingleDatePicker_picker__fullScreenPortal: {
697
+ backgroundColor: color.background,
698
+ },
699
+
700
+ SingleDatePicker_closeButton: {
701
+ background: 'none',
702
+ border: 0,
703
+ color: 'inherit',
704
+ font: 'inherit',
705
+ lineHeight: 'normal',
706
+ overflow: 'visible',
707
+ cursor: 'pointer',
708
+
709
+ position: 'absolute',
710
+ top: 0,
711
+ right: noflip(0),
712
+ padding: 15,
713
+ zIndex: zIndex + 2,
714
+
715
+ ':hover': {
716
+ color: darken(color.core.grayLighter, 0.1),
717
+ textDecoration: 'none',
718
+ },
719
+
720
+ ':focus': {
721
+ color: darken(color.core.grayLighter, 0.1),
722
+ textDecoration: 'none',
723
+ },
724
+ },
725
+
726
+ SingleDatePicker_closeButton_svg: {
727
+ height: 15,
728
+ width: 15,
729
+ fill: color.core.grayLighter,
730
+ },
731
+ }), { pureComponent: typeof React.PureComponent !== 'undefined' })(SingleDatePicker);