@eclipse-scout/core
Version:
Eclipse Scout runtime
304 lines (262 loc) • 12.4 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 {
Action, arrays, Cell, Column, Event, Form, InitModelOf, MoveTableRowMenuHelper, scout, ShowInvisibleColumnsForm, StringField, strings, Table, TableCompleteCellEditEvent, TableOrganizerFormWidgetMap, TableRow, TableRowModel,
TableRowsSelectedEvent, TableStartCellEditEvent, tableUiPreferences, TableUiPreferences, WidgetModel
} from '../../index';
import TableOrganizerFormModel, {ColumnsTable0, ProfilesTable} from './TableOrganizerFormModel';
export class TableOrganizerForm extends Form {
declare widgetMap: TableOrganizerFormWidgetMap;
table: Table;
profilesTable: ProfilesTable;
columnsTable: ColumnsTable0;
keyColumn: Column<Column>;
protected override _jsonModel(): WidgetModel {
return TableOrganizerFormModel();
}
protected override _init(model: InitModelOf<this>) {
super._init(model);
this.table = this.findParent(Table);
this.profilesTable = this.widget('ProfilesTable');
this.columnsTable = this.widget('ColumnsTable');
this.keyColumn = this.columnsTable.columnById('KeyColumn');
this.widget('NewConfigMenu').on('action', event => this._addNewConfig());
this.widget('LoadConfigMenu').on('action', event => this._loadConfig());
this.widget('UpdateConfigMenu').on('action', event => this._updateConfig());
this.widget('DeleteConfigMenu').on('action', event => this._deleteConfigs());
this.widget('RenameConfigMenu').on('action', event => this._renameConfig());
this.profilesTable.on('rowsSelected', event => this._onProfilesTableRowsSelected(event));
this.profilesTable.on('startCellEdit', event => this._onProfilesTableStartCellEdit(event));
this.profilesTable.on('completeCellEdit', event => this._onProfilesTableCompleteCellEdit(event));
this.widget('AddColumnMenu').on('action', event => this._onAddColumnMenuAction(event));
this.widget('ModifyColumnMenu').on('action', event => this._onModifyColumnMenuAction(event));
this.widget('RemoveColumnMenu').on('action', event => this._onRemoveColumnMenuAction(event));
this.columnsTable.on('rowsSelected', event => this._onColumnsTableRowsSelected(event));
this.columnsTable.columnById('WidthColumn').setVisible(!this.table.autoResizeColumns);
this._installColumnUpDownMenus();
// Pages in a bookmark outline should only have one profile (the one stored in the bookmark) -> disable menu to create new profiles
if (strings.startsWith(this.table.userPreferenceContext, `${TableUiPreferences.PROFILE_ID_BOOKMARK}:`)) {
this.widget('NewConfigMenu').setEnabled(false);
}
}
protected override _load(): JQuery.Promise<any> {
this._reloadProfilesTable();
this._reloadColumnsTable();
this._updateColumnMenus();
return super._load();
}
protected _reloadProfilesTable() {
const rows: TableRowModel[] = [{cells: [this.session.text('DefaultSettings'), true]}];
// Create a row for each preference profile, except the GLOBAL profile (this represents the current state and cannot be activated explicitly)
let prefs = tableUiPreferences.get(this.table);
if (prefs?.tablePreferenceProfiles) {
[...prefs.tablePreferenceProfiles.keys()]
.filter(key => key !== TableUiPreferences.PROFILE_ID_GLOBAL)
.forEach(key => rows.push({cells: [key]}));
}
this.profilesTable.replaceRows(rows);
}
protected _onProfilesTableRowsSelected(event: TableRowsSelectedEvent<ProfilesTable>) {
this._updateProfileMenus();
if (this.profilesTable.selectedRows.length) {
this.columnsTable.deselectAll();
}
}
protected _onProfilesTableStartCellEdit(event: TableStartCellEditEvent<string, ProfilesTable>) {
(event.field as StringField).selectAll();
}
protected _onProfilesTableCompleteCellEdit(event: TableCompleteCellEditEvent<string>) {
event.cell.setEditable(false);
this.profilesTable.updateRow(event.row);
let oldConfigName = this.profilesTable.columnById('ConfigNameColumn').cellValue(event.row);
let newConfigName = event.field.value;
tableUiPreferences.renameProfile(this.table, oldConfigName, newConfigName);
}
protected _updateProfileMenus() {
const defaultConfigSelected = this.profilesTable.columnById('DefaultConfigColumn').selectedCellValues().includes(true);
this.widget('UpdateConfigMenu').setVisible(!defaultConfigSelected);
this.widget('DeleteConfigMenu').setVisible(!defaultConfigSelected);
this.widget('RenameConfigMenu').setVisible(!defaultConfigSelected);
}
protected _addNewConfig() {
let configName = this._newConfigName();
let profile = tableUiPreferences.createProfile(this.table, {includeUserFilters: true});
tableUiPreferences.storeProfile(this.table, configName, profile);
let row = scout.create(TableRow, {parent: this.profilesTable});
this.profilesTable.columnById('ConfigNameColumn').setCellValue(row, configName);
this.profilesTable.columnById('DefaultConfigColumn').setCellValue(row, false);
this.profilesTable.insertRow(row);
this._renameConfig(row);
}
protected _newConfigName(): string {
let profileNo = 1;
const baseName = this.session.text('New');
const existingNames = this.profilesTable.columnById('ConfigNameColumn').cellValues();
while (existingNames.includes(`${baseName} ${profileNo}`)) {
profileNo++;
}
return `${baseName} ${profileNo}`;
}
protected _loadConfig(row?: TableRow) {
row = scout.nvl(row, this.profilesTable.selectedRow());
let configName = this.profilesTable.columnById('ConfigNameColumn').cellValue(row);
let defaultConfig = this.profilesTable.columnById('DefaultConfigColumn').cellValue(row);
if (defaultConfig) {
this.table.resetToInitialUiPreferences();
} else {
let prefs = tableUiPreferences.get(this.table);
let profile = tableUiPreferences.getProfile(prefs, configName);
tableUiPreferences.applyProfile(this.table, profile);
// Store activated profile as current state
tableUiPreferences.storeGlobalProfile(this.table);
}
this._reloadColumnsTable();
}
protected _updateConfig(row?: TableRow) {
row = scout.nvl(row, this.profilesTable.selectedRow());
if (this.profilesTable.columnById('DefaultConfigColumn').cellValue(row)) {
return;
}
let configName = this.profilesTable.columnById('ConfigNameColumn').cellValue(row);
let profile = tableUiPreferences.createProfile(this.table);
tableUiPreferences.storeProfile(this.table, configName, profile);
}
protected _renameConfig(row?: TableRow) {
row = scout.nvl(row, this.profilesTable.selectedRow());
const column = this.profilesTable.columnById('ConfigNameColumn');
column.cell(row).setEditable(true);
this.profilesTable.updateRow(row);
this.profilesTable.focusCell(column, row);
}
protected _deleteConfigs(rows?: TableRow[]) {
rows = scout.nvl(rows, this.profilesTable.selectedRows);
const configNameColumn = this.profilesTable.columnById('ConfigNameColumn');
const defaultConfigColumn = this.profilesTable.columnById('DefaultConfigColumn');
rows = rows.filter(row => !defaultConfigColumn.cellValue(row));
rows.forEach(row => {
let configName = configNameColumn.cellValue(row);
tableUiPreferences.removeProfile(this.table, configName);
});
this.profilesTable.deleteRows(rows);
}
protected _reloadColumnsTable() {
const columns = this.table.visibleColumns(false);
const rows = columns.map(column => {
return {
cells: [
column,
ShowInvisibleColumnsForm.createColumnTitleCell(column),
scout.create(Cell, {value: this._computeColumnStatus(column), tooltipText: this._computeColumnStatusTooltip(column)}),
column.width
]
} as TableRowModel;
});
this.columnsTable.replaceRows(rows);
}
protected _computeColumnStatus(column: Column<any>): string {
let filtered = column.filtered;
let groupSymbol = 'G';
let filterSymbol = 'F';
let sortSymbol = column.sortAscending ? '↑' : '↓';
let sortIndex = column.sortIndex + 1;
let sortCount = this.table.visibleSortColumnsCount();
let $container = $('<div>');
let $status = $container.appendDiv('status');
let $left = $status.appendSpan('group-filter');
$left.appendDiv().text(groupSymbol).toggleClass('hidden', !column.grouped);
$left.appendDiv().text(filterSymbol).toggleClass('hidden', !filtered);
if (!column.grouped && !filtered) {
// Reserve space if neither grouped nor filtered to align with the status on the other rows
$left.children().removeClass('hidden').addClass('invisible');
}
$status.appendSpan('sort-direction').text(sortSymbol).toggleClass('invisible', !column.sortActive);
$status.appendSpan('sort-index').text(sortIndex).toggleClass('invisible', !column.sortActive || sortCount <= 1);
return $container.html();
}
protected _computeColumnStatusTooltip(column: Column<any>): string {
let result = [];
if (column.grouped) {
result.push(this.session.text('Grouped'));
}
if (column.filtered) {
result.push(this.session.text('Filtered'));
}
if (column.sortActive) {
result.push(this.session.text('Sorted'));
}
return result.join('\n');
}
protected _updateColumnMenus() {
let selectedColumns = this.keyColumn.selectedCellValues();
let columnAddable = this.table.organizer.isColumnAddable();
let columnRemovable = false;
let columnModifiable = false;
for (const column of selectedColumns) {
if (this.table.organizer.isColumnModifiable(column)) {
columnModifiable = true;
}
if (this._isColumnRemovable(column)) {
columnRemovable = true;
}
}
// Add and remove menus are either used to show and hide columns or to add and remove custom columns
this.widget('AddColumnMenu').setVisible(columnAddable);
this.widget('ModifyColumnMenu').setVisible(columnModifiable);
this.widget('RemoveColumnMenu').setVisible(columnRemovable);
}
protected _isColumnRemovable(column: Column<any>) {
return this.table.organizer.isColumnRemovable(column, true);
}
protected async _onAddColumnMenuAction(event: Event<Action>): Promise<void> {
let oldColumns = this.table.visibleColumns();
await this.table.organizer.addColumn(arrays.last(this.keyColumn.selectedCellValues()));
this._reloadColumnsTable();
// Select inserted columns
let insertedColumns = arrays.diff(this.table.visibleColumns(), oldColumns);
this.columnsTable.selectRows(this.columnsTable.rows.filter(row => insertedColumns.includes(this.keyColumn.cellValue(row))));
this.columnsTable.focus();
}
protected async _onModifyColumnMenuAction(event: Event<Action>) {
await this.table.organizer.modifyColumn(this.keyColumn.selectedCellValue());
this._reloadColumnsTable();
}
protected _onRemoveColumnMenuAction(event: Event<Action>) {
this.table.organizer.removeColumns(this.keyColumn.selectedCellValues().filter(column => this._isColumnRemovable(column)));
this._reloadColumnsTable();
}
protected _onColumnsTableRowsSelected(event: TableRowsSelectedEvent) {
this._updateColumnMenus();
if (this.columnsTable.selectedRows.length) {
this.profilesTable.deselectAll();
}
}
protected _installColumnUpDownMenus() {
const moveRowUpMenu = this.widget('MoveColumnUpMenu');
const moveRowDownMenu = this.widget('MoveColumnDownMenu');
scout.create(MoveTableRowMenuHelper).install({
table: this.columnsTable,
moveRowUpMenu,
moveRowDownMenu,
alwaysShowMenus: true,
rowFilter: (row, direction) => {
let column = this.keyColumn.cellValue(row);
if (direction === 'up') {
return this.table.organizer.isColumnMovableToLeft(column);
}
return this.table.organizer.isColumnMovableToRight(column);
}
});
this.columnsTable.on('rowOrderChanged', event => {
let newVisibleColumns = this.keyColumn.cellValues();
this.table.organizer.moveColumns(newVisibleColumns);
this._updateColumnMenus();
});
}
}