UNPKG

@syncfusion/ej2-spreadsheet

Version:

Feature-rich JavaScript Spreadsheet (Excel) control with built-in support for selection, editing, formatting, importing and exporting to Excel

996 lines (995 loc) 143 kB
import { locale, dialog, mouseDown, renderFilterCell, initiateFilterUI, getStartEvent, focus, getChartsIndexes, refreshChartCellModel, readonlyAlert } from '../index'; import { reapplyFilter, filterCellKeyDown, refreshFilterRange, createNoteIndicator } from '../index'; import { getFilteredColumn, cMenuBeforeOpen, filterByCellValue, clearFilter, getFilterRange, applySort } from '../index'; import { applyPredicates, isHiddenRow, isReadOnlyCells } from '../../workbook/index'; import { checkIsNumberAndGetNumber } from '../../workbook/common/internalization'; import { filterRangeAlert, setFilteredCollection, beforeDelete, sheetsDestroyed, initiateFilter, duplicateSheetFilterHandler, moveSheetHandler, updateSortCollection, checkNumberFormat } from '../../workbook/common/event'; import { getRangeIndexes, beforeInsert, parseLocaleNumber } from '../../workbook/index'; import { getIndexesFromAddress, getSwapRange, getColumnHeaderText, getDataRange, isCustomDateTime } from '../../workbook/index'; import { getData, getTypeFromFormat, getCell, getCellIndexes, getRangeAddress, getSheet, inRange } from '../../workbook/index'; import { sortImport, clear, getColIndex, setRow, hideShow } from '../../workbook/index'; import { beginAction, getValueFromFormat } from '../../workbook/index'; import { isFilterHidden, isNumber, checkDateFormat, isDateTime, dateToInt, getFormatFromType } from '../../workbook/index'; import { getComponent, EventHandler, isUndefined, isNullOrUndefined, Browser, removeClass, IntlBase, cldrData, getNumberDependable, defaultCurrencyCode } from '@syncfusion/ej2-base'; import { detach, classList, getNumericObject } from '@syncfusion/ej2-base'; import { Internationalization } from '@syncfusion/ej2-base'; import { refreshFilterCellsOnResize, updateWrapCell, isTouchStart } from '../common/index'; import { ExcelFilterBase, beforeFltrcMenuOpen, CheckBoxFilterBase, getUid } from '@syncfusion/ej2-grids'; import { filterCmenuSelect, filterCboxValue, filterDialogCreated, filterDialogClose, createCboxWithWrap } from '@syncfusion/ej2-grids'; import { parentsUntil, toogleCheckbox, fltrPrevent, customFilterOpen } from '@syncfusion/ej2-grids'; import { Query, DataManager, Predicate, Deferred } from '@syncfusion/ej2-data'; import { TreeView } from '@syncfusion/ej2-navigations'; import { TextBox } from '@syncfusion/ej2-inputs'; import { completeAction, contentLoaded, beforeCheckboxRender, refreshCheckbox } from '../../spreadsheet/index'; /** * `Filter` module is used to handle the filter action in Spreadsheet. */ var Filter = /** @class */ (function () { /** * Constructor for filter module. * * @param {Spreadsheet} parent - Specifies the Spreadsheet. */ function Filter(parent) { this.parent = parent; this.filterCollection = new Map(); this.filterRange = new Map(); this.filterBtn = parent.createElement('div', { className: 'e-filter-btn e-control e-btn e-lib e-filter-iconbtn e-icon-btn' + (this.parent.enableRtl ? ' e-rtl' : '') }); this.filterBtn.appendChild(parent.createElement('span', { className: 'e-btn-icon e-icons e-filter-icon' })); this.addEventListener(); } /** * To destroy the filter module. * * @returns {void} - To destroy the filter module. */ Filter.prototype.destroy = function () { var _this = this; this.removeEventListener(); if (this.parent.refreshing && this.filterRange.size) { this.parent.filterCollection = []; this.filterRange.forEach(function (_value, sheetIdx) { _this.setFilteredCollection({ sheetIdx: sheetIdx, saveJson: { filterCollection: _this.parent.filterCollection } }); }); } this.filterRange = null; this.filterCollection = null; if (this.filterBtn) { this.filterBtn.remove(); } this.filterBtn = null; if (this.treeViewObj) { this.treeViewObj.destroy(); } this.treeViewObj = null; if (this.treeViewEle) { this.treeViewEle.remove(); } this.treeViewEle = null; if (this.cBox) { this.cBox.remove(); this.cBox = null; } var filterPopupElement = document.querySelectorAll('.e-filter-popup'); if (filterPopupElement) { filterPopupElement.forEach(function (element) { element.remove(); }); } this.parent = null; }; Filter.prototype.addEventListener = function () { this.parent.on(filterRangeAlert, this.filterRangeAlertHandler, this); this.parent.on(initiateFilterUI, this.initiateFilterUIHandler, this); this.parent.on(mouseDown, this.filterMouseDownHandler, this); this.parent.on(renderFilterCell, this.renderFilterCellHandler, this); this.parent.on(refreshFilterRange, this.refreshFilterRange, this); this.parent.on(updateSortCollection, this.updateSortCollectionHandler, this); this.parent.on(beforeFltrcMenuOpen, this.beforeFilterMenuOpenHandler, this); this.parent.on(filterCmenuSelect, this.closeDialog, this); this.parent.on(reapplyFilter, this.reapplyFilterHandler, this); this.parent.on(filterByCellValue, this.filterByCellValueHandler, this); this.parent.on(clearFilter, this.clearFilterHandler, this); this.parent.on(getFilteredColumn, this.getFilteredColumnHandler, this); this.parent.on(cMenuBeforeOpen, this.cMenuBeforeOpenHandler, this); this.parent.on(filterCboxValue, this.filterCboxValueHandler, this); this.parent.on(getFilterRange, this.getFilterRangeHandler, this); this.parent.on(filterCellKeyDown, this.filterCellKeyDownHandler, this); this.parent.on(setFilteredCollection, this.setFilteredCollection, this); this.parent.on(contentLoaded, this.updateFilter, this); this.parent.on(beforeInsert, this.beforeInsertHandler, this); this.parent.on(beforeDelete, this.beforeDeleteHandler, this); this.parent.on(sheetsDestroyed, this.deleteSheetHandler, this); this.parent.on(clear, this.clearHandler, this); this.parent.on(filterDialogCreated, this.filterDialogCreatedHandler, this); this.parent.on(filterDialogClose, this.removeFilterClass, this); this.parent.on(duplicateSheetFilterHandler, this.duplicateSheetFilterHandler, this); this.parent.on(fltrPrevent, this.beforeFilteringHandler, this); this.parent.on(customFilterOpen, this.customFilterOpen, this); this.parent.on(moveSheetHandler, this.moveSheetHandler, this); this.parent.on(refreshFilterCellsOnResize, this.refreshFilterCellsOnResize, this); }; Filter.prototype.removeEventListener = function () { if (!this.parent.isDestroyed) { this.parent.off(filterRangeAlert, this.filterRangeAlertHandler); this.parent.off(initiateFilterUI, this.initiateFilterUIHandler); this.parent.off(mouseDown, this.filterMouseDownHandler); this.parent.off(renderFilterCell, this.renderFilterCellHandler); this.parent.off(refreshFilterRange, this.refreshFilterRange); this.parent.off(updateSortCollection, this.updateSortCollectionHandler); this.parent.off(beforeFltrcMenuOpen, this.beforeFilterMenuOpenHandler); this.parent.off(filterCmenuSelect, this.closeDialog); this.parent.off(reapplyFilter, this.reapplyFilterHandler); this.parent.off(filterByCellValue, this.filterByCellValueHandler); this.parent.off(clearFilter, this.clearFilterHandler); this.parent.off(getFilteredColumn, this.getFilteredColumnHandler); this.parent.off(cMenuBeforeOpen, this.cMenuBeforeOpenHandler); this.parent.off(filterCboxValue, this.filterCboxValueHandler); this.parent.off(getFilterRange, this.getFilterRangeHandler); this.parent.off(filterCellKeyDown, this.filterCellKeyDownHandler); this.parent.off(setFilteredCollection, this.setFilteredCollection); this.parent.off(contentLoaded, this.updateFilter); this.parent.off(beforeInsert, this.beforeInsertHandler); this.parent.off(beforeDelete, this.beforeDeleteHandler); this.parent.off(sheetsDestroyed, this.deleteSheetHandler); this.parent.off(clear, this.clearHandler); this.parent.off(filterDialogCreated, this.filterDialogCreatedHandler); this.parent.off(filterDialogClose, this.removeFilterClass); this.parent.off(duplicateSheetFilterHandler, this.duplicateSheetFilterHandler); this.parent.off(fltrPrevent, this.beforeFilteringHandler); this.parent.off(customFilterOpen, this.customFilterOpen); this.parent.off(moveSheetHandler, this.moveSheetHandler); this.parent.off(refreshFilterCellsOnResize, this.refreshFilterCellsOnResize); } }; /** * Gets the module name. * * @returns {string} - Gets the module name. */ Filter.prototype.getModuleName = function () { return 'filter'; }; /** * Validates the range and returns false when invalid. * * @param {SheetModel} sheet - Specify the sheet. * @param {string} range - Specify the range. * @returns {void} - Validates the range and returns false when invalid. */ Filter.prototype.isInValidFilterRange = function (sheet, range) { var selectedRange = range ? getSwapRange(getIndexesFromAddress(range)) : getSwapRange(getIndexesFromAddress(sheet.selectedRange)); var isEmptySheet = false; if (sheet.usedRange.colIndex === 0 && sheet.usedRange.rowIndex === 0 && isNullOrUndefined(sheet.rows[sheet.usedRange.rowIndex])) { isEmptySheet = true; // For Filtering Empty sheet's A1 cell. } return selectedRange[0] > sheet.usedRange.rowIndex || selectedRange[1] > sheet.usedRange.colIndex || isEmptySheet; }; /** * Shows the range error alert dialog. * * @param {any} args - Specifies the args * @param {string} args.error - range error string. * @returns {void} - Shows the range error alert dialog. */ Filter.prototype.filterRangeAlertHandler = function (args) { var _this = this; var dialogInst = this.parent.serviceLocator.getService(dialog); dialogInst.show({ content: args.error, isModal: true, height: 180, width: 400, showCloseIcon: true, beforeOpen: function (args) { var dlgArgs = { dialogName: 'FilterRangeDialog', element: args.element, target: args.target, cancel: args.cancel }; _this.parent.trigger('dialogBeforeOpen', dlgArgs); if (dlgArgs.cancel) { args.cancel = true; } else { focus(_this.parent.element); } } }); this.parent.hideSpinner(); }; /** * Triggers before filter context menu opened and used to add sorting items. * * @param {any} args - Specifies the args * @param {HTMLElement} args.element - Specify the element * @returns {void} - Triggers before filter context menu opened and used to add sorting items. */ Filter.prototype.beforeFilterMenuOpenHandler = function (args) { var l10n = this.parent.serviceLocator.getService(locale); args.element.classList.add('e-spreadsheet-contextmenu'); // to show sort icons var ul = args.element.querySelector('ul'); this.addMenuItem(ul, l10n.getConstant('SortDescending'), 'e-filter-sortdesc', 'e-sort-desc'); this.addMenuItem(ul, l10n.getConstant('SortAscending'), 'e-filter-sortasc', 'e-sort-asc'); args.element.appendChild(ul); }; /** * Creates new menu item element * * @param {Element} ul - Specify the element. * @param {string} text - Specify the text. * @param {string} className - Specify the className * @param {string} iconCss - Specify the iconCss * @returns {void} - Creates new menu item element */ Filter.prototype.addMenuItem = function (ul, text, className, iconCss) { var li = this.parent.createElement('li', { className: className + ' e-menu-item' }); if (!this.parent.allowSorting) { li.classList.add('e-disabled'); } li.innerText = text; li.insertBefore(this.parent.createElement('span', { className: 'e-menu-icon e-icons ' + iconCss }), li.firstChild); ul.insertBefore(li, ul.firstChild); }; /** * Initiates the filter UI for the selected range. * * @param {any} args - Specifies the args * @param {PredicateModel[]} args.predicates - Specify the predicates. * @param {number} args.range - Specify the range. * @param {Promise<FilterEventArgs>} args.promise - Spefify the promise. * @param {number} args.sIdx - Specify the sIdx * @param {boolean} args.isCut - Specify the bool value * @param {boolean} args.isUndoRedo - Specify the bool value * @param {boolean} args.isInternal - Specify the isInternal. * @param {boolean} args.useFilterRange - Specify the use Filter Range. * @param {boolean} args.isOpen - Specify the isOpen. * @param {boolean} args.allowHeaderFilter - Specify the allow header filter. * @returns {void} - Initiates the filter UI for the selected range. */ Filter.prototype.initiateFilterUIHandler = function (args) { var _this = this; var predicates = args ? args.predicates : null; var sheetIdx = args.sIdx; if (!sheetIdx && sheetIdx !== 0) { sheetIdx = args.isOpen ? 0 : this.parent.activeSheetIndex; } var deferred; if (args.promise) { deferred = new Deferred(); args.promise = deferred.promise; } var resolveFn = function () { if (deferred) { deferred.resolve(); } }; var isInternal = args.isInternal || args.isCut; if (this.filterRange.size > 0 && this.filterRange.has(sheetIdx) && !this.parent.isOpen && !predicates) { //disable filter this.removeFilter(sheetIdx, isInternal, false); resolveFn(); return; } var sheet = getSheet(this.parent, sheetIdx); if (this.isInValidFilterRange(sheet, args.range)) { var l10n = this.parent.serviceLocator.getService(locale); this.filterRangeAlertHandler({ error: l10n.getConstant('FilterOutOfRangeError') }); resolveFn(); return; } var selectedRange = args.range || sheet.selectedRange; var eventArgs; var actionArgs; if (!isInternal) { eventArgs = { range: selectedRange, sheetIndex: sheetIdx, cancel: false, allowHeaderFilter: false }; if (args.predicates) { eventArgs.predicates = args.predicates; eventArgs.previousPredicates = this.filterCollection.get(sheetIdx) && [].slice.call(this.filterCollection.get(sheetIdx)); } else { eventArgs.filterOptions = { predicates: args.predicates }; } eventArgs.useFilterRange = false; actionArgs = { action: 'filter', eventArgs: eventArgs }; this.parent.notify(beginAction, actionArgs); if (eventArgs.cancel) { resolveFn(); return; } delete eventArgs.cancel; args.useFilterRange = eventArgs.useFilterRange; args.allowHeaderFilter = eventArgs.allowHeaderFilter; } if (!args.range && (isInternal || selectedRange === eventArgs.range)) { var rangeIdx = getRangeIndexes(selectedRange); if (rangeIdx[0] === rangeIdx[2] && rangeIdx[1] === rangeIdx[3]) { rangeIdx = getDataRange(rangeIdx[0], rangeIdx[1], sheet); selectedRange = getRangeAddress(rangeIdx); if (!isInternal) { eventArgs.range = selectedRange; } } } else if (!isInternal) { selectedRange = eventArgs.range; } if (predicates) { if (predicates.length) { var filterRange = this.filterRange.get(sheetIdx); if (filterRange) { args.useFilterRange = filterRange.useFilterRange; args.allowHeaderFilter = filterRange.allowHeaderFilter; } this.processRange(sheet, sheetIdx, selectedRange, true, args.useFilterRange, args.allowHeaderFilter); var range = this.filterRange.get(sheetIdx).range.slice(); if (!args.allowHeaderFilter) { range[0] = range[0] + 1; // to skip first row. } if (!args.useFilterRange) { range[2] = sheet.usedRange.rowIndex; //filter range should be till used range. } range[1] = range[3] = getColIndex(predicates[0].field); var addr = sheet.name + "!" + this.getPredicateRange(range, predicates.slice(1, predicates.length)); var fullAddr = getRangeAddress(range); getData(this.parent, addr, true, true, null, true, null, null, false, fullAddr).then(function (jsonData) { _this.filterSuccessHandler(new DataManager(jsonData), { action: 'filtering', filterCollection: predicates, field: predicates[0].field, sIdx: args.sIdx, isInternal: isInternal, isOpen: args.isOpen, prevPredicates: eventArgs && eventArgs.previousPredicates }); resolveFn(); }); return; } else { this.clearFilterHandler({ sheetIndex: sheetIdx }); resolveFn(); } } else { this.processRange(sheet, sheetIdx, selectedRange, false, args.useFilterRange, args.allowHeaderFilter); resolveFn(); } if (!isInternal) { this.parent.notify(completeAction, actionArgs); focus(this.parent.element); } }; /** * Processes the range if no filter applied. * * @param {SheetModel} sheet - Specify the sheet. * @param {number} sheetIdx - Specify the sheet index. * @param {string} filterRange - Specify the filterRange. * @param {boolean} preventRefresh - To prevent refreshing the filter buttons. * @param {boolean} useFilterRange - Specifies whether to consider filtering range or used range during filering. * @param {boolean} allowHeaderFilter - Specifies whether to consider first row during filtering. * @returns {void} - Processes the range if no filter applied. */ Filter.prototype.processRange = function (sheet, sheetIdx, filterRange, preventRefresh, useFilterRange, allowHeaderFilter) { var range = getSwapRange(getIndexesFromAddress(filterRange || sheet.selectedRange)); if (range[0] === range[2] && range[1] === range[3]) { //if selected range is a single cell range[0] = 0; range[1] = 0; range[2] = sheet.usedRange.rowIndex; range[3] = sheet.usedRange.colIndex; } else if (range[3] > sheet.usedRange.colIndex) { range[3] = sheet.usedRange.colIndex; } var filterOption = { useFilterRange: useFilterRange, range: range }; if (allowHeaderFilter) { filterOption.allowHeaderFilter = allowHeaderFilter; } this.filterRange.set(sheetIdx, filterOption); this.filterCollection.set(sheetIdx, []); if (!preventRefresh) { this.refreshFilterRange(range, false, sheetIdx); } }; /** * Removes all the filter related collections for the active sheet. * * @param {number} sheetIdx - Specify the sheet index. * @param {boolean} isCut - Specify the bool value. * @param {boolean} preventRefresh - Specify the preventRefresh. * @param {boolean} clearAction - Specify the current action is clear or not. * @returns {void} - Removes all the filter related collections for the active sheet. */ Filter.prototype.removeFilter = function (sheetIdx, isCut, preventRefresh, clearAction) { var filterOption = this.filterRange.get(sheetIdx); var range = filterOption.range.slice(); var allowHeaderFilter = filterOption.allowHeaderFilter; var rangeAddr = getRangeAddress(range); var args; if (!isCut) { args = { action: 'filter', eventArgs: { range: rangeAddr, sheetIndex: sheetIdx, cancel: false }, isClearAction: clearAction }; this.parent.notify(beginAction, args); if (args.eventArgs.cancel) { return; } delete args.eventArgs.cancel; } if (this.filterCollection.get(sheetIdx).length || preventRefresh) { if (this.filterCollection.get(sheetIdx).length && clearAction) { var newArgs = { action: 'filter', eventArgs: { range: rangeAddr, sheetIndex: sheetIdx, predicates: [], previousPredicates: this.filterCollection.get(sheetIdx) }, isClearAction: clearAction }; this.parent.notify(completeAction, newArgs); } this.clearFilterHandler({ preventRefresh: preventRefresh, sheetIndex: sheetIdx }); } this.filterRange.delete(sheetIdx); this.filterCollection.delete(sheetIdx); this.refreshFilterRange(range, true, sheetIdx, allowHeaderFilter); if (!isCut) { this.parent.notify(completeAction, args); } }; /** * Handles filtering cell value based on context menu. * * @returns {void} - Handles filtering cell value based on context menu. */ Filter.prototype.filterByCellValueHandler = function () { var _this = this; var sheetIdx = this.parent.activeSheetIndex; var sheet = this.parent.getActiveSheet(); if (this.isInValidFilterRange(sheet)) { var l10n = this.parent.serviceLocator.getService(locale); this.filterRangeAlertHandler({ error: l10n.getConstant('FilterOutOfRangeError') }); return; } var cell = getRangeIndexes(sheet.activeCell); var isNotFilterRange; if (!this.isFilterRange(sheetIdx, cell[0], cell[1])) { isNotFilterRange = true; this.processRange(sheet, sheetIdx); } var filterOption = this.filterRange.get(sheetIdx); var range = filterOption.range.slice(); var filterRange = getRangeAddress(range); range[0] = range[0] + 1; // to skip first row. range[2] = sheet.usedRange.rowIndex; //filter range should be till used range. range[1] = range[3] = cell[1]; var field = getColumnHeaderText(cell[1] + 1); var selectedCell = getCell(cell[0], cell[1], sheet); var cellVal = getValueFromFormat(this.parent, selectedCell, cell[0], cell[1], sheetIdx); var predicates = [{ field: field, operator: 'equal', type: this.getColumnType(sheet, cell[1], cell).type, matchCase: false, value: cellVal }]; var prevPredicates = [].slice.call(this.filterCollection.get(sheetIdx)); if (!prevPredicates.length) { prevPredicates = undefined; } var eventArgs = { range: filterRange, predicates: predicates, previousPredicates: prevPredicates, sheetIndex: sheetIdx, cancel: false, allowHeaderFilter: false }; this.parent.notify(beginAction, { action: 'filter', eventArgs: eventArgs }); if (eventArgs.cancel) { if (isNotFilterRange) { this.removeFilter(sheetIdx, true); } return; } if (eventArgs.allowHeaderFilter) { filterOption.allowHeaderFilter = eventArgs.allowHeaderFilter; range[0]--; } var addr = sheet.name + "!" + this.getPredicateRange(range, this.filterCollection.get(sheetIdx)); var fullAddr = getRangeAddress(range); getData(this.parent, addr, true, true, null, true, null, null, false, fullAddr).then(function (jsonData) { _this.filterSuccessHandler(new DataManager(jsonData), { action: 'filtering', filterCollection: predicates, field: field, isFilterByValue: true }); }); }; /** * Creates filter buttons and renders the filter applied cells. * * @param { any} args - Specifies the args * @param { HTMLElement} args.td - specify the element * @param { number} args.rowIndex - specify the rowIndex * @param { number} args.colIndex - specify the colIndex * @param { number} args.sIdx - specify the sIdx * @param { boolean} args.isAction - specify the apply filter action. * @returns {void} - Creates filter buttons and renders the filter applied cells. */ Filter.prototype.renderFilterCellHandler = function (args) { var sheetIdx = !isNullOrUndefined(args.sIdx) ? args.sIdx : this.parent.activeSheetIndex; if (sheetIdx === this.parent.activeSheetIndex) { var option = this.filterRange.get(sheetIdx) && this.filterRange.get(sheetIdx); var range = option && option.range; if (range && (range[0] === args.rowIndex || option.allowHeaderFilter) && range[1] <= args.colIndex && range[3] >= args.colIndex) { if (!args.td || args.td.classList.contains(option.allowHeaderFilter ? 'e-cell' : 'e-header-cell')) { return; } var filterButton = args.td.querySelector('.e-filter-icon'); var filterSortCls = ''; var sortCollection = this.parent.sortCollection; var field = getColumnHeaderText(args.colIndex + 1); var predicates = this.filterCollection.get(sheetIdx); for (var i = 0; i < predicates.length; i++) { if (predicates[i].field === field) { filterSortCls = ' e-filtered'; break; } } if (sortCollection) { for (var i = 0; i < sortCollection.length; i++) { if (sortCollection[i].sheetIndex === sheetIdx && sortCollection[i].columnIndex === args.colIndex) { filterSortCls += sortCollection[i].order === 'Ascending' ? ' e-sortasc-filter' : ' e-sortdesc-filter'; break; } } } if (filterButton) { filterButton.className = "e-btn-icon e-icons e-filter-icon" + filterSortCls; } else { var isNoteAvailable = false; filterButton = this.filterBtn.cloneNode(true); if (args.td.children.length > 0 && args.td.children[args.td.childElementCount - 1].className.indexOf('e-addNoteIndicator') > -1) { args.td.removeChild(args.td.children[args.td.childElementCount - 1]); isNoteAvailable = true; } filterButton.firstElementChild.className = "e-btn-icon e-icons e-filter-icon" + filterSortCls; args.td.insertBefore(filterButton, args.td.firstChild); if (args.isAction) { var sheet = getSheet(this.parent, sheetIdx); if (getCell(args.rowIndex, args.colIndex, sheet, false, true).wrap) { this.parent.notify(updateWrapCell, { rowIdx: args.rowIndex, colIdx: args.colIndex, sheet: sheet, ele: args.td }); } } if (isNoteAvailable) { this.parent.notify(createNoteIndicator, { targetElement: args.td, rowIndex: args.rowIndex, columnIndex: args.colIndex }); } } } } }; /** * Refreshes the filter header range. * * @param {number[]} filterRange - Specify the filterRange. * @param {boolean} remove - Specify the bool value * @param {number} sIdx - Specify the index. * @param {boolean} allowHeaderFilter - Specifies whether to consider first row during filtering. * @returns {void} - Refreshes the filter header range. */ Filter.prototype.refreshFilterRange = function (filterRange, remove, sIdx, allowHeaderFilter) { var sheetIdx = sIdx; if (!sheetIdx && sheetIdx !== 0) { sheetIdx = this.parent.activeSheetIndex; } var filterOption = this.filterRange && this.filterRange.get(sheetIdx); if (!filterOption) { if (!filterRange) { filterRange = [0, 0, 0, 0]; } } else { filterRange = filterRange || filterOption.range.slice(); allowHeaderFilter = filterOption.allowHeaderFilter; } var range = filterRange; var cell; var sheet = getSheet(this.parent, sheetIdx); var frozenCol = this.parent.frozenColCount(sheet); for (var index = range[1]; index <= range[3]; index++) { if (allowHeaderFilter) { var table = index < frozenCol ? this.parent.sheetModule.getSelectAllTable() : this.parent.getColHeaderTable(); var headerRow = table && this.parent.getRow(0, table); cell = headerRow && this.parent.getCell(0, index, headerRow); } else { if (!isHiddenRow(sheet, range[0])) { cell = this.parent.getCell(range[0], index); } else { cell = null; } } if (remove) { if (cell) { var filterBtn = cell.querySelector('.e-filter-btn'); if (filterBtn) { var isNoteAvailable = false; if (cell.children.length > 0 && cell.children[cell.childElementCount - 1].className.indexOf('e-addNoteIndicator') > -1) { cell.removeChild(cell.children[cell.childElementCount - 1]); isNoteAvailable = true; } filterBtn.parentElement.removeChild(filterBtn); if (isNoteAvailable) { this.parent.notify(createNoteIndicator, { targetElement: cell, rowIndex: range[0], columnIndex: index }); } } } } else { this.renderFilterCellHandler({ td: cell, rowIndex: range[0], colIndex: index, sIdx: sheetIdx, isAction: true }); } } if (this.parent.sortCollection) { this.parent.notify(sortImport, null); } }; /** * Checks whether the provided cell is a filter cell. * * @param {number} sheetIdx - Specify the sheet index. * @param {number} rowIndex - Specify the row index * @param {number} colIndex - Specify the col index. * @returns {boolean} - Checks whether the provided cell is a filter cell. */ Filter.prototype.isFilterCell = function (sheetIdx, rowIndex, colIndex) { var range = this.filterRange.has(sheetIdx) && this.filterRange.get(sheetIdx).range; return (range && range[0] === rowIndex && range[1] <= colIndex && range[3] >= colIndex); }; /** * Checks whether the provided cell is in a filter range * * @param {number} sheetIdx - Specify the sheet index. * @param {number} rowIndex - Specify the row index * @param {number} colIndex - Specify the col index. * @returns {boolean} - Checks whether the provided cell is in a filter range */ Filter.prototype.isFilterRange = function (sheetIdx, rowIndex, colIndex) { var range = this.filterRange.get(sheetIdx) && this.filterRange.get(sheetIdx).range; return (range && range[0] <= rowIndex && range[2] >= rowIndex && range[1] <= colIndex && range[3] >= colIndex); }; /** * Gets the filter information from active cell * * @param {any} args - Specifies the args * @param {string} args.field - Specify the field * @param {string} args.clearFilterText - Specify the clearFilterText * @param {boolean} args.isFiltered - Specify the isFiltered * @param {boolean} args.isClearAll - Specify the isClearAll * @param {number} args.sheetIndex - Specify the sheet index value. * @returns {void} - Triggers before context menu created to enable or disable items. */ Filter.prototype.getFilteredColumnHandler = function (args) { var sheetIdx = isUndefined(args.sheetIndex) ? this.parent.activeSheetIndex : args.sheetIndex; var l10n = this.parent.serviceLocator.getService(locale); args.clearFilterText = l10n.getConstant('ClearFilter'); if (this.filterRange.has(sheetIdx)) { var filterCollection = this.filterCollection.get(sheetIdx); if (args.isClearAll) { args.isFiltered = filterCollection && filterCollection.length > 0; return; } var range = this.filterRange.get(sheetIdx).range.slice(); var sheet = getSheet(this.parent, sheetIdx); var cell = getCellIndexes(sheet.activeCell); if (this.isFilterRange(sheetIdx, cell[0], cell[1])) { args.field = getColumnHeaderText(cell[1] + 1); var headerCell = getCell(range[0], cell[1], sheet); var cellValue = this.parent.getDisplayText(headerCell); args.clearFilterText = l10n.getConstant('ClearFilterFrom') + '"' + (cellValue ? cellValue.toString() : 'Column ' + args.field) + '"'; filterCollection.some(function (value) { args.isFiltered = value.field === args.field; return args.isFiltered; }); } } }; /** * Triggers before context menu created to enable or disable items. * * @param {any} e - Specifies the args * @param {HTMLElement} e.element - Specify the element * @param {MenuItemModel[]} e.items - Specify the items * @param {MenuItemModel} e.parentItem - Specify the parentItem * @param {string} e.target - Specify the target * @returns {void} - Triggers before context menu created to enable or disable items. */ Filter.prototype.cMenuBeforeOpenHandler = function (e) { var id = this.parent.element.id + '_cmenu'; if (e.parentItem && e.parentItem.id === id + '_filter' && e.target === '') { var args = { isFiltered: false }; this.getFilteredColumnHandler(args); this.parent.enableContextMenuItems([id + '_clearfilter', id + '_reapplyfilter'], !!args.isFiltered, true); } }; /** * Closes the filter popup. * * @returns {void} - Closes the filter popup. */ Filter.prototype.closeDialog = function () { var filterPopup = document.querySelector('.e-filter-popup'); if (filterPopup && filterPopup.id.includes(this.parent.element.id)) { var excelFilter = getComponent(filterPopup, 'dialog'); EventHandler.remove(filterPopup, getStartEvent(), this.filterMouseDownHandler); if (excelFilter) { excelFilter.hide(); } this.parent.notify(filterDialogClose, null); } }; Filter.prototype.removeFilterClass = function () { if (this.parent.element.style.position === 'relative') { this.parent.element.style.position = ''; } if (this.parent.element.classList.contains('e-filter-open')) { this.parent.element.classList.remove('e-filter-open'); } }; /** * Returns true if the filter popup is opened. * * @returns {boolean} - Returns true if the filter popup is opened. */ Filter.prototype.isPopupOpened = function () { var filterPopup = document.getElementsByClassName('e-filter-popup')[0]; return filterPopup && filterPopup.id.includes(this.parent.element.id) && filterPopup.style.display !== 'none'; }; Filter.prototype.filterCellKeyDownHandler = function (args) { var sheet = this.parent.getActiveSheet(); var indexes = getCellIndexes(sheet.activeCell); if (this.isFilterCell(this.parent.activeSheetIndex, indexes[0], indexes[1])) { if (args.closePopup) { this.closeDialog(); } else { args.isFilterCell = true; if (!this.isPopupOpened()) { var target = this.parent.getCell(indexes[0], indexes[1]); if (target) { this.openDialog(target); } } } } }; Filter.prototype.filterMouseDownHandler = function (e) { if ((Browser.isDevice && e.type === 'mousedown') || this.parent.getActiveSheet().isProtected) { return; } var target = e.target; if (target.classList.contains('e-filter-icon') || target.classList.contains('e-filter-btn')) { if (Browser.isDevice && isTouchStart(e)) { e.preventDefault(); } if (this.isPopupOpened()) { this.closeDialog(); } this.openDialog((parentsUntil(target, 'e-cell') || parentsUntil(target, 'e-header-cell'))); } else if (this.isPopupOpened()) { var offsetEle = target.offsetParent; if (!target.classList.contains('e-searchinput') && !target.classList.contains('e-searchclear') && (offsetEle && !offsetEle.classList.contains('e-filter-popup') && !offsetEle.classList.contains('e-text-content') && !offsetEle.classList.contains('e-checkboxtree') && !offsetEle.classList.contains('e-checkbox-wrapper'))) { this.closeDialog(); } else { this.selectSortItemHandler(target); } } }; Filter.prototype.cboxListSelected = function (args, selectedList, listCount, e, searched) { var wrapper = parentsUntil(e.target, 'e-ftrchk'); if (wrapper) { var addCurCbox = searched && wrapper.querySelector('.e-add-current'); if (addCurCbox) { if (addCurCbox.classList.contains('e-check')) { classList(addCurCbox, ['e-uncheck'], ['e-check']); if (!selectedList.length) { args.btnObj.element.disabled = true; } return false; } else { classList(addCurCbox, ['e-check'], ['e-uncheck']); args.btnObj.element.disabled = false; return true; } } var selectAll = wrapper.querySelector('.e-selectall'); if (selectAll) { wrapper.querySelector('.e-chk-hidden').indeterminate = false; var uncheck = wrapper.querySelector('.e-frame').classList.contains('e-check'); var checkBoxFrame = void 0; var text = void 0; for (var idx = searched ? 2 : 1, len = args.element.childElementCount; idx < len; idx++) { checkBoxFrame = args.element.children[idx].querySelector('.e-frame'); removeClass([checkBoxFrame], ['e-check', 'e-stop', 'e-uncheck']); if (uncheck) { args.element.children[idx].querySelector('.e-chk-hidden').checked = false; checkBoxFrame.classList.add('e-uncheck'); selectedList.splice(0, 1); } else { args.element.children[idx].querySelector('.e-chk-hidden').checked = true; checkBoxFrame.classList.add('e-check'); text = args.element.children[idx].querySelector('.e-checkboxfiltertext').textContent; if (selectedList.indexOf(text) === -1) { selectedList.push(text); } } } } else { var text = wrapper.querySelector('.e-checkboxfiltertext').textContent; if (wrapper.querySelector('.e-frame').classList.contains('e-check')) { selectedList.splice(selectedList.indexOf(text), 1); } else { selectedList.push(text); } toogleCheckbox(wrapper); selectAll = args.element.querySelector('.e-selectall'); } this.updateState(args, selectAll, selectAll.parentElement.querySelector('.e-chk-hidden'), selectedList.length !== listCount, selectedList.length); } return null; }; Filter.prototype.initCboxList = function (args, excelFilter, filterData) { var _this = this; var field = args.column.field; var sortedData = new DataManager(args.dataSource).executeLocal(new Query().sortBy(field + '_value', 'ascending')); var listData = []; var sheet = this.parent.getActiveSheet(); var l10n = this.parent.serviceLocator.getService(locale); var cBoxFrag = document.createDocumentFragment(); var selectAll = this.createSelectAll(args, excelFilter); cBoxFrag.appendChild(selectAll); var idCol = {}; var hidden; var initSelectedList = []; var selectedList = []; var dataVal; sortedData.forEach(function (data) { if (data[field] === '') { if (!idCol['isBlank']) { idCol['isBlank'] = true; var blankObj = {}; blankObj[field] = l10n.getConstant('Blanks'); hidden = isFilterHidden(sheet, Number(data['__rowIndex']) - 1); var blankCbox = createCboxWithWrap( // eslint-disable-next-line @typescript-eslint/no-explicit-any getUid('cbox'), excelFilter.createCheckbox(blankObj[field], !hidden, blankObj), 'e-ftrchk'); if (cBoxFrag.childElementCount === 1) { cBoxFrag.appendChild(blankCbox); } else { cBoxFrag.insertBefore(blankCbox, cBoxFrag.children[1]); } listData.splice(0, 0, blankObj[field]); if (!hidden) { initSelectedList.push(blankObj[field]); selectedList.push(blankObj[field]); } } } else if (!idCol[data[field]]) { idCol[data[field]] = true; hidden = isFilterHidden(sheet, Number(data['__rowIndex']) - 1); dataVal = data[field]; cBoxFrag.appendChild( // eslint-disable-next-line @typescript-eslint/no-explicit-any createCboxWithWrap(getUid('cbox'), excelFilter.createCheckbox(dataVal, !hidden, data), 'e-ftrchk')); listData.push(dataVal); if (!hidden) { initSelectedList.push(dataVal); selectedList.push(dataVal); } } }); args.element.appendChild(cBoxFrag); var cBoxFrame = selectAll.querySelector('.e-frame'); cBoxFrame.classList.add('e-selectall'); var cBox = selectAll.querySelector('.e-chk-hidden'); this.updateState(args, cBoxFrame, cBox, selectedList.length !== listData.length, selectedList.length); var mainCboxList = [].slice.call(args.element.childNodes); var searchedSelectedList; var searchedList; var addCurCboxSelected; args.element.addEventListener('click', function (e) { if (searchedSelectedList) { var isCurSelect = _this.cboxListSelected(args, searchedSelectedList, args.element.childElementCount - 2, e, true); if (isCurSelect !== null) { addCurCboxSelected = isCurSelect; } } else { _this.cboxListSelected(args, selectedList, listData.length, e); } }); var sortedFullData; var searchValue; var updateSearchedList = function (val) { if (val.toLowerCase().includes(searchValue)) { var obj = {}; obj[args.column.field] = val; cBoxFrag.appendChild( // eslint-disable-next-line @typescript-eslint/no-explicit-any createCboxWithWrap(getUid('cbox'), excelFilter.createCheckbox(val, true, obj), 'e-ftrchk')); searchedList.push(val); searchedSelectedList.push(val); } }; var performSearchOnData; var filterDataCount = args.dataSource.length > 1000 ? args.dataSource.length : 1000; var fullListData = listData; if (filterData.length <= filterDataCount) { performSearchOnData = function () { listData.forEach(function (val) { updateSearchedList(val); }); }; } else { performSearchOnData = function () { if (!sortedFullData) { fullListData = []; initSelectedList = []; selectedList = []; sortedFullData = new DataManager(filterData).executeLocal(new Query().sortBy(field + '_value', 'ascending')); idCol = {}; sortedFullData.forEach(function (data) { if (data[field] === '') { if (!idCol['isBlank']) { idCol['isBlank'] = true; dataVal = l10n.getConstant('Blanks'); fullListData.splice(0, 0, dataVal); if (!isFilterHidden(sheet, Number(data['__rowIndex']) - 1)) { initSelectedList.push(dataVal); selectedList.push(dataVal); } } } else if (!idCol[data[field]]) { dataVal = data[field]; idCol[dataVal] = true; fullListData.push(data[field]); if (!isFilterHidden(sheet, Number(data['__rowIndex']) - 1)) { selectedList.push(dataVal); initSelectedList.push(dataVal); } } }); } for (var filterIdx = 0, len = fullListData.length; filterIdx < len; filterIdx++) { if (searchedList.length < filterDataCount) { updateSearchedList(fullListData[filterIdx]); } else { break; } } }; } var refreshCheckbox = function (e) { if (e.event.type === 'keyup') { searchValue = e.event.target.value.toLowerCase(); } else if (e.event.target.classList.contains('e-search-icon')) { return; } cBoxFrag = document.createDocumentFragment(); cBoxFrag.appendChild(selectAll); if (searchValue) { searchedList = []; searchedSelectedList = []; performSearchOnData(); if (searchedSelectedList.length) { _this.updateState(args, cBoxFrame, cBox, false, 0); selectAll.classList.remove('e-hide'); var obj = {}; obj[field] = l10n.getConstant('AddCurrentSelection'); var addCurrentCbox = createCboxWithWrap( // eslint-disable-next-line @typescript-eslint/no-explicit-any getUid('cbox'), excelFilter.createCheckbox(obj[field], false, obj), 'e-ftrchk'); cBoxFrag.insertBefore(addCurrentCbox, cBoxFrag.children[1]); addCurrentCbox.querySelector('.e-frame').classList.add('e-add-current'); } else { selectAll.classList.add('e-hide'); var noRecordEle = _this.parent.createElement('div', { className: 'e-checkfltrnmdiv' }); var noRecordText = _this.parent.createElement('span'); noRecordText.innerText = l10n.getConstant('NoResult'); noRecordEle.appendChild(noRecordText);