diff --git a/.eslintrc.js b/.eslintrc.js
index 7beb879..b5ff0bf 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -17,8 +17,7 @@ module.exports = {
],
plugins: ["@typescript-eslint", "react", "react-hooks", "prettier"],
rules: {
- "prettier/prettier": "error",
-
+ "prettier/prettier": ["error", { "endOfLine": "auto" }],
"import/first": "error",
"import/order": [
"error",
diff --git a/components/BarGraph.module.css b/components/BarGraph.module.css
index 7ccdc8b..d709ac5 100644
--- a/components/BarGraph.module.css
+++ b/components/BarGraph.module.css
@@ -33,4 +33,4 @@
font-family: "Inconsolata", monospace;
font-weight: 800;
fill: var(--label);
-}
+}
\ No newline at end of file
diff --git a/components/LineGraph.module.css b/components/LineGraph.module.css
index e69de29..bb8cdfe 100644
--- a/components/LineGraph.module.css
+++ b/components/LineGraph.module.css
@@ -0,0 +1,35 @@
+.tickLabel {
+ font-family: "Inconsolata", monospace;
+ font-weight: 800;
+ fill: var(--label);
+}
+
+.line:hover {
+ filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent));
+}
+
+.tooltip {
+ font-family: "Inconsolata", monospace;
+ font-weight: bold;
+ top: 0;
+ left: 0;
+ position: absolute;
+ background-color: var(--label);
+ color: var(--primary-background);
+ box-shadow: 0px calc(1rem / 16) calc(2rem / 16) var(--card-background);
+ pointer-events: none;
+ padding: calc(10rem / 16);
+ font-size: calc(18rem / 16);
+ border-radius: calc(10rem / 16);
+}
+
+.wrapper {
+ display: flex;
+ align-items: center;
+ width: min-content;
+}
+
+.legend {
+ display: flex;
+ margin: calc(16rem / 8);
+}
\ No newline at end of file
diff --git a/components/LineGraph.tsx b/components/LineGraph.tsx
index d39b4de..673bd05 100644
--- a/components/LineGraph.tsx
+++ b/components/LineGraph.tsx
@@ -1,22 +1,18 @@
import { AxisBottom, AxisLeft } from "@visx/axis";
import { bottomTickLabelProps } from "@visx/axis/lib/axis/AxisBottom";
-import * as allCurves from "@visx/curve";
+import { leftTickLabelProps } from "@visx/axis/lib/axis/AxisLeft";
+import { localPoint } from "@visx/event";
+import { GridColumns, GridRows } from "@visx/grid";
import { Group } from "@visx/group";
-import { scaleBand, scaleLinear } from "@visx/scale";
+import { LegendOrdinal } from "@visx/legend";
+import { Point } from "@visx/point";
+import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale";
import { LinePath } from "@visx/shape";
-import { extent, max } from "d3-array";
+import { useTooltip, useTooltipInPortal } from "@visx/tooltip";
+import React from "react";
import { Color } from "utils/Color";
-type CurveType = keyof typeof allCurves;
-
-const curveTypes = Object.keys(allCurves);
-const lineCount = 2;
-// const series = new Array(lineCount).fill(null).map((_, i) =>
-// // vary each series value deterministically
-// generateDateValue(25, /* seed= */ i / 72).sort(
-// (a: DateValue, b: DateValue) => a.date.getTime() - b.date.getTime()
-// )
-// );
+import styles from "./LineGraph.module.css";
interface LineData {
label: string;
@@ -33,6 +29,22 @@ interface LineGraphData {
lines: LineData[];
}
+interface LegendProps {
+ /** Position of the legend, relative to the graph. */
+ position?: "top" | "right";
+ /** Font size of the labels in the legend, in pixels. Default is 16px. */
+ itemLabelSize?: number;
+ /** Gap between items in the legend, in pixels. */
+ itemGap?: number;
+ /** Distance between the legend and other adjacent elements, in pixels. */
+ margin?: {
+ top?: number;
+ bottom?: number;
+ left?: number;
+ right?: number;
+ };
+}
+
interface LineGraphProps {
data: LineGraphData;
/** Width of the entire graph, in pixels. */
@@ -65,9 +77,11 @@ interface LineGraphProps {
yAxisLabelSize?: number;
/** Controls the distance between the value axis label and the value axis. */
yAxisLabelOffset?: number;
+ legendProps?: LegendProps;
}
const DEFAULT_LABEL_SIZE = 16;
+const DEFAULT_LEGEND_GAP = 16;
export function LineGraph(props: LineGraphProps) {
const {
@@ -85,14 +99,39 @@ export function LineGraph(props: LineGraphProps) {
yAxisLabel,
yAxisLabelSize = DEFAULT_LABEL_SIZE,
yAxisLabelOffset = 0,
+ legendProps,
} = props;
- const curveType = "curveLinear";
- const svgHeight = height - 40;
- //const allData = data.reduce((rec, d) => rec.concat(d), []);
- //console.log(allData);
+ const {
+ position: legendPosition = "right",
+ itemLabelSize: legendLabelSize = DEFAULT_LABEL_SIZE,
+ itemGap: legendItemGap = DEFAULT_LEGEND_GAP,
+ margin: legendMargin = {},
+ } = legendProps ?? {};
- // update scale output ranges
+ const xLength = data.xValues.length;
+
+ data.lines.forEach((line) => {
+ if (line.yValues.length != xLength) {
+ throw new Error("Invalid data with wrong length.");
+ }
+ });
+
+ const {
+ tooltipData,
+ tooltipLeft,
+ tooltipTop,
+ tooltipOpen,
+ showTooltip,
+ hideTooltip,
+ } = useTooltip();
+
+ const { containerRef, TooltipInPortal } = useTooltipInPortal({
+ // use TooltipWithBounds
+ detectBounds: true,
+ // when tooltip containers are scrolled, this will correctly update the Tooltip position
+ scroll: true,
+ });
const yMax = height - margin.top - margin.bottom;
const xMax = width - margin.left - margin.right;
@@ -103,6 +142,12 @@ export function LineGraph(props: LineGraphProps) {
});
});
+ const yMaxValue = Math.max(
+ ...data.lines.map((line) => {
+ return Math.max(...line.yValues);
+ })
+ );
+
// data accessors
const getX = (d: PointData) => d.x;
const getY = (d: PointData) => d.y;
@@ -112,56 +157,153 @@ export function LineGraph(props: LineGraphProps) {
range: [0, xMax],
domain: data.xValues,
});
+
const yScale = scaleLinear({
range: [0, yMax],
nice: true,
- domain: [0, 100],
+ domain: [yMaxValue, 0],
+ });
+
+ const keys = data.lines.map((line) => line.label);
+
+ const legendScale = scaleOrdinal({
+ domain: keys,
+ range: [Color.primaryAccent, Color.secondaryAccent],
});
return (
-
+
+ {""}
+
+
+
{""}