WIP Stacked Bar Graph

This commit is contained in:
e26chiu 2022-12-15 21:27:04 -05:00
parent d77ebe0f8d
commit 803457c083
5 changed files with 149 additions and 166 deletions

View File

@ -8,10 +8,17 @@
.legend {
display: flex;
justify-content: center;
font-size: calc(16rem / 16);
top: 0;
}
.tickLabel {
font-family: "Inconsolata", monospace;
font-weight: 800;
fill: var(--label);
}
.key {
font-weight: bold;
}

View File

@ -43,17 +43,14 @@ export type StackedBarProps = {
/** Colours for each key */
colorRange: string[];
/** 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 */
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. */
strokeWidth?: number;
/** Length of the dashes and the gaps in the graph, in px. */
@ -62,9 +59,6 @@ export type StackedBarProps = {
scalePadding?: number;
/** Margin for each item in the legend */
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;
@ -82,14 +76,9 @@ export const StackedBarGraphVertical = withTooltip<
margin,
scalePadding = 0.3,
numTicksValueAxis = 6,
axisLeftOffset = 40,
axisBottomOffset = 40,
strokeWidth = 2.5,
strokeDashArray = "10,4",
legendLeftOffset = 40,
legendTopOffset = 40,
itemMargin = "0 0 0 15px",
categoryAxisLeftFactor = 1,
tooltipOpen,
tooltipLeft,
tooltipTop,
@ -126,18 +115,15 @@ export const StackedBarGraphVertical = withTooltip<
});
// bounds
const xMax = width;
const yMax = height - margin.top - axisBottomOffset;
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
categoryScale.rangeRound([0, xMax - axisLeftOffset]);
categoryScale.rangeRound([0, xMax]);
valueScale.range([yMax, 0]);
return width < 10 ? null : (
<div className={styles.container}>
<div
className={styles.legend}
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
>
<div className={styles.legend}>
<LegendOrdinal
scale={colorScale}
direction="row"
@ -152,7 +138,6 @@ export const StackedBarGraphVertical = withTooltip<
scale={valueScale}
width={xMax}
height={yMax}
left={axisLeftOffset}
numTicks={numTicksValueAxis}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
@ -161,94 +146,89 @@ export const StackedBarGraphVertical = withTooltip<
<GridColumns
scale={categoryScale}
height={yMax}
left={axisLeftOffset}
offset={categoryScale.bandwidth() / 2}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Group left={axisLeftOffset}>
<BarStack<StackedBarData, string>
data={data}
keys={keys}
x={getCategory}
xScale={categoryScale}
yScale={valueScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height}
width={bar.width / 2}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const eventSvgCoords = localPoint(event);
const left = bar.x + bar.width / 2;
showTooltip({
tooltipData: bar,
tooltipTop: eventSvgCoords?.y,
tooltipLeft: left,
});
}}
/>
))
)
}
</BarStack>
</Group>
<BarStack<StackedBarData, string>
data={data}
keys={keys}
x={getCategory}
xScale={categoryScale}
yScale={valueScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height}
width={bar.width}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const eventSvgCoords = localPoint(event);
const left = bar.x + bar.width / 2;
showTooltip({
tooltipData: bar,
tooltipTop: eventSvgCoords?.y,
tooltipLeft: left,
});
}}
/>
))
)
}
</BarStack>
<Line
fill={Color.tertiaryBackground}
to={new Point({ x: axisLeftOffset, y: 0 })}
from={new Point({ x: axisLeftOffset, y: yMax })}
to={new Point({ x: 0, y: 0 })}
from={new Point({ x: 0, y: yMax })}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<AxisBottom
top={yMax}
scale={categoryScale}
left={
((categoryScale.bandwidth() * 100) / width) *
categoryAxisLeftFactor
}
hideTicks
hideAxisLine
labelProps={{
fontSize: `${10 / 16}rem`,
}}
tickLabelProps={() => ({
</Group>
<AxisBottom
top={yMax}
scale={categoryScale}
hideTicks
hideAxisLine
labelProps={{
fontSize: `${10 / 16}rem`,
}}
tickLabelProps={() => ({
className: styles.tickLabel,
fill: Color.label,
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,
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>
{tooltipOpen && tooltipData ? (
@ -279,14 +259,9 @@ export const StackedBarGraphHorizontal = withTooltip<
margin,
scalePadding = 0.3,
numTicksValueAxis = 6,
axisLeftOffset = 40,
axisBottomOffset = 40,
strokeWidth = 2.5,
strokeDashArray = "10,4",
legendLeftOffset = 40,
legendTopOffset = 40,
itemMargin = "0 0 0 15px",
categoryAxisLeftFactor = 1,
tooltipOpen,
tooltipLeft,
tooltipTop,
@ -323,18 +298,15 @@ export const StackedBarGraphHorizontal = withTooltip<
});
// bounds
const xMax = width;
const yMax = height - margin.top - axisBottomOffset;
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
categoryScale.rangeRound([yMax, 0]);
valueScale.range([0, xMax - axisLeftOffset]);
valueScale.range([0, xMax]);
return width < 10 ? null : (
<div className={styles.container}>
<div
className={styles.legend}
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
>
<div className={styles.legend}>
<LegendOrdinal
scale={colorScale}
direction="row"
@ -349,7 +321,6 @@ export const StackedBarGraphHorizontal = withTooltip<
width={xMax}
height={yMax}
offset={categoryScale.bandwidth() / 2}
left={axisLeftOffset}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
@ -358,56 +329,52 @@ export const StackedBarGraphHorizontal = withTooltip<
scale={valueScale}
height={yMax}
numTicks={numTicksValueAxis}
left={axisLeftOffset}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Group left={axisLeftOffset}>
<BarStackHorizontal<StackedBarData, string>
data={data}
keys={keys}
y={getCategory}
xScale={valueScale}
yScale={categoryScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height / 2}
width={bar.width}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const eventSvgCoords = localPoint(event);
const left = bar.x + bar.width / 2;
showTooltip({
tooltipData: bar,
tooltipTop: eventSvgCoords?.y,
tooltipLeft: left,
});
}}
/>
))
)
}
</BarStackHorizontal>
</Group>
<BarStackHorizontal<StackedBarData, string>
data={data}
keys={keys}
y={getCategory}
xScale={valueScale}
yScale={categoryScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height / 2}
width={bar.width}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const eventSvgCoords = localPoint(event);
const left = bar.x + bar.width / 2;
showTooltip({
tooltipData: bar,
tooltipTop: eventSvgCoords?.y,
tooltipLeft: left,
});
}}
/>
))
)
}
</BarStackHorizontal>
<AxisBottom
top={yMax}
scale={valueScale}
left={axisLeftOffset}
numTicks={numTicksValueAxis}
hideAxisLine
hideTicks
@ -420,10 +387,6 @@ export const StackedBarGraphHorizontal = withTooltip<
})}
/>
<AxisLeft
top={
-((categoryScale.bandwidth() * 100) / width) *
categoryAxisLeftFactor
}
scale={categoryScale}
hideAxisLine
hideTicks

View File

@ -251,10 +251,11 @@ export default function Academics() {
Color.primaryAccentLighter,
]}
data={A12i}
axisLeftOffset={80}
margin={{
top: 20,
left: 20,
left: 80,
bottom: 0,
right: 0,
}}
/>
</ComponentWrapper>
@ -273,8 +274,8 @@ export default function Academics() {
noBackground
>
<StackedBarGraphVertical
width={600}
height={400}
width={800}
height={500}
keys={A16Keys}
colorRange={[
Color.primaryAccent,
@ -284,7 +285,9 @@ export default function Academics() {
data={A16}
margin={{
top: 20,
left: 20,
left: 80,
bottom: 70,
right: 0,
}}
/>
</ComponentWrapper>

View File

@ -226,6 +226,8 @@ export default function CoopPage() {
margin={{
top: 20,
left: 20,
bottom: 0,
right: 0,
}}
/>
</div>
@ -254,6 +256,8 @@ export default function CoopPage() {
margin={{
top: 20,
left: 20,
bottom: 0,
right: 0,
}}
/>
</div>
@ -297,6 +301,8 @@ export default function CoopPage() {
margin={{
top: 20,
left: 20,
bottom: 0,
right: 0,
}}
/>
</div>

View File

@ -126,6 +126,8 @@ export default function Home() {
margin={{
top: 20,
left: 20,
bottom: 0,
right: 0,
}}
/>
@ -149,6 +151,8 @@ export default function Home() {
margin={{
top: 20,
left: 20,
bottom: 0,
right: 0,
}}
/>