diff --git a/Readme.md b/Readme.md index 2aa426f..88b5162 100644 --- a/Readme.md +++ b/Readme.md @@ -1,15 +1,58 @@ # datepicker - Example date picker ui component built on component/calendar. - - Not yet feature-rich, just a blog post example. +A date picker UI component built on component/calendar. Forked from component/datepicker. ![screen shot 2013-06-17 at 13 31 47](https://f.cloud.github.com/assets/574696/661644/4593118a-d739-11e2-9bdf-4b91b99b8a38.png) +## Install -## Installation +``` +$ component install redbadger/datepicker +``` - $ component install component/datepicker +## Features + +* Takes and returns a value of type `Date` +* Hide on click outside +* Manual date change support +* Keyboard interaction +* Fully configurable date format with custom divider symbols + +## Example + +``` javascript +var Datepicker = require('datepicker'); + +var picker = Datepicker(document.getElementById('date'), "DD/MM/YYYY") + +picker.value(new Date()); +picker.value() // => currently selected date as a Date instance + +``` + +## Custom date format + +You can specify the format of the date you expect it to produce. Format is a string, which can be something like this: + +* "DD/MM/YYYY" +* "YYYY-MM-DD" +* "mm.dd.yy" + +Order of month, day and year will be recognized by the component, divider symbol will also be honored. You can ask for 2 digit year or 4 with "YY" or "YYYY" respectably. + +## API + +### .value(value) + +Get or set current value. `value` argument is optional. + +### .show() + +Show the date picker popover. + +### .hide() + +Hide the date picker popover. ## License diff --git a/component.json b/component.json index 3b4cb43..506b621 100644 --- a/component.json +++ b/component.json @@ -1,14 +1,23 @@ { "name": "datepicker", - "repo": "component/datepicker", - "description": "Datepicker ui component built on component/calendar", - "version": "1.0.0", - "keywords": ["date", "picker", "calendar"], + "repo": "redbadger/datepicker", + "description": "Improved datepicker UI forked from component/datepicker", + "version": "0.1.0", + "keywords": [ + "date", + "picker", + "calendar" + ], "dependencies": { "component/calendar": "*", "component/popover": "*", "component/aurora": "*", - "component/event": "*" + "component/event": "*", + "component/events": "*", + "component/keyname": "*", + "component/aurora-tip": "0.0.2", + "component/tip": "0.2.1", + "component/emitter": "*" }, "development": {}, "license": "MIT", @@ -18,4 +27,4 @@ "styles": [ "datepicker.css" ] -} +} \ No newline at end of file diff --git a/datepicker.css b/datepicker.css index 9eb1690..75e0c75 100644 --- a/datepicker.css +++ b/datepicker.css @@ -9,4 +9,9 @@ .datepicker-popover .tip-inner { border: none; + background-color: transparent; +} + +.datepicker-popover .calendar-table { + background-color: #fff; } \ No newline at end of file diff --git a/index.js b/index.js index 9f8cf96..8bcf3e3 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,10 @@ var Calendar = require('calendar') , Popover = require('popover') , event = require('event') + , events = require('events') + , keyname = require('keyname') + , Emitter = require('emitter'); + /** * Expose `Datepicker`. @@ -16,40 +20,199 @@ module.exports = Datepicker; * Initialize a new date picker with the given input `el`. * * @param {Element} el + * @format String "DD/MM/YYYY", "YYYY/MM/DD", "MM/DD/YYYY" * @api public */ -function Datepicker(el) { - if (!(this instanceof Datepicker)) return new Datepicker(el); +function Datepicker(el, format) { + if (!(this instanceof Datepicker)) return new Datepicker(el, format); this.el = el; this.cal = new Calendar; this.cal.addClass('datepicker-calendar'); - event.bind(el, 'click', this.onclick.bind(this)); + + this.events = events(this.el, this); + this.events.bind('click', 'onclick'); + this.events.bind('change', 'onchange'); + this.events.bind('keydown', 'onkeydown'); + + this.cal.on('change', this.value.bind(this)); + + if (typeof(format) === 'undefined') { + this.format = "DD/MM/YYYY"; + } else { + this.format = format; + } + + this.initDateFormat(this.format); + + event.bind(document, 'click', this.hide.bind(this)); + + return this; } /** - * Handle input clicks. + * Mixin emitter. */ -Datepicker.prototype.onclick = function(e){ +Emitter(Datepicker.prototype); + +/** + * Parsing the format string + */ + +Datepicker.prototype.initDateFormat = function(format) { + var reDay = /DD/i + , reMonth = /MM/i + , reYear = /Y/gi + , reDivider = /[^a-zA-Z0-9]/ + , dayPos = 0 + , monthPos = 0 + , yearPos = 0 + , yearCount = 0 + , divider = "" + , dateArray = []; + + this.dayPos = format.search(reDay); + this.monthPos = format.search(reMonth); + this.yearPos = format.search(reYear); + this.yearCount = format.match(reYear).length; + this.divider = format.match(reDivider)[0]; +} + +Datepicker.prototype.processDate = function(date) { + var dateArray = [] + , year + , finalString = "" + , divider = this.divider; + + if (this.yearCount == 2) { + year = String(date.getFullYear()).slice(2, 4); + } else { + year = date.getFullYear(); + } + + dateArray = [ + { value: date.getDate(), position: this.dayPos }, + { value: (date.getMonth() + 1), position: this.monthPos }, + { value: year, position: this.yearPos } + ]; + + dateArray.sort(function(a, b) { + if (a.position < b.position) + return -1; + if (a.position > b.position) + return 1; + return 0; + }); + + dateArray.forEach(function(entry){ + finalString += entry.value + divider; + }); + + // Slicing away the last applied divider + return finalString.slice(0, -1); +} + +/** + * Get/set value. + * + * @param {Date} date (optional) + * @api public + */ + +Datepicker.prototype.value = function(date) { + if(!date) { + if(!this.el.value.match(/\d{1,2}\/\d{1,2}\/\d{4}/)) + return null; + + var parts = this.el.value.split("/"); + + return new Date(parts[2], parts[1] - 1, parts[0]); + } + + this.cal.select(date); + this.el.value = this.processDate(date); + + this.el.focus(); + + this.hide(); + this.emit('change', date); + + return true; +} + +/** + * Show popover + * + * @api public + */ + +Datepicker.prototype.show = function() { + var ev = new Event('click'); + document.dispatchEvent(ev); + if (this.popover) return; - this.cal.once('change', this.onchange.bind(this)); + this.popover = new Popover(this.cal.el); this.popover.classname = 'datepicker-popover popover'; this.popover.show(this.el); -}; + + event.bind(this.popover.el[0], 'click', function(e) { e.stopPropagation(); return false; }); +} /** - * Handle date changes. + * Hide popover + * + * @api public */ -Datepicker.prototype.onchange = function(date){ - this.el.value = date.getFullYear() - + '/' - + (date.getMonth() + 1) - + '/' - + date.getDate(); +Datepicker.prototype.hide = function() { + if (!this.popover) return; this.popover.remove(); this.popover = null; +} + +/** + * Handle input clicks. + */ + +Datepicker.prototype.onclick = function(e){ + e.stopPropagation(); + + this.show(); + return false; +}; + +Datepicker.prototype.onkeydown = function(e){ + switch (keyname(e.which)) { + case 'enter': + e.preventDefault(); + this.onchange(e); + + break; + case 'esc': + this.hide(); + + break; + default: + console.log(keyname(e.which)); + } +}; + +/** + * Handle date changes. + */ + +Datepicker.prototype.onchange = function(e){ + var parts = this.el.value.split("/"); + if(parts.length < 3) + return this.value(null); + + var date = new Date(parts[2], parts[1] - 1, parts[0]); + + this.value(date); }; + + + diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..1f751a4 --- /dev/null +++ b/test/index.html @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + +