diff --git a/docs/examples/body-overflow.tsx b/docs/examples/body-overflow.tsx
index b6ab5182..744e86a3 100644
--- a/docs/examples/body-overflow.tsx
+++ b/docs/examples/body-overflow.tsx
@@ -80,10 +80,11 @@ export default () => {
htmlRegion: 'scroll',
}}
>
- {
display: 'inline-block',
}}
>
- Target
-
+ Button Target
+
{
- if (mergedOpen !== nextOpen) {
- setMergedOpen(nextOpen);
- onPopupVisibleChange?.(nextOpen);
- }
+ // Enter or Pointer will both trigger open state change
+ // We only need take one to avoid duplicated change event trigger
+ flushSync(() => {
+ if (mergedOpen !== nextOpen) {
+ setMergedOpen(nextOpen);
+ onPopupVisibleChange?.(nextOpen);
+ }
+ });
});
// Trigger for delay
@@ -354,7 +359,9 @@ export function generateTrigger(
0, 0,
]);
- const setMousePosByEvent = (event: React.MouseEvent) => {
+ const setMousePosByEvent = (
+ event: Pick,
+ ) => {
setMousePos([event.clientX, event.clientY]);
};
@@ -463,13 +470,15 @@ export function generateTrigger(
hideAction,
);
- // Util wrapper for trigger action
- const wrapperAction = (
+ /**
+ * Util wrapper for trigger action
+ */
+ function wrapperAction(
eventName: string,
nextOpen: boolean,
delay?: number,
- preEvent?: (event: any) => void,
- ) => {
+ preEvent?: (event: Event) => void,
+ ) {
cloneProps[eventName] = (event: any, ...args: any[]) => {
preEvent?.(event);
triggerOpen(nextOpen, delay);
@@ -477,7 +486,7 @@ export function generateTrigger(
// Pass to origin
originChildProps[eventName]?.(event, ...args);
};
- };
+ }
// ======================= Action: Click ========================
const clickToShow = showActions.has('click');
@@ -521,9 +530,23 @@ export function generateTrigger(
let onPopupMouseLeave: VoidFunction;
if (hoverToShow) {
- wrapperAction('onMouseEnter', true, mouseEnterDelay, (event) => {
- setMousePosByEvent(event);
- });
+ // Compatible with old browser which not support pointer event
+ wrapperAction(
+ 'onMouseEnter',
+ true,
+ mouseEnterDelay,
+ (event) => {
+ setMousePosByEvent(event);
+ },
+ );
+ wrapperAction(
+ 'onPointerEnter',
+ true,
+ mouseEnterDelay,
+ (event) => {
+ setMousePosByEvent(event);
+ },
+ );
onPopupMouseEnter = () => {
// Only trigger re-open when popup is visible
if (mergedOpen || inMotion) {
@@ -542,6 +565,7 @@ export function generateTrigger(
if (hoverToHide) {
wrapperAction('onMouseLeave', false, mouseLeaveDelay);
+ wrapperAction('onPointerLeave', false, mouseLeaveDelay);
onPopupMouseLeave = () => {
triggerOpen(false, mouseLeaveDelay);
};
diff --git a/tests/basic.test.jsx b/tests/basic.test.jsx
index 936a1407..cbee245c 100644
--- a/tests/basic.test.jsx
+++ b/tests/basic.test.jsx
@@ -131,22 +131,42 @@ describe('Trigger.Basic', () => {
expect(isPopupHidden()).toBeTruthy();
});
- it('hover works', () => {
- const { container } = render(
- trigger}
- >
- click
- ,
- );
+ describe('hover works', () => {
+ it('mouse event', () => {
+ const { container } = render(
+ trigger}
+ >
+ click
+ ,
+ );
- trigger(container, '.target', 'mouseEnter');
- expect(isPopupHidden()).toBeFalsy();
+ trigger(container, '.target', 'mouseEnter');
+ expect(isPopupHidden()).toBeFalsy();
- trigger(container, '.target', 'mouseLeave');
- expect(isPopupHidden()).toBeTruthy();
+ trigger(container, '.target', 'mouseLeave');
+ expect(isPopupHidden()).toBeTruthy();
+ });
+
+ it('pointer event', () => {
+ const { container } = render(
+ trigger}
+ >
+ click
+ ,
+ );
+
+ trigger(container, '.target', 'pointerEnter');
+ expect(isPopupHidden()).toBeFalsy();
+
+ trigger(container, '.target', 'pointerLeave');
+ expect(isPopupHidden()).toBeTruthy();
+ });
});
it('contextMenu works', () => {