marquex
2015-07-23 8abb28c2b289d7d86d85b4bd24f475775a57a99d
commit | author | age
c7776a 1 'use strict';
417bf4 2
3ab634 3 require('classlist-polyfill');
MK 4
c7776a 5 var assign = require('object-assign'),
4ad788 6     React = require('react'),
d76f7b 7     DaysView = require('./src/DaysView'),
M 8     MonthsView = require('./src/MonthsView'),
9     YearsView = require('./src/YearsView'),
10     TimeView = require('./src/TimeView'),
4ad788 11     moment = require('moment')
c7776a 12 ;
417bf4 13
c37f80 14 var TYPES = React.PropTypes;
9fb8e8 15 var Datetime = React.createClass({
c7776a 16     mixins: [
M 17         require('react-onclickoutside')
18     ],
19     viewComponents: {
20         days: DaysView,
21         months: MonthsView,
22         years: YearsView,
23         time: TimeView
24     },
25     propTypes: {
c658ad 26         value: TYPES.object,
M 27         defaultValue: TYPES.object,
0ef08f 28         onBlur: TYPES.func,
c37f80 29         onChange: TYPES.func,
M 30         locale: TYPES.string,
31         input: TYPES.bool,
cbe644 32         // dateFormat: TYPES.string | TYPES.bool,
M 33         // timeFormat: TYPES.string | TYPES.bool,
c37f80 34         inputProps: TYPES.object,
M 35         viewMode: TYPES.oneOf(['years', 'months', 'days', 'time']),
e7f876 36         isValidDate: TYPES.func,
c37f80 37         minDate: TYPES.object,
M 38         maxDate: TYPES.object
c7776a 39     },
8abb28 40
c7776a 41     getDefaultProps: function() {
4e9d38 42         var nof = function(){};
c7776a 43         return {
8abb28 44             className: '',
c658ad 45             value: false,
M 46             defaultValue: new Date(),
c7776a 47             viewMode: 'days',
d76f7b 48             inputProps: {},
a3a33b 49             input: true,
4e9d38 50             onBlur: nof,
cbe644 51             onChange: nof,
839cd8 52             timeFormat: true,
cbe644 53             dateFormat: true
c7776a 54         };
M 55     },
c658ad 56
c7776a 57     getInitialState: function() {
c658ad 58         var state = this.getStateFromProps( this.props );
M 59
60         state.open = !this.props.input;
61         state.currentView = this.props.viewMode;
62
63         return state;
64     },
65
66     getStateFromProps: function( props ){
67         var formats = this.getFormats( props ),
68             date = props.value || props.defaultValue,
69             selectedDate
c7776a 70         ;
3515a4 71
c658ad 72         if( typeof date == 'string')
M 73             selectedDate = this.localMoment( date, formats.datetime );
74         else
75             selectedDate = this.localMoment( date );
3515a4 76
c7776a 77         return {
M 78             inputFormat: formats.datetime,
c37f80 79             viewDate: this.localMoment(date).startOf("month"),
M 80             selectedDate: this.localMoment(date),
81             inputValue: this.localMoment(date).format( formats.datetime )
c7776a 82         };
M 83     },
aca70a 84
c7776a 85     getFormats: function( props ){
M 86         var formats = {
839cd8 87                 date: props.dateFormat || '',
M 88                 time: props.timeFormat || ''
c37f80 89             },
M 90             locale = this.localMoment( props.date ).localeData()
91         ;
5e870c 92
3515a4 93         if( formats.date === true ){
M 94             formats.date = locale.longDateFormat('L');
c7776a 95         }
3515a4 96         if( formats.time === true ){
M 97             formats.time = locale.longDateFormat('LT');
c7776a 98         }
5e870c 99
3515a4 100         formats.datetime = formats.date + ' ' + formats.time;
c7776a 101
M 102         return formats;
103     },
104
105     componentWillReceiveProps: function(nextProps) {
c658ad 106         var formats = this.getFormats( nextProps ),
M 107             update = {}
c37f80 108         ;
M 109
c658ad 110         if( nextProps.value ){
M 111             update = this.getStateFromProps( nextProps );
112         }
113         if ( formats.datetime !== this.getFormats( this.props ).datetime ) {
114             update.inputFormat = formats.datetime;
c7776a 115         }
M 116
c658ad 117         this.setState( update );
M 118     },
119
120     onInputChange: function( e ) {
121         var value = e.target == null ? e : e.target.value,
122             localMoment = this.localMoment( value, this.state.inputFormat ),
123             update = { inputValue: value }
124         ;
125
126         if ( localMoment.isValid() && !this.props.value ) {
127             update.selectedDate = localMoment;
128             update.viewDate = localMoment.clone().startOf("month");
129         }
130
131         return this.setState( update, function() {
2bb9ca 132             if( localMoment.isValid() )
M 133                 return this.props.onChange( localMoment );
c7776a 134         });
M 135     },
136
137     showView: function( view ){
138         var me = this;
139         return function( e ){
140             me.setState({ currentView: view });
141         };
142     },
143
144     setDate: function( type ){
145         var me = this,
4ad788 146             nextViews = {
M 147                 month: 'days',
148                 year: 'months'
149             }
c7776a 150         ;
M 151         return function( e ){
152             me.setState({
d4a1e8 153                 viewDate: me.state.viewDate.clone()[ type ]( parseInt(e.target.getAttribute('data-value')) ).startOf( type ),
c7776a 154                 currentView: nextViews[ type ]
M 155             });
9fb8e8 156         };
c7776a 157     },
M 158
159     addTime: function( amount, type, toSelected ){
160         return this.updateTime( 'add', amount, type, toSelected );
161     },
9fb8e8 162
c7776a 163     subtractTime: function( amount, type, toSelected ){
M 164         return this.updateTime( 'subtract', amount, type, toSelected );
165     },
9fb8e8 166
c7776a 167     updateTime: function( op, amount, type, toSelected ){
M 168         var me = this;
169
170         return function(){
171             var update = {},
172                 date = toSelected ? 'selectedDate' : 'viewDate'
173             ;
174
175             update[ date ] = me.state[ date ].clone()[ op ]( amount, type );
176
177             me.setState( update );
178         };
179     },
180
181     allowedSetTime: ['hours','minutes','seconds', 'milliseconds'],
182     setTime: function( type, value ){
183         var index = this.allowedSetTime.indexOf( type ) + 1,
184             date = this.state.selectedDate.clone(),
185             nextType
186         ;
187
4ad788 188         // It is needed to set all the time properties
M 189         // to not to reset the time
c7776a 190         date[ type ]( value );
M 191         for (; index < this.allowedSetTime.length; index++) {
192             nextType = this.allowedSetTime[index];
193             date[ nextType ]( date[nextType]() );
194         }
4ad788 195
c658ad 196         if( !this.props.value ){
M 197             this.setState({
198                 selectedDate: date,
199                 inputValue: date.format( this.state.inputFormat )
200             });
201         }
4e9d38 202         this.props.onChange( date );
c7776a 203     },
M 204
c658ad 205     updateSelectedDate: function( e ) {
c7776a 206         var target = e.target,
c37f80 207             modifier = 0,
M 208             currentDate = this.state.selectedDate,
209             date
c7776a 210         ;
M 211
212         if(target.className.indexOf("new") != -1)
213             modifier = 1;
214         else if(target.className.indexOf("old") != -1)
215             modifier = -1;
216
217         date = this.state.viewDate.clone()
218             .month( this.state.viewDate.month() + modifier )
d4a1e8 219             .date( parseInt( target.getAttribute('data-value') ) )
c7776a 220             .hours( currentDate.hours() )
M 221             .minutes( currentDate.minutes() )
222             .seconds( currentDate.seconds() )
223             .milliseconds( currentDate.milliseconds() )
224         ;
225
c658ad 226         if( !this.props.value ){
M 227             this.setState({
228                 selectedDate: date,
229                 viewDate: date.clone().startOf('month'),
230                 inputValue: date.format( this.state.inputFormat )
231             });
232         }
4e9d38 233
M 234         this.props.onChange( date );
c7776a 235     },
M 236
237     openCalendar: function() {
a3a33b 238         this.setState({ open: true });
c7776a 239     },
M 240
241     handleClickOutside: function(){
4e9d38 242         this.props.onBlur( this.state.selectedDate );
a3a33b 243         if( this.props.input && this.state.open )
M 244             this.setState({ open: false });
c7776a 245     },
M 246
c658ad 247     localMoment: function( date, format ){
M 248         var m = moment( date, format );
c37f80 249         if( this.props.locale )
M 250             m.locale( this.props.locale );
251         return m;
252     },
253
c7776a 254     componentProps: {
c658ad 255         fromProps: ['value', 'isValidDate', 'renderDay', 'renderMonth', 'renderYear'],
9fb8e8 256         fromState: ['viewDate', 'selectedDate' ],
c658ad 257         fromThis: ['setDate', 'setTime', 'showView', 'addTime', 'subtractTime', 'updateSelectedDate', 'localMoment']
c7776a 258     },
M 259
260     getComponentProps: function(){
261         var me = this,
a3a33b 262             formats = this.getFormats( this.props ),
M 263             props = {dateFormat: formats.date, timeFormat: formats.time}
c7776a 264         ;
M 265
266         this.componentProps.fromProps.forEach( function( name ){
267             props[ name ] = me.props[ name ];
268         });
269         this.componentProps.fromState.forEach( function( name ){
270             props[ name ] = me.state[ name ];
271         });
272         this.componentProps.fromThis.forEach( function( name ){
273             props[ name ] = me[ name ];
274         });
275
276         return props;
277     },
278
279     render: function() {
d76f7b 280         var Component = this.viewComponents[ this.state.currentView ],
a3a33b 281             DOM = React.DOM,
8abb28 282             className = 'rdt ' + this.props.className,
a3a33b 283             children = []
M 284         ;
285
286         if( this.props.input ){
287             children = [ DOM.input( assign({
18dc17 288                 key: 'i',
d76f7b 289                 type:'text',
2bb9ca 290                 className: 'form-control',
d76f7b 291                 onFocus: this.openCalendar,
c658ad 292                 onChange: this.onInputChange,
d76f7b 293                 value: this.state.inputValue
a3a33b 294             }, this.props.inputProps ))];
M 295         }
296         else {
297             className += ' rdtStatic';
298         }
d76f7b 299
a3a33b 300         if( this.state.open )
M 301             className += ' rdtOpen';
302
303         return DOM.div({className: className}, children.concat(
304             DOM.div(
305                 { key: 'dt', className: 'rdtPicker' },
306                 React.createElement( Component, this.getComponentProps())
d76f7b 307             )
a3a33b 308         ));
c7776a 309     }
47e834 310 });
LC 311
cc4dda 312 // Make moment accessible through the Datetime class
M 313 Datetime.moment = moment;
314
9fb8e8 315 module.exports = Datetime;