@@ -9,7 +9,7 @@ import ChartContainer from '../Components/ChartContainer';
99const StackedAreaChart = ( { title, icon, color, prefix, names, baseColorHue = 0 } ) => {
1010 const theme = useTheme ( ) ;
1111 const [ activeSeries , setActiveSeries ] = useState ( null ) ;
12- const [ stackOffset , setStackOffset ] = useState ( 'none ' ) ;
12+ const [ viewMode , setViewMode ] = useState ( 'volume ' ) ; // 'volume', 'lines', 'gains'
1313 const { timeRange, customRange, hiddenSeries, toggleSeries, isolateSeries } = useHistoryContext ( ) ;
1414 const { filteredData, rawHistory } = useHistoryData ( timeRange , customRange ) ;
1515
@@ -35,32 +35,37 @@ const StackedAreaChart = ({ title, icon, color, prefix, names, baseColorHue = 0
3535 } , [ rawHistory , names , prefix ] ) ;
3636
3737 const chartData = useMemo ( ( ) => {
38+ if ( ! filteredData || filteredData . length === 0 ) return [ ] ;
39+
40+ // For 'gains' mode, capture the baseline values (first timestamp)
41+ const baseValues = { } ;
42+ if ( viewMode === 'gains' ) {
43+ const firstEntry = filteredData [ 0 ] ;
44+ visibleSeries . forEach ( ( { i } ) => {
45+ const key = `${ prefix } _${ i } ` ;
46+ baseValues [ key ] = Number ( firstEntry [ key ] ) || 0 ;
47+ } ) ;
48+ }
49+
3850 return filteredData . map ( d => {
3951 const safeData = { ...d } ;
40- let visibleSum = 0 ;
41- let firstVisibleKey = null ;
4252
4353 visibleSeries . forEach ( ( { i } ) => {
4454 const key = `${ prefix } _${ i } ` ;
4555 let val = Number ( safeData [ key ] ) ;
46- if ( isNaN ( val ) || val < 0 ) val = 0 ; // Prevent negative or invalid values
47-
48- safeData [ key ] = val ;
56+ if ( isNaN ( val ) || val < 0 ) val = 0 ;
4957
50- if ( ! hiddenSeries . has ( key ) ) {
51- visibleSum += val ;
52- if ( ! firstVisibleKey ) firstVisibleKey = key ;
58+ if ( viewMode === 'gains' ) {
59+ // Show only growth since the start
60+ safeData [ key ] = Math . max ( 0 , val - baseValues [ key ] ) ;
61+ } else {
62+ safeData [ key ] = val ;
5363 }
5464 } ) ;
5565
56- // Prevent React/Recharts from crashing in Expand mode when total sum is exactly 0
57- if ( stackOffset === 'expand' && visibleSum === 0 && firstVisibleKey ) {
58- safeData [ firstVisibleKey ] = 0.000001 ;
59- }
60-
6166 return safeData ;
6267 } ) ;
63- } , [ filteredData , stackOffset , visibleSeries , hiddenSeries , prefix ] ) ;
68+ } , [ filteredData , viewMode , visibleSeries , hiddenSeries , prefix ] ) ;
6469
6570 const getClosestSeries = ( e ) => {
6671 if ( ! e || ! e . activePayload || e . activePayload . length === 0 ) return null ;
@@ -114,11 +119,7 @@ const StackedAreaChart = ({ title, icon, color, prefix, names, baseColorHue = 0
114119 gap : useGrid ? 1.5 : 0.5
115120 } } >
116121 { sorted . map ( ( entry , index ) => {
117- // In 'expand' mode, Recharts converts values to percentages implicitly,
118- // but payload.value usually holds the original value, unless it's transformed.
119- // However, we want to show original values and possibly percentage.
120- // Let's just show original values as shorten() for now, maybe with % if expand mode.
121- const originalValue = entry . value ;
122+ const displayValue = entry . value ;
122123 return (
123124 < Box key = { index } sx = { { display : 'flex' , alignItems : 'center' , justifyContent : 'space-between' , gap : 1 } } >
124125 < Box sx = { { display : 'flex' , alignItems : 'center' , gap : 1 , minWidth : 0 } } >
@@ -129,7 +130,7 @@ const StackedAreaChart = ({ title, icon, color, prefix, names, baseColorHue = 0
129130 </ Box >
130131 < Box sx = { { display : 'flex' , gap : 1 } } >
131132 < Typography variant = "caption" sx = { { fontWeight : 800 , fontFamily : 'monospace' , color : entry . color , fontSize : '0.75rem' } } >
132- { shorten ( originalValue ) }
133+ { viewMode === 'gains' && displayValue > 0 ? '+' : '' } { shorten ( displayValue ) }
133134 </ Typography >
134135 </ Box >
135136 </ Box >
@@ -144,14 +145,15 @@ const StackedAreaChart = ({ title, icon, color, prefix, names, baseColorHue = 0
144145
145146 const controls = (
146147 < ToggleButtonGroup
147- value = { stackOffset }
148+ value = { viewMode }
148149 exclusive
149- onChange = { ( e , val ) => val && setStackOffset ( val ) }
150+ onChange = { ( e , val ) => val && setViewMode ( val ) }
150151 size = "small"
151- sx = { { height : 26 , '.MuiToggleButton-root' : { py : 0 , px : 1.5 , fontSize : '0.7rem' , fontWeight : 700 } } }
152+ sx = { { height : 26 , '.MuiToggleButton-root' : { py : 0 , px : 2 , fontSize : '0.7rem' , fontWeight : 800 } } }
152153 >
153- < ToggleButton value = "none" > Total</ ToggleButton >
154- < ToggleButton value = "expand" > %</ ToggleButton >
154+ < ToggleButton value = "volume" > Total</ ToggleButton >
155+ < ToggleButton value = "lines" > Lines</ ToggleButton >
156+ < ToggleButton value = "gains" > Gains</ ToggleButton >
155157 </ ToggleButtonGroup >
156158 ) ;
157159
@@ -162,7 +164,6 @@ const StackedAreaChart = ({ title, icon, color, prefix, names, baseColorHue = 0
162164 < AreaChart
163165 data = { chartData }
164166 margin = { { top : 10 , right : 30 , left : 10 , bottom : 0 } }
165- stackOffset = { stackOffset }
166167 >
167168 < CartesianGrid strokeDasharray = "3 3" opacity = { 0.1 } vertical = { false } style = { { pointerEvents : 'none' } } />
168169 < XAxis
@@ -174,8 +175,8 @@ const StackedAreaChart = ({ title, icon, color, prefix, names, baseColorHue = 0
174175 fontSize = { 10 }
175176 />
176177 < YAxis
177- tickFormatter = { ( v ) => stackOffset === 'expand' ? `${ ( v * 100 ) . toFixed ( 0 ) } % ` : shorten ( v ) }
178- domain = { stackOffset === 'expand' ? [ 0 , 1 ] : [ 'auto' , 'auto' ] }
178+ tickFormatter = { ( v ) => ( viewMode === 'gains' && v > 0 ) ? `+ ${ shorten ( v ) } ` : shorten ( v ) }
179+ domain = { [ 'auto' , 'auto' ] }
179180 stroke = { theme . palette . text . secondary }
180181 fontSize = { 10 }
181182 width = { 55 }
@@ -198,12 +199,16 @@ const StackedAreaChart = ({ title, icon, color, prefix, names, baseColorHue = 0
198199 type = "monotone"
199200 dataKey = { seriesKey }
200201 name = { name }
201- stackId = "1"
202+ stackId = { viewMode === 'volume' ? "1" : undefined }
202203 stroke = { fillColor }
203204 fill = { fillColor }
204- fillOpacity = { activeSeries ? ( activeSeries === seriesKey ? 0.8 : 0.1 ) : 0.5 }
205+ fillOpacity = {
206+ viewMode === 'volume'
207+ ? ( activeSeries ? ( activeSeries === seriesKey ? 0.8 : 0.1 ) : 0.5 )
208+ : ( activeSeries === seriesKey ? 0.2 : 0 )
209+ }
205210 strokeOpacity = { activeSeries ? ( activeSeries === seriesKey ? 1 : 0.2 ) : 1 }
206- strokeWidth = { activeSeries === seriesKey ? 2 : 1 }
211+ strokeWidth = { viewMode !== 'volume' ? 2 : ( activeSeries === seriesKey ? 2 : 1 ) }
207212 activeDot = { { r : 4 , strokeWidth : 0 } }
208213 />
209214 ) ;
0 commit comments