aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/components/layout-basic.tsx
blob: 05f3eefc1a22e30922cfcc58a67ddc33f3214899 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import { useState, type ReactNode } from 'react'
import { useMatches } from 'react-router'
import type { PageMetadata } from '../route-config'
import CollapsedNavigation from './collapsed-navigation'
import { ActionButton } from '@react-spectrum/s2/ActionButton'
import { Tooltip, TooltipTrigger } from '@react-spectrum/s2/Tooltip'
import SidebarMenu from './sidebar-menu'
import MobileNavigation from './mobile-navigation'
import GlobalSettings from './global-settings'
import HomeIcon from '@react-spectrum/s2/icons/Home'
import ChevronLeftIcon from '@react-spectrum/s2/icons/ChevronLeft'
import ChevronRightIcon from '@react-spectrum/s2/icons/ChevronRight'
import styles from './layout-basic.module.css'

interface BasicLayoutProps {
  children: ReactNode
}

function BasicLayout({ children }: BasicLayoutProps) {
  const [isCollapsed, setIsCollapsed] = useState(false)
  const currentPage = useMatches().at(-1)?.handle as PageMetadata | undefined

  return (
    <div className={`${styles.appLayout}${isCollapsed ? ` ${styles.isCollapsed}` : ''}`}>
      <a className={styles.skipLink} href="#main-content">Skip to content</a>
      <aside id="sidebar" className={styles.sidebar} aria-label="Sidebar">
        <div className={styles.sidebarBrand} aria-label="React Spectrum">
          {isCollapsed ? <HomeIcon /> : 'React Spectrum'}
        </div>
        <nav className={styles.sidebarNavigation} aria-label="Main navigation">
          {isCollapsed && <CollapsedNavigation activePageId={currentPage?.id} />}
          <div className={styles.expandedNavigation} hidden={isCollapsed}>
            <SidebarMenu />
          </div>
        </nav>
      </aside>
      <div className={styles.contentLayout}>
        <header className={styles.topbar}>
          <MobileNavigation />
          <div className={styles.desktopMenuToggle}>
            <TooltipTrigger>
              <ActionButton
                aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
                aria-expanded={!isCollapsed}
                aria-controls="sidebar"
                isQuiet
                onPress={() => setIsCollapsed((collapsed) => !collapsed)}
              >
                {isCollapsed ? <ChevronRightIcon /> : <ChevronLeftIcon />}
              </ActionButton>
              <Tooltip>{isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}</Tooltip>
            </TooltipTrigger>
          </div>
          <span>{currentPage?.title}</span>
          <div className={styles.topbarActions}>
            <GlobalSettings />
          </div>
        </header>
        <main id="main-content" className={styles.pageContent} tabIndex={-1}>
          {children}
        </main>
      </div>
    </div>
  )
}

export default BasicLayout