Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Constrain dates #25

Closed
wants to merge 4 commits into from

1 participant

Damian Krzeminski
Damian Krzeminski
Owner

Add optional date restriction for calendar.min and max methods added to restrict dates. Invalid dates (dates outside of valid range) are rendered with 'invalid' class. Clicks on invalid dates do not generate change events.

pirxpilot added some commits
Damian Krzeminski pirxpilot add `test` target in Makefile
mocha as devel dependency
f0f30ac
Damian Krzeminski pirxpilot Options for jshint 4ce7cdc
Damian Krzeminski pirxpilot Implement dates restriction
Calendar has new `min` and `max` functions to define inclusive range of
valid dates.

Range checking is performed by DayRange class.
When rendering calendar dates ourside of the range are rendered with
`invalid` CSS class, so that can they be styled differently from valid
dates.

Days will not fire 'change' event for invalid (out-of-range) dates.
Calendar ignores attempts to select invalid date, although is allows to
show it.
7cf6cba
Damian Krzeminski pirxpilot Document new `min` and `max` methods 16b5f20
Damian Krzeminski
Owner

I'll submit another (cleaned up version) soon. @visionmedia could you just merge #22 and #23 - there should be nothing controversial about them.

Damian Krzeminski pirxpilot closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 17, 2013
  1. Damian Krzeminski

    add `test` target in Makefile

    pirxpilot authored
    mocha as devel dependency
Commits on Feb 18, 2013
  1. Damian Krzeminski

    Options for jshint

    pirxpilot authored
  2. Damian Krzeminski

    Implement dates restriction

    pirxpilot authored
    Calendar has new `min` and `max` functions to define inclusive range of
    valid dates.
    
    Range checking is performed by DayRange class.
    When rendering calendar dates ourside of the range are rendered with
    `invalid` CSS class, so that can they be styled differently from valid
    dates.
    
    Days will not fire 'change' event for invalid (out-of-range) dates.
    Calendar ignores attempts to select invalid date, although is allows to
    show it.
  3. Damian Krzeminski
This page is out of date. Refresh to see the latest.
16 .jshintrc
View
@@ -0,0 +1,16 @@
+{
+ "undef": "true",
+ "unused": "true",
+ "laxbreak": "true",
+ "laxcomma": "true",
+ "es5": "true",
+ "supernew": "false",
+ "globals": {
+ "console": false,
+ "describe": false,
+ "exports": false,
+ "it": false,
+ "module": false,
+ "require": false
+ }
+}
6 Makefile
View
@@ -17,4 +17,8 @@ lib/template.js: lib/template.html
clean:
rm -fr build components lib/template.js
-.PHONY: clean
+test:
+ @./node_modules/.bin/mocha \
+ --reporter spec
+
+.PHONY: clean test
8 Readme.md
View
@@ -56,6 +56,14 @@ cal.el.appendTo('body');
Show the next view (month).
+### Calendar#mix()
+
+ Define earliest valid date - calendar won't generate `change` events for dates before this one.
+
+### Calendar#max()
+
+ Define latest valid date - calendar won't generate `change` events for dates after this one.
+
## Themes
[Aurora](https://github.com/component/aurora-calendar):
1  component.json
View
@@ -25,6 +25,7 @@
"lib/utils.js",
"lib/template.js",
"lib/calendar.js",
+ "lib/dayrange.js",
"lib/days.js"
],
"license": "MIT"
11 example.html
View
@@ -35,6 +35,12 @@
font-size: 10px;
padding: 3px;
}
+
+ .calendar td a.invalid {
+ opacity: .2;
+ background-color: rgba(0, 0, 0, .2);
+ cursor: default;
+ }
</style>
</head>
<body>
@@ -71,6 +77,11 @@
var large = new Calendar;
large.addClass('large');
large.el.appendTo('body');
+
+ var restricted = new Calendar(new Date(2004, 6, 11))
+ .min(new Date(2004, 5, 12))
+ .max([2004, 7, 19]);
+ restricted.el.appendTo('body');
</script>
</body>
</html>
31 lib/calendar.js
View
@@ -77,8 +77,10 @@ Calendar.prototype.addClass = function(name){
*/
Calendar.prototype.select = function(date){
- this.selected = date;
- this.days.select(date);
+ if (this.days.validRange.valid(date)) {
+ this.selected = date;
+ this.days.select(date);
+ }
this.show(date);
return this;
};
@@ -98,6 +100,31 @@ Calendar.prototype.show = function(date){
};
/**
+ * Set minimum valid date (inclusive)
+ *
+ * @param {Date} date
+ * @api public
+ */
+
+Calendar.prototype.min = function(date) {
+ this.days.validRange.min(date);
+ return this;
+};
+
+
+/**
+ * Set maximum valid date (inclusive)
+ *
+ * @param {Date} date
+ * @api public
+ */
+
+Calendar.prototype.max = function(date) {
+ this.days.validRange.max(date);
+ return this;
+};
+
+/**
* Enable a year dropdown.
*
* @param {Number} from
46 lib/dayrange.js
View
@@ -0,0 +1,46 @@
+module.exports = DayRange;
+
+function ymd(date) {
+ if (Array.isArray(date)) {
+ return date;
+ }
+ return [date.getFullYear(), date.getMonth(), date.getDate()];
+}
+
+function compare(a, b) {
+ var i, diff;
+ for (i = 0; i < a.length; ++i) {
+ diff = a[i] - b[i];
+ if (diff !== 0) {
+ return diff;
+ }
+ }
+ return 0;
+}
+
+function DayRange(min, max) {
+ this.min(min).max(max);
+ return this;
+}
+
+DayRange.prototype.min = function(v) {
+ this._min = v ? ymd(v) : undefined;
+ return this;
+};
+
+DayRange.prototype.max = function(v) {
+ this._max = v ? ymd(v) : undefined;
+ return this;
+};
+
+DayRange.prototype.before = function(day) {
+ return this._min && (compare(ymd(day), this._min) < 0);
+};
+
+DayRange.prototype.after = function(day) {
+ return this._max && (compare(ymd(day), this._max) > 0);
+};
+
+DayRange.prototype.valid = function(day) {
+ return !this.before(day) && !this.after(day);
+};
62 lib/days.js
View
@@ -9,6 +9,7 @@ var o = require('jquery')
, inGroupsOf = require('in-groups-of')
, clamp = require('./utils').clamp
, range = require('range')
+ , DayRange = require('./dayrange');
/**
* Days.
@@ -74,12 +75,16 @@ function Days() {
this.body = this.el.find('tbody');
this.title = this.head.find('.title');
this.select(new Date);
+ this.validRange = new DayRange;
// emit "day"
this.body.on('click', 'a', function(e){
var el = o(e.target);
var day = parseInt(el.text(), 10);
var data = el.data('date').split('-');
+ if (!self.validRange.valid(data)) {
+ return false;
+ }
var year = data[0];
var month = data[1];
var date = new Date;
@@ -123,6 +128,7 @@ Days.prototype.select = function(date){
return this;
};
+
/**
* Show date selection.
*
@@ -278,21 +284,17 @@ Days.prototype.rowsFor = function(date){
var cells = [];
// cells before
- cells = cells.concat(cellsBefore(before, month, year));
+ cells = cells.concat(cellsBefore(before, month, year, this.validRange));
- // current cells
+ // current cells
for (var i = 0; i < total; ++i) {
- var day = i + 1;
- var date = 'data-date=' + [year, month, day].join('-');
- if (day == selectedDay && month == selectedMonth && year == selectedYear) {
- cells.push('<td class=selected><a href="#" ' + date + '>' + day + '</a></td>');
- } else {
- cells.push('<td><a href="#" ' + date + '>' + day + '</a></td>');
- }
+ var day = i + 1
+ , select = (day == selectedDay && month == selectedMonth && year == selectedYear);
+ cells.push(renderDay([year, month, day], this.validRange, select));
}
// after cells
- cells = cells.concat(cellsAfter(after, month, year));
+ cells = cells.concat(cellsAfter(after, month, year, this.validRange));
return inGroupsOf(cells, 7);
};
@@ -336,12 +338,12 @@ Days.prototype.showSelectedMonth = function(month) {
* @api private
*/
-function cellsBefore(n, month, year){
+function cellsBefore(n, month, year, validRange){
var cells = [];
if (month == 0) --year;
var prev = clamp(month - 1);
var before = daysInMonth(prev);
- while (n--) cells.push(prevMonthDay(year, prev, before--));
+ while (n--) cells.push(renderDay([year, prev, before--], validRange, false, 'prev-day'));
return cells.reverse();
}
@@ -354,31 +356,41 @@ function cellsBefore(n, month, year){
* @api private
*/
-function cellsAfter(n, month, year){
+function cellsAfter(n, month, year, validRange){
var cells = [];
var day = 0;
if (month == 11) ++year;
var next = clamp(month + 1);
- while (n--) cells.push(nextMonthDay(year, next, ++day));
+ while (n--) cells.push(renderDay([year, next, ++day], validRange, false, 'next-day'));
return cells;
}
+
/**
- * Prev month day template.
+ * Day template.
*/
-function prevMonthDay(year, month, day) {
- var date = 'data-date=' + [year, month, day].join('-');
- return '<td><a href="#" ' + date + ' class=prev-day>' + day + '</a></td>';
-}
+function renderDay(ymd, validRange, selected, style) {
+ var date = 'data-date=' + ymd.join('-')
+ , styles = []
+ , tdClass = ''
+ , aClass = '';
+
+ if (selected) {
+ tdClass = ' class="selected"';
+ }
+ if (style) {
+ styles.push(style);
+ }
+ if (!validRange.valid(ymd)) {
+ styles.push('invalid');
+ }
+ if (styles.length) {
+ aClass = ' class="' + styles.join(' ') + '"';
+ }
-/**
- * Next month day template.
- */
-function nextMonthDay(year, month, day) {
- var date = 'data-date=' + [year, month, day].join('-');
- return '<td><a href="#" ' + date + ' class=next-day>' + day + '</a></td>';
+ return '<td' + tdClass + '><a href="#" ' + date + aClass + '>' + ymd[2] + '</a></td>';
}
/**
15 package.json
View
@@ -2,14 +2,20 @@
"name": "calendar-component",
"description": "Calendar component",
"version": "0.0.3",
- "keywords": ["calendar", "date", "ui"],
+ "keywords": [
+ "calendar",
+ "date",
+ "ui"
+ ],
"dependencies": {
"jquery-component": "*",
"emitter-component": "*",
"in-groups-of": "*"
},
"component": {
- "styles": ["lib/calendar.css"],
+ "styles": [
+ "lib/calendar.css"
+ ],
"scripts": {
"calendar/index.js": "index.js",
"calendar/lib/utils.js": "lib/utils.js",
@@ -17,5 +23,8 @@
"calendar/lib/calendar.js": "lib/calendar.js",
"calendar/lib/days.js": "lib/days.js"
}
+ },
+ "devDependencies": {
+ "mocha": "~1.8.1"
}
-}
+}
49 test/dayrange.js
View
@@ -0,0 +1,49 @@
+var DayRange = require('../lib/dayrange')
+ , assert = require('assert');
+
+describe('day range', function(){
+ it('should consider all dates as valid if no min/max specified', function(){
+ var dr = new DayRange;
+ assert.ok(!dr.before(new Date));
+ assert.ok(!dr.after(new Date));
+ assert.ok(dr.valid([2002, 12, 10]));
+ });
+
+ it('should consider dates inside of the range as valid', function(){
+ var dr = new DayRange([2014, 3, 2], [2014, 4, 3]);
+ assert.ok(dr.before([2014, 3, 1]));
+ assert.ok(!dr.valid([2014, 3, 1]));
+ assert.ok(dr.valid([2014, 3, 2]));
+ assert.ok(dr.valid([2014, 3, 30]));
+ assert.ok(dr.valid([2014, 4, 3]));
+ assert.ok(!dr.valid([2014, 4, 4]));
+ assert.ok(dr.after([2014, 4, 4]));
+ });
+
+ it('should work with mixture of dates and arrays', function(){
+ var dr = new DayRange()
+ .min([2014, 3, 2])
+ .max(new Date(2014, 4, 3));
+ assert.ok(dr.before(new Date(2014, 3, 1)));
+ assert.ok(!dr.valid(new Date(2014, 3, 1)));
+ assert.ok(dr.valid(new Date(2014, 3, 2)));
+ assert.ok(dr.valid(new Date(2014, 3, 30)));
+ assert.ok(dr.valid(new Date(2014, 4, 3)));
+ assert.ok(!dr.valid(new Date(2014, 4, 4)));
+ assert.ok(dr.after(new Date(2014, 4, 4)));
+ });
+
+ it('should work if only min is specified', function(){
+ var dr = new DayRange([2013, 3, 3]);
+ assert.ok(!dr.valid([2013, 3, 2]));
+ assert.ok(dr.valid([2013, 3, 3]));
+ assert.ok(dr.valid([2013, 3, 4]));
+ });
+
+ it('should work if only max is specified', function(){
+ var dr = new DayRange(null, [2013, 3, 3]);
+ assert.ok(dr.valid([2013, 3, 2]));
+ assert.ok(dr.valid([2013, 3, 3]));
+ assert.ok(!dr.valid([2013, 3, 4]));
+ });
+});
Something went wrong with that request. Please try again.