fix cases around blurring

This commit is contained in:
dwelle 2021-04-10 12:23:31 +02:00
parent 652d35b7ce
commit 959734a348
7 changed files with 34 additions and 6 deletions

View file

@ -18,7 +18,7 @@ import { isBindingElement } from "../element/typeChecks";
export const actionFinalize = register({ export const actionFinalize = register({
name: "finalize", name: "finalize",
perform: (elements, appState, _, { canvas }) => { perform: (elements, appState, _, { canvas, focusContainer }) => {
if (appState.editingLinearElement) { if (appState.editingLinearElement) {
const { const {
elementId, elementId,
@ -51,7 +51,7 @@ export const actionFinalize = register({
let newElements = elements; let newElements = elements;
if (window.document.activeElement instanceof HTMLElement) { if (window.document.activeElement instanceof HTMLElement) {
window.document.activeElement.blur(); focusContainer();
} }
const multiPointElement = appState.multiElement const multiPointElement = appState.multiElement

View file

@ -12,7 +12,11 @@ import { MODES } from "../constants";
// This is the <App> component, but for now we don't care about anything but its // This is the <App> component, but for now we don't care about anything but its
// `canvas` state. // `canvas` state.
type App = { canvas: HTMLCanvasElement | null; props: AppProps }; type App = {
canvas: HTMLCanvasElement | null;
focusContainer: () => void;
props: AppProps;
};
export class ActionManager implements ActionsManagerInterface { export class ActionManager implements ActionsManagerInterface {
actions = {} as ActionsManagerInterface["actions"]; actions = {} as ActionsManagerInterface["actions"];

View file

@ -15,11 +15,13 @@ export type ActionResult =
} }
| false; | false;
type AppAPI = { canvas: HTMLCanvasElement | null; focusContainer(): void };
type ActionFn = ( type ActionFn = (
elements: readonly ExcalidrawElement[], elements: readonly ExcalidrawElement[],
appState: Readonly<AppState>, appState: Readonly<AppState>,
formData: any, formData: any,
app: { canvas: HTMLCanvasElement | null }, app: AppAPI,
) => ActionResult | Promise<ActionResult>; ) => ActionResult | Promise<ActionResult>;
export type UpdaterFn = (res: ActionResult) => void; export type UpdaterFn = (res: ActionResult) => void;

View file

@ -510,6 +510,10 @@ class App extends React.Component<AppProps, AppState> {
); );
} }
public focusContainer = () => {
this.excalidrawContainerRef.current?.focus();
};
public getSceneElementsIncludingDeleted = () => { public getSceneElementsIncludingDeleted = () => {
return this.scene.getElementsIncludingDeleted(); return this.scene.getElementsIncludingDeleted();
}; };
@ -789,6 +793,10 @@ class App extends React.Component<AppProps, AppState> {
this.scene.addCallback(this.onSceneUpdated); this.scene.addCallback(this.onSceneUpdated);
this.addEventListeners(); this.addEventListeners();
if (this.excalidrawContainerRef.current) {
this.focusContainer();
}
if ("ResizeObserver" in window && this.excalidrawContainerRef?.current) { if ("ResizeObserver" in window && this.excalidrawContainerRef?.current) {
this.resizeObserver = new ResizeObserver(() => { this.resizeObserver = new ResizeObserver(() => {
// compute isMobile state // compute isMobile state
@ -1617,7 +1625,7 @@ class App extends React.Component<AppProps, AppState> {
setCursorForShape(this.canvas, elementType); setCursorForShape(this.canvas, elementType);
} }
if (isToolIcon(document.activeElement)) { if (isToolIcon(document.activeElement)) {
document.activeElement.blur(); this.focusContainer();
} }
if (!isLinearElementType(elementType)) { if (!isLinearElementType(elementType)) {
this.setState({ suggestedBindings: [] }); this.setState({ suggestedBindings: [] });
@ -1747,6 +1755,8 @@ class App extends React.Component<AppProps, AppState> {
if (this.state.elementLocked) { if (this.state.elementLocked) {
setCursorForShape(this.canvas, this.state.elementType); setCursorForShape(this.canvas, this.state.elementType);
} }
this.focusContainer();
}), }),
element, element,
}); });

View file

@ -38,6 +38,7 @@ export const Modal = (props: {
<div <div
className="Modal__content" className="Modal__content"
style={{ "--max-width": `${props.maxWidth}px` }} style={{ "--max-width": `${props.maxWidth}px` }}
tabIndex={0}
> >
{props.children} {props.children}
</div> </div>

View file

@ -17,6 +17,14 @@ export class ProjectName extends Component<Props, State> {
fileName: this.props.value, fileName: this.props.value,
}; };
private handleBlur = (event: any) => { 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; const value = event.target.value;
if (value !== this.props.value) { if (value !== this.props.value) {
this.props.onChange(value); this.props.onChange(value);

View file

@ -159,11 +159,14 @@ export const textWysiwyg = ({
// so that we don't need to create separate a callback for event handlers // so that we don't need to create separate a callback for event handlers
let submittedViaKeyboard = false; let submittedViaKeyboard = false;
const handleSubmit = () => { 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({ onSubmit({
text: normalizeText(editable.value), text: normalizeText(editable.value),
viaKeyboard: submittedViaKeyboard, viaKeyboard: submittedViaKeyboard,
}); });
cleanup();
}; };
const cleanup = () => { const cleanup = () => {