├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── dist ├── rm-datepicker.css ├── rm-datepicker.js ├── rm-datepicker.min.css └── rm-datepicker.min.js ├── gulpfile.js ├── index.html ├── package.json └── src ├── sass ├── _framework.scss ├── _var.scss ├── components │ ├── _base.scss │ ├── _date.scss │ ├── _day.scss │ ├── _mi_icons.scss │ ├── _month.scss │ ├── _nav.scss │ ├── _square.scss │ ├── _sunSat.scss │ └── _year.scss ├── rm-datepicker.scss └── themes │ └── _theme1.scss └── scripts └── rm-datepicker.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .sass-cache 3 | node_modules 4 | bower_components 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sergiu Ghenciu, RUBYMAGE 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular datepicker 2 | 3 | RM-DATEPICKER is a directive for angular js. It allows you to render a responsive datepicker inline or as a modal on input focus. 4 | The rm-datepicker is very nice, is responsive, fast and user friendly. 5 | It is well optimized and lightweight (around 5 kb) and has no other dependencies than Angular itself. 6 | 7 | ###### Tags: 8 | 9 | angular.js datepicker, angularjs datepicker, angular datepicker, angular-datepicker, pickadate, datepicker , timepicker, 10 | pikaday, datepicker directive, datepicker add-on, simple datepicker, clean datepicker, fluid datepicker, 11 | customizable datepicker, inline datepicker, dropdown datepicker, modal datepicker, lightweight datepicker, 12 | datepicker component 13 | 14 | ### Demo 15 | 16 | Try it on Plunker 17 | 18 | ### Usage 19 | 20 | 1) Add the `rmDatepicker` module to your dependencies 21 | 22 | ```javascript 23 | angular.module('myApp', ['rmDatepicker']); 24 | ``` 25 | 26 | 2) Use the `rm-datepicker` directive in any element 27 | 28 | ```html 29 |
30 | ``` 31 | 32 | ### Configuration 33 | 34 | #### Scope configuration 35 | 36 | ```html 37 |
38 | ``` 39 | 40 | ```javascript 41 | function MyAppController($scope) { 42 | $scope.rmConfig1 = { 43 | mondayStart: false, 44 | initState : "month", /* decade || year || month */ 45 | maxState : "decade", 46 | minState : "month", 47 | decadeSize: 12, 48 | monthSize: 42, /* "auto" || fixed nr. (35 or 42) */ 49 | min: new Date("2010-10-10"), 50 | max: null, 51 | format: "yyyy-MM-dd" /* https://docs.angularjs.org/api/ng/filter/date */ 52 | } 53 | $scope.oDate1 = new Date(); 54 | } 55 | ``` 56 | 57 | #### Global configuration 58 | 59 | ```javascript 60 | app.config(['rmDatepickerConfig', function(rmDatepickerConfig) { 61 | rmDatepickerConfig.mondayStart = true; 62 | rmDatepickerConfig.initState : "month"; 63 | /* ... */ 64 | }]); 65 | ``` 66 | 67 | ## License 68 | 69 | RM-DATEPICKER is under MIT license: 70 | 71 | > Copyright (C) 2015 Sergiu Ghenciu, RUBYMAGE 72 | > 73 | > Permission is hereby granted, free of charge, to any person 74 | > obtaining a copy of this software and associated documentation files 75 | > (the "Software"), to deal in the Software without restriction, 76 | > including without limitation the rights to use, copy, modify, merge, 77 | > publish, distribute, sublicense, and/or sell copies of the Software, 78 | > and to permit persons to whom the Software is furnished to do so, 79 | > subject to the following conditions: 80 | > 81 | > The above copyright notice and this permission notice shall be 82 | > included in all copies or substantial portions of the Software. 83 | > 84 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 86 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 87 | > NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 88 | > BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 89 | > ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 90 | > CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 91 | > SOFTWARE. 92 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rm-datepicker", 3 | "version": "1.0.1", 4 | "description": "Clean and well optimized datepicker directive for angular", 5 | "main": [ 6 | "dist/rm-datepicker.js", 7 | "dist/rm-datepicker.css" 8 | ], 9 | "authors": [ 10 | "Sergiu Ghenciu <6180rm@gmail.com>" 11 | ], 12 | "license": "MIT", 13 | "keywords": [ 14 | "angular.js", 15 | "datepicker", 16 | "angularjs", 17 | "datepicker", 18 | "angular", 19 | "datepicker", 20 | "angular-datepicker", 21 | "pickadate", 22 | "datepicker", 23 | "timepicker", 24 | "pikaday", 25 | "datepicker", 26 | "directive", 27 | "datepicker", 28 | "add-on", 29 | "simple", 30 | "datepicker", 31 | "clean", 32 | "datepicker", 33 | "fluid", 34 | "datepicker", 35 | "customizable", 36 | "datepicker", 37 | "inline", 38 | "datepicker", 39 | "dropdown", 40 | "datepicker", 41 | "modal", 42 | "datepicker", 43 | "lightweight", 44 | "datepicker", 45 | "datepicker", 46 | "component" 47 | ], 48 | "homepage": "https://github.com/RUBYMAGE/angular-datepicker", 49 | "dependencies": { 50 | "angular": "1.x" 51 | }, 52 | "ignore": [ 53 | "**/.*", 54 | ".sass-cache", 55 | "node_modules", 56 | "src", 57 | ".gitignore", 58 | "gulpfile.js", 59 | "index.html", 60 | "LICENSE", 61 | "package.json" 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /dist/rm-datepicker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * RM-DATEPICKER v1.0.0 3 | * http://rubymage.com 4 | * 5 | * Copyright 2015 Sergiu Ghenciu, RUBYMAGE 6 | * Released under the MIT license 7 | * https://github.com/RUBYMAGE/angular-datepicker/blob/master/LICENSE 8 | */ 9 | /********************************** 10 | Material Design Icons https://google.github.io/material-design-icons/ 11 | **********************************/ 12 | @import url(https://fonts.googleapis.com/icon?family=Material+Icons); 13 | .rm-datepicker [class*="mi_"] { 14 | speak: none; 15 | line-height: inherit; 16 | font-family: "Material Icons"; 17 | font-style: normal; 18 | font-weight: 400; 19 | font-size: inherit; 20 | font-variant: normal; 21 | text-rendering: auto; 22 | text-transform: none; 23 | letter-spacing: normal; 24 | word-wrap: normal; 25 | white-space: nowrap; 26 | direction: ltr; 27 | display: inline-block; 28 | -webkit-font-smoothing: antialiased; 29 | -moz-osx-font-smoothing: grayscale; 30 | } 31 | .rm-datepicker [class*="mi_"]:before { 32 | display: inline-block; 33 | speak: none; 34 | text-decoration: inherit; 35 | } 36 | .rm-datepicker .mi_arrow_back:before { 37 | content: "\e5C4"; 38 | } 39 | .rm-datepicker .mi_keyboard_arrow_up:before { 40 | content: "\e316"; 41 | } 42 | .rm-datepicker .mi_keyboard_arrow_down:before { 43 | content: "\e313"; 44 | } 45 | .rm-datepicker .mi_close:before { 46 | content: "\e5CD"; 47 | } 48 | 49 | /********************************** 50 | Base 51 | **********************************/ 52 | .rm-datepicker [ng-cloak] { 53 | display: none !important; 54 | } 55 | 56 | .rm-datepicker { 57 | background-color: #fff; 58 | line-height: 1.5; 59 | font-family: arial, sans-serif; 60 | font-weight: normal; 61 | color: rgba(0, 0, 0, 0.87); 62 | box-sizing: border-box; 63 | } 64 | .rm-datepicker *, .rm-datepicker *:before, .rm-datepicker *:after { 65 | box-sizing: inherit; 66 | } 67 | .rm-datepicker a { 68 | color: rgba(0, 0, 0, 0.87); 69 | text-decoration: none; 70 | -webkit-tap-highlight-color: transparent; 71 | } 72 | .rm-datepicker:after { 73 | content: ''; 74 | display: block; 75 | clear: both; 76 | } 77 | 78 | .rm-overlay { 79 | z-index: 999980; 80 | position: fixed; 81 | left: 0; 82 | right: 0; 83 | top: 0; 84 | bottom: 0; 85 | background: rgba(0, 0, 0, 0.3); 86 | } 87 | 88 | .rm-datepicker.it-is-input { 89 | position: absolute; 90 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); 91 | z-index: 999999; 92 | min-width: 360px; 93 | } 94 | 95 | @media only screen and (max-width: 480px) { 96 | .rm-datepicker.it-is-input { 97 | left: 0; 98 | right: 0; 99 | margin: 0 auto; 100 | width: 100%; 101 | min-width: 0; 102 | max-width: 360px; 103 | } 104 | } 105 | @media only screen and (min-width: 0) { 106 | .rm-datepicker { 107 | font-size: 14px; 108 | } 109 | } 110 | @media only screen and (min-width: 992px) { 111 | .rm-datepicker { 112 | font-size: 14.5px; 113 | } 114 | } 115 | @media only screen and (min-width: 1200px) { 116 | .rm-datepicker { 117 | font-size: 15px; 118 | } 119 | } 120 | .rm-datepicker .waves-effect .waves-ripple { 121 | z-index: 1; 122 | background: rgba(255, 255, 255, 0.4); 123 | background: radial-gradient(rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); 124 | } 125 | 126 | .rm-datepicker .body .waves-effect .waves-ripple { 127 | background: rgba(0, 150, 136, 0.7); 128 | } 129 | 130 | .waves-effect.waves-light .waves-ripple { 131 | background: rgba(255, 255, 255, 0.45); 132 | } 133 | 134 | .waves-effect.waves-red .waves-ripple { 135 | background: rgba(244, 67, 54, 0.7); 136 | } 137 | 138 | .waves-effect.waves-yellow .waves-ripple { 139 | background: rgba(255, 235, 59, 0.7); 140 | } 141 | 142 | .waves-effect.waves-orange .waves-ripple { 143 | background: rgba(255, 152, 0, 0.7); 144 | } 145 | 146 | .waves-effect.waves-purple .waves-ripple { 147 | background: rgba(156, 39, 176, 0.7); 148 | } 149 | 150 | .waves-effect.waves-green .waves-ripple { 151 | background: rgba(76, 175, 80, 0.7); 152 | } 153 | 154 | .waves-effect.waves-teal .waves-ripple { 155 | background: rgba(0, 150, 136, 0.7); 156 | } 157 | 158 | /********************************** 159 | Nav 160 | **********************************/ 161 | .rm-datepicker .nav { 162 | background-color: #00bfa5; 163 | width: 100%; 164 | position: relative; 165 | } 166 | 167 | .rm-datepicker .nav:after { 168 | content: ''; 169 | display: block; 170 | clear: both; 171 | } 172 | 173 | .rm-datepicker .nav a { 174 | height: 50px; 175 | line-height: 50px; 176 | cursor: pointer; 177 | font-size: 1em; 178 | color: #fff; 179 | padding: 0 10px; 180 | display: inline-block; 181 | text-align: center; 182 | transition: background-color 0.3s; 183 | float: left; 184 | } 185 | 186 | .rm-datepicker .nav .today { 187 | float: right; 188 | text-align: right; 189 | } 190 | 191 | .rm-datepicker .nav a * { 192 | height: inherit; 193 | line-height: inherit; 194 | } 195 | 196 | .rm-datepicker .nav a:hover { 197 | background-color: rgba(0, 0, 0, 0.1); 198 | } 199 | 200 | .rm-datepicker .nav i { 201 | font-size: 1.1em; 202 | } 203 | 204 | .rm-datepicker .nav .adjacent i { 205 | font-size: 1.4em; 206 | } 207 | 208 | .rm-datepicker .nav > a:first-child { 209 | width: 60px; 210 | text-align: left; 211 | } 212 | 213 | .rm-datepicker .nav .back { 214 | width: 50%; 215 | max-width: 170px; 216 | margin-left: -60px; 217 | padding-left: 32px; 218 | } 219 | 220 | .rm-datepicker .nav .adjacent { 221 | width: 15%; 222 | max-width: 50px; 223 | } 224 | 225 | .rm-datepicker .nav .today { 226 | width: 20%; 227 | } 228 | 229 | @media only screen and (min-width: 339px) { 230 | .rm-datepicker .nav a:first-child, 231 | .rm-datepicker .nav a:last-child { 232 | padding: 0 15px; 233 | } 234 | 235 | .rm-datepicker .nav .back { 236 | padding: 0 0 0 40px; 237 | } 238 | } 239 | /* All the styles below can be deleted (it is for very very old devices)*/ 240 | @media only screen and (max-width: 315px) { 241 | .rm-datepicker .nav a { 242 | height: 35px; 243 | line-height: 35px; 244 | padding: 0 15px; 245 | } 246 | 247 | .rm-datepicker .nav .back { 248 | width: 100%; 249 | max-width: 100%; 250 | text-align: left; 251 | padding-left: 45px; 252 | } 253 | 254 | .rm-datepicker .nav .adjacent, 255 | .rm-datepicker .nav .today { 256 | width: 33.3333%; 257 | max-width: 100%; 258 | text-align: center; 259 | } 260 | } 261 | /********************************** 262 | Square 263 | **********************************/ 264 | .rm-datepicker .square > * { 265 | float: left; 266 | margin: 0; 267 | position: relative; 268 | } 269 | 270 | .rm-datepicker .square a { 271 | z-index: 9; 272 | position: absolute; 273 | top: 0; 274 | left: 0; 275 | width: 100%; 276 | height: 100%; 277 | text-align: center; 278 | cursor: pointer; 279 | } 280 | 281 | .rm-datepicker .square a:before { 282 | content: ""; 283 | display: inline-block; 284 | vertical-align: middle; 285 | height: 100%; 286 | } 287 | 288 | .rm-datepicker .square a * { 289 | vertical-align: middle; 290 | } 291 | 292 | /********************************** 293 | Date 294 | **********************************/ 295 | .rm-datepicker .date a { 296 | color: rgba(0, 0, 0, 0.87); 297 | font-size: 0.8em; 298 | font-weight: 400; 299 | cursor: pointer; 300 | border-top: 1px solid #eee; 301 | border-left: 1px solid #eee; 302 | } 303 | 304 | .rm-datepicker .date .j a, 305 | .rm-datepicker .date a.j, 306 | .rm-datepicker .date a:hover { 307 | font-weight: 600; 308 | } 309 | 310 | /* active date */ 311 | .rm-datepicker .date .j a { 312 | background-color: rgba(0, 191, 165, 0.5); 313 | } 314 | 315 | .rm-datepicker .date .out a { 316 | background-color: #E4E3E3 !important; 317 | opacity: 0.3; 318 | border-color: #BBB; 319 | } 320 | 321 | .rm-datepicker .date .off a { 322 | background-color: #F2F2F2 !important; 323 | opacity: 0.05; 324 | border-color: #555; 325 | cursor: not-allowed; 326 | } 327 | 328 | .rm-datepicker .decade.date > *:nth-child(-n+4) a, 329 | .rm-datepicker .year.date > *:nth-child(-n+4) a, 330 | .rm-datepicker .month.date > *:nth-child(-n+7) a { 331 | border-top-color: transparent; 332 | } 333 | 334 | .rm-datepicker .date > *:nth-child(1) a, 335 | .rm-datepicker .decade.date > *:nth-child(4n+1) a, 336 | .rm-datepicker .year.date > *:nth-child(4n+1) a, 337 | .rm-datepicker .month.date > *:nth-child(7n+1) a { 338 | border-left-color: transparent; 339 | } 340 | 341 | /********************************** 342 | Day of week 343 | **********************************/ 344 | .rm-datepicker .day { 345 | background-color: #eee; 346 | } 347 | 348 | .rm-datepicker .day > * { 349 | display: inline-block; 350 | width: 14.28571428570%; 351 | text-align: center; 352 | padding: 4px 0; 353 | color: rgba(0, 0, 0, 0.87); 354 | font-size: 0.8em; 355 | } 356 | 357 | /********************************** 358 | Sunday Saturday 359 | **********************************/ 360 | /* saturday */ 361 | .rm-datepicker .sunSat > *:nth-child(7), 362 | .rm-datepicker .sunSat > *:nth-child(7n) a, 363 | .rm-datepicker.mondayStart .sunSat > *:nth-child(6), 364 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n+6) a, 365 | .rm-datepicker .sunSat + * > *:nth-child(7), 366 | .rm-datepicker .sunSat + * > *:nth-child(7n) a, 367 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(6), 368 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n+6) a { 369 | color: #039BE5 !important; 370 | } 371 | 372 | /* sunday */ 373 | .rm-datepicker .sunSat > *:nth-child(1), 374 | .rm-datepicker .sunSat > *:nth-child(7n+1) a, 375 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7), 376 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n) a, 377 | .rm-datepicker .sunSat + * > *:nth-child(1), 378 | .rm-datepicker .sunSat + * > *:nth-child(7n+1) a, 379 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7), 380 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n) a { 381 | color: #f44336 !important; 382 | } 383 | 384 | /* reset saturday */ 385 | .rm-datepicker.mondayStart .sunSat > *:nth-child(1), 386 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n+1) a, 387 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(1), 388 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n+1) a { 389 | color: rgba(0, 0, 0, 0.87) !important; 390 | } 391 | 392 | /********************************** 393 | Year and decade 394 | **********************************/ 395 | .rm-datepicker .decade > *, 396 | .rm-datepicker .year > * { 397 | width: 25%; 398 | padding: 25% 0 0; 399 | } 400 | 401 | /********************************** 402 | Month 403 | **********************************/ 404 | .rm-datepicker .month > * { 405 | width: 14.28571428570%; 406 | padding: 14.28571428570% 0 0; 407 | } 408 | -------------------------------------------------------------------------------- /dist/rm-datepicker.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * RM-DATEPICKER v1.0.0 3 | * http://rubymage.com 4 | * 5 | * Copyright 2015 Sergiu Ghenciu, RUBYMAGE 6 | * Released under the MIT license 7 | * https://github.com/RUBYMAGE/angular-datepicker/blob/master/LICENSE 8 | */ 9 | 10 | (function () { 11 | 12 | var Module = angular.module('rmDatepicker', []); 13 | 14 | Module.constant('rmDatepickerConfig', { 15 | mondayStart: false, 16 | textToday: "Today", 17 | 18 | initState: "month", 19 | maxState: "decade", 20 | minState: "month", 21 | toggleState: true, 22 | 23 | decadeSize: 12, 24 | monthSize: 42, /* "auto" || fixed nr. (35 or 42) */ 25 | 26 | min: null, 27 | max: null, 28 | format: "yyyy-MM-dd" 29 | }); 30 | 31 | Module.directive("rmDatepicker", ['rmDatepickerConfig', '$compile', '$filter', '$document', '$timeout', 32 | function (rmDatepickerConfig, $compile, $filter, $document, $timeout) { 33 | 34 | var link = function (scope, element, attrs, ngModel) { 35 | var conf = angular.copy(rmDatepickerConfig); 36 | 37 | if (scope.rmConfig) { 38 | for (var prop in conf) 39 | if (conf.hasOwnProperty(prop)) 40 | if (scope.rmConfig[prop] != undefined) conf[prop] = scope.rmConfig[prop]; 41 | } 42 | if (conf.min) conf.min.setHours(0, 0, 0, 0); 43 | if (conf.max) conf.max.setHours(23, 59, 59, 999); 44 | 45 | var isInput = element[0].tagName.toUpperCase() == "INPUT"; 46 | var isReached = { 47 | min: false, 48 | max: false 49 | }; 50 | var daysInMonth = function (year, month) { 51 | return new Date(year, month + 1, 0).getDate(); 52 | }; 53 | var adjustDate = function (date) { 54 | var date = parseInt(date, 10); 55 | if (!isNaN(date)) { 56 | var max = daysInMonth(scope.j.getFullYear(), scope.j.getMonth()); 57 | if (date < 1) date = 1; 58 | if (date > max) date = max; 59 | scope.j.setDate(date); 60 | } 61 | }; 62 | var gen = { 63 | decade: function (oDate) { 64 | var Y = oDate.getFullYear(), 65 | m = oDate.getMonth(), 66 | d = oDate.getDate(), 67 | max, 68 | i = 0, 69 | n = conf.decadeSize || 12; // count of years in decade 70 | var aDecade = new Array(n); 71 | 72 | Y = Math.floor(Y / n) * n; // begin year of current decade 73 | 74 | for (; i < n; Y++, i++) { 75 | max = daysInMonth(Y, m); 76 | if (d > max) d = max; 77 | aDecade[i] = new Date(Y, m, d, 3, 0, 1, 0); 78 | } 79 | return aDecade; 80 | }, 81 | year: function (oDate) { 82 | var Y = oDate.getFullYear(), 83 | m = 0, 84 | d = oDate.getDate(), 85 | max; 86 | var aYear = []; 87 | for (; m < 12; m++) { 88 | max = daysInMonth(Y, m); 89 | if (d > max) d = max; 90 | aYear.push(new Date(Y, m, d, 3, 0, 1, 0)); 91 | } 92 | return aYear; 93 | }, 94 | month: function (oDate) { 95 | var Y = oDate.getFullYear(), 96 | m = oDate.getMonth(), 97 | startDate = new Date(Y, m, 1, 3, 0, 1, 0), 98 | n; 99 | var startPos = startDate.getDay() || 7; 100 | if (scope.mondayStart) startPos = startPos - 1 || 7; 101 | 102 | if (conf.monthSize == "auto") 103 | n = ( startPos + daysInMonth(Y, m) < 35 ) ? 35 : 42; 104 | else 105 | n = conf.monthSize; 106 | 107 | startDate.setDate(-startPos + 1); 108 | return gen.dates(startDate, n); 109 | }, 110 | dates: function (startDate, n) { 111 | var aDates = new Array(n), 112 | current = new Date(startDate), 113 | i = 0; 114 | while (i < n) { 115 | aDates[i++] = new Date(current); 116 | current.setDate(current.getDate() + 1); 117 | } 118 | return aDates; 119 | } 120 | }; 121 | var refresh = function (state) { 122 | state = state || scope.state; 123 | scope.aDates = gen[state](scope.j); 124 | 125 | if (conf.min) { 126 | //if(scope.aDates[0] < conf.min) scope.aDates[0].setDate( conf.min.getDate() ); 127 | isReached.min = scope.aDates[0] < conf.min; 128 | } 129 | if (conf.max) { 130 | var oDate = scope.aDates[scope.aDates.length - 1]; 131 | //if(oDate > conf.max) oDate.setDate( conf.max.getDate() ); 132 | isReached.max = oDate > conf.max; 133 | } 134 | }; 135 | var init = function () { 136 | scope.j = scope.outsideModel && (scope.outsideModel instanceof Date) 137 | ? scope.outsideModel 138 | : new Date(); 139 | return refresh(); 140 | }; 141 | 142 | //TODO: optimize this method 143 | var isBefore = function (oDate1, oDate2) { 144 | if (scope.state == "decade") 145 | return oDate1.getFullYear() < oDate2.getFullYear(); 146 | 147 | if (scope.state == "year") { 148 | if (oDate1.getFullYear() == oDate2.getFullYear()) 149 | return oDate1.getMonth() < oDate2.getMonth(); 150 | else 151 | return oDate1.getFullYear() < oDate2.getFullYear(); 152 | } 153 | 154 | return oDate1 < oDate2; 155 | }; 156 | scope.isOff = function (oDate) { 157 | if (!conf.min && !conf.max) 158 | return false; 159 | if (conf.min && isBefore(oDate, conf.min)) 160 | return true; 161 | if (conf.max && isBefore(conf.max, oDate)) 162 | return true; 163 | }; 164 | scope.isActive = { 165 | year: function (oDate) { 166 | return oDate.getFullYear() == scope.j.getFullYear(); 167 | }, 168 | month: function (oDate) { 169 | return oDate.getMonth() == scope.j.getMonth(); 170 | }, 171 | date: function (oDate) { 172 | return oDate.getDate() == scope.j.getDate(); 173 | } 174 | }; 175 | scope.isToday = function (oDate) { 176 | return scope.isActive.date(oDate) 177 | && scope.isActive.month(oDate) 178 | && scope.isActive.year(oDate); 179 | }; 180 | 181 | scope.go = function (oDate) { 182 | if (scope.isOff(oDate)) return; 183 | 184 | if( isInput && scope.state == conf.minState && scope.isActive.month(oDate) ) { 185 | togglePicker(false); 186 | } 187 | 188 | var m = scope.j.getMonth(); 189 | 190 | scope.j = new Date(oDate); 191 | scope.outsideModel = scope.j; 192 | $timeout(function () { 193 | ngModel.$setViewValue(scope.j); 194 | }); 195 | if (conf.toggleState) scope.toggleState(1); 196 | 197 | if (m != scope.j.getMonth()) 198 | refresh(); 199 | }; 200 | scope.now = function () { 201 | scope.j = new Date(); 202 | refresh(); 203 | }; 204 | scope.next = function (delta) { 205 | delta = delta || 1; 206 | 207 | if (delta > 0) { 208 | if (isReached.max) return; 209 | } 210 | else { 211 | if (isReached.min) return; 212 | } 213 | 214 | var Y = scope.j.getFullYear(), 215 | m = scope.j.getMonth(), 216 | d = scope.j.getDate(); 217 | 218 | switch (scope.state) { 219 | case "decade": 220 | delta = delta * scope.aDates.length; 221 | case "year": 222 | scope.j.setFullYear(Y + delta, m, 15); 223 | adjustDate(d); 224 | break; 225 | case "month": 226 | scope.j.setMonth(m + delta, 15); 227 | adjustDate(d); 228 | break; 229 | case "week" : 230 | scope.j.setDate(d + (delta * 7)); 231 | break; 232 | } 233 | refresh(); 234 | }; 235 | scope.prev = function (delta) { 236 | // delta = (delta == undefined) ? 1 : Math.abs( delta ); 237 | return scope.next(-delta || -1); 238 | }; 239 | scope.toggleState = function (direction) { 240 | direction = direction || 1; 241 | 242 | if (scope.state == conf.maxState && direction == -1 || 243 | scope.state == conf.minState && direction == 1) { 244 | return; 245 | } 246 | scope.state = scope.aStates[scope.aStates.indexOf(scope.state) + direction]; 247 | refresh(); 248 | }; 249 | 250 | scope.mondayStart = conf.mondayStart; 251 | scope.textToday = conf.textToday; 252 | 253 | scope.aStates = ["decade", "year", "month"]; 254 | scope.state = conf.initState; 255 | 256 | //TODO: this(together with rmInclude directive below) is a quick implementation, maybe there is a better idea 257 | scope.activeDateTpl = { 258 | decade: "{{aDates[0].getFullYear()}} - {{aDates[aDates.length-1].getFullYear()}}", 259 | year: "{{j.getFullYear()}}", 260 | month: "{{j | date: 'MMMM yyyy'}}", 261 | week: "{{ j | date: 'd MMMM yyyy' }}" 262 | }; 263 | 264 | init(); // generate initial state 265 | 266 | var offset = function (objElement) { 267 | var x = 0, y = 0; 268 | 269 | if (objElement.offsetParent) { 270 | do { 271 | x += objElement.offsetLeft; 272 | y += objElement.offsetTop; 273 | } while (objElement = objElement.offsetParent); 274 | } 275 | return {top: y, left: x}; 276 | }; 277 | var togglePicker = function (toggle) { 278 | overlay.css("display", toggle ? "block" : "none"); 279 | calendar.css("display", toggle ? "block" : "none"); 280 | }; 281 | var adjustPos = function (pos, el) { 282 | var scrollX = window.scrollX, 283 | scrollY = window.scrollY, 284 | innerWidth = window.innerWidth, 285 | innerHeight = window.innerHeight; 286 | 287 | if (window.innerWidth < 481) { 288 | return {top: scrollY, left: 0}; 289 | } 290 | 291 | var marginBottom = scrollY + innerHeight - pos.top - el.clientHeight, 292 | marginRight = scrollX + innerWidth - pos.left - el.clientWidth; 293 | 294 | if (marginBottom < 0) pos.top += marginBottom; 295 | if (pos.top < scrollY) pos.top = scrollY; 296 | if (marginRight < 0) pos.left += marginRight; 297 | if (pos.left < 0) pos.left = 0; 298 | 299 | return pos; 300 | }; 301 | 302 | if (isInput) { 303 | ngModel.$parsers.push(function (sDate) { 304 | var d = new Date(sDate); 305 | if (!isNaN(d.valueOf())) { 306 | scope.j = d; 307 | refresh(); 308 | } 309 | return d; 310 | }); 311 | ngModel.$formatters.push(function (oDate) { 312 | return $filter('date')(oDate, conf.format); 313 | }); 314 | 315 | var overlay = angular.element(''); 316 | overlay.on('click', function () { 317 | togglePicker(false); 318 | }); 319 | $document.find('body').eq(0).append(overlay); 320 | overlay.after($compile(TEMPLATE)(scope)); 321 | var calendar = overlay.next(); 322 | calendar.css({display: "none"}); 323 | calendar.addClass('it-is-input'); 324 | 325 | element.on('click', function () { 326 | 327 | if (window.innerWidth < 481) element[0].blur(); 328 | var pos = offset(element[0]); 329 | pos.top += element[0].offsetHeight + 1; 330 | 331 | calendar.css({top: pos.top + "px", left: pos.left + "px", display: "block"}); 332 | togglePicker(true); 333 | pos = adjustPos(pos, calendar[0]); 334 | calendar.css({top: pos.top + "px", left: pos.left + "px"}); 335 | }); 336 | 337 | $document.on('keydown', function (e) { 338 | if ([9, 13, 27].indexOf(e.keyCode) >= 0) togglePicker(false); 339 | }); 340 | } 341 | else { 342 | element.append($compile(TEMPLATE)(scope)); 343 | } 344 | }; 345 | 346 | //TODO: template may need optimization :) 347 | var TEMPLATE = 348 | '
' + 349 | '' + 356 | '
' + 357 | '
' + 358 | 359 | '' + 366 | 367 | '' + 374 | 375 | ''; 385 | 386 | return { 387 | require: 'ngModel', 388 | scope: { 389 | outsideModel: '=ngModel', /* active date */ 390 | rmConfig: "=rmConfig" 391 | }, 392 | link: link 393 | } 394 | }]); 395 | 396 | Module.directive('rmInclude', ['$compile', function ($compile) { 397 | 398 | var link = function (scope, element, attrs) { 399 | scope.$watch( 400 | function (scope) { 401 | return scope.$eval(attrs.rmInclude); 402 | } 403 | , function (value) { 404 | element.html(value); 405 | $compile(element.contents())(scope); 406 | }); 407 | }; 408 | 409 | return { 410 | restrict: "A", 411 | link: link 412 | }; 413 | }]); 414 | 415 | 416 | }()); -------------------------------------------------------------------------------- /dist/rm-datepicker.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * RM-DATEPICKER v1.0.0 3 | * http://rubymage.com 4 | * 5 | * Copyright 2015 Sergiu Ghenciu, RUBYMAGE 6 | * Released under the MIT license 7 | * https://github.com/RUBYMAGE/angular-datepicker/blob/master/LICENSE 8 | */@import url(https://fonts.googleapis.com/icon?family=Material+Icons);.rm-datepicker [class*=mi_]{speak:none;line-height:inherit;font-family:"Material Icons";font-style:normal;font-weight:400;font-size:inherit;font-variant:normal;text-rendering:auto;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.rm-datepicker [class*=mi_]:before{display:inline-block;speak:none;text-decoration:inherit}.rm-datepicker .mi_arrow_back:before{content:"\e5C4"}.rm-datepicker .mi_keyboard_arrow_up:before{content:"\e316"}.rm-datepicker .mi_keyboard_arrow_down:before{content:"\e313"}.rm-datepicker .mi_close:before{content:"\e5CD"}.rm-datepicker [ng-cloak]{display:none!important}.rm-datepicker .nav:after,.rm-datepicker:after{content:'';display:block;clear:both}.rm-datepicker{background-color:#fff;line-height:1.5;font-family:arial,sans-serif;font-weight:400;color:rgba(0,0,0,.87);box-sizing:border-box}.rm-datepicker *,.rm-datepicker :after,.rm-datepicker :before{box-sizing:inherit}.rm-datepicker a{color:rgba(0,0,0,.87);text-decoration:none;-webkit-tap-highlight-color:transparent}.rm-overlay{z-index:999980;position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,.3)}.rm-datepicker.it-is-input{position:absolute;box-shadow:0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);z-index:999999;min-width:360px}@media only screen and (max-width:480px){.rm-datepicker.it-is-input{left:0;right:0;margin:0 auto;width:100%;min-width:0;max-width:360px}}@media only screen and (min-width:0){.rm-datepicker{font-size:14px}}@media only screen and (min-width:992px){.rm-datepicker{font-size:14.5px}}@media only screen and (min-width:1200px){.rm-datepicker{font-size:15px}}.rm-datepicker .waves-effect .waves-ripple{z-index:1;background:rgba(255,255,255,.4);background:radial-gradient(rgba(255,255,255,.2) 0,rgba(255,255,255,.3) 40%,rgba(255,255,255,.4) 50%,rgba(255,255,255,.5) 60%,rgba(255,255,255,0) 70%)}.rm-datepicker .body .waves-effect .waves-ripple{background:rgba(0,150,136,.7)}.waves-effect.waves-light .waves-ripple{background:rgba(255,255,255,.45)}.waves-effect.waves-red .waves-ripple{background:rgba(244,67,54,.7)}.waves-effect.waves-yellow .waves-ripple{background:rgba(255,235,59,.7)}.waves-effect.waves-orange .waves-ripple{background:rgba(255,152,0,.7)}.waves-effect.waves-purple .waves-ripple{background:rgba(156,39,176,.7)}.waves-effect.waves-green .waves-ripple{background:rgba(76,175,80,.7)}.waves-effect.waves-teal .waves-ripple{background:rgba(0,150,136,.7)}.rm-datepicker .nav{background-color:#00bfa5;width:100%;position:relative}.rm-datepicker .nav a{height:50px;line-height:50px;cursor:pointer;font-size:1em;color:#fff;padding:0 10px;display:inline-block;text-align:center;transition:background-color .3s;float:left}.rm-datepicker .nav .today{float:right;text-align:right}.rm-datepicker .nav a *{height:inherit;line-height:inherit}.rm-datepicker .nav a:hover{background-color:rgba(0,0,0,.1)}.rm-datepicker .nav i{font-size:1.1em}.rm-datepicker .nav .adjacent i{font-size:1.4em}.rm-datepicker .nav>a:first-child{width:60px;text-align:left}.rm-datepicker .nav .back{width:50%;max-width:170px;margin-left:-60px;padding-left:32px}.rm-datepicker .nav .adjacent{width:15%;max-width:50px}.rm-datepicker .nav .today{width:20%}@media only screen and (min-width:339px){.rm-datepicker .nav a:first-child,.rm-datepicker .nav a:last-child{padding:0 15px}.rm-datepicker .nav .back{padding:0 0 0 40px}}@media only screen and (max-width:315px){.rm-datepicker .nav a{height:35px;line-height:35px;padding:0 15px}.rm-datepicker .nav .back{width:100%;max-width:100%;text-align:left;padding-left:45px}.rm-datepicker .nav .adjacent,.rm-datepicker .nav .today{width:33.3333%;max-width:100%;text-align:center}}.rm-datepicker .square>*{float:left;margin:0;position:relative}.rm-datepicker .square a{z-index:9;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;cursor:pointer}.rm-datepicker .square a:before{content:"";display:inline-block;vertical-align:middle;height:100%}.rm-datepicker .square a *{vertical-align:middle}.rm-datepicker .date a{color:rgba(0,0,0,.87);font-size:.8em;font-weight:400;cursor:pointer;border-top:1px solid #eee;border-left:1px solid #eee}.rm-datepicker .date .j a,.rm-datepicker .date a.j,.rm-datepicker .date a:hover{font-weight:600}.rm-datepicker .date .j a{background-color:rgba(0,191,165,.5)}.rm-datepicker .date .out a{background-color:#E4E3E3!important;opacity:.3;border-color:#BBB}.rm-datepicker .date .off a{background-color:#F2F2F2!important;opacity:.05;border-color:#555;cursor:not-allowed}.rm-datepicker .decade.date>:nth-child(-n+4) a,.rm-datepicker .month.date>:nth-child(-n+7) a,.rm-datepicker .year.date>:nth-child(-n+4) a{border-top-color:transparent}.rm-datepicker .date>:nth-child(1) a,.rm-datepicker .decade.date>:nth-child(4n+1) a,.rm-datepicker .month.date>:nth-child(7n+1) a,.rm-datepicker .year.date>:nth-child(4n+1) a{border-left-color:transparent}.rm-datepicker .day{background-color:#eee}.rm-datepicker .day>*{display:inline-block;width:14.2857142857%;text-align:center;padding:4px 0;color:rgba(0,0,0,.87);font-size:.8em}.rm-datepicker .sunSat+*>:nth-child(7),.rm-datepicker .sunSat+*>:nth-child(7n) a,.rm-datepicker .sunSat>:nth-child(7),.rm-datepicker .sunSat>:nth-child(7n) a,.rm-datepicker.mondayStart .sunSat+*>:nth-child(6),.rm-datepicker.mondayStart .sunSat+*>:nth-child(7n+6) a,.rm-datepicker.mondayStart .sunSat>:nth-child(6),.rm-datepicker.mondayStart .sunSat>:nth-child(7n+6) a{color:#039BE5!important}.rm-datepicker .sunSat+*>:nth-child(1),.rm-datepicker .sunSat+*>:nth-child(7n+1) a,.rm-datepicker .sunSat>:nth-child(1),.rm-datepicker .sunSat>:nth-child(7n+1) a,.rm-datepicker.mondayStart .sunSat+*>:nth-child(7),.rm-datepicker.mondayStart .sunSat+*>:nth-child(7n) a,.rm-datepicker.mondayStart .sunSat>:nth-child(7),.rm-datepicker.mondayStart .sunSat>:nth-child(7n) a{color:#f44336!important}.rm-datepicker.mondayStart .sunSat+*>:nth-child(1),.rm-datepicker.mondayStart .sunSat+*>:nth-child(7n+1) a,.rm-datepicker.mondayStart .sunSat>:nth-child(1),.rm-datepicker.mondayStart .sunSat>:nth-child(7n+1) a{color:rgba(0,0,0,.87)!important}.rm-datepicker .decade>*,.rm-datepicker .year>*{width:25%;padding:25% 0 0}.rm-datepicker .month>*{width:14.2857142857%;padding:14.2857142857% 0 0} -------------------------------------------------------------------------------- /dist/rm-datepicker.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * RM-DATEPICKER v1.0.0 3 | * http://rubymage.com 4 | * 5 | * Copyright 2015 Sergiu Ghenciu, RUBYMAGE 6 | * Released under the MIT license 7 | * https://github.com/RUBYMAGE/angular-datepicker/blob/master/LICENSE 8 | */ 9 | !function(){var t=angular.module("rmDatepicker",[]);t.constant("rmDatepickerConfig",{mondayStart:!1,textToday:"Today",initState:"month",maxState:"decade",minState:"month",toggleState:!0,decadeSize:12,monthSize:42,min:null,max:null,format:"yyyy-MM-dd"}),t.directive("rmDatepicker",["rmDatepickerConfig","$compile","$filter","$document","$timeout",function(t,e,a,n,i){var o=function(o,s,c,l){var d=angular.copy(t);if(o.rmConfig)for(var f in d)d.hasOwnProperty(f)&&void 0!=o.rmConfig[f]&&(d[f]=o.rmConfig[f]);d.min&&d.min.setHours(0,0,0,0),d.max&&d.max.setHours(23,59,59,999);var u="INPUT"==s[0].tagName.toUpperCase(),g={min:!1,max:!1},m=function(t,e){return new Date(t,e+1,0).getDate()},v=function(t){var t=parseInt(t,10);if(!isNaN(t)){var e=m(o.j.getFullYear(),o.j.getMonth());t<1&&(t=1),t>e&&(t=e),o.j.setDate(t)}},p={decade:function(t){var e,a=t.getFullYear(),n=t.getMonth(),i=t.getDate(),o=0,r=d.decadeSize||12,s=new Array(r);for(a=Math.floor(a/r)*r;oe&&(i=e),s[o]=new Date(a,n,i,3,0,1,0);return s},year:function(t){for(var e,a=t.getFullYear(),n=0,i=t.getDate(),o=[];n<12;n++)e=m(a,n),i>e&&(i=e),o.push(new Date(a,n,i,3,0,1,0));return o},month:function(t){var e,a=t.getFullYear(),n=t.getMonth(),i=new Date(a,n,1,3,0,1,0),r=i.getDay()||7;return o.mondayStart&&(r=r-1||7),e="auto"==d.monthSize?r+m(a,n)<35?35:42:d.monthSize,i.setDate(-r+1),p.dates(i,e)},dates:function(t,e){for(var a=new Array(e),n=new Date(t),i=0;id.max}},D=function(){return o.j=o.outsideModel&&o.outsideModel instanceof Date?o.outsideModel:new Date,y()},h=function(t,e){return"decade"==o.state?t.getFullYear()0){if(g.max)return}else if(g.min)return;var e=o.j.getFullYear(),a=o.j.getMonth(),n=o.j.getDate();switch(o.state){case"decade":t*=o.aDates.length;case"year":o.j.setFullYear(e+t,a,15),v(n);break;case"month":o.j.setMonth(a+t,15),v(n);break;case"week":o.j.setDate(n+7*t)}y()},o.prev=function(t){return o.next(-t||-1)},o.toggleState=function(t){t=t||1,o.state==d.maxState&&t==-1||o.state==d.minState&&1==t||(o.state=o.aStates[o.aStates.indexOf(o.state)+t],y())},o.mondayStart=d.mondayStart,o.textToday=d.textToday,o.aStates=["decade","year","month"],o.state=d.initState,o.activeDateTpl={decade:"{{aDates[0].getFullYear()}} - {{aDates[aDates.length-1].getFullYear()}}",year:"{{j.getFullYear()}}",month:"{{j | date: 'MMMM yyyy'}}",week:"{{ j | date: 'd MMMM yyyy' }}"},D();var w=function(t){var e=0,a=0;if(t.offsetParent)do e+=t.offsetLeft,a+=t.offsetTop;while(t=t.offsetParent);return{top:a,left:e}},M=function(t){j.css("display",t?"block":"none"),k.css("display",t?"block":"none")},x=function(t,e){var a=window.scrollX,n=window.scrollY,i=window.innerWidth,o=window.innerHeight;if(window.innerWidth<481)return{top:n,left:0};var r=n+o-t.top-e.clientHeight,s=a+i-t.left-e.clientWidth;return r<0&&(t.top+=r),t.top