Skip to content

Commit

Permalink
CB-4401 feat: save value panel state per column (#2272)
Browse files Browse the repository at this point in the history
* CB-4401 feat: save value panel state per column

* CB-4401 fix: pr review

---------

Co-authored-by: Daria Marutkina <[email protected]>
  • Loading branch information
Wroud and dariamarutkina authored Jan 11, 2024
1 parent 5041312 commit b0586e7
Show file tree
Hide file tree
Showing 19 changed files with 271 additions and 105 deletions.
2 changes: 2 additions & 0 deletions webapp/packages/core-events/src/ExceptionsCatcherService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export class ExceptionsCatcherService extends Bootstrap {
});
}

console.error(_error);

if (this.baseCatcher) {
return this.baseCatcher(event, source, lineno, colno, _error);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2023 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 { ResultDataFormat } from '@cloudbeaver/core-sdk';

import { DatabaseDataAction } from '../DatabaseDataAction';
import type { IDatabaseDataResult } from '../IDatabaseDataResult';
import type { IDatabaseDataSource } from '../IDatabaseDataSource';
import { databaseDataAction } from './DatabaseDataActionDecorator';
import type { IDatabaseDataResultAction } from './IDatabaseDataResultAction';

@databaseDataAction()
export abstract class DatabaseDataResultAction<TKey, TResult extends IDatabaseDataResult>
extends DatabaseDataAction<any, TResult>
implements IDatabaseDataResultAction<TKey, TResult>
{
static dataFormat: ResultDataFormat[] | null = null;

constructor(source: IDatabaseDataSource<any, TResult>) {
super(source);
}
abstract getIdentifier(key: TKey): string;
abstract serialize(key: TKey): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2023 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 { ResultDataFormat } from '@cloudbeaver/core-sdk';
import { MetadataMap } from '@cloudbeaver/core-utils';

import { DatabaseDataAction } from '../DatabaseDataAction';
import type { IDatabaseDataResult } from '../IDatabaseDataResult';
import type { IDatabaseDataSource } from '../IDatabaseDataSource';
import { databaseDataAction } from './DatabaseDataActionDecorator';
import type { IDatabaseDataMetadataAction } from './IDatabaseDataMetadataAction';

@databaseDataAction()
export class DatabaseMetadataAction<TKey, TResult extends IDatabaseDataResult>
extends DatabaseDataAction<any, TResult>
implements IDatabaseDataMetadataAction<TKey, TResult>
{
static dataFormat: ResultDataFormat[] | null = null;
readonly metadata: MetadataMap<string, any>;

constructor(source: IDatabaseDataSource<any, TResult>) {
super(source);
this.metadata = new MetadataMap();
}

has(key: string): boolean {
return this.metadata.has(key);
}

get<T>(key: string): T | undefined;
get<T>(key: string, getDefaultValue: (() => T) | undefined): T;
get<T>(key: string, getDefaultValue?: (() => T) | undefined): T | undefined {
return this.metadata.get(key, getDefaultValue);
}

set(key: string, value: any): void {
this.metadata.set(key, value);
}

delete(key: string): void {
this.metadata.delete(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { computed, makeObservable } from 'mobx';

import { ResultDataFormat } from '@cloudbeaver/core-sdk';

import { DatabaseDataAction } from '../../DatabaseDataAction';
import type { IDatabaseDataSource } from '../../IDatabaseDataSource';
import type { IDatabaseResultSet } from '../../IDatabaseResultSet';
import { databaseDataAction } from '../DatabaseDataActionDecorator';
import type { IDatabaseDataResultAction } from '../IDatabaseDataResultAction';
import { DatabaseDataResultAction } from '../DatabaseDataResultAction';
import type { IDatabaseDataDocument } from './IDatabaseDataDocument';
import type { IDocumentElementKey } from './IDocumentElementKey';

@databaseDataAction()
export class DocumentDataAction extends DatabaseDataAction<any, IDatabaseResultSet> implements IDatabaseDataResultAction<IDatabaseResultSet> {
export class DocumentDataAction extends DatabaseDataResultAction<IDocumentElementKey, IDatabaseResultSet> {
static dataFormat = [ResultDataFormat.Document];

get documents(): IDatabaseDataDocument[] {
Expand All @@ -37,6 +37,14 @@ export class DocumentDataAction extends DatabaseDataAction<any, IDatabaseResultS
});
}

getIdentifier(key: IDocumentElementKey): string {
return key.index.toString();
}

serialize(key: IDocumentElementKey): string {
return key.index.toString();
}

get(index: number): IDatabaseDataDocument | undefined {
if (this.documents.length <= index) {
return undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2023 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';

export interface IDatabaseDataMetadataAction<TKey, TResult extends IDatabaseDataResult> extends IDatabaseDataAction<any, TResult> {
get<T>(key: string): T | undefined;
get<T>(key: string, getDefaultValue: () => T): T;
set(key: string, value: any): void;
delete(key: string): void;
has(key: string): boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@
import type { IDatabaseDataAction } from '../IDatabaseDataAction';
import type { IDatabaseDataResult } from '../IDatabaseDataResult';

export type IDatabaseDataResultAction<TResult extends IDatabaseDataResult> = IDatabaseDataAction<any, TResult>;
export interface IDatabaseDataResultAction<TKey, TResult extends IDatabaseDataResult> extends IDatabaseDataAction<any, TResult> {
getIdentifier(key: TKey): string;
serialize(key: TKey): string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import { computed, makeObservable } from 'mobx';

import { DataTypeLogicalOperation, ResultDataFormat, SqlResultColumn } from '@cloudbeaver/core-sdk';

import { DatabaseDataAction } from '../../DatabaseDataAction';
import type { IDatabaseDataSource } from '../../IDatabaseDataSource';
import type { IDatabaseResultSet } from '../../IDatabaseResultSet';
import { databaseDataAction } from '../DatabaseDataActionDecorator';
import type { IDatabaseDataResultAction } from '../IDatabaseDataResultAction';
import { DatabaseDataResultAction } from '../DatabaseDataResultAction';
import type { IResultSetContentValue } from './IResultSetContentValue';
import type { IResultSetColumnKey, IResultSetElementKey, IResultSetRowKey } from './IResultSetDataKey';
import { isResultSetContentValue } from './isResultSetContentValue';
import { ResultSetDataKeysUtils } from './ResultSetDataKeysUtils';
import type { IResultSetValue } from './ResultSetFormatAction';

@databaseDataAction()
export class ResultSetDataAction extends DatabaseDataAction<any, IDatabaseResultSet> implements IDatabaseDataResultAction<IDatabaseResultSet> {
export class ResultSetDataAction extends DatabaseDataResultAction<IResultSetElementKey, IDatabaseResultSet> {
static dataFormat = [ResultDataFormat.Resultset];

get rows(): IResultSetValue[][] {
Expand All @@ -39,6 +39,14 @@ export class ResultSetDataAction extends DatabaseDataAction<any, IDatabaseResult
});
}

getIdentifier(key: IResultSetElementKey): string {
return ResultSetDataKeysUtils.serialize(key.column);
}

serialize(key: IResultSetElementKey): string {
return ResultSetDataKeysUtils.serializeElementKey(key);
}

getDefaultKey(): IResultSetElementKey {
return {
row: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { action, makeObservable, observable } from 'mobx';
import { DataTypeLogicalOperation, ResultDataFormat, SqlResultColumn } from '@cloudbeaver/core-sdk';

import { DatabaseDataAction } from '../../DatabaseDataAction';
import type { IDatabaseDataAction } from '../../IDatabaseDataAction';
import type { IDatabaseDataSource } from '../../IDatabaseDataSource';
import type { IDatabaseResultSet } from '../../IDatabaseResultSet';
import { databaseDataAction } from '../DatabaseDataActionDecorator';
import type { IDatabaseDataResultAction } from '../IDatabaseDataResultAction';
import { compareResultSetRowKeys } from './compareResultSetRowKeys';
import type { IResultSetComplexValue } from './IResultSetComplexValue';
import type { IResultSetColumnKey, IResultSetElementKey, IResultSetRowKey } from './IResultSetDataKey';
Expand All @@ -24,7 +24,7 @@ import { ResultSetEditAction } from './ResultSetEditAction';
import type { IResultSetValue } from './ResultSetFormatAction';

@databaseDataAction()
export class ResultSetViewAction extends DatabaseDataAction<any, IDatabaseResultSet> implements IDatabaseDataResultAction<IDatabaseResultSet> {
export class ResultSetViewAction extends DatabaseDataAction<any, IDatabaseResultSet> implements IDatabaseDataAction<any, IDatabaseResultSet> {
static dataFormat = [ResultDataFormat.Resultset];

get rowKeys(): IResultSetRowKey[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
* 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 { observer } from 'mobx-react-lite';
import { useRef, useState } from 'react';
import styled, { css } from 'reshadow';

import { useService } from '@cloudbeaver/core-di';
import { BASE_TAB_STYLES, TabList, TabPanelList, TabsState, UNDERLINE_TAB_STYLES } from '@cloudbeaver/core-ui';
import { MetadataMap } from '@cloudbeaver/core-utils';

import { DatabaseDataResultAction } from '../../DatabaseDataModel/Actions/DatabaseDataResultAction';
import { DatabaseMetadataAction } from '../../DatabaseDataModel/Actions/DatabaseMetadataAction';
import { DatabaseSelectAction } from '../../DatabaseDataModel/Actions/DatabaseSelectAction';
import type { IDatabaseResultSet } from '../../DatabaseDataModel/IDatabaseResultSet';
import type { DataPresentationComponent } from '../../DataPresentationService';
import { DataValuePanelService } from './DataValuePanelService';
Expand Down Expand Up @@ -47,17 +51,37 @@ const styles = css`

export const ValuePanel: DataPresentationComponent<any, IDatabaseResultSet> = observer(function ValuePanel({ dataFormat, model, resultIndex }) {
const service = useService(DataValuePanelService);
const [currentTabId, setCurrentTabId] = useState('');
const lastTabId = useRef('');
const selectAction = model.source.getActionImplementation(resultIndex, DatabaseSelectAction);
const dataResultAction = model.source.getActionImplementation(resultIndex, DatabaseDataResultAction);
const metadataAction = model.source.getAction(resultIndex, DatabaseMetadataAction);
const activeElements = selectAction?.getActiveElements();
let elementKey: string | null = null;

if (dataResultAction && activeElements && activeElements.length > 0) {
elementKey = dataResultAction.getIdentifier(activeElements[0]);
}

const state = metadataAction.get(`value-panel-${elementKey}`, () =>
observable(
{
currentTabId: '',
tabsState: new MetadataMap<string, any>(),
setCurrentTabId(tabId: string) {
this.currentTabId = tabId;
},
},
{ tabsState: false },
{},
),
);

const displayed = service.getDisplayed({ dataFormat, model, resultIndex });
let currentTabId = state.currentTabId;

if (displayed.length > 0) {
const firstTabId = displayed[0].key;
if (firstTabId !== lastTabId.current) {
setCurrentTabId(firstTabId);
lastTabId.current = firstTabId;
}
const hasCurrentTabCells = currentTabId && displayed.some(tab => tab.key === currentTabId);

if (displayed.length > 0 && !hasCurrentTabCells) {
currentTabId = displayed[0].key;
}

return styled(
Expand All @@ -71,8 +95,9 @@ export const ValuePanel: DataPresentationComponent<any, IDatabaseResultSet> = ob
dataFormat={dataFormat}
model={model}
resultIndex={resultIndex}
localState={state.tabsState}
lazy
onChange={tab => setCurrentTabId(tab.tabId)}
onChange={tab => state.setCurrentTabId(tab.tabId)}
>
<TabList style={[BASE_TAB_STYLES, styles, UNDERLINE_TAB_STYLES]} />
<TabPanelList style={[BASE_TAB_STYLES, styles, UNDERLINE_TAB_STYLES]} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export const BooleanValuePresentation: TabContainerPanelComponent<IDataValuePane
function BooleanValuePresentation({ model, resultIndex }) {
const translate = useTranslate();
const selection = model.source.getAction(resultIndex, ResultSetSelectAction);
const focusCell = selection.getFocusedElement();
const activeElements = selection.getActiveElements();

if (!selection.elements.length && !focusCell) {
if (activeElements.length === 0) {
return null;
}

Expand All @@ -44,7 +44,7 @@ export const BooleanValuePresentation: TabContainerPanelComponent<IDataValuePane
const view = model.source.getAction(resultIndex, ResultSetViewAction);
const editor = model.source.getAction(resultIndex, ResultSetEditAction);

const firstSelectedCell = selection.elements[0] || focusCell;
const firstSelectedCell = activeElements[0];
const cellValue = view.getCellValue(firstSelectedCell);

if (typeof cellValue === 'string' && isStringifiedBoolean(cellValue)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ export class BooleanValuePresentationBootstrap extends Bootstrap {

const selection = context.model.source.getAction(context.resultIndex, ResultSetSelectAction);

const focusedElement = selection.getFocusedElement();
const activeElements = selection.getActiveElements();

if (selection.elements.length > 0 || focusedElement) {
if (activeElements.length > 0) {
const view = context.model.source.getAction(context.resultIndex, ResultSetViewAction);

const firstSelectedCell = selection.elements[0] || focusedElement;
const firstSelectedCell = activeElements[0];
const cellValue = view.getCellValue(firstSelectedCell);

if (cellValue === undefined) {
Expand Down
Loading

0 comments on commit b0586e7

Please sign in to comment.