Last active
May 31, 2016 09:16
-
-
Save Qvatra/ebc80375ab71666725b8e1bdb85c25c2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link rel='import' href="../../../bower_components/polymer/polymer.html"> | |
<link rel='import' href="../../../bower_components/paper-menu-button/paper-menu-button.html"> | |
<link rel='import' href="../../../bower_components/paper-item/paper-item.html"> | |
<link rel='import' href="../../../bower_components/iron-icons/iron-icons.html"> | |
<link rel='import' href="../../../bower_components/paper-material/paper-material.html"> | |
<link rel='import' href="../../../bower_components/iron-selector/iron-selector.html"> | |
<link rel='import' href="../rg-paper-input-search.html"> | |
<link rel='import' href="rg-select-input.html"> | |
<!-- | |
Custom Polymer element. | |
Validatable dropdown selector (multi or single) element with an optional searchbar. | |
rg-select-input used as a trigger for the dropdown. | |
Options feed could also containt 'title elements' that are not selectable items. | |
Returns selectedKey (selectedKeys if multi===true). | |
--> | |
<dom-module id="rg-select"> | |
<template> | |
<style include="shared-styles"> | |
:host paper-menu-button { | |
padding: 0; | |
} | |
:host paper-menu { | |
padding: 0; | |
} | |
:host paper-item { | |
cursor: pointer; | |
min-height: 38px; | |
} | |
:host .search { | |
background: var(--sidebar-background-color-light); | |
} | |
:host .disabled { | |
color: var(--disabled-text-color); | |
} | |
:host .groupName { | |
font-weight: bold; | |
font-variant: small-caps; | |
cursor: default; | |
background: var(--sidebar-background-color-light); | |
} | |
:host .selected{ | |
background: var(--light-primary-color); | |
} | |
:host paper-item { | |
--paper-item-focused-before: { | |
background: none; | |
} | |
} | |
</style> | |
<!-- ignore-select to prevent auto closing if some value was selected and users search for the second time --> | |
<paper-menu-button id="dropdown" horizontal-align="left" on-paper-dropdown-open="_onOpen" on-paper-dropdown-close="_onClose" ignore-select> | |
<rg-select-input validator="[[validator]]" | |
label="[[label]]" | |
value="[[_valuetoShow]]" | |
name="[[name]]" | |
required="[[required]]" | |
class="dropdown-trigger" | |
error-message="Invalid input!"> | |
</rg-select-input> | |
<div class="dropdown-content"> | |
<div class="search layout horizontal" hidden$="[[!enableSearch]]"> | |
<rg-paper-input-search id="searchInput" value="{{_searchQuery}}" elevation="0"></rg-paper-input-search> | |
</div> | |
<iron-selector multi="[[multi]]" | |
attr-for-selected="key" | |
selected="{{selectedKey}}" | |
selected-values="{{_selectedKeys}}" | |
selected-class="selected" | |
selectable=".selectable"> | |
<template is="dom-repeat" items="[[_options]]" filter="[[_computeSearchFilter(_searchQuery)]]"> | |
<paper-item key="[[item.key]]" class$="[[_getItemCssClass(item.occursInContext)]] [[_getSelectableCssClass(item.isCategoryName)]]" on-tap="_select"> | |
[[item.label]] | |
</paper-item> | |
</template> | |
</iron-selector> | |
</div> | |
</paper-menu-button> | |
</template> | |
<script> | |
(function() { | |
'use strict'; | |
Polymer({ | |
is: 'rg-select', | |
properties: { | |
// optional validator function wrapped in an Object: {validate: (val) => {}} | |
validator: Object, | |
label: { | |
type: String, | |
value: '' | |
}, | |
multi: { | |
type: Boolean, | |
value: false | |
}, | |
name: String, | |
required: Boolean, | |
// array of {key, label, isCategoryName, occursInContext} objects feed | |
options: Array, | |
// internal copy of options. | |
_options: { | |
type: Array, | |
computed: '_copyArray(options)' | |
}, | |
// Set and Get key value of selected key (works only if multi=false) | |
selectedKey: { | |
type: String, | |
notify: true, | |
observer: '_selectedKeyChange' | |
}, | |
// Set selected keys if multi=true. Get is implemented using 'on-selected-keys-change' event. | |
selectedKeys: Array, | |
// internal copy of selectedKeys. | |
_selectedKeys: { | |
type: Array, | |
computed: '_copyArray(selectedKeys)' | |
}, | |
// show or hide a search bar | |
enableSearch: { | |
type: Boolean, | |
value: false | |
}, | |
// return selected items with occursInContext === false | |
invalidSelectedKeys: { | |
type: Array, | |
notify: true, | |
value: [] | |
}, | |
_searchQuery: String, | |
// value to show in the 'rg-select-input' trigger | |
_valueToShow: String, | |
}, | |
observers: [ | |
'_selectedKeysChange(_selectedKeys.*)', | |
'_evalInvalidSelectedItems(options, selectedKey)', | |
'_evalInvalidSelectedItems(options, selectedKeys.*)' | |
], | |
ready() { | |
// Closes dropdown if clicking outsite the element | |
window.addEventListener('click', (ev) => { | |
for (var element = ev.target; element; element = element.parentNode) { | |
if (element === this) { | |
return; | |
} | |
} | |
this._close(); | |
}); | |
}, | |
_selectedKeyChange(newVal, oldVal) { | |
if (Util.hasText(newVal) && !this.multi) { | |
this.set('_valuetoShow', newVal); | |
} | |
if (Util.exists(newVal) && Util.exists(oldVal) && newVal !== oldVal) { | |
this.fire('selected-key-change', newVal); | |
} | |
}, | |
_selectedKeysChange(vals) { | |
if (!Util.exists(vals) || !Util.exists(vals.base)) | |
return; | |
if (this.multi) { | |
this.set('_valuetoShow', vals.base.join(", ")); | |
this.debounce('_selectedKeysChange', () => { | |
if (JSON.stringify(vals.base) !== JSON.stringify(this.selectedKeys)) { | |
this.fire('selected-keys-change', vals.base); | |
} | |
}, 50); | |
} | |
}, | |
_select(ev) { | |
// don't close on multiselect and if item could not be selected (e.g. groupname) | |
if (!this.multi && ev.target.className.indexOf('selectable') > -1) { | |
this._close(); | |
} | |
}, | |
_getItemCssClass(occursInContext) { | |
return occursInContext === false ? 'disabled' : ''; | |
}, | |
_getSelectableCssClass(isCategoryName) { | |
return isCategoryName === true ? 'groupName' : 'selectable'; | |
}, | |
_onOpen() { | |
if (this.enableSearch) { | |
// search input needs to be visible before it can be focussed. | |
this.async(() => this.$.searchInput.focus(), 300); | |
} | |
}, | |
_onClose() { | |
// wait for close animation to complete before resetting the filter | |
this.async(() => { this.set('_searchQuery', null); }, 200); | |
}, | |
_close() { | |
// to close dropdown upon selecting an already selected item | |
this.$.dropdown.close(); | |
}, | |
_computeSearchFilter(searchText) { | |
return Util.hasText(searchText) ? option => Util.isStringContainingIgnoreCase(option.label, searchText) : null; | |
}, | |
_evalInvalidSelectedItems(options, selected) { | |
if (Util.notEmptyArray(this.options) && Util.exists(selected)) { | |
let selectedOptions; | |
if (this.multi) { | |
selectedOptions = options.filter(opt => selected.base.indexOf(opt.key) > -1); | |
} else { | |
selectedOptions = options.filter(opt => opt.key === selected); | |
} | |
this.set('invalidSelectedKeys', selectedOptions.filter(opt => opt.occursInContext === false).map(i => i.key)); | |
} | |
}, | |
_copyArray(arr) { | |
return Util.exists(arr) ? arr.slice() : []; | |
} | |
}); | |
})(); | |
</script> | |
</dom-module> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment