mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-26 13:17:15 -05:00
53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
import * as React from "react";
|
|
import { useLocation } from "react-router";
|
|
import { DESKTOP_LAYOUT_QUERY, MOBILE_LAYOUT_QUERY } from "./useLayoutSize";
|
|
|
|
/**
|
|
* Closes the open popovers in `ref` (the element itself or any inside it) on
|
|
* navigation and when the viewport crosses into another layout. For the
|
|
* popovers of the layout shell, which outlive the page they were opened on.
|
|
* Manual popovers are left alone, their owner decides when they show.
|
|
*/
|
|
export function useClosePopoversOnNavigation(
|
|
ref: React.RefObject<HTMLElement | null>,
|
|
) {
|
|
const { key: locationKey } = useLocation();
|
|
const previousLocationKeyRef = React.useRef(locationKey);
|
|
|
|
// hydration must not close a popover opened before it, hence only changes count
|
|
React.useEffect(() => {
|
|
if (previousLocationKeyRef.current === locationKey) return;
|
|
previousLocationKeyRef.current = locationKey;
|
|
hidePopoversWithin(ref.current);
|
|
}, [locationKey, ref]);
|
|
|
|
React.useEffect(() => {
|
|
const breakpoints = [MOBILE_LAYOUT_QUERY, DESKTOP_LAYOUT_QUERY].map(
|
|
(query) => window.matchMedia(query),
|
|
);
|
|
const onChange = () => hidePopoversWithin(ref.current);
|
|
|
|
for (const breakpoint of breakpoints) {
|
|
breakpoint.addEventListener("change", onChange);
|
|
}
|
|
return () => {
|
|
for (const breakpoint of breakpoints) {
|
|
breakpoint.removeEventListener("change", onChange);
|
|
}
|
|
};
|
|
}, [ref]);
|
|
}
|
|
|
|
function hidePopoversWithin(element: HTMLElement | null) {
|
|
if (!element) return;
|
|
|
|
for (const popover of [
|
|
element,
|
|
...element.querySelectorAll<HTMLElement>("[popover]"),
|
|
]) {
|
|
if (popover.popover !== "manual" && popover.matches(":popover-open")) {
|
|
popover.hidePopover();
|
|
}
|
|
}
|
|
}
|