diff --git a/README.md b/README.md index f8ce601..d75cbd4 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Dropdown menu with search and multiple options selection ### Element properties - -* allowOutsideScroll - boolean, default: false -* autoValidate - boolean +* allowOutsideScroll - Boolean, default: false +* autoValidate - Boolean +* capitalizeInputShown - Boolean * customObjectOptions - Boolean, default: false * disabled - Boolean, default: false * dynamicAlign - Boolean, default: false @@ -19,6 +19,8 @@ Dropdown menu with search and multiple options selection * menuOpened - Boolean, default: false – notifies * multi - Boolean, default: false * noChangeEvent - Boolean, default: false +* noOptionsAvailable - Boolean, default: false +* notFoundValues - Array * optionLabel - String, default: label * options - Array * optionValue - String, default: 'value' @@ -27,6 +29,8 @@ Dropdown menu with search and multiple options selection * required - boolean * search - String * selected - Number/Array - notifies +* selectedRequiresValidOptions - Boolean, default: false; using this and selected will ensure selected values will +be set only if the options are not empty * selectedValues - Object notifies * showLimitWarning - Boolean * shownItems - Array diff --git a/bower.json b/bower.json index 763558f..83a8f33 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "etools-searchable-multiselection-menu", "description": "Dropdown menu with search and multiple options selection", - "version": "1.0.31", + "version": "1.0.35", "license": "https://github.com/unicef-polymer/etools-searchable-multiselection-menu/blob/master/LICENSE.md", "main": "etools-searchable-multiselection-menu.html", "dependencies": { diff --git a/demo/demo-custom-objects-as-options.html b/demo/demo-custom-objects-as-options.html index 60cebde..f0fa515 100644 --- a/demo/demo-custom-objects-as-options.html +++ b/demo/demo-custom-objects-as-options.html @@ -89,9 +89,9 @@

Custom objects as options and selected property with no change events

// this.set('selectedValues', [{value: 2, label: 'Option 2'}]); // this.set('selectedId', 3); - setTimeout(function() { + //setTimeout(function() { this.set('customObjOptions', this.generateOptions()); - }.bind(this), 3000); + //}.bind(this), 3000); // setTimeout(function() { // this.set('selectedIds', null); diff --git a/demo/index.html b/demo/index.html index 086da49..4f72605 100644 --- a/demo/index.html +++ b/demo/index.html @@ -131,7 +131,7 @@

Error messages and validations

- +

Custom objects as options. You can specify value and label properties from object option to be used.

diff --git a/etools-searchable-multiselection-menu.html b/etools-searchable-multiselection-menu.html index 58bc840..1ccf227 100644 --- a/etools-searchable-multiselection-menu.html +++ b/etools-searchable-multiselection-menu.html @@ -377,7 +377,8 @@ reflectToAttribute: true }, options: { - type: Array + type: Array, + observer: '_optionsChanged' }, shownItems: { type: Array, @@ -459,7 +460,8 @@ value: 'label' }, selected: { - notify: true + notify: true, + observer: '_selectedOptionsChanged' }, _prevSelectedValues: { type: Object @@ -477,10 +479,18 @@ type: Boolean, reflectToAttribute: true, }, + selectedRequiresValidOptions: { + type: Boolean, + value: false + }, + notFoundValues: { + type: Array, + notify: true, + value: [] + } }, observers: [ - '_selectedChanged(selected, options)', '_optionsLengthChanged(options.length)' ], @@ -557,8 +567,16 @@ if (this.readonly) { return; } - if(!this.multi) { + if (!this.multi) { this.selectedValues = e.model.item.value; + + if (this.selectedValues === null) { + if (this.value === null && this.updateSelected && + this.notFoundValues instanceof Array && this.notFoundValues.length) { + this.set('selected', null); + } + } + this._valueSelected(); this.$.dropdownMenu.close(); } @@ -684,60 +702,86 @@ } }, - _selectedChanged: function(selected, options) { - if (!this.updateSelected) { + _optionsChanged: function(options) { + if (options instanceof Array) { + this._selectionChanged(this.selected, options); + } + }, + + _selectedOptionsChanged: function(selected, oldSelected) { + if (this.multi && JSON.stringify(selected) === JSON.stringify(oldSelected)) { return; } - var currentValue = null; - var notFoundValue = []; - if (this.multi) { - currentValue = []; - if (selected instanceof Array && selected.length > 0) { - if (options instanceof Array && options.length > 0) { - selected.forEach(function(selectedVal) { - var s = this._prepareValue(selectedVal); - notFoundValue.push(s); - for (var i = 0; i < options.length; i++) { - if (this._prepareValue(options[i][this.optionValue]) === s) { - currentValue.push(options[i]); - notFoundValue.splice(notFoundValue.indexOf(s), 1); + this._selectionChanged(selected, this.options); + }, + + _selectionChanged: function(selected, options) { + this.debounce('selectionChanged', function() { + if (!this.updateSelected) { + return; + } + + if (this.selectedRequiresValidOptions && + (options instanceof Array === false || + (options instanceof Array && options.length === 0))) { + return; + } + + var currentValue = null; + var notFoundValues = []; + var i; + if (this.multi) { + // multiple selection + currentValue = []; + if (selected instanceof Array && selected.length > 0) { + notFoundValues = JSON.parse(JSON.stringify(selected)); + if (options instanceof Array && options.length > 0) { + selected.forEach(function(selectedVal) { + var s = this._prepareValue(selectedVal); + for (i = 0; i < options.length; i++) { + if (this._prepareValue(options[i][this.optionValue]) === s) { + currentValue.push(options[i]); + notFoundValues.splice(notFoundValues.indexOf(selectedVal), 1); + break; + } + } + }.bind(this)); + } + } + } else { + // single selection + if (selected && (typeof selected === 'string' || typeof selected === 'number')) { + var selectedVal = this._prepareValue(selected); + notFoundValues.push(selectedVal); + if (options instanceof Array && options.length > 0) { + for (i = 0; i < options.length; i++) { + if (this._prepareValue(options[i][this.optionValue]) === selectedVal) { + currentValue = options[i]; + notFoundValues.splice(notFoundValues.indexOf(selectedVal), 1); break; } } - }.bind(this)); - } - } - - } else { - if ((selected && (typeof selected === 'string' || typeof selected === 'number')) && - options instanceof Array && options.length > 0) { - var selectedVal = this._prepareValue(selected); - notFoundValue.push(selectedVal); - for (var i = 0; i < options.length; i++) { - if (this._prepareValue(options[i][this.optionValue]) === selectedVal) { - currentValue = options[i]; - notFoundValue.splice(notFoundValue.indexOf(selectedVal), 1); - break; } } } - } - if (JSON.stringify(this.value) !== JSON.stringify(currentValue)) { - this.set('value', currentValue); - } + this.set('notFoundValues', notFoundValues); + if (JSON.stringify(this.value) !== JSON.stringify(currentValue)) { + this.set('value', currentValue); + } - // not found selected values in options - if (notFoundValue.length > 0) { - var warnMsg = '[etools-esmm] Selected '; - if (notFoundValue.length === 1) { - warnMsg += 'value '; - } else { - warnMsg += 'values ' + // not found selected values in options + if (notFoundValues.length > 0) { + var warnMsg = '[etools-esmm] Selected '; + if (notFoundValues.length === 1) { + warnMsg += 'value '; + } else { + warnMsg += 'values ' + } + warnMsg += notFoundValues.join(', ') + ' not found in dropdown\'s options!' ; + console.warn(warnMsg); } - warnMsg += notFoundValue.join(', ') + ' not found in dropdown\'s options!' ; - console.warn(warnMsg); - } + }.bind(this), 50); }, _updateSelected: function(selectedValues, oldSelectedValues) { @@ -771,6 +815,11 @@ return this._prepareValue(s); }.bind(this)); } + if (this.notFoundValues.length) { + this.notFoundValues.forEach(function(notFoundVal) { + selectedVals.push(this._prepareValue(notFoundVal)); + }.bind(this)); + } } if (!this.multi) { @@ -778,7 +827,12 @@ if (selectedValues && !(typeof selectedValues === 'string' && selectedValues === '')) { selectedVals = this._prepareValue(selectedValues); } + if (!selectedVals && this.notFoundValues.length) { + this.set('_prevSelectedValues', this.notFoundValues[0]); + return; + } } + this.set('_prevSelectedValues', selectedVals); this.set('selected', selectedVals); }.bind(this), 50);