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
2 changes: 2 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ export default [{

"rsp-rules/no-react-key": [ERROR],
"rsp-rules/sort-imports": [ERROR],
"rsp-rules/no-non-shadow-contains": [ERROR],
"rulesdir/imports": [ERROR],
"rulesdir/useLayoutEffectRule": [ERROR],
"rulesdir/pure-render": [ERROR],
Expand Down Expand Up @@ -428,6 +429,7 @@ export default [{
"rsp-rules/no-react-key": [ERROR],
"rsp-rules/act-events-test": ERROR,
"rsp-rules/no-getByRole-toThrow": ERROR,
"rsp-rules/no-non-shadow-contains": OFF,
"rulesdir/imports": OFF,
"monorepo/no-internal-import": OFF,
"jsdoc/require-jsdoc": OFF
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/actiongroup/src/useActionGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {AriaActionGroupProps} from '@react-types/actiongroup';
import {createFocusManager} from '@react-aria/focus';
import {DOMAttributes, FocusableElement, Orientation, RefObject} from '@react-types/shared';
import {filterDOMProps, useLayoutEffect} from '@react-aria/utils';
import {filterDOMProps, nodeContains, useLayoutEffect} from '@react-aria/utils';
import {ListState} from '@react-stately/list';
import {useLocale} from '@react-aria/i18n';
import {useState} from 'react';
Expand Down Expand Up @@ -48,7 +48,7 @@ export function useActionGroup<T>(props: AriaActionGroupProps<T>, state: ListSta
let focusManager = createFocusManager(ref);
let flipDirection = direction === 'rtl' && orientation === 'horizontal';
let onKeyDown = (e) => {
if (!e.currentTarget.contains(e.target)) {
if (!nodeContains(e.currentTarget, e.target)) {
return;
}

Expand Down
8 changes: 4 additions & 4 deletions packages/@react-aria/calendar/src/useRangeCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
import {AriaRangeCalendarProps, DateValue} from '@react-types/calendar';
import {CalendarAria, useCalendarBase} from './useCalendarBase';
import {FocusableElement, RefObject} from '@react-types/shared';
import {nodeContains, useEvent} from '@react-aria/utils';
import {RangeCalendarState} from '@react-stately/calendar';
import {useEvent} from '@react-aria/utils';
import {useRef} from 'react';

/**
Expand Down Expand Up @@ -52,8 +52,8 @@ export function useRangeCalendar<T extends DateValue>(props: AriaRangeCalendarPr
let target = e.target as Element;
if (
ref.current &&
ref.current.contains(document.activeElement) &&
(!ref.current.contains(target) || !target.closest('button, [role="button"]'))
nodeContains(ref.current, document.activeElement) &&
(!nodeContains(ref.current, target) || !target.closest('button, [role="button"]'))
) {
state.selectFocusedDate();
}
Expand All @@ -66,7 +66,7 @@ export function useRangeCalendar<T extends DateValue>(props: AriaRangeCalendarPr
if (!ref.current) {
return;
}
if ((!e.relatedTarget || !ref.current.contains(e.relatedTarget)) && state.anchorDate) {
if ((!e.relatedTarget || !nodeContains(ref.current, e.relatedTarget)) && state.anchorDate) {
state.selectFocusedDate();
}
};
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/combobox/src/useComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {AriaComboBoxProps} from '@react-types/combobox';
import {ariaHideOutside} from '@react-aria/overlays';
import {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';
import {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';
import {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
import {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
import {ComboBoxState} from '@react-stately/combobox';
import {dispatchVirtualFocus} from '@react-aria/focus';
import {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef} from 'react';
Expand Down Expand Up @@ -181,7 +181,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta

let onBlur = (e: FocusEvent<HTMLInputElement>) => {
let blurFromButton = buttonRef?.current && buttonRef.current === e.relatedTarget;
let blurIntoPopover = popoverRef.current?.contains(e.relatedTarget);
let blurIntoPopover = nodeContains(popoverRef.current, e.relatedTarget);
// Ignore blur if focused moved to the button(if exists) or into the popover.
if (blurFromButton || blurIntoPopover) {
return;
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/datepicker/src/useDatePicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {CalendarProps} from '@react-types/calendar';
import {createFocusManager} from '@react-aria/focus';
import {DatePickerState} from '@react-stately/datepicker';
import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, RefObject, ValidationResult} from '@react-types/shared';
import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
import {filterDOMProps, mergeProps, nodeContains, useDescription, useId} from '@react-aria/utils';
// @ts-ignore
import intlMessages from '../intl/*.json';
import {privateValidationStateProp} from '@react-stately/form';
Expand Down Expand Up @@ -84,7 +84,7 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
onBlurWithin: e => {
// Ignore when focus moves into the popover.
let dialog = document.getElementById(dialogId);
if (!dialog?.contains(e.relatedTarget)) {
if (!nodeContains(dialog, e.relatedTarget)) {
isFocused.current = false;
props.onBlur?.(e);
props.onFocusChange?.(false);
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/datepicker/src/useDatePickerGroup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {createFocusManager, getFocusableTreeWalker} from '@react-aria/focus';
import {DateFieldState, DatePickerState, DateRangePickerState} from '@react-stately/datepicker';
import {DOMAttributes, FocusableElement, KeyboardEvent, RefObject} from '@react-types/shared';
import {mergeProps} from '@react-aria/utils';
import {mergeProps, nodeContains} from '@react-aria/utils';
import {useLocale} from '@react-aria/i18n';
import {useMemo} from 'react';
import {usePress} from '@react-aria/interactions';
Expand All @@ -12,7 +12,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState

// Open the popover on alt + arrow down
let onKeyDown = (e: KeyboardEvent) => {
if (!e.currentTarget.contains(e.target)) {
if (!nodeContains(e.currentTarget, e.target as Element)) {
return;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/datepicker/src/useDateRangePicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {DateRange, RangeCalendarProps} from '@react-types/calendar';
import {DateRangePickerState} from '@react-stately/datepicker';
import {DEFAULT_VALIDATION_RESULT, mergeValidation, privateValidationStateProp} from '@react-stately/form';
import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, RefObject, ValidationResult} from '@react-types/shared';
import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
import {filterDOMProps, mergeProps, nodeContains, useDescription, useId} from '@react-aria/utils';
import {focusManagerSymbol, roleSymbol} from './useDateField';
// @ts-ignore
import intlMessages from '../intl/*.json';
Expand Down Expand Up @@ -116,7 +116,7 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
onBlurWithin: e => {
// Ignore when focus moves into the popover.
let dialog = document.getElementById(dialogId);
if (!dialog?.contains(e.relatedTarget)) {
if (!nodeContains(dialog, e.relatedTarget)) {
isFocused.current = false;
props.onBlur?.(e);
props.onFocusChange?.(false);
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/datepicker/src/useDateSegment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import {CalendarDate, toCalendar} from '@internationalized/date';
import {DateFieldState, DateSegment} from '@react-stately/datepicker';
import {getScrollParent, isIOS, isMac, mergeProps, scrollIntoViewport, useEvent, useId, useLabels, useLayoutEffect} from '@react-aria/utils';
import {getScrollParent, isIOS, isMac, mergeProps, nodeContains, scrollIntoViewport, useEvent, useId, useLabels, useLayoutEffect} from '@react-aria/utils';
import {hookData} from './useDateField';
import {NumberParser} from '@internationalized/number';
import React, {CSSProperties, useMemo, useRef} from 'react';
Expand Down Expand Up @@ -281,7 +281,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
// Otherwise, when tapping on a segment in Android Chrome and then entering text,
// composition events will be fired that break the DOM structure and crash the page.
let selection = window.getSelection();
if (selection?.anchorNode && ref.current?.contains(selection?.anchorNode)) {
if (selection?.anchorNode && nodeContains(ref.current, selection?.anchorNode)) {
selection.collapse(ref.current);
}
});
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/dialog/src/useDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import {AriaDialogProps} from '@react-types/dialog';
import {DOMAttributes, FocusableElement, RefObject} from '@react-types/shared';
import {filterDOMProps, useSlotId} from '@react-aria/utils';
import {filterDOMProps, nodeContains, useSlotId} from '@react-aria/utils';
import {focusSafely} from '@react-aria/interactions';
import {useEffect, useRef} from 'react';
import {useOverlayFocusContain} from '@react-aria/overlays';
Expand Down Expand Up @@ -40,7 +40,7 @@ export function useDialog(props: AriaDialogProps, ref: RefObject<FocusableElemen

// Focus the dialog itself on mount, unless a child element is already focused.
useEffect(() => {
if (ref.current && !ref.current.contains(document.activeElement)) {
if (ref.current && !nodeContains(ref.current, document.activeElement)) {
focusSafely(ref.current);

// Safari on iOS does not move the VoiceOver cursor to the dialog
Expand Down
16 changes: 8 additions & 8 deletions packages/@react-aria/dnd/src/DragManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {announce} from '@react-aria/live-announcer';
import {ariaHideOutside} from '@react-aria/overlays';
import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget, FocusableElement} from '@react-types/shared';
import {getDragModality, getTypes} from './utils';
import {isVirtualClick, isVirtualPointerEvent} from '@react-aria/utils';
import {isVirtualClick, isVirtualPointerEvent, nodeContains} from '@react-aria/utils';
import type {LocalizedStringFormatter} from '@internationalized/string';
import {RefObject, useEffect, useState} from 'react';

Expand Down Expand Up @@ -114,7 +114,7 @@ function endDragging() {

export function isValidDropTarget(element: Element): boolean {
for (let target of dropTargets.keys()) {
if (target.contains(element)) {
if (nodeContains(target, element)) {
return true;
}
}
Expand Down Expand Up @@ -243,7 +243,7 @@ class DragSession {
this.cancelEvent(e);

if (e.key === 'Enter') {
if (e.altKey || this.getCurrentActivateButton()?.contains(e.target as Node)) {
if (e.altKey || nodeContains(this.getCurrentActivateButton(), e.target as Node)) {
this.activate(this.currentDropTarget, this.currentDropItem);
} else {
this.drop();
Expand Down Expand Up @@ -275,7 +275,7 @@ class DragSession {

let dropTarget =
this.validDropTargets.find(target => target.element === e.target as HTMLElement) ||
this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
this.validDropTargets.find(target => nodeContains(target.element, e.target as HTMLElement));

if (!dropTarget) {
// if (e.target === activateButton) {
Expand Down Expand Up @@ -321,10 +321,10 @@ class DragSession {
this.cancelEvent(e);
if (isVirtualClick(e) || this.isVirtualClick) {
let dropElements = dropItems.values();
let item = [...dropElements].find(item => item.element === e.target as HTMLElement || item.activateButtonRef?.current?.contains(e.target as HTMLElement));
let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
let item = [...dropElements].find(item => item.element === e.target as HTMLElement || nodeContains(item.activateButtonRef?.current, e.target as HTMLElement));
let dropTarget = this.validDropTargets.find(target => nodeContains(target.element, e.target as HTMLElement));
let activateButton = item?.activateButtonRef?.current ?? dropTarget?.activateButtonRef?.current;
if (activateButton?.contains(e.target as HTMLElement) && dropTarget) {
if (nodeContains(activateButton, e.target as HTMLElement) && dropTarget) {
this.activate(dropTarget, item);
return;
}
Expand Down Expand Up @@ -401,7 +401,7 @@ class DragSession {
// Filter out drop targets that contain valid items. We don't want to stop hiding elements
// other than the drop items that exist inside the collection.
let visibleDropTargets = this.validDropTargets.filter(target =>
!validDropItems.some(item => target.element.contains(item.element))
!validDropItems.some(item => nodeContains(target.element, item.element))
);

this.restoreAriaHidden = ariaHideOutside([
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/dnd/src/useDrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {DragEvent, useRef, useState} from 'react';
import * as DragManager from './DragManager';
import {DragTypes, globalAllowedDropOperations, globalDndState, readFromDataTransfer, setGlobalDnDState, setGlobalDropEffect} from './utils';
import {DROP_EFFECT_TO_DROP_OPERATION, DROP_OPERATION, DROP_OPERATION_ALLOWED, DROP_OPERATION_TO_DROP_EFFECT} from './constants';
import {isIPad, isMac, useEffectEvent, useLayoutEffect} from '@react-aria/utils';
import {isIPad, isMac, nodeContains, useEffectEvent, useLayoutEffect} from '@react-aria/utils';
import {useVirtualDrop} from './useVirtualDrop';

export interface DropOptions {
Expand Down Expand Up @@ -234,7 +234,7 @@ export function useDrop(options: DropOptions): DropResult {

state.dragOverElements.delete(e.target as Element);
for (let element of state.dragOverElements) {
if (!e.currentTarget.contains(element)) {
if (!nodeContains(e.currentTarget, element)) {
state.dragOverElements.delete(element);
}
}
Expand Down
9 changes: 5 additions & 4 deletions packages/@react-aria/focus/src/FocusScope.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
isChrome,
isFocusable,
isTabbable,
nodeContains,
ShadowTreeWalker,
useLayoutEffect
} from '@react-aria/utils';
Expand Down Expand Up @@ -440,7 +441,7 @@ function isElementInScope(element?: Element | null, scope?: Element[] | null) {
if (!scope) {
return false;
}
return scope.some(node => node.contains(element));
return scope.some(node => nodeContains(node, element));
}

function isElementInChildScope(element: Element, scope: ScopeRef = null) {
Expand Down Expand Up @@ -771,7 +772,7 @@ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions
{
acceptNode(node) {
// Skip nodes inside the starting node.
if (opts?.from?.contains(node)) {
if (nodeContains(opts?.from, node)) {
return NodeFilter.FILTER_REJECT;
}

Expand Down Expand Up @@ -822,7 +823,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
let node = from || getActiveElement(getOwnerDocument(root));
let walker = getFocusableTreeWalker(root, {tabbable, accept});
if (root.contains(node)) {
if (nodeContains(root, node)) {
walker.currentNode = node!;
}
let nextNode = walker.nextNode() as FocusableElement;
Expand All @@ -843,7 +844,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
let node = from || getActiveElement(getOwnerDocument(root));
let walker = getFocusableTreeWalker(root, {tabbable, accept});
if (root.contains(node)) {
if (nodeContains(root, node)) {
walker.currentNode = node!;
} else {
let next = last(walker);
Expand Down
6 changes: 3 additions & 3 deletions packages/@react-aria/grid/src/useGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import {AriaLabelingProps, DOMAttributes, DOMProps, Key, KeyboardDelegate, RefObject} from '@react-types/shared';
import {filterDOMProps, mergeProps, useId} from '@react-aria/utils';
import {filterDOMProps, mergeProps, nodeContains, useId} from '@react-aria/utils';
import {GridCollection} from '@react-types/grid';
import {GridKeyboardDelegate} from './GridKeyboardDelegate';
import {gridMap} from './utils';
Expand Down Expand Up @@ -136,15 +136,15 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
let onFocus = useCallback((e) => {
if (manager.isFocused) {
// If a focus event bubbled through a portal, reset focus state.
if (!e.currentTarget.contains(e.target)) {
if (!nodeContains(e.currentTarget, e.target)) {
manager.setFocused(false);
}

return;
}

// Focus events can bubble through portals. Ignore these events.
if (!e.currentTarget.contains(e.target)) {
if (!nodeContains(e.currentTarget, e.target)) {
return;
}

Expand Down
10 changes: 5 additions & 5 deletions packages/@react-aria/grid/src/useGridCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {DOMAttributes, FocusableElement, Key, RefObject} from '@react-types/shared';
import {focusSafely, isFocusVisible} from '@react-aria/interactions';
import {getFocusableTreeWalker} from '@react-aria/focus';
import {getScrollParent, mergeProps, scrollIntoViewport} from '@react-aria/utils';
import {getScrollParent, mergeProps, nodeContains, scrollIntoViewport} from '@react-aria/utils';
import {GridCollection, GridNode} from '@react-types/grid';
import {gridMap} from './utils';
import {GridState} from '@react-stately/grid';
Expand Down Expand Up @@ -75,7 +75,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
let treeWalker = getFocusableTreeWalker(ref.current);
if (focusMode === 'child') {
// If focus is already on a focusable child within the cell, early return so we don't shift focus
if (ref.current.contains(document.activeElement) && ref.current !== document.activeElement) {
if (nodeContains(ref.current, document.activeElement) && ref.current !== document.activeElement) {
return;
}

Expand All @@ -90,7 +90,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps

if (
(keyWhenFocused.current != null && node.key !== keyWhenFocused.current) ||
!ref.current.contains(document.activeElement)
!nodeContains(ref.current, document.activeElement)
) {
focusSafely(ref.current);
}
Expand All @@ -109,7 +109,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
});

let onKeyDownCapture = (e: ReactKeyboardEvent) => {
if (!e.currentTarget.contains(e.target as Element) || state.isKeyboardNavigationDisabled || !ref.current || !document.activeElement) {
if (!nodeContains(e.currentTarget, e.target as Element) || state.isKeyboardNavigationDisabled || !ref.current || !document.activeElement) {
return;
}

Expand Down Expand Up @@ -213,7 +213,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
// Prevent this event from reaching cell children, e.g. menu buttons. We want arrow keys to navigate
// to the cell above/below instead. We need to re-dispatch the event from a higher parent so it still
// bubbles and gets handled by useSelectableCollection.
if (!e.altKey && ref.current.contains(e.target as Element)) {
if (!e.altKey && nodeContains(ref.current, e.target as Element)) {
e.stopPropagation();
e.preventDefault();
ref.current.parentElement?.dispatchEvent(
Expand Down
Loading
Loading