From e67e01fd0ab12261a9a0730906d9300d6476d9bf Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Thu, 22 Feb 2024 14:57:30 +0000 Subject: [PATCH] Add Donut component that can render server side (if that ever actually works) --- src/app/donut/page.tsx | 29 ++++++ src/app/donut/styles.css | 19 ++++ src/components/Donut.tsx | 90 +++++++++++++++++++ src/components/DonutTooltip.tsx | 7 ++ src/components/OverviewPage.tsx | 2 +- .../cards/subjectComparisonCard.tsx | 29 +++--- src/components/donut-styles.css | 19 ++++ 7 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 src/app/donut/page.tsx create mode 100644 src/app/donut/styles.css create mode 100644 src/components/Donut.tsx create mode 100644 src/components/DonutTooltip.tsx create mode 100644 src/components/donut-styles.css diff --git a/src/app/donut/page.tsx b/src/app/donut/page.tsx new file mode 100644 index 0000000..afb1a60 --- /dev/null +++ b/src/app/donut/page.tsx @@ -0,0 +1,29 @@ +import './styles.css'; +import {Donut} from "@/components/Donut"; + +const data = [ + { + label: "One", + colour: "#ce4b99", + value: 35, + }, + { + label: "Two", + colour: "#b1c94e", + value: 15, + }, + { + label: "Three", + colour: "#377bbc", + value: 50, + }, + { + label: "Four", + colour: "#f49f35", + value: 100, + } +] + +export default function App() { + return +} diff --git a/src/app/donut/styles.css b/src/app/donut/styles.css new file mode 100644 index 0000000..4423406 --- /dev/null +++ b/src/app/donut/styles.css @@ -0,0 +1,19 @@ +.chart-text { + font: 16px/1.4em "Montserrat", Arial, sans-serif; + fill: #000; + transform: translateY(0.25em); +} + +.chart-number { + font-size: 0.6em; + line-height: 1; + text-anchor: middle; + transform: translateY(-0.25em); +} + +.chart-label { + font-size: 0.2em; + text-transform: uppercase; + text-anchor: middle; + transform: translateY(0.7em); +} diff --git a/src/components/Donut.tsx b/src/components/Donut.tsx new file mode 100644 index 0000000..c5ddd77 --- /dev/null +++ b/src/components/Donut.tsx @@ -0,0 +1,90 @@ +import * as R from "ramda"; +import DonutTooltip from "@/components/DonutTooltip"; +import './donut-styles.css' + +export type DonutChartData = { + label: string; + colour: string; + value: number; +}[]; + +export function Donut({data, centerLabel, formatter, hoverSuffix}: { + data: DonutChartData, + formatter: (value: number) => string, + centerLabel: string, + hoverSuffix: string, +}) { + const radius = 100 / (2 * Math.PI); + const backgroundColour = "#d2d3d4"; + + const preppedData = R.sortBy(R.compose(R.negate, R.prop('value')), data); + const total = R.sum(R.pluck('value', preppedData)); + const normalisingFactor = 100 / total; + + return ( + <> + + + {/* The hole in the middle of the donut */} + + + {/* The ring in the background of the donut, if it was not 100% full */} + + + { + preppedData.map((segment, index, array) => { + const { + label, + colour, + value + } = segment; + const precedingSegments = array.slice(0, index); + const offset = precedingSegments.reduce((acc, segment) => acc + segment.value, 0); + + return ( + + ) + }) + } + + + + {formatter(total)} + + + {centerLabel} + + + + + ); +} diff --git a/src/components/DonutTooltip.tsx b/src/components/DonutTooltip.tsx new file mode 100644 index 0000000..0c5f8f9 --- /dev/null +++ b/src/components/DonutTooltip.tsx @@ -0,0 +1,7 @@ +'use client'; + +import {Tooltip} from "react-tooltip"; + +export default function DonutTooltip() { + return +} diff --git a/src/components/OverviewPage.tsx b/src/components/OverviewPage.tsx index ee20d8d..2b9f45e 100644 --- a/src/components/OverviewPage.tsx +++ b/src/components/OverviewPage.tsx @@ -34,7 +34,7 @@ export default function OverviewPage({

{config.title}

-
+
{config.subjects.map((subject) => ( { const project = data.projects.find(p => p.projectId === entries[0].projectId)!; + const totalDuration = (entries).map((entry) => entry.duration).reduce((a, b) => a + b, 0); return ({ - name: project.name, - value: (entries).map((entry) => entry.duration).reduce((a, b) => a + b, 0), + label: project.name, + value: (totalDuration / (60 * 60)), colour: project.color, }); }) @@ -30,20 +26,15 @@ export function SubjectComparisonCard({ data: Data, }) { const breakdownData = useBreakdownData(data); - const colours = breakdownData.map((entry) => entry.colour); - - const valueFormatter = (number: number) => `${(number / (60 * 60)).toFixed(2)} hours`; return ( Relative Breakdown - v.toFixed(2)} + centerLabel={"hours"} + hoverSuffix={"h"} /> ) diff --git a/src/components/donut-styles.css b/src/components/donut-styles.css new file mode 100644 index 0000000..4423406 --- /dev/null +++ b/src/components/donut-styles.css @@ -0,0 +1,19 @@ +.chart-text { + font: 16px/1.4em "Montserrat", Arial, sans-serif; + fill: #000; + transform: translateY(0.25em); +} + +.chart-number { + font-size: 0.6em; + line-height: 1; + text-anchor: middle; + transform: translateY(-0.25em); +} + +.chart-label { + font-size: 0.2em; + text-transform: uppercase; + text-anchor: middle; + transform: translateY(0.7em); +}