From f0d9de866ced3df73649fbcf597a520e4f340b30 Mon Sep 17 00:00:00 2001 From: Arif Hossen Date: Thu, 29 Oct 2026 18:24:01 +0600 Subject: [PATCH 1/2] fix: keep a click on a button that moved under a still pointer --- resources/js/components/ui/tooltip.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/resources/js/components/ui/tooltip.tsx b/resources/js/components/ui/tooltip.tsx index 151590950..58cb60176 100644 --- a/resources/js/components/ui/tooltip.tsx +++ b/resources/js/components/ui/tooltip.tsx @@ -48,7 +48,7 @@ function Tooltip({ isArmed, arm: (byFocus) => { takesFocus.current = byFocus - // At once: React waits to render after a pointer enters, and a click landing meanwhile would press the trigger + // At once: React waits to render after a pointer moves, and a click landing meanwhile would press the trigger // before it is mounted again and be released on the new one, which loses it. flushSync(() => setHasArmed(true)) }, @@ -97,7 +97,7 @@ function focusableIn(element: HTMLElement): HTMLElement | null { function TooltipTrigger({ ref, asChild, - onPointerEnter, + onPointerMove, onFocus, ...props }: React.ComponentProps) { @@ -111,10 +111,12 @@ function TooltipTrigger({ ref={ref} {...(!asChild && { type: "button" as const })} {...props} - onPointerEnter={(event: React.PointerEvent) => { - onPointerEnter?.(event) - // A tap would land on a trigger mounted again under the finger, and touch shows no tooltip anyway. - if (event.pointerType !== "touch") { + onPointerMove={(event: React.PointerEvent) => { + onPointerMove?.(event) + // On a move with nothing pressed, not on entering: a trigger that scrolled or shifted under a still pointer + // is entered by the press itself, and mounting it again during a press would drop it. A tap would land on a + // trigger mounted again under the finger, and touch shows no tooltip anyway. + if (event.pointerType !== "touch" && event.buttons === 0) { arming.arm(false) } }} @@ -140,7 +142,7 @@ function TooltipTrigger({ } }} asChild={asChild} - onPointerEnter={onPointerEnter} + onPointerMove={onPointerMove} onFocus={onFocus} {...props} /> From 1bec39afc8f0ff11b2bd8a7528ef9b539f8fb9ab Mon Sep 17 00:00:00 2001 From: Arif Hossen Date: Thu, 29 Oct 2026 19:07:12 +0600 Subject: [PATCH 2/2] fix: keep focus on a clicked button when the pointer then arms its tooltip A click on a button that moved under a still pointer focuses it while its tooltip stays unarmed. The next move mounted the trigger again and dropped that focus to the document. Arming by pointer now hands the focus back to the trigger mounted again, as keyboard arming already does. Co-Authored-By: Claude Opus 5.5 --- resources/js/components/ui/tooltip.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/js/components/ui/tooltip.tsx b/resources/js/components/ui/tooltip.tsx index 58cb60176..64d7f055f 100644 --- a/resources/js/components/ui/tooltip.tsx +++ b/resources/js/components/ui/tooltip.tsx @@ -115,9 +115,10 @@ function TooltipTrigger({ onPointerMove?.(event) // On a move with nothing pressed, not on entering: a trigger that scrolled or shifted under a still pointer // is entered by the press itself, and mounting it again during a press would drop it. A tap would land on a - // trigger mounted again under the finger, and touch shows no tooltip anyway. + // trigger mounted again under the finger, and touch shows no tooltip anyway. A click that landed before any + // move left its button focused, which the trigger mounted again takes back. if (event.pointerType !== "touch" && event.buttons === 0) { - arming.arm(false) + arming.arm(event.currentTarget.contains(document.activeElement)) } }} onFocus={(event: React.FocusEvent) => {