├── .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 | '
' +
350 | '
' +
351 | '
' +
352 | '
' +
353 | '
' +
354 | '
{{textToday}} ' +
355 | '
' +
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');j.on("click",function(){M(!1)}),n.find("body").eq(0).append(j),j.after(e(r)(o));var k=j.next();k.css({display:"none"}),k.addClass("it-is-input"),s.on("click",function(){window.innerWidth<481&&s[0].blur();var t=w(s[0]);t.top+=s[0].offsetHeight+1,k.css({top:t.top+"px",left:t.left+"px",display:"block"}),M(!0),t=x(t,k[0]),k.css({top:t.top+"px",left:t.left+"px"})}),n.on("keydown",function(t){[9,13,27].indexOf(t.keyCode)>=0&&M(!1)})}else s.append(e(r)(o))},r='';return{require:"ngModel",scope:{outsideModel:"=ngModel",rmConfig:"=rmConfig"},link:o}}]),t.directive("rmInclude",["$compile",function(t){var e=function(e,a,n){e.$watch(function(t){return t.$eval(n.rmInclude)},function(n){a.html(n),t(a.contents())(e)})};return{restrict:"A",link:e}}])}();
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | //load plugins
2 | var gulp = require('gulp'),
3 | compass = require('gulp-compass'),
4 | autoprefixer = require('gulp-autoprefixer'),
5 | minifycss = require('gulp-minify-css'),
6 | uglify = require('gulp-uglify'),
7 | rename = require('gulp-rename'),
8 | concat = require('gulp-concat'),
9 | notify = require('gulp-notify'),
10 | livereload = require('gulp-livereload'),
11 | plumber = require('gulp-plumber'),
12 | path = require('path');
13 |
14 | //the title and icon that will be used for the Grunt notifications
15 | var notifyInfo = {
16 | title: 'Gulp',
17 | icon: path.join(__dirname, 'gulp.png')
18 | };
19 |
20 | //error notification settings for plumber
21 | var plumberErrorHandler = {
22 | errorHandler: notify.onError({
23 | title: notifyInfo.title,
24 | icon: notifyInfo.icon,
25 | message: "Error: <%= error.message %>"
26 | })
27 | };
28 |
29 | //styles
30 | gulp.task('styles', function () {
31 | return gulp.src(['src/sass/rm-datepicker.scss'])
32 | .pipe(plumber(plumberErrorHandler))
33 | .pipe(compass({
34 | css: 'dist',
35 | sass: 'src/sass',
36 | image: 'dist'
37 | }))
38 | .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 7', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
39 | .pipe(gulp.dest('dist'))
40 | .pipe(rename({suffix: '.min'}))
41 | .pipe(minifycss({processImport: false}))
42 | .pipe(gulp.dest('dist'));
43 | });
44 |
45 | //scripts
46 | gulp.task('scripts', function () {
47 | return gulp.src('src/scripts/rm-datepicker.js')
48 | .pipe(plumber(plumberErrorHandler))
49 | //.pipe(concat('main.js'))
50 | .pipe(gulp.dest('dist'))
51 | .pipe(rename({suffix: '.min'}))
52 | .pipe(uglify({preserveComments: "license"}))
53 | .pipe(gulp.dest('dist'));
54 | });
55 |
56 | //watch
57 | gulp.task('live', function () {
58 | livereload.listen();
59 |
60 | //watch .scss files
61 | gulp.watch('src/sass/**/*.scss', ['styles']);
62 |
63 | //watch .js files
64 | gulp.watch('src/scripts/**/*.js', ['scripts']);
65 |
66 | //reload when a template file, the minified css, or the minified js file changes
67 | gulp.watch('index.html', 'dist/rm-datepicker.min.css', 'dist/rm-datepicker.min.js', function (event) {
68 | gulp.src(event.path)
69 | .pipe(plumber())
70 | .pipe(livereload())
71 | .pipe(notify({
72 | title: notifyInfo.title,
73 | icon: notifyInfo.icon,
74 | message: event.path.replace(__dirname, '').replace(/\\/g, '/') + ' was ' + event.type + ' and reloaded'
75 | })
76 | );
77 | });
78 | });
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RUBYMAGE Datepicker
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rm-datepicker",
3 | "version": "1.0.1",
4 | "description": "Clean and well optimized datepicker directive for angular",
5 | "main": "dist/rm-datepicker.js",
6 | "author": {
7 | "name": "Sergiu Ghenciu",
8 | "email": "6180rm@gmail.com",
9 | "url": "http://rubymage.com"
10 | },
11 | "licenses": [
12 | {
13 | "type": "MIT",
14 | "url": "https://github.com/RUBYMAGE/angular-datepicker/blob/master/LICENSE"
15 | }
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/RUBYMAGE/angular-datepicker.git"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/RUBYMAGE/angular-datepicker/issues"
23 | },
24 | "homepage": "https://github.com/RUBYMAGE/angular-datepicker#readme",
25 | "dependencies": {
26 | "angular": "1.x"
27 | },
28 | "devDependencies": {
29 | "gulp": "^3.9.0",
30 | "gulp-autoprefixer": "^3.1.0",
31 | "gulp-compass": "^2.1.0",
32 | "gulp-concat": "^2.6.0",
33 | "gulp-livereload": "^3.8.1",
34 | "gulp-minify-css": "^1.2.1",
35 | "gulp-notify": "^2.2.0",
36 | "gulp-plumber": "^1.0.1",
37 | "gulp-rename": "^1.2.2",
38 | "gulp-uglify": "^1.5.1"
39 | },
40 | "engines": {
41 | "node": "5.0.0",
42 | "npm": "3.3.6"
43 | },
44 | "scripts": {
45 | "test": "echo \"Error: no test specified\" && exit 1"
46 | },
47 | "keywords": [
48 | "angular.js datepicker",
49 | "angularjs datepicker",
50 | "angular datepicker",
51 | "angular-datepicker",
52 | "pickadate",
53 | "datepicker",
54 | "timepicker",
55 | "pikaday",
56 | "datepicker directive",
57 | "datepicker add-on",
58 | "simple datepicker",
59 | "clean datepicker",
60 | "fluid datepicker",
61 | "customizable datepicker",
62 | "inline datepicker",
63 | "dropdown datepicker",
64 | "modal datepicker",
65 | "lightweight datepicker",
66 | "datepicker component"
67 | ],
68 | "tags": [
69 | "angular.js datepicker",
70 | "angularjs datepicker",
71 | "angular datepicker",
72 | "angular-datepicker",
73 | "pickadate",
74 | "datepicker",
75 | "timepicker",
76 | "pikaday",
77 | "datepicker directive",
78 | "datepicker add-on",
79 | "simple datepicker",
80 | "clean datepicker",
81 | "fluid datepicker",
82 | "customizable datepicker",
83 | "inline datepicker",
84 | "dropdown datepicker",
85 | "modal datepicker",
86 | "lightweight datepicker",
87 | "datepicker component"
88 | ],
89 | "contributors": [
90 | {
91 | "name": "Sergiu Ghenciu",
92 | "email": "1024gs@gmail.com",
93 | "url": "http://rubymage.com"
94 | }
95 | ]
96 | }
97 |
--------------------------------------------------------------------------------
/src/sass/_framework.scss:
--------------------------------------------------------------------------------
1 |
2 | // ==============================================
3 | // SASS / Compass Framework
4 | // ==============================================
5 |
6 | // ----------------------------------------------
7 | // Compass
8 |
9 | @import "compass";
10 | @import "compass/css3";
11 |
12 | // ----------------------------------------------
13 | // Vars
14 |
15 | @import "var";
--------------------------------------------------------------------------------
/src/sass/_var.scss:
--------------------------------------------------------------------------------
1 |
2 | // ----------------------------------------------
3 | // Colors
4 |
5 | $c-defaultText: rgba(0, 0, 0, 0.87);
6 | $c-saturday: #039BE5;
7 | $c-sunday: #f44336;
8 | $bg-activeDate: rgba(0,191,165,0.5);
9 |
10 | $bg-nav: #00bfa5;
11 | $c-nav: #fff;
12 |
--------------------------------------------------------------------------------
/src/sass/components/_base.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Base
3 | **********************************/
4 |
5 | .rm-datepicker [ng-cloak] {
6 | display: none !important;
7 | }
8 |
9 | .rm-datepicker {
10 | background-color: #fff;
11 | line-height: 1.5;
12 | font-family: arial, sans-serif;
13 | font-weight: normal;
14 | color: $c-defaultText;
15 | box-sizing: border-box;
16 | *, *:before, *:after {
17 | box-sizing: inherit;
18 | }
19 | a {
20 | color: $c-defaultText;
21 | text-decoration: none;
22 | -webkit-tap-highlight-color: transparent;
23 | }
24 |
25 | &:after {
26 | content: '';
27 | display: block;
28 | clear: both;
29 | }
30 | }
31 |
32 | .rm-overlay {
33 | z-index: 999980;
34 | position: fixed;
35 | left: 0;
36 | right: 0;
37 | top: 0;
38 | bottom: 0;
39 | background: rgba(0, 0, 0, 0.3);
40 | }
41 |
42 | .rm-datepicker.it-is-input {
43 | position: absolute;
44 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
45 | z-index: 999999;
46 | min-width: 360px;
47 | }
48 |
49 | @media only screen and (max-width: 480px) {
50 | .rm-datepicker.it-is-input {
51 | left: 0;
52 | right: 0;
53 | margin: 0 auto;
54 | width: 100%;
55 | min-width: 0;
56 | max-width: 360px;
57 | }
58 | }
59 |
60 | @media only screen and (min-width: 0) {
61 | .rm-datepicker {
62 | font-size: 14px;
63 | }
64 | }
65 |
66 | @media only screen and (min-width: 992px) {
67 | .rm-datepicker {
68 | font-size: 14.5px;
69 | }
70 | }
71 |
72 | @media only screen and (min-width: 1200px) {
73 | .rm-datepicker {
74 | font-size: 15px;
75 | }
76 | }
77 |
78 | .rm-datepicker .waves-effect .waves-ripple {
79 | z-index: 1;
80 | background: rgba(255, 255, 255, 0.4);
81 | background: -webkit-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%);
82 | background: -o-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%);
83 | background: -moz-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%);
84 | 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%);
85 | }
86 |
87 | .rm-datepicker .body .waves-effect .waves-ripple {
88 | background: rgba(0, 150, 136, 0.7);
89 | }
90 |
91 | .waves-effect.waves-light .waves-ripple {
92 | background: rgba(255, 255, 255, 0.45);
93 | }
94 |
95 | .waves-effect.waves-red .waves-ripple {
96 | background: rgba(244, 67, 54, 0.7);
97 | }
98 |
99 | .waves-effect.waves-yellow .waves-ripple {
100 | background: rgba(255, 235, 59, 0.7);
101 | }
102 |
103 | .waves-effect.waves-orange .waves-ripple {
104 | background: rgba(255, 152, 0, 0.7);
105 | }
106 |
107 | .waves-effect.waves-purple .waves-ripple {
108 | background: rgba(156, 39, 176, 0.7);
109 | }
110 |
111 | .waves-effect.waves-green .waves-ripple {
112 | background: rgba(76, 175, 80, 0.7);
113 | }
114 |
115 | .waves-effect.waves-teal .waves-ripple {
116 | background: rgba(0, 150, 136, 0.7);
117 | }
118 |
119 |
--------------------------------------------------------------------------------
/src/sass/components/_date.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Date
3 | **********************************/
4 |
5 | .rm-datepicker .date a {
6 | color: $c-defaultText;
7 | font-size: 0.8em;
8 | font-weight: 400;
9 | cursor: pointer;
10 | border-top: 1px solid #eee;
11 | border-left: 1px solid #eee;
12 | }
13 |
14 | .rm-datepicker .date .j a,
15 | .rm-datepicker .date a.j,
16 | .rm-datepicker .date a:hover {
17 | font-weight: 600;
18 | }
19 |
20 | /* active date */
21 | .rm-datepicker .date .j a {
22 | background-color: $bg-activeDate;
23 | }
24 |
25 | .rm-datepicker .date .out a {
26 | background-color: #E4E3E3 !important;
27 | opacity: 0.3;
28 | border-color: #BBB;
29 | }
30 |
31 | .rm-datepicker .date .off a {
32 | background-color: #F2F2F2 !important;
33 | opacity: 0.05;
34 | border-color: #555;
35 | cursor: not-allowed;
36 | }
37 |
38 | .rm-datepicker .decade.date > *:nth-child(-n+4) a,
39 | .rm-datepicker .year.date > *:nth-child(-n+4) a,
40 | .rm-datepicker .month.date > *:nth-child(-n+7) a {
41 | border-top-color: transparent;
42 | }
43 |
44 | .rm-datepicker .date > *:nth-child(1) a,
45 | .rm-datepicker .decade.date > *:nth-child(4n+1) a,
46 | .rm-datepicker .year.date > *:nth-child(4n+1) a,
47 | .rm-datepicker .month.date > *:nth-child(7n+1) a {
48 | border-left-color: transparent;
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/src/sass/components/_day.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Day of week
3 | **********************************/
4 |
5 | .rm-datepicker .day {
6 | background-color: #eee;
7 | }
8 |
9 | .rm-datepicker .day > * {
10 | display: inline-block;
11 | width: 14.28571428570%;
12 | text-align: center;
13 | padding: 4px 0;
14 | color: $c-defaultText;
15 | font-size: 0.8em;
16 | }
--------------------------------------------------------------------------------
/src/sass/components/_mi_icons.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Material Design Icons https://google.github.io/material-design-icons/
3 | **********************************/
4 | @import url(https://fonts.googleapis.com/icon?family=Material+Icons);
5 |
6 | .rm-datepicker {
7 | [class*="mi_"] {
8 | speak: none;
9 | line-height: inherit;
10 | font-family: "Material Icons";
11 | font-style: normal;
12 | font-weight: 400;
13 | font-size: inherit;
14 | font-variant: normal;
15 | text-rendering: auto;
16 | text-transform: none;
17 | letter-spacing: normal;
18 | word-wrap: normal;
19 | white-space: nowrap;
20 | direction: ltr;
21 | display: inline-block;
22 | -webkit-font-smoothing: antialiased;
23 | -moz-osx-font-smoothing: grayscale;
24 | }
25 |
26 | [class*="mi_"]:before {
27 | display: inline-block;
28 | speak: none;
29 | text-decoration: inherit;
30 | }
31 |
32 | .mi_arrow_back:before {
33 | content: "\e5C4";
34 | }
35 | .mi_keyboard_arrow_up:before {
36 | content: "\e316";
37 | }
38 | .mi_keyboard_arrow_down:before {
39 | content: "\e313";
40 | }
41 | .mi_close:before {
42 | content: "\e5CD";
43 | }
44 | }
--------------------------------------------------------------------------------
/src/sass/components/_month.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Month
3 | **********************************/
4 |
5 | .rm-datepicker .month > * {
6 | width: 14.28571428570%;
7 | padding: 14.28571428570% 0 0;
8 | }
9 |
--------------------------------------------------------------------------------
/src/sass/components/_nav.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Nav
3 | **********************************/
4 |
5 | .rm-datepicker .nav {
6 | background-color: #00bfa5;
7 | width: 100%;
8 | position: relative;
9 | }
10 |
11 | .rm-datepicker .nav:after {
12 | content: '';
13 | display: block;
14 | clear: both;
15 | }
16 |
17 | .rm-datepicker .nav a {
18 | height: 50px;
19 | line-height: 50px;
20 | cursor: pointer;
21 | font-size: 1em;
22 | color: #fff;
23 | padding: 0 10px;
24 | display: inline-block;
25 | text-align: center;
26 | transition: background-color 0.3s;
27 | float: left;
28 | }
29 |
30 | .rm-datepicker .nav .today {
31 | float: right;
32 | text-align: right;
33 | }
34 |
35 | .rm-datepicker .nav a * {
36 | height: inherit;
37 | line-height: inherit;
38 | }
39 |
40 | .rm-datepicker .nav a:hover {
41 | background-color: rgba(0, 0, 0, 0.1);
42 | }
43 |
44 | .rm-datepicker .nav i {
45 | font-size: 1.1em;
46 | }
47 |
48 | .rm-datepicker .nav .adjacent i {
49 | font-size: 1.4em;
50 | }
51 |
52 | .rm-datepicker .nav > a:first-child {
53 | width: 60px;
54 | text-align: left;
55 | }
56 |
57 | .rm-datepicker .nav .back {
58 | width: 50%;
59 | max-width: 170px;
60 | margin-left: -60px;
61 | padding-left: 32px;
62 | }
63 |
64 | .rm-datepicker .nav .adjacent {
65 | width: 15%;
66 | max-width: 50px;
67 | }
68 |
69 | .rm-datepicker .nav .today {
70 | width: 20%;
71 | }
72 |
73 | @media only screen and (min-width: 339px) {
74 | .rm-datepicker .nav a:first-child,
75 | .rm-datepicker .nav a:last-child {
76 | padding: 0 15px;
77 | }
78 | .rm-datepicker .nav .back {
79 | padding: 0 0 0 40px;
80 | }
81 | }
82 |
83 | /* All the styles below can be deleted (it is for very very old devices)*/
84 | @media only screen and (max-width: 315px) {
85 | .rm-datepicker .nav a {
86 | height: 35px;
87 | line-height: 35px;
88 | padding: 0 15px;
89 | }
90 | .rm-datepicker .nav .back {
91 | width: 100%;
92 | max-width: 100%;
93 | text-align: left;
94 | padding-left: 45px;
95 | }
96 | .rm-datepicker .nav .adjacent,
97 | .rm-datepicker .nav .today {
98 | width: 33.3333%;
99 | max-width: 100%;
100 | text-align: center;
101 | }
102 | }
--------------------------------------------------------------------------------
/src/sass/components/_square.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Square
3 | **********************************/
4 | .rm-datepicker .square > * {
5 | float: left;
6 | margin: 0;
7 | position: relative;
8 | }
9 |
10 | .rm-datepicker .square a {
11 | z-index: 9;
12 | position: absolute;
13 | top: 0;
14 | left: 0;
15 | width: 100%;
16 | height: 100%;
17 | text-align: center;
18 | cursor: pointer;
19 | }
20 |
21 | .rm-datepicker .square a:before {
22 | content: "";
23 | display: inline-block;
24 | vertical-align: middle;
25 | height: 100%;
26 | }
27 |
28 | .rm-datepicker .square a * {
29 | vertical-align: middle;
30 | }
31 |
--------------------------------------------------------------------------------
/src/sass/components/_sunSat.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Sunday Saturday
3 | **********************************/
4 |
5 | /* saturday */
6 | .rm-datepicker .sunSat > *:nth-child(7),
7 | .rm-datepicker .sunSat > *:nth-child(7n) a,
8 | .rm-datepicker.mondayStart .sunSat > *:nth-child(6),
9 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n+6) a,
10 | .rm-datepicker .sunSat + * > *:nth-child(7),
11 | .rm-datepicker .sunSat + * > *:nth-child(7n) a,
12 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(6),
13 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n+6) a {
14 | color: $c-saturday !important;
15 | }
16 |
17 | /* sunday */
18 | .rm-datepicker .sunSat > *:nth-child(1),
19 | .rm-datepicker .sunSat > *:nth-child(7n+1) a,
20 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7),
21 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n) a,
22 | .rm-datepicker .sunSat + * > *:nth-child(1),
23 | .rm-datepicker .sunSat + * > *:nth-child(7n+1) a,
24 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7),
25 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n) a {
26 | color: $c-sunday !important;
27 | }
28 |
29 | /* reset saturday */
30 | .rm-datepicker.mondayStart .sunSat > *:nth-child(1),
31 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n+1) a,
32 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(1),
33 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n+1) a {
34 | color: $c-defaultText !important;
35 | }
36 |
--------------------------------------------------------------------------------
/src/sass/components/_year.scss:
--------------------------------------------------------------------------------
1 | /**********************************
2 | Year and decade
3 | **********************************/
4 |
5 | .rm-datepicker .decade > *,
6 | .rm-datepicker .year > * {
7 | width: 25%;
8 | padding: 25% 0 0;
9 | }
10 |
--------------------------------------------------------------------------------
/src/sass/rm-datepicker.scss:
--------------------------------------------------------------------------------
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 | @import "framework";
11 |
12 | @import "components/_mi_icons";
13 | @import "components/_base";
14 | @import "components/_nav";
15 | @import "components/_square";
16 | @import "components/_date";
17 | @import "components/_day";
18 | @import "components/_sunSat";
19 | @import "components/_year";
20 | @import "components/_month";
21 |
22 | //@import "themes/_theme1";
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/sass/themes/_theme1.scss:
--------------------------------------------------------------------------------
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 | /**********************************
11 | Theme1
12 | **********************************/
13 |
14 | /*
15 | * base
16 | */
17 | .rm-datepicker {
18 | font-family: "Roboto", sans-serif;
19 | color: rgba(0, 0, 0, 0.87);
20 | }
21 |
22 | .rm-datepicker a {
23 | color: rgba(0, 0, 0, 0.87);
24 | }
25 |
26 | /*
27 | * waves
28 | */
29 | .rm-datepicker .nav .waves-effect .waves-ripple {
30 | background: rgba(255, 255, 255, 0.4);
31 | }
32 |
33 | .rm-datepicker .body .waves-effect .waves-ripple {
34 | background: rgba(0, 150, 136, 0.7);
35 | }
36 |
37 | /*
38 | * nav
39 | */
40 | .rm-datepicker .nav {
41 | background-color: #00bfa5;
42 | }
43 |
44 | .rm-datepicker .nav a {
45 | color: #fff;
46 | }
47 |
48 | .rm-datepicker .nav a:hover {
49 | background-color: rgba(0, 0, 0, 0.1);
50 | }
51 |
52 | /*
53 | * date
54 | */
55 | .rm-datepicker .date a {
56 | font-weight: 400;
57 | }
58 |
59 | .rm-datepicker .date .j a,
60 | .rm-datepicker .date a.j,
61 | .rm-datepicker .date a:hover {
62 | font-weight: 700;
63 | }
64 |
65 | /* active date */
66 | .rm-datepicker .date .j a {
67 | background-color: rgba(0, 191, 165, 0.5);
68 | }
69 |
70 | /*
71 | * sunSat
72 | */
73 | /* saturday */
74 | .rm-datepicker .sunSat > *:nth-child(7),
75 | .rm-datepicker .sunSat > *:nth-child(7n) a,
76 | .rm-datepicker.mondayStart .sunSat > *:nth-child(6),
77 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n+6) a,
78 | .rm-datepicker .sunSat + * > *:nth-child(7),
79 | .rm-datepicker .sunSat + * > *:nth-child(7n) a,
80 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(6),
81 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n+6) a {
82 | color: #039BE5 !important;
83 | }
84 |
85 | /* sunday */
86 | .rm-datepicker .sunSat > *:nth-child(1),
87 | .rm-datepicker .sunSat > *:nth-child(7n+1) a,
88 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7),
89 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n) a,
90 | .rm-datepicker .sunSat + * > *:nth-child(1),
91 | .rm-datepicker .sunSat + * > *:nth-child(7n+1) a,
92 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7),
93 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n) a {
94 | color: #f44336 !important;
95 | }
96 |
97 | /* reset saturday */
98 | .rm-datepicker.mondayStart .sunSat > *:nth-child(1),
99 | .rm-datepicker.mondayStart .sunSat > *:nth-child(7n+1) a,
100 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(1),
101 | .rm-datepicker.mondayStart .sunSat + * > *:nth-child(7n+1) a {
102 | color: rgba(0, 0, 0, 0.87) !important;
103 | }
--------------------------------------------------------------------------------
/src/scripts/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 | '
' +
350 | '
' +
351 | '
' +
352 | '
' +
353 | '
' +
354 | '
{{textToday}} ' +
355 | '
' +
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 | }());
--------------------------------------------------------------------------------