64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
'use client';
|
|
|
|
import {IconButton} from '@astryxdesign/core/IconButton';
|
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
import {ICONS} from '@/shared/utils/icons';
|
|
import {useSidebar} from './SidebarProvider';
|
|
|
|
/**
|
|
* The one control that is never absent.
|
|
*
|
|
* It renders in the top bar's leading slot at EVERY width — the header is the
|
|
* only region of the shell that survives all three navigation states, so it is
|
|
* the only place a control can live and still be reachable when the nav it
|
|
* governs is collapsed or gone. Anything mounted inside the sidebar disappears
|
|
* with the sidebar, which is exactly how the rail became unreopenable.
|
|
*
|
|
* Three states, one button:
|
|
* drawer closed ☰ → open the overlay
|
|
* drawer open ☰ → close it (aria-expanded carries the state)
|
|
* rail collapsed panel-open → expand to 260px
|
|
* rail expanded panel-close → collapse to 84px
|
|
*
|
|
* 44px square below the breakpoint: IconButton's `lg` is 36px, which clears
|
|
* neither the WCAG 2.2 target-size minimum nor a thumb. `size-11` is the
|
|
* spacing-11 token (44px) through the Tailwind bridge, and it sits in the
|
|
* utilities layer so it wins over Astryx's own sizing without !important.
|
|
*/
|
|
export function NavMenuButton() {
|
|
const {isDrawer, isDrawerOpen, isCollapsed, toggle, controlsId} =
|
|
useSidebar();
|
|
|
|
const label = isDrawer
|
|
? isDrawerOpen
|
|
? 'Close navigation menu'
|
|
: 'Open navigation menu'
|
|
: isCollapsed
|
|
? 'Expand navigation'
|
|
: 'Collapse navigation';
|
|
|
|
const icon = isDrawer
|
|
? ICONS.menu
|
|
: isCollapsed
|
|
? ICONS.sidebarOpen
|
|
: ICONS.sidebarClose;
|
|
|
|
return (
|
|
<IconButton
|
|
icon={<Icon icon={icon} size={isDrawer ? 'md' : 'sm'} />}
|
|
label={label}
|
|
tooltip={label}
|
|
variant="ghost"
|
|
size={isDrawer ? 'lg' : 'md'}
|
|
onClick={toggle}
|
|
// Expanded/collapsed is the drawer's state; above the breakpoint the rail
|
|
// is always present, so only its width changes and aria-expanded would be
|
|
// a lie. aria-controls still points at the rail so the relationship holds.
|
|
aria-expanded={isDrawer ? isDrawerOpen : undefined}
|
|
aria-controls={controlsId}
|
|
className={isDrawer ? 'size-11' : undefined}
|
|
data-testid="nav-menu-button"
|
|
/>
|
|
);
|
|
}
|