diff --git a/react-common/components/controls/FocusList.tsx b/react-common/components/controls/FocusList.tsx index 102dbd399d9..8cac53464b1 100644 --- a/react-common/components/controls/FocusList.tsx +++ b/react-common/components/controls/FocusList.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { ContainerProps } from "../util"; +import { ContainerProps, findNextFocusableElement } from "../util"; export interface FocusListProps extends ContainerProps { role: string; @@ -41,7 +41,9 @@ export const FocusList = (props: FocusListProps) => { focusableElements = []; for (const element of focusable.values()) { - focusableElements.push(element as HTMLElement); + if (getComputedStyle(element).display !== "none") { + focusableElements.push(element as HTMLElement); + } // Remove them from the tab order, menu items are navigable using the arrow keys element.setAttribute("tabindex", "-1"); @@ -57,42 +59,6 @@ export const FocusList = (props: FocusListProps) => { } } - const isFocusable = (e: HTMLElement) => { - return e.getAttribute("data-isfocusable") === "true" - && getComputedStyle(e).display !== "none"; - } - - const firstFocusableElement = () => { - return focusableElements.find(e => isFocusable(e)) - } - - const lastFocusableElement = () => { - for (let i = 0; i < focusableElements.length; i++) { - if (isFocusable(focusableElements[focusableElements.length - 1 - i])) { - return focusableElements[focusableElements.length - 1 - i]; - } - } - - return focusableElements[0]; - } - - const nextFocusableElement = (index: number, forwards: boolean) => { - let current: HTMLElement - for (let i = 1; i < focusableElements.length; i++) { - if (forwards) { - current = focusableElements[(index + i) % focusableElements.length]; - } - else { - current = focusableElements[(index + focusableElements.length - i) % focusableElements.length]; - } - - if (isFocusable(current)) { - return current; - } - } - return focusableElements[0]; - } - const onKeyDown = (e: React.KeyboardEvent) => { if (!focusableElements?.length) return; @@ -120,31 +86,31 @@ export const FocusList = (props: FocusListProps) => { } else if (e.key === (useUpAndDownArrowKeys ? "ArrowDown" : "ArrowRight")) { if (index === focusableElements.length - 1 || target === focusList) { - focus(firstFocusableElement()); + focus(findNextFocusableElement(focusableElements, index, 0, true)); } else { - focus(nextFocusableElement(index, true)); + focus(findNextFocusableElement(focusableElements, index, index + 1, true)); } e.preventDefault(); e.stopPropagation(); } else if (e.key === (useUpAndDownArrowKeys ? "ArrowUp" : "ArrowLeft")) { if (index === 0 || target === focusList) { - focus(lastFocusableElement()); + focus(findNextFocusableElement(focusableElements, index, focusableElements.length - 1, false)); } else { - focus(nextFocusableElement(index, false)); + focus(findNextFocusableElement(focusableElements, index, index - 1, false)); } e.preventDefault(); e.stopPropagation(); } else if (e.key === "Home") { - focus(firstFocusableElement()); + focus(findNextFocusableElement(focusableElements, index, 0, true)); e.preventDefault(); e.stopPropagation(); } else if (e.key === "End") { - focus(lastFocusableElement()); + focus(findNextFocusableElement(focusableElements, index, focusableElements.length - 1, true)); e.preventDefault(); e.stopPropagation(); } diff --git a/react-common/components/controls/FocusTrap.tsx b/react-common/components/controls/FocusTrap.tsx index 17ff6c343ca..5c214a44fc8 100644 --- a/react-common/components/controls/FocusTrap.tsx +++ b/react-common/components/controls/FocusTrap.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { classList, nodeListToArray } from "../util"; +import { classList, nodeListToArray, findNextFocusableElement } from "../util"; export interface FocusTrapProps extends React.PropsWithChildren<{}> { onEscape: () => void; @@ -58,24 +58,24 @@ export const FocusTrap = (props: FocusTrapProps) => { if (forward) { if (goToEnd) { - focusable[focusable.length - 1].focus(); + findNextFocusableElement(focusable, index, focusable.length - 1, forward).focus(); } else if (index === focusable.length - 1) { - focusable[0].focus(); + findNextFocusableElement(focusable, index, 0, forward).focus(); } else { - focusable[index + 1].focus(); + findNextFocusableElement(focusable, index, index + 1, forward).focus(); } } else { if (goToEnd) { - focusable[0].focus(); + findNextFocusableElement(focusable, index, 0, forward).focus(); } else if (index === 0) { - focusable[focusable.length - 1].focus(); + findNextFocusableElement(focusable, index, focusable.length - 1, forward).focus(); } else { - focusable[Math.max(index - 1, 0)].focus(); + findNextFocusableElement(focusable, index, Math.max(index - 1, 0), forward).focus(); } } diff --git a/react-common/components/share/ShareInfo.tsx b/react-common/components/share/ShareInfo.tsx index 024ce2c44f2..ffd1a492889 100644 --- a/react-common/components/share/ShareInfo.tsx +++ b/react-common/components/share/ShareInfo.tsx @@ -32,18 +32,6 @@ export interface ShareInfoProps { onClose: () => void; } -function useWindowWidth() { - const [size, setSize] = React.useState(window.innerWidth); - React.useLayoutEffect(() => { - function updateSize() { - setSize(window.innerWidth); - } - window.addEventListener('resize', updateSize); - updateSize(); - return () => window.removeEventListener('resize', updateSize); - }, []); - return size; - } export const ShareInfo = (props: ShareInfoProps) => { const { @@ -71,8 +59,6 @@ export const ShareInfo = (props: ShareInfoProps) => { const [ kioskState, setKioskState ] = React.useState(false); const [ isAnonymous, setIsAnonymous ] = React.useState(!isLoggedIn || anonymousShareByDefault); const [ isShowingMultiConfirmation, setIsShowingMultiConfirmation ] = React.useState(false); - // this is 854 because we have some dissonance between our styling and semantic ui's styling - const hideEmbed = useWindowWidth() <= 766; const { simScreenshot, simGif } = pxt.appTarget.appTheme; const showSimulator = (simScreenshot || simGif) && !!simRecorder; @@ -437,7 +423,6 @@ export const ShareInfo = (props: ShareInfoProps) => {