108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
import {
|
|
DeesElement,
|
|
property,
|
|
html,
|
|
type TemplateResult,
|
|
} from '@design.estate/dees-element';
|
|
|
|
import * as domtools from '@design.estate/dees-domtools';
|
|
import { DeesServiceLibLoader, type IEchartsBundle, type IEchartsInstance } from '../../services/index.js';
|
|
import { getEchartsThemeOptions } from './dees-chart-echarts-theme.js';
|
|
import '../00group-layout/dees-tile/dees-tile.js';
|
|
|
|
/**
|
|
* Abstract base class for ECharts-based chart components.
|
|
* Handles library loading, chart lifecycle, resize observation, and theme switching.
|
|
* Subclasses implement `buildOption()` to define their chart configuration.
|
|
*/
|
|
export abstract class DeesChartEchartsBase extends DeesElement {
|
|
@property()
|
|
accessor label: string = 'Untitled Chart';
|
|
|
|
protected chartInstance: IEchartsInstance | null = null;
|
|
protected echartsBundle: IEchartsBundle | null = null;
|
|
private resizeObserver: ResizeObserver | null = null;
|
|
|
|
constructor() {
|
|
super();
|
|
domtools.elementBasic.setup();
|
|
this.registerGarbageFunction(async () => {
|
|
if (this.resizeObserver) {
|
|
this.resizeObserver.disconnect();
|
|
this.resizeObserver = null;
|
|
}
|
|
if (this.chartInstance) {
|
|
try {
|
|
this.chartInstance.dispose();
|
|
this.chartInstance = null;
|
|
} catch (e) {
|
|
console.error('Error disposing ECharts instance:', e);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public render(): TemplateResult {
|
|
return html`
|
|
<dees-tile>
|
|
<div slot="header" class="chartHeader">
|
|
<span class="chartLabel">${this.label}</span>
|
|
</div>
|
|
<div class="chartContainer"></div>
|
|
</dees-tile>
|
|
`;
|
|
}
|
|
|
|
public async firstUpdated() {
|
|
await this.domtoolsPromise;
|
|
this.echartsBundle = await DeesServiceLibLoader.getInstance().loadEcharts();
|
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
|
|
const chartContainer = this.shadowRoot!.querySelector('.chartContainer') as HTMLDivElement;
|
|
if (!chartContainer) return;
|
|
|
|
try {
|
|
this.chartInstance = this.echartsBundle.init(chartContainer);
|
|
this.updateChart();
|
|
|
|
this.resizeObserver = new ResizeObserver(() => {
|
|
this.chartInstance?.resize();
|
|
});
|
|
this.resizeObserver.observe(chartContainer);
|
|
} catch (error) {
|
|
console.error('Failed to initialize ECharts:', error);
|
|
}
|
|
}
|
|
|
|
public async updated(changedProperties: Map<string, any>) {
|
|
super.updated(changedProperties);
|
|
if (changedProperties.has('goBright') && this.chartInstance) {
|
|
this.applyTheme();
|
|
}
|
|
}
|
|
|
|
protected abstract buildOption(): Record<string, any>;
|
|
|
|
protected updateChart(): void {
|
|
if (!this.chartInstance) return;
|
|
const themeOptions = getEchartsThemeOptions(this.goBright);
|
|
const chartOption = this.buildOption();
|
|
// Merge theme defaults with chart-specific options
|
|
const merged = {
|
|
...themeOptions,
|
|
...chartOption,
|
|
textStyle: { ...themeOptions.textStyle, ...(chartOption.textStyle || {}) },
|
|
tooltip: { ...themeOptions.tooltip, ...(chartOption.tooltip || {}) },
|
|
};
|
|
this.chartInstance.setOption(merged, true);
|
|
}
|
|
|
|
protected applyTheme(): void {
|
|
this.updateChart();
|
|
}
|
|
|
|
public async forceResize(): Promise<void> {
|
|
this.chartInstance?.resize();
|
|
}
|
|
}
|