@@ -14,40 +14,56 @@ import {
1414} from "../../api" ;
1515
1616import { BrowserIcon , MobileDeviceIcon , OSIcon , ReferrerIcon } from "../icons" ;
17- import { countryCodeToFlag , formatFullUrl , formatHost , getHref , tryParseUrl } from "../../utils" ;
17+ import { countryCodeToFlag , formatFullUrl , formatHost , formatPath , getHref , tryParseUrl } from "../../utils" ;
1818import { DetailsModal } from "./modal" ;
1919import { formatMetricVal } from "../../utils" ;
2020import type { ProjectQuery } from "../project" ;
2121
2222export const cardStyles = styles . card ;
2323
24- export const DimensionCard = ( {
25- dimension,
26- query,
27- } : {
24+ type DimensionProps = {
2825 dimension : Dimension ;
2926 query : ProjectQuery ;
30- } ) => {
27+ onSelect : ( value : DimensionTableRow ) => void ;
28+ } ;
29+
30+ export const DimensionCard = ( props : DimensionProps ) => {
3131 return (
3232 < article className = { styles . card } >
3333 < div className = { styles . dimensionHeader } >
34- < div > { dimensionNames [ dimension ] } </ div >
35- < div > { metricNames [ query . metric ] } </ div >
34+ < div > { dimensionNames [ props . dimension ] } </ div >
35+ < div > { metricNames [ props . query . metric ] } </ div >
3636 </ div >
37- < DimensionTable dimension = { dimension } query = { query } />
37+ < DimensionTable { ... props } />
3838 </ article >
3939 ) ;
4040} ;
4141
42- export const DimensionTabsCard = ( { dimensions, query } : { dimensions : Dimension [ ] ; query : ProjectQuery } ) => {
42+ export const DimensionTabsCard = ( {
43+ dimensions,
44+ query,
45+ onSelect,
46+ } : {
47+ dimensions : Dimension [ ] ;
48+ query : ProjectQuery ;
49+ onSelect : ( value : DimensionTableRow , dimension : Dimension ) => void ;
50+ } ) => {
4351 return (
4452 < article className = { styles . card } >
45- < DimensionTabs dimensions = { dimensions } query = { query } />
53+ < DimensionTabs dimensions = { dimensions } query = { query } onSelect = { onSelect } />
4654 </ article >
4755 ) ;
4856} ;
4957
50- export const DimensionTabs = ( { dimensions, query } : { dimensions : Dimension [ ] ; query : ProjectQuery } ) => {
58+ export const DimensionTabs = ( {
59+ dimensions,
60+ query,
61+ onSelect,
62+ } : {
63+ dimensions : Dimension [ ] ;
64+ query : ProjectQuery ;
65+ onSelect : ( value : DimensionTableRow , dimension : Dimension ) => void ;
66+ } ) => {
5167 return (
5268 < Tabs . Root className = { styles . tabs } defaultValue = { dimensions [ 0 ] } >
5369 < Tabs . List className = { styles . tabsList } >
@@ -60,18 +76,15 @@ export const DimensionTabs = ({ dimensions, query }: { dimensions: Dimension[];
6076 </ Tabs . List >
6177 { dimensions . map ( ( dimension ) => (
6278 < Tabs . Content key = { dimension } value = { dimension } className = { styles . tabsContent } >
63- < DimensionTable dimension = { dimension } noHeader query = { query } />
79+ < DimensionTable dimension = { dimension } query = { query } onSelect = { ( value ) => onSelect ( value , dimension ) } />
6480 </ Tabs . Content >
6581 ) ) }
6682 </ Tabs . Root >
6783 ) ;
6884} ;
6985
70- export const DimensionTable = ( {
71- dimension,
72- query,
73- } : { dimension : Dimension ; noHeader ?: boolean ; query : ProjectQuery } ) => {
74- const { data, biggest, order, isLoading } = useDimension ( { dimension, ...query } ) ;
86+ export const DimensionTable = ( props : DimensionProps ) => {
87+ const { data, biggest, order, isLoading } = useDimension ( { dimension : props . dimension , ...props . query } ) ;
7588
7689 const dataTruncated = data ?. slice ( 0 , 6 ) ;
7790 return (
@@ -85,7 +98,7 @@ export const DimensionTable = ({
8598 className = { styles . dimensionRow }
8699 >
87100 < DimensionValueBar value = { d . value } biggest = { biggest } >
88- < DimensionLabel dimension = { dimension } value = { d } />
101+ < DimensionLabel dimension = { props . dimension } value = { d } onSelect = { props . onSelect } />
89102 </ DimensionValueBar >
90103 < div > { formatMetricVal ( d . value ) } </ div >
91104 </ div >
@@ -99,77 +112,103 @@ export const DimensionTable = ({
99112 </ div >
100113 ) }
101114 </ div >
102- < DetailsModal dimension = { dimension } query = { query } />
115+ < DetailsModal { ... props } />
103116 </ >
104117 ) ;
105118} ;
106119
107- const dimensionLabels : Record < Dimension , ( value : DimensionTableRow ) => React . ReactNode > = {
108- platform : ( value ) => (
120+ const DimensionValueButton = ( {
121+ children,
122+ onSelect,
123+ } : {
124+ children : React . ReactNode ;
125+ onSelect : ( ) => void ;
126+ } ) => (
127+ < button type = "button" className = { styles . dimensionItemSelect } onClick = { onSelect } >
128+ { children }
129+ </ button >
130+ ) ;
131+
132+ const dimensionLabels : Record < Dimension , ( value : DimensionTableRow , onSelect : ( ) => void ) => React . ReactNode > = {
133+ platform : ( value , onSelect ) => (
109134 < >
110135 < OSIcon os = { value . dimensionValue } size = { 24 } />
111- { value . dimensionValue }
136+ < DimensionValueButton onSelect = { onSelect } > { value . dimensionValue } </ DimensionValueButton >
112137 </ >
113138 ) ,
114- browser : ( value ) => (
139+ browser : ( value , onSelect ) => (
115140 < >
116141 < BrowserIcon browser = { value . dimensionValue } size = { 24 } />
117- { value . dimensionValue }
142+ < DimensionValueButton onSelect = { onSelect } > { value . dimensionValue } </ DimensionValueButton >
118143 </ >
119144 ) ,
120- url : ( value ) => {
145+ url : ( value , onSelect ) => {
121146 const url = tryParseUrl ( value . dimensionValue ) ;
122147
123148 return (
124149 < >
125150 < LinkIcon size = { 16 } />
126- < a target = "_blank" rel = "noreferrer" href = { getHref ( url ) } >
127- { formatFullUrl ( url ) }
151+ < DimensionValueButton onSelect = { onSelect } > { formatPath ( url ) } </ DimensionValueButton >
152+ < a href = { getHref ( url ) } target = "_blank" rel = "noreferrer" className = { styles . external } >
153+ < SquareArrowOutUpRightIcon size = { 16 } />
128154 </ a >
155+ { typeof url !== "string" && < span className = { styles . hostname } > { formatHost ( url ) } </ span > }
129156 </ >
130157 ) ;
131158 } ,
132- fqdn : ( value ) => {
159+ fqdn : ( value , onSelect ) => {
133160 const url = tryParseUrl ( value . dimensionValue ) ;
134161 return (
135162 < >
136163 < LinkIcon size = { 16 } />
137- < a target = "_blank" rel = "noreferrer" href = { getHref ( url ) } >
138- { formatHost ( url ) }
164+ < DimensionValueButton onSelect = { onSelect } > { formatHost ( url ) } </ DimensionValueButton >
165+ < a href = { getHref ( url ) } target = "_blank" rel = "noreferrer" className = { styles . external } >
166+ < SquareArrowOutUpRightIcon size = { 16 } />
139167 </ a >
140168 </ >
141169 ) ;
142170 } ,
143- mobile : ( value ) => (
171+ mobile : ( value , onSelect ) => (
144172 < >
145173 < MobileDeviceIcon isMobile = { value . dimensionValue === "true" } size = { 24 } />
146- { value . dimensionValue === "true" ? "Mobile" : "Desktop" }
174+ < DimensionValueButton onSelect = { onSelect } >
175+ { value . dimensionValue === "true" ? "Mobile" : "Desktop" }
176+ </ DimensionValueButton >
147177 </ >
148178 ) ,
149- country : ( value ) => (
179+ country : ( value , onSelect ) => (
150180 < >
151181 < span > { countryCodeToFlag ( value . dimensionValue ) } </ span >
152- { value . displayName || value . dimensionValue || "Unknown" }
182+ < DimensionValueButton onSelect = { onSelect } >
183+ { value . displayName || value . dimensionValue || "Unknown" }
184+ </ DimensionValueButton >
153185 </ >
154186 ) ,
155- city : ( value ) => (
187+ city : ( value , onSelect ) => (
156188 < >
157189 < span > { countryCodeToFlag ( value . icon || "XX" ) } </ span >
158- { value . displayName || "Unknown" }
190+ < DimensionValueButton onSelect = { onSelect } > { value . displayName || "Unknown" } </ DimensionValueButton >
159191 </ >
160192 ) ,
161- referrer : ( value ) => (
193+ referrer : ( value , onSelect ) => (
162194 < >
163195 < ReferrerIcon referrer = { value . dimensionValue } icon = { value . icon } size = { 24 } />
164- { value . displayName || value . dimensionValue || "Unknown" }
196+ < DimensionValueButton onSelect = { onSelect } >
197+ { value . displayName || value . dimensionValue || "Unknown" }
198+ </ DimensionValueButton >
165199 { value . dimensionValue && isValidFqdn ( value . dimensionValue ) && (
166200 < a href = { `https://${ value . dimensionValue } ` } target = "_blank" rel = "noreferrer" className = { styles . external } >
167201 < SquareArrowOutUpRightIcon size = { 16 } />
168202 </ a >
169203 ) }
170204 </ >
171205 ) ,
172- path : ( value ) => value . dimensionValue ,
206+ path : ( value , onSelect ) => (
207+ < >
208+ < LinkIcon size = { 16 } />
209+ < DimensionValueButton onSelect = { onSelect } > { value . dimensionValue } </ DimensionValueButton >
210+ </ >
211+ ) ,
173212} ;
174213
175214const isValidFqdn = ( fqdn : string ) => {
@@ -182,8 +221,12 @@ const isValidFqdn = (fqdn: string) => {
182221 }
183222} ;
184223
185- export const DimensionLabel = ( { dimension, value } : { dimension : Dimension ; value : DimensionTableRow } ) =>
186- dimensionLabels [ dimension ] ( value ) ;
224+ export const DimensionLabel = ( {
225+ dimension,
226+ value,
227+ onSelect,
228+ } : { dimension : Dimension ; value : DimensionTableRow ; onSelect : ( value : DimensionTableRow ) => void } ) =>
229+ dimensionLabels [ dimension ] ( value , ( ) => onSelect ( value ) ) ;
187230
188231export const DimensionValueBar = ( {
189232 value,
0 commit comments