@eclipse-scout/core
Version:
Eclipse Scout runtime
236 lines (197 loc) • 8.07 kB
text/typescript
/*
* Copyright (c) 2010, 2024 BSI Business Systems Integration AG
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
import {arrays, Device, EventHandler, graphics, icons, InitModelOf, TableColumnMovedEvent, TableControl, TableRow, tooltips} from '../../index';
import $ from 'jquery';
export class AggregateTableControl extends TableControl {
/**
* List of aggregated values per {@link Table#visibleColumns visible column}. If a column has no aggregated value,
* the corresponding entry is empty. This array needs to be updated whenever the list of visible columns changes.
*
* The additional "selection" property indicates whether the values are based on the current table selection (true)
* or all rows (false, default).
*/
aggregateRow: any[] & { selection?: boolean };
protected _tableDataScrollHandler: () => void;
protected _tableColumnResizedHandler: () => void;
protected _tableColumnMovedHandler: EventHandler<TableColumnMovedEvent>;
protected _tableColumnStructureChangedHandler: () => void;
protected _tableChangedHandler: () => void;
protected _aggregationFunctionChangedHandler: () => void;
constructor() {
super();
this._tableDataScrollHandler = this._onTableDataScroll.bind(this);
this._tableColumnResizedHandler = this._onTableColumnResized.bind(this);
this._tableColumnMovedHandler = this._onTableColumnMoved.bind(this);
this._tableColumnStructureChangedHandler = this._onTableColumnStructureChanged.bind(this);
this._tableChangedHandler = this._onTableChanged.bind(this);
this._aggregationFunctionChangedHandler = this._onAggregationFunctionChanged.bind(this);
this.animateDuration = AggregateTableControl.CONTAINER_ANIMATE_DURATION;
this.aggregateRow = [];
this.cssClass = 'aggregate';
this.height = 0;
this.iconId = icons.SUM;
this.tooltipText = '${textKey:ui.Total}';
this.resizerVisible = false;
}
static override CONTAINER_ANIMATE_DURATION = 200;
protected override _init(model: InitModelOf<this>) {
super._init(model);
this.table.on('columnStructureChanged', this._tableColumnStructureChangedHandler);
this.table.on('aggregationFunctionChanged', this._aggregationFunctionChangedHandler);
}
protected override _destroy() {
super._destroy();
this.table.off('columnStructureChanged', this._tableColumnStructureChangedHandler);
this.table.off('aggregationFunctionChanged', this._aggregationFunctionChangedHandler);
}
protected override _render() {
super._render();
this._updateEnabledAndSelectedState();
this.height = this.table.rowHeight + graphics.insets(this.table.footer.$controlContainer).vertical();
}
protected override _renderContent($parent: JQuery) {
this.$contentContainer = $parent.appendDiv('table-aggregate');
this._aggregate();
this._renderAggregate();
this._reconcileScrollPos();
this.table.$data.on('scroll', this._tableDataScrollHandler);
this.table.on('columnResized', this._tableColumnResizedHandler);
this.table.on('columnMoved', this._tableColumnMovedHandler);
this.table.on('rowsSelected rowsInserted rowsUpdated rowsDeleted filter group allRowsDeleted', this._tableChangedHandler);
}
protected override _removeContent() {
this.$contentContainer.remove();
this.table.$data.off('scroll', this._tableDataScrollHandler);
this.table.off('columnResized', this._tableColumnResizedHandler);
this.table.off('columnMoved', this._tableColumnMovedHandler);
this.table.off('rowsSelected rowsInserted rowsUpdated rowsDeleted filter group allRowsDeleted', this._tableChangedHandler);
}
protected _renderAggregate() {
let aggregateCells: JQuery[] = [];
this.table.visibleColumns().forEach((column, c) => {
let aggregateValue, cell, $cell;
aggregateValue = this.aggregateRow[c];
// Aggregation functions are not available if column is grouped -> do not show aggregated value
let isEmpty = aggregateValue === undefined || aggregateValue === null || column.grouped;
if (isEmpty) {
cell = column.createAggrEmptyCell();
} else {
cell = column.createAggrValueCell(aggregateValue);
}
$cell = $(column.buildCell(cell, {}));
if (!isEmpty) {
aggregateCells.push($cell);
}
// install tooltips
this._installCellTooltip($cell);
// If aggregation is based on the selection and not on all rows -> mark it
if (this.aggregateRow.selection) {
$cell.addClass('selection');
}
$cell.appendTo(this.$contentContainer);
});
if (this.aggregateRow.selection) {
this.$contentContainer.addClass('selection');
}
aggregateCells.forEach($c => this.table._resizeAggregateCell($c));
}
protected _rerenderAggregate() {
this.$contentContainer.empty();
this._renderAggregate();
this._reconcileScrollPos();
}
protected _installCellTooltip($cell: JQuery) {
tooltips.install($cell, {
parent: this,
text: this.table._cellTooltipText.bind(this.table),
htmlEnabled: true,
arrowPosition: 50,
arrowPositionUnit: '%',
nativeTooltip: !Device.get().isCustomEllipsisTooltipPossible()
});
}
protected _aggregate() {
let rows: TableRow[],
aggregateRow: any[] & { selection?: boolean } = [],
selectedRows = this.table.selectedRows;
if (selectedRows.length > 1) {
rows = selectedRows;
aggregateRow.selection = true;
} else {
rows = this.table.filteredRows();
}
this.table._forEachVisibleColumn('aggrStart', aggregateRow);
rows.forEach(row => this.table._forEachVisibleColumn('aggrStep', aggregateRow, row));
this.table._forEachVisibleColumn('aggrFinish', aggregateRow);
this.aggregateRow = aggregateRow;
if (this.contentRendered && this.selected) {
this._rerenderAggregate();
}
}
protected _reconcileScrollPos() {
// When scrolling horizontally scroll aggregate content as well
let scrollLeft = this.table.$data.scrollLeft();
this.$contentContainer.scrollLeft(scrollLeft);
}
protected _updateEnabledAndSelectedState(aggregationFunctionChanged?: boolean) {
if (!this.initialized) {
// During init the columns are not resolved yet -> containsAggregatedNumberColumn won't return a correct value
return;
}
let enabled = this.table.containsAggregatedNumberColumn();
// Select control if enabled, aggregation function changed and table is not grouped
if (enabled) {
if (aggregationFunctionChanged && !this.table.isGrouped()) {
this.setSelected(true);
}
} else if (this.selected) {
// Make sure a disabled control is not selected
this.setSelected(false);
}
this.setEnabled(enabled);
}
protected override _setEnabled(enabled: boolean) {
super._setEnabled(enabled);
this._updateEnabledAndSelectedState();
}
protected override _setSelected(selected: boolean) {
this._setProperty('selected', selected);
this._updateEnabledAndSelectedState();
}
protected _onTableDataScroll() {
this._reconcileScrollPos();
}
/**
* Generic handler for various events
*/
protected _onTableChanged() {
this._aggregate();
}
protected _onAggregationFunctionChanged() {
this._updateEnabledAndSelectedState(true);
if (this.contentRendered && this.selected) {
this._aggregate();
}
}
protected _onTableColumnResized() {
this._rerenderAggregate();
}
protected _onTableColumnMoved(event: TableColumnMovedEvent) {
// move aggregated value in aggregateRow
arrays.move(this.aggregateRow, event.oldPos, event.newPos);
this._rerenderAggregate();
}
protected _onTableColumnStructureChanged() {
this._updateEnabledAndSelectedState();
if (this.contentRendered && this.selected) {
this._aggregate();
}
}
}