From 9005e909baba3c106081ad2c04fc1cf459239769 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 31 Jan 2021 16:20:14 -0600 Subject: [PATCH] refactor common context menu code --- src/editor/Editor.tsx | 40 +++-------------------------- src/terminal/Terminal.tsx | 41 +++--------------------------- src/utils/IContextMenuTarget.ts | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 73 deletions(-) create mode 100644 src/utils/IContextMenuTarget.ts diff --git a/src/editor/Editor.tsx b/src/editor/Editor.tsx index 361d66a5..e6eaf9c7 100644 --- a/src/editor/Editor.tsx +++ b/src/editor/Editor.tsx @@ -1,13 +1,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2021 The Pybricks Authors -import { - ContextMenu, - Menu, - MenuDivider, - MenuItem, - ResizeSensor, -} from '@blueprintjs/core'; +import { Menu, MenuDivider, MenuItem, ResizeSensor } from '@blueprintjs/core'; import { WithI18nProps, withI18n } from '@shopify/react-i18n'; import { Ace, config } from 'ace-builds'; import React from 'react'; @@ -18,6 +12,7 @@ import { compile } from '../mpy/actions'; import { RootState } from '../reducers'; import { toggleBoolean } from '../settings/actions'; import { SettingId } from '../settings/defaults'; +import { IContextMenuTarget, handleContextMenu } from '../utils/IContextMenuTarget'; import { isMacOS } from '../utils/os'; import { setEditSession, storageChanged } from './actions'; import { EditorStringId } from './i18n'; @@ -47,7 +42,7 @@ type DispatchProps = { type EditorProps = StateProps & DispatchProps & WithI18nProps; -class Editor extends React.Component { +class Editor extends React.Component implements IContextMenuTarget { private editorRef: React.RefObject; private keyBindings?: Array<{ key: string; command: string }>; @@ -82,34 +77,7 @@ class Editor extends React.Component { render(): JSX.Element { const { i18n, darkMode, onSessionChanged, onCheck, onToggleDocs } = this.props; return ( -
{ - // istanbul ignore if: not expected - if (e.defaultPrevented) { - return; - } - - e.preventDefault(); - const menu = this.renderContextMenu(); - const listener = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - // istanbul ignore if: not expected - if (e.defaultPrevented) { - return; - } - - e.preventDefault(); - ContextMenu.hide(); - } - }; - window.addEventListener('keydown', listener); - ContextMenu.show(menu, { left: e.clientX, top: e.clientY }, () => { - window.removeEventListener('keydown', listener); - this.onContextMenuClose(); - }); - }} - > +
handleContextMenu(e, this)}> this.editor?.resize()}> { +class Terminal extends React.Component implements IContextMenuTarget { private xterm: XTerm; private fitAddon: FitAddon; private terminalRef: React.RefObject; @@ -118,34 +112,7 @@ class Terminal extends React.Component { : 'rgba(181,213,255,0.5)', // this should match AceEditor theme }); return ( -
{ - // istanbul ignore if: not expected - if (e.defaultPrevented) { - return; - } - - e.preventDefault(); - const menu = this.renderContextMenu(); - const listener = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - // istanbul ignore if: not expected - if (e.defaultPrevented) { - return; - } - - e.preventDefault(); - ContextMenu.hide(); - } - }; - window.addEventListener('keydown', listener); - ContextMenu.show(menu, { left: e.clientX, top: e.clientY }, () => { - window.removeEventListener('keydown', listener); - this.onContextMenuClose(); - }); - }} - > +
handleContextMenu(e, this)}> this.fitAddon.fit()}>
diff --git a/src/utils/IContextMenuTarget.ts b/src/utils/IContextMenuTarget.ts new file mode 100644 index 00000000..9e0f598e --- /dev/null +++ b/src/utils/IContextMenuTarget.ts @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors +// +// Alternative to @blueprintjs/core ContextMenuTarget decorator. + +import { ContextMenu } from '@blueprintjs/core'; + +export interface IContextMenuTarget { + renderContextMenu(): JSX.Element; + onContextMenuClose(): void; +} + +export function handleContextMenu( + event: React.MouseEvent, + target: IContextMenuTarget, +): void { + // istanbul ignore if: not expected + if (event.defaultPrevented) { + return; + } + + event.preventDefault(); + + const listener = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + // istanbul ignore if: not expected + if (e.defaultPrevented) { + return; + } + + e.preventDefault(); + ContextMenu.hide(); + } + }; + window.addEventListener('keydown', listener); + + ContextMenu.show( + target.renderContextMenu(), + { left: event.clientX, top: event.clientY }, + () => { + window.removeEventListener('keydown', listener); + target.onContextMenuClose(); + }, + ); +}