Skip to content

Commit

Permalink
Merge pull request #29 from bprusinowski/feat/support-custom-svg-back…
Browse files Browse the repository at this point in the history
…grounds

feat: Add support for custom SVG color backgrounds
  • Loading branch information
bprusinowski committed Jul 1, 2023
2 parents 4875fec + 62e4bc6 commit f648685
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 72 deletions.
46 changes: 39 additions & 7 deletions src/charts/BarChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import {
InputGroupValue,
TextDims,
} from "../types";
import { FONT_SIZE, getTextColor, max, sum } from "../utils";
import {
FONT_SIZE,
deriveSubtlerColor,
getTextColor,
max,
sum,
} from "../utils";
import * as Chart from "./Chart";
import {
STROKE_WIDTH,
Expand All @@ -33,7 +39,10 @@ export type Info = Chart.BaseInfo & {
horizontalAxis: InputAxis | undefined;
};

export const info = (inputStep: BarInputStep): Info => {
export const info = (
svgBackgroundColor: string,
inputStep: BarInputStep
): Info => {
const {
chartSubtype = "grouped",
layout = "vertical",
Expand All @@ -42,7 +51,7 @@ export const info = (inputStep: BarInputStep): Info => {
} = inputStep;

return {
...Chart.baseInfo(inputStep, shareDomain),
...Chart.baseInfo(svgBackgroundColor, inputStep, shareDomain),
type: "bar",
subtype: chartSubtype,
layout,
Expand Down Expand Up @@ -115,6 +124,7 @@ export const getters = (
shareDomain,
showValues,
maxValue,
svgBackgroundColor,
} = info;
const {
showDatumLabels,
Expand All @@ -130,6 +140,10 @@ export const getters = (
const labelYShift = textDims.datumLabel.yShift;
const valueHeight = textDims.datumValue.height;
const valueYShift = textDims.datumValue.yShift;
const groupFill = deriveSubtlerColor(svgBackgroundColor);
const groupLabelFill = getTextColor(svgBackgroundColor);
const groupLabelStroke = svgBackgroundColor;
const datumStroke = svgBackgroundColor;

if (isVertical) {
const { x0Scale, x0bw, x1Scale, x1bw, yScale } = getVerticalScales({
Expand Down Expand Up @@ -164,7 +178,10 @@ export const getters = (
labelX,
labelY,
labelFontSize,
labelStroke: groupLabelStroke,
labelStrokeWidth,
labelFill: groupLabelFill,
fill: groupFill,
opacity,
};
},
Expand Down Expand Up @@ -225,8 +242,12 @@ export const getters = (
const labelY = isGrouped
? s(0, dHeight * 0.5 - labelHeight - TEXT_MARGIN)
: s(0, -(dHeight * 0.5 + labelYShift));
const labelFontSize = showDatumLabels ? FONT_SIZE.datumLabel : 0;
const labelFontSize = s(
0,
showDatumLabels ? FONT_SIZE.datumLabel : 0
);
const labelFill = getTextColor(datumFill);
const labelStroke = datumFill;
const valueWidth = svg.measureText(value, "datumValue").width;
const valueX = isGrouped
? 0
Expand All @@ -248,7 +269,7 @@ export const getters = (
: -(dHeight * 0.5 + valueYShift)
);
const valueFontSize = showValues ? s(0, FONT_SIZE.datumValue) : 0;
const valueFill = isGrouped ? "black" : labelFill;
const valueFill = isGrouped ? groupLabelFill : labelFill;
const opacity = datum.opacity ?? 1;

return {
Expand All @@ -258,10 +279,12 @@ export const getters = (
y,
rotate,
fill: datumFill,
stroke: datumStroke,
strokeWidth,
labelX,
labelY,
labelFontSize,
labelStroke,
labelFill,
valueX,
valueY,
Expand Down Expand Up @@ -317,7 +340,10 @@ export const getters = (
labelX,
labelY,
labelFontSize,
labelStroke: groupLabelStroke,
labelStrokeWidth,
labelFill: groupLabelFill,
fill: groupFill,
opacity,
};
},
Expand Down Expand Up @@ -379,8 +405,12 @@ export const getters = (
-dWidth * 0.5 + labelWidth * 0.5 + BASE_MARGIN * 0.5
);
const labelY = s(0, labelYShift);
const labelFontSize = showDatumLabels ? FONT_SIZE.datumLabel : 0;
const labelFontSize = s(
0,
showDatumLabels ? FONT_SIZE.datumLabel : 0
);
const labelFill = getTextColor(datumFill);
const labelStroke = datumFill;
const valueWidth = svg.measureText(value, "datumValue").width;
const valueX = isGrouped
? labelX + labelWidth > (dWidth + valueWidth + BASE_MARGIN) * 0.5
Expand All @@ -391,7 +421,7 @@ export const getters = (
: 0;
const valueY = s(valueHeight * 0.5, valueYShift);
const valueFontSize = showValues ? s(0, FONT_SIZE.datumValue) : 0;
const valueFill = isGrouped ? "black" : labelFill;
const valueFill = isGrouped ? groupLabelFill : labelFill;
const opacity = datum.opacity ?? 1;

return {
Expand All @@ -401,10 +431,12 @@ export const getters = (
y,
rotate,
fill: datumFill,
stroke: datumStroke,
strokeWidth,
labelX,
labelY,
labelFontSize,
labelStroke,
labelFill,
valueX,
valueY,
Expand Down
27 changes: 22 additions & 5 deletions src/charts/BubbleChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Svg } from "../components";
import { BUBBLE, getPathData } from "../coords";
import { Dimensions, ResolvedDimensions } from "../dims";
import { BaseMax, BubbleInputStep, InputGroupValue, TextDims } from "../types";
import { FONT_SIZE, getTextColor } from "../utils";
import { FONT_SIZE, deriveSubtlerColor, getTextColor } from "../utils";
import * as Chart from "./Chart";
import {
STROKE_WIDTH,
Expand All @@ -22,11 +22,14 @@ export type Info = Chart.BaseInfo & {
canUseHorizontalAxis: false;
};

export const info = (inputStep: BubbleInputStep): Info => {
export const info = (
svgBackgroundColor: string,
inputStep: BubbleInputStep
): Info => {
const { groups, shareDomain = false } = inputStep;

return {
...Chart.baseInfo(inputStep, shareDomain),
...Chart.baseInfo(svgBackgroundColor, inputStep, shareDomain),
type: "bubble",
groups,
maxValue: getMaxValue(inputStep),
Expand All @@ -51,7 +54,8 @@ export const getters = (
cartoonize: boolean;
}
) => {
const { groups, maxValue, shareDomain, showValues } = info;
const { groups, maxValue, shareDomain, showValues, svgBackgroundColor } =
info;
const {
showDatumLabels,
dims: { width, height, size, margin },
Expand All @@ -64,6 +68,10 @@ export const getters = (
// If a custom maxValue was provided, we need to shift the bubbles to the center.
const maxValueShift = maxValue.kc * size * 0.5;
const showDatumLabelsAndValues = showDatumLabels && showValues;
const groupFill = deriveSubtlerColor(svgBackgroundColor);
const groupLabelFill = getTextColor(svgBackgroundColor);
const groupLabelStroke = svgBackgroundColor;
const datumStroke = svgBackgroundColor;

for (const group of root.children || []) {
const { key } = group.data;
Expand Down Expand Up @@ -103,7 +111,10 @@ export const getters = (
labelX,
labelY,
labelFontSize,
labelStroke: groupLabelStroke,
labelStrokeWidth,
labelFill: groupLabelFill,
fill: groupFill,
opacity,
};
},
Expand Down Expand Up @@ -137,8 +148,12 @@ export const getters = (
const labelY =
textDims.datumLabel.yShift -
(showDatumLabelsAndValues ? textDims.datumLabel.height * 0.5 : 0);
const labelFontSize = showDatumLabels ? FONT_SIZE.datumLabel : 0;
const labelFontSize = s(
0,
showDatumLabels ? FONT_SIZE.datumLabel : 0
);
const labelFill = getTextColor(datumFill);
const labelStroke = datumFill;
const valueX = 0;
const valueY = s(
0,
Expand All @@ -159,10 +174,12 @@ export const getters = (
y,
rotate,
fill: datumFill,
stroke: datumStroke,
strokeWidth,
labelX,
labelY,
labelFontSize,
labelStroke,
labelFill,
valueX,
valueY,
Expand Down
25 changes: 15 additions & 10 deletions src/charts/Chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ export type BaseInfo = {
dataKeys: string[];
shareDomain: boolean;
showValues: boolean;
svgBackgroundColor: string;
};

export const baseInfo = (
svgBackgroundColor: string,
inputStep: InputStep,
shareDomain: boolean
): BaseInfo => {
Expand All @@ -25,21 +27,21 @@ export const baseInfo = (
);
const showValues = inputStep.showValues ?? false;

return { groupsKeys, dataKeys, shareDomain, showValues };
return { groupsKeys, dataKeys, shareDomain, showValues, svgBackgroundColor };
};

export const info = (inputStep: InputStep) => {
export const info = (svgBackgroundColor: string, inputStep: InputStep) => {
switch (inputStep.chartType) {
case "bar":
return BarChart.info(inputStep);
return BarChart.info(svgBackgroundColor, inputStep);
case "bubble":
return BubbleChart.info(inputStep);
return BubbleChart.info(svgBackgroundColor, inputStep);
case "pie":
return PieChart.info(inputStep);
return PieChart.info(svgBackgroundColor, inputStep);
case "scatter":
return ScatterChart.info(inputStep);
return ScatterChart.info(svgBackgroundColor, inputStep);
case "treemap":
return TreemapChart.info(inputStep);
return TreemapChart.info(svgBackgroundColor, inputStep);
default:
const _exhaustiveCheck: never = inputStep;
return _exhaustiveCheck;
Expand Down Expand Up @@ -73,7 +75,10 @@ export type G = {
labelX: number;
labelY: number;
labelFontSize: number;
labelStroke: string;
labelStrokeWidth: number;
labelFill: string;
fill: string;
opacity: number;
};

Expand Down Expand Up @@ -205,7 +210,7 @@ export const render = ({
.join("path")
.attr("class", "background")
.attr("transform", (d) => `translate(${d.x}, ${d.y})`)
.style("fill", "#f5f5f5")
.style("fill", (d) => d.fill)
.attr("d", (d) => d.d);

const dataSelection = groupsSelection
Expand Down Expand Up @@ -265,9 +270,9 @@ export const render = ({
.attr("class", "label")
.attr("x", (d) => d.labelX)
.attr("y", (d) => d.labelY)
.style("fill", "#333333")
.style("fill", (d) => d.labelFill)
.style("paint-order", "stroke")
.style("stroke", "white")
.style("stroke", (d) => d.labelStroke)
.style("stroke-width", (d) => d.labelStrokeWidth)
.style("stroke-linecap", "round")
.style("stroke-linejoin", "round")
Expand Down
11 changes: 4 additions & 7 deletions src/charts/Datum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ export type G = {
y: number;
rotate: number;
fill: string;
stroke: string;
strokeWidth: number;
labelX: number;
labelY: number;
labelFontSize: number;
labelStroke: string;
labelFill: string;
valueX: number;
valueY: number;
Expand Down Expand Up @@ -144,7 +146,7 @@ export const render = ({
.join("path")
.attr("class", "shape")
.attr("d", (d) => d.d)
.style("stroke", "white")
.style("stroke", (d) => d.stroke)
.style("stroke-width", (d) => d.strokeWidth)
.style("fill", (d) => d.fill);

Expand Down Expand Up @@ -200,12 +202,7 @@ export const render = ({
.style("text-anchor", "middle")
.style("dominant-baseline", "hanging")
.style("paint-order", "stroke")
.style("stroke", (d) =>
// FIXME
d.labelFill === "white" || d.labelFill === "rgb(255, 255, 255)"
? "black"
: "white"
)
.style("stroke", (d) => d.labelStroke)
.style("stroke-width", 2)
.style("user-select", "none")
.style("pointer-events", "none")
Expand Down
Loading

0 comments on commit f648685

Please sign in to comment.