Skip to content

Commit

Permalink
Custom Reports: Enable Show Labels (actualbudget#2124)
Browse files Browse the repository at this point in the history
* work

* updates

* merge fixes

* syntax fix

* Add Label element

* updates

* notes

* normalize customLabel

* fix

* range adjustments

* margin update

* merge fixes

* review Updates

* labelFix

* Fix adjustTextSize
  • Loading branch information
carkom authored Jan 12, 2024
1 parent 9ddc918 commit 80b4dc4
Show file tree
Hide file tree
Showing 16 changed files with 318 additions and 29 deletions.
13 changes: 12 additions & 1 deletion packages/desktop-client/src/components/reports/ChooseGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type ChooseGraphProps = {
scrollWidth: number;
setScrollWidth: (value: number) => void;
months: Month[];
viewLabels: boolean;
};

export function ChooseGraph({
Expand All @@ -37,6 +38,7 @@ export function ChooseGraph({
scrollWidth,
setScrollWidth,
months,
viewLabels,
}: ChooseGraphProps) {
const balanceTypeOp = ReportOptions.balanceTypeMap.get(balanceType);

Expand Down Expand Up @@ -69,6 +71,7 @@ export function ChooseGraph({
style={{ flexGrow: 1 }}
data={data}
balanceTypeOp={balanceTypeOp}
viewLabels={viewLabels}
/>
);
}
Expand All @@ -79,6 +82,7 @@ export function ChooseGraph({
data={data}
groupBy={groupBy}
balanceTypeOp={balanceTypeOp}
viewLabels={viewLabels}
/>
);
}
Expand All @@ -92,14 +96,21 @@ export function ChooseGraph({
data={data}
groupBy={groupBy}
balanceTypeOp={balanceTypeOp}
viewLabels={viewLabels}
/>
);
}
if (graphType === 'LineGraph') {
return <LineGraph style={{ flexGrow: 1 }} graphData={data} />;
}
if (graphType === 'StackedBarGraph') {
return <StackedBarGraph style={{ flexGrow: 1 }} data={data} />;
return (
<StackedBarGraph
style={{ flexGrow: 1 }}
data={data}
viewLabels={viewLabels}
/>
);
}
if (graphType === 'TableGraph') {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ export function ReportTopbar({
onChangeViews('viewLabels');
}}
style={{ marginRight: 15 }}
title="Show labels"
disabled={true}
title="Show Labels"
>
<SvgTag width={15} height={15} />
</GraphButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import {
XAxis,
YAxis,
Tooltip,
LabelList,
ResponsiveContainer,
} from 'recharts';

import { usePrivacyMode } from 'loot-core/src/client/privacy';
import { amountToCurrency } from 'loot-core/src/shared/util';
import {
amountToCurrency,
amountToCurrencyNoDecimal,
} from 'loot-core/src/shared/util';

import { theme } from '../../../style';
import { type CSSProperties } from '../../../style';
Expand All @@ -22,6 +26,9 @@ import { Container } from '../Container';
import { type DataEntity } from '../entities';
import { numberFormatterTooltip } from '../numberFormatter';

import { adjustTextSize } from './adjustTextSize';
import { renderCustomLabel } from './renderCustomLabel';

type PayloadItem = {
payload: {
date: string;
Expand Down Expand Up @@ -91,30 +98,61 @@ const CustomTooltip = ({
}
};

const customLabel = (props, width, end) => {
//Add margin to first and last object
const calcX =
props.x + (props.index === end ? -10 : props.index === 0 ? 5 : 0);
const calcY = props.y - (props.value > 0 ? 10 : -10);
const textAnchor = props.index === 0 ? 'left' : 'middle';
const display =
props.value !== 0 && `${amountToCurrencyNoDecimal(props.value)}`;
const textSize = adjustTextSize(width, 'area');

return renderCustomLabel(calcX, calcY, textAnchor, display, textSize);
};

type AreaGraphProps = {
style?: CSSProperties;
data: DataEntity;
balanceTypeOp: string;
compact?: boolean;
viewLabels: boolean;
};

export function AreaGraph({
style,
data,
balanceTypeOp,
compact,
viewLabels,
}: AreaGraphProps) {
const privacyMode = usePrivacyMode();
const dataMax = Math.max(...data.monthData.map(i => i[balanceTypeOp]));
const dataMin = Math.min(...data.monthData.map(i => i[balanceTypeOp]));

const labelsMargin = viewLabels ? 30 : 0;
const dataDiff = dataMax - dataMin;
//Calculate how much to add to max and min values for graph range
const extendRangeAmount = Math.floor(dataDiff / 20);
const labelsMin =
//If min is zero or graph range passes zero then set it to zero
dataMin === 0 || Math.abs(dataMin) <= extendRangeAmount
? 0
: //Else add the range and round to nearest 100s
Math.floor((dataMin - extendRangeAmount) / 100) * 100;
//Same as above but for max
const labelsMax =
dataMax === 0 || Math.abs(dataMax) <= extendRangeAmount
? 0
: Math.ceil((dataMax + extendRangeAmount) / 100) * 100;
const lastLabel = data.monthData.length - 1;

const tickFormatter = tick => {
if (!privacyMode) return `${Math.round(tick).toLocaleString()}`; // Formats the tick values as strings with commas
return '...';
};

const gradientOffset = () => {
const dataMax = Math.max(...data.monthData.map(i => i[balanceTypeOp]));
const dataMin = Math.min(...data.monthData.map(i => i[balanceTypeOp]));

if (dataMax <= 0) {
return 0;
}
Expand Down Expand Up @@ -143,7 +181,7 @@ export function AreaGraph({
width={width}
height={height}
data={data.monthData}
margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
margin={{ top: 0, right: labelsMargin, left: 0, bottom: 0 }}
>
{compact ? null : (
<CartesianGrid strokeDasharray="3 3" vertical={false} />
Expand All @@ -158,7 +196,10 @@ export function AreaGraph({
{compact ? null : (
<YAxis
dataKey={balanceTypeOp}
domain={['auto', 'auto']}
domain={[
viewLabels ? labelsMin : 'auto',
viewLabels ? labelsMax : 'auto',
]}
tickFormatter={tickFormatter}
tick={{ fill: theme.pageText }}
tickLine={{ stroke: theme.pageText }}
Expand Down Expand Up @@ -193,7 +234,14 @@ export function AreaGraph({
stroke={theme.reportsBlue}
fill="url(#splitColor)"
fillOpacity={1}
/>
>
{viewLabels && (
<LabelList
dataKey={balanceTypeOp}
content={e => customLabel(e, width, lastLabel)}
/>
)}
</Area>
</AreaChart>
</div>
</ResponsiveContainer>
Expand Down
35 changes: 33 additions & 2 deletions packages/desktop-client/src/components/reports/graphs/BarGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ import {
XAxis,
YAxis,
Tooltip,
LabelList,
ResponsiveContainer,
} from 'recharts';

import { usePrivacyMode } from 'loot-core/src/client/privacy';
import { amountToCurrency } from 'loot-core/src/shared/util';
import {
amountToCurrency,
amountToCurrencyNoDecimal,
} from 'loot-core/src/shared/util';

import { theme } from '../../../style';
import { type CSSProperties } from '../../../style';
Expand All @@ -25,6 +29,9 @@ import { type DataEntity } from '../entities';
import { getCustomTick } from '../getCustomTick';
import { numberFormatterTooltip } from '../numberFormatter';

import { adjustTextSize } from './adjustTextSize';
import { renderCustomLabel } from './renderCustomLabel';

type PayloadChild = {
props: {
name: string;
Expand Down Expand Up @@ -106,12 +113,24 @@ const CustomTooltip = ({
}
};

const customLabel = props => {
const calcX = props.x + props.width / 2;
const calcY = props.y - (props.value > 0 ? 15 : -15);
const textAnchor = 'middle';
const display =
props.value !== 0 && `${amountToCurrencyNoDecimal(props.value)}`;
const textSize = adjustTextSize(props.width, 'variable', props.value);

return renderCustomLabel(calcX, calcY, textAnchor, display, textSize);
};

type BarGraphProps = {
style?: CSSProperties;
data: DataEntity;
groupBy: string;
balanceTypeOp: string;
compact?: boolean;
viewLabels: boolean;
};

export function BarGraph({
Expand All @@ -120,11 +139,13 @@ export function BarGraph({
groupBy,
balanceTypeOp,
compact,
viewLabels,
}: BarGraphProps) {
const privacyMode = usePrivacyMode();

const yAxis = ['Month', 'Year'].includes(groupBy) ? 'date' : 'name';
const splitData = ['Month', 'Year'].includes(groupBy) ? 'monthData' : 'data';
const labelsMargin = viewLabels ? 30 : 0;

const getVal = obj => {
if (balanceTypeOp === 'totalDebts') {
Expand Down Expand Up @@ -155,9 +176,10 @@ export function BarGraph({
height={height}
stackOffset="sign"
data={data[splitData]}
margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
margin={{ top: labelsMargin, right: 0, left: 0, bottom: 0 }}
>
<Tooltip
cursor={{ fill: 'transparent' }}
content={
<CustomTooltip
balanceTypeOp={balanceTypeOp}
Expand Down Expand Up @@ -189,6 +211,12 @@ export function BarGraph({
<ReferenceLine y={0} stroke={theme.pageTextLight} />
)}
<Bar dataKey={val => getVal(val)} stackId="a">
{viewLabels && (
<LabelList
dataKey={val => getVal(val)}
content={customLabel}
/>
)}
{data.legend.map((entry, index) => (
<Cell
key={`cell-${index}`}
Expand All @@ -199,6 +227,9 @@ export function BarGraph({
</Bar>
{yAxis === 'date' && balanceTypeOp === 'totalTotals' && (
<Bar dataKey="totalDebts" stackId="a">
{viewLabels && (
<LabelList dataKey="totalDebts" content={customLabel} />
)}
{data[splitData].map((entry, index) => (
<Cell
key={`cell-${index}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ import React, { useState } from 'react';

import { PieChart, Pie, Cell, Sector, ResponsiveContainer } from 'recharts';

import { type CSSProperties } from '../../../style';
import { amountToCurrency } from 'loot-core/src/shared/util';

import { theme, type CSSProperties } from '../../../style';
import { Container } from '../Container';
import { type DataEntity } from '../entities';

import { adjustTextSize } from './adjustTextSize';
import { renderCustomLabel } from './renderCustomLabel';

const RADIAN = Math.PI / 180;
const ActiveShape = props => {
const {
Expand Down Expand Up @@ -70,7 +75,7 @@ const ActiveShape = props => {
dy={18}
textAnchor={textAnchor}
fill={fill}
>{`${value.toFixed(2)}`}</text>
>{`${amountToCurrency(value)}`}</text>
<text
x={ex + (cos <= 0 ? 1 : -1) * 16}
y={ey}
Expand All @@ -84,12 +89,39 @@ const ActiveShape = props => {
);
};

const customLabel = props => {
const radius =
props.innerRadius + (props.outerRadius - props.innerRadius) * 0.5;
const size = props.cx > props.cy ? props.cy : props.cx;

const calcX = props.cx + radius * Math.cos(-props.midAngle * RADIAN);
const calcY = props.cy + radius * Math.sin(-props.midAngle * RADIAN);
const textAnchor = calcX > props.cx ? 'start' : 'end';
const display = props.value !== 0 && `${(props.percent * 100).toFixed(0)}%`;
const textSize = adjustTextSize(size, 'donut');
const showLabel = props.percent;
const showLabelThreshold = 0.05;
const fill = theme.reportsInnerLabel;

return renderCustomLabel(
calcX,
calcY,
textAnchor,
display,
textSize,
showLabel,
showLabelThreshold,
fill,
);
};

type DonutGraphProps = {
style?: CSSProperties;
data: DataEntity;
groupBy: string;
balanceTypeOp: string;
compact?: boolean;
viewLabels: boolean;
};

export function DonutGraph({
Expand All @@ -98,6 +130,7 @@ export function DonutGraph({
groupBy,
balanceTypeOp,
compact,
viewLabels,
}: DonutGraphProps) {
const yAxis = ['Month', 'Year'].includes(groupBy) ? 'date' : 'name';
const splitData = ['Month', 'Year'].includes(groupBy) ? 'monthData' : 'data';
Expand Down Expand Up @@ -139,6 +172,7 @@ export function DonutGraph({
innerRadius={Math.min(width, height) * 0.2}
fill="#8884d8"
labelLine={false}
label={e => (viewLabels ? customLabel(e) : <div />)}
onMouseEnter={onPieEnter}
>
{data.legend.map((entry, index) => (
Expand Down
Loading

0 comments on commit 80b4dc4

Please sign in to comment.