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