UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

174 lines (173 loc) 8.04 kB
import { ROW_SUMMARY_ROW_ID, WEIGHTED_AVERAGE_AGGREGATED_FUNCTION, } from '../../AdaptableState/Common/RowSummary'; import { RowSummarySet } from '../../Redux/ActionsReducers/InternalRedux'; import * as ModuleConstants from '../../Utilities/Constants/ModuleConstants'; import Helper from '../Helpers/Helper'; import { AggregatedScalarLiveValue } from './AggregatedScalarLiveValue'; import { isObjectEmpty } from '../Extensions/ObjectExtensions'; import throttle from 'lodash/throttle'; import isEqual from 'lodash/isEqual'; /** * The logic is extracted here to make it easier to follow */ export class RowSummaryService { constructor(api) { this.api = api; this.cachedCellSummary = new Map(); this._throttleAcumulatedColumnsThatChanged = new Set(); /** * * @param colId optional to evaluate only one column */ this._throttledEvaluateRowSummary = throttle(this.evaluateRowSummary, 300); } onAdaptableReady() { this.rowSummariesSubscriptions(); } rowSummariesSubscriptions() { // return; if (this.api.isDestroyed()) { return; } // Currently not available for serverside model if (!this.api.layoutApi.internalApi.getLayoutSupportedFeatures().RowSummaries) { return; } this.throttledEvaluateRowSummary(); this.api.eventApi.on('AdaptableStateReloaded', () => { this.throttledEvaluateRowSummary(); }); this.api.internalApi .getDataService() .on('RowDataChanged', (rowDataChangedInfo) => { this.throttledEvaluateRowSummary(); }); this.api.eventApi.on('CellChanged', (event) => { const columnId = event.cellDataChange.column.columnId; this.throttledEvaluateRowSummary({ columnIds: [columnId], }); }); this.api.eventApi.on('LayoutChanged', (event) => { // exclude filter events, those are handled in another event if (event.actionName.includes('FILTER')) { return; } setTimeout(() => { // the timeout is added so the grid has time to repond to the layout changed this.throttledEvaluateRowSummary(); }, 16); }); const adaptable = this.api.internalApi.getAdaptableInstance(); adaptable._on('AdapTableFiltersApplied', () => { // we need to use this instead of layout changed so the rows have time to update this.throttledEvaluateRowSummary(); }); adaptable._on('FirstDataRendered', () => { this.throttledEvaluateRowSummary(); }); } throttledEvaluateRowSummary(reason) { if (reason) { reason.columnIds.forEach((col) => this._throttleAcumulatedColumnsThatChanged.add(col)); } this._throttledEvaluateRowSummary(reason); } evaluateRowSummary(reason) { if (this._throttleAcumulatedColumnsThatChanged.size > 0) { const columnIds = Array.from(this._throttleAcumulatedColumnsThatChanged.values()); reason = { columnIds, }; this._throttleAcumulatedColumnsThatChanged.clear(); } if (this.api.isDestroyed() || this.api.layoutApi.isCurrentLayoutPivot()) { return; } const currentLayout = this.api.layoutApi.getCurrentLayout(); let previousLayout = this.previousLayout; // it is added here to be sure it is saved this.previousLayout = currentLayout; /** * If the previous & current layout does not have row summaries, it is safe to exit */ if (isObjectEmpty(currentLayout?.RowSummaries) && isObjectEmpty(previousLayout?.RowSummaries)) { return; } const rowSummaries = currentLayout.RowSummaries ?? []; const aggColsMap = (currentLayout.TableAggregationColumns || []).reduce((acc, { ColumnId, AggFunc }) => { acc[ColumnId] = AggFunc; return acc; }, {}); const rowSummariesResults = rowSummaries .filter((rowSummary) => !rowSummary.IsSuspended) .map((rowSummary, index) => { const { ColumnsMap, Position, // it defaults to true IncludeOnlyFilteredRows = true, } = rowSummary; return { Position, RowData: Object.entries(ColumnsMap ?? {}).reduce((acc, [columnId, expression]) => { if (columnId === 'Uuid' || columnId === 'Source' || columnId === 'AdaptableVersion') { return acc; } const key = `${columnId}-${expression}-IncludeOnlyFilteredRows=${IncludeOnlyFilteredRows ? 'filtered' : 'all'}`; let expressionLiveValue = this.cachedCellSummary.get(key); if (expressionLiveValue) { if (!reason) { // refresh all of them expressionLiveValue.refresh(); } else if ('columnIds' in reason && reason.columnIds.includes(columnId)) { expressionLiveValue.refresh(); } } if (!expressionLiveValue) { try { let aggregatedScalarExpression = `${expression}([${columnId}])`; if (aggregatedScalarExpression.includes(WEIGHTED_AVERAGE_AGGREGATED_FUNCTION) && aggColsMap[columnId] && typeof aggColsMap[columnId] === 'object') { const weight = aggColsMap[columnId].weightedColumnId; if (weight) { aggregatedScalarExpression = `AVG([${columnId}], WEIGHT([${weight}]))`; } } expressionLiveValue = new AggregatedScalarLiveValue({ aggregatedScalarExpression, }, ModuleConstants.LayoutModuleId, this.api, () => { if (rowSummary.IncludeOnlyFilteredRows ?? true) { return this.api.gridApi.getVisibleRowNodes(); } else { return this.api.gridApi.getAllRowNodes(); } }); } catch (e) { this.api.logError('Error evaluating row summary', e); } this.cachedCellSummary.set(key, expressionLiveValue); } let value = null; if (expressionLiveValue) { value = expressionLiveValue.getGlobalAggregatedValue(); if (typeof value === 'number' && !isNaN(value)) { value = Helper.roundNumber(value, 2); } } const column = this.api.columnApi.getColumnWithColumnId(columnId); const fieldName = column?.field ?? column?.columnId ?? columnId; acc = this.api.internalApi.setValueUsingField(acc, fieldName, value); return acc; }, { [ROW_SUMMARY_ROW_ID]: `row-summary-${index}`, }), }; }); if (this.previousRowSummaries && isEqual(rowSummariesResults, this.previousRowSummaries)) { return; } this.api.internalApi.dispatchReduxAction(RowSummarySet(rowSummariesResults)); this.previousRowSummaries = rowSummariesResults; } }