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. #9116
							parent
							
								
									e09af0a0e9
								
							
						
					
					
						commit
						2dac2d0e21
					
				| 
						 | 
				
			
			@ -34,4 +34,5 @@ Bug fixes
 | 
			
		|||
*********
 | 
			
		||||
 | 
			
		||||
  | `Issue #9090 <https://github.com/pgadmin-org/pgadmin4/issues/9090>`_ -  Pin Paramiko to version 3.5.1 to fix the DSSKey error introduced in the latest release.
 | 
			
		||||
  | `Issue #9095 <https://github.com/pgadmin-org/pgadmin4/issues/9095>`_ -  Fixed an issue where pgAdmin config migration was failing while upgrading to v9.7.
 | 
			
		||||
  | `Issue #9095 <https://github.com/pgadmin-org/pgadmin4/issues/9095>`_ -  Fixed an issue where pgAdmin config migration was failing while upgrading to v9.7.
 | 
			
		||||
  | `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.
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import CustomPropTypes from '../../custom_prop_types';
 | 
			
		|||
import FindDialog from './components/FindDialog';
 | 
			
		||||
import GotoDialog from './components/GotoDialog';
 | 
			
		||||
import usePreferences from '../../../../preferences/static/js/store';
 | 
			
		||||
import { toCodeMirrorKey } from '../../utils';
 | 
			
		||||
import { parseKeyEventValue, parseShortcutValue } from '../../utils';
 | 
			
		||||
 | 
			
		||||
const Root = styled('div')(() => ({
 | 
			
		||||
  position: 'relative',
 | 
			
		||||
| 
						 | 
				
			
			@ -103,39 +103,46 @@ export default function CodeMirror({className, currEditor, showCopyBtn=false, cu
 | 
			
		|||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const finalCustomKeyMap = useMemo(()=>[{
 | 
			
		||||
    key: toCodeMirrorKey(editorPrefs.find), run: () => {
 | 
			
		||||
      setShowFind(prevVal => [true, false, !prevVal[2]]);
 | 
			
		||||
  // We're not using CodeMirror keymap and using any instead
 | 
			
		||||
  // because on Mac, the alt key combination creates special
 | 
			
		||||
  // chars and https://github.com/codemirror/view/commit/3cea8dba19845fe75bea4eae756c6103694f49f3
 | 
			
		||||
  const customShortcuts = {
 | 
			
		||||
    [parseShortcutValue(editorPrefs.find, true)]: () => {
 | 
			
		||||
      setTimeout(()=>{
 | 
			
		||||
        setShowFind(prevVal => [true, false, !prevVal[2]]);
 | 
			
		||||
      }, 0);
 | 
			
		||||
    },
 | 
			
		||||
    preventDefault: true,
 | 
			
		||||
    stopPropagation: true,
 | 
			
		||||
  }, {
 | 
			
		||||
    key: toCodeMirrorKey(editorPrefs.replace), run: () => {
 | 
			
		||||
      setShowFind(prevVal => [true, true, !prevVal[2]]);
 | 
			
		||||
    [parseShortcutValue(editorPrefs.replace, true)]: () => {
 | 
			
		||||
      setTimeout(()=>{
 | 
			
		||||
        setShowFind(prevVal => [true, true, !prevVal[2]]);
 | 
			
		||||
      }, 0);
 | 
			
		||||
    },
 | 
			
		||||
    preventDefault: true,
 | 
			
		||||
    stopPropagation: true,
 | 
			
		||||
  }, {
 | 
			
		||||
    key: toCodeMirrorKey(editorPrefs.goto_line_col), run: () => {
 | 
			
		||||
    [parseShortcutValue(editorPrefs.goto_line_col, true)]: () => {
 | 
			
		||||
      setShowGoto(true);
 | 
			
		||||
    },
 | 
			
		||||
    preventDefault: true,
 | 
			
		||||
    stopPropagation: true,
 | 
			
		||||
  }, {
 | 
			
		||||
    key: toCodeMirrorKey(editorPrefs.comment), run: () => {
 | 
			
		||||
    [parseShortcutValue(editorPrefs.comment, true)]: () => {
 | 
			
		||||
      editor.current?.execCommand('toggleComment');
 | 
			
		||||
    },
 | 
			
		||||
    preventDefault: true,
 | 
			
		||||
    stopPropagation: true,
 | 
			
		||||
  },{
 | 
			
		||||
    key: toCodeMirrorKey(editorPrefs.format_sql), run: formatSQL,
 | 
			
		||||
    preventDefault: true,
 | 
			
		||||
    stopPropagation: true,
 | 
			
		||||
  },{
 | 
			
		||||
    key: toCodeMirrorKey(preferences.auto_complete), run: startCompletion,
 | 
			
		||||
    preventDefault: true,
 | 
			
		||||
  },
 | 
			
		||||
  ...customKeyMap], [customKeyMap]);
 | 
			
		||||
    [parseShortcutValue(editorPrefs.format_sql, true)]: formatSQL,
 | 
			
		||||
    [parseShortcutValue(preferences.auto_complete, true)]: startCompletion,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const finalCustomKeyMap = useMemo(() => [
 | 
			
		||||
    {
 | 
			
		||||
      any: (view, e) => {
 | 
			
		||||
        const eventStr = parseKeyEventValue(e, true);
 | 
			
		||||
        const callback = customShortcuts[eventStr];
 | 
			
		||||
        if(callback) {
 | 
			
		||||
          e.preventDefault();
 | 
			
		||||
          e.stopPropagation();
 | 
			
		||||
          callback(view);
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    ...customKeyMap
 | 
			
		||||
  ], [customKeyMap]);
 | 
			
		||||
 | 
			
		||||
  const closeFind = () => {
 | 
			
		||||
    setShowFind([false, false, false]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@ import pgAdmin from 'sources/pgadmin';
 | 
			
		|||
import { isMac } from './keyboard_shortcuts';
 | 
			
		||||
import { WORKSPACES } from '../../browser/static/js/constants';
 | 
			
		||||
 | 
			
		||||
export function parseShortcutValue(obj) {
 | 
			
		||||
export function parseShortcutValue(obj, useKeyboardCode=false) {
 | 
			
		||||
  let shortcut = '';
 | 
			
		||||
  if (!obj){
 | 
			
		||||
    return null;
 | 
			
		||||
| 
						 | 
				
			
			@ -27,11 +27,11 @@ export function parseShortcutValue(obj) {
 | 
			
		|||
  if (obj.shift) { shortcut += 'shift+'; }
 | 
			
		||||
  if (isMac() && obj.ctrl_is_meta) { shortcut += 'meta+'; }
 | 
			
		||||
  else if (obj.control) { shortcut += 'ctrl+'; }
 | 
			
		||||
  shortcut += obj?.key.char?.toLowerCase();
 | 
			
		||||
  shortcut += useKeyboardCode ? shortcutCharToCode(obj?.key.char) : obj?.key.char?.toLowerCase();
 | 
			
		||||
  return shortcut;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function parseKeyEventValue(e) {
 | 
			
		||||
export function parseKeyEventValue(e, useKeyboardCode=false) {
 | 
			
		||||
  let shortcut = '';
 | 
			
		||||
  if(!e) {
 | 
			
		||||
    return null;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ export function parseKeyEventValue(e) {
 | 
			
		|||
  if (e.shiftKey) { shortcut += 'shift+'; }
 | 
			
		||||
  if (isMac() && e.metaKey) { shortcut += 'meta+'; }
 | 
			
		||||
  else if (e.ctrlKey) { shortcut += 'ctrl+'; }
 | 
			
		||||
  shortcut += e.key.toLowerCase();
 | 
			
		||||
  shortcut += useKeyboardCode? e.code : e.key.toLowerCase();
 | 
			
		||||
  return shortcut;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +49,42 @@ export function isShortcutValue(obj) {
 | 
			
		|||
  return [obj.alt, obj.control, obj?.key, obj?.key?.char].every((k)=>!_.isUndefined(k));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Map shortcut character to key code
 | 
			
		||||
export function shortcutCharToCode(char) {
 | 
			
		||||
  const punctuationMap = {
 | 
			
		||||
    '`': 'Backquote',
 | 
			
		||||
    '-': 'Minus',
 | 
			
		||||
    '=': 'Equal',
 | 
			
		||||
    '[': 'BracketLeft',
 | 
			
		||||
    ']': 'BracketRight',
 | 
			
		||||
    '\\': 'Backslash',
 | 
			
		||||
    ';': 'Semicolon',
 | 
			
		||||
    '\'': 'Quote',
 | 
			
		||||
    ',': 'Comma',
 | 
			
		||||
    '.': 'Period',
 | 
			
		||||
    '/': 'Slash',
 | 
			
		||||
    ' ': 'Space',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const mappedCode = punctuationMap[char.toLowerCase()];
 | 
			
		||||
  if (mappedCode) {
 | 
			
		||||
    return mappedCode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Fallback for alphanumeric keys (A-Z, 0-9)
 | 
			
		||||
  const isAlphanumeric = /^[a-z0-9]$/i.test(char);
 | 
			
		||||
  if (isAlphanumeric) {
 | 
			
		||||
    if (char.length === 1 && /[a-zA-Z]/.test(char)) {
 | 
			
		||||
      return `Key${char.toUpperCase()}`;
 | 
			
		||||
    }
 | 
			
		||||
    if (char.length === 1 && /[0-9]/.test(char)) {
 | 
			
		||||
      return `Digit${char}`;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return char;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export function getEnterKeyHandler(clickHandler) {
 | 
			
		||||
  return (e)=>{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ import { LayoutDockerContext, LAYOUT_EVENTS } from '../../../../../../static/js/
 | 
			
		|||
import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveContent';
 | 
			
		||||
import gettext from 'sources/gettext';
 | 
			
		||||
import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
 | 
			
		||||
import { checkTrojanSource, isShortcutValue, parseKeyEventValue, parseShortcutValue } from '../../../../../../static/js/utils';
 | 
			
		||||
import { checkTrojanSource, isShortcutValue, parseKeyEventValue, parseShortcutValue, shortcutCharToCode } from '../../../../../../static/js/utils';
 | 
			
		||||
import { usePgAdmin } from '../../../../../../static/js/PgAdminProvider';
 | 
			
		||||
import ConfirmPromotionContent from '../dialogs/ConfirmPromotionContent';
 | 
			
		||||
import ConfirmExecuteQueryContent from '../dialogs/ConfirmExecuteQueryContent';
 | 
			
		||||
| 
						 | 
				
			
			@ -296,6 +296,7 @@ export default function Query({onTextSelect, setQtStatePartial}) {
 | 
			
		|||
      // this function creates a key object from the shortcut preference
 | 
			
		||||
      let key = {
 | 
			
		||||
        keyCode: pref.key.key_code,
 | 
			
		||||
        code: shortcutCharToCode(pref.key.char),
 | 
			
		||||
        metaKey: false,
 | 
			
		||||
        ctrlKey: pref.control,
 | 
			
		||||
        shiftKey: pref.shift,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue