Skip to content

Commit

Permalink
Merge branch 'master' into fix/menu-open-on-hover
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks authored Jan 8, 2025
2 parents 95e7c52 + 7c95b65 commit 8e06b2d
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 80 deletions.
4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@
},
"dependencies": {
"@babel/runtime": "^7.26.0",
"@floating-ui/react": "^0.27.2",
"@floating-ui/utils": "^0.2.8",
"@floating-ui/react": "^0.27.3",
"@floating-ui/utils": "^0.2.9",
"@react-aria/overlays": "^3.24.0",
"prop-types": "^15.8.1",
"use-sync-external-store": "^1.4.0"
Expand Down
6 changes: 2 additions & 4 deletions packages/react/src/alert-dialog/popup/AlertDialogPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,14 @@ const AlertDialogPopup = React.forwardRef(function AlertDialogPopup(

const mergedRef = useForkRef(forwardedRef, popupRef);

const { getRootProps, floatingContext, resolvedInitialFocus } = useDialogPopup({
const { getRootProps, resolvedInitialFocus } = useDialogPopup({
descriptionElementId,
floatingRootContext,
getPopupProps,
id,
initialFocus,
modal: true,
mounted,
setOpen,
open,
openMethod,
ref: mergedRef,
setPopupElement,
Expand Down Expand Up @@ -105,7 +103,7 @@ const AlertDialogPopup = React.forwardRef(function AlertDialogPopup(
<React.Fragment>
{mounted && modal && <InternalBackdrop inert={!open} />}
<FloatingFocusManager
context={floatingContext}
context={floatingRootContext}
modal={open}
disabled={!mounted}
initialFocus={resolvedInitialFocus}
Expand Down
6 changes: 2 additions & 4 deletions packages/react/src/dialog/popup/DialogPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,14 @@ const DialogPopup = React.forwardRef(function DialogPopup(

const mergedRef = useForkRef(forwardedRef, popupRef);

const { getRootProps, floatingContext, resolvedInitialFocus } = useDialogPopup({
const { getRootProps, resolvedInitialFocus } = useDialogPopup({
descriptionElementId,
floatingRootContext,
getPopupProps,
id,
initialFocus,
modal,
mounted,
setOpen,
open,
openMethod,
ref: mergedRef,
setPopupElement,
Expand Down Expand Up @@ -101,7 +99,7 @@ const DialogPopup = React.forwardRef(function DialogPopup(
<React.Fragment>
{mounted && modal && <InternalBackdrop inert={!open} />}
<FloatingFocusManager
context={floatingContext}
context={floatingRootContext}
modal={open}
disabled={!mounted}
closeOnFocusOut={dismissible}
Expand Down
44 changes: 1 addition & 43 deletions packages/react/src/dialog/popup/useDialogPopup.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,33 @@
'use client';
import * as React from 'react';
import {
type FloatingRootContext,
useFloating,
type FloatingContext,
type OpenChangeReason as FloatingUIOpenChangeReason,
} from '@floating-ui/react';
import { useBaseUiId } from '../../utils/useBaseUiId';
import { useForkRef } from '../../utils/useForkRef';
import { mergeReactProps } from '../../utils/mergeReactProps';
import { useScrollLock } from '../../utils/useScrollLock';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect';
import { type InteractionType } from '../../utils/useEnhancedClickHandler';
import { GenericHTMLProps } from '../../utils/types';
import {
translateOpenChangeReason,
type OpenChangeReason,
} from '../../utils/translateOpenChangeReason';
import { type OpenChangeReason } from '../../utils/translateOpenChangeReason';

export function useDialogPopup(parameters: useDialogPopup.Parameters): useDialogPopup.ReturnValue {
const {
descriptionElementId,
floatingRootContext,
getPopupProps,
id: idParam,
initialFocus,
modal,
mounted,
setOpen,
open,
openMethod,
ref,
setPopupElementId,
setPopupElement,
titleElementId,
} = parameters;

const handleFloatingUIOpenChange = (
isOpen: boolean,
event: Event | undefined,
reason: FloatingUIOpenChangeReason | undefined,
) => {
setOpen(isOpen, event, translateOpenChangeReason(reason));
};

const { context, elements } = useFloating({
open,
onOpenChange: handleFloatingUIOpenChange,
rootContext: floatingRootContext,
});

const popupRef = React.useRef<HTMLElement>(null);

const id = useBaseUiId(idParam);
const handleRef = useForkRef(ref, popupRef, setPopupElement);

useScrollLock(open && modal, elements.floating);

// Default initial focus logic:
// If opened by touch, focus the popup element to prevent the virtual keyboard from opening
// (this is required for Android specifically as iOS handles this automatically).
Expand Down Expand Up @@ -101,7 +72,6 @@ export function useDialogPopup(parameters: useDialogPopup.Parameters): useDialog
});

return {
floatingContext: context,
getRootProps,
resolvedInitialFocus,
};
Expand All @@ -121,10 +91,6 @@ export namespace useDialogPopup {
* Whether the dialog should prevent outside clicks and lock page scroll when open.
*/
modal: boolean;
/**
* Whether the dialog is currently open.
*/
open: boolean;
openMethod: InteractionType | null;
/**
* Event handler called when the dialog is opened or closed.
Expand Down Expand Up @@ -153,10 +119,6 @@ export namespace useDialogPopup {
initialFocus?:
| React.RefObject<HTMLElement | null>
| ((interactionType: InteractionType) => React.RefObject<HTMLElement | null>);
/**
* The Floating UI root context.
*/
floatingRootContext: FloatingRootContext;
/**
* Determines if the dialog should be mounted.
*/
Expand All @@ -172,10 +134,6 @@ export namespace useDialogPopup {
}

export interface ReturnValue {
/**
* Floating UI context for the dialog's FloatingFocusManager.
*/
floatingContext: FloatingContext;
/**
* Resolver for the root element props.
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/react/src/dialog/root/useDialogRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@floating-ui/react';
import { useControlled } from '../../utils/useControlled';
import { useEventCallback } from '../../utils/useEventCallback';
import { useScrollLock } from '../../utils/useScrollLock';
import { useTransitionStatus, type TransitionStatus } from '../../utils/useTransitionStatus';
import { type InteractionType } from '../../utils/useEnhancedClickHandler';
import type { RequiredExcept, GenericHTMLProps } from '../../utils/types';
Expand Down Expand Up @@ -68,6 +69,8 @@ export function useDialogRoot(params: useDialogRoot.Parameters): useDialogRoot.R
},
});

useScrollLock(open && modal, popupElement);

const handleFloatingUIOpenChange = (
nextOpen: boolean,
event: Event | undefined,
Expand Down
25 changes: 11 additions & 14 deletions packages/react/src/menu/trigger/useMenuTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,9 @@ export function useMenuTrigger(parameters: useMenuTrigger.Parameters): useMenuTr
const handleRef = useForkRef(buttonRef, setTriggerElement);

React.useEffect(() => {
if (open) {
// mousedown -> mouseup on menu item should not trigger it within 200ms.
allowMouseUpTriggerTimeoutRef.current = window.setTimeout(() => {
allowMouseUpTriggerRef.current = true;
}, 200);

return () => {
clearTimeout(allowMouseUpTriggerTimeoutRef.current);
};
if (!open) {
allowMouseUpTriggerRef.current = false;
}

allowMouseUpTriggerRef.current = false;

return undefined;
}, [allowMouseUpTriggerRef, open]);

const getTriggerProps = React.useCallback(
Expand All @@ -62,14 +51,22 @@ export function useMenuTrigger(parameters: useMenuTrigger.Parameters): useMenuTr
return;
}

// mousedown -> mouseup on menu item should not trigger it within 200ms.
allowMouseUpTriggerTimeoutRef.current = window.setTimeout(() => {
allowMouseUpTriggerRef.current = true;
}, 200);

const doc = ownerDocument(event.currentTarget);

function handleMouseUp(mouseEvent: MouseEvent) {
if (!triggerRef.current) {
return;
}

clearTimeout(allowMouseUpTriggerTimeoutRef.current);
if (allowMouseUpTriggerTimeoutRef.current !== -1) {
clearTimeout(allowMouseUpTriggerTimeoutRef.current);
allowMouseUpTriggerTimeoutRef.current = -1;
}
allowMouseUpTriggerRef.current = false;

const mouseUpTarget = mouseEvent.target as Element | null;
Expand Down
26 changes: 13 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8e06b2d

Please sign in to comment.