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);
|
padding: calc(10rem / 16);
|
||||||
font-size: calc(18rem / 16);
|
font-size: calc(18rem / 16);
|
||||||
border-radius: calc(10rem / 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 { localPoint } from "@visx/event";
|
||||||
import { GridColumns, GridRows } from "@visx/grid";
|
import { GridColumns, GridRows } from "@visx/grid";
|
||||||
import { Group } from "@visx/group";
|
import { Group } from "@visx/group";
|
||||||
|
import { LegendOrdinal } from "@visx/legend";
|
||||||
import { Point } from "@visx/point";
|
import { Point } from "@visx/point";
|
||||||
import { scaleBand, scaleLinear } from "@visx/scale";
|
import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale";
|
||||||
import { LinePath } from "@visx/shape";
|
import { LinePath } from "@visx/shape";
|
||||||
import { useTooltip, useTooltipInPortal } from "@visx/tooltip";
|
import { useTooltip, useTooltipInPortal } from "@visx/tooltip";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -28,6 +29,22 @@ interface LineGraphData {
|
||||||
lines: LineData[];
|
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 {
|
interface LineGraphProps {
|
||||||
data: LineGraphData;
|
data: LineGraphData;
|
||||||
/** Width of the entire graph, in pixels. */
|
/** Width of the entire graph, in pixels. */
|
||||||
|
@ -60,9 +77,11 @@ interface LineGraphProps {
|
||||||
yAxisLabelSize?: number;
|
yAxisLabelSize?: number;
|
||||||
/** Controls the distance between the value axis label and the value axis. */
|
/** Controls the distance between the value axis label and the value axis. */
|
||||||
yAxisLabelOffset?: number;
|
yAxisLabelOffset?: number;
|
||||||
|
legendProps?: LegendProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_LABEL_SIZE = 16;
|
const DEFAULT_LABEL_SIZE = 16;
|
||||||
|
const DEFAULT_LEGEND_GAP = 16;
|
||||||
|
|
||||||
export function LineGraph(props: LineGraphProps) {
|
export function LineGraph(props: LineGraphProps) {
|
||||||
const {
|
const {
|
||||||
|
@ -80,8 +99,16 @@ export function LineGraph(props: LineGraphProps) {
|
||||||
yAxisLabel,
|
yAxisLabel,
|
||||||
yAxisLabelSize = DEFAULT_LABEL_SIZE,
|
yAxisLabelSize = DEFAULT_LABEL_SIZE,
|
||||||
yAxisLabelOffset = 0,
|
yAxisLabelOffset = 0,
|
||||||
|
legendProps,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
position: legendPosition = "right",
|
||||||
|
itemLabelSize: legendLabelSize = DEFAULT_LABEL_SIZE,
|
||||||
|
itemGap: legendItemGap = DEFAULT_LEGEND_GAP,
|
||||||
|
margin: legendMargin = {},
|
||||||
|
} = legendProps ?? {};
|
||||||
|
|
||||||
const xLength = data.xValues.length;
|
const xLength = data.xValues.length;
|
||||||
|
|
||||||
data.lines.forEach((line) => {
|
data.lines.forEach((line) => {
|
||||||
|
@ -137,8 +164,20 @@ export function LineGraph(props: LineGraphProps) {
|
||||||
domain: [yMaxValue, 0],
|
domain: [yMaxValue, 0],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const keys = data.lines.map((line) => line.label);
|
||||||
|
|
||||||
|
const legendScale = scaleOrdinal<string, string>({
|
||||||
|
domain: keys,
|
||||||
|
range: [Color.primaryAccent, Color.secondaryAccent],
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
|
className={className ? `${className} ${styles.wrapper}` : styles.wrapper}
|
||||||
|
style={{
|
||||||
|
flexDirection: legendPosition === "right" ? "row" : "column-reverse",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<svg ref={containerRef} width={width} height={height}>
|
<svg ref={containerRef} width={width} height={height}>
|
||||||
<Group top={margin.top} left={margin.left}>
|
<Group top={margin.top} left={margin.left}>
|
||||||
<GridColumns
|
<GridColumns
|
||||||
|
@ -232,6 +271,27 @@ export function LineGraph(props: LineGraphProps) {
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</svg>
|
</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 && (
|
{tooltipOpen && (
|
||||||
<TooltipInPortal
|
<TooltipInPortal
|
||||||
|
@ -244,6 +304,6 @@ export function LineGraph(props: LineGraphProps) {
|
||||||
<>{tooltipData}</>
|
<>{tooltipData}</>
|
||||||
</TooltipInPortal>
|
</TooltipInPortal>
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
|
"name": "cs-2022-class-profile",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@visx/axis": "^2.10.0",
|
"@visx/axis": "^2.10.0",
|
||||||
"@visx/event": "^2.6.0",
|
"@visx/event": "^2.6.0",
|
||||||
"@visx/grid": "^2.10.0",
|
"@visx/grid": "^2.10.0",
|
||||||
"@visx/group": "^2.10.0",
|
"@visx/group": "^2.10.0",
|
||||||
|
"@visx/legend": "^2.10.0",
|
||||||
"@visx/mock-data": "^2.1.2",
|
"@visx/mock-data": "^2.1.2",
|
||||||
"@visx/scale": "^2.2.2",
|
"@visx/scale": "^2.2.2",
|
||||||
"@visx/shape": "^2.10.0",
|
"@visx/shape": "^2.10.0",
|
||||||
|
@ -973,6 +975,21 @@
|
||||||
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
|
"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": {
|
"node_modules/@visx/mock-data": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
||||||
|
@ -5093,6 +5110,18 @@
|
||||||
"prop-types": "^15.6.2"
|
"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": {
|
"@visx/mock-data": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@visx/event": "^2.6.0",
|
"@visx/event": "^2.6.0",
|
||||||
"@visx/grid": "^2.10.0",
|
"@visx/grid": "^2.10.0",
|
||||||
"@visx/group": "^2.10.0",
|
"@visx/group": "^2.10.0",
|
||||||
|
"@visx/legend": "^2.10.0",
|
||||||
"@visx/mock-data": "^2.1.2",
|
"@visx/mock-data": "^2.1.2",
|
||||||
"@visx/scale": "^2.2.2",
|
"@visx/scale": "^2.2.2",
|
||||||
"@visx/shape": "^2.10.0",
|
"@visx/shape": "^2.10.0",
|
||||||
|
|
Loading…
Reference in New Issue