mirror of
https://github.com/pi-hole/web.git
synced 2024-12-06 19:36:21 +01:00
Use custom legends for doughnut charts
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
This commit is contained in:
committed by
Christian König
parent
bbdc4c3d6b
commit
62297be1da
@@ -116,9 +116,10 @@ require_once 'scripts/pi-hole/php/gravity.php';
|
||||
<h3 class="box-title">Query Types</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="pull-left" style="width:100%">
|
||||
<canvas id="queryTypePieChart"></canvas>
|
||||
<div style="width:50%">
|
||||
<canvas id="queryTypePieChart" width="280" height="280"></canvas>
|
||||
</div>
|
||||
<div class="chart-legend" style="width:50%" id="query-types-legend" ></div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-sync fa-spin"></i>
|
||||
@@ -132,9 +133,10 @@ require_once 'scripts/pi-hole/php/gravity.php';
|
||||
<h3 class="box-title">Upstream servers</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="pull-left" style="width:100%">
|
||||
<canvas id="forwardDestinationPieChart" class="extratooltipcanvas no-user-select"></canvas>
|
||||
<div style="width:50%">
|
||||
<canvas id="forwardDestinationPieChart" width="280" height="280" class="extratooltipcanvas no-user-select"></canvas>
|
||||
</div>
|
||||
<div class="chart-legend" style="width:50%" id="forward-destinations-legend"></div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-sync fa-spin"></i>
|
||||
|
||||
+92
-120
@@ -318,115 +318,21 @@ function updateQueryTypesPie() {
|
||||
queryTypePieChart.data.labels = k;
|
||||
$("#query-types-pie .overlay").hide();
|
||||
queryTypePieChart.update();
|
||||
|
||||
// Don't use rotation animation for further updates
|
||||
queryTypePieChart.options.animation.duration = 0;
|
||||
queryTypePieChart.options.legendCallback = customLegend;
|
||||
|
||||
// Generate legend in separate div
|
||||
// $("#query-types-legend").html(queryTypePieChart.generateLegend());
|
||||
$("#query-types-legend > ul > li").click(function (e) {
|
||||
if (iscolorBox(e.target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#query-types-legend > ul > li > p").click(function () {
|
||||
window.location.href = "queries.php?querytype=" + querytypeids[$(this).index()];
|
||||
});
|
||||
$("#query-types-legend .colorBoxWrapper").click(function (e) {
|
||||
hidePieSlice(e);
|
||||
});
|
||||
}).done(function () {
|
||||
// Reload graph after minute
|
||||
setTimeout(updateQueryTypesPie, 60000);
|
||||
});
|
||||
}
|
||||
|
||||
function customLegend(chart) {
|
||||
var text = [];
|
||||
var data = chart.data;
|
||||
var datasets = data.datasets;
|
||||
var labels = data.labels;
|
||||
|
||||
text.push('<ul class="' + chart.id + '-legend">');
|
||||
|
||||
if (datasets.length > 0) {
|
||||
for (var i = 0; i < datasets[0].data.length; ++i) {
|
||||
var color = datasets[0].backgroundColor[i];
|
||||
|
||||
var txt = "";
|
||||
|
||||
// legend box icon
|
||||
txt =
|
||||
'<span class="colorBoxWrapper" style="color: ' +
|
||||
color +
|
||||
'" title="Toggle visibility">' +
|
||||
'<i class="fa fa-check-square"></i>' +
|
||||
"</span>";
|
||||
|
||||
// color block
|
||||
txt += '<span class="legend-color-box" style="background-color:' + color + '"></span>';
|
||||
|
||||
// label
|
||||
if (labels[i]) {
|
||||
txt +=
|
||||
'<span class="legend-label-text" title="List ' +
|
||||
labels[i] +
|
||||
' queries">' +
|
||||
labels[i] +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
text.push("<li>" + txt + "</li>");
|
||||
}
|
||||
}
|
||||
|
||||
text.push("</ul>");
|
||||
return text.join("");
|
||||
}
|
||||
|
||||
function hidePieSlice(event) {
|
||||
togglecolorBox(event.target);
|
||||
|
||||
var legendID = $(event.target).closest(".chart-legend").attr("id");
|
||||
var ci =
|
||||
legendID === "query-types-legend"
|
||||
? event.view.queryTypePieChart
|
||||
: event.view.forwardDestinationPieChart;
|
||||
|
||||
var listItemParent = $(event.target).closest("li");
|
||||
$(listItemParent).toggleClass("strike");
|
||||
|
||||
var index = $(listItemParent).index();
|
||||
var mobj = ci.data.datasets[0]._meta;
|
||||
var metas = Object.keys(mobj).map(function (e) {
|
||||
return mobj[e];
|
||||
});
|
||||
metas.forEach(function (meta) {
|
||||
var curr = meta.data[index];
|
||||
curr.hidden = !curr.hidden;
|
||||
});
|
||||
|
||||
ci.update();
|
||||
}
|
||||
|
||||
function togglecolorBox(target) {
|
||||
var parentListItem = $(target).closest("li");
|
||||
var colorBox = $(parentListItem).find(".fa-check-square, .fa-square");
|
||||
|
||||
if (colorBox) {
|
||||
$(colorBox).toggleClass("fa-check-square");
|
||||
$(colorBox).toggleClass("fa-square");
|
||||
}
|
||||
}
|
||||
|
||||
function iscolorBox(target) {
|
||||
// See if click happened on colorBoxWrapper or child SVG
|
||||
if ($(target).closest(".colorBoxWrapper")[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateClientsOverTime() {
|
||||
$.getJSON("api.php?overTimeDataClients&getClientNames", function (data) {
|
||||
if ("FTLnotrunning" in data) {
|
||||
@@ -563,25 +469,18 @@ function updateForwardDestinationsPie() {
|
||||
// and push it at once
|
||||
$("#forward-destinations-pie .overlay").hide();
|
||||
forwardDestinationPieChart.update();
|
||||
|
||||
// Don't use rotation animation for further updates
|
||||
forwardDestinationPieChart.options.animation.duration = 0;
|
||||
forwardDestinationPieChart.options.legendCallback = customLegend;
|
||||
|
||||
// Generate legend in separate div
|
||||
// $("#forward-destinations-legend").html(forwardDestinationPieChart.generateLegend());
|
||||
$("#forward-destinations-legend > ul > li").click(function (e) {
|
||||
if (iscolorBox(e.target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#forward-destinations-legend > ul > li > p").click(function (e) {
|
||||
var obj = encodeURIComponent(e.target.textContent);
|
||||
if (obj.length > 0) {
|
||||
window.location.href = "queries.php?forwarddest=" + obj;
|
||||
}
|
||||
});
|
||||
$("#forward-destinations-legend .colorBoxWrapper").click(function (e) {
|
||||
hidePieSlice(e);
|
||||
});
|
||||
}).done(function () {
|
||||
// Reload graph after one minute
|
||||
setTimeout(updateForwardDestinationsPie, 60000);
|
||||
@@ -869,6 +768,84 @@ function getMaxlogage() {
|
||||
});
|
||||
}
|
||||
|
||||
// chartjs plugin used by the custom doughnut legend
|
||||
const getOrCreateLegendList = (chart, id) => {
|
||||
const legendContainer = document.getElementById(id);
|
||||
let listContainer = legendContainer.querySelector("ul");
|
||||
|
||||
if (!listContainer) {
|
||||
listContainer = document.createElement("ul");
|
||||
listContainer.style.display = "flex";
|
||||
listContainer.style.flexDirection = "column";
|
||||
listContainer.style.margin = 0;
|
||||
listContainer.style.padding = 0;
|
||||
|
||||
legendContainer.append(listContainer);
|
||||
}
|
||||
|
||||
return listContainer;
|
||||
};
|
||||
|
||||
const htmlLegendPlugin = {
|
||||
id: "htmlLegend",
|
||||
afterUpdate(chart, args, options) {
|
||||
const ul = getOrCreateLegendList(chart, options.containerID);
|
||||
|
||||
// Remove old legend items
|
||||
while (ul.firstChild) {
|
||||
ul.firstChild.remove();
|
||||
}
|
||||
|
||||
// Reuse the built-in legendItems generator
|
||||
const items = chart.options.plugins.legend.labels.generateLabels(chart);
|
||||
|
||||
items.forEach(item => {
|
||||
const li = document.createElement("li");
|
||||
li.style.alignItems = "center";
|
||||
li.style.cursor = "pointer";
|
||||
li.style.display = "flex";
|
||||
li.style.flexDirection = "row";
|
||||
|
||||
li.addEventListener('click', () => {
|
||||
const { type } = chart.config;
|
||||
|
||||
if (type === "pie" || type === "doughnut") {
|
||||
// Pie and doughnut charts only have a single dataset and visibility is per item
|
||||
chart.toggleDataVisibility(item.index);
|
||||
} else {
|
||||
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
|
||||
}
|
||||
|
||||
chart.update();
|
||||
};
|
||||
|
||||
// Color checkbox
|
||||
const boxSpan = document.createElement("span");
|
||||
boxSpan.style.color = item.fillStyle;
|
||||
boxSpan.style.display = "inline-block";
|
||||
boxSpan.style.margin = "0 10px";
|
||||
boxSpan.innerHTML = item.hidden
|
||||
? '<i class="colorBoxWrapper fa fa-square"></i>'
|
||||
: '<i class="colorBoxWrapper fa fa-check-square"></i>';
|
||||
|
||||
// Text
|
||||
const textContainer = document.createElement("p");
|
||||
textContainer.style.color = item.fontColor;
|
||||
textContainer.style.margin = 0;
|
||||
textContainer.style.padding = 0;
|
||||
textContainer.style.textDecoration = item.hidden ? "line-through" : "";
|
||||
textContainer.className = "legend-label-text";
|
||||
|
||||
const text = document.createTextNode(item.text);
|
||||
textContainer.append(text);
|
||||
|
||||
li.append(boxSpan);
|
||||
li.append(textContainer);
|
||||
ul.append(li);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
$(function () {
|
||||
// Pull in data via AJAX
|
||||
getMaxlogage();
|
||||
@@ -1139,6 +1116,7 @@ $(function () {
|
||||
labels: [],
|
||||
datasets: [{ data: [], parsing: false }],
|
||||
},
|
||||
plugins: [htmlLegendPlugin],
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
@@ -1148,14 +1126,11 @@ $(function () {
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
htmlLegend: {
|
||||
containerID: "query-types-legend",
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: "right",
|
||||
align: "start",
|
||||
labels: {
|
||||
boxWidth: 13,
|
||||
color: ticksColor,
|
||||
},
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
// Disable the on-canvas tooltip
|
||||
@@ -1169,7 +1144,6 @@ $(function () {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
animation: {
|
||||
duration: 750,
|
||||
},
|
||||
@@ -1188,6 +1162,7 @@ $(function () {
|
||||
labels: [],
|
||||
datasets: [{ data: [], parsing: false }],
|
||||
},
|
||||
plugins: [htmlLegendPlugin],
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
@@ -1197,14 +1172,11 @@ $(function () {
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
htmlLegend: {
|
||||
containerID: "forward-destinations-legend",
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: "right",
|
||||
align: "start",
|
||||
labels: {
|
||||
boxWidth: 13,
|
||||
color: ticksColor,
|
||||
},
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
// Disable the on-canvas tooltip
|
||||
|
||||
+10
-22
@@ -234,49 +234,37 @@ td.lookatme {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.chart-legend {
|
||||
overflow: auto;
|
||||
#query-types-pie .box-body,
|
||||
#forward-destinations-pie .box-body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chart-legend ul {
|
||||
list-style-type: none;
|
||||
padding-left: 45px;
|
||||
.chart-legend {
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.chart-legend li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
line-height: 1;
|
||||
margin: 0 0 6px;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.chart-legend li span {
|
||||
display: inline-block;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.colorBoxWrapper {
|
||||
font-size: 14px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.colorBoxWrapper:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.chart-legend li .colorBoxWrapper {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
left: -20px;
|
||||
top: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.chart-legend li .legend-color-box {
|
||||
display: none;
|
||||
width: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-legend li .legend-label-text {
|
||||
line-height: 1;
|
||||
word-break: break-word;
|
||||
|
||||
Reference in New Issue
Block a user