bulma-extensions
Version:
Set of extensions for Bulma.io CSS Framework
633 lines (549 loc) • 19.4 kB
JavaScript
import * as utils from '../utils';
import * as type from '../utils/type';
import * as dateFns from 'date-fns';
import EventEmitter from '../utils/events';
import template from './templates/timepicker';
import defaultOptions from './defaultOptions';
export default class timePicker extends EventEmitter {
constructor(options = {}) {
super();
this.options = {
...defaultOptions,
...options
};
this._clickEvents = ['click', 'touch'];
this._supportsPassive = utils.detectSupportsPassive();
this._id = utils.uuid('timePicker');
this.node = null;
this.onPreviousHourStartTimePicker = this.onPreviousHourStartTimePicker.bind(this);
this.onNextHourStartTimePicker = this.onNextHourStartTimePicker.bind(this);
this.onPreviousMinuteStartTimePicker = this.onPreviousMinuteStartTimePicker.bind(this);
this.onNextMinuteStartTimePicker = this.onNextMinuteStartTimePicker.bind(this);
this.onPreviousHourEndTimePicker = this.onPreviousHourEndTimePicker.bind(this);
this.onNextHourEndTimePicker = this.onNextHourEndTimePicker.bind(this);
this.onPreviousMinuteEndTimePicker = this.onPreviousMinuteEndTimePicker.bind(this);
this.onNextMinuteEndTimePicker = this.onNextMinuteEndTimePicker.bind(this);
this._init();
}
/****************************************************
* *
* PRIVATE FUNCTIONS *
* *
****************************************************/
_init() {
this._open = false;
this._snapshots = [];
this.lang = this.options.lang;
this.format = this.options.format || 'HH:mm';
this.min = this.options.min;
this.max = this.options.max;
this._time = {
start: dateFns.startOfToday(),
end: dateFns.endOfToday()
};
this.start = this.options.start || dateFns.startOfToday();
this.end = this.options.isRange ? this.options.end : dateFns.endOfToday();
this._build();
this._bindEvents();
this.emit('ready', this);
}
_build() {
this.node = document.createRange().createContextualFragment(template({
locale: this.locale,
isRange: this.options.isRange,
icons: this.options.icons
}));
this._ui = {
container: this.node.firstChild,
start: {
container: this.node.querySelector('.timepicker-start'),
hours: {
container: this.node.querySelector('.timepicker-start .timepicker-hours'),
input: this.node.querySelector('.timepicker-start .timepicker-hours input'),
number: this.node.querySelector('.timepicker-start .timepicker-hours .timepicker-input-number'),
previous: this.node.querySelector('.timepicker-start .timepicker-hours .timepicker-previous'),
next: this.node.querySelector('.timepicker-start .timepicker-hours .timepicker-next')
},
minutes: {
container: this.node.querySelector('.timepicker-start .timepicker-minutes'),
input: this.node.querySelector('.timepicker-start .timepicker-minutes input'),
number: this.node.querySelector('.timepicker-start .timepicker-minutes .timepicker-input-number'),
previous: this.node.querySelector('.timepicker-start .timepicker-minutes .timepicker-previous'),
next: this.node.querySelector('.timepicker-start .timepicker-minutes .timepicker-next')
}
},
end: {
container: this.node.querySelector('.timepicker-end'),
hours: {
container: this.node.querySelector('.timepicker-end .timepicker-hours'),
input: this.node.querySelector('.timepicker-end .timepicker-hours input'),
number: this.node.querySelector('.timepicker-end .timepicker-hours .timepicker-input-number'),
previous: this.node.querySelector('.timepicker-end .timepicker-hours .timepicker-previous'),
next: this.node.querySelector('.timepicker-end .timepicker-hours .timepicker-next')
},
minutes: {
container: this.node.querySelector('.timepicker-end .timepicker-minutes'),
input: this.node.querySelector('.timepicker-end .timepicker-minutes input'),
number: this.node.querySelector('.timepicker-end .timepicker-minutes .timepicker-input-number'),
previous: this.node.querySelector('.timepicker-end .timepicker-minutes .timepicker-previous'),
next: this.node.querySelector('.timepicker-end .timepicker-minutes .timepicker-next')
}
}
};
}
_bindEvents() {
this._clickEvents.forEach(clickEvent => {
this._ui.start.hours.previous.addEventListener(clickEvent, this.onPreviousHourStartTimePicker);
this._ui.start.hours.next.addEventListener(clickEvent, this.onNextHourStartTimePicker);
this._ui.start.minutes.previous.addEventListener(clickEvent, this.onPreviousMinuteStartTimePicker);
this._ui.start.minutes.next.addEventListener(clickEvent, this.onNextMinuteStartTimePicker);
if (this.options.isRange) {
this._ui.end.hours.previous.addEventListener(clickEvent, this.onPreviousHourEndTimePicker);
this._ui.end.hours.next.addEventListener(clickEvent, this.onNextHourEndTimePicker);
this._ui.end.minutes.previous.addEventListener(clickEvent, this.onPreviousMinuteEndTimePicker);
this._ui.end.minutes.next.addEventListener(clickEvent, this.onNextMinuteEndTimePicker);
}
});
}
_select(time = undefined) {
this.snapshot();
time = type.isDate(time) ? time : new Date(time);
if (this.options.isRange && (!this._isValidTime(this.start) || (this._isValidTime(this.start) && this._isValidTime(this.end)))) {
this.start = time;
this.end = dateFns.endOfToday();
this.emit('select:start', this);
} else if (this.options.isRange && !this._isValidTime(this.end)) {
if (dateFns.isBefore(time, this.start)) {
this.end = this.start;
this.start = dateFns.endOfToday();
this.emit('select', this);
} else if (dateFns.isAfter(time, this.start)) {
this.end = time;
this.emit('select', this);
} else {
this.start = time;
this.end = dateFns.endOfToday();
this.emit('select:start', this);
}
} else {
this.start = time;
this.end = dateFns.endOfToday();
this.emit('select', this);
}
}
_isValidTime(time, min, max) {
try {
if (!time) {
return false;
}
if (dateFns.isValid(time)) {
if (!min && !max) {
return true;
}
if (min && max) {
return dateFns.isWithinRange(time, min, max);
}
if (max) {
return dateFns.isBefore(time, max) || dateFns.isEqual(time, max);
}
return dateFns.isAfter(time, min) || dateFns.isEqual(time, min);
} else {
return false;
}
} catch (e) {
return false;
}
}
/****************************************************
* *
* GETTERS and SETTERS *
* *
****************************************************/
/**
* Get id of current TimePicker
*/
get id() {
return this._id;
}
set time(time) {
if (type.isObject(time) && time.start && time.end) {
this._time = time;
}
return this;
}
get time() {
return this._time || {
start: undefined,
end: undefined
}
}
// Set TimePicker language
set lang(lang = 'en') {
this._lang = lang;
this._locale = require('date-fns/locale/' + lang);
return this;
}
// Get current TimePicker language
get lang() {
return this._lang;
}
get locale() {
return this._locale;
}
set start(time) {
this._time.start = time ? (this._isValidTime(time, this.min, this.max) ? time : this._time.start) : dateFns.startOfToday();
return this;
}
get start() {
return this._time.start;
}
set end(time) {
this._time.end = time ? (this._isValidTime(time, this.min, this.max) ? time : this._time.end) : dateFns.endOfToday();
return this;
}
get end() {
return this._time.end;
}
// Set min
set min(time = undefined) {
this._min = time ? (this._isValidTime(time) ? time : this._min) : undefined;
return this;
}
// Get min
get min() {
return this._min;
}
// Set max
set max(time = null) {
this._max = time ? (this._isValidTime(time) ? time : this._max) : undefined;
return this;
}
// Get max
get max() {
return this._max;
}
// Set time format (set to HH:mm by default)
set format(format = 'HH:mm') {
this._format = format;
return this;
}
// Get time format
get format() {
return this._format;
}
/****************************************************
* *
* EVENTS FUNCTIONS *
* *
****************************************************/
onPreviousHourStartTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.start = dateFns.subHours(this.start, 1);
setTimeout(() => {
this._ui.start.hours.number.classList.add('is-decrement-hide');
setTimeout(() => {
this._ui.start.hours.number.innerText = dateFns.format(this.start, 'HH');
this._ui.start.hours.input.value = dateFns.format(this.start, 'HH');
this._ui.start.hours.number.classList.add('is-decrement-visible');
}, 100);
setTimeout(() => {
this._ui.start.hours.number.classList.remove('is-decrement-hide');
this._ui.start.hours.number.classList.remove('is-decrement-visible');
}, 1100);
}, 100);
// this.emit(this.options.isRange ? 'select:start' : 'select', {
// time: this.time,
// instance: this
// });
}
onNextHourStartTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.start = dateFns.addHours(this.start, 1);
setTimeout(() => {
this._ui.start.hours.number.classList.add('is-increment-hide');
setTimeout(() => {
this._ui.start.hours.number.innerText = dateFns.format(this.start, 'HH');
this._ui.start.hours.input.value = dateFns.format(this.start, 'HH');
this._ui.start.hours.number.classList.add('is-increment-visible');
}, 100);
setTimeout(() => {
this._ui.start.hours.number.classList.remove('is-increment-hide');
this._ui.start.hours.number.classList.remove('is-increment-visible');
}, 1100);
}, 100);
// this._selec(this.time);
// this.emit(this.options.isRange ? 'select:start' : 'select', {
// time: this.time,
// instance: this
// });
}
onPreviousMinuteStartTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.start = dateFns.subMinutes(this.start, this.options.minuteSteps);
setTimeout(() => {
this._ui.start.minutes.number.classList.add('is-decrement-hide');
setTimeout(() => {
this._ui.start.minutes.number.innerText = dateFns.format(this.start, 'mm');
this._ui.start.minutes.input.value = dateFns.format(this.start, 'mm');
this._ui.start.minutes.number.classList.add('is-decrement-visible');
if (dateFns.format(this.start, 'HH') !== this._ui.start.hours.input.value) {
this._ui.start.hours.number.innerText = dateFns.format(this.start, 'HH');
this._ui.start.hours.input.value = dateFns.format(this.start, 'HH');
this._ui.start.hours.number.classList.add('is-decrement-visible');
}
}, 100);
setTimeout(() => {
this._ui.start.minutes.number.classList.remove('is-decrement-hide');
this._ui.start.minutes.number.classList.remove('is-decrement-visible');
this._ui.start.hours.number.classList.remove('is-decrement-hide');
this._ui.start.hours.number.classList.remove('is-decrement-visible');
}, 1100);
}, 100);
// this.emit(this.options.isRange ? 'select:start' : 'select', {
// time: this.time,
// instance: this
// });
}
onNextMinuteStartTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.start = dateFns.addMinutes(this.start, this.options.minuteSteps);
setTimeout(() => {
this._ui.start.minutes.number.classList.add('is-increment-hide');
setTimeout(() => {
this._ui.start.minutes.number.innerText = dateFns.format(this.start, 'mm');
this._ui.start.minutes.input.value = dateFns.format(this.start, 'mm');
this._ui.start.minutes.number.classList.add('is-increment-visible');
if (dateFns.format(this.start, 'HH') !== this._ui.start.hours.input.value) {
this._ui.start.hours.number.innerText = dateFns.format(this.start, 'HH');
this._ui.start.hours.input.value = dateFns.format(this.start, 'HH');
this._ui.start.hours.number.classList.add('is-increment-visible');
}
}, 100);
setTimeout(() => {
this._ui.start.minutes.number.classList.remove('is-increment-hide');
this._ui.start.minutes.number.classList.remove('is-increment-visible');
this._ui.start.hours.number.classList.remove('is-increment-hide');
this._ui.start.hours.number.classList.remove('is-increment-visible');
}, 1100);
}, 100);
// this.emit(this.options.isRange ? 'select:start' : 'select', {
// time: this.time,
// instance: this
// });
}
onPreviousHourEndTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.end = dateFns.subHours(this.end, 1);
setTimeout(() => {
this._ui.end.hours.number.classList.add('is-decrement-hide');
setTimeout(() => {
this._ui.end.hours.number.innerText = dateFns.format(this.end, 'HH');
this._ui.end.hours.input.value = dateFns.format(this.end, 'HH');
this._ui.end.hours.number.classList.add('is-decrement-visible');
}, 100);
setTimeout(() => {
this._ui.end.hours.number.classList.remove('is-decrement-hide');
this._ui.end.hours.number.classList.remove('is-decrement-visible');
}, 1100);
}, 100);
// this.emit('select:end', {
// time: this.time,
// instance: this
// });
}
onNextHourEndTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.end = dateFns.addHours(this.end, 1);
setTimeout(() => {
this._ui.end.hours.number.classList.add('is-increment-hide');
setTimeout(() => {
this._ui.end.hours.number.innerText = dateFns.format(this.end, 'HH');
this._ui.end.hours.input.value = dateFns.format(this.end, 'HH');
this._ui.end.hours.number.classList.add('is-increment-visible');
}, 100);
setTimeout(() => {
this._ui.end.hours.number.classList.remove('is-increment-hide');
this._ui.end.hours.number.classList.remove('is-increment-visible');
}, 1100);
}, 100);
// this.emit('select:end', {
// time: this.time,
// instance: this
// });
}
onPreviousMinuteEndTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.end = dateFns.subMinutes(this.end, this.options.minuteSteps);
setTimeout(() => {
this._ui.end.minutes.number.classList.add('is-decrement-hide');
setTimeout(() => {
this._ui.end.minutes.number.innerText = dateFns.format(this.end, 'mm');
this._ui.end.minutes.input.value = dateFns.format(this.end, 'mm');
this._ui.end.minutes.number.classList.add('is-decrement-visible');
if (dateFns.format(this.end, 'HH') !== this._ui.end.hours.input.value) {
this._ui.end.hours.number.innerText = dateFns.format(this.end, 'HH');
this._ui.end.hours.input.value = dateFns.format(this.end, 'HH');
this._ui.end.hours.number.classList.add('is-decrement-visible');
}
}, 100);
setTimeout(() => {
this._ui.end.minutes.number.classList.remove('is-decrement-hide');
this._ui.end.minutes.number.classList.remove('is-decrement-visible');
}, 1100);
}, 100);
// this.emit('select:end', {
// time: this.time,
// instance: this
// });
}
onNextMinuteEndTimePicker(e) {
if (!this._supportsPassive) {
e.preventDefault();
}
e.stopPropagation();
this.end = dateFns.addMinutes(this.end, this.options.minuteSteps);
setTimeout(() => {
this._ui.end.minutes.number.classList.add('is-increment-hide');
setTimeout(() => {
this._ui.end.minutes.number.innerText = dateFns.format(this.end, 'mm');
this._ui.end.minutes.input.value = dateFns.format(this.end, 'mm');
this._ui.end.minutes.number.classList.add('is-increment-visible');
if (dateFns.format(this.end, 'HH') !== this._ui.end.hours.input.value) {
this._ui.end.hours.number.innerText = dateFns.format(this.end, 'HH');
this._ui.end.hours.input.value = dateFns.format(this.end, 'HH');
this._ui.end.hours.number.classList.add('is-increment-visible');
}
}, 100);
setTimeout(() => {
this._ui.end.minutes.number.classList.remove('is-increment-hide');
this._ui.end.minutes.number.classList.remove('is-increment-visible');
}, 1100);
}, 100);
// this.emit('select:end', {
// time: this.time,
// instance: this
// });
}
/****************************************************
* *
* PUBLIC FUNCTIONS *
* *
****************************************************/
isRange() {
return this.options.isRange;
}
show() {
if (!this._open) {
this._ui.container.classList.add('is-active');
this._open = true;
this._focus = true;
this.emit('show', this);
}
}
hide() {
this._open = false;
this._focus = false;
this._ui.container.classList.remove('is-active');
this.emit('hide', this);
}
toggle() {
if (!this._open) {
this.show();
} else {
this.hide();
}
}
/**
* Get / Set TimePicker value
* @param {null|time|Object|String} time optional if null then return the current time as String
*/
value(value = null) {
if (value) {
if (this.options.isRange) {
if (type.isString(value)) {
const times = value.split(' - ');
if (times.length) {
this.start = dateFns.format(new Date(times[0]), this.format, {
locale: this.locale
});
}
if (times.length === 2) {
this.end = dateFns.format(new Date(times[1]), this.format, {
locale: this.locale
});
}
}
if (type.isObject(value) || type.isDate(value)) {
this._select(value);
}
} else {
this._select(value);
}
} else {
let string = (this.start && this._isValidTime(this.start)) ? dateFns.format(this.start, this.format, {
locale: this.locale
}) : '';
if (this.options.isRange) {
if (this.end && this._isValidTime(this.end)) {
string += ` - ${dateFns.format(this.end, this.format, { locale: this.locale })}`;
}
}
return string;
}
}
/**
* Refresh Timepicker with new year/month days
* @method _refreshTimepicker
* @return {[type]} [description]
*/
refresh() {
this._ui.start.hours.input.value = dateFns.format(this.start, 'HH');
this._ui.start.hours.number.innerText = dateFns.format(this.start, 'HH');
this._ui.start.minutes.input.value = dateFns.format(this.start, 'mm');
this._ui.start.minutes.number.innerText = dateFns.format(this.start, 'mm');
if (this.options.isRange) {
this._ui.end.hours.input.value = dateFns.format(this.end, 'HH');
this._ui.end.hours.number.innerText = dateFns.format(this.end, 'HH');
this._ui.end.minutes.input.value = dateFns.format(this.end, 'mm');
this._ui.end.minutes.number.innerText = dateFns.format(this.end, 'mm');
}
return this;
}
clear() {
this.time = {
start: dateFns.startOfToday(),
end: dateFns.endOfToday()
};
this.refresh();
}
snapshot() {
this._snapshots.push({
...this._time
});
}
render() {
this.refresh();
return this.node;
}
}