mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
feat(plugin-data-viewer): CB-665 sort by column from context menu
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
+3
-3
@@ -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)) {
|
||||
|
||||
@@ -84,7 +84,7 @@ class ComputedMenuItemOptionsWithContext<T> 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<T> implements IComputedMenuItemOptions
|
||||
private context: IMenuContext<T>) {
|
||||
// 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<T> implements IComputedMenuItemOptions
|
||||
if (options.isChecked) {
|
||||
this.isChecked = () => options.isChecked!(this.context);
|
||||
}
|
||||
if (options.titleGetter) {
|
||||
this.titleGetter = () => options.titleGetter!(this.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<T> extends IMenuItemOptions {
|
||||
onClick?: (context: IMenuContext<T>) => void;
|
||||
titleGetter?: (context: IMenuContext<T>) => TLocalizationToken;
|
||||
// if isPresent is false menu item will not be included in resulting context menu
|
||||
isPresent: (context: IMenuContext<T>) => boolean;
|
||||
isDisabled?: (context: IMenuContext<T>) => boolean;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
+91
-10
@@ -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<any>;
|
||||
@@ -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<any>,
|
||||
resultIndex: number,
|
||||
@@ -43,28 +48,104 @@ export class DataGridContextMenuService {
|
||||
});
|
||||
}
|
||||
|
||||
private getColumnName(model: IDatabaseDataModel<any>, resultIndex: number, columnIndex: number) {
|
||||
const data = model.source.getAction(resultIndex, ResultSetDataAction);
|
||||
return data.getColumn(columnIndex)?.name;
|
||||
}
|
||||
|
||||
private async changeSortMode(
|
||||
model: IDatabaseDataModel<any>,
|
||||
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<any>, 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<IDataGridCellMenuContext>(
|
||||
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<IDataGridCellMenuContext>(
|
||||
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<IDataGridCellMenuContext>(
|
||||
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<IDataGridCellMenuContext>(
|
||||
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',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
-21
@@ -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<string, SqlDataFilterConstraint>;
|
||||
setSortMode: (colId: string, sort: SortMode, multiple: boolean) => Promise<void> | void;
|
||||
getSortMode: (colId: string) => SortMode;
|
||||
}
|
||||
|
||||
export const DataGridSortingContext = createContext<IDataGridSortingContext | null>(null);
|
||||
-56
@@ -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<any>) {
|
||||
const [sortedColumns] = useState(() => observable.map<string, SqlDataFilterConstraint>());
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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<Props> = 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<Props> = observer(function DataGridTable({
|
||||
|
||||
return styled(styles)(
|
||||
<DataGridContext.Provider value={gridContext}>
|
||||
<DataGridSortingContext.Provider value={gridSortingContext}>
|
||||
<DataGridSelectionContext.Provider value={gridSelectionContext}>
|
||||
<EditingContext.Provider value={editingContext}>
|
||||
<TableDataContext.Provider value={tableData}>
|
||||
<grid-container
|
||||
ref={gridContainerRef}
|
||||
as='div'
|
||||
className="cb-react-grid-container"
|
||||
tabIndex={-1}
|
||||
onKeyDown={onKeydownHandler}
|
||||
onMouseDown={onMouseDownHandler}
|
||||
onMouseMove={onMouseMoveHandler}
|
||||
>
|
||||
<DataGrid
|
||||
ref={dataGridRef}
|
||||
className={`cb-react-grid-theme ${className}`}
|
||||
columns={tableData.columns}
|
||||
defaultColumnOptions={{
|
||||
minWidth: 40,
|
||||
resizable: true,
|
||||
formatter: CellFormatter,
|
||||
}}
|
||||
rows={tableData.rows}
|
||||
headerRowHeight={28}
|
||||
rowHeight={24}
|
||||
rowRenderer={RowRenderer}
|
||||
onSelectedCellChange={handleFocusChange}
|
||||
onColumnResize={(idx, width) => columnResize.execute({ column: idx, width })}
|
||||
onScroll={handleScroll}
|
||||
/>
|
||||
<div ref={editorRef} />
|
||||
</grid-container>
|
||||
</TableDataContext.Provider>
|
||||
</EditingContext.Provider>
|
||||
</DataGridSelectionContext.Provider>
|
||||
</DataGridSortingContext.Provider>
|
||||
<DataGridSelectionContext.Provider value={gridSelectionContext}>
|
||||
<EditingContext.Provider value={editingContext}>
|
||||
<TableDataContext.Provider value={tableData}>
|
||||
<grid-container
|
||||
ref={gridContainerRef}
|
||||
as='div'
|
||||
className="cb-react-grid-container"
|
||||
tabIndex={-1}
|
||||
onKeyDown={onKeydownHandler}
|
||||
onMouseDown={onMouseDownHandler}
|
||||
onMouseMove={onMouseMoveHandler}
|
||||
>
|
||||
<DataGrid
|
||||
ref={dataGridRef}
|
||||
className={`cb-react-grid-theme ${className}`}
|
||||
columns={tableData.columns}
|
||||
defaultColumnOptions={{
|
||||
minWidth: 60,
|
||||
resizable: true,
|
||||
formatter: CellFormatter,
|
||||
}}
|
||||
rows={tableData.rows}
|
||||
headerRowHeight={28}
|
||||
rowHeight={24}
|
||||
rowRenderer={RowRenderer}
|
||||
onSelectedCellChange={handleFocusChange}
|
||||
onColumnResize={(idx, width) => columnResize.execute({ column: idx, width })}
|
||||
onScroll={handleScroll}
|
||||
/>
|
||||
<div ref={editorRef} />
|
||||
</grid-container>
|
||||
</TableDataContext.Provider>
|
||||
</EditingContext.Provider>
|
||||
</DataGridSelectionContext.Provider>
|
||||
</DataGridContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
+9
-19
@@ -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<HeaderRendererProps<any>> = 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<HeaderRendererProps<any>> = 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<HTMLDivElement>) => {
|
||||
@@ -126,19 +126,9 @@ export const TableColumnHeader: React.FC<HeaderRendererProps<any>> = 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<HTMLDivElement>) => {
|
||||
|
||||
@@ -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'],
|
||||
];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export default [
|
||||
['data_grid_table_sorting', 'Сортировка'],
|
||||
['data_grid_table_sorting_toggle', 'Переключить сортировку'],
|
||||
['data_grid_table_tooltip_column_header_sort', 'Сортировка по колонке'],
|
||||
];
|
||||
|
||||
+3
@@ -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<TKey, TResult extends IDatabaseDataResult>
|
||||
extends IDatabaseDataAction<any, TResult> {
|
||||
getCellValue: (cell: IResultSetElementKey) => any;
|
||||
getColumn: (columnIndex: number) => SqlResultColumn | undefined;
|
||||
}
|
||||
|
||||
+17
@@ -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<TKey, TResult extends IDatabaseDataResult>
|
||||
extends IDatabaseDataAction<any, TResult> {
|
||||
setSortMode: (columnName: string, sortMode: SortMode, multiple: boolean) => void;
|
||||
getSortMode: (columnName: string) => SortMode;
|
||||
}
|
||||
+8
@@ -18,4 +18,12 @@ export class ResultSetDataAction extends DatabaseDataAction<any, IDatabaseResult
|
||||
|
||||
return this.result.data.rows[cell.row][cell.column];
|
||||
}
|
||||
|
||||
getColumn(columnIndex: number) {
|
||||
if (!this.result.data?.columns) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.result.data.columns[columnIndex];
|
||||
}
|
||||
}
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 { ResultDataFormat } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { DatabaseDataAction } from '../../DatabaseDataAction';
|
||||
import type { IDatabaseDataOptions } from '../../IDatabaseDataOptions';
|
||||
import type { IDatabaseResultSet } from '../../IDatabaseResultSet';
|
||||
import { databaseDataAction } from '../DatabaseDataActionDecorator';
|
||||
import type { IDatabaseDataSortAction } from '../IDatabaseDataSortAction';
|
||||
import type { IResultSetElementKey } from './IResultSetElementKey';
|
||||
|
||||
export enum ESortMode {
|
||||
'asc' = 'asc',
|
||||
'desc' = 'desc'
|
||||
}
|
||||
|
||||
export type SortMode = ESortMode | null;
|
||||
|
||||
@databaseDataAction()
|
||||
export class ResultSetSortAction extends DatabaseDataAction<IDatabaseDataOptions, IDatabaseResultSet>
|
||||
implements IDatabaseDataSortAction<IResultSetElementKey, IDatabaseResultSet> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user