Files
dees-catalog/ts_web/elements/00group-chart/dees-chart-donut/component.ts

143 lines
4.1 KiB
TypeScript
Raw Normal View History

2026-04-04 11:05:01 +00:00
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';
2026-04-04 11:05:01 +00:00
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<string, any>) {
super.updated(changedProperties);
if (
this.chartInstance &&
(changedProperties.has('data') ||
changedProperties.has('showLegend') ||
changedProperties.has('showLabels') ||
changedProperties.has('innerRadiusPercent'))
) {
this.updateChart();
}
}
protected buildOption(): Record<string, any> {
const themeColors = getThemeColors(this.goBright);
2026-04-04 11:05:01 +00:00
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,
},
},
};
});
2026-04-04 11:05:01 +00:00
const formatter = this.valueFormatter;
return {
tooltip: {
trigger: 'item',
formatter: (params: any) => {
const solidColor = params.data?.itemStyle?.borderColor || params.color;
const marker = `<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${solidColor};"></span>`;
return `${marker}${params.name}: <strong>${formatter(params.value)}</strong> (${params.percent}%)`;
2026-04-04 11:05:01 +00:00
},
},
legend: this.showLegend
? {
orient: 'vertical',
right: 16,
top: 'center',
itemWidth: 10,
itemHeight: 10,
itemGap: 12,
data: legendData,
2026-04-04 11:05:01 +00:00
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,
2026-04-04 11:05:01 +00:00
itemStyle: {
borderRadius: 4,
},
label: this.showLabels
? {
show: true,
formatter: '{b}: {d}%',
fontSize: 11,
color: themeColors.textSecondary,
textBorderColor: 'transparent',
2026-04-04 11:05:01 +00:00
}
: { show: false },
data,
},
],
};
}
}