Răsfoiți Sursa

fix: preserve scroll position when switching tabs in settings (#7587)

* fix: preserve scroll position when switching tabs in settings

* ui(settings): restore scroll synchronously to prevent flicker; dx(ui): name TabContent for clearer DevTools

---------

Co-authored-by: Daniel Riccio <[email protected]>
淡草 4 luni în urmă
părinte
comite
1f5ee4307e

+ 4 - 3
webview-ui/src/components/common/Tab.tsx

@@ -17,7 +17,7 @@ export const TabHeader = ({ className, children, ...props }: TabProps) => (
 	</div>
 	</div>
 )
 )
 
 
-export const TabContent = ({ className, children, ...props }: TabProps) => {
+export const TabContent = forwardRef<HTMLDivElement, TabProps>(({ className, children, ...props }, ref) => {
 	const { renderContext } = useExtensionState()
 	const { renderContext } = useExtensionState()
 
 
 	const onWheel = useCallback(
 	const onWheel = useCallback(
@@ -40,11 +40,12 @@ export const TabContent = ({ className, children, ...props }: TabProps) => {
 	)
 	)
 
 
 	return (
 	return (
-		<div className={cn("flex-1 overflow-auto p-5", className)} onWheel={onWheel} {...props}>
+		<div ref={ref} className={cn("flex-1 overflow-auto p-5", className)} onWheel={onWheel} {...props}>
 			{children}
 			{children}
 		</div>
 		</div>
 	)
 	)
-}
+})
+TabContent.displayName = "TabContent"
 
 
 export const TabList = forwardRef<
 export const TabList = forwardRef<
 	HTMLDivElement,
 	HTMLDivElement,

+ 16 - 3
webview-ui/src/components/settings/SettingsView.tsx

@@ -112,6 +112,11 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 			: "providers",
 			: "providers",
 	)
 	)
 
 
+	const scrollPositions = useRef<Record<SectionName, number>>(
+		Object.fromEntries(sectionNames.map((s) => [s, 0])) as Record<SectionName, number>,
+	)
+	const contentRef = useRef<HTMLDivElement | null>(null)
+
 	const prevApiConfigName = useRef(currentApiConfigName)
 	const prevApiConfigName = useRef(currentApiConfigName)
 	const confirmDialogHandler = useRef<() => void>()
 	const confirmDialogHandler = useRef<() => void>()
 
 
@@ -398,12 +403,20 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 	// Handle tab changes with unsaved changes check
 	// Handle tab changes with unsaved changes check
 	const handleTabChange = useCallback(
 	const handleTabChange = useCallback(
 		(newTab: SectionName) => {
 		(newTab: SectionName) => {
-			// Directly switch tab without checking for unsaved changes
+			if (contentRef.current) {
+				scrollPositions.current[activeTab] = contentRef.current.scrollTop
+			}
 			setActiveTab(newTab)
 			setActiveTab(newTab)
 		},
 		},
-		[], // No dependency on isChangeDetected needed anymore
+		[activeTab],
 	)
 	)
 
 
+	useLayoutEffect(() => {
+		if (contentRef.current) {
+			contentRef.current.scrollTop = scrollPositions.current[activeTab] ?? 0
+		}
+	}, [activeTab])
+
 	// Store direct DOM element refs for each tab
 	// Store direct DOM element refs for each tab
 	const tabRefs = useRef<Record<SectionName, HTMLButtonElement | null>>(
 	const tabRefs = useRef<Record<SectionName, HTMLButtonElement | null>>(
 		Object.fromEntries(sectionNames.map((name) => [name, null])) as Record<SectionName, HTMLButtonElement | null>,
 		Object.fromEntries(sectionNames.map((name) => [name, null])) as Record<SectionName, HTMLButtonElement | null>,
@@ -579,7 +592,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 				</TabList>
 				</TabList>
 
 
 				{/* Content area */}
 				{/* Content area */}
-				<TabContent className="p-0 flex-1 overflow-auto">
+				<TabContent ref={contentRef} className="p-0 flex-1 overflow-auto">
 					{/* Providers Section */}
 					{/* Providers Section */}
 					{activeTab === "providers" && (
 					{activeTab === "providers" && (
 						<div>
 						<div>