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 { barStyles } from './styles.js';
|
|
|
|
|
import { renderChartBar } from './template.js';
|
2026-04-04 12:29:39 +00:00
|
|
|
import { getEchartsSeriesColors, getThemeColors, hexToRgba } from '../dees-chart-echarts-theme.js';
|
2026-04-04 11:05:01 +00:00
|
|
|
|
|
|
|
|
export interface IBarSeriesItem {
|
|
|
|
|
name: string;
|
|
|
|
|
data: number[];
|
|
|
|
|
color?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
declare global {
|
|
|
|
|
interface HTMLElementTagNameMap {
|
|
|
|
|
'dees-chart-bar': DeesChartBar;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@customElement('dees-chart-bar')
|
|
|
|
|
export class DeesChartBar extends DeesChartEchartsBase {
|
|
|
|
|
public static demo = demoFunc;
|
|
|
|
|
public static demoGroups = ['Chart'];
|
|
|
|
|
|
|
|
|
|
@property({ type: Array })
|
|
|
|
|
accessor categories: string[] = [];
|
|
|
|
|
|
|
|
|
|
@property({ type: Array })
|
|
|
|
|
accessor series: IBarSeriesItem[] = [];
|
|
|
|
|
|
|
|
|
|
@property({ type: Boolean })
|
|
|
|
|
accessor horizontal: boolean = false;
|
|
|
|
|
|
|
|
|
|
@property({ type: Boolean })
|
|
|
|
|
accessor stacked: boolean = false;
|
|
|
|
|
|
|
|
|
|
@property({ type: Boolean })
|
|
|
|
|
accessor showLegend: boolean = true;
|
|
|
|
|
|
|
|
|
|
@property({ attribute: false })
|
|
|
|
|
accessor valueFormatter: (value: number) => string = (val) => `${val}`;
|
|
|
|
|
|
|
|
|
|
public static styles = barStyles;
|
|
|
|
|
|
|
|
|
|
public render(): TemplateResult {
|
|
|
|
|
return renderChartBar(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async updated(changedProperties: Map<string, any>) {
|
|
|
|
|
super.updated(changedProperties);
|
|
|
|
|
if (
|
|
|
|
|
this.chartInstance &&
|
|
|
|
|
(changedProperties.has('categories') ||
|
|
|
|
|
changedProperties.has('series') ||
|
|
|
|
|
changedProperties.has('horizontal') ||
|
|
|
|
|
changedProperties.has('stacked') ||
|
|
|
|
|
changedProperties.has('showLegend'))
|
|
|
|
|
) {
|
|
|
|
|
this.updateChart();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected buildOption(): Record<string, any> {
|
2026-04-04 11:25:19 +00:00
|
|
|
const colors = getThemeColors(this.goBright);
|
2026-04-04 11:05:01 +00:00
|
|
|
const seriesColors = getEchartsSeriesColors(this.goBright);
|
|
|
|
|
const formatter = this.valueFormatter;
|
|
|
|
|
|
|
|
|
|
const categoryAxis: Record<string, any> = {
|
|
|
|
|
type: 'category',
|
|
|
|
|
data: this.categories,
|
2026-04-04 11:25:19 +00:00
|
|
|
axisLine: { lineStyle: { color: colors.borderStrong } },
|
|
|
|
|
axisLabel: { color: colors.textMuted },
|
2026-04-04 11:05:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const valueAxis: Record<string, any> = {
|
|
|
|
|
type: 'value',
|
|
|
|
|
axisLine: { show: false },
|
|
|
|
|
axisLabel: {
|
2026-04-04 11:25:19 +00:00
|
|
|
color: colors.textMuted,
|
2026-04-04 11:05:01 +00:00
|
|
|
formatter: (val: number) => formatter(val),
|
|
|
|
|
},
|
2026-04-04 11:25:19 +00:00
|
|
|
splitLine: { lineStyle: { color: colors.borderSubtle } },
|
2026-04-04 11:05:01 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-04 12:29:39 +00:00
|
|
|
const fillAlpha = this.goBright ? 0.15 : 0.25;
|
|
|
|
|
const borderRadius = this.horizontal ? [0, 4, 4, 0] : [4, 4, 0, 0];
|
|
|
|
|
const noBorderRadius = [0, 0, 0, 0];
|
|
|
|
|
|
|
|
|
|
const legendData: Array<{ name: string; itemStyle: { color: string } }> = [];
|
|
|
|
|
|
|
|
|
|
const seriesData = this.series.map((s, index) => {
|
|
|
|
|
const color = s.color || seriesColors[index % seriesColors.length];
|
|
|
|
|
legendData.push({ name: s.name, itemStyle: { color } });
|
|
|
|
|
return {
|
|
|
|
|
name: s.name,
|
|
|
|
|
type: 'bar' as const,
|
|
|
|
|
data: s.data,
|
|
|
|
|
stack: this.stacked ? 'total' : undefined,
|
2026-04-04 11:05:01 +00:00
|
|
|
itemStyle: {
|
2026-04-04 12:29:39 +00:00
|
|
|
color: hexToRgba(color, fillAlpha),
|
|
|
|
|
borderColor: color,
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderRadius: this.stacked ? noBorderRadius : borderRadius,
|
2026-04-04 11:05:01 +00:00
|
|
|
},
|
2026-04-04 12:29:39 +00:00
|
|
|
barMaxWidth: 40,
|
|
|
|
|
barGap: '20%',
|
|
|
|
|
emphasis: {
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: hexToRgba(color, fillAlpha + 0.15),
|
|
|
|
|
borderColor: color,
|
|
|
|
|
borderWidth: 1.5,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
});
|
2026-04-04 11:05:01 +00:00
|
|
|
|
|
|
|
|
// For stacked bars, round the top corners of the last visible series
|
|
|
|
|
if (this.stacked && seriesData.length > 0) {
|
|
|
|
|
const last = seriesData[seriesData.length - 1];
|
2026-04-04 12:29:39 +00:00
|
|
|
last.itemStyle.borderRadius = borderRadius;
|
2026-04-04 11:05:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
axisPointer: { type: 'shadow' },
|
|
|
|
|
formatter: (params: any) => {
|
|
|
|
|
const items = Array.isArray(params) ? params : [params];
|
|
|
|
|
let result = `<strong>${items[0].axisValueLabel}</strong><br/>`;
|
|
|
|
|
for (const p of items) {
|
2026-04-04 12:29:39 +00:00
|
|
|
const solidColor = p.borderColor || p.color;
|
|
|
|
|
const marker = `<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${solidColor};"></span>`;
|
|
|
|
|
result += `${marker}${p.seriesName}: <strong>${formatter(p.value)}</strong><br/>`;
|
2026-04-04 11:05:01 +00:00
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
legend: this.showLegend && this.series.length > 1
|
2026-04-04 12:29:39 +00:00
|
|
|
? { bottom: 8, itemWidth: 10, itemHeight: 10, data: legendData }
|
2026-04-04 11:05:01 +00:00
|
|
|
: { show: false },
|
|
|
|
|
grid: {
|
|
|
|
|
left: 16,
|
|
|
|
|
right: 16,
|
|
|
|
|
top: 16,
|
|
|
|
|
bottom: this.showLegend && this.series.length > 1 ? 40 : 16,
|
|
|
|
|
containLabel: true,
|
|
|
|
|
},
|
|
|
|
|
xAxis: this.horizontal ? valueAxis : categoryAxis,
|
|
|
|
|
yAxis: this.horizontal ? categoryAxis : valueAxis,
|
|
|
|
|
series: seriesData,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|