Permalink
Browse files

Implement month and year selection - relates to issues #2 and #3

  • Loading branch information...
1 parent 4419e81 commit c1cb45132cdcd632351e7a578e2a4475ca17cc72 @colinf colinf committed with tj Oct 11, 2012
Showing with 213 additions and 6 deletions.
  1. +14 −1 Readme.md
  2. +47 −0 lib/calendar.js
  3. +143 −2 lib/days.js
  4. +1 −1 lib/template.html
  5. +1 −1 lib/template.js
  6. +7 −1 test/index.html
View
@@ -4,7 +4,7 @@
Calendar UI component designed for use as a date-picker,
full-sized calendar or anything in-between.
- ![javascript calendar component](http://f.cl.ly/items/043N1r0e1L130y162R2f/Screen%20Shot%202012-09-17%20at%209.17.32%20PM.png)
+ ![javascript calendar component](http://f.cl.ly/items/2u3w1D421W0C370Z3G1U/Screen%20Shot%202012-10-11%20at%2014.32.41.png)
## Installation
@@ -22,6 +22,7 @@ cal.el.appendTo('body');
- `prev` when the prev link is clicked
- `next` when the next link is clicked
+ - `view change` (date) when the month/year dropdowns are changed
- `change` (date) when the selected date is modified
## API
@@ -40,6 +41,18 @@ cal.el.appendTo('body');
Show the given `date`. This does _not_ select the given date,
it simply ensures that it is visible in the current view.
+### Calendar#canSelectMonth()
+
+ Adds a month dropdown to allow jumping to selected month.
+
+### Calendar#selectYear([from], [to])
+
+ Adds a year dropdown to allow jumping to selected year. `From` specifies the first year shown in the dropdown and `to` the last year. This means that if `to` is less than `from`, the years will be listed in descending order.
+
+ If `from`/`to` are both not specified the dropdown defaults to -/+ 10 years from the calendar's date.
+
+ If only `from` specified it defaults `to` +20 years from that year.
+
### Calendar#prev()
Show the previous view (month).
View
@@ -39,6 +39,7 @@ function Calendar(date) {
this.on('change', this.show.bind(this));
this.days.on('prev', this.prev.bind(this));
this.days.on('next', this.next.bind(this));
+ this.days.on('view change', this.viewChange.bind(this));
this.show(date || new Date);
this.days.on('change', function(date){
self.emit('change', date);
@@ -96,6 +97,39 @@ Calendar.prototype.show = function(date){
};
/**
+ * Enable a year dropdown.
+ *
+ * @param {from} Number
+ * @param {to} Number
+ * @return {Calendar}
+ * @api public
+ */
+
+Calendar.prototype.canSelectYear = function(from, to){
+ if (!from) {
+ from = this._date.getFullYear() - 10;
+ to = this._date.getFullYear() + 10;
+ };
+ to = to || from + 20;
+ this.days.canSelectYear(from, to);
+ this.show(this._date);
+ return this;
+};
+
+/**
+ * Enable a month dropdown.
+ *
+ * @return {Calendar}
+ * @api public
+ */
+
+Calendar.prototype.canSelectMonth = function(){
+ this.days.canSelectMonth();
+ this.show(this._date);
+ return this;
+};
+
+/**
* Return the previous month.
*
* @return {Date}
@@ -146,3 +180,16 @@ Calendar.prototype.next = function(){
this.emit('next');
return this;
};
+
+/**
+ * Jump to a date/month (using dropdowns).
+ *
+ * @return {Calendar}
+ * @api public
+ */
+
+Calendar.prototype.viewChange = function(date){
+ this.show(date);
+ this.emit('view change', date);
+ return this;
+};
View
@@ -71,7 +71,7 @@ function Days() {
this.el = o(template).addClass('calendar-days');
this.head = this.el.find('thead');
this.body = this.el.find('tbody');
- this.title = this.el.find('.title a');
+ this.title = this.head.find('.title');
this.select(new Date);
// emit "day"
@@ -132,14 +132,78 @@ Days.prototype.select = function(date){
Days.prototype.show = function(date){
var year = date.getFullYear();
var month = date.getMonth();
- this.title.text(months[month] + ' ' + year);
+ this.setTitle(year, month);
this.head.find('.subheading').remove();
this.head.append(this.renderHeading(2));
this.body.empty();
this.body.append(this.renderDays(date));
};
/**
+ * Enable a year dropdown.
+ *
+ * @param {Number} from
+ * @param {Number} to
+ * @api public
+ */
+
+Days.prototype.canSelectYear = function(from, to){
+ this.selectYear = true;
+ this.title.find('.year').html(yearDropdown(from, to));
+ var self = this;
+ this.title.find('.year .calendar-select').change(function() {
+ self.emit('view change', new Date(self.titleYear(), self.titleMonth(), 1));
+ return false;
+ });
+
+};
+
+/**
+ * Enable a month dropdown.
+ *
+ * @api public
+ */
+
+Days.prototype.canSelectMonth = function(){
+ this.selectMonth = true;
+ this.title.find('.month').html(monthDropdown());
+ var self = this;
+ this.title.find('.month .calendar-select').change(function() {
+ self.emit('view change', new Date(self.titleYear(), self.titleMonth(), 1));
+ return false;
+ });
+
+};
+
+/**
+ * Return current year of view from title.
+ *
+ * @api private
+ */
+
+Days.prototype.titleYear = function(){
+ if (this.selectYear) {
+ return this.title.find('.year .calendar-select').val();
+ } else {
+ return this.title.find('.year').text();
+ }
+};
+
+/**
+ * Return current month of view from title.
+ *
+ * @api private
+ */
+
+Days.prototype.titleMonth = function(){
+ if (this.selectMonth) {
+ return this.title.find('.month .calendar-select').val();
+ } else {
+ return this.title.find('.month').text();
+ }
+};
+
+/**
* Render days of the week heading with
* the given `length`, for example 2 for "Tu",
* 3 for "Tue" etc.
@@ -224,6 +288,28 @@ Days.prototype.rowsFor = function(date){
};
/**
+ * Update view title for `year` and `month`.
+ *
+ * @param {year} number
+ * @param {month} number
+ * @return null
+ * @api private
+ */
+
+Days.prototype.setTitle = function(year, month) {
+ if (this.selectMonth) {
+ this.title.find('.month .calendar-select').val(month);
+ } else {
+ this.title.find('.month').text(months[month]);
+ };
+ if (this.selectYear) {
+ this.title.find('.year .calendar-select').val(year);
+ } else {
+ this.title.find('.year').text(year);
+ };
+};
+
+/**
* Return `n` days before `month`.
*
* @param {Number} n
@@ -277,3 +363,58 @@ function nextMonthDay(year, month, day) {
return '<td><a href="#" ' + date + ' class=next-day>' + day + '</a></td>';
}
+/**
+ * Year dropdown template.
+ */
+
+function yearDropdown(from, to) {
+ var years = range(from, to);
+ var options = o.map(years, yearOption).join('');
+ return '<select class="calendar-select">'+options+'</select>';
+}
+
+/**
+ * Month dropdown template.
+ */
+
+function monthDropdown() {
+ var options = o.map(months, monthOption).join('');
+ return '<select class="calendar-select">'+options+'</select>';
+}
+
+/**
+ * Year dropdown option template.
+ */
+
+function yearOption(year) {
+ return '<option value="'+year+'">'+year+'</option>';
+}
+
+/**
+ * Month dropdown option template.
+ */
+
+function monthOption(month, i) {
+ return '<option value="'+i+'">'+month+'</option>';
+}
+
+/**
+ * Return an array of the values between 2 integers.
+ * With a blank at the start.
+ */
+
+function range(from, to) {
+ if (from > to) {
+ var reverseTo = to;
+ to = from;
+ from = reverseTo;
+ };
+ var rangeArray = [];
+ rangeArray.push('');
+ for (var i = from; i <= to; i++) {
+ rangeArray.push(i);
+ };
+ if (reverseTo) rangeArray.reverse();
+ return rangeArray;
+}
+
View
@@ -2,7 +2,7 @@
<thead>
<tr>
<td class="prev"><a href="#">←</a></td>
- <td colspan="5" class="title"><a href="#">title</a></td>
+ <td colspan="5" class="title"><span class="month"></span> <span class="year"></span></td>
<td class="next"><a href="#">→</a></td>
</tr>
</thead>
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
@@ -23,7 +23,7 @@
<script src="../build/build.js"></script>
<script>
var Calendar = require('calendar');
- var one = new Calendar;
+ var one = (new Calendar).canSelectMonth().canSelectYear();
one.on('next', function(){
two.next();
@@ -33,6 +33,12 @@
two.prev();
});
+ one.on('view change', function(date){
+ var twoDate = new Date(date);
+ twoDate.setMonth(date.getMonth() + 1)
+ two.show(twoDate);
+ });
+
one.on('change', function(date){
console.log('selected: %s of %s %s',
date.getDate(),

0 comments on commit c1cb451

Please sign in to comment.