@@ -16,8 +16,8 @@ interface Props {
1616 width : number
1717}
1818
19- const BAR_HEIGHT = 20
20- const BAR_PADDING = 8
19+ const BAR_HEIGHT = 16
20+ const BAR_PADDING = 6
2121const TIME_COLUMN_INTERVAL = 1000 // 1 second
2222const _MIN_SCALE = 0.1
2323const _MAX_SCALE = 10
@@ -51,11 +51,25 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
5151 const [ dragStart , setDragStart ] = useState ( { x : 0 , scrollLeft : 0 } )
5252 const [ selectedRequestIndex , setSelectedRequest ] = useState < number | null > ( null )
5353 const [ now , setNow ] = useState ( Date . now ( ) )
54- const [ activeFilter , setActiveFilter ] = useState < EventType | "all" > ( "all" )
54+ const [ activeTypeFilter , setActiveTypeFilter ] = useState < EventType | "all" > ( "all" )
55+ const [ activeRouteFilters , setActiveRouteFilters ] = useState < Set < string > > ( new Set ( ) )
5556 const selectedRequest = selectedRequestIndex !== null ? requests [ selectedRequestIndex ] : null
5657
57- // Filter requests based on active filter
58- const filteredRequests = activeFilter === "all" ? requests : requests . filter ( ( req ) => req . type === activeFilter )
58+ // Get unique routes from all requests
59+ const uniqueRoutes = Array . from ( new Set ( requests . map ( ( req ) => req . routeId ) ) ) . sort ( )
60+
61+ // Filter requests based on active filters
62+ let filteredRequests = requests
63+
64+ // Apply type filter
65+ if ( activeTypeFilter !== "all" ) {
66+ filteredRequests = filteredRequests . filter ( ( req ) => req . type === activeTypeFilter )
67+ }
68+
69+ // Apply route filters (if any selected)
70+ if ( activeRouteFilters . size > 0 ) {
71+ filteredRequests = filteredRequests . filter ( ( req ) => activeRouteFilters . has ( req . routeId ) )
72+ }
5973
6074 // Check if there are any active requests
6175 const hasActiveRequests = filteredRequests . some (
@@ -150,25 +164,42 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
150164 onChangeRequest ( order + 1 )
151165 }
152166 } )
167+
168+ const toggleRouteFilter = ( route : string ) => {
169+ setActiveRouteFilters ( ( prev ) => {
170+ const newFilters = new Set ( prev )
171+ if ( newFilters . has ( route ) ) {
172+ newFilters . delete ( route )
173+ } else {
174+ newFilters . add ( route )
175+ }
176+ return newFilters
177+ } )
178+ }
179+
180+ const clearRouteFilters = ( ) => {
181+ setActiveRouteFilters ( new Set ( ) )
182+ }
183+
153184 return (
154185 < div className = { styles . network . waterfall . container } >
155- { /* Filter Bar */ }
186+ { /* Type Filter Bar */ }
156187 < div className = { styles . network . waterfall . filterBar } >
157- < div className = { styles . network . waterfall . filterLabel } > Filter :</ div >
188+ < div className = { styles . network . waterfall . filterLabel } > Type :</ div >
158189 < div className = { styles . network . waterfall . filterButtons } >
159190 { EVENT_TYPE_FILTERS . map ( ( filter ) => (
160191 < button
161192 key = { filter . value }
162193 type = "button"
163194 className = { cx (
164195 styles . network . waterfall . filterButton ,
165- activeFilter === filter . value && styles . network . waterfall . filterButtonActive
196+ activeTypeFilter === filter . value && styles . network . waterfall . filterButtonActive
166197 ) }
167198 style = { {
168- borderColor : activeFilter === filter . value ? filter . color : "transparent" ,
169- color : activeFilter === filter . value ? filter . color : "#9ca3af" ,
199+ borderColor : activeTypeFilter === filter . value ? filter . color : "transparent" ,
200+ color : activeTypeFilter === filter . value ? filter . color : "#9ca3af" ,
170201 } }
171- onClick = { ( ) => setActiveFilter ( filter . value ) }
202+ onClick = { ( ) => setActiveTypeFilter ( filter . value ) }
172203 >
173204 { filter . label }
174205 { filter . value !== "all" && (
@@ -184,6 +215,69 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
184215 </ div >
185216 </ div >
186217
218+ { /* Route Filter Bar */ }
219+ < div className = { styles . network . waterfall . filterBar } >
220+ < div className = { styles . network . waterfall . filterLabel } >
221+ Routes:
222+ { activeRouteFilters . size > 0 && (
223+ < span className = { styles . network . waterfall . filterCount } style = { { marginLeft : "0.25rem" } } >
224+ ({ activeRouteFilters . size } selected)
225+ </ span >
226+ ) }
227+ </ div >
228+ < div className = { styles . network . waterfall . filterButtons } >
229+ < button
230+ type = "button"
231+ className = { cx (
232+ styles . network . waterfall . filterButton ,
233+ activeRouteFilters . size === 0 && styles . network . waterfall . filterButtonActive
234+ ) }
235+ style = { {
236+ borderColor : activeRouteFilters . size === 0 ? "#ffffff" : "transparent" ,
237+ color : activeRouteFilters . size === 0 ? "#ffffff" : "#9ca3af" ,
238+ } }
239+ onClick = { clearRouteFilters }
240+ >
241+ All Routes
242+ < span className = { styles . network . waterfall . filterCount } > ({ uniqueRoutes . length } )</ span >
243+ </ button >
244+ { uniqueRoutes . map ( ( route ) => {
245+ const isActive = activeRouteFilters . has ( route )
246+ const routeCount = requests . filter ( ( r ) => r . routeId === route ) . length
247+ return (
248+ < button
249+ key = { route }
250+ type = "button"
251+ className = { cx (
252+ styles . network . waterfall . filterButton ,
253+ isActive && styles . network . waterfall . filterButtonActive
254+ ) }
255+ style = { {
256+ borderColor : isActive ? "#60a5fa" : "transparent" ,
257+ color : isActive ? "#60a5fa" : "#9ca3af" ,
258+ } }
259+ onClick = { ( ) => toggleRouteFilter ( route ) }
260+ >
261+ { route }
262+ < span className = { styles . network . waterfall . filterCount } > ({ routeCount } )</ span >
263+ </ button >
264+ )
265+ } ) }
266+ </ div >
267+ </ div >
268+
269+ { /* Results summary */ }
270+ { ( activeTypeFilter !== "all" || activeRouteFilters . size > 0 ) && (
271+ < div className = { styles . network . waterfall . filterSummary } >
272+ Showing { filteredRequests . length } of { requests . length } events
273+ { activeRouteFilters . size > 0 && (
274+ < button type = "button" className = { styles . network . waterfall . clearFiltersButton } onClick = { clearRouteFilters } >
275+ Clear route filters
276+ </ button >
277+ ) }
278+ </ div >
279+ ) }
280+
187281 < div className = { styles . network . waterfall . flexContainer } >
188282 < div >
189283 < div className = { styles . network . waterfall . requestsHeader } > Requests</ div >
0 commit comments