]>
Commit | Line | Data |
---|---|---|
1 | import React, { Component } from 'react'; | |
2 | import PropTypes from 'prop-types'; | |
3 | import Trigger from 'rc-trigger'; | |
4 | import Panel from './Panel'; | |
5 | import placements from './placements'; | |
6 | import moment from 'moment'; | |
7 | ||
8 | function noop() { | |
9 | } | |
10 | ||
11 | function refFn(field, component) { | |
12 | this[field] = component; | |
13 | } | |
14 | ||
15 | export default class Picker extends Component { | |
16 | static propTypes = { | |
17 | prefixCls: PropTypes.string, | |
18 | clearText: PropTypes.string, | |
19 | value: PropTypes.object, | |
20 | defaultOpenValue: PropTypes.object, | |
21 | disabled: PropTypes.bool, | |
22 | allowEmpty: PropTypes.bool, | |
23 | defaultValue: PropTypes.object, | |
24 | open: PropTypes.bool, | |
25 | defaultOpen: PropTypes.bool, | |
26 | align: PropTypes.object, | |
27 | placement: PropTypes.any, | |
28 | transitionName: PropTypes.string, | |
29 | getPopupContainer: PropTypes.func, | |
30 | placeholder: PropTypes.string, | |
31 | format: PropTypes.string, | |
32 | showHour: PropTypes.bool, | |
33 | showMinute: PropTypes.bool, | |
34 | showSecond: PropTypes.bool, | |
35 | style: PropTypes.object, | |
36 | className: PropTypes.string, | |
37 | popupClassName: PropTypes.string, | |
38 | disabledHours: PropTypes.func, | |
39 | disabledMinutes: PropTypes.func, | |
40 | disabledSeconds: PropTypes.func, | |
41 | hideDisabledOptions: PropTypes.bool, | |
42 | onChange: PropTypes.func, | |
43 | onOpen: PropTypes.func, | |
44 | onClose: PropTypes.func, | |
45 | onFocus: PropTypes.func, | |
46 | onBlur: PropTypes.func, | |
47 | addon: PropTypes.func, | |
48 | name: PropTypes.string, | |
49 | autoComplete: PropTypes.string, | |
50 | use12Hours: PropTypes.bool, | |
51 | hourStep: PropTypes.number, | |
52 | minuteStep: PropTypes.number, | |
53 | secondStep: PropTypes.number, | |
54 | focusOnOpen: PropTypes.bool, | |
55 | onKeyDown: PropTypes.func, | |
56 | autoFocus: PropTypes.bool, | |
57 | }; | |
58 | ||
59 | static defaultProps = { | |
60 | clearText: 'clear', | |
61 | prefixCls: 'rc-time-picker', | |
62 | defaultOpen: false, | |
63 | style: {}, | |
64 | className: '', | |
65 | popupClassName: '', | |
66 | align: {}, | |
67 | defaultOpenValue: moment(), | |
68 | allowEmpty: true, | |
69 | showHour: true, | |
70 | showMinute: true, | |
71 | showSecond: true, | |
72 | disabledHours: noop, | |
73 | disabledMinutes: noop, | |
74 | disabledSeconds: noop, | |
75 | hideDisabledOptions: false, | |
76 | placement: 'bottomLeft', | |
77 | onChange: noop, | |
78 | onOpen: noop, | |
79 | onClose: noop, | |
80 | onFocus: noop, | |
81 | onBlur: noop, | |
82 | addon: noop, | |
83 | use12Hours: false, | |
84 | focusOnOpen: false, | |
85 | onKeyDown: noop, | |
86 | }; | |
87 | ||
88 | constructor(props) { | |
89 | super(props); | |
90 | this.saveInputRef = refFn.bind(this, 'picker'); | |
91 | this.savePanelRef = refFn.bind(this, 'panelInstance'); | |
92 | const { defaultOpen, defaultValue, open = defaultOpen, value = defaultValue } = props; | |
93 | this.state = { | |
94 | open, | |
95 | value, | |
96 | }; | |
97 | } | |
98 | ||
99 | componentWillReceiveProps(nextProps) { | |
100 | const { value, open } = nextProps; | |
101 | if ('value' in nextProps) { | |
102 | this.setState({ | |
103 | value, | |
104 | }); | |
105 | } | |
106 | if (open !== undefined) { | |
107 | this.setState({ open }); | |
108 | } | |
109 | } | |
110 | ||
111 | onPanelChange = (value) => { | |
112 | this.setValue(value); | |
113 | } | |
114 | ||
115 | onPanelClear = () => { | |
116 | this.setValue(null); | |
117 | this.setOpen(false); | |
118 | } | |
119 | ||
120 | onVisibleChange = (open) => { | |
121 | this.setOpen(open); | |
122 | } | |
123 | ||
124 | onEsc = () => { | |
125 | this.setOpen(false); | |
126 | this.focus(); | |
127 | } | |
128 | ||
129 | onKeyDown = (e) => { | |
130 | if (e.keyCode === 40) { | |
131 | this.setOpen(true); | |
132 | } | |
133 | } | |
134 | ||
135 | setValue(value) { | |
136 | if (!('value' in this.props)) { | |
137 | this.setState({ | |
138 | value, | |
139 | }); | |
140 | } | |
141 | this.props.onChange(value); | |
142 | } | |
143 | ||
144 | getFormat() { | |
145 | const { format, showHour, showMinute, showSecond, use12Hours } = this.props; | |
146 | if (format) { | |
147 | return format; | |
148 | } | |
149 | ||
150 | if (use12Hours) { | |
151 | const fmtString = ([ | |
152 | showHour ? 'h' : '', | |
153 | showMinute ? 'mm' : '', | |
154 | showSecond ? 'ss' : '', | |
155 | ].filter(item => !!item).join(':')); | |
156 | ||
157 | return fmtString.concat(' a'); | |
158 | } | |
159 | ||
160 | return [ | |
161 | showHour ? 'HH' : '', | |
162 | showMinute ? 'mm' : '', | |
163 | showSecond ? 'ss' : '', | |
164 | ].filter(item => !!item).join(':'); | |
165 | } | |
166 | ||
167 | getPanelElement() { | |
168 | const { | |
169 | prefixCls, placeholder, disabledHours, | |
170 | disabledMinutes, disabledSeconds, hideDisabledOptions, | |
171 | allowEmpty, showHour, showMinute, showSecond, defaultOpenValue, clearText, | |
172 | addon, use12Hours, focusOnOpen, onKeyDown, hourStep, minuteStep, secondStep, | |
173 | } = this.props; | |
174 | return ( | |
175 | <Panel | |
176 | clearText={clearText} | |
177 | prefixCls={`${prefixCls}-panel`} | |
178 | ref={this.savePanelRef} | |
179 | value={this.state.value} | |
180 | onChange={this.onPanelChange} | |
181 | onClear={this.onPanelClear} | |
182 | defaultOpenValue={defaultOpenValue} | |
183 | showHour={showHour} | |
184 | showMinute={showMinute} | |
185 | showSecond={showSecond} | |
186 | onEsc={this.onEsc} | |
187 | allowEmpty={allowEmpty} | |
188 | format={this.getFormat()} | |
189 | placeholder={placeholder} | |
190 | disabledHours={disabledHours} | |
191 | disabledMinutes={disabledMinutes} | |
192 | disabledSeconds={disabledSeconds} | |
193 | hideDisabledOptions={hideDisabledOptions} | |
194 | use12Hours={use12Hours} | |
195 | hourStep={hourStep} | |
196 | minuteStep={minuteStep} | |
197 | secondStep={secondStep} | |
198 | addon={addon} | |
199 | focusOnOpen={focusOnOpen} | |
200 | onKeyDown={onKeyDown} | |
201 | /> | |
202 | ); | |
203 | } | |
204 | ||
205 | getPopupClassName() { | |
206 | const { showHour, showMinute, showSecond, use12Hours, prefixCls } = this.props; | |
207 | let popupClassName = this.props.popupClassName; | |
208 | // Keep it for old compatibility | |
209 | if ((!showHour || !showMinute || !showSecond) && !use12Hours) { | |
210 | popupClassName += ` ${prefixCls}-panel-narrow`; | |
211 | } | |
212 | let selectColumnCount = 0; | |
213 | if (showHour) { | |
214 | selectColumnCount += 1; | |
215 | } | |
216 | if (showMinute) { | |
217 | selectColumnCount += 1; | |
218 | } | |
219 | if (showSecond) { | |
220 | selectColumnCount += 1; | |
221 | } | |
222 | if (use12Hours) { | |
223 | selectColumnCount += 1; | |
224 | } | |
225 | popupClassName += ` ${prefixCls}-panel-column-${selectColumnCount}`; | |
226 | return popupClassName; | |
227 | } | |
228 | ||
229 | setOpen(open) { | |
230 | const { onOpen, onClose } = this.props; | |
231 | if (this.state.open !== open) { | |
232 | if (!('open' in this.props)) { | |
233 | this.setState({ open }); | |
234 | } | |
235 | if (open) { | |
236 | onOpen({ open }); | |
237 | } else { | |
238 | onClose({ open }); | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
243 | focus() { | |
244 | this.picker.focus(); | |
245 | } | |
246 | ||
247 | blur() { | |
248 | this.picker.blur(); | |
249 | } | |
250 | ||
251 | render() { | |
252 | const { | |
253 | prefixCls, placeholder, placement, align, | |
254 | disabled, transitionName, style, className, getPopupContainer, name, autoComplete, | |
255 | onFocus, onBlur, autoFocus, | |
256 | } = this.props; | |
257 | const { open, value } = this.state; | |
258 | const popupClassName = this.getPopupClassName(); | |
259 | return ( | |
260 | <Trigger | |
261 | prefixCls={`${prefixCls}-panel`} | |
262 | popupClassName={popupClassName} | |
263 | popup={this.getPanelElement()} | |
264 | popupAlign={align} | |
265 | builtinPlacements={placements} | |
266 | popupPlacement={placement} | |
267 | action={disabled ? [] : ['click']} | |
268 | destroyPopupOnHide | |
269 | getPopupContainer={getPopupContainer} | |
270 | popupTransitionName={transitionName} | |
271 | popupVisible={open} | |
272 | onPopupVisibleChange={this.onVisibleChange} | |
273 | > | |
274 | <span className={`${prefixCls} ${className}`} style={style}> | |
275 | <input | |
276 | className={`${prefixCls}-input`} | |
277 | ref={this.saveInputRef} | |
278 | type="text" | |
279 | placeholder={placeholder} | |
280 | name={name} | |
281 | onKeyDown={this.onKeyDown} | |
282 | disabled={disabled} | |
283 | value={value && value.format(this.getFormat()) || ''} | |
284 | autoComplete={autoComplete} | |
285 | onFocus={onFocus} | |
286 | onBlur={onBlur} | |
287 | autoFocus={autoFocus} | |
288 | onChange={noop} | |
289 | /> | |
290 | <span className={`${prefixCls}-icon`}/> | |
291 | </span> | |
292 | </Trigger> | |
293 | ); | |
294 | } | |
295 | } |