Skip to content

Commit

Permalink
Return focus to active element after send shortcut
Browse files Browse the repository at this point in the history
  • Loading branch information
microbit-robert committed Apr 23, 2024
1 parent e313a04 commit d5bb7ef
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 37 deletions.
6 changes: 3 additions & 3 deletions src/project/SaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ const SaveButton = (props: SaveButtonProps) => {
const actions = useProjectActions();
const intl = useIntl();
const menuButtonRef = useRef<HTMLButtonElement>(null);
const ref = useRef<HTMLElement | null>(null);
const activeElementRef = useRef<HTMLElement | null>(null);
const handleSave = useCallback(() => {
ref.current = document.activeElement as HTMLElement;
actions.save(ref);
activeElementRef.current = document.activeElement as HTMLElement;
actions.save(activeElementRef);
}, [actions]);
useHotkeys(keyboardShortcuts.saveProject, handleSave, globalShortcutConfig);
return (
Expand Down
45 changes: 27 additions & 18 deletions src/project/SendButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
globalShortcutConfig,
keyboardShortcuts,
} from "../common/keyboard-shortcuts";
import { FinalFocusRef } from "./project-actions";

interface SendButtonProps {
size?: ThemeTypings["components"]["Button"]["sizes"];
Expand All @@ -53,24 +54,27 @@ const SendButton = React.forwardRef(
flashing: false,
lastCompleteFlash: 0,
});
const handleSendToMicrobit = useCallback(async () => {
if (flashing.current.flashing) {
// Ignore repeated clicks.
return;
}
flashing.current = {
flashing: true,
lastCompleteFlash: flashing.current.lastCompleteFlash,
};
try {
await actions.flash(sendButtonRef);
} finally {
const handleSendToMicrobit = useCallback(
async (finalFocusRef: FinalFocusRef) => {
if (flashing.current.flashing) {
// Ignore repeated clicks.
return;
}
flashing.current = {
flashing: false,
lastCompleteFlash: new Date().getTime(),
flashing: true,
lastCompleteFlash: flashing.current.lastCompleteFlash,
};
}
}, [flashing, actions, sendButtonRef]);
try {
await actions.flash(finalFocusRef);
} finally {
flashing.current = {
flashing: false,
lastCompleteFlash: new Date().getTime(),
};
}
},
[flashing, actions]
);
const handleFocus = useCallback(
(e: FocusEvent<unknown>) => {
const inProgress = flashing.current.flashing;
Expand All @@ -84,9 +88,14 @@ const SendButton = React.forwardRef(
[flashing]
);
const menuButtonRef = useRef<HTMLButtonElement>(null);
const activeElementRef = useRef<HTMLElement | null>(null);
const handleSendToMicrobitShortcut = useCallback(() => {
activeElementRef.current = document.activeElement as HTMLElement;
handleSendToMicrobit(activeElementRef);
}, [handleSendToMicrobit]);
useHotkeys(
keyboardShortcuts.sendToMicrobit,
handleSendToMicrobit,
handleSendToMicrobitShortcut,
globalShortcutConfig
);
return (
Expand All @@ -106,7 +115,7 @@ const SendButton = React.forwardRef(
size={size}
variant="solid"
leftIcon={<RiUsbLine />}
onClick={handleSendToMicrobit}
onClick={() => handleSendToMicrobit(sendButtonRef)}
>
<FormattedMessage id="send-action" />
</Button>
Expand Down
24 changes: 12 additions & 12 deletions src/project/project-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ import ProjectNameQuestion from "./ProjectNameQuestion";
*/
export type LoadType = "drop-load" | "file-upload";

export type FinalFocusRef = React.RefObject<HTMLElement>;
export type FinalFocusRef = React.RefObject<HTMLElement> | undefined;

export interface MainScriptChoice {
main: string | undefined;
Expand Down Expand Up @@ -122,7 +122,7 @@ export class ProjectActions {
connect = async (
forceConnectHelp: boolean,
userAction: ConnectionAction,
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
): Promise<boolean | undefined> => {
this.logging.event({
type: "connect",
Expand All @@ -149,7 +149,7 @@ export class ProjectActions {
*/
private async showConnectHelp(
force: boolean,
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
): Promise<boolean> {
const showConnectHelpSetting = this.settings.values.showConnectHelp;
if (
Expand Down Expand Up @@ -188,11 +188,11 @@ export class ProjectActions {
private async connectInternal(
options: ConnectOptions,
userAction: ConnectionAction,
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
) {
try {
await this.device.connect(options);
finalFocusRef.current?.focus();
finalFocusRef?.current?.focus();
return true;
} catch (e) {
this.handleWebUSBError(e, userAction, finalFocusRef);
Expand All @@ -203,7 +203,7 @@ export class ProjectActions {
/**
* Disconnect from the device.
*/
disconnect = async (finalFocusRef: React.RefObject<HTMLButtonElement>) => {
disconnect = async (finalFocusRef: FinalFocusRef) => {
this.logging.event({
type: "disconnect",
});
Expand Down Expand Up @@ -490,7 +490,7 @@ export class ProjectActions {
* Flash the device, reporting progress via a dialog.
*/
flash = async (
finalFocusRef: React.RefObject<HTMLButtonElement>,
finalFocusRef: FinalFocusRef,
tryAgain?: boolean
): Promise<void> => {
this.logging.event({
Expand Down Expand Up @@ -787,7 +787,7 @@ export class ProjectActions {
private handleConnectErrorChoice = (
choice: ConnectErrorChoice,
userAction: ConnectionAction,
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
) => {
if (choice !== ConnectErrorChoice.TRY_AGAIN) {
return;
Expand All @@ -801,7 +801,7 @@ export class ProjectActions {

private async handleNotFound(
userAction: ConnectionAction,
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
) {
const choice = await this.dialogs.show<ConnectErrorChoice>((callback) => (
<NotFoundDialog callback={callback} finalFocusRef={finalFocusRef} />
Expand All @@ -812,7 +812,7 @@ export class ProjectActions {
private async handleFirmwareUpdate(
_errorCode: WebUSBErrorCode,
userAction: ConnectionAction,
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
) {
this.device.clearDevice();
const choice = await this.dialogs.show<ConnectErrorChoice>((callback) => (
Expand All @@ -824,7 +824,7 @@ export class ProjectActions {
private async handleWebUSBError(
e: any,
userAction: ConnectionAction,
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
) {
if (e instanceof WebUSBError) {
this.device.emit(EVENT_END_USB_SELECT);
Expand Down Expand Up @@ -859,7 +859,7 @@ export class ProjectActions {
}

private async webusbNotSupportedError(
finalFocusRef: React.RefObject<HTMLButtonElement>
finalFocusRef: FinalFocusRef
): Promise<void> {
if (this.sessionSettings.values.showWebUsbNotSupported) {
await this.dialogs.show<void>((callback) => (
Expand Down
3 changes: 2 additions & 1 deletion src/workbench/connect-dialogs/ConnectDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { FormattedMessage } from "react-intl";
import { GenericDialog } from "../../common/GenericDialog";
import ConnectCableDialogBody from "./ConnectCableDialog";
import ConnectHelpDialogBody from "./ConnectHelpDialog";
import { FinalFocusRef } from "../../project/project-actions";

export const enum ConnectHelpChoice {
Next,
Expand All @@ -21,7 +22,7 @@ interface ConnectHelpDialogProps {
callback: (choice: ConnectHelpChoice) => void;
dialogNormallyHidden: boolean;
shownByRequest: boolean;
finalFocusRef: React.RefObject<HTMLButtonElement>;
finalFocusRef: FinalFocusRef;
}

const enum Stage {
Expand Down
3 changes: 2 additions & 1 deletion src/workbench/connect-dialogs/FirmwareDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RiExternalLinkLine } from "react-icons/ri";
import { FormattedMessage } from "react-intl";
import { GenericDialog } from "../../common/GenericDialog";
import firmwareUpgrade from "./firmware-upgrade.svg";
import { FinalFocusRef } from "../../project/project-actions";

export const enum ConnectErrorChoice {
TRY_AGAIN = "TRY_AGAIN",
Expand All @@ -19,7 +20,7 @@ export const enum ConnectErrorChoice {

interface FirmwareDialogProps {
callback: (choice: ConnectErrorChoice) => void;
finalFocusRef: React.RefObject<HTMLButtonElement>;
finalFocusRef: FinalFocusRef;
}

const FirmwareDialog = ({ callback, finalFocusRef }: FirmwareDialogProps) => {
Expand Down
3 changes: 2 additions & 1 deletion src/workbench/connect-dialogs/NotFoundDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import { GenericDialog } from "../../common/GenericDialog";
import SaveButton from "../../project/SaveButton";
import { ConnectErrorChoice } from "./FirmwareDialog";
import notFound from "./not-found.svg";
import { FinalFocusRef } from "../../project/project-actions";

interface NotFoundDialogProps {
callback: (value: ConnectErrorChoice) => void;
finalFocusRef: React.RefObject<HTMLButtonElement>;
finalFocusRef: FinalFocusRef;
}

export const NotFoundDialog = ({
Expand Down
3 changes: 2 additions & 1 deletion src/workbench/connect-dialogs/WebUSBDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import { FormattedMessage } from "react-intl";
import { GenericDialog } from "../../common/GenericDialog";
import { isChromeOS105 } from "../../device/webusb";
import chromeOSErrorImage from "./chrome-os-105-error.png";
import { FinalFocusRef } from "../../project/project-actions";

interface WebUSBDialogProps {
callback: () => void;
finalFocusRef: React.RefObject<HTMLButtonElement>;
finalFocusRef: FinalFocusRef;
}

export const WebUSBDialog = ({
Expand Down

0 comments on commit d5bb7ef

Please sign in to comment.