add tooltip
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Rebecca-Chou 2022-09-03 01:13:11 -04:00
parent a21d4cc453
commit 934ab39dba
2 changed files with 139 additions and 74 deletions

View File

@ -6,4 +6,19 @@
.line:hover { .line:hover {
filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent)); 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);
} }

View File

@ -1,11 +1,12 @@
import { AxisBottom, AxisLeft } from "@visx/axis"; import { AxisBottom, AxisLeft } from "@visx/axis";
import { bottomTickLabelProps } from "@visx/axis/lib/axis/AxisBottom"; import { bottomTickLabelProps } from "@visx/axis/lib/axis/AxisBottom";
import { leftTickLabelProps } from "@visx/axis/lib/axis/AxisLeft"; import { leftTickLabelProps } from "@visx/axis/lib/axis/AxisLeft";
import * as allCurves from "@visx/curve"; 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 { scaleBand, scaleLinear } from "@visx/scale"; import { scaleBand, scaleLinear } from "@visx/scale";
import { LinePath } from "@visx/shape"; import { LinePath } from "@visx/shape";
import { useTooltip, useTooltipInPortal } from "@visx/tooltip";
import React from "react"; import React from "react";
import { Color } from "utils/Color"; import { Color } from "utils/Color";
@ -80,6 +81,24 @@ export function LineGraph(props: LineGraphProps) {
yAxisLabelOffset = 0, yAxisLabelOffset = 0,
} = props; } = props;
const {
tooltipData,
tooltipLeft,
tooltipTop,
tooltipOpen,
showTooltip,
hideTooltip,
} = useTooltip();
// If you don't want to use a Portal, simply replace `TooltipInPortal` below with
// `Tooltip` or `TooltipWithBounds` and remove `containerRef`
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 yMax = height - margin.top - margin.bottom;
const xMax = width - margin.left - margin.right; const xMax = width - margin.left - margin.right;
@ -106,79 +125,110 @@ export function LineGraph(props: LineGraphProps) {
}); });
return ( return (
<svg width={width} height={height}> <>
<Group top={margin.top} left={margin.left}> <svg ref={containerRef} width={width} height={height}>
<GridColumns <Group top={margin.top} left={margin.left}>
scale={xScale} <GridColumns
height={yMax} scale={xScale}
left={margin.left} height={yMax}
numTicks={5} left={margin.left}
stroke={Color.tertiaryBackground} numTicks={5}
strokeWidth={4} stroke={Color.tertiaryBackground}
strokeDasharray="10" strokeWidth={4}
strokeLinecap="round" strokeDasharray="10"
/> strokeLinecap="round"
<GridRows />
scale={yScale} <GridRows
width={xMax} scale={yScale}
left={margin.left * 2} width={xMax}
numTicks={data.xValues.length} left={margin.left * 2}
stroke={Color.tertiaryBackground} numTicks={data.xValues.length}
strokeWidth={4} stroke={Color.tertiaryBackground}
strokeDasharray="10" strokeWidth={4}
strokeLinecap="round" strokeDasharray="10"
/> strokeLinecap="round"
<AxisBottom />
scale={xScale} <AxisBottom
top={margin.top + yMax} scale={xScale}
left={margin.left} top={margin.top + yMax}
hideAxisLine left={margin.left}
hideTicks hideAxisLine
tickLabelProps={() => { hideTicks
return { tickLabelProps={() => {
...bottomTickLabelProps(), return {
className: styles.tickLabel, ...bottomTickLabelProps(),
dy: "-0.25rem", className: styles.tickLabel,
fontSize: `${xTickLabelSize / 16}rem`, dy: "-0.25rem",
width: xScale.bandwidth(), fontSize: `${xTickLabelSize / 16}rem`,
}; width: xScale.bandwidth(),
}} };
/> }}
<AxisLeft />
scale={yScale} <AxisLeft
left={margin.left} scale={yScale}
hideAxisLine left={margin.left}
hideTicks hideAxisLine
numTicks={5} hideTicks
tickLabelProps={() => { numTicks={5}
return { tickLabelProps={() => {
...leftTickLabelProps(), return {
className: styles.tickLabel, ...leftTickLabelProps(),
dx: "0.75rem", className: styles.tickLabel,
dy: "0.25rem", dx: "0.75rem",
fontSize: `${yTickLabelSize / 16}rem`, dy: "0.25rem",
}; fontSize: `${yTickLabelSize / 16}rem`,
}} };
/> }}
<Group left={margin.left + xMax / (data.xValues.length * 2)}> />
{actualData.map((lineData, i) => { <Group left={margin.left + xMax / (data.xValues.length * 2)}>
const even = i % 2 === 0; {actualData.map((lineData, i) => {
return ( const even = i % 2 === 0;
<Group key={`line-${i}`}> return (
<LinePath <Group key={`line-${i}`}>
data={lineData} <LinePath
className={styles.line} onMouseMove={(e) => {
x={(d) => xScale(getX(d)) ?? 0} const eventSvgCoords = localPoint(
y={(d) => yScale(getY(d)) ?? 0} // ownerSVGElement is given by visx docs but not recognized by typescript
stroke={even ? Color.primaryAccent : Color.secondaryAccent} // eslint-disable-next-line @typescript-eslint/ban-ts-comment
strokeWidth={3} // @ts-ignore
strokeOpacity={2} e.target.ownerSVGElement as Element,
/> e
</Group> );
); showTooltip({
})} tooltipData: data.lines[i].label,
tooltipTop: eventSvgCoords.y,
tooltipLeft: eventSvgCoords.x,
});
}}
onMouseOut={hideTooltip}
data={lineData}
className={styles.line}
x={(d) => xScale(getX(d)) ?? 0}
y={(d) => yScale(getY(d)) ?? 0}
stroke={even ? Color.primaryAccent : Color.secondaryAccent}
strokeWidth={3}
strokeOpacity={2}
/>
</Group>
);
})}
</Group>
</Group> </Group>
</Group> </svg>
</svg>
{tooltipOpen && (
<TooltipInPortal
// set this to random so it correctly updates with parent bounds
key={Math.random()}
top={tooltipTop}
left={tooltipLeft}
className={styles.tooltip}
unstyled
applyPositionStyle
>
{tooltipData}
</TooltipInPortal>
)}
</>
); );
} }