Skip to content

Commit

Permalink
undo small mobile styling, add scrolling to smare modal, fix focusabl…
Browse files Browse the repository at this point in the history
…e in focustrap, unite it with focusable for focuslist
  • Loading branch information
srietkerk committed May 8, 2024
1 parent d8472f7 commit 9ae3b08
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 110 deletions.
54 changes: 10 additions & 44 deletions 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;
Expand Down Expand Up @@ -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");
Expand All @@ -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<HTMLElement>) => {
if (!focusableElements?.length) return;

Expand Down Expand Up @@ -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();
}
Expand Down
14 changes: 7 additions & 7 deletions 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;
Expand Down Expand Up @@ -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();
}
}

Expand Down
15 changes: 0 additions & 15 deletions react-common/components/share/ShareInfo.tsx
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -437,7 +423,6 @@ export const ShareInfo = (props: ShareInfoProps) => {
<div className="project-share-social">
<Button className="square-button gray embed mobile-portrait-hidden"
title={lf("Show embed code")}
disabled={hideEmbed}
leftIcon="fas fa-code"
onClick={handleEmbedClick} />
<SocialButton className="square-button facebook"
Expand Down
21 changes: 21 additions & 0 deletions react-common/components/util.tsx
Expand Up @@ -91,4 +91,25 @@ export function screenToSVGCoord(ref: SVGSVGElement, coord: ClientCoordinates) {
screenCoord.x = coord.clientX;
screenCoord.y = coord.clientY;
return screenCoord.matrixTransform(ref.getScreenCTM().inverse());
}

export function findNextFocusableElement(elements: HTMLElement[], focusedIndex: number, index: number, forward: boolean): HTMLElement {
const increment = forward ? 1 : -1;
const element = elements[index];
// in this case, there are no focusable elements
if (focusedIndex === index) {
return element;
}
if (getComputedStyle(element).display !== "none") {
return element;
} else {
if (index + increment >= elements.length) {
index = 0;
} else if (index + increment < 0) {
index = elements.length - 1;
} else {
index += increment;
}
}
return findNextFocusableElement(elements, focusedIndex, index, forward);
}
10 changes: 3 additions & 7 deletions react-common/styles/controls/Modal.less
Expand Up @@ -20,8 +20,10 @@
.common-modal {
width: 50%;
max-width: 40rem;
max-height: 100%;
border-radius: .285rem;
overflow: hidden;
overflow-x: hidden;
overflow-y: auto;
}

.wide > .common-modal {
Expand Down Expand Up @@ -154,9 +156,3 @@
border: 1px solid @highContrastTextColor;
}
}

@media @smallMobileShare {
.wide>.common-modal {
height: 95%;
}
}
3 changes: 0 additions & 3 deletions react-common/styles/react-common-breakpoints.less
Expand Up @@ -4,7 +4,6 @@
@largeMonitorBreakpoint: 1200px;
@widescreenMonitorBreakpoint: 1920px;

@smallMobileScreen: (@mobileBreakpoint + 1px);
@largestMobilePortraitScreen: (@mobileBreakpoint + 100px);
@largestMobileScreen: (@tabletBreakpoint - 1px);
@largestTabletScreen: (@computerBreakpoint - 1px);
Expand All @@ -13,8 +12,6 @@

@portraitMobileAndBelow: ~"only screen and (max-width: @{largestMobilePortraitScreen})";
@mobileAndBelow: ~"only screen and (max-width: @{largestMobileScreen})";
@mobileShare: ~"only screen and (max-width: @{largestMobilePortraitScreen})";
@smallMobileShare: ~"only screen and (max-width: @{smallMobileScreen})";
@tabletAndBelow: ~"only screen and (max-width: @{largestTabletScreen})";
@computerAndBelow: ~"only screen and (max-width: @{largestSmallMonitor})";
@largeMonitorAndBelow: ~"only screen and (max-width: @{largestLargeMonitor})";
Expand Down
35 changes: 1 addition & 34 deletions react-common/styles/share/share.less
Expand Up @@ -320,40 +320,7 @@
}
}

@media only screen and (max-width: 513px) {
.project-share-thumbnail {
img {
max-width: 15rem;
}
}

.project-share-social {
max-width: 12rem;
}

.project-share-content {
margin-left: 1rem;
}

.project-share-actions {
margin: 0.5rem 0;
}

.common-button.menu-button.project-qrcode {
padding: 0 0.25rem;
}

.project-share-data .common-input-attached-button .common-input-wrapper .common-input-group {
height: 2.5rem;
}

.project-share-data .common-input-attached-button .common-button {
height: 2.5rem;
padding: 0.5rem 1rem 0.95rem 0.1rem;
}
}

@media @mobileShare {
@media @mobileAndBelow {
.project-share-info, .gif-recorder-content {
flex-direction: column;
}
Expand Down

0 comments on commit 9ae3b08

Please sign in to comment.