Worked on tooltip centering
This commit is contained in:
parent
519f925155
commit
bd757f56e9
|
@ -11,7 +11,7 @@ import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withToolti
|
|||
import React from "react";
|
||||
import { Color } from "utils/Color";
|
||||
|
||||
import { TooltipWrapper } from "./TooltipWrapper";
|
||||
import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper";
|
||||
|
||||
import styles from "./Boxplot.module.css";
|
||||
|
||||
|
@ -96,8 +96,8 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
|
|||
valueAxisLabelTopOffset = 5,
|
||||
valueAxisLabelLeftOffset = 10,
|
||||
categoryAxisLabelLeftOffset = 30,
|
||||
toolTipTopOffset = 20,
|
||||
toolTipLeftOffset = 5,
|
||||
toolTipTopOffset = 0,
|
||||
toolTipLeftOffset = 0,
|
||||
categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
|
||||
valueAxisLabelSize = DEFAULT_LABEL_SIZE,
|
||||
boxPlotWidthFactor = 0.4,
|
||||
|
@ -149,6 +149,21 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
|
|||
|
||||
const constrainedWidth = Math.min(200, xScale.bandwidth());
|
||||
|
||||
const mouseOverEventHandler =
|
||||
(d: Stats) =>
|
||||
(e: React.MouseEvent<SVGLineElement | SVGRectElement, MouseEvent>) => {
|
||||
const pos = getTooltipPosition(e);
|
||||
|
||||
showTooltip({
|
||||
tooltipLeft: pos.x + toolTipLeftOffset,
|
||||
tooltipTop: pos.y + toolTipTopOffset,
|
||||
tooltipData: {
|
||||
...d.boxPlot,
|
||||
category: getX(d),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return width < 10 ? null : (
|
||||
<div>
|
||||
<svg width={width} height={height}>
|
||||
|
@ -254,58 +269,19 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
|
|||
strokeWidth={strokeWidth}
|
||||
valueScale={yScale}
|
||||
minProps={{
|
||||
onMouseOver: () => {
|
||||
showTooltip({
|
||||
tooltipTop:
|
||||
(yScale(getMin(d)) ?? 0) + toolTipTopOffset,
|
||||
tooltipLeft:
|
||||
xScale(getX(d))! +
|
||||
constrainedWidth +
|
||||
toolTipLeftOffset,
|
||||
tooltipData: {
|
||||
...d.boxPlot,
|
||||
category: getX(d),
|
||||
},
|
||||
});
|
||||
},
|
||||
onMouseMove: mouseOverEventHandler(d),
|
||||
onMouseLeave: () => {
|
||||
hideTooltip();
|
||||
},
|
||||
}}
|
||||
maxProps={{
|
||||
onMouseOver: () => {
|
||||
showTooltip({
|
||||
tooltipTop:
|
||||
(yScale(getMax(d)) ?? 0) + toolTipTopOffset,
|
||||
tooltipLeft:
|
||||
xScale(getX(d))! +
|
||||
constrainedWidth +
|
||||
toolTipLeftOffset,
|
||||
tooltipData: {
|
||||
...d.boxPlot,
|
||||
category: getX(d),
|
||||
},
|
||||
});
|
||||
},
|
||||
onMouseMove: mouseOverEventHandler(d),
|
||||
onMouseLeave: () => {
|
||||
hideTooltip();
|
||||
},
|
||||
}}
|
||||
boxProps={{
|
||||
onMouseOver: () => {
|
||||
showTooltip({
|
||||
tooltipTop:
|
||||
(yScale(getMedian(d)) ?? 0) + toolTipTopOffset,
|
||||
tooltipLeft:
|
||||
xScale(getX(d))! +
|
||||
constrainedWidth +
|
||||
toolTipLeftOffset,
|
||||
tooltipData: {
|
||||
...d.boxPlot,
|
||||
category: getX(d),
|
||||
},
|
||||
});
|
||||
},
|
||||
onMouseMove: mouseOverEventHandler(d),
|
||||
strokeWidth: 0,
|
||||
onMouseLeave: () => {
|
||||
hideTooltip();
|
||||
|
@ -315,20 +291,7 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
|
|||
style: {
|
||||
stroke: Color.label,
|
||||
},
|
||||
onMouseOver: () => {
|
||||
showTooltip({
|
||||
tooltipTop:
|
||||
(yScale(getMedian(d)) ?? 0) + toolTipTopOffset,
|
||||
tooltipLeft:
|
||||
xScale(getX(d))! +
|
||||
constrainedWidth +
|
||||
toolTipLeftOffset,
|
||||
tooltipData: {
|
||||
...d.boxPlot,
|
||||
category: getX(d),
|
||||
},
|
||||
});
|
||||
},
|
||||
onMouseMove: mouseOverEventHandler(d),
|
||||
onMouseLeave: () => {
|
||||
hideTooltip();
|
||||
},
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
.barBackground {
|
||||
|
@ -25,4 +24,4 @@
|
|||
display: flex;
|
||||
margin: calc(16rem / 16);
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import { withTooltip } from "@visx/tooltip";
|
|||
import React, { useState } from "react";
|
||||
import { Color } from "utils/Color";
|
||||
|
||||
import { TooltipWrapper } from "./TooltipWrapper";
|
||||
import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper";
|
||||
|
||||
import styles from "./GroupedBarGraph.module.css";
|
||||
|
||||
|
@ -257,17 +257,12 @@ export const GroupedBarGraphVertical = withTooltip<
|
|||
{barGroup.bars.map((bar) => (
|
||||
<HoverableBar
|
||||
onMouseMove={(e) => {
|
||||
const eventSvgCoords = localPoint(
|
||||
// ownerSVGElement is given by visx docs but not recognized by typescript
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
e.target.ownerSVGElement as Element,
|
||||
e
|
||||
) as Point;
|
||||
const tooltipPos = getTooltipPosition(e);
|
||||
|
||||
showTooltip({
|
||||
tooltipData: bar.value.toString(),
|
||||
tooltipTop: eventSvgCoords.y,
|
||||
tooltipLeft: eventSvgCoords.x,
|
||||
tooltipTop: tooltipPos.y,
|
||||
tooltipLeft: tooltipPos.x,
|
||||
});
|
||||
}}
|
||||
onMouseOut={hideTooltip}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
display: flex;
|
||||
font-size: calc(16rem / 16);
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.key {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import localPoint from "@visx/event/lib/localPoint";
|
||||
import { Point } from "@visx/point";
|
||||
import { Tooltip } from "@visx/tooltip";
|
||||
import React from "react";
|
||||
|
||||
|
@ -11,6 +13,23 @@ type TooltipWrapperProps = {
|
|||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
// Finds the SVG Element which is the outmost from element (highest parent of element which is svg)
|
||||
|
||||
function getOutmostSVG(element: Element): SVGElement | undefined {
|
||||
let rootSVG: HTMLElement | Element | null = element ?? null;
|
||||
let current: HTMLElement | Element | null = element;
|
||||
|
||||
while (current) {
|
||||
console.log(current);
|
||||
if (current.tagName == "svg") {
|
||||
rootSVG = current;
|
||||
}
|
||||
current = current.parentElement;
|
||||
}
|
||||
|
||||
return rootSVG as SVGElement;
|
||||
}
|
||||
|
||||
const TooltipWrapper = ({
|
||||
top,
|
||||
left,
|
||||
|
@ -32,4 +51,45 @@ const TooltipWrapper = ({
|
|||
);
|
||||
};
|
||||
|
||||
export { TooltipWrapper };
|
||||
function getTooltipPosition(
|
||||
e: React.MouseEvent<
|
||||
SVGTextElement | SVGPathElement | SVGLineElement,
|
||||
MouseEvent
|
||||
>
|
||||
) {
|
||||
// ownerSVGElement is given by visx docs but not recognized by typescript
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const eventElement = e.target.ownerSVGElement as Element;
|
||||
const eventSvgCoords = localPoint(eventElement, e) as Point;
|
||||
const rootSVG: SVGElement | undefined = getOutmostSVG(eventElement);
|
||||
|
||||
if (!rootSVG) {
|
||||
console.error("Failed to find parent SVG for tooltip!");
|
||||
return { x: 0, y: 0 };
|
||||
}
|
||||
console.log(eventElement);
|
||||
console.log(rootSVG);
|
||||
console.log("parent is ", rootSVG.parentElement);
|
||||
const rootSVGLeft = rootSVG.getBoundingClientRect().left ?? 0;
|
||||
console.log(rootSVGLeft);
|
||||
|
||||
const parentDivLeft =
|
||||
rootSVG.parentElement?.getBoundingClientRect().left ?? 0;
|
||||
console.log(parentDivLeft);
|
||||
// const parentParentDivLeft =
|
||||
// eventElement.parentElement?.parentElement?.parentElement?.parentElement?.getBoundingClientRect()
|
||||
// .left ?? 0;
|
||||
// console.log(parentParentDivLeft);
|
||||
|
||||
// visx localPoint does not account for the horizontal shift due to centering of the parent element,
|
||||
// so manually add any shift from that
|
||||
const alignmentOffset = rootSVGLeft - parentDivLeft;
|
||||
console.log(alignmentOffset);
|
||||
|
||||
return {
|
||||
x: eventSvgCoords.x + alignmentOffset,
|
||||
y: eventSvgCoords.y,
|
||||
};
|
||||
}
|
||||
export { TooltipWrapper, getTooltipPosition };
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { localPoint } from "@visx/event";
|
||||
import { Point } from "@visx/point";
|
||||
import { scaleLog } from "@visx/scale";
|
||||
import { Text } from "@visx/text";
|
||||
import { useTooltip, withTooltip } from "@visx/tooltip";
|
||||
|
@ -9,7 +7,7 @@ import { Color } from "utils/Color";
|
|||
import { inDevEnvironment } from "utils/inDevEnviroment";
|
||||
import { useIsMobile } from "utils/isMobile";
|
||||
|
||||
import { TooltipWrapper } from "./TooltipWrapper";
|
||||
import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper";
|
||||
|
||||
import styles from "./WordCloud.module.css";
|
||||
|
||||
|
@ -197,33 +195,22 @@ const WordCloudWords: React.FC<WordCloudWordsProps> = ({
|
|||
className={styles.word}
|
||||
textAnchor="middle"
|
||||
onMouseMove={
|
||||
((e: React.MouseEvent<SVGTextElement, MouseEvent>) => {
|
||||
// ownerSVGElement is given by visx docs but not recognized by typescript
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const eventElement = e.target.ownerSVGElement as Element;
|
||||
const eventSvgCoords = localPoint(eventElement, e) as Point;
|
||||
const rootSVGLeft =
|
||||
eventElement.parentElement?.parentElement?.getBoundingClientRect()
|
||||
.left ?? 0;
|
||||
const parentDivLeft =
|
||||
eventElement.parentElement?.parentElement?.parentElement?.getBoundingClientRect()
|
||||
.left ?? 0;
|
||||
|
||||
// visx localPoint does not account for the horizontal shift due to centering of the parent element,
|
||||
// so manually add any shift from that
|
||||
const alignmentOffset = rootSVGLeft - parentDivLeft;
|
||||
|
||||
((
|
||||
e: React.MouseEvent<
|
||||
SVGTextElement | SVGLineElement,
|
||||
MouseEvent
|
||||
>
|
||||
) => {
|
||||
const tooltipPos = getTooltipPosition(e);
|
||||
if (word.text) {
|
||||
showTooltip(
|
||||
{
|
||||
text: word.text,
|
||||
value: (cloudWords[index] as WordData).value,
|
||||
},
|
||||
eventSvgCoords.x -
|
||||
word.text.length * TOOLTIP_HORIZONTAL_SHIFT_SCALER +
|
||||
alignmentOffset,
|
||||
eventSvgCoords.y
|
||||
tooltipPos.x -
|
||||
word.text.length * TOOLTIP_HORIZONTAL_SHIFT_SCALER,
|
||||
tooltipPos.y
|
||||
);
|
||||
}
|
||||
}) as React.MouseEventHandler<SVGTextElement>
|
||||
|
|
|
@ -18,6 +18,7 @@ import React from "react";
|
|||
import { Color } from "utils/Color";
|
||||
|
||||
import { About } from "@/components/About";
|
||||
import { ComponentWrapper } from "@/components/ComponentWrapper";
|
||||
import {
|
||||
GroupedBarGraphHorizontal,
|
||||
GroupedBarGraphVertical,
|
||||
|
@ -26,6 +27,7 @@ import { LineGraph } from "@/components/LineGraph";
|
|||
import { PieChart } from "@/components/PieChart";
|
||||
import { QuotationCarousel } from "@/components/QuotationCarousel";
|
||||
import { Sections } from "@/components/Sections";
|
||||
import { SectionWrapper } from "@/components/SectionWrapper";
|
||||
import {
|
||||
StackedBarGraphVertical,
|
||||
StackedBarGraphHorizontal,
|
||||
|
@ -67,18 +69,7 @@ export default function Home() {
|
|||
<h2>
|
||||
<code>{"<BarGraphHorizontal />"}</code>
|
||||
</h2>
|
||||
<BarGraphHorizontal
|
||||
className={styles.barGraphDemo}
|
||||
data={mockCategoricalData}
|
||||
width={800}
|
||||
height={500}
|
||||
margin={{
|
||||
top: 25,
|
||||
bottom: 40,
|
||||
left: 150,
|
||||
right: 20,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
<h2>
|
||||
<code>{"<BarGraphVertical />"}</code>
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import {
|
||||
mockBoxPlotData,
|
||||
mockCategoricalData,
|
||||
mockGroupedBarGraphData,
|
||||
mockLineData,
|
||||
mockPieData,
|
||||
mockStackedBarGraphData,
|
||||
mockStackedBarKeys,
|
||||
moreMockCategoricalData,
|
||||
} from "data/mocks";
|
||||
import { pageRoutes } from "data/routes";
|
||||
|
@ -14,9 +18,19 @@ import { BarGraphVertical, BarGraphHorizontal } from "@/components/BarGraph";
|
|||
import { BottomNav } from "@/components/BottomNav";
|
||||
import { BoxPlot } from "@/components/Boxplot";
|
||||
import { ComponentWrapper } from "@/components/ComponentWrapper";
|
||||
import {
|
||||
GroupedBarGraphHorizontal,
|
||||
GroupedBarGraphVertical,
|
||||
} from "@/components/GroupedBarGraph";
|
||||
import { Header } from "@/components/Header";
|
||||
import { LineGraph } from "@/components/LineGraph";
|
||||
import { PieChart } from "@/components/PieChart";
|
||||
import { SectionHeader } from "@/components/SectionHeader";
|
||||
import { SectionWrapper } from "@/components/SectionWrapper";
|
||||
import {
|
||||
StackedBarGraphVertical,
|
||||
StackedBarGraphHorizontal,
|
||||
} from "@/components/StackedBarGraph";
|
||||
import { WordCloud } from "@/components/WordCloud";
|
||||
|
||||
import styles from "./samplePage.module.css";
|
||||
|
@ -46,6 +60,128 @@ export default function SamplePage() {
|
|||
return (
|
||||
<div className={styles.page}>
|
||||
<Header />
|
||||
<SectionWrapper title="Transfer" />
|
||||
<ComponentWrapper align="right" heading={""}>
|
||||
<PieChart data={mockPieData} width={800} labelWidth={215} />
|
||||
<BarGraphHorizontal
|
||||
className={styles.barGraphDemo}
|
||||
data={mockCategoricalData}
|
||||
width={800}
|
||||
height={500}
|
||||
margin={{
|
||||
top: 25,
|
||||
bottom: 40,
|
||||
left: 150,
|
||||
right: 20,
|
||||
}}
|
||||
/>
|
||||
<BarGraphVertical
|
||||
className={styles.barGraphDemo}
|
||||
data={mockCategoricalData}
|
||||
width={800}
|
||||
height={500}
|
||||
margin={{
|
||||
top: 20,
|
||||
bottom: 80,
|
||||
left: 60,
|
||||
right: 20,
|
||||
}}
|
||||
/>
|
||||
<StackedBarGraphVertical
|
||||
width={600}
|
||||
height={400}
|
||||
keys={mockStackedBarKeys}
|
||||
colorRange={[
|
||||
Color.primaryAccent,
|
||||
Color.secondaryAccentLight,
|
||||
Color.primaryAccentLighter,
|
||||
]}
|
||||
data={mockStackedBarGraphData}
|
||||
margin={{
|
||||
top: 20,
|
||||
left: 20,
|
||||
}}
|
||||
/>
|
||||
<StackedBarGraphHorizontal
|
||||
width={600}
|
||||
height={400}
|
||||
keys={mockStackedBarKeys}
|
||||
colorRange={[
|
||||
Color.primaryAccent,
|
||||
Color.secondaryAccentLight,
|
||||
Color.primaryAccentLighter,
|
||||
]}
|
||||
data={mockStackedBarGraphData}
|
||||
margin={{
|
||||
top: 20,
|
||||
left: 20,
|
||||
}}
|
||||
/>
|
||||
|
||||
<LineGraph
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
data={mockLineData}
|
||||
width={600}
|
||||
height={400}
|
||||
margin={{
|
||||
top: 20,
|
||||
bottom: 80,
|
||||
left: 30,
|
||||
right: 20,
|
||||
}}
|
||||
colorRange={[Color.primaryAccent, Color.secondaryAccentLight]}
|
||||
/>
|
||||
<GroupedBarGraphHorizontal
|
||||
className={styles.barGraphDemo}
|
||||
data={mockGroupedBarGraphData}
|
||||
barColors={[Color.primaryAccentLight, Color.secondaryAccentLight]}
|
||||
barHoverColorsMap={{
|
||||
[Color.primaryAccentLight]: Color.primaryAccent,
|
||||
[Color.secondaryAccentLight]: Color.secondaryAccent,
|
||||
}}
|
||||
width={600}
|
||||
height={400}
|
||||
margin={{
|
||||
top: 20,
|
||||
bottom: 40,
|
||||
left: 60,
|
||||
right: 20,
|
||||
}}
|
||||
/>
|
||||
<GroupedBarGraphVertical
|
||||
className={styles.barGraphDemo}
|
||||
data={mockGroupedBarGraphData}
|
||||
barColors={[Color.primaryAccentLight, Color.secondaryAccentLight]}
|
||||
barHoverColorsMap={{
|
||||
[Color.primaryAccentLight]: Color.primaryAccent,
|
||||
[Color.secondaryAccentLight]: Color.secondaryAccent,
|
||||
}}
|
||||
width={500}
|
||||
height={400}
|
||||
margin={{
|
||||
top: 20,
|
||||
bottom: 40,
|
||||
left: 50,
|
||||
right: 20,
|
||||
}}
|
||||
/>
|
||||
<BoxPlot
|
||||
width={600}
|
||||
height={400}
|
||||
data={mockBoxPlotData}
|
||||
margin={{
|
||||
top: 20,
|
||||
left: 20,
|
||||
}}
|
||||
/>
|
||||
|
||||
<WordCloud
|
||||
data={moreMockCategoricalData.map((word) => ({
|
||||
text: word.key,
|
||||
value: word.value,
|
||||
}))}
|
||||
/>
|
||||
</ComponentWrapper>
|
||||
<SectionHeader
|
||||
title="Demographics"
|
||||
subtitle="An insight into the demographics of UW’s CS programs"
|
||||
|
|
Loading…
Reference in New Issue