feat: add legend
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
1642f5651c
commit
6df4dfdb05
|
@ -21,4 +21,15 @@
|
|||
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);
|
||||
}
|
|
@ -4,8 +4,9 @@ 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 { LegendOrdinal } from "@visx/legend";
|
||||
import { Point } from "@visx/point";
|
||||
import { scaleBand, scaleLinear } from "@visx/scale";
|
||||
import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale";
|
||||
import { LinePath } from "@visx/shape";
|
||||
import { useTooltip, useTooltipInPortal } from "@visx/tooltip";
|
||||
import React from "react";
|
||||
|
@ -28,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. */
|
||||
|
@ -60,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 {
|
||||
|
@ -80,8 +99,16 @@ export function LineGraph(props: LineGraphProps) {
|
|||
yAxisLabel,
|
||||
yAxisLabelSize = DEFAULT_LABEL_SIZE,
|
||||
yAxisLabelOffset = 0,
|
||||
legendProps,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
position: legendPosition = "right",
|
||||
itemLabelSize: legendLabelSize = DEFAULT_LABEL_SIZE,
|
||||
itemGap: legendItemGap = DEFAULT_LEGEND_GAP,
|
||||
margin: legendMargin = {},
|
||||
} = legendProps ?? {};
|
||||
|
||||
const xLength = data.xValues.length;
|
||||
|
||||
data.lines.forEach((line) => {
|
||||
|
@ -137,8 +164,20 @@ export function LineGraph(props: LineGraphProps) {
|
|||
domain: [yMaxValue, 0],
|
||||
});
|
||||
|
||||
const keys = data.lines.map((line) => line.label);
|
||||
|
||||
const legendScale = scaleOrdinal<string, string>({
|
||||
domain: keys,
|
||||
range: [Color.primaryAccent, Color.secondaryAccent],
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={className ? `${className} ${styles.wrapper}` : styles.wrapper}
|
||||
style={{
|
||||
flexDirection: legendPosition === "right" ? "row" : "column-reverse",
|
||||
}}
|
||||
>
|
||||
<svg ref={containerRef} width={width} height={height}>
|
||||
<Group top={margin.top} left={margin.left}>
|
||||
<GridColumns
|
||||
|
@ -232,6 +271,27 @@ export function LineGraph(props: LineGraphProps) {
|
|||
</Group>
|
||||
</Group>
|
||||
</svg>
|
||||
<LegendOrdinal
|
||||
className={styles.legend}
|
||||
style={{
|
||||
marginTop: legendMargin.top,
|
||||
marginRight: legendMargin.right,
|
||||
marginBottom: legendMargin.bottom,
|
||||
marginLeft: legendMargin.left,
|
||||
fontSize: legendLabelSize,
|
||||
}}
|
||||
scale={legendScale}
|
||||
direction={legendPosition === "right" ? "column" : "row"}
|
||||
itemMargin={
|
||||
legendPosition === "right"
|
||||
? `calc(${legendItemGap / 2}rem / 16) 0 calc(${
|
||||
legendItemGap / 2
|
||||
}rem / 16) 0`
|
||||
: `0 calc(${legendItemGap / 2}rem / 16) 0 calc(${
|
||||
legendItemGap / 2
|
||||
}rem / 16)`
|
||||
}
|
||||
/>
|
||||
|
||||
{tooltipOpen && (
|
||||
<TooltipInPortal
|
||||
|
@ -244,6 +304,6 @@ export function LineGraph(props: LineGraphProps) {
|
|||
<>{tooltipData}</>
|
||||
</TooltipInPortal>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cs-2022-class-profile",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@visx/axis": "^2.10.0",
|
||||
"@visx/event": "^2.6.0",
|
||||
"@visx/grid": "^2.10.0",
|
||||
"@visx/group": "^2.10.0",
|
||||
"@visx/legend": "^2.10.0",
|
||||
"@visx/mock-data": "^2.1.2",
|
||||
"@visx/scale": "^2.2.2",
|
||||
"@visx/shape": "^2.10.0",
|
||||
|
@ -973,6 +975,21 @@
|
|||
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@visx/legend": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/legend/-/legend-2.10.0.tgz",
|
||||
"integrity": "sha512-OI8BYE6QQI9eXAng/C7UzuVw7d0fwlzrth6RmrdhlyT1K+BA3WpExapV+pDfwxu/tkEik8Ps5cZRV6HjX1/Mww==",
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"@visx/group": "2.10.0",
|
||||
"@visx/scale": "2.2.2",
|
||||
"classnames": "^2.3.1",
|
||||
"prop-types": "^15.5.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@visx/mock-data": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
||||
|
@ -5093,6 +5110,18 @@
|
|||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"@visx/legend": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@visx/legend/-/legend-2.10.0.tgz",
|
||||
"integrity": "sha512-OI8BYE6QQI9eXAng/C7UzuVw7d0fwlzrth6RmrdhlyT1K+BA3WpExapV+pDfwxu/tkEik8Ps5cZRV6HjX1/Mww==",
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"@visx/group": "2.10.0",
|
||||
"@visx/scale": "2.2.2",
|
||||
"classnames": "^2.3.1",
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"@visx/mock-data": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"@visx/event": "^2.6.0",
|
||||
"@visx/grid": "^2.10.0",
|
||||
"@visx/group": "^2.10.0",
|
||||
"@visx/legend": "^2.10.0",
|
||||
"@visx/mock-data": "^2.1.2",
|
||||
"@visx/scale": "^2.2.2",
|
||||
"@visx/shape": "^2.10.0",
|
||||
|
|
Loading…
Reference in New Issue