Files
iistwin/client/src/hooks/useScrollRestoration.ts

44 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useRef } from 'react';
import { useLocation } from 'wouter';
// Позиции скролла по пути — живут на уровне модуля, переживают размонтирование страниц
const scrollPositions = new Map<string, number>();
/**
* Запоминает scrollTop скролл-контейнера при уходе со страницы и восстанавливает
* его при возврате на тот же путь (после рендера, через requestAnimationFrame).
*
* Скроллящим элементом может быть как сам контейнер (десктоп: overflow-auto),
* так и родительский <main> (мобильный Sidebar) — читаем/пишем оба, берём максимум.
*
* noScroll-страницы и виртуализированные списки пропускаются через disabled —
* они управляют скроллом сами.
*/
export function useScrollRestoration<T extends HTMLElement>(disabled?: boolean) {
const ref = useRef<T>(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;
}