diff --git a/packages/core/primitive/src/index.ts b/packages/core/primitive/src/index.ts index edd754dec..598a26fd3 100644 --- a/packages/core/primitive/src/index.ts +++ b/packages/core/primitive/src/index.ts @@ -1 +1 @@ -export { composeEventHandlers } from './primitive'; +export { composeEventHandlers, activeElement } from './primitive'; diff --git a/packages/core/primitive/src/primitive.tsx b/packages/core/primitive/src/primitive.tsx index d645e9c09..fab00f8fa 100644 --- a/packages/core/primitive/src/primitive.tsx +++ b/packages/core/primitive/src/primitive.tsx @@ -6,10 +6,14 @@ function composeEventHandlers( return function handleEvent(event: E) { originalEventHandler?.(event); - if (checkForDefaultPrevented === false || !((event as unknown) as Event).defaultPrevented) { + if (checkForDefaultPrevented === false || !(event as unknown as Event).defaultPrevented) { return ourEventHandler?.(event); } }; } -export { composeEventHandlers }; +function activeElement() { + return document.activeElement?.shadowRoot?.activeElement ?? document.activeElement; +} + +export { composeEventHandlers, activeElement }; diff --git a/packages/react/accordion/src/Accordion.test.tsx b/packages/react/accordion/src/Accordion.test.tsx index 00ef42ecb..216c84e3a 100644 --- a/packages/react/accordion/src/Accordion.test.tsx +++ b/packages/react/accordion/src/Accordion.test.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { axe } from 'jest-axe'; import { render, fireEvent, RenderResult } from '@testing-library/react'; import * as Accordion from '@radix-ui/react-accordion'; +import { activeElement } from '@radix-ui/primitive'; const ITEMS = ['One', 'Two', 'Three']; @@ -27,14 +28,14 @@ describe('given a single Accordion', () => { describe('on `ArrowDown`', () => { it('should move focus to the next trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowDown' }); expect(rendered.getByText('Trigger Two')).toHaveFocus(); }); it('should move focus to the first item if at the end', () => { const trigger = rendered.getByText('Trigger Three'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowDown' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); @@ -43,28 +44,28 @@ describe('given a single Accordion', () => { it('should move focus to the previous trigger', () => { const trigger = rendered.getByText('Trigger Three'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowUp' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowUp' }); expect(rendered.getByText('Trigger Two')).toHaveFocus(); }); it('should move focus to the last item if at the beginning', () => { const trigger = rendered.getByText('Trigger One'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowUp' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowUp' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); describe('on `Home`', () => { it('should move focus to the first trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'Home' }); + fireEvent.keyDown(activeElement()!, { key: 'Home' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `End`', () => { it('should move focus to the last trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'End' }); + fireEvent.keyDown(activeElement()!, { key: 'End' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); @@ -145,28 +146,28 @@ describe('given a single Accordion', () => { describe('on `ArrowUp`', () => { it('should do nothing', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowUp' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowUp' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `ArrowDown`', () => { it('should do nothing', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowDown' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `ArrowRight`', () => { it('should move focus to the next trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowRight' }); expect(rendered.getByText('Trigger Two')).toHaveFocus(); }); it('should move focus to the first item if at the end', () => { const trigger = rendered.getByText('Trigger Three'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowRight' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); @@ -175,28 +176,28 @@ describe('given a single Accordion', () => { it('should move focus to the previous trigger', () => { const trigger = rendered.getByText('Trigger Three'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowLeft' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowLeft' }); expect(rendered.getByText('Trigger Two')).toHaveFocus(); }); it('should move focus to the last item if at the beginning', () => { const trigger = rendered.getByText('Trigger One'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowLeft' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowLeft' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); describe('on `Home`', () => { it('should move focus to the first trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'Home' }); + fireEvent.keyDown(activeElement()!, { key: 'Home' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `End`', () => { it('should move focus to the last trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'End' }); + fireEvent.keyDown(activeElement()!, { key: 'End' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); @@ -224,14 +225,14 @@ describe('given a single Accordion', () => { describe('on `ArrowUp`', () => { it('should do nothing', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowUp' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowUp' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `ArrowDown`', () => { it('should do nothing', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowDown' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); @@ -240,40 +241,40 @@ describe('given a single Accordion', () => { it('should move focus to the previous trigger', () => { const trigger = rendered.getByText('Trigger Two'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowRight' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); it('should move focus to the last item if at the beginning', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowRight' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); describe('on `ArrowLeft`', () => { it('should move focus to the next trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowLeft' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowLeft' }); expect(rendered.getByText('Trigger Two')).toHaveFocus(); }); it('should move focus to the first item if at the end', () => { const trigger = rendered.getByText('Trigger Three'); trigger.focus(); - fireEvent.keyDown(document.activeElement!, { key: 'ArrowLeft' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowLeft' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `Home`', () => { it('should move focus to the first trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'Home' }); + fireEvent.keyDown(activeElement()!, { key: 'Home' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `End`', () => { it('should move focus to the last trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'End' }); + fireEvent.keyDown(activeElement()!, { key: 'End' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); @@ -302,28 +303,28 @@ describe('given a multiple Accordion', () => { describe('on `ArrowDown`', () => { it('should move focus to the next trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowDown' }); expect(rendered.getByText('Trigger Two')).toHaveFocus(); }); }); describe('on `ArrowUp`', () => { it('should move focus to the previous trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'ArrowUp' }); + fireEvent.keyDown(activeElement()!, { key: 'ArrowUp' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); describe('on `Home`', () => { it('should move focus to the first trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'Home' }); + fireEvent.keyDown(activeElement()!, { key: 'Home' }); expect(rendered.getByText('Trigger One')).toHaveFocus(); }); }); describe('on `End`', () => { it('should move focus to the last trigger', () => { - fireEvent.keyDown(document.activeElement!, { key: 'End' }); + fireEvent.keyDown(activeElement()!, { key: 'End' }); expect(rendered.getByText('Trigger Three')).toHaveFocus(); }); }); diff --git a/packages/react/dialog/src/Dialog.test.tsx b/packages/react/dialog/src/Dialog.test.tsx index 1b37dab28..117258801 100644 --- a/packages/react/dialog/src/Dialog.test.tsx +++ b/packages/react/dialog/src/Dialog.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { axe } from 'jest-axe'; import { render, fireEvent, RenderResult, cleanup } from '@testing-library/react'; import * as Dialog from '@radix-ui/react-dialog'; +import { activeElement } from '@radix-ui/primitive'; const OPEN_TEXT = 'Open'; const CLOSE_TEXT = 'Close'; @@ -126,7 +127,7 @@ describe('given a default Dialog', () => { describe('when pressing escape', () => { beforeEach(() => { - fireEvent.keyDown(document.activeElement!, { key: 'Escape' }); + fireEvent.keyDown(activeElement()!, { key: 'Escape' }); }); it('should close the content', () => { diff --git a/packages/react/dropdown-menu/src/DropdownMenu.stories.tsx b/packages/react/dropdown-menu/src/DropdownMenu.stories.tsx index 3071bbe83..9413717db 100644 --- a/packages/react/dropdown-menu/src/DropdownMenu.stories.tsx +++ b/packages/react/dropdown-menu/src/DropdownMenu.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; -import { css } from '../../../../stitches.config'; +import { css, getCssText } from '../../../../stitches.config'; import * as Tooltip from '@radix-ui/react-tooltip'; import * as Dialog from '@radix-ui/react-dialog'; import { SIDE_OPTIONS, ALIGN_OPTIONS } from '@radix-ui/react-popper'; @@ -867,6 +867,160 @@ export const InPopupWindow = () => { ); }; +export const InShadowRoot = () => { + const ref = React.useRef(null); + React.useEffect(() => { + if (!ref.current?.shadowRoot) { + ref.current!.attachShadow({ mode: 'open' }); + } + + const shadowRoot = ref.current!.shadowRoot!; + + const styleElement = document.createElement('style'); + styleElement.textContent = getCssText(); + + const container = document.createElement('div'); + shadowRoot.appendChild(styleElement); + shadowRoot.appendChild(container); + const appRoot = ReactDOM.createRoot(container); + appRoot.render( + + Open + + console.log('new-tab')}> + New Tab + + console.log('new-window')}> + New Window + + + + + Bookmarks → + + + console.log('index')}> + Inbox + + console.log('calendar')}> + Calendar + + + + + WorkOS → + + + + console.log('stitches')} + > + Stitches + + console.log('composer')} + > + Composer + + console.log('radix')} + > + Radix + + + + + + + console.log('notion')}> + Notion + + + + + + + History → + + + + console.log('github')}> + Github + + console.log('google')}> + Google + + console.log('stack-overflow')} + > + Stack Overflow + + + + + + + Tools → + + + console.log('extensions')} + > + Extensions + + console.log('task-manager')} + > + Task Manager + + console.log('developer-tools')} + > + Developer Tools + + + + + + + console.log('print')}> + Print… + + console.log('cast')}> + Cast… + + console.log('find')}> + Find… + + + + + ); + + return () => { + appRoot.unmount(); + }; + }, []); + return ( +
+
+
+ ); +}; + // change order slightly for more pleasing visual const SIDES = SIDE_OPTIONS.filter((side) => side !== 'bottom').concat(['bottom']); diff --git a/packages/react/focus-scope/src/FocusScope.tsx b/packages/react/focus-scope/src/FocusScope.tsx index bda929941..13461f84d 100644 --- a/packages/react/focus-scope/src/FocusScope.tsx +++ b/packages/react/focus-scope/src/FocusScope.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { useComposedRefs } from '@radix-ui/react-compose-refs'; import { Primitive } from '@radix-ui/react-primitive'; import { useCallbackRef } from '@radix-ui/react-use-callback-ref'; +import { activeElement } from '@radix-ui/primitive'; const AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount'; const AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount'; @@ -109,7 +110,7 @@ const FocusScope = React.forwardRef((props, // back to the document.body. In this case, we move focus to the container // to keep focus trapped correctly. function handleMutations(mutations: MutationRecord[]) { - const focusedElement = document.activeElement as HTMLElement | null; + const focusedElement = activeElement() as HTMLElement | null; if (focusedElement !== document.body) return; for (const mutation of mutations) { if (mutation.removedNodes.length > 0) focus(container); @@ -132,7 +133,7 @@ const FocusScope = React.forwardRef((props, React.useEffect(() => { if (container) { focusScopesStack.add(focusScope); - const previouslyFocusedElement = document.activeElement as HTMLElement | null; + const previouslyFocusedElement = activeElement() as HTMLElement | null; const hasFocusedCandidate = container.contains(previouslyFocusedElement); if (!hasFocusedCandidate) { @@ -141,7 +142,7 @@ const FocusScope = React.forwardRef((props, container.dispatchEvent(mountEvent); if (!mountEvent.defaultPrevented) { focusFirst(removeLinks(getTabbableCandidates(container)), { select: true }); - if (document.activeElement === previouslyFocusedElement) { + if (activeElement() === previouslyFocusedElement) { focus(container); } } @@ -176,7 +177,7 @@ const FocusScope = React.forwardRef((props, if (focusScope.paused) return; const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey; - const focusedElement = document.activeElement as HTMLElement | null; + const focusedElement = activeElement() as HTMLElement | null; if (isTabKey && focusedElement) { const container = event.currentTarget as HTMLElement; @@ -216,10 +217,10 @@ FocusScope.displayName = FOCUS_SCOPE_NAME; * Stops when focus has actually moved. */ function focusFirst(candidates: HTMLElement[], { select = false } = {}) { - const previouslyFocusedElement = document.activeElement; + const previouslyFocusedElement = activeElement(); for (const candidate of candidates) { focus(candidate, { select }); - if (document.activeElement !== previouslyFocusedElement) return; + if (activeElement() !== previouslyFocusedElement) return; } } @@ -290,7 +291,7 @@ function isSelectableInput(element: any): element is FocusableTarget & { select: function focus(element?: FocusableTarget | null, { select = false } = {}) { // only focus if that element is focusable if (element && element.focus) { - const previouslyFocusedElement = document.activeElement; + const previouslyFocusedElement = activeElement(); // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users element.focus({ preventScroll: true }); // only select if its not the same element, it supports selection and we need to select diff --git a/packages/react/menu/src/Menu.tsx b/packages/react/menu/src/Menu.tsx index 1b3b34f1d..8764585b5 100644 --- a/packages/react/menu/src/Menu.tsx +++ b/packages/react/menu/src/Menu.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { composeEventHandlers } from '@radix-ui/primitive'; +import { activeElement, composeEventHandlers } from '@radix-ui/primitive'; import { createCollection } from '@radix-ui/react-collection'; import { useComposedRefs, composeRefs } from '@radix-ui/react-compose-refs'; import { createContextScope } from '@radix-ui/react-context'; @@ -395,7 +395,7 @@ const MenuContentImpl = React.forwardRef { const search = searchRef.current + key; const items = getItems().filter((item) => !item.disabled); - const currentItem = document.activeElement; + const currentItem = activeElement(); const currentMatch = items.find((item) => item.ref.current === currentItem)?.textValue; const values = items.map((item) => item.textValue); const nextMatch = getNextMatch(values, search, currentMatch); @@ -1236,12 +1236,12 @@ function getCheckedState(checked: CheckedState) { } function focusFirst(candidates: HTMLElement[]) { - const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement; + const PREVIOUSLY_FOCUSED_ELEMENT = activeElement(); for (const candidate of candidates) { // if focus is already where we want to go, we don't want to keep going through the candidates if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return; candidate.focus(); - if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return; + if (activeElement() !== PREVIOUSLY_FOCUSED_ELEMENT) return; } } diff --git a/packages/react/navigation-menu/src/NavigationMenu.tsx b/packages/react/navigation-menu/src/NavigationMenu.tsx index d322b4474..ac3e94956 100644 --- a/packages/react/navigation-menu/src/NavigationMenu.tsx +++ b/packages/react/navigation-menu/src/NavigationMenu.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { createContextScope } from '@radix-ui/react-context'; -import { composeEventHandlers } from '@radix-ui/primitive'; +import { activeElement, composeEventHandlers } from '@radix-ui/primitive'; import { Primitive, dispatchDiscreteCustomEvent } from '@radix-ui/react-primitive'; import { useControllableState } from '@radix-ui/react-use-controllable-state'; import { composeRefs, useComposedRefs } from '@radix-ui/react-compose-refs'; @@ -875,7 +875,7 @@ const NavigationMenuContentImpl = React.forwardRef< const handleClose = () => { onItemDismiss(); onRootContentClose(); - if (content.contains(document.activeElement)) triggerRef.current?.focus(); + if (content.contains(activeElement())) triggerRef.current?.focus(); }; content.addEventListener(ROOT_CONTENT_DISMISS, handleClose); return () => content.removeEventListener(ROOT_CONTENT_DISMISS, handleClose); @@ -946,7 +946,7 @@ const NavigationMenuContentImpl = React.forwardRef< const isTabKey = event.key === 'Tab' && !isMetaKey; if (isTabKey) { const candidates = getTabbableCandidates(event.currentTarget); - const focusedElement = document.activeElement; + const focusedElement = activeElement(); const index = candidates.findIndex((candidate) => candidate === focusedElement); const isMovingBackwards = event.shiftKey; const nextCandidates = isMovingBackwards @@ -1175,12 +1175,12 @@ function getTabbableCandidates(container: HTMLElement) { } function focusFirst(candidates: HTMLElement[]) { - const previouslyFocusedElement = document.activeElement; + const previouslyFocusedElement = activeElement(); return candidates.some((candidate) => { // if focus is already where we want to go, we don't want to keep going through the candidates if (candidate === previouslyFocusedElement) return true; candidate.focus(); - return document.activeElement !== previouslyFocusedElement; + return activeElement() !== previouslyFocusedElement; }); } diff --git a/packages/react/roving-focus/src/RovingFocusGroup.tsx b/packages/react/roving-focus/src/RovingFocusGroup.tsx index 84f91ca15..d9a2385b5 100644 --- a/packages/react/roving-focus/src/RovingFocusGroup.tsx +++ b/packages/react/roving-focus/src/RovingFocusGroup.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { composeEventHandlers } from '@radix-ui/primitive'; +import { activeElement, composeEventHandlers } from '@radix-ui/primitive'; import { createCollection } from '@radix-ui/react-collection'; import { useComposedRefs } from '@radix-ui/react-compose-refs'; import { createContextScope } from '@radix-ui/react-context'; @@ -316,12 +316,12 @@ function getFocusIntent(event: React.KeyboardEvent, orientation?: Orientation, d } function focusFirst(candidates: HTMLElement[], preventScroll = false) { - const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement; + const PREVIOUSLY_FOCUSED_ELEMENT = activeElement(); for (const candidate of candidates) { // if focus is already where we want to go, we don't want to keep going through the candidates if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return; candidate.focus({ preventScroll }); - if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return; + if (activeElement() !== PREVIOUSLY_FOCUSED_ELEMENT) return; } } diff --git a/packages/react/select/src/Select.tsx b/packages/react/select/src/Select.tsx index 3cfcce0fc..9d5e91280 100644 --- a/packages/react/select/src/Select.tsx +++ b/packages/react/select/src/Select.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { clamp } from '@radix-ui/number'; -import { composeEventHandlers } from '@radix-ui/primitive'; +import { activeElement, composeEventHandlers } from '@radix-ui/primitive'; import { createCollection } from '@radix-ui/react-collection'; import { useComposedRefs } from '@radix-ui/react-compose-refs'; import { createContextScope } from '@radix-ui/react-context'; @@ -530,7 +530,7 @@ const SelectContentImpl = React.forwardRef item.ref.current); const [lastItem] = restItems.slice(-1); - const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement; + const PREVIOUSLY_FOCUSED_ELEMENT = activeElement(); for (const candidate of candidates) { // if focus is already where we want to go, we don't want to keep going through the candidates if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return; @@ -539,7 +539,7 @@ const SelectContentImpl = React.forwardRef { const enabledItems = getItems().filter((item) => !item.disabled); - const currentItem = enabledItems.find((item) => item.ref.current === document.activeElement); + const currentItem = enabledItems.find((item) => item.ref.current === activeElement()); const nextItem = findNextItem(enabledItems, search, currentItem); if (nextItem) { /** @@ -1252,7 +1252,7 @@ const SelectItem = React.forwardRef( } })} onPointerLeave={composeEventHandlers(itemProps.onPointerLeave, (event) => { - if (event.currentTarget === document.activeElement) { + if (event.currentTarget === activeElement()) { contentContext.onItemLeave?.(); } })} @@ -1476,7 +1476,7 @@ const SelectScrollButtonImpl = React.forwardRef< // the viewport, potentially causing the active item to now be partially out of view. // We re-run the `scrollIntoView` logic to make sure it stays within the viewport. useLayoutEffect(() => { - const activeItem = getItems().find((item) => item.ref.current === document.activeElement); + const activeItem = getItems().find((item) => item.ref.current === activeElement()); activeItem?.ref.current?.scrollIntoView({ block: 'nearest' }); }, [getItems]); diff --git a/packages/react/toast/src/Toast.tsx b/packages/react/toast/src/Toast.tsx index af7f36aa2..2201125d7 100644 --- a/packages/react/toast/src/Toast.tsx +++ b/packages/react/toast/src/Toast.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { composeEventHandlers } from '@radix-ui/primitive'; +import { activeElement, composeEventHandlers } from '@radix-ui/primitive'; import { useComposedRefs } from '@radix-ui/react-compose-refs'; import { createCollection } from '@radix-ui/react-collection'; import { createContextScope } from '@radix-ui/react-context'; @@ -192,7 +192,7 @@ const ToastViewport = React.forwardRef }; const handlePointerLeaveResume = () => { - const isFocusInside = wrapper.contains(document.activeElement); + const isFocusInside = wrapper.contains(activeElement()); if (!isFocusInside) handleResume(); }; @@ -242,7 +242,7 @@ const ToastViewport = React.forwardRef const isTabKey = event.key === 'Tab' && !isMetaKey; if (isTabKey) { - const focusedElement = document.activeElement; + const focusedElement = activeElement(); const isTabbingBackwards = event.shiftKey; const targetIsViewport = event.target === viewport; @@ -490,7 +490,7 @@ const ToastImpl = React.forwardRef( const handleClose = useCallbackRef(() => { // focus viewport if focus is within toast to read the remaining toast // count to SR users and ensure focus isn't lost - const isFocusInToast = node?.contains(document.activeElement); + const isFocusInToast = node?.contains(activeElement()); if (isFocusInToast) context.viewport?.focus(); onClose(); }); @@ -939,12 +939,12 @@ function getTabbableCandidates(container: HTMLElement) { } function focusFirst(candidates: HTMLElement[]) { - const previouslyFocusedElement = document.activeElement; + const previouslyFocusedElement = activeElement(); return candidates.some((candidate) => { // if focus is already where we want to go, we don't want to keep going through the candidates if (candidate === previouslyFocusedElement) return true; candidate.focus(); - return document.activeElement !== previouslyFocusedElement; + return activeElement() !== previouslyFocusedElement; }); } diff --git a/stitches.config.ts b/stitches.config.ts index 6a8bc1ebf..313316c21 100644 --- a/stitches.config.ts +++ b/stitches.config.ts @@ -1,6 +1,6 @@ import { createStitches } from '@stitches/core'; -export const { css, keyframes } = createStitches({ +export const { css, keyframes, getCssText } = createStitches({ theme: { colors: { white: '#fff',