1 |
|
2 |
|
3 |
|
4 |
|
5 | import Calendar from "./rc-calendar";
|
6 | import React, { Component } from "react";
|
7 | import ReactDOM from 'react-dom';
|
8 | import { KeyCode } from 'tinper-bee-core';
|
9 | import Picker from "./rc-calendar/Picker";
|
10 | import FormControl from "bee-form-control";
|
11 | import TimePickerPanel from "rc-time-picker/lib/Panel";
|
12 | import moment from "moment";
|
13 | import Icon from "bee-icon";
|
14 | import classnames from 'classnames';
|
15 | import InputGroup from 'bee-input-group';
|
16 | import zhCN from "./locale/zh_CN";
|
17 | import omit from 'omit.js';
|
18 |
|
19 | function noop() {
|
20 | }
|
21 | let timerDatePicker = true;
|
22 | class 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)=> {
|
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 |
|
117 |
|
118 |
|
119 | ReactDOM.findDOMNode(this.outInput).focus();
|
120 | ReactDOM.findDOMNode(this.outInput).select();
|
121 |
|
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 |
|
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)=>{
|
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 |
|
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 |
|
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 |
|
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 |
|
458 | DatePicker.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 |
|
480 | export default DatePicker;
|