WIP Stacked Bar Graph
This commit is contained in:
parent
d77ebe0f8d
commit
803457c083
|
@ -8,10 +8,17 @@
|
||||||
|
|
||||||
.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,17 +43,14 @@ 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: { top: number; left: number };
|
margin: {
|
||||||
|
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. */
|
||||||
|
@ -62,9 +59,6 @@ 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;
|
||||||
|
@ -82,14 +76,9 @@ 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,
|
||||||
|
@ -126,18 +115,15 @@ export const StackedBarGraphVertical = withTooltip<
|
||||||
});
|
});
|
||||||
|
|
||||||
// bounds
|
// bounds
|
||||||
const xMax = width;
|
const xMax = width - margin.left - margin.right;
|
||||||
const yMax = height - margin.top - axisBottomOffset;
|
const yMax = height - margin.top - margin.bottom;
|
||||||
|
|
||||||
categoryScale.rangeRound([0, xMax - axisLeftOffset]);
|
categoryScale.rangeRound([0, xMax]);
|
||||||
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
|
<div className={styles.legend}>
|
||||||
className={styles.legend}
|
|
||||||
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
|
|
||||||
>
|
|
||||||
<LegendOrdinal
|
<LegendOrdinal
|
||||||
scale={colorScale}
|
scale={colorScale}
|
||||||
direction="row"
|
direction="row"
|
||||||
|
@ -152,7 +138,6 @@ 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}
|
||||||
|
@ -161,94 +146,89 @@ 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}
|
||||||
/>
|
/>
|
||||||
<Group left={axisLeftOffset}>
|
<BarStack<StackedBarData, string>
|
||||||
<BarStack<StackedBarData, string>
|
data={data}
|
||||||
data={data}
|
keys={keys}
|
||||||
keys={keys}
|
x={getCategory}
|
||||||
x={getCategory}
|
xScale={categoryScale}
|
||||||
xScale={categoryScale}
|
yScale={valueScale}
|
||||||
yScale={valueScale}
|
color={colorScale}
|
||||||
color={colorScale}
|
>
|
||||||
>
|
{(barStacks) =>
|
||||||
{(barStacks) =>
|
barStacks.map((barStack) =>
|
||||||
barStacks.map((barStack) =>
|
barStack.bars.map((bar) => (
|
||||||
barStack.bars.map((bar) => (
|
<rect
|
||||||
<rect
|
className={styles.barStack}
|
||||||
className={styles.barStack}
|
key={`bar-stack-${barStack.index}-${bar.index}`}
|
||||||
key={`bar-stack-${barStack.index}-${bar.index}`}
|
x={bar.x}
|
||||||
x={bar.x}
|
y={bar.y}
|
||||||
y={bar.y}
|
height={bar.height}
|
||||||
height={bar.height}
|
width={bar.width}
|
||||||
width={bar.width / 2}
|
fill={bar.color}
|
||||||
fill={bar.color}
|
onMouseLeave={() => {
|
||||||
onMouseLeave={() => {
|
tooltipTimeout = window.setTimeout(() => {
|
||||||
tooltipTimeout = window.setTimeout(() => {
|
hideTooltip();
|
||||||
hideTooltip();
|
}, 300);
|
||||||
}, 300);
|
}}
|
||||||
}}
|
onMouseMove={(event) => {
|
||||||
onMouseMove={(event) => {
|
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
||||||
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
const eventSvgCoords = localPoint(event);
|
||||||
const eventSvgCoords = localPoint(event);
|
const left = bar.x + bar.width / 2;
|
||||||
const left = bar.x + bar.width / 2;
|
showTooltip({
|
||||||
showTooltip({
|
tooltipData: bar,
|
||||||
tooltipData: bar,
|
tooltipTop: eventSvgCoords?.y,
|
||||||
tooltipTop: eventSvgCoords?.y,
|
tooltipLeft: left,
|
||||||
tooltipLeft: left,
|
});
|
||||||
});
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
))
|
||||||
))
|
)
|
||||||
)
|
}
|
||||||
}
|
</BarStack>
|
||||||
</BarStack>
|
|
||||||
</Group>
|
|
||||||
<Line
|
<Line
|
||||||
fill={Color.tertiaryBackground}
|
fill={Color.tertiaryBackground}
|
||||||
to={new Point({ x: axisLeftOffset, y: 0 })}
|
to={new Point({ x: 0, y: 0 })}
|
||||||
from={new Point({ x: axisLeftOffset, y: yMax })}
|
from={new Point({ x: 0, y: yMax })}
|
||||||
stroke={Color.tertiaryBackground}
|
stroke={Color.tertiaryBackground}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
strokeDasharray={strokeDashArray}
|
strokeDasharray={strokeDashArray}
|
||||||
/>
|
/>
|
||||||
<AxisBottom
|
</Group>
|
||||||
top={yMax}
|
<AxisBottom
|
||||||
scale={categoryScale}
|
top={yMax}
|
||||||
left={
|
scale={categoryScale}
|
||||||
((categoryScale.bandwidth() * 100) / width) *
|
hideTicks
|
||||||
categoryAxisLeftFactor
|
hideAxisLine
|
||||||
}
|
labelProps={{
|
||||||
hideTicks
|
fontSize: `${10 / 16}rem`,
|
||||||
hideAxisLine
|
}}
|
||||||
labelProps={{
|
tickLabelProps={() => ({
|
||||||
fontSize: `${10 / 16}rem`,
|
className: styles.tickLabel,
|
||||||
}}
|
fill: Color.label,
|
||||||
tickLabelProps={() => ({
|
fontWeight: TICK_LABEL_FONT_WEIGHT,
|
||||||
|
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 ? (
|
||||||
|
@ -279,14 +259,9 @@ 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,
|
||||||
|
@ -323,18 +298,15 @@ export const StackedBarGraphHorizontal = withTooltip<
|
||||||
});
|
});
|
||||||
|
|
||||||
// bounds
|
// bounds
|
||||||
const xMax = width;
|
const xMax = width - margin.left - margin.right;
|
||||||
const yMax = height - margin.top - axisBottomOffset;
|
const yMax = height - margin.top - margin.bottom;
|
||||||
|
|
||||||
categoryScale.rangeRound([yMax, 0]);
|
categoryScale.rangeRound([yMax, 0]);
|
||||||
valueScale.range([0, xMax - axisLeftOffset]);
|
valueScale.range([0, xMax]);
|
||||||
|
|
||||||
return width < 10 ? null : (
|
return width < 10 ? null : (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div
|
<div className={styles.legend}>
|
||||||
className={styles.legend}
|
|
||||||
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
|
|
||||||
>
|
|
||||||
<LegendOrdinal
|
<LegendOrdinal
|
||||||
scale={colorScale}
|
scale={colorScale}
|
||||||
direction="row"
|
direction="row"
|
||||||
|
@ -349,7 +321,6 @@ 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}
|
||||||
|
@ -358,56 +329,52 @@ 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}
|
||||||
/>
|
/>
|
||||||
<Group left={axisLeftOffset}>
|
<BarStackHorizontal<StackedBarData, string>
|
||||||
<BarStackHorizontal<StackedBarData, string>
|
data={data}
|
||||||
data={data}
|
keys={keys}
|
||||||
keys={keys}
|
y={getCategory}
|
||||||
y={getCategory}
|
xScale={valueScale}
|
||||||
xScale={valueScale}
|
yScale={categoryScale}
|
||||||
yScale={categoryScale}
|
color={colorScale}
|
||||||
color={colorScale}
|
>
|
||||||
>
|
{(barStacks) =>
|
||||||
{(barStacks) =>
|
barStacks.map((barStack) =>
|
||||||
barStacks.map((barStack) =>
|
barStack.bars.map((bar) => (
|
||||||
barStack.bars.map((bar) => (
|
<rect
|
||||||
<rect
|
className={styles.barStack}
|
||||||
className={styles.barStack}
|
key={`bar-stack-${barStack.index}-${bar.index}`}
|
||||||
key={`bar-stack-${barStack.index}-${bar.index}`}
|
x={bar.x}
|
||||||
x={bar.x}
|
y={bar.y}
|
||||||
y={bar.y}
|
height={bar.height / 2}
|
||||||
height={bar.height / 2}
|
width={bar.width}
|
||||||
width={bar.width}
|
fill={bar.color}
|
||||||
fill={bar.color}
|
onMouseLeave={() => {
|
||||||
onMouseLeave={() => {
|
tooltipTimeout = window.setTimeout(() => {
|
||||||
tooltipTimeout = window.setTimeout(() => {
|
hideTooltip();
|
||||||
hideTooltip();
|
}, 300);
|
||||||
}, 300);
|
}}
|
||||||
}}
|
onMouseMove={(event) => {
|
||||||
onMouseMove={(event) => {
|
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
||||||
if (tooltipTimeout) clearTimeout(tooltipTimeout);
|
const eventSvgCoords = localPoint(event);
|
||||||
const eventSvgCoords = localPoint(event);
|
const left = bar.x + bar.width / 2;
|
||||||
const left = bar.x + bar.width / 2;
|
showTooltip({
|
||||||
showTooltip({
|
tooltipData: bar,
|
||||||
tooltipData: bar,
|
tooltipTop: eventSvgCoords?.y,
|
||||||
tooltipTop: eventSvgCoords?.y,
|
tooltipLeft: left,
|
||||||
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
|
||||||
|
@ -420,10 +387,6 @@ export const StackedBarGraphHorizontal = withTooltip<
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<AxisLeft
|
<AxisLeft
|
||||||
top={
|
|
||||||
-((categoryScale.bandwidth() * 100) / width) *
|
|
||||||
categoryAxisLeftFactor
|
|
||||||
}
|
|
||||||
scale={categoryScale}
|
scale={categoryScale}
|
||||||
hideAxisLine
|
hideAxisLine
|
||||||
hideTicks
|
hideTicks
|
||||||
|
|
|
@ -251,10 +251,11 @@ export default function Academics() {
|
||||||
Color.primaryAccentLighter,
|
Color.primaryAccentLighter,
|
||||||
]}
|
]}
|
||||||
data={A12i}
|
data={A12i}
|
||||||
axisLeftOffset={80}
|
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 80,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ComponentWrapper>
|
</ComponentWrapper>
|
||||||
|
@ -273,8 +274,8 @@ export default function Academics() {
|
||||||
noBackground
|
noBackground
|
||||||
>
|
>
|
||||||
<StackedBarGraphVertical
|
<StackedBarGraphVertical
|
||||||
width={600}
|
width={800}
|
||||||
height={400}
|
height={500}
|
||||||
keys={A16Keys}
|
keys={A16Keys}
|
||||||
colorRange={[
|
colorRange={[
|
||||||
Color.primaryAccent,
|
Color.primaryAccent,
|
||||||
|
@ -284,7 +285,9 @@ export default function Academics() {
|
||||||
data={A16}
|
data={A16}
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 80,
|
||||||
|
bottom: 70,
|
||||||
|
right: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ComponentWrapper>
|
</ComponentWrapper>
|
||||||
|
|
|
@ -226,6 +226,8 @@ export default function CoopPage() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -254,6 +256,8 @@ export default function CoopPage() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -297,6 +301,8 @@ export default function CoopPage() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -126,6 +126,8 @@ export default function Home() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -149,6 +151,8 @@ export default function Home() {
|
||||||
margin={{
|
margin={{
|
||||||
top: 20,
|
top: 20,
|
||||||
left: 20,
|
left: 20,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue