@eclipse-scout/core
Version:
Eclipse Scout runtime
217 lines (183 loc) • 8.3 kB
text/typescript
/*
* Copyright (c) 2010, 2026 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, ChildModelOf, Column, FullModelOf, IColumnConfigDo, InitModelOf, ITableCustomizerDo, ObjectType, ObjectWithType, scout, Session, SomeRequired, Table, TableCustomizerModel} from '../../index';
/**
* Manages custom columns for a table, i.e. columns that are dynamically created from configuration rather than from a static model.
*/
export abstract class TableCustomizer implements TableCustomizerModel, ObjectWithType {
declare model: TableCustomizerModel;
declare initModel: SomeRequired<this['model'], 'parent'>;
objectType: string;
parent: Table;
protected _busyCounter = 0;
protected _readyPromise = Promise.resolve();
protected _resolveReadyPromise: () => void;
init(model: InitModelOf<this>) {
scout.assertParameter('parent', model.parent);
$.extend(this, model);
}
get table(): Table {
return this.parent;
}
get session(): Session {
return this.parent.session;
}
// --------------------
get ready(): boolean {
return !this.busy;
}
get busy(): boolean {
return this._busyCounter > 0;
}
/**
* Returns a promise that is resolved when all asynchronous tasks are done (e.g. creating columns).
*/
whenReady(): Promise<void> {
return this._readyPromise;
}
protected _setBusy(busy: boolean) {
if (busy) {
this._busyCounter++;
if (this._busyCounter === 1) {
// set unready
this._readyPromise = new Promise((resolve, reject) => {
this._resolveReadyPromise = resolve;
});
}
} else {
this._busyCounter = Math.max(this._busyCounter - 1, 0);
if (this._busyCounter === 0) {
this._resolveReadyPromise();
}
}
}
// --------------------
/**
* Adjusts the table according to the given customizer data. Existing custom columns are removed and replaced
* by new custom columns. Depending on the implementation, creating columns may involve loading additional data.
* Therefore, a promise is returned that is resolved when all columns are ready.
*/
abstract setCustomizerData(customizerData: ITableCustomizerDo): Promise<void>;
/**
* Returns a data object describing the custom columns. Can be persisted and re-applied later using {@link setCustomizerData}.
* If there are no custom columns, `null` is returned.
*/
abstract getCustomizerData(): ITableCustomizerDo;
// --------------------
/**
* Creates a new column instance for the given column configuration. The configuration is *not* added to the list of
* custom columns (use {@link addCustomColumnConfig} for that).
*/
async createColumn(columnConfig: IColumnConfigDo, options?: TableCustomizerCreateColumnsOptions): Promise<Column<any>> {
return arrays.first(await this.createColumns(arrays.ensure(columnConfig), options));
}
/**
* Creates new column instances for the given column configurations. The configurations are *not* added to the list of
* custom columns (use {@link addCustomColumnConfig} for that).
*
* The resulting list has the same length and order as the input. Columns that could not be created are represented by `null`.
*/
abstract createColumns(columnConfigs: IColumnConfigDo[], options?: TableCustomizerCreateColumnsOptions): Promise<Column<any>[]>;
// --------------------
/**
* Opens a form that lets the user configure a new column. The new configuration is added to the list of custom columns and
* a new column instance is created and inserted into the table at the given position.
*
* Depending on the implementation, the form may allow the user to create additional columns ("save and new"). This method
* returns a list of all created columns when the form is finally closed.
*
* @param positionOrInsertAfterColumn Specifies the position for the new columns:
* - If a number is provided, the columns are inserted at the specified index in {@link Table#columns}.
* - If a {@link Column} is provided, the columns are inserted immediately after that column.
* - Otherwise, the columns are appended to the end of the table.
*/
abstract addCustomColumn(positionOrInsertAfterColumn?: number | Column<any>): Promise<Column<any>[]>;
/**
* Same as {@link addCustomColumn} but without user interaction.
*/
abstract addCustomColumnConfig(columnConfig: IColumnConfigDo, positionOrInsertAfterColumn?: number | Column<any>): Promise<Column<any>>;
// --------------------
/**
* Opens a form that lets the user change the configuration of the given column. If the customizer does not have a column
* configuration for the given column, nothing happens. When the form is stored, the existing configuration is replaced.
* A new column instance is created and replaced in the table. The column ID remains unchanged.
*
* Depending on the implementation, the form may allow the user to create additional columns ("save and new"). This method
* returns a list of all created columns when the form is finally closed.
*/
abstract modifyCustomColumn(column: Column<any>): Promise<Column<any>[]>;
/**
* Same as {@link modifyCustomColumn} but without user interaction.
*/
abstract modifyCustomColumnConfig(columnConfig: IColumnConfigDo): Promise<Column<any>>;
// --------------------
/**
* Removes the given column from the table. If the column represents a custom column, the corresponding column configuration
* is removed from the customizer as well.
*/
removeCustomColumn(column: Column<any>) {
this.removeCustomColumns(arrays.ensure(column));
}
/**
* Removes the given columns from the table. If a column represents a custom column, the corresponding column configuration
* is removed from the customizer as well.
*/
abstract removeCustomColumns(columns: Column<any>[]);
/**
* Removes all custom columns from the table. The corresponding column configurations are removed from the customizer as well.
* All other columns remain untouched.
*/
abstract removeAllCustomColumns();
// --------------------
/**
* Returns true if the given column is a "custom column", i.e. the customizer has a corresponding column configuration.
* Note that columns that are simply created by {@link createColumn} are *not* considered to be custom columns.
*/
abstract isCustomizable(column: Column<any>): boolean;
// --------------------
static ensure<TTableCustomizer extends TableCustomizer>(tableCustomizer: TableCustomizerOrModel<TTableCustomizer>, table: Table): TTableCustomizer {
if (!tableCustomizer) {
return tableCustomizer as TTableCustomizer;
}
if (tableCustomizer instanceof TableCustomizer) {
return tableCustomizer;
}
if (typeof tableCustomizer === 'string' || typeof tableCustomizer === 'function') {
return scout.create(tableCustomizer, {
parent: table
} as InitModelOf<TTableCustomizer>);
}
tableCustomizer.parent = table;
return scout.create(tableCustomizer as FullModelOf<TTableCustomizer>);
}
}
/**
* A {@link TableCustomizer}, a {@link TableCustomizerModel} or an object type.
*/
export type TableCustomizerOrModel<TTableCustomizer extends TableCustomizer = TableCustomizer> = TTableCustomizer | ChildModelOf<TTableCustomizer> | ObjectType<TTableCustomizer>;
export interface TableCustomizerCreateColumnsOptions {
/**
* Whether to automatically insert the created columns into the table.
*
* Default is false.
*/
insertIntoTable?: boolean;
/**
* Specifies the position for the new columns:
* - If a number is provided, the columns are inserted at the specified index in {@link Table#columns}.
* - If a {@link Column} is provided, the columns are inserted immediately after that column.
* - Otherwise, the columns are appended to the end of the table.
*
* Only relevant if {@link insertIntoTable} is true.
*
* Default is undefined.
*/
positionOrInsertAfterColumn?: number | Column<any>;
}