@@ -7,62 +7,133 @@ interface TirePressureCardProps {
77}
88
99/**
10- * Read-only display of one motorcycle's recommended tire pressures.
11- * Three rows max — Vorne / Hinten / Beiwagen — each shows the user's
12- * preferred unit prominently with the converted unit in a small
13- * mono-uppercase secondary line.
10+ * Read-only display of one motorcycle's recommended tire pressures as a
11+ * matrix: one row per tire position (Vorne / Hinten / Beiwagen), one
12+ * column per riding configuration (Solo / Sozius / Offroad). Only
13+ * recorded configurations get a column; with a single configuration no
14+ * column headers render and the card stays a simple two-row list.
1415 */
1516export function TirePressureCard ( { pressure } : TirePressureCardProps ) {
17+ // Only recorded configurations become columns — every set is optional.
18+ const variants = [
19+ {
20+ key : "solo" ,
21+ label : "Solo" ,
22+ front : pressure . frontBar ,
23+ rear : pressure . rearBar ,
24+ sidecar : pressure . sidecarBar ,
25+ } ,
26+ {
27+ key : "passenger" ,
28+ label : "Sozius" ,
29+ front : pressure . frontPassengerBar ,
30+ rear : pressure . rearPassengerBar ,
31+ sidecar : pressure . sidecarPassengerBar ,
32+ } ,
33+ {
34+ key : "offroad" ,
35+ label : "Offroad" ,
36+ front : pressure . frontOffroadBar ,
37+ rear : pressure . rearOffroadBar ,
38+ sidecar : pressure . sidecarOffroadBar ,
39+ } ,
40+ ] . filter ( ( v ) => v . front != null || v . rear != null ) ;
41+ // Headers render for several columns — and for a single non-solo column,
42+ // where values without their configuration label would be ambiguous.
43+ const showHeader = variants . length > 1 || variants . some ( ( v ) => v . key !== "solo" ) ;
44+ const hasSidecar = variants . some ( ( v ) => v . sidecar != null ) ;
45+
1646 return (
1747 < Card >
18- < div className = "px-4 py-4" >
19- < dl className = "space-y-3" >
20- < PressureRow
21- label = "Vorne"
22- bar = { pressure . frontBar }
23- preferred = { pressure . preferredUnit }
24- />
25- < PressureRow
26- label = "Hinten"
27- bar = { pressure . rearBar }
28- preferred = { pressure . preferredUnit }
29- />
30- { pressure . sidecarBar != null && (
31- < PressureRow
32- label = "Beiwagen"
33- bar = { pressure . sidecarBar }
48+ { /* overflow-x-auto: three value columns plus labels can exceed a
49+ narrow phone viewport; the card must scroll, not the page. */ }
50+ < div className = "overflow-x-auto px-4 py-4" >
51+ < table className = "w-full" >
52+ { showHeader && (
53+ < thead >
54+ < tr >
55+ < th aria-label = "Reifenposition" />
56+ { variants . map ( ( v ) => (
57+ < th
58+ key = { v . key }
59+ scope = "col"
60+ className = "pb-2 text-right font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-base-content/45 dark:text-navy-500"
61+ >
62+ { v . label }
63+ </ th >
64+ ) ) }
65+ </ tr >
66+ </ thead >
67+ ) }
68+ < tbody >
69+ < PositionRow
70+ label = "Vorne"
71+ values = { variants . map ( ( v ) => ( { key : v . key , bar : v . front } ) ) }
3472 preferred = { pressure . preferredUnit }
3573 />
36- ) }
37- </ dl >
74+ < PositionRow
75+ label = "Hinten"
76+ values = { variants . map ( ( v ) => ( { key : v . key , bar : v . rear } ) ) }
77+ preferred = { pressure . preferredUnit }
78+ />
79+ { hasSidecar && (
80+ < PositionRow
81+ label = "Beiwagen"
82+ values = { variants . map ( ( v ) => ( { key : v . key , bar : v . sidecar } ) ) }
83+ preferred = { pressure . preferredUnit }
84+ />
85+ ) }
86+ </ tbody >
87+ </ table >
3888 </ div >
3989 </ Card >
4090 ) ;
4191}
4292
43- function PressureRow ( {
93+ function PositionRow ( {
4494 label,
45- bar ,
95+ values ,
4696 preferred,
4797} : {
4898 label : string ;
99+ values : { key : string ; bar : number | null } [ ] ;
100+ preferred : TirePressure [ "preferredUnit" ] ;
101+ } ) {
102+ return (
103+ < tr className = "border-b border-base-200/70 last:border-b-0 dark:border-navy-700/60" >
104+ < th
105+ scope = "row"
106+ className = "py-2.5 pr-4 text-left align-baseline font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-base-content/60 dark:text-navy-400"
107+ >
108+ { label }
109+ </ th >
110+ { values . map ( ( { key, bar } ) => (
111+ < td key = { key } className = "py-2.5 pl-4 text-right align-baseline" >
112+ { bar != null ? < PressureValue bar = { bar } preferred = { preferred } /> : (
113+ < span aria-hidden = "true" className = "text-base-content/30" > —</ span >
114+ ) }
115+ </ td >
116+ ) ) }
117+ </ tr >
118+ ) ;
119+ }
120+
121+ function PressureValue ( {
122+ bar,
123+ preferred,
124+ } : {
49125 bar : number ;
50126 preferred : TirePressure [ "preferredUnit" ] ;
51127} ) {
52128 const formatted = formatPressure ( bar , preferred ) ;
53129 return (
54- < div className = "flex items-baseline justify-between gap-4 border-b border-base-200/70 pb-2 last:border-b-0 last:pb-0 dark:border-navy-700/60" >
55- < dt className = "font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-base-content/60 dark:text-navy-400" >
56- { label }
57- </ dt >
58- < dd className = "flex items-baseline gap-2" >
59- < span className = "font-numeric text-base font-semibold tabular-nums text-base-content dark:text-white" >
60- { formatted . primary }
61- </ span >
62- < span className = "font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-base-content/50" >
63- ≈ { formatted . secondary }
64- </ span >
65- </ dd >
66- </ div >
130+ < span className = "inline-flex flex-col items-end gap-0.5 sm:flex-row sm:items-baseline sm:gap-2" >
131+ < span className = "font-numeric text-base font-semibold tabular-nums text-base-content dark:text-white" >
132+ { formatted . primary }
133+ </ span >
134+ < span className = "whitespace-nowrap font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-base-content/50" >
135+ ≈ { formatted . secondary }
136+ </ span >
137+ </ span >
67138 ) ;
68139}
0 commit comments