Skip to content

Commit

Permalink
added rome datevalidator
Browse files Browse the repository at this point in the history
  • Loading branch information
jivings committed Dec 23, 2015
1 parent 69b5967 commit 52ac4aa
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 50 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"rules": {
"comma-dangle": [2, "never"],
"no-console": 0,
"func-names": 0
"func-names": 0,
"no-use-before-define": 0
}
}
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ All options are optional, including the `el`.
// the default value of the picker
default: moment(),
// the container to append the picker
container: document.body,
container: document.body,
// cosmetic classes that can be overriden
// mostly used for styling the calendar
styles: {
Expand All @@ -118,7 +118,9 @@ All options are optional, including the `el`.
timeList: 'c-datepicker__time-list',
timeOption: 'c-datepicker__time-option',
clockNum: 'c-datepicker__clock__num'
}
},
// date range to allow (see rome validator factories)
dateValidator: null
}
```

Expand Down
8 changes: 6 additions & 2 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,21 @@
<span class="material-icon">calender</span>
</a>

<pre id="output"></pre>

<script src="../dist/js/datepicker.standalone.js" charset="utf-8"></script>

<script>

const picker = new MaterialDatePicker({})
.on('submit', (d) => console.log(d));
.on('submit', (d) => {
output.innerText = d;
});

const el = document.querySelector('.c-datepicker-btn');
el.addEventListener('click', () => {
picker.open();
});
}, false);

</script>
</body>
Expand Down
61 changes: 36 additions & 25 deletions dist/js/datepicker.standalone.js

Large diffs are not rendered by default.

276 changes: 276 additions & 0 deletions lib/js/datepicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import rome from 'rome';
import _defaults from 'lodash/object/defaults';
import moment from 'moment';

import popupTemplate from '../template/datepicker.hbs';
import scrimTemplate from '../template/scrim.hbs';
import Events from './events';

const prefix = 'c-datepicker';
const defaults = {
default: moment(),
// allow the user to override all the classes
// used for styling the calendar
styles: {
scrim: 'c-scrim',
back: prefix + '__back',
container: prefix + '__calendar',
date: prefix + '__date',
dayBody: prefix + '__days-body',
dayBodyElem: prefix + '__day-body',
dayConcealed: prefix + '__day--concealed',
dayDisabled: prefix + '__day--disabled',
dayHead: prefix + '__days-head',
dayHeadElem: prefix + '__day-head',
dayRow: prefix + '__days-row',
dayTable: prefix + '__days',
month: prefix + '__month',
next: prefix + '__next',
positioned: prefix + '--fixed',
selectedDay: prefix + '__day--selected',
selectedTime: prefix + '__time--selected',
time: prefix + '__time',
timeList: prefix + '__time-list',
timeOption: prefix + '__time-option',
clockNum: prefix + '__clock__num'
},
// format to display in the input, or set on the element
format: 'dd/MM/YY',
// the container to append the picker
container: document.body,
// allow any dates
dateValidator: undefined,
// create a range picker
range: false
};

class DateTimePicker extends Events {
constructor(options) {
super();
this.options = _defaults({}, options, defaults);
this.value = this.options.default;

// listen to any event
this.on('*', (evtName, evtData) => {
if (this.options.el) {
// if there is a custom element, fire a real dom
// event on that now
const event = new CustomEvent(evtName, this, evtData);
this.options.el.dispatchEvent(event);
}
});
}

// intialize the rom calendar with our default date and
// style options
initializeRome(container, validator) {
return rome(container, {
styles: this.options.styles,
time: false,
dateValidator: validator
}).on('data', (value) => this.set(value));
}

// called to open the picker
open() {
const scrimEl = scrimTemplate.replace('{{styles.scrim}}', this.options.styles.scrim);
_appendTemplate(document.body, scrimEl);
_appendTemplate(this.options.container, popupTemplate);
this.pickerEl = this.options.container.querySelector('.' + prefix);
this.scrimEl = this.options.container.querySelector('.' + this.options.styles.scrim);
this.amToggleEl = this.$('.c-datepicker__clock--am');
this.pmToggleEl = this.$('.c-datepicker__clock--pm');

this.set(this.value || this.options.default, {
silent: true
});
this.initializeRome(this.$(`.${this.options.styles.container}`), this.options.dateValidator);
this._show();
}

close() {
this._hide();
}

_hide() {
this.pickerEl.classList.remove('open');
window.setTimeout(() => {
this.options.container.removeChild(this.pickerEl);
document.body.removeChild(this.scrimEl);
this.trigger('close');
}, 200);
return this;
}

_show() {
this.delegateEvents();
// add the animation classes on the next animation tick
// so that they actually work
window.requestAnimationFrame(() => {
this.scrimEl.classList.add(this.options.styles.scrim + '--shown');
this.pickerEl.classList.add(prefix + '--open');
this.trigger('open');
});
return this;
}

delegateEvents() {
this.$('.js-cancel')
.addEventListener('click', () => this.clickCancel(), false);
this.$('.js-ok')
.addEventListener('click', () => this.clickSubmit(), false);

this.$(`.${this.options.styles.clockNum}`).forEach(el => {
el.addEventListener('click', (e) => this.clickClock(e), false);
});

this.$('.c-datepicker__clock--am')
.addEventListener('click', (e) => this.clickAm(e), false);
this.$('.c-datepicker__clock--pm')
.addEventListener('click', (e) => this.clickPm(e), false);

return this;
}

clickSubmit() {
this.close();
this.trigger('submit', this.value, this);
return this;
}

clickCancel() {
this.close();
this.trigger('cancel', this.value, this);
return this;
}

clickClock(e) {
let number = parseInt(e.currentTarget.getAttribute('data-number'), 10);
if (number === 0 && this.meridiem === 'pm') {
number = 12;
} else if (this.meridiem === 'pm') {
number += 12;
}

this.value.hour(number);
this.setTime(this.value);
return this;
}

clickAm() {
if (this.meridiem === 'pm') {
this.meridiem = 'am';
this.value.hour(this.value.hour() + 12);
}
this.setTime(this.value);
return this;
}

clickPm() {
if (this.meridiem === 'am') {
this.meridiem = 'pm';
this.value.hour(this.value.hour() - 12);
}
this.setTime(this.value);
return this;
}

data(val) {
return (val ? this.set(val) : this.value);
}

set(value, opts) {
const m = moment(value);
// maintain a list of change events to fire all at once later
const evts = [];
if (!this.value || (this.value && (m.date() !== this.value.date()
|| m.month() !== this.value.month()
|| m.year() !== this.value.year()
))) {
// if the date has not been set yet, or has changed then set it
// and fire a change:date event
this.setDate(m);
evts.push('change:date');
} else {
// otherwise just the time is being set
// so fire a change:time event
this.setTime(m);
evts.push('change:time');
}
if (this.options.el) {
// if there is an element to fire events on
if (this.options.el.tagName === 'INPUT') {
// and it is an input element then set the value
this.options.el.value = m.format(this.options.format);
} else {
// or any other element set a data-value attribute
this.options.el.setAttribute('data-value', m.format(this.options.format));
}
}
if (!opts || !opts.silent) {
// fire all the events we've collected
this.trigger(['change', ...evts].join(' '), this.value, this);
}
}

// set the value and header elements to `date`
// the calendar will be updated automatically
// by rome when clicked
setDate(date) {
const m = moment(date);
const month = m.format('MMM');
const day = m.format('Do');
const dayOfWeek = m.format('dddd');
const year = m.format('YYYY');

this.$('.js-day').innerText = dayOfWeek;
this.$('.js-date-month').innerText = (month + ' ' + year);
this.$('.js-date-day').innerText = day;
this.value.year(m.year());
this.value.month(m.month());
this.value.date(m.date());
return this;
}

// set the value and header elements to `time`
// also update the hands of the clock
setTime(time) {
const m = moment(time);
const hour = m.format('HH');
const hourAsInt = parseInt(hour, 10) % 12;

this.$('.js-date-hours').innerText = hour;

this.$(`.c-datepicker__clock__hours .${this.options.styles.clockNum}[data-number="${hourAsInt}"]`)
.classList.add(`.${this.options.styles.clockNum}--active`);
this.value.hours(m.hours());
this.meridiem = this.value.format('a');

if (this.meridiem === 'pm') {
this.amToggleEl.removeAttribute('checked');
this.pmToggleEl.setAttribute('checked', 'checked');
this.amToggleEl.parentElement.classList.remove('c-datepicker__toggle--checked');
this.pmToggleEl.parentElement.classList.add('c-datepicker__toggle--checked');
} else {
this.pmToggleEl.removeAttribute('checked');
this.amToggleEl.setAttribute('checked', 'checked');
this.pmToggleEl.parentElement.classList.remove('c-datepicker__toggle--checked');
this.amToggleEl.parentElement.classList.add('c-datepicker__toggle--checked');
}
return this;
}

$(selector) {
const els = this.pickerEl.querySelectorAll(selector);
return els.length > 1 ? [...els] : els[0];
}
}

export default DateTimePicker;

function _appendTemplate(parent, template) {
const tempEl = document.createElement('div');
tempEl.innerHTML = template;
parent.appendChild(tempEl.firstChild);
return this;
}
Loading

0 comments on commit 52ac4aa

Please sign in to comment.