Files
community-rule/app/components/modals/ModalHeader/ModalHeader.container.tsx
T
2026-05-21 23:25:56 -06:00

79 lines
2.3 KiB
TypeScript

"use client";
import { memo, useEffect, useId, useRef, useState } from "react";
import { useTranslation } from "../../../contexts/MessagesContext";
import { ModalHeaderView } from "./ModalHeader.view";
import type { ModalHeaderProps } from "./ModalHeader.types";
/**
* Figma: "Utility / ModalHeader". Lives under `modals/` with other composed modal chrome.
* Sticky 48px modal header with optional close (left) and more-options
* (right) icon buttons.
*/
const ModalHeaderContainer = memo<ModalHeaderProps>((props) => {
const t = useTranslation("controlsChrome");
const {
closeButtonAriaLabel = t("closeDialog"),
moreOptionsAriaLabel = t("moreOptions"),
menuAriaLabel = t("moreOptionsMenu"),
menuItems = [],
...rest
} = props;
const hasMenu = menuItems.length > 0;
const [menuOpen, setMenuOpen] = useState(false);
const menuId = useId();
const menuWrapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!menuOpen || !hasMenu) return;
const onDoc = (event: MouseEvent) => {
if (
menuWrapRef.current &&
!menuWrapRef.current.contains(event.target as Node)
) {
setMenuOpen(false);
}
};
document.addEventListener("mousedown", onDoc);
return () => document.removeEventListener("mousedown", onDoc);
}, [hasMenu, menuOpen]);
useEffect(() => {
if (!menuOpen || !hasMenu) return;
const onKey = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setMenuOpen(false);
}
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [hasMenu, menuOpen]);
return (
<div ref={menuWrapRef}>
<ModalHeaderView
{...rest}
menuItems={menuItems}
closeButtonAriaLabel={closeButtonAriaLabel}
moreOptionsAriaLabel={moreOptionsAriaLabel}
menuAriaLabel={menuAriaLabel}
menuId={menuId}
menuOpen={menuOpen}
onToggleMenu={hasMenu ? () => setMenuOpen((open) => !open) : undefined}
onMenuItemClick={
hasMenu
? (item) => {
item.onClick?.();
setMenuOpen(false);
}
: undefined
}
/>
</div>
);
});
ModalHeaderContainer.displayName = "ModalHeader";
export default ModalHeaderContainer;