Skip to content

Commit 7746141

Browse files
committed
better ui
1 parent 2c41716 commit 7746141

File tree

2 files changed

+109
-10
lines changed

2 files changed

+109
-10
lines changed

packages/react-router-devtools/src/client/components/network-tracer/NetworkWaterfall.tsx

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ const TYPE_TEXT_COLORS = {
3232
"custom-event": "text-white",
3333
}
3434

35+
type EventType = "loader" | "client-loader" | "action" | "client-action" | "custom-event"
36+
37+
const EVENT_TYPE_FILTERS: { value: EventType | "all"; label: string; color: string }[] = [
38+
{ value: "all", label: "All Events", color: "#ffffff" },
39+
{ value: "loader", label: "Loader", color: "#4ade80" },
40+
{ value: "client-loader", label: "Client Loader", color: "#60a5fa" },
41+
{ value: "action", label: "Action", color: "#f59e0b" },
42+
{ value: "client-action", label: "Client Action", color: "#ef4444" },
43+
{ value: "custom-event", label: "Custom Event", color: "#ffffff" },
44+
]
45+
3546
const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
3647
const containerRef = useRef<HTMLDivElement>(null)
3748
const { styles } = useStyles()
@@ -40,9 +51,14 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
4051
const [dragStart, setDragStart] = useState({ x: 0, scrollLeft: 0 })
4152
const [selectedRequestIndex, setSelectedRequest] = useState<number | null>(null)
4253
const [now, setNow] = useState(Date.now())
54+
const [activeFilter, setActiveFilter] = useState<EventType | "all">("all")
4355
const selectedRequest = selectedRequestIndex !== null ? requests[selectedRequestIndex] : null
56+
57+
// Filter requests based on active filter
58+
const filteredRequests = activeFilter === "all" ? requests : requests.filter((req) => req.type === activeFilter)
59+
4460
// Check if there are any active requests
45-
const hasActiveRequests = requests.some(
61+
const hasActiveRequests = filteredRequests.some(
4662
(req) => !req.endTime || (req.endTime && now - req.endTime < INACTIVE_THRESHOLD)
4763
)
4864
useEffect(() => {
@@ -53,11 +69,11 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
5369
return () => clearInterval(interval)
5470
}, [hasActiveRequests])
5571

56-
const minTime = Math.min(...requests.map((r) => r.startTime))
72+
const minTime = Math.min(...filteredRequests.map((r) => r.startTime))
5773
const maxTime = hasActiveRequests
5874
? now + FUTURE_BUFFER
59-
: requests.length > 0
60-
? Math.max(...requests.map((r) => r.endTime || r.startTime)) + 1000
75+
: filteredRequests.length > 0
76+
? Math.max(...filteredRequests.map((r) => r.endTime || r.startTime)) + 1000
6177
: now
6278
const duration = maxTime - minTime
6379
const pixelsPerMs = scale
@@ -130,17 +146,49 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
130146
if (e.key === "ArrowLeft" && order > 0) {
131147
onChangeRequest(order - 1)
132148
}
133-
if (e.key === "ArrowRight" && order < requests.length - 1) {
149+
if (e.key === "ArrowRight" && order < filteredRequests.length - 1) {
134150
onChangeRequest(order + 1)
135151
}
136152
})
137153
return (
138154
<div className={styles.network.waterfall.container}>
155+
{/* Filter Bar */}
156+
<div className={styles.network.waterfall.filterBar}>
157+
<div className={styles.network.waterfall.filterLabel}>Filter:</div>
158+
<div className={styles.network.waterfall.filterButtons}>
159+
{EVENT_TYPE_FILTERS.map((filter) => (
160+
<button
161+
key={filter.value}
162+
type="button"
163+
className={cx(
164+
styles.network.waterfall.filterButton,
165+
activeFilter === filter.value && styles.network.waterfall.filterButtonActive
166+
)}
167+
style={{
168+
borderColor: activeFilter === filter.value ? filter.color : "transparent",
169+
color: activeFilter === filter.value ? filter.color : "#9ca3af",
170+
}}
171+
onClick={() => setActiveFilter(filter.value)}
172+
>
173+
{filter.label}
174+
{filter.value !== "all" && (
175+
<span className={styles.network.waterfall.filterCount}>
176+
({requests.filter((r) => r.type === filter.value).length})
177+
</span>
178+
)}
179+
{filter.value === "all" && (
180+
<span className={styles.network.waterfall.filterCount}>({requests.length})</span>
181+
)}
182+
</button>
183+
))}
184+
</div>
185+
</div>
186+
139187
<div className={styles.network.waterfall.flexContainer}>
140188
<div>
141189
<div className={styles.network.waterfall.requestsHeader}>Requests</div>
142190
<div style={{ gap: BAR_PADDING }} className={styles.network.waterfall.requestsList}>
143-
{requests.map((request, index) => {
191+
{filteredRequests.map((request, index) => {
144192
const borderColorClass =
145193
request.type === "loader"
146194
? styles.network.waterfall.requestButtonGreen
@@ -208,7 +256,7 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
208256
isDragging ? styles.network.waterfall.scrollContainerGrabbing : styles.network.waterfall.scrollContainerGrab
209257
)}
210258
style={{
211-
height: Math.min(requests.length * (BAR_HEIGHT + BAR_PADDING) + 24, window.innerHeight - 200),
259+
height: Math.min(filteredRequests.length * (BAR_HEIGHT + BAR_PADDING) + 24, window.innerHeight - 200),
212260
}}
213261
onMouseDown={handleMouseDown}
214262
onMouseMove={handleMouseMove}
@@ -229,14 +277,16 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
229277
<span className={styles.network.waterfall.timeLabel}>{i}s</span>
230278
<div
231279
className={styles.network.waterfall.timeDivider}
232-
style={{ height: BAR_HEIGHT * requests.length + 1 + (BAR_PADDING * requests.length + 1) }}
280+
style={{
281+
height: BAR_HEIGHT * filteredRequests.length + 1 + (BAR_PADDING * filteredRequests.length + 1),
282+
}}
233283
/>
234284
</div>
235285
))}
236286
</div>
237287

238288
<AnimatePresence mode="popLayout">
239-
{requests.map((request, index) => (
289+
{filteredRequests.map((request, index) => (
240290
<NetworkBar
241291
key={request.id + request.startTime}
242292
request={request}
@@ -258,7 +308,7 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
258308
<AnimatePresence mode="wait">
259309
<RequestDetails
260310
key={selectedRequest.id + selectedRequest.startTime}
261-
total={requests.length}
311+
total={filteredRequests.length}
262312
index={selectedRequestIndex}
263313
request={selectedRequest}
264314
onChangeRequest={onChangeRequest}

packages/react-router-devtools/src/client/styles/use-styles.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,55 @@ const stylesFactory = (theme: "light" | "dark") => {
15841584
container: css`
15851585
position: relative;
15861586
`,
1587+
filterBar: css`
1588+
display: flex;
1589+
align-items: center;
1590+
gap: 1rem;
1591+
padding: 1rem;
1592+
background: linear-gradient(135deg, #1f2937 0%, #111827 100%);
1593+
border-bottom: 2px solid #4b5563;
1594+
border-radius: 0.5rem 0.5rem 0 0;
1595+
`,
1596+
filterLabel: css`
1597+
font-size: 0.875rem;
1598+
font-weight: 700;
1599+
color: #d1d5db;
1600+
text-transform: uppercase;
1601+
letter-spacing: 0.05em;
1602+
white-space: nowrap;
1603+
`,
1604+
filterButtons: css`
1605+
display: flex;
1606+
flex-wrap: wrap;
1607+
gap: 0.5rem;
1608+
align-items: center;
1609+
`,
1610+
filterButton: css`
1611+
display: flex;
1612+
align-items: center;
1613+
gap: 0.375rem;
1614+
padding: 0.5rem 0.875rem;
1615+
font-size: 0.8125rem;
1616+
font-weight: 600;
1617+
border: 2px solid transparent;
1618+
border-radius: 0.375rem;
1619+
background-color: rgba(31, 41, 55, 0.5);
1620+
transition: all 0.2s;
1621+
cursor: pointer;
1622+
white-space: nowrap;
1623+
&:hover {
1624+
background-color: rgba(31, 41, 55, 0.8);
1625+
}
1626+
`,
1627+
filterButtonActive: css`
1628+
background-color: rgba(31, 41, 55, 1);
1629+
font-weight: 700;
1630+
`,
1631+
filterCount: css`
1632+
font-size: 0.75rem;
1633+
opacity: 0.7;
1634+
font-weight: 500;
1635+
`,
15871636
flexContainer: css`
15881637
display: flex;
15891638
`,

0 commit comments

Comments
 (0)