import { customElement, property, type TemplateResult, } from '@design.estate/dees-element'; import { DeesChartEchartsBase } from '../dees-chart-echarts-base.js'; import { demoFunc } from './demo.js'; import { donutStyles } from './styles.js'; import { renderChartDonut } from './template.js'; import { getEchartsSeriesColors, getThemeColors, hexToRgba } from '../dees-chart-echarts-theme.js'; export interface IDonutDataItem { name: string; value: number; color?: string; } declare global { interface HTMLElementTagNameMap { 'dees-chart-donut': DeesChartDonut; } } @customElement('dees-chart-donut') export class DeesChartDonut extends DeesChartEchartsBase { public static demo = demoFunc; public static demoGroups = ['Chart']; @property({ type: Array }) accessor data: IDonutDataItem[] = []; @property({ type: Boolean }) accessor showLegend: boolean = true; @property({ type: Boolean }) accessor showLabels: boolean = true; @property({ type: String }) accessor innerRadiusPercent: string = '55%'; @property({ attribute: false }) accessor valueFormatter: (value: number) => string = (val) => `${val}`; public static styles = donutStyles; public render(): TemplateResult { return renderChartDonut(this); } public async updated(changedProperties: Map) { super.updated(changedProperties); if ( this.chartInstance && (changedProperties.has('data') || changedProperties.has('showLegend') || changedProperties.has('showLabels') || changedProperties.has('innerRadiusPercent')) ) { this.updateChart(); } } protected buildOption(): Record { const themeColors = getThemeColors(this.goBright); const seriesColors = getEchartsSeriesColors(this.goBright); const fillAlpha = this.goBright ? 0.15 : 0.2; const legendData: Array<{ name: string; itemStyle: { color: string } }> = []; const data = this.data.map((item, index) => { const color = item.color || seriesColors[index % seriesColors.length]; legendData.push({ name: item.name, itemStyle: { color } }); return { name: item.name, value: item.value, itemStyle: { color: hexToRgba(color, fillAlpha), borderColor: color, borderWidth: 1, }, emphasis: { itemStyle: { color: hexToRgba(color, fillAlpha + 0.15), borderColor: color, borderWidth: 1.5, }, }, }; }); const formatter = this.valueFormatter; return { tooltip: { trigger: 'item', formatter: (params: any) => { const solidColor = params.data?.itemStyle?.borderColor || params.color; const marker = ``; return `${marker}${params.name}: ${formatter(params.value)} (${params.percent}%)`; }, }, legend: this.showLegend ? { orient: 'vertical', right: 16, top: 'center', itemWidth: 10, itemHeight: 10, itemGap: 12, data: legendData, formatter: (name: string) => { const item = this.data.find((d) => d.name === name); return item ? `${name} ${formatter(item.value)}` : name; }, } : { show: false }, series: [ { type: 'pie', radius: [this.innerRadiusPercent, '85%'], center: this.showLegend ? ['35%', '50%'] : ['50%', '50%'], avoidLabelOverlap: true, padAngle: 2, itemStyle: { borderRadius: 4, }, label: this.showLabels ? { show: true, formatter: '{b}: {d}%', fontSize: 11, color: themeColors.textSecondary, textBorderColor: 'transparent', } : { show: false }, data, }, ], }; } }