diff --git a/src/actions/manager.tsx b/src/actions/manager.tsx index 481dc70b..757d47ca 100644 --- a/src/actions/manager.tsx +++ b/src/actions/manager.tsx @@ -37,7 +37,7 @@ export class ActionManager implements ActionsManagerInterface { action => action.keyTest && action.keyTest(event, elements, appState), ); - if (data.length === 0) return {}; + if (data.length === 0) return null; event.preventDefault(); return data[0].perform(elements, appState, null); diff --git a/src/actions/types.ts b/src/actions/types.ts index 07e2855a..921709d4 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -45,7 +45,7 @@ export interface ActionsManagerInterface { event: KeyboardEvent, elements: readonly ExcalidrawElement[], appState: AppState, - ) => ActionResult | {}; + ) => ActionResult | null; getContextMenuItems: ( elements: readonly ExcalidrawElement[], appState: AppState, diff --git a/src/index.tsx b/src/index.tsx index fc774a30..8731bcac 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -307,11 +307,15 @@ export class App extends React.Component { } if (isInputLike(event.target)) return; - const data = this.actionManager.handleKeyDown(event, elements, this.state); - this.syncActionResult(data); + const actionResult = this.actionManager.handleKeyDown( + event, + elements, + this.state, + ); - if (data.elements !== undefined || data.appState !== undefined) { - return; + if (actionResult) { + this.syncActionResult(actionResult); + if (actionResult) return; } const shape = findShapeByKey(event.key); @@ -371,18 +375,20 @@ export class App extends React.Component { private removeWheelEventListener: (() => void) | undefined; private copyToClipboard = () => { - if (navigator.clipboard) { - const text = JSON.stringify( - elements - .filter(element => element.isSelected) - .map(({ shape, ...el }) => el), - ); + const text = JSON.stringify( + elements + .filter(element => element.isSelected) + .map(({ shape, ...el }) => el), + ); + if ("clipboard" in navigator && "writeText" in navigator.clipboard) { navigator.clipboard.writeText(text); + } else { + document.execCommand("copy"); } }; private pasteFromClipboard = () => { - if (navigator.clipboard) { + if ("clipboard" in navigator && "readText" in navigator.clipboard) { navigator.clipboard .readText() .then(text => this.addElementsFromPaste(text)); @@ -790,9 +796,9 @@ export class App extends React.Component { // fixes mousemove causing selection of UI texts #32 e.preventDefault(); // Preventing the event above disables default behavior - // of defocusing potentially focused input, which is what we want - // when clicking inside the canvas. - if (isInputLike(document.activeElement)) { + // of defocusing potentially focused element, which is what we + // want when clicking inside the canvas. + if (document.activeElement instanceof HTMLElement) { document.activeElement.blur(); } diff --git a/src/utils.ts b/src/utils.ts index 5f6b7965..4c2e2061 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -14,14 +14,25 @@ export function capitalizeString(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } +export function isToolIcon( + target: Element | EventTarget | null, +): target is HTMLElement { + return target instanceof HTMLElement && target.className.includes("ToolIcon"); +} + export function isInputLike( target: Element | EventTarget | null, -): target is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement { +): target is + | HTMLInputElement + | HTMLTextAreaElement + | HTMLSelectElement + | HTMLDivElement { return ( - (target instanceof HTMLElement && target.dataset.type === "wysiwyg") || - target instanceof HTMLInputElement || - target instanceof HTMLTextAreaElement || - target instanceof HTMLSelectElement + ((target instanceof HTMLElement && target.dataset.type === "wysiwyg") || + target instanceof HTMLInputElement || + target instanceof HTMLTextAreaElement || + target instanceof HTMLSelectElement) && + !isToolIcon(target) ); }