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: 7 additions & 6 deletions packages/@adobe/spectrum-css-temp/components/tray/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@

.spectrum-Tray-wrapper {
inset-inline-start: 0;
/* Positioned at the top of the window */
position: fixed;
position: absolute;
top: 0;

display: flex;
justify-content: center;
width: 100%;
height: 100vh;
height: 100dvh;

/* Don't catch clicks */
pointer-events: none;
Expand Down Expand Up @@ -55,10 +54,12 @@
max-height: calc(var(--spectrum-visual-viewport-height) - var(--spectrum-tray-margin-top));
/* Add padding at the bottom to account for the rest of the viewport height behind the keyboard.
* This is necessary so that there isn't a visible gap that appears while the keyboard is animating
* in and out. Fall back to the safe area inset to account for things like iOS home indicator. */
padding-bottom: max(calc(100vh - var(--spectrum-visual-viewport-height)), env(safe-area-inset-bottom));
* in and out. Fall back to the safe area inset to account for things like iOS home indicator.
We also add an additional 100vh of padding (offset by the bottom position below) so the tray
extends behind Safari's address bar and keyboard in iOS 26. */
padding-bottom: calc(max(calc(100dvh - var(--spectrum-visual-viewport-height)), env(safe-area-inset-bottom)) + 100vh);
position: absolute;
bottom: 0;
bottom: -100vh;
outline: none;
display: flex;
flex-direction: column;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ governing permissions and limitations under the License.
.spectrum-Underlay {
composes: spectrum-overlay;

position: fixed;
/* Use position: absolute instead of fixed to avoid being clipped to the "inner" viewport in iOS 26 */
position: absolute;
top: 0;
right: 0;
bottom: 0;
Expand Down
5 changes: 1 addition & 4 deletions packages/@react-aria/autocomplete/src/useAutocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {AriaLabelingProps, BaseEvent, DOMProps, FocusableElement, FocusEvents, KeyboardEvents, Node, RefObject, ValueBase} from '@react-types/shared';
import {AriaTextFieldProps} from '@react-aria/textfield';
import {AutocompleteProps, AutocompleteState} from '@react-stately/autocomplete';
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getOwnerDocument, getOwnerWindow, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useLabels, useObjectRef, useSlotId} from '@react-aria/utils';
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getOwnerDocument, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useLabels, useObjectRef, useSlotId} from '@react-aria/utils';
import {dispatchVirtualBlur, dispatchVirtualFocus, getVirtuallyFocusedElement, moveVirtualFocus} from '@react-aria/focus';
import {getInteractionModality} from '@react-aria/interactions';
// @ts-ignore
Expand Down Expand Up @@ -106,9 +106,6 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
// Ensure input is focused if the user clicks on the collection directly.
if (!e.isTrusted && shouldUseVirtualFocus && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) !== inputRef.current) {
inputRef.current.focus();
if (inputRef.current instanceof getOwnerWindow(inputRef.current).HTMLInputElement) {
inputRef.current.select();
}
}

let target = e.target as Element | null;
Expand Down
18 changes: 10 additions & 8 deletions packages/@react-aria/collections/src/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,16 +461,14 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
}

private removeNode(node: ElementNode<T>): void {
if (node.node == null) {
return;
}

for (let child of node) {
this.removeNode(child);
}

let collection = this.getMutableCollection();
collection.removeNode(node.node.key);
if (node.node) {
let collection = this.getMutableCollection();
collection.removeNode(node.node.key);
}
}

/** Finalizes the collection update, updating all nodes and freezing the collection. */
Expand Down Expand Up @@ -508,12 +506,16 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
this.addNode(element);
}

if (element.node) {
this.dirtyNodes.delete(element);
}

element.isMutated = false;
} else {
this.dirtyNodes.delete(element);
}
}

this.dirtyNodes.clear();

// Finally, update the collection.
if (this.nextCollection) {
this.nextCollection.commit(this.firstVisibleChild?.node?.key ?? null, this.lastVisibleChild?.node?.key ?? null, this.isSSR);
Expand Down
22 changes: 16 additions & 6 deletions packages/@react-aria/dialog/docs/useDialog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ The `Modal` and `ModalTrigger` components render the dialog within a typical mod
```tsx example export=true render=false
import {useOverlayTriggerState} from '@react-stately/overlays';
import {Overlay, useModalOverlay, useOverlayTrigger} from '@react-aria/overlays';
import {useViewportSize} from '@react-aria/utils';

function Modal({state, children, ...props}) {
let ref = React.useRef(null);
Expand All @@ -140,18 +141,27 @@ function Modal({state, children, ...props}) {
<Overlay>
<div
style={{
position: 'fixed',
position: 'absolute',
zIndex: 100,
top: 0,
left: 0,
bottom: 0,
right: 0,
background: 'rgba(0, 0, 0, 0.5)',
width: '100%',
height: document.body.clientHeight,
background: 'rgba(0, 0, 0, 0.5)'
}}
{...underlayProps} />
<div
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: useViewportSize().height + 'px',
zIndex: 101,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
{...underlayProps}>
}}>
<div
{...modalProps}
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-aria/disclosure/src/useDisclosure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
role: 'group',
'aria-labelledby': triggerId,
'aria-hidden': !state.isExpanded,
hidden: isSSR ? !state.isExpanded : undefined
hidden: (isSSR || isDisabled) ? (isDisabled || !state.isExpanded) : undefined
}
};
}
11 changes: 0 additions & 11 deletions packages/@react-aria/focus/src/FocusScope.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
getActiveElement,
getEventTarget,
getOwnerDocument,
getOwnerWindow,
isAndroid,
isChrome,
isFocusable,
Expand Down Expand Up @@ -372,10 +371,6 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
// restore focus to the previously focused node or the first tabbable element in the active scope.
if (focusedNode.current) {
focusedNode.current.focus();

if (focusedNode.current instanceof getOwnerWindow(focusedNode.current).HTMLInputElement) {
focusedNode.current.select();
}
} else if (activeScope && activeScope.current) {
focusFirstInScope(activeScope.current);
}
Expand Down Expand Up @@ -404,9 +399,6 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
if (target && target.isConnected) {
focusedNode.current = target;
focusedNode.current?.focus();
if (focusedNode.current instanceof getOwnerWindow(focusedNode.current).HTMLInputElement) {
focusedNode.current.select();
}
} else if (activeScope.current) {
focusFirstInScope(activeScope.current);
}
Expand Down Expand Up @@ -494,9 +486,6 @@ function focusElement(element: FocusableElement | null, scroll = false) {
} else if (element != null) {
try {
element.focus();
if (element instanceof getOwnerWindow(element).HTMLInputElement) {
element.select();
}
} catch {
// ignore
}
Expand Down
7 changes: 0 additions & 7 deletions packages/@react-aria/interactions/src/focusSafely.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
focusWithoutScrolling,
getActiveElement,
getOwnerDocument,
getOwnerWindow,
runAfterTransition
} from '@react-aria/utils';
import {getInteractionModality} from './useFocusVisible';
Expand All @@ -38,15 +37,9 @@ export function focusSafely(element: FocusableElement): void {
// If focus did not move and the element is still in the document, focus it.
if (getActiveElement(ownerDocument) === lastFocusedElement && element.isConnected) {
focusWithoutScrolling(element);
if (element instanceof getOwnerWindow(element).HTMLInputElement) {
element.select();
}
}
});
} else {
focusWithoutScrolling(element);
if (element instanceof getOwnerWindow(element).HTMLInputElement) {
element.select();
}
}
}
22 changes: 16 additions & 6 deletions packages/@react-aria/overlays/docs/useModalOverlay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The `Modal` component uses an &lt;<TypeLink links={docs.links} type={docs.export

```tsx example export=true render=false
import {Overlay, useModalOverlay} from '@react-aria/overlays';
import {useViewportSize} from '@react-aria/utils';

function Modal({state, children, ...props}) {
let ref = React.useRef(null);
Expand All @@ -84,18 +85,27 @@ function Modal({state, children, ...props}) {
<Overlay>
<div
style={{
position: 'fixed',
position: 'absolute',
zIndex: 100,
top: 0,
left: 0,
bottom: 0,
right: 0,
background: 'rgba(0, 0, 0, 0.5)',
width: '100%',
height: document.body.clientHeight,
background: 'rgba(0, 0, 0, 0.5)'
}}
{...underlayProps} />
<div
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: useViewportSize().height + 'px',
zIndex: 101,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
{...underlayProps}>
}}>
<div
{...modalProps}
ref={ref}
Expand Down
Loading
Loading