Перенесён диапазон arm-web 46639b1..8345f5e (9 коммитов, 41 файлов набора синхронизации) скриптом docs/scripts/sync-web.sh. После переноса web/ и arm-web@test идентичны по набору. Коммиты arm-web: - 5b13b74 Форматирование под ESLint + Prettier, зелёный make check (#31) - 681697f fixed eslint bugs - 25d2211 fixed bugs - 2cddb55 Форматирование под ESLint + Prettier и зелёный make check (#31) - 71dae21 updated company group and forms - 83d4c2b added company group table - d2dc230 fixed merge bug - 3977dae added forms for catalogue jur - 8e8e46e addded legalEntities table and move form Co-Authored-By: sync-web.sh <noreply@aepif.ru>
47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
|
|
type Options = {
|
|
/** высота одного элемента в px (фиксированная) */
|
|
itemHeight: number;
|
|
/** высота контейнера в px (видимая область) */
|
|
containerHeight: number;
|
|
/** сколько элементов рендерить дополнительно сверху/снизу */
|
|
overscan?: number;
|
|
};
|
|
|
|
export function useVirtualList<T>(items: T[], options: Options) {
|
|
const { itemHeight, containerHeight, overscan = 5 } = options;
|
|
|
|
const [scrollTop, setScrollTop] = useState(0);
|
|
const ref = useRef<HTMLDivElement | null>(null);
|
|
|
|
const totalHeight = items.length * itemHeight;
|
|
|
|
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
|
|
|
|
const endIndex = Math.min(items.length, Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan);
|
|
|
|
const visibleItems = useMemo(
|
|
() =>
|
|
items.slice(startIndex, endIndex).map((item, i) => ({
|
|
item,
|
|
index: startIndex + i,
|
|
})),
|
|
[items, startIndex, endIndex],
|
|
);
|
|
|
|
const offsetY = startIndex * itemHeight;
|
|
|
|
const onScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
|
|
setScrollTop(e.currentTarget.scrollTop);
|
|
}, []);
|
|
|
|
return {
|
|
ref,
|
|
onScroll,
|
|
visibleItems,
|
|
totalHeight,
|
|
offsetY,
|
|
};
|
|
}
|