11'use client' ;
22import * as React from 'react' ;
3- import * as ReactDOM from 'react-dom' ;
43import { useTimeout } from '@base-ui/utils/useTimeout' ;
54import { ownerDocument } from '@base-ui/utils/owner' ;
65import { fastComponentRef } from '@base-ui/utils/fastHooks' ;
@@ -18,14 +17,7 @@ import {
1817 useFloatingParentNodeId ,
1918} from '../../floating-ui-react' ;
2019import { FloatingTreeStore } from '../../floating-ui-react/components/FloatingTreeStore' ;
21- import {
22- contains ,
23- type FocusableElement ,
24- getNextTabbable ,
25- getTabbableAfterElement ,
26- getTabbableBeforeElement ,
27- isOutsideEvent ,
28- } from '../../floating-ui-react/utils' ;
20+ import { contains } from '../../floating-ui-react/utils' ;
2921import { useMenuRootContext } from '../root/MenuRootContext' ;
3022import { pressableTriggerOpenStateMapping } from '../../utils/popupStateMapping' ;
3123import { useRenderElement } from '../../utils/useRenderElement' ;
@@ -36,6 +28,7 @@ import { CompositeItem } from '../../composite/item/CompositeItem';
3628import { useCompositeRootContext } from '../../composite/root/CompositeRootContext' ;
3729import { findRootOwnerId } from '../utils/findRootOwnerId' ;
3830import { useTriggerDataForwarding } from '../../utils/popups' ;
31+ import { useTriggerFocusGuards } from '../../utils/popups/useTriggerFocusGuards' ;
3932import { useBaseUiId } from '../../utils/useBaseUiId' ;
4033import { REASONS } from '../../utils/reasons' ;
4134import { useMixedToggleClickHandler } from '../../utils/useMixedToggleClickHandler' ;
@@ -45,7 +38,6 @@ import { useMenubarContext } from '../../menubar/MenubarContext';
4538import { MenuParent } from '../root/MenuRoot' ;
4639import { PATIENT_CLICK_THRESHOLD } from '../../utils/constants' ;
4740import { FocusGuard } from '../../utils/FocusGuard' ;
48- import { createChangeEventDetails } from '../../utils/createBaseUIEventDetails' ;
4941
5042const BOUNDARY_OFFSET = 2 ;
5143
@@ -255,57 +247,8 @@ export const MenuTrigger = fastComponentRef(function MenuTrigger(
255247 getButtonProps ,
256248 ] ;
257249
258- const preFocusGuardRef = React . useRef < HTMLElement > ( null ) ;
259-
260- const handlePreFocusGuardFocus = useStableCallback ( ( event : React . FocusEvent ) => {
261- ReactDOM . flushSync ( ( ) => {
262- store . setOpen (
263- false ,
264- createChangeEventDetails (
265- REASONS . focusOut ,
266- event . nativeEvent ,
267- event . currentTarget as HTMLElement ,
268- ) ,
269- ) ;
270- } ) ;
271-
272- const previousTabbable : FocusableElement | null = getTabbableBeforeElement (
273- preFocusGuardRef . current ,
274- ) ;
275- previousTabbable ?. focus ( ) ;
276- } ) ;
277-
278- const handleFocusTargetFocus = useStableCallback ( ( event : React . FocusEvent ) => {
279- const currentPositionerElement = store . select ( 'positionerElement' ) ;
280- if ( currentPositionerElement && isOutsideEvent ( event , currentPositionerElement ) ) {
281- store . context . beforeContentFocusGuardRef . current ?. focus ( ) ;
282- } else {
283- ReactDOM . flushSync ( ( ) => {
284- store . setOpen (
285- false ,
286- createChangeEventDetails (
287- REASONS . focusOut ,
288- event . nativeEvent ,
289- event . currentTarget as HTMLElement ,
290- ) ,
291- ) ;
292- } ) ;
293-
294- let nextTabbable = getTabbableAfterElement (
295- store . context . triggerFocusTargetRef . current || triggerElementRef . current ,
296- ) ;
297-
298- while ( nextTabbable !== null && contains ( currentPositionerElement , nextTabbable ) ) {
299- const prevTabbable = nextTabbable ;
300- nextTabbable = getNextTabbable ( nextTabbable ) ;
301- if ( nextTabbable === prevTabbable ) {
302- break ;
303- }
304- }
305-
306- nextTabbable ?. focus ( ) ;
307- }
308- } ) ;
250+ const { preFocusGuardRef, handlePreFocusGuardFocus, handleFocusTargetFocus } =
251+ useTriggerFocusGuards ( store , triggerElementRef ) ;
309252
310253 const element = useRenderElement ( 'button' , componentProps , {
311254 enabled : ! isInMenubar ,
0 commit comments