Skip to content

Commit 2c41716

Browse files
committed
make ui look much nicer on event tab
1 parent 6b49184 commit 2c41716

File tree

4 files changed

+264
-95
lines changed

4 files changed

+264
-95
lines changed

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

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { animate, motion, useMotionValue } from "framer-motion"
1+
import { motion, useMotionValue } from "framer-motion"
22
import type React from "react"
33
import { useEffect } from "react"
44
import type { RequestEvent } from "../../../shared/request-event"
@@ -40,44 +40,50 @@ export const NetworkBar: React.FC<NetworkBarProps> = ({
4040
}) => {
4141
const { styles } = useStyles()
4242
const startX = (request.startTime - minTime) * pixelsPerMs
43-
const currentEndTime = request.endTime || now
44-
const duration = currentEndTime - request.startTime
4543
const y = index * (barHeight + barPadding) + 24
4644
const state = request.endTime ? "finished" : "pending"
4745

4846
const color =
4947
state === "pending" ? COLORS.pending : COLORS[request.aborted ? "error" : (request.type as keyof typeof COLORS)]
5048

51-
const barWidth = useMotionValue(2)
49+
// For finished requests, use the final width directly without animation
50+
const finalWidth = request.endTime
51+
? Math.max(2, (request.endTime - request.startTime) * pixelsPerMs)
52+
: Math.max(2, (now - request.startTime) * pixelsPerMs)
53+
54+
const barWidth = useMotionValue(finalWidth)
5255

5356
useEffect(() => {
54-
const updateWidth = () => {
55-
if (request.endTime) {
56-
animate(barWidth, Math.max(2, (request.endTime - request.startTime) * pixelsPerMs), {
57-
duration: 0.3,
58-
ease: "easeOut",
59-
})
60-
} else if (isActive) {
61-
barWidth.set(Math.max(2, (now - request.startTime) * pixelsPerMs))
62-
requestAnimationFrame(updateWidth)
57+
// Only animate if the request is not finished
58+
if (!request.endTime && isActive) {
59+
let animationFrameId: number
60+
61+
const updateWidth = () => {
62+
const currentWidth = Math.max(2, (Date.now() - request.startTime) * pixelsPerMs)
63+
barWidth.set(currentWidth)
64+
animationFrameId = requestAnimationFrame(updateWidth)
6365
}
64-
}
6566

66-
if (isActive) {
67-
requestAnimationFrame(updateWidth)
68-
}
67+
animationFrameId = requestAnimationFrame(updateWidth)
6968

70-
if (!isActive) {
71-
barWidth.stop()
69+
return () => {
70+
cancelAnimationFrame(animationFrameId)
71+
barWidth.stop()
72+
}
7273
}
7374

74-
return () => {
75-
barWidth.stop()
75+
// For finished requests, set the width once and never change it
76+
if (request.endTime) {
77+
barWidth.set(finalWidth)
7678
}
77-
}, [request.endTime, request.startTime, pixelsPerMs, now, barWidth, isActive])
79+
}, [request.endTime, request.startTime, pixelsPerMs, barWidth, isActive, finalWidth])
80+
81+
const currentEndTime = request.endTime || now
82+
const duration = currentEndTime - request.startTime
7883

7984
return (
8085
<motion.div
86+
initial={false}
8187
style={{
8288
position: "absolute",
8389
top: y,
@@ -90,11 +96,12 @@ export const NetworkBar: React.FC<NetworkBarProps> = ({
9096
}}
9197
transition={{
9298
x: { duration: 0.3, ease: "easeOut" },
99+
backgroundColor: { duration: 0.2, ease: "easeOut" },
93100
}}
94101
className={styles.network.bar.container}
95102
onClick={(e) => onClick(e, request, index)}
96103
>
97-
{isActive && (
104+
{!request.endTime && (
98105
<motion.div
99106
className={styles.network.bar.shimmer}
100107
animate={{ x: ["-100%", "100%"] }}

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

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
235235
))}
236236
</div>
237237

238-
<AnimatePresence>
238+
<AnimatePresence mode="popLayout">
239239
{requests.map((request, index) => (
240240
<NetworkBar
241241
key={request.id + request.startTime}
@@ -254,19 +254,18 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
254254
</div>
255255
</div>
256256
</div>
257-
<div className={styles.network.waterfall.detailsContainer}>
258-
{selectedRequest && (
259-
<AnimatePresence>
260-
<RequestDetails
261-
total={requests.length}
262-
index={selectedRequestIndex}
263-
request={selectedRequest}
264-
onChangeRequest={onChangeRequest}
265-
onClose={onClose}
266-
/>
267-
</AnimatePresence>
268-
)}
269-
</div>
257+
{selectedRequest && (
258+
<AnimatePresence mode="wait">
259+
<RequestDetails
260+
key={selectedRequest.id + selectedRequest.startTime}
261+
total={requests.length}
262+
index={selectedRequestIndex}
263+
request={selectedRequest}
264+
onChangeRequest={onChangeRequest}
265+
onClose={onClose}
266+
/>
267+
</AnimatePresence>
268+
)}
270269
{/* <div className="sticky top-0 z-10 bg-gray-900 p-2 border-b border-gray-700 flex items-center gap-2">
271270
<button
272271
type="button"

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

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,33 +33,23 @@ export const RequestDetails: React.FC<RequestDetailsProps> = ({ request, onClose
3333
? styles.network.details.typeBadgePurple
3434
: styles.network.details.typeBadgeWhite
3535

36+
const duration = request.endTime ? request.endTime - request.startTime : 0
37+
3638
return (
3739
<div className={styles.network.details.container}>
3840
<div className={styles.network.details.content}>
41+
{/* Header with close button */}
3942
<div className={styles.network.details.header}>
40-
<div className={styles.network.details.headerRow}>
41-
<div className={styles.network.details.tagsContainer}>
42-
{request?.method && (
43-
<Tag className="w-max" color={METHOD_COLORS[request.method]}>
44-
{request.method}
45-
</Tag>
46-
)}
47-
{request?.type && (
48-
<div className={cx(styles.network.details.typeBadge, typeBadgeColorClass)}>{request.type}</div>
49-
)}
50-
{request?.aborted && (
51-
<div className={cx(styles.network.details.typeBadge, styles.network.details.typeBadgeRed)}>
52-
Request aborted
53-
</div>
54-
)}
55-
</div>
43+
<div className={styles.network.details.headerTop}>
44+
<div className={styles.network.details.headerTitle}>Request Details</div>
5645
<div className={styles.network.details.controls}>
5746
<div className={styles.network.details.navButtons}>
5847
{index > 0 ? (
5948
<button
6049
type="button"
6150
onClick={() => onChangeRequest(index - 1)}
6251
className={cx(styles.network.details.navButton, styles.network.details.navButtonLeft)}
52+
title="Previous request"
6353
>
6454
<Icon name="ChevronDown" />
6555
</button>
@@ -69,40 +59,105 @@ export const RequestDetails: React.FC<RequestDetailsProps> = ({ request, onClose
6959
type="button"
7060
onClick={() => onChangeRequest(index + 1)}
7161
className={cx(styles.network.details.navButton, styles.network.details.navButtonRight)}
62+
title="Next request"
7263
>
7364
<Icon name="ChevronDown" />
7465
</button>
7566
) : null}
7667
</div>
77-
<button type="button" className={styles.network.details.closeButton} onClick={onClose}>
68+
<button type="button" className={styles.network.details.closeButton} onClick={onClose} title="Close">
7869
<Icon name="X" />
7970
</button>
8071
</div>
8172
</div>
82-
{request.id} <span className={styles.network.details.title}>- {request.url}</span>
73+
74+
{/* Main request info */}
75+
<div className={styles.network.details.mainInfo}>
76+
<div className={styles.network.details.requestPath}>
77+
<div className={styles.network.details.requestUrl}>{request.url}</div>
78+
<div className={styles.network.details.requestId}>ID: {request.id}</div>
79+
</div>
80+
</div>
8381
</div>
8482

85-
<div>
86-
Request duration: {new Date(request.startTime).toLocaleTimeString()}{" "}
87-
{request.endTime && `- ${new Date(request.endTime).toLocaleTimeString()} `}
83+
{/* Metadata grid */}
84+
<div className={styles.network.details.metadataGrid}>
85+
<div className={styles.network.details.metadataItem}>
86+
<div className={styles.network.details.metadataLabel}>Method</div>
87+
<div className={styles.network.details.metadataValue}>
88+
{request?.method && (
89+
<Tag className="!px-2 !py-1" color={METHOD_COLORS[request.method]}>
90+
{request.method}
91+
</Tag>
92+
)}
93+
</div>
94+
</div>
95+
96+
<div className={styles.network.details.metadataItem}>
97+
<div className={styles.network.details.metadataLabel}>Type</div>
98+
<div className={styles.network.details.metadataValue}>
99+
{request?.type && (
100+
<div className={cx(styles.network.details.typeBadge, typeBadgeColorClass)}>{request.type}</div>
101+
)}
102+
</div>
103+
</div>
104+
105+
<div className={styles.network.details.metadataItem}>
106+
<div className={styles.network.details.metadataLabel}>Duration</div>
107+
<div className={styles.network.details.metadataValue}>
108+
{request.endTime ? (
109+
<span className={styles.network.details.duration}>{duration.toFixed(0)}ms</span>
110+
) : (
111+
<span className={styles.network.details.durationPending}>Pending...</span>
112+
)}
113+
</div>
114+
</div>
115+
116+
<div className={styles.network.details.metadataItem}>
117+
<div className={styles.network.details.metadataLabel}>Started</div>
118+
<div className={styles.network.details.metadataValue}>
119+
{new Date(request.startTime).toLocaleTimeString()}
120+
</div>
121+
</div>
122+
88123
{request.endTime && (
89-
<span className={styles.network.details.duration}>
90-
({(request.endTime - request.startTime).toFixed(0)}ms)
91-
</span>
124+
<div className={styles.network.details.metadataItem}>
125+
<div className={styles.network.details.metadataLabel}>Completed</div>
126+
<div className={styles.network.details.metadataValue}>
127+
{new Date(request.endTime).toLocaleTimeString()}
128+
</div>
129+
</div>
130+
)}
131+
132+
{request?.aborted && (
133+
<div className={styles.network.details.metadataItem}>
134+
<div className={styles.network.details.metadataLabel}>Status</div>
135+
<div className={styles.network.details.metadataValue}>
136+
<div className={cx(styles.network.details.typeBadge, styles.network.details.typeBadgeRed)}>Aborted</div>
137+
</div>
138+
</div>
92139
)}
93140
</div>
94141

142+
{/* Data sections */}
95143
{request.data && (
96144
<div className={styles.network.details.section}>
97-
<div className={styles.network.details.sectionHeader}>Returned Data</div>
145+
<div className={styles.network.details.sectionHeader}>
146+
<Icon name="Layers" />
147+
<span>Response Data</span>
148+
</div>
98149
<div className={styles.network.details.sectionContent}>
99150
<JsonRenderer data={request.data} />
100151
</div>
101152
</div>
102153
)}
154+
103155
{request.headers && Object.keys(request.headers).length > 0 && (
104156
<div className={styles.network.details.section}>
105-
<div className={styles.network.details.sectionHeader}>Headers</div>
157+
<div className={styles.network.details.sectionHeader}>
158+
<Icon name="List" />
159+
<span>Headers</span>
160+
</div>
106161
<div className={styles.network.details.sectionContent}>
107162
<JsonRenderer data={request.headers} />
108163
</div>

0 commit comments

Comments
 (0)