Skip to content

Commit 7ff3c4b

Browse files
Fixed an issue where shortcuts are not working as expected on multiple keyboard layouts. #9157
1 parent e559079 commit 7ff3c4b

File tree

10 files changed

+34
-76
lines changed

10 files changed

+34
-76
lines changed

docs/en_US/release_notes_9_8.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ Bug fixes
3737
| `Issue #9095 <https://github.com/pgadmin-org/pgadmin4/issues/9095>`_ - Fixed an issue where pgAdmin config migration was failing while upgrading to v9.7.
3838
| `Issue #9114 <https://github.com/pgadmin-org/pgadmin4/issues/9114>`_ - Fixed Cross-Origin Opener Policy (COOP) vulnerability in the OAuth 2.0 authentication flow (CVE-2025-9636).
3939
| `Issue #9116 <https://github.com/pgadmin-org/pgadmin4/issues/9116>`_ - Fixed an issue where editor shortcuts fail when using Option key combinations on macOS, due to macOS treating Option+Key as a different key input.
40-
| `Issue #9125 <https://github.com/pgadmin-org/pgadmin4/issues/9125>`_ - Fixed an issue where the pgAdmin configuration database wasn't being created on a fresh install when an external database was used for the configuration.
40+
| `Issue #9125 <https://github.com/pgadmin-org/pgadmin4/issues/9125>`_ - Fixed an issue where the pgAdmin configuration database wasn't being created on a fresh install when an external database was used for the configuration.
41+
| `Issue #9157 <https://github.com/pgadmin-org/pgadmin4/issues/9157>`_ - Fixed an issue where shortcuts are not working as expected on multiple keyboard layouts.

web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"@date-io/core": "^3.0.0",
7878
"@date-io/date-fns": "3.x",
7979
"@emotion/sheet": "^1.0.1",
80+
"@fluentui/keyboard-key": "^0.4.23",
8081
"@fortawesome/fontawesome-free": "latest",
8182
"@mui/icons-material": "^7.3.2",
8283
"@mui/material": "^7.3.2",

web/pgadmin/browser/register_editor_preferences.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ def register_editor_preferences(self):
5252
gettext('Replace'),
5353
'keyboardshortcut',
5454
{
55-
'alt': True,
55+
'alt': False,
5656
'shift': False,
5757
'control': True,
5858
'ctrl_is_meta': True,
5959
'key': {
60-
'key_code': 70,
61-
'char': 'f'
60+
'key_code': 82,
61+
'char': 'r'
6262
}
6363
},
6464
category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,

web/pgadmin/static/js/components/KeyboardShortcuts.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { InputText, ToggleCheckButton } from './FormComponents';
1313
import PropTypes from 'prop-types';
1414
import { isMac } from '../keyboard_shortcuts';
1515
import gettext from 'sources/gettext';
16+
import { getCode } from '@fluentui/keyboard-key';
1617

1718
export default function KeyboardShortcuts({ value, onChange, fields, name }) {
1819
const keyCid = `key-${name}`;
@@ -29,7 +30,7 @@ export default function KeyboardShortcuts({ value, onChange, fields, name }) {
2930
}
3031
newVal.key = {
3132
char: _val,
32-
key_code: e.keyCode
33+
key_code: getCode(e),
3334
};
3435
onChange(newVal);
3536
};

web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ function handlePaste(e) {
102102
checkTrojanSource(copiedText, true);
103103
}
104104

105-
106105
function insertTabWithUnit({ state, dispatch }) {
107106
if (state.selection.ranges.some(r => !r.empty))
108107
return indentMore({ state, dispatch });

web/pgadmin/static/js/custom_hooks.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import React, {useRef, useEffect, useState, useCallback, useLayoutEffect} from '
1010
import moment from 'moment';
1111
import { isMac } from './keyboard_shortcuts';
1212
import { getBrowser } from './utils';
13+
import { getCode } from '@fluentui/keyboard-key';
1314

1415
/* React hook for setInterval */
1516
export function useInterval(callback, delay) {
@@ -185,17 +186,18 @@ export function useKeyboardShortcuts(shortcuts, eleRef) {
185186

186187
const matchFound = (shortcut, e)=>{
187188
if(!shortcut) return false;
188-
let keyCode = e.which || e.keyCode;
189+
let keyCode = getCode(e);
189190
const ctrlKey = (isMac() && shortcut.ctrl_is_meta) ? e.metaKey : e.ctrlKey;
190191

191192
return Boolean(shortcut.alt) == e.altKey &&
192193
Boolean(shortcut.shift) == e.shiftKey &&
193194
Boolean(shortcut.control) == ctrlKey &&
194195
shortcut.key.key_code == keyCode;
195196
};
197+
196198
useEffect(()=>{
197199
let ele = eleRef.current ?? document;
198-
const keydownCallback = (e)=>{
200+
const dispatch = (e)=>{
199201
Promise.resolve(0).then(()=>{
200202
let allListeners = _.filter(shortcutsRef.current, (s)=>matchFound(s.shortcut, e));
201203
for(const {options} of allListeners) {
@@ -209,9 +211,11 @@ export function useKeyboardShortcuts(shortcuts, eleRef) {
209211
}
210212
});
211213
};
212-
ele.addEventListener('keydown', keydownCallback);
214+
ele.addEventListener('keydown', dispatch);
215+
ele.addEventListener('keyup', dispatch);
213216
return ()=>{
214-
ele.removeEventListener('keydown', keydownCallback);
217+
ele.removeEventListener('keydown', dispatch);
218+
ele.removeEventListener('keyup', dispatch);
215219
};
216220
}, [eleRef.current]);
217221

web/pgadmin/static/js/keyboard_shortcuts.js

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,29 +80,8 @@ function shortcut_accesskey_title(title, shortcut) {
8080
}
8181

8282

83-
function _stopEventPropagation(event) {
84-
event.cancelBubble = true;
85-
event.preventDefault();
86-
event.stopPropagation();
87-
event.stopImmediatePropagation();
88-
}
89-
90-
/* Function use to validate shortcut keys */
91-
function validateShortcutKeys(user_defined_shortcut, event) {
92-
if(!user_defined_shortcut) {
93-
return false;
94-
}
95-
96-
let keyCode = event.which || event.keyCode;
97-
return user_defined_shortcut.alt == event.altKey &&
98-
user_defined_shortcut.shift == event.shiftKey &&
99-
user_defined_shortcut.control == event.ctrlKey &&
100-
user_defined_shortcut.key.key_code == keyCode;
101-
}
102-
10383
export {
104-
validateShortcutKeys,
105-
_stopEventPropagation, isMac, isKeyCtrlAlt, isKeyAltShift, isKeyCtrlShift,
84+
isMac, isKeyCtrlAlt, isKeyAltShift, isKeyCtrlShift,
10685
isKeyCtrlAltShift, isAltShiftBoth, isCtrlShiftBoth, isCtrlAltBoth,
10786
shortcut_key, shortcut_title, shortcut_accesskey_title,
10887
};

web/pgadmin/static/js/utils.js

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ import usePreferences from '../../preferences/static/js/store';
1717
import pgAdmin from 'sources/pgadmin';
1818
import { isMac } from './keyboard_shortcuts';
1919
import { WORKSPACES } from '../../browser/static/js/constants';
20+
import { getCode } from '@fluentui/keyboard-key';
2021

21-
export function parseShortcutValue(obj, useKeyboardCode=false) {
22+
export function parseShortcutValue(obj, useCode=false) {
2223
let shortcut = '';
2324
if (!obj){
2425
return null;
@@ -27,11 +28,11 @@ export function parseShortcutValue(obj, useKeyboardCode=false) {
2728
if (obj.shift) { shortcut += 'shift+'; }
2829
if (isMac() && obj.ctrl_is_meta) { shortcut += 'meta+'; }
2930
else if (obj.control) { shortcut += 'ctrl+'; }
30-
shortcut += useKeyboardCode ? shortcutCharToCode(obj?.key.char) : obj?.key.char?.toLowerCase();
31+
shortcut += useCode ? obj?.key.key_code : obj?.key.char?.toLowerCase();
3132
return shortcut;
3233
}
3334

34-
export function parseKeyEventValue(e, useKeyboardCode=false) {
35+
export function parseKeyEventValue(e, useCode=false) {
3536
let shortcut = '';
3637
if(!e) {
3738
return null;
@@ -40,7 +41,7 @@ export function parseKeyEventValue(e, useKeyboardCode=false) {
4041
if (e.shiftKey) { shortcut += 'shift+'; }
4142
if (isMac() && e.metaKey) { shortcut += 'meta+'; }
4243
else if (e.ctrlKey) { shortcut += 'ctrl+'; }
43-
shortcut += useKeyboardCode? e.code : e.key.toLowerCase();
44+
shortcut += useCode? getCode(e) : e.key.toLowerCase();
4445
return shortcut;
4546
}
4647

@@ -49,43 +50,6 @@ export function isShortcutValue(obj) {
4950
return [obj.alt, obj.control, obj?.key, obj?.key?.char].every((k)=>!_.isUndefined(k));
5051
}
5152

52-
// Map shortcut character to key code
53-
export function shortcutCharToCode(char) {
54-
const punctuationMap = {
55-
'`': 'Backquote',
56-
'-': 'Minus',
57-
'=': 'Equal',
58-
'[': 'BracketLeft',
59-
']': 'BracketRight',
60-
'\\': 'Backslash',
61-
';': 'Semicolon',
62-
'\'': 'Quote',
63-
',': 'Comma',
64-
'.': 'Period',
65-
'/': 'Slash',
66-
' ': 'Space',
67-
};
68-
69-
const mappedCode = punctuationMap[char.toLowerCase()];
70-
if (mappedCode) {
71-
return mappedCode;
72-
}
73-
74-
// Fallback for alphanumeric keys (A-Z, 0-9)
75-
const isAlphanumeric = /^[a-z0-9]$/i.test(char);
76-
if (isAlphanumeric) {
77-
if (char.length === 1 && /[a-zA-Z]/.test(char)) {
78-
return `Key${char.toUpperCase()}`;
79-
}
80-
if (char.length === 1 && /[0-9]/.test(char)) {
81-
return `Digit${char}`;
82-
}
83-
}
84-
85-
return char;
86-
}
87-
88-
8953
export function getEnterKeyHandler(clickHandler) {
9054
return (e)=>{
9155
if(e.code === 'Enter'){

web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { LayoutDockerContext, LAYOUT_EVENTS } from '../../../../../../static/js/
1515
import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveContent';
1616
import gettext from 'sources/gettext';
1717
import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
18-
import { checkTrojanSource, isShortcutValue, parseKeyEventValue, parseShortcutValue, shortcutCharToCode } from '../../../../../../static/js/utils';
18+
import { checkTrojanSource, isShortcutValue, parseKeyEventValue, parseShortcutValue } from '../../../../../../static/js/utils';
1919
import { usePgAdmin } from '../../../../../../static/js/PgAdminProvider';
2020
import ConfirmPromotionContent from '../dialogs/ConfirmPromotionContent';
2121
import ConfirmExecuteQueryContent from '../dialogs/ConfirmExecuteQueryContent';
@@ -296,7 +296,6 @@ export default function Query({onTextSelect, setQtStatePartial}) {
296296
// this function creates a key object from the shortcut preference
297297
let key = {
298298
keyCode: pref.key.key_code,
299-
code: shortcutCharToCode(pref.key.char),
300299
metaKey: false,
301300
ctrlKey: pref.control,
302301
shiftKey: pref.shift,

web/yarn.lock

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1967,6 +1967,15 @@ __metadata:
19671967
languageName: node
19681968
linkType: hard
19691969

1970+
"@fluentui/keyboard-key@npm:^0.4.23":
1971+
version: 0.4.23
1972+
resolution: "@fluentui/keyboard-key@npm:0.4.23"
1973+
dependencies:
1974+
tslib: "npm:^2.1.0"
1975+
checksum: 10c0/f4c159f40632387e227efc2d7f1a0b4ec088da2c2cc669bc0598b4dd3c00953dbc554d665961fd57bbb9952a5081ab7f39b2c67d4cfc46d0a2a70d54fc60def2
1976+
languageName: node
1977+
linkType: hard
1978+
19701979
"@fortawesome/fontawesome-common-types@npm:6.7.2":
19711980
version: 6.7.2
19721981
resolution: "@fortawesome/fontawesome-common-types@npm:6.7.2"
@@ -12861,6 +12870,7 @@ __metadata:
1286112870
"@emotion/sheet": "npm:^1.0.1"
1286212871
"@emotion/styled": "npm:^11.11.0"
1286312872
"@emotion/utils": "npm:^1.0.0"
12873+
"@fluentui/keyboard-key": "npm:^0.4.23"
1286412874
"@fortawesome/fontawesome-free": "npm:latest"
1286512875
"@mui/icons-material": "npm:^7.3.2"
1286612876
"@mui/material": "npm:^7.3.2"
@@ -14351,7 +14361,7 @@ __metadata:
1435114361
languageName: node
1435214362
linkType: hard
1435314363

14354-
"tslib@npm:^2.0.3, tslib@npm:^2.4.0, tslib@npm:^2.7.0":
14364+
"tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.7.0":
1435514365
version: 2.8.1
1435614366
resolution: "tslib@npm:2.8.1"
1435714367
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62

0 commit comments

Comments
 (0)