UNPKG

15 kBJavaScriptView Raw
1/**
2 * Created by chief on 17/4/6.
3 */
4
5import Calendar from "./rc-calendar";
6import React, { Component } from "react";
7import ReactDOM from 'react-dom';
8import { KeyCode } from 'tinper-bee-core';
9import Picker from "./rc-calendar/Picker";
10import FormControl from "bee-form-control";
11import TimePickerPanel from "rc-time-picker/lib/Panel";
12import moment from "moment";
13import Icon from "bee-icon";
14import classnames from 'classnames';
15import InputGroup from 'bee-input-group';
16import zhCN from "./locale/zh_CN";
17import omit from 'omit.js';
18
19function noop() {
20}
21let timerDatePicker = true;
22class DatePicker extends Component {
23 constructor(props, context) {
24 super(props, context);
25 this.state = {
26 type: "month",
27 value: this.initValue(props),
28 open: props.open||false,
29 inputValue:this.initValue(props),
30 showClose:false
31 };
32 this.fileChange = true;
33
34 }
35 initValue=(props)=>{
36 let value = props.value || props.defaultValue;
37 if(value){
38 if(typeof value == 'string'){
39 if(moment(value, this.props.format).isValid()){
40 value = moment(value, this.props.format);
41 }else{
42 console.error('value is not in the correct format');
43 value = ''
44 }
45 }else if(value.format&&value.isValid()){
46 value = value;
47 }else{
48 console.error('value is not in the correct format');
49 value = ''
50 }
51 }
52 return value;
53 }
54 componentWillReceiveProps(nextProps) {
55 if ("value" in nextProps) {
56 this.setState({
57 value: this.initValue(nextProps),
58 inputValue: (nextProps.value && this.getValue(nextProps.value)) || ''
59 });
60 }
61 if ("open" in nextProps) {
62 this.setState({
63 open: nextProps.open
64 });
65 }
66 if ("renderIcon" in nextProps) {
67 this.setState({
68 renderIcon: nextProps.renderIcon
69 });
70 }
71 }
72
73 getValue = value =>{
74 let { format } = this.props;
75 if(value.format){
76 if(typeof format == 'string'){
77 return value.format(format)
78 }else{
79 return value.format(format[0])
80 }
81 }
82 return value
83 }
84
85 onChange = value => {
86 this.setState({ value:value });
87 };
88
89 inputFocus=()=>{
90 const { format,validatorFunc, disabledDate } = this.props;
91 let input = document.querySelector('.rc-calendar-input');
92 if(input){
93 if(input.value){
94 input.select()
95 }else{
96 input.focus()
97 }
98 input.onkeydown = (e)=> { // 日历的input
99 if(e.keyCode == KeyCode.DELETE){
100 input.value = '';
101 this.fireChange('','');
102 } else if (e.keyCode == KeyCode.ESC || e.keyCode == KeyCode.TAB) {
103 if (e.keyCode == KeyCode.TAB) {
104 console.debug('[bee-datepicker] [DatePicker] e.keyCode == KeyCode.TAB')
105 }
106 this.setState({
107 open:false
108 });
109 e.target._dataTransfer = {
110 owner: ReactDOM.findDOMNode(this.outInput),
111 _target: e.target,
112 open: false
113 }
114
115 console.debug(' [bee-datepicker] [DatePicker] ReactDOM.findDOMNode(this.outInput)', ReactDOM.findDOMNode(this.outInput))
116 // input.blur();
117
118 // 按esc时候焦点回到input输入框
119 ReactDOM.findDOMNode(this.outInput).focus();
120 ReactDOM.findDOMNode(this.outInput).select();
121 // e.stopPropagation();
122
123 let v = this.state.value;
124 this.props.onOpenChange(false,v, (v && this.getValue(v)) || '');
125 }else if(e.keyCode == KeyCode.ENTER){
126 let parsed = moment(input.value, format, true);
127 let isDisabled = disabledDate && disabledDate(parsed);
128 if(parsed.isValid()&&validatorFunc(input.value) && !isDisabled){
129 this.setState({
130 open:false
131 });
132 let v = this.state.value;
133 this.props.onOpenChange(false,v, (v && this.getValue(v)) || '');
134 ReactDOM.findDOMNode(this.outInput).focus();
135 }
136 if (!input.value) {
137 this.setState({
138 open:false
139 });
140 }
141 } else if(e.keyCode >= 37 && e.keyCode <= 40){ // 向下
142 // 自定义_dataTransfer
143 e.target._dataTransfer = {
144 owner: ReactDOM.findDOMNode(this.outInput),
145 _target: e.target,
146 open: this.state.open
147 }
148 }
149 this.props.onKeyDown&&this.props.onKeyDown(e);
150 }
151 }
152 }
153
154 onOpenChange = open => {
155 const props = this.props;
156 const self = this;
157 this.setState({
158 open
159 },function(){
160 if(open){
161 setTimeout(() => {
162 self.inputFocus()
163 }, 0);
164 }
165 });
166 const value = self.state.value;
167 props.onOpenChange(open,value, (value && this.getValue(value)) || '');
168 if(open){
169 setTimeout(()=>{
170 self.inputFocus()
171 },200);
172 }
173 };
174
175 handleCalendarChange = (value) => {
176 const props = this.props;
177 this.setState({ value: value,inputValue:(value && this.getValue(value)) || '' });
178 this.fireChange(value, (value && this.getValue(value)) || '');
179 }
180 handleChange = value => {
181 const props = this.props;
182 this.setState({
183 value: value && Object.assign(value, {_type:'date'}) || value,
184 inputValue:(value && this.getValue(value)) || ''
185 });
186 if(timerDatePicker){
187 clearTimeout(this.timerout);
188 this.fireChange(value, (value && this.getValue(value)) || '');
189 timerDatePicker=false;
190 this.timerout = window.setTimeout(()=>{
191 timerDatePicker=true
192 },300)
193 }
194 }
195 onClick = (e) =>{
196 const props = this.props;
197 if(props.keyboardInput)e.stopPropagation();
198 let value = this.state.value;
199 if(props.keyboardInput){
200 props.onClick&&props.onClick(e.nativeEvent,value||null,this.state.inputValue);
201 }else{
202 props.onClick&&props.onClick(e.nativeEvent,value||null,(value && this.getValue(value)) || '');
203 }
204 }
205 inputChange = (value,e) => {
206 if(this.props.keyboardInput)e.stopPropagation();
207 this.setState({
208 inputValue:value
209 });
210 if(moment(value,this.props.format, true).isValid()&&this.props.validatorFunc(value)){
211 this.setState({
212 value:moment(value,this.props.format)
213 });
214 value = moment(value,this.props.format);
215 this.fireChange(value, (value && this.getValue(value)) || '');
216 }else{
217 this.fireChange(null,value);
218 }
219 }
220 outInputFocus = (e)=>{
221 if(this.props.hasOwnProperty('open'))e.stopPropagation();
222 this.props.outInputFocus&&this.props.outInputFocus(e);
223 }
224 iconClick=(e)=>{
225 this.props.iconClick&&this.props.iconClick(e);
226 }
227 outInputKeydown = (e)=>{ // 外部(非弹窗日历)核心input
228 if(e.keyCode == KeyCode.DELETE){
229 this.setState({
230 inputValue:''
231 });
232 this.fireChange('','');
233 }else if(e.keyCode == KeyCode.ESC){
234 console.debug('c%==========================[bee-datepicker] [DatePicker] [outInputKeydown()] e.keyCode == KeyCode.ESC', 'color:blue');
235 this.setState({
236 open:false
237 });
238 e.target._dataTransfer = {
239 open: false,
240 owner: e.target,
241 _target: e.target,
242 ownerIsTarget: true
243 }
244 let value = this.state.inputValue;
245 if(moment(value,this.props.format).isValid()&&this.props.validatorFunc(value)){
246 this.setState({
247 value:moment(value,this.props.format)
248 });
249 value = moment(value,this.props.format);
250 this.fireChange(value, (value && this.getValue(value)) || '');
251 }else{
252 this.fireChange(null,value);
253 }
254 } else {
255 console.debug('==========================[bee-datepicker] [DatePicker] [outInputKeydown()] e.keyCode == ' + e.keyCode);
256 }
257 if (this.props.outInputKeydown) {
258 console.debug('======================[bee-datepicker] [DatePicker] [outInputKeydown()] exist this.props.outInputKeydown and the props is ,' + this.props);
259 this.props.outInputKeydown(e);
260 } else {
261 console.debug('======================[bee-datepicker] [DatePicker] [outInputKeydown()] don\'t exist this.props.outInputKeydown and the props is ,' + this.props);
262 }
263 }
264 onMouseLeave=(e)=>{
265 this.setState({
266 showClose:false
267 })
268 }
269 onMouseEnter=(e)=>{
270 this.setState({
271 showClose:true
272 })
273 }
274 clear=(e)=>{
275 e.stopPropagation();
276 this.setState({
277 inputValue:'',
278 value:''
279 })
280 this.fireChange('','');
281 }
282 handleSelect=(value)=>{
283 this.setState({
284 value:value
285 })
286 this.props.onSelect&&this.props.onSelect(value, (value && this.getValue(value)) || '');
287 // ReactDOM.findDOMNode(this.outInput).focus()
288 }
289 //日期面板中输入框的失焦事件
290 onDateInputBlur = (e) => {
291 let input = document.querySelector('.rc-calendar-input');
292 let value;
293 if(input) {
294 value = input.value ? input.value : '';
295 }
296 this.props.onDateInputBlur && this.props.onDateInputBlur(e,value);
297 }
298 //fix:更改系统时区后,日期框需要触发 onChange 事件
299 onDateHover = ()=>{
300 let {format, inputShowValue} = this.props;
301 let {value} = this.state,
302 newValue = value && this.getValue(value);
303 let inputValue = this.outInput.state.value;
304 inputValue = format ? inputValue : ( inputValue && this.getValue(moment(inputValue)) );
305 if(newValue && (!inputShowValue) && inputValue !== newValue) {
306 this.fireChange(value, newValue || '')
307 }
308 }
309
310 fireChange = (value,stringValue)=>{
311 this.fileChange&&this.props.onChange(value,stringValue);
312 this.fileChange = false;
313 this.fileChangeTimer = window.setTimeout(()=>{
314 this.fileChange = true;
315 },10)
316 }
317 render() {
318 let state = this.state;
319 let props = this.props;
320 const { showClose, defaultPanelShown,onBlur,showHour,showMinute,showSecond,autoTriggerChange,inputShowValue,tabIndex,...others} = props;
321 let value = state.value;
322 let pickerChangeHandler = {};
323 let calendarHandler = {};
324 const autofocus = (!this.state.open && this.props.autofocus)?{autofocus:'autofocus'}:null;
325
326 if (props.showTime) {
327 calendarHandler = {
328 // fix https://github.com/ant-design/ant-design/issues/1902
329 onSelect: this.handleChange
330 };
331 } else {
332 pickerChangeHandler = {
333 onChange: this.handleChange
334 };
335 }
336
337
338 let splitNumber = '3';
339 if(!showHour)splitNumber-=1;
340 if(!showMinute)splitNumber-=1;
341 if(!showSecond)splitNumber-=1;
342
343 let calendarProps = {};
344 if(autoTriggerChange) {
345 calendarProps.value = value;
346 calendarProps.onChange = this.handleCalendarChange;
347 } else {
348 calendarProps.onChange = noop;
349 }
350
351 const calendar = (
352 <Calendar
353 timePicker={props.showTime ? <TimePickerPanel
354 className={'time-split-'+splitNumber}
355 showHour={showHour} showMinute={showMinute} showSecond={showSecond}
356 defaultValue={moment(moment().format("HH:mm:ss"), "HH:mm:ss")} /> : null}
357 {...omit(props, ['value'])}
358 {...calendarProps}
359 onSelect={this.handleSelect}
360 onInputBlur={this.onDateInputBlur}
361 />
362 );
363
364 let keyboardInputProps = {};
365 if(props.keyboardInput){
366 keyboardInputProps.readOnly=false;
367 keyboardInputProps.onChange=this.inputChange;
368 keyboardInputProps.value=inputShowValue||(state.inputValue && state.inputValue.format&&(state.inputValue.isValid()&&this.props.validatorFunc(state.inputValue))?state.inputValue.format(props.format):state.inputValue) ;
369 }else{
370 keyboardInputProps.readOnly=true;
371 keyboardInputProps.value=inputShowValue||((value && this.getValue(value)) || "")
372 }
373 let classes = classnames(props.className, "datepicker-container");
374 return (
375 <div className={classes} onMouseEnter={this.onDateHover}
376 {...omit(others, [
377 'onDateInputBlur',
378 'getCalendarContainer',
379 'showToday',
380 'renderFooter',
381 'keyboardInput',
382 'showDateInput',
383 'showTime',
384 'closeIcon',
385 'renderIcon',
386 'focusOnOpen',
387 'defultSelect',
388 'onOpenChange',
389 'locale',
390 'showMonthInput',
391 'onKeyDown',
392 'renderError',
393 'format',
394 'placeholder',
395 'disabledTime',
396 'onChange',
397 'disabledDate',
398 'iconClick',
399 'outInputKeydown'
400 ])}
401 >
402 <Picker
403 animation="slide-up"
404 {...props}
405 {...pickerChangeHandler}
406 onOpenChange={this.onOpenChange}
407 calendar={calendar}
408 mode = {'year'}
409 open={'defaultPanelShown' in props ? defaultPanelShown : this.state.open}
410 value={state.value}
411 >
412 {() => {
413 return (
414 <InputGroup simple className="datepicker-input-group"
415 onMouseEnter={this.onMouseEnter}
416 onMouseLeave={this.onMouseLeave}
417 >
418 <FormControl
419 tabIndex={tabIndex}
420 ref = { ref => this.outInput = ref }
421 disabled={props.disabled}
422 placeholder={this.props.placeholder}
423 onClick={ (event) => {this.onClick(event)}}
424 focusSelect={props.defaultSelected}
425 onFocus={(v,e)=>{this.outInputFocus(e)}}
426 onKeyDown={this.outInputKeydown}
427 // value={(value && value.format(props.format)) || ""}
428 {...keyboardInputProps}
429 {...autofocus}
430 />
431 {
432 showClose&&this.state.value&&this.state.showClose&&(!props.disabled)?(
433 <InputGroup.Button shape="border"
434 onClick={this.clear}>
435 { props.closeIcon() }
436 </InputGroup.Button>
437 ):<InputGroup.Button shape="border"
438 onClick={(e)=>{props.keyboardInput?this.iconClick(e):''}}>
439 { props.renderIcon() }
440 </InputGroup.Button>
441 }
442 </InputGroup>
443
444 );
445 }}
446 </Picker>
447 </div>
448 );
449 }
450
451 componentDidUpdate(prevProps, prevState) {
452 if (prevState.open && !this.state.open) {
453 ReactDOM.findDOMNode(this.outInput).focus();// 按esc时候焦点回到input输入框
454 }
455 }
456}
457
458DatePicker.defaultProps = {
459 closeIcon:()=><Icon type="uf-close-c"/>,
460 renderIcon: () => <Icon type="uf-calendar" />,
461 focusOnOpen:true,
462 defultSelect:false,
463 onOpenChange:()=>{},
464 onChange:()=>{},
465 locale:zhCN,
466 showMonthInput:false,
467 onKeyDown:()=>{},
468 renderError:()=>{},
469 showClose:true,
470 format: "YYYY-MM-DD",
471 showSecond:true,
472 showHour:true,
473 showMinute:true,
474 autoTriggerChange:true,
475 validatorFunc:()=>{
476 return true;
477 }
478}
479
480export default DatePicker;