@hazyflame/ej2-dropdowns
Version:
Essential JS 2 DropDown Components
1,133 lines (1,128 loc) • 654 kB
JavaScript
import { Animation, Browser, ChildProperty, Complex, Component, Event, EventHandler, KeyboardEvents, L10n, NotifyPropertyChanges, Property, SanitizeHtmlHelper, addClass, append, attributes, classList, closest, compile, createElement, detach, extend, formatUnit, getComponent, getUniqueID, getValue, isBlazor, isNullOrUndefined, isUndefined, matches, prepend, remove, removeClass, resetBlazorTemplate, rippleEffect, select, selectAll, setStyleAttribute, setValue, updateBlazorTemplate } from '@syncfusion/ej2-base';
import { DataManager, DataUtil, Predicate, Query } from '@syncfusion/ej2-data';
import { ListBase, Sortable, cssClass, moveTo } from '@syncfusion/ej2-lists';
import { Popup, createSpinner, getZindexPartial, hideSpinner, isCollide, showSpinner } from '@syncfusion/ej2-popups';
import { Input, TextBox } from '@syncfusion/ej2-inputs';
import { Button, createCheckBox } from '@syncfusion/ej2-buttons';
import { TreeView } from '@syncfusion/ej2-navigations';
/**
* IncrementalSearch module file
*/
let queryString = '';
let prevString = '';
let matches$1 = [];
const activeClass = 'e-active';
let prevElementId = '';
/**
* Search and focus the list item based on key code matches with list text content
*
* @param { number } keyCode - Specifies the key code which pressed on keyboard events.
* @param { HTMLElement[]} items - Specifies an array of HTMLElement, from which matches find has done.
* @param { number } selectedIndex - Specifies the selected item in list item, so that search will happen
* after selected item otherwise it will do from initial.
* @param { boolean } ignoreCase - Specifies the case consideration when search has done.
* @param {string} elementId - Specifies the list element ID.
* @param {boolean} isBlazor - Specifies the platform is Blazor or not.
* @returns {Element} Returns list item based on key code matches with list text content.
*/
function incrementalSearch(keyCode, items, selectedIndex, ignoreCase, elementId, isBlazor$$1) {
queryString += String.fromCharCode(keyCode);
setTimeout(() => {
queryString = '';
}, 1000);
let index;
queryString = ignoreCase ? queryString.toLowerCase() : queryString;
if (prevElementId === elementId && prevString === queryString) {
for (let i = 0; i < matches$1.length; i++) {
if (matches$1[i].classList.contains(activeClass)) {
index = i;
break;
}
}
index = index + 1;
return matches$1[index];
}
else {
const listItems = items;
const strLength = queryString.length;
let text;
let item;
selectedIndex = selectedIndex ? selectedIndex + 1 : 0;
let i = selectedIndex;
matches$1 = [];
do {
if (i === listItems.length) {
i = -1;
}
if (i === -1) {
index = 0;
}
else {
index = i;
}
item = listItems[index];
if (isBlazor$$1) {
text = ignoreCase ? item.textContent.trim().toLowerCase() : item.textContent.trim();
}
else {
text = ignoreCase ? item.innerText.toLowerCase() : item.innerText;
}
if (text.substr(0, strLength) === queryString) {
matches$1.push(listItems[index]);
}
i++;
} while (i !== selectedIndex);
prevString = queryString;
prevElementId = elementId;
return matches$1[0];
}
}
/**
* Search the list item based on given input value matches with search type.
*
* @param {string} inputVal - Specifies the given input value.
* @param {HTMLElement[]} items - Specifies the list items.
* @param {SearchType} searchType - Specifies the filter type.
* @param {boolean} ignoreCase - Specifies the case sensitive option for search operation.
* @returns {Element | number} Returns the search matched items.
*/
function Search(inputVal, items, searchType, ignoreCase) {
const listItems = items;
ignoreCase = ignoreCase !== undefined && ignoreCase !== null ? ignoreCase : true;
const itemData = { item: null, index: null };
if (inputVal && inputVal.length) {
const strLength = inputVal.length;
const queryStr = ignoreCase ? inputVal.toLocaleLowerCase() : inputVal;
for (let i = 0, itemsData = listItems; i < itemsData.length; i++) {
const item = itemsData[i];
const text = (ignoreCase ? item.textContent.toLocaleLowerCase() : item.textContent).replace(/^\s+|\s+$/g, '');
if ((searchType === 'Equal' && text === queryStr) || (searchType === 'StartsWith' && text.substr(0, strLength) === queryStr)) {
itemData.item = item;
itemData.index = i;
return { item: item, index: i };
}
}
return itemData;
}
return itemData;
}
/* eslint-disable jsdoc/require-param, valid-jsdoc */
/**
* Function helps to find which highlightSearch is to call based on your data.
*
* @param {HTMLElement} element - Specifies an li element.
* @param {string} query - Specifies the string to be highlighted.
* @param {boolean} ignoreCase - Specifies the ignoreCase option.
* @param {HightLightType} type - Specifies the type of highlight.
* @returns {void}
*/
function highlightSearch(element, query, ignoreCase, type, isBlazor$$1) {
if (query === '') {
return;
}
else {
const ignoreRegex = ignoreCase ? 'gim' : 'gm';
// eslint-disable-next-line
query = /^[a-zA-Z0-9- ]*$/.test(query) ? query : query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
const replaceQuery = type === 'StartsWith' ? '^(' + query + ')' : type === 'EndsWith' ?
'(' + query + ')$' : '(' + query + ')';
findTextNode(element, new RegExp(replaceQuery, ignoreRegex), isBlazor$$1);
}
}
/* eslint-enable jsdoc/require-param, valid-jsdoc */
/**
*
* @param {HTMLElement} element - Specifies the element.
* @param {RegExp} pattern - Specifies the regex to match the searched text.
* @param {boolean} isBlazor - Specifies the platform is Blazor or not.
* @returns {void}
*/
function findTextNode(element, pattern, isBlazor$$1) {
for (let index = 0; element.childNodes && (index < element.childNodes.length); index++) {
if (element.childNodes[index].nodeType === 3 && element.childNodes[index].textContent.trim() !== '') {
element = (isBlazor$$1 && element.classList.contains('e-highlight')) ? element.parentElement : element;
if (isBlazor$$1 && element.getAttribute('data-value')) {
element.innerHTML = element.getAttribute('data-value').replace(pattern, '<span class="e-highlight">$1</span>');
}
else {
const value = element.childNodes[index].nodeValue.trim().replace(pattern, '<span class="e-highlight">$1</span>');
element.childNodes[index].nodeValue = '';
element.innerHTML = element.innerHTML.trim() + value;
}
break;
}
else {
findTextNode(element.childNodes[index], pattern, isBlazor$$1);
}
}
}
/**
* Function helps to remove highlighted element based on your data.
*
* @param {HTMLElement} content - Specifies an content element.
* @returns {void}
*/
function revertHighlightSearch(content) {
const contentElement = content.querySelectorAll('.e-highlight');
for (let i = contentElement.length - 1; i >= 0; i--) {
const parent = contentElement[i].parentNode;
const text = document.createTextNode(contentElement[i].textContent);
parent.replaceChild(text, contentElement[i]);
}
}
/**
* Common source
*/
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
class FieldSettings extends ChildProperty {
}
__decorate([
Property()
], FieldSettings.prototype, "text", void 0);
__decorate([
Property()
], FieldSettings.prototype, "value", void 0);
__decorate([
Property()
], FieldSettings.prototype, "iconCss", void 0);
__decorate([
Property()
], FieldSettings.prototype, "groupBy", void 0);
__decorate([
Property()
], FieldSettings.prototype, "htmlAttributes", void 0);
const dropDownBaseClasses = {
root: 'e-dropdownbase',
rtl: 'e-rtl',
content: 'e-content',
selected: 'e-active',
hover: 'e-hover',
noData: 'e-nodata',
fixedHead: 'e-fixed-head',
focus: 'e-item-focus',
li: 'e-list-item',
group: 'e-list-group-item',
disabled: 'e-disabled',
grouping: 'e-dd-group'
};
const ITEMTEMPLATE_PROPERTY = 'ItemTemplate';
const VALUETEMPLATE_PROPERTY = 'ValueTemplate';
const GROUPTEMPLATE_PROPERTY = 'GroupTemplate';
const HEADERTEMPLATE_PROPERTY = 'HeaderTemplate';
const FOOTERTEMPLATE_PROPERTY = 'FooterTemplate';
const NORECORDSTEMPLATE_PROPERTY = 'NoRecordsTemplate';
const ACTIONFAILURETEMPLATE_PROPERTY = 'ActionFailureTemplate';
/**
* DropDownBase component will generate the list items based on given data and act as base class to drop-down related components
*/
let DropDownBase = class DropDownBase extends Component {
/**
* * Constructor for DropDownBase class
*
* @param {DropDownBaseModel} options - Specifies the DropDownBase model.
* @param {string | HTMLElement} element - Specifies the element to render as component.
* @private
*/
constructor(options, element) {
super(options, element);
this.preventChange = false;
this.isAngular = false;
this.isPreventChange = false;
}
getPropObject(prop, newProp, oldProp) {
const newProperty = new Object();
const oldProperty = new Object();
const propName = (prop) => {
return prop;
};
newProperty[propName(prop)] = newProp[propName(prop)];
oldProperty[propName(prop)] = oldProp[propName(prop)];
const data = new Object();
data.newProperty = newProperty;
data.oldProperty = oldProperty;
return data;
}
getValueByText(text, ignoreCase, ignoreAccent) {
let value = null;
if (!isNullOrUndefined(this.listData)) {
if (ignoreCase) {
value = this.checkValueCase(text, true, ignoreAccent);
}
else {
value = this.checkValueCase(text, false, ignoreAccent);
}
}
return value;
}
checkValueCase(text, ignoreCase, ignoreAccent, isTextByValue) {
let value = null;
if (isTextByValue) {
value = text;
}
const dataSource = this.listData;
const fields = this.fields;
const type = this.typeOfData(dataSource).typeof;
if (type === 'string' || type === 'number' || type === 'boolean') {
for (const item of dataSource) {
if (!isNullOrUndefined(item)) {
if (ignoreAccent) {
value = this.checkingAccent(String(item), text, ignoreCase);
}
else {
if (ignoreCase) {
if (this.checkIgnoreCase(String(item), text)) {
value = this.getItemValue(String(item), text, ignoreCase);
}
}
else {
if (this.checkNonIgnoreCase(String(item), text)) {
value = this.getItemValue(String(item), text, ignoreCase, isTextByValue);
}
}
}
}
}
}
else {
if (ignoreCase) {
dataSource.filter((item) => {
const itemValue = getValue(fields.value, item);
if (!isNullOrUndefined(itemValue) && this.checkIgnoreCase(getValue(fields.text, item).toString(), text)) {
value = getValue(fields.value, item);
}
});
}
else {
if (isTextByValue) {
dataSource.filter((item) => {
const itemValue = getValue(fields.value, item);
if (!isNullOrUndefined(itemValue) && !isNullOrUndefined(value) && itemValue.toString() === value.toString()) {
value = getValue(fields.text, item);
}
});
}
else {
dataSource.filter((item) => {
if (this.checkNonIgnoreCase(getValue(fields.text, item), text)) {
value = getValue(fields.value, item);
}
});
}
}
}
return value;
}
checkingAccent(item, text, ignoreCase) {
const dataItem = DataUtil.ignoreDiacritics(String(item));
const textItem = DataUtil.ignoreDiacritics(text.toString());
let value = null;
if (ignoreCase) {
if (this.checkIgnoreCase(dataItem, textItem)) {
value = this.getItemValue(String(item), text, ignoreCase);
}
}
else {
if (this.checkNonIgnoreCase(String(item), text)) {
value = this.getItemValue(String(item), text, ignoreCase);
}
}
return value;
}
checkIgnoreCase(item, text) {
return String(item).toLowerCase() === text.toString().toLowerCase() ? true : false;
}
checkNonIgnoreCase(item, text) {
return String(item) === text.toString() ? true : false;
}
getItemValue(dataItem, typedText, ignoreCase, isTextByValue) {
let value = null;
const dataSource = this.listData;
const type = this.typeOfData(dataSource).typeof;
if (isTextByValue) {
value = dataItem.toString();
}
else {
if (ignoreCase) {
value = type === 'string' ? String(dataItem) : this.getFormattedValue(String(dataItem));
}
else {
value = type === 'string' ? typedText : this.getFormattedValue(typedText);
}
}
return value;
}
templateCompiler(baseTemplate) {
let checkTemplate = false;
if (baseTemplate) {
try {
checkTemplate = (selectAll(baseTemplate, document).length) ? true : false;
}
catch (exception) {
checkTemplate = false;
}
}
return checkTemplate;
}
l10nUpdate(actionFailure) {
const ele = this.getModuleName() === 'listbox' ? this.ulElement : this.list;
if (this.noRecordsTemplate !== 'No records found' || this.actionFailureTemplate !== 'Request failed') {
this.DropDownBaseresetBlazorTemplates(false, false, true, true);
const template = actionFailure ? this.actionFailureTemplate : this.noRecordsTemplate;
let compiledString;
const templateId = actionFailure ? this.actionFailureTemplateId : this.noRecordsTemplateId;
ele.innerHTML = '';
const tempaltecheck = this.templateCompiler(template);
if (tempaltecheck) {
compiledString = compile(select(template, document).innerHTML.trim());
}
else {
compiledString = compile(template);
}
const templateName = actionFailure ? 'actionFailureTemplate' : 'noRecordsTemplate';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const noDataCompTemp = compiledString({}, this, templateName, templateId, this.isStringTemplate, null, ele);
if (noDataCompTemp && noDataCompTemp.length > 0) {
for (let i = 0; i < noDataCompTemp.length; i++) {
ele.appendChild(noDataCompTemp[i]);
}
}
this.renderReactTemplates();
this.DropDownBaseupdateBlazorTemplates(false, false, !actionFailure, actionFailure, false, false, false, false);
}
else {
const l10nLocale = { noRecordsTemplate: 'No records found', actionFailureTemplate: 'Request failed' };
const componentLocale = new L10n(this.getLocaleName(), {}, this.locale);
if (componentLocale.getConstant('actionFailureTemplate') !== '') {
this.l10n = componentLocale;
}
else {
this.l10n = new L10n(this.getModuleName() === 'listbox' ? 'listbox' : 'dropdowns', l10nLocale, this.locale);
}
const content = actionFailure ?
this.l10n.getConstant('actionFailureTemplate') : this.l10n.getConstant('noRecordsTemplate');
if (this.getModuleName() === 'listbox') {
const liElem = this.createElement('li');
liElem.textContent = content;
ele.appendChild(liElem);
liElem.classList.add('e-list-nrt');
}
else {
ele.innerHTML = content;
}
}
}
getLocaleName() {
return 'drop-down-base';
}
getTextByValue(value) {
const text = this.checkValueCase(value, false, false, true);
return text;
}
getFormattedValue(value) {
if (this.listData && this.listData.length) {
const item = this.typeOfData(this.listData);
if (isBlazor() && isNullOrUndefined(value) || value === 'null') {
return null;
}
if (typeof getValue((this.fields.value ? this.fields.value : 'value'), item.item) === 'number'
|| item.typeof === 'number') {
return parseFloat(value);
}
if (typeof getValue((this.fields.value ? this.fields.value : 'value'), item.item) === 'boolean'
|| item.typeof === 'boolean') {
return ((value === 'true') || ('' + value === 'true'));
}
}
return value;
}
/**
* Sets RTL to dropdownbase wrapper
*
* @returns {void}
*/
setEnableRtl() {
if (this.list) {
this.enableRtlElements.push(this.list);
}
if (this.enableRtl) {
addClass(this.enableRtlElements, dropDownBaseClasses.rtl);
}
else {
removeClass(this.enableRtlElements, dropDownBaseClasses.rtl);
}
}
/**
* Initialize the Component.
*
* @returns {void}
*/
initialize() {
this.bindEvent = true;
this.actionFailureTemplateId = `${this.element.id}${ACTIONFAILURETEMPLATE_PROPERTY}`;
if (this.element.tagName === 'UL') {
const jsonElement = ListBase.createJsonFromElement(this.element);
this.setProperties({ fields: { text: 'text', value: 'text' } }, true);
this.resetList(jsonElement, this.fields);
}
else if (this.element.tagName === 'SELECT') {
const dataSource = this.dataSource instanceof Array ? (this.dataSource.length > 0 ? true : false)
: !isNullOrUndefined(this.dataSource) ? true : false;
if (!dataSource) {
this.renderItemsBySelect();
}
}
else {
this.setListData(this.dataSource, this.fields, this.query);
}
}
DropDownBaseupdateBlazorTemplates(item, group, noRecord, action, value, header, footer, isEmpty) {
if (!this.isStringTemplate) {
if (this.itemTemplate && item) {
updateBlazorTemplate(this.itemTemplateId, ITEMTEMPLATE_PROPERTY, this, isEmpty);
}
if (this.groupTemplate && group) {
updateBlazorTemplate(this.groupTemplateId, GROUPTEMPLATE_PROPERTY, this, isEmpty);
}
if (this.noRecordsTemplate && noRecord) {
updateBlazorTemplate(this.noRecordsTemplateId, NORECORDSTEMPLATE_PROPERTY, this, isEmpty);
}
if (this.actionFailureTemplate && action) {
updateBlazorTemplate(this.actionFailureTemplateId, ACTIONFAILURETEMPLATE_PROPERTY, this, isEmpty);
}
if (value) {
updateBlazorTemplate(this.valueTemplateId, VALUETEMPLATE_PROPERTY, this, isEmpty);
}
if (header) {
updateBlazorTemplate(this.headerTemplateId, HEADERTEMPLATE_PROPERTY, this);
}
if (footer) {
updateBlazorTemplate(this.footerTemplateId, FOOTERTEMPLATE_PROPERTY, this);
}
}
}
DropDownBaseresetBlazorTemplates(item, group, noRecord, action, value, header, footer) {
if (!this.isStringTemplate) {
if (this.itemTemplate && item) {
resetBlazorTemplate(this.itemTemplateId, ITEMTEMPLATE_PROPERTY);
}
if (this.groupTemplate && group) {
resetBlazorTemplate(this.groupTemplateId, GROUPTEMPLATE_PROPERTY);
}
if (this.noRecordsTemplate && noRecord) {
resetBlazorTemplate(this.noRecordsTemplateId, NORECORDSTEMPLATE_PROPERTY);
}
if (this.actionFailureTemplate && action) {
resetBlazorTemplate(this.actionFailureTemplateId, ACTIONFAILURETEMPLATE_PROPERTY);
}
if (value) {
resetBlazorTemplate(this.valueTemplateId, VALUETEMPLATE_PROPERTY);
}
if (header) {
resetBlazorTemplate(this.headerTemplateId, HEADERTEMPLATE_PROPERTY);
}
if (footer) {
resetBlazorTemplate(this.footerTemplateId, FOOTERTEMPLATE_PROPERTY);
}
}
}
/**
* Get the properties to be maintained in persisted state.
*
* @returns {string} Returns the persisted data of the component.
*/
getPersistData() {
return this.addOnPersist([]);
}
/**
* Sets the enabled state to DropDownBase.
*
* @returns {void}
*/
setEnabled() {
this.element.setAttribute('aria-disabled', (this.enabled) ? 'false' : 'true');
}
/**
* Sets the enabled state to DropDownBase.
*
* @param {string} value - Specifies the attribute values to add on the input element.
* @returns {void}
*/
updateDataAttribute(value) {
const invalidAttr = ['class', 'style', 'id', 'type'];
const attr = {};
for (let a = 0; a < this.element.attributes.length; a++) {
if (invalidAttr.indexOf(this.element.attributes[a].name) === -1 &&
!(this.getModuleName() === 'dropdownlist' && this.element.attributes[a].name === 'readonly')) {
attr[this.element.attributes[a].name] = this.element.getAttribute(this.element.attributes[a].name);
}
}
extend(attr, value, attr);
this.setProperties({ htmlAttributes: attr }, true);
}
renderItemsBySelect() {
const element = this.element;
const fields = { value: 'value', text: 'text' };
const jsonElement = [];
const group = element.querySelectorAll('select>optgroup');
const option = element.querySelectorAll('select>option');
this.getJSONfromOption(jsonElement, option, fields);
if (group.length) {
for (let i = 0; i < group.length; i++) {
const item = group[i];
const optionGroup = {};
optionGroup[fields.text] = item.label;
optionGroup.isHeader = true;
const child = item.querySelectorAll('option');
jsonElement.push(optionGroup);
this.getJSONfromOption(jsonElement, child, fields);
}
element.querySelectorAll('select>option');
}
this.updateFields(fields.text, fields.value, this.fields.groupBy, this.fields.htmlAttributes, this.fields.iconCss);
this.resetList(jsonElement, fields);
}
updateFields(text, value, groupBy, htmlAttributes, iconCss) {
const field = {
'fields': {
text: text,
value: value,
groupBy: !isNullOrUndefined(groupBy) ? groupBy : this.fields && this.fields.groupBy,
htmlAttributes: !isNullOrUndefined(htmlAttributes) ? htmlAttributes : this.fields && this.fields.htmlAttributes,
iconCss: !isNullOrUndefined(iconCss) ? iconCss : this.fields && this.fields.iconCss
}
};
this.setProperties(field, true);
}
getJSONfromOption(items, options, fields) {
for (const option of options) {
const json = {};
json[fields.text] = option.innerText;
json[fields.value] = !isNullOrUndefined(option.getAttribute(fields.value)) ?
option.getAttribute(fields.value) : option.innerText;
items.push(json);
}
}
/**
* Execute before render the list items
*
* @private
* @returns {void}
*/
preRender() {
// there is no event handler
this.scrollTimer = -1;
this.enableRtlElements = [];
this.isRequested = false;
this.isDataFetched = false;
this.itemTemplateId = `${this.element.id}${ITEMTEMPLATE_PROPERTY}`;
this.valueTemplateId = `${this.element.id}${VALUETEMPLATE_PROPERTY}`;
this.groupTemplateId = `${this.element.id}${GROUPTEMPLATE_PROPERTY}`;
this.headerTemplateId = `${this.element.id}${HEADERTEMPLATE_PROPERTY}`;
this.footerTemplateId = `${this.element.id}${FOOTERTEMPLATE_PROPERTY}`;
this.noRecordsTemplateId = `${this.element.id}${NORECORDSTEMPLATE_PROPERTY}`;
}
/**
* Creates the list items of DropDownBase component.
*
* @param {Object[] | string[] | number[] | DataManager | boolean[]} dataSource - Specifies the data to generate the list.
* @param {FieldSettingsModel} fields - Maps the columns of the data table and binds the data to the component.
* @param {Query} query - Accepts the external Query that execute along with data processing.
* @returns {void}
*/
setListData(dataSource, fields, query) {
fields = fields ? fields : this.fields;
let ulElement;
this.isActive = true;
const eventArgs = { cancel: false, data: dataSource, query: query };
this.isPreventChange = this.isAngular && this.preventChange ? true : this.isPreventChange;
this.trigger('actionBegin', eventArgs, (eventArgs) => {
if (!eventArgs.cancel) {
this.showSpinner();
if (dataSource instanceof DataManager) {
this.isRequested = true;
if (this.isDataFetched) {
this.emptyDataRequest(fields);
return;
}
eventArgs.data.executeQuery(this.getQuery(eventArgs.query)).then((e) => {
this.isPreventChange = this.isAngular && this.preventChange ? true : this.isPreventChange;
this.trigger('actionComplete', e, (e) => {
if (!e.cancel) {
const listItems = e.result;
if (listItems.length === 0) {
this.isDataFetched = true;
}
ulElement = this.renderItems(listItems, fields);
this.onActionComplete(ulElement, listItems, e);
if (this.groupTemplate) {
this.renderGroupTemplate(ulElement);
}
this.isRequested = false;
this.bindChildItems(listItems, ulElement, fields, e);
}
});
}).catch((e) => {
this.isRequested = false;
this.onActionFailure(e);
this.hideSpinner();
});
}
else {
const dataManager = new DataManager(eventArgs.data);
const listItems = (this.getQuery(eventArgs.query)).executeLocal(dataManager);
const localDataArgs = { cancel: false, result: listItems };
this.isPreventChange = this.isAngular && this.preventChange ? true : this.isPreventChange;
this.trigger('actionComplete', localDataArgs, (localDataArgs) => {
if (!localDataArgs.cancel) {
ulElement = this.renderItems(localDataArgs.result, fields);
this.onActionComplete(ulElement, localDataArgs.result);
if (this.groupTemplate) {
this.renderGroupTemplate(ulElement);
}
this.bindChildItems(localDataArgs.result, ulElement, fields);
}
});
}
}
});
}
bindChildItems(listItems, ulElement, fields, e) {
if (listItems.length >= 100 && this.getModuleName() === 'autocomplete') {
setTimeout(() => {
const childNode = this.remainingItems(this.sortedData, fields);
append(childNode, ulElement);
this.DropDownBaseupdateBlazorTemplates(true, false, false, false);
this.liCollections = this.list.querySelectorAll('.' + dropDownBaseClasses.li);
this.updateListValues();
this.raiseDataBound(listItems, e);
}, 0);
}
else {
this.raiseDataBound(listItems, e);
}
}
updateListValues() {
// Used this method in component side.
}
findListElement(list, findNode, attribute, value) {
let liElement = null;
if (list) {
const listArr = [].slice.call(list.querySelectorAll(findNode));
for (let index = 0; index < listArr.length; index++) {
if (listArr[index].getAttribute(attribute) === (value + '')) {
liElement = listArr[index];
break;
}
}
}
return liElement;
}
raiseDataBound(listItems, e) {
this.hideSpinner();
const dataBoundEventArgs = {
items: listItems,
e: e
};
this.trigger('dataBound', dataBoundEventArgs);
}
remainingItems(dataSource, fields) {
const spliceData = new DataManager(dataSource).executeLocal(new Query().skip(100));
if (this.itemTemplate) {
const listElements = this.templateListItem(spliceData, fields);
return [].slice.call(listElements.childNodes);
}
const type = this.typeOfData(spliceData).typeof;
if (type === 'string' || type === 'number' || type === 'boolean') {
return ListBase.createListItemFromArray(this.createElement, spliceData, true, this.listOption(spliceData, fields), this);
}
return ListBase.createListItemFromJson(this.createElement, spliceData, this.listOption(spliceData, fields), 1, true, this);
}
emptyDataRequest(fields) {
const listItems = [];
this.onActionComplete(this.renderItems(listItems, fields), listItems);
this.isRequested = false;
this.hideSpinner();
}
showSpinner() {
// Used this method in component side.
}
hideSpinner() {
// Used this method in component side.
}
onActionFailure(e) {
this.liCollections = [];
this.trigger('actionFailure', e);
this.l10nUpdate(true);
addClass([this.list], dropDownBaseClasses.noData);
}
/* eslint-disable @typescript-eslint/no-unused-vars */
onActionComplete(ulElement, list, e) {
/* eslint-enable @typescript-eslint/no-unused-vars */
this.listData = list;
if (isBlazor() && this.isServerRendered && this.getModuleName() === 'listbox') {
remove(this.list.querySelector('.e-list-parent'));
remove(this.list.querySelector('.e-hidden-select'));
}
else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (this.isReact) {
this.clearTemplate(['itemTemplate', 'groupTemplate', 'actionFailureTemplate', 'noRecordsTemplate']);
}
this.list.innerHTML = '';
}
this.fixedHeaderElement = isNullOrUndefined(this.fixedHeaderElement) ? this.fixedHeaderElement : null;
this.list.appendChild(ulElement);
this.liCollections = this.list.querySelectorAll('.' + dropDownBaseClasses.li);
this.ulElement = this.list.querySelector('ul');
this.postRender(this.list, list, this.bindEvent);
}
/* eslint-disable @typescript-eslint/no-unused-vars */
postRender(listElement, list, bindEvent) {
/* eslint-enable @typescript-eslint/no-unused-vars */
const focusItem = listElement.querySelector('.' + dropDownBaseClasses.li);
const selectedItem = listElement.querySelector('.' + dropDownBaseClasses.selected);
if (focusItem && !selectedItem) {
focusItem.classList.add(dropDownBaseClasses.focus);
}
if (list.length <= 0) {
this.l10nUpdate();
addClass([listElement], dropDownBaseClasses.noData);
}
else {
listElement.classList.remove(dropDownBaseClasses.noData);
}
}
/**
* Get the query to do the data operation before list item generation.
*
* @param {Query} query - Accepts the external Query that execute along with data processing.
* @returns {Query} Returns the query to do the data query operation.
*/
getQuery(query) {
return query ? query : this.query ? this.query : new Query();
}
/**
* To render the template content for group header element.
*
* @param {HTMLElement} listEle - Specifies the group list elements.
* @returns {void}
*/
renderGroupTemplate(listEle) {
if (this.fields.groupBy !== null && this.dataSource || this.element.querySelector('.' + dropDownBaseClasses.group)) {
const dataSource = this.dataSource;
const option = { groupTemplateID: this.groupTemplateId, isStringTemplate: this.isStringTemplate };
const headerItems = listEle.querySelectorAll('.' + dropDownBaseClasses.group);
const groupcheck = this.templateCompiler(this.groupTemplate);
if (groupcheck) {
const groupValue = select(this.groupTemplate, document).innerHTML.trim();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const tempHeaders = ListBase.renderGroupTemplate(groupValue, dataSource, this.fields.properties, headerItems, option, this);
}
else {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const tempHeaders = ListBase.renderGroupTemplate(this.groupTemplate, dataSource, this.fields.properties, headerItems, option, this);
}
this.DropDownBaseupdateBlazorTemplates(false, true, false, false, false, false, false, false);
}
}
/**
* To create the ul li list items
*
* @param {object []} dataSource - Specifies the data to generate the list.
* @param {FieldSettingsModel} fields - Maps the columns of the data table and binds the data to the component.
* @returns {HTMLElement} Return the ul li list items.
*/
createListItems(dataSource, fields) {
if (dataSource && fields.groupBy || this.element.querySelector('optgroup')) {
if (fields.groupBy) {
if (this.sortOrder !== 'None') {
dataSource = this.getSortedDataSource(dataSource);
}
dataSource = ListBase.groupDataSource(dataSource, fields.properties, this.sortOrder);
}
addClass([this.list], dropDownBaseClasses.grouping);
}
else {
dataSource = this.getSortedDataSource(dataSource);
}
const options = this.listOption(dataSource, fields);
const spliceData = (dataSource.length > 100) ?
new DataManager(dataSource).executeLocal(new Query().take(100))
: dataSource;
this.sortedData = dataSource;
return ListBase.createList(this.createElement, (this.getModuleName() === 'autocomplete') ? spliceData : dataSource, options, true, this);
}
listOption(dataSource, fields) {
const iconCss = isNullOrUndefined(fields.iconCss) ? false : true;
const fieldValues = !isNullOrUndefined(fields.properties) ?
fields.properties : fields;
const options = (fields.text !== null || fields.value !== null) ? {
fields: fieldValues,
showIcon: iconCss, ariaAttributes: { groupItemRole: 'presentation' }
} : { fields: { value: 'text' } };
return extend({}, options, fields, true);
}
setFloatingHeader(e) {
if (isNullOrUndefined(this.fixedHeaderElement)) {
this.fixedHeaderElement = this.createElement('div', { className: dropDownBaseClasses.fixedHead });
if (!this.list.querySelector('li').classList.contains(dropDownBaseClasses.group)) {
this.fixedHeaderElement.style.display = 'none';
}
prepend([this.fixedHeaderElement], this.list);
this.setFixedHeader();
}
if (!isNullOrUndefined(this.fixedHeaderElement) && this.fixedHeaderElement.style.zIndex === '0') {
this.setFixedHeader();
}
this.scrollStop(e);
}
scrollStop(e) {
let target = !isNullOrUndefined(e) ? e.target : this.list;
let liHeight = parseInt(getComputedStyle(this.getValidLi(), null).getPropertyValue('height'), 10);
const topIndex = Math.round(target.scrollTop / liHeight);
const liCollections = this.list.querySelectorAll('li' + ':not(.e-hide-listitem)');
for (let i = topIndex; i > -1; i--) {
if (!isNullOrUndefined(liCollections[i]) && liCollections[i].classList.contains(dropDownBaseClasses.group)) {
const currentLi = liCollections[i];
this.fixedHeaderElement.innerHTML = currentLi.innerHTML;
this.fixedHeaderElement.style.top = target.scrollTop + 'px';
this.fixedHeaderElement.style.display = 'block';
break;
}
else {
this.fixedHeaderElement.style.display = 'none';
this.fixedHeaderElement.style.top = 'none';
}
}
}
getValidLi() {
return this.liCollections[0];
}
/**
* To render the list items
*
* @param {object[]} listData - Specifies the list of array of data.
* @param {FieldSettingsModel} fields - Maps the columns of the data table and binds the data to the component.
* @returns {HTMLElement} Return the list items.
*/
renderItems(listData, fields) {
let ulElement;
if (this.itemTemplate && listData) {
let dataSource = listData;
if (dataSource && fields.groupBy) {
if (this.sortOrder !== 'None') {
dataSource = this.getSortedDataSource(dataSource);
}
dataSource = ListBase.groupDataSource(dataSource, fields.properties, this.sortOrder);
}
else {
dataSource = this.getSortedDataSource(dataSource);
}
this.sortedData = dataSource;
const spliceData = (dataSource.length > 100) ?
new DataManager(dataSource).executeLocal(new Query().take(100))
: dataSource;
ulElement = this.templateListItem((this.getModuleName() === 'autocomplete') ? spliceData : dataSource, fields);
const isTempEmpty = (this.getModuleName() === 'listbox') ? true : false;
this.DropDownBaseupdateBlazorTemplates(true, false, false, false, false, false, false, isTempEmpty);
}
else {
ulElement = this.createListItems(listData, fields);
}
return ulElement;
}
templateListItem(dataSource, fields) {
this.DropDownBaseresetBlazorTemplates(true, false, false, false);
const option = this.listOption(dataSource, fields);
option.templateID = this.itemTemplateId;
option.isStringTemplate = this.isStringTemplate;
const itemcheck = this.templateCompiler(this.itemTemplate);
if (itemcheck) {
const itemValue = select(this.itemTemplate, document).innerHTML.trim();
return ListBase.renderContentTemplate(this.createElement, itemValue, dataSource, fields.properties, option, this);
}
else {
return ListBase.renderContentTemplate(this.createElement, this.itemTemplate, dataSource, fields.properties, option, this);
}
}
typeOfData(items) {
let item = { typeof: null, item: null };
for (let i = 0; (!isNullOrUndefined(items) && i < items.length); i++) {
if (!isNullOrUndefined(items[i])) {
const listDataType = typeof (items[i]) === 'string' ||
typeof (items[i]) === 'number' || typeof (items[i]) === 'boolean';
const isNullData = listDataType ? isNullOrUndefined(items[i]) :
isNullOrUndefined(getValue((this.fields.value ? this.fields.value : 'value'), items[i]));
if (!isNullData) {
return item = { typeof: typeof items[i], item: items[i] };
}
}
}
return item;
}
setFixedHeader() {
this.list.parentElement.style.display = 'block';
let borderWidth = 0;
if (this.list && this.list.parentElement) {
borderWidth = parseInt(document.defaultView.getComputedStyle(this.list.parentElement, null).getPropertyValue('border-width'), 10);
/*Shorthand property not working in Firefox for getComputedStyle method.
Refer bug report https://bugzilla.mozilla.org/show_bug.cgi?id=137688
Refer alternate solution https://stackoverflow.com/a/41696234/9133493*/
if (isNaN(borderWidth)) {
let borderTopWidth = parseInt(document.defaultView.getComputedStyle(this.list.parentElement, null).getPropertyValue('border-top-width'), 10);
let borderBottomWidth = parseInt(document.defaultView.getComputedStyle(this.list.parentElement, null).getPropertyValue('border-bottom-width'), 10);
let borderLeftWidth = parseInt(document.defaultView.getComputedStyle(this.list.parentElement, null).getPropertyValue('border-left-width'), 10);
let borderRightWidth = parseInt(document.defaultView.getComputedStyle(this.list.parentElement, null).getPropertyValue('border-right-width'), 10);
borderWidth = (borderTopWidth + borderBottomWidth + borderLeftWidth + borderRightWidth);
}
}
const liWidth = this.getValidLi().offsetWidth - borderWidth;
this.fixedHeaderElement.style.width = liWidth.toString() + 'px';
setStyleAttribute(this.fixedHeaderElement, { zIndex: 10 });
const firstLi = this.ulElement.querySelector('.' + dropDownBaseClasses.group + ':not(.e-hide-listitem)');
this.fixedHeaderElement.innerHTML = firstLi.innerHTML;
}
getSortedDataSource(dataSource) {
if (dataSource && this.sortOrder !== 'None') {
let textField = this.fields.text ? this.fields.text : 'text';
if (this.typeOfData(dataSource).typeof === 'string' || this.typeOfData(dataSource).typeof === 'number'
|| this.typeOfData(dataSource).typeof === 'boolean') {
textField = '';
}
dataSource = ListBase.getDataSource(dataSource, ListBase.addSorting(this.sortOrder, textField));
}
return dataSource;
}
/**
* Return the index of item which matched with given value in data source
*
* @param {string | number | boolean} value - Specifies given value.
* @returns {number} Returns the index of the item.
*/
getIndexByValue(value) {
let index;
const listItems = this.getItems();
for (let i = 0; i < listItems.length; i++) {
if (!isNullOrUndefined(value) && listItems[i].getAttribute('data-value') === value.toString()) {
index = i;
break;
}
}
return index;
}
/**
* To dispatch the event manually
*
* @param {HTMLElement} element - Specifies the element to dispatch the event.
* @param {string} type - Specifies the name of the event.
* @returns {void}
*/
dispatchEvent(element, type) {
const evt = document.createEvent('HTMLEvents');
evt.initEvent(type, false, true);
element.dispatchEvent(evt);
}
/**
* To set the current fields
*
* @returns {void}
*/
setFields() {
if (this.fields.value && !this.fields.text) {
this.updateFields(this.fields.value, this.fields.value);
}
else if (!this.fields.value && this.fields.text) {
this.updateFields(this.fields.text, this.fields.text);
}
else if (!this.fields.value && !this.fields.text) {
this.updateFields('text', 'text');
}
}
/**
* reset the items list.
*
* @param {Object[] | string[] | number[] | DataManager | boolean[]} dataSource - Specifies the data to generate the list.
* @param {FieldSettingsModel} fields - Maps the columns of the data table and binds the data to the component.
* @param {Query} query - Accepts the external Query that execute along with data processing.
* @returns {void}
*/
resetList(dataSource, fields, query) {
if (this.list) {
if ((this.element.tagName === 'SELECT' && this.element.options.length > 0)
|| (this.element.tagName === 'UL' && this.element.childNodes.length > 0)) {
const data = dataSource instanceof Array ? (dataSource.length > 0)
: !isNullOrUndefined(dataSource);
if (!data && this.selectData && this.selectData.length > 0) {
dataSource = this.selectData;
}
}
this.setListData(dataSource, fields, query);
}
}
updateSelectElementData(isFiltering) {
if (isFiltering && isNullOrUndefined(this.selectData) && this.listData && this.listData.length > 0) {
this.selectData = this.listData;
}
}
updateSelection() {
// This is for after added the item, need to update the selected index values.
}
renderList() {
// This is for render the list items.
this.render();
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
updateDataSource(props) {
this.resetList(this.dataSource);
}
setUpdateInitial(props, newProp) {
this.isDataFetched = false;
const updateData = {};
for (let j = 0; props.length > j; j++) {
if (newProp[props[j]] && props[j] === 'fields') {
this.setFields();
updateData[props[j]] = newProp[props[j]];
}
else if (newProp[props[j]]) {
updateData[props[j]] = newProp[props[j]];
}
}
if (Object.keys(updateData).length > 0) {
if (Object.keys(updateData).indexOf('dataSource') === -1) {
updateData.dataSource = this.dataSource;
}
this.updateDataSource(updateData);
}
}
/**
* When property value changes happened, then onPropertyChanged method will execute the respective changes in this component.
*
* @param {DropDownBaseModel} newProp - Returns the dynamic property value of the component.
* @param {DropDownBaseModel} oldProp - Returns the previous property value of the component.
* @private
* @returns {void}
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onPropertyChanged(newProp, oldProp) {
if (this.getModuleName() === 'dropdownbase') {
this.setUpdateInitial(['fields', 'query', 'dataSource'], newProp);
}
this.setUpdateInitial(['sortOrder', 'itemTemplate'], newProp);
for (const prop of Object.keys(newProp)) {
switch (prop) {
case 'query':
case 'sortOrder':
case 'dataSource':
case 'itemTemplate':
break;
case 'enableRtl':
this.setEnableRtl();
break;
case 'enabled':
this.setEnabled();
break;