Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dual-listbox

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dual-listbox - npm Package Compare versions

Comparing version 0.1.1 to 1.0.1

build/tasks/watch.js

4

build/paths.js

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc