├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── HISTORY.md ├── LICENSE ├── README.md ├── bower.json ├── dist ├── component.css ├── react-date-select.js └── react-date-select.min.js ├── example └── src │ ├── .gitignore │ ├── example.js │ ├── example.less │ └── index.html ├── gulpfile.js ├── images ├── date-select-button-next.png └── date-select-button-prev.png ├── less ├── component.less ├── dateselect.less ├── mixins.less └── variables.less ├── lib ├── DateSelect.js ├── DateSelectCalendar.js ├── DateSelectDialog.js └── DateSelectHeader.js ├── package.json └── src ├── Calendar.js ├── CalendarHeader.js ├── DateSelect.js ├── Popup.js ├── PopupDialog.js └── __tests__ └── DateSelect-test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = true 10 | indent_style = tab 11 | 12 | [*.json] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.yml] 17 | indent_style = space 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .publish/* 2 | dist/* 3 | example/dist/* 4 | lib/* 5 | node_modules/* 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | "plugins": [ 8 | "react" 9 | ], 10 | "rules": { 11 | "curly": [2, "multi-line"], 12 | "no-shadow": 0, 13 | "no-trailing-spaces": 0, 14 | "no-underscore-dangle": 0, 15 | "no-unused-expressions": 0, 16 | "object-curly-spacing": [1, "always"], 17 | "quotes": [2, "single", "avoid-escape"], 18 | "react/jsx-quotes": 1, 19 | "react/jsx-no-undef": 1, 20 | "react/jsx-uses-react": 1, 21 | "react/jsx-uses-vars": 1, 22 | "react/no-did-mount-set-state": 1, 23 | "react/no-did-update-set-state": 1, 24 | "react/self-closing-comp": 1, 25 | "react/wrap-multilines": 1, 26 | "semi": 2, 27 | "strict": 0 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage tools 11 | lib-cov 12 | coverage 13 | 14 | # Dependency directory 15 | node_modules 16 | 17 | # Publish directory 18 | .publish 19 | example/dist 20 | 21 | # Other 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | bower.json 3 | example/dist 4 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # React-Date-Select 2 | 3 | ## v0.0.6 / 2015-05-10 4 | 5 | * Fixed dialog animation 6 | * Separated animation namespace from layout element 7 | 8 | ## v0.0.5 / 2015-05-08 9 | 10 | * Allowing use of child component(s) to trigger DateSelect 11 | * Updated gulp-tasks 12 | * Fixed component dependency config 13 | 14 | ## v0.0.4 / 2015-05-07 15 | 16 | * Removed lodash dependency 17 | 18 | ## v0.0.3 / 2015-05-07 19 | 20 | * Extensive Styles & Markup cleanup 21 | * Added npm scripts for development & release 22 | 23 | ## v0.0.2 / 2015-04-20 24 | 25 | * Styles & Markup added (still WIP) 26 | 27 | ## v0.0.1 / 2015-04-20 28 | 29 | * Initial Release (WIP) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jed Watson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Under Development 2 | ================= 3 | 4 | **NOTE** This component is currently under development and _doesn't actually work yet_. If you're interested, please add a star / watch the repo and we'll have it ready for use very soon! Thanks :) 5 | 6 | React-Date-Select 7 | ================= 8 | 9 | A Date Select / Picker control built with and for [React](http://facebook.github.io/react/index.html). Initially built for use in [KeystoneJS](http://www.keystonejs.com). 10 | 11 | 12 | ## Demo & Examples 13 | 14 | Live demo: [jedwatson.github.io/react-date-select](http://jedwatson.github.io/react-date-select/) 15 | 16 | To build the examples locally, run: 17 | 18 | ``` 19 | npm install 20 | gulp dev 21 | ``` 22 | 23 | Then open [`localhost:8000`](http://localhost:8000) in a browser. 24 | 25 | 26 | ## Project Status 27 | 28 | This project is currently under initial development. Feedback and contributions welcome! 29 | 30 | 31 | ## Installation 32 | 33 | The easiest way to use React-Date-Select is to install it from NPM and include it in your own React build process (using [Browserify](http://browserify.org), etc). 34 | 35 | You can also use the standalone build by including `dist/react-date-select.js` and `dist/react-date-select.css` in your page. If you use this, make sure you have already included the React, Lodash and classNames packages. 36 | 37 | ``` 38 | npm install react-date-select --save 39 | ``` 40 | 41 | 42 | ## Usage 43 | 44 | _coming soon_ 45 | 46 | 47 | # License 48 | 49 | MIT Licensed. Copyright (c) Jed Watson 2015. 50 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-date-select", 3 | "main": "dist/react-date-select.min.js", 4 | "version": "0.0.7", 5 | "homepage": "https://github.com/JedWatson/react-date-select", 6 | "authors": [ 7 | "Jed Watson" 8 | ], 9 | "description": "A Date Select / Picker Input Component built with and for ReactJS", 10 | "moduleType": [ 11 | "amd", 12 | "globals", 13 | "node" 14 | ], 15 | "keywords": [ 16 | "react", 17 | "react-component", 18 | "date", 19 | "datepicker", 20 | "dateinput", 21 | "input", 22 | "form", 23 | "ui" 24 | ], 25 | "license": "MIT", 26 | "ignore": [ 27 | ".editorconfig", 28 | ".gitignore", 29 | "package.json", 30 | "src", 31 | "node_modules", 32 | "example", 33 | "test" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /dist/component.css: -------------------------------------------------------------------------------- 1 | .DateSelect { 2 | font-size: 14px; 3 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | -webkit-font-smoothing: antialiased; 5 | } 6 | .DateSelect * { 7 | -webkit-box-sizing: border-box; 8 | -moz-box-sizing: border-box; 9 | box-sizing: border-box; 10 | } 11 | .DateSelect button, 12 | .DateSelect input, 13 | .DateSelect select { 14 | font-size: inherit; 15 | font-family: inherit; 16 | margin: 0; 17 | -webkit-appearance: none; 18 | -moz-appearance: none; 19 | } 20 | .DateSelect-dialog { 21 | left: 0; 22 | margin-left: auto; 23 | margin-right: auto; 24 | position: absolute; 25 | right: 0; 26 | top: 0; 27 | width: 100%; 28 | z-index: 110; 29 | } 30 | .modal-dialog-enter { 31 | -webkit-animation-name: modalDialogEnter; 32 | animation-name: modalDialogEnter; 33 | -webkit-animation-duration: 280ms; 34 | animation-duration: 280ms; 35 | -webkit-animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); 36 | animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); 37 | } 38 | .modal-dialog-leave { 39 | -webkit-animation-duration: 140ms; 40 | animation-duration: 140ms; 41 | -webkit-animation-name: modalDialogLeave; 42 | animation-name: modalDialogLeave; 43 | } 44 | .DateSelect-content { 45 | background-color: #fafafa; 46 | outline: none; 47 | position: relative; 48 | } 49 | .DateSelectCalendar__legend:before, 50 | .DateSelectCalendar__month:before, 51 | .DateSelectCalendar__legend:after, 52 | .DateSelectCalendar__month:after { 53 | content: " "; 54 | display: table; 55 | } 56 | .DateSelectCalendar__legend:after, 57 | .DateSelectCalendar__month:after { 58 | clear: both; 59 | } 60 | .DateSelectCalendar__legend { 61 | padding: 8px 8px 0; 62 | } 63 | .DateSelectCalendar__month { 64 | padding: 0 8px 8px; 65 | } 66 | .DateSelectCalendar__legend__day, 67 | .DateSelectCalendar__month__day { 68 | float: left; 69 | font-size: inherit; 70 | line-height: 1; 71 | padding: 10px 2px; 72 | position: relative; 73 | text-align: center; 74 | width: 14.286%; 75 | } 76 | .DateSelectCalendar__toolbar { 77 | border-bottom: 1px solid #d4d4d4; 78 | position: relative; 79 | text-align: center; 80 | } 81 | .DateSelectCalendar__toolbar__select { 82 | background: none; 83 | border: none; 84 | display: inline-block; 85 | font-size: inherit; 86 | font-weight: 500; 87 | line-height: 30px; 88 | padding-left: .5em; 89 | padding-right: .5em; 90 | } 91 | .DateSelectCalendar__toolbar__select:focus { 92 | color: #1076e5; 93 | outline: none; 94 | } 95 | .DateSelectCalendar__toolbar__button { 96 | background-color: transparent; 97 | background-image: none; 98 | background-position: center center; 99 | background-repeat: no-repeat; 100 | background-size: 20px 30px; 101 | border: none; 102 | display: inline-block; 103 | height: 30px; 104 | opacity: .6; 105 | overflow: hidden; 106 | position: absolute; 107 | text-indent: -9999px; 108 | top: 0; 109 | white-space: nowrap; 110 | width: 36px; 111 | } 112 | .DateSelectCalendar__toolbar__button:hover { 113 | opacity: 1; 114 | } 115 | .DateSelectCalendar__toolbar__button--prev { 116 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA8CAYAAAAUufjgAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjBBQkU4NjZENUM5MTFFNEI1MkFDNEUxMUIxMzU3NDEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjBBQkU4NjdENUM5MTFFNEI1MkFDNEUxMUIxMzU3NDEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMEFCRTg2NEQ1QzkxMUU0QjUyQUM0RTExQjEzNTc0MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGMEFCRTg2NUQ1QzkxMUU0QjUyQUM0RTExQjEzNTc0MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp2znPUAAAD9SURBVHja7JnBCQIxEEUzkgZsQkuwAW3GfrQRj+rdFkwRNuAhTiCBIARNMnFH9g989rLsPObzk+wuee+N5loY5QVAAAIQgAAEIAABCMB5A9qam4mouVHryX32FgeHDl1PCKP/VpW1ZJ1Di9o+ueygya1Yp3hVZ/GWdZOAG2HxnvWMtuZqtlgKMIXBFzQpYB4GcUCrJQwjQiIbBuGQlMIgbnHrBMn8qjpCsmM9Rk+wN8VrltNocao7a8O6arT4faE+al2o/2Kr+xQeVYBph3EaQlIqF8Nz0XbcKh75WydINY3xVtf7XjzFXyl8+gAgAAEIQAACEIAABKDqegkwAHr84FvNdKczAAAAAElFTkSuQmCC"); 117 | left: 0; 118 | } 119 | .DateSelectCalendar__toolbar__button--next { 120 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA8CAYAAAAUufjgAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6M0UyMTVBRTNENUNBMTFFNEI1MkFDNEUxMUIxMzU3NDEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6M0UyMTVBRTRENUNBMTFFNEI1MkFDNEUxMUIxMzU3NDEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMEFCRTg2OEQ1QzkxMUU0QjUyQUM0RTExQjEzNTc0MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozRTIxNUFFMkQ1Q0ExMUU0QjUyQUM0RTExQjEzNTc0MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgHkmaEAAAEASURBVHja7JnRDYIwEIapYQGHkBVcAN90EfeRZdR3VhB3YAEf6jWpiRDBpj3gT/gv+R9Lv9z1b6/UWGsz5Nhk4EFAAhKQgAQkIAEJSMB1A+Ypg40x0WNDO/lVlLhKrcTfVMfq8wnRVbSdYl4tQKdGVGgDaq7BnagWHRBL/K2X6IxY4r4uY+ZBABw1Dwqg0+OXeZYwyVAU3jwlikmG1DEPUgY7xzdqBtv+/hg6Rz5D1p6ikzcLXD94F+1j4aYucYW6UUMfdW1os7CESZwZjr7tgrs03bwZGnWbKZQ4quUPncOkvJPwVpd6G5vjlYq/PghIQAISkIAEJCABCQgdbwEGAOhL4l/o/UvgAAAAAElFTkSuQmCC"); 121 | right: 0; 122 | } 123 | .DateSelectCalendar__legend { 124 | color: #999999; 125 | font-weight: 500; 126 | } 127 | .DateSelectCalendar__legend__day { 128 | border-bottom: none; 129 | cursor: default; 130 | } 131 | .DateSelectCalendar__month__day { 132 | background: none; 133 | border: none; 134 | border-radius: 0.25em; 135 | outline: none; 136 | } 137 | .DateSelectCalendar__month__day:hover { 138 | background-color: #e3edf8; 139 | } 140 | .DateSelectCalendar__month__day:focus { 141 | box-shadow: 0 0 0 1px #fafafa, 0 0 0 3px rgba(16, 118, 229, 0.15); 142 | color: #1076e5; 143 | outline: 0; 144 | } 145 | .DateSelectCalendar__month__day:active { 146 | background-color: #0e6acd; 147 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 148 | color: white; 149 | } 150 | .DateSelectCalendar__month__day.is-current-day { 151 | background-color: #f2f2f2; 152 | box-shadow: inset 0 0 0 1px #d4d4d4; 153 | } 154 | .DateSelectCalendar__month__day.is-current-day.is-selected { 155 | box-shadow: none; 156 | } 157 | .DateSelectCalendar__month__day.is-current-day.is-selected:focus { 158 | box-shadow: 0 0 0 1px #fafafa, 0 0 0 3px rgba(16, 118, 229, 0.15); 159 | outline: 0; 160 | } 161 | .DateSelectCalendar__month__day.is-selected { 162 | background-color: #1076e5; 163 | color: white; 164 | } 165 | .multi-picker .DateSelectCalendar:first-child .DateSelectCalendar__month__day.is-selected::after, 166 | .multi-picker .DateSelectCalendar:last-child .DateSelectCalendar__month__day.is-selected::after { 167 | border-bottom: 5px solid transparent; 168 | border-top: 5px solid transparent; 169 | content: ""; 170 | height: 0; 171 | margin-top: -5px; 172 | position: absolute; 173 | top: 50%; 174 | width: 0; 175 | z-index: 1; 176 | } 177 | .multi-picker .DateSelectCalendar:first-child .DateSelectCalendar__month__day.is-selected::after { 178 | border-left: 5px solid #1076e5; 179 | right: -5px; 180 | } 181 | .multi-picker .DateSelectCalendar:last-child .DateSelectCalendar__month__day.is-selected::after { 182 | border-right: 5px solid #1076e5; 183 | left: -5px; 184 | } 185 | .multi-picker .DateSelectCalendar:first-child .is-after-selected-day { 186 | border-radius: 0; 187 | background-color: #eef3f9; 188 | color: #22558c; 189 | } 190 | .multi-picker .DateSelectCalendar:last-child .is-before-selected-day { 191 | border-radius: 0; 192 | background-color: #eef3f9; 193 | color: #22558c; 194 | } 195 | .DateSelectHeader { 196 | background-image: -webkit-linear-gradient(top, #2781e3 0%, #1a6dc7 100%); 197 | background-image: -o-linear-gradient(top, #2781e3 0%, #1a6dc7 100%); 198 | background-image: linear-gradient(to bottom, #2781e3 0%, #1a6dc7 100%); 199 | background-repeat: repeat-x; 200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2781e3', endColorstr='#ff1a6dc7', GradientType=0); 201 | background-color: #1c77d9; 202 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 0 white; 203 | color: white; 204 | display: block; 205 | text-align: center; 206 | } 207 | .DateSelectHeader--expanded { 208 | line-height: 1; 209 | } 210 | .DateSelectHeader--expanded > .DateSelectHeader { 211 | display: block; 212 | font-weight: 300; 213 | } 214 | .DateSelectHeader--expanded > .DateSelectHeader__dow { 215 | background-color: rgba(0, 0, 0, 0.1); 216 | border-top-right-radius: 0.33em; 217 | border-top-left-radius: 0.33em; 218 | display: block; 219 | font-size: .85em; 220 | padding: 5px; 221 | } 222 | .DateSelectHeader--expanded > .DateSelectHeader__month { 223 | display: block; 224 | padding-top: 10px; 225 | text-transform: uppercase; 226 | } 227 | .DateSelectHeader--expanded > .DateSelectHeader__day { 228 | display: block; 229 | font-size: 4em; 230 | margin-bottom: 5px; 231 | margin-top: 5px; 232 | } 233 | .DateSelectHeader--expanded > .DateSelectHeader__year { 234 | color: rgba(255, 255, 255, 0.75); 235 | display: block; 236 | padding-bottom: 10px; 237 | } 238 | .DateSelectHeader--condensed { 239 | font-weight: bold; 240 | letter-spacing: 0.0125em; 241 | padding: 8px; 242 | } 243 | .DateSelectHeader--condensed > .DateSelectHeader, 244 | .DateSelectHeader--condensed > .DateSelectHeader__dow, 245 | .DateSelectHeader--condensed > .DateSelectHeader__month, 246 | .DateSelectHeader--condensed > .DateSelectHeader__day, 247 | .DateSelectHeader--condensed > .DateSelectHeader__year { 248 | display: inline-block; 249 | margin-left: .15em; 250 | margin-right: .15em; 251 | } 252 | .DateSelectHeader--condensed > .DateSelectHeader__dow { 253 | opacity: .75; 254 | display: block; 255 | font-size: .75em; 256 | font-weight: 500; 257 | margin: 0; 258 | text-transform: uppercase; 259 | -webkit-font-smoothing: antialiased; 260 | } 261 | .DateSelect__range { 262 | border-top: 1px solid #d4d4d4; 263 | display: table; 264 | font-size: .85em; 265 | position: relative; 266 | text-align: center; 267 | width: 100%; 268 | } 269 | .DateSelect__range:hover { 270 | color: #666666; 271 | } 272 | .DateSelect__range__item { 273 | -webkit-transition: color 200ms; 274 | -o-transition: color 200ms; 275 | transition: color 200ms; 276 | background-color: transparent; 277 | background-image: none; 278 | border: none; 279 | color: inherit; 280 | cursor: pointer; 281 | display: inline-block; 282 | outline: none; 283 | padding: 8px; 284 | position: relative; 285 | text-align: center; 286 | } 287 | .DateSelect__range__item:hover { 288 | color: #000000; 289 | } 290 | .DateSelect__range__item.active, 291 | .DateSelect__range__item:focus { 292 | box-shadow: 0 1px 0 #666666; 293 | color: #000000; 294 | } 295 | .DateSelect__range__item.active:after, 296 | .DateSelect__range__item:focus:after { 297 | border-bottom: 4px solid #666666; 298 | border-left: 4px solid transparent; 299 | border-right: 4px solid transparent; 300 | bottom: 0; 301 | content: ""; 302 | display: inline-block; 303 | height: 0; 304 | left: 50%; 305 | margin-left: -4px; 306 | position: absolute; 307 | width: 0; 308 | } 309 | .DateSelectFooter { 310 | background-color: #ededed; 311 | border-top: 1px solid #d4d4d4; 312 | border-bottom-right-radius: 0.33em; 313 | border-bottom-left-radius: 0.33em; 314 | } 315 | .DateSelectFooter:before, 316 | .DateSelectFooter:after { 317 | content: " "; 318 | display: table; 319 | } 320 | .DateSelectFooter:after { 321 | clear: both; 322 | } 323 | .DateSelectFooter__button { 324 | background-color: transparent; 325 | background-image: none; 326 | border: none; 327 | border-radius: 0; 328 | color: #666666; 329 | float: left; 330 | font-size: inherit; 331 | outline: none; 332 | padding: 8px; 333 | text-align: center; 334 | width: 50%; 335 | } 336 | .DateSelectFooter__button:last-child { 337 | border-left: 1px solid #d4d4d4; 338 | } 339 | .DateSelectFooter__button--primary { 340 | color: #1076e5; 341 | font-weight: 500; 342 | } 343 | .single-picker .DateSelectFooter__button:hover, 344 | .single-picker .DateSelectFooter__button:focus { 345 | background-color: rgba(255, 255, 255, 0.66); 346 | } 347 | .single-picker .DateSelectFooter__button:active { 348 | background-color: rgba(0, 0, 0, 0.05); 349 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 350 | } 351 | .single-picker .DateSelectFooter__button:first-child { 352 | border-bottom-left-radius: 0.33em; 353 | } 354 | .single-picker .DateSelectFooter__button:last-child { 355 | border-bottom-right-radius: 0.33em; 356 | } 357 | .multi-picker .DateSelectFooter { 358 | padding: 12px 16px; 359 | } 360 | .multi-picker .DateSelectFooter__button { 361 | background: none; 362 | border-radius: .25em; 363 | border: 0; 364 | border: 1px solid transparent; 365 | cursor: pointer; 366 | display: inline-block; 367 | float: none; 368 | font-size: inherit; 369 | font-weight: 500; 370 | height: 2.4em; 371 | line-height: 2.3em; 372 | margin-bottom: 0; 373 | overflow: hidden; 374 | padding: 0 1em; 375 | text-align: center; 376 | touch-action: manipulation; 377 | vertical-align: middle; 378 | white-space: nowrap; 379 | width: 80px; 380 | } 381 | .multi-picker .DateSelectFooter__button:hover, 382 | .multi-picker .DateSelectFooter__button:focus, 383 | .multi-picker .DateSelectFooter__button.focus { 384 | color: #1076e5; 385 | text-decoration: none; 386 | } 387 | .multi-picker .DateSelectFooter__button:active, 388 | .multi-picker .DateSelectFooter__button.active { 389 | background-image: none; 390 | outline: 0; 391 | } 392 | .multi-picker .DateSelectFooter__button.disabled, 393 | .multi-picker .DateSelectFooter__button[disabled] { 394 | opacity: .4; 395 | pointer-events: none; 396 | } 397 | .multi-picker .DateSelectFooter__button--primary { 398 | background-image: -webkit-linear-gradient(top, #1a80ef 0%, #0f6cd2 100%); 399 | background-image: -o-linear-gradient(top, #1a80ef 0%, #0f6cd2 100%); 400 | background-image: linear-gradient(to bottom, #1a80ef 0%, #0f6cd2 100%); 401 | background-repeat: repeat-x; 402 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1a80ef', endColorstr='#ff0f6cd2', GradientType=0); 403 | background-color: #1076e5; 404 | color: white; 405 | } 406 | .multi-picker .DateSelectFooter__button--primary:hover, 407 | .multi-picker .DateSelectFooter__button--primary:focus, 408 | .multi-picker .DateSelectFooter__button--primary.focus { 409 | background-image: -webkit-linear-gradient(top, #2d8bf0 0%, #1076e5 100%); 410 | background-image: -o-linear-gradient(top, #2d8bf0 0%, #1076e5 100%); 411 | background-image: linear-gradient(to bottom, #2d8bf0 0%, #1076e5 100%); 412 | background-repeat: repeat-x; 413 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2d8bf0', endColorstr='#ff1076e5', GradientType=0); 414 | color: white; 415 | outline: none; 416 | } 417 | .multi-picker .DateSelectFooter__button--primary:focus, 418 | .multi-picker .DateSelectFooter__button--primary.focus { 419 | box-shadow: 0 0 0 3px rgba(16, 118, 229, 0.25); 420 | } 421 | .multi-picker .DateSelectFooter__button--primary:active, 422 | .multi-picker .DateSelectFooter__button--primary.active { 423 | background-color: #0e6acd; 424 | background-image: none; 425 | box-shadow: none; 426 | } 427 | @media (min-width: 768px) { 428 | .DateSelect-dialog { 429 | margin-bottom: 100px; 430 | margin-top: 100px; 431 | width: 280px; 432 | } 433 | .DateSelect-content { 434 | border-radius: 0.33em; 435 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.175), 0 3px 8px rgba(0, 0, 0, 0.175); 436 | } 437 | .DateSelectHeader { 438 | border-top-right-radius: 0.33em; 439 | border-top-left-radius: 0.33em; 440 | } 441 | .multi-picker .DateSelect-body:before, 442 | .multi-picker .DateSelect-body:after { 443 | content: " "; 444 | display: table; 445 | } 446 | .multi-picker .DateSelect-body:after { 447 | clear: both; 448 | } 449 | .multi-picker .DateSelect-dialog { 450 | width: 560px; 451 | } 452 | .multi-picker .DateSelectHeader { 453 | border-radius: 0; 454 | box-shadow: -1px 0 0 #165eac; 455 | } 456 | .multi-picker .DateSelectCalendar { 457 | border-left: 1px solid #d4d4d4; 458 | float: left; 459 | width: 50%; 460 | } 461 | .multi-picker .DateSelectCalendar:first-child { 462 | border-left: none; 463 | } 464 | .multi-picker .DateSelectCalendar:first-child .DateSelectHeader { 465 | border-top-left-radius: 0.33em; 466 | box-shadow: none; 467 | } 468 | .multi-picker .DateSelectCalendar:last-child .DateSelectHeader { 469 | border-top-right-radius: 0.33em; 470 | } 471 | } 472 | .DateSelect-backdrop { 473 | -webkit-animation-duration: 140ms; 474 | animation-duration: 140ms; 475 | background-color: rgba(0, 0, 0, 0.5); 476 | height: 100%; 477 | left: 0; 478 | position: fixed; 479 | top: 0; 480 | width: 100%; 481 | z-index: 109; 482 | } 483 | .modal-backdrop-enter { 484 | -webkit-animation-name: modalBackdropEnter; 485 | animation-name: modalBackdropEnter; 486 | } 487 | .modal-backdrop-leave { 488 | -webkit-animation-delay: 240ms; 489 | animation-delay: 240ms; 490 | -webkit-animation-name: modalBackdropLeave; 491 | animation-name: modalBackdropLeave; 492 | } 493 | @-webkit-keyframes modalDialogEnter { 494 | from { 495 | opacity: 0; 496 | -webkit-transform: scale(0.8); 497 | } 498 | to { 499 | opacity: 1; 500 | -webkit-transform: scale(1); 501 | } 502 | } 503 | @keyframes modalDialogEnter { 504 | from { 505 | opacity: 0; 506 | transform: scale(0.8); 507 | } 508 | to { 509 | opacity: 1; 510 | transform: scale(1); 511 | } 512 | } 513 | @-webkit-keyframes modalDialogLeave { 514 | from { 515 | opacity: 1; 516 | -webkit-transform: scale(1); 517 | } 518 | to { 519 | opacity: 0; 520 | -webkit-transform: scale(0.8); 521 | } 522 | } 523 | @keyframes modalDialogLeave { 524 | from { 525 | opacity: 1; 526 | transform: scale(1); 527 | } 528 | to { 529 | opacity: 0; 530 | transform: scale(0.8); 531 | } 532 | } 533 | @-webkit-keyframes modalBackdropEnter { 534 | from { 535 | opacity: 0; 536 | } 537 | to { 538 | opacity: 1; 539 | } 540 | } 541 | @keyframes modalBackdropEnter { 542 | from { 543 | opacity: 0; 544 | } 545 | to { 546 | opacity: 1; 547 | } 548 | } 549 | @-webkit-keyframes modalBackdropLeave { 550 | from { 551 | opacity: 1; 552 | } 553 | to { 554 | opacity: 0; 555 | } 556 | } 557 | @keyframes modalBackdropLeave { 558 | from { 559 | opacity: 1; 560 | } 561 | to { 562 | opacity: 0; 563 | } 564 | } 565 | -------------------------------------------------------------------------------- /dist/react-date-select.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.DateSelect = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o self.state.selectedDate 158 | }); 159 | return React.createElement( 160 | 'button', 161 | { key: 'day' + day, onClick: self.handleDaySelection.bind(self, day), className: dayClass }, 162 | day 163 | ); 164 | }); 165 | 166 | var titleMonths = months.map(function (month, i) { 167 | return React.createElement( 168 | 'option', 169 | { key: 'month' + i, value: month }, 170 | month.slice(0, 3) 171 | ); 172 | }); 173 | var titleYears = years.map(function (year, i) { 174 | return React.createElement( 175 | 'option', 176 | { key: 'year' + i, value: year }, 177 | year 178 | ); 179 | }); 180 | 181 | var calendar = React.createElement( 182 | 'div', 183 | { className: calendarClass }, 184 | !this.props.isHeaderless && React.createElement(DateSelectHeader, { selectedDate: this.state.selectedDate, isExpanded: this.props.isExpanded }), 185 | React.createElement( 186 | 'div', 187 | { className: 'DateSelectCalendar__toolbar' }, 188 | React.createElement( 189 | 'button', 190 | { className: 'DateSelectCalendar__toolbar__button DateSelectCalendar__toolbar__button--prev' }, 191 | 'Previous Month' 192 | ), 193 | React.createElement( 194 | 'select', 195 | { className: 'DateSelectCalendar__toolbar__select', defaultValue: currentMonth }, 196 | titleMonths 197 | ), 198 | React.createElement( 199 | 'select', 200 | { className: 'DateSelectCalendar__toolbar__select', defaultValue: currentYear }, 201 | titleYears 202 | ), 203 | React.createElement( 204 | 'button', 205 | { className: 'DateSelectCalendar__toolbar__button DateSelectCalendar__toolbar__button--next' }, 206 | 'Next Month' 207 | ) 208 | ), 209 | React.createElement( 210 | 'div', 211 | { className: 'DateSelectCalendar__legend' }, 212 | weekDays 213 | ), 214 | React.createElement( 215 | 'div', 216 | { className: 'DateSelectCalendar__month' }, 217 | monthDays 218 | ) 219 | ); 220 | 221 | return calendar; 222 | } 223 | }); 224 | 225 | },{"./DateSelectHeader":4,"react/addons":undefined}],3:[function(require,module,exports){ 226 | 'use strict'; 227 | 228 | var React = require('react/addons'); 229 | var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; 230 | var moment = (window.moment); 231 | var classNames = (window.classNames); 232 | 233 | var DateSelectCalendar = require('./DateSelectCalendar'); 234 | 235 | module.exports = React.createClass({ 236 | displayName: 'DateSelectDialog', 237 | propTypes: { 238 | backdropClosesDateSelect: React.PropTypes.bool, 239 | className: React.PropTypes.string, 240 | isExpanded: React.PropTypes.bool, 241 | isHeaderless: React.PropTypes.bool, 242 | isInstant: React.PropTypes.bool, 243 | isMulti: React.PropTypes.bool, 244 | onCancel: React.PropTypes.func, 245 | onSelect: React.PropTypes.func, 246 | predefinedRangeOptions: React.PropTypes.array, 247 | showPredefinedRanges: React.PropTypes.bool 248 | }, 249 | getInitialState: function getInitialState() { 250 | return { 251 | startDate: '', 252 | endDate: '' 253 | }; 254 | }, 255 | renderDialog: function renderDialog() { 256 | if (!this.props.isOpen) return null; 257 | return React.createElement( 258 | 'div', 259 | { className: 'DateSelect-dialog' }, 260 | React.createElement( 261 | 'div', 262 | { className: 'DateSelect-content' }, 263 | React.createElement( 264 | 'div', 265 | { className: 'DateSelect-body' }, 266 | React.createElement(DateSelectCalendar, { selectedDate: this.state.startDate, isHeaderless: this.props.isHeaderless, isInstant: this.props.isInstant }), 267 | this.props.isMulti && React.createElement(DateSelectCalendar, { selectedDate: this.state.endDate, isHeaderless: this.props.isHeaderless }) 268 | ), 269 | this.renderRanges(), 270 | !this.props.isInstant && React.createElement( 271 | 'div', 272 | { className: 'DateSelectFooter' }, 273 | React.createElement( 274 | 'button', 275 | { onClick: this.props.onSelect, className: 'DateSelectFooter__button DateSelectFooter__button--primary' }, 276 | 'Confirm' 277 | ), 278 | React.createElement( 279 | 'button', 280 | { onClick: this.props.onCancel, className: 'DateSelectFooter__button DateSelectFooter__button--link' }, 281 | 'Cancel' 282 | ) 283 | ) 284 | ) 285 | ); 286 | }, 287 | renderRanges: function renderRanges() { 288 | if (!this.props.showPredefinedRanges) return null; 289 | var self = this; 290 | var rangeItems = this.props.predefinedRangeOptions.map(function (r, i) { 291 | function action() { 292 | self.setState({ 293 | startDate: moment().format('D'), 294 | endDate: r.value.format('D') 295 | }); 296 | } 297 | return React.createElement( 298 | 'button', 299 | { key: 'range-button' + i, onClick: action, className: 'DateSelect__range__item' }, 300 | r.label 301 | ); 302 | }); 303 | return React.createElement( 304 | 'div', 305 | { className: 'DateSelect__range' }, 306 | rangeItems 307 | ); 308 | }, 309 | renderBackdrop: function renderBackdrop() { 310 | if (!this.props.isOpen) return null; 311 | return React.createElement('div', { className: 'DateSelect-backdrop', onClick: this.props.backdropClosesDateSelect ? this.props.onCancel : null }); 312 | }, 313 | render: function render() { 314 | // classes 315 | var componentClass = classNames('DateSelect', { 316 | 'single-picker': !this.props.isMulti, 317 | 'multi-picker': this.props.isMulti, 318 | 'range-picker': this.props.showPredefinedRanges 319 | }, this.props.className); 320 | 321 | // build the components 322 | return React.createElement( 323 | 'div', 324 | { className: componentClass }, 325 | React.createElement( 326 | ReactCSSTransitionGroup, 327 | { transitionName: 'modal-dialog', component: 'div' }, 328 | this.renderDialog() 329 | ), 330 | React.createElement( 331 | ReactCSSTransitionGroup, 332 | { transitionName: 'modal-backdrop', component: 'div' }, 333 | this.renderBackdrop() 334 | ) 335 | ); 336 | } 337 | }); 338 | 339 | },{"./DateSelectCalendar":2,"react/addons":undefined}],4:[function(require,module,exports){ 340 | 'use strict'; 341 | 342 | var React = require('react/addons'); 343 | var moment = (window.moment); 344 | var classNames = (window.classNames); 345 | 346 | module.exports = React.createClass({ 347 | displayName: 'DateSelectHeader', 348 | propTypes: { 349 | expanded: React.PropTypes.bool, 350 | date: React.PropTypes.object 351 | }, 352 | render: function render() { 353 | // helpers 354 | var date = moment(this.props.date); 355 | 356 | // classes 357 | var componentClass = classNames('DateSelectHeader', { 358 | 'DateSelectHeader--expanded': this.props.expanded, 359 | 'DateSelectHeader--condensed': !this.props.expanded, 360 | 'no-date': !this.props.date 361 | }); 362 | 363 | // elements 364 | var header = this.props.expanded ? React.createElement( 365 | 'div', 366 | { className: componentClass }, 367 | React.createElement( 368 | 'span', 369 | { className: 'DateSelectHeader__dow' }, 370 | date.format('dddd') 371 | ), 372 | React.createElement( 373 | 'span', 374 | { className: 'DateSelectHeader__month' }, 375 | date.format('MMMM') 376 | ), 377 | React.createElement( 378 | 'span', 379 | { className: 'DateSelectHeader__day' }, 380 | date.format('D') 381 | ), 382 | React.createElement( 383 | 'span', 384 | { className: 'DateSelectHeader__year' }, 385 | date.format('YYYY') 386 | ) 387 | ) : React.createElement( 388 | 'div', 389 | { className: componentClass }, 390 | React.createElement( 391 | 'span', 392 | { className: 'DateSelectHeader__dow' }, 393 | date.format('dddd') 394 | ), 395 | React.createElement( 396 | 'span', 397 | { className: 'DateSelectHeader__day' }, 398 | date.format('Do') 399 | ), 400 | React.createElement( 401 | 'span', 402 | { className: 'DateSelectHeader__month' }, 403 | date.format('MMMM') 404 | ), 405 | React.createElement( 406 | 'span', 407 | { className: 'DateSelectHeader__year' }, 408 | date.format('YYYY') 409 | ) 410 | ); 411 | 412 | if (this.props.date) { 413 | header = this.props.expanded ? React.createElement( 414 | 'div', 415 | { className: componentClass }, 416 | React.createElement( 417 | 'span', 418 | { className: 'DateSelectHeader__dow' }, 419 | date.format('dddd') 420 | ), 421 | React.createElement( 422 | 'span', 423 | { className: 'DateSelectHeader__month' }, 424 | date.format('MMMM') 425 | ), 426 | React.createElement( 427 | 'span', 428 | { className: 'DateSelectHeader__day' }, 429 | date.format('D') 430 | ), 431 | React.createElement( 432 | 'span', 433 | { className: 'DateSelectHeader__year' }, 434 | date.format('YYYY') 435 | ) 436 | ) : React.createElement( 437 | 'div', 438 | { className: componentClass }, 439 | React.createElement( 440 | 'span', 441 | { className: 'DateSelectHeader__dow' }, 442 | date.format('dddd') 443 | ), 444 | React.createElement( 445 | 'span', 446 | { className: 'DateSelectHeader__day' }, 447 | date.format('Do') 448 | ), 449 | React.createElement( 450 | 'span', 451 | { className: 'DateSelectHeader__month' }, 452 | date.format('MMMM') 453 | ), 454 | React.createElement( 455 | 'span', 456 | { className: 'DateSelectHeader__year' }, 457 | date.format('YYYY') 458 | ) 459 | ); 460 | } 461 | 462 | return header; 463 | } 464 | }); 465 | 466 | },{"react/addons":undefined}]},{},[1])(1) 467 | }); -------------------------------------------------------------------------------- /dist/react-date-select.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.DateSelect=e()}}(function(){return function e(t,a,s){function r(o,l){if(!a[o]){if(!t[o]){var c="function"==typeof require&&require;if(!l&&c)return c(o,!0);if(n)return n(o,!0);var i=new Error("Cannot find module '"+o+"'");throw i.code="MODULE_NOT_FOUND",i}var d=a[o]={exports:{}};t[o][0].call(d.exports,function(e){var a=t[o][1][e];return r(a?a:e)},d,d.exports,e,t,a,s)}return a[o].exports}for(var n="function"==typeof require&&require,o=0;oD;D++)u.push(D);for(var h=this.props.yearRange[0];he.state.selectedDate});return s.createElement("button",{key:"day"+t,onClick:e.handleDaySelection.bind(e,t),className:a},t)}),_=m.map(function(e,t){return s.createElement("option",{key:"month"+t,value:e},e.slice(0,3))}),b=p.map(function(e,t){return s.createElement("option",{key:"year"+t,value:e},e)}),N=s.createElement("div",{className:c},!this.props.isHeaderless&&s.createElement(o,{selectedDate:this.state.selectedDate,isExpanded:this.props.isExpanded}),s.createElement("div",{className:"DateSelectCalendar__toolbar"},s.createElement("button",{className:"DateSelectCalendar__toolbar__button DateSelectCalendar__toolbar__button--prev"},"Previous Month"),s.createElement("select",{className:"DateSelectCalendar__toolbar__select",defaultValue:i},_),s.createElement("select",{className:"DateSelectCalendar__toolbar__select",defaultValue:d},b),s.createElement("button",{className:"DateSelectCalendar__toolbar__button DateSelectCalendar__toolbar__button--next"},"Next Month")),s.createElement("div",{className:"DateSelectCalendar__legend"},y),s.createElement("div",{className:"DateSelectCalendar__month"},S));return N}})},{"./DateSelectHeader":4,"react/addons":void 0}],3:[function(e,t,a){"use strict";var s=e("react/addons"),r=s.addons.CSSTransitionGroup,n=window.moment,o=window.classNames,l=e("./DateSelectCalendar");t.exports=s.createClass({displayName:"DateSelectDialog",propTypes:{backdropClosesDateSelect:s.PropTypes.bool,className:s.PropTypes.string,isExpanded:s.PropTypes.bool,isHeaderless:s.PropTypes.bool,isInstant:s.PropTypes.bool,isMulti:s.PropTypes.bool,onCancel:s.PropTypes.func,onSelect:s.PropTypes.func,predefinedRangeOptions:s.PropTypes.array,showPredefinedRanges:s.PropTypes.bool},getInitialState:function(){return{startDate:"",endDate:""}},renderDialog:function(){return this.props.isOpen?s.createElement("div",{className:"DateSelect-dialog"},s.createElement("div",{className:"DateSelect-content"},s.createElement("div",{className:"DateSelect-body"},s.createElement(l,{selectedDate:this.state.startDate,isHeaderless:this.props.isHeaderless,isInstant:this.props.isInstant}),this.props.isMulti&&s.createElement(l,{selectedDate:this.state.endDate,isHeaderless:this.props.isHeaderless})),this.renderRanges(),!this.props.isInstant&&s.createElement("div",{className:"DateSelectFooter"},s.createElement("button",{onClick:this.props.onSelect,className:"DateSelectFooter__button DateSelectFooter__button--primary"},"Confirm"),s.createElement("button",{onClick:this.props.onCancel,className:"DateSelectFooter__button DateSelectFooter__button--link"},"Cancel")))):null},renderRanges:function(){if(!this.props.showPredefinedRanges)return null;var e=this,t=this.props.predefinedRangeOptions.map(function(t,a){function r(){e.setState({startDate:n().format("D"),endDate:t.value.format("D")})}return s.createElement("button",{key:"range-button"+a,onClick:r,className:"DateSelect__range__item"},t.label)});return s.createElement("div",{className:"DateSelect__range"},t)},renderBackdrop:function(){return this.props.isOpen?s.createElement("div",{className:"DateSelect-backdrop",onClick:this.props.backdropClosesDateSelect?this.props.onCancel:null}):null},render:function(){var e=o("DateSelect",{"single-picker":!this.props.isMulti,"multi-picker":this.props.isMulti,"range-picker":this.props.showPredefinedRanges},this.props.className);return s.createElement("div",{className:e},s.createElement(r,{transitionName:"modal-dialog",component:"div"},this.renderDialog()),s.createElement(r,{transitionName:"modal-backdrop",component:"div"},this.renderBackdrop()))}})},{"./DateSelectCalendar":2,"react/addons":void 0}],4:[function(e,t,a){"use strict";var s=e("react/addons"),r=window.moment,n=window.classNames;t.exports=s.createClass({displayName:"DateSelectHeader",propTypes:{expanded:s.PropTypes.bool,date:s.PropTypes.object},render:function(){var e=r(this.props.date),t=n("DateSelectHeader",{"DateSelectHeader--expanded":this.props.expanded,"DateSelectHeader--condensed":!this.props.expanded,"no-date":!this.props.date}),a=this.props.expanded?s.createElement("div",{className:t},s.createElement("span",{className:"DateSelectHeader__dow"},e.format("dddd")),s.createElement("span",{className:"DateSelectHeader__month"},e.format("MMMM")),s.createElement("span",{className:"DateSelectHeader__day"},e.format("D")),s.createElement("span",{className:"DateSelectHeader__year"},e.format("YYYY"))):s.createElement("div",{className:t},s.createElement("span",{className:"DateSelectHeader__dow"},e.format("dddd")),s.createElement("span",{className:"DateSelectHeader__day"},e.format("Do")),s.createElement("span",{className:"DateSelectHeader__month"},e.format("MMMM")),s.createElement("span",{className:"DateSelectHeader__year"},e.format("YYYY")));return this.props.date&&(a=this.props.expanded?s.createElement("div",{className:t},s.createElement("span",{className:"DateSelectHeader__dow"},e.format("dddd")),s.createElement("span",{className:"DateSelectHeader__month"},e.format("MMMM")),s.createElement("span",{className:"DateSelectHeader__day"},e.format("D")),s.createElement("span",{className:"DateSelectHeader__year"},e.format("YYYY"))):s.createElement("div",{className:t},s.createElement("span",{className:"DateSelectHeader__dow"},e.format("dddd")),s.createElement("span",{className:"DateSelectHeader__day"},e.format("Do")),s.createElement("span",{className:"DateSelectHeader__month"},e.format("MMMM")),s.createElement("span",{className:"DateSelectHeader__year"},e.format("YYYY")))),a}})},{"react/addons":void 0}]},{},[1])(1)}); -------------------------------------------------------------------------------- /example/src/.gitignore: -------------------------------------------------------------------------------- 1 | ## This file is here to ensure it is included in the gh-pages branch, 2 | ## when `gulp deploy` is used to push updates to the demo site. 3 | 4 | # Dependency directory 5 | node_modules 6 | -------------------------------------------------------------------------------- /example/src/example.js: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | var DateSelect = require('react-date-select'); 3 | 4 | var DateSelectExamples = React.createClass({ 5 | displayName: 'DateSelectExamples', 6 | getInitialState() { 7 | return { 8 | singleDateValue: new Date(), 9 | multiDateValue1: [new Date(), new Date()], 10 | multiDateValue2: [new Date(), new Date()] 11 | }; 12 | }, 13 | onDateChange(key, value) { 14 | this.setState({ key: value }); 15 | }, 16 | render() { 17 | return ( 18 |
19 |

Calendar

20 | 21 | 22 |

Popup

23 | 24 |

Pick a single date

25 | 26 | 27 |

Pick a start and end date

28 | 29 | 30 |

Pick a start and end date, with the option to use predefined ranges.

31 | 32 |
33 | ); 34 | } 35 | }); 36 | 37 | React.render( 38 | , 39 | document.getElementById('example') 40 | ); 41 | -------------------------------------------------------------------------------- /example/src/example.less: -------------------------------------------------------------------------------- 1 | // 2 | // Common Example Styles 3 | // ------------------------------ 4 | 5 | 6 | 7 | // normalize 8 | html { 9 | font-family: sans-serif; 10 | } 11 | body { 12 | background-color: white; 13 | color: #333; 14 | font-size: 16px; 15 | font-weight: 300; 16 | line-height: 1.4; 17 | margin: 0; 18 | } 19 | 20 | 21 | 22 | // Layout 23 | 24 | .container { 25 | margin-left: auto; 26 | margin-right: auto; 27 | max-width: 400px; 28 | padding: 1em; 29 | } 30 | 31 | .example-header { 32 | margin-bottom: 50px; 33 | border-bottom: 1px solid #ccc; 34 | padding: 20px 0; 35 | } 36 | 37 | .example-footer { 38 | margin-top: 50px; 39 | border-top: 1px solid #ccc; 40 | padding: 20px 0; 41 | font-size: 12px; 42 | color: #999; 43 | } 44 | 45 | 46 | // Type 47 | 48 | a { 49 | color: #08c; 50 | text-decoration: none; 51 | 52 | &:hover { 53 | text-decoration: underline; 54 | } 55 | } 56 | p { 57 | margin-top: 2em; 58 | } 59 | h1, h2, h3, h4, h5, h6 { 60 | color: #222; 61 | margin: 0; 62 | } 63 | 64 | h3 { 65 | margin: 20px 0; 66 | } 67 | 68 | 69 | // Small Devices 70 | 71 | @media (max-width: 480px) { 72 | body { 73 | font-size: 14px; 74 | font-weight: 400; 75 | } 76 | } 77 | 78 | 79 | // 80 | // Select Control 81 | // ------------------------------ 82 | 83 | @import "../../less/component.less"; 84 | -------------------------------------------------------------------------------- /example/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | React-Date-Select Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |

React Date Select

20 |

21 | A flexible Date Select input for ReactJS. 22 |
23 | View project and documentation on GitHub. 24 |

25 | 26 |
27 | 30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var initGulpTasks = require('react-component-gulp-tasks'); 3 | 4 | var taskConfig = { 5 | 6 | component: { 7 | name: 'DateSelect', 8 | dependencies: [ 9 | 'blacklist', 10 | 'classnames', 11 | 'moment', 12 | 'react', 13 | 'react/addons' 14 | ], 15 | less: { 16 | path: 'less', 17 | entry: 'component.less' 18 | } 19 | }, 20 | 21 | example: { 22 | src: 'example/src', 23 | dist: 'example/dist', 24 | files: [ 25 | 'index.html', 26 | '.gitignore' 27 | ], 28 | scripts: [ 29 | 'example.js' 30 | ], 31 | less: [ 32 | 'example.less' 33 | ] 34 | } 35 | 36 | }; 37 | 38 | initGulpTasks(gulp, taskConfig); 39 | -------------------------------------------------------------------------------- /images/date-select-button-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JedWatson/react-date-select/abbc72385bbcd6a1f86c7baf8f2a1779d12718d6/images/date-select-button-next.png -------------------------------------------------------------------------------- /images/date-select-button-prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JedWatson/react-date-select/abbc72385bbcd6a1f86c7baf8f2a1779d12718d6/images/date-select-button-prev.png -------------------------------------------------------------------------------- /less/component.less: -------------------------------------------------------------------------------- 1 | @import "dateselect.less"; 2 | @import "mixins.less"; 3 | @import "variables.less"; 4 | -------------------------------------------------------------------------------- /less/dateselect.less: -------------------------------------------------------------------------------- 1 | // 2 | // Date Select 3 | // ============================== 4 | 5 | // Inspired by https://github.com/twbs/bootstrap 6 | 7 | 8 | 9 | 10 | // Normalize 11 | // ------------------------------ 12 | 13 | .DateSelect { 14 | font-size: @DateSelect-font-size; 15 | font-family: @DateSelect-font-family; 16 | -webkit-font-smoothing: antialiased; 17 | 18 | * { 19 | #DateSelect .box-sizing(border-box); 20 | } 21 | button, 22 | input, 23 | select { 24 | font-size: inherit; 25 | font-family: inherit; 26 | margin: 0; 27 | -webkit-appearance: none; 28 | -moz-appearance: none; 29 | } 30 | } 31 | 32 | 33 | 34 | 35 | // Dialog 36 | // ------------------------------ 37 | 38 | 39 | 40 | // shell div to position the date picker 41 | .DateSelect-dialog { 42 | left: 0; 43 | margin-left: auto; 44 | margin-right: auto; 45 | position: absolute; 46 | right: 0; 47 | top: 0; 48 | width: 100%; 49 | z-index: @DateSelect-dialog-zindex; 50 | } 51 | .modal-dialog-enter { 52 | #DateSelect .animation-name( modalDialogEnter ); 53 | #DateSelect .animation-duration( 280ms ); 54 | #DateSelect .animation-timing-function( cubic-bezier(0.680, -0.550, 0.265, 1.550) ); 55 | } 56 | .modal-dialog-leave { 57 | #DateSelect .animation-duration( 140ms ); 58 | #DateSelect .animation-name( modalDialogLeave ); 59 | } 60 | 61 | 62 | // actual date picker 63 | 64 | .DateSelect-content { 65 | background-color: @DateSelect-bg; 66 | outline: none; // remove focus outline from opened date picker 67 | position: relative; 68 | } 69 | 70 | 71 | 72 | 73 | // Days - Common 74 | // ------------------------------ 75 | 76 | .DateSelectCalendar__legend, 77 | .DateSelectCalendar__month { 78 | #DateSelect .clearfix(); 79 | } 80 | .DateSelectCalendar__legend { 81 | padding: @DateSelect-padding @DateSelect-padding 0; 82 | } 83 | .DateSelectCalendar__month { 84 | padding: 0 @DateSelect-padding @DateSelect-padding; 85 | } 86 | .DateSelectCalendar__legend__day, 87 | .DateSelectCalendar__month__day { 88 | float: left; 89 | font-size: inherit; 90 | line-height: 1; 91 | padding: 10px 2px; 92 | position: relative; 93 | text-align: center; 94 | width: 14.286%; 95 | } 96 | 97 | 98 | 99 | 100 | // Title 101 | // ------------------------------ 102 | 103 | 104 | // base 105 | 106 | .DateSelectCalendar__toolbar { 107 | border-bottom: 1px solid @DateSelect-footer-border; 108 | position: relative; 109 | text-align: center; 110 | } 111 | 112 | 113 | // selects 114 | 115 | .DateSelectCalendar__toolbar__select { 116 | background: none; 117 | border: none; 118 | display: inline-block; 119 | font-size: inherit; 120 | font-weight: 500; 121 | line-height: @DateSelect-title-button-height; 122 | padding-left: .5em; 123 | padding-right: .5em; 124 | 125 | &:focus { 126 | color: @DateSelect-primary; 127 | outline: none; 128 | } 129 | } 130 | 131 | 132 | // buttons 133 | 134 | .DateSelectCalendar__toolbar__button { 135 | background-color: transparent; 136 | background-image: none; 137 | background-position: center center; 138 | background-repeat: no-repeat; 139 | background-size: @DateSelect-title-button-width @DateSelect-title-button-height; 140 | border: none; 141 | display: inline-block; 142 | height: @DateSelect-title-button-height; 143 | opacity: .6; 144 | overflow: hidden; 145 | position: absolute; 146 | text-indent: -9999px; 147 | top: 0; 148 | white-space: nowrap; 149 | width: @DateSelect-title-button-width + (@DateSelect-padding * 2); 150 | 151 | &:hover { 152 | opacity: 1; 153 | } 154 | } 155 | 156 | .DateSelectCalendar__toolbar__button--prev { 157 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA8CAYAAAAUufjgAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjBBQkU4NjZENUM5MTFFNEI1MkFDNEUxMUIxMzU3NDEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjBBQkU4NjdENUM5MTFFNEI1MkFDNEUxMUIxMzU3NDEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMEFCRTg2NEQ1QzkxMUU0QjUyQUM0RTExQjEzNTc0MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGMEFCRTg2NUQ1QzkxMUU0QjUyQUM0RTExQjEzNTc0MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp2znPUAAAD9SURBVHja7JnBCQIxEEUzkgZsQkuwAW3GfrQRj+rdFkwRNuAhTiCBIARNMnFH9g989rLsPObzk+wuee+N5loY5QVAAAIQgAAEIAABCMB5A9qam4mouVHryX32FgeHDl1PCKP/VpW1ZJ1Di9o+ueygya1Yp3hVZ/GWdZOAG2HxnvWMtuZqtlgKMIXBFzQpYB4GcUCrJQwjQiIbBuGQlMIgbnHrBMn8qjpCsmM9Rk+wN8VrltNocao7a8O6arT4faE+al2o/2Kr+xQeVYBph3EaQlIqF8Nz0XbcKh75WydINY3xVtf7XjzFXyl8+gAgAAEIQAACEIAABKDqegkwAHr84FvNdKczAAAAAElFTkSuQmCC"); 158 | left: 0; 159 | } 160 | .DateSelectCalendar__toolbar__button--next { 161 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA8CAYAAAAUufjgAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6M0UyMTVBRTNENUNBMTFFNEI1MkFDNEUxMUIxMzU3NDEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6M0UyMTVBRTRENUNBMTFFNEI1MkFDNEUxMUIxMzU3NDEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMEFCRTg2OEQ1QzkxMUU0QjUyQUM0RTExQjEzNTc0MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozRTIxNUFFMkQ1Q0ExMUU0QjUyQUM0RTExQjEzNTc0MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgHkmaEAAAEASURBVHja7JnRDYIwEIapYQGHkBVcAN90EfeRZdR3VhB3YAEf6jWpiRDBpj3gT/gv+R9Lv9z1b6/UWGsz5Nhk4EFAAhKQgAQkIAEJSMB1A+Ypg40x0WNDO/lVlLhKrcTfVMfq8wnRVbSdYl4tQKdGVGgDaq7BnagWHRBL/K2X6IxY4r4uY+ZBABw1Dwqg0+OXeZYwyVAU3jwlikmG1DEPUgY7xzdqBtv+/hg6Rz5D1p6ikzcLXD94F+1j4aYucYW6UUMfdW1os7CESZwZjr7tgrs03bwZGnWbKZQ4quUPncOkvJPwVpd6G5vjlYq/PghIQAISkIAEJCABCQgdbwEGAOhL4l/o/UvgAAAAAElFTkSuQmCC"); 162 | right: 0; 163 | } 164 | 165 | 166 | 167 | 168 | // Legend 169 | // ------------------------------ 170 | 171 | .DateSelectCalendar__legend { 172 | color: @DateSelect-legend-color; 173 | font-weight: 500; 174 | } 175 | .DateSelectCalendar__legend__day { 176 | border-bottom: none; 177 | cursor: default; 178 | } 179 | 180 | 181 | 182 | 183 | // Calendar 184 | // ------------------------------ 185 | 186 | .DateSelectCalendar__month__day { 187 | background: none; 188 | border: none; 189 | border-radius: @DateSelect-border-radius-inner; 190 | outline: none; 191 | 192 | &:hover { 193 | background-color: mix(@DateSelect-primary, @DateSelect-bg, 10%); 194 | } 195 | &:focus { 196 | box-shadow: 0 0 0 1px @DateSelect-bg, 0 0 0 3px fade(@DateSelect-primary, 15%); 197 | color: @DateSelect-primary; 198 | outline: 0; 199 | } 200 | &:active { 201 | background-color: darken(@DateSelect-selected-day-bg, 5%); 202 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 203 | color: @DateSelect-selected-day-color; 204 | } 205 | 206 | // current day 207 | &.is-current-day { 208 | background-color: @DateSelect-current-day-bg; 209 | box-shadow: inset 0 0 0 1px @DateSelect-current-day-border; 210 | 211 | &.is-selected { 212 | box-shadow: none; 213 | 214 | &:focus { 215 | box-shadow: 0 0 0 1px @DateSelect-bg, 0 0 0 3px fade(@DateSelect-primary, 15%); 216 | outline: 0; 217 | } 218 | } 219 | } 220 | 221 | // selected day 222 | &.is-selected { 223 | background-color: @DateSelect-selected-day-bg; 224 | color: @DateSelect-selected-day-color; 225 | } 226 | } 227 | 228 | // add an arrow on multi pickers 229 | 230 | .multi-picker .DateSelectCalendar:first-child .DateSelectCalendar__month__day.is-selected, 231 | .multi-picker .DateSelectCalendar:last-child .DateSelectCalendar__month__day.is-selected { 232 | 233 | &::after { 234 | border-bottom: 5px solid transparent; 235 | border-top: 5px solid transparent; 236 | content: ""; 237 | height: 0; 238 | margin-top: -5px; 239 | position: absolute; 240 | top: 50%; 241 | width: 0; 242 | z-index: 1; 243 | } 244 | } 245 | .multi-picker .DateSelectCalendar:first-child .DateSelectCalendar__month__day.is-selected::after { 246 | border-left: 5px solid @DateSelect-primary; 247 | right: -5px; 248 | } 249 | .multi-picker .DateSelectCalendar:last-child .DateSelectCalendar__month__day.is-selected::after { 250 | border-right: 5px solid @DateSelect-primary; 251 | left: -5px; 252 | } 253 | .multi-picker .DateSelectCalendar:first-child .is-after-selected-day { 254 | border-radius: 0; 255 | background-color: mix(@DateSelect-primary, @DateSelect-bg, 5%); 256 | color: mix(@DateSelect-primary, @DateSelect-text-color, 50%); 257 | } 258 | .multi-picker .DateSelectCalendar:last-child .is-before-selected-day { 259 | border-radius: 0; 260 | background-color: mix(@DateSelect-primary, @DateSelect-bg, 5%); 261 | color: mix(@DateSelect-primary, @DateSelect-text-color, 50%); 262 | } 263 | 264 | 265 | 266 | 267 | // Body 268 | // ------------------------------ 269 | 270 | .DateSelect-body { 271 | } 272 | 273 | 274 | 275 | 276 | // Header 277 | // ------------------------------ 278 | 279 | 280 | // base 281 | 282 | .DateSelectHeader { 283 | #DateSelect .gradient-vertical(lighten(@DateSelect-header-bg, 4%), darken(@DateSelect-header-bg, 4%)); 284 | background-color: @DateSelect-header-bg; 285 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 0 white; 286 | color: @DateSelect-header-color; 287 | display: block; 288 | text-align: center; 289 | } 290 | 291 | 292 | // expanded 293 | 294 | .DateSelectHeader--expanded { 295 | line-height: 1; 296 | 297 | > .DateSelectHeader { 298 | display: block; 299 | font-weight: 300; 300 | } 301 | > .DateSelectHeader__dow { 302 | background-color: rgba(0, 0, 0, 0.1); 303 | border-top-right-radius: @DateSelect-border-radius-outer; 304 | border-top-left-radius: @DateSelect-border-radius-outer; 305 | display: block; 306 | font-size: .85em; 307 | padding: 5px; 308 | } 309 | > .DateSelectHeader__month { 310 | display: block; 311 | padding-top: 10px; 312 | text-transform: uppercase; 313 | } 314 | > .DateSelectHeader__day { 315 | display: block; 316 | font-size: 4em; 317 | margin-bottom: 5px; 318 | margin-top: 5px; 319 | } 320 | > .DateSelectHeader__year { 321 | color: rgba(255, 255, 255, 0.75); 322 | display: block; 323 | padding-bottom: 10px; 324 | } 325 | } 326 | 327 | 328 | // condensed 329 | 330 | .DateSelectHeader--condensed { 331 | font-weight: bold; 332 | letter-spacing: 0.0125em; 333 | padding: @DateSelect-padding; 334 | 335 | > .DateSelectHeader, 336 | > .DateSelectHeader__dow, 337 | > .DateSelectHeader__month, 338 | > .DateSelectHeader__day, 339 | > .DateSelectHeader__year { 340 | display: inline-block; 341 | margin-left: .15em; 342 | margin-right: .15em; 343 | } 344 | 345 | > .DateSelectHeader__dow { 346 | opacity: .75; 347 | display: block; 348 | font-size: .75em; 349 | font-weight: 500; 350 | margin: 0; 351 | text-transform: uppercase; 352 | -webkit-font-smoothing: antialiased; 353 | } 354 | } 355 | 356 | 357 | 358 | 359 | // Ranges 360 | // ------------------------------ 361 | 362 | 363 | // base 364 | 365 | .DateSelect__range { 366 | border-top: 1px solid @DateSelect-footer-border; 367 | display: table; 368 | font-size: .85em; 369 | position: relative; 370 | text-align: center; 371 | width: 100%; 372 | 373 | // dim on hover 374 | &:hover { 375 | color: lighten(@DateSelect-text-color, 20%); 376 | } 377 | } 378 | 379 | 380 | // items 381 | 382 | .DateSelect__range__item { 383 | #DateSelect .transition( color 200ms ); 384 | background-color: transparent; 385 | background-image: none; 386 | border: none; 387 | color: inherit; 388 | cursor: pointer; 389 | display: inline-block; 390 | outline: none; 391 | padding: @DateSelect-padding; 392 | position: relative; 393 | text-align: center; 394 | 395 | &:hover { 396 | color: darken(@DateSelect-text-color, 20%); 397 | } 398 | &.active, 399 | &:focus { 400 | box-shadow: 0 1px 0 @DateSelect-range-color; 401 | color: darken(@DateSelect-text-color, 20%); 402 | 403 | &:after { 404 | border-bottom: 4px solid @DateSelect-range-color; 405 | border-left: 4px solid transparent; 406 | border-right: 4px solid transparent; 407 | bottom: 0; 408 | content: ""; 409 | display: inline-block; 410 | height: 0; 411 | left: 50%; 412 | margin-left: -4px; 413 | position: absolute; 414 | width: 0; 415 | } 416 | } 417 | } 418 | 419 | 420 | 421 | 422 | // Footer 423 | // ------------------------------ 424 | 425 | .DateSelectFooter { 426 | #DateSelect .clearfix(); 427 | background-color: @DateSelect-footer-bg; 428 | border-top: 1px solid @DateSelect-footer-border; 429 | border-bottom-right-radius: @DateSelect-border-radius-outer; 430 | border-bottom-left-radius: @DateSelect-border-radius-outer; 431 | } 432 | .DateSelectFooter__button { 433 | background-color: transparent; 434 | background-image: none; 435 | border: none; 436 | border-radius: 0; 437 | color: @DateSelect-footer-color; 438 | float: left; 439 | font-size: inherit; 440 | outline: none; 441 | padding: @DateSelect-padding; 442 | text-align: center; 443 | width: 50%; 444 | 445 | &:last-child { 446 | border-left: 1px solid @DateSelect-footer-border; 447 | } 448 | } 449 | .DateSelectFooter__button--primary { 450 | color: @DateSelect-footer-single-button-primary-color; 451 | font-weight: @DateSelect-footer-button-font-weight-primary; 452 | } 453 | 454 | 455 | 456 | 457 | // Single Day Picker 458 | // ------------------------------ 459 | 460 | .single-picker { 461 | .DateSelectFooter__button { 462 | &:hover, 463 | &:focus { 464 | background-color: @DateSelect-footer-single-button-hover-bg; 465 | } 466 | &:active { 467 | background-color: @DateSelect-footer-single-button-active-bg; 468 | } 469 | 470 | &:first-child { 471 | border-bottom-left-radius: @DateSelect-border-radius-outer; 472 | } 473 | &:last-child { 474 | border-bottom-right-radius: @DateSelect-border-radius-outer; 475 | } 476 | } 477 | } 478 | 479 | 480 | 481 | 482 | // Multi Picker 483 | // ------------------------------ 484 | 485 | .multi-picker { 486 | // footer 487 | .DateSelectFooter { 488 | padding: (@DateSelect-padding * 1.5) (@DateSelect-padding * 2); 489 | } 490 | .DateSelectFooter__button { 491 | background: none; 492 | border-radius: .25em; 493 | border: none; 494 | cursor: pointer; 495 | display: inline-block; 496 | float: none; 497 | font-size: inherit; 498 | height: 2.4em; 499 | line-height: 2.3em; 500 | margin-bottom: 0; 501 | overflow: hidden; // contain gradient on IE9 502 | padding: 0 1em; 503 | text-align: center; 504 | touch-action: manipulation; 505 | vertical-align: middle; 506 | white-space: nowrap; 507 | width: 80px; 508 | 509 | &:hover, 510 | &:focus, 511 | &.focus { 512 | color: @DateSelect-footer-multi-button-hover-color; 513 | text-decoration: none; 514 | } 515 | 516 | &:active, 517 | &.active { 518 | background-image: none; 519 | outline: 0; 520 | } 521 | 522 | &.disabled, 523 | &[disabled] { 524 | opacity: .4; 525 | pointer-events: none; 526 | } 527 | } 528 | .DateSelectFooter__button--primary { 529 | #DateSelect .gradient-vertical(lighten(@DateSelect-footer-multi-button-primary-bg, 4%), darken(@DateSelect-footer-multi-button-primary-bg, 4%)); 530 | background-color: @DateSelect-footer-multi-button-primary-bg; 531 | color: @DateSelect-footer-multi-button-primary-color; 532 | 533 | &:hover, 534 | &:focus, 535 | &.focus { 536 | #DateSelect .gradient-vertical(lighten(@DateSelect-footer-multi-button-primary-bg, 8%), @DateSelect-footer-multi-button-primary-bg); 537 | color: @DateSelect-footer-multi-button-primary-color; 538 | outline: none; 539 | } 540 | &:focus, 541 | &.focus { 542 | box-shadow: 0 0 0 3px fade(@DateSelect-footer-multi-button-primary-bg, 25%); 543 | } 544 | &:active, 545 | &.active { 546 | background-color: darken(@DateSelect-footer-multi-button-primary-bg, 5%); 547 | background-image: none; 548 | box-shadow: none; 549 | } 550 | } 551 | } 552 | 553 | 554 | 555 | 556 | // InlineCalendar 557 | // ------------------------------ 558 | 559 | .DateSelect-inline-container { 560 | border-radius: @DateSelect-border-radius-outer; 561 | box-shadow: 0 0 0 1px rgba(0,0,0,.175); 562 | width: @DateSelect-dialog-width; 563 | } 564 | 565 | 566 | // Sizes 567 | // ------------------------------ 568 | 569 | @media (min-width: @DateSelect-screen-sm-min) { 570 | .DateSelect-dialog { 571 | margin-bottom: 100px; 572 | margin-top: 100px; 573 | width: @DateSelect-dialog-width; 574 | } 575 | .DateSelect-content { 576 | border-radius: @DateSelect-border-radius-outer; 577 | box-shadow: 0 0 0 1px rgba(0,0,0,.175), 0 3px 8px rgba(0,0,0,.175); 578 | } 579 | .DateSelectHeader { 580 | border-top-right-radius: @DateSelect-border-radius-outer; 581 | border-top-left-radius: @DateSelect-border-radius-outer; 582 | } 583 | .multi-picker { 584 | .DateSelect-body { 585 | #DateSelect .clearfix(); 586 | } 587 | .DateSelect-dialog { 588 | width: (@DateSelect-dialog-width * 2); 589 | } 590 | .DateSelectHeader { 591 | border-radius: 0; 592 | box-shadow: -1px 0 0 darken(@DateSelect-header-bg, 10%); 593 | } 594 | .DateSelectCalendar { 595 | border-left: 1px solid @DateSelect-footer-border; 596 | float: left; 597 | width: 50%; 598 | 599 | &:first-child { 600 | border-left: none; 601 | .DateSelectHeader { 602 | border-top-left-radius: @DateSelect-border-radius-outer; 603 | box-shadow: none; 604 | } 605 | } 606 | &:last-child .DateSelectHeader { 607 | border-top-right-radius: @DateSelect-border-radius-outer; 608 | } 609 | } 610 | } 611 | } 612 | 613 | 614 | 615 | 616 | // Background 617 | // ------------------------------ 618 | 619 | .DateSelect-backdrop { 620 | #DateSelect .animation-duration( 140ms ); 621 | background-color: @DateSelect-backdrop-bg; 622 | height: 100%; 623 | left: 0; 624 | position: fixed; 625 | top: 0; 626 | width: 100%; 627 | z-index: @DateSelect-backdrop-zindex; 628 | } 629 | .modal-backdrop-enter { 630 | #DateSelect .animation-name( modalBackdropEnter ); 631 | } 632 | .modal-backdrop-leave { 633 | #DateSelect .animation-delay( 240ms ); 634 | #DateSelect .animation-name( modalBackdropLeave ); 635 | } 636 | 637 | 638 | 639 | 640 | // Animation 641 | // -------------------- 642 | 643 | 644 | // modal dialog 645 | 646 | @-webkit-keyframes modalDialogEnter { 647 | from { opacity: 0; -webkit-transform: scale( .8 ); } 648 | to { opacity: 1; -webkit-transform: scale( 1 ); } 649 | } 650 | @keyframes modalDialogEnter { 651 | from { opacity: 0; transform: scale( .8 ); } 652 | to { opacity: 1; transform: scale( 1 ); } 653 | } 654 | 655 | @-webkit-keyframes modalDialogLeave { 656 | from { opacity: 1; -webkit-transform: scale( 1 ); } 657 | to { opacity: 0; -webkit-transform: scale( .8 ); } 658 | } 659 | @keyframes modalDialogLeave { 660 | from { opacity: 1; transform: scale( 1 ); } 661 | to { opacity: 0; transform: scale( .8 ); } 662 | } 663 | 664 | 665 | // modal backdrop 666 | 667 | @-webkit-keyframes modalBackdropEnter { 668 | from { opacity: 0; } 669 | to { opacity: 1; } 670 | } 671 | @keyframes modalBackdropEnter { 672 | from { opacity: 0; } 673 | to { opacity: 1; } 674 | } 675 | 676 | @-webkit-keyframes modalBackdropLeave { 677 | from { opacity: 1; } 678 | to { opacity: 0; } 679 | } 680 | @keyframes modalBackdropLeave { 681 | from { opacity: 1; } 682 | to { opacity: 0; } 683 | } 684 | -------------------------------------------------------------------------------- /less/mixins.less: -------------------------------------------------------------------------------- 1 | // 2 | // Mixins 3 | // ============================== 4 | 5 | #DateSelect { 6 | 7 | // ============================== 8 | // CLEAR FIX 9 | // ============================== 10 | .clearfix() { 11 | &:before, 12 | &:after { 13 | content: " "; 14 | display: table; 15 | } 16 | &:after { 17 | clear: both; 18 | } 19 | } 20 | 21 | 22 | 23 | 24 | // ============================== 25 | // GRADIENTS 26 | // ============================== 27 | 28 | // Horizontal gradient, from left to right 29 | // 30 | // Creates two color stops, start and end, by specifying a color and position for each color stop. 31 | // Color stops are not available in IE9 and below. 32 | .gradient-horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) { 33 | background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+ 34 | background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12 35 | background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ 36 | background-repeat: repeat-x; 37 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down 38 | } 39 | 40 | // Vertical gradient, from top to bottom 41 | // 42 | // Creates two color stops, start and end, by specifying a color and position for each color stop. 43 | // Color stops are not available in IE9 and below. 44 | .gradient-vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) { 45 | background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+ 46 | background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12 47 | background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ 48 | background-repeat: repeat-x; 49 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down 50 | } 51 | 52 | .gradient-directional(@start-color: #555; @end-color: #333; @deg: 45deg) { 53 | background-repeat: repeat-x; 54 | background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+ 55 | background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12 56 | background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ 57 | } 58 | 59 | 60 | 61 | 62 | // ============================== 63 | // VENDOR PREFIXES 64 | // ============================== 65 | 66 | 67 | // Animations 68 | .animation(@animation) { 69 | -webkit-animation: @animation; 70 | -o-animation: @animation; 71 | animation: @animation; 72 | } 73 | .animation-name(@name) { 74 | -webkit-animation-name: @name; 75 | animation-name: @name; 76 | } 77 | .animation-duration(@duration) { 78 | -webkit-animation-duration: @duration; 79 | animation-duration: @duration; 80 | } 81 | .animation-timing-function(@timing-function) { 82 | -webkit-animation-timing-function: @timing-function; 83 | animation-timing-function: @timing-function; 84 | } 85 | .animation-delay(@delay) { 86 | -webkit-animation-delay: @delay; 87 | animation-delay: @delay; 88 | } 89 | .animation-iteration-count(@iteration-count) { 90 | -webkit-animation-iteration-count: @iteration-count; 91 | animation-iteration-count: @iteration-count; 92 | } 93 | .animation-direction(@direction) { 94 | -webkit-animation-direction: @direction; 95 | animation-direction: @direction; 96 | } 97 | .animation-fill-mode(@fill-mode) { 98 | -webkit-animation-fill-mode: @fill-mode; 99 | animation-fill-mode: @fill-mode; 100 | } 101 | 102 | // Box sizing 103 | .box-sizing(@boxmodel) { 104 | -webkit-box-sizing: @boxmodel; 105 | -moz-box-sizing: @boxmodel; 106 | box-sizing: @boxmodel; 107 | } 108 | 109 | // CSS3 Content Columns 110 | .content-columns(@column-count; @column-gap: @grid-gutter-width) { 111 | -webkit-column-count: @column-count; 112 | -moz-column-count: @column-count; 113 | column-count: @column-count; 114 | -webkit-column-gap: @column-gap; 115 | -moz-column-gap: @column-gap; 116 | column-gap: @column-gap; 117 | } 118 | 119 | // Transformations 120 | .scale(@ratio) { 121 | -webkit-transform: scale(@ratio); 122 | -ms-transform: scale(@ratio); // IE9 only 123 | -o-transform: scale(@ratio); 124 | transform: scale(@ratio); 125 | } 126 | .scale(@ratioX; @ratioY) { 127 | -webkit-transform: scale(@ratioX, @ratioY); 128 | -ms-transform: scale(@ratioX, @ratioY); // IE9 only 129 | -o-transform: scale(@ratioX, @ratioY); 130 | transform: scale(@ratioX, @ratioY); 131 | } 132 | .scaleX(@ratio) { 133 | -webkit-transform: scaleX(@ratio); 134 | -ms-transform: scaleX(@ratio); // IE9 only 135 | -o-transform: scaleX(@ratio); 136 | transform: scaleX(@ratio); 137 | } 138 | .scaleY(@ratio) { 139 | -webkit-transform: scaleY(@ratio); 140 | -ms-transform: scaleY(@ratio); // IE9 only 141 | -o-transform: scaleY(@ratio); 142 | transform: scaleY(@ratio); 143 | } 144 | .skew(@x; @y) { 145 | -webkit-transform: skewX(@x) skewY(@y); 146 | -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+ 147 | -o-transform: skewX(@x) skewY(@y); 148 | transform: skewX(@x) skewY(@y); 149 | } 150 | .translate(@x; @y) { 151 | -webkit-transform: translate(@x, @y); 152 | -ms-transform: translate(@x, @y); // IE9 only 153 | -o-transform: translate(@x, @y); 154 | transform: translate(@x, @y); 155 | } 156 | .translate3d(@x; @y; @z) { 157 | -webkit-transform: translate3d(@x, @y, @z); 158 | transform: translate3d(@x, @y, @z); 159 | } 160 | .rotate(@degrees) { 161 | -webkit-transform: rotate(@degrees); 162 | -ms-transform: rotate(@degrees); // IE9 only 163 | -o-transform: rotate(@degrees); 164 | transform: rotate(@degrees); 165 | } 166 | .rotateX(@degrees) { 167 | -webkit-transform: rotateX(@degrees); 168 | -ms-transform: rotateX(@degrees); // IE9 only 169 | -o-transform: rotateX(@degrees); 170 | transform: rotateX(@degrees); 171 | } 172 | .rotateY(@degrees) { 173 | -webkit-transform: rotateY(@degrees); 174 | -ms-transform: rotateY(@degrees); // IE9 only 175 | -o-transform: rotateY(@degrees); 176 | transform: rotateY(@degrees); 177 | } 178 | .perspective(@perspective) { 179 | -webkit-perspective: @perspective; 180 | -moz-perspective: @perspective; 181 | perspective: @perspective; 182 | } 183 | .perspective-origin(@perspective) { 184 | -webkit-perspective-origin: @perspective; 185 | -moz-perspective-origin: @perspective; 186 | perspective-origin: @perspective; 187 | } 188 | .transform-origin(@origin) { 189 | -webkit-transform-origin: @origin; 190 | -moz-transform-origin: @origin; 191 | -ms-transform-origin: @origin; // IE9 only 192 | transform-origin: @origin; 193 | } 194 | 195 | 196 | // Transitions 197 | 198 | .transition(@transition) { 199 | -webkit-transition: @transition; 200 | -o-transition: @transition; 201 | transition: @transition; 202 | } 203 | .transition-property(@transition-property) { 204 | -webkit-transition-property: @transition-property; 205 | transition-property: @transition-property; 206 | } 207 | .transition-delay(@transition-delay) { 208 | -webkit-transition-delay: @transition-delay; 209 | transition-delay: @transition-delay; 210 | } 211 | .transition-duration(@transition-duration) { 212 | -webkit-transition-duration: @transition-duration; 213 | transition-duration: @transition-duration; 214 | } 215 | .transition-timing-function(@timing-function) { 216 | -webkit-transition-timing-function: @timing-function; 217 | transition-timing-function: @timing-function; 218 | } 219 | .transition-transform(@transition) { 220 | -webkit-transition: -webkit-transform @transition; 221 | -moz-transition: -moz-transform @transition; 222 | -o-transition: -o-transform @transition; 223 | transition: transform @transition; 224 | } 225 | 226 | 227 | // User select 228 | // For selecting text on the page 229 | 230 | .user-select(@select) { 231 | -webkit-user-select: @select; 232 | -moz-user-select: @select; 233 | -ms-user-select: @select; // IE10+ 234 | user-select: @select; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /less/variables.less: -------------------------------------------------------------------------------- 1 | // 2 | // Variables 3 | // ============================== 4 | 5 | 6 | 7 | 8 | // Colours 9 | // ------------------------------ 10 | 11 | 12 | @DateSelect-bg: #fafafa; 13 | @DateSelect-primary: #1076e5; 14 | @DateSelect-text-color: #333333; 15 | @DateSelect-backdrop-bg: rgba(0, 0, 0, 0.5); 16 | 17 | @DateSelect-header-bg: desaturate(@DateSelect-primary, 10%); 18 | @DateSelect-header-color: white; 19 | 20 | @DateSelect-selected-day-bg: @DateSelect-primary; 21 | @DateSelect-selected-day-color: white; 22 | @DateSelect-current-day-bg: darken(@DateSelect-bg, 3%); 23 | @DateSelect-current-day-border: darken(@DateSelect-bg, 15%); 24 | 25 | @DateSelect-legend-color: #999; 26 | @DateSelect-range-color: #666; 27 | 28 | 29 | 30 | 31 | // Typography 32 | // ------------------------------ 33 | 34 | @DateSelect-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 35 | @DateSelect-font-size: 14px; 36 | 37 | 38 | 39 | 40 | // Spacing and Sizes 41 | // ------------------------------ 42 | 43 | @DateSelect-padding: 8px; 44 | @DateSelect-dialog-width: 280px; 45 | 46 | @DateSelect-title-button-height: 30px; 47 | @DateSelect-title-button-width: 20px; 48 | 49 | 50 | 51 | 52 | // Footer / Buttons 53 | // ------------------------------ 54 | 55 | 56 | // common 57 | 58 | @DateSelect-footer-bg: darken(@DateSelect-bg, 5%); 59 | @DateSelect-footer-border: darken(@DateSelect-bg, 15%); 60 | @DateSelect-footer-color: #666; 61 | @DateSelect-footer-button-font-weight: normal; 62 | @DateSelect-footer-button-font-weight-primary: 500; 63 | 64 | 65 | // single picker 66 | 67 | @DateSelect-footer-single-button-primary-color: @DateSelect-primary; 68 | @DateSelect-footer-single-button-hover-bg: rgba(255, 255, 255, 0.66); 69 | @DateSelect-footer-single-button-active-bg: rgba(0, 0, 0, 0.05); 70 | 71 | 72 | // multi picker 73 | 74 | @DateSelect-footer-multi-button-primary-bg: @DateSelect-primary; 75 | @DateSelect-footer-multi-button-primary-color: white; 76 | @DateSelect-footer-multi-button-color: @DateSelect-footer-color; 77 | @DateSelect-footer-multi-button-hover-color: @DateSelect-primary; 78 | 79 | 80 | 81 | 82 | // Z-Index List 83 | // ------------------------------ 84 | 85 | @DateSelect-dialog-zindex: 110; 86 | @DateSelect-backdrop-zindex: @DateSelect-dialog-zindex - 1; 87 | 88 | 89 | 90 | 91 | // Misc 92 | // ------------------------------ 93 | 94 | @DateSelect-border-radius-outer: .33em; 95 | @DateSelect-border-radius-inner: .25em; 96 | 97 | 98 | 99 | 100 | // Media queries breakpoints 101 | // ------------------------------ 102 | 103 | @DateSelect-screen-xs-min: 480px; 104 | @DateSelect-screen-sm-min: 768px; 105 | @DateSelect-screen-md-min: 992px; 106 | @DateSelect-screen-lg-min: 1200px; 107 | 108 | @DateSelect-screen-xs-max: (@DateSelect-screen-sm-min - 1); 109 | @DateSelect-screen-sm-max: (@DateSelect-screen-md-min - 1); 110 | @DateSelect-screen-md-max: (@DateSelect-screen-lg-min - 1); 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /lib/DateSelect.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 4 | 5 | var blacklist = require('blacklist'); 6 | var moment = require('moment'); 7 | var React = require('react'); 8 | 9 | var DateSelectDialog = require('./DateSelectDialog'); 10 | 11 | var DEFAULT_RANGES = [{ value: moment().subtract(1, 'weeks'), label: 'Past week' }, { value: moment().subtract(1, 'months'), label: 'Past month' }, { value: moment().subtract(3, 'months'), label: 'Past 3 months' }, { value: moment().subtract(6, 'months'), label: 'Past 6 months' }, { value: moment().subtract(12, 'months'), label: 'Past 12 months' }]; 12 | 13 | var DateSelect = React.createClass({ 14 | displayName: 'DateSelect', 15 | 16 | propTypes: { 17 | backdropClosesDateSelect: React.PropTypes.bool, 18 | dialogClassName: React.PropTypes.string, 19 | isExpanded: React.PropTypes.bool, 20 | isHeaderless: React.PropTypes.bool, 21 | isInstant: React.PropTypes.bool, 22 | isMulti: React.PropTypes.bool, 23 | predefinedRangeOptions: React.PropTypes.array, 24 | showPredefinedRanges: React.PropTypes.bool, 25 | value: React.PropTypes.any 26 | }, 27 | getDefaultProps: function getDefaultProps() { 28 | return { 29 | buttonLabel: 'Launch Date Select', 30 | predefinedRangeOptions: DEFAULT_RANGES 31 | }; 32 | }, 33 | getInitialState: function getInitialState() { 34 | return { 35 | isOpen: false 36 | }; 37 | }, 38 | openDateSelect: function openDateSelect() { 39 | this.setState({ isOpen: true }); 40 | }, 41 | closeDateSelect: function closeDateSelect() { 42 | this.setState({ isOpen: false }); 43 | }, 44 | renderDateSelect: function renderDateSelect() { 45 | // if (!this.state.isOpen) return; 46 | var dialogProps = blacklist(this.props, 'dialogClassName'); 47 | dialogProps.className = this.props.dialogClassName; 48 | dialogProps.onCancel = this.closeDateSelect; 49 | dialogProps.onSelect = this.closeDateSelect; 50 | return React.createElement(DateSelectDialog, _extends({ isOpen: this.state.isOpen }, dialogProps)); 51 | }, 52 | renderChildren: function renderChildren() { 53 | var _this = this; 54 | 55 | return React.Children.map(this.props.children, function (child) { 56 | return React.cloneElement(child, { onClick: _this.openDateSelect }); 57 | }); 58 | }, 59 | renderButton: function renderButton() { 60 | return React.createElement( 61 | 'button', 62 | { onClick: this.openDateSelect, type: 'button', className: this.props.buttonClassName }, 63 | this.props.buttonLabel 64 | ); 65 | }, 66 | render: function render() { 67 | return React.createElement( 68 | 'span', 69 | null, 70 | React.Children.count(this.props.children) ? this.renderChildren() : this.renderButton(), 71 | this.renderDateSelect() 72 | ); 73 | } 74 | }); 75 | 76 | module.exports = DateSelect; -------------------------------------------------------------------------------- /lib/DateSelectCalendar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react/addons'); 4 | var moment = require('moment'); 5 | var classNames = require('classnames'); 6 | 7 | var DateSelectHeader = require('./DateSelectHeader'); 8 | 9 | module.exports = React.createClass({ 10 | displayName: 'DateSelectCalendar', 11 | propTypes: { 12 | isExpanded: React.PropTypes.bool, 13 | isHeaderless: React.PropTypes.bool, 14 | isInstant: React.PropTypes.bool, 15 | selectedDate: React.PropTypes.string, 16 | weekStartsOn: React.PropTypes.string, 17 | yearRange: React.PropTypes.arrayOf(React.PropTypes.number) 18 | }, 19 | getDefaultProps: function getDefaultProps() { 20 | var yearRange = []; 21 | yearRange.push(parseInt(moment().subtract(10, 'years').format('YYYY'))); 22 | yearRange.push(parseInt(moment().add(10, 'years').format('YYYY'))); 23 | 24 | return { 25 | weekStartsOn: 'Monday', 26 | yearRange: yearRange 27 | }; 28 | }, 29 | getInitialState: function getInitialState() { 30 | return { 31 | selectedDate: this.props.selectedDate 32 | }; 33 | }, 34 | 35 | handleDaySelection: function handleDaySelection(day) { 36 | this.setState({ selectedDate: day }); 37 | }, 38 | 39 | render: function render() { 40 | var self = this; 41 | var firstDayOfMonth = moment().startOf('month').format('D'); 42 | var lastDayOfMonth = moment().endOf('month').format('D'); 43 | var currentDayOfMonth = moment().format('D'); 44 | 45 | var calendarClass = classNames('DateSelectCalendar', { 46 | 'DateSelectCalendar--start': this.props.startDate, 47 | 'DateSelectCalendar--end': this.props.endDate 48 | }); 49 | 50 | // variables 51 | var currentMonth = moment().format('MMMM'); 52 | var currentYear = moment().format('YYYY'); 53 | var years = []; 54 | var months = ['January', 'February', 'March', 'April', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 55 | var daysOfTheMonth = []; 56 | var daysOfTheWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; 57 | for (var i = firstDayOfMonth; i < lastDayOfMonth; i++) { 58 | daysOfTheMonth.push(i); 59 | } 60 | for (var j = this.props.yearRange[0]; j < this.props.yearRange[1]; j++) { 61 | years.push(j); 62 | } 63 | 64 | // elements 65 | 66 | var weekDays = daysOfTheWeek.map(function (day, i) { 67 | return React.createElement( 68 | 'abbr', 69 | { key: 'day' + i, className: 'DateSelectCalendar__legend__day', title: day }, 70 | day.slice(0, 1) 71 | ); 72 | }); 73 | var monthDays = daysOfTheMonth.map(function (day) { 74 | var dayClass = classNames('DateSelectCalendar__month__day', { 75 | 'is-current-day': day === currentDayOfMonth, 76 | 'is-selected': day === self.state.selectedDate, 77 | 'is-before-selected-day': self.state.selectedDate && day < self.state.selectedDate, 78 | 'is-after-selected-day': self.state.selectedDate && day > self.state.selectedDate 79 | }); 80 | return React.createElement( 81 | 'button', 82 | { key: 'day' + day, onClick: self.handleDaySelection.bind(self, day), className: dayClass }, 83 | day 84 | ); 85 | }); 86 | 87 | var titleMonths = months.map(function (month, i) { 88 | return React.createElement( 89 | 'option', 90 | { key: 'month' + i, value: month }, 91 | month.slice(0, 3) 92 | ); 93 | }); 94 | var titleYears = years.map(function (year, i) { 95 | return React.createElement( 96 | 'option', 97 | { key: 'year' + i, value: year }, 98 | year 99 | ); 100 | }); 101 | 102 | var calendar = React.createElement( 103 | 'div', 104 | { className: calendarClass }, 105 | !this.props.isHeaderless && React.createElement(DateSelectHeader, { selectedDate: this.state.selectedDate, isExpanded: this.props.isExpanded }), 106 | React.createElement( 107 | 'div', 108 | { className: 'DateSelectCalendar__toolbar' }, 109 | React.createElement( 110 | 'button', 111 | { className: 'DateSelectCalendar__toolbar__button DateSelectCalendar__toolbar__button--prev' }, 112 | 'Previous Month' 113 | ), 114 | React.createElement( 115 | 'select', 116 | { className: 'DateSelectCalendar__toolbar__select', defaultValue: currentMonth }, 117 | titleMonths 118 | ), 119 | React.createElement( 120 | 'select', 121 | { className: 'DateSelectCalendar__toolbar__select', defaultValue: currentYear }, 122 | titleYears 123 | ), 124 | React.createElement( 125 | 'button', 126 | { className: 'DateSelectCalendar__toolbar__button DateSelectCalendar__toolbar__button--next' }, 127 | 'Next Month' 128 | ) 129 | ), 130 | React.createElement( 131 | 'div', 132 | { className: 'DateSelectCalendar__legend' }, 133 | weekDays 134 | ), 135 | React.createElement( 136 | 'div', 137 | { className: 'DateSelectCalendar__month' }, 138 | monthDays 139 | ) 140 | ); 141 | 142 | return calendar; 143 | } 144 | }); -------------------------------------------------------------------------------- /lib/DateSelectDialog.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react/addons'); 4 | var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; 5 | var moment = require('moment'); 6 | var classNames = require('classnames'); 7 | 8 | var DateSelectCalendar = require('./DateSelectCalendar'); 9 | 10 | module.exports = React.createClass({ 11 | displayName: 'DateSelectDialog', 12 | propTypes: { 13 | backdropClosesDateSelect: React.PropTypes.bool, 14 | className: React.PropTypes.string, 15 | isExpanded: React.PropTypes.bool, 16 | isHeaderless: React.PropTypes.bool, 17 | isInstant: React.PropTypes.bool, 18 | isMulti: React.PropTypes.bool, 19 | onCancel: React.PropTypes.func, 20 | onSelect: React.PropTypes.func, 21 | predefinedRangeOptions: React.PropTypes.array, 22 | showPredefinedRanges: React.PropTypes.bool 23 | }, 24 | getInitialState: function getInitialState() { 25 | return { 26 | startDate: '', 27 | endDate: '' 28 | }; 29 | }, 30 | renderDialog: function renderDialog() { 31 | if (!this.props.isOpen) return null; 32 | return React.createElement( 33 | 'div', 34 | { className: 'DateSelect-dialog' }, 35 | React.createElement( 36 | 'div', 37 | { className: 'DateSelect-content' }, 38 | React.createElement( 39 | 'div', 40 | { className: 'DateSelect-body' }, 41 | React.createElement(DateSelectCalendar, { selectedDate: this.state.startDate, isHeaderless: this.props.isHeaderless, isInstant: this.props.isInstant }), 42 | this.props.isMulti && React.createElement(DateSelectCalendar, { selectedDate: this.state.endDate, isHeaderless: this.props.isHeaderless }) 43 | ), 44 | this.renderRanges(), 45 | !this.props.isInstant && React.createElement( 46 | 'div', 47 | { className: 'DateSelectFooter' }, 48 | React.createElement( 49 | 'button', 50 | { onClick: this.props.onSelect, className: 'DateSelectFooter__button DateSelectFooter__button--primary' }, 51 | 'Confirm' 52 | ), 53 | React.createElement( 54 | 'button', 55 | { onClick: this.props.onCancel, className: 'DateSelectFooter__button DateSelectFooter__button--link' }, 56 | 'Cancel' 57 | ) 58 | ) 59 | ) 60 | ); 61 | }, 62 | renderRanges: function renderRanges() { 63 | if (!this.props.showPredefinedRanges) return null; 64 | var self = this; 65 | var rangeItems = this.props.predefinedRangeOptions.map(function (r, i) { 66 | function action() { 67 | self.setState({ 68 | startDate: moment().format('D'), 69 | endDate: r.value.format('D') 70 | }); 71 | } 72 | return React.createElement( 73 | 'button', 74 | { key: 'range-button' + i, onClick: action, className: 'DateSelect__range__item' }, 75 | r.label 76 | ); 77 | }); 78 | return React.createElement( 79 | 'div', 80 | { className: 'DateSelect__range' }, 81 | rangeItems 82 | ); 83 | }, 84 | renderBackdrop: function renderBackdrop() { 85 | if (!this.props.isOpen) return null; 86 | return React.createElement('div', { className: 'DateSelect-backdrop', onClick: this.props.backdropClosesDateSelect ? this.props.onCancel : null }); 87 | }, 88 | render: function render() { 89 | // classes 90 | var componentClass = classNames('DateSelect', { 91 | 'single-picker': !this.props.isMulti, 92 | 'multi-picker': this.props.isMulti, 93 | 'range-picker': this.props.showPredefinedRanges 94 | }, this.props.className); 95 | 96 | // build the components 97 | return React.createElement( 98 | 'div', 99 | { className: componentClass }, 100 | React.createElement( 101 | ReactCSSTransitionGroup, 102 | { transitionName: 'modal-dialog', component: 'div' }, 103 | this.renderDialog() 104 | ), 105 | React.createElement( 106 | ReactCSSTransitionGroup, 107 | { transitionName: 'modal-backdrop', component: 'div' }, 108 | this.renderBackdrop() 109 | ) 110 | ); 111 | } 112 | }); -------------------------------------------------------------------------------- /lib/DateSelectHeader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react/addons'); 4 | var moment = require('moment'); 5 | var classNames = require('classnames'); 6 | 7 | module.exports = React.createClass({ 8 | displayName: 'DateSelectHeader', 9 | propTypes: { 10 | expanded: React.PropTypes.bool, 11 | date: React.PropTypes.object 12 | }, 13 | render: function render() { 14 | // helpers 15 | var date = moment(this.props.date); 16 | 17 | // classes 18 | var componentClass = classNames('DateSelectHeader', { 19 | 'DateSelectHeader--expanded': this.props.expanded, 20 | 'DateSelectHeader--condensed': !this.props.expanded, 21 | 'no-date': !this.props.date 22 | }); 23 | 24 | // elements 25 | var header = this.props.expanded ? React.createElement( 26 | 'div', 27 | { className: componentClass }, 28 | React.createElement( 29 | 'span', 30 | { className: 'DateSelectHeader__dow' }, 31 | date.format('dddd') 32 | ), 33 | React.createElement( 34 | 'span', 35 | { className: 'DateSelectHeader__month' }, 36 | date.format('MMMM') 37 | ), 38 | React.createElement( 39 | 'span', 40 | { className: 'DateSelectHeader__day' }, 41 | date.format('D') 42 | ), 43 | React.createElement( 44 | 'span', 45 | { className: 'DateSelectHeader__year' }, 46 | date.format('YYYY') 47 | ) 48 | ) : React.createElement( 49 | 'div', 50 | { className: componentClass }, 51 | React.createElement( 52 | 'span', 53 | { className: 'DateSelectHeader__dow' }, 54 | date.format('dddd') 55 | ), 56 | React.createElement( 57 | 'span', 58 | { className: 'DateSelectHeader__day' }, 59 | date.format('Do') 60 | ), 61 | React.createElement( 62 | 'span', 63 | { className: 'DateSelectHeader__month' }, 64 | date.format('MMMM') 65 | ), 66 | React.createElement( 67 | 'span', 68 | { className: 'DateSelectHeader__year' }, 69 | date.format('YYYY') 70 | ) 71 | ); 72 | 73 | if (this.props.date) { 74 | header = this.props.expanded ? React.createElement( 75 | 'div', 76 | { className: componentClass }, 77 | React.createElement( 78 | 'span', 79 | { className: 'DateSelectHeader__dow' }, 80 | date.format('dddd') 81 | ), 82 | React.createElement( 83 | 'span', 84 | { className: 'DateSelectHeader__month' }, 85 | date.format('MMMM') 86 | ), 87 | React.createElement( 88 | 'span', 89 | { className: 'DateSelectHeader__day' }, 90 | date.format('D') 91 | ), 92 | React.createElement( 93 | 'span', 94 | { className: 'DateSelectHeader__year' }, 95 | date.format('YYYY') 96 | ) 97 | ) : React.createElement( 98 | 'div', 99 | { className: componentClass }, 100 | React.createElement( 101 | 'span', 102 | { className: 'DateSelectHeader__dow' }, 103 | date.format('dddd') 104 | ), 105 | React.createElement( 106 | 'span', 107 | { className: 'DateSelectHeader__day' }, 108 | date.format('Do') 109 | ), 110 | React.createElement( 111 | 'span', 112 | { className: 'DateSelectHeader__month' }, 113 | date.format('MMMM') 114 | ), 115 | React.createElement( 116 | 'span', 117 | { className: 'DateSelectHeader__year' }, 118 | date.format('YYYY') 119 | ) 120 | ); 121 | } 122 | 123 | return header; 124 | } 125 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-date-select", 3 | "version": "0.0.7", 4 | "description": "A Date Select / Picker Input Component built with and for ReactJS", 5 | "main": "lib/DateSelect.js", 6 | "author": "Jed Watson", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/JedWatson/react-date-select.git" 11 | }, 12 | "dependencies": { 13 | "blacklist": "^1.1.2", 14 | "classnames": "^2.1.3", 15 | "moment": "~2.10.6" 16 | }, 17 | "devDependencies": { 18 | "babel-eslint": "^4.0.10", 19 | "babel-jest": "^5.3.0", 20 | "eslint": "^1.2.1", 21 | "eslint-plugin-react": "^3.2.3", 22 | "gulp": "^3.9.0", 23 | "jest-cli": "^0.4.7", 24 | "react": ">=0.13.0", 25 | "react-component-gulp-tasks": "^0.7.0" 26 | }, 27 | "peerDependencies": { 28 | "react": ">=0.13.3" 29 | }, 30 | "browserify-shim": { 31 | "blacklist": "global:blacklist", 32 | "classnames": "global:classNames", 33 | "moment": "global:moment", 34 | "react": "global:React" 35 | }, 36 | "scripts": { 37 | "build": "gulp clean && NODE_ENV=production gulp build", 38 | "bump": "gulp bump", 39 | "bump:major": "gulp bump:major", 40 | "bump:minor": "gulp bump:minor", 41 | "start": "gulp dev", 42 | "examples": "gulp dev:server", 43 | "lint": "eslint .", 44 | "release": "gulp release", 45 | "publish:site": "gulp publish:examples", 46 | "test": "jest; true", 47 | "watch": "gulp watch:lib" 48 | }, 49 | "jest": { 50 | "scriptPreprocessor": "/node_modules/babel-jest", 51 | "unmockedModulePathPatterns": [ 52 | "/node_modules/react" 53 | ], 54 | "testPathDirs": [ 55 | "/src" 56 | ] 57 | }, 58 | "keywords": [ 59 | "react", 60 | "react-component", 61 | "date", 62 | "datepicker", 63 | "dateinput", 64 | "input", 65 | "form", 66 | "ui" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /src/Calendar.js: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | var moment = require('moment'); 3 | var classNames = require('classnames'); 4 | 5 | var CalendarHeader = require('./CalendarHeader'); 6 | 7 | module.exports = React.createClass({ 8 | propTypes: { 9 | header: React.PropTypes.oneOf(['full', 'simple', 'none']), 10 | selectedDate: React.PropTypes.any, 11 | weekStartsOn: React.PropTypes.string, 12 | yearRange: React.PropTypes.arrayOf(React.PropTypes.number) 13 | }, 14 | getDefaultProps() { 15 | var yearRange = []; 16 | yearRange.push(parseInt(moment().subtract(100, 'years').format('YYYY'))); 17 | yearRange.push(parseInt(moment().add(100, 'years').format('YYYY'))); 18 | 19 | return { 20 | header: 'full', 21 | weekStartsOn: 'Monday', 22 | yearRange: yearRange 23 | }; 24 | }, 25 | getInitialState() { 26 | return { 27 | selectedDate: this.props.selectedDate 28 | }; 29 | }, 30 | handleDaySelection(day) { 31 | this.setState({ selectedDate: day }); 32 | }, 33 | render() { 34 | var self = this; 35 | var firstDayOfMonth = moment().startOf('month').format('D'); 36 | var lastDayOfMonth = moment().endOf('month').format('D'); 37 | var currentDayOfMonth = moment().format('D'); 38 | 39 | var calendarClass = classNames('DateSelectCalendar', { 40 | 'DateSelectCalendar--start': this.props.startDate, 41 | 'DateSelectCalendar--end': this.props.endDate 42 | }); 43 | 44 | 45 | // variables 46 | var currentMonth = moment().format('MMMM'); 47 | var currentYear = moment().format('YYYY'); 48 | var years = []; 49 | var months = ['January', 'February', 'March', 'April', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 50 | var daysOfTheMonth = []; 51 | var daysOfTheWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; 52 | for (var i = firstDayOfMonth; i < lastDayOfMonth; i++) { 53 | daysOfTheMonth.push(i); 54 | } 55 | for (var j = this.props.yearRange[0]; j < this.props.yearRange[1]; j++) { 56 | years.push(j); 57 | } 58 | 59 | // elements 60 | 61 | var weekDays = daysOfTheWeek.map(function(day, i) { 62 | return {day.slice(0, 1)}; 63 | }); 64 | var monthDays = daysOfTheMonth.map(function(day) { 65 | var dayClass = classNames('DateSelectCalendar__month__day', { 66 | 'is-current-day': day === currentDayOfMonth, 67 | 'is-selected': day === self.state.selectedDate, 68 | 'is-before-selected-day': self.state.selectedDate && (day < self.state.selectedDate), 69 | 'is-after-selected-day': self.state.selectedDate && (day > self.state.selectedDate) 70 | }); 71 | return ; 72 | }); 73 | 74 | var titleMonths = months.map(function(month, i) { 75 | return ; 76 | }); 77 | var titleYears = years.map(function(year, i) { 78 | return ; 79 | }); 80 | 81 | var calendar = ( 82 |
83 | {this.props.header !== 'none' && } 84 |
85 | 86 | 87 | 88 | 89 |
90 |
{weekDays}
91 |
{monthDays}
92 |
93 | ); 94 | 95 | return calendar; 96 | } 97 | }); 98 | -------------------------------------------------------------------------------- /src/CalendarHeader.js: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | var moment = require('moment'); 3 | var classNames = require('classnames'); 4 | 5 | module.exports = React.createClass({ 6 | displayName: 'DateSelectHeader', 7 | propTypes: { 8 | expanded: React.PropTypes.bool, 9 | date: React.PropTypes.object 10 | }, 11 | render() { 12 | // helpers 13 | var date = moment(this.props.date); 14 | 15 | // classes 16 | var componentClass = classNames('DateSelectHeader', { 17 | 'DateSelectHeader--expanded': this.props.expanded, 18 | 'DateSelectHeader--condensed': !this.props.expanded, 19 | 'no-date': !this.props.date 20 | }); 21 | 22 | // elements 23 | var header = this.props.expanded ? (
24 | {date.format('dddd')} 25 | {date.format('MMMM')} 26 | {date.format('D')} 27 | {date.format('YYYY')} 28 |
) : (
29 | {date.format('dddd')} 30 | {date.format('Do')} 31 | {date.format('MMMM')} 32 | {date.format('YYYY')} 33 |
); 34 | 35 | if (this.props.date) { 36 | header = this.props.expanded ? (
37 | {date.format('dddd')} 38 | {date.format('MMMM')} 39 | {date.format('D')} 40 | {date.format('YYYY')} 41 |
) : (
42 | {date.format('dddd')} 43 | {date.format('Do')} 44 | {date.format('MMMM')} 45 | {date.format('YYYY')} 46 |
); 47 | } 48 | 49 | return header; 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /src/DateSelect.js: -------------------------------------------------------------------------------- 1 | var blacklist = require('blacklist'); 2 | var CalendarComponent = require('./Calendar'); 3 | var classNames = require('classnames'); 4 | var React = require('react/addons'); 5 | 6 | export var Popup = require('./Popup'); 7 | 8 | var InlineCalendar = React.createClass({ 9 | propTypes: { 10 | className: React.PropTypes.string, 11 | style: React.PropTypes.object 12 | }, 13 | render () { 14 | var calendarProps = blacklist(this.props, 'className', 'style'); 15 | var componentClass = classNames('DateSelect DateSelect-inline-container', this.props.className); 16 | return ( 17 |
18 | 19 |
20 | ); 21 | } 22 | }); 23 | 24 | export var Calendar = InlineCalendar; 25 | -------------------------------------------------------------------------------- /src/Popup.js: -------------------------------------------------------------------------------- 1 | var blacklist = require('blacklist'); 2 | var moment = require('moment'); 3 | var React = require('react'); 4 | 5 | var PopupDialog = require('./PopupDialog'); 6 | 7 | var DEFAULT_RANGES = [ 8 | { value: moment().subtract(1, 'weeks'), label: 'Past week' }, 9 | { value: moment().subtract(1, 'months'), label: 'Past month' }, 10 | { value: moment().subtract(3, 'months'), label: 'Past 3 months' }, 11 | { value: moment().subtract(6, 'months'), label: 'Past 6 months' }, 12 | { value: moment().subtract(12, 'months'), label: 'Past 12 months' } 13 | ]; 14 | 15 | var DateSelect = React.createClass({ 16 | propTypes: { 17 | backdropClosesDateSelect: React.PropTypes.bool, 18 | dialogClassName: React.PropTypes.string, 19 | isExpanded: React.PropTypes.bool, 20 | isHeaderless: React.PropTypes.bool, 21 | confirmationIsRequired: React.PropTypes.bool, 22 | isMulti: React.PropTypes.bool, 23 | predefinedRangeOptions: React.PropTypes.array, 24 | showPredefinedRanges: React.PropTypes.bool, 25 | value: React.PropTypes.any 26 | }, 27 | getDefaultProps () { 28 | return { 29 | buttonLabel: 'Launch Date Select', 30 | predefinedRangeOptions: DEFAULT_RANGES 31 | }; 32 | }, 33 | getInitialState () { 34 | return { 35 | isOpen: false 36 | }; 37 | }, 38 | openDateSelect () { 39 | this.setState({ isOpen: true }); 40 | }, 41 | closeDateSelect () { 42 | this.setState({ isOpen: false }); 43 | }, 44 | renderDateSelect () { 45 | var dialogProps = blacklist(this.props, 'dialogClassName'); 46 | dialogProps.className = this.props.dialogClassName; 47 | dialogProps.onCancel = this.closeDateSelect; 48 | dialogProps.onSelect = this.closeDateSelect; 49 | return ; 50 | }, 51 | renderChildren () { 52 | return React.Children.map(this.props.children, (child) => { 53 | return React.cloneElement(child, { onClick: this.openDateSelect }); 54 | }); 55 | }, 56 | renderButton () { 57 | return ; 58 | }, 59 | render () { 60 | return ( 61 | 62 | {React.Children.count(this.props.children) ? this.renderChildren() : this.renderButton()} 63 | {this.renderDateSelect()} 64 | 65 | ); 66 | } 67 | }); 68 | 69 | module.exports = DateSelect; 70 | -------------------------------------------------------------------------------- /src/PopupDialog.js: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; 3 | var moment = require('moment'); 4 | var classNames = require('classnames'); 5 | 6 | var Calendar = require('./Calendar'); 7 | 8 | module.exports = React.createClass({ 9 | propTypes: { 10 | backdropClosesDateSelect: React.PropTypes.bool, 11 | className: React.PropTypes.string, 12 | isExpanded: React.PropTypes.bool, 13 | isHeaderless: React.PropTypes.bool, 14 | confirmationIsRequired: React.PropTypes.bool, 15 | isMulti: React.PropTypes.bool, 16 | onCancel: React.PropTypes.func, 17 | onSelect: React.PropTypes.func, 18 | predefinedRangeOptions: React.PropTypes.array, 19 | showPredefinedRanges: React.PropTypes.bool 20 | }, 21 | getDefaultProps() { 22 | return { 23 | confirmationIsRequired: true 24 | }; 25 | }, 26 | getInitialState() { 27 | return { 28 | startDate: '', 29 | endDate: '' 30 | }; 31 | }, 32 | renderFooter() { 33 | if (!this.props.confirmationIsRequired) return null; 34 | return ( 35 |
36 | 37 | 38 |
39 | ); 40 | }, 41 | renderDialog() { 42 | if (!this.props.isOpen) return null; 43 | return ( 44 |
45 |
46 |
47 | 48 | {this.props.isMulti && } 49 |
50 | {this.renderRanges()} 51 | {this.renderFooter()} 52 |
53 |
54 | ); 55 | }, 56 | renderRanges() { 57 | if (!this.props.showPredefinedRanges) return null; 58 | var self = this; 59 | var rangeItems = this.props.predefinedRangeOptions.map(function(r, i) { 60 | function action() { 61 | self.setState({ 62 | startDate: moment().format('D'), 63 | endDate: r.value.format('D') 64 | }); 65 | } 66 | return ; 67 | }); 68 | return ( 69 |
70 | {rangeItems} 71 |
72 | ); 73 | }, 74 | renderBackdrop() { 75 | if (!this.props.isOpen) return null; 76 | return ( 77 |
78 | ); 79 | }, 80 | render() { 81 | // classes 82 | var componentClass = classNames('DateSelect', { 83 | 'single-picker': !this.props.isMulti, 84 | 'multi-picker': this.props.isMulti, 85 | 'range-picker': this.props.showPredefinedRanges 86 | }, this.props.className); 87 | 88 | // build the components 89 | return ( 90 |
91 | 92 | {this.renderDialog()} 93 | 94 | 95 | {this.renderBackdrop()} 96 | 97 |
98 | ); 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /src/__tests__/DateSelect-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*global describe, it, jest, expect*/ 3 | 4 | jest.dontMock('moment'); 5 | jest.dontMock('../DateSelect'); 6 | 7 | var React = require('react/addons'); 8 | var DateSelect = require('../DateSelect'); 9 | var TestUtils = React.addons.TestUtils; 10 | 11 | describe('DateSelect test', function() { 12 | 13 | // TODO: Add Tests 14 | 15 | // Render an instance of the component 16 | var instance = TestUtils.renderIntoDocument( 17 | 18 | ); 19 | 20 | it('should be rendered', function() { 21 | expect(instance).toBeDefined(); 22 | }); 23 | 24 | }); 25 | --------------------------------------------------------------------------------