Skip to content

Instantly share code, notes, and snippets.

@felixeichler
Forked from eldh/MouseSafeArea.ts
Last active October 7, 2024 10:21
Show Gist options
  • Save felixeichler/ed9bb8c0de6d0abc0d34e6b6f81b792f to your computer and use it in GitHub Desktop.
Save felixeichler/ed9bb8c0de6d0abc0d34e6b6f81b792f to your computer and use it in GitHub Desktop.
import { useMouse } from '@uidotdev/usehooks';
import * as React from 'react';
interface Positions {
/* Sub-menu height */
h: number;
/* Mouse x */
mouseX: number;
/* Mouse y */
mouseY: number;
/* Sub-menu width */
w: number;
/* Sub-menu x */
x: number;
/* Sub-menu y */
y: number;
}
const getLeft = ({ mouseX, x }: Positions) => (mouseX > x ? undefined : -Math.max(x - mouseX, 10) + 'px');
const getRight = ({ mouseX, w, x }: Positions) => (mouseX > x ? -Math.max(mouseX - (x + w), 10) + 'px' : undefined);
const getWidth = ({ mouseX, w, x }: Positions) =>
mouseX > x ? Math.max(mouseX - (x + w), 10) + 'px' : Math.max(x - mouseX, 10) + 'px';
const getClipPath = ({ h, mouseX, mouseY, x, y }: Positions) =>
mouseX > x
? `polygon(0% 0%, 0% 100%, 100% ${(100 * (mouseY - y)) / h}%)`
: `polygon(100% 0%, 0% ${(100 * (mouseY - y)) / h}%, 100% 100%)`;
/**
* Component to cover the area between the mouse cursor and the sub-menu, to allow moving cursor to lower parts of sub-menu without the sub-menu disappearing.
* thank Linear for this: https://linear.app/blog/invisible-details
* based on: https://gist.github.com/eldh/51e3825b7aa55694f2a5ffa5f7de8a6a
*/
const MouseSafeArea = (props: { parentRef: React.RefObject<HTMLDivElement> }) => {
const { height: h = 0, width: w = 0, x = 0, y = 0 } = props.parentRef.current?.getBoundingClientRect() || {};
const [mouseState] = useMouse();
const [mouseX, mouseY] = [mouseState.x, mouseState.y];
const positions = { h, mouseX, mouseY, w, x, y };
return (
<div
style={{
//backgroundColor: 'rgba(255,0,0,0.1)', // Debug
clipPath: getClipPath(positions),
height: h,
left: getLeft(positions),
position: 'absolute',
right: getRight(positions),
top: 0,
width: getWidth(positions),
}}
/>
);
};
export default MouseSafeArea;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment