| ... | ... | @@ -8,6 +8,7 @@ import { |
| 8 | 8 | useState, |
| 9 | 9 | type ComponentType, |
| 10 | 10 | type KeyboardEvent, |
| 11 | type ReactNode, |
| 11 | 12 | } from "react"; |
| 12 | 13 | import { |
| 13 | 14 | ArchiveIcon, |
| ... | ... | @@ -24,6 +25,7 @@ import { |
| 24 | 25 | import { useLocation, useNavigate } from "@tanstack/react-router"; |
| 25 | 26 | |
| 26 | 27 | import { Button } from "../ui/button"; |
| 28 | import { Collapsible, CollapsiblePanel } from "../ui/collapsible"; |
| 27 | 29 | import { Input } from "../ui/input"; |
| 28 | 30 | import { Kbd } from "../ui/kbd"; |
| 29 | 31 | import { |
| ... | ... | @@ -114,6 +116,22 @@ function SettingsSectionIcon({ to }: { to: SettingsPath }) { |
| 114 | 116 | return <Icon className="mt-0.5 size-3.5 shrink-0 text-sidebar-muted-foreground/60" />; |
| 115 | 117 | } |
| 116 | 118 | |
| 119 | function SettingsSubmenuCollapse({ |
| 120 | open, |
| 121 | children, |
| 122 | }: { |
| 123 | readonly open: boolean; |
| 124 | readonly children: ReactNode; |
| 125 | }) { |
| 126 | return ( |
| 127 | <Collapsible open={open}> |
| 128 | <CollapsiblePanel className="duration-150 ease-out motion-reduce:transition-none"> |
| 129 | {children} |
| 130 | </CollapsiblePanel> |
| 131 | </Collapsible> |
| 132 | ); |
| 133 | } |
| 134 | |
| 117 | 135 | export function SettingsSidebarNav({ pathname }: { pathname: string }) { |
| 118 | 136 | const navigate = useNavigate(); |
| 119 | 137 | const currentHash = useLocation({ select: (location) => location.hash }); |
| ... | ... | @@ -125,6 +143,9 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) { |
| 125 | 143 | const results = useMemo(() => searchSettings(query, searchableItems), [query, searchableItems]); |
| 126 | 144 | const isSearching = query.trim().length > 0; |
| 127 | 145 | const hasResults = results.length > 0; |
| 146 | const activeSettingsPath = SETTINGS_NAV_ITEMS.find( |
| 147 | (item) => pathname === item.to || pathname.startsWith(`${item.to}/`), |
| 148 | )?.to; |
| 128 | 149 | |
| 129 | 150 | useEffect(() => { |
| 130 | 151 | setActiveResultIndex((index) => Math.min(index, Math.max(results.length - 1, 0))); |
| ... | ... | @@ -347,8 +368,8 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) { |
| 347 | 368 | <SidebarMenu className="ps-px"> |
| 348 | 369 | {SETTINGS_NAV_ITEMS.map((item) => { |
| 349 | 370 | const Icon = item.icon; |
| 350 | | const isActive = pathname === item.to || pathname.startsWith(`${item.to}/`); |
| 351 | 371 | const pageSections = SETTINGS_PAGE_SECTIONS[item.to]; |
| 372 | const isActive = activeSettingsPath === item.to; |
| 352 | 373 | return ( |
| 353 | 374 | <SidebarMenuItem key={item.to}> |
| 354 | 375 | <SidebarMenuButton |
| ... | ... | @@ -358,21 +379,23 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) { |
| 358 | 379 | <Icon /> |
| 359 | 380 | <span className="truncate">{item.label}</span> |
| 360 | 381 | </SidebarMenuButton> |
| 361 | | {isActive && pageSections ? ( |
| 362 | | <SidebarMenuSub className="border-l-0"> |
| 363 | | {pageSections.map((section) => ( |
| 364 | | <SidebarMenuSubItem key={section.targetId}> |
| 365 | | <SidebarMenuSubButton |
| 366 | | render={<button type="button" />} |
| 367 | | size="sm" |
| 368 | | className="w-full text-sidebar-muted-foreground/65" |
| 369 | | onClick={() => handlePageSectionClick(item.to, section.targetId)} |
| 370 | | > |
| 371 | | <span className="ms-0.5">{section.label}</span> |
| 372 | | </SidebarMenuSubButton> |
| 373 | | </SidebarMenuSubItem> |
| 374 | | ))} |
| 375 | | </SidebarMenuSub> |
| 382 | {pageSections ? ( |
| 383 | <SettingsSubmenuCollapse open={isActive}> |
| 384 | <SidebarMenuSub className="border-l-0"> |
| 385 | {pageSections.map((section) => ( |
| 386 | <SidebarMenuSubItem key={section.targetId}> |
| 387 | <SidebarMenuSubButton |
| 388 | render={<button type="button" />} |
| 389 | size="sm" |
| 390 | className="w-full text-sidebar-muted-foreground/65" |
| 391 | onClick={() => handlePageSectionClick(item.to, section.targetId)} |
| 392 | > |
| 393 | <span className="ms-0.5">{section.label}</span> |
| 394 | </SidebarMenuSubButton> |
| 395 | </SidebarMenuSubItem> |
| 396 | ))} |
| 397 | </SidebarMenuSub> |
| 398 | </SettingsSubmenuCollapse> |
| 376 | 399 | ) : null} |
| 377 | 400 | </SidebarMenuItem> |
| 378 | 401 | ); |