Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/@adobe/spectrum-css-temp/components/tray/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
justify-content: center;
width: 100%;

/* On mobile browsers, vh units are fixed based on the maximum height of the screen.
* However, when you scroll, the toolbar and address bar shrink, making the viewport resize.
* We use the fill-available value to counteract this where supported. */
height: 100vh;
height: -moz-available;
height: -webkit-fill-available;
height: fill-available;

/* Appear above underlay */
z-index: 2;
}
Expand All @@ -39,12 +47,13 @@
width: var(--spectrum-tray-width);
max-width: var(--spectrum-tray-max-width);
min-height: var(--spectrum-tray-min-height);
max-height: calc(100vh - var(--spectrum-tray-margin-top));
max-height: calc(100% - var(--spectrum-tray-margin-top));
position: absolute;
bottom: 0;
overflow: auto;
outline: none;

border-radius: var(--spectrum-tray-full-width-border-radius) var(--spectrum-tray-full-width-border-radius) var(--spectrum-tray-border-radius) var(--spectrum-tray-border-radius);
padding: var(--spectrum-tray-padding-y) var(--spectrum-tray-padding-x);

/* Start offset by the animation distance */
transform: translateY(100%);
Expand Down
18 changes: 17 additions & 1 deletion packages/@react-aria/interactions/src/useFocusVisible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {useEffect, useState} from 'react';

type Modality = 'keyboard' | 'pointer';
type HandlerEvent = PointerEvent | MouseEvent | KeyboardEvent;
type HandlerEvent = PointerEvent | MouseEvent | KeyboardEvent | FocusEvent;
type Handler = (modality: Modality, e: HandlerEvent) => void;
interface FocusVisibleProps {
isTextInput?: boolean,
Expand All @@ -27,6 +27,7 @@ interface FocusVisibleResult {
let isGlobalFocusVisible = true;
let changeHandlers = new Set<Handler>();
let hasSetupGlobalListeners = false;
let hasEventBeforeFocus = false;

const isMac =
typeof window !== 'undefined' && window.navigator != null
Expand All @@ -51,6 +52,7 @@ function isValidKey(e: KeyboardEvent) {
}

function handleKeyboardEvent(e: KeyboardEvent) {
hasEventBeforeFocus = true;
if (isValidKey(e)) {
isGlobalFocusVisible = true;
triggerChangeHandlers('keyboard', e);
Expand All @@ -60,10 +62,22 @@ function handleKeyboardEvent(e: KeyboardEvent) {
function handlePointerEvent(e: PointerEvent | MouseEvent) {
isGlobalFocusVisible = false;
if (e.type === 'mousedown' || e.type === 'pointerdown') {
hasEventBeforeFocus = true;
triggerChangeHandlers('pointer', e);
}
}

function handleFocusEvent(e: FocusEvent) {
// If a focus event occurs without a preceding keyboard or pointer event, switch to keyboard modality.
// This occurs, for example, when navigating a form with the next/previous buttons on iOS.
if (!hasEventBeforeFocus) {
isGlobalFocusVisible = true;
triggerChangeHandlers('keyboard', e);
}

hasEventBeforeFocus = false;
}

// Setup global event listeners to control when keyboard focus style should be visible
function setupGlobalFocusEvents() {
if (hasSetupGlobalListeners) {
Expand All @@ -83,6 +97,8 @@ function setupGlobalFocusEvents() {
document.addEventListener('mouseup', handlePointerEvent, true);
}

document.addEventListener('focusin', handleFocusEvent, false);

hasSetupGlobalListeners = true;
}

Expand Down
15 changes: 15 additions & 0 deletions packages/@react-spectrum/picker/src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ function Picker<T>(props: SpectrumPickerProps<T>, ref: DOMRef<HTMLDivElement>) {
);
}

let [isFocused, onFocusChange] = useState(false);

// If used in a <form>, use a hidden input so the value can be submitted to a server.
// If the collection isn't too big, use a hidden <select> element for this so that browser
// autofill will work. Otherwise, use an <input type="hidden">.
Expand All @@ -148,8 +150,20 @@ function Picker<T>(props: SpectrumPickerProps<T>, ref: DOMRef<HTMLDivElement>) {
// The solution is to use <VisuallyHidden> to hide the elements, which clips the elements to a
// 1px rectangle. In addition, we hide from screen readers with aria-hidden, and make the <select>
// non tabbable with tabIndex={-1}.
//
// In mobile browsers, there are next/previous buttons above the software keyboard for navigating
// between fields in a form. These only support native form inputs that are tabbable. In order to
// support those, an additional hidden input is used to marshall focus to the button. It is tabbable
// except when the button is focused, so that shift tab works properly to go to the actual previous
// input in the form. Using the <select> for this also works, but Safari on iOS briefly flashes
// the native menu on focus, so this isn't ideal. A font-size of 16px or greater is required to
// prevent Safari from zooming in on the input when it is focused.
input = (
<VisuallyHidden aria-hidden="true">
<input
tabIndex={isFocused ? -1 : 0}
style={{fontSize: 16}}
onFocus={() => triggerRef.current.focus()} />
<label>
{label}
<select
Expand Down Expand Up @@ -209,6 +223,7 @@ function Picker<T>(props: SpectrumPickerProps<T>, ref: DOMRef<HTMLDivElement>) {
<FieldButton
{...filterDOMProps(props)}
{...triggerProps}
onFocusChange={onFocusChange}
ref={triggerRef}
isActive={state.isOpen}
isQuiet={isQuiet}
Expand Down