Skip to content

Instantly share code, notes, and snippets.

@aaronanderson
Last active May 29, 2017 23:26
Show Gist options
  • Save aaronanderson/f9fb81bad790725c4b3c691921ad1f08 to your computer and use it in GitHub Desktop.
Save aaronanderson/f9fb81bad790725c4b3c691921ad1f08 to your computer and use it in GitHub Desktop.
A Polymer 2.0 multiselection list component consisting of two adjacent lists, one with available items (source) and one with selected items (target).
<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="../iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../paper-listbox/paper-listbox.html">
<link rel="import" href="../paper-item/paper-item.html">
<link rel="import" href="../paper-button/paper-button.html" />
<link rel="import" href="../paper-input/paper-input.html">
<dom-module id="intworkspace-multi-listbox">
<template>
<style include="iron-flex iron-flex-alignment">
.container {
background-color: #ccc;
margin: 10px;
}
paper-button {
margin: 5px;
}
paper-input {
}
paper-listbox {
background-color: white;
width: var(--multi-listbox-width, 200px);
height: var(--multi-listbox-height, 300px);
overflow: auto;
}
</style>
<slot id="labelView"><template preserve-content><paper-item value="[[item]]" item-id="[[item.id]]">[[item.name]]</paper-item></template></slot>
</template>
<template id="listView">
<div class="layout horizontal container fullbleed">
<div class="layout vertical container">
<paper-input value="{{sourceFilter}}" label="Filter" floatingLabel id="sourceFilter"></paper-input>
<paper-listbox id="sourceItems" attr-for-selected="item-id" multi selected-items="{{selectedSourceItems}}">
<template is="dom-repeat" items="[[sourceItems]]" as="item" filter="{{filter(sourceFilter)}}" sort="{{sort}}">
</template>
</paper-listbox>
</div>
<div class="layout vertical container">
<div class="flex"></div>
<paper-button id="add" raised on-click="add">&gt;</paper-button>
<paper-button id="addAll" raised on-click="addAll">&gt;&gt;</paper-button>
<paper-button id="remove" raised on-click="remove">&lt;</paper-button>
<paper-button id="removeAll" raised on-click="removeAll">&lt;&lt;</paper-button>
<div class="flex"></div>
</div>
<div class="layout vertical container">
<paper-input value="{{targetFilter}}" label="Filter" floatingLabel id="targetFilter"></paper-input>
<paper-listbox id="targetItems" attr-for-selected="item-id" multi selected-items="{{selectedTargetItems}}">
<template is="dom-repeat" items="[[targetItems]]" as="item" filter="{{filter(targetFilter)}}" sort="{{sort}}">
</template>
</paper-listbox>
</div>
</div>
</template>
<script>
class IntMultiListBox extends Polymer.Element {
static get is() {
return 'intworkspace-multi-listbox';
}
static get properties() {
return {
sourceItems: {
notify: true,
type: Array,
value: []
},
selectedSourceItems: {
notify: true,
type: Array,
value: []
},
targetItems: {
notify: true,
type: Array,
value: []
},
selectedTargetItems: {
notify: true,
type: Array,
value: []
},
sourceFilter: {
type: String,
},
targetFilter: {
type: String,
},
filter: {
type: Object,
value: function(){
return IntMultiListBox.defaultFilter;
}
},
sort: {
type: Object,
value: function(){
return IntMultiListBox.defaultSort;
}
},
}
}
static get observers() {
return [
'sourceItemsChanged(sourceItems.*)',
'targetItemsChanged(targetItems.*)',
'selectedSourceItemsChanged(selectedSourceItems.*)',
'selectedTargetItemsChanged(selectedTargetItems.*)',
]
}
ready(){
super.ready();
let labelTemplate = this.$.labelView.assignedNodes().find((e) => {
return e instanceof HTMLTemplateElement;
});
labelTemplate = labelTemplate ? labelTemplate : this.$.labelView.children[0];
let listTemplate = document.importNode(Polymer.DomModule.import(IntMultiListBox.is, "#listView"), true);
let repeatTemplate = listTemplate.content.querySelector("#sourceItems > template[is='dom-repeat']");
repeatTemplate.content.appendChild(document.importNode(labelTemplate.content, true));
repeatTemplate = listTemplate.content.querySelector("#targetItems > template[is='dom-repeat']");
repeatTemplate.content.appendChild(document.importNode(labelTemplate.content, true));
this.listInstance = this._stampTemplate(listTemplate);
this.shadowRoot.appendChild(this.listInstance);
}
sourceItemsChanged(sourceItems) {
//console.log("sourceItems changed", sourceItems);
if (this.listInstance) {
if (sourceItems.base.length > 0) {
this.listInstance.$.addAll.disabled = false;
} else {
this.listInstance.$.addAll.disabled = true;
}
}
}
targetItemsChanged(targetItems) {
//console.log("targetItems changed", targetItems);
if (this.listInstance) {
if (targetItems.base.length > 0) {
this.listInstance.$.removeAll.disabled = false;
} else {
this.listInstance.$.removeAll.disabled = true;
}
}
}
selectedSourceItemsChanged(sourceItems) {
//console.log("selected sourceItems changed", sourceItems);
if (this.listInstance) {
if (sourceItems.base.length > 0) {
this.listInstance.$.add.disabled = false;
} else {
this.listInstance.$.add.disabled = true;
}
}
}
selectedTargetItemsChanged(targetItems) {
//console.log("selected targetItems changed", targetItems);
if (this.listInstance) {
if (targetItems.base.length > 0) {
this.listInstance.$.remove.disabled = false;
} else {
this.listInstance.$.remove.disabled = true;
}
}
}
add() {
this.moveItems(Array.from(this.selectedSourceItems), 'sourceItems', 'targetItems');
this.listInstance.$.sourceItems.selectedValues = [];
}
addAll() {
this.moveItems(Array.from(this.sourceItems).filter(this.filter(this.sourceFilter)), 'sourceItems', 'targetItems');
}
remove() {
this.moveItems(Array.from(this.selectedTargetItems), 'targetItems', 'sourceItems');
this.listInstance.$.targetItems.selectedValues = [];
}
removeAll() {
this.moveItems(Array.from(this.targetItems).filter(this.filter(this.targetFilter)), 'targetItems', 'sourceItems');
}
moveItems(selectedItems, source, target) {
selectedItems.forEach((item) => {
if (item instanceof Polymer.Element) {
item = item.value;
}
var index = this[source].indexOf(item);
if (index != -1) {
var item = this.splice(source, index, 1);
this.push(target, item[0]);
}
});
}
static defaultFilter(key) {
return function(item) {
if (!item) return false;
if (key != null || key != undefined) {
return (item.name && ~item.name.toLowerCase().indexOf(key.toLowerCase()));
//|| (item.id && ~item.id.toLowerCase().indexOf(key.toLowerCase()));
} else
return true;
};
}
static defaultSort(item1,item2){
if (item1 && item2){
if (item1.name && item2.name){
if (item1.name < item2.name){
return -1;
}
if (item1.name > item2.name){
return 1;
}
return 0;
}
}
return -1;
}
}
window.customElements.define(IntMultiListBox.is, IntMultiListBox);
</script>
</dom-module>
<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="intworkspace-multi-listbox.html">
<dom-module id="multlist-test">
<template>
<style>
:host {
display: block;
}
</style>
<intworkspace-multi-listbox id="listbox" target-items="{{targetItems}}">
<template preserve-content><paper-item value="[[item]]" item-id="[[item.id]]">Override [[item.name]]</paper-item></template>
</intworkspace-multi-listbox>
</template>
<script>
/**
* `multlist-test`
*
*
* @customElement
* @polymer
* @demo demo/index.html
*/
class MultlistTest extends Polymer.Element {
static get is() { return 'multlist-test'; }
static get properties() {
return {
targetItems: {
type: Array,
value: [],
}
}
}
static get observers() {
return [
'targetItemsChanged(targetItems.*)',
]
}
ready(){
super.ready();
this.$.listbox.set('sourceItems',[{id: 1, name: "test 1"},{id: 2, name: "test 2"}]);
}
targetItemsChanged(change){
console.log("targetItemsChanged", change, this.targetItems);
}
}
window.customElements.define(MultlistTest.is, MultlistTest);
</script>
</dom-module>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment