Skip to content
Open
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
3 changes: 3 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ export default [{
"rsp-rules/no-react-key": [ERROR],
"rsp-rules/sort-imports": [ERROR],
"rsp-rules/no-non-shadow-contains": [ERROR],
"rsp-rules/safe-event-target": [ERROR],
"rulesdir/imports": [ERROR],
"rulesdir/useLayoutEffectRule": [ERROR],
"rulesdir/pure-render": [ERROR],
Expand Down Expand Up @@ -430,6 +431,7 @@ export default [{
"rsp-rules/act-events-test": ERROR,
"rsp-rules/no-getByRole-toThrow": ERROR,
"rsp-rules/no-non-shadow-contains": OFF,
"rsp-rules/safe-event-target": OFF,
"rulesdir/imports": OFF,
"monorepo/no-internal-import": OFF,
"jsdoc/require-jsdoc": OFF
Expand Down Expand Up @@ -470,6 +472,7 @@ export default [{
rules: {
"jsdoc/require-jsdoc": OFF,
"jsdoc/require-description": OFF,
"rsp-rules/safe-event-target": OFF,
},
}, {
files: [
Expand Down
8 changes: 4 additions & 4 deletions packages/@react-aria/actiongroup/src/useActionGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
import {AriaActionGroupProps} from '@react-types/actiongroup';
import {createFocusManager} from '@react-aria/focus';
import {DOMAttributes, FocusableElement, Orientation, RefObject} from '@react-types/shared';
import {filterDOMProps, nodeContains, useLayoutEffect} from '@react-aria/utils';
import {filterDOMProps, getEventTarget, nodeContains, useLayoutEffect} from '@react-aria/utils';
import {KeyboardEventHandler, useState} from 'react';
import {ListState} from '@react-stately/list';
import {useLocale} from '@react-aria/i18n';
import {useState} from 'react';

const BUTTON_GROUP_ROLES = {
'none': 'toolbar',
Expand Down Expand Up @@ -47,8 +47,8 @@ export function useActionGroup<T>(props: AriaActionGroupProps<T>, state: ListSta
let {direction} = useLocale();
let focusManager = createFocusManager(ref);
let flipDirection = direction === 'rtl' && orientation === 'horizontal';
let onKeyDown = (e) => {
if (!nodeContains(e.currentTarget, e.target)) {
let onKeyDown: KeyboardEventHandler = (e) => {
if (!nodeContains(e.currentTarget, getEventTarget(e))) {
return;
}

Expand Down
10 changes: 5 additions & 5 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, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useId, useLabels, useLayoutEffect, useObjectRef} from '@react-aria/utils';
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getEventTarget, getOwnerDocument, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useId, useLabels, useLayoutEffect, useObjectRef} from '@react-aria/utils';
import {dispatchVirtualBlur, dispatchVirtualFocus, getVirtuallyFocusedElement, moveVirtualFocus} from '@react-aria/focus';
import {getInteractionModality, getPointerType} from '@react-aria/interactions';
// @ts-ignore
Expand Down Expand Up @@ -112,7 +112,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
inputRef.current.focus();
}

let target = e.target as Element | null;
let target = getEventTarget(e) as Element | null;
if (e.isTrusted || !target || queuedActiveDescendant.current === target.id) {
return;
}
Expand Down Expand Up @@ -225,7 +225,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
let keyDownTarget = useRef<Element | null>(null);
// For textfield specific keydown operations
let onKeyDown = (e: BaseEvent<ReactKeyboardEvent<any>>) => {
keyDownTarget.current = e.target as Element;
keyDownTarget.current = getEventTarget(e) as Element;
if (e.nativeEvent.isComposing) {
return;
}
Expand Down Expand Up @@ -329,7 +329,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
// Dispatch simulated key up events for things like triggering links in listbox
// Make sure to stop the propagation of the input keyup event so that the simulated keyup/down pair
// is detected by usePress instead of the original keyup originating from the input
if (e.target === keyDownTarget.current) {
if (getEventTarget(e) === keyDownTarget.current) {
e.stopImmediatePropagation();
let focusedNodeId = queuedActiveDescendant.current;
if (focusedNodeId == null) {
Expand Down Expand Up @@ -386,7 +386,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut

let curFocusedNode = queuedActiveDescendant.current ? document.getElementById(queuedActiveDescendant.current) : null;
if (curFocusedNode) {
let target = e.target;
let target = getEventTarget(e);
queueMicrotask(() => {
// instead of focusing the last focused node, just focus the collection instead and have the collection handle what item to focus via useSelectableCollection/Item
dispatchVirtualBlur(target, collectionRef.current);
Expand Down
15 changes: 8 additions & 7 deletions packages/@react-aria/calendar/src/useCalendarCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {CalendarDate, isEqualDay, isSameDay, isToday} from '@internationalized/date';
import {CalendarState, RangeCalendarState} from '@react-stately/calendar';
import {DOMAttributes, RefObject} from '@react-types/shared';
import {focusWithoutScrolling, getScrollParent, mergeProps, scrollIntoViewport, useDeepMemo, useDescription} from '@react-aria/utils';
import {focusWithoutScrolling, getEventTarget, getScrollParent, mergeProps, scrollIntoViewport, useDeepMemo, useDescription} from '@react-aria/utils';
import {getEraFormat, hookData} from './utils';
import {getInteractionModality, usePress} from '@react-aria/interactions';
// @ts-ignore
Expand Down Expand Up @@ -333,17 +333,18 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
state.highlightDate(date);
}
},
onPointerDown(e) {
onPointerDown(e: PointerEvent) {
// This is necessary on touch devices to allow dragging
// outside the original pressed element.
// (JSDOM does not support this)
if ('releasePointerCapture' in e.target) {
if ('hasPointerCapture' in e.target) {
if (e.target.hasPointerCapture(e.pointerId)) {
e.target.releasePointerCapture(e.pointerId);
let target = getEventTarget(e);
if (target instanceof HTMLElement && 'releasePointerCapture' in target) {
if ('hasPointerCapture' in target) {
if (target.hasPointerCapture(e.pointerId)) {
target.releasePointerCapture(e.pointerId);
}
} else {
e.target.releasePointerCapture(e.pointerId);
(target as HTMLElement).releasePointerCapture(e.pointerId);
}
}
},
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/calendar/src/useRangeCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
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 {getEventTarget, nodeContains, useEvent} from '@react-aria/utils';
import {RangeCalendarState} from '@react-stately/calendar';
import {useRef} from 'react';

Expand Down Expand Up @@ -49,7 +49,7 @@ export function useRangeCalendar<T extends DateValue>(props: AriaRangeCalendarPr
return;
}

let target = e.target as Element;
let target = getEventTarget(e) as Element;
if (
ref.current &&
nodeContains(ref.current, document.activeElement) &&
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/color/src/useColorWheel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {AriaColorWheelProps} from '@react-types/color';
import {ColorWheelState} from '@react-stately/color';
import {DOMAttributes, RefObject} from '@react-types/shared';
import {focusWithoutScrolling, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
import {focusWithoutScrolling, getEventTarget, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
import React, {ChangeEvent, InputHTMLAttributes, useCallback, useRef} from 'react';
import {useKeyboard, useMove} from '@react-aria/interactions';
import {useLocale} from '@react-aria/i18n';
Expand Down Expand Up @@ -328,7 +328,7 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
name,
form,
onChange: (e: ChangeEvent<HTMLInputElement>) => {
state.setHue(parseFloat(e.target.value));
state.setHue(parseFloat(getEventTarget(e).value));
},
style: visuallyHiddenProps.style,
'aria-errormessage': props['aria-errormessage'],
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, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
import {chain, getActiveElement, getEventTarget, 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 @@ -264,7 +264,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
return;
}

let rect = (e.target as Element).getBoundingClientRect();
let rect = (getEventTarget(e) as Element).getBoundingClientRect();
let touch = e.changedTouches[0];

let centerX = Math.ceil(rect.left + .5 * rect.width);
Expand Down
8 changes: 4 additions & 4 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, nodeContains} from '@react-aria/utils';
import {getEventTarget, 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 (!nodeContains(e.currentTarget, e.target as Element)) {
if (!nodeContains(e.currentTarget, getEventTarget(e) as Element)) {
return;
}

Expand All @@ -32,7 +32,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
e.stopPropagation();
if (direction === 'rtl') {
if (ref.current) {
let target = e.target as FocusableElement;
let target = getEventTarget(e) as FocusableElement;
let prev = findNextSegment(ref.current, target.getBoundingClientRect().left, -1);

if (prev) {
Expand All @@ -48,7 +48,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
e.stopPropagation();
if (direction === 'rtl') {
if (ref.current) {
let target = e.target as FocusableElement;
let target = getEventTarget(e) as FocusableElement;
let next = findNextSegment(ref.current, target.getBoundingClientRect().left, 1);

if (next) {
Expand Down
28 changes: 14 additions & 14 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, nodeContains} from '@react-aria/utils';
import {getEventTarget, isVirtualClick, isVirtualPointerEvent, nodeContains} from '@react-aria/utils';
import type {LocalizedStringFormatter} from '@internationalized/string';
import {RefObject, useEffect, useState} from 'react';

Expand Down Expand Up @@ -243,7 +243,7 @@ class DragSession {
this.cancelEvent(e);

if (e.key === 'Enter') {
if (e.altKey || nodeContains(this.getCurrentActivateButton(), e.target as Node)) {
if (e.altKey || nodeContains(this.getCurrentActivateButton(), getEventTarget(e) as Node)) {
this.activate(this.currentDropTarget, this.currentDropItem);
} else {
this.drop();
Expand All @@ -257,25 +257,25 @@ class DragSession {

onFocus(e: FocusEvent): void {
let activateButton = this.getCurrentActivateButton();
if (e.target === activateButton) {
if (getEventTarget(e) === activateButton) {
// TODO: canceling this breaks the focus ring. Revisit when we support tabbing.
this.cancelEvent(e);
return;
}

// Prevent focus events, except to the original drag target.
if (e.target !== this.dragTarget.element) {
if (getEventTarget(e) !== this.dragTarget.element) {
this.cancelEvent(e);
}

// Ignore focus events on the window/document (JSDOM). Will be handled in onBlur, below.
if (!(e.target instanceof HTMLElement) || e.target === this.dragTarget.element) {
if (!(getEventTarget(e) instanceof HTMLElement) || getEventTarget(e) === this.dragTarget.element) {
return;
}

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

if (!dropTarget) {
// if (e.target === activateButton) {
Expand All @@ -289,7 +289,7 @@ class DragSession {
return;
}

let item = dropItems.get(e.target as HTMLElement);
let item = dropItems.get(getEventTarget(e) as HTMLElement);
if (dropTarget) {
this.setCurrentDropTarget(dropTarget, item);
}
Expand All @@ -302,7 +302,7 @@ class DragSession {
return;
}

if (e.target !== this.dragTarget.element) {
if (getEventTarget(e) !== this.dragTarget.element) {
this.cancelEvent(e);
}

Expand All @@ -321,15 +321,15 @@ 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 || nodeContains(item.activateButtonRef?.current, e.target as HTMLElement));
let dropTarget = this.validDropTargets.find(target => nodeContains(target.element, e.target as HTMLElement));
let item = [...dropElements].find(item => item.element === getEventTarget(e) as HTMLElement || nodeContains(item.activateButtonRef?.current, getEventTarget(e) as HTMLElement));
let dropTarget = this.validDropTargets.find(target => nodeContains(target.element, getEventTarget(e) as HTMLElement));
let activateButton = item?.activateButtonRef?.current ?? dropTarget?.activateButtonRef?.current;
if (nodeContains(activateButton, e.target as HTMLElement) && dropTarget) {
if (nodeContains(activateButton, getEventTarget(e) as HTMLElement) && dropTarget) {
this.activate(dropTarget, item);
return;
}

if (e.target === this.dragTarget.element) {
if (getEventTarget(e) === this.dragTarget.element) {
this.cancel();
return;
}
Expand All @@ -350,7 +350,7 @@ class DragSession {

cancelEvent(e: Event): void {
// Allow focusin and focusout on the drag target so focus ring works properly.
if ((e.type === 'focusin' || e.type === 'focusout') && (e.target === this.dragTarget?.element || e.target === this.getCurrentActivateButton())) {
if ((e.type === 'focusin' || e.type === 'focusout') && (getEventTarget(e) === this.dragTarget?.element || getEventTarget(e) === this.getCurrentActivateButton())) {
return;
}

Expand Down
20 changes: 10 additions & 10 deletions packages/@react-aria/dnd/src/useDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import {DragEndEvent, DragItem, DragMoveEvent, DragPreviewRenderer, DragStartEve
import {DragEvent, HTMLAttributes, version as ReactVersion, useEffect, useRef, useState} from 'react';
import * as DragManager from './DragManager';
import {DROP_EFFECT_TO_DROP_OPERATION, DROP_OPERATION, EFFECT_ALLOWED} from './constants';
import {getEventTarget, isVirtualClick, isVirtualPointerEvent, useDescription, useGlobalListeners} from '@react-aria/utils';
import {globalDropEffect, setGlobalAllowedDropOperations, setGlobalDropEffect, useDragModality, writeToDataTransfer} from './utils';
// @ts-ignore
import intlMessages from '../intl/*.json';
import {isVirtualClick, isVirtualPointerEvent, useDescription, useGlobalListeners} from '@react-aria/utils';
import {useLocalizedStringFormatter} from '@react-aria/i18n';

export interface DragOptions {
Expand Down Expand Up @@ -102,7 +102,7 @@ export function useDrag(options: DragOptions): DragResult {
// If this drag was initiated by a mobile screen reader (e.g. VoiceOver or TalkBack), enter virtual dragging mode.
if (modalityOnPointerDown.current === 'virtual') {
e.preventDefault();
startDragging(e.target as HTMLElement);
startDragging(getEventTarget(e) as HTMLElement);
modalityOnPointerDown.current = null;
return;
}
Expand Down Expand Up @@ -188,7 +188,7 @@ export function useDrag(options: DragOptions): DragResult {

// Wait a frame before we set dragging to true so that the browser has time to
// render the preview image before we update the element that has been dragged.
let target = e.target;
let target = getEventTarget(e);
requestAnimationFrame(() => {
setDragging(target as Element);
});
Expand Down Expand Up @@ -266,12 +266,12 @@ export function useDrag(options: DragOptions): DragResult {
};
}, [state]);

let onPress = (e: PressEvent) => {
if (e.pointerType !== 'keyboard' && e.pointerType !== 'virtual') {
let onPress = (pressEvent: PressEvent) => {
if (pressEvent.pointerType !== 'keyboard' && pressEvent.pointerType !== 'virtual') {
return;
}

startDragging(e.target as HTMLElement);
startDragging(pressEvent.target as HTMLElement);
};

let startDragging = (target: HTMLElement) => {
Expand Down Expand Up @@ -340,24 +340,24 @@ export function useDrag(options: DragOptions): DragResult {
}
},
onKeyDownCapture(e) {
if (e.target === e.currentTarget && e.key === 'Enter') {
if (getEventTarget(e) === e.currentTarget && e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
}
},
onKeyUpCapture(e) {
if (e.target === e.currentTarget && e.key === 'Enter') {
if (getEventTarget(e) === e.currentTarget && e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
startDragging(e.target as HTMLElement);
startDragging(getEventTarget(e) as HTMLElement);
}
},
onClick(e) {
// Handle NVDA/JAWS in browse mode, and touch screen readers. In this case, no keyboard events are fired.
if (isVirtualClick(e.nativeEvent) || modalityOnPointerDown.current === 'virtual') {
e.preventDefault();
e.stopPropagation();
startDragging(e.target as HTMLElement);
startDragging(getEventTarget(e) as HTMLElement);
}
}
};
Expand Down
Loading