Skip to content

Commit d6dbe8d

Browse files
t3dotggclaude
andauthored
fix(web): stop the settings sidebar shifting when switching pages (#10705)
Co-authored-by: Claude Fable 5.1 <[email protected]>
1 parent 134b719 commit d6dbe8d

4 files changed

Lines changed: 3 additions & 570 deletions

File tree

‎apps/web/src/components/settings/SettingsSidebarNav.tsx‎

Lines changed: 2 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
useState,
99
type ComponentType,
1010
type KeyboardEvent,
11-
type ReactNode,
1211
} from "react";
1312
import {
1413
ArchiveIcon,
@@ -24,32 +23,22 @@ import {
2423
Settings2Icon,
2524
XIcon,
2625
} from "lucide-react";
27-
import { useLocation, useNavigate, useRouterState } from "@tanstack/react-router";
26+
import { useLocation, useNavigate } from "@tanstack/react-router";
2827

2928
import { Button } from "../ui/button";
30-
import { Collapsible, CollapsiblePanel } from "../ui/collapsible";
3129
import { Input } from "../ui/input";
3230
import { Kbd } from "../ui/kbd";
33-
import { cn } from "../../lib/utils";
3431
import {
3532
SidebarContent,
3633
SidebarFooter,
3734
SidebarGroup,
3835
SidebarMenu,
3936
SidebarMenuButton,
4037
SidebarMenuItem,
41-
SidebarMenuSub,
42-
SidebarMenuSubButton,
43-
SidebarMenuSubItem,
4438
useSidebar,
4539
} from "../ui/sidebar";
4640
import { SidebarUtilityMenu } from "../sidebar/SidebarChrome";
4741
import { scrollToSettingsTarget } from "./settingsLayout";
48-
import {
49-
getVisibleSettingsSectionIds,
50-
observeSettingsSectionVisibility,
51-
type SettingsSectionVisibilityState,
52-
} from "./settingsSectionVisibility";
5342
import {
5443
searchSettings,
5544
SETTINGS_SECTION_LABELS,
@@ -106,102 +95,22 @@ const SETTINGS_NAV_ITEMS: ReadonlyArray<{
10695
icon: SETTINGS_SECTION_ICONS[to],
10796
}));
10897

109-
const SETTINGS_PAGE_SECTIONS: Partial<
110-
Readonly<Record<SettingsPath, ReadonlyArray<{ label: string; targetId: string }>>>
111-
> = {
112-
"/settings/general": [
113-
{ label: "Organization", targetId: "organization" },
114-
{ label: "Behavior", targetId: "behavior" },
115-
{ label: "Projects & threads", targetId: "projects-and-threads" },
116-
{ label: "Confirmations", targetId: "confirmations" },
117-
{ label: "Text generation", targetId: "text-generation" },
118-
{ label: "About", targetId: "about" },
119-
{ label: "Legacy features", targetId: "legacy-features" },
120-
],
121-
"/settings/appearance": [
122-
{ label: "Colors & themes", targetId: "appearance" },
123-
{ label: "Interface", targetId: "appearance-interface" },
124-
{ label: "Motion", targetId: "motion" },
125-
{ label: "Typography", targetId: "typography" },
126-
],
127-
"/settings/source-control": [
128-
{ label: "Version control", targetId: "source-control" },
129-
{ label: "Text generation", targetId: "source-control-text-generation" },
130-
],
131-
"/settings/connections": [
132-
{ label: "This environment", targetId: "connections-environment" },
133-
{ label: "Remote environments", targetId: "remote-environments" },
134-
],
135-
};
136-
13798
function SettingsSectionIcon({ to }: { to: SettingsPath }) {
13899
const Icon = SETTINGS_SECTION_ICONS[to];
139100
return <Icon className="mt-0.5 size-3.5 shrink-0 text-sidebar-muted-foreground/60" />;
140101
}
141102

142-
function SettingsSubmenuCollapse({
143-
open,
144-
children,
145-
}: {
146-
readonly open: boolean;
147-
readonly children: ReactNode;
148-
}) {
149-
return (
150-
<Collapsible open={open}>
151-
<CollapsiblePanel className="duration-150 ease-out motion-reduce:transition-none">
152-
{children}
153-
</CollapsiblePanel>
154-
</Collapsible>
155-
);
156-
}
157-
158103
export function SettingsSidebarNav({ pathname }: { pathname: string }) {
159104
const navigate = useNavigate();
160105
const currentHash = useLocation({ select: (location) => location.hash });
161-
const resolvedPathname = useRouterState({
162-
select: (state) => state.resolvedLocation?.pathname,
163-
});
164106
const { isMobile, setOpenMobile, open, setOpen } = useSidebar();
165107
const searchInputRef = useRef<HTMLInputElement>(null);
166108
const [query, setQuery] = useState("");
167109
const [activeResultIndex, setActiveResultIndex] = useState(0);
168-
const [sectionVisibility, setSectionVisibility] = useState<SettingsSectionVisibilityState | null>(
169-
null,
170-
);
171110
const searchableItems = useAvailableSettingsSearchItems();
172111
const results = useMemo(() => searchSettings(query, searchableItems), [query, searchableItems]);
173112
const isSearching = query.trim().length > 0;
174113
const hasResults = results.length > 0;
175-
const activeSettingsPath = SETTINGS_NAV_ITEMS.find(
176-
(item) => pathname === item.to || pathname.startsWith(`${item.to}/`),
177-
)?.to;
178-
const observedVisibilityScope = useMemo(() => {
179-
const path = SETTINGS_NAV_ITEMS.find(
180-
(item) =>
181-
resolvedPathname === item.to || resolvedPathname?.startsWith(`${item.to}/`) === true,
182-
)?.to;
183-
const pageSections = path ? SETTINGS_PAGE_SECTIONS[path] : undefined;
184-
return path && pageSections ? { path, pageSections } : null;
185-
}, [resolvedPathname]);
186-
const visiblePageSectionIds = getVisibleSettingsSectionIds({
187-
activePath: activeSettingsPath,
188-
scope: observedVisibilityScope,
189-
visibility: sectionVisibility,
190-
});
191-
192-
useEffect(() => {
193-
if (!observedVisibilityScope) return;
194-
const container = document.querySelector<HTMLElement>("[data-settings-page-layout]");
195-
if (!container) return;
196-
197-
return observeSettingsSectionVisibility({
198-
container,
199-
targetIds: observedVisibilityScope.pageSections.map((section) => section.targetId),
200-
onChange(targetIds) {
201-
setSectionVisibility({ scope: observedVisibilityScope, targetIds: new Set(targetIds) });
202-
},
203-
});
204-
}, [observedVisibilityScope]);
205114

206115
useEffect(() => {
207116
setActiveResultIndex((index) => Math.min(index, Math.max(results.length - 1, 0)));
@@ -261,24 +170,6 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
261170
},
262171
[isMobile, navigate, setOpenMobile],
263172
);
264-
const handlePageSectionClick = useCallback(
265-
(to: SettingsPath, targetId: string) => {
266-
if (isMobile) {
267-
setOpenMobile(false);
268-
}
269-
if (pathname === to && scrollToSettingsTarget(targetId, { highlight: false })) {
270-
return;
271-
}
272-
void navigate({
273-
to,
274-
hash: targetId,
275-
replace: true,
276-
hashScrollIntoView: false,
277-
state: { settingsTargetHighlight: false },
278-
});
279-
},
280-
[isMobile, navigate, pathname, setOpenMobile],
281-
);
282173
const clearSearch = useCallback(() => {
283174
setQuery("");
284175
setActiveResultIndex(0);
@@ -430,8 +321,7 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
430321
<SidebarMenu className="ps-px">
431322
{SETTINGS_NAV_ITEMS.map((item) => {
432323
const Icon = item.icon;
433-
const pageSections = SETTINGS_PAGE_SECTIONS[item.to];
434-
const isActive = activeSettingsPath === item.to;
324+
const isActive = pathname === item.to || pathname.startsWith(`${item.to}/`);
435325
return (
436326
<SidebarMenuItem key={item.to}>
437327
<SidebarMenuButton
@@ -441,29 +331,6 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
441331
<Icon />
442332
<span className="truncate">{item.label}</span>
443333
</SidebarMenuButton>
444-
{pageSections ? (
445-
<SettingsSubmenuCollapse open={isActive}>
446-
<SidebarMenuSub className="border-l-0">
447-
{pageSections.map((section) => (
448-
<SidebarMenuSubItem key={section.targetId}>
449-
<SidebarMenuSubButton
450-
render={<button type="button" />}
451-
size="sm"
452-
data-visible={visiblePageSectionIds.has(section.targetId)}
453-
className={cn(
454-
"w-full text-sidebar-muted-foreground/65",
455-
visiblePageSectionIds.has(section.targetId) &&
456-
"font-medium text-sidebar-foreground",
457-
)}
458-
onClick={() => handlePageSectionClick(item.to, section.targetId)}
459-
>
460-
<span className="ms-0.5">{section.label}</span>
461-
</SidebarMenuSubButton>
462-
</SidebarMenuSubItem>
463-
))}
464-
</SidebarMenuSub>
465-
</SettingsSubmenuCollapse>
466-
) : null}
467334
</SidebarMenuItem>
468335
);
469336
})}

0 commit comments

Comments
 (0)