Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Constrain dates #25

Closed
wants to merge 4 commits into from

1 participant

@pirxpilot
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
@pirxpilot pirxpilot add `test` target in Makefile
mocha as devel dependency
f0f30ac
@pirxpilot pirxpilot Options for jshint 4ce7cdc
@pirxpilot 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
@pirxpilot pirxpilot Document new `min` and `max` methods 16b5f20
@pirxpilot
Owner

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

@pirxpilot 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. @pirxpilot

    add `test` target in Makefile

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

    Options for jshint

    pirxpilot authored
  2. @pirxpilot

    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. @pirxpilot
This page is out of date. Refresh to see the latest.
View
16 .jshintrc
@@ -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
+ }
+}
View
6 Makefile
@@ -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
View
8 Readme.md
@@ -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):
View
1  component.json
@@ -25,6 +25,7 @@
"lib/utils.js",
"lib/template.js",
"lib/calendar.js",
+ "lib/dayrange.js",
"lib/days.js"
],
"license": "MIT"
View
11 example.html
@@ -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>
View
31 lib/calendar.js
@@ -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
View
46 lib/dayrange.js
@@ -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);
+};
View
62 lib/days.js
@@ -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>';
}
/**
View
15 package.json
@@ -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"
}
-}
+}
View
49 test/dayrange.js
@@ -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.