11import { useState , useEffect , useMemo , useCallback } from 'react' ;
2- import { ComposedChart , Bar , Line , XAxis , YAxis , CartesianGrid , Tooltip , ErrorBar , ResponsiveContainer , Legend , Cell } from 'recharts' ;
2+ import { ComposedChart , Bar , Line , XAxis , YAxis , CartesianGrid , Tooltip , ErrorBar , ResponsiveContainer , Legend , Cell , ReferenceLine , LabelList } from 'recharts' ;
33import * as api from '../api/cdm' ;
44import { timeWork } from '../debugLog' ;
55
@@ -25,6 +25,18 @@ function formatYTick(value) {
2525 return value . toPrecision ( 3 ) ;
2626}
2727
28+ // Compact value for bar labels — max 4 significant digits
29+ function formatBarLabel ( v ) {
30+ if ( v == null ) return '' ;
31+ var abs = Math . abs ( v ) ;
32+ if ( abs === 0 ) return '0' ;
33+ if ( abs >= 1000000 ) return ( v / 1000000 ) . toPrecision ( 3 ) + 'M' ;
34+ if ( abs >= 1000 ) return ( v / 1000 ) . toPrecision ( 3 ) + 'k' ;
35+ if ( abs >= 100 ) return v . toPrecision ( 4 ) ;
36+ if ( abs >= 1 ) return v . toPrecision ( 3 ) ;
37+ return v . toPrecision ( 2 ) ;
38+ }
39+
2840function formatValue ( v ) {
2941 if ( v == null ) return '' ;
3042 if ( Math . abs ( v ) >= 1000 ) return v . toFixed ( 0 ) ;
@@ -1375,8 +1387,37 @@ export default function CompareView({ selected, groupByList, setGroupByList, hid
13751387 radius = { ct === 'stacked' ? [ 0 , 0 , 0 , 0 ] : [ 3 , 3 , 0 , 0 ] }
13761388 stackId = { ct === 'stacked' ? 'stack' : undefined }
13771389 name = { labelName } >
1390+ < LabelList dataKey = { lk } content = { function ( props ) {
1391+ if ( ct === 'stacked' ) {
1392+ // For stacked: check both width and individual segment height
1393+ var val3 = props . value ;
1394+ var w3 = props . width ;
1395+ var h3 = props . height ;
1396+ if ( val3 == null || w3 == null || h3 == null ) return null ;
1397+ var text3 = formatBarLabel ( val3 ) ;
1398+ if ( text3 . length * 8 > w3 - 4 || Math . abs ( h3 ) < 14 ) return null ;
1399+ return (
1400+ < text x = { props . x + w3 / 2 } y = { props . y + h3 / 2 } textAnchor = "middle" dominantBaseline = "middle"
1401+ fontSize = { 12 } fontWeight = { 700 } fontFamily = "ui-monospace, Consolas, monospace"
1402+ fill = "rgba(255,255,255,0.9)" > { text3 } </ text >
1403+ ) ;
1404+ }
1405+ var val2 = props . value ;
1406+ var w2 = props . width ;
1407+ var h2 = props . height ;
1408+ if ( val2 == null || w2 == null || h2 == null ) return null ;
1409+ var text2 = formatBarLabel ( val2 ) ;
1410+ if ( text2 . length * 8 > w2 - 4 || h2 < 16 ) return null ;
1411+ return (
1412+ < text x = { props . x + w2 / 2 } y = { props . y + h2 / 2 } textAnchor = "middle" dominantBaseline = "middle"
1413+ fontSize = { 12 } fontWeight = { 700 } fontFamily = "ui-monospace, Consolas, monospace"
1414+ fill = "rgba(255,255,255,0.9)" > { text2 } </ text >
1415+ ) ;
1416+ } } />
13781417 { chart . data . map ( function ( entry , idx ) {
1379- return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : itemColor } fillOpacity = { 0.7 } /> ;
1418+ var isPinnedBk = pinnedEntry && pinnedEntry . entry && pinnedEntry . entry . iterationId === entry . iterationId ;
1419+ var bkOpacity = pinnedEntry ? ( isPinnedBk ? 0.9 : 0.2 ) : 0.7 ;
1420+ return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : itemColor } fillOpacity = { bkOpacity } /> ;
13801421 } ) }
13811422 </ Bar >
13821423 ) ;
@@ -1387,8 +1428,23 @@ export default function CompareView({ selected, groupByList, setGroupByList, hid
13871428 return (
13881429 < Bar dataKey = { dataKey } yAxisId = "left" radius = { [ 3 , 3 , 0 , 0 ] } >
13891430 < ErrorBar dataKey = { dataKey + '_error' } width = { 4 } strokeWidth = { 2 } stroke = "var(--text-secondary)" />
1431+ < LabelList dataKey = { dataKey } content = { function ( props ) {
1432+ var val4 = props . value ;
1433+ var w4 = props . width ;
1434+ var h4 = props . height ;
1435+ if ( val4 == null || w4 == null || h4 == null ) return null ;
1436+ var text4 = formatBarLabel ( val4 ) ;
1437+ if ( text4 . length * 8 > w4 - 4 || h4 < 16 ) return null ;
1438+ return (
1439+ < text x = { props . x + w4 / 2 } y = { props . y + h4 / 2 } textAnchor = "middle" dominantBaseline = "middle"
1440+ fontSize = { 12 } fontWeight = { 700 } fontFamily = "ui-monospace, Consolas, monospace"
1441+ fill = "rgba(255,255,255,0.9)" > { text4 } </ text >
1442+ ) ;
1443+ } } />
13901444 { chart . data . map ( function ( entry , idx ) {
1391- return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : color } fillOpacity = { 0.7 } /> ;
1445+ var isPinnedCell = pinnedEntry && pinnedEntry . entry && pinnedEntry . entry . iterationId === entry . iterationId ;
1446+ var cellOpacity = pinnedEntry ? ( isPinnedCell ? 0.9 : 0.2 ) : 0.7 ;
1447+ return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : color } fillOpacity = { cellOpacity } /> ;
13921448 } ) }
13931449 </ Bar >
13941450 ) ;
@@ -1532,6 +1588,9 @@ export default function CompareView({ selected, groupByList, setGroupByList, hid
15321588 ) ;
15331589 } }
15341590 />
1591+ { pinnedEntry && pinnedEntry . entry && pinnedEntry . entry . name && (
1592+ < ReferenceLine x = { pinnedEntry . entry . name } yAxisId = "left" stroke = "#ff6b6b" strokeDasharray = "6 4" strokeWidth = { 2 } />
1593+ ) }
15351594 < Bar dataKey = "value" yAxisId = "left" radius = { [ 4 , 4 , 0 , 0 ] } style = { { cursor : 'pointer' } }
15361595 onClick = { function ( data ) {
15371596 if ( data && ! data . isGap && data . value != null ) {
@@ -1543,8 +1602,29 @@ export default function CompareView({ selected, groupByList, setGroupByList, hid
15431602 } }
15441603 >
15451604 < ErrorBar dataKey = "errorY" width = { 4 } strokeWidth = { 2 } stroke = "var(--text-secondary)" />
1605+ < LabelList dataKey = "value" content = { function ( props ) {
1606+ var val = props . value ;
1607+ var w = props . width ;
1608+ var h = props . height ;
1609+ if ( val == null || w == null || h == null ) return null ;
1610+ var text = formatBarLabel ( val ) ;
1611+ var charWidth = 8 ; // approximate pixels per character at font-size 12
1612+ var textWidth = text . length * charWidth ;
1613+ // Show inside bar if it fits width-wise and bar is tall enough
1614+ if ( textWidth > w - 4 ) return null ;
1615+ if ( h < 16 ) return null ;
1616+ return (
1617+ < text x = { props . x + w / 2 } y = { props . y + h / 2 } textAnchor = "middle" dominantBaseline = "middle"
1618+ fontSize = { 12 } fontWeight = { 700 } fontFamily = "ui-monospace, Consolas, monospace"
1619+ fill = "rgba(255,255,255,0.9)" >
1620+ { text }
1621+ </ text >
1622+ ) ;
1623+ } } />
15461624 { chart . data . map ( function ( entry , idx ) {
1547- return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : entry . color } /> ;
1625+ var isPinned = pinnedEntry && pinnedEntry . entry && pinnedEntry . entry . iterationId === entry . iterationId ;
1626+ var opacity = pinnedEntry ? ( isPinned ? 1 : 0.3 ) : 1 ;
1627+ return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : entry . color } fillOpacity = { opacity } /> ;
15481628 } ) }
15491629 </ Bar >
15501630 { supplementalMetrics . map ( function ( sm , si ) {
@@ -1682,6 +1762,9 @@ export default function CompareView({ selected, groupByList, setGroupByList, hid
16821762 ) : (
16831763 < YAxis yAxisId = "right" orientation = "right" width = { 1 } tick = { false } axisLine = { false } />
16841764 ) }
1765+ { pinnedEntry && pinnedEntry . entry && pinnedEntry . entry . name && (
1766+ < ReferenceLine x = { pinnedEntry . entry . name } yAxisId = "left" stroke = "#ff6b6b" strokeDasharray = "6 4" strokeWidth = { 2 } />
1767+ ) }
16851768 { ( function ( ) {
16861769 if ( sm . breakouts . length > 0 ) {
16871770 var labelSet = new Set ( ) ;
@@ -1713,8 +1796,37 @@ export default function CompareView({ selected, groupByList, setGroupByList, hid
17131796 radius = { ct === 'stacked' ? [ 0 , 0 , 0 , 0 ] : [ 3 , 3 , 0 , 0 ] }
17141797 stackId = { ct === 'stacked' ? 'stack' : undefined }
17151798 name = { labelName } >
1799+ < LabelList dataKey = { lk } content = { function ( props ) {
1800+ if ( ct === 'stacked' ) {
1801+ // For stacked: check both width and individual segment height
1802+ var val3 = props . value ;
1803+ var w3 = props . width ;
1804+ var h3 = props . height ;
1805+ if ( val3 == null || w3 == null || h3 == null ) return null ;
1806+ var text3 = formatBarLabel ( val3 ) ;
1807+ if ( text3 . length * 8 > w3 - 4 || Math . abs ( h3 ) < 14 ) return null ;
1808+ return (
1809+ < text x = { props . x + w3 / 2 } y = { props . y + h3 / 2 } textAnchor = "middle" dominantBaseline = "middle"
1810+ fontSize = { 12 } fontWeight = { 700 } fontFamily = "ui-monospace, Consolas, monospace"
1811+ fill = "rgba(255,255,255,0.9)" > { text3 } </ text >
1812+ ) ;
1813+ }
1814+ var val2 = props . value ;
1815+ var w2 = props . width ;
1816+ var h2 = props . height ;
1817+ if ( val2 == null || w2 == null || h2 == null ) return null ;
1818+ var text2 = formatBarLabel ( val2 ) ;
1819+ if ( text2 . length * 8 > w2 - 4 || h2 < 16 ) return null ;
1820+ return (
1821+ < text x = { props . x + w2 / 2 } y = { props . y + h2 / 2 } textAnchor = "middle" dominantBaseline = "middle"
1822+ fontSize = { 12 } fontWeight = { 700 } fontFamily = "ui-monospace, Consolas, monospace"
1823+ fill = "rgba(255,255,255,0.9)" > { text2 } </ text >
1824+ ) ;
1825+ } } />
17161826 { chart . data . map ( function ( entry , idx ) {
1717- return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : itemColor } fillOpacity = { 0.7 } /> ;
1827+ var isPinnedBk = pinnedEntry && pinnedEntry . entry && pinnedEntry . entry . iterationId === entry . iterationId ;
1828+ var bkOpacity = pinnedEntry ? ( isPinnedBk ? 0.9 : 0.2 ) : 0.7 ;
1829+ return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : itemColor } fillOpacity = { bkOpacity } /> ;
17181830 } ) }
17191831 </ Bar >
17201832 ) ;
@@ -1724,8 +1836,23 @@ export default function CompareView({ selected, groupByList, setGroupByList, hid
17241836 return (
17251837 < Bar dataKey = { dataKey } yAxisId = "left" radius = { [ 3 , 3 , 0 , 0 ] } >
17261838 < ErrorBar dataKey = { dataKey + '_error' } width = { 4 } strokeWidth = { 2 } stroke = "var(--text-secondary)" />
1839+ < LabelList dataKey = { dataKey } content = { function ( props ) {
1840+ var val4 = props . value ;
1841+ var w4 = props . width ;
1842+ var h4 = props . height ;
1843+ if ( val4 == null || w4 == null || h4 == null ) return null ;
1844+ var text4 = formatBarLabel ( val4 ) ;
1845+ if ( text4 . length * 8 > w4 - 4 || h4 < 16 ) return null ;
1846+ return (
1847+ < text x = { props . x + w4 / 2 } y = { props . y + h4 / 2 } textAnchor = "middle" dominantBaseline = "middle"
1848+ fontSize = { 12 } fontWeight = { 700 } fontFamily = "ui-monospace, Consolas, monospace"
1849+ fill = "rgba(255,255,255,0.9)" > { text4 } </ text >
1850+ ) ;
1851+ } } />
17271852 { chart . data . map ( function ( entry , idx ) {
1728- return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : color } fillOpacity = { 0.7 } /> ;
1853+ var isPinnedCell = pinnedEntry && pinnedEntry . entry && pinnedEntry . entry . iterationId === entry . iterationId ;
1854+ var cellOpacity = pinnedEntry ? ( isPinnedCell ? 0.9 : 0.2 ) : 0.7 ;
1855+ return < Cell key = { idx } fill = { entry . isGap ? 'transparent' : color } fillOpacity = { cellOpacity } /> ;
17291856 } ) }
17301857 </ Bar >
17311858 ) ;
0 commit comments