import { useEffect, useRef } from 'react'; import { useLocation } from 'wouter'; // Позиции скролла по пути — живут на уровне модуля, переживают размонтирование страниц const scrollPositions = new Map(); /** * Запоминает scrollTop скролл-контейнера при уходе со страницы и восстанавливает * его при возврате на тот же путь (после рендера, через requestAnimationFrame). * * Скроллящим элементом может быть как сам контейнер (десктоп: overflow-auto), * так и родительский
(мобильный Sidebar) — читаем/пишем оба, берём максимум. * * noScroll-страницы и виртуализированные списки пропускаются через disabled — * они управляют скроллом сами. */ export function useScrollRestoration(disabled?: boolean) { const ref = useRef(null); const [location] = useLocation(); useEffect(() => { const el = ref.current; if (!el || disabled) return; // Ключ включает search, чтобы ?tab=... и подобные варианты страницы не делили позицию const key = location + window.location.search; const main = el.closest('main'); const raf = requestAnimationFrame(() => { const saved = scrollPositions.get(key); if (saved == null) return; el.scrollTop = saved; if (main instanceof HTMLElement) main.scrollTop = saved; }); return () => { cancelAnimationFrame(raf); const top = Math.max(el.scrollTop, main instanceof HTMLElement ? main.scrollTop : 0); scrollPositions.set(key, top); }; }, [location, disabled]); return ref; }