Skip to content

Commit

Permalink
Put the total value in the center of the chart
Browse files Browse the repository at this point in the history
  • Loading branch information
moisseev committed Sep 7, 2022
1 parent 31046c6 commit 3b75ada
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 30 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ Default settings:
pieInnerRadius: "20%",
pieOuterRadius: "85%"
},
title: ""
title: "",
total: {
enabled: false
}
}
```

Expand Down Expand Up @@ -99,6 +102,9 @@ size.canvasWidth | `number` | 600 | Width of the chart in pixels.
size.pieOuterRadius | `string`, `number` | 85% | Outer radius of the pie. Can be specified as a percentage of available space (a string like "50%") or a pixel value (a number like 200).
size.pieInnerRadius | `string`, `number` | 20% | Inner radius of the pie. Can be specified as a percentage of the outer radius (a string like "50%") or a pixel value (a number like 200).
**title** | `string` | empty | Title of the chart.
**total** | `hash` | | The `total` hash object.
total.enabled | `boolean` | true | Put the total value in the center of the chart.
total.label | `string` | Total | Total label.

### Methods
Method | Default | Description
Expand Down
8 changes: 8 additions & 0 deletions d3pie.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
line-height: normal;
}

.d3pie .total-text {
text-anchor: middle;
dominant-baseline: central;
}
.d3pie .total-value {
font-family: Arial, sans-serif;
}

.d3pie .inner-label {
fill: #eeeeee;
pointer-events: none;
Expand Down
84 changes: 58 additions & 26 deletions d3pie.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ function D3Pie (id, options) {
pieInnerRadius: "20%",
pieOuterRadius: "85%"
},
title: ""
title: "",
total: {
enabled: false
}
}, options);

this.destroy = function () {
Expand Down Expand Up @@ -100,6 +103,51 @@ function D3Pie (id, options) {
.append("span")
.attr("id", id + "-tooltip-text");

function attachListeners (selection) {
selection
.on("mouseover", function (_, d) {
const tot = tooltip.datum().total;
const percentage = (tot) ? Math.round(100 * d.data.value / tot) : NaN;
if (d.data.value) {
tooltip
.transition().duration(300)
.style("opacity", 1);
tooltipText
.text(d.data.label + ((tot) ? ": " + d.data.value + " (" + percentage + "%)" : ""));
} else {
tooltip.transition().duration(300).style("opacity", 0);
}
// eslint-disable-next-line no-invalid-this
tooltip.each(function (datum) { datum.height = this.getBoundingClientRect().height; });
})
.on("mouseout", function () {
tooltip.transition().duration(300).style("opacity", 0);
})
.on("mousemove", (event) => {
const {pageX, pageY} = event;
tooltip
.style("left", (pageX) + "px")
.style("top", function (d) { return (pageY - d.height - 2) + "px"; });
});
}

const totalG = g.append("g");
attachListeners(totalG);
totalG.append("circle")
.attr("r", innerRadius)
.style("opacity", 0);
if (opts.total.enabled) {
const totalText = totalG.append("text")
.attr("class", "total-text");
totalText.append("tspan")
.attr("class", "total-value")
.style("font-size", (0.6 * innerRadius) + "px");
totalText.append("tspan")
.attr("x", "0")
.attr("dy", 0.5 * innerRadius)
.text((typeof opts.total.label !== "undefined") ? opts.total.label : "Total");
}

const defs = svg.append("defs");

this.data = function (arg) {
Expand All @@ -111,6 +159,13 @@ function D3Pie (id, options) {
return a + (b.value || 0);
}, 0);
tooltip.datum({total});
totalG.datum({
data: {
label: (typeof opts.total.label !== "undefined") ? opts.total.label : "Total",
value: total
}
});
if (opts.total.enabled) totalG.select(".total-value").text(d3.format(".3~s")(total));

// Add placeholder path for empty pie chart
data.unshift({
Expand Down Expand Up @@ -234,31 +289,8 @@ function D3Pie (id, options) {
const sliceG = g.selectAll(".slice-g").data(pie(data), key);

const sliceGEnter = sliceG.enter().append("g")
.attr("class", "slice-g")
.on("mouseover", function (_, d) {
const tot = tooltip.datum().total;
const percentage = (tot) ? Math.round(100 * d.data.value / tot) : NaN;
if (d.data.value) {
tooltip
.transition().duration(300)
.style("opacity", 1);
tooltipText
.text(d.data.label + ((tot) ? ": " + d.data.value + " (" + percentage + "%)" : ""));
} else {
tooltip.transition().duration(300).style("opacity", 0);
}
// eslint-disable-next-line no-invalid-this
tooltip.each(function (datum) { datum.height = this.getBoundingClientRect().height; });
})
.on("mouseout", function () {
tooltip.transition().duration(300).style("opacity", 0);
})
.on("mousemove", (event) => {
const {pageX, pageY} = event;
tooltip
.style("left", (pageX) + "px")
.style("top", function (d) { return (pageY - d.height - 2) + "px"; });
});
.attr("class", "slice-g");
attachListeners(sliceGEnter);

sliceGEnter
.append("path")
Expand Down
19 changes: 16 additions & 3 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,16 @@ <h1>D3Pie demo</h1>
duration: 5000,
title: "Rspamd filter stats",
size: {
pieInnerRadius: "60%"
pieInnerRadius: "50%"
},
total: {
enabled: true,
label: "Scanned"
},
labels: {
inner: {
offset: 0
}
}
},
right: {
Expand All @@ -80,15 +89,19 @@ <h1>D3Pie demo</h1>
size: {
canvasWidth: 400,
canvasHeight: 180,
pieInnerRadius: "20%",
pieInnerRadius: "50%",
pieOuterRadius: "80%"
},
total: {
enabled: true
},
labels: {
outer: {
format: "none"
},
inner: {
hideWhenLessThanPercentage: 10
hideWhenLessThanPercentage: 10,
offset: 0
}
},
padAngle: 0.02,
Expand Down

0 comments on commit 3b75ada

Please sign in to comment.