Created
May 14, 2020 11:15
-
-
Save medatech/500a281c11ce754921d2948cee426c15 to your computer and use it in GitHub Desktop.
Ensure that an element is always on screen
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect } from "react" | |
/** | |
* This hook takes an element and ensures that it does not go outside the bounds of the viewport. It is useful | |
* for things like a context menu where you don't want the menu to appear off screen. | |
* | |
* The only requriements for the DOM reference passed in is that it is absolutely positioned. | |
* To prevent it flickering in the original place and then moving with the correction, set the visibility CSS attribute | |
* of the DOM element to 'hidden'. This hook will set it to 'visible' when it has computed the corrected position | |
* | |
* @param {Reference} ref The React reference for the DOM element to ensure is on screen. | |
* @param {Number} padding The amount of space to leave around the edge of the screen | |
**/ | |
export default function useEnsureOnScreen(ref, padding = 2) { | |
useEffect(() => { | |
if (ref.current === null) return | |
const bounds = ref.current.getBoundingClientRect() | |
const measurement = { | |
viewportWidth: | |
ref.current.ownerDocument.documentElement.clientWidth, | |
viewportHeight: | |
ref.current.ownerDocument.documentElement.clientHeight, | |
left: bounds.left, | |
right: bounds.right, | |
top: bounds.top, | |
bottom: bounds.bottom, | |
width: bounds.width, | |
height: bounds.height, | |
} | |
if (measurement.right > measurement.viewportWidth) { | |
// It's too far to the right, so bring it left | |
const newLeft = | |
measurement.viewportWidth - measurement.width - padding | |
ref.current.style.left = `${newLeft}px` | |
} | |
if (measurement.left < 0) { | |
ref.current.style.left = `${padding}px` | |
} | |
if (measurement.bottom > measurement.viewportHeight) { | |
// It's too far off the bottom, so bring it top | |
const newTop = | |
measurement.viewportHeight - measurement.height - padding | |
ref.current.style.top = `${newTop}px` | |
} | |
if (measurement.top < 0) { | |
ref.current.style.top = `${padding}px` | |
} | |
// Now we can show it | |
ref.current.style.visibility = `visible` | |
}, [ref.current, padding]) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment