2016-03-17 00:15:03 +01:00
|
|
|
import { wrap, getSiblings, isType } from './lib/utils.js';
|
2016-03-16 21:24:11 +01:00
|
|
|
|
2016-03-15 23:42:10 +01:00
|
|
|
(function (root, factory) {
|
|
|
|
if (typeof define === 'function' && define.amd) {
|
|
|
|
define(function() {
|
|
|
|
return factory(root);
|
|
|
|
});
|
|
|
|
} else if (typeof exports === 'object') {
|
|
|
|
module.exports = factory;
|
|
|
|
} else {
|
|
|
|
root.Choices = factory(root);
|
|
|
|
}
|
|
|
|
})(this, function (root) {
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
class Choices {
|
|
|
|
constructor() {
|
2016-03-16 15:41:13 +01:00
|
|
|
const fakeEl = document.createElement("fakeelement");
|
2016-03-16 10:03:59 +01:00
|
|
|
const DEFAULT_OPTIONS = {
|
2016-03-15 23:42:10 +01:00
|
|
|
element: '[data-choice]',
|
|
|
|
disabled: false,
|
2016-03-17 00:15:03 +01:00
|
|
|
maxItems: 5,
|
|
|
|
debug: true,
|
2016-03-16 15:41:13 +01:00
|
|
|
placeholder: false,
|
2016-03-15 23:42:10 +01:00
|
|
|
callbackOnInit: function(){},
|
|
|
|
callbackOnRender: function(){},
|
|
|
|
callbackOnKeyUp: function(){},
|
|
|
|
callbackOnKeyDown: function(){},
|
|
|
|
callbackOnEntry: function(){},
|
|
|
|
callbackOnRemove: function(){}
|
|
|
|
};
|
|
|
|
|
2016-03-16 10:03:59 +01:00
|
|
|
// Merge options with user options
|
|
|
|
this.options = DEFAULT_OPTIONS;
|
2016-03-16 15:41:13 +01:00
|
|
|
this.initialised = false;
|
|
|
|
this.supports = 'querySelector' in document && 'addEventListener' in document && 'classList' in fakeEl;
|
2016-03-16 10:03:59 +01:00
|
|
|
|
|
|
|
// Retrieve elements
|
|
|
|
this.elements = document.querySelectorAll(this.options.element);
|
|
|
|
|
|
|
|
// Bind methods
|
2016-03-15 23:42:10 +01:00
|
|
|
this.onClick = this.onClick.bind(this);
|
|
|
|
this.onKeyDown = this.onKeyDown.bind(this);
|
|
|
|
this.onChange = this.onChange.bind(this);
|
|
|
|
this.onFocus = this.onFocus.bind(this);
|
|
|
|
this.onBlur = this.onChange.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* State */
|
|
|
|
|
|
|
|
isOpen() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
isDisabled() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
isEmpty() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-03-17 00:15:03 +01:00
|
|
|
clearInput(el) {
|
|
|
|
if(el.value) el.value = '';
|
|
|
|
}
|
|
|
|
|
2016-03-15 23:42:10 +01:00
|
|
|
/* Event handling */
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
onKeyDown(e) {
|
2016-03-17 00:15:03 +01:00
|
|
|
// Handle enter key
|
2016-03-16 21:24:11 +01:00
|
|
|
if(e.keyCode === 13 && e.target.value) {
|
2016-03-17 00:15:03 +01:00
|
|
|
let el = e.target;
|
|
|
|
let value = el.value;
|
|
|
|
let list = e.target.parentNode.querySelector('.choice__list');
|
|
|
|
|
|
|
|
let handleEnterKey = () => {
|
|
|
|
this.addItem(el, value, list);
|
|
|
|
this.updateInputValue(el, value);
|
|
|
|
this.clearInput(el);
|
|
|
|
};
|
|
|
|
|
|
|
|
if(this.options.maxItems) {
|
|
|
|
if(this.options.maxItems > list.children.length) {
|
|
|
|
handleEnterKey();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
handleEnterKey();
|
|
|
|
}
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
onFocus(e) {
|
2016-03-17 00:15:03 +01:00
|
|
|
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
onClick(e) {
|
2016-03-17 00:15:03 +01:00
|
|
|
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
onChange(e) {
|
2016-03-17 00:15:03 +01:00
|
|
|
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Event listeners */
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
addEventListeners(el) {
|
|
|
|
el.addEventListener('click', this.onClick);
|
|
|
|
el.addEventListener('keydown', this.onKeyDown);
|
|
|
|
el.addEventListener('change', this.onChange);
|
|
|
|
el.addEventListener('focus', this.onFocus);
|
|
|
|
el.addEventListener('blur', this.onBlur);
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
removeEventListeners(el) {
|
|
|
|
el.removeEventListener('click', this.onClick);
|
|
|
|
el.removeEventListener('keydown', this.onKeyDown);
|
|
|
|
el.removeEventListener('change', this.onChange);
|
|
|
|
el.removeEventListener('focus', this.onFocus);
|
|
|
|
el.removeEventListener('blur', this.onBlur);
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
setPlaceholder() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
setValue() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
getValue() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
getPlaceholder() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
search() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-03-17 00:15:03 +01:00
|
|
|
updateInputValue(el, value) {
|
|
|
|
if(this.options.debug) console.debug('Update input value');
|
|
|
|
// Find hidden element
|
|
|
|
let hiddenInput = el.parentNode.querySelector('.choice__input--hidden');
|
|
|
|
// If input already has values, parse the array, otherwise create a blank array
|
|
|
|
let valueArray = hiddenInput.value !== '' && isType('Array', JSON.parse(hiddenInput.value)) ? JSON.parse(hiddenInput.value) : [];
|
|
|
|
// Push new value to array
|
|
|
|
valueArray.push(value);
|
|
|
|
// Caste array to string and set it as the hidden inputs value
|
|
|
|
hiddenInput.value = JSON.stringify(valueArray);
|
|
|
|
}
|
|
|
|
|
|
|
|
addItem(el, value, list) {
|
|
|
|
if(this.options.debug) console.debug('Add item');
|
|
|
|
|
|
|
|
// Create new list element
|
2016-03-16 21:24:11 +01:00
|
|
|
let item = document.createElement('li');
|
|
|
|
item.classList.add('choice__item');
|
|
|
|
item.textContent = value;
|
2016-03-15 23:42:10 +01:00
|
|
|
|
2016-03-17 00:15:03 +01:00
|
|
|
// Append it to list
|
|
|
|
list.appendChild(item);
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
removeItem() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
removeAllItems() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
createItemList() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
init() {
|
|
|
|
if(!this.supports) console.error('Your browser doesn\'nt support shit');
|
|
|
|
this.initialised = true;
|
|
|
|
|
|
|
|
let els = this.elements;
|
|
|
|
for (let i = els.length - 1; i >= 0; i--) {
|
|
|
|
let el = els[i];
|
|
|
|
this.render(el);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render(el) {
|
2016-03-17 00:15:03 +01:00
|
|
|
if(this.options.debug) console.debug('Render');
|
2016-03-16 15:41:13 +01:00
|
|
|
|
|
|
|
let wrapper = document.createElement('div');
|
2016-03-16 21:24:11 +01:00
|
|
|
let input = document.createElement('input');
|
|
|
|
let list = document.createElement('ul');
|
|
|
|
|
|
|
|
wrapper.classList.add('choice', 'choice--active');
|
2016-03-16 15:41:13 +01:00
|
|
|
|
2016-03-17 00:15:03 +01:00
|
|
|
el.classList.add('choice__input', 'choice__input--hidden');
|
2016-03-16 15:41:13 +01:00
|
|
|
el.tabIndex = '-1';
|
|
|
|
el.setAttribute('style', 'display:none;');
|
2016-03-16 21:32:19 +01:00
|
|
|
el.setAttribute('aria-hidden', 'true');
|
2016-03-16 15:41:13 +01:00
|
|
|
|
|
|
|
wrap(el, wrapper);
|
|
|
|
|
2016-03-16 21:24:11 +01:00
|
|
|
list.classList.add('choice__list');
|
|
|
|
|
2016-03-17 00:15:03 +01:00
|
|
|
if(el.value !== '') {
|
|
|
|
let valueArray = JSON.parse(el.value);
|
|
|
|
valueArray.map((v) => {
|
|
|
|
this.addItem(el, v, list);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
input.type = 'text';
|
|
|
|
input.classList.add('choice__input', 'choice__input--cloned');
|
2016-03-16 21:24:11 +01:00
|
|
|
|
|
|
|
wrapper.appendChild(list);
|
2016-03-16 15:41:13 +01:00
|
|
|
wrapper.appendChild(input);
|
|
|
|
|
2016-03-16 21:24:11 +01:00
|
|
|
this.addEventListeners(input);
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
destroy() {
|
2016-03-16 15:41:13 +01:00
|
|
|
this.options = null;
|
|
|
|
this.elements = null;
|
|
|
|
|
|
|
|
let els = this.elements;
|
|
|
|
|
|
|
|
for (let i = els.length - 1; i >= 0; i--) {
|
|
|
|
let el = els[i];
|
2016-03-15 23:42:10 +01:00
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
this.removeEventListeners(el);
|
|
|
|
}
|
2016-03-15 23:42:10 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-16 15:41:13 +01:00
|
|
|
var choices = new Choices();
|
|
|
|
choices.init();
|
2016-03-15 23:42:10 +01:00
|
|
|
});
|