No results were found would you like to use the search value?
+ [[searchValue]]
+
+
+
+
+
+
+
+
+ [[item]]
+
+
+`,
+
+ is: 'paper-dropdown-input',
+
+ behaviors: [
+ IronButtonState,
+ IronControlState,
+ IronFormElementBehavior,
+ Templatizer,
+ IronValidatableBehavior
+ ],
+
+ properties: {
+ /**
+ * The derived "label" of the currently selected item. This value
+ * is the `label` property on the selected item if set, or else the
+ * trimmed text content of the selected item.
+ */
+ selectedItemLabel: {
+ type: String,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The last selected item. An item is selected if the dropdown menu has
+ * a child with slot `dropdown-content`, and that child triggers an
+ * `iron-select` event with the selected `item` in the `detail`.
+ *
+ * @type {?Object}
+ */
+ selectedItem: {
+ type: Object,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The value for this element that will be used when submitting in
+ * a form. It is read only, and will always have the same value
+ * as `selectedItemLabel`.
+ */
+ value: {
+ type: String,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The label for the dropdown.
+ */
+ label: {
+ type: String
+ },
+
+ /**
+ * The placeholder for the dropdown.
+ */
+ placeholder: {
+ type: String
+ },
+
+ /**
+ * The error message to display when invalid.
+ */
+ errorMessage: {
+ type: String
+ },
+
+ /**
+ * True if the dropdown is open. Otherwise, false.
+ */
+ opened: {
+ type: Boolean,
+ notify: true,
+ value: false,
+ observer: '_openedChanged'
+ },
+
+ /**
+ * By default, the dropdown will constrain scrolling on the page
+ * to itself when opened.
+ * Set to true in order to prevent scroll from being constrained
+ * to the dropdown when it opens.
+ */
+ allowOutsideScroll: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable the floating label. Bind this to the
+ * ``'s `noLabelFloat` property.
+ */
+ noLabelFloat: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+
+ /**
+ * Set to true to always float the label. Bind this to the
+ * ``'s `alwaysFloatLabel` property.
+ */
+ alwaysFloatLabel: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable animations when opening and closing the
+ * dropdown.
+ */
+ noAnimations: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * horizontally relative to the dropdown trigger.
+ */
+ horizontalAlign: {
+ type: String,
+ value: 'left'
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * vertically relative to the dropdown trigger.
+ */
+ verticalAlign: {
+ type: String,
+ value: 'top'
+ },
+
+ /**
+ * If true, the `horizontalAlign` and `verticalAlign` properties will
+ * be considered preferences instead of strict requirements when
+ * positioning the dropdown and may be changed if doing so reduces
+ * the area of the dropdown falling outside of `fitInto`.
+ */
+ dynamicAlign: {
+ type: Boolean
+ },
+
+ /**
+ * If true, the `horizontalAlign`, `verticalAlign` and dynamicAlign
+ * properties will be used, placing the dropdown around the input
+ * else the dropdow will be placed over the input.
+ */
+ noOverlap: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Whether focus should be restored to the dropdown when the menu closes.
+ */
+ restoreFocusOnClose: {
+ type: Boolean,
+ value: true
+ },
+
+ /**
+ * The maximum amount of items the dropdown will render
+ * User will be asked to enter a more specific query if this
+ * threshold is exceeded
+ */
+ maxSize: {
+ type: Number,
+ value: 50
+ },
+ /**
+ * Set to true if the maxSize threshold is exceeded
+ */
+ tooBig: {
+ type: Boolean,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * If set, the user can choose to use the entered search query
+ * as the value.
+ * This makes the element behave more like an autocompletion element.
+ */
+ freedom: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The items that this element will filter on, and present to the
+ * user if it matches the user's search query
+ */
+ items: {
+ type: Array,
+ value: function() {return []}
+ },
+
+ /**
+ * The current search query entered by the user
+ */
+ searchValue: {
+ type: String,
+ notify: true,
+ value: ""
+ },
+
+ /**
+ * The property name that items will have that paper-dropdown-input
+ * can filter on
+ */
+ filterProperty: {
+ type: String,
+ value: "value"
+ },
+
+ /**
+ * The filter function, executed each time 'items' or 'searchValue' changes
+ * The default function expects an array of strings or an array of
+ * objects containing the 'value' property
+ */
+ filter: {
+ type: Function,
+ value: function() {
+ return function(items, searchValue, filterProperty) {
+ // older version of filter did not have filterProperty as an argument
+ // fallback for backwards compatibility
+ if (!filterProperty) {
+ filterProperty = this.filterProperty;
+ }
+ if (!searchValue) {
+ return items;
+ } else {
+ var _searchValue = searchValue.toLowerCase();
+ return items.filter( function(item) {
+ if (!item[filterProperty] && typeof item != "string") {
+ console.error("paper-dropdown-input: item in `items`:", item, " is not a string or does not contain `" + filterProperty + "` property");
+ return true; // everything goes through
+ } else {
+ return (item[filterProperty] || item).toLowerCase().indexOf(_searchValue) > -1;
+ }
+ });
+ }
+ }
+ }
+ },
+
+ /**
+ * The remaining items after filtering.
+ * These are shown to the user in the dropdown.
+ * This list is truncated if 'maxSize' is exceeded
+ */
+ _filtereditems: {
+ type: Array,
+ computed: "_filterItems(items, searchValue, filterProperty)"
+ },
+
+ /**
+ * Makes the element read-only. The dropdown will not open.
+ */
+ readonly: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+
+ /**
+ * passed to paper-listbox.
+ * https://www.webcomponents.org/element/PolymerElements/paper-listbox/paper-listbox#property-selectable
+ */
+ selectable: {
+ type: String
+ },
+
+ /**
+ * disables search, making it more like a regular dropdown.
+ */
+ noSearch: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ }
+ },
+
+ listeners: {
+ 'tap': '_onTap',
+ 'dom-change': '_refit'
+ // 'neon-animation-finish': '_focusSearch'
+ },
+
+ keyBindings: {
+ 'up down': 'open',
+ 'esc': 'close'
+ },
+
+ hostAttributes: {
+ role: 'combobox',
+ 'aria-autocomplete': 'none',
+ 'aria-haspopup': 'true'
+ },
+
+ observers: [
+ '_selectedItemChanged(selectedItem)',
+ '_updateTemplate(_filtereditems)',
+ '_updateSelectedItem(items.length, selectedItemLabel, filterProperty)',
+ '_updateSelectedItemLabel(items, value, filterProperty)'
+ ],
+
+ attached: function() {
+ // NOTE(cdata): Due to timing, a preselected value in a `IronSelectable`
+ // child will cause an `iron-select` event to fire while the element is
+ // still in a `DocumentFragment`. This has the effect of causing
+ // handlers not to fire. So, we double check this value on attached:
+ var contentElement = this.contentElement;
+ if (contentElement && contentElement.selectedItem) {
+ this._setSelectedItem(contentElement.selectedItem);
+ }
+ if (!this._isSetupTemplate){
+ this._isSetupTemplate = true;
+ this._setupTemplate();
+ }
+ },
+
+ /**
+ * The content element that is contained by the dropdown menu, if any.
+ */
+ get contentElement() {
+ return this.$.menu;
+ },
+
+ /**
+ * Show the dropdown content.
+ */
+ open: function() {
+ this.$.menuButton.open();
+ },
+
+ /**
+ * Hide the dropdown content.
+ */
+ close: function() {
+ this.$.menuButton.close();
+ },
+
+ /**
+ * A handler that is called when `iron-select` is fired.
+ *
+ * @param {CustomEvent} event An `iron-select` event.
+ */
+ _onIronSelect: function(event) {
+ this._setSelectedItem(event.detail.item);
+ },
+
+ /**
+ * A handler that is called when `iron-deselect` is fired.
+ *
+ * @param {CustomEvent} event An `iron-deselect` event.
+ */
+ _onIronDeselect: function(event) {
+ this._setSelectedItem(null);
+ },
+
+ /**
+ * A handler that is called when the dropdown is tapped.
+ *
+ * @param {CustomEvent} event A tap event.
+ */
+ _onTap: function(event) {
+ if (gestures.findOriginalTarget(event) === this) {
+ this.open();
+ }
+ },
+
+ /**
+ * Compute the label for the dropdown given a selected item.
+ *
+ * @param {Element} selectedItem A selected Element item, with an
+ * optional `label` property.
+ */
+ _selectedItemChanged: function(selectedItem) {
+ if (this.opened) { // user is searching
+ return;
+ }
+ var value = '';
+ var displayValue = '';
+ if (!selectedItem) {
+ value = '';
+ } else {
+ displayValue = selectedItem.textContent.trim() || selectedItem.label;
+ value = selectedItem.label || selectedItem.getAttribute('label') || selectedItem.textContent.trim();
+ }
+ if (!value && this.selectedItemLabel) { // selected item re-appeared in _filteredItems
+ return;
+ }
+ this._setValue(value);
+ // this._setSelectedItemLabel(value);
+ this._setSelectedItemLabel(displayValue);
+ },
+
+ /**
+ * Compute the vertical offset of the menu based on the value of
+ * `noLabelFloat`.
+ *
+ * @param {boolean} noLabelFloat True if the label should not float
+ * above the input, otherwise false.
+ */
+ _computeMenuVerticalOffset: function(noLabelFloat) {
+ // NOTE(cdata): These numbers are somewhat magical because they are
+ // derived from the metrics of elements internal to `paper-input`'s
+ // template. The metrics will change depending on whether or not the
+ // input has a floating label.
+ return noLabelFloat ? -4 : 8;
+ },
+
+ /**
+ * Returns false if the element is required and does not have a selection,
+ * and true otherwise.
+ * @param {*=} _value Ignored.
+ * @return {boolean} true if `required` is false, or if `required` is true
+ * and the element has a valid selection.
+ */
+ _getValidity: function(_value) {
+ return this.disabled || !this.required || (this.required && !!this.value);
+ },
+
+ _openedChanged: function() {
+ var openState = this.opened ? 'true' : 'false';
+ var e = this.contentElement;
+ if (e) {
+ e.setAttribute('aria-expanded', openState);
+ }
+ if (openState) {
+ setTimeout(this._focusSearch.bind(this), 100);
+ }
+ },
+
+ _focusSearch: function() {
+ this.$.searchInput.focus();
+ this.$.searchInput.select();
+ },
+
+ /**
+ * Sets up the template
+ */
+ _setupTemplate: function() {
+ // when the user clears the search field, the selectedItem is null
+ // this causes an iron-select event causing the dropdown to close
+ this.$.menuButton._onIronSelect = function(event) {
+ var value = this.selectedItem && (this.selectedItem.label || this.selectedItem.getAttribute('label') || this.selectedItem.textContent.trim());
+ if (!value && this.selectedItemLabel) {
+ return;
+ }
+ if (!this.ignoreSelect) {
+ this.$.menuButton.close();
+ }
+ }.bind(this);
+
+ // normally a user can select an option by typign the first letter
+ // this clashes with the search field
+ this.$.menu._focusWithKeyboardEvent = function() {};
+
+ var template = dom$0(this).querySelector('template') || this.$.menuTemplate;
+ this.templatize(template);
+ this._templateInstance = this.stamp({
+ items: this._filtereditems
+ });
+ var dom = dom$0(this);
+ dom.appendChild(this._templateInstance.root);
+ // dom.insertBefore(this._templateInstance.root, dom.firstChild);
+ },
+
+ /**
+ * Updates the stamped template's data
+ *
+ * @param {Array.