Handle on one element per instance + remove list items on back space

This commit is contained in:
Josh Johnson 2016-03-17 15:00:22 +00:00
parent 8ea38d8d6b
commit ecee8cff47
3 changed files with 273 additions and 228 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,24 +1,13 @@
'use strict';
import { wrap, getSiblings, isType } from './lib/utils.js'; import { wrap, getSiblings, isType } from './lib/utils.js';
(function (root, factory) { class Choices {
if (typeof define === 'function' && define.amd) { constructor(options) {
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() {
const fakeEl = document.createElement("fakeelement"); const fakeEl = document.createElement("fakeelement");
const USER_OPTIONS = options || {};
const DEFAULT_OPTIONS = { const DEFAULT_OPTIONS = {
element: '[data-choice]', element: document.querySelector('[data-choice]'),
disabled: false, disabled: false,
maxItems: 5, maxItems: 5,
debug: true, debug: true,
@ -31,13 +20,17 @@ import { wrap, getSiblings, isType } from './lib/utils.js';
callbackOnRemove: function(){} callbackOnRemove: function(){}
}; };
// Merge options with user options // Merge options with user options
this.options = DEFAULT_OPTIONS; this.options = this.extend(DEFAULT_OPTIONS, USER_OPTIONS || {});
this.initialised = false; this.initialised = false;
this.supports = 'querySelector' in document && 'addEventListener' in document && 'classList' in fakeEl; this.supports = 'querySelector' in document && 'addEventListener' in document && 'classList' in fakeEl;
// Retrieve elements // Retrieve elements
this.elements = document.querySelectorAll(this.options.element); this.element = this.options.element;
// If input already has values, parse the array, otherwise create a blank array
this.valueArray = this.element.value !== '' && isType('Array', JSON.parse(this.element.value)) ? JSON.parse(this.element.value) : [];
// Bind methods // Bind methods
this.onClick = this.onClick.bind(this); this.onClick = this.onClick.bind(this);
@ -47,6 +40,41 @@ import { wrap, getSiblings, isType } from './lib/utils.js';
this.onBlur = this.onChange.bind(this); this.onBlur = this.onChange.bind(this);
} }
/**
* Merges unspecified amount of objects into new object
* @private
* @return {Object} Merged object of arguments
*/
extend() {
let extended = {};
let length = arguments.length;
/**
* Merge one object into another
* @param {Object} obj Object to merge into extended object
*/
let merge = function(obj) {
for(let prop in obj) {
extended[prop] = obj[prop];
}
};
// Loop through each passed argument
for(let i = 0; i < length; i++) {
// Store argument at position i
let obj = arguments[i];
// If we are in fact dealing with an object, merge it. Otherwise throw error
if(isType('Object', obj)) {
merge(obj);
} else {
console.error('Custom options must be an object');
}
}
return extended;
};
/* State */ /* State */
isOpen() { isOpen() {
@ -61,8 +89,8 @@ import { wrap, getSiblings, isType } from './lib/utils.js';
} }
clearInput(el) { clearInput() {
if(el.value) el.value = ''; if(this.input.value) this.input.value = '';
} }
/* Event handling */ /* Event handling */
@ -70,24 +98,34 @@ import { wrap, getSiblings, isType } from './lib/utils.js';
onKeyDown(e) { onKeyDown(e) {
// Handle enter key // Handle enter key
if(e.keyCode === 13 && e.target.value) { if(e.keyCode === 13 && e.target.value) {
let el = e.target; let value = this.input.value;
let value = el.value;
let list = e.target.parentNode.querySelector('.choice__list');
let handleEnterKey = () => { let handleEnterKey = () => {
this.addItem(el, value, list); this.addItem(value);
this.updateInputValue(el, value); this.updateInputValue(value);
this.clearInput(el); this.clearInput(this.element);
}; };
if(this.options.maxItems) { if(this.options.maxItems) {
if(this.options.maxItems > list.children.length) { if(this.options.maxItems > this.list.children.length) {
handleEnterKey(); handleEnterKey();
} }
} else { } else {
handleEnterKey(); handleEnterKey();
} }
} }
if(e.keyCode === 8 && !e.target.value) {
let handleBackspaceKey = () => {
let lastItem = this.list.children[this.list.children.length - 1];
lastItem.parentNode.removeChild(lastItem);
};
handleBackspaceKey();
e.preventDefault();
}
} }
onFocus(e) { onFocus(e) {
@ -142,19 +180,16 @@ import { wrap, getSiblings, isType } from './lib/utils.js';
} }
updateInputValue(el, value) { updateInputValue(value) {
if(this.options.debug) console.debug('Update input 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 // Push new value to array
valueArray.push(value); this.valueArray.push(value);
// Caste array to string and set it as the hidden inputs value // Caste array to string and set it as the hidden inputs value
hiddenInput.value = JSON.stringify(valueArray); this.element.value = JSON.stringify(this.valueArray);
} }
addItem(el, value, list) { addItem(value) {
if(this.options.debug) console.debug('Add item'); if(this.options.debug) console.debug('Add item');
// Create new list element // Create new list element
@ -163,7 +198,7 @@ import { wrap, getSiblings, isType } from './lib/utils.js';
item.textContent = value; item.textContent = value;
// Append it to list // Append it to list
list.appendChild(item); this.list.appendChild(item);
} }
removeItem() { removeItem() {
@ -182,61 +217,71 @@ import { wrap, getSiblings, isType } from './lib/utils.js';
if(!this.supports) console.error('Your browser doesn\'nt support shit'); if(!this.supports) console.error('Your browser doesn\'nt support shit');
this.initialised = true; this.initialised = true;
let els = this.elements; this.render(this.element);
for (let i = els.length - 1; i >= 0; i--) {
let el = els[i];
this.render(el);
}
} }
render(el) { render() {
if(this.options.debug) console.debug('Render'); if(this.options.debug) console.debug('Render');
let wrapper = document.createElement('div'); // Create DOM elements
let container = document.createElement('div');
let input = document.createElement('input'); let input = document.createElement('input');
let list = document.createElement('ul'); let list = document.createElement('ul');
wrapper.classList.add('choice', 'choice--active'); container.className = 'choice choice--active';
el.classList.add('choice__input', 'choice__input--hidden'); // Hide passed input
el.tabIndex = '-1'; this.element.classList.add('choice__input', 'choice__input--hidden');
el.setAttribute('style', 'display:none;'); this.element.tabIndex = '-1';
el.setAttribute('aria-hidden', 'true'); this.element.setAttribute('style', 'display:none;');
this.element.setAttribute('aria-hidden', 'true');
wrap(el, wrapper); // Wrap input in container
wrap(this.element, container);
list.classList.add('choice__list'); list.className = 'choice__list';
if(el.value !== '') { input.type = 'text';
let valueArray = JSON.parse(el.value); input.placeholder = this.element.placeholder;
valueArray.map((v) => { input.className = 'choice__input choice__input--cloned';
this.addItem(el, v, list);
container.appendChild(list);
container.appendChild(input);
this.container = container;
this.input = input;
this.list = list;
if(this.element.value !== '') {
let initialValues = JSON.parse(this.element.value);
initialValues.forEach((value) => {
this.addItem(value);
}); });
} }
input.type = 'text'; this.addEventListeners(this.input);
input.classList.add('choice__input', 'choice__input--cloned');
wrapper.appendChild(list);
wrapper.appendChild(input);
this.addEventListeners(input);
} }
destroy() { destroy() {
this.options = null; this.options = null;
this.elements = null; this.element = null;
let els = this.elements; this.removeEventListeners(this.input);
for (let i = els.length - 1; i >= 0; i--) {
let el = els[i];
this.removeEventListeners(el);
} }
} };
};
window.addEventListener('load', function() {
let choiceInputs = document.querySelectorAll('[data-choice]');
for (let i = choiceInputs.length - 1; i >= 0; i--) {
let input = choiceInputs[i];
var choices = new Choices({
element : input
});
var choices = new Choices();
choices.init(); choices.init();
};
}); });

View file

@ -5,8 +5,8 @@
<title>Choices</title> <title>Choices</title>
</head> </head>
<body> <body>
<input id="1" type="text" data-choice value='["preset-1", "preset-2"]'> <input id="1" type="text" data-choice value='["preset-1", "preset-2"]' placeholder="This is a placeholder" class="custom class">
<input id="2" type="text" data-choice> <input id="2" type="text" data-choice >
<script src="assets/scripts/dist/bundle.js"></script> <script src="assets/scripts/dist/bundle.js"></script>
</body> </body>
</html> </html>