update
This commit is contained in:
142
ts_web/elements/00group-chart/dees-chart-bar/component.ts
Normal file
142
ts_web/elements/00group-chart/dees-chart-bar/component.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
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';
|
||||
import { getEchartsSeriesColors } from '../dees-chart-echarts-theme.js';
|
||||
|
||||
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> {
|
||||
const seriesColors = getEchartsSeriesColors(this.goBright);
|
||||
const isDark = !this.goBright;
|
||||
const formatter = this.valueFormatter;
|
||||
|
||||
const categoryAxis: Record<string, any> = {
|
||||
type: 'category',
|
||||
data: this.categories,
|
||||
axisLine: { lineStyle: { color: isDark ? 'hsl(0 0% 20%)' : 'hsl(0 0% 85%)' } },
|
||||
axisLabel: { color: isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 40%)' },
|
||||
};
|
||||
|
||||
const valueAxis: Record<string, any> = {
|
||||
type: 'value',
|
||||
axisLine: { show: false },
|
||||
axisLabel: {
|
||||
color: isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 40%)',
|
||||
formatter: (val: number) => formatter(val),
|
||||
},
|
||||
splitLine: { lineStyle: { color: isDark ? 'hsl(0 0% 14%)' : 'hsl(0 0% 92%)' } },
|
||||
};
|
||||
|
||||
const seriesData = this.series.map((s, index) => ({
|
||||
name: s.name,
|
||||
type: 'bar' as const,
|
||||
data: s.data,
|
||||
stack: this.stacked ? 'total' : undefined,
|
||||
itemStyle: {
|
||||
color: s.color || seriesColors[index % seriesColors.length],
|
||||
borderRadius: this.stacked ? [0, 0, 0, 0] : this.horizontal ? [0, 4, 4, 0] : [4, 4, 0, 0],
|
||||
},
|
||||
barMaxWidth: 40,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 6,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.15)',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// For stacked bars, round the top corners of the last visible series
|
||||
if (this.stacked && seriesData.length > 0) {
|
||||
const last = seriesData[seriesData.length - 1];
|
||||
last.itemStyle.borderRadius = this.horizontal ? [0, 4, 4, 0] : [4, 4, 0, 0];
|
||||
}
|
||||
|
||||
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) {
|
||||
result += `${p.marker} ${p.seriesName}: <strong>${formatter(p.value)}</strong><br/>`;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
},
|
||||
legend: this.showLegend && this.series.length > 1
|
||||
? { bottom: 8, itemWidth: 10, itemHeight: 10 }
|
||||
: { 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
120
ts_web/elements/00group-chart/dees-chart-bar/demo.ts
Normal file
120
ts_web/elements/00group-chart/dees-chart-bar/demo.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { html, css, cssManager } from '@design.estate/dees-element';
|
||||
import type { DeesChartBar } from './component.js';
|
||||
import '@design.estate/dees-wcctools/demotools';
|
||||
import './component.js';
|
||||
|
||||
export const demoFunc = () => {
|
||||
const endpointCategories = ['/api/users', '/api/orders', '/api/products', '/api/auth', '/api/search'];
|
||||
const endpointSeries = [
|
||||
{ name: 'GET', data: [1240, 890, 720, 2100, 560] },
|
||||
{ name: 'POST', data: [320, 450, 180, 890, 40] },
|
||||
{ name: 'PUT', data: [90, 210, 150, 30, 10] },
|
||||
];
|
||||
|
||||
const regionCategories = ['US-East', 'US-West', 'EU', 'Asia', 'Other'];
|
||||
const regionSeries = [
|
||||
{ name: 'Requests', data: [4500, 3200, 2800, 1900, 600] },
|
||||
];
|
||||
|
||||
return html`
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
const vertChart = elementArg.querySelector('#vert-chart') as DeesChartBar;
|
||||
const horizChart = elementArg.querySelector('#horiz-chart') as DeesChartBar;
|
||||
const stackChart = elementArg.querySelector('#stack-chart') as DeesChartBar;
|
||||
|
||||
const buttons = elementArg.querySelectorAll('dees-button');
|
||||
buttons.forEach((button: any) => {
|
||||
const text = button.text?.trim();
|
||||
if (text === 'Randomize') {
|
||||
button.addEventListener('click', () => {
|
||||
vertChart.series = endpointSeries.map((s) => ({
|
||||
...s,
|
||||
data: s.data.map((v) => Math.round(v * (0.5 + Math.random()))),
|
||||
}));
|
||||
horizChart.series = regionSeries.map((s) => ({
|
||||
...s,
|
||||
data: s.data.map((v) => Math.round(v * (0.5 + Math.random()))),
|
||||
}));
|
||||
stackChart.series = endpointSeries.map((s) => ({
|
||||
...s,
|
||||
data: s.data.map((v) => Math.round(v * (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-bar
|
||||
id="vert-chart"
|
||||
.label=${'Requests by Endpoint'}
|
||||
.categories=${endpointCategories}
|
||||
.series=${endpointSeries}
|
||||
.valueFormatter=${(val: number) => `${val} req`}
|
||||
></dees-chart-bar>
|
||||
|
||||
<dees-chart-bar
|
||||
id="horiz-chart"
|
||||
.label=${'Traffic by Region'}
|
||||
.categories=${regionCategories}
|
||||
.series=${regionSeries}
|
||||
.horizontal=${true}
|
||||
.valueFormatter=${(val: number) => `${(val / 1000).toFixed(1)}k`}
|
||||
></dees-chart-bar>
|
||||
</div>
|
||||
|
||||
<dees-chart-bar
|
||||
id="stack-chart"
|
||||
.label=${'Stacked: Requests by Endpoint'}
|
||||
.categories=${endpointCategories}
|
||||
.series=${endpointSeries}
|
||||
.stacked=${true}
|
||||
.valueFormatter=${(val: number) => `${val} req`}
|
||||
></dees-chart-bar>
|
||||
|
||||
<div class="info">
|
||||
Bar chart with vertical, horizontal, and stacked modes •
|
||||
Click 'Randomize' to update data with animation
|
||||
</div>
|
||||
</div>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
};
|
||||
1
ts_web/elements/00group-chart/dees-chart-bar/index.ts
Normal file
1
ts_web/elements/00group-chart/dees-chart-bar/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './component.js';
|
||||
7
ts_web/elements/00group-chart/dees-chart-bar/styles.ts
Normal file
7
ts_web/elements/00group-chart/dees-chart-bar/styles.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { css } from '@design.estate/dees-element';
|
||||
import { echartsBaseStyles } from '../dees-chart-echarts-styles.js';
|
||||
|
||||
export const barStyles = [
|
||||
...echartsBaseStyles,
|
||||
css``,
|
||||
];
|
||||
13
ts_web/elements/00group-chart/dees-chart-bar/template.ts
Normal file
13
ts_web/elements/00group-chart/dees-chart-bar/template.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { html, type TemplateResult } from '@design.estate/dees-element';
|
||||
import type { DeesChartBar } from './component.js';
|
||||
|
||||
export const renderChartBar = (component: DeesChartBar): TemplateResult => {
|
||||
return html`
|
||||
<dees-tile>
|
||||
<div slot="header" class="chartHeader">
|
||||
<span class="chartLabel">${component.label}</span>
|
||||
</div>
|
||||
<div class="chartContainer"></div>
|
||||
</dees-tile>
|
||||
`;
|
||||
};
|
||||
Reference in New Issue
Block a user