UNPKG

hanav

Version:

A React Navigation Menu Component Library.

115 lines (103 loc) 3.2 kB
import React, { useEffect, useId, useMemo, useRef, useState } from "react"; import { ContextForMiniMenu, ContextForMiniItem, ContextForMiniContent, ContextForMiniToggle, ContextForMiniBack } from "./context"; export default function NavBar({ children, ...navProps }) { const navRef = useRef(null); const [expanded, setE] = useState(); // 是否展开 const [openedMenuIdx, setIdx] = useState(-1); // 当前打开 trigger 的序号 const prevIdx = useRef(-1); /** 菜单按钮的元素们 */ const btnsRef = useRef([]); /** 每个内容面板的头元素 */ const headFocusItemInContent = useRef([]); /** 每个内容面板的尾元素 */ const tailFocusItemInContent = useRef([]); const menuId = useId(); const triggerIdsRef = useRef([]); const contentIdsRef = useRef([]); const menuRef = useRef(); const mockTail = useRef(); const mockHead = useRef(); const mockTailFocusByWrap = useRef(); const mockHeadFocusByWrap = useRef(); useEffect(() => { if (expanded) { if (openedMenuIdx < 0) { btnsRef.current[prevIdx.current].focus(); } else { const _head = headFocusItemInContent.current[openedMenuIdx] || menuRef.current; _head.focus(); } } }, [openedMenuIdx]); const { style, ..._navProps } = navProps; const menuContextVal = useMemo(() => ({ navRef, expanded, setE, btnsRef, openedMenuIdx, tailFocusItemInContent, headFocusItemInContent, menuId, menuRef, mockTail, mockHead, mockTailFocusByWrap, mockHeadFocusByWrap, }), [expanded, openedMenuIdx]); const itemContextVal = useMemo(() => ({ openedMenuIdx, openOrCloseContentById, btnsRef, headFocusItemInContent, tailFocusItemInContent, triggerIdsRef, contentIdsRef, menuRef, mockTail, mockTailFocusByWrap, mockHead, mockHeadFocusByWrap, }), [openedMenuIdx]); const contentContextVal = useMemo(() => ({ openedMenuIdx, }), [openedMenuIdx]); const toggleContextVal = useMemo(() => ({ setE, expanded, openOrCloseContentById, btnsRef, menuId }), [expanded]); return <ContextForMiniMenu.Provider value={menuContextVal}> <ContextForMiniItem.Provider value={itemContextVal}> <ContextForMiniContent.Provider value={contentContextVal}> <ContextForMiniToggle.Provider value={toggleContextVal}> <ContextForMiniBack.Provider value={openOrCloseContentById}> <nav aria-label="Main" tabIndex={-1} // 用于 macOS 旁白转子ref={navRef} style={{ position: "fixed", top: 0, left: 0, width: "100%", zIndex: 2, ...style, }} {..._navProps}> {children} </nav> </ContextForMiniBack.Provider> </ContextForMiniToggle.Provider> </ContextForMiniContent.Provider> </ContextForMiniItem.Provider> </ContextForMiniMenu.Provider>; function openOrCloseContentById(idx) { setIdx(prev => { prevIdx.current = prev; return idx; }); } }