fix(dees-chart-area): Improve resize handling and initial rendering for better responsiveness

fix(dees-chart-log): Simplify firstUpdated method by removing unnecessary variable
This commit is contained in:
Juergen Kunz
2025-06-12 11:00:33 +00:00
parent fad7fda2a6
commit e39590df2c
3 changed files with 72 additions and 18 deletions

View File

@ -8,8 +8,10 @@
### dees-chart-area ### dees-chart-area
- Fully functional area chart component using ApexCharts - Fully functional area chart component using ApexCharts
- Displays time-series data with gradient fills - Displays time-series data with gradient fills
- Responsive with ResizeObserver - Responsive with ResizeObserver (debounced to prevent flicker)
- Demo shows CPU and memory usage metrics - Demo shows CPU and memory usage metrics
- Fixed: Chart now properly respects container boundaries on initial render
- Overflow prevention with proper CSS containment
### dees-chart-log ### dees-chart-log
- Server log viewer component (not a chart despite the name) - Server log viewer component (not a chart despite the name)

View File

@ -33,27 +33,40 @@ export class DeesChartArea extends DeesElement {
public label: string = 'Untitled Chart'; public label: string = 'Untitled Chart';
private resizeObserver: ResizeObserver; private resizeObserver: ResizeObserver;
private resizeTimeout: number;
constructor() { constructor() {
super(); super();
domtools.elementBasic.setup(); domtools.elementBasic.setup();
this.resizeObserver = new ResizeObserver((entries) => { this.resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) { // Debounce resize calls to prevent excessive updates
if (entry.target.classList.contains('mainbox')) { if (this.resizeTimeout) {
this.resizeChart(); // Call resizeChart when the .mainbox size changes clearTimeout(this.resizeTimeout);
}
} }
this.resizeTimeout = window.setTimeout(() => {
for (let entry of entries) {
if (entry.target.classList.contains('mainbox') && this.chart) {
this.resizeChart();
}
}
}, 100); // 100ms debounce
}); });
this.registerStartupFunction(async () => { this.registerStartupFunction(async () => {
this.updateComplete.then(() => { this.updateComplete.then(() => {
const mainbox = this.shadowRoot.querySelector('.mainbox'); const mainbox = this.shadowRoot.querySelector('.mainbox');
if (mainbox) { if (mainbox) {
this.resizeObserver.observe(mainbox); // Start observing the .mainbox element this.resizeObserver.observe(mainbox);
} }
}); });
}); });
this.registerGarbageFunction(async () => { this.registerGarbageFunction(async () => {
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
}
this.resizeObserver.disconnect(); this.resizeObserver.disconnect();
}); });
} }
@ -73,6 +86,7 @@ export class DeesChartArea extends DeesElement {
height: 400px; height: 400px;
background: #111; background: #111;
border-radius: 8px; border-radius: 8px;
overflow: hidden;
} }
.chartTitle { .chartTitle {
@ -82,6 +96,7 @@ export class DeesChartArea extends DeesElement {
width: 100%; width: 100%;
text-align: center; text-align: center;
padding-top: 16px; padding-top: 16px;
z-index: 10;
} }
.chartContainer { .chartContainer {
position: absolute; position: absolute;
@ -90,6 +105,7 @@ export class DeesChartArea extends DeesElement {
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
padding: 32px 16px 16px 0px; padding: 32px 16px 16px 0px;
overflow: hidden;
} }
`, `,
]; ];
@ -104,7 +120,30 @@ export class DeesChartArea extends DeesElement {
} }
public async firstUpdated() { public async firstUpdated() {
const domtoolsInstance = await this.domtoolsPromise; await this.domtoolsPromise;
// Wait for next animation frame to ensure layout is complete
await new Promise(resolve => requestAnimationFrame(resolve));
// Get actual dimensions of the container
const mainbox: HTMLDivElement = this.shadowRoot.querySelector('.mainbox');
const chartContainer: HTMLDivElement = this.shadowRoot.querySelector('.chartContainer');
if (!mainbox || !chartContainer) {
console.error('Chart containers not found');
return;
}
// Calculate initial dimensions
const styleChartContainer = window.getComputedStyle(chartContainer);
const paddingTop = parseInt(styleChartContainer.paddingTop, 10);
const paddingBottom = parseInt(styleChartContainer.paddingBottom, 10);
const paddingLeft = parseInt(styleChartContainer.paddingLeft, 10);
const paddingRight = parseInt(styleChartContainer.paddingRight, 10);
const initialWidth = mainbox.clientWidth - paddingLeft - paddingRight;
const initialHeight = mainbox.offsetHeight - paddingTop - paddingBottom;
var options: ApexCharts.ApexOptions = { var options: ApexCharts.ApexOptions = {
series: [ series: [
{ {
@ -129,12 +168,20 @@ export class DeesChartArea extends DeesElement {
}, },
], ],
chart: { chart: {
width: 0, // Adjusted for responsive width width: initialWidth || 100, // Use actual width or fallback
height: 0, // Adjusted for responsive height height: initialHeight || 100, // Use actual height or fallback
type: 'area', type: 'area',
toolbar: { toolbar: {
show: false, // This line disables the toolbar show: false, // This line disables the toolbar
}, },
animations: {
enabled: true,
speed: 400,
animateGradually: {
enabled: true,
delay: 150
},
},
}, },
dataLabels: { dataLabels: {
enabled: false, enabled: false,
@ -184,14 +231,11 @@ export class DeesChartArea extends DeesElement {
x: { x: {
format: 'dd/MM/yy HH:mm', format: 'dd/MM/yy HH:mm',
}, },
custom: function ({ series, seriesIndex, dataPointIndex, w }) { custom: function ({ series, dataPointIndex, w }: any) {
// Get the x value
const xValue = w.globals.labels[dataPointIndex];
// Iterate through each series and get its value // Iterate through each series and get its value
let tooltipContent = `<div style="padding: 10px; background: #1e1e2f; color: white; border-radius: 5px;">`; let tooltipContent = `<div style="padding: 10px; background: #1e1e2f; color: white; border-radius: 5px;">`;
tooltipContent += ``; // `<strong>Time:</strong> ${xValue}<br/>`;
series.forEach((s, index) => { series.forEach((s: number[], index: number) => {
const label = w.globals.seriesNames[index]; // Get series label const label = w.globals.seriesNames[index]; // Get series label
const value = s[dataPointIndex]; // Get value at data point const value = s[dataPointIndex]; // Get value at data point
tooltipContent += `<strong>${label}:</strong> ${value} Mbps<br/>`; tooltipContent += `<strong>${label}:</strong> ${value} Mbps<br/>`;
@ -235,15 +279,25 @@ export class DeesChartArea extends DeesElement {
}; };
this.chart = new ApexCharts(this.shadowRoot.querySelector('.chartContainer'), options); this.chart = new ApexCharts(this.shadowRoot.querySelector('.chartContainer'), options);
await this.chart.render(); await this.chart.render();
// Give the chart a moment to fully initialize before resizing
await new Promise(resolve => setTimeout(resolve, 100));
await this.resizeChart(); await this.resizeChart();
} }
public async resizeChart() { public async resizeChart() {
if (!this.chart) {
return;
}
const mainbox: HTMLDivElement = this.shadowRoot.querySelector('.mainbox'); const mainbox: HTMLDivElement = this.shadowRoot.querySelector('.mainbox');
const chartContainer: HTMLDivElement = this.shadowRoot.querySelector('.chartContainer'); const chartContainer: HTMLDivElement = this.shadowRoot.querySelector('.chartContainer');
if (!mainbox || !chartContainer) {
return;
}
// Get computed style of the element // Get computed style of the element
const styleMainbox = window.getComputedStyle(mainbox);
const styleChartContainer = window.getComputedStyle(chartContainer); const styleChartContainer = window.getComputedStyle(chartContainer);
// Extract padding values // Extract padding values

View File

@ -5,8 +5,6 @@ import {
customElement, customElement,
html, html,
property, property,
state,
type CSSResult,
type TemplateResult, type TemplateResult,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
@ -259,7 +257,7 @@ export class DeesChartLog extends DeesElement {
} }
public async firstUpdated() { public async firstUpdated() {
const domtoolsInstance = await this.domtoolsPromise; await this.domtoolsPromise;
this.logContainer = this.shadowRoot.querySelector('.logContainer'); this.logContainer = this.shadowRoot.querySelector('.logContainer');
// Initialize with demo server logs // Initialize with demo server logs