Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -44,6 +44,7 @@ export const ContextMenu = observer<IContextMenuNewProps>(function ContextMenuIn
const handler = menuData.handler;

useAutoLoad({ name: `${ContextMenuInner.name}(${menuData.menu.id})` }, menuData.loaders, true, isMenuOpen, true);
menu.setAutoFocusOnShow(true);

const handlers = useObjectRef(
() => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 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 { getCommonAndOSSpecificKeys } from './getCommonAndOSSpecificKeys.js';
import type { IKeyBinding } from './IKeyBinding.js';

// Overrides for keys where the display value in FORMAT_SHORTCUT_KEYS_MAP differs from event.key
const EVENT_KEY_OVERRIDES: Record<string, string> = {
backspace: 'Backspace',
enter: 'Enter',
return: 'Enter',
escape: 'Escape',
esc: 'Escape',
del: 'Delete',
delete: 'Delete',
tab: 'Tab',
space: ' ',
up: 'ArrowUp',
down: 'ArrowDown',
left: 'ArrowLeft',
right: 'ArrowRight',
};

function resolveEventKey(keyPart: string): string {
return EVENT_KEY_OVERRIDES[keyPart] ?? keyPart;
}

const MODIFIER_KEYS = new Set(['mod', 'ctrl', 'shift', 'alt', 'meta']);

function matchesCombo(event: KeyboardEvent | React.KeyboardEvent, combo: string): boolean {
const parts = combo.toLowerCase().split('+');
const keyParts = parts.filter(p => !MODIFIER_KEYS.has(p));

const needsMod = parts.includes('mod');
const needsCtrl = parts.includes('ctrl');
const needsShift = parts.includes('shift');
const needsAlt = parts.includes('alt');
const needsMeta = parts.includes('meta');

if (needsMod && !(event.ctrlKey || event.metaKey)) {
return false;
}
if (needsCtrl && !event.ctrlKey) {
return false;
}
if (needsMeta && !event.metaKey) {
return false;
}
if (needsShift && !event.shiftKey) {
return false;
}
if (needsAlt && !event.altKey) {
return false;
}

// Disallow unexpected modifiers
if (!needsMod && !needsCtrl && !needsMeta && (event.ctrlKey || event.metaKey)) {
return false;
}
if (!needsShift && event.shiftKey) {
return false;
}
if (!needsAlt && event.altKey) {
return false;
}

const eventKey = event.key.toLowerCase();
return keyParts.some(k => resolveEventKey(k).toLowerCase() === eventKey);
}

export function matchesKeyBinding(event: KeyboardEvent | React.KeyboardEvent, binding: IKeyBinding): boolean {
return getCommonAndOSSpecificKeys(binding).some(combo => matchesCombo(event, combo));
}
1 change: 1 addition & 0 deletions webapp/packages/core-view/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export * from './Action/IActionItem.js';
export * from './Action/KeyBinding/IKeyBinding.js';
export * from './Action/KeyBinding/getCommonAndOSSpecificKeys.js';
export * from './Action/KeyBinding/getBindingLabel.js';
export * from './Action/KeyBinding/matchesKeyBinding.js';
export * from './LoadableStateContext/DATA_CONTEXT_LOADABLE_STATE.js';
export * from './Menu/MenuItem/IMenuCheckboxItem.js';
export * from './Menu/MenuItem/IMenuActionItem.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { getComputed, useHover, useMergeRefs, useObjectRef, useObservableRef } f
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
import { clsx } from '@dbeaver/ui-kit';
import { type IDataGridCellRenderer, type ICellPosition } from '@cloudbeaver/plugin-data-grid';
import { DatabaseEditChangeType, type IGridDataKey, type IGridRowKey } from '@cloudbeaver/plugin-data-viewer';
import { DatabaseEditChangeType, type IGridDataKey, type IGridRowKey, KEY_BINDING_OPEN_CELL_CONTEXT_MENU } from '@cloudbeaver/plugin-data-viewer';
import { matchesKeyBinding } from '@cloudbeaver/core-view';
import { isObjectsEqual } from '@cloudbeaver/core-utils';

import { ColumnDnDContext } from '../ColumnDnDContext.js';
Expand Down Expand Up @@ -144,6 +145,13 @@ export const CellRenderer = observer<Props>(function CellRenderer({ rowIdx, colI
this.selectionContext.clearSelection();
}
},
keyDown(event: React.KeyboardEvent<HTMLDivElement>) {
if (matchesKeyBinding(event, KEY_BINDING_OPEN_CELL_CONTEXT_MENU)) {
event.preventDefault();
event.stopPropagation();
this.cellContext.setMenuVisibility(!this.cellContext.isMenuVisible);
}
},
openContextMenu(event: React.MouseEvent<HTMLDivElement>) {
if (EventContext.has(event, EventStopPropagationFlag)) {
return;
Expand Down Expand Up @@ -177,7 +185,7 @@ export const CellRenderer = observer<Props>(function CellRenderer({ rowIdx, colI
dataGridContext,
cellContext,
},
['mouseUp', 'openContextMenu'],
['keyDown', 'mouseUp', 'openContextMenu'],
);

const formatting = getComputed(
Expand All @@ -195,6 +203,7 @@ export const CellRenderer = observer<Props>(function CellRenderer({ rowIdx, colI
style: formatting || undefined,
'data-row-index': rowIdx,
'data-column-index': colIdx,
onKeyDown: state.keyDown,
onMouseUp: state.mouseUp,
onContextMenu: state.openContextMenu,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ export const KEY_BINDING_CANCEL: IKeyBinding = {
id: 'data-viewer-cancel',
keys: ['mod+period'],
};

export const KEY_BINDING_OPEN_CELL_CONTEXT_MENU: IKeyBinding = {
id: 'data-viewer-open-cell-context-menu',
keys: ['mod+/'],
preventDefault: true,
};
5 changes: 5 additions & 0 deletions webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
KEY_BINDING_CANCEL,
KEY_BINDING_DELETE_ROW,
KEY_BINDING_DUPLICATE_ROW,
KEY_BINDING_OPEN_CELL_CONTEXT_MENU,
KEY_BINDING_REVERT_INLINE_EDITOR_CHANGES,
KEY_BINDING_SAVE,
} from '@cloudbeaver/plugin-data-viewer';
Expand Down Expand Up @@ -41,6 +42,10 @@ const KEY_BINDING_FIND = createKeyBinding({
import type { IShortcut } from './IShortcut.js';

export const DATA_VIEWER_SHORTCUTS: IShortcut[] = [
{
label: 'data_viewer_shortcut_open_cell_context_menu',
code: transformKeys(KEY_BINDING_OPEN_CELL_CONTEXT_MENU),
},
{
label: 'data_viewer_shortcut_revert_inline_editor_changes',
code: transformKeys(KEY_BINDING_REVERT_INLINE_EDITOR_CHANGES),
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default [
['plugin_help_welcome_docs_label', 'Documentation'],
['plugin_help_welcome_docs_description', 'Learn how to use {arg:product}'],

['data_viewer_shortcut_open_cell_context_menu', 'Open context menu for the selected Cell'],
['data_viewer_shortcut_start_inline_editing', 'Open Inline Editor for the selected Cell'],
['data_viewer_shortcut_add_new_row', 'Add a new Row'],
['data_viewer_shortcut_duplicate_row', 'Duplicate row'],
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default [
['plugin_help_welcome_docs_label', 'Documentation'],
['plugin_help_welcome_docs_description', 'Apprenez à utiliser {arg:product}'],

['data_viewer_shortcut_open_cell_context_menu', 'Ouvrir le menu contextuel pour la cellule sélectionnée'],
['data_viewer_shortcut_start_inline_editing', "Ouvrir l'éditeur en ligne pour la cellule sélectionnée"],
['data_viewer_shortcut_add_new_row', 'Ajouter une nouvelle ligne'],
['data_viewer_shortcut_duplicate_row', 'Dupliquer la ligne'],
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default [
['plugin_help_welcome_docs_label', 'Documentation'],
['plugin_help_welcome_docs_description', 'Learn how to use {arg:product}'],

['data_viewer_shortcut_open_cell_context_menu', 'Apri il menu contestuale per la cella selezionata'],
['data_viewer_shortcut_start_inline_editing', 'Open Inline Editor for the selected Cell'],
['data_viewer_shortcut_add_new_row', 'Add a new Row'],
['data_viewer_shortcut_duplicate_row', 'Duplicate row'],
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default [
['plugin_help_welcome_docs_label', 'Документация'],
['plugin_help_welcome_docs_description', 'Узнайте как использовать {arg:product}'],

['data_viewer_shortcut_open_cell_context_menu', 'Открыть контекстное меню для выбранной ячейки'],
['data_viewer_shortcut_start_inline_editing', 'Начать редактирование ячейки во встроенном редакторе'],
['data_viewer_shortcut_add_new_row', 'Добавить новую строку'],
['data_viewer_shortcut_duplicate_row', 'Продублировать строку'],
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default [
['plugin_help_welcome_docs_label', 'Documentation'],
['plugin_help_welcome_docs_description', 'Learn how to use {arg:product}'],

['data_viewer_shortcut_open_cell_context_menu', 'Mở menu ngữ cảnh cho ô được chọn'],
['data_viewer_shortcut_start_inline_editing', 'Mở Trình chỉnh sửa inline cho ô được chọn'],
['data_viewer_shortcut_add_new_row', 'Thêm một hàng mới'],
['data_viewer_shortcut_duplicate_row', 'Sao chép hàng'],
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default [
['plugin_help_welcome_docs_label', 'Documentation'],
['plugin_help_welcome_docs_description', 'Learn how to use {arg:product}'],

['data_viewer_shortcut_open_cell_context_menu', '打开所选单元格的上下文菜单'],
['data_viewer_shortcut_start_inline_editing', '为所选单元格打开内联编辑器'],
['data_viewer_shortcut_add_new_row', '添加行'],
['data_viewer_shortcut_duplicate_row', '复制行'],
Expand Down
Loading