This commit is contained in:
2026-04-04 11:05:01 +00:00
parent 4dba14060e
commit ff32470d8a
29 changed files with 1431 additions and 2 deletions

View File

@@ -0,0 +1,129 @@
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 } 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<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 seriesColors = getEchartsSeriesColors(this.goBright);
const data = this.data.map((item, index) => ({
name: item.name,
value: item.value,
itemStyle: item.color ? { color: item.color } : { color: seriesColors[index % seriesColors.length] },
}));
const formatter = this.valueFormatter;
return {
tooltip: {
trigger: 'item',
formatter: (params: any) => {
return `${params.marker} ${params.name}: <strong>${formatter(params.value)}</strong> (${params.percent}%)`;
},
},
legend: this.showLegend
? {
orient: 'vertical',
right: 16,
top: 'center',
itemWidth: 10,
itemHeight: 10,
itemGap: 12,
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,
itemStyle: {
borderRadius: 4,
borderColor: 'transparent',
borderWidth: 2,
},
label: this.showLabels
? {
show: true,
formatter: '{b}: {d}%',
fontSize: 11,
}
: { show: false },
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.2)',
},
label: {
show: true,
fontWeight: 'bold',
},
},
data,
},
],
};
}
}

View File

@@ -0,0 +1,127 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import type { DeesChartDonut } from './component.js';
import '@design.estate/dees-wcctools/demotools';
import './component.js';
export const demoFunc = () => {
const diskData = [
{ name: 'Documents', value: 42 },
{ name: 'Media', value: 28 },
{ name: 'Applications', value: 15 },
{ name: 'System', value: 10 },
{ name: 'Other', value: 5 },
];
const statusData = [
{ name: 'Healthy', value: 156 },
{ name: 'Warning', value: 23 },
{ name: 'Critical', value: 8 },
{ name: 'Unknown', value: 3 },
];
const trafficData = [
{ name: 'API', value: 45200 },
{ name: 'Static Assets', value: 23100 },
{ name: 'WebSocket', value: 12800 },
{ name: 'GraphQL', value: 8900 },
];
return html`
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
const diskChart = elementArg.querySelector('#disk-chart') as DeesChartDonut;
const statusChart = elementArg.querySelector('#status-chart') as DeesChartDonut;
const trafficChart = elementArg.querySelector('#traffic-chart') as DeesChartDonut;
// Wire up buttons
const buttons = elementArg.querySelectorAll('dees-button');
buttons.forEach((button: any) => {
const text = button.text?.trim();
if (text === 'Randomize') {
button.addEventListener('click', () => {
diskChart.data = diskData.map((d) => ({
...d,
value: Math.round(d.value * (0.5 + Math.random())),
}));
statusChart.data = statusData.map((d) => ({
...d,
value: Math.round(d.value * (0.3 + Math.random() * 1.4)),
}));
trafficChart.data = trafficData.map((d) => ({
...d,
value: Math.round(d.value * (0.5 + Math.random())),
}));
});
}
});
}}>
<style>
${css`
.demoBox {
position: relative;
background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(0 0% 9%)')};
height: 100%;
width: 100%;
padding: 40px;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 24px;
}
.chartRow {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
.controls {
display: flex;
gap: 12px;
margin-bottom: 8px;
}
.info {
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
font-size: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Geist Sans', sans-serif;
text-align: center;
margin-top: 8px;
}
`}
</style>
<div class="demoBox">
<div class="controls">
<dees-button-group label="Actions:">
<dees-button>Randomize</dees-button>
</dees-button-group>
</div>
<div class="chartRow">
<dees-chart-donut
id="disk-chart"
.label=${'Disk Usage (GB)'}
.data=${diskData}
.valueFormatter=${(val: number) => `${val} GB`}
></dees-chart-donut>
<dees-chart-donut
id="status-chart"
.label=${'Service Status'}
.data=${statusData}
.valueFormatter=${(val: number) => `${val} services`}
.innerRadiusPercent=${'0%'}
></dees-chart-donut>
</div>
<dees-chart-donut
id="traffic-chart"
.label=${'Traffic Distribution'}
.data=${trafficData}
.valueFormatter=${(val: number) => `${(val / 1000).toFixed(1)}k req`}
></dees-chart-donut>
<div class="info">
Donut chart with configurable inner radius (set to 0% for full pie) •
Click 'Randomize' to update data with animation
</div>
</div>
</dees-demowrapper>
`;
};

View File

@@ -0,0 +1 @@
export * from './component.js';

View File

@@ -0,0 +1,11 @@
import { css, cssManager } from '@design.estate/dees-element';
import { echartsBaseStyles } from '../dees-chart-echarts-styles.js';
export const donutStyles = [
...echartsBaseStyles,
css`
:host {
height: 360px;
}
`,
];

View File

@@ -0,0 +1,13 @@
import { html, type TemplateResult } from '@design.estate/dees-element';
import type { DeesChartDonut } from './component.js';
export const renderChartDonut = (component: DeesChartDonut): TemplateResult => {
return html`
<dees-tile>
<div slot="header" class="chartHeader">
<span class="chartLabel">${component.label}</span>
</div>
<div class="chartContainer"></div>
</dees-tile>
`;
};