commit 8bb998f: [WebUI] Put total in the center of pie charts

moisseev moiseev at mezonplus.ru
Wed Sep 7 15:56:03 UTC 2022


Author: moisseev
Date: 2022-09-07 14:58:50 +0300
URL: https://github.com/rspamd/rspamd/commit/8bb998f26c9a5ef9bf0579106cad45e39e26cc4a (refs/pull/4262/head)

[WebUI] Put total in the center of pie charts

---
 interface/css/d3pie.css       |  8 ++++++++
 interface/css/rspamd.css      |  3 ---
 interface/js/app/graph.js     |  9 +++++++--
 interface/js/app/stats.js     | 12 +++++++++++-
 interface/js/lib/d3pie.min.js |  4 ++--
 5 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/interface/css/d3pie.css b/interface/css/d3pie.css
index 71419a3d6..813ce9fe5 100644
--- a/interface/css/d3pie.css
+++ b/interface/css/d3pie.css
@@ -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;
diff --git a/interface/css/rspamd.css b/interface/css/rspamd.css
index 044bdd645..11e0819e2 100644
--- a/interface/css/rspamd.css
+++ b/interface/css/rspamd.css
@@ -345,9 +345,6 @@ table#symbolsTable input[type="number"] {
     text-align: left;
     font-size: 12px;
 }
-#rrd-pie {
-    font-size: 10px;
-}
 
 /* Throughput graph controls */
 #graph_controls select {
diff --git a/interface/js/app/graph.js b/interface/js/app/graph.js
index 6bd83c72b..3e4983a71 100644
--- a/interface/js/app/graph.js
+++ b/interface/js/app/graph.js
@@ -34,6 +34,7 @@ define(["jquery", "d3evolution", "d3pie", "footable"],
             size: {
                 canvasWidth: 400,
                 canvasHeight: 180,
+                pieInnerRadius: "50%",
                 pieOuterRadius: "80%"
             },
             labels: {
@@ -41,14 +42,18 @@ define(["jquery", "d3evolution", "d3pie", "footable"],
                     format: "none"
                 },
                 inner: {
-                    hideWhenLessThanPercentage: 8
+                    hideWhenLessThanPercentage: 8,
+                    offset: 0
                 },
             },
             padAngle: 0.02,
             pieCenterOffset: {
                 x: -120,
                 y: 10,
-            }
+            },
+            total: {
+                enabled: true
+            },
         };
 
         var ui = {};
diff --git a/interface/js/app/stats.js b/interface/js/app/stats.js
index 8b38d7f10..7ce239ef9 100644
--- a/interface/js/app/stats.js
+++ b/interface/js/app/stats.js
@@ -226,11 +226,21 @@ define(["jquery", "d3pie"],
             if (!graphs.chart) {
                 graphs.chart = new D3Pie("chart", {
                     labels: {
+                        inner: {
+                            offset: 0
+                        },
                         outer: {
                             collideHeight: 18,
                         }
                     },
-                    title: "Rspamd filter stats"
+                    size: {
+                        pieInnerRadius: "50%"
+                    },
+                    title: "Rspamd filter stats",
+                    total: {
+                        enabled: true,
+                        label: "Scanned"
+                    }
                 });
             }
 
diff --git a/interface/js/lib/d3pie.min.js b/interface/js/lib/d3pie.min.js
index f4a29c540..823f69735 100644
--- a/interface/js/lib/d3pie.min.js
+++ b/interface/js/lib/d3pie.min.js
@@ -1,5 +1,5 @@
 /*!
- * rspamd-D3Pie 1.0.0 (https://github.com/moisseev/rspamd-D3Pie)
+ * rspamd-D3Pie 1.1.0 (https://github.com/moisseev/rspamd-D3Pie)
  * Copyright (c) 2022, Alexander Moisseev, BSD 2-Clause
  */
-function D3Pie(v,t){"use strict";const A=$.extend(!0,{canvasPadding:5,cornerRadius:3,duration:1250,gradient:{enabled:!0,percentage:100},labels:{inner:{hideWhenLessThanPercentage:4,offset:.15},outer:{collideHeight:13,format:"label",pieDistance:30}},padAngle:.01,pieCenterOffset:{x:0,y:0},size:{canvasHeight:400,canvasWidth:600,pieInnerRadius:"20%",pieOuterRadius:"85%"},title:""},t),e=(this.destroy=function(){d3.selectAll("#"+v+" svg, #"+v+"-tooltip").remove()},this.destroy(),d3.select("#"+v).append("svg").attr("class","d3pie").attr("width",A.size.canvasWidth).attr("height",A.size.canvasHeight));let l=0;if(""!==A.title){const a=e.append("svg:text").attr("class","chart-title").attr("x",A.size.canvasWidth/2);a.append("tspan").text(A.title+" "),l=a.node().getBBox().height,a.attr("y",l+A.canvasPadding)}const x=e.append("g").attr("transform","translate("+(A.size.canvasWidth/2+A.pieCenterOffset.x)+","+(A.size.canvasHeight/2+l/2+A.pieCenterOffset.y)+")"),m={},y={},{outerRadius:M,innerRadius:R}=function(){function t(t,e){var a;return/%/u.test(t)?(a=Math.max(0,Math.min(99,parseInt(t.replace(/[\D]/u,""),10)))/100,Math.floor(e*a)):parseInt(t,10)}var e=A.size.canvasWidth-2*A.canvasPadding,a=A.size.canvasHeight-2*A.canvasPadding-l;let n=Math.min(e,a)/2;"none"!==A.labels.outer.format&&(e=parseInt(A.labels.outer.pieDistance,10),n>e&&(n-=e));a=t(A.size.pieOuterRadius,n);return{outerRadius:a,innerRadius:t(A.size.pieInnerRadius,a)}}(),w=M+A.labels.outer.pieDistance,P=d3.line().curve(d3.curveCatmullRomOpen),z=d3.select("body").append("div").attr("id",v+"-tooltip").attr("class","d3pie-tooltip"),I=z.append("span").attr("id",v+"-tooltip-text"),H=e.append("defs");this.data=function(t){let l=$.extend(!0,[],t);const o=[],e=l.reduce(function(t,e){return t+(e.value||0)},0),a=(z.datum({total:e}),l.unshift({label:"undefined",color:A.gradient.enabled?"steelblue":"#ecf1f5",value:0===e?1:0}),d3.scaleOrdinal(d3.schemeSet1));function n(t,e){return void 0!==t&&void 0!==t.color?t.color:a(e)}function s(t,e,a=e){return d3.arc().innerRadius(e).outerRadius(a).centroid(t)}function c(t){return d3.interpolate(m[t.data.label],t)}function u(t){var e=w-.1;return Math.max(-e,Math.min(e,t))}function r(r,i){y[r.data.label].newAngle=function(){var t=u(o[i].y);let e=Math.sqrt(Math.pow(w,2)-Math.pow(t,2)),a=((r.endAngle+r.startAngle)/2>Math.PI&&(e*=-1),Math.PI/2-Math.atan2(-t,e));return a<0&&(a+=2*Math.PI),{startAngle:a,endAngle:a}}();const d=d3.interpolate(y[r.data.label].currentAngle,y[r.data.label].newAngle);return function(t){var e=s(d(t),w),[a,n]=e,l=0<a?{dx:5,textAnchor:"start"}:{dx:-5,textAnchor:"end"};return d3.select(x.selectAll(".link").nodes()[i]).datum([s(c(r)(t),M),s(c(r)(t),M+5),[a,n],[a+l.dx,n]]).attr("d",P),d3.select(x.selectAll(".outer-label").nodes()[i]).attr("dx",l.dx).style("text-anchor",l.textAnchor),"translate("+e+")"}}t=d3.transition().duration(A.duration);if(A.gradient.enabled){const g=H.selectAll("radialGradient").data(l,function(t){return t.label}),h=g.enter().append("radialGradient").attr("gradientUnits","userSpaceOnUse").attr("cx",0).attr("cy",0).attr("r","120%").attr("id",function(t,e){return v+"-grad"+e});h.append("stop").attr("class","grad-stop-0").style("stop-color",n),h.append("stop").attr("class","grad-stop-1").attr("offset",A.gradient.percentage+"%"),H.selectAll("radialGradient").select(".grad-stop-0").transition(t).style("stop-color",n)}function i(t){return t.data.label}const d=d3.pie().sort(null).value(function(t){return t.value}),f=x.selectAll(".slice-g").data(d(l),i),p=f.enter().append("g").attr("class","slice-g").on("mouseover",function(t,e){var a=z.datum().total,n=a?Math.round(100*e.data.value/a):NaN;e.data.value?(z.transition().duration(300).style("opacity",1),I.text(e.data.label+(a?": "+e.data.value+" ("+n+"%)":""))):z.transition().duration(300).style("opacity",0),z.each(function(t){t.height=this.getBoundingClientRect().height})}).on("mouseout",function(){z.transition().duration(300).style("opacity",0)}).on("mousemove",t=>{const{pageX:e,pageY:a}=t;z.style("left",e+"px").style("top",function(t){return a-t.height-2+"px"})});if(p.append("path").attr("id",function(t,e){return v+"-slice"+e}).attr("class","slice").attr("fill",function(t,e){return A.gradient.enabled?"url(#"+v+"-grad"+e+")":n(t.data,e)}),f.exit().each(function(t,e){l[e]={value:0,label:t.data.label}}),d(l).forEach(function(t,e){void 0===m[t.data.label]&&(e=e?m[d(l)[e-1].data.label].endAngle:0,m[t.data.label]={startAngle:e,endAngle:e})}),x.selectAll(".slice").data(d(l),i).transition(t).attrTween("d",function(e){return function(t){return d3.arc().padAngle(A.padAngle).cornerRadius(A.cornerRadius).innerRadius(R).outerRadius(M)(c(e)(t))}}).end().then(function(){l=l.filter(function(t,e){return 0===e||t.value}),H.selectAll("radialGradient").data(l,function(t){return t.label}).exit().remove(),x.selectAll(".slice-g").data(d(l),i).exit().each(function(t){delete m[t.data.label],delete y[t.data.label]}).remove();for(const t of d(l))m[t.data.label]=t,"none"!==A.labels.outer.format&&(y[t.data.label].currentAngle=y[t.data.label].newAngle)}).catch(function(t){console.warn(t)}),p.append("text").attr("class","inner-label").attr("dy",".35em"),x.selectAll(".inner-label").data(d(l),i).text(function(t){return"undefined"===t.data.label?"undefined":Math.round(100*t.data.value/e)+"%"}).transition(t).attrTween("opacity",function(e){return e.data.value?function(t){t=c(e)(t);return 100*(t.endAngle-t.startAngle)/(2*Math.PI)<A.labels.inner.hideWhenLessThanPercentage?0:1}:function(){return 0}}).attrTween("transform",function(e){return function(t){return"translate("+s(c(e)(t),R*(1-A.labels.inner.offset),M*(1+A.labels.inner.offset))+")"}}),"none"!==A.labels.outer.format){d(l).forEach(function(t,e){void 0===y[t.data.label]&&(e=e?m[d(l)[e-1].data.label].endAngle:0,y[t.data.label]={currentAngle:{startAngle:e,endAngle:e},newAngle:{startAngle:e,endAngle:e}});let a=0;var[e,n]=s(t,w);t.data.value&&(a=0<=e?A.labels.outer.collideHeight:-A.labels.outer.collideHeight),o.push({fx:a,y:n})}),d3.forceSimulation(o).alphaMin(.5).force("collide",d3.forceCollide(A.labels.outer.collideHeight/2)).force("boundY",function(){for(const t of o)t.y=u(t.y)}).tick(30);const b=p.append("g").attr("class","outer-label-g");b.append("text").attr("class","outer-label").attr("dy",".35em").text(i),b.append("path").attr("class","link"),x.selectAll(".outer-label-g").data(d(l),i).transition(t).style("opacity",function(t,e){return e&&t.value?1:0}).each(function(t,e){$(this).children(".link").attr("stroke",n(t.data,e))}),x.selectAll(".outer-label").data(d(l),i).transition(t).attrTween("transform",r)}}}
\ No newline at end of file
+function D3Pie(v,t){"use strict";const A=$.extend(!0,{canvasPadding:5,cornerRadius:3,duration:1250,gradient:{enabled:!0,percentage:100},labels:{inner:{hideWhenLessThanPercentage:4,offset:.15},outer:{collideHeight:13,format:"label",pieDistance:30}},padAngle:.01,pieCenterOffset:{x:0,y:0},size:{canvasHeight:400,canvasWidth:600,pieInnerRadius:"20%",pieOuterRadius:"85%"},title:"",total:{enabled:!1}},t),e=(this.destroy=function(){d3.selectAll("#"+v+" svg, #"+v+"-tooltip").remove()},this.destroy(),d3.select("#"+v).append("svg").attr("class","d3pie").attr("width",A.size.canvasWidth).attr("height",A.size.canvasHeight));let l=0;if(""!==A.title){const a=e.append("svg:text").attr("class","chart-title").attr("x",A.size.canvasWidth/2);a.append("tspan").text(A.title+" "),l=a.node().getBBox().height,a.attr("y",l+A.canvasPadding)}const x=e.append("g").attr("transform","translate("+(A.size.canvasWidth/2+A.pieCenterOffset.x)+","+(A.size.canvasHeight/2+l/2+A.pieCenterOffset.y)+")"),y={},m={},{outerRadius:M,innerRadius:R}=function(){function t(t,e){var a;return/%/u.test(t)?(a=Math.max(0,Math.min(99,parseInt(t.replace(/[\D]/u,""),10)))/100,Math.floor(e*a)):parseInt(t,10)}var e=A.size.canvasWidth-2*A.canvasPadding,a=A.size.canvasHeight-2*A.canvasPadding-l;let n=Math.min(e,a)/2;"none"!==A.labels.outer.format&&(e=parseInt(A.labels.outer.pieDistance,10),n>e&&(n-=e));a=t(A.size.pieOuterRadius,n);return{outerRadius:a,innerRadius:t(A.size.pieInnerRadius,a)}}(),w=M+A.labels.outer.pieDistance,z=d3.line().curve(d3.curveCatmullRomOpen),P=d3.select("body").append("div").attr("id",v+"-tooltip").attr("class","d3pie-tooltip"),r=P.append("span").attr("id",v+"-tooltip-text");function I(t){t.on("mouseover",function(t,e){var a=P.datum().total,n=a?Math.round(100*e.data.value/a):NaN;e.data.value?(P.transition().duration(300).style("opacity",1),r.text(e.data.label+(a?": "+e.data.value+" ("+n+"%)":""))):P.transition().duration(300).style("opacity",0),P.each(function(t){t.height=this.getBoundingClientRect().height})}).on("mouseout",function(){P.transition().duration(300).style("opacity",0)}).on("mousemove",t=>{const{pageX:e,pageY:a}=t;P.style("left",e+"px").style("top",function(t){return a-t.height-2+"px"})})}const H=x.append("g");if(I(H),H.append("circle").attr("r",R).style("opacity",0),A.total.enabled){const n=H.append("text").attr("class","total-text");n.append("tspan").attr("class","total-value").style("font-size",.6*R+"px"),n.append("tspan").attr("x","0").attr("dy",.5*R).text(void 0!==A.total.label?A.total.label:"Total")}const O=e.append("defs");this.data=function(t){let l=$.extend(!0,[],t);const d=[],e=l.reduce(function(t,e){return t+(e.value||0)},0),a=(P.datum({total:e}),H.datum({data:{label:void 0!==A.total.label?A.total.label:"Total",value:e}}),A.total.enabled&&H.select(".total-value").text(d3.format(".3~s")(e)),l.unshift({label:"undefined",color:A.gradient.enabled?"steelblue":"#ecf1f5",value:0===e?1:0}),d3.scaleOrdinal(d3.schemeSet1));function n(t,e){return void 0!==t&&void 0!==t.color?t.color:a(e)}function s(t,e,a=e){return d3.arc().innerRadius(e).outerRadius(a).centroid(t)}function c(t){return d3.interpolate(y[t.data.label],t)}function u(t){var e=w-.1;return Math.max(-e,Math.min(e,t))}function r(r,i){m[r.data.label].newAngle=function(){var t=u(d[i].y);let e=Math.sqrt(Math.pow(w,2)-Math.pow(t,2)),a=((r.endAngle+r.startAngle)/2>Math.PI&&(e*=-1),Math.PI/2-Math.atan2(-t,e));return a<0&&(a+=2*Math.PI),{startAngle:a,endAngle:a}}();const o=d3.interpolate(m[r.data.label].currentAngle,m[r.data.label].newAngle);return function(t){var e=s(o(t),w),[a,n]=e,l=0<a?{dx:5,textAnchor:"start"}:{dx:-5,textAnchor:"end"};return d3.select(x.selectAll(".link").nodes()[i]).datum([s(c(r)(t),M),s(c(r)(t),M+5),[a,n],[a+l.dx,n]]).attr("d",z),d3.select(x.selectAll(".outer-label").nodes()[i]).attr("dx",l.dx).style("text-anchor",l.textAnchor),"translate("+e+")"}}t=d3.transition().duration(A.duration);if(A.gradient.enabled){const g=O.selectAll("radialGradient").data(l,function(t){return t.label}),h=g.enter().append("radialGradient").attr("gradientUnits","userSpaceOnUse").attr("cx",0).attr("cy",0).attr("r","120%").attr("id",function(t,e){return v+"-grad"+e});h.append("stop").attr("class","grad-stop-0").style("stop-color",n),h.append("stop").attr("class","grad-stop-1").attr("offset",A.gradient.percentage+"%"),O.selectAll("radialGradient").select(".grad-stop-0").transition(t).style("stop-color",n)}function i(t){return t.data.label}const o=d3.pie().sort(null).value(function(t){return t.value}),p=x.selectAll(".slice-g").data(o(l),i),f=p.enter().append("g").attr("class","slice-g");if(I(f),f.append("path").attr("id",function(t,e){return v+"-slice"+e}).attr("class","slice").attr("fill",function(t,e){return A.gradient.enabled?"url(#"+v+"-grad"+e+")":n(t.data,e)}),p.exit().each(function(t,e){l[e]={value:0,label:t.data.label}}),o(l).forEach(function(t,e){void 0===y[t.data.label]&&(e=e?y[o(l)[e-1].data.label].endAngle:0,y[t.data.label]={startAngle:e,endAngle:e})}),x.selectAll(".slice").data(o(l),i).transition(t).attrTween("d",function(e){return function(t){return d3.arc().padAngle(A.padAngle).cornerRadius(A.cornerRadius).innerRadius(R).outerRadius(M)(c(e)(t))}}).end().then(function(){l=l.filter(function(t,e){return 0===e||t.value}),O.selectAll("radialGradient").data(l,function(t){return t.label}).exit().remove(),x.selectAll(".slice-g").data(o(l),i).exit().each(function(t){delete y[t.data.label],delete m[t.data.label]}).remove();for(const t of o(l))y[t.data.label]=t,"none"!==A.labels.outer.format&&(m[t.data.label].currentAngle=m[t.data.label].newAngle)}).catch(function(t){console.warn(t)}),f.append("text").attr("class","inner-label").attr("dy",".35em"),x.selectAll(".inner-label").data(o(l),i).text(function(t){return"undefined"===t.data.label?"undefined":Math.round(100*t.data.value/e)+"%"}).transition(t).attrTween("opacity",function(e){return e.data.value?function(t){t=c(e)(t);return 100*(t.endAngle-t.startAngle)/(2*Math.PI)<A.labels.inner.hideWhenLessThanPercentage?0:1}:function(){return 0}}).attrTween("transform",function(e){return function(t){return"translate("+s(c(e)(t),R*(1-A.labels.inner.offset),M*(1+A.labels.inner.offset))+")"}}),"none"!==A.labels.outer.format){o(l).forEach(function(t,e){void 0===m[t.data.label]&&(e=e?y[o(l)[e-1].data.label].endAngle:0,m[t.data.label]={currentAngle:{startAngle:e,endAngle:e},newAngle:{startAngle:e,endAngle:e}});let a=0;var[e,n]=s(t,w);t.data.value&&(a=0<=e?A.labels.outer.collideHeight:-A.labels.outer.collideHeight),d.push({fx:a,y:n})}),d3.forceSimulation(d).alphaMin(.5).force("collide",d3.forceCollide(A.labels.outer.collideHeight/2)).force("boundY",function(){for(const t of d)t.y=u(t.y)}).tick(30);const b=f.append("g").attr("class","outer-label-g");b.append("text").attr("class","outer-label").attr("dy",".35em").text(i),b.append("path").attr("class","link"),x.selectAll(".outer-label-g").data(o(l),i).transition(t).style("opacity",function(t,e){return e&&t.value?1:0}).each(function(t,e){$(this).children(".link").attr("stroke",n(t.data,e))}),x.selectAll(".outer-label").data(o(l),i).transition(t).attrTween("transform",r)}}}
\ No newline at end of file


More information about the Commits mailing list