dual-listbox
Advanced tools
Comparing version 0.1.1 to 1.0.1
@@ -5,2 +5,3 @@ var path = require('path'); | ||
var appRoot = 'src/'; | ||
var distDir = 'dist/'; | ||
var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); | ||
@@ -11,5 +12,6 @@ | ||
source: appRoot + '**/*.js', | ||
sassSource: appRoot + '**/*.scss', | ||
tests: 'test/**/*.spec.js', | ||
packageName: pkg.name, | ||
output: 'dist/' | ||
output: distDir | ||
}; |
var gulp = require('gulp'); | ||
var webpack = require('webpack-stream'); | ||
var autoprefixer = require('gulp-autoprefixer'); | ||
var purge = require('gulp-css-purge'); | ||
var minify = require('gulp-minify-css'); | ||
var sass = require('gulp-sass'); | ||
var paths = require('../paths'); | ||
@@ -9,6 +13,6 @@ var webpackConfig = require('../../webpack.config.js'); | ||
* Build task | ||
* Run using "gulp build" | ||
* Run using "gulp build-js" | ||
* Runs webpack to compile javascript | ||
*/ | ||
gulp.task('build', function() { | ||
gulp.task('build-js', function() { | ||
gulp.src('') | ||
@@ -18,1 +22,39 @@ .pipe(webpack(webpackConfig)) | ||
}); | ||
/** | ||
* Build task | ||
* Run using "gulp build-sass" | ||
* Runs gulp-sass to compile sass to css | ||
*/ | ||
gulp.task('build-sass', function() { | ||
gulp.src(paths.sassSource) | ||
// Compiles sass to css | ||
.pipe(sass({ | ||
// Allow importing from node_modules in .scss files | ||
includePaths: 'node_modules/', | ||
}).on('error', sass.logError)) | ||
// Auto prefixes css | ||
.pipe(autoprefixer({ | ||
browsers: ['last 2 versions'], | ||
cascade: false | ||
})) | ||
// Remove duplicated code | ||
.pipe(purge()) | ||
// Minify CSS | ||
.pipe(minify()) | ||
// Writes css to paths.cssDir | ||
.pipe(gulp.dest(paths.output)); | ||
}); | ||
/** | ||
* Build task | ||
* Run using "gulp build" | ||
* Runs build-js and build-sass | ||
*/ | ||
gulp.task('build', ['build-js', 'build-sass']); |
@@ -1,1 +0,1 @@ | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var n in i)("object"==typeof exports?exports:e)[n]=i[n]}}(this,function(){return function(e){function t(n){if(i[n])return i[n].exports;var a=i[n]={exports:{},id:n,loaded:!1};return e[n].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var i=0;i<t.length;i++){var n=t[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,i,n){return i&&e(t.prototype,i),n&&e(t,n),t}}(),a="dual-listbox",l="dual-listbox__available",r="dual-listbox__selected",s="dual-listbox__item",o="dual-listbox__item--selected",c=function(){function e(t){i(this,e),this.select=document.querySelector(t),this.selected=[],this.available=[],this.splitSelectOptions(this.select),this.initDualListbox(this.select.parentNode)}return n(e,[{key:"addSelected",value:function(e){var t=this.available.indexOf(e);t>-1&&this.available.splice(t,1),this.selected.push(e),this.selectOption(e.dataset.id),this.redraw()}},{key:"removeSelected",value:function(e){var t=this.selected.indexOf(e);t>-1&&this.selected.splice(t,1),this.available.push(e),this.deselectOption(e.dataset.id),this.redraw()}},{key:"selectOption",value:function(e){var t=this.select.options,i=!0,n=!1,a=void 0;try{for(var l,r=t[Symbol.iterator]();!(i=(l=r.next()).done);i=!0){var s=l.value;s.value===e&&(s.selected=!0)}}catch(e){n=!0,a=e}finally{try{!i&&r.return&&r.return()}finally{if(n)throw a}}}},{key:"deselectOption",value:function(e){var t=this.select.options,i=!0,n=!1,a=void 0;try{for(var l,r=t[Symbol.iterator]();!(i=(l=r.next()).done);i=!0){var s=l.value;s.value===e&&(s.selected=!1)}}catch(e){n=!0,a=e}finally{try{!i&&r.return&&r.return()}finally{if(n)throw a}}}},{key:"redraw",value:function(){var e=this,t=this.createListbox(this.available,l),i=this.createListbox(this.selected,r),n=this.createButtons(),a=document.createElement("input");a.onchange=function(){e.search(this.value)},a.onkeyup=function(){e.search(this.value)},this.dualListbox.innerHTML="",this.dualListbox.appendChild(a),this.dualListbox.appendChild(t),this.dualListbox.appendChild(n),this.dualListbox.appendChild(i)}},{key:"search",value:function(e){this.dualListbox.querySelectorAll("."+s).forEach(function(t){e?t.innerText.includes(e)?t.style.display="block":t.style.display="none":t.style.display="block"})}},{key:"initDualListbox",value:function(e){var t=document.createElement("div");t.classList.add(a),e.insertBefore(t,this.select),this.select.style.display="none",this.dualListbox=t,this.redraw()}},{key:"createListbox",value:function(e,t){var i=document.createElement("ul");i.classList.add(t);var n=!0,a=!1,l=void 0;try{for(var r,s=e[Symbol.iterator]();!(n=(r=s.next()).done);n=!0){var o=r.value;i.appendChild(this.addClickActions(o))}}catch(e){a=!0,l=e}finally{try{!n&&s.return&&s.return()}finally{if(a)throw l}}return i}},{key:"addClickActions",value:function(e){var t=this;return e.ondblclick=function(){t.selected.indexOf(this)>-1?t.removeSelected(e):t.addSelected(e)},e.onclick=function(){t.dualListbox.querySelectorAll("."+s).forEach(function(e){e.classList.remove(o)}),this.classList.contains(o)?this.classList.remove(o):this.classList.add(o)},e}},{key:"createButtons",value:function(){var e=this,t=document.createElement("div"),i=document.createElement("button"),n=document.createElement("button");return i.innerText="Add",i.onclick=function(){var t=e.dualListbox.querySelector("."+o);t&&e.addSelected(t)},n.innerText="remove",n.onclick=function(){var t=e.dualListbox.querySelector("."+o);t&&e.removeSelected(t)},t.appendChild(i),t.appendChild(n),t}},{key:"splitSelectOptions",value:function(e){var t=e.options,i=!0,n=!1,a=void 0;try{for(var l,r=t[Symbol.iterator]();!(i=(l=r.next()).done);i=!0){var s=l.value,o=this.createListItem(s);s.attributes.selected?this.selected.push(o):this.available.push(o)}}catch(e){n=!0,a=e}finally{try{!i&&r.return&&r.return()}finally{if(n)throw a}}}},{key:"createListItem",value:function(e){var t=document.createElement("li");return t.classList.add(s),t.innerText=e.innerText,t.dataset.id=e.value,t}}]),e}();t.default=c,t.DualListbox=c}])}); | ||
!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var i=e();for(var s in i)("object"==typeof exports?exports:t)[s]=i[s]}}(this,function(){return function(t){function e(s){if(i[s])return i[s].exports;var l=i[s]={exports:{},id:s,loaded:!1};return t[s].call(l.exports,l,l.exports,e),l.loaded=!0,l.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var s=function(){function t(t,e){for(var i=0;i<e.length;i++){var s=e[i];s.enumerable=s.enumerable||!1,s.configurable=!0,"value"in s&&(s.writable=!0),Object.defineProperty(t,s.key,s)}}return function(e,i,s){return i&&t(e.prototype,i),s&&t(e,s),e}}(),l="dual-listbox",a="dual-lsitbox__container",n="dual-listbox__available",o="dual-listbox__selected",d="dual-listbox__title",r="dual-listbox__item",c="dual-listbox__buttons",u="dual-listbox__button",h="dual-listbox__search",v="dual-listbox__item--selected",b=function(){function t(e){var s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};i(this,t),this.select=document.querySelector(e),this.selected=[],this.available=[],this._initOptions(s),this._initReusableElements(),this._splitSelectOptions(this.select),this._buildDualListbox(this.select.parentNode),this._addActions(),this.redraw()}return s(t,[{key:"addSelected",value:function(t){var e=this.available.indexOf(t);e>-1&&this.available.splice(e,1),this.selected.push(t),this._selectOption(t.dataset.id),this.redraw()}},{key:"redraw",value:function(){this.updateAvailableListbox(),this.updateSelectedListbox()}},{key:"removeSelected",value:function(t){var e=this.selected.indexOf(t);e>-1&&this.selected.splice(e,1),this.available.push(t),this._deselectOption(t.dataset.id),this.redraw()}},{key:"searchLists",value:function(t){this.dualListbox.querySelectorAll("."+r).forEach(function(e){t?e.innerText.includes(t)?e.style.display="block":e.style.display="none":e.style.display="block"})}},{key:"updateAvailableListbox",value:function(){this.availebleList.innerHTML="",this.availebleList.appendChild(this.availableListTitle);var t=!0,e=!1,i=void 0;try{for(var s,l=this.available[Symbol.iterator]();!(t=(s=l.next()).done);t=!0){var a=s.value;this.availebleList.appendChild(this._addClickActions(a))}}catch(t){e=!0,i=t}finally{try{!t&&l.return&&l.return()}finally{if(e)throw i}}}},{key:"updateSelectedListbox",value:function(){this.selectedList.innerHTML="",this.selectedList.appendChild(this.selectedListTitle);var t=!0,e=!1,i=void 0;try{for(var s,l=this.selected[Symbol.iterator]();!(t=(s=l.next()).done);t=!0){var a=s.value;this.selectedList.appendChild(this._addClickActions(a))}}catch(t){e=!0,i=t}finally{try{!t&&l.return&&l.return()}finally{if(e)throw i}}}},{key:"_addActions",value:function(){this._addButtonActions(),this._addSearchActions()}},{key:"_addButtonActions",value:function(){var t=this;this.add_all_button.onclick=function(e){for(e.preventDefault();t.available.length>0;)t.addSelected(t.available[0])},this.add_button.onclick=function(e){e.preventDefault();var i=t.dualListbox.querySelector("."+v);i&&t.addSelected(i)},this.remove_button.onclick=function(e){e.preventDefault();var i=t.dualListbox.querySelector("."+v);i&&t.removeSelected(i)},this.remove_all_button.onclick=function(e){for(e.preventDefault();t.selected.length>0;)t.removeSelected(t.selected[0])}}},{key:"_addClickActions",value:function(t){var e=this;return t.ondblclick=function(){e.selected.indexOf(this)>-1?e.removeSelected(t):e.addSelected(t)},t.onclick=function(){e.dualListbox.querySelectorAll("."+r).forEach(function(t){t.classList.remove(v)}),this.classList.contains(v)?this.classList.remove(v):this.classList.add(v)},t}},{key:"_addSearchActions",value:function(){var t=this;this.search.onchange=function(e){return t.searchLists(e.target.value)},this.search.onkeyup=function(e){return t.searchLists(e.target.value)}}},{key:"_buildDualListbox",value:function(t){this.select.style.display="none",this.dualListBoxContainer.appendChild(this.availebleList),this.dualListBoxContainer.appendChild(this.buttons),this.dualListBoxContainer.appendChild(this.selectedList),this.dualListbox.appendChild(this.search),this.dualListbox.appendChild(this.dualListBoxContainer),t.insertBefore(this.dualListbox,this.select)}},{key:"_createButtons",value:function(){this.buttons=document.createElement("div"),this.buttons.classList.add(c),this.add_all_button=document.createElement("button"),this.add_all_button.classList.add(u),this.add_all_button.innerHTML=this.addAllButtonText,this.add_button=document.createElement("button"),this.add_button.classList.add(u),this.add_button.innerHTML=this.addButtonText,this.remove_button=document.createElement("button"),this.remove_button.classList.add(u),this.remove_button.innerHTML=this.removeButtonText,this.remove_all_button=document.createElement("button"),this.remove_all_button.classList.add(u),this.remove_all_button.innerHTML=this.removeAllButtonText,this.buttons.appendChild(this.add_all_button),this.buttons.appendChild(this.add_button),this.buttons.appendChild(this.remove_button),this.buttons.appendChild(this.remove_all_button)}},{key:"_createListItem",value:function(t){var e=document.createElement("li");return e.classList.add(r),e.innerHTML=t.innerHTML,e.dataset.id=t.value,e}},{key:"_createSearch",value:function(){this.search=document.createElement("input"),this.search.classList.add(h)}},{key:"_deselectOption",value:function(t){var e=this.select.options,i=!0,s=!1,l=void 0;try{for(var a,n=e[Symbol.iterator]();!(i=(a=n.next()).done);i=!0){var o=a.value;o.value===t&&(o.selected=!1)}}catch(t){s=!0,l=t}finally{try{!i&&n.return&&n.return()}finally{if(s)throw l}}this.removeEvent&&this.removeEvent(t)}},{key:"_initOptions",value:function(t){this.addEvent=t.addEvent,this.removeEvent=t.removeEvent,this.availableTitle=t.availableTitle||"Available options",this.selectedTitle=t.selectedTitle||"Selected options",this.addButtonText=t.addButtonText||"add",this.removeButtonText=t.removeButtonText||"remove",this.addAllButtonText=t.addAllButtonText||"add all",this.removeAllButtonText=t.removeAllButtonText||"remove all"}},{key:"_initReusableElements",value:function(){this.dualListbox=document.createElement("div"),this.dualListbox.classList.add(l),this.select.id&&this.dualListbox.classList.add(this.select.id),this.dualListBoxContainer=document.createElement("div"),this.dualListBoxContainer.classList.add(a),this.availebleList=document.createElement("ul"),this.availebleList.classList.add(n),this.selectedList=document.createElement("ul"),this.selectedList.classList.add(o),this.availableListTitle=document.createElement("li"),this.availableListTitle.classList.add(d),this.availableListTitle.innerText=this.availableTitle,this.selectedListTitle=document.createElement("li"),this.selectedListTitle.classList.add(d),this.selectedListTitle.innerText=this.selectedTitle,this._createButtons(),this._createSearch()}},{key:"_selectOption",value:function(t){var e=this.select.options,i=!0,s=!1,l=void 0;try{for(var a,n=e[Symbol.iterator]();!(i=(a=n.next()).done);i=!0){var o=a.value;o.value===t&&(o.selected=!0)}}catch(t){s=!0,l=t}finally{try{!i&&n.return&&n.return()}finally{if(s)throw l}}this.addEvent&&this.addEvent(t)}},{key:"_splitSelectOptions",value:function(t){var e=this,i=t.options;[].forEach.call(i,function(t){var i=e._createListItem(t);t.attributes.selected?e.selected.push(i):e.available.push(i)})}}]),t}();e.default=b,e.DualListbox=b}])}); |
{ | ||
"name": "dual-listbox", | ||
"version": "0.1.1", | ||
"version": "1.0.1", | ||
"description": "Dual listbox for multi-select elements", | ||
@@ -18,4 +18,8 @@ "main": "dist/dual-listbox.js", | ||
"gulp": "^3.9.1", | ||
"gulp-autoprefixer": "^3.1.0", | ||
"gulp-babel": "^6.1.2", | ||
"gulp-css-purge": "^1.0.27", | ||
"gulp-jshint": "^2.0.0", | ||
"gulp-minify-css": "^1.2.4", | ||
"gulp-sass": "^2.2.0", | ||
"jasmine-core": "^2.4.1", | ||
@@ -33,2 +37,4 @@ "jshint": "^2.9.2", | ||
"karma-webpack": "^1.7.0", | ||
"node-sass": "^3.9.3", | ||
"postcss-loader": "^0.11.1", | ||
"require-dir": "^0.3.0", | ||
@@ -35,0 +41,0 @@ "vinyl-paths": "^2.1.0", |
@@ -5,4 +5,12 @@ [![Build Status](https://travis-ci.org/maykinmedia/dual-listbox.svg?branch=1.0)](https://travis-ci.org/maykinmedia/dual-listbox) | ||
> TODO. | ||
> Make your multi select pretty and easy to use with only javascript. | ||
Styling. | ||
![Default](screenshots/select1.png) | ||
with selected options and one option highlighted. | ||
![selected](screenshots/select2.png) | ||
## Install | ||
@@ -16,6 +24,29 @@ | ||
## Usage | ||
## Usage | ||
TODO | ||
```javascript | ||
new DualListbox('select'); // Selects the first selectbox on the page. | ||
new DualListbox('.select'); // Selects the first element with the class 'select' | ||
new DualListbox('#select'); // Selects the first element with the id 'select' | ||
``` | ||
You can also pass some options to the DualListbox | ||
```javascript | ||
new DualListbox('#select', { | ||
addEvent: function(value) { | ||
console.log(value); | ||
}, | ||
removeEvent: function(value) { | ||
console.log(value); | ||
}, | ||
availableTitle: 'Different title', | ||
selectedTitle: 'Different title', | ||
addButtonText: '>', | ||
removeButtonText: '<', | ||
addAllButtonText: '>>', | ||
removeAllButtonText: '<<' | ||
}); | ||
``` | ||
## Contributing | ||
@@ -22,0 +53,0 @@ |
@@ -1,20 +0,33 @@ | ||
/** | ||
* Dual select interface allowing the user to select items from a list of provided options. | ||
* @class | ||
*/ | ||
const CONTAINER_BLOCK = 'dual-listbox'; | ||
const MAIN_BLOCK = 'dual-listbox'; | ||
const CONTAINER_ELEMENT = 'dual-lsitbox__container'; | ||
const AVAILABLE_ELEMENT = 'dual-listbox__available'; | ||
const SELECTED_ELEMENT = 'dual-listbox__selected'; | ||
const TITLE_ELEMENT = 'dual-listbox__title'; | ||
const ITEM_ELEMENT = 'dual-listbox__item'; | ||
const BUTTONS_ELEMENT = 'dual-listbox__buttons'; | ||
const BUTTON_ELEMENT = 'dual-listbox__button'; | ||
const SEARCH_ELEMENT = 'dual-listbox__search'; | ||
const SELECTED_MODIFIER = 'dual-listbox__item--selected'; | ||
/** | ||
* Dual select interface allowing the user to select items from a list of provided options. | ||
* @class | ||
*/ | ||
class DualListbox { | ||
constructor(selector) { | ||
constructor(selector, options={}) { | ||
this.select = document.querySelector(selector); | ||
this.selected = []; | ||
this.available = []; | ||
this.splitSelectOptions(this.select); | ||
this.initDualListbox(this.select.parentNode); | ||
this._initOptions(options); | ||
this._initReusableElements(); | ||
this._splitSelectOptions(this.select); | ||
this._buildDualListbox(this.select.parentNode); | ||
this._addActions(); | ||
this.redraw(); | ||
} | ||
@@ -24,3 +37,3 @@ | ||
* Add the listItem to the selected list. | ||
* | ||
* | ||
* @param {NodeElement} listItem | ||
@@ -32,11 +45,19 @@ */ | ||
this.available.splice(index, 1); | ||
this.selected.push(listItem); | ||
this._selectOption(listItem.dataset.id); | ||
this.redraw(); | ||
} | ||
this.selected.push(listItem); | ||
this.selectOption(listItem.dataset.id); | ||
this.redraw(); | ||
} | ||
/** | ||
* Redraws the Dual listbox content | ||
*/ | ||
redraw() { | ||
this.updateAvailableListbox(); | ||
this.updateSelectedListbox(); | ||
} | ||
/** | ||
* Removes the listItem from the selected list. | ||
* | ||
* | ||
* @param {NodeElement} listItem | ||
@@ -48,20 +69,37 @@ */ | ||
this.selected.splice(index, 1); | ||
this.available.push(listItem); | ||
this._deselectOption(listItem.dataset.id); | ||
this.redraw(); | ||
} | ||
this.available.push(listItem); | ||
this.deselectOption(listItem.dataset.id); | ||
this.redraw(); | ||
} | ||
/** | ||
* Selects the option with the matching value | ||
* | ||
* @param {Object} value | ||
* Filters the listboxes with the given searchString. | ||
* | ||
* @param {Object} searchString | ||
*/ | ||
selectOption(value) { | ||
let options = this.select.options; | ||
searchLists(searchString) { | ||
let items = this.dualListbox.querySelectorAll(`.${ITEM_ELEMENT}`); | ||
for(let option of options) { | ||
if(option.value === value) { | ||
option.selected = true; | ||
items.forEach(function(item) { | ||
if(searchString) { | ||
if(!item.innerText.includes(searchString)) { | ||
item.style.display = 'none'; | ||
} else { | ||
item.style.display = 'block'; | ||
} | ||
} else { | ||
item.style.display = 'block'; | ||
} | ||
}); | ||
} | ||
/** | ||
* Update the elements in the availeble listbox; | ||
*/ | ||
updateAvailableListbox() { | ||
this.availebleList.innerHTML = ''; | ||
this.availebleList.appendChild(this.availableListTitle); | ||
for(let listItem of this.available){ | ||
this.availebleList.appendChild(this._addClickActions(listItem)); | ||
} | ||
@@ -71,13 +109,26 @@ } | ||
/** | ||
* Deselects the option with the matching value | ||
* | ||
* @param {Object} value | ||
* Update the elements in the selected listbox; | ||
*/ | ||
deselectOption(value) { | ||
let options = this.select.options; | ||
updateSelectedListbox() { | ||
this.selectedList.innerHTML = ''; | ||
this.selectedList.appendChild(this.selectedListTitle); | ||
for(let listItem of this.selected){ | ||
this.selectedList.appendChild(this._addClickActions(listItem)); | ||
} | ||
} | ||
for(let option of options) { | ||
if(option.value === value) { | ||
option.selected = false; | ||
} | ||
// | ||
// | ||
// PRIVATE FUNCTIONS | ||
// | ||
// | ||
/** | ||
* Action to set all listItems to selected. | ||
*/ | ||
_actionAllSelected(event) { | ||
event.preventDefault(); | ||
while(this.available.length > 0) { | ||
this.addSelected(this.available[0]); | ||
} | ||
@@ -87,94 +138,161 @@ } | ||
/** | ||
* Redraws the Dual listbox content | ||
* Action to set one listItem to selected. | ||
*/ | ||
redraw() { | ||
let that = this; | ||
_actionItemSelected(event) { | ||
event.preventDefault(); | ||
let availebleList = this.createListbox(this.available, AVAILABLE_ELEMENT); | ||
let selectedList = this.createListbox(this.selected, SELECTED_ELEMENT); | ||
let buttons = this.createButtons(); | ||
let selected = this.dualListbox.querySelector(`.${SELECTED_MODIFIER}`); | ||
if(selected) { | ||
this.addSelected(selected); | ||
} | ||
} | ||
let search = document.createElement('input'); | ||
search.onchange = function(){that.search(this.value);}; | ||
search.onkeyup = function(){that.search(this.value);}; | ||
/** | ||
* Action to set all listItems to available. | ||
*/ | ||
_actionAllDeselected(event) { | ||
event.preventDefault(); | ||
this.dualListbox.innerHTML = ''; | ||
this.dualListbox.appendChild(search); | ||
this.dualListbox.appendChild(availebleList); | ||
this.dualListbox.appendChild(buttons); | ||
this.dualListbox.appendChild(selectedList); | ||
while(this.selected.length > 0) { | ||
this.removeSelected(this.selected[0]); | ||
} | ||
} | ||
/** | ||
* Filters the listboxes. | ||
* | ||
* @param {Object} searchString | ||
* Action to set one listItem to available. | ||
*/ | ||
search(searchString) { | ||
_actionItemDeselected(event) { | ||
event.preventDefault(); | ||
let selected = this.dualListbox.querySelector(`.${SELECTED_MODIFIER}`); | ||
if(selected) { | ||
this.removeSelected(selected); | ||
} | ||
} | ||
/** | ||
* Action when double clicked on a listItem. | ||
*/ | ||
_actionItemDoubleClick(listItem) { | ||
if (this.selected.indexOf(this) > -1) { | ||
this.removeSelected(listItem); | ||
} else { | ||
this.addSelected(listItem); | ||
} | ||
} | ||
/** | ||
* Action when single clicked on a listItem. | ||
*/ | ||
_actionItemClick(listItem) { | ||
let items = this.dualListbox.querySelectorAll(`.${ITEM_ELEMENT}`); | ||
items.forEach(function(item) { | ||
if(searchString) { | ||
if(!item.innerText.includes(searchString)) { | ||
item.style.display = 'none'; | ||
} else { | ||
item.style.display = 'block'; | ||
} | ||
} else { | ||
item.style.display = 'block'; | ||
items.forEach(function(value) { | ||
if (value !== listItem) { | ||
value.classList.remove(SELECTED_MODIFIER); | ||
} | ||
}); | ||
if(listItem.classList.contains(SELECTED_MODIFIER)) { | ||
listItem.classList.remove(SELECTED_MODIFIER); | ||
} else{ | ||
listItem.classList.add(SELECTED_MODIFIER); | ||
} | ||
} | ||
/** | ||
* Creates the DualListbox. | ||
* @Private | ||
* Adds the needed actions to the elements. | ||
*/ | ||
initDualListbox(container) { | ||
let dualListbox = document.createElement('div'); | ||
dualListbox.classList.add(CONTAINER_BLOCK); | ||
container.insertBefore(dualListbox, this.select); | ||
_addActions() { | ||
this._addButtonActions(); | ||
this._addSearchActions(); | ||
} | ||
/** | ||
* Adds the actions to the buttons that are created. | ||
*/ | ||
_addButtonActions() { | ||
this.add_all_button.addEventListener('click', this._actionAllSelected); | ||
this.add_button.addEventListener('click', this._actionItemSelected); | ||
this.remove_button.addEventListener('click', this._actionItemDeselected); | ||
this.remove_all_button.addEventListener('click', this._actionAllDeselected); | ||
} | ||
/** | ||
* Adds the click items to the listItem. | ||
* | ||
* @param {Object} listItem | ||
*/ | ||
_addClickActions(listItem) { | ||
listItem.addEventListener('dblclick', () => this._actionItemDoubleClick(listItem)); | ||
listItem.addEventListener('click', () => this._actionItemClick(listItem)); | ||
return listItem; | ||
} | ||
/** | ||
* @Private | ||
* Adds the actions to the search input. | ||
*/ | ||
_addSearchActions() { | ||
this.search.addEventListener('change', (event) => this.searchLists(event.target.value)); | ||
this.search.addEventListener('keyup', (event) => this.searchLists(event.target.value)); | ||
} | ||
/** | ||
* @Private | ||
* Builds the Dual listbox and makes it visible to the user. | ||
*/ | ||
_buildDualListbox(container) { | ||
this.select.style.display = 'none'; | ||
this.dualListbox = dualListbox; | ||
this.redraw(); | ||
this.dualListBoxContainer.appendChild(this.availebleList); | ||
this.dualListBoxContainer.appendChild(this.buttons); | ||
this.dualListBoxContainer.appendChild(this.selectedList); | ||
this.dualListbox.appendChild(this.search); | ||
this.dualListbox.appendChild(this.dualListBoxContainer); | ||
container.insertBefore(this.dualListbox, this.select); | ||
} | ||
/** | ||
* Creates a listbox | ||
* Creates the buttons to add/remove the selected item. | ||
*/ | ||
createListbox(optionList, className) { | ||
let selectList = document.createElement('ul'); | ||
selectList.classList.add(className); | ||
_createButtons() { | ||
this.buttons = document.createElement('div'); | ||
this.buttons.classList.add(BUTTONS_ELEMENT); | ||
for(let listItem of optionList){ | ||
selectList.appendChild(this.addClickActions(listItem)); | ||
} | ||
return selectList; | ||
this.add_all_button = document.createElement('button'); | ||
this.add_all_button.classList.add(BUTTON_ELEMENT); | ||
this.add_all_button.innerHTML = this.addAllButtonText; | ||
this.add_button = document.createElement('button'); | ||
this.add_button.classList.add(BUTTON_ELEMENT); | ||
this.add_button.innerHTML = this.addButtonText; | ||
this.remove_button = document.createElement('button'); | ||
this.remove_button.classList.add(BUTTON_ELEMENT); | ||
this.remove_button.innerHTML = this.removeButtonText; | ||
this.remove_all_button = document.createElement('button'); | ||
this.remove_all_button.classList.add(BUTTON_ELEMENT); | ||
this.remove_all_button.innerHTML = this.removeAllButtonText; | ||
this.buttons.appendChild(this.add_all_button); | ||
this.buttons.appendChild(this.add_button); | ||
this.buttons.appendChild(this.remove_button); | ||
this.buttons.appendChild(this.remove_all_button); | ||
} | ||
/** | ||
* Adds the click items to the listItem | ||
* @Private | ||
* Creates the listItem out of the option. | ||
*/ | ||
addClickActions(listItem) { | ||
let that = this; | ||
listItem.ondblclick = function() { | ||
if (that.selected.indexOf(this) > -1) { | ||
that.removeSelected(listItem); | ||
} else { | ||
that.addSelected(listItem); | ||
} | ||
}; | ||
_createListItem(option) { | ||
let listItem = document.createElement('li'); | ||
listItem.onclick = function() { | ||
let items = that.dualListbox.querySelectorAll(`.${ITEM_ELEMENT}`); | ||
items.forEach(function(value) { | ||
value.classList.remove(SELECTED_MODIFIER); | ||
}); | ||
if(this.classList.contains(SELECTED_MODIFIER)) { | ||
this.classList.remove(SELECTED_MODIFIER); | ||
} else{ | ||
this.classList.add(SELECTED_MODIFIER); | ||
} | ||
}; | ||
listItem.classList.add(ITEM_ELEMENT); | ||
listItem.innerHTML = option.innerHTML; | ||
listItem.dataset.id = option.value; | ||
@@ -185,59 +303,112 @@ return listItem; | ||
/** | ||
* Creates the buttons to add/remove the selected item. | ||
* @Private | ||
* Creates the search input. | ||
*/ | ||
createButtons() { | ||
let that = this; | ||
_createSearch() { | ||
this.search = document.createElement('input'); | ||
this.search.classList.add(SEARCH_ELEMENT); | ||
} | ||
let buttons = document.createElement('div'); | ||
let add_button = document.createElement('button'); | ||
let remove_button = document.createElement('button'); | ||
/** | ||
* @Private | ||
* Deselects the option with the matching value | ||
* | ||
* @param {Object} value | ||
*/ | ||
_deselectOption(value) { | ||
let options = this.select.options; | ||
add_button.innerText = 'Add'; | ||
add_button.onclick = function() { | ||
let selected = that.dualListbox.querySelector(`.${SELECTED_MODIFIER}`); | ||
if(selected) { | ||
that.addSelected(selected); | ||
for(let option of options) { | ||
if(option.value === value) { | ||
option.selected = false; | ||
} | ||
}; | ||
remove_button.innerText = 'remove'; | ||
remove_button.onclick = function() { | ||
let selected = that.dualListbox.querySelector(`.${SELECTED_MODIFIER}`); | ||
if(selected) { | ||
that.removeSelected(selected); | ||
} | ||
}; | ||
} | ||
buttons.appendChild(add_button); | ||
buttons.appendChild(remove_button); | ||
if(this.removeEvent) { | ||
this.removeEvent(value); | ||
} | ||
} | ||
return buttons; | ||
/** | ||
* @Private | ||
* Set the option variables to this. | ||
*/ | ||
_initOptions(options) { | ||
this.addEvent = options.addEvent; | ||
this.removeEvent = options.removeEvent; | ||
this.availableTitle = options.availableTitle || 'Available options'; | ||
this.selectedTitle = options.selectedTitle || 'Selected options'; | ||
this.addButtonText = options.addButtonText || 'add'; | ||
this.removeButtonText = options.removeButtonText || 'remove'; | ||
this.addAllButtonText = options.addAllButtonText || 'add all'; | ||
this.removeAllButtonText = options.removeAllButtonText || 'remove all'; | ||
} | ||
/** | ||
* splits the select options and places them in the correct list. | ||
* @Private | ||
* Creates all the static elements for the Dual listbox. | ||
*/ | ||
splitSelectOptions(select) { | ||
let options = select.options; | ||
_initReusableElements() { | ||
this.dualListbox = document.createElement('div'); | ||
this.dualListbox.classList.add(MAIN_BLOCK); | ||
if(this.select.id) { | ||
this.dualListbox.classList.add(this.select.id); | ||
} | ||
for(let option of options){ | ||
let listItem = this.createListItem(option); | ||
this.dualListBoxContainer = document.createElement('div'); | ||
this.dualListBoxContainer.classList.add(CONTAINER_ELEMENT); | ||
if(option.attributes.selected) { | ||
this.selected.push(listItem); | ||
} else { | ||
this.available.push(listItem); | ||
this.availebleList = document.createElement('ul'); | ||
this.availebleList.classList.add(AVAILABLE_ELEMENT); | ||
this.selectedList = document.createElement('ul'); | ||
this.selectedList.classList.add(SELECTED_ELEMENT); | ||
this.availableListTitle = document.createElement('li'); | ||
this.availableListTitle.classList.add(TITLE_ELEMENT); | ||
this.availableListTitle.innerText = this.availableTitle; | ||
this.selectedListTitle = document.createElement('li'); | ||
this.selectedListTitle.classList.add(TITLE_ELEMENT); | ||
this.selectedListTitle.innerText = this.selectedTitle; | ||
this._createButtons(); | ||
this._createSearch(); | ||
} | ||
/** | ||
* @Private | ||
* Selects the option with the matching value | ||
* | ||
* @param {Object} value | ||
*/ | ||
_selectOption(value) { | ||
let options = this.select.options; | ||
for(let option of options) { | ||
if(option.value === value) { | ||
option.selected = true; | ||
} | ||
} | ||
if(this.addEvent) { | ||
this.addEvent(value); | ||
} | ||
} | ||
/** | ||
* Creates the listItem out of the option | ||
* @Private | ||
* Splits the select options and places them in the correct list. | ||
*/ | ||
createListItem(option) { | ||
let listItem = document.createElement('li'); | ||
listItem.classList.add(ITEM_ELEMENT); | ||
listItem.innerText = option.innerText; | ||
listItem.dataset.id = option.value; | ||
_splitSelectOptions(select) { | ||
let options = select.options; | ||
[].forEach.call(options, (option) => { | ||
let listItem = this._createListItem(option); | ||
return listItem; | ||
if(option.attributes.selected) { | ||
this.selected.push(listItem); | ||
} else { | ||
this.available.push(listItem); | ||
} | ||
}); | ||
} | ||
@@ -244,0 +415,0 @@ } |
import DualListbox, { DualListbox as DualListbox2 } from '../src/dual-listbox.js'; | ||
const SELECT_CLASS = 'select'; | ||
const FIXTURE_EMPTY_SELECT = ` | ||
<select class="${SELECT_CLASS}"></select> | ||
`; | ||
const FIXTURE_FILLED_SELECT = ` | ||
<select class="${SELECT_CLASS}"> | ||
<option value="1">One</option> | ||
<option value="2">Two</option> | ||
<option value="3">Three</option> | ||
<option value="4">Four</option> | ||
<option value="5">Five</option> | ||
<option value="6">Six</option> | ||
<option value="7">Seven</option> | ||
<option value="8">Eight</option> | ||
<option value="9">Nine</option> | ||
<option value="10">Ten</option> | ||
</select> | ||
`; | ||
const FIXTURE_FILLED_SELECT_PRESELECTED = ` | ||
<select class="${SELECT_CLASS}"> | ||
<option value="1">One</option> | ||
<option value="2" selected>Two</option> | ||
<option value="3">Three</option> | ||
<option value="4">Four</option> | ||
<option value="5">Five</option> | ||
<option value="6">Six</option> | ||
<option value="7">Seven</option> | ||
<option value="8">Eight</option> | ||
<option value="9">Nine</option> | ||
<option value="10">Ten</option> | ||
</select> | ||
`; | ||
const FIXTURE_FILLED_SELECT_WITH_ID = ` | ||
<select class="${SELECT_CLASS}" id='select'> | ||
<option value="1">One</option> | ||
<option value="2" selected>Two</option> | ||
<option value="3">Three</option> | ||
<option value="4">Four</option> | ||
<option value="5">Five</option> | ||
<option value="6">Six</option> | ||
<option value="7">Seven</option> | ||
<option value="8">Eight</option> | ||
<option value="9">Nine</option> | ||
<option value="10">Ten</option> | ||
</select> | ||
`; | ||
describe('module', function() { | ||
@@ -13,1 +69,207 @@ it('should export a default', () => { | ||
}); | ||
describe('Duallistbox', function() { | ||
it('should be able to initialize an empty select', () => { | ||
setFixtures(FIXTURE_EMPTY_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
expect(dlb.available.length).toBe(0); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
it('should be able to initialize a filled select', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
it('should be able to initialize a filled select with preselected items', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT_PRESELECTED); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
expect(dlb.available.length).toBe(9); | ||
expect(dlb.selected.length).toBe(1); | ||
}); | ||
it('should be able to initialize a filled select with id', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT_WITH_ID); | ||
let dlb = new DualListbox(`#select`); | ||
expect(dlb.available.length).toBe(9); | ||
expect(dlb.selected.length).toBe(1); | ||
}); | ||
it('should be able to add a list item to selected', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
let listItem = document.querySelector('[data-id="1"]'); | ||
dlb.addSelected(listItem); | ||
expect(dlb.available.length).toBe(9); | ||
expect(dlb.selected.length).toBe(1); | ||
}); | ||
it('should be able to add a list item to selected that is already selected', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
let listItem = document.querySelector('[data-id="1"]'); | ||
dlb.addSelected(listItem); | ||
expect(dlb.available.length).toBe(9); | ||
expect(dlb.selected.length).toBe(1); | ||
dlb.addSelected(listItem); | ||
expect(dlb.available.length).toBe(9); | ||
expect(dlb.selected.length).toBe(1); | ||
}); | ||
it('should be able to remove a list item from selected', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
let listItem = document.querySelector('[data-id="1"]'); | ||
dlb.addSelected(listItem); | ||
expect(dlb.available.length).toBe(9); | ||
expect(dlb.selected.length).toBe(1); | ||
dlb.removeSelected(listItem); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
it('should be able to remove a list item from selected that is not selected', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
let listItem = document.querySelector('[data-id="1"]'); | ||
dlb.removeSelected(listItem); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
it('should be able to seach the items', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
dlb.searchLists('One'); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
it('should be able to seach the items with no text', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
dlb.searchLists(''); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
it('should be able to hit the addEvent callback', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
function addCallback(value) { | ||
expect(value).toBe("1"); | ||
} | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`, { | ||
addEvent: addCallback | ||
}); | ||
let listItem = document.querySelector('[data-id="1"]'); | ||
dlb.addSelected(listItem); | ||
}); | ||
it('should be able to hit the removeEvent callback', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT_PRESELECTED); | ||
function addCallback(value) { | ||
expect(value).toBe("2"); | ||
} | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`, { | ||
removeEvent: addCallback | ||
}); | ||
let listItem = document.querySelector('[data-id="2"]'); | ||
dlb.removeSelected(listItem); | ||
}); | ||
it('should be able to click on one of the elements', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
new DualListbox(`.${SELECT_CLASS}`); | ||
let listItem = document.querySelector('[data-id="2"]'); | ||
let clickEvent = document.createEvent('MouseEvents'); | ||
clickEvent.initEvent('click', true, true); | ||
listItem.dispatchEvent(clickEvent); | ||
let selectedItem = document.querySelector('.dual-listbox__item--selected'); | ||
expect(selectedItem).toBeTruthy(); | ||
}); | ||
it('should be able to click on one of the elements to remove the class', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
new DualListbox(`.${SELECT_CLASS}`); | ||
let listItem = document.querySelector('[data-id="2"]'); | ||
let clickEvent = document.createEvent('MouseEvents'); | ||
clickEvent.initEvent('click', true, true); | ||
listItem.dispatchEvent(clickEvent); | ||
let selectedItem = document.querySelector('.dual-listbox__item--selected'); | ||
expect(selectedItem).toBeTruthy(); | ||
listItem.dispatchEvent(clickEvent); | ||
let selectedItem2 = document.querySelector('.dual-listbox__item--selected'); | ||
expect(selectedItem2).toBeFalsy(); | ||
}); | ||
it('should be able to doubleclick on one of the elements to select', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
let listItem = document.querySelector('[data-id="2"]'); | ||
let clickEvent = document.createEvent('MouseEvents'); | ||
clickEvent.initEvent('dblclick', true, true); | ||
listItem.dispatchEvent(clickEvent); | ||
expect(dlb.available.length).toBe(9); | ||
expect(dlb.selected.length).toBe(1); | ||
}); | ||
// it('should be able to doubleclick on one of the elements to deselect', () => { | ||
// setFixtures(FIXTURE_FILLED_SELECT_PRESELECTED); | ||
// let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
// expect(dlb.available.length).toBe(9); | ||
// expect(dlb.selected.length).toBe(1); | ||
// let listItem = document.querySelector('[data-id="2"]'); | ||
// let clickEvent = document.createEvent('MouseEvents'); | ||
// clickEvent.initEvent('dblclick', true, true); | ||
// listItem.dispatchEvent(clickEvent); | ||
// expect(dlb.available.length).toBe(10); | ||
// expect(dlb.selected.length).toBe(0); | ||
// }); | ||
it('should be able fire search on change', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
let search = document.querySelector('.dual-listbox__search'); | ||
let changeEvent = document.createEvent('MouseEvents'); | ||
changeEvent.initEvent('change', true, true); | ||
search.dispatchEvent(changeEvent); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
it('should be able fire search with the keyboard', () => { | ||
setFixtures(FIXTURE_FILLED_SELECT); | ||
let dlb = new DualListbox(`.${SELECT_CLASS}`); | ||
let search = document.querySelector('.dual-listbox__search'); | ||
let keyupEvent = document.createEvent('MouseEvents'); | ||
keyupEvent.initEvent('keyup', true, true); | ||
search.dispatchEvent(keyupEvent); | ||
expect(dlb.available.length).toBe(10); | ||
expect(dlb.selected.length).toBe(0); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
66851
26
778
0
66
33
1