diff --git a/frontend/web/src/admin/components.tsx b/frontend/web/src/admin/components.tsx index 002057d..700dbe3 100644 --- a/frontend/web/src/admin/components.tsx +++ b/frontend/web/src/admin/components.tsx @@ -1,4 +1,4 @@ -import { useMemo, type ReactNode } from "react"; +import { useMemo, type ReactNode, type RefObject } from "react"; import { EmptyState, Tag, type TagTone } from "../components/primitives"; import { Icon } from "../components/Icon"; import { useVirtualRows, VIRTUAL_ROW_ESTIMATE } from "./hooks"; @@ -198,6 +198,9 @@ export interface VirtualListProps { /** 少于该行数时全量渲染(与 `AdminTable` 同口径)。 */ threshold?: number; className?: string; + /** 外部滚动容器 ref:窗口化时调用方要在同一容器上做滚动锚定(「顶部前插不顶跑 + * 阅读位置」)时传进来;非窗口化时不生效(那种渲染没有滚动容器)。 */ + containerRef?: RefObject; } /** 窗口化的长列表:日志 / 消息流这类「条数多、还会被 WS 持续追帧」的面用它, @@ -212,10 +215,16 @@ export function VirtualList({ rowClassName = "", threshold = VIRTUAL_THRESHOLD, className = "", + containerRef, }: VirtualListProps): ReactNode { const count = items.length; const windowed = count > threshold; - const { range, containerRef, rowAttrs } = useVirtualRows({ count, enabled: windowed, rowHeight }); + const { range, containerRef: innerRef, rowAttrs } = useVirtualRows({ + count, + enabled: windowed, + rowHeight, + containerRef, + }); if (!windowed) { return ( @@ -231,7 +240,7 @@ export function VirtualList({ const from = range.start; return ( -
+