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