mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge remote-tracking branch 'origin/devel' into devel
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
<extension point="org.eclipse.core.runtime.products" id="product">
|
||||
<product application="io.cloudbeaver.product.ce.application" description="Cloudbeaver Web UI Application" name="CloudBeaver CE Server">
|
||||
<property name="appName" value="CloudBeaver CE Server"/>
|
||||
<property name="versionUpdateURL" value="https://cloudbeaver.io/product/cloudbeaver-ce-version.xml"/>
|
||||
<property name="versionUpdateURL" value="https://cloudbeaver.io/product/cloudbeaver-ce-version.json"/>
|
||||
</product>
|
||||
</extension>
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ interface IState {
|
||||
|
||||
export interface IMouseHook<T> {
|
||||
state: IState;
|
||||
ref: T | null;
|
||||
reference: (obj: T) => void;
|
||||
}
|
||||
|
||||
@@ -73,5 +74,5 @@ export function useMouse<T extends HTMLElement>(options: IOptions = {}): IMouseH
|
||||
};
|
||||
}, [reference]);
|
||||
|
||||
return { state, reference: setReference };
|
||||
return { ref: reference, state, reference: setReference };
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
|
||||
const package = require(resolve('package.json'));
|
||||
|
||||
const certPath = resolve(__dirname, '../../../../../server-cert.pem')
|
||||
const keyPath = resolve(__dirname, '../../../../../server-key.pem')
|
||||
const certPath = resolve(__dirname, '../../../../../certs/private.pem')
|
||||
const keyPath = resolve(__dirname, '../../../../../certs/private.key')
|
||||
let https = false;
|
||||
|
||||
|
||||
// if(fs.existsSync(certPath) && fs.existsSync(keyPath)) {
|
||||
// https = {
|
||||
// key: fs.readFileSync(keyPath),
|
||||
// cert: fs.readFileSync(certPath)
|
||||
// }
|
||||
// }
|
||||
if(fs.existsSync(certPath) && fs.existsSync(keyPath)) {
|
||||
https = {
|
||||
key: fs.readFileSync(keyPath),
|
||||
cert: fs.readFileSync(certPath),
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (env, argv) => merge(commonConfig(env, argv), {
|
||||
entry: {
|
||||
|
||||
@@ -11,7 +11,7 @@ export default [
|
||||
['administration_configuration_wizard_configuration', 'Server configuration'],
|
||||
['administration_configuration_wizard_configuration_step_description', 'Main server configuration'],
|
||||
['administration_configuration_wizard_configuration_title', 'You can configure the main server parameters here.'],
|
||||
['administration_configuration_wizard_configuration_message', 'You can allow integration to external services such as SSO once the easy configuration is completed.\n\r Administrator is a super user who can configure server, set databases connections, manage other users and much more. Please, remember the entered password. It is not possible to recover administrator password automatically.'],
|
||||
['administration_configuration_wizard_configuration_message', 'You will be able to add additional services after the server configuration.\n\r Administrator is a super user who can configure server, set databases connections, manage other users and much more. Please, remember the entered password. It is not possible to recover administrator password automatically.'],
|
||||
|
||||
['administration_configuration_tools_save_tooltip', 'Save configuration'],
|
||||
['administration_configuration_tools_cancel_tooltip', 'Reset changes'],
|
||||
|
||||
+17
-3
@@ -43,14 +43,19 @@ export const AuthenticationProviders: PlaceholderComponent<IConfigurationPlaceho
|
||||
});
|
||||
|
||||
const localProvider = providers.resource.get(AUTH_PROVIDER_LOCAL_ID);
|
||||
const primaryProvider = providers.resource.get(providers.resource.getPrimary());
|
||||
const externalAuthentication = localProvider === undefined && providerList.length === 1;
|
||||
const authenticationDisabled = serverConfig.enabledAuthProviders?.length === 0;
|
||||
|
||||
useExecutor({
|
||||
executor: formContext.changeExecutor,
|
||||
handlers: [function switchControls() {
|
||||
if (serverConfig.enabledAuthProviders?.length === 0 && localProvider) {
|
||||
serverConfig.anonymousAccessEnabled = true;
|
||||
if (serverConfig.enabledAuthProviders?.length === 0) {
|
||||
if (localProvider) {
|
||||
serverConfig.anonymousAccessEnabled = true;
|
||||
} else if (primaryProvider) {
|
||||
serverConfig.enabledAuthProviders.push(primaryProvider.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (serverConfig.enabledAuthProviders?.length) {
|
||||
@@ -91,9 +96,18 @@ export const AuthenticationProviders: PlaceholderComponent<IConfigurationPlaceho
|
||||
<>
|
||||
{providerList.map(provider => {
|
||||
const links = authProviderService.getServiceDescriptionLinks(provider);
|
||||
const disabled = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat));
|
||||
let disabled = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat));
|
||||
const tooltip = disabled ? `Following services need to be enabled: "${provider.requiredFeatures.join(', ')}"` : '';
|
||||
|
||||
if (
|
||||
!localProvider
|
||||
&& primaryProvider?.id === provider.id
|
||||
&& serverConfig.enabledAuthProviders?.length === 1
|
||||
&& serverConfig.enabledAuthProviders.includes(provider.id)
|
||||
) {
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
if (
|
||||
configurationWizard
|
||||
&& (
|
||||
|
||||
@@ -41,7 +41,7 @@ export interface IEditorRef {
|
||||
focus: () => void;
|
||||
}
|
||||
|
||||
const lockNavigation = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'];
|
||||
const lockNavigation = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter'];
|
||||
|
||||
export const CellEditor = observer<Pick<EditorProps<IResultSetRowKey>, 'row' | 'column' | 'onClose'>, IEditorRef>(function CellEditor({
|
||||
row,
|
||||
|
||||
+31
-25
@@ -8,11 +8,11 @@
|
||||
|
||||
import { computed, observable } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext, useEffect, useMemo } from 'react';
|
||||
import type { CellRendererProps } from 'react-data-grid';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import type { CalculatedColumn, CellRendererProps } from 'react-data-grid';
|
||||
import { Cell } from 'react-data-grid';
|
||||
|
||||
import { useMouse, useObjectRef, useObservableRef } from '@cloudbeaver/core-blocks';
|
||||
import { getComputed, useMouse, useObjectRef, useObservableRef } from '@cloudbeaver/core-blocks';
|
||||
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
|
||||
import { IResultSetElementKey, IResultSetRowKey, isBooleanValuePresentationAvailable, DatabaseEditChangeType } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
@@ -65,7 +65,7 @@ export const CellRenderer = observer<CellRendererProps<IResultSetRowKey>>(functi
|
||||
editionState: computed,
|
||||
}, { row, column, rowIdx });
|
||||
|
||||
const classes = useMemo(() => computed(() => {
|
||||
const classes = getComputed(() => {
|
||||
let classes = '';
|
||||
if (cellContext.isSelected) {
|
||||
classes += ' rdg-cell-custom-selected';
|
||||
@@ -88,7 +88,31 @@ export const CellRenderer = observer<CellRendererProps<IResultSetRowKey>>(functi
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}), []).get();
|
||||
});
|
||||
|
||||
function isEditable(column: CalculatedColumn<IResultSetRowKey, unknown>): boolean {
|
||||
if (
|
||||
!editingContext
|
||||
|| !tableDataContext
|
||||
|| !cellContext.cell
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const resultColumn = tableDataContext.getColumnInfo(cellContext.cell.column);
|
||||
const value = tableDataContext.getCellValue(cellContext.cell);
|
||||
|
||||
if (!resultColumn || value === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const handleByBooleanFormatter = isBooleanValuePresentationAvailable(value, resultColumn);
|
||||
|
||||
return !(
|
||||
handleByBooleanFormatter
|
||||
|| tableDataContext.isCellReadonly(cellContext.cell)
|
||||
);
|
||||
}
|
||||
|
||||
const state = useObjectRef(() => ({
|
||||
mouseUp(event: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
@@ -112,27 +136,8 @@ export const CellRenderer = observer<CellRendererProps<IResultSetRowKey>>(functi
|
||||
},
|
||||
doubleClick(event: React.MouseEvent<HTMLDivElement>) {
|
||||
if (
|
||||
!this.editingContext
|
||||
|| !this.tableDataContext
|
||||
!this.isEditable(this.column)
|
||||
|| EventContext.has(event, EventStopPropagationFlag)
|
||||
|| !cellContext.cell
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resultColumn = this.tableDataContext.getColumnInfo(cellContext.cell.column);
|
||||
const value = this.tableDataContext.getCellValue(cellContext.cell);
|
||||
|
||||
if (!resultColumn || value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleByBooleanFormatter = isBooleanValuePresentationAvailable(value, resultColumn);
|
||||
|
||||
if (
|
||||
!this.column.editable
|
||||
|| handleByBooleanFormatter
|
||||
|| this.tableDataContext.format.isReadOnly(cellContext.cell)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -147,6 +152,7 @@ export const CellRenderer = observer<CellRendererProps<IResultSetRowKey>>(functi
|
||||
dataGridContext,
|
||||
editingContext,
|
||||
tableDataContext,
|
||||
isEditable,
|
||||
}, ['doubleClick', 'mouseUp']);
|
||||
|
||||
useEffect(() => () => editingContext?.closeEditor(cellContext.position), []);
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface IDataGridContext {
|
||||
isGridInFocus: () => boolean;
|
||||
getEditorPortal: () => HTMLDivElement | null;
|
||||
getDataGridApi: () => DataGridHandle | null;
|
||||
focus: () => void;
|
||||
}
|
||||
|
||||
export const DataGridContext = createContext<IDataGridContext>(undefined as any);
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { isBooleanValuePresentationAvailable, DatabaseEditChangeType, ResultSetEditAction, ResultSetFormatAction, ResultSetViewAction, DatabaseSelectAction, ResultSetSelectAction } from '@cloudbeaver/plugin-data-viewer';
|
||||
import { isBooleanValuePresentationAvailable, DatabaseEditChangeType, ResultSetEditAction, ResultSetFormatAction, ResultSetViewAction, ResultSetSelectAction } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { DataGridContextMenuService } from './DataGridContextMenuService';
|
||||
|
||||
@@ -124,7 +124,7 @@ export class DataGridContextMenuCellEditingService {
|
||||
},
|
||||
onClick(context) {
|
||||
const editor = context.data.model.source.getAction(context.data.resultIndex, ResultSetEditAction);
|
||||
editor.addRowCopy(context.data.key.row);
|
||||
editor.duplicateRow(context.data.key.row);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
+4
-4
@@ -62,7 +62,7 @@ export class DataGridContextMenuFilterService {
|
||||
|
||||
await model.requestDataAction(async () => {
|
||||
constraints.setFilter(columnLabel, operator, filterValue);
|
||||
await model.refresh(true);
|
||||
await model.request(true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ export class DataGridContextMenuFilterService {
|
||||
|
||||
await model.requestDataAction(async () => {
|
||||
constraints.deleteData();
|
||||
await model.refresh(true);
|
||||
await model.request(true);
|
||||
});
|
||||
},
|
||||
}
|
||||
@@ -403,7 +403,7 @@ export class DataGridContextMenuFilterService {
|
||||
|
||||
await model.requestDataAction(async () => {
|
||||
constraints.deleteFilter(columnLabel);
|
||||
await model.refresh(true);
|
||||
await model.request(true);
|
||||
});
|
||||
},
|
||||
}
|
||||
@@ -430,7 +430,7 @@ export class DataGridContextMenuFilterService {
|
||||
|
||||
await model.requestDataAction(async () => {
|
||||
constraints.deleteDataFilters();
|
||||
await model.refresh(true);
|
||||
await model.request(true);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ export class DataGridContextMenuOrderService {
|
||||
|
||||
await model.requestDataAction(async () => {
|
||||
constraints.setOrder(columnLabel, order, true);
|
||||
await model.refresh(true);
|
||||
await model.request(true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ export class DataGridContextMenuOrderService {
|
||||
const constraints = context.data.model.source.getAction(context.data.resultIndex, ResultSetConstraintAction);
|
||||
await context.data.model.requestDataAction(async () => {
|
||||
constraints.deleteOrders();
|
||||
await context.data.model.refresh(true);
|
||||
await context.data.model.request(true);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,9 +13,12 @@ import type { DataGridHandle } from 'react-data-grid';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { TextPlaceholder, useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
|
||||
import { Executor } from '@cloudbeaver/core-executor';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
import { ClipboardService } from '@cloudbeaver/core-ui';
|
||||
import {
|
||||
DatabaseDataSelectActionsData, DatabaseEditChangeType, IDatabaseResultSet, IDataPresentationProps,
|
||||
IResultSetEditActionData, IResultSetElementKey, IResultSetPartialKey, ResultSetDataKeysUtils, ResultSetSelectAction
|
||||
@@ -51,6 +54,7 @@ const headerHeight = 28;
|
||||
export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResultSet>>(function DataGridTable({ model, actions, resultIndex, className }) {
|
||||
const translate = useTranslate();
|
||||
|
||||
const clipboardService = useService(ClipboardService);
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const editorRef = useRef<HTMLDivElement>(null);
|
||||
const dataGridDivRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -64,13 +68,11 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
|
||||
const selectionAction = model.source.getAction(resultIndex, ResultSetSelectAction);
|
||||
|
||||
const tableData = useTableData(model, resultIndex, dataGridDivRef);
|
||||
const focusSyncRef = useRef<CellPosition | null>(null);
|
||||
|
||||
const gridSelectionContext = useGridSelectionContext(tableData, selectionAction);
|
||||
const editingContext = useEditing({
|
||||
readonly: model.isReadonly() || model.isDisabled(resultIndex),
|
||||
onEdit: (position, key) => {
|
||||
onEdit: (position, code, key) => {
|
||||
const column = tableData.getColumn(position.idx);
|
||||
const row = tableData.getRow(position.rowIdx);
|
||||
|
||||
@@ -80,22 +82,42 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
|
||||
const cellKey: IResultSetElementKey = { row, column: column.columnDataIndex };
|
||||
|
||||
// TODO: not works yet
|
||||
switch (key) {
|
||||
case 'Delete':
|
||||
if (tableData.isCellReadonly(cellKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case 'Backspace':
|
||||
tableData.editor.set(cellKey, '');
|
||||
break;
|
||||
case 'Enter':
|
||||
break;
|
||||
default:
|
||||
if (key) {
|
||||
tableData.editor.set(cellKey, key);
|
||||
if (/^[\d\p{L}]$/iu.test(key) && key.length === 1) {
|
||||
tableData.editor.set(cellKey, key);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onCloseEditor: () => {
|
||||
restoreFocus();
|
||||
},
|
||||
});
|
||||
|
||||
const tableData = useTableData(model, resultIndex, dataGridDivRef);
|
||||
const gridSelectionContext = useGridSelectionContext(tableData, selectionAction);
|
||||
|
||||
function restoreFocus() {
|
||||
const gridDiv = gridContainerRef.current;
|
||||
const focusSink = gridDiv?.querySelector<HTMLDivElement>('.rdg-focus-sink');
|
||||
focusSink?.focus();
|
||||
}
|
||||
|
||||
function isGridInFocus(): boolean {
|
||||
const gridDiv = gridContainerRef.current;
|
||||
const focusSink = gridDiv?.querySelector('.rdg-focus-sink');
|
||||
@@ -121,7 +143,7 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
}
|
||||
}
|
||||
|
||||
const { onKeydownHandler } = useGridSelectedCellsCopy(tableData, selectionAction, gridSelectionContext);
|
||||
const gridSelectedCellCopy = useGridSelectedCellsCopy(tableData, selectionAction, gridSelectionContext);
|
||||
const { onMouseDownHandler, onMouseMoveHandler } = useGridDragging({
|
||||
onDragStart: startPosition => {
|
||||
dataGridRef.current?.selectCell({ idx: startPosition.colIdx, rowIdx: startPosition.rowIdx });
|
||||
@@ -134,6 +156,99 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
},
|
||||
});
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
gridSelectedCellCopy.onKeydownHandler(event);
|
||||
|
||||
if (EventContext.has(event, EventStopPropagationFlag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cell = selectionAction.getFocusedElement();
|
||||
const activeElements = selectionAction.getActiveElements();
|
||||
const activeRows = selectionAction.getActiveRows();
|
||||
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = tableData.getColumnIndexFromColumnKey(cell.column);
|
||||
const rowIdx = tableData.getRowIndexFromKey(cell.row);
|
||||
const position: CellPosition = { idx, rowIdx };
|
||||
|
||||
if (editingContext.isEditing(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.nativeEvent.code) {
|
||||
case 'Escape': {
|
||||
tableData.editor.revert(...activeElements);
|
||||
return;
|
||||
}
|
||||
case 'Insert': {
|
||||
if (event.altKey) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
tableData.editor.duplicate(...activeRows);
|
||||
} else {
|
||||
tableData.editor.add(cell);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const editingState = tableData.editor.getElementState(cell);
|
||||
|
||||
switch (event.nativeEvent.code) {
|
||||
case 'Delete': {
|
||||
const filteredRows = activeRows
|
||||
.filter(cell => tableData.editor.getElementState(cell) !== DatabaseEditChangeType.delete);
|
||||
|
||||
if (filteredRows.length > 0) {
|
||||
const editor = tableData.editor;
|
||||
const firstRow = filteredRows[0];
|
||||
const editingState = tableData.editor.getElementState(firstRow);
|
||||
|
||||
editor.delete(...filteredRows);
|
||||
|
||||
if (editingState === DatabaseEditChangeType.add) {
|
||||
if (rowIdx - 1 > 0) {
|
||||
dataGridRef.current?.selectCell({ idx, rowIdx: rowIdx - 1 });
|
||||
}
|
||||
} else {
|
||||
if (rowIdx + 1 < tableData.rows.length) {
|
||||
dataGridRef.current?.selectCell({ idx, rowIdx: rowIdx + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case 'KeyV': {
|
||||
if (editingState === DatabaseEditChangeType.delete) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (!clipboardService.clipboardAvailable || clipboardService.state === 'denied' || tableData.isCellReadonly(cell)) {
|
||||
return;
|
||||
}
|
||||
|
||||
clipboardService
|
||||
.read()
|
||||
.then(value => tableData.editor.set(cell, value))
|
||||
.catch();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (editingState === DatabaseEditChangeType.delete) {
|
||||
return;
|
||||
}
|
||||
|
||||
editingContext.edit({ idx, rowIdx }, event.nativeEvent.code, event.key);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
function syncEditor(data: IResultSetEditActionData) {
|
||||
const editor = tableData.editor;
|
||||
@@ -234,7 +349,7 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
|
||||
selectionAction.focus({
|
||||
row,
|
||||
column: column.columnDataIndex ?? { index: 0 },
|
||||
column: { index: 0, ...column.columnDataIndex },
|
||||
});
|
||||
};
|
||||
|
||||
@@ -267,7 +382,8 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
isGridInFocus,
|
||||
getEditorPortal: () => editorRef.current,
|
||||
getDataGridApi: () => dataGridRef.current,
|
||||
}), [model, actions, resultIndex, editorRef, dataGridRef, gridContainerRef]);
|
||||
focus: restoreFocus,
|
||||
}), [model, actions, resultIndex, editorRef, dataGridRef, gridContainerRef, restoreFocus]);
|
||||
|
||||
if (!tableData.columns.length) {
|
||||
return <TextPlaceholder>{translate('data_grid_table_empty_placeholder')}</TextPlaceholder>;
|
||||
@@ -282,7 +398,7 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
ref={setContainersRef}
|
||||
className="cb-react-grid-container"
|
||||
tabIndex={-1}
|
||||
onKeyDown={onKeydownHandler}
|
||||
onKeyDown={handleKeyDown}
|
||||
onMouseDown={onMouseDownHandler}
|
||||
onMouseMove={onMouseMoveHandler}
|
||||
>
|
||||
@@ -290,7 +406,6 @@ export const DataGridTable = observer<IDataPresentationProps<any, IDatabaseResul
|
||||
ref={dataGridRef}
|
||||
className={`cb-react-grid-theme ${className}`}
|
||||
columns={tableData.columns}
|
||||
cellNavigationMode={editingContext.isEditorActive() ? 'NONE' : 'CHANGE_ROW'}
|
||||
defaultColumnOptions={{
|
||||
minWidth: 50,
|
||||
resizable: true,
|
||||
|
||||
+6
-1
@@ -70,15 +70,20 @@ export const OrderButton = observer<Props>(function OrderButton({
|
||||
const nextOrder = getNextOrder(currentOrder);
|
||||
await model.requestDataAction(async () => {
|
||||
constraints.setOrder(attribute, nextOrder, e.ctrlKey || e.metaKey);
|
||||
await model.refresh(true);
|
||||
await model.request(true);
|
||||
});
|
||||
};
|
||||
|
||||
function preventFocus(event: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
return styled(styles)(
|
||||
<order-button
|
||||
as='div'
|
||||
title={translate('data_grid_table_tooltip_column_header_order')}
|
||||
className={className}
|
||||
onMouseDown={preventFocus}
|
||||
onClick={handleSort}
|
||||
{...use({ disabled: loading })}
|
||||
>
|
||||
|
||||
+9
-4
@@ -93,13 +93,18 @@ export const TableColumnHeader = observer<HeaderRendererProps<any>>(function Tab
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleColumnSelection = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
gridSelectionContext.selectColumn(calculatedColumn.idx, e.ctrlKey || e.metaKey);
|
||||
};
|
||||
|
||||
function preventFocus(event: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function handleClick(event: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
gridSelectionContext.selectColumn(calculatedColumn.idx, event.ctrlKey || event.metaKey);
|
||||
}
|
||||
|
||||
return styled(useStyles(headerStyles))(
|
||||
<table-header>
|
||||
<shrink-container as='div' title={columnTooltip} onClick={handleColumnSelection}>
|
||||
<shrink-container as='div' title={columnTooltip} onMouseDown={preventFocus} onClick={handleClick}>
|
||||
<icon>
|
||||
{icon && <StaticImage icon={icon} />}
|
||||
{!dataReadonly && columnReadOnly && <readonly-status className='rdg-table-header__readonly-status' />}
|
||||
|
||||
+13
-4
@@ -43,12 +43,21 @@ export const TableIndexColumnHeader = observer<HeaderRendererProps<any>>(functio
|
||||
throw new Error('Contexts required');
|
||||
}
|
||||
|
||||
const readonly = getComputed(
|
||||
() => tableDataContext.isReadOnly() || dataGridContext.model.isReadonly()
|
||||
);
|
||||
const readonly = getComputed(() => (
|
||||
tableDataContext.isReadOnly()
|
||||
|| dataGridContext.model.isReadonly()
|
||||
));
|
||||
|
||||
function preventFocus(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
||||
selectionContext.selectTable();
|
||||
}
|
||||
|
||||
return styled(styles)(
|
||||
<container title={translate('data_grid_table_index_column_tooltip')} onClick={() => selectionContext.selectTable()}>
|
||||
<container title={translate('data_grid_table_index_column_tooltip')} onMouseDown={preventFocus} onClick={handleClick}>
|
||||
{readonly && <IconOrImage title={translate('data_grid_table_readonly_tooltip')} icon='/icons/lock.png' />}
|
||||
{props.column.name}
|
||||
</container>
|
||||
|
||||
@@ -49,6 +49,7 @@ export interface ITableData {
|
||||
isIndexColumn: (columnKey: string) => boolean;
|
||||
isIndexColumnInRange: (columnsRange: Array<Column<IResultSetRowKey, any>>) => boolean;
|
||||
isReadOnly: () => boolean;
|
||||
isCellReadonly: (key: Partial<IResultSetElementKey>) => boolean;
|
||||
}
|
||||
|
||||
export const TableDataContext = createContext<ITableData>(undefined as any);
|
||||
|
||||
+5
-5
@@ -9,6 +9,7 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
|
||||
import { copyToClipboard } from '@cloudbeaver/core-utils';
|
||||
import { IResultSetColumnKey, IResultSetElementKey, ResultSetDataKeysUtils, ResultSetSelectAction } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
@@ -65,9 +66,6 @@ function getSelectedCellsValue(
|
||||
return rowsValues.join('\r\n');
|
||||
}
|
||||
|
||||
// needed for event.code
|
||||
type IKeyboardEvent = React.KeyboardEvent<HTMLDivElement> & KeyboardEvent;
|
||||
|
||||
export function useGridSelectedCellsCopy(
|
||||
tableData: ITableData,
|
||||
resultSetSelectAction: ResultSetSelectAction,
|
||||
@@ -75,8 +73,10 @@ export function useGridSelectedCellsCopy(
|
||||
) {
|
||||
const props = useObjectRef({ tableData, selectionContext, resultSetSelectAction });
|
||||
|
||||
const onKeydownHandler = useCallback((event: IKeyboardEvent) => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.code === EVENT_KEY_CODE.C) {
|
||||
const onKeydownHandler = useCallback((event: React.KeyboardEvent) => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.nativeEvent.code === EVENT_KEY_CODE.C) {
|
||||
EventContext.set(event, EventStopPropagationFlag);
|
||||
|
||||
const focusedElement = props.resultSetSelectAction.getFocusedElement();
|
||||
let value: string | null = null;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { Column } from 'react-data-grid';
|
||||
|
||||
import { useObservableRef } from '@cloudbeaver/core-blocks';
|
||||
import { TextTools, uuid } from '@cloudbeaver/core-utils';
|
||||
import { IDatabaseDataModel, IDatabaseResultSet, IResultSetColumnKey, IResultSetRowKey, ResultSetDataAction, ResultSetDataKeysUtils, ResultSetEditAction, ResultSetFormatAction, ResultSetViewAction } from '@cloudbeaver/plugin-data-viewer';
|
||||
import { IDatabaseDataModel, IDatabaseResultSet, IResultSetColumnKey, IResultSetElementKey, IResultSetRowKey, ResultSetDataAction, ResultSetDataKeysUtils, ResultSetEditAction, ResultSetFormatAction, ResultSetViewAction } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { IndexFormatter } from './Formatters/IndexFormatter';
|
||||
import { TableColumnHeader } from './TableColumnHeader/TableColumnHeader';
|
||||
@@ -33,7 +33,8 @@ export const indexColumn: Column<IResultSetRowKey, any> = {
|
||||
export function useTableData(
|
||||
model: IDatabaseDataModel<any, IDatabaseResultSet>,
|
||||
resultIndex: number,
|
||||
gridDIVElement: React.RefObject<HTMLDivElement | null>
|
||||
gridDIVElement: React.RefObject<HTMLDivElement | null>,
|
||||
onCellKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void
|
||||
): ITableData {
|
||||
const format = model.source.getAction(resultIndex, ResultSetFormatAction);
|
||||
const data = model.source.getAction(resultIndex, ResultSetDataAction);
|
||||
@@ -77,6 +78,9 @@ export function useTableData(
|
||||
editable: true,
|
||||
width: Math.min(300, measuredCells[index]),
|
||||
headerRenderer: TableColumnHeader,
|
||||
editorOptions: {
|
||||
onCellKeyDown,
|
||||
},
|
||||
}));
|
||||
columns.unshift(indexColumn);
|
||||
|
||||
@@ -152,6 +156,18 @@ export function useTableData(
|
||||
isReadOnly() {
|
||||
return this.columnKeys.every(column => this.getColumnInfo(column)?.readOnly);
|
||||
},
|
||||
isCellReadonly(key: Partial<IResultSetElementKey>) {
|
||||
if (!key.column) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const column = this.getColumnByDataIndex(key.column);
|
||||
|
||||
return (
|
||||
!column.editable
|
||||
|| this.format.isReadOnly(key)
|
||||
);
|
||||
},
|
||||
}), {
|
||||
columns: computed,
|
||||
rows: computed,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { createContext } from 'react';
|
||||
|
||||
export interface IEditingContext {
|
||||
readonly readonly: boolean;
|
||||
edit: (position: CellPosition, key?: string) => void;
|
||||
edit: (position: CellPosition, code?: string, key?: string) => void;
|
||||
closeEditor: (position: CellPosition) => void;
|
||||
close: () => void;
|
||||
isEditorActive: () => boolean;
|
||||
|
||||
@@ -24,7 +24,8 @@ function getPositionHash(position: CellPosition): string {
|
||||
|
||||
interface IEditingOptions {
|
||||
readonly?: boolean;
|
||||
onEdit: (position: CellPosition, key?: string) => boolean;
|
||||
onEdit: (position: CellPosition, code?: string, key?: string) => boolean;
|
||||
onCloseEditor?: (position: CellPosition) => void;
|
||||
}
|
||||
|
||||
export function useEditing(options: IEditingOptions): IEditingContext {
|
||||
@@ -40,18 +41,12 @@ export function useEditing(options: IEditingOptions): IEditingContext {
|
||||
get readonly() {
|
||||
return state.readonly;
|
||||
},
|
||||
edit(position: CellPosition, key?: string) {
|
||||
edit(position: CellPosition, code?: string, key?: string) {
|
||||
if (state.options.readonly) {
|
||||
return;
|
||||
}
|
||||
// TODO: not works yet
|
||||
switch (key) {
|
||||
case 'Escape':
|
||||
state.editingCells.delete(getPositionHash(position));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.options.onEdit(position, key)) {
|
||||
if (!state.options.onEdit(position, code, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,8 +57,15 @@ export function useEditing(options: IEditingOptions): IEditingContext {
|
||||
},
|
||||
closeEditor(position: CellPosition) {
|
||||
const info = state.editingCells.get(getPositionHash(position));
|
||||
|
||||
if (!info.editing) {
|
||||
return;
|
||||
}
|
||||
|
||||
info.editing = false;
|
||||
state.editorOpened = false;
|
||||
|
||||
state.options.onCloseEditor?.(position);
|
||||
},
|
||||
close() {
|
||||
state.editingCells.clear();
|
||||
|
||||
@@ -126,7 +126,7 @@ export class DataViewerTabService {
|
||||
// TODO: used for initial data fetch, but can repeat request each time data tab is selected,
|
||||
// so probably should be refactored and managed by presentation
|
||||
if (model.source.error === null && model.source.results.length === 0) {
|
||||
model.refresh();
|
||||
model.request();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ export class DataViewerTabService {
|
||||
await model.requestDataAction(() => {
|
||||
canClose = true;
|
||||
});
|
||||
} catch {}
|
||||
} catch { }
|
||||
|
||||
return canClose;
|
||||
}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ export abstract class DatabaseEditAction<TKey, TValue, TResult extends IDatabase
|
||||
abstract get(key: TKey): TValue | undefined;
|
||||
abstract set(key: TKey, value: TValue): void;
|
||||
abstract add(key?: TKey): void;
|
||||
abstract addCopy(key: TKey): void;
|
||||
abstract duplicate(...key: TKey[]): void;
|
||||
abstract delete(...key: TKey[]): void;
|
||||
abstract applyUpdate(result: TResult): void;
|
||||
abstract revert(...key: TKey[]): void;
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ export abstract class DatabaseSelectAction<TKey, TResult extends IDatabaseDataRe
|
||||
abstract isElementSelected(key: TKey): boolean;
|
||||
abstract getFocusedElement(): TKey | null;
|
||||
abstract getSelectedElements(): TKey[];
|
||||
abstract getActiveElements(): TKey[];
|
||||
abstract set(key: TKey, selected: boolean): void;
|
||||
abstract clear(): void;
|
||||
}
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ export class DocumentEditAction
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
addCopy(key: IDocumentElementKey): void {
|
||||
duplicate(key: IDocumentElementKey): void {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ export interface IDatabaseDataEditAction<TKey, TValue, TResult extends IDatabase
|
||||
get: (key: TKey) => TValue | undefined;
|
||||
set: (key: TKey, value: TValue) => void;
|
||||
add: (key?: TKey) => void;
|
||||
addCopy: (key: TKey) => void;
|
||||
duplicate: (...key: TKey[]) => void;
|
||||
delete: (key: TKey) => void;
|
||||
applyUpdate: (result: TResult) => void;
|
||||
revert: (key: TKey) => void;
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 { createAction } from '@cloudbeaver/core-view';
|
||||
|
||||
export const DATA_VIEWER_CONSTRAINTS_DELETE_ACTION = createAction('data-viewer-constraints-delete', {
|
||||
label: 'data_grid_table_delete_filters_and_orders',
|
||||
tooltip: 'data_grid_table_delete_filters_and_orders',
|
||||
icon: 'erase',
|
||||
});
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 { createAction } from '@cloudbeaver/core-view';
|
||||
|
||||
export const DATA_VIEWER_REFRESH_ACTION = createAction('data-viewer-refresh', {
|
||||
label: 'ui_refresh',
|
||||
tooltip: 'data_viewer_refresh_result_set',
|
||||
icon: 'reload',
|
||||
});
|
||||
+25
-11
@@ -208,21 +208,35 @@ export class ResultSetEditAction
|
||||
}
|
||||
}
|
||||
|
||||
addCopy(key: IResultSetElementKey): void {
|
||||
this.addRowCopy(key.row);
|
||||
}
|
||||
duplicate(...keys: IResultSetElementKey[]): void {
|
||||
const rows: IResultSetRowKey[] = [];
|
||||
const rowKeys = new Set<string>();
|
||||
|
||||
addRowCopy(row: IResultSetRowKey): void {
|
||||
let value = this.data.getRowValue(row);
|
||||
for (const key of keys) {
|
||||
const serialized = ResultSetDataKeysUtils.serialize(key.row);
|
||||
|
||||
const editedValue = this.editorData
|
||||
.get(ResultSetDataKeysUtils.serialize(row));
|
||||
|
||||
if (editedValue) {
|
||||
value = editedValue.update;
|
||||
if (!rowKeys.has(serialized)) {
|
||||
rows.push(key.row);
|
||||
rowKeys.add(serialized);
|
||||
}
|
||||
}
|
||||
|
||||
this.addRow(row, JSON.parse(JSON.stringify(value)));
|
||||
this.duplicateRow(...rows);
|
||||
}
|
||||
|
||||
duplicateRow(...rows: IResultSetRowKey[]): void {
|
||||
for (const row of rows) {
|
||||
let value = this.data.getRowValue(row);
|
||||
|
||||
const editedValue = this.editorData
|
||||
.get(ResultSetDataKeysUtils.serialize(row));
|
||||
|
||||
if (editedValue) {
|
||||
value = editedValue.update;
|
||||
}
|
||||
|
||||
this.addRow(row, JSON.parse(JSON.stringify(value)));
|
||||
}
|
||||
}
|
||||
|
||||
delete(...keys: IResultSetElementKey[]): void {
|
||||
|
||||
+42
-2
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
|
||||
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, toJS } from 'mobx';
|
||||
|
||||
import { ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
|
||||
import { ResultDataFormat } from '@cloudbeaver/core-sdk';
|
||||
@@ -160,6 +160,46 @@ export class ResultSetSelectAction extends DatabaseSelectAction<any, IDatabaseRe
|
||||
return Array.from(this.selectedElements.values()).flat();
|
||||
}
|
||||
|
||||
getActiveElements(): IResultSetElementKey[] {
|
||||
const elements = this.getSelectedElements();
|
||||
const focus = this.getFocusedElement();
|
||||
|
||||
if (elements.length === 0 && focus) {
|
||||
return [focus];
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
getSelectedRows(): IResultSetElementKey[] {
|
||||
const cells: IResultSetElementKey[] = [];
|
||||
const rowsKeys = new Set<string>();
|
||||
|
||||
const elements = this.getSelectedElements();
|
||||
|
||||
for (const cell of elements) {
|
||||
const key = ResultSetDataKeysUtils.serialize(cell.row);
|
||||
|
||||
if (!rowsKeys.has(key)) {
|
||||
cells.push(cell);
|
||||
rowsKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
getActiveRows(): IResultSetElementKey[] {
|
||||
const elements = this.getSelectedRows();
|
||||
const focus = this.getFocusedElement();
|
||||
|
||||
if (elements.length === 0 && focus) {
|
||||
return [focus];
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
set(key: IResultSetPartialKey, selected: boolean, silent?: boolean): void {
|
||||
if (key.row === undefined) {
|
||||
for (const row of this.view.rowKeys) {
|
||||
@@ -233,7 +273,7 @@ export class ResultSetSelectAction extends DatabaseSelectAction<any, IDatabaseRe
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusedElement = key;
|
||||
this.focusedElement = toJS(key);
|
||||
this.actions.execute({
|
||||
type: 'focus',
|
||||
resultId: this.result.id,
|
||||
|
||||
@@ -118,6 +118,14 @@ implements IDatabaseDataModel<TOptions, TResult> {
|
||||
}
|
||||
|
||||
async refresh(concurrent?: boolean): Promise<void> {
|
||||
if (concurrent) {
|
||||
await this.source.refreshData();
|
||||
return;
|
||||
}
|
||||
await this.requestDataAction(() => this.source.refreshData());
|
||||
}
|
||||
|
||||
async request(concurrent?: boolean): Promise<void> {
|
||||
if (concurrent) {
|
||||
await this.source.requestData();
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable, makeObservable, action } from 'mobx';
|
||||
import { observable, makeObservable, action, toJS } from 'mobx';
|
||||
|
||||
import type { IConnectionExecutionContext } from '@cloudbeaver/core-connections';
|
||||
import { ResultDataFormat } from '@cloudbeaver/core-sdk';
|
||||
@@ -26,6 +26,7 @@ implements IDatabaseDataSource<TOptions, TResult> {
|
||||
results: TResult[];
|
||||
offset: number;
|
||||
count: number;
|
||||
prevOptions: Readonly<TOptions> | null;
|
||||
options: TOptions | null;
|
||||
requestInfo: IRequestInfo;
|
||||
error: Error | null;
|
||||
@@ -45,6 +46,7 @@ implements IDatabaseDataSource<TOptions, TResult> {
|
||||
this.results = [];
|
||||
this.offset = 0;
|
||||
this.count = 0;
|
||||
this.prevOptions = null;
|
||||
this.options = null;
|
||||
this.disabled = false;
|
||||
this.activeRequest = null;
|
||||
@@ -69,6 +71,7 @@ implements IDatabaseDataSource<TOptions, TResult> {
|
||||
results: observable,
|
||||
offset: observable,
|
||||
count: observable,
|
||||
prevOptions: observable,
|
||||
options: observable,
|
||||
requestInfo: observable,
|
||||
error: observable.ref,
|
||||
@@ -276,6 +279,13 @@ implements IDatabaseDataSource<TOptions, TResult> {
|
||||
}
|
||||
}
|
||||
|
||||
async refreshData(): Promise<void> {
|
||||
if (this.prevOptions) {
|
||||
this.options = toJS(this.prevOptions);
|
||||
}
|
||||
await this.requestData();
|
||||
}
|
||||
|
||||
async saveData(): Promise<void> {
|
||||
if (this.activeRequest) {
|
||||
try {
|
||||
@@ -318,6 +328,7 @@ implements IDatabaseDataSource<TOptions, TResult> {
|
||||
abstract dispose(): Promise<void>;
|
||||
|
||||
async requestDataAction(): Promise<TResult[] | null> {
|
||||
this.prevOptions = toJS(this.options);
|
||||
return this.request(this.results);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface IDatabaseDataModel<TOptions = any, TResult extends IDatabaseDat
|
||||
retry: () => Promise<void>;
|
||||
save: () => Promise<void>;
|
||||
refresh: (concurrent?: boolean) => Promise<void>;
|
||||
request: (concurrent?: boolean) => Promise<void>;
|
||||
reload: () => Promise<void>;
|
||||
requestDataPortion: (offset: number, count: number) => Promise<void>;
|
||||
cancel: () => Promise<void> | void;
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface IDatabaseDataSource<TOptions, TResult extends IDatabaseDataResu
|
||||
readonly results: TResult[];
|
||||
readonly offset: number;
|
||||
readonly count: number;
|
||||
readonly prevOptions: Readonly<TOptions> | null;
|
||||
readonly options: TOptions | null;
|
||||
readonly requestInfo: IRequestInfo;
|
||||
readonly error: Error | null;
|
||||
@@ -80,6 +81,7 @@ export interface IDatabaseDataSource<TOptions, TResult extends IDatabaseDataResu
|
||||
retry: () => Promise<void>;
|
||||
runTask: <T>(task: () => Promise<T>) => Promise<T>;
|
||||
requestData: () => Promise<void> | void;
|
||||
refreshData: () => Promise<void> | void;
|
||||
saveData: () => Promise<void> | void;
|
||||
cancel: () => Promise<void> | void;
|
||||
clearError: () => void;
|
||||
|
||||
+11
-27
@@ -101,19 +101,17 @@ export class TableFooterMenuService {
|
||||
return !editor?.hasFeature('add');
|
||||
},
|
||||
isDisabled(context) {
|
||||
const select = context.data.model.source.getActionImplementation(
|
||||
context.data.resultIndex,
|
||||
DatabaseSelectAction
|
||||
);
|
||||
|
||||
const focus = select?.getFocusedElement();
|
||||
|
||||
return (
|
||||
if (
|
||||
context.data.model.isLoading()
|
||||
|| context.data.model.isDisabled(context.data.resultIndex)
|
||||
|| !context.data.model.source.hasResult(context.data.resultIndex)
|
||||
|| !focus
|
||||
);
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const selectedElements = getActiveElements(context.data.model, context.data.resultIndex);
|
||||
|
||||
return selectedElements.length === 0;
|
||||
},
|
||||
onClick(context) {
|
||||
const editor = context.data.model.source.getActionImplementation(
|
||||
@@ -125,16 +123,9 @@ export class TableFooterMenuService {
|
||||
return;
|
||||
}
|
||||
|
||||
const select = context.data.model.source.getActionImplementation(
|
||||
context.data.resultIndex,
|
||||
DatabaseSelectAction
|
||||
);
|
||||
const selectedElements = getActiveElements(context.data.model, context.data.resultIndex);
|
||||
|
||||
const focus = select?.getFocusedElement();
|
||||
|
||||
if (focus) {
|
||||
editor.addCopy(focus);
|
||||
}
|
||||
editor.duplicate(...selectedElements);
|
||||
},
|
||||
});
|
||||
this.registerMenuItem({
|
||||
@@ -375,12 +366,5 @@ function getActiveElements(model: IDatabaseDataModel<any, IDatabaseDataResult>,
|
||||
DatabaseSelectAction
|
||||
);
|
||||
|
||||
const selectedElements = select?.getSelectedElements() || [];
|
||||
const focus = select?.getFocusedElement();
|
||||
|
||||
if (selectedElements.length === 0 && focus) {
|
||||
selectedElements.push(focus);
|
||||
}
|
||||
|
||||
return selectedElements;
|
||||
return select?.getActiveElements() ?? [];
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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 { createDataContext } from '@cloudbeaver/core-view';
|
||||
|
||||
import type { IDatabaseDataModel } from '../../DatabaseDataModel/IDatabaseDataModel';
|
||||
import type { IDatabaseDataOptions } from '../../DatabaseDataModel/IDatabaseDataOptions';
|
||||
|
||||
export const DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL = createDataContext<IDatabaseDataModel<IDatabaseDataOptions, any>>('data-viewer-database-data-model');
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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 { createDataContext } from '@cloudbeaver/core-view';
|
||||
|
||||
export const DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX = createDataContext<number>('data-viewer-database-data-model-result-index');
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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 { createMenu } from '@cloudbeaver/core-view';
|
||||
|
||||
export const DATA_VIEWER_DATA_MODEL_TOOLS_MENU = createMenu('data-viewer-data-model-tools', 'Data viewer data model tools menu');
|
||||
@@ -27,16 +27,18 @@ const styles = css`
|
||||
interface Props {
|
||||
model: IDatabaseDataModel<any, any>;
|
||||
resultIndex: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TableHeader = observer<Props>(function TableHeader({
|
||||
model,
|
||||
resultIndex,
|
||||
className,
|
||||
}) {
|
||||
const service = useService(TableHeaderService);
|
||||
|
||||
return styled(styles)(
|
||||
<table-header as="div">
|
||||
<table-header className={className}>
|
||||
<Placeholder container={service.tableHeaderPlaceholder} model={model} resultIndex={resultIndex} />
|
||||
</table-header>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 { observer } from 'mobx-react-lite';
|
||||
import { css } from 'reshadow';
|
||||
|
||||
import type { PlaceholderComponent } from '@cloudbeaver/core-blocks';
|
||||
import { composes } from '@cloudbeaver/core-theming';
|
||||
import { MenuBar } from '@cloudbeaver/core-ui';
|
||||
import { useMenu } from '@cloudbeaver/core-view';
|
||||
|
||||
import { DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL } from './DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL';
|
||||
import { DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX } from './DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX';
|
||||
import { DATA_VIEWER_DATA_MODEL_TOOLS_MENU } from './DATA_VIEWER_DATA_MODEL_TOOLS_MENU';
|
||||
import type { ITableHeaderPlaceholderProps } from './TableHeaderService';
|
||||
|
||||
const TABLE_HEADER_MENU_BAR_STYLES = composes(
|
||||
css`
|
||||
menu-bar {
|
||||
composes: theme-border-color-background theme-background-surface theme-text-on-surface from global;
|
||||
}
|
||||
menu-bar-item {
|
||||
composes: theme-ripple from global;
|
||||
}
|
||||
MenuSeparator {
|
||||
composes: theme-border-color-background from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
menu-bar {
|
||||
composes: theme-typography--body2 from global;
|
||||
display: flex;
|
||||
margin-left: 8px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
menu-bar-item {
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
|
||||
&[use|hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& IconOrImage {
|
||||
display: block;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
& Loader {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
& item-label {
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
& IconOrImage + item-label, & Loader + item-label {
|
||||
padding-left: 8px
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-right: 1px solid;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
export const TableHeaderMenu: PlaceholderComponent<ITableHeaderPlaceholderProps> = observer(function TableHeaderMenu({
|
||||
model,
|
||||
resultIndex,
|
||||
}) {
|
||||
const menu = useMenu(DATA_VIEWER_DATA_MODEL_TOOLS_MENU);
|
||||
|
||||
menu.context.set(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL, model);
|
||||
menu.context.set(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX, resultIndex);
|
||||
|
||||
return (
|
||||
<MenuBar menu={menu} style={TABLE_HEADER_MENU_BAR_STYLES} />
|
||||
);
|
||||
});
|
||||
@@ -8,8 +8,15 @@
|
||||
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
import { MenuService, ActionService, DATA_CONTEXT_MENU } from '@cloudbeaver/core-view';
|
||||
|
||||
import { DATA_VIEWER_CONSTRAINTS_DELETE_ACTION } from '../../DatabaseDataModel/Actions/ResultSet/Actions/DATA_VIEWER_CONSTRAINTS_DELETE_ACTION';
|
||||
import { ResultSetConstraintAction } from '../../DatabaseDataModel/Actions/ResultSet/ResultSetConstraintAction';
|
||||
import type { IDatabaseDataModel } from '../../DatabaseDataModel/IDatabaseDataModel';
|
||||
import { DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL } from './DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL';
|
||||
import { DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX } from './DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX';
|
||||
import { DATA_VIEWER_DATA_MODEL_TOOLS_MENU } from './DATA_VIEWER_DATA_MODEL_TOOLS_MENU';
|
||||
import { TableHeaderMenu } from './TableHeaderMenu';
|
||||
import { TableWhereFilter } from './TableWhereFilter';
|
||||
|
||||
export interface ITableHeaderPlaceholderProps {
|
||||
@@ -21,8 +28,79 @@ export interface ITableHeaderPlaceholderProps {
|
||||
export class TableHeaderService extends Bootstrap {
|
||||
readonly tableHeaderPlaceholder = new PlaceholderContainer<ITableHeaderPlaceholderProps>();
|
||||
|
||||
constructor(
|
||||
private readonly menuService: MenuService,
|
||||
private readonly actionService: ActionService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
register(): void {
|
||||
this.tableHeaderPlaceholder.add(TableWhereFilter, 1);
|
||||
this.tableHeaderPlaceholder.add(TableHeaderMenu, 2);
|
||||
|
||||
this.actionService.addHandler({
|
||||
id: 'table-header-menu-base-handler',
|
||||
isActionApplicable(context, action) {
|
||||
const menu = context.find(DATA_CONTEXT_MENU, DATA_VIEWER_DATA_MODEL_TOOLS_MENU);
|
||||
const model = context.tryGet(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL);
|
||||
const resultIndex = context.tryGet(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX);
|
||||
|
||||
if (!menu || !model || resultIndex === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
handler: async (context, action) => {
|
||||
switch (action) {
|
||||
case DATA_VIEWER_CONSTRAINTS_DELETE_ACTION: {
|
||||
const model = context.get(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL);
|
||||
const resultIndex = context.get(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX);
|
||||
const constraints = model.source.tryGetAction(resultIndex, ResultSetConstraintAction);
|
||||
|
||||
if (constraints) {
|
||||
constraints.deleteData();
|
||||
await model.request();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
getActionInfo: (context, action) => {
|
||||
if (context.get(DATA_CONTEXT_MENU) === DATA_VIEWER_DATA_MODEL_TOOLS_MENU) {
|
||||
return { ...action.info, label: '' };
|
||||
}
|
||||
|
||||
return action.info;
|
||||
},
|
||||
isDisabled: (context, action) => {
|
||||
const model = context.get(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL);
|
||||
const resultIndex = context.get(DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX);
|
||||
|
||||
if (model.isLoading() || model.isDisabled(resultIndex)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action === DATA_VIEWER_CONSTRAINTS_DELETE_ACTION) {
|
||||
const constraints = model.source.tryGetAction(resultIndex, ResultSetConstraintAction);
|
||||
|
||||
if (constraints) {
|
||||
return constraints.filterConstraints.length === 0 && constraints.orderConstraints.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
this.menuService.addCreator({
|
||||
isApplicable: context => context.get(DATA_CONTEXT_MENU) === DATA_VIEWER_DATA_MODEL_TOOLS_MENU,
|
||||
getItems: (context, items) => [
|
||||
...items,
|
||||
DATA_VIEWER_CONSTRAINTS_DELETE_ACTION,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
load(): void | Promise<void> { }
|
||||
|
||||
+9
-55
@@ -7,16 +7,15 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { InlineEditor } from '@cloudbeaver/core-app';
|
||||
import { PlaceholderComponent, useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import type { PlaceholderComponent } from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { ResultSetConstraintAction } from '../../DatabaseDataModel/Actions/ResultSet/ResultSetConstraintAction';
|
||||
import type { ITableHeaderPlaceholderProps } from './TableHeaderService';
|
||||
import { useWhereFilter } from './useWhereFilter';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
@@ -37,65 +36,20 @@ export const TableWhereFilter: PlaceholderComponent<ITableHeaderPlaceholderProps
|
||||
resultIndex,
|
||||
}) {
|
||||
const translate = useTranslate();
|
||||
const hasResult = model.source.hasResult(resultIndex);
|
||||
let filterValue = model.source.options?.whereFilter || '';
|
||||
let supported = false;
|
||||
|
||||
if (hasResult) {
|
||||
const constraints = model.source.tryGetAction(resultIndex, ResultSetConstraintAction);
|
||||
supported = constraints?.supported || false;
|
||||
if (constraints && constraints.filterConstraints.length > 0 && model.source.requestInfo.requestFilter) {
|
||||
filterValue = model.source.requestInfo.requestFilter;
|
||||
}
|
||||
}
|
||||
|
||||
const setValue = useCallback((filterValue: string) => {
|
||||
model.source.options.whereFilter = filterValue;
|
||||
|
||||
const constraints = model.source.tryGetAction(resultIndex, ResultSetConstraintAction);
|
||||
constraints?.deleteFilters();
|
||||
}, [model.source.options]);
|
||||
|
||||
const props = useObjectRef({ model, resultIndex, filterValue });
|
||||
|
||||
const handleApply = useCallback(() => {
|
||||
const { model, resultIndex } = props;
|
||||
if (model.isLoading() || model.isDisabled(resultIndex)) {
|
||||
return;
|
||||
}
|
||||
model.refresh();
|
||||
}, []);
|
||||
|
||||
const resetFilter = useCallback(async () => {
|
||||
const { model, resultIndex } = props;
|
||||
const constraints = model.source.getAction(resultIndex, ResultSetConstraintAction);
|
||||
if (model.isLoading() || model.isDisabled(resultIndex)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await model.requestDataAction(async () => {
|
||||
constraints.deleteDataFilters();
|
||||
|
||||
const applyNeeded = !!model.requestInfo.requestFilter;
|
||||
if (applyNeeded) {
|
||||
await model.refresh(true);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
const state = useWhereFilter(model, resultIndex);
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<InlineEditor
|
||||
name="data_where"
|
||||
value={filterValue}
|
||||
value={state.filter}
|
||||
placeholder={translate('table_header_sql_expression')}
|
||||
controlsPosition='inside'
|
||||
edited={!!filterValue}
|
||||
disableSave={model.source.options?.whereFilter === model.source.requestInfo.requestFilter}
|
||||
disabled={!supported || model.isLoading() || model.isDisabled(resultIndex)}
|
||||
edited={!!state.filter}
|
||||
disableSave={!state.applicableFilter}
|
||||
disabled={state.disabled}
|
||||
simple
|
||||
onSave={handleApply}
|
||||
onUndo={resetFilter}
|
||||
onChange={setValue}
|
||||
onSave={state.apply}
|
||||
onChange={state.set}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 { action, computed } from 'mobx';
|
||||
|
||||
import { useObservableRef } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import { ResultSetConstraintAction } from '../../DatabaseDataModel/Actions/ResultSet/ResultSetConstraintAction';
|
||||
import type { IDatabaseDataModel } from '../../DatabaseDataModel/IDatabaseDataModel';
|
||||
import type { IDatabaseDataOptions } from '../../DatabaseDataModel/IDatabaseDataOptions';
|
||||
|
||||
interface IState {
|
||||
model: IDatabaseDataModel<IDatabaseDataOptions, any>;
|
||||
resultIndex: number;
|
||||
readonly filter: string;
|
||||
readonly constraints: ResultSetConstraintAction | null;
|
||||
readonly disabled: boolean;
|
||||
readonly applicableFilter: boolean;
|
||||
set: (value: string) => void;
|
||||
apply: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function useWhereFilter(
|
||||
model: IDatabaseDataModel<IDatabaseDataOptions, any>,
|
||||
resultIndex: number
|
||||
): Readonly<IState> {
|
||||
return useObservableRef(() => ({
|
||||
get filter() {
|
||||
if (this.constraints?.filterConstraints.length && this.model.source.requestInfo.requestFilter) {
|
||||
return this.model.requestInfo.requestFilter;
|
||||
}
|
||||
|
||||
return this.model.source.options?.whereFilter ?? '';
|
||||
},
|
||||
get constraints() {
|
||||
if (!this.model.source.hasResult(this.resultIndex)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.model.source.tryGetAction(this.resultIndex, ResultSetConstraintAction) ?? null;
|
||||
},
|
||||
get disabled() {
|
||||
const supported = this.constraints?.supported ?? false;
|
||||
return !supported || this.model.isLoading() || this.model.isDisabled(resultIndex);
|
||||
},
|
||||
get applicableFilter() {
|
||||
return this.model.source.prevOptions?.whereFilter !== this.model.source.options?.whereFilter
|
||||
|| this.model.source.options?.whereFilter !== this.model.source.requestInfo.requestFilter;
|
||||
},
|
||||
set(value: string) {
|
||||
if (this.constraints) {
|
||||
this.constraints.deleteFilters();
|
||||
}
|
||||
|
||||
if (this.model.source.options) {
|
||||
this.model.source.options.whereFilter = value;
|
||||
}
|
||||
},
|
||||
async apply() {
|
||||
if (!this.applicableFilter || this.model.isLoading() || this.model.isDisabled(this.resultIndex)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.model.request();
|
||||
},
|
||||
}),
|
||||
{
|
||||
filter: computed,
|
||||
constraints: computed,
|
||||
disabled: computed,
|
||||
applicableFilter: computed,
|
||||
set: action.bound,
|
||||
apply: action.bound,
|
||||
},
|
||||
{ model, resultIndex });
|
||||
}
|
||||
@@ -77,6 +77,7 @@ const viewerStyles = composes(
|
||||
margin: 0;
|
||||
}
|
||||
TablePresentationBar {
|
||||
padding-top: 40px;
|
||||
&:first-child {
|
||||
margin-right: 4px;
|
||||
}
|
||||
@@ -235,7 +236,6 @@ export const TableViewer = observer<Props>(function TableViewer({
|
||||
|
||||
return styled(styles)(
|
||||
<table-viewer className={className}>
|
||||
<TableHeader model={dataModel} resultIndex={resultIndex} />
|
||||
<table-content>
|
||||
<TablePresentationBar
|
||||
type={DataPresentationType.main}
|
||||
@@ -247,6 +247,7 @@ export const TableViewer = observer<Props>(function TableViewer({
|
||||
onPresentationChange={dataTableActions.setPresentation}
|
||||
/>
|
||||
<table-data>
|
||||
<TableHeader model={dataModel} resultIndex={resultIndex} />
|
||||
<Split sticky={30} mode={valuePanelDisplayed ? undefined : 'minimize'} keepRatio>
|
||||
<Pane>
|
||||
<pane-content>
|
||||
@@ -257,6 +258,13 @@ export const TableViewer = observer<Props>(function TableViewer({
|
||||
presentation={presentation}
|
||||
resultIndex={resultIndex}
|
||||
/>
|
||||
<TableError model={dataModel} loading={loading} />
|
||||
<Loader
|
||||
loading={loading}
|
||||
cancelDisabled={!dataModel.source.canCancel}
|
||||
overlay={overlay}
|
||||
onCancel={() => dataModel.source.cancel()}
|
||||
/>
|
||||
</pane-content>
|
||||
</Pane>
|
||||
{valuePanelDisplayed && <ResizerControls />}
|
||||
@@ -274,13 +282,7 @@ export const TableViewer = observer<Props>(function TableViewer({
|
||||
</pane-content>
|
||||
</Pane>
|
||||
</Split>
|
||||
<TableError model={dataModel} loading={loading} />
|
||||
<Loader
|
||||
loading={loading}
|
||||
cancelDisabled={!dataModel.source.canCancel}
|
||||
overlay={overlay}
|
||||
onCancel={() => dataModel.source.cancel()}
|
||||
/>
|
||||
|
||||
</table-data>
|
||||
<TablePresentationBar
|
||||
type={DataPresentationType.toolsPanel}
|
||||
|
||||
@@ -28,4 +28,5 @@ export default [
|
||||
['data_viewer_script_preview', 'Script'],
|
||||
['data_viewer_script_preview_dialog_title', 'Preview changes'],
|
||||
['data_viewer_script_preview_error_title', "Can't get the script"],
|
||||
['data_viewer_refresh_result_set', 'Refresh result set'],
|
||||
];
|
||||
|
||||
@@ -21,4 +21,5 @@ export default [
|
||||
['data_viewer_presentation_value_image_fit', 'Adatta alla Finestra'],
|
||||
['data_viewer_presentation_value_image_original_size', 'Dimensioni Originali'],
|
||||
['data_viewer_presentation_value_boolean_placeholder', 'Non posso rappresentare il valore corrente come booleano'],
|
||||
['data_viewer_refresh_result_set', 'Refresh result set'],
|
||||
];
|
||||
|
||||
@@ -24,4 +24,5 @@ export default [
|
||||
['data_viewer_script_preview', 'Скрипт'],
|
||||
['data_viewer_script_preview_dialog_title', 'Предпросмотр изменений'],
|
||||
['data_viewer_script_preview_error_title', 'Не удалось получить скрипт'],
|
||||
['data_viewer_refresh_result_set', 'Обновить резалт сет'],
|
||||
];
|
||||
|
||||
@@ -28,4 +28,5 @@ export default [
|
||||
['data_viewer_script_preview', '脚本'],
|
||||
['data_viewer_script_preview_dialog_title', '预览更改'],
|
||||
['data_viewer_script_preview_error_title', '无法获取脚本'],
|
||||
['data_viewer_refresh_result_set', 'Refresh result set'],
|
||||
];
|
||||
|
||||
@@ -105,7 +105,7 @@ export class SqlQueryService {
|
||||
await model
|
||||
.setCountGain(getDefaultRowsCount())
|
||||
.setSlice(0)
|
||||
.refresh();
|
||||
.request();
|
||||
|
||||
this.sqlQueryResultService.updateGroupTabs(editorState, model, tabGroup.groupId);
|
||||
} catch (exception) {
|
||||
@@ -179,7 +179,7 @@ export class SqlQueryService {
|
||||
await model
|
||||
.setCountGain(getDefaultRowsCount())
|
||||
.setSlice(0)
|
||||
.refresh();
|
||||
.request();
|
||||
|
||||
statistics.executedQueries++;
|
||||
statistics.executeTime += source.requestInfo.requestDuration;
|
||||
|
||||
+4
-9
@@ -4542,15 +4542,10 @@ caniuse-api@^3.0.0:
|
||||
lodash.memoize "^4.1.2"
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001219:
|
||||
version "1.0.30001243"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001243.tgz#d9250155c91e872186671c523f3ae50cfc94a3aa"
|
||||
integrity sha512-vNxw9mkTBtkmLFnJRv/2rhs1yufpDfCkBZexG3Y0xdOH2Z/eE/85E4Dl5j1YUN34nZVsSp6vVRFQRrez9wJMRA==
|
||||
|
||||
caniuse-lite@^1.0.30001274:
|
||||
version "1.0.30001280"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz#066a506046ba4be34cde5f74a08db7a396718fb7"
|
||||
integrity sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001274:
|
||||
version "1.0.30001286"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz"
|
||||
integrity sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==
|
||||
|
||||
capital-case@^1.0.4:
|
||||
version "1.0.4"
|
||||
|
||||
Reference in New Issue
Block a user