diff --git a/assets/scripts/dist/bundle.js b/assets/scripts/dist/bundle.js index 6d51289..3512636 100644 --- a/assets/scripts/dist/bundle.js +++ b/assets/scripts/dist/bundle.js @@ -1 +1 @@ -!function(e){function n(o){if(t[o])return t[o].exports;var i=t[o]={exports:{},id:o,loaded:!1};return e[o].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}var t={};return n.m=e,n.c=t,n.p="/assets/scripts/dist/",n(0)}([function(e,n,t){e.exports=t(1)},function(e,n,t){function o(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}var i,s=function(){function e(e,n){for(var t=0;t=0;n--){var t=e[n];this.render(t)}}},{key:"render",value:function(e){console.log("Render");var t=document.createElement("div");t.classList.add("choice","choice--active"),e.classList.add("choice__input","choice__input--original"),e.tabIndex="-1",e.setAttribute("style","display:none;"),n(e,t);var o=document.createElement("input");o.type="text",o.classList.add("choice__input","choice__input--cloned"),t.appendChild(o),this.addEventListeners(e)}},{key:"destroy",value:function(){this.options=null,this.elements=null;for(var e=this.elements,n=e.length-1;n>=0;n--){var t=e[n];this.removeEventListeners(t)}}}]),e}(),i=new t;i.init(),console.log(i)})}]); \ No newline at end of file +!function(e){function n(i){if(t[i])return t[i].exports;var o=t[i]={exports:{},id:i,loaded:!1};return e[i].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var t={};return n.m=e,n.c=t,n.p="/assets/scripts/dist/",n(0)}([function(e,n,t){e.exports=t(1)},function(e,n,t){function i(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}var o,r=function(){function e(e,n){for(var t=0;t=0;n--){var t=e[n];this.render(t)}}},{key:"render",value:function(e){console.log("Render");var n=document.createElement("div"),t=document.createElement("input"),i=document.createElement("ul");n.classList.add("choice","choice--active"),e.classList.add("choice__input","choice__input--original"),e.tabIndex="-1",e.setAttribute("style","display:none;"),(0,s.wrap)(e,n),i.classList.add("choice__list"),t.type="text",t.classList.add("choice__input","choice__input--cloned"),n.appendChild(i),n.appendChild(t),this.addEventListeners(t)}},{key:"destroy",value:function(){this.options=null,this.elements=null;for(var e=this.elements,n=e.length-1;n>=0;n--){var t=e[n];this.removeEventListeners(t)}}}]),e}(),t=new n;t.init(),console.log(t)})},function(e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=(n.capitalise=function(e){return e.replace(/\w\S*/g,function(e){return e.charAt(0).toUpperCase()+e.substr(1).toLowerCase()})},n.isType=function(e,n){var t=Object.prototype.toString.call(n).slice(8,-1);return void 0!==n&&null!==n&&t===e},n.whichTransitionEvent=function(){var e,n=document.createElement("fakeelement"),t={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(e in t)if(void 0!==n.style[e])return t[e]},n.whichAnimationEvent=function(){var e,n=document.createElement("fakeelement"),t={animation:"animationend",OAnimation:"oAnimationEnd",MozAnimation:"animationend",WebkitAnimation:"webkitAnimationEnd"};for(e in t)if(void 0!==n.style[e])return t[e]});n.getParentsUntil=function(e,n,t){for(var i=[];e&&e!==document;e=e.parentNode){if(n){var o=n.charAt(0);if("."===o&&e.classList.contains(n.substr(1)))break;if("#"===o&&e.id===n.substr(1))break;if("["===o&&e.hasAttribute(n.substr(1,n.length-1)))break;if(e.tagName.toLowerCase()===n)break}if(t){var r=t.charAt(0);"."===r&&e.classList.contains(t.substr(1))&&i.push(e),"#"===r&&e.id===t.substr(1)&&i.push(e),"["===r&&e.hasAttribute(t.substr(1,t.length-1))&&i.push(e),e.tagName.toLowerCase()===t&&i.push(e)}else i.push(e)}return 0===i.length?null:i},n.wrap=function(e,n){return n=n||document.createElement("div"),e.nextSibling?e.parentNode.insertBefore(n,e.nextSibling):e.parentNode.appendChild(n),n.appendChild(e)},n.getSiblings=function(e){for(var n=[],t=e.parentNode.firstChild;t;t=t.nextSibling)1===t.nodeType&&t!==e&&n.push(t);return n},n.findAncestor=function(e,n){for(;(e=e.parentElement)&&!e.classList.contains(n););return e},n.debounce=function(e,n,t){var i;return function(){var o=this,r=arguments,s=function(){i=null,t||e.apply(o,r)},a=t&&!i;clearTimeout(i),i=setTimeout(s,n),a&&e.apply(o,r)}},n.getElemDistance=function(e){var n=0;if(e.offsetParent)do n+=e.offsetTop,e=e.offsetParent;while(e);return n>=0?n:0},n.getElementOffset=function(e,n){var t=n;return t>1&&(t=1),t>0&&(t=0),Math.max(e.offsetHeight*t)},n.getScrollPosition=function(e){return"bottom"===e?Math.max((window.scrollY||window.pageYOffset)+(window.innerHeight||document.documentElement.clientHeight)):window.scrollY||window.pageYOffset},n.isInView=function(e,n,t){return this.getScrollPosition(n)>this.getElemDistance(e)+this.getElementOffset(e,t)},n.stripHTML=function(e){var n=document.createElement("DIV");return n.innerHTML=e,n.textContent||n.innerText||""},n.addAnimation=function(e,n){var i=t(),o=function r(){e.classList.remove(n),e.removeEventListener(i,r,!1)};e.classList.add(n),e.addEventListener(i,o,!1)},n.getRandomNumber=function(e,n){return Math.floor(Math.random()*(n-e)+e)}}]); \ No newline at end of file diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index e37431c..41b893a 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -1,3 +1,5 @@ +import { wrap, getSiblings } from './lib/utils.js'; + (function (root, factory) { if (typeof define === 'function' && define.amd) { define(function() { @@ -12,16 +14,6 @@ 'use strict'; - let wrap = function (element, wrapper) { - wrapper = wrapper || document.createElement('div'); - if (element.nextSibling) { - element.parentNode.insertBefore(wrapper, element.nextSibling); - } else { - element.parentNode.appendChild(wrapper); - } - return wrapper.appendChild(element); - }; - class Choices { constructor() { const fakeEl = document.createElement("fakeelement"); @@ -72,23 +64,25 @@ /* Event handling */ onKeyDown(e) { + // let input = console.log('Key down') - console.log(e.target); + + if(e.keyCode === 13 && e.target.value) { + this.addItem(e.target, e.target.value); + e.target.value = ''; + } } onFocus(e) { console.log('Focus') - console.log(e.target); } onClick(e) { console.log('Click') - console.log(e.target); } onChange(e) { console.log('Change') - console.log(e.target); } /* Event listeners */ @@ -131,8 +125,16 @@ } - addItem() { + addItem(el, value) { + console.log('Add item'); + let wrapper = el.parentNode; + let list = wrapper.querySelector('.choice__list'); + + let item = document.createElement('li'); + item.classList.add('choice__item'); + item.textContent = value; + wrapper.appendChild(item); } removeItem() { @@ -162,7 +164,10 @@ console.log('Render'); let wrapper = document.createElement('div'); - wrapper.classList.add('choice', 'choice--active') + let input = document.createElement('input'); + let list = document.createElement('ul'); + + wrapper.classList.add('choice', 'choice--active'); el.classList.add('choice__input', 'choice__input--original'); el.tabIndex = '-1'; @@ -170,12 +175,15 @@ wrap(el, wrapper); - let input = document.createElement('input'); + list.classList.add('choice__list'); + input.type = 'text'; input.classList.add('choice__input', 'choice__input--cloned'); + + wrapper.appendChild(list); wrapper.appendChild(input); - this.addEventListeners(el); + this.addEventListeners(input); } destroy() { diff --git a/assets/scripts/src/lib/utils.js b/assets/scripts/src/lib/utils.js new file mode 100644 index 0000000..f1868b4 --- /dev/null +++ b/assets/scripts/src/lib/utils.js @@ -0,0 +1,305 @@ +/** + * Capitalises the first letter of each word in a string + * @param {String} str String to capitalise + * @return {String} Capitalised string + */ +export let capitalise = function(str) { + return str.replace(/\w\S*/g, function(txt){ + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); +}; + +/** + * Tests the type of an object + * @param {String} type Type to test object against + * @param {Object} obj Object to be tested + * @return {Boolean} + */ +export let isType = function(type, obj) { + var clas = Object.prototype.toString.call(obj).slice(8, -1); + return obj !== undefined && obj !== null && clas === type; +}; + + +/** + * CSS transition end event listener + * @return + */ +export let whichTransitionEvent = function(){ + var t, + el = document.createElement("fakeelement"); + + var transitions = { + "transition" : "transitionend", + "OTransition" : "oTransitionEnd", + "MozTransition" : "transitionend", + "WebkitTransition": "webkitTransitionEnd" + } + + for (t in transitions){ + if (el.style[t] !== undefined){ + return transitions[t]; + } + } +} + +/** + * CSS animation end event listener + * @return + */ +export let whichAnimationEvent = function() { + var t, + el = document.createElement('fakeelement'); + + var animations = { + 'animation': 'animationend', + 'OAnimation': 'oAnimationEnd', + 'MozAnimation': 'animationend', + 'WebkitAnimation': 'webkitAnimationEnd' + }; + + for (t in animations) { + if (el.style[t] !== undefined) { + return animations[t]; + } + } +}; + +/** + * Get the ancestors of each element in the current set of matched elements, + * up to but not including the element matched by the selector + * @param {NodeElement} elem Element to begin search from + * @param {NodeElement} parent Parent to find + * @param {String} selector Class to find + * @return {Array} Array of parent elements + */ +export let getParentsUntil = function(elem, parent, selector) { + var parents = []; + // Get matches + for (; elem && elem !== document; elem = elem.parentNode) { + + // Check if parent has been reached + if (parent) { + + var parentType = parent.charAt(0); + + // If parent is a class + if (parentType === '.') { + if (elem.classList.contains(parent.substr(1))) { + break; + } + } + + // If parent is an ID + if (parentType === '#') { + if (elem.id === parent.substr(1)) { + break; + } + } + + // If parent is a data attribute + if (parentType === '[') { + if (elem.hasAttribute(parent.substr(1, parent.length - 1))) { + break; + } + } + + // If parent is a tag + if (elem.tagName.toLowerCase() === parent) { + break; + } + + } + if (selector) { + var selectorType = selector.charAt(0); + + // If selector is a class + if (selectorType === '.') { + if (elem.classList.contains(selector.substr(1))) { + parents.push(elem); + } + } + + // If selector is an ID + if (selectorType === '#') { + if (elem.id === selector.substr(1)) { + parents.push(elem); + } + } + + // If selector is a data attribute + if (selectorType === '[') { + if (elem.hasAttribute(selector.substr(1, selector.length - 1))) { + parents.push(elem); + } + } + + // If selector is a tag + if (elem.tagName.toLowerCase() === selector) { + parents.push(elem); + } + + } else { + parents.push(elem); + } + } + + // Return parents if any exist + if (parents.length === 0) { + return null; + } else { + return parents; + } +}; + +export let wrap = function (element, wrapper) { + wrapper = wrapper || document.createElement('div'); + if (element.nextSibling) { + element.parentNode.insertBefore(wrapper, element.nextSibling); + } else { + element.parentNode.appendChild(wrapper); + } + return wrapper.appendChild(element); +}; + +export let getSiblings = function (elem) { + var siblings = []; + var sibling = elem.parentNode.firstChild; + for ( ; sibling; sibling = sibling.nextSibling ) { + if ( sibling.nodeType === 1 && sibling !== elem ) { + siblings.push( sibling ); + } + } + return siblings; +}; + +/** + * Find ancestor in DOM tree + * @param {NodeElement} el Element to start search from + * @param {[type]} cls Class of parent + * @return {NodeElement} Found parent element + */ +export let findAncestor = function(el, cls) { + while ((el = el.parentElement) && !el.classList.contains(cls)); + return el; +}; + +/** + * Debounce an event handler. + * @param {Function} func Function to run after wait + * @param {Number} wait The delay before the function is executed + * @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) { + var timeout; + return function() { + var context = this, + args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +}; + +/** + * Get an element's distance from the top of the page + * @private + * @param {NodeElement} el Element to test for + * @return {Number} Elements Distance from top of page + */ +export let getElemDistance = function(el) { + var location = 0; + if (el.offsetParent) { + do { + location += el.offsetTop; + el = el.offsetParent; + } while (el); + } + return location >= 0 ? location : 0; +}; + +/** + * Determine element height multiplied by any offsets + * @private + * @param {Node} el Element to test for + * @return {Number} Height of element + */ +export let getElementOffset = function(el, offset) { + var elOffset = offset; + if(elOffset > 1) elOffset = 1; + if(elOffset > 0) elOffset = 0; + + return Math.max(el.offsetHeight*elOffset); +}; + +/** + * Get scroll position based on top/bottom position + * @private + * @return {String} Position of scroll + */ +export let 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)); + } else { + // Scroll position from the top of the viewport + return (window.scrollY || window.pageYOffset); + } +}; + +/** + * Determine whether an element is within the viewport + * @param {Node} el Element to test for + * @return {String} Position of scroll + * @return {Boolean} + */ +export let 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; +}; + +/** + * Remove html tags from a string + * @param {String} Initial string/html + * @return {String} Sanitised string + */ +export let stripHTML = function(html) { + let el = document.createElement("DIV"); + el.innerHTML = html; + return el.textContent || el.innerText || ""; +}; + +/** + * Adds animation to an element and removes it upon animation completion + * @param {Element} el Element to add animation to + * @param {String} animation Animation class to add to element + * @return + */ +export let addAnimation = (el, animation) => { + let animationEvent = whichAnimationEvent(); + + let removeAnimation = () => { + el.classList.remove(animation); + el.removeEventListener(animationEvent, removeAnimation, false); + }; + + el.classList.add(animation); + el.addEventListener(animationEvent, removeAnimation, false); +}; + + +/** + * Get a random number between a range + * @param {Number} min Minimum range + * @param {Number} max Maximum range + * @return {Number} Random number + */ +export let getRandomNumber = function(min, max) { + return Math.floor(Math.random() * (max - min) + min); +} \ No newline at end of file