Skip to content

Commit

Permalink
[Tooltip] Better "close on scroll" implementation (#1315)
Browse files Browse the repository at this point in the history
* [Tooltip] Better "close on scroll" implementation

Fixes #937

* Update Tooltip.tsx
  • Loading branch information
benoitgrelard authored Apr 11, 2022
1 parent 0b720cf commit 589ebdc
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 33 deletions.
5 changes: 5 additions & 0 deletions .yarn/versions/4c8bef63.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
releases:
"@radix-ui/react-tooltip": patch

declined:
- primitives
2 changes: 0 additions & 2 deletions packages/react/tooltip/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
"@radix-ui/react-primitive": "workspace:*",
"@radix-ui/react-slot": "workspace:*",
"@radix-ui/react-use-controllable-state": "workspace:*",
"@radix-ui/react-use-previous": "workspace:*",
"@radix-ui/react-use-rect": "workspace:*",
"@radix-ui/react-visually-hidden": "workspace:*"
},
"peerDependencies": {
Expand Down
37 changes: 37 additions & 0 deletions packages/react/tooltip/src/Tooltip.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,43 @@ export const KeepOpenOnActivation = () => {
);
};

export const WithinScrollable = () => (
<TooltipProvider>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
height: 500,
width: 300,
border: '1px solid black',
overflow: 'auto',
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: 600 }}>
<Tooltip>
<TooltipTrigger className={triggerClass}>Hover or Focus me</TooltipTrigger>
<TooltipContent className={contentClass} sideOffset={5}>
Nicely done!
<TooltipArrow className={arrowClass} offset={10} />
</TooltipContent>
</Tooltip>
</div>
</div>
<div
style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '150vh' }}
>
<Tooltip>
<TooltipTrigger className={triggerClass}>Hover or Focus me</TooltipTrigger>
<TooltipContent className={contentClass} sideOffset={5}>
Nicely done!
<TooltipArrow className={arrowClass} offset={10} />
</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
);

// change order slightly for more pleasing visual
const SIDES = SIDE_OPTIONS.filter((side) => side !== 'bottom').concat(['bottom']);

Expand Down
42 changes: 13 additions & 29 deletions packages/react/tooltip/src/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { Presence } from '@radix-ui/react-presence';
import { Primitive } from '@radix-ui/react-primitive';
import { Slottable } from '@radix-ui/react-slot';
import { useControllableState } from '@radix-ui/react-use-controllable-state';
import { usePrevious } from '@radix-ui/react-use-previous';
import { useRect } from '@radix-ui/react-use-rect';
import * as VisuallyHiddenPrimitive from '@radix-ui/react-visually-hidden';

import type * as Radix from '@radix-ui/react-primitive';
Expand Down Expand Up @@ -342,15 +340,26 @@ const TooltipContentImpl = React.forwardRef<TooltipContentImplElement, TooltipCo
const PortalWrapper = portalled ? Portal : React.Fragment;
const { onClose } = context;

// Close this tooltip if another one opens
React.useEffect(() => {
// Close this tooltip if another one opens
document.addEventListener(TOOLTIP_OPEN, onClose);
return () => document.removeEventListener(TOOLTIP_OPEN, onClose);
}, [onClose]);

// Close the tooltip if the trigger is scrolled
React.useEffect(() => {
if (context.trigger) {
const handleScroll = (event: Event) => {
const target = event.target as HTMLElement;
if (target?.contains(context.trigger)) onClose();
};
window.addEventListener('scroll', handleScroll, { capture: true });
return () => window.removeEventListener('scroll', handleScroll, { capture: true });
}
}, [context.trigger, onClose]);

return (
<PortalWrapper>
<CheckTriggerMoved __scopeTooltip={__scopeTooltip} />
<DismissableLayer
asChild
disableOutsidePointerEvents={false}
Expand Down Expand Up @@ -406,31 +415,6 @@ TooltipArrow.displayName = ARROW_NAME;

/* -----------------------------------------------------------------------------------------------*/

function CheckTriggerMoved(props: ScopedProps<{}>) {
const { __scopeTooltip } = props;
const context = useTooltipContext('CheckTriggerMoved', __scopeTooltip);

const triggerRect = useRect(context.trigger);
const triggerLeft = triggerRect?.left;
const previousTriggerLeft = usePrevious(triggerLeft);
const triggerTop = triggerRect?.top;
const previousTriggerTop = usePrevious(triggerTop);
const handleClose = context.onClose;

React.useEffect(() => {
// checking if the user has scrolled…
const hasTriggerMoved =
(previousTriggerLeft !== undefined && previousTriggerLeft !== triggerLeft) ||
(previousTriggerTop !== undefined && previousTriggerTop !== triggerTop);

if (hasTriggerMoved) {
handleClose();
}
}, [handleClose, previousTriggerLeft, previousTriggerTop, triggerLeft, triggerTop]);

return null;
}

const Provider = TooltipProvider;
const Root = Tooltip;
const Trigger = TooltipTrigger;
Expand Down
2 changes: 0 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4008,8 +4008,6 @@ __metadata:
"@radix-ui/react-primitive": "workspace:*"
"@radix-ui/react-slot": "workspace:*"
"@radix-ui/react-use-controllable-state": "workspace:*"
"@radix-ui/react-use-previous": "workspace:*"
"@radix-ui/react-use-rect": "workspace:*"
"@radix-ui/react-visually-hidden": "workspace:*"
peerDependencies:
react: ^16.8 || ^17.0
Expand Down

0 comments on commit 589ebdc

Please sign in to comment.