From 959734a348024fe4ddd75b9b3cca647a75223c67 Mon Sep 17 00:00:00 2001 From: dwelle Date: Sat, 10 Apr 2021 12:23:31 +0200 Subject: [PATCH] fix cases around blurring --- src/actions/actionFinalize.tsx | 4 ++-- src/actions/manager.tsx | 6 +++++- src/actions/types.ts | 4 +++- src/components/App.tsx | 12 +++++++++++- src/components/Modal.tsx | 1 + src/components/ProjectName.tsx | 8 ++++++++ src/element/textWysiwyg.tsx | 5 ++++- 7 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/actions/actionFinalize.tsx b/src/actions/actionFinalize.tsx index 30db774f97..d314f1a363 100644 --- a/src/actions/actionFinalize.tsx +++ b/src/actions/actionFinalize.tsx @@ -18,7 +18,7 @@ import { isBindingElement } from "../element/typeChecks"; export const actionFinalize = register({ name: "finalize", - perform: (elements, appState, _, { canvas }) => { + perform: (elements, appState, _, { canvas, focusContainer }) => { if (appState.editingLinearElement) { const { elementId, @@ -51,7 +51,7 @@ export const actionFinalize = register({ let newElements = elements; if (window.document.activeElement instanceof HTMLElement) { - window.document.activeElement.blur(); + focusContainer(); } const multiPointElement = appState.multiElement diff --git a/src/actions/manager.tsx b/src/actions/manager.tsx index 3cdc81f0a5..429face9c2 100644 --- a/src/actions/manager.tsx +++ b/src/actions/manager.tsx @@ -12,7 +12,11 @@ import { MODES } from "../constants"; // This is the component, but for now we don't care about anything but its // `canvas` state. -type App = { canvas: HTMLCanvasElement | null; props: AppProps }; +type App = { + canvas: HTMLCanvasElement | null; + focusContainer: () => void; + props: AppProps; +}; export class ActionManager implements ActionsManagerInterface { actions = {} as ActionsManagerInterface["actions"]; diff --git a/src/actions/types.ts b/src/actions/types.ts index 1597714d9e..062b830265 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -15,11 +15,13 @@ export type ActionResult = } | false; +type AppAPI = { canvas: HTMLCanvasElement | null; focusContainer(): void }; + type ActionFn = ( elements: readonly ExcalidrawElement[], appState: Readonly, formData: any, - app: { canvas: HTMLCanvasElement | null }, + app: AppAPI, ) => ActionResult | Promise; export type UpdaterFn = (res: ActionResult) => void; diff --git a/src/components/App.tsx b/src/components/App.tsx index d30f6df1ef..f505409085 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -510,6 +510,10 @@ class App extends React.Component { ); } + public focusContainer = () => { + this.excalidrawContainerRef.current?.focus(); + }; + public getSceneElementsIncludingDeleted = () => { return this.scene.getElementsIncludingDeleted(); }; @@ -789,6 +793,10 @@ class App extends React.Component { this.scene.addCallback(this.onSceneUpdated); this.addEventListeners(); + if (this.excalidrawContainerRef.current) { + this.focusContainer(); + } + if ("ResizeObserver" in window && this.excalidrawContainerRef?.current) { this.resizeObserver = new ResizeObserver(() => { // compute isMobile state @@ -1617,7 +1625,7 @@ class App extends React.Component { setCursorForShape(this.canvas, elementType); } if (isToolIcon(document.activeElement)) { - document.activeElement.blur(); + this.focusContainer(); } if (!isLinearElementType(elementType)) { this.setState({ suggestedBindings: [] }); @@ -1747,6 +1755,8 @@ class App extends React.Component { if (this.state.elementLocked) { setCursorForShape(this.canvas, this.state.elementType); } + + this.focusContainer(); }), element, }); diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index fd5fcb03da..25736832ac 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -38,6 +38,7 @@ export const Modal = (props: {
{props.children}
diff --git a/src/components/ProjectName.tsx b/src/components/ProjectName.tsx index a7d723ef36..fd3486eadb 100644 --- a/src/components/ProjectName.tsx +++ b/src/components/ProjectName.tsx @@ -17,6 +17,14 @@ export class ProjectName extends Component { fileName: this.props.value, }; private handleBlur = (event: any) => { + let parent = (event.target as HTMLInputElement).parentElement; + while (parent) { + if (parent.tabIndex > -1) { + parent.focus(); + break; + } + parent = parent.parentElement; + } const value = event.target.value; if (value !== this.props.value) { this.props.onChange(value); diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index 862a957780..1329e13b0c 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -159,11 +159,14 @@ export const textWysiwyg = ({ // so that we don't need to create separate a callback for event handlers let submittedViaKeyboard = false; const handleSubmit = () => { + // cleanup must be run before onSubmit otherwise when app blurs the wysiwyg + // it'd get stuck in an infinite loop of blur→onSubmit after we re-focus the + // wysiwyg on update + cleanup(); onSubmit({ text: normalizeText(editable.value), viaKeyboard: submittedViaKeyboard, }); - cleanup(); }; const cleanup = () => {