diff --git a/webapp/packages/core-app/public/icons/sorting.png b/webapp/packages/core-app/public/icons/sorting.png new file mode 100644 index 0000000000..1d94297cfc Binary files /dev/null and b/webapp/packages/core-app/public/icons/sorting.png differ diff --git a/webapp/packages/core-app/src/TopNavBar/ConnectionSchemaManager/ConnectionSelector/ConnectionSelectorController.ts b/webapp/packages/core-app/src/TopNavBar/ConnectionSchemaManager/ConnectionSelector/ConnectionSelectorController.ts index 35c0d8b141..b6f0f03114 100644 --- a/webapp/packages/core-app/src/TopNavBar/ConnectionSchemaManager/ConnectionSelector/ConnectionSelectorController.ts +++ b/webapp/packages/core-app/src/TopNavBar/ConnectionSchemaManager/ConnectionSelector/ConnectionSelectorController.ts @@ -75,14 +75,14 @@ export class ConnectionSelectorController { || this.getObjectContainerItems().length === 0; } - private get currentConnectionTitle(): string | undefined { + private get currentConnectionTitle(): string { if (this.currentConnection) { return this.currentConnection.name; } return 'app_topnavbar_connection_schema_manager_not_selected'; } - private get currentObjectContainerTitle(): string | undefined { + private get currentObjectContainerTitle(): string { const value = NodeManagerUtils.concatSchemaAndCatalog( this.connectionSelectorService.currentObjectCatalogId, this.connectionSelectorService.currentObjectSchemaId @@ -97,7 +97,7 @@ export class ConnectionSelectorController { private get currentObjectContainerIcon(): string { if (this.connectionSelectorService.currentObjectSchema?.features?.includes(EObjectFeature.schema)) { - // TODO move such kind of icon paths to a set of constants + // TODO move such kind of icon paths to a set of constants return 'schema_system'; } if (this.connectionSelectorService.currentObjectCatalog?.features?.includes(EObjectFeature.catalog)) { diff --git a/webapp/packages/core-dialogs/src/Menu/ContextMenu/ContextMenu.ts b/webapp/packages/core-dialogs/src/Menu/ContextMenu/ContextMenu.ts index 406eb7f6a0..620fbee840 100644 --- a/webapp/packages/core-dialogs/src/Menu/ContextMenu/ContextMenu.ts +++ b/webapp/packages/core-dialogs/src/Menu/ContextMenu/ContextMenu.ts @@ -84,7 +84,7 @@ class ComputedMenuItemOptionsWithContext implements IComputedMenuItemOptions onClick?: () => void; // set title or getter title?: TLocalizationToken; - titleGetter?: () => TLocalizationToken | undefined; + titleGetter?: () => TLocalizationToken; tooltip?: TLocalizationToken; tooltipGetter?: () => TLocalizationToken | undefined; isDisabled?: () => boolean; @@ -100,7 +100,6 @@ class ComputedMenuItemOptionsWithContext implements IComputedMenuItemOptions private context: IMenuContext) { // doesn't depend on context this.title = options.title; - this.titleGetter = options.titleGetter; this.tooltip = options.tooltip; this.tooltipGetter = options.tooltipGetter; this.icon = options.icon; @@ -122,6 +121,9 @@ class ComputedMenuItemOptionsWithContext implements IComputedMenuItemOptions if (options.isChecked) { this.isChecked = () => options.isChecked!(this.context); } + if (options.titleGetter) { + this.titleGetter = () => options.titleGetter!(this.context); + } } } diff --git a/webapp/packages/core-dialogs/src/Menu/ContextMenu/IContextMenuItem.ts b/webapp/packages/core-dialogs/src/Menu/ContextMenu/IContextMenuItem.ts index 88822b08d3..b304934273 100644 --- a/webapp/packages/core-dialogs/src/Menu/ContextMenu/IContextMenuItem.ts +++ b/webapp/packages/core-dialogs/src/Menu/ContextMenu/IContextMenuItem.ts @@ -6,6 +6,8 @@ * you may not use this file except in compliance with the License. */ +import type { TLocalizationToken } from '@cloudbeaver/core-localization'; + import type { IMenuItemOptions } from '../models/MenuOptionsStore'; import type { IMenuContext } from './IMenuContext'; @@ -14,6 +16,7 @@ import type { IMenuContext } from './IMenuContext'; */ export interface IContextMenuItem extends IMenuItemOptions { onClick?: (context: IMenuContext) => void; + titleGetter?: (context: IMenuContext) => TLocalizationToken; // if isPresent is false menu item will not be included in resulting context menu isPresent: (context: IMenuContext) => boolean; isDisabled?: (context: IMenuContext) => boolean; diff --git a/webapp/packages/core-dialogs/src/Menu/MenuPanel/menuPanelStyles.ts b/webapp/packages/core-dialogs/src/Menu/MenuPanel/menuPanelStyles.ts index ef7629cb5e..528b96341c 100644 --- a/webapp/packages/core-dialogs/src/Menu/MenuPanel/menuPanelStyles.ts +++ b/webapp/packages/core-dialogs/src/Menu/MenuPanel/menuPanelStyles.ts @@ -38,6 +38,7 @@ export const menuPanelStyles = composes( & box { display: flex; align-items: center; + flex: 1; height: inherit; } } @@ -110,6 +111,7 @@ export const menuPanelStyles = composes( & IconOrImage { width: 16px; height: 16px; + object-fit: contain; } } ` diff --git a/webapp/packages/core-dialogs/src/Menu/models/ComputedMenuItemModel.ts b/webapp/packages/core-dialogs/src/Menu/models/ComputedMenuItemModel.ts index 334813b673..283e3181b1 100644 --- a/webapp/packages/core-dialogs/src/Menu/models/ComputedMenuItemModel.ts +++ b/webapp/packages/core-dialogs/src/Menu/models/ComputedMenuItemModel.ts @@ -15,6 +15,7 @@ import type { IMenuItemOptions, MenuItemType } from './MenuOptionsStore'; export interface IComputedMenuItemOptions extends IMenuItemOptions { onClick?: () => void; + titleGetter?: () => string; isDisabled?: () => boolean; isHidden?: () => boolean; isChecked?: () => boolean; diff --git a/webapp/packages/core-dialogs/src/Menu/models/MenuOptionsStore.ts b/webapp/packages/core-dialogs/src/Menu/models/MenuOptionsStore.ts index 9149336837..09488ffd60 100644 --- a/webapp/packages/core-dialogs/src/Menu/models/MenuOptionsStore.ts +++ b/webapp/packages/core-dialogs/src/Menu/models/MenuOptionsStore.ts @@ -9,6 +9,7 @@ import type { TLocalizationToken } from '@cloudbeaver/core-localization'; import { OrderedMap } from '@cloudbeaver/core-utils'; +import type { IMenuContext } from '../ContextMenu/IMenuContext'; import type { IMenuPanel } from '../IMenuPanel'; const DEFAULT_ITEM_ORDER = 100; @@ -18,7 +19,7 @@ export interface IMenuItemOptions { id: string; // set title or getter title?: TLocalizationToken; - titleGetter?: () => TLocalizationToken | undefined; + titleGetter?: (...args: any[]) => TLocalizationToken; // set icon or getter icon?: string; iconGetter?: () => string | undefined; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenuService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenuService.ts index 52759fb571..0324f387be 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenuService.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenuService.ts @@ -8,7 +8,7 @@ import { injectable } from '@cloudbeaver/core-di'; import { ContextMenuService, IMenuPanel } from '@cloudbeaver/core-dialogs'; -import type { IDatabaseDataModel } from '@cloudbeaver/plugin-data-viewer'; +import { IDatabaseDataModel, ResultSetDataAction, ESortMode, getNextSortMode, ResultSetSortAction, SortMode } from '@cloudbeaver/plugin-data-viewer'; export interface IDataGridCellMenuContext { model: IDatabaseDataModel; @@ -21,15 +21,20 @@ export interface IDataGridCellMenuContext { export class DataGridContextMenuService { static cellContext = 'data-grid-cell-context-menu'; private static menuToken = 'dataGridCell'; + private static menuSortingToken = 'menuSorting'; constructor( - private contextMenuService: ContextMenuService + private contextMenuService: ContextMenuService, ) { } getMenuToken(): string { return DataGridContextMenuService.menuToken; } + getMenuSortingToken(): string { + return DataGridContextMenuService.menuSortingToken; + } + constructMenuWithContext( model: IDatabaseDataModel, resultIndex: number, @@ -43,28 +48,104 @@ export class DataGridContextMenuService { }); } + private getColumnName(model: IDatabaseDataModel, resultIndex: number, columnIndex: number) { + const data = model.source.getAction(resultIndex, ResultSetDataAction); + return data.getColumn(columnIndex)?.name; + } + + private async changeSortMode( + model: IDatabaseDataModel, + resultIndex: number, + columnIndex: number, + sortMode: SortMode + ) { + const columnName = this.getColumnName(model, resultIndex, columnIndex)!; + const sorting = model.source.getAction(resultIndex, ResultSetSortAction); + + sorting.setSortMode(columnName, sortMode, true); + await model.refresh(); + } + + private getSortMode(model: IDatabaseDataModel, resultIndex: number, columnIndex: number) { + const columnName = this.getColumnName(model, resultIndex, columnIndex)!; + const sorting = model.source.getAction(resultIndex, ResultSetSortAction); + + return sorting.getSortMode(columnName); + } + register() { this.contextMenuService.addMenuItem( this.contextMenuService.getRootMenuToken(), { - id: this.getMenuToken(), + id: this.getMenuSortingToken(), isPresent(context) { return context.contextType === DataGridContextMenuService.cellContext; }, - order: 2, - title: 'Cell', + order: 0, + title: 'data_grid_table_sorting', + icon: '/icons/sorting.png', + isPanel: true, } ); - this.contextMenuService.addMenuItem( - this.getMenuToken(), + this.getMenuSortingToken(), { - id: 'edit', + id: 'asc', isPresent(context) { return context.contextType === DataGridContextMenuService.cellContext; }, - order: 2, - title: 'Cell', + isDisabled: context => context.data.model.isLoading(), + onClick: async context => { + await this.changeSortMode(context.data.model, context.data.resultIndex, context.data.column, ESortMode.asc); + }, + type: 'radio', + isChecked: context => this.getSortMode( + context.data.model, + context.data.resultIndex, + context.data.column) === ESortMode.asc, + titleGetter: context => { + const columnName = this.getColumnName(context.data.model, context.data.resultIndex, context.data.column)!; + return `Order by ${columnName} ASC`; + }, + } + ); + this.contextMenuService.addMenuItem( + this.getMenuSortingToken(), + { + id: 'desc', + isPresent(context) { + return context.contextType === DataGridContextMenuService.cellContext; + }, + isDisabled: context => context.data.model.isLoading(), + onClick: async context => { + await this.changeSortMode(context.data.model, context.data.resultIndex, context.data.column, ESortMode.desc); + }, + type: 'radio', + isChecked: context => this.getSortMode( + context.data.model, + context.data.resultIndex, + context.data.column) === ESortMode.desc, + titleGetter: context => { + const columnName = this.getColumnName(context.data.model, context.data.resultIndex, context.data.column)!; + return `Order by ${columnName} DESC`; + }, + } + ); + this.contextMenuService.addMenuItem( + this.getMenuSortingToken(), + { + id: 'toggleSorting', + isPresent(context) { + return context.contextType === DataGridContextMenuService.cellContext; + }, + isDisabled: context => context.data.model.isLoading(), + onClick: async context => { + const nextSortMode = getNextSortMode( + this.getSortMode(context.data.model, context.data.resultIndex, context.data.column) + ); + await this.changeSortMode(context.data.model, context.data.resultIndex, context.data.column, nextSortMode); + }, + title: 'data_grid_table_sorting_toggle', } ); } diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSorting/DataGridSortingContext.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSorting/DataGridSortingContext.ts deleted file mode 100644 index bbbc1a2fda..0000000000 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSorting/DataGridSortingContext.ts +++ /dev/null @@ -1,21 +0,0 @@ - -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2021 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0. - * you may not use this file except in compliance with the License. - */ - -import { createContext } from 'react'; - -import type { SqlDataFilterConstraint } from '@cloudbeaver/core-sdk'; -import type { SortMode } from '@cloudbeaver/plugin-data-viewer'; - -export interface IDataGridSortingContext { - sortedColumns: Map; - setSortMode: (colId: string, sort: SortMode, multiple: boolean) => Promise | void; - getSortMode: (colId: string) => SortMode; -} - -export const DataGridSortingContext = createContext(null); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSorting/useGridSortingContext.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSorting/useGridSortingContext.tsx deleted file mode 100644 index 4c37a9fed1..0000000000 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSorting/useGridSortingContext.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2021 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0. - * you may not use this file except in compliance with the License. - */ - -import { observable } from 'mobx'; -import { useCallback, useMemo, useState } from 'react'; - -import type { SqlDataFilterConstraint } from '@cloudbeaver/core-sdk'; -import type { IDatabaseDataModel, SortMode } from '@cloudbeaver/plugin-data-viewer'; - -import type { IDataGridSortingContext } from './DataGridSortingContext'; - -export function useGridSortingContext(model: IDatabaseDataModel) { - const [sortedColumns] = useState(() => observable.map()); - - const refreshModel = useCallback(async (constraints: SqlDataFilterConstraint[]) => { - await model.setOptions({ ...model.source.options, constraints }).refresh(); - }, [model]); - - const setColumnSortMode = useCallback(async (colId: string, sort: SortMode, multiple: boolean) => { - if (!multiple) { - sortedColumns.clear(); - } - - if (sort !== null) { - sortedColumns.set(colId, { attribute: colId, orderAsc: sort === 'asc' }); - } else { - sortedColumns.delete(colId); - } - - const constraints = Array.from(sortedColumns.values()) - .map((constrait, idx) => ({ ...constrait, orderPosition: idx })); - - await refreshModel(constraints); - }, [refreshModel, sortedColumns]); - - const getColumnSortMode = useCallback((colId: string): SortMode => { - if (!sortedColumns.has(colId)) { - return null; - } - - return sortedColumns.get(colId)!.orderAsc ? 'asc' : 'desc'; - }, [sortedColumns]); - - const gridSortingContext: IDataGridSortingContext = useMemo(() => ({ - sortedColumns, - setSortMode: setColumnSortMode, - getSortMode: getColumnSortMode, - }), [sortedColumns, setColumnSortMode, getColumnSortMode]); - - return gridSortingContext; -} diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx index 73b1f9fe42..8189d872c2 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -23,8 +23,6 @@ import { reactGridStyles } from '../styles/styles'; import { DataGridContext, IColumnResizeInfo, IDataGridContext } from './DataGridContext'; import { DataGridSelectionContext } from './DataGridSelection/DataGridSelectionContext'; import { useGridSelectionContext } from './DataGridSelection/useGridSelectionContext'; -import { DataGridSortingContext } from './DataGridSorting/DataGridSortingContext'; -import { useGridSortingContext } from './DataGridSorting/useGridSortingContext'; import { CellFormatter } from './Formatters/CellFormatter'; import { RowRenderer } from './RowRenderer/RowRenderer'; import { TableDataContext } from './TableDataContext'; @@ -54,7 +52,6 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ const tableData = useTableData(model, resultIndex); - const gridSortingContext = useGridSortingContext(model); const gridSelectionContext = useGridSelectionContext(tableData, selectionAction); const editingContext = useEditing({ readonly: model.isReadonly(), @@ -155,42 +152,40 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ return styled(styles)( - - - - - - columnResize.execute({ column: idx, width })} - onScroll={handleScroll} - /> -
- - - - - + + + + + columnResize.execute({ column: idx, width })} + onScroll={handleScroll} + /> +
+ + + + ); }); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader.tsx index a1bf8578e5..3a54f4d3c5 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader.tsx @@ -15,11 +15,10 @@ import { StaticImage, Icon } from '@cloudbeaver/core-blocks'; import { useTranslate } from '@cloudbeaver/core-localization'; import type { SqlResultSet } from '@cloudbeaver/core-sdk'; import { composes, useStyles } from '@cloudbeaver/core-theming'; -import type { SortMode } from '@cloudbeaver/plugin-data-viewer'; +import { getNextSortMode, ResultSetSortAction } from '@cloudbeaver/plugin-data-viewer'; import { DataGridContext } from '../DataGridContext'; import { DataGridSelectionContext } from '../DataGridSelection/DataGridSelectionContext'; -import { DataGridSortingContext } from '../DataGridSorting/DataGridSortingContext'; const headerStyles = css` table-header { @@ -102,15 +101,15 @@ export const TableColumnHeader: React.FC> = observer(fu column: calculatedColumn, }) { const dataGridContext = useContext(DataGridContext); - const gridSortingContext = useContext(DataGridSortingContext); const gridSelectionContext = useContext(DataGridSelectionContext); const translate = useTranslate(); - if (!dataGridContext || !gridSortingContext || !gridSelectionContext) { - throw new Error('One of the following contexts are missed(data grid context, grid sorting context, grid selection context)'); + if (!dataGridContext || !gridSelectionContext) { + throw new Error('One of the following contexts are missed(data grid context, grid selection context)'); } const model = dataGridContext.model; + const sorting = model.source.getAction(dataGridContext.resultIndex, ResultSetSortAction); const columnName = calculatedColumn.name as string; const column = getColumn(Number(calculatedColumn.key), model.getResult(dataGridContext.resultIndex)?.data); @@ -118,7 +117,8 @@ export const TableColumnHeader: React.FC> = observer(fu // TODO we want to get "sortable" property from SqlResultColumn data const sortable = model.source.results.length === 1; - const currentSortMode = gridSortingContext.getSortMode(columnName); + const currentSortMode = sorting.getSortMode(columnName); + const columnTooltip = columnName + (column?.fullTypeName ? ': ' + column.fullTypeName : ''); const handleSort = (e: React.MouseEvent) => { @@ -126,19 +126,9 @@ export const TableColumnHeader: React.FC> = observer(fu if (loading) { return; } - - let nextSort: SortMode; - switch (currentSortMode) { - case 'asc': - nextSort = 'desc'; - break; - case 'desc': - nextSort = null; - break; - default: - nextSort = 'asc'; - } - gridSortingContext.setSortMode(columnName, nextSort, e.ctrlKey || e.metaKey); + const nextSortMode = getNextSortMode(sorting.getSortMode(columnName)); + sorting.setSortMode(columnName, nextSortMode, e.ctrlKey || e.metaKey); + model.refresh(); }; const handleColumnSelection = (e: React.MouseEvent) => { diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/locales/en.ts b/webapp/packages/plugin-data-spreadsheet-new/src/locales/en.ts index 8cb2385bd6..afddde70c7 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/locales/en.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/locales/en.ts @@ -1,3 +1,5 @@ export default [ + ['data_grid_table_sorting', 'Sorting'], + ['data_grid_table_sorting_toggle', 'Toggle sorting'], ['data_grid_table_tooltip_column_header_sort', 'Sort by column'], ]; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/locales/ru.ts b/webapp/packages/plugin-data-spreadsheet-new/src/locales/ru.ts index a091d87fd5..a9369744fd 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/locales/ru.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/locales/ru.ts @@ -1,3 +1,5 @@ export default [ + ['data_grid_table_sorting', 'Сортировка'], + ['data_grid_table_sorting_toggle', 'Переключить сортировку'], ['data_grid_table_tooltip_column_header_sort', 'Сортировка по колонке'], ]; diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataAction.ts index afb6d6cd79..c58bcdf289 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataAction.ts @@ -6,6 +6,8 @@ * you may not use this file except in compliance with the License. */ +import type { SqlResultColumn } from '@cloudbeaver/core-sdk'; + import type { IDatabaseDataAction } from '../IDatabaseDataAction'; import type { IDatabaseDataResult } from '../IDatabaseDataResult'; import type { IResultSetElementKey } from './ResultSet/IResultSetElementKey'; @@ -13,4 +15,5 @@ import type { IResultSetElementKey } from './ResultSet/IResultSetElementKey'; export interface IDatabaseResultDataAction extends IDatabaseDataAction { getCellValue: (cell: IResultSetElementKey) => any; + getColumn: (columnIndex: number) => SqlResultColumn | undefined; } diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataSortAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataSortAction.ts new file mode 100644 index 0000000000..2b5533cc47 --- /dev/null +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataSortAction.ts @@ -0,0 +1,17 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import type { IDatabaseDataAction } from '../IDatabaseDataAction'; +import type { IDatabaseDataResult } from '../IDatabaseDataResult'; +import type { SortMode } from './ResultSet/ResultSetSortAction'; + +export interface IDatabaseDataSortAction + extends IDatabaseDataAction { + setSortMode: (columnName: string, sortMode: SortMode, multiple: boolean) => void; + getSortMode: (columnName: string) => SortMode; +} diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataAction.ts index 6d520677cb..927ab760bb 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataAction.ts @@ -18,4 +18,12 @@ export class ResultSetDataAction extends DatabaseDataAction + implements IDatabaseDataSortAction { + static dataFormat = ResultDataFormat.Resultset; + + private clear() { + if (!this.source.options) { + return; + } + + this.source.options.constraints = []; + } + + private deleteConstraint(columnName: string) { + if (!this.source.options) { + return; + } + + this.source.options.constraints = this.source.options.constraints + .filter(constraint => constraint.attribute !== columnName); + } + + private reorderConstraints() { + if (!this.source.options) { + return; + } + + this.source.options.constraints = this.source.options.constraints + .map((constaint, idx) => ({ ...constaint, orderPosition: idx })); + } + + setSortMode(columnName: string, sortMode: SortMode, multiple: boolean): void { + if (!this.source.options) { + return; + } + + const resetSortMode = sortMode === null; + const currentConstraint = this.source.options.constraints.find(constraint => constraint.attribute === columnName); + + if (currentConstraint && multiple) { + if (resetSortMode) { + this.deleteConstraint(currentConstraint.attribute); + this.reorderConstraints(); + } else { + currentConstraint.orderAsc = sortMode === ESortMode.asc; + } + return; + } + + if (!multiple) { + this.clear(); + } + + if (!resetSortMode) { + this.source.options.constraints.push({ + attribute: columnName, + orderPosition: this.source.options.constraints.length, + orderAsc: sortMode === ESortMode.asc, + }); + } + } + + getSortMode(columnName: string): SortMode { + if (!this.source.options) { + throw new Error('Options must be provided'); + } + + const currentConstraints = this.source.options.constraints; + const currentConstraint = currentConstraints.find(constraint => constraint.attribute === columnName); + + if (!currentConstraint) { + return null; + } + + return currentConstraint.orderAsc ? ESortMode.asc : ESortMode.desc; + } +} + +export function getNextSortMode(sortMode: SortMode): SortMode { + switch (sortMode) { + case ESortMode.asc: + return ESortMode.desc; + case ESortMode.desc: + return null; + default: + return ESortMode.asc; + } +} diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewerModel.ts b/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewerModel.ts index 0a8a8d279e..2594c6e49a 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewerModel.ts +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TableViewerModel.ts @@ -12,6 +12,8 @@ import { Subject, Observable } from 'rxjs'; import type { SqlDataFilterConstraint } from '@cloudbeaver/core-sdk'; import { MetadataMap } from '@cloudbeaver/core-utils'; +import type { SortMode } from '../DatabaseDataModel/Actions/ResultSet/ResultSetSortAction'; +import { ESortMode } from '../DatabaseDataModel/Actions/ResultSet/ResultSetSortAction'; import { DatabaseDataAccessMode } from '../DatabaseDataModel/IDatabaseDataSource'; import type { RowDiff } from './TableDataModel/EditedRow'; import type { TableColumn } from './TableDataModel/TableColumn'; @@ -27,8 +29,6 @@ export const fetchingSettings = { export type AgGridRow = any[]; -export type SortMode = 'asc' | 'desc' | null; - export type SortModel = Array<{ colId: string; sort: SortMode; @@ -257,7 +257,7 @@ export class TableViewerModel { getSortModel(): SortModel { return Array.from(this.sortedColumns.values()).map(v => ({ colId: v.attribute, - sort: v.orderAsc ? 'asc' : 'desc', + sort: v.orderAsc ? ESortMode.asc : ESortMode.desc, })); } diff --git a/webapp/packages/plugin-data-viewer/src/index.ts b/webapp/packages/plugin-data-viewer/src/index.ts index 926794d867..4e71be961b 100644 --- a/webapp/packages/plugin-data-viewer/src/index.ts +++ b/webapp/packages/plugin-data-viewer/src/index.ts @@ -6,6 +6,7 @@ export * from './DatabaseDataModel/Actions/ResultSet/IResultSetElementKey'; export * from './DatabaseDataModel/Actions/ResultSet/ResultSetFormatAction'; export * from './DatabaseDataModel/Actions/ResultSet/ResultSetSelectAction'; export * from './DatabaseDataModel/Actions/ResultSet/ResultSetDataAction'; +export * from './DatabaseDataModel/Actions/ResultSet/ResultSetSortAction'; export * from './DatabaseDataModel/Actions/DatabaseDataActionDecorator'; export * from './DatabaseDataModel/Actions/IDatabaseDataFormatAction'; export * from './DatabaseDataModel/Actions/IDatabaseDataSelectAction';