refactor(ui): enhance chart destruction and instance management
Improve robustness of chart updates in dashboard components by adding force destroy checks across canvas properties, component state, and Chart.js registry to prevent memory leaks and instance conflicts. Save new chart instances to both canvas and component for better tracking.
This commit is contained in:
@@ -135,16 +135,6 @@ window.DashboardCharts.createDataset = function (label, data, color, canvas) {
|
|||||||
* @param {object} component - Dashboard component instance
|
* @param {object} component - Dashboard component instance
|
||||||
*/
|
*/
|
||||||
window.DashboardCharts.updateCharts = function (component) {
|
window.DashboardCharts.updateCharts = function (component) {
|
||||||
// Safely destroy existing chart instance FIRST
|
|
||||||
if (component.charts.quotaDistribution) {
|
|
||||||
try {
|
|
||||||
component.charts.quotaDistribution.destroy();
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Failed to destroy quota chart:", e);
|
|
||||||
}
|
|
||||||
component.charts.quotaDistribution = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const canvas = document.getElementById("quotaChart");
|
const canvas = document.getElementById("quotaChart");
|
||||||
|
|
||||||
// Safety checks
|
// Safety checks
|
||||||
@@ -152,6 +142,33 @@ window.DashboardCharts.updateCharts = function (component) {
|
|||||||
console.debug("quotaChart canvas not found");
|
console.debug("quotaChart canvas not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FORCE DESTROY: Check for existing chart on the canvas element property
|
||||||
|
// This handles cases where Component state is lost but DOM persists
|
||||||
|
if (canvas._chartInstance) {
|
||||||
|
console.debug("Destroying existing quota chart from canvas property");
|
||||||
|
try {
|
||||||
|
canvas._chartInstance.destroy();
|
||||||
|
} catch(e) { console.warn(e); }
|
||||||
|
canvas._chartInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check component state as backup
|
||||||
|
if (component.charts.quotaDistribution) {
|
||||||
|
try {
|
||||||
|
component.charts.quotaDistribution.destroy();
|
||||||
|
} catch(e) { }
|
||||||
|
component.charts.quotaDistribution = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also try Chart.js registry
|
||||||
|
if (typeof Chart !== "undefined" && Chart.getChart) {
|
||||||
|
const regChart = Chart.getChart(canvas);
|
||||||
|
if (regChart) {
|
||||||
|
try { regChart.destroy(); } catch(e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof Chart === "undefined") {
|
if (typeof Chart === "undefined") {
|
||||||
console.warn("Chart.js not loaded");
|
console.warn("Chart.js not loaded");
|
||||||
return;
|
return;
|
||||||
@@ -250,39 +267,46 @@ window.DashboardCharts.updateCharts = function (component) {
|
|||||||
labels.push(depletedLabel);
|
labels.push(depletedLabel);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create Chart
|
||||||
try {
|
try {
|
||||||
component.charts.quotaDistribution = new Chart(canvas, {
|
const newChart = new Chart(canvas, {
|
||||||
type: "doughnut",
|
// ... config
|
||||||
data: {
|
type: "doughnut",
|
||||||
labels: labels,
|
data: {
|
||||||
datasets: [
|
labels: labels,
|
||||||
{
|
datasets: [
|
||||||
data: data,
|
{
|
||||||
backgroundColor: colors,
|
data: data,
|
||||||
borderColor: getThemeColor("--color-space-950"),
|
backgroundColor: colors,
|
||||||
borderWidth: 0,
|
borderColor: getThemeColor("--color-space-950"),
|
||||||
hoverOffset: 0,
|
borderWidth: 0,
|
||||||
borderRadius: 0,
|
hoverOffset: 0,
|
||||||
},
|
borderRadius: 0,
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
options: {
|
},
|
||||||
responsive: true,
|
options: {
|
||||||
maintainAspectRatio: false,
|
responsive: true,
|
||||||
cutout: "85%",
|
maintainAspectRatio: false,
|
||||||
rotation: -90,
|
cutout: "85%",
|
||||||
circumference: 360,
|
rotation: -90,
|
||||||
plugins: {
|
circumference: 360,
|
||||||
legend: { display: false },
|
plugins: {
|
||||||
tooltip: { enabled: false },
|
legend: { display: false },
|
||||||
title: { display: false },
|
tooltip: { enabled: false },
|
||||||
},
|
title: { display: false },
|
||||||
animation: {
|
},
|
||||||
animateScale: true,
|
animation: {
|
||||||
animateRotate: true,
|
animateScale: true,
|
||||||
},
|
animateRotate: true,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SAVE INSTANCE TO CANVAS AND COMPONENT
|
||||||
|
canvas._chartInstance = newChart;
|
||||||
|
component.charts.quotaDistribution = newChart;
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to create quota chart:", e);
|
console.error("Failed to create quota chart:", e);
|
||||||
}
|
}
|
||||||
@@ -302,28 +326,46 @@ window.DashboardCharts.updateTrendChart = function (component) {
|
|||||||
|
|
||||||
console.log("[updateTrendChart] Starting update...");
|
console.log("[updateTrendChart] Starting update...");
|
||||||
|
|
||||||
// Safely destroy existing chart instance FIRST
|
const canvas = document.getElementById("usageTrendChart");
|
||||||
if (component.charts.usageTrend) {
|
|
||||||
console.log("[updateTrendChart] Destroying existing chart");
|
// FORCE DESTROY: Check for existing chart on the canvas element property
|
||||||
try {
|
if (canvas) {
|
||||||
// Stop all animations before destroying to prevent null context errors
|
if (canvas._chartInstance) {
|
||||||
component.charts.usageTrend.stop();
|
console.debug("Destroying existing trend chart from canvas property");
|
||||||
component.charts.usageTrend.destroy();
|
try {
|
||||||
} catch (e) {
|
canvas._chartInstance.stop();
|
||||||
console.error("[updateTrendChart] Failed to destroy chart:", e);
|
canvas._chartInstance.destroy();
|
||||||
}
|
} catch(e) { console.warn(e); }
|
||||||
component.charts.usageTrend = null;
|
canvas._chartInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also try Chart.js registry
|
||||||
|
if (typeof Chart !== "undefined" && Chart.getChart) {
|
||||||
|
const regChart = Chart.getChart(canvas);
|
||||||
|
if (regChart) {
|
||||||
|
try { regChart.stop(); regChart.destroy(); } catch(e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvas = document.getElementById("usageTrendChart");
|
// Also check component state
|
||||||
|
if (component.charts.usageTrend) {
|
||||||
|
try {
|
||||||
|
component.charts.usageTrend.stop();
|
||||||
|
component.charts.usageTrend.destroy();
|
||||||
|
} catch (e) { }
|
||||||
|
component.charts.usageTrend = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Safety checks
|
// Safety checks
|
||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
console.error("[updateTrendChart] Canvas not found in DOM!");
|
console.error("[updateTrendChart] Canvas not found in DOM!");
|
||||||
|
_trendChartUpdateLock = false; // Release lock!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof Chart === "undefined") {
|
if (typeof Chart === "undefined") {
|
||||||
console.error("[updateTrendChart] Chart.js not loaded");
|
console.error("[updateTrendChart] Chart.js not loaded");
|
||||||
|
_trendChartUpdateLock = false; // Release lock!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,7 +518,7 @@ window.DashboardCharts.updateTrendChart = function (component) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
component.charts.usageTrend = new Chart(canvas, {
|
const newChart = new Chart(canvas, {
|
||||||
type: "line",
|
type: "line",
|
||||||
data: { labels, datasets },
|
data: { labels, datasets },
|
||||||
options: {
|
options: {
|
||||||
@@ -533,6 +575,11 @@ window.DashboardCharts.updateTrendChart = function (component) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SAVE INSTANCE
|
||||||
|
canvas._chartInstance = newChart;
|
||||||
|
component.charts.usageTrend = newChart;
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to create trend chart:", e);
|
console.error("Failed to create trend chart:", e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user