33import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation" ;
44import { FileHeader } from "@/app/[domain]/components/fileHeader" ;
55import { LightweightCodeHighlighter } from "@/app/[domain]/components/lightweightCodeHighlighter" ;
6- import { ScrollArea } from "@/components/ui/scroll-area" ;
76import { FindRelatedSymbolsResponse } from "@/features/codeNav/types" ;
87import { RepositoryInfo , SourceRange } from "@/features/search/types" ;
98import { base64Decode } from "@/lib/utils" ;
10- import { useMemo } from "react" ;
9+ import { useMemo , useRef } from "react" ;
1110import useCaptureEvent from "@/hooks/useCaptureEvent" ;
11+ import { useVirtualizer } from "@tanstack/react-virtual" ;
1212
1313interface ReferenceListProps {
1414 data : FindRelatedSymbolsResponse ;
1515 revisionName : string ;
1616}
1717
18+ const ESTIMATED_LINE_HEIGHT_PX = 30 ;
19+ const ESTIMATED_MATCH_CONTAINER_HEIGHT_PX = 30 ;
20+
1821export const ReferenceList = ( {
1922 data,
2023 revisionName,
@@ -29,52 +32,102 @@ export const ReferenceList = ({
2932 const { navigateToPath } = useBrowseNavigation ( ) ;
3033 const captureEvent = useCaptureEvent ( ) ;
3134
32- return (
33- < ScrollArea className = "h-full" >
34- { data . files . map ( ( file , index ) => {
35- const repoInfo = repoInfoMap [ file . repositoryId ] ;
35+ // Virtualization setup
36+ const parentRef = useRef < HTMLDivElement > ( null ) ;
37+ const virtualizer = useVirtualizer ( {
38+ count : data . files . length ,
39+ getScrollElement : ( ) => parentRef . current ,
40+ estimateSize : ( index ) => {
41+ const file = data . files [ index ] ;
42+
43+ const estimatedSize =
44+ file . matches . length * ESTIMATED_LINE_HEIGHT_PX +
45+ ESTIMATED_MATCH_CONTAINER_HEIGHT_PX ;
46+
47+ return estimatedSize ;
48+ } ,
49+ overscan : 5 ,
50+ enabled : true ,
51+ } ) ;
3652
37- return (
38- < div key = { index } >
39- < div className = "bg-accent py-1 px-2 flex flex-row sticky top-0" >
40- < FileHeader
41- repo = { {
42- name : repoInfo . name ,
43- displayName : repoInfo . displayName ,
44- codeHostType : repoInfo . codeHostType ,
45- webUrl : repoInfo . webUrl ,
53+ return (
54+ < div
55+ ref = { parentRef }
56+ style = { {
57+ width : "100%" ,
58+ height : "100%" ,
59+ overflowY : "auto" ,
60+ contain : "strict" ,
61+ } }
62+ >
63+ < div
64+ style = { {
65+ height : virtualizer . getTotalSize ( ) ,
66+ width : "100%" ,
67+ position : "relative" ,
68+ } }
69+ >
70+ { virtualizer . getVirtualItems ( ) . map ( ( virtualRow ) => {
71+ const file = data . files [ virtualRow . index ] ;
72+ const repoInfo = repoInfoMap [ file . repositoryId ] ;
73+ return (
74+ < div
75+ key = { virtualRow . key }
76+ data-index = { virtualRow . index }
77+ ref = { virtualizer . measureElement }
78+ style = { {
79+ position : "absolute" ,
80+ transform : `translateY(${ virtualRow . start } px)` ,
81+ top : 0 ,
82+ left : 0 ,
83+ width : "100%" ,
84+ } }
85+ >
86+ < div
87+ className = "bg-accent py-1 px-2 flex flex-row sticky top-0 z-10"
88+ style = { {
89+ top : `-${ virtualRow . start } px` ,
4690 } }
47- fileName = { file . fileName }
48- branchDisplayName = { revisionName === "HEAD" ? undefined : revisionName }
49- />
50- </ div >
51- < div className = "divide-y" >
52- { file . matches
53- . sort ( ( a , b ) => a . range . start . lineNumber - b . range . start . lineNumber )
54- . map ( ( match , index ) => (
55- < ReferenceListItem
56- key = { index }
57- lineContent = { match . lineContent }
58- range = { match . range }
59- language = { file . language }
60- onClick = { ( ) => {
61- captureEvent ( 'wa_explore_menu_reference_clicked' , { } ) ;
62- navigateToPath ( {
63- repoName : file . repository ,
64- revisionName,
65- path : file . fileName ,
66- pathType : 'blob' ,
67- highlightRange : match . range ,
68- } )
69- } }
70- />
71- ) ) }
91+ >
92+ < FileHeader
93+ repo = { {
94+ name : repoInfo . name ,
95+ displayName : repoInfo . displayName ,
96+ codeHostType : repoInfo . codeHostType ,
97+ webUrl : repoInfo . webUrl ,
98+ } }
99+ fileName = { file . fileName }
100+ branchDisplayName = { revisionName === "HEAD" ? undefined : revisionName }
101+ />
102+ </ div >
103+ < div className = "divide-y" >
104+ { file . matches
105+ . sort ( ( a , b ) => a . range . start . lineNumber - b . range . start . lineNumber )
106+ . map ( ( match , index ) => (
107+ < ReferenceListItem
108+ key = { index }
109+ lineContent = { match . lineContent }
110+ range = { match . range }
111+ language = { file . language }
112+ onClick = { ( ) => {
113+ captureEvent ( 'wa_explore_menu_reference_clicked' , { } ) ;
114+ navigateToPath ( {
115+ repoName : file . repository ,
116+ revisionName,
117+ path : file . fileName ,
118+ pathType : 'blob' ,
119+ highlightRange : match . range ,
120+ } )
121+ } }
122+ />
123+ ) ) }
124+ </ div >
72125 </ div >
73- </ div >
74- )
75- } ) }
76- </ ScrollArea >
77- )
126+ ) ;
127+ } ) }
128+ </ div >
129+ </ div >
130+ ) ;
78131}
79132
80133
0 commit comments