diff options
| author | shu <nvcsadjkojf@outlook.com> | 2026-09-07 15:48:36 +0800 |
|---|---|---|
| committer | shu <nvcsadjkojf@outlook.com> | 2026-09-07 15:48:36 +0800 |
| commit | 481402e09fe760634b826b09e302e2e1bfec88b1 (patch) | |
| tree | 9b55025bfcd6acd2c5d795f057de27028b422603 /src/components/mobile-navigation.tsx | |
feat: initialize React Spectrum template with layouts and showcases
Diffstat (limited to 'src/components/mobile-navigation.tsx')
| -rw-r--r-- | src/components/mobile-navigation.tsx | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/src/components/mobile-navigation.tsx b/src/components/mobile-navigation.tsx new file mode 100644 index 0000000..abf4f24 --- /dev/null +++ b/src/components/mobile-navigation.tsx @@ -0,0 +1,64 @@ +import styles from './mobile-navigation.module.css' +import { useEffect, useRef, useState } from 'react' +import { useLocation } from 'react-router' +import { ActionButton } from '@react-spectrum/s2/ActionButton' +import CloseIcon from '@react-spectrum/s2/icons/Close' +import ViewListIcon from '@react-spectrum/s2/icons/ViewList' +import SidebarMenu from './sidebar-menu' + +function MobileNavigation() { + const dialogRef = useRef<HTMLDialogElement>(null) + const [isOpen, setIsOpen] = useState(false) + const { key } = useLocation() + + // Close after all route changes, including selecting the current page. + useEffect(() => { + dialogRef.current?.close() + }, [key]) + + useEffect(() => { + const media = window.matchMedia('(max-width: 640px)') + const closeOnDesktop = () => { + if (!media.matches) dialogRef.current?.close() + } + media.addEventListener('change', closeOnDesktop) + return () => media.removeEventListener('change', closeOnDesktop) + }, []) + + return ( + <div className={styles.mobileMenuToggle}> + <ActionButton + aria-label="打开菜单" + aria-haspopup="dialog" + aria-expanded={isOpen} + aria-controls="mobile-navigation" + isQuiet + onPress={() => { + dialogRef.current?.showModal() + setIsOpen(true) + }} + > + <ViewListIcon /> + </ActionButton> + <dialog + ref={dialogRef} + id="mobile-navigation" + className={styles.mobileNavigation} + aria-labelledby="mobile-navigation-title" + onClose={() => setIsOpen(false)} + > + <header className={styles.mobileNavigationHeader}> + <span id="mobile-navigation-title">菜单</span> + <ActionButton aria-label="关闭菜单" isQuiet onPress={() => dialogRef.current?.close()}> + <CloseIcon /> + </ActionButton> + </header> + <nav className={styles.mobileNavigationContent} aria-label="页面导航"> + <SidebarMenu /> + </nav> + </dialog> + </div> + ) +} + +export default MobileNavigation |
