From 174aa399c2bd1b162708a679e483a940852ce65f Mon Sep 17 00:00:00 2001 From: Josh Johnson Date: Sun, 3 Apr 2016 23:07:10 +0100 Subject: [PATCH] Handle click events on list items + set active status in store + set select status in store --- assets/scripts/dist/bundle.js | 2 +- assets/scripts/src/actions/index.js | 13 +- assets/scripts/src/choices.js | 371 +++++++++++---------------- assets/scripts/src/lib/utils.js | 56 ++-- assets/scripts/src/reducers/index.js | 23 +- index.html | 6 +- 6 files changed, 213 insertions(+), 258 deletions(-) diff --git a/assets/scripts/dist/bundle.js b/assets/scripts/dist/bundle.js index 3a12f2a..c2ede6e 100644 --- a/assets/scripts/dist/bundle.js +++ b/assets/scripts/dist/bundle.js @@ -1 +1 @@ -!function(e){function t(i){if(n[i])return n[i].exports;var r=n[i]={exports:{},id:i,loaded:!1};return e[i].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/assets/scripts/dist/",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0}),t.Choices=void 0;var o=function(){function e(e,t){for(var n=0;ni;i++){var r=arguments[i];(0,c.isType)("Object",r)?n(r):console.error("Custom options must be an object")}return e}},{key:"isOpen",value:function(){}},{key:"isDisabled",value:function(){}},{key:"isEmpty",value:function(){return 0===this.valueCount.length}},{key:"clearInput",value:function(){this.input.value&&(this.input.value="")}},{key:"onKeyUp",value:function(e){}},{key:"onKeyDown",value:function(e){var t=this,n=e.ctrlKey||e.metaKey,i=8,r=13,o=65;if(n&&e.keyCode===o&&this.list&&this.list.children){var s=function(){t.options.removeItems&&!t.input.value&&t.selectAll(t.list.children)};s()}if(e.keyCode===r&&e.target.value&&!function(){var e=t.input.value,n=function(){var n=!0;if(t.options.maxItems&&t.options.maxItems<=t.list.children.length&&(n=!1),t.options.allowDuplicates===!1&&t.element.value&&t.valueArray.indexOf(e)>-1&&(n=!1),n&&"text"===t.element.type){var i=!0;t.options.regexFilter&&(i=t.regexFilter(e)),i&&(t.addItem(t.list,e),t.updateInputValue(e),t.clearInput(t.element),t.unselectAll(t.list.children))}};n()}(),e.keyCode===i&&!e.target.value){var a=function(){if(t.options.removeItems){var e=t.list.querySelectorAll(".choices__item"),n=t.list.querySelectorAll(".is-selected"),i=e[e.length-1];i&&i.classList.add("is-selected"),t.options.editItems&&i&&n.length<=1?(t.input.value=i.innerHTML,t.removeItem(i)):t.removeAll(e)}};a(),e.preventDefault()}}},{key:"onFocus",value:function(e){}},{key:"onClick",value:function(e){}},{key:"onChange",value:function(e){}},{key:"addEventListeners",value:function(e){e.addEventListener("click",this.onClick),e.addEventListener("keyup",this.onKeyUp),e.addEventListener("keydown",this.onKeyDown),e.addEventListener("change",this.onChange),e.addEventListener("focus",this.onFocus),e.addEventListener("blur",this.onBlur)}},{key:"removeEventListeners",value:function(e){e.removeEventListener("click",this.onClick),e.removeEventListener("keyup",this.onKeyUp),e.removeEventListener("keydown",this.onKeyDown),e.removeEventListener("change",this.onChange),e.removeEventListener("focus",this.onFocus),e.removeEventListener("blur",this.onBlur)}},{key:"setValue",value:function(){}},{key:"getValue",value:function(){}},{key:"getValues",value:function(){}},{key:"regexFilter",value:function(e){var t=new RegExp(this.options.regexFilter,"i"),n=t.test(e);return n}},{key:"getPlaceholder",value:function(){}},{key:"selectAll",value:function(e){for(var t=0;t()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/}),new d({element:i,addItems:!1}),new d({element:r,prependValue:"item-",appendValue:"-"+Date.now()})}()},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0,t.compose=t.applyMiddleware=t.bindActionCreators=t.combineReducers=t.createStore=void 0;var r=n(3),o=i(r),s=n(7),a=i(s),u=n(9),l=i(u),c=n(10),d=i(c),f=n(11),h=i(f),p=n(8);i(p);t.createStore=o.default,t.combineReducers=a.default,t.bindActionCreators=l.default,t.applyMiddleware=d.default,t.compose=h.default},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}function r(e,t,n){function i(){p===h&&(p=h.slice())}function o(){return f}function u(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return i(),p.push(e),function(){if(t){t=!1,i();var n=p.indexOf(e);p.splice(n,1)}}}function l(e){if(!(0,s.default)(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if("undefined"==typeof e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(v)throw new Error("Reducers may not dispatch actions.");try{v=!0,f=d(f,e)}finally{v=!1}for(var t=h=p,n=0;nn;n++)t[n]=arguments[n];return function(e){return function(n,i,r){var s=e(n,i,r),u=s.dispatch,l=[],c={getState:s.getState,dispatch:function(e){return u(e)}};return l=t.map(function(e){return e(c)}),u=a.default.apply(void 0,l)(s.dispatch),o({},s,{dispatch:u})}}}var o=Object.assign||function(e){for(var t=1;tn;n++)t[n]=arguments[n];return function(){if(0===t.length)return arguments.length<=0?void 0:arguments[0];var e=t[t.length-1],n=t.slice(0,-1);return n.reduceRight(function(e,t){return t(e)},e.apply(void 0,arguments))}}t.__esModule=!0,t.default=n},function(e,t){"use strict";function n(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t=0?t:0},t.getElementOffset=function(e,t){var n=t;return n>1&&(n=1),n>0&&(n=0),Math.max(e.offsetHeight*n)},t.getScrollPosition=function(e){return"bottom"===e?Math.max((window.scrollY||window.pageYOffset)+(window.innerHeight||document.documentElement.clientHeight)):window.scrollY||window.pageYOffset},t.isInView=function(e,t,n){return this.getScrollPosition(t)>this.getElemDistance(e)+this.getElementOffset(e,n)},t.stripHTML=function(e){var t=document.createElement("DIV");return t.innerHTML=e,t.textContent||t.innerText||""},t.addAnimation=function(e,t){var i=n(),r=function o(){e.classList.remove(t),e.removeEventListener(i,o,!1)};e.classList.add(t),e.addEventListener(i,r,!1)},t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)}}]); \ No newline at end of file +!function(e){function t(i){if(n[i])return n[i].exports;var r=n[i]={exports:{},id:i,loaded:!1};return e[i].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/assets/scripts/dist/",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0}),t.Choices=void 0;var o=function(){function e(e,t){for(var n=0;ni;i++){var r=arguments[i];(0,l.isType)("Object",r)?n(r):console.error("Custom options must be an object")}return e}},{key:"isOpen",value:function(){}},{key:"isDisabled",value:function(){}},{key:"isEmpty",value:function(){return 0===this.valueCount.length}},{key:"clearInput",value:function(){this.input.value&&(this.input.value="")}},{key:"onKeyUp",value:function(e){}},{key:"onKeyDown",value:function(e){var t=this,n=e.ctrlKey||e.metaKey,i=8,r=13,o=65;if(e.target===this.input){if(n&&e.keyCode===o&&this.list&&this.list.children){var a=function(){t.options.removeItems&&!t.input.value&&t.options.selectAll&&t.selectAll(t.list.children)};a()}e.keyCode===r&&e.target.value&&!function(){var e=t.input.value,n=function(){var n=!0;if(t.options.maxItems&&t.options.maxItems<=t.list.children.length&&(n=!1),t.options.allowDuplicates===!1&&t.element.value&&t.valueArray.indexOf(e)>-1&&(n=!1),n&&"text"===t.element.type){var i=!0;t.options.regexFilter&&(i=t.regexFilter(e)),i&&(t.addItem(t.list,e),t.updateInputValue(e),t.clearInput(t.element))}};n()}()}if(e.keyCode===i&&!e.target.value){var s=function(){if(t.options.removeItems){var e=t.list.querySelectorAll(".choices__item"),n=t.list.querySelectorAll(".is-selected"),i=e[e.length-1];i&&!t.options.editItems&&t.selectItem(i),t.options.editItems&&i&&1===n.length?(t.input.value=i.innerHTML,t.removeItem(i)):t.removeAll(e)}};s(),e.preventDefault()}}},{key:"onClick",value:function(e){var t=this;if("LI"===e.target.tagName){var n=e.target,i=function(e){for(var n=e.getAttribute("data-choice-id"),i=t.list.children,r=0;r'),n=(0,l.strToEl)('
');this.element.classList.add("choices__input","choices__input--hidden"),this.element.tabIndex="-1",this.element.setAttribute("style","display:none;"),this.element.setAttribute("aria-hidden","true"),(0,l.wrap)(this.element,n),(0,l.wrap)(n,t);var i=(0,l.strToEl)('
    '),r=(0,l.strToEl)('');r.placeholder&&(r.placeholder=this.element.placeholder),this.options.addItems||(r.disabled=!0),n.appendChild(i),n.appendChild(r),t.appendChild(n),this.containerOuter=t,this.containerInner=n,this.input=r,this.list=i,""!==this.element.value&&this.valueArray.forEach(function(t){e.addItem(e.list,t)}),document.addEventListener("keydown",this.onKeyDown),this.list.addEventListener("click",this.onClick)}},{key:"render",value:function(){switch(this.options.debug&&console.debug("Render"),this.element.type){case"text":this.renderTextInput();break;case"select-one":break;case"select-multiple":break;default:this.renderTextInput()}}},{key:"destroy",value:function(){this.options=null,this.element=null,this.initialised=null,this.removeEventListeners(this.input)}}]),e}();!function(){var e=document.getElementById(1);document.getElementById(2),document.getElementById(3),document.getElementById(4),document.getElementById(5),new d({element:e})}()},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0,t.compose=t.applyMiddleware=t.bindActionCreators=t.combineReducers=t.createStore=void 0;var r=n(3),o=i(r),a=n(8),s=i(a),u=n(10),c=i(u),l=n(11),d=i(l),f=n(12),p=i(f),h=n(9);i(h);t.createStore=o.default,t.combineReducers=s.default,t.bindActionCreators=c.default,t.applyMiddleware=d.default,t.compose=p.default},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}function r(e,t,n){function i(){h===p&&(h=p.slice())}function o(){return f}function u(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return i(),h.push(e),function(){if(t){t=!1,i();var n=h.indexOf(e);h.splice(n,1)}}}function c(e){if(!(0,a.default)(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if("undefined"==typeof e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(v)throw new Error("Reducers may not dispatch actions.");try{v=!0,f=d(f,e)}finally{v=!1}for(var t=p=h,n=0;nn;n++)t[n]=arguments[n];return function(e){return function(n,i,r){var a=e(n,i,r),u=a.dispatch,c=[],l={getState:a.getState,dispatch:function(e){return u(e)}};return c=t.map(function(e){return e(l)}),u=s.default.apply(void 0,c)(a.dispatch),o({},a,{dispatch:u})}}}var o=Object.assign||function(e){for(var t=1;tn;n++)t[n]=arguments[n];return function(){if(0===t.length)return arguments.length<=0?void 0:arguments[0];var e=t[t.length-1],n=t.slice(0,-1);return n.reduceRight(function(e,t){return t(e)},e.apply(void 0,arguments))}}t.__esModule=!0,t.default=n},function(e,t){"use strict";function n(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t=0?t:0},t.getElementOffset=function(e,t){var n=t;return n>1&&(n=1),n>0&&(n=0),Math.max(e.offsetHeight*n)},t.getScrollPosition=function(e){return"bottom"===e?Math.max((window.scrollY||window.pageYOffset)+(window.innerHeight||document.documentElement.clientHeight)):window.scrollY||window.pageYOffset},t.isInView=function(e,t,n){return this.getScrollPosition(t)>this.getElemDistance(e)+this.getElementOffset(e,n)},t.stripHTML=function(e){var t=document.createElement("DIV");return t.innerHTML=e,t.textContent||t.innerText||""},t.addAnimation=function(e,t){var i=n(),r=function o(){e.classList.remove(t),e.removeEventListener(i,o,!1)};e.classList.add(t),e.addEventListener(i,r,!1)},t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.strToEl=function(){var e=document.createElement("div");return function(t){var n;for(e.innerHTML=t,n=e.children[0];e.firstChild;)e.removeChild(e.firstChild);return n}}()}]); \ No newline at end of file diff --git a/assets/scripts/src/actions/index.js b/assets/scripts/src/actions/index.js index 9b2614b..f1f3f06 100644 --- a/assets/scripts/src/actions/index.js +++ b/assets/scripts/src/actions/index.js @@ -5,18 +5,19 @@ export const addItemToStore = (value, element, id) => { element: element, id: id } -} +}; export const removeItemFromStore = (id) => { return { type: 'REMOVE_ITEM', - id: id, + id: id } -} +}; -export const updateItemInStore = (value) => { +export const selectItemFromStore = (id, value) => { return { - type: 'UPDATE_ITEM', + type: 'SELECT_ITEM', + id: id, value: value } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index 7613412..6b6630f 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -2,8 +2,8 @@ import { createStore } from 'redux'; import choices from './reducers/index.js'; -import { addItemToStore, removeItemFromStore } from './actions/index'; -import { hasClass, wrap, getSiblings, isType } from './lib/utils.js'; +import { addItemToStore, removeItemFromStore, selectItemFromStore } from './actions/index'; +import { hasClass, wrap, getSiblings, isType, strToEl } from './lib/utils.js'; /** @@ -18,10 +18,10 @@ import { hasClass, wrap, getSiblings, isType } from './lib/utils.js'; export class Choices { constructor(options) { - const FAKE_EL = document.createElement("fakeel"); - const USER_OPTIONS = options || {}; - const STORE = createStore(choices); - const DEFAULT_OPTIONS = { + const fakeEl = document.createElement("fakeel"); + const userOptions = options || {}; + const store = createStore(choices); + const defaultOptions = { element: document.querySelector('[data-choice]'), disabled: false, addItems: true, @@ -35,6 +35,7 @@ export class Choices { placeholder: false, prependValue: false, appendValue: false, + selectAll: true, callbackOnInit: function() {}, callbackOnRender: function() {}, callbackOnRemoveItem: function() {}, @@ -42,27 +43,25 @@ export class Choices { }; // Merge options with user options - this.options = this.extend(DEFAULT_OPTIONS, USER_OPTIONS || {}); - this.store = STORE; + this.options = this.extend(defaultOptions, userOptions || {}); + this.store = store; this.initialised = false; - this.supports = 'querySelector' in document && 'addEventListener' in document && 'classList' in FAKE_EL; + this.supports = 'querySelector' in document && 'addEventListener' in document && 'classList' in fakeEl; - // Retrieve elements + // Retrieve triggering element (i.e. element with 'data-choice' trigger) this.element = this.options.element; // If input already has values, parse the array, otherwise create a blank array + // Hmm, this should really map this.store this.valueArray = this.element.value !== '' ? this.cleanInputValue(this.element.value) : []; // How many values in array this.valueCount = this.valueArray.length; // Bind methods - 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); + this.onClick = this.onClick.bind(this); this.init(); } @@ -93,7 +92,7 @@ export class Choices { // Loop through each passed argument for (let i = 0; i < length; i++) { - // Store argument at position i + // store argument at position i let obj = arguments[i]; // If we are in fact dealing with an object, merge it. Otherwise throw error @@ -131,74 +130,74 @@ export class Choices { } onKeyDown(e) { - const CTRLDOWN_KEY = e.ctrlKey || e.metaKey; - const DELETE_KEY = 8 || 46; - const ENTER_KEY = 13; - const A_KEY = 65; + const ctrlDownKey = e.ctrlKey || e.metaKey; + const deleteKey = 8 || 46; + const enterKey = 13; + const aKey = 65; - // If CTRL + A or CMD + A have been pressed and there are items to select - if (CTRLDOWN_KEY && e.keyCode === A_KEY && this.list && this.list.children) { - let handleSelectAll = () => { - if(this.options.removeItems && !this.input.value) { - this.selectAll(this.list.children); - } - }; + // If we are typing in the input + if(e.target === this.input) { + // If CTRL + A or CMD + A have been pressed and there are items to select + if (ctrlDownKey && e.keyCode === aKey && this.list && this.list.children) { + let handleSelectAll = () => { + if(this.options.removeItems && !this.input.value && this.options.selectAll) { + this.selectAll(this.list.children); + } + }; - handleSelectAll(); - } + handleSelectAll(); + } - // If enter key is pressed and the input has a value - if (e.keyCode === ENTER_KEY && e.target.value) { - let value = this.input.value; + // If enter key is pressed and the input has a value + if (e.keyCode === enterKey && e.target.value) { + let value = this.input.value; - let handleEnter = () => { - let canUpdate = true; + let handleEnter = () => { + let canUpdate = true; - // If there is a max entry limit and we have reached that limit - // don't update - if (this.options.maxItems && this.options.maxItems <= this.list.children.length) { - canUpdate = false; - } - - // If no duplicates are allowed, and the value already exists - // in the array, don't update - if (this.options.allowDuplicates === false && this.element.value) { - if (this.valueArray.indexOf(value) > -1) { + // If there is a max entry limit and we have reached that limit + // don't update + if (this.options.maxItems && this.options.maxItems <= this.list.children.length) { canUpdate = false; } - } - // All is good, update - if (canUpdate) { - if(this.element.type === 'text') { - let canAddItem = true; + // If no duplicates are allowed, and the value already exists + // in the array, don't update + if (this.options.allowDuplicates === false && this.element.value) { + if (this.valueArray.indexOf(value) > -1) { + canUpdate = false; + } + } - // If a user has supplied a regular expression filter - if(this.options.regexFilter) { - // Determine whether we can update based on whether - // our regular expression passes - canAddItem = this.regexFilter(value); + // All is good, update + if (canUpdate) { + if(this.element.type === 'text') { + let canAddItem = true; + + // If a user has supplied a regular expression filter + if(this.options.regexFilter) { + // Determine whether we can update based on whether + // our regular expression passes + canAddItem = this.regexFilter(value); + } + + // All is good, add + if(canAddItem) { + this.addItem(this.list, value); + this.updateInputValue(value); + this.clearInput(this.element); + } } - // All is good, add - if(canAddItem) { - this.addItem(this.list, value); - this.updateInputValue(value); - this.clearInput(this.element); - this.unselectAll(this.list.children); - } - } else { - } - - } - }; + }; - handleEnter(); + handleEnter(); + } } // If backspace or delete key is pressed and the input has no value - if (e.keyCode === DELETE_KEY && !e.target.value) { + if (e.keyCode === deleteKey && !e.target.value) { let handleBackspaceKey = () => { if(this.options.removeItems) { @@ -206,14 +205,14 @@ export class Choices { let selectedItems = this.list.querySelectorAll('.is-selected'); let lastItem = currentListItems[currentListItems.length - 1]; - if(lastItem) { - lastItem.classList.add('is-selected'); + if(lastItem && !this.options.editItems) { + this.selectItem(lastItem); } // If editing the last item is allowed and there is a last item and // there are not other selected items (minus the last item), we can edit // the item value. Otherwise if we can remove items, remove all items - if(this.options.editItems && lastItem && selectedItems.length <= 1) { + if(this.options.editItems && lastItem && selectedItems.length === 1) { this.input.value = lastItem.innerHTML; this.removeItem(lastItem); } else { @@ -228,36 +227,30 @@ export class Choices { } } - onFocus(e) { - - } - onClick(e) { + if(e.target.tagName === 'LI') { + let item = e.target; - } + let handleClick = (item) => { + let passedId = item.getAttribute('data-choice-id'); + let items = this.list.children; - onChange(e) { + // We only want to select one item with a click + // so we unselect any items that aren't the target + for (var i = 0; i < items.length; i++) { + let singleItem = items[i]; + let id = singleItem.getAttribute('data-choice-id');; - } - - /* Event listeners */ - - addEventListeners(el) { - el.addEventListener('click', this.onClick); - el.addEventListener('keyup', this.onKeyUp); - el.addEventListener('keydown', this.onKeyDown); - el.addEventListener('change', this.onChange); - el.addEventListener('focus', this.onFocus); - el.addEventListener('blur', this.onBlur); - } - - removeEventListeners(el) { - el.removeEventListener('click', this.onClick); - el.removeEventListener('keyup', this.onKeyUp); - el.removeEventListener('keydown', this.onKeyDown); - el.removeEventListener('change', this.onChange); - el.removeEventListener('focus', this.onFocus); - el.removeEventListener('blur', this.onBlur); + if(id === passedId && !singleItem.classList.contains('is-selected')) { + this.selectItem(singleItem); + } else { + this.unselectItem(singleItem); + } + } + } + + handleClick(item); + } } /* Methods */ @@ -274,26 +267,24 @@ export class Choices { return passesTest; } - getPlaceholder() {} + selectItem(item) { + let id = item.getAttribute('data-choice-id'); + item.classList.add('is-selected'); + this.store.dispatch(selectItemFromStore(id, true)); + console.log(this.store.getState()); + } + + unselectItem(item) { + let id = item.getAttribute('data-choice-id'); + item.classList.remove('is-selected'); + this.store.dispatch(selectItemFromStore(id, false)); + console.log(this.store.getState()); + } selectAll(items) { for (let i = 0; i < items.length; i++) { let item = items[i]; - - if (!item.classList.contains('is-selected')) { - item.classList.add('is-selected'); - } - }; - } - - - unselectAll(items) { - for (let i = 0; i < items.length; i++) { - let item = items[i]; - - if (item.classList.contains('is-selected')) { - item.classList.remove('is-selected'); - } + this.selectItem(item); }; } @@ -333,11 +324,8 @@ export class Choices { let id = this.store.getState().length + 1; - // Create new list element - let item = document.createElement('li'); - item.classList.add('choices__item'); - item.textContent = passedValue; - item.id = id; + // Create new list element + let item = strToEl(`
  • ${passedValue}
  • `); // Append it to list parent.appendChild(item); @@ -361,7 +349,7 @@ export class Choices { return; } - let id = item.id; + let id = item.getAttribute('data-choice-id'); let value = item.innerHTML; item.parentNode.removeChild(item); @@ -396,21 +384,20 @@ export class Choices { } renderTextInput() { - // Template: - // - //
    - //
    - // - //
      - // - //
      - //
      + /* + Template: - let containerOuter = document.createElement('div'); - containerOuter.className = 'choices choices--active'; +
      +
      + +
        + +
        +
        + */ - let containerInner = document.createElement('div'); - containerInner.className = 'choices__inner'; + let containerOuter = strToEl('
        '); + let containerInner = strToEl('
        '); // Hide passed input this.element.classList.add('choices__input', 'choices__input--hidden'); @@ -424,12 +411,8 @@ export class Choices { // Wrapper inner container with outer container wrap(containerInner, containerOuter); - let list = document.createElement('ul'); - list.className = 'choices__list choices__list--items'; - - let input = document.createElement('input'); - input.type = 'text'; - input.className = 'choices__input choices__input--cloned'; + let list = strToEl('
          '); + let input = strToEl(''); if (input.placeholder) { input.placeholder = this.element.placeholder; @@ -456,70 +439,10 @@ export class Choices { } // Trigger event listeners - this.addEventListeners(this.input); + document.addEventListener('keydown', this.onKeyDown); + this.list.addEventListener('click', this.onClick); } - renderSelectInput() { - let containerOuter = document.createElement('div'); - containerOuter.className = 'choices choices--active'; - - let containerInner = document.createElement('div'); - containerInner.className = 'choices__inner'; - - // Hide passed input - this.element.classList.add('choices__input', 'choices__input--hidden'); - this.element.tabIndex = '-1'; - this.element.setAttribute('style', 'display:none;'); - this.element.setAttribute('aria-hidden', 'true'); - - // Wrap input in container preserving DOM ordering - wrap(this.element, containerInner); - - // Wrapper inner container with outer container - wrap(containerInner, containerOuter); - - let options = document.createElement('ul'); - options.className = 'choices__list choices__list--options'; - - let input = document.createElement('input'); - input.type = 'text'; - input.className = 'choices__input choices__input--cloned'; - - containerInner.appendChild(input); - containerInner.appendChild(options); - containerOuter.appendChild(containerInner); - - this.containerOuter = containerOuter; - this.containerInner = containerInner; - this.input = input; - this.list = null; - this.options = options; - - let initialOptions = this.element.children; - - if (initialOptions) { - for (let i = 0; i < initialOptions.length; i++) { - let parentOption = initialOptions[i]; - - if(parentOption.tagName === 'OPTGROUP') { - this.addItem(this.options, parentOption.label); - for (let j = 0; j < parentOption.children.length; j++) { - let childOption = parentOption.children[j]; - this.addItem(this.options, childOption.innerHTML); - } - } else if(parentOption.tagName === 'OPTION') { - this.addItem(this.options, parentOption.innerHTML); - } - } - } - - // Trigger event listeners - this.addEventListeners(this.input); - } - - renderMultipleSelectInput() { - - } render() { if (this.options.debug) console.debug('Render'); @@ -529,13 +452,13 @@ export class Choices { this.renderTextInput(); break; case "select-one": - this.renderSelectInput(); + // this.renderSelectInput(); break; case "select-multiple": - this.renderMultipleSelectInput(); + // this.renderMultipleSelectInput(); break; default: - rthis.renderTextInput(); + this.renderTextInput(); break; } @@ -558,8 +481,8 @@ export class Choices { let choices1 = new Choices({ element : input1, - delimiter: ' ', - maxItems: 5, + // delimiter: ' ', + // maxItems: 5, // callbackOnRemoveItem: function(value) { // console.log(value); // }, @@ -568,27 +491,27 @@ export class Choices { // } }); - let choices2 = new Choices({ - element : input2, - allowDuplicates: false, - editItems: true, - }); + // let choices2 = new Choices({ + // element : input2, + // allowDuplicates: false, + // editItems: true, + // }); - let choices3 = new Choices({ - element : input3, - allowDuplicates: false, - editItems: true, - regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ - }); + // let choices3 = new Choices({ + // element : input3, + // allowDuplicates: false, + // editItems: true, + // regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + // }); - let choices4 = new Choices({ - element : input4, - addItems: false - }); + // let choices4 = new Choices({ + // element : input4, + // addItems: false + // }); - let choices5 = new Choices({ - element: input5, - prependValue: 'item-', - appendValue: `-${Date.now()}` - }); + // let choices5 = new Choices({ + // element: input5, + // prependValue: 'item-', + // appendValue: `-${Date.now()}` + // }); })(); \ No newline at end of file diff --git a/assets/scripts/src/lib/utils.js b/assets/scripts/src/lib/utils.js index a0543c1..9b372b7 100644 --- a/assets/scripts/src/lib/utils.js +++ b/assets/scripts/src/lib/utils.js @@ -1,4 +1,4 @@ -export let hasClass = (elem, className) => { +export const hasClass = (elem, className) => { return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' '); } @@ -7,7 +7,7 @@ export let hasClass = (elem, className) => { * @param {String} str String to capitalise * @return {String} Capitalised string */ -export let capitalise = function(str) { +export const capitalise = function(str) { return str.replace(/\w\S*/g, function(txt){ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); @@ -19,7 +19,7 @@ export let capitalise = function(str) { * @param {Object} obj Object to be tested * @return {Boolean} */ -export let isType = function(type, obj) { +export const isType = function(type, obj) { var clas = Object.prototype.toString.call(obj).slice(8, -1); return obj !== undefined && obj !== null && clas === type; }; @@ -29,7 +29,7 @@ export let isType = function(type, obj) { * CSS transition end event listener * @return */ -export let whichTransitionEvent = function(){ +export const whichTransitionEvent = function(){ var t, el = document.createElement("fakeelement"); @@ -51,7 +51,7 @@ export let whichTransitionEvent = function(){ * CSS animation end event listener * @return */ -export let whichAnimationEvent = function() { +export const whichAnimationEvent = function() { var t, el = document.createElement('fakeelement'); @@ -77,7 +77,7 @@ export let whichAnimationEvent = function() { * @param {String} selector Class to find * @return {Array} Array of parent elements */ -export let getParentsUntil = function(elem, parent, selector) { +export const getParentsUntil = function(elem, parent, selector) { var parents = []; // Get matches for (; elem && elem !== document; elem = elem.parentNode) { @@ -156,7 +156,7 @@ export let getParentsUntil = function(elem, parent, selector) { } }; -export let wrap = function (element, wrapper) { +export const wrap = function (element, wrapper) { wrapper = wrapper || document.createElement('div'); if (element.nextSibling) { element.parentNode.insertBefore(wrapper, element.nextSibling); @@ -166,7 +166,7 @@ export let wrap = function (element, wrapper) { return wrapper.appendChild(element); }; -export let getSiblings = function (elem) { +export const getSiblings = function (elem) { var siblings = []; var sibling = elem.parentNode.firstChild; for ( ; sibling; sibling = sibling.nextSibling ) { @@ -183,7 +183,7 @@ export let getSiblings = function (elem) { * @param {[type]} cls Class of parent * @return {NodeElement} Found parent element */ -export let findAncestor = function(el, cls) { +export const findAncestor = function(el, cls) { while ((el = el.parentElement) && !el.classList.contains(cls)); return el; }; @@ -195,7 +195,7 @@ export let findAncestor = function(el, cls) { * @param {Boolean} immediate If passed, trigger the function on the leading edge, instead of the trailing. * @return {Function} A function will be called after it stops being called for a given delay */ -export let debounce = function(func, wait, immediate) { +export const debounce = function(func, wait, immediate) { var timeout; return function() { var context = this, @@ -217,7 +217,7 @@ export let debounce = function(func, wait, immediate) { * @param {NodeElement} el Element to test for * @return {Number} Elements Distance from top of page */ -export let getElemDistance = function(el) { +export const getElemDistance = function(el) { var location = 0; if (el.offsetParent) { do { @@ -234,7 +234,7 @@ export let getElemDistance = function(el) { * @param {Node} el Element to test for * @return {Number} Height of element */ -export let getElementOffset = function(el, offset) { +export const getElementOffset = function(el, offset) { var elOffset = offset; if(elOffset > 1) elOffset = 1; if(elOffset > 0) elOffset = 0; @@ -247,7 +247,7 @@ export let getElementOffset = function(el, offset) { * @private * @return {String} Position of scroll */ -export let getScrollPosition = function(position) { +export const getScrollPosition = function(position) { if(position === 'bottom') { // Scroll position from the bottom of the viewport return Math.max((window.scrollY || window.pageYOffset) + (window.innerHeight || document.documentElement.clientHeight)); @@ -263,7 +263,7 @@ export let getScrollPosition = function(position) { * @return {String} Position of scroll * @return {Boolean} */ -export let isInView = function(el, position, offset) { +export const isInView = function(el, position, offset) { // If the user has scrolled further than the distance from the element to the top of its parent return this.getScrollPosition(position) > (this.getElemDistance(el) + this.getElementOffset(el, offset)) ? true : false; }; @@ -273,7 +273,7 @@ export let isInView = function(el, position, offset) { * @param {String} Initial string/html * @return {String} Sanitised string */ -export let stripHTML = function(html) { +export const stripHTML = function(html) { let el = document.createElement("DIV"); el.innerHTML = html; return el.textContent || el.innerText || ""; @@ -285,7 +285,7 @@ export let stripHTML = function(html) { * @param {String} animation Animation class to add to element * @return */ -export let addAnimation = (el, animation) => { +export const addAnimation = (el, animation) => { let animationEvent = whichAnimationEvent(); let removeAnimation = () => { @@ -304,6 +304,26 @@ export let addAnimation = (el, animation) => { * @param {Number} max Maximum range * @return {Number} Random number */ -export let getRandomNumber = function(min, max) { +export const getRandomNumber = function(min, max) { return Math.floor(Math.random() * (max - min) + min); -} \ No newline at end of file +} + +/** + * Turn a string into a node + * @param {String} String to convert + * @return {Node} Converted node element + */ +export const strToEl = (function() { + var tmpEl = document.createElement('div'); + return function(str) { + var r; + tmpEl.innerHTML = str; + r = tmpEl.children[0]; + + while (tmpEl.firstChild) { + tmpEl.removeChild(tmpEl.firstChild); + } + + return r; + }; +}()); \ No newline at end of file diff --git a/assets/scripts/src/reducers/index.js b/assets/scripts/src/reducers/index.js index 341da2d..f6f615c 100644 --- a/assets/scripts/src/reducers/index.js +++ b/assets/scripts/src/reducers/index.js @@ -1,4 +1,3 @@ -// Array of choices const choices = (state = [], action) => { switch (action.type) { case 'ADD_ITEM': @@ -7,17 +6,29 @@ const choices = (state = [], action) => { id: parseInt(action.id), value: action.value, element: action.element, - active: true + active: true, + selected: false }]; case 'REMOVE_ITEM': - // Remove item from items array - return state.filter(function(item) { - if(item.id !== parseInt(action.id)) { - return item; + // Set item to inactive + return state.map((item) => { + if(item.id === parseInt(action.id)) { + item.active = false; } + return item; }); + case 'SELECT_ITEM': + return state.map((item) => { + if(item.id === parseInt(action.id)) { + item.selected = action.value; + } + + return item; + }); + + default: return state; } diff --git a/index.html b/index.html index d071bf0..d9c95ba 100644 --- a/index.html +++ b/index.html @@ -7,8 +7,8 @@ - - + +