Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
5d02643
dbeaver/pro#5471 fix: "Delete" data viewer action tooltip
SychevAndrey Sep 16, 2025
f20a80b
dbeaver/pro#5471 refactor: "merge" save and save as script actions
SychevAndrey Sep 17, 2025
9207cd5
dbeaver/pro#5471 feat: adds Find and Comment shortcuts to the shortcu…
SychevAndrey Sep 17, 2025
480de59
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
SychevAndrey Sep 19, 2025
b137117
dbeaver/pro#5471 refactor: use common l10n keys for save/undo/redo
SychevAndrey Sep 22, 2025
ccb4913
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
SychevAndrey Sep 22, 2025
7636405
dbeaver/pro#5471 refactor: use key bindings API for Delete action in …
SychevAndrey Sep 22, 2025
0431127
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
SychevAndrey Sep 23, 2025
aa51270
dbeaver/pro#5471 refactor: use key bindings API for add, duplicate an…
SychevAndrey Sep 23, 2025
60a4d1a
dbeaver/pro#5471 fix: extra import
SychevAndrey Sep 23, 2025
50ff53a
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
SychevAndrey Sep 23, 2025
a682283
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
dariamarutkina Sep 23, 2025
bfe0286
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
dariamarutkina Sep 24, 2025
157626b
dbeaver/pro#5471 refactor: don't use a symbol for delete shortcut
SychevAndrey Sep 24, 2025
3332071
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
dariamarutkina Sep 24, 2025
9fabf1f
Merge branch 'devel' into 5471-cb-add-shortcuts-to-tooltips-data-edit…
dariamarutkina Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 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 { createKeyBinding } from '../createKeyBinding.js';

export const KEY_BINDING_CANCEL = createKeyBinding({
id: 'cancel',
keys: ['Escape'],
preventDefault: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 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 { createKeyBinding } from '../createKeyBinding.js';

export const KEY_BINDING_DELETE = createKeyBinding({
id: 'delete',
keys: ['Delete'],
preventDefault: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const FORMAT_SHORTCUT_KEYS_MAP: Record<string, string> = {
right: '→',
pageup: 'pageup',
pagedown: 'pagedown',
del: '',
delete: '',
del: 'delete',
delete: 'delete',
};
const SOURCE_DIVIDER_REGEXP = /\+/gi;
const APPLIED_DIVIDER = ' + ';
Expand Down
2 changes: 2 additions & 0 deletions webapp/packages/core-view/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export * from './Action/KeyBinding/Bindings/KEY_BINDING_UNDO.js';
export * from './Action/KeyBinding/Bindings/KEY_BINDING_SAVE.js';
export * from './Action/KeyBinding/Bindings/KEY_BINDING_ADD.js';
export * from './Action/KeyBinding/Bindings/KEY_BINDING_DUPLICATE.js';
export * from './Action/KeyBinding/Bindings/KEY_BINDING_DELETE.js';
export * from './Action/KeyBinding/Bindings/KEY_BINDING_CANCEL.js';
export * from './Action/KeyBinding/KeyBindingService.js';
export * from './Action/KeyBinding/createKeyBinding.js';
export * from './Action/ActionService.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,64 +451,19 @@ export const DataGridTable = observer<IDataPresentationProps>(function DataGridT
return <TextPlaceholder>{translate('data_grid_table_empty_placeholder')}</TextPlaceholder>;
}

const handleCellKeyDown: DataGridProps['onCellKeyDown'] = ({ rowIdx, colIdx }, event) => {
const handleCellKeyDown: DataGridProps['onCellKeyDown'] = (_, event) => {
gridSelectedCellCopy.onKeydownHandler(event);
const cell = selectionAction.getFocusedElement();

if (EventContext.has(event, EventStopPropagationFlag) || model.isReadonly(resultIndex) || !cell) {
return;
}

// we can't edit table cells if table doesn't have row identifier, but we can edit just added/duplicated rows before insert (CB-6063)
const canEdit = model.hasElementIdentifier(resultIndex) || tableData.editor.getElementState(cell) === DatabaseEditChangeType.add;
const activeElements = selectionAction.getActiveElements();
const activeRows = selectionAction.getActiveRows();

switch (event.code) {
case 'Escape': {
if (!canEdit) {
return;
}
tableData.editor.revert(...activeElements);
return;
}
case 'KeyR': {
if (event.altKey) {
if (event.shiftKey) {
tableData.editor.duplicate(...activeRows);
} else {
tableData.editor.add(cell);
}
return;
}
return;
}
case 'Delete': {
if (!canEdit) {
return;
}
case 'KeyR':
case 'Escape':
case 'Delete':
event.preventGridDefault();

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) {
handlers.selectCell({ colIdx, rowIdx: rowIdx - 1 });
}
} else {
if (rowIdx + 1 < tableData.rows.length) {
handlers.selectCell({ colIdx, rowIdx: rowIdx + 1 });
}
}
}
}
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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 type { HTMLAttributes } from 'react';
import { type HTMLAttributes } from 'react';

import { CRegistry, s, useS } from '@cloudbeaver/core-blocks';
import { useDataContextLink } from '@cloudbeaver/core-data-context';
import { MenuBar, MenuBarItemStyles, MenuBarStyles } from '@cloudbeaver/core-ui';
import { useMenu } from '@cloudbeaver/core-view';
import { useCaptureViewContext, useMenu } from '@cloudbeaver/core-view';

import { DATA_CONTEXT_DV_DDM } from '../../../DatabaseDataModel/DataContext/DATA_CONTEXT_DV_DDM.js';
import { DATA_CONTEXT_DV_DDM_RESULT_INDEX } from '../../../DatabaseDataModel/DataContext/DATA_CONTEXT_DV_DDM_RESULT_INDEX.js';
Expand All @@ -32,6 +32,12 @@ export const TableFooterMenu = observer<Props>(function TableFooterMenu({ result
const styles = useS(MenuBarStyles, MenuBarItemStyles);
const menu = useMenu({ menu: DATA_VIEWER_DATA_MODEL_ACTIONS_MENU });

useCaptureViewContext((context, id) => {
context.set(DATA_CONTEXT_DV_DDM, model, id);
context.set(DATA_CONTEXT_DV_DDM_RESULT_INDEX, resultIndex, id);
context.set(DATA_CONTEXT_DV_SIMPLE, simple, id);
});

useDataContextLink(menu.context, (context, id) => {
context.set(DATA_CONTEXT_DV_DDM, model, id);
context.set(DATA_CONTEXT_DV_DDM_RESULT_INDEX, resultIndex, id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import {
ACTION_REVERT,
ACTION_SAVE,
ActionService,
getBindingLabel,
DATA_CONTEXT_MENU,
KEY_BINDING_ADD,
KEY_BINDING_DELETE,
KEY_BINDING_DUPLICATE,
KEY_BINDING_CANCEL,
KeyBindingService,
MenuService,
type IAction,
} from '@cloudbeaver/core-view';
Expand All @@ -31,17 +34,61 @@ import { DATA_CONTEXT_DV_DDM_RESULT_INDEX } from '../../../DatabaseDataModel/Dat
import { DATA_CONTEXT_DV_PRESENTATION, DataViewerPresentationType } from '../../../DatabaseDataModel/DataContext/DATA_CONTEXT_DV_PRESENTATION.js';
import type { IDatabaseDataModel } from '../../../DatabaseDataModel/IDatabaseDataModel.js';
import { DATA_VIEWER_DATA_MODEL_ACTIONS_MENU } from './DATA_VIEWER_DATA_MODEL_ACTIONS_MENU.js';
import { DataViewerViewService } from '../../DataViewerViewService.js';

@injectable(() => [ActionService, LocalizationService, MenuService])
@injectable(() => [ActionService, KeyBindingService, DataViewerViewService, LocalizationService, MenuService])
export class TableFooterMenuService {
constructor(
private readonly actionService: ActionService,
private readonly keyBindingService: KeyBindingService,
private readonly dataViewerViewService: DataViewerViewService,
private readonly localizationService: LocalizationService,
private readonly menuService: MenuService,
) { }
) {}

register(): void {
this.registerEditingActions();
this.keyBindingService.addKeyBindingHandler({
id: 'table-footer-delete',
binding: KEY_BINDING_DELETE,
contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
isBindingApplicable(context, action) {
return action === ACTION_DELETE;
},
handler: this.tableFooterMenuActionHandler.bind(this),
});

this.keyBindingService.addKeyBindingHandler({
id: 'table-footer-cancel',
binding: KEY_BINDING_CANCEL,
contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
isBindingApplicable(context, action) {
return action === ACTION_CANCEL;
},
handler: this.tableFooterMenuActionHandler.bind(this),
});

this.keyBindingService.addKeyBindingHandler({
id: 'table-footer-add',
binding: KEY_BINDING_ADD,
contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
isBindingApplicable(context, action) {
return action === ACTION_ADD;
},
handler: this.tableFooterMenuActionHandler.bind(this),
});

this.keyBindingService.addKeyBindingHandler({
id: 'table-footer-duplicate',
binding: KEY_BINDING_DUPLICATE,
contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
isBindingApplicable(context, action) {
return action === ACTION_DUPLICATE;
},
handler: this.tableFooterMenuActionHandler.bind(this),
});

this.dataViewerViewService.registerAction(ACTION_DELETE, ACTION_CANCEL, ACTION_ADD, ACTION_DUPLICATE);
}

private registerEditingActions() {
Expand All @@ -62,11 +109,14 @@ export class TableFooterMenuService {
this.actionService.addHandler({
id: 'data-base-editing-handler',
contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
menus: [DATA_VIEWER_DATA_MODEL_ACTIONS_MENU],
actions: [ACTION_ADD, ACTION_DUPLICATE, ACTION_DELETE, ACTION_REVERT, ACTION_SAVE, ACTION_CANCEL],
isActionApplicable(context, action) {
const model = context.get(DATA_CONTEXT_DV_DDM)!;
const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX)!;
const currentMenu = context.getOwn(DATA_CONTEXT_MENU);
if (currentMenu !== undefined && currentMenu !== DATA_VIEWER_DATA_MODEL_ACTIONS_MENU) {
return false;
}

if (model.isReadonly(resultIndex)) {
return false;
Expand Down Expand Up @@ -185,23 +235,32 @@ export class TableFooterMenuService {
break;
}
case ACTION_SAVE:
model.save().catch(() => { });
model.save().catch(() => {});
break;
case ACTION_CANCEL: {
editor.clear();
break;
}
}

}

private tableFooterMenuGetActionInfo(context: IDataContextProvider, action: IAction) {
const t = this.localizationService.translate;
switch (action) {
case ACTION_ADD:
return { ...action.info, label: '', icon: '/icons/data_add_sm.svg', tooltip: t('data_viewer_action_edit_add') + ' (' + getBindingLabel(KEY_BINDING_ADD) + ')' };
return {
...action.info,
label: '',
icon: '/icons/data_add_sm.svg',
tooltip: t('data_viewer_action_edit_add'),
};
case ACTION_DUPLICATE:
return { ...action.info, label: '', icon: '/icons/data_add_copy_sm.svg', tooltip: t('data_viewer_action_edit_add_copy') + ' (' + getBindingLabel(KEY_BINDING_DUPLICATE) + ')' };
return {
...action.info,
label: '',
icon: '/icons/data_add_copy_sm.svg',
tooltip: t('data_viewer_action_edit_add_copy'),
};
case ACTION_DELETE:
return { ...action.info, label: '', icon: '/icons/data_delete_sm.svg', tooltip: t('data_viewer_action_edit_delete') };
case ACTION_REVERT:
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-data-viewer/src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default [
['data_viewer_action_auto_refresh_menu_stop_tooltip', 'Stop auto-refreshing data'],
['data_viewer_action_auto_refresh_menu_configure_tooltip', 'Configure auto-refresh settings'],
['data_viewer_action_auto_refresh_interval_tooltip', 'Set auto-refresh interval to {arg:interval}'],
['data_viewer_action_edit_delete', 'Ausgewählte löschen'],
['data_viewer_action_edit_delete', 'Ausgewählte Zeilen löschen'],
['data_viewer_action_edit_add', 'Hinzufügen'],
['data_viewer_action_edit_add_copy', 'Duplikat'],
['data_viewer_action_edit_revert', 'Ausgewählt abbrechen'],
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-data-viewer/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default [
['data_viewer_action_auto_refresh_menu_stop_tooltip', 'Stop auto-refreshing data'],
['data_viewer_action_auto_refresh_menu_configure_tooltip', 'Configure auto-refresh settings'],
['data_viewer_action_auto_refresh_interval_tooltip', 'Set auto-refresh interval to {arg:interval}'],
['data_viewer_action_edit_delete', 'Delete selected'],
['data_viewer_action_edit_delete', 'Delete selected rows'],
['data_viewer_action_edit_add', 'Add'],
['data_viewer_action_edit_add_copy', 'Duplicate'],
['data_viewer_action_edit_revert', 'Cancel selected'],
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-data-viewer/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default [
['data_viewer_action_auto_refresh_menu_stop_tooltip', 'Stop auto-refreshing data'],
['data_viewer_action_auto_refresh_menu_configure_tooltip', 'Configure auto-refresh settings'],
['data_viewer_action_auto_refresh_interval_tooltip', 'Set auto-refresh interval to {arg:interval}'],
['data_viewer_action_edit_delete', 'Supprimer la sélection'],
['data_viewer_action_edit_delete', 'Supprimer les lignes sélectionnées'],
['data_viewer_action_edit_add', 'Ajouter'],
['data_viewer_action_edit_add_copy', 'Dupliquer'],
['data_viewer_action_edit_revert', 'Annuler la sélection'],
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-data-viewer/src/locales/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default [
['data_viewer_action_auto_refresh_menu_stop_tooltip', 'Остановить автоматическое обновление данных'],
['data_viewer_action_auto_refresh_menu_configure_tooltip', 'Настроить параметры автоматического обновления'],
['data_viewer_action_auto_refresh_interval_tooltip', 'Установить интервал автоматического обновления в {arg:interval}'],
['data_viewer_action_edit_delete', 'Удалить'],
['data_viewer_action_edit_delete', 'Удалить выбранные строки'],
['data_viewer_action_edit_add', 'Добавить'],
['data_viewer_action_edit_add_copy', 'Дублировать'],
['data_viewer_action_edit_revert', 'Отменить'],
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-data-viewer/src/locales/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default [
['data_viewer_action_auto_refresh_menu_stop_tooltip', 'Dừng làm mới dữ liệu tự động'],
['data_viewer_action_auto_refresh_menu_configure_tooltip', 'Cấu hình cài đặt làm mới tự động'],
['data_viewer_action_auto_refresh_interval_tooltip', 'Đặt khoảng thời gian làm mới tự động thành {arg:interval}'],
['data_viewer_action_edit_delete', 'Xóa mục đã chọn'],
['data_viewer_action_edit_delete', 'Xóa các hàng đã chọn'],
['data_viewer_action_edit_add', 'Thêm'],
['data_viewer_action_edit_add_copy', 'Sao chép'],
['data_viewer_action_edit_revert', 'Hủy mục đã chọn'],
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/plugin-data-viewer/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default [
['data_viewer_action_auto_refresh_menu_stop_tooltip', '停止自动刷新数据'],
['data_viewer_action_auto_refresh_menu_configure_tooltip', '配置自动刷新'],
['data_viewer_action_auto_refresh_interval_tooltip', '自动刷新间隔设置为 {arg:interval}'],
['data_viewer_action_edit_delete', '删除选中'],
['data_viewer_action_edit_delete', '删除选中行'],
['data_viewer_action_edit_add', '添加'],
['data_viewer_action_edit_add_copy', '复制并添加行'],
['data_viewer_action_edit_revert', '还原选中'],
Expand Down
26 changes: 22 additions & 4 deletions webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { getOS, OperatingSystem } from '@cloudbeaver/core-utils';
import { getCommonAndOSSpecificKeys, type IKeyBinding, KEY_BINDING_OPEN_IN_TAB, KEY_BINDING_REDO, KEY_BINDING_UNDO } from '@cloudbeaver/core-view';
import { createKeyBinding, getCommonAndOSSpecificKeys, type IKeyBinding, KEY_BINDING_OPEN_IN_TAB, KEY_BINDING_REDO, KEY_BINDING_UNDO } from '@cloudbeaver/core-view';
import {
KEY_BINDING_ADD_NEW_ROW,
KEY_BINDING_DUPLICATE_ROW,
Expand All @@ -23,6 +23,16 @@ import {
} from '@cloudbeaver/plugin-sql-editor';
import { KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT } from '@cloudbeaver/plugin-sql-editor-navigation-tab-script';

const KEY_BINDING_SQL_EDITOR_COMMENT = createKeyBinding({
id: 'sql-editor-comment',
keys: ['mod+/'],
});

const KEY_BINDING_SQL_EDITOR_FIND = createKeyBinding({
id: 'sql-editor-find',
keys: ['mod+f'],
});

import type { IShortcut } from './IShortcut.js';

const FORMAT_SHORTCUT_KEYS_MAP: Record<string, string> = {
Expand Down Expand Up @@ -91,17 +101,25 @@ export const SQL_EDITOR_SHORTCUTS: IShortcut[] = [
code: transformKeys(KEY_BINDING_SQL_EDITOR_FORMAT),
},
{
label: 'sql_editor_shortcut_save_as_script',
label: 'ui_processing_save',
code: transformKeys(KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT),
},
{
label: 'sql_editor_shortcut_undo',
label: 'ui_undo',
code: transformKeys(KEY_BINDING_UNDO),
},
{
label: 'sql_editor_shortcut_redo',
label: 'ui_redo',
code: transformKeys(KEY_BINDING_REDO),
},
{
label: 'sql_editor_shortcut_find',
code: transformKeys(KEY_BINDING_SQL_EDITOR_FIND),
},
{
label: 'sql_editor_shortcut_comment_uncomment_selection',
code: transformKeys(KEY_BINDING_SQL_EDITOR_COMMENT),
},
{
label: 'sql_editor_shortcut_open_editor_in_new_tab',
code: transformKeys(KEY_BINDING_OPEN_IN_TAB),
Expand Down
Loading
Loading