diff --git a/webapp/packages/core-blocks/package.json b/webapp/packages/core-blocks/package.json index 4d8f460cd63..5d75b5de626 100644 --- a/webapp/packages/core-blocks/package.json +++ b/webapp/packages/core-blocks/package.json @@ -42,7 +42,7 @@ "mobx-react-lite": "^4", "react": "^19", "react-dom": "^19", - "react-hotkeys-hook": "5.1.0", + "react-hotkeys-hook": "5.2.3", "react-minisearch": "^7", "subscript": "^9", "tslib": "^2" diff --git a/webapp/packages/core-blocks/src/useHotkeys.ts b/webapp/packages/core-blocks/src/useHotkeys.ts index d5c6c90ea59..4690ae4bca0 100644 --- a/webapp/packages/core-blocks/src/useHotkeys.ts +++ b/webapp/packages/core-blocks/src/useHotkeys.ts @@ -1,54 +1,108 @@ /* * 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. */ + +import type { DependencyList } from 'react'; export { useHotkeys, type Hotkey } from 'react-hotkeys-hook'; // TODO: types broken in ESM declare module 'react-hotkeys-hook' { - export type FormTags = 'input' | 'textarea' | 'select' | 'INPUT' | 'TEXTAREA' | 'SELECT'; + export type FormTags = + | 'input' + | 'textarea' + | 'select' + | 'INPUT' + | 'TEXTAREA' + | 'SELECT' + | 'searchbox' + | 'slider' + | 'spinbutton' + | 'menuitem' + | 'menuitemcheckbox' + | 'menuitemradio' + | 'option' + | 'radio' + | 'textbox'; export type Keys = string | readonly string[]; export type Scopes = string | readonly string[]; + + export type EventListenerOptions = + | { + capture?: boolean; + once?: boolean; + passive?: boolean; + signal?: AbortSignal; + } + | boolean; // useCapture + export type KeyboardModifiers = { alt?: boolean; ctrl?: boolean; meta?: boolean; shift?: boolean; mod?: boolean; - useKey?: boolean; + useKey?: boolean; // Custom modifier to listen to the produced key instead of the code }; + export type Hotkey = KeyboardModifiers & { keys?: readonly string[]; scopes?: Scopes; description?: string; isSequence?: boolean; + hotkey: string; + metadata?: Record; }; + export type HotkeysEvent = Hotkey; + export type HotkeyCallback = (keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => void; + export type Trigger = boolean | ((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean); + export type Options = { + // Main setting that determines if the hotkey is enabled or not. (Default: true) enabled?: Trigger; + // Enable hotkeys on a list of tags. (Default: false) enableOnFormTags?: readonly FormTags[] | boolean; + // Enable hotkeys on tags with contentEditable props. (Default: false) enableOnContentEditable?: boolean; + // Ignore evenets based on a condition (Default: undefined) ignoreEventWhen?: (e: KeyboardEvent) => boolean; + // Character to split keys in hotkeys combinations. (Default: +) splitKey?: string; + // Character to separate different hotkeys. (Default: ,) delimiter?: string; + // Scope of the hotkey. (Default: undefined) scopes?: Scopes; + // Trigger on keyup event? (Default: undefined) keyup?: boolean; + // Trigger on keydown event? (Default: true) keydown?: boolean; + // Prevent default browser behavior? (Default: false) preventDefault?: Trigger; + // Use this option to describe what the hotkey does. (Default: undefined) description?: string; + // Listen to events on the document instead of the window. (Default: false) document?: Document; + // Ignore modifiers when matching hotkeys. (Default: false) ignoreModifiers?: boolean; + // Pass through event listener options. (Default: undefined) eventListenerOptions?: EventListenerOptions; + // Listen to the produced key instead of the code. (Default: false) useKey?: boolean; + // The timeout to wait for the next key to be pressed. (Default: 1000ms) sequenceTimeoutMs?: number; + // The character to split the sequence of keys. (Default: >) sequenceSplitKey?: string; + // MetaData | Custom data to store and retrieve with the hotkey (Default: undefined) + metadata?: Record; }; - export type OptionsOrDependencyArray = Options | import('react').DependencyList; + + export type OptionsOrDependencyArray = Options | DependencyList; export function useHotkeys( keys: Keys, callback: HotkeyCallback, diff --git a/webapp/packages/core-view/src/View/CaptureView.tsx b/webapp/packages/core-view/src/View/CaptureView.tsx index 0788a0185c2..a1f2d9a4695 100644 --- a/webapp/packages/core-view/src/View/CaptureView.tsx +++ b/webapp/packages/core-view/src/View/CaptureView.tsx @@ -66,6 +66,7 @@ export const CaptureView = observer>( }, { enabled: allKeys.length > 0, + useKey: true, enableOnFormTags: ['INPUT', 'SELECT', 'TEXTAREA'], preventDefault(event, handler) { // Don't prevent default if event was already handled by a child view diff --git a/webapp/packages/core-view/src/View/parseHotkey.ts b/webapp/packages/core-view/src/View/parseHotkey.ts index 563b12cb8ad..5349da5db08 100644 --- a/webapp/packages/core-view/src/View/parseHotkey.ts +++ b/webapp/packages/core-view/src/View/parseHotkey.ts @@ -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. @@ -65,9 +65,11 @@ export function parseHotkey(hotkey: string, combinationKey = '+'): Hotkey { return { ...modifiers, + hotkey, keys: singleCharKeys, isSequence: false, - useKey: false, + metadata: undefined, + useKey: true, description: undefined, }; } diff --git a/webapp/yarn.lock b/webapp/yarn.lock index d287b3be99e..485e7338ecc 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -1480,7 +1480,7 @@ __metadata: mobx-react-lite: "npm:^4" react: "npm:^19" react-dom: "npm:^19" - react-hotkeys-hook: "npm:5.1.0" + react-hotkeys-hook: "npm:5.2.3" react-minisearch: "npm:^7" rimraf: "npm:^6" subscript: "npm:^9" @@ -16707,13 +16707,13 @@ __metadata: languageName: node linkType: hard -"react-hotkeys-hook@npm:5.1.0": - version: 5.1.0 - resolution: "react-hotkeys-hook@npm:5.1.0" +"react-hotkeys-hook@npm:5.2.3": + version: 5.2.3 + resolution: "react-hotkeys-hook@npm:5.2.3" peerDependencies: react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 10c0/99df6d3c305b139ac7afd073b58575961bf30a819fb23e8f1251b1b3a9f1c7662737f8b6266e3fc42bd5bdfdaca81aa1e019613f95f9a6313267de265e45836d + checksum: 10c0/24ba0eaa2195bb504628aec7813c9014370ae71ab1489359690fba00f04c06d189ed191be1fbcdc3d1b6119e3e6a4051342c0a30b7c5dacd7f7b8457a1672624 languageName: node linkType: hard