Yuki Takei 3 месяцев назад
Родитель
Сommit
1b575694fe

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

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

+ 19 - 4
apps/app/src/features/page-tree/components/ItemsTree.tsx

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