replaced chartjs legend with custom HTML legend

main
Simon Lang 3 months ago
parent 7a0c26ce5d
commit e84662a746
  1. 143
      public/elotracker/chart-integration.js
  2. 1
      public/elotracker/index.php
  3. 42
      public/elotracker/styles.css

@ -46,48 +46,115 @@ const zoom_plugin = {
mode: "x",
},
}
const trigger_legend_hover = function (e, legendItem, legend) {
if (legendItem.hidden) {
let hovered = false;
const trigger_legend_hover = function (legendItem, chart) {
if (legendItem.hidden || hovered) {
return;
}
legend.chart.data.datasets.forEach((dataset, i) => {
hovered = true;
chart.data.datasets.forEach((dataset, i) => {
dataset.backgroundColor = (legendItem.datasetIndex === i || dataset.backgroundColor.length === 9) ? dataset.backgroundColor : dataset.backgroundColor + '3D';
dataset.borderColor = (legendItem.datasetIndex === i || dataset.borderColor.length === 9) ? dataset.borderColor : dataset.borderColor + '3D';
dataset.borderWidth = legendItem.datasetIndex === i ? 4 : 3;
dataset.pointRadius = dataset.pointRadius===0 ? 0 : 2;
});
legend.chart.update();
chart.update();
}
const trigger_legend_leave = function (e, legendItem, legend) {
legend.chart.data.datasets.forEach((dataset, i) => {
const trigger_legend_leave = function (chart) {
chart.data.datasets.forEach((dataset, i) => {
dataset.backgroundColor = dataset.backgroundColor.length === 9 ? dataset.backgroundColor.slice(0, -2) : dataset.backgroundColor;
dataset.borderColor = dataset.borderColor.length === 9 ? dataset.borderColor.slice(0, -2) : dataset.borderColor;
dataset.borderWidth = 3;
});
legend.chart.update();
}
const legend_plugin = {
position: "bottom",
labels: {
padding: 10,
useBorderRadius: true,
borderRadius: 2,
boxWidth: 20,
},
onHover: trigger_legend_hover,
onLeave: trigger_legend_leave,
onClick: function (e, legendItem, legend) {
if (legend.chart.isDatasetVisible(legendItem.datasetIndex)) {
trigger_legend_leave(e, legendItem, legend);
legend.chart.hide(legendItem.datasetIndex);
legendItem.hidden = true;
chart.update();
hovered = false;
}
const trigger_legend_click = function (item) {
combined_charts.forEach(c_chart => {
if (c_chart.isDatasetVisible(item.datasetIndex)) {
trigger_legend_leave(c_chart)
c_chart.hide(item.datasetIndex);
item.hidden = true;
} else {
legend.chart.show(legendItem.datasetIndex);
legendItem.hidden = false;
trigger_legend_hover(e, legendItem, legend);
c_chart.show(item.datasetIndex);
item.hidden = false;
trigger_legend_hover(item, c_chart)
}
})
}
// break hover state when mouseout fails to trigger
window.onmousemove = (e) => {
if (hovered && (e.target.closest("ul") === null || !e.target.closest("ul").classList.contains("legend-list"))) {
console.log("breaking hover")
combined_charts.forEach(chart => {
trigger_legend_leave(chart);
})
}
}
const get_create_LegendList = (chart, id) => {
let created = false;
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector("ul");
if (!listContainer) {
created = true;
listContainer = document.createElement("ul");
listContainer.classList.add('legend-list');
legendContainer.appendChild(listContainer);
}
return {list: listContainer, created: created};
}
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
//console.log("update")
const legendList = get_create_LegendList(chart, options.containerID);
const ul = legendList.list;
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
if (legendList.created) {
const li = document.createElement('li');
li.classList.add("legend-element");
li.onmouseover = () => {trigger_legend_hover(item, chart)};
li.onmouseout = () => {trigger_legend_leave(chart)};
li.onclick = () => {trigger_legend_click(item)};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = item.fontColor;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
} else {
const list_element = ul.querySelector(`li.legend-element:nth-of-type(${item.datasetIndex+1})`);
const boxSpan = list_element.querySelector("span");
const textContainer = list_element.querySelector("p");
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
textContainer.style.color = item.fontColor;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
}
});
},
};
window.onload = create_charts;
@ -205,7 +272,12 @@ async function create_charts() {
plugins: {
tooltip: tooltip_plugin,
zoom: {...zoom_plugin},
legend: legend_plugin,
legend: {
display: false,
},
htmlLegend: {
containerID: 'combined-chart-legend',
}
},
scales: {
x: x_scale,
@ -221,7 +293,8 @@ async function create_charts() {
},
responsive: true,
maintainAspectRatio: false,
}
},
plugins: [htmlLegendPlugin],
}));
combined_charts.push(new Chart(`progress-chart-combined-progress`, {
type: "line",
@ -232,7 +305,12 @@ async function create_charts() {
plugins: {
tooltip: tooltip_plugin,
zoom: {...zoom_plugin},
legend: legend_plugin,
legend: {
display: false,
},
htmlLegend: {
containerID: 'combined-chart-legend',
}
},
scales: {
x: x_scale,
@ -242,7 +320,8 @@ async function create_charts() {
},
responsive: true,
maintainAspectRatio: false,
}
},
plugins: [htmlLegendPlugin],
}));
minval -= 7200000;
maxval += 7200000;
@ -384,9 +463,11 @@ function toggle_combined_chart() {
buttons.forEach(element => element.classList.remove("dropdown-open"));
chart.classList.remove("closed");
this.classList.add("dropdown-open");
document.querySelector("#combined-chart-legend").classList.remove("hidden");
} else {
chart.classList.add("closed");
this.classList.remove("dropdown-open");
document.querySelector("#combined-chart-legend").classList.add("hidden");
}
}
@ -471,7 +552,7 @@ async function update_leaderboard_elements() {
// replace updated Leaderboard
document.querySelector(".leaderboard-list").outerHTML = leaderboard_list;
// reapply EventListeners
document.querySelectorAll("button.leaderboard-element").forEach(element => element.addEventListener("click", toggle_leaderboard_chart));
document.querySelectorAll("button.leaderboard-element").forEach(element => element.addEventListener("mousedown", toggle_leaderboard_chart));
// recreate charts
create_charts();
})

@ -19,6 +19,7 @@
</div>
<div class='graph-wrapper rank-graph closed'><canvas id="progress-chart-combined" style="width:100%;max-width:700px"></canvas></div>
<div class='graph-wrapper progress-graph closed'><canvas id="progress-chart-combined-progress" style="width:100%;max-width:700px"></canvas></div>
<div id="combined-chart-legend" class="combined-chart-legend hidden"></div>
<?php include "leaderboard_list.php"; ?>
</div>
<script src="chart-integration.js"></script>

@ -273,3 +273,45 @@ button.button-updating svg {
.leaderboard>.graph-wrapper.closed {
height: 0;
}
.combined-chart-legend {
display: grid;
grid-template-rows: 1fr;
transition: height 400ms, grid-template-rows 400ms;
overflow: hidden;
}
.combined-chart-legend.hidden {
grid-template-rows: 0fr;
}
ul.legend-list {
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
min-height: 0;
}
li.legend-element {
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
padding: 2px 8px;
border-radius: 4px;
transition: background-color 200ms;
}
li.legend-element:hover {
background-color: #1f2931;
}
li.legend-element span {
display: inline-block;
flex-shrink: 0;
height: 20px;
width: 20px;
margin-right: 10px;
border-radius: 4px;
transition: background-color 200ms, border-color 200ms;
}
li.legend-element p {
margin: 0;
padding: 0;
}
Loading…
Cancel
Save