UNPKG

@ag-grid-enterprise/row-grouping

Version:

Advanced Data Grid / Data Table supporting Javascript / Typescript / React / Angular / Vue

1,433 lines (1,418 loc) 137 kB
// enterprise-modules/row-grouping/src/rowGroupingModule.ts import { ModuleNames, _ColumnFilterModule, _FloatingFilterModule, _defineModule } from "@ag-grid-community/core"; import { EnterpriseCoreModule, GroupCellRenderer, GroupCellRendererCtrl } from "@ag-grid-enterprise/core"; // enterprise-modules/row-grouping/src/rowGrouping/aggFuncService.ts import { BeanStub, _exists, _existsAndNotEmpty, _includes, _iterateObject, _last } from "@ag-grid-community/core"; var defaultAggFuncNames = { sum: "Sum", first: "First", last: "Last", min: "Min", max: "Max", count: "Count", avg: "Average" }; var AggFuncService = class extends BeanStub { constructor() { super(...arguments); this.beanName = "aggFuncService"; this.aggFuncsMap = {}; this.initialised = false; } postConstruct() { this.init(); } init() { if (this.initialised) { return; } this.initialiseWithDefaultAggregations(); this.addAggFuncs(this.gos.get("aggFuncs")); } initialiseWithDefaultAggregations() { const aggMap = this.aggFuncsMap; aggMap["sum"] = aggSum; aggMap["first"] = aggFirst; aggMap["last"] = aggLast; aggMap["min"] = aggMin; aggMap["max"] = aggMax; aggMap["count"] = aggCount; aggMap["avg"] = aggAvg; this.initialised = true; } isAggFuncPossible(column, func) { const allKeys = this.getFuncNames(column); const allowed = _includes(allKeys, func); const funcExists = _exists(this.aggFuncsMap[func]); return allowed && funcExists; } getDefaultFuncLabel(fctName) { return defaultAggFuncNames[fctName] ?? fctName; } getDefaultAggFunc(column) { const defaultAgg = column.getColDef().defaultAggFunc; if (_exists(defaultAgg) && this.isAggFuncPossible(column, defaultAgg)) { return defaultAgg; } if (this.isAggFuncPossible(column, "sum")) { return "sum"; } const allKeys = this.getFuncNames(column); return _existsAndNotEmpty(allKeys) ? allKeys[0] : null; } addAggFuncs(aggFuncs) { this.init(); _iterateObject(aggFuncs, (key, aggFunc) => { this.aggFuncsMap[key] = aggFunc; }); } getAggFunc(name) { this.init(); return this.aggFuncsMap[name]; } getFuncNames(column) { const userAllowedFuncs = column.getColDef().allowedAggFuncs; return userAllowedFuncs == null ? Object.keys(this.aggFuncsMap).sort() : userAllowedFuncs; } clear() { this.aggFuncsMap = {}; } }; function aggSum(params) { const { values } = params; let result = null; for (let i = 0; i < values.length; i++) { const value = values[i]; if (typeof value === "number") { if (result === null) { result = value; } else { result += typeof result === "number" ? value : BigInt(value); } } else if (typeof value === "bigint") { if (result === null) { result = value; } else { result = (typeof result === "bigint" ? result : BigInt(result)) + value; } } } return result; } function aggFirst(params) { return params.values.length > 0 ? params.values[0] : null; } function aggLast(params) { return params.values.length > 0 ? _last(params.values) : null; } function aggMin(params) { const { values } = params; let result = null; for (let i = 0; i < values.length; i++) { const value = values[i]; if ((typeof value === "number" || typeof value === "bigint") && (result === null || result > value)) { result = value; } } return result; } function aggMax(params) { const { values } = params; let result = null; for (let i = 0; i < values.length; i++) { const value = values[i]; if ((typeof value === "number" || typeof value === "bigint") && (result === null || result < value)) { result = value; } } return result; } function aggCount(params) { const { values } = params; let result = 0; for (let i = 0; i < values.length; i++) { const value = values[i]; result += value != null && typeof value.value === "number" ? value.value : 1; } const existingAggData = params.rowNode?.aggData?.[params.column.getColId()]; if (existingAggData && existingAggData.value === result) { return existingAggData; } return { value: result, toString: function() { return this.value.toString(); }, // used for sorting toNumber: function() { return this.value; } }; } function aggAvg(params) { const { values } = params; let sum = 0; let count = 0; for (let i = 0; i < values.length; i++) { const currentValue = values[i]; let valueToAdd = null; if (typeof currentValue === "number" || typeof currentValue === "bigint") { valueToAdd = currentValue; count++; } else if (currentValue != null && (typeof currentValue.value === "number" || typeof currentValue.value === "bigint") && typeof currentValue.count === "number") { valueToAdd = currentValue.value * (typeof currentValue.value === "number" ? currentValue.count : BigInt(currentValue.count)); count += currentValue.count; } if (typeof valueToAdd === "number") { sum += typeof sum === "number" ? valueToAdd : BigInt(valueToAdd); } else if (typeof valueToAdd === "bigint") { sum = (typeof sum === "bigint" ? sum : BigInt(sum)) + valueToAdd; } } let value = null; if (count > 0) { value = sum / (typeof sum === "number" ? count : BigInt(count)); } const existingAggData = params.rowNode?.aggData?.[params.column?.getColId()]; if (existingAggData && existingAggData.count === count && existingAggData.value === value) { return existingAggData; } return { count, value, // the grid by default uses toString to render values for an object, so this // is a trick to get the default cellRenderer to display the avg value toString: function() { return typeof this.value === "number" || typeof this.value === "bigint" ? this.value.toString() : ""; }, // used for sorting toNumber: function() { return this.value; } }; } // enterprise-modules/row-grouping/src/rowGrouping/aggregationStage.ts import { BeanStub as BeanStub2, _errorOnce, _getGrandTotalRow, _getGroupAggFiltering, _missingOrEmpty } from "@ag-grid-community/core"; var AggregationStage = class extends BeanStub2 { constructor() { super(...arguments); this.beanName = "aggregationStage"; } wireBeans(beans) { this.columnModel = beans.columnModel; this.aggFuncService = beans.aggFuncService; this.funcColsService = beans.funcColsService; this.pivotResultColsService = beans.pivotResultColsService; this.valueService = beans.valueService; } // it's possible to recompute the aggregate without doing the other parts // + api.refreshClientSideRowModel('aggregate') execute(params) { const noValueColumns = _missingOrEmpty(this.funcColsService.getValueColumns()); const noUserAgg = !this.gos.getCallback("getGroupRowAgg"); const changedPathActive = params.changedPath && params.changedPath.isActive(); if (noValueColumns && noUserAgg && changedPathActive) { return; } const aggDetails = this.createAggDetails(params); this.recursivelyCreateAggData(aggDetails); } createAggDetails(params) { const pivotActive = this.columnModel.isPivotActive(); const measureColumns = this.funcColsService.getValueColumns(); const pivotColumns = pivotActive ? this.funcColsService.getPivotColumns() : []; const aggDetails = { alwaysAggregateAtRootLevel: this.gos.get("alwaysAggregateAtRootLevel"), groupIncludeTotalFooter: !!_getGrandTotalRow(this.gos), changedPath: params.changedPath, valueColumns: measureColumns, pivotColumns, filteredOnly: !this.isSuppressAggFilteredOnly(), userAggFunc: this.gos.getCallback("getGroupRowAgg") }; return aggDetails; } isSuppressAggFilteredOnly() { const isGroupAggFiltering = _getGroupAggFiltering(this.gos) !== void 0; return isGroupAggFiltering || this.gos.get("suppressAggFilteredOnly"); } recursivelyCreateAggData(aggDetails) { const callback = (rowNode) => { const hasNoChildren = !rowNode.hasChildren(); if (hasNoChildren) { if (rowNode.aggData) { rowNode.setAggData(null); } return; } const isRootNode = rowNode.level === -1; if (isRootNode && !aggDetails.groupIncludeTotalFooter) { const notPivoting = !this.columnModel.isPivotMode(); if (!aggDetails.alwaysAggregateAtRootLevel && notPivoting) { rowNode.setAggData(null); return; } } this.aggregateRowNode(rowNode, aggDetails); }; aggDetails.changedPath.forEachChangedNodeDepthFirst(callback, true); } aggregateRowNode(rowNode, aggDetails) { const measureColumnsMissing = aggDetails.valueColumns.length === 0; const pivotColumnsMissing = aggDetails.pivotColumns.length === 0; let aggResult; if (aggDetails.userAggFunc) { aggResult = aggDetails.userAggFunc({ nodes: rowNode.childrenAfterFilter }); } else if (measureColumnsMissing) { aggResult = null; } else if (pivotColumnsMissing) { aggResult = this.aggregateRowNodeUsingValuesOnly(rowNode, aggDetails); } else { aggResult = this.aggregateRowNodeUsingValuesAndPivot(rowNode); } rowNode.setAggData(aggResult); if (rowNode.sibling) { rowNode.sibling.setAggData(aggResult); } } aggregateRowNodeUsingValuesAndPivot(rowNode) { const result = {}; const secondaryColumns = this.pivotResultColsService.getPivotResultCols()?.list ?? []; let canSkipTotalColumns = true; for (let i = 0; i < secondaryColumns.length; i++) { const secondaryCol = secondaryColumns[i]; const colDef = secondaryCol.getColDef(); if (colDef.pivotTotalColumnIds != null) { canSkipTotalColumns = false; continue; } const keys = colDef.pivotKeys ?? []; let values; if (rowNode.leafGroup) { values = this.getValuesFromMappedSet(rowNode.childrenMapped, keys, colDef.pivotValueColumn); } else { values = this.getValuesPivotNonLeaf(rowNode, colDef.colId); } result[colDef.colId] = this.aggregateValues( values, colDef.pivotValueColumn.getAggFunc(), colDef.pivotValueColumn, rowNode, secondaryCol ); } if (!canSkipTotalColumns) { for (let i = 0; i < secondaryColumns.length; i++) { const secondaryCol = secondaryColumns[i]; const colDef = secondaryCol.getColDef(); if (colDef.pivotTotalColumnIds == null || !colDef.pivotTotalColumnIds.length) { continue; } const aggResults = colDef.pivotTotalColumnIds.map( (currentColId) => result[currentColId] ); result[colDef.colId] = this.aggregateValues( aggResults, colDef.pivotValueColumn.getAggFunc(), colDef.pivotValueColumn, rowNode, secondaryCol ); } } return result; } aggregateRowNodeUsingValuesOnly(rowNode, aggDetails) { const result = {}; const changedValueColumns = aggDetails.changedPath.isActive() ? aggDetails.changedPath.getValueColumnsForNode(rowNode, aggDetails.valueColumns) : aggDetails.valueColumns; const notChangedValueColumns = aggDetails.changedPath.isActive() ? aggDetails.changedPath.getNotValueColumnsForNode(rowNode, aggDetails.valueColumns) : null; const values2d = this.getValuesNormal(rowNode, changedValueColumns, aggDetails.filteredOnly); const oldValues = rowNode.aggData; changedValueColumns.forEach((valueColumn, index) => { result[valueColumn.getId()] = this.aggregateValues( values2d[index], valueColumn.getAggFunc(), valueColumn, rowNode ); }); if (notChangedValueColumns && oldValues) { notChangedValueColumns.forEach((valueColumn) => { result[valueColumn.getId()] = oldValues[valueColumn.getId()]; }); } return result; } getValuesPivotNonLeaf(rowNode, colId) { return rowNode.childrenAfterFilter.map((childNode) => childNode.aggData[colId]); } getValuesFromMappedSet(mappedSet, keys, valueColumn) { let mapPointer = mappedSet; for (let i = 0; i < keys.length; i++) { const key = keys[i]; mapPointer = mapPointer ? mapPointer[key] : null; } if (!mapPointer) { return []; } return mapPointer.map((rowNode) => this.valueService.getValue(valueColumn, rowNode)); } getValuesNormal(rowNode, valueColumns, filteredOnly) { const values = []; valueColumns.forEach(() => values.push([])); const valueColumnCount = valueColumns.length; const nodeList = filteredOnly ? rowNode.childrenAfterFilter : rowNode.childrenAfterGroup; const rowCount = nodeList.length; for (let i = 0; i < rowCount; i++) { const childNode = nodeList[i]; for (let j = 0; j < valueColumnCount; j++) { const valueColumn = valueColumns[j]; const value = this.valueService.getValue(valueColumn, childNode); values[j].push(value); } } return values; } aggregateValues(values, aggFuncOrString, column, rowNode, pivotResultColumn) { const aggFunc = typeof aggFuncOrString === "string" ? this.aggFuncService.getAggFunc(aggFuncOrString) : aggFuncOrString; if (typeof aggFunc !== "function") { _errorOnce(`unrecognised aggregation function ${aggFuncOrString}`); return null; } const aggFuncAny = aggFunc; const params = this.gos.addGridCommonParams({ values, column, colDef: column ? column.getColDef() : void 0, pivotResultColumn, rowNode, data: rowNode ? rowNode.data : void 0 }); return aggFuncAny(params); } }; // enterprise-modules/row-grouping/src/rowGrouping/autoColService.ts import { AgColumn, BeanStub as BeanStub3, GROUP_AUTO_COLUMN_ID, _isColumnsSortingCoupledToGroup, _isGroupMultiAutoColumn, _mergeDeep, _missing, _warnOnce } from "@ag-grid-community/core"; var AutoColService = class extends BeanStub3 { constructor() { super(...arguments); this.beanName = "autoColService"; } wireBeans(beans) { this.columnModel = beans.columnModel; this.columnNameService = beans.columnNameService; this.columnFactory = beans.columnFactory; } createAutoCols(rowGroupCols) { const autoCols = []; const doingTreeData = this.gos.get("treeData"); let doingMultiAutoColumn = _isGroupMultiAutoColumn(this.gos); if (doingTreeData && doingMultiAutoColumn) { _warnOnce( 'you cannot mix groupDisplayType = "multipleColumns" with treeData, only one column can be used to display groups when doing tree data' ); doingMultiAutoColumn = false; } if (doingMultiAutoColumn) { rowGroupCols.forEach((rowGroupCol, index) => { autoCols.push(this.createOneAutoCol(rowGroupCol, index)); }); } else { autoCols.push(this.createOneAutoCol()); } return autoCols; } updateAutoCols(autoGroupCols, source) { autoGroupCols.forEach((col, index) => this.updateOneAutoCol(col, index, source)); } // rowGroupCol and index are missing if groupDisplayType != "multipleColumns" createOneAutoCol(rowGroupCol, index) { let colId; if (rowGroupCol) { colId = `${GROUP_AUTO_COLUMN_ID}-${rowGroupCol.getId()}`; } else { colId = GROUP_AUTO_COLUMN_ID; } const colDef = this.createAutoColDef(colId, rowGroupCol, index); colDef.colId = colId; const newCol = new AgColumn(colDef, null, colId, true); this.createBean(newCol); return newCol; } /** * Refreshes an auto group col to load changes from defaultColDef or autoGroupColDef */ updateOneAutoCol(colToUpdate, index, source) { const oldColDef = colToUpdate.getColDef(); const underlyingColId = typeof oldColDef.showRowGroup == "string" ? oldColDef.showRowGroup : void 0; const underlyingColumn = underlyingColId != null ? this.columnModel.getColDefCol(underlyingColId) : void 0; const colDef = this.createAutoColDef(colToUpdate.getId(), underlyingColumn ?? void 0, index); colToUpdate.setColDef(colDef, null, source); this.columnFactory.applyColumnState(colToUpdate, colDef, source); } createAutoColDef(colId, underlyingColumn, index) { let res = this.createBaseColDef(underlyingColumn); const autoGroupColumnDef = this.gos.get("autoGroupColumnDef"); _mergeDeep(res, autoGroupColumnDef); res = this.columnFactory.addColumnDefaultAndTypes(res, colId); if (!this.gos.get("treeData")) { const noFieldOrValueGetter = _missing(res.field) && _missing(res.valueGetter) && _missing(res.filterValueGetter) && res.filter !== "agGroupColumnFilter"; if (noFieldOrValueGetter) { res.filter = false; } } if (index && index > 0) { res.headerCheckboxSelection = false; } const isSortingCoupled = _isColumnsSortingCoupledToGroup(this.gos); const hasOwnData = res.valueGetter || res.field != null; if (isSortingCoupled && !hasOwnData) { res.sortIndex = void 0; res.initialSort = void 0; } return res; } createBaseColDef(rowGroupCol) { const userDef = this.gos.get("autoGroupColumnDef"); const localeTextFunc = this.localeService.getLocaleTextFunc(); const res = { headerName: localeTextFunc("group", "Group") }; const userHasProvidedGroupCellRenderer = userDef && (userDef.cellRenderer || userDef.cellRendererSelector); if (!userHasProvidedGroupCellRenderer) { res.cellRenderer = "agGroupCellRenderer"; } if (rowGroupCol) { const colDef = rowGroupCol.getColDef(); Object.assign(res, { headerName: this.columnNameService.getDisplayNameForColumn(rowGroupCol, "header"), headerValueGetter: colDef.headerValueGetter }); if (colDef.cellRenderer) { Object.assign(res, { cellRendererParams: { innerRenderer: colDef.cellRenderer, innerRendererParams: colDef.cellRendererParams } }); } res.showRowGroup = rowGroupCol.getColId(); } else { res.showRowGroup = true; } return res; } }; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/columnDropZoneService.ts import { BeanStub as BeanStub4 } from "@ag-grid-community/core"; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/agGridHeaderDropZones.ts import { Component as Component2, _setAriaRole } from "@ag-grid-community/core"; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/pivotDropZonePanel.ts import { _createIconNoSpan } from "@ag-grid-community/core"; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/baseDropZonePanel.ts import { DragSourceType as DragSourceType2 } from "@ag-grid-community/core"; import { PillDropZonePanel } from "@ag-grid-enterprise/core"; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/dropZoneColumnComp.ts import { Component, DragSourceType, KeyCode, RefPlaceholder, SortIndicatorSelector, _loadTemplate } from "@ag-grid-community/core"; import { PillDragComp, VirtualList } from "@ag-grid-enterprise/core"; var DropZoneColumnComp = class extends PillDragComp { constructor(column, dragSourceDropTarget, ghost, dropZonePurpose, horizontal) { super( dragSourceDropTarget, ghost, horizontal, /* html */ ` <span role="option"> <span data-ref="eDragHandle" class="ag-drag-handle ag-column-drop-cell-drag-handle" role="presentation"></span> <span data-ref="eText" class="ag-column-drop-cell-text" aria-hidden="true"></span> <ag-sort-indicator data-ref="eSortIndicator"></ag-sort-indicator> <span data-ref="eButton" class="ag-column-drop-cell-button" role="presentation"></span> </span> `, [SortIndicatorSelector] ); this.column = column; this.dropZonePurpose = dropZonePurpose; this.eSortIndicator = RefPlaceholder; this.popupShowing = false; } wireBeans(beans) { super.wireBeans(beans); this.popupService = beans.popupService; this.sortController = beans.sortController; this.columnModel = beans.columnModel; this.columnNameService = beans.columnNameService; this.funcColsService = beans.funcColsService; this.aggFuncService = beans.aggFuncService; } postConstruct() { this.displayName = this.columnNameService.getDisplayNameForColumn(this.column, "columnDrop"); super.postConstruct(); this.setupSort(); this.addManagedEventListeners({ sortChanged: () => { this.setupAria(); } }); if (this.isGroupingZone()) { this.addManagedPropertyListener("groupLockGroupColumns", () => { this.refreshRemove(); this.refreshDraggable(); this.setupAria(); }); } } getItem() { return this.column; } getDisplayName() { return this.displayName; } getTooltip() { return this.column.getColDef().headerTooltip; } addAdditionalAriaInstructions(ariaInstructions, translate) { const isSortSuppressed = this.gos.get("rowGroupPanelSuppressSort"); const isFunctionsReadOnly = this.gos.get("functionsReadOnly"); if (this.isAggregationZone() && !isFunctionsReadOnly) { const aggregationMenuAria = translate( "ariaDropZoneColumnValueItemDescription", "Press ENTER to change the aggregation type" ); ariaInstructions.push(aggregationMenuAria); } if (this.isGroupingZone() && this.column.isSortable() && !isSortSuppressed) { const sortProgressAria = translate("ariaDropZoneColumnGroupItemDescription", "Press ENTER to sort"); ariaInstructions.push(sortProgressAria); } super.addAdditionalAriaInstructions(ariaInstructions, translate); } isDraggable() { return this.isReadOnly(); } isRemovable() { return this.isReadOnly(); } isReadOnly() { return !this.isGroupingAndLocked() && !this.gos.get("functionsReadOnly"); } getAriaDisplayName() { const translate = this.localeService.getLocaleTextFunc(); const { name, aggFuncName } = this.getColumnAndAggFuncName(); const aggSeparator = translate("ariaDropZoneColumnComponentAggFuncSeparator", " of "); const sortDirection = { asc: translate("ariaDropZoneColumnComponentSortAscending", "ascending"), desc: translate("ariaDropZoneColumnComponentSortDescending", "descending") }; const columnSort = this.column.getSort(); const isSortSuppressed = this.gos.get("rowGroupPanelSuppressSort"); return [ aggFuncName && `${aggFuncName}${aggSeparator}`, name, this.isGroupingZone() && !isSortSuppressed && columnSort && `, ${sortDirection[columnSort]}` ].filter((part) => !!part).join(""); } getColumnAndAggFuncName() { const name = this.displayName; let aggFuncName = ""; if (this.isAggregationZone()) { const aggFunc = this.column.getAggFunc(); const aggFuncString = typeof aggFunc === "string" ? aggFunc : "agg"; const localeTextFunc = this.localeService.getLocaleTextFunc(); aggFuncName = localeTextFunc(aggFuncString, aggFuncString); } return { name, aggFuncName }; } setupSort() { const canSort = this.column.isSortable(); const isGroupingZone = this.isGroupingZone(); if (!canSort || !isGroupingZone) { return; } if (!this.gos.get("rowGroupPanelSuppressSort")) { this.eSortIndicator.setupSort(this.column, true); const performSort = (event) => { event.preventDefault(); const sortUsingCtrl = this.gos.get("multiSortKey") === "ctrl"; const multiSort = sortUsingCtrl ? event.ctrlKey || event.metaKey : event.shiftKey; this.sortController.progressSort(this.column, multiSort, "uiColumnSorted"); }; this.addGuiEventListener("click", performSort); this.addGuiEventListener("keydown", (e) => { const isEnter = e.key === KeyCode.ENTER; if (isEnter && this.isGroupingZone()) { performSort(e); } }); } } getDefaultIconName() { return "hide"; } createGetDragItem() { const { column } = this; return () => { const visibleState = {}; visibleState[column.getId()] = column.isVisible(); return { columns: [column], visibleState }; }; } setupComponents() { super.setupComponents(); if (this.isAggregationZone() && !this.gos.get("functionsReadOnly")) { this.addGuiEventListener("click", this.onShowAggFuncSelection.bind(this)); } } onKeyDown(e) { super.onKeyDown(e); const isEnter = e.key === KeyCode.ENTER; if (isEnter && this.isAggregationZone() && !this.gos.get("functionsReadOnly")) { e.preventDefault(); this.onShowAggFuncSelection(); } } getDisplayValue() { const { name, aggFuncName } = this.getColumnAndAggFuncName(); return this.isAggregationZone() ? `${aggFuncName}(${name})` : name; } onShowAggFuncSelection() { if (this.popupShowing) { return; } this.popupShowing = true; const virtualList = new VirtualList({ cssIdentifier: "select-agg-func" }); const rows = this.aggFuncService.getFuncNames(this.column); const eGui = this.getGui(); const virtualListGui = virtualList.getGui(); virtualList.setModel({ getRow: function(index) { return rows[index]; }, getRowCount: function() { return rows.length; } }); this.createBean(virtualList); const ePopup = _loadTemplate( /* html*/ `<div class="ag-select-agg-func-popup"></div>` ); ePopup.style.top = "0px"; ePopup.style.left = "0px"; ePopup.appendChild(virtualListGui); ePopup.style.width = `${eGui.clientWidth}px`; const [focusoutListener] = this.addManagedElementListeners(ePopup, { focusout: (e) => { if (!ePopup.contains(e.relatedTarget) && addPopupRes) { addPopupRes.hideFunc(); } } }); const popupHiddenFunc = (callbackEvent) => { this.destroyBean(virtualList); this.popupShowing = false; if (callbackEvent?.key === "Escape") { eGui.focus(); } if (focusoutListener) { focusoutListener(); } }; const translate = this.localeService.getLocaleTextFunc(); const addPopupRes = this.popupService.addPopup({ modal: true, eChild: ePopup, closeOnEsc: true, closedCallback: popupHiddenFunc, ariaLabel: translate("ariaLabelAggregationFunction", "Aggregation Function") }); if (addPopupRes) { virtualList.setComponentCreator(this.createAggSelect.bind(this, addPopupRes.hideFunc)); } virtualList.addGuiEventListener("keydown", (e) => { if (e.key === KeyCode.ENTER || e.key === KeyCode.SPACE) { const row = virtualList.getLastFocusedRow(); if (row == null) { return; } const comp = virtualList.getComponentAt(row); if (comp) { comp.selectItem(); } } }); this.popupService.positionPopupByComponent({ type: "aggFuncSelect", eventSource: eGui, ePopup, keepWithinBounds: true, column: this.column, position: "under" }); virtualList.refresh(); let rowToFocus = rows.findIndex((r) => r === this.column.getAggFunc()); if (rowToFocus === -1) { rowToFocus = 0; } virtualList.focusRow(rowToFocus); } createAggSelect(hidePopup, value) { const itemSelected = () => { hidePopup(); this.getGui().focus(); this.funcColsService.setColumnAggFunc(this.column, value, "toolPanelDragAndDrop"); }; const localeTextFunc = this.localeService.getLocaleTextFunc(); const aggFuncString = value.toString(); const aggFuncStringTranslated = localeTextFunc(aggFuncString, aggFuncString); const comp = new AggItemComp(itemSelected, aggFuncStringTranslated); return comp; } isGroupingAndLocked() { return this.isGroupingZone() && this.columnModel.isColGroupLocked(this.column); } isAggregationZone() { return this.dropZonePurpose === "aggregation"; } isGroupingZone() { return this.dropZonePurpose === "rowGroup"; } getDragSourceType() { return DragSourceType.ToolPanel; } destroy() { super.destroy(); this.column = null; } }; var AggItemComp = class extends Component { constructor(itemSelected, value) { super( /* html */ `<div class="ag-select-agg-func-item"/>` ); this.selectItem = itemSelected; this.getGui().innerText = value; this.addGuiEventListener("click", this.selectItem); } }; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/baseDropZonePanel.ts var BaseDropZonePanel = class extends PillDropZonePanel { constructor(horizontal, dropZonePurpose) { super(horizontal); this.dropZonePurpose = dropZonePurpose; } wireBeans(beans) { super.wireBeans(beans); this.columnModel = beans.columnModel; this.funcColsService = beans.funcColsService; } init(params) { super.init(params); this.addManagedEventListeners({ newColumnsLoaded: this.refreshGui.bind(this) }); this.addManagedPropertyListeners( ["functionsReadOnly", "rowGroupPanelSuppressSort", "groupLockGroupColumns"], this.refreshGui.bind(this) ); } getItems(dragItem) { return dragItem.columns ?? []; } isInterestedIn(type) { return type === DragSourceType2.HeaderCell || type === DragSourceType2.ToolPanel; } minimumAllowedNewInsertIndex() { const numberOfLockedCols = this.gos.get("groupLockGroupColumns"); const numberOfGroupCols = this.funcColsService.getRowGroupColumns().length; if (numberOfLockedCols === -1) { return numberOfGroupCols; } return Math.min(numberOfLockedCols, numberOfGroupCols); } showOrHideColumnOnExit(draggingEvent) { return this.isRowGroupPanel() && !this.gos.get("suppressRowGroupHidesColumns") && !draggingEvent.fromNudge; } handleDragEnterEnd(draggingEvent) { const hideColumnOnExit = this.showOrHideColumnOnExit(draggingEvent); if (hideColumnOnExit) { const dragItem = draggingEvent.dragSource.getDragItem(); const columns = dragItem.columns; this.setColumnsVisible(columns, false, "uiColumnDragged"); } } handleDragLeaveEnd(draggingEvent) { const showColumnOnExit = this.showOrHideColumnOnExit(draggingEvent); if (showColumnOnExit) { const dragItem = draggingEvent.dragSource.getDragItem(); this.setColumnsVisible(dragItem.columns, true, "uiColumnDragged"); } } setColumnsVisible(columns, visible, source) { if (columns) { const allowedCols = columns.filter((c) => !c.getColDef().lockVisible); this.columnModel.setColsVisible(allowedCols, visible, source); } } isRowGroupPanel() { return this.dropZonePurpose === "rowGroup"; } createPillComponent(column, dropTarget, ghost, horizontal) { return new DropZoneColumnComp(column, dropTarget, ghost, this.dropZonePurpose, horizontal); } }; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/pivotDropZonePanel.ts var PivotDropZonePanel = class extends BaseDropZonePanel { constructor(horizontal) { super(horizontal, "pivot"); } postConstruct() { const localeTextFunc = this.localeService.getLocaleTextFunc(); const emptyMessage = localeTextFunc("pivotColumnsEmptyMessage", "Drag here to set column labels"); const title = localeTextFunc("pivots", "Column Labels"); super.init({ icon: _createIconNoSpan("pivotPanel", this.gos, null), emptyMessage, title }); this.addManagedEventListeners({ newColumnsLoaded: this.refresh.bind(this), columnPivotChanged: this.refresh.bind(this), columnPivotModeChanged: this.checkVisibility.bind(this) }); this.refresh(); } getAriaLabel() { const translate = this.localeService.getLocaleTextFunc(); const label = translate("ariaPivotDropZonePanelLabel", "Column Labels"); return label; } getTooltipParams() { const res = super.getTooltipParams(); res.location = "pivotColumnsList"; return res; } refresh() { this.checkVisibility(); this.refreshGui(); } checkVisibility() { const pivotMode = this.columnModel.isPivotMode(); if (this.isHorizontal()) { switch (this.gos.get("pivotPanelShow")) { case "always": this.setDisplayed(pivotMode); break; case "onlyWhenPivoting": { const pivotActive = this.columnModel.isPivotActive(); this.setDisplayed(pivotMode && pivotActive); break; } default: this.setDisplayed(false); break; } } else { this.setDisplayed(pivotMode); } } isItemDroppable(column, draggingEvent) { if (this.gos.get("functionsReadOnly") || !column.isPrimary()) { return false; } return column.isAllowPivot() && (!column.isPivotActive() || this.isSourceEventFromTarget(draggingEvent)); } updateItems(columns) { this.funcColsService.setPivotColumns(columns, "toolPanelUi"); } getIconName() { return this.isPotentialDndItems() ? "pivot" : "notAllowed"; } getExistingItems() { return this.funcColsService.getPivotColumns(); } }; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/rowGroupDropZonePanel.ts import { _createIconNoSpan as _createIconNoSpan2 } from "@ag-grid-community/core"; var RowGroupDropZonePanel = class extends BaseDropZonePanel { constructor(horizontal) { super(horizontal, "rowGroup"); } postConstruct() { const localeTextFunc = this.localeService.getLocaleTextFunc(); const emptyMessage = localeTextFunc("rowGroupColumnsEmptyMessage", "Drag here to set row groups"); const title = localeTextFunc("groups", "Row Groups"); super.init({ icon: _createIconNoSpan2("rowGroupPanel", this.gos, null), emptyMessage, title }); this.addManagedEventListeners({ columnRowGroupChanged: this.refreshGui.bind(this) }); } getAriaLabel() { const translate = this.localeService.getLocaleTextFunc(); const label = translate("ariaRowGroupDropZonePanelLabel", "Row Groups"); return label; } getTooltipParams() { const res = super.getTooltipParams(); res.location = "rowGroupColumnsList"; return res; } isItemDroppable(column, draggingEvent) { if (this.gos.get("functionsReadOnly") || !column.isPrimary()) { return false; } return column.isAllowRowGroup() && (!column.isRowGroupActive() || this.isSourceEventFromTarget(draggingEvent)); } updateItems(columns) { this.funcColsService.setRowGroupColumns(columns, "toolPanelUi"); } getIconName() { return this.isPotentialDndItems() ? "group" : "notAllowed"; } getExistingItems() { return this.funcColsService.getRowGroupColumns(); } }; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/agGridHeaderDropZones.ts var AgGridHeaderDropZones = class extends Component2 { wireBeans(beans) { this.columnModel = beans.columnModel; this.funcColsService = beans.funcColsService; } constructor() { super(); } postConstruct() { this.setGui(this.createNorthPanel()); const onRowGroupChanged = this.onRowGroupChanged.bind(this); this.addManagedEventListeners({ columnRowGroupChanged: onRowGroupChanged, newColumnsLoaded: onRowGroupChanged }); this.addManagedPropertyListener("rowGroupPanelShow", onRowGroupChanged); this.addManagedPropertyListener("pivotPanelShow", () => this.onPivotPanelShow()); this.onRowGroupChanged(); } createNorthPanel() { const topPanelGui = document.createElement("div"); topPanelGui.classList.add("ag-column-drop-wrapper"); _setAriaRole(topPanelGui, "presentation"); this.rowGroupComp = new RowGroupDropZonePanel(true); this.createManagedBean(this.rowGroupComp); this.pivotComp = new PivotDropZonePanel(true); this.createManagedBean(this.pivotComp); topPanelGui.appendChild(this.rowGroupComp.getGui()); topPanelGui.appendChild(this.pivotComp.getGui()); const listener = this.onDropPanelVisible.bind(this); this.addManagedListeners(this.rowGroupComp, { displayChanged: listener }); this.addManagedListeners(this.pivotComp, { displayChanged: listener }); this.onDropPanelVisible(); return topPanelGui; } onDropPanelVisible() { const bothDisplayed = this.rowGroupComp.isDisplayed() && this.pivotComp.isDisplayed(); const classStr = "ag-column-drop-horizontal-half-width"; this.rowGroupComp.addOrRemoveCssClass(classStr, bothDisplayed); this.pivotComp.addOrRemoveCssClass(classStr, bothDisplayed); } onRowGroupChanged() { if (!this.rowGroupComp) { return; } const rowGroupPanelShow = this.gos.get("rowGroupPanelShow"); if (rowGroupPanelShow === "always") { this.rowGroupComp.setDisplayed(true); } else if (rowGroupPanelShow === "onlyWhenGrouping") { const grouping = !this.funcColsService.isRowGroupEmpty(); this.rowGroupComp.setDisplayed(grouping); } else { this.rowGroupComp.setDisplayed(false); } } onPivotPanelShow() { if (!this.pivotComp) { return; } const pivotPanelShow = this.gos.get("pivotPanelShow"); if (pivotPanelShow === "always") { this.pivotComp.setDisplayed(true); } else if (pivotPanelShow === "onlyWhenPivoting") { const pivoting = this.columnModel.isPivotActive(); this.pivotComp.setDisplayed(pivoting); } else { this.pivotComp.setDisplayed(false); } } }; var AgGridHeaderDropZonesSelector = { selector: "AG-GRID-HEADER-DROP-ZONES", component: AgGridHeaderDropZones }; // enterprise-modules/row-grouping/src/rowGrouping/columnDropZones/columnDropZoneService.ts var ColumnDropZoneService = class extends BeanStub4 { constructor() { super(...arguments); this.beanName = "columnDropZonesService"; } getDropZoneSelector() { return AgGridHeaderDropZonesSelector; } }; // enterprise-modules/row-grouping/src/rowGrouping/filterAggregatesStage.ts import { BeanStub as BeanStub5, _getGroupAggFiltering as _getGroupAggFiltering2 } from "@ag-grid-community/core"; var FilterAggregatesStage = class extends BeanStub5 { constructor() { super(...arguments); this.beanName = "filterAggregatesStage"; } wireBeans(beans) { this.filterManager = beans.filterManager; this.columnModel = beans.columnModel; } execute(params) { const isPivotMode2 = this.columnModel.isPivotMode(); const isAggFilterActive = this.filterManager?.isAggregateFilterPresent() || this.filterManager?.isAggregateQuickFilterPresent(); const defaultPrimaryColumnPredicate = (params2) => !params2.node.group; const defaultSecondaryColumnPredicate = (params2) => params2.node.leafGroup; const applyFilterToNode = _getGroupAggFiltering2(this.gos) || (isPivotMode2 ? defaultSecondaryColumnPredicate : defaultPrimaryColumnPredicate); const { changedPath } = params; const preserveChildren = (node, recursive = false) => { if (node.childrenAfterFilter) { node.childrenAfterAggFilter = node.childrenAfterFilter; if (recursive) { node.childrenAfterAggFilter.forEach((child) => preserveChildren(child, recursive)); } this.setAllChildrenCount(node); } if (node.sibling) { node.sibling.childrenAfterAggFilter = node.childrenAfterAggFilter; } }; const filterChildren = (node) => { node.childrenAfterAggFilter = node.childrenAfterFilter?.filter((child) => { const shouldFilterRow = applyFilterToNode({ node: child }); if (shouldFilterRow) { const doesNodePassFilter = this.filterManager.doesRowPassAggregateFilters({ rowNode: child }); if (doesNodePassFilter) { preserveChildren(child, true); return true; } } const hasChildPassed = child.childrenAfterAggFilter?.length; return hasChildPassed; }) || null; this.setAllChildrenCount(node); if (node.sibling) { node.sibling.childrenAfterAggFilter = node.childrenAfterAggFilter; } }; changedPath.forEachChangedNodeDepthFirst(isAggFilterActive ? filterChildren : preserveChildren, true); } /** for tree data, we include all children, groups and leafs */ setAllChildrenCountTreeData(rowNode) { const childrenAfterAggFilter = rowNode.childrenAfterAggFilter; let allChildrenCount = 0; if (childrenAfterAggFilter) { const length = childrenAfterAggFilter.length; allChildrenCount = length; for (let i = 0; i < length; ++i) { allChildrenCount += childrenAfterAggFilter[i].allChildrenCount ?? 0; } } rowNode.setAllChildrenCount( // Maintain the historical behaviour: // - allChildrenCount is 0 in the root if there are no children // - allChildrenCount is null in any non-root row if there are no children allChildrenCount === 0 && rowNode.level >= 0 ? null : allChildrenCount ); } /* for grid data, we only count the leafs */ setAllChildrenCountGridGrouping(rowNode) { let allChildrenCount = 0; rowNode.childrenAfterAggFilter.forEach((child) => { if (child.group) { allChildrenCount += child.allChildrenCount; } else { allChildrenCount++; } }); rowNode.setAllChildrenCount(allChildrenCount); } setAllChildrenCount(rowNode) { if (!rowNode.hasChildren()) { rowNode.setAllChildrenCount(null); return; } if (this.gos.get("treeData")) { this.setAllChildrenCountTreeData(rowNode); } else { this.setAllChildrenCountGridGrouping(rowNode); } } }; // enterprise-modules/row-grouping/src/rowGrouping/groupFilter/groupFilter.ts import { AgPromise, AgSelect, FilterWrapperComp, RefPlaceholder as RefPlaceholder2, TabGuardComp, _clearElement, _loadTemplate as _loadTemplate2, _setDisplayed, _warnOnce as _warnOnce2 } from "@ag-grid-community/core"; var GroupFilter = class extends TabGuardComp { constructor() { super( /* html */ ` <div class="ag-group-filter"> <div data-ref="eGroupField"></div> <div data-ref="eUnderlyingFilter"></div> </div> ` ); this.eGroupField = RefPlaceholder2; this.eUnderlyingFilter = RefPlaceholder2; } wireBeans(beans) { this.filterManager = beans.filterManager; this.columnNameService = beans.columnNameService; this.funcColsService = beans.funcColsService; } postConstruct() { this.initialiseTabGuard({}); } init(params) { return this.updateParams(params).then(() => { this.addManagedEventListeners({ columnRowGroupChanged: () => this.onColumnRowGroupChanged(), filterDestroyed: (event) => this.onFilterDestroyed(event) }); }); } refresh(params) { this.updateParams(params); return true; } updateParams(params) { this.params = params; this.validateParams(); return this.updateGroups(); } validateParams() { const { colDef } = this.params; if (colDef.field) { _warnOnce2( 'Group Column Filter does not work with the colDef property "field". This property will be ignored.' ); } if (colDef.filterValueGetter) { _warnOnce2( 'Group Column Filter does not work with the colDef property "filterValueGetter". This property will be ignored.' ); } if (colDef.filterParams) { _warnOnce2( 'Group Column Filter does not work with the colDef property "filterParams". This property will be ignored.' ); } } updateGroups() { const sourceColumns = this.updateGroupField(); return this.getUnderlyingFilters(sourceColumns); } getSourceColumns() { this.groupColumn = this.params.column; if (this.gos.get("treeData")) { _warnOnce2( "Group Column Filter does not work with Tree Data enabled. Please disable Tree Data, or use a different filter." ); return []; } const sourceColumns = this.funcColsService.getSourceColumnsForGroupColumn(this.groupColumn); if (!sourceColumns) { _warnOnce2("Group Column Filter only works on group columns. Please use a different filter."); return []; } return sourceColumns; } updateGroupField() { _clearElement(this.eGroupField); if (this.eGroupFieldSelect) { this.destroyBean(this.eGroupFieldSelect); } const allSourceColumns = this.getSourceColumns(); const sourceColumns = allSourceColumns.filter((sourceColumn) => sourceColumn.isFilterAllowed()); if (!sourceColumns.length) { this.selectedColumn = void 0; _setDisplayed(this.eGroupField, false); return null; } if (allSourceColumns.length === 1) { this.selectedColumn = sourceColumns[0]; _setDisplayed(this.eGroupField, false); } else { if (!this.selectedColumn || !sourceColumns.some((column) => column.getId() === this.selectedColumn.getId())) { this.selectedColumn = sourceColumns[0]; } this.createGroupFieldSelectElement(sourceColumns); this.eGroupField.appendChild(this.eGroupFieldSelect.getGui()); this.eGroupField.appendChild(_loadTemplate2( /* html */ `<div class="ag-filter-separator"></div>` )); _setDisplayed(this.eGroupField, true); } return sourceColumns; } createGroupFieldSelectElement(sourceColumns) { this.eGroupFieldSelect = this.createManagedBean(new AgSelect()); const localeTextFunc = this.localeService.getLocaleTextFunc(); this.eGroupFieldSelect.setLabel(localeTextFunc("groupFilterSelect", "Select field:")); this.eGroupFieldSelect.setLabelAlignment("top"); this.eGroupFieldSelect.addOptions( sourceColumns.map((sourceColumn) => ({ value: sourceColumn.getId(), text: this.columnNameService.getDisplayNameForColumn(sourceColumn, "groupFilter", false) ?? void 0 })) ); this.eGroupFieldSelect.setValue(this.selectedColumn.getId()); this.eGroupFieldSelect.onValueChange((newValue) => this.updateSelectedColumn(newValue)); this.eGroupFieldSelect.addCssClass("ag-group-filter-field-select-wrapper"); if (sourceColumns.length === 1) { this.eGroupFieldSelect.setDisabled(true); } } getUnderlyingFilters(sourceColumns) { if (!sourceColumns) { this.filterColumnPairs = void 0; this.selectedFilter = void 0; this.groupColumn.setFilterActive(false, "columnRowGroupChanged"); return AgPromise.resolve(); } const filterPromises = []; const filterColumnPairs = []; sourceColumns.forEach((column) => { const filterWrapper = this.filterManager.getOrCreateFilterWrapper(column); if (filterWrapper?.filterPromise) { filterPromises.push( filterWrapper.filterPromise.then((filter) => { if (filter) { filterColumnPairs.push({ filter, column }); } if (column.getId() === this.selectedColumn.getId()) { this.selectedFilter = filter ?? void 0; } return filter; }) ); } }); return AgPromise.all(filterPromises).then(() => { this.filterColumnPairs = filterColumnPairs; this.groupColumn.setFilterActive(this.isFilterActive(), "columnRowGroupChanged"); }); } addUnderlyingFilterElement() { _clearElement(this.eUnderlyingFilter); if (!this.selectedColumn) { return AgPromise.resolve(); } const comp = this.createManagedBean(new FilterWrapperComp(this.selectedColumn, "COLUMN_MENU")); this.filterWrapperComp = comp; if (!comp.hasFilter()) { return AgPromise.resolve(); } this.eUnderlyingFilter.appendChild(comp.getGui()); return comp.getFilter()?.then(() => { comp.afterGuiAttached?.(this.afterGuiAttachedParams); if (!this.afterGuiAttachedParams?.suppressFocus && this.eGroupFieldSelect && !this.eGroupFieldSelect.isDisabled()) { this.eGroupFieldSelect.getFocusableElement().focus(); } }) ?? AgPromise.resolve(); } updateSelectedColumn(columnId) { if (!columnId) { return; } this.filterWrapperComp?.afterGuiDetached(); this.destroyBean(this.filterWrapperComp); const selectedFilterColumnPair = this.getFilterColumnPair(columnId); this.selectedColumn = selectedFilterColumnPair?.column; this.selectedFilter = selectedFilterColumnPair?.filter; this.dispatchLocalEvent({ type: "selectedColumnChanged" }); this.addUnderlyingFilterElement(); } isFilterActive() { return !!this.filterColumnPairs?.some(({ filter }) => filter.isFilterActive()); } doesFilterPass() { return true; } getModel() { return null; } setModel() { return AgPromise.resolve(); } afterGuiAttached(params) { this.afterGuiAttachedParams = params; this.addUnderlyingFilterElement(); } afterGuiDetached() { _clearElement(this.eUnderlyingFilter); this.selectedFilter?.afterGuiDetached?.(); } onColumnRowGroupChanged() { this.updateGroups().then(() => { this.dispatchLocalEvent({ type: "columnRowGroupChanged" }); }); } onFilterDestroyed({ column: eventColumn, source }) { if (source === "gridDestroyed") { return; } const colId = eventColumn.getColId(); if (this.filterColumnPairs?.some(({ column }) => column.getColId() === colId)) { setTimeout(() => { if (this.isAlive())