feat(plugin-data-viewer): CB-665 sort by column from context menu

This commit is contained in:
Naumov Alexey
2021-05-17 19:26:11 +03:00
parent 7550db718b
commit c39fae5b3b
20 changed files with 297 additions and 154 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -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;
@@ -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',
}
);
}
@@ -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);
@@ -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>
);
});
@@ -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', 'Сортировка по колонке'],
];
@@ -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;
}
@@ -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;
}
@@ -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];
}
}
@@ -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';