authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-04 17:06:27-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 17:06:27-07:00
log2e61301b1325e30cb9464e193299a7e469f7ec08
tree5a71e79af31a39f3379f51219169742bdc9b80d6
parent4f1092cec2ebaa6ed0e49821238dfe6f1add362d
signature Signed by PGP key B5690EEEBB952194

fix(web): smooth settings sidebar transitions (#9811)


1 files changed, 39 insertions(+), 16 deletions(-)

apps/web/src/components/settings/SettingsSidebarNav.tsx+39-16
......@@ -8,6 +8,7 @@ import {
88 useState,
99 type ComponentType,
1010 type KeyboardEvent,
11 type ReactNode,
1112} from "react";
1213import {
1314 ArchiveIcon,
......@@ -24,6 +25,7 @@ import {
2425import { useLocation, useNavigate } from "@tanstack/react-router";
2526
2627import { Button } from "../ui/button";
28import { Collapsible, CollapsiblePanel } from "../ui/collapsible";
2729import { Input } from "../ui/input";
2830import { Kbd } from "../ui/kbd";
2931import {
......@@ -114,6 +116,22 @@ function SettingsSectionIcon({ to }: { to: SettingsPath }) {
114116 return <Icon className="mt-0.5 size-3.5 shrink-0 text-sidebar-muted-foreground/60" />;
115117}
116118
119function 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
117135export function SettingsSidebarNav({ pathname }: { pathname: string }) {
118136 const navigate = useNavigate();
119137 const currentHash = useLocation({ select: (location) => location.hash });
......@@ -125,6 +143,9 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
125143 const results = useMemo(() => searchSettings(query, searchableItems), [query, searchableItems]);
126144 const isSearching = query.trim().length > 0;
127145 const hasResults = results.length > 0;
146 const activeSettingsPath = SETTINGS_NAV_ITEMS.find(
147 (item) => pathname === item.to || pathname.startsWith(`${item.to}/`),
148 )?.to;
128149
129150 useEffect(() => {
130151 setActiveResultIndex((index) => Math.min(index, Math.max(results.length - 1, 0)));
......@@ -347,8 +368,8 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
347368 <SidebarMenu className="ps-px">
348369 {SETTINGS_NAV_ITEMS.map((item) => {
349370 const Icon = item.icon;
350 const isActive = pathname === item.to || pathname.startsWith(`${item.to}/`);
351371 const pageSections = SETTINGS_PAGE_SECTIONS[item.to];
372 const isActive = activeSettingsPath === item.to;
352373 return (
353374 <SidebarMenuItem key={item.to}>
354375 <SidebarMenuButton
......@@ -358,21 +379,23 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
358379 <Icon />
359380 <span className="truncate">{item.label}</span>
360381 </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>
376399 ) : null}
377400 </SidebarMenuItem>
378401 );