/* * 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; objectType: string; parent: Table; protected _busyCounter = 0; protected _readyPromise = Promise.resolve(); protected _resolveReadyPromise: () => void; init(model: InitModelOf) { 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 { 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; /** * 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> { 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[]>; // -------------------- /** * 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): Promise[]>; /** * Same as {@link addCustomColumn} but without user interaction. */ abstract addCustomColumnConfig(columnConfig: IColumnConfigDo, positionOrInsertAfterColumn?: number | Column): Promise>; // -------------------- /** * 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): Promise[]>; /** * Same as {@link modifyCustomColumn} but without user interaction. */ abstract modifyCustomColumnConfig(columnConfig: IColumnConfigDo): Promise>; // -------------------- /** * 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) { 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[]); /** * 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): boolean; // -------------------- static ensure(tableCustomizer: TableCustomizerOrModel, 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); } tableCustomizer.parent = table; return scout.create(tableCustomizer as FullModelOf); } } /** * A {@link TableCustomizer}, a {@link TableCustomizerModel} or an object type. */ export type TableCustomizerOrModel = TTableCustomizer | ChildModelOf | ObjectType; 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; }