Compare commits

..

3 Commits

Author SHA1 Message Date
Simon Lang 829e882270 Merge remote-tracking branch 'origin/main' 3 months ago
Simon Lang 955c4d146e custom chart legend sorted by latest entry 3 months ago
Simon Lang e84662a746 replaced chartjs legend with custom HTML legend 3 months ago
  1. 160
      public/elotracker/chart-integration.js
  2. 4
      public/elotracker/index.php
  3. 47
      public/elotracker/styles.css

@ -46,48 +46,130 @@ 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 (chart.legend.legendItems[legendItem.datasetIndex].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();
chart.update();
hovered = false;
}
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;
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);
c_chart.legend.legendItems[item.datasetIndex].hidden = true;
} else {
legend.chart.show(legendItem.datasetIndex);
legendItem.hidden = false;
trigger_legend_hover(e, legendItem, legend);
c_chart.show(item.datasetIndex);
c_chart.legend.legendItems[item.datasetIndex].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) {
function sort_legend(a,b) {
const a_values = chart.data.datasets[a.datasetIndex].data
const b_values = chart.data.datasets[b.datasetIndex].data
const a_value = a_values[a_values.length - 1].y;
const b_value = b_values[b_values.length - 1].y;
if (a_value > b_value) {
return -1;
} else if (a_value < b_value) {
return 1;
}
return 0;
}
//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.sort(sort_legend);
items.forEach((item, i) => {
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(${i+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 +287,12 @@ async function create_charts() {
plugins: {
tooltip: tooltip_plugin,
zoom: {...zoom_plugin},
legend: legend_plugin,
legend: {
display: false,
},
htmlLegend: {
containerID: 'legend-rank-graph',
}
},
scales: {
x: x_scale,
@ -221,7 +308,8 @@ async function create_charts() {
},
responsive: true,
maintainAspectRatio: false,
}
},
plugins: [htmlLegendPlugin],
}));
combined_charts.push(new Chart(`progress-chart-combined-progress`, {
type: "line",
@ -232,7 +320,12 @@ async function create_charts() {
plugins: {
tooltip: tooltip_plugin,
zoom: {...zoom_plugin},
legend: legend_plugin,
legend: {
display: false,
},
htmlLegend: {
containerID: 'legend-progress-graph',
}
},
scales: {
x: x_scale,
@ -242,7 +335,8 @@ async function create_charts() {
},
responsive: true,
maintainAspectRatio: false,
}
},
plugins: [htmlLegendPlugin],
}));
minval -= 7200000;
maxval += 7200000;
@ -379,14 +473,20 @@ function toggle_combined_chart() {
const comb_charts = this.parentNode.parentNode.querySelectorAll(`.leaderboard>.graph-wrapper`);
const chart = this.parentNode.parentNode.querySelector(`.graph-wrapper.${this.id}`);
const buttons = document.querySelectorAll("button.open-general-graph");
const legend = document.querySelector(`#combined-chart-legends #legend-${this.id}`)
const all_legends = document.querySelectorAll(`#combined-chart-legends .chart-legend`);
all_legends.forEach(element => {element.classList.add("hidden")});
legend.classList.remove("hidden");
if (chart.classList.contains("closed")) {
comb_charts.forEach(element => element.classList.add("closed"));
buttons.forEach(element => element.classList.remove("dropdown-open"));
chart.classList.remove("closed");
this.classList.add("dropdown-open");
document.querySelector("#combined-chart-legends").classList.remove("hidden");
} else {
chart.classList.add("closed");
this.classList.remove("dropdown-open");
document.querySelector("#combined-chart-legends").classList.add("hidden");
}
}
@ -471,7 +571,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,10 @@
</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-legends" class="hidden">
<div id="legend-rank-graph" class="chart-legend "></div>
<div id="legend-progress-graph" class="chart-legend "></div>
</div>
<?php include "leaderboard_list.php"; ?>
</div>
<script src="chart-integration.js"></script>

@ -264,7 +264,7 @@ button.button-updating svg {
position: relative;
display: flex;
justify-content: center;
height: 600px;
height: 520px;
width: 90vw;
max-width: 800px;
transition: height 400ms;
@ -272,4 +272,49 @@ button.button-updating svg {
}
.leaderboard>.graph-wrapper.closed {
height: 0;
}
#combined-chart-legends .chart-legend {
display: grid;
grid-template-rows: 1fr;
transition: height 400ms, grid-template-rows 400ms;
overflow: hidden;
}
#combined-chart-legends.hidden .chart-legend {
grid-template-rows: 0fr;
}
#combined-chart-legends .chart-legend.hidden {
display: none;
}
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