@eclipse-scout/core
Version:
Eclipse Scout runtime
257 lines (221 loc) • 8.8 kB
text/typescript
/*
* Copyright (c) 2010, 2025 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 {
BooleanColumn, Cell, Column, CompactBean, CompactLine, Event, EventHandler, InitModelOf, objects, ObjectWithType, SomeRequired, Table, TableCompactHandlerModel, TableRow, TableRowsInsertedEvent, TableRowsUpdatedEvent
} from '../index';
export class TableCompactHandler implements TableCompactHandlerModel, ObjectWithType {
declare model: TableCompactHandlerModel;
declare initModel: SomeRequired<this['model'], 'table'>;
objectType: string;
table: Table;
useOnlyVisibleColumns: boolean;
maxContentLines: number;
moreLinkAvailable: boolean;
lineCustomizer: (line: CompactLine) => void;
protected _oldStates: Record<string, any>;
protected _updateHandler: EventHandler<TableRowsInsertedEvent | TableRowsUpdatedEvent | Event<Table>>;
constructor() {
this.table = null;
this.useOnlyVisibleColumns = true;
this.maxContentLines = 3;
this.moreLinkAvailable = true;
this.lineCustomizer = null;
this._oldStates = objects.createMap();
this._updateHandler = null;
}
init(model: InitModelOf<this>) {
$.extend(this, model);
}
setUseOnlyVisibleColumns(useOnlyVisibleColumns: boolean) {
this.useOnlyVisibleColumns = useOnlyVisibleColumns;
}
setMaxContentLines(maxContentLines: number) {
this.maxContentLines = maxContentLines;
}
setMoreLinkAvailable(moreLinkAvailable: boolean) {
this.moreLinkAvailable = moreLinkAvailable;
}
setLineCustomizer(lineCustomizer: (line: CompactLine) => void) {
this.lineCustomizer = lineCustomizer;
}
handle(compact: boolean) {
if (compact) {
this._compactColumns(true);
this._attachTableHandler();
} else {
this._detachTableHandler();
this._compactColumns(false);
}
this._adjustTable(compact);
if (compact) {
this.updateValues(this.table.rows);
}
}
protected _adjustTable(compact: boolean) {
if (compact) {
this._cacheAndSetProperty('headerVisible', () => this.table.headerVisible, () => this.table.setHeaderVisible(false));
this._cacheAndSetProperty('autoResizeColumns', () => this.table.autoResizeColumns, () => this.table.setAutoResizeColumns(true));
} else {
this._resetProperty('headerVisible', value => this.table.setHeaderVisible(value));
this._resetProperty('autoResizeColumns', value => this.table.setAutoResizeColumns(value));
}
}
protected _cacheAndSetProperty(propertyName: string, getter: () => any, setter: () => void) {
if (objects.isNullOrUndefined(this._oldStates[propertyName])) {
this._oldStates[propertyName] = getter();
}
setter();
}
protected _resetProperty(propertyName: string, setter: (oldValue: any) => void) {
let oldState = this._oldStates[propertyName];
if (!objects.isNullOrUndefined(oldState)) {
setter(oldState);
delete this._oldStates[propertyName];
}
}
protected _compactColumns(compact: boolean) {
this.table.displayableColumns(false).forEach(column => column.setCompacted(compact, false));
this.table.onColumnVisibilityChanged();
}
protected _attachTableHandler() {
if (this._updateHandler == null) {
this._updateHandler = this._onTableEvent.bind(this);
this.table.on('rowsInserted rowsUpdated columnStructureChanged', this._updateHandler);
}
}
protected _detachTableHandler() {
if (this._updateHandler != null) {
this.table.off('rowsInserted rowsUpdated columnStructureChanged', this._updateHandler);
this._updateHandler = null;
}
}
updateValues(rows: TableRow[]) {
if (rows.length === 0) {
return;
}
let columns = this.getColumns();
rows.forEach(row => this._updateValue(columns, row));
}
protected _updateValue(columns: Column<any>[], row: TableRow) {
row.setCompactValue(this.buildValue(columns, row));
}
buildValue(columns: Column<any>[], row: TableRow): string {
return this._buildValue(this._createBean(columns, row));
}
protected _createBean(columns: Column<any>[], row: TableRow): CompactBean {
let bean = new CompactBean();
this._processColumns(columns, row, bean);
this._postProcessBean(bean);
return bean;
}
protected _processColumns(columns: Column<any>[], row: TableRow, bean: CompactBean) {
columns.forEach((column, i) => this._processColumn(column, i, row, bean));
}
getColumns(): Column<any>[] {
return this.table.filterColumns(column => this._acceptColumn(column));
}
protected _acceptColumn(column: Column<any>): boolean {
return !column.guiOnly && (!this.useOnlyVisibleColumns || column.visibleIgnoreCompacted);
}
protected _processColumn(column: Column<any>, index: number, row: TableRow, bean: CompactBean) {
this._updateBean(bean, column, index, row);
}
/**
* @param bean
* the bean for the current row
* @param column
* the currently processed column
* @param index
* visible column index of the currently processed column
* @param row
* the current row
*/
protected _updateBean(bean: CompactBean, column: Column<any>, index: number, row: TableRow) {
if (this._acceptColumnForTitle(column, index)) {
bean.setTitleLine(this._createCompactLine(column, index, row));
} else if (this._acceptColumnForTitleSuffix(column, index)) {
bean.setTitleSuffixLine(this._createCompactLine(column, index, row));
} else if (this._acceptColumnForSubtitle(column, index)) {
bean.setSubtitleLine(this._createCompactLine(column, index, row));
} else {
bean.addContentLine(this._createCompactLine(column, index, row));
}
}
protected _acceptColumnForTitle(column: Column<any>, index: number): boolean {
return index === 0;
}
protected _acceptColumnForSubtitle(column: Column<any>, index: number): boolean {
return index === 1;
}
protected _acceptColumnForTitleSuffix(column: Column<any>, index: number): boolean {
return false;
}
protected _createCompactLine(column: Column<any>, index: number, row: TableRow): CompactLine {
let headerCell: Cell;
if (this._showLabel(column, index, row)) {
headerCell = column.headerCell();
}
let cell = column.cell(row);
let line = new CompactLine(headerCell, cell);
this._adaptCompactLine(line, column, headerCell, cell);
return line;
}
protected _showLabel(column: Column<any>, index: number, row: TableRow): boolean {
return !this._acceptColumnForTitle(column, index) && !this._acceptColumnForSubtitle(column, index) && !this._acceptColumnForTitleSuffix(column, index);
}
protected _adaptCompactLine<TValue>(line: CompactLine, column: Column<TValue>, headerCell: Cell<TValue>, cell: Cell<TValue>) {
if (column instanceof BooleanColumn) {
let text = '';
let value = cell.value as boolean;
if (value) {
text = 'X';
} else if (value === null) {
text = '?';
}
line.textBlock.setText(text);
}
this.lineCustomizer?.(line);
}
protected _postProcessBean(bean: CompactBean) {
bean.transform({
maxContentLines: this.maxContentLines,
moreLinkAvailable: this.moreLinkAvailable
});
// If only title is set move it to content. A title without content does not look good.
if (bean.title && !bean.subtitle && !bean.titleSuffix && !bean.content) {
bean.setContent(bean.title);
bean.setTitle('');
}
}
protected _buildValue(bean: CompactBean): string {
let hasHeader = (bean.title + bean.titleSuffix + bean.subtitle) ? ' has-header' : '';
let moreLink = (this.moreLinkAvailable && bean.moreContent) ? `<div class="compact-cell-more"><span class="more-link link">${this.table.session.text('More')}</span></div>` : '';
return `
<div class="compact-cell-header">
<div class="compact-cell-title">
<span class="left">${bean.title}</span>
<span class="right">${bean.titleSuffix}</span>
</div>
<div class="compact-cell-subtitle">${bean.subtitle}</div>
</div>
<div class="compact-cell-content${hasHeader}">${bean.content}</div>
<div class="compact-cell-more-content hidden${hasHeader}">${bean.moreContent}</div>
${moreLink}`;
}
protected _onTableEvent(event: TableRowsInsertedEvent | TableRowsUpdatedEvent | Event<Table>) {
let rows: TableRow[];
if (event.type === 'columnStructureChanged') {
rows = this.table.rows;
} else {
rows = (event as TableRowsInsertedEvent | TableRowsUpdatedEvent).rows;
}
this.updateValues(rows);
}
}