UNPKG

test-isc

Version:

An Ionic component similar to Ionic Select, that allows to search items, including async search, group, add, edit, delete items, and much more.

1,562 lines 74.9 kB
import { r as registerInstance, d as createEvent, h, H as Host, e as getElement } from './index-b6f64b02.js';
import './ionic-global-5d790111.js';
import { i as IonicSelectableSelectedEvent, g as IonicSelectableChangedEvent, k as IonicSelectableOpenedEvent, j as IonicSelectableClosedEvent, f as IonicSelectableClearedEvent, e as IonicSelectableItemAddingEvent, b as IonicSelectableSearchingEvent, a as IonicSelectableInfiniteScrolledEvent, d as IonicSelectableSearchSuccessedEvent, c as IonicSelectableSearchFailedEvent } from './ionic-selectable.interfaces.component-e50cb548.js';
import './utils-a3067e86.js';
import { g as getMode } from './index-9fd7b36f.js';
import './helpers-4a6bd295.js';
import './animation-3124e2ad.js';
import './index-367b30bb.js';
import './ios.transition-6129ad9e.js';
import './md.transition-aa16e3db.js';
import './cubic-bezier-89113939.js';
import './index-9b41fcc6.js';
import './index-86d5f3ab.js';
import './hardware-back-button-b3b61715.js';
import './index-59323445.js';
import { m as modalController } from './overlays-40d1f8b3.js';

const findItemLabel = (componentElement) => {
    const itemElement = componentElement.closest('ion-item');
    if (itemElement) {
        return itemElement.querySelector('ion-label');
    }
    return null;
};
const findItem = (componentEl) => {
    const itemEl = componentEl.closest('ion-item');
    return itemEl;
};
const addRippleEffectElement = (componentElement) => {
    const itemElement = componentElement.closest('ion-item');
    const itemNative = itemElement.shadowRoot.querySelector('div.item-native');
    if (itemNative) {
        const ionRipple = itemNative.ownerDocument.createElement('ion-ripple-effect');
        itemNative.appendChild(ionRipple);
    }
};
const hostContext = (selector, element) => {
    return element.closest(selector) !== null;
};
const renderHiddenInput = (always, container, name, value, disabled) => {
    if (always || hasShadowDom(container)) {
        let input = container.querySelector('input.aux-input');
        if (!input) {
            input = container.ownerDocument.createElement('input');
            input.type = 'hidden';
            input.classList.add('aux-input');
            container.appendChild(input);
        }
        input.disabled = disabled;
        input.name = name;
        input.value = value || '';
    }
};
const hasShadowDom = (element) => {
    return !!element.shadowRoot && !!element.attachShadow;
};

const ionicSelectableIosComponentCss = ":host{--font-family:var(--ion-font-family, inherit)}:host{--placeholder-color:currentColor;--placeholder-opacity:0.33;padding-left:var(--padding-start);padding-right:var(--padding-end);padding-top:var(--padding-top);padding-bottom:var(--padding-bottom);display:-ms-flexbox;display:flex;position:relative;-ms-flex-align:center;align-items:center;font-family:var(--font-family, inherit);overflow:hidden;z-index:var(--z-index-item-input)}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){:host{padding-left:unset;padding-right:unset;-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end)}}:host(.in-item){position:static;max-width:45%}:host(.ionic-selectable-is-disabled){opacity:0.4;pointer-events:none}:host(.ion-focused) button{border:2px solid #5e9ed6}:host(.item-multiple-inputs){position:relative}:host(.item-label-stacked),:host(.item-label-floating){--padding-start:0;-ms-flex-item-align:stretch;align-self:stretch;width:100%;max-width:100%}button{left:0;top:0;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:absolute;width:100%;height:100%;border:0;background:transparent;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none}[dir=rtl] button,:host-context([dir=rtl]) button{left:unset;right:unset;right:0}button::-moz-focus-inner{border:0}.ionic-selectable-placeholder{color:var(--placeholder-color);opacity:var(--placeholder-opacity)}.ionic-selectable-text{-ms-flex:1;flex:1;min-width:16px;font-size:inherit;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ionic-selectable-icon{position:relative;opacity:0.33}.ionic-selectable-icon-inner{left:5px;top:50%;margin-top:-3px;position:absolute;width:0;height:0;border-top:5px solid;border-right:5px solid transparent;border-left:5px solid transparent;color:currentColor;pointer-events:none}[dir=rtl] .ionic-selectable-icon-inner,:host-context([dir=rtl]) .ionic-selectable-icon-inner{left:unset;right:unset;right:5px}.ionic-selectable-icon-template{-ms-flex-item-align:center;align-self:center;margin-left:5px}:host{--padding-top:var(--item-ios-padding-top, 10px);--padding-end:10px;--padding-bottom:var(--item-ios-padding-bottom, 10px);--padding-start:var(--item-ios-padding-start, 20px)}:host(.item-label-stacked),:host(.item-label-floating){--padding-top:8px;--padding-bottom:8px;--padding-start:0}.ionic-selectable-icon{width:12px;height:18px}";

const ionicSelectableMdComponentCss = ":host{--font-family:var(--ion-font-family, inherit)}:host{--placeholder-color:currentColor;--placeholder-opacity:0.33;padding-left:var(--padding-start);padding-right:var(--padding-end);padding-top:var(--padding-top);padding-bottom:var(--padding-bottom);display:-ms-flexbox;display:flex;position:relative;-ms-flex-align:center;align-items:center;font-family:var(--font-family, inherit);overflow:hidden;z-index:var(--z-index-item-input)}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){:host{padding-left:unset;padding-right:unset;-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end)}}:host(.in-item){position:static;max-width:45%}:host(.ionic-selectable-is-disabled){opacity:0.4;pointer-events:none}:host(.ion-focused) button{border:2px solid #5e9ed6}:host(.item-multiple-inputs){position:relative}:host(.item-label-stacked),:host(.item-label-floating){--padding-start:0;-ms-flex-item-align:stretch;align-self:stretch;width:100%;max-width:100%}button{left:0;top:0;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:absolute;width:100%;height:100%;border:0;background:transparent;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none}[dir=rtl] button,:host-context([dir=rtl]) button{left:unset;right:unset;right:0}button::-moz-focus-inner{border:0}.ionic-selectable-placeholder{color:var(--placeholder-color);opacity:var(--placeholder-opacity)}.ionic-selectable-text{-ms-flex:1;flex:1;min-width:16px;font-size:inherit;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ionic-selectable-icon{position:relative;opacity:0.33}.ionic-selectable-icon-inner{left:5px;top:50%;margin-top:-3px;position:absolute;width:0;height:0;border-top:5px solid;border-right:5px solid transparent;border-left:5px solid transparent;color:currentColor;pointer-events:none}[dir=rtl] .ionic-selectable-icon-inner,:host-context([dir=rtl]) .ionic-selectable-icon-inner{left:unset;right:unset;right:5px}.ionic-selectable-icon-template{-ms-flex-item-align:center;align-self:center;margin-left:5px}:host{--padding-top:var(--item-md-padding-top, 10px);--padding-end:0;--padding-bottom:var(--item-md-padding-bottom, 11px);--padding-start:var(--item-md-padding-start, 16px)}:host(.item-label-stacked),:host(.item-label-floating){--padding-top:8px;--padding-bottom:8px;--padding-start:0}.ionic-selectable-icon{width:19px;height:19px}";

const IonicSelectableComponent = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.id = this.element.id ? this.element.id : `ionic-selectable-${nextId++}`;
        this.isInited = false;
        this.isRendered = false;
        this.isChangeInternal = false;
        this.groups = [];
        this.filteredGroups = [];
        this.hasFilteredItems = false;
        this.hasObjects = false;
        this.hasGroups = false;
        this.footerButtonsCount = 0;
        this.isSearching = false;
        this.isAddItemTemplateVisible = false;
        this.isFooterVisible = true;
        this.itemToAdd = null;
        this.selectedItems = [];
        this.valueItems = [];
        this.itemsToConfirm = [];
        /**
         * Determines whether Modal is opened.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#isopened).
         *
         * @default false
         * @readonly
         * @memberof IonicSelectableComponent
         */
        this.isOpened = false;
        /**
         * Determines whether the component is disabled.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#isdisabled).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.isDisabled = false;
        /**
         * Close button text.
         * The field is only applicable to **iOS** platform, on **Android** only Cross icon is displayed.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#closebuttontext).
         *
         * @default 'Cancel'
         * @memberof IonicSelectableComponent
         */
        this.closeButtonText = 'Cancel';
        /**
         * Close button slot. [Ionic slots](https://ionicframework.com/docs/api/buttons) are supported.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#closebuttonslot).
         *
         * @default 'start'
         * @memberof IonicSelectableComponent
         */
        this.closeButtonSlot = 'start';
        /**
         * Item icon slot. [Ionic slots](https://ionicframework.com/docs/api/item) are supported.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemiconslot).
         *
         * @default 'start'
         * @memberof IonicSelectableComponent
         */
        this.itemIconSlot = 'start';
        /**
         * Confirm button text.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#confirmbuttontext).
         *
         * @default 'OK'
         * @memberof IonicSelectableComponent
         */
        this.confirmButtonText = 'OK';
        /**
         * Clear button text.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#clearbuttontext).
         *
         * @default 'Clear'
         * @memberof IonicSelectableComponent
         */
        this.clearButtonText = 'Clear';
        /**
         * Add button text.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#addbuttontext).
         *
         * @default 'Add'
         * @memberof IonicSelectableComponent
         */
        this.addButtonText = 'Add';
        /**
         * The name of the control, which is submitted with the form data.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#name).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.name = this.id;
        /**
         * Determines whether multiple items can be selected.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#ismultiple).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.isMultiple = false;
        /**
         * The value of the component.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#value).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.value = null;
        /**
         * Is set to true, the value will be extracted from the itemValueField of the objects.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#shouldStoreItemValue).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.shouldStoreItemValue = false;
        /**
         * A list of items.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#items).
         *
         * @default []
         * @memberof IonicSelectableComponent
         */
        this.items = [];
        /**
         * A list of items to disable.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#disableditems).
         *
         * @default []
         * @memberof IonicSelectableComponent
         */
        this.disabledItems = [];
        /**
         * Item property to use as a unique identifier, e.g, `'id'`.
         * **Note**: `items` should be an object array.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemvaluefield).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.itemValueField = null;
        /**
         * Item property to display, e.g, `'name'`.
         * **Note**: `items` should be an object array.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemtextfield).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.itemTextField = null;
        /**
         * Modal CSS class.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#modalcssclass).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.modalCssClass = null;
        /**
         * Modal enter animation.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#modalenteranimation).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.modalEnterAnimation = null;
        /**
         * Modal leave animation.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#modalleaveanimation).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.modalLeaveAnimation = null;
        /**
         * Text of [Ionic Label](https://ionicframework.com/docs/api/label).
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#label).
         *
         * @readonly
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.titleText = null;
        /**
         *
         * Group property to use as a unique identifier to group items, e.g. `'country.id'`.
         * **Note**: `items` should be an object array.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#groupvaluefield).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.groupValueField = null;
        /**
         * Group property to display, e.g. `'country.name'`.
         * **Note**: `items` should be an object array.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#grouptextfield).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.groupTextField = null;
        /**
         * Determines whether Ionic [InfiniteScroll](https://ionicframework.com/docs/api/infinite-scroll) is enabled.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hasinfinitescroll).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.hasInfiniteScroll = false;
        /**
         * The threshold distance from the bottom of the content to call the infinite output event when scrolled.
         * Use the value 100px when the scroll is within 100 pixels from the bottom of the page.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#infinite-scroll).
         *
         * @default '100px'
         * @memberof IonicSelectableComponent
         */
        this.infiniteScrollThreshold = '100px';
        /**
         * Determines whether Ionic [VirtualScroll](https://ionicframework.com/docs/api/virtual-scroll) is enabled.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hasvirtualscroll).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.hasVirtualScroll = false;
        /**
         * See Ionic VirtualScroll [approxHeaderHeight](https://ionicframework.com/docs/api/virtual-scroll).
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#virtualscrollheaderfn).
         *
         * @default 30
         * @memberof IonicSelectableComponent
         */
        this.virtualScrollApproxHeaderHeight = 30;
        /**
         * See Ionic VirtualScroll [approxItemHeight](https://ionicframework.com/docs/api/virtual-scroll).
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#virtualscrollheaderfn).
         *
         * @default 45
         * @memberof IonicSelectableComponent
         */
        this.virtualScrollApproxItemHeight = 45;
        /**
         * Determines whether Confirm button is visible for single selection.
         * By default Confirm button is visible only for multiple selection.
         * **Note**: It is always true for multiple selection and cannot be changed.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hasconfirmbutton).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.hasConfirmButton = false;
        /**
         * Determines whether to allow adding items.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#canadditem).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.canAddItem = false;
        /**
         * Determines whether to show Clear button.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#canclear).
         * @default false
         * @memberof IonicSelectableComponent
         */
        // Pending - @HostBinding('class.ionic-selectable-can-clear')
        this.canClear = false;
        /**
         * Determines whether to show [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#cansearch).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.canSearch = false;
        /**
         * Determines the search is delegate to event, and not handled internally.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#cansearch).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.shouldDelegateSearchToEvent = false;
        /**
         * How long, in milliseconds, to wait to filter items or to trigger `onSearch` event after each keystroke.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchdebounce).
         *
         * @default 250
         * @memberof IonicSelectableComponent
         */
        this.searchDebounce = 250;
        /**
         * A placeholder for [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchplaceholder).
         *
         * @default 'Search'
         * @memberof IonicSelectableComponent
         */
        this.searchPlaceholder = 'Search';
        /**
         * Text in [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchtext).
         *
         * @default ''
         * @memberof IonicSelectableComponent
         */
        this.searchText = '';
        /**
         * Text to display when no items have been found during search.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchfailtext).
         *
         * @default 'No items found.'
         * @memberof IonicSelectableComponent
         */
        this.searchFailText = 'No items found.';
        /**
         * Determines whether Searchbar should receive focus when Modal is opened.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#shouldfocussearchbar).
         *
         * @default false
         * @memberof IonicSelectableComponent
         */
        this.shouldFocusSearchbar = false;
        /**
         * Set the cancel button icon of the [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * Only applies to md mode. Defaults to "arrow-back-sharp".
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hassearchtext).
         *
         * @default 'arrow-back-sharp'
         * @memberof IonicSelectableComponent
         */
        this.searchCancelButtonIcon = 'arrow-back-sharp';
        /**
         * Set the the cancel button text of the [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * Only applies to ios mode.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hassearchtext).
         *
         * @default 'Cancel'
         * @memberof IonicSelectableComponent
         */
        this.searchCancelButtonText = 'Cancel';
        /**
         * Set the clear icon of the [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * Defaults to "close-circle" for ios and "close-sharp" for md.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hassearchtext).
         *
         * @memberof IonicSelectableComponent
         */
        this.searchClearIcon = getMode() === 'ios' ? 'close-circle' : 'close-sharp';
        /**
         * A hint to the browser for which keyboard to display.
         * Possible values: "none", "text", "tel", "url", "email", "numeric", "decimal", and "search".
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hassearchtext).
         * @default 'none'
         * @memberof IonicSelectableComponent
         */
        this.searchInputmode = 'none';
        /**
         * The icon to use as the search icon in the [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * Defaults to "search-outline" in ios mode and "search-sharp" in md mode.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hassearchtext).
         * @default 'none'
         * @memberof IonicSelectableComponent
         */
        this.searchIcon = getMode() === 'ios' ? 'search-outline' : 'search-sharp';
        /**
         * Sets the behavior for the cancel button of the [Searchbar](https://ionicframework.com/docs/api/searchbar).
         * Defaults to "never".
         * Setting to "focus" shows the cancel button on focus.
         * Setting to "never" hides the cancel button.
         * Setting to "always" shows the cancel button regardless of focus state.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hassearchtext).
         * @default 'none'
         * @memberof IonicSelectableComponent
         */
        this.searchShowCancelButton = 'never';
        /**
         * Determines whether Confirm button is enabled.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#isconfirmbuttonenabled).
         *
         * @default true
         * @memberof IonicSelectableComponent
         */
        this.isConfirmButtonEnabled = true;
        /**
         * Header color. [Ionic colors](https://ionicframework.com/docs/theming/advanced#colors) are supported.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#headercolor).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.headerColor = null;
        /**
         * Group color. [Ionic colors](https://ionicframework.com/docs/theming/advanced#colors) are supported.
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#groupcolor).
         *
         * @default null
         * @memberof IonicSelectableComponent
         */
        this.groupColor = null;
        /**
         * See Ionic VirtualScroll [headerFn](https://ionicframework.com/docs/api/virtual-scroll).
         * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#virtualscrollheaderfn).
         *
         * @memberof IonicSelectableComponent
         */
        this.virtualScrollHeaderFn = () => null;
        this.onClick = async () => {
            this.setFocus();
            this.open();
        };
        this.onFocus = () => {
            this.focused.emit();
        };
        this.onBlur = () => {
            this.blurred.emit();
        };
        this.infiniteScrolled = createEvent(this, "infiniteScrolled", 7);
        this.searching = createEvent(this, "searching", 7);
        this.searchFailed = createEvent(this, "searchFailed", 7);
        this.searchSuccessed = createEvent(this, "searchSuccessed", 7);
        this.itemAdding = createEvent(this, "itemAdding", 7);
        this.cleared = createEvent(this, "cleared", 7);
        this.changed = createEvent(this, "changed", 7);
        this.itemsChanged = createEvent(this, "itemsChanged", 7);
        this.selected = createEvent(this, "selected", 7);
        this.opened = createEvent(this, "opened", 7);
        this.closed = createEvent(this, "closed", 7);
        this.focused = createEvent(this, "focused", 7);
        this.blurred = createEvent(this, "blurred", 7);
        this.ionStyle = createEvent(this, "ionStyle", 7);
    }
    onShouldStoreItemValueChanged(value) {
        if (!value && !this.hasObjects) {
            throw new Error(`If items contains primitive elements, shouldStoreItemValue must be null or true: ${this.element.id}`);
        }
    }
    onItemValueFieldChanged(value) {
        if (this.hasObjects && this.isNullOrWhiteSpace(value)) {
            throw new Error(`If items contains object elements, itemValueField must be non null or non whitespace : ${this.element.id}`);
        }
        else if (!this.hasObjects && !this.isNullOrWhiteSpace(value)) {
            throw new Error(`If items contains primitive elements, itemValueField must be null: ${this.element.id}`);
        }
    }
    onItemTextFieldChanged(value) {
        if (this.hasObjects && this.isNullOrWhiteSpace(value)) {
            throw new Error(`If items contains object elements, itemTextField must be non null or non whitespace : ${this.element.id}`);
        }
        else if (!this.hasObjects && !this.isNullOrWhiteSpace(value)) {
            throw new Error(`If items contains primitive elements, itemTextField must be null: ${this.element.id}`);
        }
    }
    onItemsChanged(value) {
        this.setItems(value);
    }
    onDisabledChanged() {
        this.emitStyle();
    }
    onValueChanged(newValue) {
        if (!this.isChangeInternal) {
            this.emitStyle();
            if (this.isInited) {
                this.setValue(newValue, false);
            }
        }
        this.isChangeInternal = false;
    }
    onSearchTextChanged(newValue) {
        if (!this.isChangeInternal) {
            if (this.isOpened) {
                this.startSearch();
                this.filterItems(newValue, false);
                this.endSearch();
            }
        }
        this.isChangeInternal = false;
    }
    onIsMultipleChanged() {
        this.countFooterButtons();
    }
    onDisabledItemsChanged() {
        var _a;
        (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
    }
    async connectedCallback() {
        this.emitStyle();
    }
    componentWillLoad() {
        this.setItems(this.items);
        this.setValue(this.value);
        this.countFooterButtons();
        this.isInited = true;
    }
    /**
     * Determines whether any item has been selected.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hasvalue).
     *
     * @returns A boolean determining whether any item has been selected.
     * @memberof IonicSelectableComponent
     */
    async hasValue() {
        return this.parseValue() !== '';
    }
    /**
     * Opens Modal.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#open).
     *
     * @returns Promise that resolves when Modal has been opened.
     * @memberof IonicSelectableComponent
     */
    async open() {
        if (this.isDisabled || this.isOpened) {
            return Promise.reject(`IonicSelectable is disabled or already opened: ${this.element.id}`);
        }
        const label = findItemLabel(this.element);
        if (label && !this.titleText) {
            this.titleText = label.textContent;
        }
        const modalOptions = {
            component: 'ionic-selectable-modal',
            componentProps: { selectableComponent: this },
            backdropDismiss: this.shouldBackdropClose,
        };
        if (this.modalCssClass) {
            modalOptions.cssClass = this.modalCssClass;
        }
        if (this.modalEnterAnimation) {
            modalOptions.enterAnimation = this.modalEnterAnimation;
        }
        if (this.modalLeaveAnimation) {
            modalOptions.leaveAnimation = this.modalLeaveAnimation;
        }
        this.filterItems(this.searchText);
        this.modalElement = await modalController.create(modalOptions);
        await this.modalElement.present();
        this.isOpened = true;
        this.setFocus();
        this.whatchModalEvents();
        this.emitOpened();
        return Promise.resolve();
    }
    /**
     * Closes Modal.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#close).
     *
     * @returns Promise that resolves when Modal has been closed.
     * @memberof IonicSelectableComponent
     */
    async close() {
        if (this.isDisabled || !this.isOpened) {
            return Promise.reject(`IonicSelectable is disabled or already closed: ${this.element.id}`);
        }
        await this.modalElement.dismiss();
        this.itemToAdd = null;
        this.hideAddItemTemplate();
        if (!this.shouldDelegateSearchToEvent) {
            this.setHasSearchText('');
        }
        this.emitClosed();
        return Promise.resolve();
    }
    /**
     * Return a list of items that are selected and awaiting confirmation by user, when he has clicked Confirm button.
     * After the user has clicked Confirm button items to confirm are cleared.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemstoconfirm).
     *
     * @returns a promise whit de list of items that are selected and awaiting confirmation by user
     * @memberof IonicSelectableComponent
     */
    async getItemsToConfirm() {
        return this.itemsToConfirm;
    }
    /**
     * Confirms selected items by updating value.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#confirm).
     *
     * @memberof IonicSelectableComponent
     */
    async confirm() {
        if (this.isMultiple) {
            this.setValue(this.selectedItems);
        }
        else if (this.hasConfirmButton || (this.hasTemplateRender && this.hasTemplateRender('footer'))) {
            this.setValue(this.selectedItems[0] || null);
        }
    }
    /**
     * Clears value.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#clear).
     *
     * @memberof IonicSelectableComponent
     */
    async clear() {
        this.clearItems();
    }
    /**
     * Enables infinite scroll.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#enableinfinitescroll).
     *
     * @memberof IonicSelectableComponent
     */
    async enableInfiniteScroll() {
        if (!this.hasInfiniteScroll) {
            return;
        }
        this.selectableModalComponent.infiniteScrollElement.disabled = false;
    }
    /**
     * Disables infinite scroll.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#disableinfinitescroll).
     *
     * @memberof IonicSelectableComponent
     */
    async disableInfiniteScroll() {
        if (!this.hasInfiniteScroll) {
            return;
        }
        this.selectableModalComponent.infiniteScrollElement.disabled = true;
    }
    /**
     * Ends infinite scroll.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#endinfinitescroll).
     *
     * @memberof IonicSelectableComponent
     */
    async endInfiniteScroll() {
        if (!this.hasInfiniteScroll) {
            return;
        }
        this.selectableModalComponent.infiniteScrollElement.complete();
        this.setItems(this.items);
    }
    /**
     * Scrolls to the top of Modal content.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#scrolltotop).
     *
     * @returns Promise that resolves when scroll has been completed.
     * @memberof IonicSelectableComponent
     */
    async scrollToTop() {
        if (!this.isOpened) {
            return Promise.reject(`IonicSelectable content cannot be scrolled: ${this.element.id}`);
        }
        await this.selectableModalComponent.contentElement.scrollToTop();
        return Promise.resolve();
    }
    /**
     * Scrolls to the bottom of Modal content.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#scrolltobottom).
     *
     * @returns Promise that resolves when scroll has been completed.
     * @memberof IonicSelectableComponent
     */
    async scrollToBottom() {
        if (!this.isOpened) {
            return Promise.reject(`IonicSelectable content cannot be scrolled: ${this.element.id}`);
        }
        await this.selectableModalComponent.contentElement.scrollToBottom();
        return Promise.resolve();
    }
    /**
     * Starts search process by showing Loading spinner.
     * Use it together with `onSearch` event to indicate search start.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#startsearch).
     *
     * @memberof IonicSelectableComponent
     */
    async startSearch() {
        if (this.isDisabled) {
            return;
        }
        this.showLoading();
    }
    /**
     * Ends search process by hiding Loading spinner and refreshing items.
     * Use it together with `onSearch` event to indicate search end.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#endsearch).
     *
     * @memberof IonicSelectableComponent
     */
    async endSearch() {
        if (this.isDisabled) {
            return;
        }
        this.hideLoading();
        // Refresh items manually.
        // Pending - this.setItems(this.items);
        this.emitOnSearchSuccessedOrFailed(this.hasFilteredItems);
    }
    /**
     * Shows Loading spinner.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#showloading).
     *
     * @memberof IonicSelectableComponent
     */
    async showLoading() {
        var _a;
        if (this.isDisabled) {
            return;
        }
        this.isSearching = true;
        (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
    }
    /**
     * Hides Loading spinner.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hideloading).
     *
     * @memberof IonicSelectableComponent
     */
    async hideLoading() {
        var _a;
        if (this.isDisabled) {
            return;
        }
        this.isSearching = false;
        (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
    }
    /**
     * Adds item.
     * **Note**: If you want an item to be added to the original array as well use two-way data binding syntax on `[(items)]` field.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#additem).
     *
     * @param item Item to add.
     * @returns Promise that resolves when item has been added.
     * @memberof IonicSelectableComponent
     */
    async addItem(item) {
        // Adding item triggers onItemsChange.
        // Return a promise that resolves when onItemsChange finishes.
        // We need a promise or user could do something after item has been added,
        // e.g. use search() method to find the added item.
        this.items.push(item);
        this.setItems(this.items);
        return Promise.resolve();
    }
    /**
     * Deletes item.
     * **Note**: If you want an item to be deleted from the original array as well use two-way data binding syntax on `[(items)]` field.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#deleteitem).
     *
     * @param item Item to delete.
     * @returns Promise that resolves when item has been deleted.
     * @memberof IonicSelectableComponent
     */
    async deleteItem(item) {
        let hasValueChanged = false;
        // Remove deleted item from selected items.
        if (this.selectedItems) {
            this.selectedItems = this.selectedItems.filter((_item) => this.getItemValue(item) !== this.getStoredItemValue(_item));
        }
        // Remove deleted item from value.
        if (this.value) {
            if (this.isMultiple) {
                const values = this.value.filter((value) => {
                    return value.id !== item.id;
                });
                if (values.length !== this.value.length) {
                    this.value = values;
                    hasValueChanged = true;
                }
            }
            else {
                if (item === this.value) {
                    this.value = null;
                    hasValueChanged = true;
                }
            }
        }
        if (hasValueChanged) {
            this.emitChanged();
        }
        // Remove deleted item from list.
        const items = this.items.filter((_item) => {
            return _item.id !== item.id;
        });
        // Refresh items on parent component.
        // Pending - this.itemsChange.emit(items);
        // Refresh list.
        this.setItems(items);
        return Promise.resolve();
    }
    /**
     * Selects or deselects all or specific items.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#toggleitems).
     *
     * @param isSelect Determines whether to select or deselect items.
     * @param [items] Items to toggle. If items are not set all items will be toggled.
     * @memberof IonicSelectableComponent
     */
    async toggleItems(isSelect, items) {
        var _a;
        if (isSelect) {
            const hasItems = items && items.length;
            let itemsToToggle = this.groups.reduce((allItems, group) => {
                return allItems.concat(group.items);
            }, []);
            // Don't allow to select all items in single mode.
            if (!this.isMultiple && !hasItems) {
                itemsToToggle = [];
            }
            // Toggle specific items.
            if (hasItems) {
                itemsToToggle = itemsToToggle.filter((itemToToggle) => {
                    return (items.find((item) => {
                        return this.getItemValue(itemToToggle) === this.getItemValue(item);
                    }) !== undefined);
                });
                // Take the first item for single mode.
                if (!this.isMultiple) {
                    itemsToToggle.splice(0, 1);
                }
            }
            itemsToToggle.forEach((item) => {
                this.addSelectedItem(item);
            });
        }
        else {
            const hasItems = items && items.length;
            if (hasItems) {
                items.forEach((item) => {
                    this.deleteSelectedItem(item);
                });
            }
            else {
                this.selectedItems = [];
            }
            (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
        }
        this.itemsToConfirm = [...this.selectedItems];
    }
    /**
     * Shows `ionicSelectableAddItemTemplate`.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#showadditemtemplate).
     *
     * @memberof IonicSelectableComponent
     */
    async showAddItemTemplate() {
        this.toggleAddItemTemplate(true);
    }
    /**
     * Hides `ionicSelectableAddItemTemplate`.
     * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hideadditemtemplate).
     *
     * @memberof IonicSelectableComponent
     */
    async hideAddItemTemplate() {
        // Clean item to add as it's no longer needed once Add Item Modal has been closed.
        this.itemToAdd = null;
        this.toggleAddItemTemplate(false);
    }
    clearItems() {
        var _a;
        this.setValue(null);
        (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
        this.emitCleared();
    }
    closeModal() {
        this.close();
    }
    addItemClick() {
        if (this.hasTemplateRender && this.hasTemplateRender('addItem')) {
            this.showAddItemTemplate();
        }
        else {
            this.emitItemAdding();
        }
    }
    onSearchbarValueChanged(event) {
        this.startSearch();
        this.filterItems(event.detail.value);
        this.endSearch();
    }
    isItemSelected(item) {
        return (this.selectedItems.find((selectedItem) => {
            return this.getItemValue(item) === this.getStoredItemValue(selectedItem);
        }) !== undefined);
    }
    isItemDisabled(item) {
        if (!this.disabledItems) {
            return;
        }
        return this.disabledItems.some((_item) => {
            return this.getItemValue(_item) === this.getItemValue(item);
        });
    }
    selectItem(item) {
        const isItemSelected = this.isItemSelected(item);
        if (this.isMultiple) {
            if (isItemSelected) {
                this.deleteSelectedItem(item);
            }
            else {
                this.addSelectedItem(item);
            }
            this.itemsToConfirm = [...this.selectedItems];
            // Emit onSelect event after setting items to confirm so they could be used inside the event.
            this.emitSelected(item, !isItemSelected);
        }
        else {
            if (this.hasConfirmButton /* || this.footerTemplate*/) {
                // Don't close Modal and keep track on items to confirm.
                // When footer template is used it's up to developer to close Modal.
                this.selectedItems = [];
                if (isItemSelected) {
                    this.deleteSelectedItem(item);
                }
                else {
                    this.addSelectedItem(item);
                }
                this.itemsToConfirm = [...this.selectedItems];
                // Emit onSelect event after setting items to confirm so they could be used inside the event.
                this.emitSelected(item, !isItemSelected);
            }
            else {
                if (!isItemSelected) {
                    this.selectedItems = [];
                    this.addSelectedItem(item);
                    // Emit onSelect before onChange.
                    this.emitSelected(item, true);
                    this.setValue(item);
                }
                this.close();
            }
        }
    }
    confirmSelection() {
        this.confirm();
        this.close();
    }
    getMoreItems() {
        this.emitIonInfiniteScrolled();
    }
    setValue(value, isChangeInternal = true) {
        var _a;
        this.isChangeInternal = isChangeInternal;
        if (value) {
            // If type is string convert to object
            value = typeof value === 'string' ? JSON.parse(value.replace(/\'/gi, '"')) : value;
            const isArray = Array.isArray(value);
            if (!isArray) {
                value = [value];
            }
            if (this.isMultiple && !isArray) {
                throw new Error(`If isMultiple is set to true, value must be array: ${this.element.id}`);
            }
            if (!this.isMultiple && isArray) {
                throw new Error(`If isMultiple is set to false, value must be object: ${this.element.id}`);
            }
            this.valueItems = [];
            value.forEach((_item) => {
                if (this.shouldStoreItemValue && typeof _item === 'object') {
                    throw new Error(`If shouldStoreItemValue is set to true, value must be primitive: ${this.element.id}`);
                }
                else if (!this.shouldStoreItemValue && typeof _item !== 'object') {
                    throw new Error(`If shouldStoreItemValue is set to false, value must be object: ${this.element.id}`);
                }
                const itemFind = this.items.find((item) => this.getItemValue(item) === this.getStoredItemValue(_item));
                if (itemFind) {
                    this.valueItems.push(this.getItem(itemFind));
                }
            });
            if (!this.isMultiple) {
                this.valueItems = this.valueItems.pop();
                this.selectedItems = [this.valueItems];
            }
            else {
                this.selectedItems = [...this.valueItems];
            }
            if (this.isChangeInternal) {
                this.value = this.valueItems;
            }
        }
        else {
            this.valueItems = [];
            this.selectedItems = [];
            if (this.isChangeInternal) {
                this.value = this.isMultiple ? [] : null;
            }
        }
        this.itemsToConfirm = [];
        if (this.isOpened) {
            (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
        }
        if (this.isInited) {
            this.emitChanged();
        }
    }
    setItems(items) {
        var _a, _b;
        if (!Array.isArray(items)) {
            throw new Error(`items must be array: ${this.element.id}`);
        }
        this.items.forEach((item) => {
            if (typeof item === 'object') {
                this.hasObjects = true;
            }
        });
        // If items contains primitive elements, isValuePrimitive is set to true
        if (!this.hasObjects) {
            this.shouldStoreItemValue = true;
        }
        this.onItemValueFieldChanged(this.itemValueField);
        this.onItemTextFieldChanged(this.itemTextField);
        this.onShouldStoreItemValueChanged(this.shouldStoreItemValue);
        // Grouping is supported for objects only.
        // Ionic VirtualScroll has it's own implementation of grouping.
        this.hasGroups = Boolean(this.hasObjects && (this.groupValueField || this.groupTextField) && !this.hasVirtualScroll);
        /* It's important to have an empty starting group with empty items (groups[0].items),
         * because we bind to it when using VirtualScroll.
         * See https://github.com/eakoriakin/ionic-selectable/issues/70.
         */
        let groups = [
            {
                items: items || [],
            },
        ];
        if (items && items.length) {
            if (this.hasGroups) {
                groups = [];
                items.forEach((item) => {
                    const groupValue = this.getPropertyValue(item, this.groupValueField || this.groupTextField);
                    const group = groups.find((_group) => _group.value === groupValue);
                    if (group) {
                        group.items.push(item);
                    }
                    else {
                        groups.push({
                            value: groupValue,
                            text: this.getPropertyValue(item, this.groupTextField),
                            items: [item],
                        });
                    }
                });
            }
        }
        this.groups = groups;
        this.filteredGroups = this.groups;
        this.hasFilteredItems = !this.areGroupsEmpty(this.filteredGroups);
        if (this.hasVirtualScroll) {
            // Rerender Virtual Scroll List After Adding New Data
            (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.virtualScrollElement.checkEnd();
        }
        (_b = this.selectableModalComponent) === null || _b === void 0 ? void 0 : _b.update();
        if (this.isInited) {
            this.emitItemsChanged();
        }
    }
    filterItems(searchText, isChangeInternal = true) {
        var _a;
        this.isChangeInternal = isChangeInternal;
        this.setHasSearchText(searchText);
        if (this.shouldDelegateSearchToEvent) {
            // Delegate filtering to the event.
            this.emitSearching();
        }
        else {
            // Default filtering.
            let groups = [];
            if (this.searchText === '') {
                groups = this.groups;
            }
            else {
                this.groups.forEach((group) => {
                    const items = group.items.filter((item) => {
                        const itemText = (this.itemTextField ? item[this.itemTextField] : item).toString().toLowerCase();
                        return itemText.indexOf(this.searchText.trim().toLowerCase()) !== -1;
                    });
                    if (items.length) {
                        groups.push({
                            value: group.value,
                            text: group.text,
                            items: items,
                        });
                    }
                });
                // No items found.
                if (!groups.length) {
                    groups.push({
                        items: [],
                    });
                }
            }
            this.filteredGroups = groups;
            this.hasFilteredItems = !this.areGroupsEmpty(groups);
            this.emitOnSearchSuccessedOrFailed(this.hasFilteredItems);
            (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
        }
    }
    addSelectedItem(item) {
        var _a;
        const exist = this.selectedItems.find((_item) => this.getItemValue(item) === this.getStoredItemValue(_item));
        if (!exist) {
            this.selectedItems.push(this.getItem(item));
        }
        (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
    }
    deleteSelectedItem(item) {
        var _a;
        let itemToDeleteIndex;
        this.selectedItems.forEach((selectedItem, itemIndex) => {
            if (this.getItemValue(item) === this.getStoredItemValue(selectedItem)) {
                itemToDeleteIndex = itemIndex;
            }
        });
        this.selectedItems.splice(itemToDeleteIndex, 1);
        (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
    }
    getItem(item) {
        if (!this.hasObjects) {
            return item;
        }
        return this.shouldStoreItemValue ? item[this.itemValueField] : item;
    }
    getItemValue(item) {
        if (!this.hasObjects) {
            return item;
        }
        return item[this.itemValueField];
    }
    getStoredItemValue(item) {
        if (!this.hasObjects) {
            return item;
        }
        return this.shouldStoreItemValue ? item : item[this.itemValueField];
    }
    toggleAddItemTemplate(isVisible) {
        var _a;
        // It should be possible to show/hide the template regardless
        // - canAddItem or canSaveItem parameters, so we could implement some
        // - custom behavior. E.g. adding item when search fails using onSearchFail event.
        if (this.hasTemplateRender && this.hasTemplateRender('addItem')) {
            // To make SaveItemTemplate visible we just position it over list using CSS.
            // We don't hide list with *ngIf or [hidden] to prevent its scroll position.
            this.isAddItemTemplateVisible = isVisible;
            this.isFooterVisible = !isVisible;
            (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
        }
    }
    emitSelected(item, isSelected) {
        this.selected.emit(new IonicSelectableSelectedEvent(item, isSelected, this.element));
    }
    emitChanged() {
        this.changed.emit(new IonicSelectableChangedEvent(this.valueItems, this.element));
    }
    emitOpened() {
        this.opened.emit(new IonicSelectableOpenedEvent(this.valueItems, this.element));
    }
    emitClosed() {
        this.closed.emit(new IonicSelectableClosedEvent(this.valueItems, this.element));
    }
    emitCleared() {
        this.cleared.emit(new IonicSelectableClearedEvent(this.selectedItems, this.element));
    }
    emitItemAdding() {
        this.itemAdding.emit(new IonicSelectableItemAddingEvent(this.valueItems, this.element));
    }
    emitItemsChanged() {
        this.itemsChanged.emit({ component: this.element, value: this.items });
    }
    emitSearching() {
        this.searching.emit(new IonicSelectableSearchingEvent(this.searchText, this.element));
    }
    emitIonInfiniteScrolled() {
        this.infiniteScrolled.emit(new IonicSelectableInfiniteScrolledEvent(this.searchText, this.element));
    }
    emitOnSearchSuccessedOrFailed(isSuccess) {
        if (isSuccess) {
            this.searchSuccessed.emit(new IonicSelectableSearchSuccessedEvent(this.searchText, this.element));
        }
        else {
            this.searchFailed.emit(new IonicSelectableSearchFailedEvent(this.searchText, this.element));
        }
    }
    isNullOrWhiteSpace(value) {
        if (value === null || value === undefined) {
            return true;
        }
        // Convert value to string in case if it's not.
        return value.toString().replace(/\s/g, '').length < 1;
    }
    setHasSearchText(searchText) {
        this.hasSearchText = !this.isNullOrWhiteSpace(searchText);
        if (this.hasSearchText) {
            this.searchText = searchText.trim();
        }
        else {
            this.searchText = '';
        }
    }
    countFooterButtons() {
        var _a;
        let footerButtonsCount = 0;
        if (this.canClear) {
            footerButtonsCount++;
        }
        if (this.isMultiple || this.hasConfirmButton) {
            footerButtonsCount++;
        }
        if (this.canAddItem) {
            footerButtonsCount++;
        }
        this.footerButtonsCount = footerButtonsCount;
        (_a = this.selectableModalComponent) === null || _a === void 0 ? void 0 : _a.update();
    }
    areGroupsEmpty(groups) {
        return (groups.length === 0 ||
            groups.every((group) => {
                return !group.items || group.items.length === 0;
            }));
    }
    getItemText(item) {
        if (!this.hasObjects) {
            return item !== null && item !== void 0 ? item : '';
        }
        return this.getPropertyValue(item, this.itemTextField);
    }
    getPropertyValue(object, property) {
        if (!property) {
            return '';
        }
        return property.split('.').reduce((_object, _property) => {
            return _object ? _object[_property] : null;
        }, object);
    }
    parseValue() {
        return JSON.stringify(this.valueItems);
    }
    generateText() {
        if (Array.isArray(this.valueItems)) {
            return this.valueItems
                .map((_item) => {
                const itemFind = this.items.find((item) => this.getItemValue(item) === this.getStoredItemValue(_item));
                return itemFind ? this.getItemText(itemFind) : '';
            })
                .filter((opt) => opt !== null)
                .join(', ');
        }
        else {
            const itemFind = this.items.find((item) => this.getItemValue(item) === this.getStoredItemValue(this.valueItems));
            return itemFind ? this.getItemText(itemFind) : '';
        }
    }
    getText() {
        const selectedText = this.selectedText;
        if (selectedText != null && selectedText !== '') {
            return selectedText;
        }
        return this.generateText();
    }
    async emitStyle() {
        this.ionStyle.emit({
            interactive: true,
            'ionic-selectable': true,
            'has-placeholder': this.placeholder != null,
            'has-value': await this.hasValue(),
            'interactive-disabled': this.isDisabled,
            'ionic-selectable-is-disabled': this.isDisabled,
        });
    }
    whatchModalEvents() {
        this.modalElement.onDidDismiss().then((event) => {
            this.isOpened = false;
            this.setFocus();
            this.itemsToConfirm = [];
            // Closed by clicking on backdrop outside modal.
            if (event.role === 'backdrop') {
                this.emitClosed();
            }
        });
    }
    setFocus() {
        if (this.buttonElement) {
            this.buttonElement.focus();
        }
    }
    render() {
        const { placeholder, name, isDisabled, isOpened, element /* isMultiple */ } = this;
        const mode = getMode();
        if (!this.isRendered) {
            // Add ripple efect
            if (mode === 'md') {
                addRippleEffectElement(element);
            }
        }
        const item = findItem(element);
        if (item) {
            item.classList.add('ion-activatable');
            if (isOpened) {
                item.classList.add('item-has-focus');
            }
            else {
                item.classList.remove('item-has-focus');
            }
        }
        const labelId = this.id + '-lbl';
        let labelPosition = 'item-label-default';
        const label = findItemLabel(element);
        if (label) {
            label.id = labelId;
            labelPosition = `item-label-${label.getAttribute('position') ? label.getAttribute('position') : 'default'}`;
        }
        renderHiddenInput(true, element, name, this.parseValue(), isDisabled);
        let addPlaceholderClass = false;
        let selectText = this.getText();
        if (selectText === '' &&
            (placeholder != null || (this.hasTemplateRender && this.hasTemplateRender('placeholder')))) {
            selectText = placeholder;
            addPlaceholderClass = true;
        }
        const selectTextClasses = {
            'ionic-selectable-text': true,
            'ionic-selectable-placeholder': addPlaceholderClass,
        };
        const textPart = addPlaceholderClass ? 'placeholder' : 'text';
        let valueRender;
        if (addPlaceholderClass && this.hasTemplateRender && this.hasTemplateRender('placeholder')) {
            valueRender = (h("div", { class: selectTextClasses, ref: (element) => {
                    this.templateRender(element, {
                        type: 'placeholder',
                    });
                } }));
        }
        else if (this.hasTemplateRender && this.hasTemplateRender('value')) {
            valueRender = (h("div", { class: selectTextClasses, ref: (element) => {
                    this.templateRender(element, {
                        type: 'value',
                        value: this.value,
                    });
                } }));
        }
        else {
            valueRender = (h("div", { class: selectTextClasses, part: textPart }, selectText));
        }
        this.isRendered = true;
        return (h(Host, { id: this.id, onClick: this.onClick, role: "combobox", "aria-haspopup": "dialog", "aria-disabled": isDisabled ? 'true' : null, "aria-expanded": `${isOpened}`, "aria-labelledby": labelId, class: {
                [mode]: true,
                'in-item': hostContext('ion-item', element),
                [labelPosition]: true,
                // 'item-multiple-inputs': isMultiple, // review
                'ionic-selectable-is-disabled': isDisabled,
            } }, valueRender, this.hasTemplateRender && this.hasTemplateRender('icon') ? (h("div", { class: "ionic-selectable-icon-template", ref: (element) => {
                this.templateRender(element, {
                    type: 'icon',
                });
            } })) : (h("div", { class: "ionic-selectable-icon", role: "presentation", part: "icon" }, h("div", { class: "ionic-selectable-icon-inner", part: "icon-inner" }))), h("button", { type: "button", onFocus: this.onFocus, onBlur: this.onBlur, disabled: isDisabled, ref: (buttonElement) => (this.buttonElement = buttonElement) })));
    }
    get element() { return getElement(this); }
    static get watchers() { return {
        "shouldStoreItemValue": ["onShouldStoreItemValueChanged"],
        "itemValueField": ["onItemValueFieldChanged"],
        "itemTextField": ["onItemTextFieldChanged"],
        "items": ["onItemsChanged"],
        "isDisabled": ["onDisabledChanged"],
        "placeholder": ["onDisabledChanged"],
        "value": ["onValueChanged"],
        "searchText": ["onSearchTextChanged"],
        "isMultiple": ["onIsMultipleChanged"],
        "canClear": ["onIsMultipleChanged"],
        "canAddItem": ["onIsMultipleChanged"],
        "hasConfirmButton": ["onIsMultipleChanged"],
        "disabledItems": ["onDisabledItemsChanged"]
    }; }
};
let nextId = 0;
IonicSelectableComponent.style = {
    /*STENCIL:MODE:ios*/ ios: ionicSelectableIosComponentCss,
    /*STENCIL:MODE:md*/ md: ionicSelectableMdComponentCss
};

const ionicSelectableModalIosComponentCss = ".sc-ionic-selectable-modal-ios-h{--font-family:var(--ion-font-family, inherit)}.ionic-selectable-add-item-template.sc-ionic-selectable-modal-ios{position:fixed;bottom:0;left:0;right:0;background-color:var(--ion-background-color, #ffffff)}.ionic-selectable-spinner.sc-ionic-selectable-modal-ios{position:fixed;bottom:0;top:0;left:0;right:0;z-index:1}.ionic-selectable-spinner-background.sc-ionic-selectable-modal-ios{top:0;bottom:0;left:0;right:0;position:absolute;background-color:#000000;opacity:0.05}.ionic-selectable-spinner.sc-ionic-selectable-modal-ios ion-spinner.sc-ionic-selectable-modal-ios{position:absolute;top:50%;left:50%;z-index:10;margin-top:-14px;margin-left:-14px}";

const ionicSelectableModalMdComponentCss = ".sc-ionic-selectable-modal-md-h{--font-family:var(--ion-font-family, inherit)}.ionic-selectable-add-item-template.sc-ionic-selectable-modal-md{position:fixed;bottom:0;left:0;right:0;background-color:var(--ion-background-color, #ffffff)}.ionic-selectable-spinner.sc-ionic-selectable-modal-md{position:fixed;bottom:0;top:0;left:0;right:0;z-index:1}.ionic-selectable-spinner-background.sc-ionic-selectable-modal-md{top:0;bottom:0;left:0;right:0;position:absolute;background-color:#000000;opacity:0.05}.ionic-selectable-spinner.sc-ionic-selectable-modal-md ion-spinner.sc-ionic-selectable-modal-md{position:absolute;top:50%;left:50%;z-index:10;margin-top:-14px;margin-left:-14px}";

const IonicSelectableModalComponent = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.toggleUpdate = false;
    }
    /**
     * Rerender the component
     */
    async update() {
        this.toggleUpdate = !this.toggleUpdate;
    }
    connectedCallback() {
        const modalElement = document.querySelector('ion-modal');
        this.selectableComponent = modalElement.componentProps.selectableComponent;
        this.selectableComponent.selectableModalComponent = this;
    }
    componentDidLoad() {
        this.infiniteScrollElement = this.element.querySelector('ion-infinite-scroll');
        this.virtualScrollElement = this.element.querySelector('ion-virtual-scroll');
        this.contentElement = this.element.querySelector('ion-content');
        this.headerElement = this.element.querySelector('ion-header');
        if (this.selectableComponent.shouldFocusSearchbar) {
            const searchBarElement = this.element.querySelector('ion-searchbar');
            searchBarElement.setFocus();
            // Focus after a delay because focus doesn't work without it.
            setTimeout(() => {
                searchBarElement.setFocus();
            }, 1000);
        }
    }
    renderItem(item) {
        return (h("ion-item", { button: true, onClick: () => this.selectableComponent.selectItem(item), disabled: this.selectableComponent.isItemDisabled(item) }, this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('item') ? (h("span", { ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'item',
                    value: item,
                    isItemSelected: this.selectableComponent.isItemSelected(item),
                    isItemDisabled: this.selectableComponent.isItemDisabled(item),
                });
            } })) : (h("ion-label", null, this.selectableComponent.getItemText(item))), this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('itemEnd') && (h("div", { slot: "end", ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'itemEnd',
                    value: item,
                    isItemSelected: this.selectableComponent.isItemSelected(item),
                    isItemDisabled: this.selectableComponent.isItemDisabled(item),
                });
            } })), this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('itemIcon') ? (h("span", { ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'itemIcon',
                    value: item,
                    isItemSelected: this.selectableComponent.isItemSelected(item),
                    isItemDisabled: this.selectableComponent.isItemDisabled(item),
                });
            } })) : (h("ion-icon", { name: this.selectableComponent.isItemSelected(item) ? 'checkmark-circle' : 'radio-button-off', size: "small", slot: this.selectableComponent.itemIconSlot }))));
    }
    renderHeader(header) {
        return (h("ion-item-divider", { color: this.selectableComponent.groupColor }, h("ion-label", null, header)));
    }
    render() {
        return (h(Host, { class: {
                'ionic-selectable-modal-is-add-item-template-visible ': this.selectableComponent.isAddItemTemplateVisible,
            } }, h("ion-header", null, this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('header') ? (h("div", { ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'header',
                });
            } })) : (h("ion-toolbar", { color: this.selectableComponent.headerColor }, h("ion-title", null, this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('title') ? (h("span", { ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'title',
                });
            } })) : (h("span", null, this.selectableComponent.titleText))), h("ion-buttons", { slot: this.selectableComponent.closeButtonSlot }, h("ion-button", { onClick: () => this.selectableComponent.closeModal() }, this.selectableComponent.hasTemplateRender &&
            this.selectableComponent.hasTemplateRender('closeButton') ? (h("span", { ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'closeButton',
                });
            } })) : (h("span", null, this.selectableComponent.closeButtonText)))))), this.selectableComponent.canSearch ||
            (this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('message') && (h("ion-toolbar", null, h("ion-searchbar", { value: this.selectableComponent.searchText, placeholder: this.selectableComponent.searchPlaceholder, debounce: this.selectableComponent.searchDebounce, cancelButtonIcon: this.selectableComponent.searchCancelButtonIcon, cancelButtonText: this.selectableComponent.searchCancelButtonText, clearIcon: this.selectableComponent.searchClearIcon, inputmode: this.selectableComponent.searchInputmode, searchIcon: this.selectableComponent.searchIcon, showCancelButton: this.selectableComponent.searchShowCancelButton, onIonChange: (event) => this.selectableComponent.onSearchbarValueChanged(event) }), this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('message') && (h("div", { class: "ionic-selectable-message", ref: (element) => {
                    this.selectableComponent.templateRender(element, {
                        type: 'message',
                    });
                } })))))), h("ion-content", null, this.selectableComponent.isSearching && (h("div", { class: "ionic-selectable-spinner" }, h("div", { class: "ionic-selectable-spinner-background" }), h("ion-spinner", null))), !this.selectableComponent.hasFilteredItems &&
            this.selectableComponent.hasTemplateRender &&
            this.selectableComponent.hasTemplateRender('searchFail') && (h("span", { ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'searchFail',
                });
            } })), !this.selectableComponent.hasFilteredItems &&
            (!this.selectableComponent.hasTemplateRender ||
                !this.selectableComponent.hasTemplateRender('searchFail')) && (h("div", { class: "ion-margin ion-text-center" }, this.selectableComponent.searchFailText)), !this.selectableComponent.hasVirtualScroll && this.selectableComponent.hasFilteredItems && (h("ion-list", null, this.selectableComponent.filteredGroups.map((group) => {
            return (h("ion-item-group", null, this.selectableComponent.hasGroups && (h("ion-item-divider", { color: this.selectableComponent.groupColor }, this.selectableComponent.hasTemplateRender &&
                this.selectableComponent.hasTemplateRender('group') ? (h("span", { ref: (element) => {
                    this.selectableComponent.templateRender(element, {
                        type: 'group',
                    });
                } })) : (h("ion-label", null, group.text)), this.selectableComponent.hasTemplateRender &&
                this.selectableComponent.hasTemplateRender('groupEnd') && (h("div", { ref: (element) => {
                    this.selectableComponent.templateRender(element, {
                        type: 'groupEnd',
                        value: group,
                    });
                }, slot: "end" })))), group.items.map((item) => this.renderItem(item))));
        }))), this.selectableComponent.hasVirtualScroll && this.selectableComponent.hasFilteredItems && (h("ion-virtual-scroll", { items: this.selectableComponent.filteredGroups[0].items, approxHeaderHeight: this.selectableComponent.virtualScrollApproxHeaderHeight, approxItemHeight: this.selectableComponent.virtualScrollApproxItemHeight, renderItem: (item) => this.renderItem(item), renderHeader: (header) => this.renderHeader(header), headerFn: this.selectableComponent.virtualScrollHeaderFn })), this.selectableComponent.hasInfiniteScroll && (h("ion-infinite-scroll", { threshold: this.selectableComponent.infiniteScrollThreshold, onIonInfinite: () => this.selectableComponent.getMoreItems() }, h("ion-infinite-scroll-content", null)))), this.selectableComponent.isAddItemTemplateVisible && (h("div", { class: "ionic-selectable-add-item-template", style: { top: this.headerElement.offsetHeight + 'px' }, ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'addItem',
                    value: this.selectableComponent.itemToAdd,
                    isAdd: this.selectableComponent.itemToAdd == null,
                });
            } })), (this.selectableComponent.footerButtonsCount > 0 ||
            (this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('footer'))) && (h("ion-footer", { style: { visibility: this.selectableComponent.isFooterVisible ? 'initial' : 'hidden' } }, this.selectableComponent.hasTemplateRender && this.selectableComponent.hasTemplateRender('footer') ? (h("div", { ref: (element) => {
                this.selectableComponent.templateRender(element, {
                    type: 'footer',
                });
            } })) : (h("ion-toolbar", null, h("ion-row", null, this.selectableComponent.canClear && (h("ion-col", null, h("ion-button", { onClick: () => this.selectableComponent.clearItems(), disabled: !(this.selectableComponent.selectedItems.length > 0), expand: "full" }, this.selectableComponent.clearButtonText))), this.selectableComponent.canAddItem && (h("ion-col", null, h("ion-button", { onClick: () => this.selectableComponent.addItemClick(), expand: "full" }, this.selectableComponent.addButtonText))), (this.selectableComponent.isMultiple ||
            this.selectableComponent.hasConfirmButton ||
            this.selectableComponent.canClear) && (h("ion-col", null, h("ion-button", { onClick: () => this.selectableComponent.confirmSelection(), disabled: !this.selectableComponent.isConfirmButtonEnabled, expand: "full" }, this.selectableComponent.confirmButtonText))))))))));
    }
    get element() { return getElement(this); }
};
IonicSelectableModalComponent.style = {
    /*STENCIL:MODE:ios*/ ios: ionicSelectableModalIosComponentCss,
    /*STENCIL:MODE:md*/ md: ionicSelectableModalMdComponentCss
};

export { IonicSelectableComponent as ionic_selectable, IonicSelectableModalComponent as ionic_selectable_modal };