@@ -2,12 +2,16 @@ import type { UIMatch } from "react-router"
22import { parseCacheControlHeader } from "../../server/parser.js"
33import { type ServerRouteInfo , defaultServerRouteState } from "../context/rdtReducer.js"
44import { useServerInfo , useSettingsContext } from "../context/useRDTContext.js"
5- import { cx , useStyles } from "../styles/use-styles.js"
5+ import { useStyles } from "../styles/use-styles.js"
66import { openSource } from "../utils/open-source.js"
77import { isLayoutRoute } from "../utils/routing.js"
88import { CacheInfo } from "./CacheInfo.js"
99import { EditorButton } from "./EditorButton.js"
1010import { InfoCard } from "./InfoCard.js"
11+ import { RouteSegmentBody } from "./RouteSegmentBody.js"
12+ import { RouteSegmentCard } from "./RouteSegmentCard.js"
13+ import { type RouteColor , RouteSegmentHeader } from "./RouteSegmentHeader.js"
14+ import { Tag } from "./Tag.js"
1115import { Icon } from "./icon/Icon.js"
1216import { JsonRenderer } from "./jsonRenderer.js"
1317
@@ -60,14 +64,6 @@ const cleanServerInfo = (routeInfo: ServerRouteInfo) => {
6064 }
6165}
6266
63- const ROUTE_COLORS = {
64- GREEN : "green" ,
65- BLUE : "blue" ,
66- TEAL : "teal" ,
67- RED : "red" ,
68- PURPLE : "purple" ,
69- } as const
70-
7167export const RouteSegmentInfo = ( { route, i } : { route : UIMatch < unknown , unknown > ; i : number } ) => {
7268 const { styles } = useStyles ( )
7369 const { server, setServerInfo } = useServerInfo ( )
@@ -98,83 +94,93 @@ export const RouteSegmentInfo = ({ route, i }: { route: UIMatch<unknown, unknown
9894 setServerInfo ( { routes : newServerInfo } )
9995 }
10096
101- const routeColor = isRoot ? ROUTE_COLORS . PURPLE : isLayout ? ROUTE_COLORS . BLUE : ROUTE_COLORS . GREEN
97+ const routeColor : RouteColor = isRoot ? "purple" : isLayout ? "blue" : "green"
98+ const iconName : "Root" | "Layout" | "CornerDownRight" = isRoot ? "Root" : isLayout ? "Layout" : "CornerDownRight"
99+
100+ const headerActions = (
101+ < div className = { styles . routeSegmentCard . headerActions } >
102+ { isDev && import . meta. env . DEV && entryRoute ?. module && (
103+ < EditorButton
104+ data-testid = { `${ route . id } -open-source` }
105+ onClick = { ( ) => {
106+ openSource ( `${ entryRoute . module } ` )
107+ } }
108+ />
109+ ) }
110+ { settings . showRouteBoundariesOn === "click" && (
111+ < button
112+ type = "button"
113+ data-testid = { `${ route . id } -show-route-boundaries` }
114+ className = { styles . routeSegmentInfo . showBoundaryButton }
115+ onClick = { ( ) => {
116+ const routeId = route . id === "root" ? "root" : i . toString ( )
117+ if ( routeId !== settings . hoveredRoute ) {
118+ // Remove the classes from the old hovered route
119+ setSettings ( {
120+ isHoveringRoute : false ,
121+ } )
122+ // Add the classes to the new hovered route
123+ setTimeout ( ( ) => {
124+ setSettings ( {
125+ hoveredRoute : routeId ,
126+ isHoveringRoute : true ,
127+ } )
128+ } )
129+ } else {
130+ // Just change the isHoveringRoute state
131+ setSettings ( {
132+ isHoveringRoute : ! settings . isHoveringRoute ,
133+ } )
134+ }
135+ } }
136+ >
137+ < Icon name = "Radio" size = "sm" />
138+ Show Route Boundary
139+ </ button >
140+ ) }
141+ </ div >
142+ )
102143
103144 return (
104- < li
145+ < RouteSegmentCard
105146 data-testid = { route . id }
106147 onMouseEnter = { ( ) => onHover ( route . id === "root" ? "root" : i . toString ( ) , "enter" ) }
107148 onMouseLeave = { ( ) => onHover ( route . id === "root" ? "root" : i . toString ( ) , "leave" ) }
108- className = { styles . routeSegmentInfo . listItem }
109149 >
110- < div
111- className = { cx (
112- styles . routeSegmentInfo . iconWrapper ,
113- styles . routeSegmentInfo [
114- `iconWrapper${ routeColor . charAt ( 0 ) . toUpperCase ( ) + routeColor . slice ( 1 ) } ` as keyof typeof styles . routeSegmentInfo
115- ]
116- ) }
117- >
118- < Icon name = { isRoot ? "Root" : isLayout ? "Layout" : "CornerDownRight" } size = "sm" />
119- </ div >
120- < h2 className = { styles . routeSegmentInfo . header } >
121- { route . pathname }
122-
123- < div className = { styles . routeSegmentInfo . headerActions } >
124- { Boolean ( cacheControl && serverInfo ?. lastLoader . timestamp ) && (
150+ < RouteSegmentHeader
151+ icon = { iconName }
152+ color = { routeColor }
153+ title = { route . pathname }
154+ subtitle = { route . id }
155+ rightContent = { headerActions }
156+ />
157+ < RouteSegmentBody >
158+ { /* Cache Info */ }
159+ { Boolean ( cacheControl && serverInfo ?. lastLoader . timestamp ) && (
160+ < div className = { styles . routeSegmentCard . cacheSection } >
125161 < CacheInfo
126162 key = { JSON . stringify ( serverInfo ?. lastLoader ?? "" ) }
127163 // biome-ignore lint/style/noNonNullAssertion: <explanation>
128164 cacheControl = { cacheControl ! }
129165 cacheDate = { new Date ( serverInfo ?. lastLoader . timestamp ?? "" ) }
130166 />
131- ) }
132- < div className = { styles . routeSegmentInfo . actionButtons } >
133- { isDev && import . meta. env . DEV && entryRoute ?. module && (
134- < EditorButton
135- data-testid = { `${ route . id } -open-source` }
136- onClick = { ( ) => {
137- openSource ( `${ entryRoute . module } ` )
138- } }
139- />
140- ) }
141- { settings . showRouteBoundariesOn === "click" && (
142- < button
143- type = "button"
144- data-testid = { `${ route . id } -show-route-boundaries` }
145- className = { styles . routeSegmentInfo . showBoundaryButton }
146- onClick = { ( ) => {
147- const routeId = route . id === "root" ? "root" : i . toString ( )
148- if ( routeId !== settings . hoveredRoute ) {
149- // Remove the classes from the old hovered route
150- setSettings ( {
151- isHoveringRoute : false ,
152- } )
153- // Add the classes to the new hovered route
154- setTimeout ( ( ) => {
155- setSettings ( {
156- hoveredRoute : routeId ,
157- isHoveringRoute : true ,
158- } )
159- } )
160- } else {
161- // Just change the isHoveringRoute state
162- setSettings ( {
163- isHoveringRoute : ! settings . isHoveringRoute ,
164- } )
165- }
166- } }
167- >
168- Show Route Boundary
169- </ button >
170- ) }
167+ </ div >
168+ ) }
169+
170+ { /* Component Tags */ }
171+ < div className = { styles . routeSegmentCard . componentsSection } >
172+ < span className = { styles . routeSegmentCard . componentsSectionLabel } > Components:</ span >
173+ < div className = { styles . routeSegmentCard . tagsContainer } >
174+ < Tag color = { entryRoute ?. hasLoader ? "GREEN" : "RED" } > Loader</ Tag >
175+ < Tag color = { entryRoute ?. hasClientLoader ? "GREEN" : "RED" } > Client Loader</ Tag >
176+ < Tag color = { entryRoute ?. hasClientAction ? "GREEN" : "RED" } > Client Action</ Tag >
177+ < Tag color = { entryRoute ?. hasAction ? "GREEN" : "RED" } > Action</ Tag >
178+ < Tag color = { entryRoute ?. hasErrorBoundary ? "GREEN" : "RED" } > ErrorBoundary</ Tag >
171179 </ div >
172180 </ div >
173- </ h2 >
174- < div className = { styles . routeSegmentInfo . content } >
175- < p className = { styles . routeSegmentInfo . routeFileLabel } > Route segment file: { route . id } </ p >
176181
177- < div className = { styles . routeSegmentInfo . cardsContainer } >
182+ { /* Info Cards */ }
183+ < div className = { styles . routeSegmentCard . cardsContainer } >
178184 { loaderData && < InfoCard title = "Returned loader data" > { < JsonRenderer data = { loaderData } /> } </ InfoCard > }
179185 { serverInfo && import . meta. env . DEV && (
180186 < InfoCard onClear = { clearServerInfoForRoute } title = "Server information" >
@@ -193,7 +199,7 @@ export const RouteSegmentInfo = ({ route, i }: { route: UIMatch<unknown, unknown
193199 </ InfoCard >
194200 ) }
195201 </ div >
196- </ div >
197- </ li >
202+ </ RouteSegmentBody >
203+ </ RouteSegmentCard >
198204 )
199205}
0 commit comments