UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

454 lines (453 loc) 21.5 kB
import { AdaptableModuleBase } from './AdaptableModuleBase'; import * as ModuleConstants from '../Utilities/Constants/ModuleConstants'; import * as LayoutRedux from '../Redux/ActionsReducers/LayoutRedux'; import ArrayExtensions from '../Utilities/Extensions/ArrayExtensions'; import { LayoutRadioSelector } from '../View/Layout/LayoutRadioSelector'; import { LayoutStatusbar } from '../View/Layout/LayoutStatusBarSubPanelPopover'; import { EditCurrentLayoutButton } from '../View/Layout/EditCurrentLayoutButton'; import { LayoutCloneButton } from '../View/Layout/LayoutCloneButton'; import { LayoutWizard } from '../View/Layout/Wizard/LayoutWizard'; import { getLayoutFilterViewItems } from './Utilities/Layout/getLayoutFilterViewItems'; import { getLayoutSortViewItems } from './Utilities/Layout/getLayoutSortViewItems'; import { WEIGHTED_AVERAGE_AGG_FN_NAME } from '../AdaptableState/Common/AggregationColumns'; import { SHOW_PIVOT_COLUMN_DETAILS } from '../View/Components/Popups/WindowPopups/windowFactory'; import flattenDeep from 'lodash/flattenDeep'; import StringExtensions from '../Utilities/Extensions/StringExtensions'; import { getGridFilterViewItems } from '../View/Layout/Wizard/getGridFilterPreview'; import { RowSummaryService } from '../Utilities/Services/RowSummaryService'; import { isPivotLayout } from '../Api/Implementation/LayoutHelpers'; export class LayoutModule extends AdaptableModuleBase { constructor(api) { super(ModuleConstants.LayoutModuleId, ModuleConstants.LayoutFriendlyName, 'grid', 'LayoutPopup', 'Named sets of column visibility, order, groupings, aggregation, pivots etc.', api); this.rowSummaryService = new RowSummaryService(this.api); } onAdaptableReady() { this.rowSummaryService.onAdaptableReady(); this.api.eventApi.on('LayoutChanged', (layoutChangedInfo) => { if (layoutChangedInfo.actionName === 'LAYOUT_SELECT' && layoutChangedInfo.newLayoutState.CurrentLayout !== layoutChangedInfo.oldLayoutState?.CurrentLayout) { this.handleLayoutSelection(); } }); requestAnimationFrame(() => { if (this.api.isDestroyed()) { return; } this.api.eventApi.internalApi.fireLayoutChangedEvent('ADAPTABLE_READY', null, this.api.layoutApi.getLayoutState()); }); } getModuleAdaptableObjects() { return this.api.layoutApi.getLayouts(); } getExplicitlyReferencedColumnIds(layout) { const columnIds = []; if (layout.TableColumns) { const visibility = layout.ColumnVisibility || {}; columnIds.push(...layout.TableColumns.filter((colId) => { return visibility[colId] !== false; })); } if (layout.TableAggregationColumns) { columnIds.push(...Object.keys(layout.TableAggregationColumns)); } if (isPivotLayout(layout)) { if (layout.PivotColumns) { columnIds.push(...layout.PivotColumns); } if (layout.PivotAggregationColumns) { columnIds.push(...layout.PivotAggregationColumns.map((x) => x.ColumnId)); } if (layout.PivotGroupedColumns) { columnIds.push(...layout.PivotGroupedColumns); } } if (layout.RowGroupedColumns) { columnIds.push(...layout.RowGroupedColumns); } return Array.from(new Set(columnIds)); } getTeamSharingReferences(adaptableObject) { const teamSharingReferences = super.getTeamSharingReferences(adaptableObject); const layoutName = adaptableObject.Name; if (this.api.layoutApi.internalApi.hasLayoutSpecificObjects() && !!layoutName) { const layoutAssociatedObjectReferences = []; const loadConfig = { associatedWithLayout: layoutName, }; // we ensured that there are layout specific objects, so all the "getAll*()" api methods will return only the objects available in the current layout this.api.alertApi.getAlertDefinitions(loadConfig).forEach((alertDefinition) => layoutAssociatedObjectReferences.push({ Reference: alertDefinition, Module: 'Alert', })); this.api.customSortApi.getCustomSorts(loadConfig).forEach((customSort) => layoutAssociatedObjectReferences.push({ Reference: customSort, Module: 'CustomSort', })); this.api.flashingCellApi.getFlashingCellDefinitions(loadConfig).forEach((flashingCell) => layoutAssociatedObjectReferences.push({ Reference: flashingCell, Module: 'FlashingCell', })); this.api.formatColumnApi.getFormatColumns(loadConfig).forEach((formatColumn) => layoutAssociatedObjectReferences.push({ Reference: formatColumn, Module: 'FormatColumn', })); this.api.styledColumnApi.getStyledColumns(loadConfig).forEach((styledcolumn) => layoutAssociatedObjectReferences.push({ Reference: styledcolumn, Module: 'StyledColumn', })); this.api.plusMinusApi.getAllPlusMinus(loadConfig).forEach((plusMinusNudge) => layoutAssociatedObjectReferences.push({ Reference: plusMinusNudge, Module: 'PlusMinus', })); this.api.shortcutApi.getShortcuts(loadConfig).forEach((shortcut) => layoutAssociatedObjectReferences.push({ Reference: shortcut, Module: 'Shortcut', })); [ ...this.api.scheduleApi.getReportSchedules(loadConfig), ...this.api.scheduleApi.getReminderSchedules(loadConfig), ...this.api.scheduleApi.getIPushPullSchedules(loadConfig), ...this.api.scheduleApi.getOpenFinSchedules(loadConfig), ].forEach((schedule) => layoutAssociatedObjectReferences.push({ Reference: schedule, Module: 'Schedule', })); teamSharingReferences.push(...layoutAssociatedObjectReferences); } return teamSharingReferences; } hasNamedQueryReferences() { return true; } createColumnMenuItems(column) { if (!this.isModuleEditable()) { return; } let returnColumnMenuItems = []; const isReadOnlyLayout = this.api.layoutApi.isCurrentLayoutReadOnly(); if (!isReadOnlyLayout) { returnColumnMenuItems.push(this.createMenuItemShowPopup('layout-edit', 'Edit Layout', this.moduleInfo.Popup, 'edit-table', { action: 'Edit', source: 'ColumnMenu', value: this.api.layoutApi.getCurrentLayout(), })); if (column && !column.isTreeColumn) { returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-caption-change', 'Change Caption', 'edit', () => this.api.layoutApi.showChangeColumnCaption(column))); } } if (column) { if (column.hideable) { returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-hide', 'Hide Column', 'visibility-off-bold', () => { this.api.columnApi.hideColumn(column.columnId); })); } const hasExistingSelection = this.api.gridApi.getSelectedCellInfo()?.gridCells?.length; if (hasExistingSelection) { returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-select-preserve', 'Select Column (Preserve Selection)', 'select-fwd', () => { this.api.columnApi.addColumnToSelection(column.columnId); })); returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-select-reset', 'Select Column (Reset Selection)', 'tab-unselected', () => { this.api.columnApi.selectColumn(column.columnId); })); } else { returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-column-select', 'Select Column', 'tab-unselected', () => { this.api.columnApi.selectColumn(column.columnId); })); } } returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-grid-select', 'Select Grid', 'select-all', () => { this.api.gridApi.selectAll(); })); return returnColumnMenuItems; } createContextMenuItems(menuContext) { let returnColumnMenuItems = []; if (this.isModuleEditable() && !this.api.layoutApi.isCurrentLayoutReadOnly()) { returnColumnMenuItems.push(this.createMenuItemShowPopup('layout-edit', 'Edit Layout', this.moduleInfo.Popup, 'edit-table', { action: 'Edit', source: 'ColumnMenu', })); } if ((menuContext.selectedCellInfo && ArrayExtensions.IsNotNullOrEmpty(menuContext.selectedCellInfo.columns)) || (menuContext.selectedRowInfo && ArrayExtensions.IsNotNullOrEmpty(menuContext.selectedRowInfo.gridRows))) { returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-clear-selection', 'Clear Selected Cells', 'select-off', () => { this.api.gridApi.deselectAll(); })); } returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-select-all', 'Select Grid', 'select-all', () => { this.api.gridApi.selectAll(); })); returnColumnMenuItems.push(this.createMenuItemClickFunction('layout-auto-size', 'Auto Size', 'arrow-expand', () => { this.api.columnApi.autosizeAllColumns(); })); const viewPivotItemsMenuItem = this.createViewPivotItemsMenuItem(menuContext); if (viewPivotItemsMenuItem) { returnColumnMenuItems.push(viewPivotItemsMenuItem); } return returnColumnMenuItems; } // TODO next time this method is touched, it should be extracted in an internal Api createViewPivotItemsMenuItem(menuContext) { // current group => menuContext.rowNode.field; const selectedCellInfo = menuContext.selectedCellInfo ?? this.api.gridApi.getSelectedCellInfo(); if (selectedCellInfo?.gridCells?.length !== 1) { return; } const gridCell = menuContext.gridCell ?? selectedCellInfo.gridCells[0]; const currentLayout = this.api.layoutApi.getCurrentLayout(); if (!isPivotLayout(currentLayout)) { return; } const { agGridColumn } = menuContext; const columnId = agGridColumn.getColId(); const isPivotColumn = this.api.columnApi.isPivotResultColumn(columnId); if (!isPivotColumn && this.api.columnApi.getColumnDataTypeForColumnId(columnId) !== 'number') { return; } /** * Cannot base the values on the pivot column id, because the value or the field may have underscrores * * * e.g. pivot_license_MIT License_stargazers_count */ // pivot_status_Booked_price // price const aggColumn = agGridColumn?.getColDef?.().pivotValueColumn?.getColDef?.()?.field; // Booked const pivotValue = agGridColumn?.getColDef?.()?.pivotKeys?.[0]; // status - the preview works only with one column const pivotColumnId = currentLayout?.PivotColumns?.[0]; function getData(rows) { return [ ...rows.map((row) => { if (row.childrenAfterFilter) { return getData(row.childrenAfterFilter); } return row.data; }), ]; } const data = flattenDeep(getData(menuContext.rowNode.childrenAfterFilter)); const rowGroups = currentLayout.RowGroupedColumns; const cellValue = isPivotColumn ? menuContext.rowNode?.aggData?.[columnId] : gridCell.displayValue; const popupProps = { hasPivotValue: isPivotColumn, columnId, rowGroups, rows: data, layout: currentLayout, aggColumn, pivotValue, pivotColumnId, }; return { name: 'layout-aggregated-view', category: this.moduleInfo.ModuleName, isVisible: true, label: 'Expand Aggregated Value', onClick: () => { this.api.internalApi.showPopupWindow({ // force only one window instance opened id: SHOW_PIVOT_COLUMN_DETAILS, factoryId: SHOW_PIVOT_COLUMN_DETAILS, title: `Rows for Aggregated Value: ${cellValue}`, icon: undefined, popupProps, }); }, icon: { name: 'arrow-expand', }, }; } getTeamSharingAction() { return { ModuleEntities: this.api.layoutApi.getLayouts(), AddAction: LayoutRedux.LayoutAdd, EditAction: LayoutRedux.LayoutSave, }; } toViewAll() { return this.getModuleAdaptableObjects().map((layout) => this.toView(layout)); } toView(layout) { const maxColumnsToDisplay = this.api.optionsApi.getLayoutOptions().layoutViewOptions?.maxColumnsToDisplay ?? 10; const columnIdToFriendlyName = (columnId) => { return this.api.columnApi.getFriendlyNameForColumnId(columnId); }; let columns = (layout.TableColumns || []) .filter((c) => !this.api.columnApi.isAutoRowGroupColumn(c)) .filter((c) => !this.api.columnApi.isPivotResultColumn(c)); if (columns.length > maxColumnsToDisplay + 1 /* +1 is to show tag only beginning with 2, 'other 2' */) { const extraColumns = columns.length - maxColumnsToDisplay; const firstNColumns = columns .slice(0, maxColumnsToDisplay) .map((column) => columnIdToFriendlyName(column)); columns = [...firstNColumns, `and ${extraColumns} more`]; } else { columns = columns.map((column) => columnIdToFriendlyName(column)); } return { items: [ { name: 'Grid Type', values: [isPivotLayout(layout) ? 'Pivot' : 'Table'], }, { name: 'Name', values: [layout.Name], }, /** * Hide pivot columns. * * The generated pivot columns cannot be prevented from beeing on the layout object. * When they are removed/not added, they no longer appear in the grid. */ !isPivotLayout(layout) && { name: 'Columns', values: columns, }, layout.ColumnSorts?.length && getLayoutSortViewItems(layout, this.api), layout?.ColumnFilters?.length && getLayoutFilterViewItems(layout, this.api), layout?.RowGroupedColumns?.length && { name: 'Row Groups', values: layout.RowGroupedColumns.map((colId) => columnIdToFriendlyName(colId)), }, layout?.PivotColumns?.length && { name: 'Pivot Columns', values: layout.PivotColumns.map((colId) => columnIdToFriendlyName(colId)), }, layout?.PivotGroupedColumns?.length && { name: 'Pivot Row Groups', values: layout.PivotGroupedColumns.map((colId) => columnIdToFriendlyName(colId)), }, layout?.TableAggregationColumns && layout.TableAggregationColumns.length && { name: 'Aggregations', values: layout.TableAggregationColumns.map(({ ColumnId, AggFunc: aggFn }) => { if (ColumnId === 'Source' || ColumnId === 'Uuid' || ColumnId === 'AdaptableVersion') { return ''; } let aggFnName = ''; if (typeof aggFn === 'string') { aggFnName = aggFn; } else if (typeof aggFn === 'object' && aggFn.type === 'weightedAverage') { aggFnName = WEIGHTED_AVERAGE_AGG_FN_NAME; } return `${aggFnName}(${columnIdToFriendlyName(ColumnId)})`; }).filter(Boolean), }, isPivotLayout(layout) && layout?.PivotAggregationColumns && layout.PivotAggregationColumns.length && { name: 'Pivot Aggregations', values: layout.PivotAggregationColumns.map(({ ColumnId, AggFunc: aggFn }) => { if (ColumnId === 'Source' || ColumnId === 'Uuid' || ColumnId === 'AdaptableVersion') { return ''; } let aggFnName = ''; if (typeof aggFn === 'string') { aggFnName = aggFn; } else if (typeof aggFn === 'object' && aggFn.type === 'weightedAverage') { aggFnName = WEIGHTED_AVERAGE_AGG_FN_NAME; } return `${aggFnName}(${columnIdToFriendlyName(ColumnId)})`; }).filter(Boolean), }, layout?.GridFilter && StringExtensions.IsNotNullOrEmpty(layout.GridFilter.Expression) && getGridFilterViewItems(layout), layout && !isPivotLayout(layout) && layout.RowSummaries?.length && { name: 'Row Summaries', values: layout.RowSummaries.map((rowSummary) => { const columns = Object.entries(rowSummary.ColumnsMap ?? {}) .map(([columnId, expression]) => { if (columnId === 'Source' || columnId === 'Uuid' || columnId === 'AdaptableVersion') { return ''; } return `${expression}(${columnIdToFriendlyName(columnId)})`; }) .filter(Boolean) .join(', '); return `${rowSummary.Position}: ${columns}`; }), }, ].filter(Boolean), abObject: layout, }; } getViewProperties() { return { abObjectTypes: [ { name: 'Table Layout', accessLevel: this.AccessLevel, }, !this.api.gridApi.isTreeDataGrid() && { name: 'Pivot Layout', accessLevel: this.AccessLevel, }, ].filter(Boolean), actions: [LayoutRadioSelector, LayoutCloneButton], getDeleteAction: (layout) => { // make sure we do not delete the last layout if (this.getModuleAdaptableObjects().length === 1) { return null; } return LayoutRedux.LayoutDelete(layout); }, getEditWizard: () => LayoutWizard, getStatusBarPanelProps: () => { return { triggerActionOnWrapperClick: false, content: LayoutStatusbar, extraActions: [EditCurrentLayoutButton], }; }, }; } handleLayoutSelection() { if (!this.api.layoutApi.internalApi.hasLayoutSpecificObjects()) { // no need if no callback implementation is provided or no auto checking return; } // PlusMinus this.api.internalApi .getModuleService() .getModuleById('PlusMinus') .checkListenToKeyDown(); // Shortcut this.api.internalApi .getModuleService() .getModuleById('Shortcut') .checkListenToKeyDown(); // Schedule this.api.internalApi .getModuleService() .getModuleById('Schedule') .setUpScheduleJobs(); // CustomSort, FlashingCell, FormatColumn, StyledColumn // we need to re-setup the column defs, as some colDefs properties may be changed this.api.internalApi.getAdaptableInstance().updateColumnModelAndRefreshGrid(); } getReferencedNamedQueryNames(layout) { if (!layout.GridFilter?.Expression) { return []; } return this.api.namedQueryApi.internalApi.getReferencedNamedQueryNames(layout.GridFilter.Expression); } }