WIP Fix Stacked Bar Graph
This commit is contained in:
parent
803457c083
commit
047889162e
|
@ -8,17 +8,10 @@
|
||||||
|
|
||||||
.legend {
|
.legend {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(16rem / 16);
|
font-size: calc(16rem / 16);
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tickLabel {
|
|
||||||
font-family: "Inconsolata", monospace;
|
|
||||||
font-weight: 800;
|
|
||||||
fill: var(--label);
|
|
||||||
}
|
|
||||||
|
|
||||||
.key {
|
.key {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
|
@ -43,14 +43,17 @@ export type StackedBarProps = {
|
||||||
/** Colours for each key */
|
/** Colours for each key */
|
||||||
colorRange: string[];
|
colorRange: string[];
|
||||||
/** Distance between the edge of the graph and the area where the bars are drawn, in pixels. */
|
/** Distance between the edge of the graph and the area where the bars are drawn, in pixels. */
|
||||||
margin: {
|
margin: { top: number; left: number };
|
||||||
top: number;
|
|
||||||
left: number;
|
|
||||||
bottom: number;
|
|
||||||
right: number;
|
|
||||||
};
|
|
||||||
/** Number of ticks for the value axis */
|
/** Number of ticks for the value axis */
|
||||||
numTicksValueAxis?: number;
|
numTicksValueAxis?: number;
|
||||||
|
/** Distance between the left axis labels and the start of the lines of the graph, in px. */
|
||||||
|
axisLeftOffset?: number;
|
||||||
|
/** Distance between the bottom axis and the bottom of the container of the graph, in px. */
|
||||||
|
axisBottomOffset?: number;
|
||||||
|
/** Distance between the right side of the graph and the legend, in px. */
|
||||||
|
legendLeftOffset?: number;
|
||||||
|
/** Distance between the top of the graph and the legend, in px. */
|
||||||
|
legendTopOffset?: number;
|
||||||
/** Width of the lines in the graph, in px. */
|
/** Width of the lines in the graph, in px. */
|
||||||
strokeWidth?: number;
|
strokeWidth?: number;
|
||||||
/** Length of the dashes and the gaps in the graph, in px. */
|
/** Length of the dashes and the gaps in the graph, in px. */
|
||||||
|
@ -59,6 +62,9 @@ export type StackedBarProps = {
|
||||||
scalePadding?: number;
|
scalePadding?: number;
|
||||||
/** Margin for each item in the legend */
|
/** Margin for each item in the legend */
|
||||||
itemMargin?: string;
|
itemMargin?: string;
|
||||||
|
/** Factor multiplied with an offset to center the labels of the category-axis depending on the width/height of the graph.
|
||||||
|
* >1 for width/height <600 and <1 for width/height >600 (vertical=width/horizontal=height) */
|
||||||
|
categoryAxisLeftFactor?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
let tooltipTimeout: number;
|
let tooltipTimeout: number;
|
||||||
|
@ -76,9 +82,14 @@ export const StackedBarGraphVertical = withTooltip<
|
||||||
margin,
|
margin,
|
||||||
scalePadding = 0.3,
|
scalePadding = 0.3,
|
||||||
numTicksValueAxis = 6,
|
numTicksValueAxis = 6,
|
||||||
|
axisLeftOffset = 40,
|
||||||
|
axisBottomOffset = 40,
|
||||||
strokeWidth = 2.5,
|
strokeWidth = 2.5,
|
||||||
strokeDashArray = "10,4",
|
strokeDashArray = "10,4",
|
||||||
|
legendLeftOffset = 40,
|
||||||
|
legendTopOffset = 40,
|
||||||
itemMargin = "0 0 0 15px",
|
itemMargin = "0 0 0 15px",
|
||||||
|
categoryAxisLeftFactor = 1,
|
||||||
tooltipOpen,
|
tooltipOpen,
|
||||||
tooltipLeft,
|
tooltipLeft,
|
||||||
tooltipTop,
|
tooltipTop,
|
||||||
|
@ -115,15 +126,18 @@ export const StackedBarGraphVertical = withTooltip<
|
||||||
});
|
});
|
||||||
|
|
||||||
// bounds
|
// bounds
|
||||||
const xMax = width - margin.left - margin.right;
|
const xMax = width;
|
||||||
const yMax = height - margin.top - margin.bottom;
|
const yMax = height - margin.top - axisBottomOffset;
|
||||||
|
|
||||||
categoryScale.rangeRound([0, xMax]);
|
categoryScale.rangeRound([0, xMax - axisLeftOffset]);
|
||||||
valueScale.range([yMax, 0]);
|
valueScale.range([yMax, 0]);
|
||||||
|
|
||||||
return width < 10 ? null : (
|
return width < 10 ? null : (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.legend}>
|
<div
|
||||||
|
className={styles.legend}
|
||||||
|
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
|
||||||
|
>
|
||||||
<LegendOrdinal
|
<LegendOrdinal
|
||||||
scale={colorScale}
|
scale={colorScale}
|
||||||
direction="row"
|
direction="row"
|
||||||
|
@ -138,6 +152,7 @@ export const StackedBarGraphVertical = withTooltip<
|
||||||
scale={valueScale}
|
scale={valueScale}
|
||||||
width={xMax}
|
width={xMax}
|
||||||
height={yMax}
|
height={yMax}
|
||||||
|
left={axisLeftOffset}
|
||||||
numTicks={numTicksValueAxis}
|
numTicks={numTicksValueAxis}
|
||||||
stroke={Color.tertiaryBackground}
|
stroke={Color.tertiaryBackground}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
|
@ -146,89 +161,94 @@ export const StackedBarGraphVertical = withTooltip<
|
||||||
<GridColumns
|
<GridColumns
|
||||||
scale={categoryScale}
|
scale={categoryScale}
|
||||||
height={yMax}
|
height={yMax}
|
||||||
|
left={axisLeftOffset}
|
||||||
|
offset={categoryScale.bandwidth() / 2}
|
||||||
stroke={Color.tertiaryBackground}
|
stroke={Color.tertiaryBackground}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
strokeDasharray={strokeDashArray}
|
strokeDasharray={strokeDashArray}
|
||||||
/>
|
/>
|
||||||
<BarStack<StackedBarData, string>
|
<Group left={axisLeftOffset}>
|
||||||
data={data}
|
<BarStack<StackedBarData, string>
|
||||||
keys={keys}
|
data={data}
|
||||||
x={getCategory}
|
keys={keys}
|
||||||
xScale={categoryScale}
|
x={getCategory}
|
||||||
yScale={valueScale}
|
xScale={categoryScale}
|
||||||
color={colorScale}
|
yScale={valueScale}
|
||||||
>
|
color={colorScale}
|
||||||
{(barStacks) =>
|
>
|
||||||
barStacks.map((barStack) =>
|
{(barStacks) =>
|
||||||
barStack.bars.map((bar) => (
|
barStacks.map((barStack) =>
|
||||||
<rect
|
barStack.bars.map((bar) => (
|
||||||
className={styles.barStack}
|
<rect
|
||||||
key={`bar-stack-${barStack.index}-${bar.index}`}
|
className={styles.barStack}
|
||||||
x={bar.x}
|
key={`bar-stack-${barStack.index}-${bar.index}`}
|
||||||
y={bar.y}
|
x={bar.x}
|
||||||
height={bar.height}
|
y={bar.y}
|
||||||
width={bar.width}
|
height={bar.height}
|
||||||
fill={bar.color}
|
width={bar.width / 2}
|
||||||
onMouseLeave={() => {
|
fill={bar.color}
|
||||||
tooltipTimeout = window.setTimeout(() => {
|
onMouseLeave={() => {
|
||||||
hideTooltip();
|
tooltipTimeout = window.setTimeout(() => {
|
||||||
}, 300);
|
hideTooltip();
|
||||||
}}
|
}, 300);
|
||||||
onMouseMove={(event) => {
|
}}
|
||||||
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
onMouseMove={(event) => {
|
||||||
const eventSvgCoords = localPoint(event);
|
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
||||||
const left = bar.x + bar.width / 2;
|
const eventSvgCoords = localPoint(event);
|
||||||
showTooltip({
|
const left = bar.x + bar.width / 2;
|
||||||
tooltipData: bar,
|
showTooltip({
|
||||||
tooltipTop: eventSvgCoords?.y,
|
tooltipData: bar,
|
||||||
tooltipLeft: left,
|
tooltipTop: eventSvgCoords?.y,
|
||||||
});
|
tooltipLeft: left,
|
||||||
}}
|
});
|
||||||
/>
|
}}
|
||||||
))
|
/>
|
||||||
)
|
))
|
||||||
}
|
)
|
||||||
</BarStack>
|
}
|
||||||
|
</BarStack>
|
||||||
|
</Group>
|
||||||
<Line
|
<Line
|
||||||
fill={Color.tertiaryBackground}
|
fill={Color.tertiaryBackground}
|
||||||
to={new Point({ x: 0, y: 0 })}
|
to={new Point({ x: axisLeftOffset, y: 0 })}
|
||||||
from={new Point({ x: 0, y: yMax })}
|
from={new Point({ x: axisLeftOffset, y: yMax })}
|
||||||
stroke={Color.tertiaryBackground}
|
stroke={Color.tertiaryBackground}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
strokeDasharray={strokeDashArray}
|
strokeDasharray={strokeDashArray}
|
||||||
/>
|
/>
|
||||||
</Group>
|
<AxisBottom
|
||||||
<AxisBottom
|
top={yMax}
|
||||||
top={yMax}
|
scale={categoryScale}
|
||||||
scale={categoryScale}
|
left={
|
||||||
hideTicks
|
((categoryScale.bandwidth() * 100) / width) *
|
||||||
hideAxisLine
|
categoryAxisLeftFactor
|
||||||
labelProps={{
|
}
|
||||||
fontSize: `${10 / 16}rem`,
|
hideTicks
|
||||||
}}
|
hideAxisLine
|
||||||
tickLabelProps={() => ({
|
labelProps={{
|
||||||
className: styles.tickLabel,
|
fontSize: `${10 / 16}rem`,
|
||||||
fill: Color.label,
|
}}
|
||||||
fontWeight: TICK_LABEL_FONT_WEIGHT,
|
tickLabelProps={() => ({
|
||||||
width: categoryScale.bandwidth(),
|
|
||||||
verticalAnchor: "start",
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<AxisLeft
|
|
||||||
scale={valueScale}
|
|
||||||
top={5}
|
|
||||||
numTicks={numTicksValueAxis}
|
|
||||||
hideAxisLine
|
|
||||||
labelProps={{
|
|
||||||
fontSize: `${10 / 16}rem`,
|
|
||||||
}}
|
|
||||||
tickLabelProps={() => {
|
|
||||||
return {
|
|
||||||
fill: Color.label,
|
fill: Color.label,
|
||||||
fontWeight: TICK_LABEL_FONT_WEIGHT,
|
fontWeight: TICK_LABEL_FONT_WEIGHT,
|
||||||
};
|
})}
|
||||||
}}
|
/>
|
||||||
/>
|
<AxisLeft
|
||||||
|
scale={valueScale}
|
||||||
|
top={5}
|
||||||
|
numTicks={numTicksValueAxis}
|
||||||
|
hideAxisLine
|
||||||
|
labelProps={{
|
||||||
|
fontSize: `${10 / 16}rem`,
|
||||||
|
}}
|
||||||
|
tickLabelProps={() => {
|
||||||
|
return {
|
||||||
|
fill: Color.label,
|
||||||
|
fontWeight: TICK_LABEL_FONT_WEIGHT,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{tooltipOpen && tooltipData ? (
|
{tooltipOpen && tooltipData ? (
|
||||||
|
@ -259,9 +279,14 @@ export const StackedBarGraphHorizontal = withTooltip<
|
||||||
margin,
|
margin,
|
||||||
scalePadding = 0.3,
|
scalePadding = 0.3,
|
||||||
numTicksValueAxis = 6,
|
numTicksValueAxis = 6,
|
||||||
|
axisLeftOffset = 40,
|
||||||
|
axisBottomOffset = 40,
|
||||||
strokeWidth = 2.5,
|
strokeWidth = 2.5,
|
||||||
strokeDashArray = "10,4",
|
strokeDashArray = "10,4",
|
||||||
|
legendLeftOffset = 40,
|
||||||
|
legendTopOffset = 40,
|
||||||
itemMargin = "0 0 0 15px",
|
itemMargin = "0 0 0 15px",
|
||||||
|
categoryAxisLeftFactor = 1,
|
||||||
tooltipOpen,
|
tooltipOpen,
|
||||||
tooltipLeft,
|
tooltipLeft,
|
||||||
tooltipTop,
|
tooltipTop,
|
||||||
|
@ -298,15 +323,18 @@ export const StackedBarGraphHorizontal = withTooltip<
|
||||||
});
|
});
|
||||||
|
|
||||||
// bounds
|
// bounds
|
||||||
const xMax = width - margin.left - margin.right;
|
const xMax = width;
|
||||||
const yMax = height - margin.top - margin.bottom;
|
const yMax = height - margin.top - axisBottomOffset;
|
||||||
|
|
||||||
categoryScale.rangeRound([yMax, 0]);
|
categoryScale.rangeRound([yMax, 0]);
|
||||||
valueScale.range([0, xMax]);
|
valueScale.range([0, xMax - axisLeftOffset]);
|
||||||
|
|
||||||
return width < 10 ? null : (
|
return width < 10 ? null : (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.legend}>
|
<div
|
||||||
|
className={styles.legend}
|
||||||
|
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
|
||||||
|
>
|
||||||
<LegendOrdinal
|
<LegendOrdinal
|
||||||
scale={colorScale}
|
scale={colorScale}
|
||||||
direction="row"
|
direction="row"
|
||||||
|
@ -321,6 +349,7 @@ export const StackedBarGraphHorizontal = withTooltip<
|
||||||
width={xMax}
|
width={xMax}
|
||||||
height={yMax}
|
height={yMax}
|
||||||
offset={categoryScale.bandwidth() / 2}
|
offset={categoryScale.bandwidth() / 2}
|
||||||
|
left={axisLeftOffset}
|
||||||
stroke={Color.tertiaryBackground}
|
stroke={Color.tertiaryBackground}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
strokeDasharray={strokeDashArray}
|
strokeDasharray={strokeDashArray}
|
||||||
|
@ -329,52 +358,56 @@ export const StackedBarGraphHorizontal = withTooltip<
|
||||||
scale={valueScale}
|
scale={valueScale}
|
||||||
height={yMax}
|
height={yMax}
|
||||||
numTicks={numTicksValueAxis}
|
numTicks={numTicksValueAxis}
|
||||||
|
left={axisLeftOffset}
|
||||||
stroke={Color.tertiaryBackground}
|
stroke={Color.tertiaryBackground}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
strokeDasharray={strokeDashArray}
|
strokeDasharray={strokeDashArray}
|
||||||
/>
|
/>
|
||||||
<BarStackHorizontal<StackedBarData, string>
|
<Group left={axisLeftOffset}>
|
||||||
data={data}
|
<BarStackHorizontal<StackedBarData, string>
|
||||||
keys={keys}
|
data={data}
|
||||||
y={getCategory}
|
keys={keys}
|
||||||
xScale={valueScale}
|
y={getCategory}
|
||||||
yScale={categoryScale}
|
xScale={valueScale}
|
||||||
color={colorScale}
|
yScale={categoryScale}
|
||||||
>
|
color={colorScale}
|
||||||
{(barStacks) =>
|
>
|
||||||
barStacks.map((barStack) =>
|
{(barStacks) =>
|
||||||
barStack.bars.map((bar) => (
|
barStacks.map((barStack) =>
|
||||||
<rect
|
barStack.bars.map((bar) => (
|
||||||
className={styles.barStack}
|
<rect
|
||||||
key={`bar-stack-${barStack.index}-${bar.index}`}
|
className={styles.barStack}
|
||||||
x={bar.x}
|
key={`bar-stack-${barStack.index}-${bar.index}`}
|
||||||
y={bar.y}
|
x={bar.x}
|
||||||
height={bar.height / 2}
|
y={bar.y}
|
||||||
width={bar.width}
|
height={bar.height / 2}
|
||||||
fill={bar.color}
|
width={bar.width}
|
||||||
onMouseLeave={() => {
|
fill={bar.color}
|
||||||
tooltipTimeout = window.setTimeout(() => {
|
onMouseLeave={() => {
|
||||||
hideTooltip();
|
tooltipTimeout = window.setTimeout(() => {
|
||||||
}, 300);
|
hideTooltip();
|
||||||
}}
|
}, 300);
|
||||||
onMouseMove={(event) => {
|
}}
|
||||||
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
onMouseMove={(event) => {
|
||||||
const eventSvgCoords = localPoint(event);
|
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
||||||
const left = bar.x + bar.width / 2;
|
const eventSvgCoords = localPoint(event);
|
||||||
showTooltip({
|
const left = bar.x + bar.width / 2;
|
||||||
tooltipData: bar,
|
showTooltip({
|
||||||
tooltipTop: eventSvgCoords?.y,
|
tooltipData: bar,
|
||||||
tooltipLeft: left,
|
tooltipTop: eventSvgCoords?.y,
|
||||||
});
|
tooltipLeft: left,
|
||||||
}}
|
});
|
||||||
/>
|
}}
|
||||||
))
|
/>
|
||||||
)
|
))
|
||||||
}
|
)
|
||||||
</BarStackHorizontal>
|
}
|
||||||
|
</BarStackHorizontal>
|
||||||
|
</Group>
|
||||||
<AxisBottom
|
<AxisBottom
|
||||||
top={yMax}
|
top={yMax}
|
||||||
scale={valueScale}
|
scale={valueScale}
|
||||||
|
left={axisLeftOffset}
|
||||||
numTicks={numTicksValueAxis}
|
numTicks={numTicksValueAxis}
|
||||||
hideAxisLine
|
hideAxisLine
|
||||||
hideTicks
|
hideTicks
|
||||||
|
@ -387,6 +420,10 @@ export const StackedBarGraphHorizontal = withTooltip<
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<AxisLeft
|
<AxisLeft
|
||||||
|
top={
|
||||||
|
-((categoryScale.bandwidth() * 100) / width) *
|
||||||
|
categoryAxisLeftFactor
|
||||||
|
}
|
||||||
scale={categoryScale}
|
scale={categoryScale}
|
||||||
hideAxisLine
|
hideAxisLine
|
||||||
hideTicks
|
hideTicks
|
||||||
|
|
|
@ -251,11 +251,10 @@ export default function Academics() {
|
||||||
Color.primaryAccentLighter,
|
Color.primaryAccentLighter,
|
||||||
]}
|
]}
|
||||||
data={A12i}
|
data={A12i}
|
||||||
|
axisLeftOffset={80}
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 80,
|
left: 20,
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ComponentWrapper>
|
</ComponentWrapper>
|
||||||
|
@ -274,8 +273,8 @@ export default function Academics() {
|
||||||
noBackground
|
noBackground
|
||||||
>
|
>
|
||||||
<StackedBarGraphVertical
|
<StackedBarGraphVertical
|
||||||
width={800}
|
width={600}
|
||||||
height={500}
|
height={400}
|
||||||
keys={A16Keys}
|
keys={A16Keys}
|
||||||
colorRange={[
|
colorRange={[
|
||||||
Color.primaryAccent,
|
Color.primaryAccent,
|
||||||
|
@ -285,9 +284,7 @@ export default function Academics() {
|
||||||
data={A16}
|
data={A16}
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 80,
|
left: 20,
|
||||||
bottom: 70,
|
|
||||||
right: 0,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ComponentWrapper>
|
</ComponentWrapper>
|
||||||
|
|
|
@ -226,8 +226,6 @@ export default function CoopPage() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -256,8 +254,6 @@ export default function CoopPage() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -301,8 +297,6 @@ export default function CoopPage() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -126,8 +126,6 @@ export default function Home() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -151,8 +149,6 @@ export default function Home() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue