import "./index.scss"; import clsx from "clsx"; import { useEffect, useRef, useState } from "react"; export type SidebarProps = React.HTMLAttributes & { open?: boolean; onClose?: () => void; overlayProps?: React.HTMLAttributes; pullRight?: boolean; shadow?: boolean; }; export function Sidebar(props: SidebarProps) { const { children, className, open = false, onClose = () => {}, overlayProps = {}, pullRight = false, shadow = true, ...other } = props; const [hidden, setHidden] = useState(!open); const sidebar = useRef(null); const prevFocus = useRef(null); function closeSidebar() { if (open && typeof onClose === "function") { onClose(); } } useEffect(() => { let element = sidebar.current; if (open) { setHidden(false); } function toggleHiddenContent() { setHidden(!open); } element?.addEventListener("transitionend", toggleHiddenContent); return () => element?.removeEventListener("transitionend", toggleHiddenContent); }, [open]); // oxlint-disable-next-line react-hooks/exhaustive-deps -- TODO useEffect(() => { function handleCloseSidebar(evt: KeyboardEvent) { if (evt.key === "Escape") { evt.stopPropagation(); closeSidebar(); } } window.addEventListener("keyup", handleCloseSidebar); return () => window.removeEventListener("keyup", handleCloseSidebar); }, [open, onClose]); useEffect(() => { if (open) { prevFocus.current = document.activeElement as HTMLElement | null; const firstFocusable = sidebar.current?.querySelector( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ) as HTMLElement | undefined; if (firstFocusable) { firstFocusable.focus(); } } else if (prevFocus.current) { prevFocus.current.focus(); prevFocus.current = null; } let prevFocusElem = prevFocus.current; return () => { if (open && prevFocusElem) { prevFocusElem.focus(); } }; }, [open]); const { className: overlayClassName, ...overlayOther } = overlayProps; return ( <>
{(open || !hidden) && children}
{/** oxlint-disable-next-line jsx-a11y/no-static-element-interactions -- legacy component */}
); }