All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.frontend.uibuilder-combobox.src.uibuilder-combobox.js Maven / Gradle / Ivy

There is a newer version: 3.0.3
Show newest version
/*
 *
 * Copyright © 2018 Webvalto Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js'
import * as PolymerAsync from '@polymer/polymer/lib/utils/async.js';
import { ComboBoxElement } from '@vaadin/vaadin-combo-box/src/vaadin-combo-box.js';
import '@vaadin/flow-frontend/uibuilder/data/data-source-mixin.js';
import '@vaadin/flow-frontend/uibuilder-core/uibuilder-ready-listener-mixin.js';


export class UibuilderComboBox extends Uibuilder.DataSourceMixin(Uibuilder.ReadyListenerMixin(ComboBoxElement)) {

    static get is() {
        return 'uibuilder-combobox'
    }

    static get properties() {
        return Object.assign({}, ComboBoxElement.properties, {
            itemValuePath: {
                type: String,
                readOnly: true,
                value: '___ITEM_KEY'
            },
            nullValuesDisabled: {
                type: Boolean,
                value: false,
                notify: true
            },
            filteredItems: {
                type: Array,
                value: [],
                observer: '_onFilteredItemsChange'
            },
            allowCustomValue: {
                type: Boolean,
                value: false,
                notify: true
            },
            customValueFunction: {
                type: String
            }
        });
    }

    constructor() {
        super();
    }

    ready() {
        super.ready();
        this.filteredItems = []; // at this point, every combobox on the same screen has the same filteredItems reference, so it has to be reinitialized

        this.clearButtonVisible = !this.nullValuesDisabled;
        this.addEventListener('selected-item-changed', ({detail: {value = null}}) => {
            const customEvent = new CustomEvent('item-changed');
            customEvent.model = {item: value};
            this.dispatchEvent(customEvent);
        });

        if (this.inputElement) {
            this.inputElement.addEventListener("input", () => {
                if (this.allowCustomValue) {
                    this._customValue = this._inputElementValue;
                    this.dispatchEvent(new CustomEvent("custom-value-changed", {
                        detail: {
                            customValue: this._customValue
                        }
                    }));
                }
            });
        }
    }

    _dataProviderFilterChanged() {
        this._debouncer = Debouncer.debounce(
            this._debouncer,
            PolymerAsync.timeOut.after(500),
            () => super._dataProviderFilterChanged()
        );
    }

    // override
    // noinspection JSUnusedGlobalSymbols
    _clear() {
        if (!this.nullValuesDisabled) {
            super._clear();
        }
        this._dispatchChanged();
    }

    // override
    // noinspection JSUnusedGlobalSymbols
    _valueChanged(value, oldVal) {
        if (!this.nullValuesDisabled || value !== null) {
            super._valueChanged(value, oldVal);
            if (this.allowCustomValue) {
                // if allowCustomValue is true, the super._valueChanged function will overwrite the input element's value
                // to the raw value. The following line will try to extract the item label, if it is not a custom value,
                // or return the custom value otherwise.
                if (this.selectedItem) {
                    this._inputElementValue = this._getItemLabel(this.selectedItem);
                } else if (!this.disabled) {
                    this._inputElementValue = this._customValue || "";
                }
            }
        }
    }

    _uibuilderReady() {
        this._ensureFirstPage(true);
        this._processDataSourceTag();
    }

    _refresh() {
        this.size = undefined;
        this.clearCache();
    }

    _onItemSelected(item) {
        if (item && item.___ITEM_IDX) {
            this.valueIdx = item.___ITEM_IDX;
        } else {
            delete this.valueIdx;
        }
        this.value = item ? item.___ITEM_KEY : ' ';
        this._handlePrematureItemSelection(item);
    }

    /**
     * In case of a DB driven datasource, the filteredItems array may arrive later than the selected item is set.
     * If it is the case, the selected item is stored in a variable, which can be used to set the selected item later
     * when the filteredItems are set.
     *
     * Otherwise, if there are filteredItems present, there is no need to store the selection,
     * so the variable will be removed.
     */
    _handlePrematureItemSelection(item) {
        if (item && (!this.filteredItems || this.filteredItems.length === 0)) {
            this.prematureSelectedItem = item;
        } else {
            delete this.prematureSelectedItem;
        }
    }

    /**
     * To observe the change of the filteredItems and if there is a premature selection, apply the selection according
     * to the items.
     */
    _onFilteredItemsChange() {
        if (this.filteredItems && this.filteredItems.length !== 0 && this.prematureSelectedItem) {
            this._onItemSelected(this.prematureSelectedItem);
        }
    }

    _getValueIndexInItems(value) {
        let foundIdx = -1;
        for (let idx in this.filteredItems) {
            if (this.filteredItems && this.filteredItems[idx] && this.filteredItems[idx].___ITEM_KEY) {
                let item = this.filteredItems[idx].___ITEM_KEY;
                if (item === value) {
                    foundIdx = idx;
                    break;
                }
            }
        }
        return foundIdx;
    }

    _getNextPageNeededForValue() {
        let pageToLoad = -1;
        const currentLen = this._getRealItemLength();

        if (currentLen === -1 || this.size === undefined || (currentLen < this.filteredItems.length)) {
            pageToLoad = this._getPageForIndex(currentLen + 1);
        }

        if (this.valueIdx && this.valueIdx >= currentLen) {
            pageToLoad = this._getPageForIndex(this.valueIdx);
        }

        return pageToLoad;
    }

    // override
    // noinspection JSUnusedGlobalSymbols
    _selectItemForValue(value) {
        super._selectItemForValue(value);

        if (this.filteredItems) {
            let foundItem = this._getValueIndexInItems(value);
            if (foundItem === -1) {
                const pageToLoad = this._getNextPageNeededForValue();
                if (pageToLoad !== -1 && value !== ' ') {
                    // selectedItem has to be undefined to trigger the next iteration of page load
                    this.set('selectedItem', undefined);
                    setTimeout(() => {
                        if (this.value === '') {
                            this.value = value;
                        }
                        this._loadPage(pageToLoad);
                    });
                }
            }
        }
    }

    // override
    // noinspection JSUnusedGlobalSymbols
    _detectAndDispatchChange() {
        if (this.value !== this._lastCommittedValue) {
            if (this._isValueReallyChanged()) {
                this._dispatchChanged();
            }
            this.dispatchEvent(new CustomEvent('change', {bubbles: true}));
            this._lastCommittedValue = this.value;
        }
    }

    _dispatchChanged() {
        this.dispatchEvent(new CustomEvent('changed', {bubbles: true}));
    }

    _isValueReallyChanged() {
        return !this._isValueInPendingState() || this.selectedItem;
    }

    _isValueInPendingState() {
        return this._isValueUndefined() && !this._lastCommittedValue;
    }

    _isValueUndefined() {
        return !this.value || this.value === '' || this.value === ' ';
    }

    _getRealItemLength() {
        return this.filteredItems ? this.filteredItems.filter(item => item.hasOwnProperty('___ITEM_KEY')).length : -1;
    }
}

customElements.define(UibuilderComboBox.is, UibuilderComboBox);




© 2015 - 2024 Weber Informatics LLC | Privacy Policy