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