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,129 lines (1,122 loc) • 75.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-366d4dde.js');
require('./ionic-global-53907587.js');
const ionicSelectable_interfaces_component = require('./ionic-selectable.interfaces.component-7f9cbc0a.js');
require('./utils-7ad8ea36.js');
const index$1 = require('./index-14b758b3.js');
require('./helpers-41dfb43a.js');
require('./animation-34a434a2.js');
require('./index-69bad4f5.js');
require('./ios.transition-d0eaafea.js');
require('./md.transition-deadd839.js');
require('./cubic-bezier-f3b34d27.js');
require('./index-ea0ef88d.js');
require('./index-4b7e4228.js');
require('./hardware-back-button-8c2f48d2.js');
require('./index-1ceca8b8.js');
const overlays = require('./overlays-2201b17c.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) {
index.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 = index$1.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 = index$1.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 = index.createEvent(this, "infiniteScrolled", 7);
this.searching = index.createEvent(this, "searching", 7);
this.searchFailed = index.createEvent(this, "searchFailed", 7);
this.searchSuccessed = index.createEvent(this, "searchSuccessed", 7);
this.itemAdding = index.createEvent(this, "itemAdding", 7);
this.cleared = index.createEvent(this, "cleared", 7);
this.changed = index.createEvent(this, "changed", 7);
this.itemsChanged = index.createEvent(this, "itemsChanged", 7);
this.selected = index.createEvent(this, "selected", 7);
this.opened = index.createEvent(this, "opened", 7);
this.closed = index.createEvent(this, "closed", 7);
this.focused = index.createEvent(this, "focused", 7);
this.blurred = index.createEvent(this, "blurred", 7);
this.ionStyle = index.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 overlays.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 = [];