├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── example ├── developer.html └── index.html ├── gulpfile.coffee ├── gulpfile.js ├── package.json └── src ├── build ├── datepicker.css └── datepicker.min.js ├── css └── datepicker.styl └── js ├── ui ├── datepicker │ ├── DatePicker.jsx │ ├── DatePickerInput.jsx │ ├── Day.jsx │ └── DayPicker.jsx └── numberpicker │ └── NumberPicker.jsx └── utils └── DateUtils.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | bower_components/ 4 | node_modules/ 5 | tmp/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 misino 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #react-datepicker 2 | 3 | Datepicker component for applications based on Facebook React library. 4 | 5 | ##Usage 6 | Standalone datepicker element. It can perform action if date is selected. Parameters are optional. 7 | For best functionality, *DatePicker* needs to be wrapped in component which sets new date on *onChangeDate* event. An example of such wrapper component could be *DatepickerInput*. 8 | 9 | ``` 10 | var callback = function(date) { 11 | alert('new selected date is: '+date); 12 | } 13 | 14 | 15 | ``` 16 | 17 | This library contains own datepicker integration with input element. 18 | All parameters are optional. 19 | Parameter *beforeUpdate* has to be function which returns string. This string will be set as value of input field before date change. 20 | 21 | ``` 22 | var returnDate = function(date) {return date}; // this callback will be called before input field update. You can use it for example for date formatting as is shown in example/index.html 23 | 24 | ``` 25 | 26 | minified javascript and css are located in directory `src/build` 27 | 28 | ###Development 29 | If you want to make changes in code and use benefits of compiling code, you need to have gulp and bower installed. 30 | 31 | ``` 32 | npm install -g gulp 33 | npm install -g bower 34 | ``` 35 | 36 | before running gulp for the first time, install dependencies and download other stuff via bower 37 | 38 | ``` 39 | cd react-datepicker 40 | npm install 41 | bower install 42 | ``` 43 | 44 | Compile code via command `gulp build` or `gulp` 45 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-datepicker", 3 | "version": "0.2.0", 4 | "private": true, 5 | "dependencies": { 6 | "este-library": "git://github.com/steida/este-library.git" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/developer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Datepicker Example - unminified files 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |

14 |

Standalone datepicker

15 |
16 |

17 |

18 |

Input element which is customized to get date from datepicker

19 |
20 |

21 | 22 |
23 | 24 | 25 | 26 | 50 | 51 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Datepicker Example 5 | 6 | 7 | 8 | 9 |
10 | 11 |

12 |

Standalone datepicker

13 |
14 |

15 |

16 |

Input element which is customized to get date from datepicker

17 |
18 |

19 | 20 |
21 | 22 | 23 | 24 | 48 | 49 | -------------------------------------------------------------------------------- /gulpfile.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | 3 | GulpEste = require 'gulp-este' 4 | runSequence = require 'run-sequence' 5 | 6 | este = new GulpEste __dirname, true, '../../../..' 7 | 8 | paths = 9 | stylus: [ 10 | 'src/css/datepicker.styl' 11 | ] 12 | coffee: [ 13 | 'bower_components/este-library/este/**/*.coffee' 14 | 'src/**/*.coffee' 15 | ] 16 | jsx: [ 17 | 'src/**/*.jsx' 18 | ] 19 | js: [ 20 | 'bower_components/closure-library/**/*.js' 21 | 'bower_components/este-library/este/**/*.js' 22 | 'src/**/*.js' 23 | 'tmp/**/*.js' 24 | '!**/build/**' 25 | ] 26 | compiler: 'bower_components/closure-compiler/compiler.jar' 27 | externs: [ 28 | 'bower_components/react-externs/externs.js' 29 | ] 30 | thirdParty: 31 | development: [ 32 | # 'bower_components/react/react.js' 33 | ] 34 | production: [ 35 | # 'bower_components/react/react.min.js' 36 | ] 37 | 38 | dirs = 39 | googBaseJs: 'bower_components/closure-library/closure/goog' 40 | watch: ['src'] 41 | 42 | gulp.task 'stylus', -> 43 | este.stylus paths.stylus 44 | 45 | gulp.task 'coffee', -> 46 | este.coffee paths.coffee 47 | 48 | gulp.task 'jsx', -> 49 | este.jsx paths.jsx 50 | 51 | gulp.task 'transpile', (done) -> 52 | runSequence 'stylus', 'coffee', 'jsx', done 53 | 54 | gulp.task 'deps', -> 55 | este.deps paths.js 56 | 57 | gulp.task 'concat-deps', -> 58 | este.concatDeps() 59 | 60 | gulp.task 'compile-datepicker', -> 61 | este.compile paths.js, 'src/build', 62 | fileName: 'datepicker.min.js' 63 | compilerPath: paths.compiler 64 | compilerFlags: 65 | closure_entry_point: 'misino.ui.datepicker.DatePickerInput' 66 | externs: paths.externs 67 | warning_level: 'VERBOSE' 68 | compilation_level: 'ADVANCED_OPTIMIZATIONS' 69 | 70 | gulp.task 'concat-all', -> 71 | este.concatAll 72 | 'src/build/react-datepicker-thirdparty.js': paths.thirdParty 73 | 74 | 75 | gulp.task 'js', (done) -> 76 | runSequence [ 77 | 'deps' if este.shouldCreateDeps() 78 | 'concat-deps' 79 | 'compile-datepicker' 80 | 'concat-all' 81 | done 82 | ].filter((task) -> task)... 83 | 84 | gulp.task 'build', (done) -> 85 | runSequence 'transpile', 'js', done 86 | 87 | gulp.task 'watch', -> 88 | este.watch dirs.watch, 89 | coffee: 'coffee' 90 | js: 'js' 91 | jsx: 'jsx' 92 | styl: 'stylus' 93 | , (task) -> gulp.start task 94 | 95 | gulp.task 'run', (done) -> 96 | runSequence [ 97 | 'watch' 98 | done 99 | ].filter((task) -> task)... 100 | 101 | gulp.task 'default', (done) -> 102 | runSequence 'build', 'run', done -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.0 2 | (function() { 3 | var GulpEste, dirs, este, gulp, paths, runSequence; 4 | 5 | gulp = require('gulp'); 6 | 7 | GulpEste = require('gulp-este'); 8 | 9 | runSequence = require('run-sequence'); 10 | 11 | este = new GulpEste(__dirname, true, '../../../..'); 12 | 13 | paths = { 14 | stylus: ['src/css/datepicker.styl'], 15 | coffee: ['bower_components/este-library/este/**/*.coffee', 'src/**/*.coffee'], 16 | jsx: ['src/**/*.jsx'], 17 | js: ['bower_components/closure-library/**/*.js', 'bower_components/este-library/este/**/*.js', 'src/**/*.js', 'tmp/**/*.js', '!**/build/**'], 18 | compiler: 'bower_components/closure-compiler/compiler.jar', 19 | externs: ['bower_components/react-externs/externs.js'], 20 | thirdParty: { 21 | development: [], 22 | production: [] 23 | } 24 | }; 25 | 26 | dirs = { 27 | googBaseJs: 'bower_components/closure-library/closure/goog', 28 | watch: ['src'] 29 | }; 30 | 31 | gulp.task('stylus', function() { 32 | return este.stylus(paths.stylus); 33 | }); 34 | 35 | gulp.task('coffee', function() { 36 | return este.coffee(paths.coffee); 37 | }); 38 | 39 | gulp.task('jsx', function() { 40 | return este.jsx(paths.jsx); 41 | }); 42 | 43 | gulp.task('transpile', function(done) { 44 | return runSequence('stylus', 'coffee', 'jsx', done); 45 | }); 46 | 47 | gulp.task('deps', function() { 48 | return este.deps(paths.js); 49 | }); 50 | 51 | gulp.task('concat-deps', function() { 52 | return este.concatDeps(); 53 | }); 54 | 55 | gulp.task('compile-datepicker', function() { 56 | return este.compile(paths.js, 'src/build', { 57 | fileName: 'datepicker.min.js', 58 | compilerPath: paths.compiler, 59 | compilerFlags: { 60 | closure_entry_point: 'misino.ui.datepicker.DatePickerInput', 61 | externs: paths.externs, 62 | warning_level: 'VERBOSE', 63 | compilation_level: 'ADVANCED_OPTIMIZATIONS' 64 | } 65 | }); 66 | }); 67 | 68 | gulp.task('concat-all', function() { 69 | return este.concatAll({ 70 | 'src/build/react-datepicker-thirdparty.js': paths.thirdParty 71 | }); 72 | }); 73 | 74 | gulp.task('js', function(done) { 75 | return runSequence.apply(null, [este.shouldCreateDeps() ? 'deps' : void 0, 'concat-deps', 'compile-datepicker', 'concat-all', done].filter(function(task) { 76 | return task; 77 | })); 78 | }); 79 | 80 | gulp.task('build', function(done) { 81 | return runSequence('transpile', 'js', done); 82 | }); 83 | 84 | gulp.task('watch', function() { 85 | return este.watch(dirs.watch, { 86 | coffee: 'coffee', 87 | js: 'js', 88 | jsx: 'jsx', 89 | styl: 'stylus' 90 | }, function(task) { 91 | return gulp.start(task); 92 | }); 93 | }); 94 | 95 | gulp.task('run', function(done) { 96 | return runSequence.apply(null, ['watch', done].filter(function(task) { 97 | return task; 98 | })); 99 | }); 100 | 101 | gulp.task('default', function(done) { 102 | return runSequence('build', 'run', done); 103 | }); 104 | 105 | }).call(this); 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-datepicker", 3 | "version": "0.2.1", 4 | "description": "", 5 | "author": "Michal Biros ", 6 | "keywords": [ 7 | "React", 8 | "Datepicker" 9 | ], 10 | "private": true, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/misino/react-datepicker.git" 14 | }, 15 | "dependencies": { 16 | "react": "^0.10.0", 17 | "gulp": "^3.6.0" 18 | }, 19 | "devDependencies": { 20 | "gulp-este": "^0.5.2", 21 | "run-sequence": "^0.3.6" 22 | }, 23 | "scripts": { 24 | "test": "gulp build" 25 | }, 26 | "license": "MIT", 27 | "subdomain": "react-datepicker" 28 | } 29 | -------------------------------------------------------------------------------- /src/build/datepicker.css: -------------------------------------------------------------------------------- 1 | div.datepicker-dates{position:relative;width:156px;height:100%;margin-top:.5em}div.datepicker-dates div.day{position:absolute}.dayInWeek-1{left:0}.dayInWeek-2{left:22px}.dayInWeek-3{left:44px}.dayInWeek-4{left:66px}.dayInWeek-5{left:88px}.dayInWeek-6{left:110px}.dayInWeek-0{left:132px}.week-1{top:0}.week-2{top:24px}.week-3{top:48px}.week-4{top:72px}.week-5{top:96px}.week-6{top:120px}.datepicker{position:relative;height:220px;width:170px;z-index:10}.datepicker .day a{width:20px;display:block;text-align:center;color:#222;text-decoration:none;padding:2px}.datepicker .day a:hover{background-color:#eaeaea}.datepicker .out .day a{color:#bcbcbc}.datepicker .numberpicker{margin:.2em;text-align:center}.datepicker .day.selected{background-color:#c9c9c9}.datepicker-container{position:relative;border:1px solid #979797;padding:.5em;background-color:#fff;height:220px}.datepicker-wrapper{position:absolute} -------------------------------------------------------------------------------- /src/build/datepicker.min.js: -------------------------------------------------------------------------------- 1 | (function(){var g=this;function h(a,b){var d=a.split("."),c=g;d[0]in c||!c.execScript||c.execScript("var "+d[0]);for(var e;d.length&&(e=d.shift());)d.length||void 0===b?c=c[e]?c[e]:c[e]={}:c[e]=b};function k(a,b){for(var d=[],c=a;c<=b;c++)d.push(c);return d}function l(a,b){var d=new Date;d.setTime(b);d.setDate(a);return d}function m(a,b,d){var c=new Date;c.setTime(d);c.setMonth(b);c.setDate(a);return c};var n=React.createClass({f:"Day",q:function(a){a.preventDefault();this.props.e(this.props.d)},getDefaultProps:function(){return{selected:!1}},render:function(){var a="day week-"+this.props.k+" dayInWeek-"+this.props.d.getDay(),a=a+(this.props.selected?" selected":"");return React.DOM.div({className:a},React.DOM.a({href:"#",onClick:this.q},this.props.d.getDate()))}});var p=React.createClass({f:"DayPicker",h:function(a){this.props.w(a)},render:function(){var a=this.props.d,b=(new Date(a.getFullYear(),a.getMonth()-1+1,0)).getDate(),d=l(1,a.getTime()),c=(0===d.getDay()?7:d.getDay())-1,b=k(b-c+1,b),e=b.map(function(b){b=m(b,a.getMonth()-1,a.getTime());return n({d:b,k:1,e:this.h})}.bind(this)),b=k(1,(new Date(a.getFullYear(),a.getMonth()+1,0)).getDate()),f=b.map(function(b){var d=l(b,a.getTime()),e=Math.ceil((b+c)/7),f=!1;a.getMonth()==this.props.j.getMonth()&&a.getFullYear()== 2 | this.props.j.getFullYear()&&(f=b==this.props.j.getDate());return n({selected:f,d:d,k:e,e:this.h})}.bind(this)),b=k(1,42-e.length-f.length),b=b.map(function(b){var c=m(b,a.getMonth()+1,a.getTime());return n({d:c,k:Math.ceil((e.length+f.length+b)/7),e:this.h})}.bind(this));return React.DOM.div({className:"datepicker-dates"},React.DOM.div({className:"out"},e),React.DOM.div(null,f),React.DOM.div({className:"out"},b))}});var q=React.createClass({f:"NumberPicker",getDefaultProps:function(){return{number:0}},l:function(a){this.props.m(a.target.getAttribute("data-number"))},render:function(){return React.DOM.div({className:"numberpicker"},React.DOM.a({onClick:this.l,"data-number":this.props.number-1,className:"btn btn-xs btn-default"},"<<"),React.DOM.span({className:"btn btn-xs"},this.props.number),React.DOM.a({onClick:this.l,"data-number":this.props.number+1,className:"btn btn-xs btn-default"},">>"))}});var r=React.createClass({f:"DatePicker",v:function(a){this.setState({c:a})},t:function(a){this.setState({c:a});this.props.onChangeDate(a)},getDefaultProps:function(){return{date:new Date,show:!0,onChangeDate:function(a){console.log("You have selected new date"+a)}}},getInitialState:function(){var a=new Date;a.setTime(this.props.date.getTime());return{c:a}},o:function(a){var b=new Date;b.setTime(this.state.c.getTime());b.setFullYear(a);this.setState({c:b})},n:function(a){var b=new Date;b.setTime(this.state.c.getTime()); 3 | b.setMonth(a-1);this.setState({c:b})},render:function(){return React.DOM.div({className:"datepicker",style:{display:this.props.show?"block":"none"}},React.DOM.div({className:"datepicker-container"},q({number:this.state.c.getFullYear(),m:this.o}),q({number:this.state.c.getMonth()+1,m:this.n}),p({d:this.state.c,j:this.props.date,e:this.v,w:this.t})))}});h("DatePicker",r);var s=React.createClass({f:"DatePickerInput",getDefaultProps:function(){return{date:new Date,beforeUpdate:function(a){return a}}},getInitialState:function(){return{show:!1}},A:function(){this.setState({show:!0})},r:function(){this.setState({show:!1})},s:function(a){this.props.date=a;this.setState({show:!1})},render:function(){return React.DOM.div(null,React.DOM.div({style:{position:"fixed",top:0,left:0,width:"100%",height:"100%",display:this.state.show?"block":"none"},onClick:this.r}),React.DOM.div({className:"datepicker-wrapper"}, 4 | r({date:this.props.date,show:this.state.show,onChangeDate:this.s})),React.DOM.input({type:"text",onFocus:this.A,value:this.props.beforeUpdate(this.props.date)}))}});h("DatePickerInput",s);})(); 5 | -------------------------------------------------------------------------------- /src/css/datepicker.styl: -------------------------------------------------------------------------------- 1 | div.datepicker-dates { 2 | position: relative; 3 | width: 156px; 4 | height: 100%; 5 | margin-top: 0.5em; 6 | div.day { 7 | position:absolute; 8 | } 9 | } 10 | 11 | .dayInWeek-1 { 12 | left:0; 13 | } 14 | .dayInWeek-2 { 15 | left:22px; 16 | } 17 | .dayInWeek-3 { 18 | left:44px; 19 | } 20 | .dayInWeek-4 { 21 | left:66px; 22 | } 23 | .dayInWeek-5 { 24 | left:88px; 25 | } 26 | .dayInWeek-6 { 27 | left:110px; 28 | } 29 | .dayInWeek-0 { 30 | left:132px; 31 | } 32 | .week-1 { 33 | top:0; 34 | } 35 | .week-2 { 36 | top:24px; 37 | } 38 | .week-3 { 39 | top:48px; 40 | } 41 | .week-4 { 42 | top:72px; 43 | } 44 | .week-5 { 45 | top:96px; 46 | } 47 | .week-6 { 48 | top:120px; 49 | } 50 | .datepicker { 51 | position:relative; 52 | height: 220px; 53 | width: 170px; 54 | z-index: 10; 55 | .day { 56 | a { 57 | width:20px; 58 | display: block; 59 | text-align: center; 60 | color: #222; 61 | text-decoration:none; 62 | padding: 2px; 63 | } 64 | a:hover { 65 | background-color: #eaeaea; 66 | } 67 | } 68 | .out .day a { 69 | color: #bcbcbc; 70 | } 71 | .numberpicker { 72 | margin:0.2em; 73 | text-align: center; 74 | } 75 | .day.selected { 76 | background-color: #c9c9c9; 77 | } 78 | } 79 | .datepicker-container { 80 | position:relative; 81 | border:1px solid #979797; 82 | padding:0.5em; 83 | background-color: #fff; 84 | height: 220px; 85 | } 86 | .datepicker-wrapper { 87 | position:absolute; 88 | } -------------------------------------------------------------------------------- /src/js/ui/datepicker/DatePicker.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | goog.provide('misino.ui.datepicker.DatePicker'); 4 | goog.require('misino.ui.numberpicker.NumberPicker'); 5 | goog.require('misino.ui.datepicker.DayPicker'); 6 | 7 | var DatePicker = React.createClass(/** @lends {React.ReactComponent.prototype} */{ 8 | /** 9 | * 10 | * @param {Date} date 11 | */ 12 | onChangeVisibleDate: function(date) { 13 | this.setState({visibleDate:date}); 14 | }, 15 | /** 16 | * 17 | * @param {Date} date 18 | */ 19 | onChangeSelectedDate: function(date) { 20 | this.setState({visibleDate:date}); 21 | this.props['onChangeDate'](date); 22 | }, 23 | /** 24 | * 25 | * @returns {{date: Date, show: boolean, onChangeDate: Function}} 26 | */ 27 | getDefaultProps: function() { 28 | return({'date':new Date(), 'show':true, 'onChangeDate': function(date) { 29 | console.log('You have selected new date' + date); 30 | }}); 31 | }, 32 | /** 33 | * 34 | * @returns {{visibleDate: Date}} 35 | */ 36 | getInitialState: function() { 37 | var date = new Date(); 38 | date.setTime(this.props['date'].getTime()); 39 | return({visibleDate:date}); 40 | }, 41 | /** 42 | * 43 | * @param {number} year 44 | */ 45 | changeYear: function(year) { 46 | var date = new Date(); 47 | date.setTime(this.state.visibleDate.getTime()); 48 | date.setFullYear(year); 49 | this.setState({visibleDate:date}); 50 | }, 51 | /** 52 | * 53 | * @param {number} month 54 | */ 55 | changeMonth: function(month) { 56 | var date = new Date(); 57 | date.setTime(this.state.visibleDate.getTime()); 58 | date.setMonth(month-1); 59 | this.setState({visibleDate:date}); 60 | }, 61 | render: function () { 62 | var style = {display:(this.props['show']?'block':'none')}; 63 | return ( 64 |
65 |
66 | 67 | 68 | 69 | 70 |
71 |
72 | ); 73 | } 74 | }); 75 | 76 | goog.exportSymbol('DatePicker', DatePicker); -------------------------------------------------------------------------------- /src/js/ui/datepicker/DatePickerInput.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | goog.provide('misino.ui.datepicker.DatePickerInput'); 4 | goog.require('misino.ui.datepicker.DatePicker'); 5 | 6 | var DatePickerInput = React.createClass(/** @lends {React.ReactComponent.prototype} */{ 7 | /** 8 | * 9 | * @returns {{date: Date}} 10 | */ 11 | getDefaultProps: function() { 12 | return({'date':new Date(), 'beforeUpdate': function(date) { 13 | return date; 14 | }}); 15 | }, 16 | /** 17 | * 18 | * @returns {{show: boolean}} 19 | */ 20 | getInitialState: function() { 21 | return {show:false}; 22 | }, 23 | showDatePicker: function() { 24 | this.setState({show:true}); 25 | }, 26 | hideDatePicker: function() { 27 | this.setState({show:false}); 28 | }, 29 | /** 30 | * 31 | * @param {Date} date 32 | */ 33 | onChangeDate: function(date) { 34 | this.props['date'] = date; 35 | this.setState({show:false}); 36 | }, 37 | render: function() { 38 | var style={position:'fixed', top:0,left:0, width:'100%', height:'100%', display:(this.state.show?'block':'none')}; 39 | // DatePicker is not defined as JSX because of closure compiler 40 | return ( 41 |
42 |
43 |
44 | {DatePicker( {'date':this.props['date'], 'show':this.state.show, 'onChangeDate':this.onChangeDate})} 45 |
46 | 47 |
48 | ); 49 | } 50 | }); 51 | 52 | goog.exportSymbol('DatePickerInput', DatePickerInput); -------------------------------------------------------------------------------- /src/js/ui/datepicker/Day.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | goog.provide('misino.ui.datepicker.Day'); 4 | 5 | var Day = React.createClass(/** @lends {React.ReactComponent.prototype} */{ 6 | /** 7 | * 8 | * @param e 9 | */ 10 | handleClick: function(e) { 11 | e.preventDefault(); 12 | this.props.changeDate(this.props.date); 13 | }, 14 | /** 15 | * 16 | * @returns {{selected: boolean}} 17 | */ 18 | getDefaultProps: function() { 19 | return {selected:false}; 20 | }, 21 | render: function() { 22 | var className="day week-"+this.props.week+" dayInWeek-"+this.props.date.getDay(); 23 | className += (this.props.selected?' selected':''); 24 | return ( 25 |
26 | {this.props.date.getDate()} 27 |
28 | ); 29 | } 30 | }); -------------------------------------------------------------------------------- /src/js/ui/datepicker/DayPicker.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | goog.provide('misino.ui.datepicker.DayPicker'); 4 | goog.require('misino.utils.DateUtils'); 5 | goog.require('misino.ui.datepicker.Day'); 6 | 7 | var DayPicker = React.createClass(/** @lends {React.ReactComponent.prototype} */{ 8 | /** 9 | * 10 | * @param {Date} date 11 | */ 12 | selectDay: function(date) { 13 | this.props.selectDate(date); 14 | }, 15 | render: function (){ 16 | var date=this.props.date, 17 | beforeDaysCount = DateUtils.daysInMonthCount((date.getMonth()-1), date.getFullYear()), 18 | firstDay = DateUtils.createNewDay(1, date.getTime()), 19 | offset = (firstDay.getDay()===0?7:firstDay.getDay())- 1, 20 | daysArray = DateUtils.getArrayByBoundary(beforeDaysCount-offset+1, beforeDaysCount); 21 | 22 | var previousMonthDays = daysArray.map(function(day){ 23 | var thisDate = DateUtils.createNewDayMonth(day, date.getMonth()-1, date.getTime()); 24 | return 25 | }.bind(this)); 26 | 27 | daysArray = DateUtils.getArrayByBoundary(1, DateUtils.daysInMonthCount(date.getMonth(), date.getFullYear())); 28 | var actualMonthDays = daysArray.map(function(day) { 29 | var thisDate = DateUtils.createNewDay(day, date.getTime()), 30 | weekNumber = Math.ceil((day+offset) / 7), 31 | selected = false; 32 | 33 | if(date.getMonth()==this.props.selectedDate.getMonth() && date.getFullYear()==this.props.selectedDate.getFullYear()) { 34 | selected = (day==this.props.selectedDate.getDate()); 35 | } 36 | return 37 | }.bind(this)); 38 | 39 | daysArray = DateUtils.getArrayByBoundary(1, 42- previousMonthDays.length - actualMonthDays.length); 40 | var nextMonthDays = daysArray.map(function(day){ 41 | var thisDate = DateUtils.createNewDayMonth(day, date.getMonth()+1, date.getTime()), 42 | weekNumber = Math.ceil((previousMonthDays.length + actualMonthDays.length + day) / 7); 43 | return 44 | }.bind(this)); 45 | 46 | return ( 47 |
48 |
49 | {previousMonthDays} 50 |
51 |
52 | {actualMonthDays} 53 |
54 |
55 | {nextMonthDays} 56 |
57 |
58 | ); 59 | } 60 | }); -------------------------------------------------------------------------------- /src/js/ui/numberpicker/NumberPicker.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | goog.provide('misino.ui.numberpicker.NumberPicker'); 4 | 5 | var NumberPicker = React.createClass(/** @lends {React.ReactComponent.prototype} */{ 6 | getDefaultProps: function() { 7 | return {number:0}; 8 | }, 9 | changeNumber: function(e) { 10 | this.props.onChangeNumber(e.target.getAttribute('data-number')); 11 | }, 12 | 13 | render: function() { 14 | return ( 15 |
16 | << 17 | {this.props.number} 18 | >> 19 |
20 | ) 21 | } 22 | }); -------------------------------------------------------------------------------- /src/js/utils/DateUtils.js: -------------------------------------------------------------------------------- 1 | goog.provide('misino.utils.DateUtils'); 2 | 3 | var DateUtils = { 4 | /** 5 | * Gets count of days in current month. 6 | * @param {number} month January has number 1. February 2, ... and so on 7 | * @param {number} year 8 | * @returns {number} 9 | */ 10 | daysInMonthCount: function(month, year) { 11 | var d =new Date(year, month+1, 0); 12 | return d.getDate(); 13 | }, 14 | /** 15 | * Gets array of numbers starting from start to end 16 | * @param {number} start 17 | * @param {number} end 18 | * @returns {Array} array of numbers 19 | */ 20 | getArrayByBoundary: function(start, end) { 21 | var out = []; 22 | for(var i= start; i<=end; i++) { 23 | out.push(i); 24 | } 25 | return out; 26 | }, 27 | /** 28 | * Creates new Date() instance. 29 | * @param {number} date day in month 30 | * @param {number} time 31 | * @returns {Date} 32 | */ 33 | createNewDay: function(date, time) { 34 | var newDate = new Date(); 35 | newDate.setTime(time); 36 | newDate.setDate(date); 37 | return newDate; 38 | }, 39 | /** 40 | * Creates new Date() instance. 41 | * @param {number} date day in month 42 | * @param {number} month 43 | * @param {number} time 44 | * @returns {Date} 45 | */ 46 | createNewDayMonth: function(date, month, time) { 47 | var newDate = new Date(); 48 | newDate.setTime(time); 49 | newDate.setMonth(month); 50 | newDate.setDate(date); 51 | return newDate; 52 | } 53 | } --------------------------------------------------------------------------------