Merge remote-tracking branch 'origin/devel' into devel

This commit is contained in:
Serge Rider
2020-06-15 20:20:20 +03:00
12 changed files with 101 additions and 69 deletions
@@ -248,6 +248,7 @@ const defaultColumnDef: ColDef = {
export const INDEX_COLUMN_DEF: ColDef = {
headerName: '#',
colId: `${Number.MAX_SAFE_INTEGER}`,
field: `${Number.MAX_SAFE_INTEGER}`,
valueGetter: 'node.id',
width: 70,
@@ -70,7 +70,7 @@ const headerStyles = css`
`;
export interface IAgColumnClickEvent extends AgEvent{
columnIndex: number;
columnId: string;
isMultiple: boolean;
}
@@ -80,7 +80,7 @@ export function TableColumnHeader(props: HeaderProps) {
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
const event: IAgColumnClickEvent = Object.freeze({
type: COLUMN_CLICK_EVENT_TYPE,
columnIndex: parseInt(props.column.getColId()),
columnId: props.column.getColId(),
isMultiple: e.ctrlKey,
});
@@ -7,31 +7,31 @@
*/
export class RowSelection {
columns = new Set<number>()
columns = new Set<string>()
constructor(readonly rowId: number) {}
add(columnIndexList: number[]) {
add(columnIndexList: string[]) {
for (const columnId of columnIndexList) {
this.columns.add(columnId);
}
}
remove(columnIndexList: number[]) {
remove(columnIndexList: string[]) {
for (const columnId of columnIndexList) {
this.columns.delete(columnId);
}
}
replace(columnIndexList: number[]) {
replace(columnIndexList: string[]) {
this.columns.clear();
this.add(columnIndexList);
}
isSelected(columnIndex: number) {
isSelected(columnIndex: string) {
return this.columns.has(columnIndex);
}
isRangeSelected(columnIndexList: number[]) {
isRangeSelected(columnIndexList: string[]) {
return columnIndexList.every(column => this.columns.has(column));
}
}
@@ -22,28 +22,28 @@ export class TableSelection {
this.selectedMap.clear();
}
selectCell(rowId: number, columnIndex: number, isMultiple: boolean, ignoreSelected = false) {
selectCell(rowId: number, columnId: string, isMultiple: boolean, ignoreSelected = false) {
if (!isMultiple) {
this.clear();
}
const rowSelection = this.selectedMap.get(rowId);
if (!ignoreSelected && rowSelection?.isSelected(columnIndex)) {
if (!ignoreSelected && rowSelection?.isSelected(columnId)) {
this.unselectColumn(
this.selectedMap,
rowSelection,
[columnIndex]
[columnId]
);
} else {
this.selectColumn(rowId, rowSelection, [columnIndex]);
this.selectColumn(rowId, rowSelection, [columnId]);
}
}
selectRange(
startPosition: number,
endPosition: number,
columns: number[],
columns: string[],
isMultiple: boolean
) {
const firstRow = Math.min(startPosition, endPosition);
@@ -70,13 +70,13 @@ export class TableSelection {
}
}
isCellSelected(rowId: number, columnIndex: number): boolean {
isCellSelected(rowId: number, columnId: string): boolean {
const current = this.selectedMap.get(rowId);
return current?.isSelected(columnIndex) || false;
return current?.isSelected(columnId) || false;
}
isRangeSelected(startPosition: number, endPosition: number, columns: number[]): boolean {
isRangeSelected(startPosition: number, endPosition: number, columns: string[]): boolean {
const start = Math.min(startPosition, endPosition);
const end = Math.max(startPosition, endPosition);
@@ -89,19 +89,19 @@ export class TableSelection {
return true;
}
private unselectColumn(source: Map<number, RowSelection>, rowSelection: RowSelection, columnIndexList: number[]) {
rowSelection.remove(columnIndexList);
private unselectColumn(source: Map<number, RowSelection>, rowSelection: RowSelection, columns: string[]) {
rowSelection.remove(columns);
if (rowSelection.columns.size === 0) {
source.delete(rowSelection.rowId);
}
}
private selectColumn(rowId: number, rowSelection: RowSelection | undefined = undefined, columnIndexList: number[]) {
private selectColumn(rowId: number, rowSelection: RowSelection | undefined = undefined, columns: string[]) {
if (!rowSelection) {
rowSelection = new RowSelection(rowId);
this.selectedMap.set(rowId, rowSelection);
}
rowSelection.add(columnIndexList);
rowSelection.add(columns);
}
}
@@ -51,7 +51,7 @@ const EVENT_KEY_CODE = {
interface TemporarySelectionRange {
firstRow: number;
lastRow: number;
columns: number[];
columns: string[];
isMultiple: boolean;
}
@@ -110,7 +110,7 @@ export class RangeController implements IRangeController {
private copySelectedData() {
const selectedRows = this.selection!.getSelectedRows();
const selectedColumns = new Set<number>();
const selectedColumns = new Set<string>();
for (const selectedRow of selectedRows) {
for (const columnId of selectedRow.columns) {
@@ -120,7 +120,7 @@ export class RangeController implements IRangeController {
const columns = this.columnController
.getAllDisplayedColumns()
.filter(column => selectedColumns.has(parseInt(column.getColId())));
.filter(column => selectedColumns.has(column.getColId()));
let data = '';
for (const selectedRow of selectedRows) {
@@ -128,7 +128,7 @@ export class RangeController implements IRangeController {
if (column !== columns[0]) {
data += '\t';
}
if (selectedRow.isSelected(parseInt(column.getColId()))) {
if (selectedRow.isSelected(column.getColId())) {
const rowNode = this.rowPositionUtils.getRowNode({
rowIndex: selectedRow.rowId,
rowPinned: undefined,
@@ -164,7 +164,7 @@ export class RangeController implements IRangeController {
return;
}
const lastRowId = this.gridApi.getInfiniteRowCount() - 1 - (this.rowModel.isLastRowFound() ? 0 : 1);
if (event.columnIndex === parseInt(INDEX_COLUMN_DEF.field!)) {
if (event.columnId === INDEX_COLUMN_DEF.colId!) {
this.selection.selectRange(
0,
lastRowId,
@@ -172,7 +172,7 @@ export class RangeController implements IRangeController {
event.isMultiple
);
} else {
this.selection.selectRange(0, lastRowId, [event.columnIndex], event.isMultiple);
this.selection.selectRange(0, lastRowId, [event.columnId], event.isMultiple);
}
this.restoreFocus();
this.dispatchChangedEvent(false, true);
@@ -183,7 +183,7 @@ export class RangeController implements IRangeController {
return;
}
if (event.column.getColId() === INDEX_COLUMN_DEF.field) {
if (event.column.getColId() === INDEX_COLUMN_DEF.colId) {
this.selection.selectRange(
event.rowIndex,
event.rowIndex,
@@ -201,16 +201,16 @@ export class RangeController implements IRangeController {
this.lastSelectedCell = cell;
this.lastFocus = cell;
if (!this.gridOptionsWrapper.isEnableRangeSelection()
|| cell.column.getColDef().field === INDEX_COLUMN_DEF.field) {
|| cell.column.getColId() === INDEX_COLUMN_DEF.colId) {
return;
}
this.selection.selectCell(cell.rowIndex, parseInt(cell.column.getColId()), appendRange);
this.selection.selectCell(cell.rowIndex, cell.column.getColId(), appendRange);
this.dispatchChangedEvent(false, true);
}
getCellRangeCount(cell: CellPosition): number {
const columnIndex = parseInt(cell.column.getColId());
if (this.isCellInTemporaryRange(cell.rowIndex, columnIndex)) {
const columnId = cell.column.getColId();
if (this.isCellInTemporaryRange(cell.rowIndex, columnId)) {
const isRangeSelected = this.selection?.isRangeSelected(
this.temporaryRange!.firstRow,
this.temporaryRange!.lastRow,
@@ -218,7 +218,7 @@ export class RangeController implements IRangeController {
);
return isRangeSelected ? 0 : 1;
}
if (this.selection?.isCellSelected(cell.rowIndex, parseInt(cell.column.getColId()))) {
if (this.selection?.isCellSelected(cell.rowIndex, cell.column.getColId())) {
return 1;
}
@@ -232,7 +232,7 @@ export class RangeController implements IRangeController {
const startRow = this.lastSelectedCell !== undefined ? this.lastSelectedCell.rowIndex : position.rowIndex;
const endRow = position.rowIndex;
let columns = this.getColumnsBetween((this.lastSelectedCell || position).column, position.column);
const isRowsSelection = columns.includes(parseInt(INDEX_COLUMN_DEF.field!));
const isRowsSelection = columns.includes(INDEX_COLUMN_DEF.colId!);
if (isRowsSelection) {
columns = this.getColumnsWithoutIndex();
@@ -293,9 +293,9 @@ export class RangeController implements IRangeController {
this.eventService.dispatchEvent(event);
}
private getColumnsBetween(firstColumn: Column, secondColumn: Column): number[] {
private getColumnsBetween(firstColumn: Column, secondColumn: Column): string[] {
if (firstColumn === secondColumn) {
return [parseInt(firstColumn.getColId())];
return [firstColumn.getColId()];
}
const columns = this.columnController.getAllDisplayedColumns();
@@ -309,14 +309,14 @@ export class RangeController implements IRangeController {
const firstIndex = Math.min(firstOffset, secondOffset);
const lastIndex = Math.max(firstOffset, secondOffset);
return columns.slice(firstIndex, lastIndex + 1).map(column => parseInt(column.getColId()));
return columns.slice(firstIndex, lastIndex + 1).map(column => column.getColId());
}
private getColumnsWithoutIndex() {
return this.columnController
.getAllDisplayedColumns()
.map(column => parseInt(column.getColId()))
.filter(column => column !== parseInt(INDEX_COLUMN_DEF.field!));
.map(column => column.getColId())
.filter(column => column !== INDEX_COLUMN_DEF.colId!);
}
getRangeStartRow(cellRange: CellRange): RowPosition {
@@ -338,10 +338,10 @@ export class RangeController implements IRangeController {
return;
}
if (startDraggingCell.column.getColId() !== INDEX_COLUMN_DEF.field) {
if (startDraggingCell.column.getColId() !== INDEX_COLUMN_DEF.colId) {
this.selection.selectCell(
startDraggingCell.rowIndex,
parseInt(startDraggingCell.column.getColId()),
startDraggingCell.column.getColId(),
true,
true
);
@@ -404,7 +404,7 @@ export class RangeController implements IRangeController {
}
let columns = this.getColumnsBetween(this.startDraggingCell.column, this.endDraggingCell.column);
const isRowsSelection = columns.includes(parseInt(INDEX_COLUMN_DEF.field!));
const isRowsSelection = columns.includes(INDEX_COLUMN_DEF.colId!);
const startRow = this.startDraggingCell.rowIndex;
const endRow = this.endDraggingCell.rowIndex;
@@ -438,7 +438,7 @@ export class RangeController implements IRangeController {
this.temporaryRange = null;
}
private isCellInTemporaryRange(rowId: number, column: number) {
private isCellInTemporaryRange(rowId: number, column: string) {
if (!this.temporaryRange
|| this.temporaryRange.firstRow > rowId || this.temporaryRange.lastRow < rowId
|| !this.temporaryRange.columns.includes(column)
@@ -180,7 +180,7 @@ export class ConnectionSchemaManagerService {
try {
await this.connectionsManagerService.loadConnectionInfoAsync(connectionId);
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t load connection info');
this.notificationService.logException(exception, 'Can\'t load connection info', true);
}
try {
@@ -41,7 +41,7 @@ export class ConnectionSelectorController {
}
get isConnectionSelectorVisible() {
return !!this.connectionSelectorService.currentConnectionId;
return !!this.currentConnection;
}
get isObjectContainerSelectorVisible() {
return !!this.connectionSelectorService.currentObjectCatalogId
@@ -173,22 +173,27 @@ export class ConnectionsManagerService {
}
async closeNavNodeConnectionAsync(navNodeId: string): Promise<void> {
const node = this.navNodeManagerService.getNode(navNodeId);
if (!node) {
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(navNodeId);
const connection = this.getConnectionById(connectionId);
if (!connection) {
return;
}
try {
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(navNodeId);
await this.graphQLService.gql.closeConnection({ id: connectionId });
await this.afterConnectionClose(connectionId);
await this.connectionInfo.refresh(true, { connectionId, close: true });
if (node.objectFeatures.includes('dataSourceTemporary')) {
if (connection.features.includes(EConnectionFeature.temporary)) {
const node = this.navNodeManagerService.getNode(navNodeId);
if (!node) {
return;
}
await this.navNodeManagerService.refreshTree(node.parentId);
} else {
await this.navNodeManagerService.refreshNode(navNodeId);
await this.navNodeManagerService.removeTree(navNodeId);
}
await this.navNodeManagerService.removeTree(navNodeId);
} catch (exception) {
this.notificationService.logException(exception, `Can't close connection: ${navNodeId}`);
}
@@ -20,22 +20,28 @@ import { useStyles } from '@dbeaver/core/theming';
import { EditorDialog } from './EditorDialog';
import { InlineEditorStyles } from './styles';
export type InlineEditorControls = 'right' | 'top' | 'bottom'
export type InlineEditorControls = 'right' | 'top' | 'bottom' | 'inside'
export type InlineEditorProps = {
value: string;
placeholder?: string;
controlsPosition?: InlineEditorControls;
simple?: boolean;
onChange(value: string): void;
onSave(): void;
onReject(): void;
className?: string;
}
export const InlineEditor = observer(function InlineEditor({
value,
placeholder,
controlsPosition = 'right',
simple,
onChange,
onSave,
onReject,
className,
}: InlineEditorProps) {
const commonDialogService = useService(CommonDialogService);
@@ -66,14 +72,21 @@ export const InlineEditor = observer(function InlineEditor({
}, []);
return styled(useStyles(InlineEditorStyles))(
<editor as="div">
<editor as="div" className={className}>
<editor-container as="div">
<input type="text" value={value} onChange={handleChange} onKeyDown={handleKeyDown} ref={inputRef}/>
<input
type="text"
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
ref={inputRef}
placeholder={placeholder}
/>
</editor-container>
<editor-actions as="div" {...use({ position: controlsPosition })}>
<editor-action as="div" onClick={onSave}><Icon name="apply" viewBox="0 0 12 10" /></editor-action>
<editor-action as="div" onClick={onReject}><Icon name="reject" viewBox="0 0 11 11" /></editor-action>
<editor-action as="div" onClick={handlePopup}><Icon name="edit" viewBox="0 0 13 13" /></editor-action>
{!simple && <editor-action as="div" onClick={handlePopup}><Icon name="edit" viewBox="0 0 13 13" /></editor-action>}
</editor-actions>
</editor>
);
@@ -28,6 +28,7 @@ export const InlineEditorStyles = composes(
editor {
position: relative;
box-sizing: border-box;
display: flex;
width: 100%;
height: 100%;
border: solid 1px #01cca3;
@@ -36,6 +37,7 @@ export const InlineEditorStyles = composes(
editor-container {
position: relative;
height: 100%;
flex: 1;
& input {
color: inherit;
@@ -66,6 +68,14 @@ export const InlineEditorStyles = composes(
border-bottom-color: #01cca3 !important;
}
editor-actions[|position=inside] {
position: relative;
flex: 0 0 auto;
bottom: auto;
left: auto;
border-right: none !important;
}
editor-actions[|position=bottom],
editor-actions[|position=top] {
right: -1px;
@@ -10,6 +10,7 @@ import { observer } from 'mobx-react';
import { useCallback } from 'react';
import styled, { css } from 'reshadow';
import { InlineEditor } from '@dbeaver/core/app';
import { InputField, IconButton, SubmittingForm } from '@dbeaver/core/blocks';
import { useTranslate } from '@dbeaver/core/localization';
import { composes, useStyles } from '@dbeaver/core/theming';
@@ -18,8 +19,8 @@ import { TableViewerModel } from '../TableViewerModel';
const styles = composes(
css`
IconButton {
composes: theme-text-primary theme-ripple from global;
InlineEditor {
composes: theme-background-surface theme-text-on-surface from global;
}
`,
css`
@@ -29,15 +30,10 @@ const styles = composes(
display: flex;
align-items: center;
}
InputField {
InlineEditor {
flex: 1;
}
IconButton {
margin-right: 18px;
position: relative;
height: 24px;
width: 24px;
display: block;
margin: 0 12px;
}
`
);
@@ -54,18 +50,25 @@ export const TableWhereFilter = observer(function TableWhereFilter({
(value: string) => context.setQueryWhereFilter(value),
[context]
);
const resetFilter = useCallback(
() => {
context.setQueryWhereFilter('');
context.applyQueryFilters();
},
[context]
);
return styled(useStyles(styles))(
<SubmittingForm onSubmit={() => context.applyQueryFilters()}>
<InputField
type='text'
name='whereFilter'
placeholder={translate('table_header_sql_expression')}
<InlineEditor
value={context.getQueryWhereFilter() || ''}
onSave={() => context.applyQueryFilters()}
onReject={resetFilter}
onChange={handleChange}
mod='surface'
placeholder={translate('table_header_sql_expression')}
controlsPosition='inside'
simple
/>
<IconButton name='apply' viewBox='' onClick={() => context.applyQueryFilters()}/>
</SubmittingForm>
);
});
@@ -165,7 +165,7 @@ export class ObjectViewerTabService {
const nodeInfo = this.navNodeManagerService
.getNodeContainerInfo(context.handlerState.objectId);
if (nodeInfo.catalogId) {
if (!nodeInfo.catalogId) {
return;
}
return nodeInfo.catalogId;