Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Reports: Enable Show Labels #2124

Merged
merged 19 commits into from
Jan 12, 2024
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 @@ -138,7 +138,6 @@ export function ReportTopbar({
}}
style={{ marginRight: 15 }}
title="Show labels"
disabled={true}
>
<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);

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 { variableTextSize } 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 = variableTextSize(props.width, 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 { adjustDonutTextSize } 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 = adjustDonutTextSize(size);
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
Loading