From b5acfa8277081065cc5dc95c9f552bd2cbde1534 Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 17 Nov 2025 21:15:13 +0800 Subject: [PATCH] refactor: grid virtualizer --- .../grid/grid-table/useGridVirtualizer.ts | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/src/components/database/components/grid/grid-table/useGridVirtualizer.ts b/src/components/database/components/grid/grid-table/useGridVirtualizer.ts index fa0c6a12..6e5b208f 100644 --- a/src/components/database/components/grid/grid-table/useGridVirtualizer.ts +++ b/src/components/database/components/grid/grid-table/useGridVirtualizer.ts @@ -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) => { + 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(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, }; }