import * as TabsPrimitive from "@radix-ui/react-tabs"; import { useAtom } from "jotai"; import { useEffect, useLayoutEffect } from "react"; import { isAtTopAtom, reusableTabRouteScrollAtom } from "~/utils/atoms"; /** * Please wrap your Route in a div, do not return a top-level fragment, * it will break navigation scroll restoration */ export function ReusableTabRoute({ route, tabs, }: { route: string; tabs: Record; }) { const [reusableTabState, setReusableTabState] = useAtom( reusableTabRouteScrollAtom ); const [isAtTop] = useAtom(isAtTopAtom); const routeState = reusableTabState?.[route] ?? { activeTab: Object.keys(tabs)[0], scrollPositions: {}, }; const activeTab = routeState.activeTab; const handleValueChange = (newTab: string) => { setReusableTabState((prev) => { const current = prev?.[route] ?? routeState; return { ...prev, [route]: { ...current, scrollPositions: { ...current.scrollPositions, [current.activeTab]: window.scrollY, }, activeTab: newTab, }, }; }); }; // // todo, warning experimental, usually this doesnt work, // // like at all, and i usually do this for each tab // useLayoutEffect(() => { // const savedScroll = routeState.scrollPositions[activeTab] ?? 0; // window.scrollTo({ top: savedScroll }); // // eslint-disable-next-line react-hooks/exhaustive-deps // }, [activeTab, route]); useLayoutEffect(() => { return () => { setReusableTabState((prev) => { const current = prev?.[route] ?? routeState; return { ...prev, [route]: { ...current, scrollPositions: { ...current.scrollPositions, [current.activeTab]: window.scrollY, }, }, }; }); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( {Object.entries(tabs).map(([key]) => ( {key} ))} {Object.entries(tabs).map(([key, node]) => ( {activeTab === key && node} ))} ); } export function useReusableTabScrollRestore(route: string) { const [reusableTabState] = useAtom( reusableTabRouteScrollAtom ); const routeState = reusableTabState?.[route]; const activeTab = routeState?.activeTab; useEffect(() => { const savedScroll = activeTab ? routeState?.scrollPositions[activeTab] ?? 0 : 0; //window.scrollTo(0, savedScroll); window.scrollTo({ top: savedScroll }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); } /* const [notifState] = useAtom(notificationsScrollAtom); const activeTab = notifState.activeTab; useEffect(() => { const savedY = notifState.scrollPositions[activeTab] ?? 0; window.scrollTo(0, savedY); }, [activeTab, notifState.scrollPositions]); */