Просмотр исходного кода

Merge pull request #10589 from growilabs/fix/page-tree-virtualization

fix: PageTree Virtualization is measuring size incorrectly
Yuki Takei 3 месяцев назад
Родитель
Сommit
f9af72675e

+ 4 - 2
apps/app/src/client/components/Sidebar/PageTree/PageTreeSubstance.tsx

@@ -4,6 +4,7 @@ import React, {
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
+import { ItemsTree } from '~/features/page-tree/components';
 import { usePageTreeInformationUpdate } from '~/features/page-tree/states/page-tree-update';
 import { usePageTreeInformationUpdate } from '~/features/page-tree/states/page-tree-update';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { useCurrentPageId, useCurrentPagePath } from '~/states/page';
 import { useCurrentPageId, useCurrentPagePath } from '~/states/page';
@@ -13,7 +14,6 @@ import {
 } from '~/stores/page-listing';
 } from '~/stores/page-listing';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
-import { ItemsTree } from '~/features/page-tree/components';
 import { PageTreeItem, pageTreeItemSize } from '../PageTreeItem';
 import { PageTreeItem, pageTreeItemSize } from '../PageTreeItem';
 import { SidebarHeaderReloadButton } from '../SidebarHeaderReloadButton';
 import { SidebarHeaderReloadButton } from '../SidebarHeaderReloadButton';
 
 
@@ -108,6 +108,8 @@ export const PageTreeContent = memo(({ isWipPageShown }: PageTreeContentProps) =
 
 
   const sidebarScrollerElem = useSidebarScrollerElem();
   const sidebarScrollerElem = useSidebarScrollerElem();
 
 
+  const estimateTreeItemSize = useCallback(() => pageTreeItemSize, []);
+
   if (!migrationStatus?.isV5Compatible) {
   if (!migrationStatus?.isV5Compatible) {
     return <PageTreeUnavailable />;
     return <PageTreeUnavailable />;
   }
   }
@@ -130,7 +132,7 @@ export const PageTreeContent = memo(({ isWipPageShown }: PageTreeContentProps) =
         targetPath={path}
         targetPath={path}
         targetPathOrId={targetPathOrId}
         targetPathOrId={targetPathOrId}
         CustomTreeItem={PageTreeItem}
         CustomTreeItem={PageTreeItem}
-        estimateTreeItemSize={() => pageTreeItemSize}
+        estimateTreeItemSize={estimateTreeItemSize}
         scrollerElem={sidebarScrollerElem}
         scrollerElem={sidebarScrollerElem}
       />
       />
 
 

+ 33 - 5
apps/app/src/features/page-tree/components/ItemsTree.tsx

@@ -168,18 +168,40 @@ export const ItemsTree: FC<Props> = (props: Props) => {
     onExpanded: triggerTreeRebuild,
     onExpanded: triggerTreeRebuild,
   });
   });
 
 
+  const getScrollElement = useCallback(
+    () => scrollerElem ?? null,
+    [scrollerElem],
+  );
+
+  const stableEstimateSize = useCallback(() => {
+    return estimateTreeItemSize();
+  }, [estimateTreeItemSize]);
+
+  const measureElement = useCallback(
+    (element: Element | null) => {
+      // Return consistent height measurement
+      return element?.getBoundingClientRect().height ?? stableEstimateSize();
+    },
+    [stableEstimateSize],
+  );
+
   const virtualizer = useVirtualizer({
   const virtualizer = useVirtualizer({
     count: items.length,
     count: items.length,
-    getScrollElement: () => scrollerElem ?? null,
-    estimateSize: estimateTreeItemSize,
-    overscan: 5,
+    getScrollElement,
+    estimateSize: stableEstimateSize,
+    overscan: 10,
+    measureElement,
   });
   });
 
 
   // Scroll to selected item on mount or when targetPathOrId changes
   // Scroll to selected item on mount or when targetPathOrId changes
   useScrollToSelectedItem({ targetPathOrId, items, virtualizer });
   useScrollToSelectedItem({ targetPathOrId, items, virtualizer });
 
 
   return (
   return (
-    <div {...tree.getContainerProps()} className="list-group">
+    <div
+      {...tree.getContainerProps()}
+      className="list-group position-relative"
+      style={{ height: `${virtualizer.getTotalSize()}px` }}
+    >
       {virtualizer.getVirtualItems().map((virtualItem) => {
       {virtualizer.getVirtualItems().map((virtualItem) => {
         const item = items[virtualItem.index];
         const item = items[virtualItem.index];
         const itemData = item.getItemData();
         const itemData = item.getItemData();
@@ -202,8 +224,14 @@ export const ItemsTree: FC<Props> = (props: Props) => {
           <div
           <div
             key={virtualItem.key}
             key={virtualItem.key}
             data-index={virtualItem.index}
             data-index={virtualItem.index}
+            style={{
+              position: 'absolute',
+              top: 0,
+              left: 0,
+              width: '100%',
+              transform: `translateY(${virtualItem.start}px)`,
+            }}
             ref={(node) => {
             ref={(node) => {
-              virtualizer.measureElement(node);
               if (node && itemRef) {
               if (node && itemRef) {
                 (itemRef as (node: HTMLElement) => void)(node);
                 (itemRef as (node: HTMLElement) => void)(node);
               }
               }