Anna Kurylo
2018-10-16 76306226877e46f967f3af26e5e45e89e40c1cd3
commit | author | age
caf7fe 1 /* eslint-disable */
AK 2
c7776a 3 'use strict';
417bf4 4
c7776a 5 var assign = require('object-assign'),
2bcc09 6     PropTypes = require('prop-types'),
cf3d92 7     createClass = require('create-react-class'),
9f1b61 8     moment = require('moment'),
4ad788 9     React = require('react'),
e09432 10     CalendarContainer = require('./src/CalendarContainer')
cf3d92 11     ;
417bf4 12
3b920b 13 var viewModes = Object.freeze({
SE 14     YEARS: 'years',
15     MONTHS: 'months',
16     DAYS: 'days',
17     TIME: 'time',
18 });
19
0cae6c 20 var TYPES = PropTypes;
84755c 21 var Datetime = createClass({
0fbbc4 22     displayName: 'DateTime',
c7776a 23     propTypes: {
0d9dc7 24         // value: TYPES.object | TYPES.string,
M 25         // defaultValue: TYPES.object | TYPES.string,
c276ec 26         // viewDate: TYPES.object | TYPES.string,
aca9e6 27         onFocus: TYPES.func,
0ef08f 28         onBlur: TYPES.func,
c37f80 29         onChange: TYPES.func,
5377a9 30         onViewModeChange: TYPES.func,
d40f6d 31         onNavigateBack: TYPES.func,
DDD 32         onNavigateForward: TYPES.func,
c37f80 33         locale: TYPES.string,
049c33 34         utc: TYPES.bool,
c37f80 35         input: TYPES.bool,
cbe644 36         // dateFormat: TYPES.string | TYPES.bool,
M 37         // timeFormat: TYPES.string | TYPES.bool,
c37f80 38         inputProps: TYPES.object,
0b3475 39         timeConstraints: TYPES.object,
3b920b 40         viewMode: TYPES.oneOf([viewModes.YEARS, viewModes.MONTHS, viewModes.DAYS, viewModes.TIME]),
e7f876 41         isValidDate: TYPES.func,
692390 42         open: TYPES.bool,
9012e8 43         strictParsing: TYPES.bool,
M 44         closeOnSelect: TYPES.bool,
45         closeOnTab: TYPES.bool
c7776a 46     },
8abb28 47
c7776a 48     getInitialState: function() {
c658ad 49         var state = this.getStateFromProps( this.props );
M 50
462115 51         if ( state.open === undefined )
87c677 52             state.open = !this.props.input;
M 53
3b920b 54         state.currentView = this.props.dateFormat ?
SE 55             (this.props.viewMode || state.updateOn || viewModes.DAYS) : viewModes.TIME;
c658ad 56
M 57         return state;
58     },
59
c276ec 60     parseDate: function (date, formats) {
SVT 61         var parsedDate;
62
63         if (date && typeof date === 'string')
64             parsedDate = this.localMoment(date, formats.datetime);
65         else if (date)
66             parsedDate = this.localMoment(date);
67
68         if (parsedDate && !parsedDate.isValid())
69             parsedDate = null;
70
71         return parsedDate;
72     },
73
9f1b61 74     getStateFromProps: function( props ) {
c658ad 75         var formats = this.getFormats( props ),
M 76             date = props.value || props.defaultValue,
0eb496 77             selectedDate, viewDate, updateOn, inputValue
cf3d92 78             ;
3515a4 79
c276ec 80         selectedDate = this.parseDate(date, formats);
62fd2f 81
c276ec 82         viewDate = this.parseDate(props.viewDate, formats);
62fd2f 83
M 84         viewDate = selectedDate ?
462115 85             selectedDate.clone().startOf('month') :
c276ec 86             viewDate ? viewDate.clone().startOf('month') : this.localMoment().startOf('month');
3515a4 87
d1be3f 88         updateOn = this.getUpdateOn(formats);
SA 89
0eb496 90         if ( selectedDate )
SE 91             inputValue = selectedDate.format(formats.datetime);
92         else if ( date.isValid && !date.isValid() )
93             inputValue = '';
94         else
95             inputValue = date || '';
96
c7776a 97         return {
d1be3f 98             updateOn: updateOn,
c7776a 99             inputFormat: formats.datetime,
62fd2f 100             viewDate: viewDate,
0d9dc7 101             selectedDate: selectedDate,
0eb496 102             inputValue: inputValue,
50a0c2 103             open: props.open
c7776a 104         };
d1be3f 105     },
SA 106
9f1b61 107     getUpdateOn: function( formats ) {
cf3d92 108         if ( formats.date.match(/[lLD]/) ) {
3b920b 109             return viewModes.DAYS;
2bcc09 110         } else if ( formats.date.indexOf('M') !== -1 ) {
3b920b 111             return viewModes.MONTHS;
2bcc09 112         } else if ( formats.date.indexOf('Y') !== -1 ) {
3b920b 113             return viewModes.YEARS;
92a2c6 114         }
M 115
3b920b 116         return viewModes.DAYS;
c7776a 117     },
aca70a 118
9f1b61 119     getFormats: function( props ) {
c7776a 120         var formats = {
839cd8 121                 date: props.dateFormat || '',
M 122                 time: props.timeFormat || ''
c37f80 123             },
64fc6a 124             locale = this.localMoment( props.date, null, props ).localeData()
cf3d92 125             ;
5e870c 126
9f1b61 127         if ( formats.date === true ) {
3515a4 128             formats.date = locale.longDateFormat('L');
c7776a 129         }
3b920b 130         else if ( this.getUpdateOn(formats) !== viewModes.DAYS ) {
92a2c6 131             formats.time = '';
M 132         }
133
9f1b61 134         if ( formats.time === true ) {
3515a4 135             formats.time = locale.longDateFormat('LT');
c7776a 136         }
5e870c 137
0d9dc7 138         formats.datetime = formats.date && formats.time ?
M 139             formats.date + ' ' + formats.time :
140             formats.date || formats.time
141         ;
c7776a 142
M 143         return formats;
144     },
145
9f1b61 146     componentWillReceiveProps: function( nextProps ) {
c658ad 147         var formats = this.getFormats( nextProps ),
64fc6a 148             updatedState = {}
c37f80 149         ;
M 150
701646 151         if ( nextProps.value !== this.props.value ||
64fc6a 152             formats.datetime !== this.getFormats( this.props ).datetime ) {
SE 153             updatedState = this.getStateFromProps( nextProps );
c7776a 154         }
M 155
64fc6a 156         if ( updatedState.open === undefined ) {
d0b63b 157             if ( typeof nextProps.open !== 'undefined' ) {
SE 158                 updatedState.open = nextProps.open;
3b920b 159             } else if ( this.props.closeOnSelect && this.state.currentView !== viewModes.TIME ) {
fee412 160                 updatedState.open = false;
SE 161             } else {
162                 updatedState.open = this.state.open;
163             }
433f26 164         }
SE 165
583af6 166         if ( nextProps.viewMode !== this.props.viewMode ) {
64fc6a 167             updatedState.currentView = nextProps.viewMode;
583af6 168         }
a8a17a 169
64fc6a 170         if ( nextProps.locale !== this.props.locale ) {
SE 171             if ( this.state.viewDate ) {
172                 var updatedViewDate = this.state.viewDate.clone().locale( nextProps.locale );
173                 updatedState.viewDate = updatedViewDate;
174             }
175             if ( this.state.selectedDate ) {
176                 var updatedSelectedDate = this.state.selectedDate.clone().locale( nextProps.locale );
177                 updatedState.selectedDate = updatedSelectedDate;
178                 updatedState.inputValue = updatedSelectedDate.format( formats.datetime );
179             }
180         }
181
182         if ( nextProps.utc !== this.props.utc ) {
183             if ( nextProps.utc ) {
184                 if ( this.state.viewDate )
185                     updatedState.viewDate = this.state.viewDate.clone().utc();
186                 if ( this.state.selectedDate ) {
187                     updatedState.selectedDate = this.state.selectedDate.clone().utc();
188                     updatedState.inputValue = updatedState.selectedDate.format( formats.datetime );
189                 }
190             } else {
191                 if ( this.state.viewDate )
192                     updatedState.viewDate = this.state.viewDate.clone().local();
193                 if ( this.state.selectedDate ) {
194                     updatedState.selectedDate = this.state.selectedDate.clone().local();
195                     updatedState.inputValue = updatedState.selectedDate.format(formats.datetime);
196                 }
197             }
198         }
0fac4a 199
S 200         if ( nextProps.viewDate !== this.props.viewDate ) {
405f4e 201             updatedState.viewDate = moment(nextProps.viewDate);
0fac4a 202         }
39b827 203         //we should only show a valid date if we are provided a isValidDate function. Removed in 2.10.3
LA 204         /*if (this.props.isValidDate) {
9509fa 205             updatedState.viewDate = updatedState.viewDate || this.state.viewDate;
JC 206             while (!this.props.isValidDate(updatedState.viewDate)) {
207                 updatedState.viewDate = updatedState.viewDate.add(1, 'day');
208             }
39b827 209         }*/
64fc6a 210         this.setState( updatedState );
c658ad 211     },
M 212
213     onInputChange: function( e ) {
462115 214         var value = e.target === null ? e : e.target.value,
c658ad 215             localMoment = this.localMoment( value, this.state.inputFormat ),
M 216             update = { inputValue: value }
cf3d92 217             ;
c658ad 218
M 219         if ( localMoment.isValid() && !this.props.value ) {
220             update.selectedDate = localMoment;
462115 221             update.viewDate = localMoment.clone().startOf('month');
2bcc09 222         } else {
62fd2f 223             update.selectedDate = null;
M 224         }
c658ad 225
M 226         return this.setState( update, function() {
62fd2f 227             return this.props.onChange( localMoment.isValid() ? localMoment : this.state.inputValue );
c7776a 228         });
M 229     },
230
9f1b61 231     onInputKey: function( e ) {
SE 232         if ( e.which === 9 && this.props.closeOnTab ) {
9012e8 233             this.closeCalendar();
M 234         }
c7776a 235     },
M 236
9f1b61 237     showView: function( view ) {
c7776a 238         var me = this;
9f1b61 239         return function() {
5377a9 240             me.state.currentView !== view && me.props.onViewModeChange( view );
c7776a 241             me.setState({ currentView: view });
M 242         };
243     },
244
9f1b61 245     setDate: function( type ) {
c7776a 246         var me = this,
4ad788 247             nextViews = {
3b920b 248                 month: viewModes.DAYS,
SE 249                 year: viewModes.MONTHS,
4ad788 250             }
c7776a 251         ;
9f1b61 252         return function( e ) {
c7776a 253             me.setState({
462115 254                 viewDate: me.state.viewDate.clone()[ type ]( parseInt(e.target.getAttribute('data-value'), 10) ).startOf( type ),
c7776a 255                 currentView: nextViews[ type ]
M 256             });
5377a9 257             me.props.onViewModeChange( nextViews[ type ] );
9fb8e8 258         };
c7776a 259     },
M 260
d40f6d 261     subtractTime: function( amount, type, toSelected ) {
DDD 262         var me = this;
263         return function() {
264             me.props.onNavigateBack( amount, type );
265             me.updateTime( 'subtract', amount, type, toSelected );
266         };
c7776a 267     },
9fb8e8 268
d40f6d 269     addTime: function( amount, type, toSelected ) {
DDD 270         var me = this;
271         return function() {
272             me.props.onNavigateForward( amount, type );
273             me.updateTime( 'add', amount, type, toSelected );
274         };
c7776a 275     },
9fb8e8 276
9f1b61 277     updateTime: function( op, amount, type, toSelected ) {
d40f6d 278         var update = {},
DDD 279             date = toSelected ? 'selectedDate' : 'viewDate';
c7776a 280
d40f6d 281         update[ date ] = this.state[ date ].clone()[ op ]( amount, type );
c7776a 282
d40f6d 283         this.setState( update );
c7776a 284     },
M 285
462115 286     allowedSetTime: ['hours', 'minutes', 'seconds', 'milliseconds'],
9f1b61 287     setTime: function( type, value ) {
c7776a 288         var index = this.allowedSetTime.indexOf( type ) + 1,
62fd2f 289             state = this.state,
M 290             date = (state.selectedDate || state.viewDate).clone(),
c7776a 291             nextType
cf3d92 292             ;
c7776a 293
4ad788 294         // It is needed to set all the time properties
M 295         // to not to reset the time
c7776a 296         date[ type ]( value );
M 297         for (; index < this.allowedSetTime.length; index++) {
298             nextType = this.allowedSetTime[index];
299             date[ nextType ]( date[nextType]() );
300         }
4ad788 301
9f1b61 302         if ( !this.props.value ) {
c658ad 303             this.setState({
M 304                 selectedDate: date,
62fd2f 305                 inputValue: date.format( state.inputFormat )
c658ad 306             });
M 307         }
4e9d38 308         this.props.onChange( date );
c7776a 309     },
M 310
1fdc4e 311     updateSelectedDate: function( e, close ) {
c7776a 312         var target = e.target,
c37f80 313             modifier = 0,
62fd2f 314             viewDate = this.state.viewDate,
M 315             currentDate = this.state.selectedDate || viewDate,
c37f80 316             date
cf3d92 317             ;
c7776a 318
9f1b61 319         if (target.className.indexOf('rdtDay') !== -1) {
462115 320             if (target.className.indexOf('rdtNew') !== -1)
d1be3f 321                 modifier = 1;
462115 322             else if (target.className.indexOf('rdtOld') !== -1)
d1be3f 323                 modifier = -1;
c7776a 324
d1be3f 325             date = viewDate.clone()
SA 326                 .month( viewDate.month() + modifier )
462115 327                 .date( parseInt( target.getAttribute('data-value'), 10 ) );
9f1b61 328         } else if (target.className.indexOf('rdtMonth') !== -1) {
d1be3f 329             date = viewDate.clone()
462115 330                 .month( parseInt( target.getAttribute('data-value'), 10 ) )
SE 331                 .date( currentDate.date() );
9f1b61 332         } else if (target.className.indexOf('rdtYear') !== -1) {
d1be3f 333             date = viewDate.clone()
SA 334                 .month( currentDate.month() )
335                 .date( currentDate.date() )
462115 336                 .year( parseInt( target.getAttribute('data-value'), 10 ) );
d1be3f 337         }
SA 338
339         date.hours( currentDate.hours() )
c7776a 340             .minutes( currentDate.minutes() )
M 341             .seconds( currentDate.seconds() )
462115 342             .milliseconds( currentDate.milliseconds() );
c7776a 343
9f1b61 344         if ( !this.props.value ) {
794700 345             var open = !( this.props.closeOnSelect && close );
SE 346             if ( !open ) {
347                 this.props.onBlur( date );
348             }
349
c658ad 350             this.setState({
M 351                 selectedDate: date,
352                 viewDate: date.clone().startOf('month'),
50a0c2 353                 inputValue: date.format( this.state.inputFormat ),
794700 354                 open: open
c658ad 355             });
462115 356         } else {
794700 357             if ( this.props.closeOnSelect && close ) {
50a0c2 358                 this.closeCalendar();
M 359             }
c658ad 360         }
4e9d38 361
M 362         this.props.onChange( date );
c7776a 363     },
M 364
689227 365     openCalendar: function( e ) {
SE 366         if ( !this.state.open ) {
f72983 367             this.setState({ open: true }, function() {
689227 368                 this.props.onFocus( e );
f72983 369             });
aca9e6 370         }
c7776a 371     },
M 372
1fdc4e 373     closeCalendar: function() {
f72983 374         this.setState({ open: false }, function () {
GV 375             this.props.onBlur( this.state.selectedDate || this.state.inputValue );
376         });
1fdc4e 377     },
EC 378
9f1b61 379     handleClickOutside: function() {
704c18 380         if ( this.props.input && this.state.open && !this.props.open && !this.props.disableOnClickOutside ) {
f72983 381             this.setState({ open: false }, function() {
GV 382                 this.props.onBlur( this.state.selectedDate || this.state.inputValue );
383             });
62fd2f 384         }
c7776a 385     },
M 386
64fc6a 387     localMoment: function( date, format, props ) {
SE 388         props = props || this.props;
389         var momentFn = props.utc ? moment.utc : moment;
390         var m = momentFn( date, format, props.strictParsing );
391         if ( props.locale )
392             m.locale( props.locale );
c37f80 393         return m;
M 394     },
395
c7776a 396     componentProps: {
0b3475 397         fromProps: ['value', 'isValidDate', 'renderDay', 'renderMonth', 'renderYear', 'timeConstraints'],
d1be3f 398         fromState: ['viewDate', 'selectedDate', 'updateOn'],
11612b 399         fromThis: ['setDate', 'setTime', 'showView', 'addTime', 'subtractTime', 'updateSelectedDate', 'localMoment', 'handleClickOutside']
c7776a 400     },
M 401
9f1b61 402     getComponentProps: function() {
c7776a 403         var me = this,
a3a33b 404             formats = this.getFormats( this.props ),
M 405             props = {dateFormat: formats.date, timeFormat: formats.time}
cf3d92 406             ;
c7776a 407
9f1b61 408         this.componentProps.fromProps.forEach( function( name ) {
c7776a 409             props[ name ] = me.props[ name ];
M 410         });
9f1b61 411         this.componentProps.fromState.forEach( function( name ) {
c7776a 412             props[ name ] = me.state[ name ];
M 413         });
9f1b61 414         this.componentProps.fromThis.forEach( function( name ) {
c7776a 415             props[ name ] = me[ name ];
M 416         });
417
418         return props;
419     },
420
421     render: function() {
f4e62d 422         // TODO: Make a function or clean up this code,
SE 423         // logic right now is really hard to follow
a50b2e 424         var className = 'rdt' + (this.props.className ?
386942 425                   ( Array.isArray( this.props.className ) ?
SE 426                   ' ' + this.props.className.join( ' ' ) : ' ' + this.props.className) : ''),
cf3d92 427             children = [];
a3a33b 428
9f1b61 429         if ( this.props.input ) {
b6f2dd 430             var finalInputProps = assign({
DF 431                 type: 'text',
432                 className: 'form-control',
433                 onClick: this.openCalendar,
434                 onFocus: this.openCalendar,
435                 onChange: this.onInputChange,
436                 onKeyDown: this.onInputKey,
437                 value: this.state.inputValue,
438             }, this.props.inputProps);
b8a9a7 439             if ( this.props.renderInput ) {
0484e2 440                 children = [ React.createElement('div', { key: 'i' }, this.props.renderInput( finalInputProps, this.openCalendar, this.closeCalendar )) ];
b8a9a7 441             } else {
b6f2dd 442                 children = [ React.createElement('input', assign({ key: 'i' }, finalInputProps ))];
b8a9a7 443             }
462115 444         } else {
a3a33b 445             className += ' rdtStatic';
M 446         }
d76f7b 447
462115 448         if ( this.state.open )
a3a33b 449             className += ' rdtOpen';
M 450
f37c3f 451         return React.createElement( 'div', { className: className }, children.concat(
SE 452             React.createElement( 'div',
a3a33b 453                 { key: 'dt', className: 'rdtPicker' },
f37c3f 454                 React.createElement( CalendarContainer, { view: this.state.currentView, viewProps: this.getComponentProps(), onClickOutside: this.handleClickOutside })
d76f7b 455             )
a3a33b 456         ));
c7776a 457     }
47e834 458 });
LC 459
e6c8d2 460 Datetime.defaultProps = {
MM 461     className: '',
462     defaultValue: '',
463     inputProps: {},
464     input: true,
465     onFocus: function() {},
466     onBlur: function() {},
467     onChange: function() {},
468     onViewModeChange: function() {},
d40f6d 469     onNavigateBack: function() {},
DDD 470     onNavigateForward: function() {},
e6c8d2 471     timeFormat: true,
MM 472     timeConstraints: {},
473     dateFormat: true,
474     strictParsing: true,
475     closeOnSelect: false,
476     closeOnTab: true,
477     utc: false
478 };
479
cc4dda 480 // Make moment accessible through the Datetime class
M 481 Datetime.moment = moment;
482
9fb8e8 483 module.exports = Datetime;
caf7fe 484 /* eslint-enable */