Comparing version 0.4.2 to 1.0.0
266
anytime.js
module.exports = AnytimePicker | ||
var moment = require('moment-timezone') | ||
, months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] | ||
, Emitter = require('events').EventEmitter | ||
var Emitter = require('events').EventEmitter | ||
, extend = require('lodash.assign') | ||
, throttle = require('lodash.throttle') | ||
, pad = require('pad-number') | ||
, classList = require('classlist') | ||
, moment = require('moment') | ||
, getYearList = require('./lib/get-year-list') | ||
, createButton = require('./lib/create-button') | ||
, getMonthDetails = require('./lib/get-month-details') | ||
, classList = require('classlist') | ||
, createMoment = require('./lib/create-moment') | ||
, defaults = | ||
@@ -16,13 +17,9 @@ { minYear: 1960 | ||
, offset: 5 | ||
, initialValue: new Date() | ||
, initialValue: null | ||
, initialView: new Date() | ||
, format: 'h:mma on dddd D MMMM YYYY' | ||
, moment: moment | ||
, minuteIncrement: 1 | ||
} | ||
function createMoment(value) { | ||
value = value !== null ? value : undefined | ||
if (this.options.timezone) return moment.tz(value, this.options.timezone) | ||
return moment(value) | ||
} | ||
function AnytimePicker(options) { | ||
@@ -34,4 +31,3 @@ | ||
// A place to store references to event callback functions | ||
// so they can be specifically unbound later on | ||
// A place to store references to event callback functions so they can be specifically unbound later on | ||
this.__events = {} | ||
@@ -42,13 +38,17 @@ | ||
var initialValue = createMoment.call(this, this.options.initialValue) | ||
this.currentView = { month: initialValue.month(), year: initialValue.year() } | ||
var initialView = this.createMoment(this.options.initialValue || this.options.initialView) | ||
this.currentView = { month: initialView.month(), year: initialView.year() } | ||
this.value = createMoment.call(this, this.options.initialValue).seconds(0).milliseconds(0) | ||
this.value = this.options.initialValue ? this.createMoment(this.options.initialValue).seconds(0).milliseconds(0) : null | ||
this.monthNames = this.options.moment.months() | ||
this.el.addEventListener('click', function (e) { | ||
if (classList(e.target).contains('js-anytime-picker-day')) { | ||
this.value.date(parseInt(e.target.getAttribute('data-date'), 10)) | ||
this.value.month(parseInt(e.target.getAttribute('data-month'), 10)) | ||
this.value.year(parseInt(e.target.getAttribute('data-year'), 10)) | ||
this.emit('change', this.value) | ||
e.stopPropagation() | ||
this.update(function (value) { | ||
return value | ||
.date(parseInt(e.target.getAttribute('data-date'), 10)) | ||
.month(parseInt(e.target.getAttribute('data-month'), 10)) | ||
.year(parseInt(e.target.getAttribute('data-year'), 10)) | ||
}) | ||
} | ||
@@ -60,16 +60,13 @@ }.bind(this)) | ||
this.on('change', function (value) { | ||
if (!value) return | ||
value = createMoment.call(this, value) | ||
this.value = value | ||
this.options.input.value = value.format(this.options.format) | ||
this.updateDisplay() | ||
}.bind(this)) | ||
this.__events['misc toggle'] = this.toggle.bind(this) | ||
if (this.options.button) this.options.button.addEventListener('click', this.__events['misc toggle']) | ||
this.options.input.addEventListener('click', this.__events['misc toggle']) | ||
this.__events['misc show'] = this.show.bind(this) | ||
if (this.options.button) this.options.button.addEventListener('click', this.__events['misc show']) | ||
this.options.input.addEventListener('click', this.__events['misc show']) | ||
this.root = this.options.anchor ? this.options.anchor : this.options.input | ||
if (this.options.input) { | ||
this.updateInput(this) | ||
this.on('change', this.updateInput.bind(this)) | ||
} | ||
} | ||
@@ -79,2 +76,29 @@ | ||
AnytimePicker.prototype.createMoment = createMoment | ||
AnytimePicker.prototype.updateInput = function () { | ||
this.options.input.value = this.value ? this.value.format(this.options.format) : '' | ||
} | ||
AnytimePicker.prototype.update = function (update) { | ||
if (update === null || update === undefined) { | ||
this.value = null | ||
this.updateDisplay() | ||
this.emit('change', null) | ||
return | ||
} | ||
if (typeof update !== 'function') { | ||
var newVal = update | ||
update = function () { return this.createMoment(newVal) }.bind(this) | ||
} | ||
var updated = update(this.value || this.createMoment()) | ||
this.value = updated | ||
this.updateDisplay() | ||
this.emit('change', this.value.toDate()) | ||
} | ||
AnytimePicker.prototype.render = function () { | ||
@@ -126,3 +150,3 @@ | ||
classList(monthSelect).add('js-anytime-picker-month', 'anytime-picker__dropdown') | ||
months.forEach(function (month, i) { | ||
this.monthNames.forEach(function (month, i) { | ||
var monthOption = document.createElement('option') | ||
@@ -137,3 +161,3 @@ monthOption.textContent = month | ||
monthSelect.addEventListener('change', function (e) { | ||
this.currentView.month = months.indexOf(e.target.value) | ||
this.currentView.month = this.monthNames.indexOf(e.target.value) | ||
this.updateDisplay() | ||
@@ -181,3 +205,3 @@ }.bind(this)) | ||
clearBtn.addEventListener('click', function () { | ||
this.emit('change', null) | ||
this.update(null) | ||
this.hide() | ||
@@ -197,46 +221,90 @@ }.bind(this)) | ||
// Days | ||
var daysEl = document.createElement('div') | ||
, monthDetails = getMonthDetails(this.currentView.month, this.currentView.year) | ||
for (var x = 1; x < monthDetails.startDay; x++) { | ||
var blank = document.createElement('span') | ||
blank.textContent = '' | ||
daysEl.appendChild(blank) | ||
/* | ||
* Create the blank days ahead of the first day of the current month so that | ||
* the days appear in the corresponding columns of the days of the week | ||
*/ | ||
function renderDayNames() { | ||
this.options.moment.weekdaysMin().forEach(function (d) { | ||
var dayName = document.createElement('span') | ||
dayName.textContent = d | ||
classList(dayName).add('anytime-picker__day-name') | ||
daysEl.appendChild(dayName) | ||
}) | ||
} | ||
var currentDayOfMonth = +moment().format('D') | ||
, isCurrentMonth = +moment().month() === this.currentView.month | ||
, isCurrentYear = +moment().year() === this.currentView.year | ||
, selectedDayOfMonth = +createMoment.call(this, this.value).format('D') | ||
, isSelectedCurrentMonth = +createMoment.call(this, this.value).month() === this.currentView.month | ||
, isSelectedCurrentYear = +createMoment.call(this, this.value).year() === this.currentView.year | ||
/* | ||
* Create the blank days ahead of the first day of the current month so that | ||
* the days appear in the corresponding columns of the days of the week | ||
*/ | ||
function padDays() { | ||
for (var x = 1; x < monthDetails.startDay; x++) { | ||
var blank = document.createElement('span') | ||
blank.textContent = '' | ||
daysEl.appendChild(blank) | ||
} | ||
} | ||
for (var y = 1; y <= monthDetails.length; y++) { | ||
var date = document.createElement('button') | ||
date.textContent = y | ||
var cl = classList(date) | ||
cl.add('anytime-picker__date', 'js-anytime-picker-day') | ||
/* | ||
* Create a day element for each day of the current month | ||
*/ | ||
function populateDays() { | ||
var now = this.createMoment() | ||
, currentDayOfMonth = parseInt(now.format('D'), 10) | ||
, isCurrentMonth = parseInt(now.month(), 10) === this.currentView.month | ||
, isCurrentYear = parseInt(now.year(), 10) === this.currentView.year | ||
, selectedDayOfMonth = null | ||
, isSelectedCurrentMonth = false | ||
, isSelectedCurrentYear = false | ||
if (y === currentDayOfMonth && isCurrentMonth && isCurrentYear) { | ||
cl.add('anytime-picker__date--current') | ||
if (this.value) { | ||
selectedDayOfMonth = parseInt(this.value.format('D'), 10) | ||
isSelectedCurrentMonth = parseInt(this.value.month(), 10) === this.currentView.month | ||
isSelectedCurrentYear = parseInt(this.value.year(), 10) === this.currentView.year | ||
} | ||
// Needs to add or remove because the current selected day can change | ||
// within the current month and need to be cleared from others | ||
cl[y === selectedDayOfMonth && isSelectedCurrentMonth && isSelectedCurrentYear ? 'add' : 'remove']('anytime-picker__date--selected') | ||
for (var y = 1; y <= monthDetails.length; y++) { | ||
var date = document.createElement('button') | ||
date.textContent = y | ||
var cl = classList(date) | ||
cl.add('anytime-picker__date', 'js-anytime-picker-day') | ||
date.setAttribute('data-date', y) | ||
date.setAttribute('data-month', this.currentView.month) | ||
date.setAttribute('data-year', this.currentView.year) | ||
daysEl.appendChild(date) | ||
if (y === currentDayOfMonth && isCurrentMonth && isCurrentYear) { | ||
cl.add('anytime-picker__date--current') | ||
} | ||
// Needs to add or remove because the current selected day can change | ||
// within the current month and need to be cleared from others | ||
var current = y === selectedDayOfMonth && isSelectedCurrentMonth && isSelectedCurrentYear | ||
cl[current ? 'add' : 'remove']('anytime-picker__date--selected') | ||
date.setAttribute('data-date', y) | ||
date.setAttribute('data-month', this.currentView.month) | ||
date.setAttribute('data-year', this.currentView.year) | ||
daysEl.appendChild(date) | ||
} | ||
} | ||
renderDayNames.call(this) | ||
padDays.call(this) | ||
populateDays.call(this) | ||
// Remove all of the old days | ||
Array.prototype.slice.call(this.dateContainer.children).forEach(function (child) { | ||
if (child.parentNode) child.parentNode.removeChild(child) | ||
}) | ||
Array.prototype.slice.call(daysEl.children).forEach(function (child) { this.dateContainer.appendChild(child) }.bind(this)) | ||
// Add all the new days | ||
Array.prototype.slice.call(daysEl.children).forEach(function (child) { | ||
this.dateContainer.appendChild(child) | ||
}.bind(this)) | ||
} | ||
AnytimePicker.prototype.getCurrentSelection = function () { | ||
} | ||
AnytimePicker.prototype.show = function () { | ||
@@ -248,8 +316,5 @@ | ||
var position = { top: this.root.offsetTop, left: this.root.offsetLeft } | ||
this.updatePosition() | ||
this.el.style.top = (position.top + this.root.offsetHeight + this.options.offset) + 'px' | ||
this.el.style.left = (position.left + this.root.offsetWidth - this.el.offsetWidth) + 'px' | ||
this.__events['doc escape keypress'] = function (e) { | ||
this.__events['doc escape hide'] = function (e) { | ||
// Hide if escape is pressed | ||
@@ -259,2 +324,9 @@ if (e.keyCode === 27) this.hide() | ||
this.__events['doc click hide'] = function (e) { | ||
// Hide if document outside of anytime is clicked | ||
if (e.target === this.el) return | ||
if (this.el.contains(e.target)) return | ||
this.hide() | ||
}.bind(this) | ||
this.__events['other anytime open'] = function (e) { | ||
@@ -265,6 +337,14 @@ // Hide if another instance is opened | ||
document.addEventListener('keyup', this.__events['doc escape keypress']) | ||
document.addEventListener('anytime::open', this.__events['other anytime open']) | ||
this.__events['window resize position'] = throttle(function () { | ||
// Update position when window is resized | ||
this.updatePosition() | ||
}.bind(this), 100) | ||
document.dispatchEvent(new CustomEvent('anytime::open', { detail: { instance: this } })) | ||
process.nextTick(function () { | ||
document.addEventListener('keyup', this.__events['doc escape hide']) | ||
document.addEventListener('click', this.__events['doc click hide']) | ||
document.addEventListener('anytime::open', this.__events['other anytime open']) | ||
window.addEventListener('resize', this.__events['window resize position']) | ||
document.dispatchEvent(new CustomEvent('anytime::open', { detail: { instance: this } })) | ||
}.bind(this)) | ||
@@ -277,8 +357,14 @@ } | ||
document.removeEventListener('keyup', this.__events['doc escape keypress']) | ||
delete this.__events['doc escape keypress'] | ||
document.removeEventListener('keyup', this.__events['doc escape hide']) | ||
delete this.__events['doc escape hide'] | ||
document.removeEventListener('click', this.__events['doc click hide']) | ||
delete this.__events['doc click hide'] | ||
document.removeEventListener('anytime::open', this.__events['other anytime open']) | ||
delete this.__events['keyup escToClose'] | ||
delete this.__events['keyup other anytime open'] | ||
window.removeEventListener('resize', this.__events['window resize position']) | ||
delete this.__events['window resize position'] | ||
if (this.el.parentNode) this.el.parentNode.removeChild(this.el) | ||
@@ -288,2 +374,16 @@ | ||
AnytimePicker.prototype.updatePosition = function () { | ||
var position = { top: this.root.offsetTop, left: this.root.offsetLeft } | ||
this.el.style.top = (position.top + this.root.offsetHeight + this.options.offset) + 'px' | ||
this.el.style.left = (position.left + this.root.offsetWidth - this.el.offsetWidth) + 'px' | ||
} | ||
AnytimePicker.prototype.toggle = function () { | ||
if (classList(this.el).contains('anytime-picker--is-visible')) { | ||
this.hide() | ||
} else { | ||
this.show() | ||
} | ||
} | ||
AnytimePicker.prototype.showPrevMonth = function () { | ||
@@ -323,3 +423,3 @@ if (this.currentView.month > 0) { | ||
hour.textContent = pad(i, 2) | ||
if (createMoment.call(this, this.options.initialValue).hours() === i) hour.setAttribute('selected', true) | ||
if (this.createMoment(this.options.initialValue).hours() === i) hour.setAttribute('selected', true) | ||
hourSelect.appendChild(hour) | ||
@@ -329,4 +429,5 @@ } | ||
hourSelect.addEventListener('change', function (e) { | ||
this.value.hours(e.target.value) | ||
this.emit('change', this.value) | ||
this.update(function (value) { | ||
return value.hours(e.target.value) | ||
}) | ||
}.bind(this)) | ||
@@ -343,7 +444,7 @@ | ||
classList(minuteSelect).add('anytime-picker__dropdown', 'anytime-picker__dropdown--minutes') | ||
for (var j = 0; j < 60; j++) { | ||
for (var j = 0; j < 60; j += this.options.minuteIncrement) { | ||
var minute = document.createElement('option') | ||
minute.setAttribute('value', j) | ||
minute.textContent = pad(j, 2) | ||
if (createMoment.call(this, this.options.initialValue).minutes() === j) minute.setAttribute('selected', true) | ||
if (this.createMoment(this.options.initialValue).minutes() === j) minute.setAttribute('selected', true) | ||
minuteSelect.appendChild(minute) | ||
@@ -353,4 +454,5 @@ } | ||
minuteSelect.addEventListener('change', function (e) { | ||
this.value.minutes(e.target.value) | ||
this.emit('change', this.value) | ||
this.update(function (value) { | ||
return value.minutes(e.target.value) | ||
}) | ||
}.bind(this)) | ||
@@ -366,6 +468,6 @@ | ||
this.removeAllListeners() | ||
if (this.options.button) this.options.button.removeEventListener('click', this.__events['misc show']) | ||
this.options.input.removeEventListener('click', this.__events['misc show']) | ||
delete this.__events['misc show'] | ||
if (this.options.button) this.options.button.removeEventListener('click', this.__events['misc toggle']) | ||
this.options.input.removeEventListener('click', this.__events['misc toggle']) | ||
delete this.__events['misc toggle'] | ||
this.el = null | ||
} |
module.exports = getMonthDetails | ||
var moment = require('moment-timezone') | ||
var moment = require('moment') | ||
@@ -5,0 +5,0 @@ /* |
{ | ||
"name": "anytime", | ||
"version": "0.4.2", | ||
"version": "1.0.0", | ||
"publishConfig": { | ||
@@ -16,6 +16,5 @@ "registry": "http://registry.npmjs.org" | ||
"pretest": "npm run-script lint", | ||
"test": "istanbul cover ./node_modules/.bin/_mocha -- -R spec test/simulate-browser-env", | ||
"test": "istanbul cover ./node_modules/.bin/_mocha -- -R spec test", | ||
"posttest": "istanbul check-coverage && rm -rf coverage", | ||
"prepublish": "npm test && npm prune", | ||
"demo": "./node_modules/.bin/browserify -r ./anytime > bundle.js && open demo.html" | ||
"prepublish": "npm test && npm prune" | ||
}, | ||
@@ -27,3 +26,4 @@ "author": "Ben Gourley", | ||
"lodash.assign": "*", | ||
"moment-timezone": "^0.3.1", | ||
"lodash.throttle": "*", | ||
"moment": "^2.10.3", | ||
"pad-number": "^0.0.1" | ||
@@ -37,4 +37,6 @@ }, | ||
"jshint-full-path": "^1.1.1", | ||
"mocha": "^1.21.5" | ||
"mocha": "^1.21.5", | ||
"moment-timezone": "^0.3.1", | ||
"uglify-js": "^2.4.23" | ||
} | ||
} |
@@ -5,2 +5,4 @@ # anytime | ||
[Documentation](https://bengourley.github.io/anytime) | ||
A date/time picker. | ||
@@ -18,28 +20,4 @@ | ||
## Usage | ||
### `var picker = new AnyTime(options)` | ||
### [Documentation Site](https://bengourley.github.io/anytime) | ||
Options can be the following: | ||
- `input` - DOM input element for displaying the date | ||
- `minYear` - minimum year. Defaults to `1960` | ||
- `maxYear` - maximum year. Defaults to `2030` | ||
- `offset` - number of pixels to offset the element top. Defaults to `5` | ||
- `initialValue` - value to set the date picker to. Defaults to `new Date()` | ||
- `format` - [moment-style](http://momentjs.com/docs/#/displaying/format/) date format string. Defaults to `'h:mma on dddd D MMMM YYYY'` | ||
- `timezone` - [moment-style](http://momentjs.com/timezone/) timezone string (e.g. 'Europe/London'). Defaults to current timezone | ||
#### `picker.render()` - Renders the date picker | ||
#### `picker.show()` - Shows the date picker | ||
#### `picker.hide()` - Hides the date picker | ||
#### `picker.destroy()` - Destroys the date picker instance | ||
#### Internal API methods - you probably won't need these | ||
##### `picker.renderHeader()` - Renders the header | ||
##### `picker.renderFooter()` - Renders the footer | ||
##### `picker.renderTimeInput()` - Renders the time input | ||
##### `picker.updateDisplay()` - Updates the elements to reflect the internal date state | ||
##### `picker.showPrevMonth()` - Shows the previous month | ||
##### `picker.showNextMonth()` - Shows the next month | ||
## Credits | ||
@@ -46,0 +24,0 @@ * [Ben Gourley](https://github.com/bengourley/) |
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
398
1
20043
5
8
3
30
+ Addedlodash.throttle@*
+ Addedmoment@^2.10.3
+ Addedlodash.throttle@4.1.1(transitive)
- Removedmoment-timezone@^0.3.1
- Removedmoment-timezone@0.3.1(transitive)