UNPKG

6.67 kBJSXView Raw
1import React from 'react';
2import PropTypes from 'prop-types';
3import moment from 'moment';
4
5import momentPropTypes from 'react-moment-proptypes';
6import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types';
7import openDirectionShape from '../shapes/OpenDirectionShape';
8
9import { SingleDatePickerInputPhrases } from '../defaultPhrases';
10import getPhrasePropTypes from '../utils/getPhrasePropTypes';
11
12import SingleDatePickerInput from './SingleDatePickerInput';
13
14import IconPositionShape from '../shapes/IconPositionShape';
15import DisabledShape from '../shapes/DisabledShape';
16
17import toMomentObject from '../utils/toMomentObject';
18import toLocalizedDateString from '../utils/toLocalizedDateString';
19
20import isInclusivelyAfterDay from '../utils/isInclusivelyAfterDay';
21
22import {
23 ICON_BEFORE_POSITION,
24 OPEN_DOWN,
25} from '../constants';
26
27const propTypes = forbidExtraProps({
28 children: PropTypes.node,
29
30 date: momentPropTypes.momentObj,
31 onDateChange: PropTypes.func.isRequired,
32
33 focused: PropTypes.bool,
34 onFocusChange: PropTypes.func.isRequired,
35
36 id: PropTypes.string.isRequired,
37 placeholder: PropTypes.string,
38 ariaLabel: PropTypes.string,
39 screenReaderMessage: PropTypes.string,
40 showClearDate: PropTypes.bool,
41 showCaret: PropTypes.bool,
42 showDefaultInputIcon: PropTypes.bool,
43 inputIconPosition: IconPositionShape,
44 disabled: DisabledShape,
45 required: PropTypes.bool,
46 readOnly: PropTypes.bool,
47 openDirection: openDirectionShape,
48 noBorder: PropTypes.bool,
49 block: PropTypes.bool,
50 small: PropTypes.bool,
51 regular: PropTypes.bool,
52 verticalSpacing: nonNegativeInteger,
53
54 keepOpenOnDateSelect: PropTypes.bool,
55 reopenPickerOnClearDate: PropTypes.bool,
56 isOutsideRange: PropTypes.func,
57 displayFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
58
59 onClose: PropTypes.func,
60 onKeyDownArrowDown: PropTypes.func,
61 onKeyDownQuestionMark: PropTypes.func,
62
63 customInputIcon: PropTypes.node,
64 customCloseIcon: PropTypes.node,
65
66 // accessibility
67 isFocused: PropTypes.bool,
68
69 // i18n
70 phrases: PropTypes.shape(getPhrasePropTypes(SingleDatePickerInputPhrases)),
71
72 isRTL: PropTypes.bool,
73});
74
75const defaultProps = {
76 children: null,
77
78 date: null,
79 focused: false,
80
81 placeholder: '',
82 ariaLabel: undefined,
83 screenReaderMessage: 'Date',
84 showClearDate: false,
85 showCaret: false,
86 showDefaultInputIcon: false,
87 inputIconPosition: ICON_BEFORE_POSITION,
88 disabled: false,
89 required: false,
90 readOnly: false,
91 openDirection: OPEN_DOWN,
92 noBorder: false,
93 block: false,
94 small: false,
95 regular: false,
96 verticalSpacing: undefined,
97
98 keepOpenOnDateSelect: false,
99 reopenPickerOnClearDate: false,
100 isOutsideRange: (day) => !isInclusivelyAfterDay(day, moment()),
101 displayFormat: () => moment.localeData().longDateFormat('L'),
102
103 onClose() {},
104 onKeyDownArrowDown() {},
105 onKeyDownQuestionMark() {},
106
107 customInputIcon: null,
108 customCloseIcon: null,
109
110 // accessibility
111 isFocused: false,
112
113 // i18n
114 phrases: SingleDatePickerInputPhrases,
115
116 isRTL: false,
117};
118
119export default class SingleDatePickerInputController extends React.PureComponent {
120 constructor(props) {
121 super(props);
122
123 this.onChange = this.onChange.bind(this);
124 this.onFocus = this.onFocus.bind(this);
125 this.onClearFocus = this.onClearFocus.bind(this);
126 this.clearDate = this.clearDate.bind(this);
127 }
128
129 onChange(dateString) {
130 const {
131 isOutsideRange,
132 keepOpenOnDateSelect,
133 onDateChange,
134 onFocusChange,
135 onClose,
136 } = this.props;
137 const newDate = toMomentObject(dateString, this.getDisplayFormat());
138
139 const isValid = newDate && !isOutsideRange(newDate);
140 if (isValid) {
141 onDateChange(newDate);
142 if (!keepOpenOnDateSelect) {
143 onFocusChange({ focused: false });
144 onClose({ date: newDate });
145 }
146 } else {
147 onDateChange(null);
148 }
149 }
150
151
152 onFocus() {
153 const {
154 onFocusChange,
155 disabled,
156 } = this.props;
157
158 if (!disabled) {
159 onFocusChange({ focused: true });
160 }
161 }
162
163 onClearFocus() {
164 const {
165 focused,
166 onFocusChange,
167 onClose,
168 date,
169 } = this.props;
170 if (!focused) return;
171
172 onFocusChange({ focused: false });
173 onClose({ date });
174 }
175
176 getDisplayFormat() {
177 const { displayFormat } = this.props;
178 return typeof displayFormat === 'string' ? displayFormat : displayFormat();
179 }
180
181 getDateString(date) {
182 const displayFormat = this.getDisplayFormat();
183 if (date && displayFormat) {
184 return date && date.format(displayFormat);
185 }
186 return toLocalizedDateString(date);
187 }
188
189 clearDate() {
190 const { onDateChange, reopenPickerOnClearDate, onFocusChange } = this.props;
191 onDateChange(null);
192 if (reopenPickerOnClearDate) {
193 onFocusChange({ focused: true });
194 }
195 }
196
197 render() {
198 const {
199 children,
200 id,
201 placeholder,
202 ariaLabel,
203 disabled,
204 focused,
205 isFocused,
206 required,
207 readOnly,
208 openDirection,
209 showClearDate,
210 showCaret,
211 showDefaultInputIcon,
212 inputIconPosition,
213 customCloseIcon,
214 customInputIcon,
215 date,
216 phrases,
217 onKeyDownArrowDown,
218 onKeyDownQuestionMark,
219 screenReaderMessage,
220 isRTL,
221 noBorder,
222 block,
223 small,
224 regular,
225 verticalSpacing,
226 } = this.props;
227
228 const displayValue = this.getDateString(date);
229
230 return (
231 <SingleDatePickerInput
232 id={id}
233 placeholder={placeholder}
234 ariaLabel={ariaLabel}
235 focused={focused}
236 isFocused={isFocused}
237 disabled={disabled}
238 required={required}
239 readOnly={readOnly}
240 openDirection={openDirection}
241 showCaret={showCaret}
242 onClearDate={this.clearDate}
243 showClearDate={showClearDate}
244 showDefaultInputIcon={showDefaultInputIcon}
245 inputIconPosition={inputIconPosition}
246 customCloseIcon={customCloseIcon}
247 customInputIcon={customInputIcon}
248 displayValue={displayValue}
249 onChange={this.onChange}
250 onFocus={this.onFocus}
251 onKeyDownShiftTab={this.onClearFocus}
252 onKeyDownArrowDown={onKeyDownArrowDown}
253 onKeyDownQuestionMark={onKeyDownQuestionMark}
254 screenReaderMessage={screenReaderMessage}
255 phrases={phrases}
256 isRTL={isRTL}
257 noBorder={noBorder}
258 block={block}
259 small={small}
260 regular={regular}
261 verticalSpacing={verticalSpacing}
262 >
263 {children}
264 </SingleDatePickerInput>
265 );
266 }
267}
268
269SingleDatePickerInputController.propTypes = propTypes;
270SingleDatePickerInputController.defaultProps = defaultProps;