Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Last active March 29, 2023 18:29
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ryanflorence/4e0ef33b9f1ad6eaa955a336dc1ee619 to your computer and use it in GitHub Desktop.
Save ryanflorence/4e0ef33b9f1ad6eaa955a336dc1ee619 to your computer and use it in GitHub Desktop.
import { useLocation, useTransition } from "@remix-run/react";
import * as React from "react";
/**
* An enhanced `<details>` component that's intended to be used as a menu (a bit
* like a menu-button).
*/
export let DetailsMenu = React.forwardRef<
HTMLDetailsElement,
React.ComponentPropsWithRef<"details">
>((props, forwardedRef) => {
let { onToggle, onMouseDown, onTouchStart, onFocus, open, ...rest } = props;
let [isOpen, setIsOpen] = React.useState(false);
let location = useLocation();
let transition = useTransition();
let clickRef = React.useRef<boolean>(false);
let focusRef = React.useRef<boolean>(false);
React.useEffect(() => {
if (transition.submission) {
setIsOpen(false);
}
}, [transition]);
React.useEffect(() => {
setIsOpen(false);
}, [location.key]);
React.useEffect(() => {
if (isOpen) {
let clickHandler = () => {
if (!clickRef.current) setIsOpen(false);
clickRef.current = false;
};
let focusHandler = () => {
if (!focusRef.current) setIsOpen(false);
focusRef.current = false;
};
document.addEventListener("mousedown", clickHandler);
document.addEventListener("touchstart", clickHandler);
document.addEventListener("focusin", focusHandler);
return () => {
document.removeEventListener("mousedown", clickHandler);
document.removeEventListener("touchstart", clickHandler);
document.removeEventListener("focusin", focusHandler);
};
}
}, [isOpen]);
return (
<details
ref={forwardedRef}
open={open ?? isOpen}
onToggle={(event) => {
onToggle && onToggle(event);
if (event.defaultPrevented) return;
setIsOpen(event.currentTarget.open);
}}
onMouseDown={(event) => {
onMouseDown && onMouseDown(event);
if (event.defaultPrevented) return;
if (isOpen) clickRef.current = true;
}}
onTouchStart={(event) => {
onTouchStart && onTouchStart(event);
if (event.defaultPrevented) return;
if (isOpen) clickRef.current = true;
}}
onFocus={(event) => {
onFocus && onFocus(event);
if (event.defaultPrevented) return;
if (isOpen) focusRef.current = true;
}}
{...rest}
/>
);
});
DetailsMenu.displayName = "DetailsMenu";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment