refactor: grid virtualizer

This commit is contained in:
Nathan
2025-11-17 21:15:13 +08:00
parent 0cb82dc740
commit b5acfa8277

View File

@@ -1,5 +1,5 @@
import { useVirtualizer } from '@tanstack/react-virtual';
import { useCallback, useLayoutEffect, useRef } from 'react';
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { PADDING_END, useDatabaseContext } from '@/application/database-yjs';
import { RenderColumn } from '@/components/database/components/grid/grid-column';
@@ -11,17 +11,32 @@ const MIN_HEIGHT = 36;
export const PADDING_INLINE = getPlatform().isMobile ? 21 : 96;
const logDebug = (...args: Parameters<typeof console.debug>) => {
if (import.meta.env.DEV) {
console.debug(...args);
}
};
export function useGridVirtualizer({ data, columns }: { columns: RenderColumn[]; data: RenderRow[] }) {
const { isDocumentBlock } = useDatabaseContext();
const { isDocumentBlock, paddingStart, paddingEnd } = useDatabaseContext();
const parentRef = useRef<HTMLDivElement | null>(null);
const parentOffsetRef = useRef(0);
const [parentOffset, setParentOffset] = useState(0);
const updateParentOffset = useCallback(() => {
if (parentRef.current) {
parentOffsetRef.current = parentRef.current.getBoundingClientRect()?.top ?? 0;
}
}, []);
if (!parentRef.current) return;
const nextOffset = parentRef.current.getBoundingClientRect()?.top ?? 0;
if (nextOffset === parentOffsetRef.current) return;
parentOffsetRef.current = nextOffset;
setParentOffset(nextOffset);
logDebug('[GridVirtualizer] parent offset updated', {
nextOffset,
scrollMarginBeforeRender: parentOffset,
});
}, [parentOffset]);
useLayoutEffect(() => {
updateParentOffset();
@@ -36,7 +51,7 @@ export function useGridVirtualizer({ data, columns }: { columns: RenderColumn[];
count: data.length,
estimateSize: () => MIN_HEIGHT,
overscan: 10,
scrollMargin: parentOffsetRef.current,
scrollMargin: parentOffset,
getScrollElement,
getItemKey: (index) => data[index].rowId || data[index].type,
paddingStart: 0,
@@ -47,20 +62,42 @@ export function useGridVirtualizer({ data, columns }: { columns: RenderColumn[];
useLayoutEffect(() => {
const scrollElement = getScrollElement();
if (!scrollElement || !isDocumentBlock) return;
if (!scrollElement || !isDocumentBlock) {
logDebug('[GridVirtualizer] skip observing resize', {
hasScrollElement: !!scrollElement,
isDocumentBlock,
});
return;
}
scrollElement.addEventListener('resize', updateParentOffset);
logDebug('[GridVirtualizer] observing scroll element for resize', {
tagName: scrollElement.tagName,
className: scrollElement.className,
});
const observer = new ResizeObserver((entries) => {
updateParentOffset();
logDebug('[GridVirtualizer] resize observed; recalculating offset', {
entries: entries.map((entry) => ({
target: entry.target instanceof HTMLElement ? entry.target.tagName : 'unknown',
width: entry.contentRect.width,
height: entry.contentRect.height,
})),
});
});
observer.observe(scrollElement);
updateParentOffset();
return () => {
scrollElement.removeEventListener('resize', updateParentOffset);
observer.disconnect();
logDebug('[GridVirtualizer] resize observer disconnected');
};
}, [getScrollElement, updateParentOffset, isDocumentBlock]);
const getColumn = useCallback((index: number) => columns[index], [columns]);
const getColumnWidth = useCallback((index: number) => getColumn(index).width, [getColumn]);
const { paddingStart, paddingEnd } = useDatabaseContext();
const columnVirtualizer = useVirtualizer({
horizontal: true,
count: columns.length,
@@ -76,6 +113,6 @@ export function useGridVirtualizer({ data, columns }: { columns: RenderColumn[];
parentRef,
virtualizer,
columnVirtualizer,
scrollMarginTop: parentOffsetRef.current,
scrollMarginTop: parentOffset,
};
}