update
This commit is contained in:
@@ -34,6 +34,7 @@
|
|||||||
"@tiptap/extension-underline": "^2.23.0",
|
"@tiptap/extension-underline": "^2.23.0",
|
||||||
"@tiptap/starter-kit": "^2.23.0",
|
"@tiptap/starter-kit": "^2.23.0",
|
||||||
"@tsclass/tsclass": "^9.5.0",
|
"@tsclass/tsclass": "^9.5.0",
|
||||||
|
"echarts": "^5.6.0",
|
||||||
"lightweight-charts": "^5.1.0",
|
"lightweight-charts": "^5.1.0",
|
||||||
"highlight.js": "11.11.1",
|
"highlight.js": "11.11.1",
|
||||||
"ibantools": "^4.5.1",
|
"ibantools": "^4.5.1",
|
||||||
|
|||||||
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
@@ -62,6 +62,9 @@ importers:
|
|||||||
'@tsclass/tsclass':
|
'@tsclass/tsclass':
|
||||||
specifier: ^9.5.0
|
specifier: ^9.5.0
|
||||||
version: 9.5.0
|
version: 9.5.0
|
||||||
|
echarts:
|
||||||
|
specifier: ^5.6.0
|
||||||
|
version: 5.6.0
|
||||||
highlight.js:
|
highlight.js:
|
||||||
specifier: 11.11.1
|
specifier: 11.11.1
|
||||||
version: 11.11.1
|
version: 11.11.1
|
||||||
@@ -2534,6 +2537,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
echarts@5.6.0:
|
||||||
|
resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
|
||||||
|
|
||||||
emoji-regex@8.0.0:
|
emoji-regex@8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
@@ -3936,6 +3942,9 @@ packages:
|
|||||||
tslib@1.14.1:
|
tslib@1.14.1:
|
||||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||||
|
|
||||||
|
tslib@2.3.0:
|
||||||
|
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||||
|
|
||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
@@ -4149,6 +4158,9 @@ packages:
|
|||||||
zod@3.25.76:
|
zod@3.25.76:
|
||||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||||
|
|
||||||
|
zrender@5.6.1:
|
||||||
|
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
|
||||||
|
|
||||||
zwitch@2.0.4:
|
zwitch@2.0.4:
|
||||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||||
|
|
||||||
@@ -7671,6 +7683,11 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
gopd: 1.2.0
|
gopd: 1.2.0
|
||||||
|
|
||||||
|
echarts@5.6.0:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
zrender: 5.6.1
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
end-of-stream@1.4.5:
|
end-of-stream@1.4.5:
|
||||||
@@ -9510,6 +9527,8 @@ snapshots:
|
|||||||
|
|
||||||
tslib@1.14.1: {}
|
tslib@1.14.1: {}
|
||||||
|
|
||||||
|
tslib@2.3.0: {}
|
||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
tsx@4.21.0:
|
tsx@4.21.0:
|
||||||
@@ -9699,4 +9718,8 @@ snapshots:
|
|||||||
|
|
||||||
zod@3.25.76: {}
|
zod@3.25.76: {}
|
||||||
|
|
||||||
|
zrender@5.6.1:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
|
||||||
zwitch@2.0.4: {}
|
zwitch@2.0.4: {}
|
||||||
|
|||||||
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>
|
||||||
|
`;
|
||||||
|
};
|
||||||
129
ts_web/elements/00group-chart/dees-chart-donut/component.ts
Normal file
129
ts_web/elements/00group-chart/dees-chart-donut/component.ts
Normal 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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
127
ts_web/elements/00group-chart/dees-chart-donut/demo.ts
Normal file
127
ts_web/elements/00group-chart/dees-chart-donut/demo.ts
Normal 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>
|
||||||
|
`;
|
||||||
|
};
|
||||||
1
ts_web/elements/00group-chart/dees-chart-donut/index.ts
Normal file
1
ts_web/elements/00group-chart/dees-chart-donut/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './component.js';
|
||||||
11
ts_web/elements/00group-chart/dees-chart-donut/styles.ts
Normal file
11
ts_web/elements/00group-chart/dees-chart-donut/styles.ts
Normal 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;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
13
ts_web/elements/00group-chart/dees-chart-donut/template.ts
Normal file
13
ts_web/elements/00group-chart/dees-chart-donut/template.ts
Normal 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>
|
||||||
|
`;
|
||||||
|
};
|
||||||
107
ts_web/elements/00group-chart/dees-chart-echarts-base.ts
Normal file
107
ts_web/elements/00group-chart/dees-chart-echarts-base.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import {
|
||||||
|
DeesElement,
|
||||||
|
property,
|
||||||
|
html,
|
||||||
|
type TemplateResult,
|
||||||
|
} from '@design.estate/dees-element';
|
||||||
|
|
||||||
|
import * as domtools from '@design.estate/dees-domtools';
|
||||||
|
import { DeesServiceLibLoader, type IEchartsBundle, type IEchartsInstance } from '../../services/index.js';
|
||||||
|
import { getEchartsThemeOptions } from './dees-chart-echarts-theme.js';
|
||||||
|
import '../00group-layout/dees-tile/dees-tile.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for ECharts-based chart components.
|
||||||
|
* Handles library loading, chart lifecycle, resize observation, and theme switching.
|
||||||
|
* Subclasses implement `buildOption()` to define their chart configuration.
|
||||||
|
*/
|
||||||
|
export abstract class DeesChartEchartsBase extends DeesElement {
|
||||||
|
@property()
|
||||||
|
accessor label: string = 'Untitled Chart';
|
||||||
|
|
||||||
|
protected chartInstance: IEchartsInstance | null = null;
|
||||||
|
protected echartsBundle: IEchartsBundle | null = null;
|
||||||
|
private resizeObserver: ResizeObserver | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
domtools.elementBasic.setup();
|
||||||
|
this.registerGarbageFunction(async () => {
|
||||||
|
if (this.resizeObserver) {
|
||||||
|
this.resizeObserver.disconnect();
|
||||||
|
this.resizeObserver = null;
|
||||||
|
}
|
||||||
|
if (this.chartInstance) {
|
||||||
|
try {
|
||||||
|
this.chartInstance.dispose();
|
||||||
|
this.chartInstance = null;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error disposing ECharts instance:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<dees-tile>
|
||||||
|
<div slot="header" class="chartHeader">
|
||||||
|
<span class="chartLabel">${this.label}</span>
|
||||||
|
</div>
|
||||||
|
<div class="chartContainer"></div>
|
||||||
|
</dees-tile>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async firstUpdated() {
|
||||||
|
await this.domtoolsPromise;
|
||||||
|
this.echartsBundle = await DeesServiceLibLoader.getInstance().loadEcharts();
|
||||||
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||||
|
|
||||||
|
const chartContainer = this.shadowRoot!.querySelector('.chartContainer') as HTMLDivElement;
|
||||||
|
if (!chartContainer) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.chartInstance = this.echartsBundle.init(chartContainer);
|
||||||
|
this.updateChart();
|
||||||
|
|
||||||
|
this.resizeObserver = new ResizeObserver(() => {
|
||||||
|
this.chartInstance?.resize();
|
||||||
|
});
|
||||||
|
this.resizeObserver.observe(chartContainer);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize ECharts:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updated(changedProperties: Map<string, any>) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
if (changedProperties.has('goBright') && this.chartInstance) {
|
||||||
|
this.applyTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract buildOption(): Record<string, any>;
|
||||||
|
|
||||||
|
protected updateChart(): void {
|
||||||
|
if (!this.chartInstance) return;
|
||||||
|
const themeOptions = getEchartsThemeOptions(this.goBright);
|
||||||
|
const chartOption = this.buildOption();
|
||||||
|
// Merge theme defaults with chart-specific options
|
||||||
|
const merged = {
|
||||||
|
...themeOptions,
|
||||||
|
...chartOption,
|
||||||
|
textStyle: { ...themeOptions.textStyle, ...(chartOption.textStyle || {}) },
|
||||||
|
tooltip: { ...themeOptions.tooltip, ...(chartOption.tooltip || {}) },
|
||||||
|
};
|
||||||
|
this.chartInstance.setOption(merged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected applyTheme(): void {
|
||||||
|
this.updateChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async forceResize(): Promise<void> {
|
||||||
|
this.chartInstance?.resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
36
ts_web/elements/00group-chart/dees-chart-echarts-styles.ts
Normal file
36
ts_web/elements/00group-chart/dees-chart-echarts-styles.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { css, cssManager } from '@design.estate/dees-element';
|
||||||
|
import { themeDefaultStyles } from '../00theme.js';
|
||||||
|
|
||||||
|
export const echartsBaseStyles = [
|
||||||
|
themeDefaultStyles,
|
||||||
|
cssManager.defaultStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 400px;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||||
|
color: var(--dees-color-text-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
dees-tile {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.chartHeader {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 8px 0 16px;
|
||||||
|
}
|
||||||
|
.chartLabel {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
color: var(--dees-color-text-secondary);
|
||||||
|
}
|
||||||
|
.chartContainer {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
55
ts_web/elements/00group-chart/dees-chart-echarts-theme.ts
Normal file
55
ts_web/elements/00group-chart/dees-chart-echarts-theme.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Shared theme utilities for ECharts-based chart components.
|
||||||
|
* Provides color palettes and option fragments that match the dees-catalog design tokens.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SERIES_COLORS = {
|
||||||
|
dark: [
|
||||||
|
'hsl(217.2 91.2% 59.8%)', // blue
|
||||||
|
'hsl(173.4 80.4% 40%)', // teal
|
||||||
|
'hsl(280.3 87.4% 66.7%)', // purple
|
||||||
|
'hsl(24.6 95% 53.1%)', // orange
|
||||||
|
'hsl(142 76% 36%)', // green
|
||||||
|
'hsl(346 77% 49%)', // rose
|
||||||
|
],
|
||||||
|
light: [
|
||||||
|
'hsl(222.2 47.4% 51.2%)',
|
||||||
|
'hsl(142.1 76.2% 36.3%)',
|
||||||
|
'hsl(280.3 47.7% 50.2%)',
|
||||||
|
'hsl(20.5 90.2% 48.2%)',
|
||||||
|
'hsl(160 60% 45%)',
|
||||||
|
'hsl(340 65% 47%)',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getEchartsSeriesColors(goBright: boolean): string[] {
|
||||||
|
return goBright ? SERIES_COLORS.light : SERIES_COLORS.dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEchartsThemeOptions(goBright: boolean): Record<string, any> {
|
||||||
|
const isDark = !goBright;
|
||||||
|
return {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textStyle: {
|
||||||
|
color: isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)',
|
||||||
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
color: goBright ? SERIES_COLORS.light : SERIES_COLORS.dark,
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: isDark ? 'hsl(0 0% 9%)' : 'hsl(0 0% 100%)',
|
||||||
|
borderColor: isDark ? 'hsl(0 0% 14.9%)' : 'hsl(0 0% 89.8%)',
|
||||||
|
textStyle: {
|
||||||
|
color: isDark ? 'hsl(0 0% 95%)' : 'hsl(0 0% 9%)',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
confine: true,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
textStyle: {
|
||||||
|
color: isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
161
ts_web/elements/00group-chart/dees-chart-gauge/component.ts
Normal file
161
ts_web/elements/00group-chart/dees-chart-gauge/component.ts
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
type TemplateResult,
|
||||||
|
} from '@design.estate/dees-element';
|
||||||
|
|
||||||
|
import { DeesChartEchartsBase } from '../dees-chart-echarts-base.js';
|
||||||
|
import { demoFunc } from './demo.js';
|
||||||
|
import { gaugeStyles } from './styles.js';
|
||||||
|
import { renderChartGauge } from './template.js';
|
||||||
|
import { getEchartsSeriesColors } from '../dees-chart-echarts-theme.js';
|
||||||
|
|
||||||
|
export interface IGaugeThreshold {
|
||||||
|
value: number;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'dees-chart-gauge': DeesChartGauge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('dees-chart-gauge')
|
||||||
|
export class DeesChartGauge extends DeesChartEchartsBase {
|
||||||
|
public static demo = demoFunc;
|
||||||
|
public static demoGroups = ['Chart'];
|
||||||
|
|
||||||
|
@property({ type: Number })
|
||||||
|
accessor value: number = 0;
|
||||||
|
|
||||||
|
@property({ type: Number })
|
||||||
|
accessor min: number = 0;
|
||||||
|
|
||||||
|
@property({ type: Number })
|
||||||
|
accessor max: number = 100;
|
||||||
|
|
||||||
|
@property({ type: String })
|
||||||
|
accessor unit: string = '%';
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor thresholds: IGaugeThreshold[] = [];
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
accessor showTicks: boolean = true;
|
||||||
|
|
||||||
|
public static styles = gaugeStyles;
|
||||||
|
|
||||||
|
public render(): TemplateResult {
|
||||||
|
return renderChartGauge(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updated(changedProperties: Map<string, any>) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
if (
|
||||||
|
this.chartInstance &&
|
||||||
|
(changedProperties.has('value') ||
|
||||||
|
changedProperties.has('min') ||
|
||||||
|
changedProperties.has('max') ||
|
||||||
|
changedProperties.has('unit') ||
|
||||||
|
changedProperties.has('thresholds') ||
|
||||||
|
changedProperties.has('showTicks'))
|
||||||
|
) {
|
||||||
|
this.updateChart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected buildOption(): Record<string, any> {
|
||||||
|
const isDark = !this.goBright;
|
||||||
|
const seriesColors = getEchartsSeriesColors(this.goBright);
|
||||||
|
const primaryColor = seriesColors[0];
|
||||||
|
|
||||||
|
// Build axis line color stops from thresholds
|
||||||
|
let axisLineColors: Array<[number, string]>;
|
||||||
|
if (this.thresholds.length > 0) {
|
||||||
|
const sorted = [...this.thresholds].sort((a, b) => a.value - b.value);
|
||||||
|
axisLineColors = sorted.map((t) => [
|
||||||
|
(t.value - this.min) / (this.max - this.min),
|
||||||
|
t.color,
|
||||||
|
]);
|
||||||
|
// Ensure we end at 1
|
||||||
|
if (axisLineColors[axisLineColors.length - 1][0] < 1) {
|
||||||
|
axisLineColors.push([1, sorted[sorted.length - 1].color]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
axisLineColors = [[1, primaryColor]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'gauge',
|
||||||
|
min: this.min,
|
||||||
|
max: this.max,
|
||||||
|
startAngle: 220,
|
||||||
|
endAngle: -40,
|
||||||
|
progress: {
|
||||||
|
show: true,
|
||||||
|
width: 14,
|
||||||
|
roundCap: true,
|
||||||
|
},
|
||||||
|
pointer: {
|
||||||
|
show: true,
|
||||||
|
length: '60%',
|
||||||
|
width: 5,
|
||||||
|
itemStyle: {
|
||||||
|
color: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
width: 14,
|
||||||
|
color: axisLineColors,
|
||||||
|
opacity: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: this.showTicks,
|
||||||
|
distance: -20,
|
||||||
|
length: 6,
|
||||||
|
lineStyle: {
|
||||||
|
color: isDark ? 'hsl(0 0% 30%)' : 'hsl(0 0% 75%)',
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: this.showTicks,
|
||||||
|
distance: -24,
|
||||||
|
length: 10,
|
||||||
|
lineStyle: {
|
||||||
|
color: isDark ? 'hsl(0 0% 40%)' : 'hsl(0 0% 60%)',
|
||||||
|
width: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: this.showTicks,
|
||||||
|
distance: 30,
|
||||||
|
color: isDark ? 'hsl(0 0% 50%)' : 'hsl(0 0% 45%)',
|
||||||
|
fontSize: 11,
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
valueAnimation: true,
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: 600,
|
||||||
|
offsetCenter: [0, '65%'],
|
||||||
|
color: isDark ? 'hsl(0 0% 90%)' : 'hsl(0 0% 15%)',
|
||||||
|
formatter: `{value}${this.unit}`,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: this.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
125
ts_web/elements/00group-chart/dees-chart-gauge/demo.ts
Normal file
125
ts_web/elements/00group-chart/dees-chart-gauge/demo.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import { html, css, cssManager } from '@design.estate/dees-element';
|
||||||
|
import type { DeesChartGauge } from './component.js';
|
||||||
|
import '@design.estate/dees-wcctools/demotools';
|
||||||
|
import './component.js';
|
||||||
|
|
||||||
|
export const demoFunc = () => {
|
||||||
|
const defaultThresholds = [
|
||||||
|
{ value: 60, color: 'hsl(142 76% 36%)' },
|
||||||
|
{ value: 80, color: 'hsl(38 92% 50%)' },
|
||||||
|
{ value: 100, color: 'hsl(0 72% 50%)' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||||
|
const cpuGauge = elementArg.querySelector('#cpu-gauge') as DeesChartGauge;
|
||||||
|
const memGauge = elementArg.querySelector('#mem-gauge') as DeesChartGauge;
|
||||||
|
const slaGauge = elementArg.querySelector('#sla-gauge') as DeesChartGauge;
|
||||||
|
|
||||||
|
let animInterval: number | null = null;
|
||||||
|
|
||||||
|
const buttons = elementArg.querySelectorAll('dees-button');
|
||||||
|
buttons.forEach((button: any) => {
|
||||||
|
const text = button.text?.trim();
|
||||||
|
if (text === 'Animate') {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
if (animInterval) return;
|
||||||
|
animInterval = window.setInterval(() => {
|
||||||
|
cpuGauge.value = Math.round(30 + Math.random() * 60);
|
||||||
|
memGauge.value = Math.round(40 + Math.random() * 50);
|
||||||
|
slaGauge.value = Math.round((95 + Math.random() * 5) * 100) / 100;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
} else if (text === 'Stop') {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
if (animInterval) {
|
||||||
|
window.clearInterval(animInterval);
|
||||||
|
animInterval = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (text === 'Spike') {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
cpuGauge.value = 95;
|
||||||
|
memGauge.value = 88;
|
||||||
|
slaGauge.value = 96.5;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
<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;
|
||||||
|
}
|
||||||
|
.gaugeRow {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 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>Animate</dees-button>
|
||||||
|
<dees-button>Stop</dees-button>
|
||||||
|
<dees-button>Spike</dees-button>
|
||||||
|
</dees-button-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gaugeRow">
|
||||||
|
<dees-chart-gauge
|
||||||
|
id="cpu-gauge"
|
||||||
|
.label=${'CPU Usage'}
|
||||||
|
.value=${42}
|
||||||
|
.unit=${'%'}
|
||||||
|
.thresholds=${defaultThresholds}
|
||||||
|
></dees-chart-gauge>
|
||||||
|
|
||||||
|
<dees-chart-gauge
|
||||||
|
id="mem-gauge"
|
||||||
|
.label=${'Memory Usage'}
|
||||||
|
.value=${67}
|
||||||
|
.unit=${'%'}
|
||||||
|
.thresholds=${defaultThresholds}
|
||||||
|
></dees-chart-gauge>
|
||||||
|
|
||||||
|
<dees-chart-gauge
|
||||||
|
id="sla-gauge"
|
||||||
|
.label=${'SLA Uptime'}
|
||||||
|
.value=${99.8}
|
||||||
|
.min=${95}
|
||||||
|
.max=${100}
|
||||||
|
.unit=${'%'}
|
||||||
|
.showTicks=${true}
|
||||||
|
></dees-chart-gauge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
Gauge chart with animated value transitions and threshold coloring •
|
||||||
|
Click 'Animate' for live updates, 'Spike' to simulate high load
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dees-demowrapper>
|
||||||
|
`;
|
||||||
|
};
|
||||||
1
ts_web/elements/00group-chart/dees-chart-gauge/index.ts
Normal file
1
ts_web/elements/00group-chart/dees-chart-gauge/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './component.js';
|
||||||
11
ts_web/elements/00group-chart/dees-chart-gauge/styles.ts
Normal file
11
ts_web/elements/00group-chart/dees-chart-gauge/styles.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { css } from '@design.estate/dees-element';
|
||||||
|
import { echartsBaseStyles } from '../dees-chart-echarts-styles.js';
|
||||||
|
|
||||||
|
export const gaugeStyles = [
|
||||||
|
...echartsBaseStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
height: 320px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
13
ts_web/elements/00group-chart/dees-chart-gauge/template.ts
Normal file
13
ts_web/elements/00group-chart/dees-chart-gauge/template.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { html, type TemplateResult } from '@design.estate/dees-element';
|
||||||
|
import type { DeesChartGauge } from './component.js';
|
||||||
|
|
||||||
|
export const renderChartGauge = (component: DeesChartGauge): TemplateResult => {
|
||||||
|
return html`
|
||||||
|
<dees-tile>
|
||||||
|
<div slot="header" class="chartHeader">
|
||||||
|
<span class="chartLabel">${component.label}</span>
|
||||||
|
</div>
|
||||||
|
<div class="chartContainer"></div>
|
||||||
|
</dees-tile>
|
||||||
|
`;
|
||||||
|
};
|
||||||
132
ts_web/elements/00group-chart/dees-chart-radar/component.ts
Normal file
132
ts_web/elements/00group-chart/dees-chart-radar/component.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
type TemplateResult,
|
||||||
|
} from '@design.estate/dees-element';
|
||||||
|
|
||||||
|
import { DeesChartEchartsBase } from '../dees-chart-echarts-base.js';
|
||||||
|
import { demoFunc } from './demo.js';
|
||||||
|
import { radarStyles } from './styles.js';
|
||||||
|
import { renderChartRadar } from './template.js';
|
||||||
|
import { getEchartsSeriesColors } from '../dees-chart-echarts-theme.js';
|
||||||
|
|
||||||
|
export interface IRadarIndicator {
|
||||||
|
name: string;
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRadarSeriesItem {
|
||||||
|
name: string;
|
||||||
|
values: number[];
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'dees-chart-radar': DeesChartRadar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement('dees-chart-radar')
|
||||||
|
export class DeesChartRadar extends DeesChartEchartsBase {
|
||||||
|
public static demo = demoFunc;
|
||||||
|
public static demoGroups = ['Chart'];
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor indicators: IRadarIndicator[] = [];
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor series: IRadarSeriesItem[] = [];
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
accessor showLegend: boolean = true;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
accessor fillArea: boolean = true;
|
||||||
|
|
||||||
|
public static styles = radarStyles;
|
||||||
|
|
||||||
|
public render(): TemplateResult {
|
||||||
|
return renderChartRadar(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updated(changedProperties: Map<string, any>) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
if (
|
||||||
|
this.chartInstance &&
|
||||||
|
(changedProperties.has('indicators') ||
|
||||||
|
changedProperties.has('series') ||
|
||||||
|
changedProperties.has('showLegend') ||
|
||||||
|
changedProperties.has('fillArea'))
|
||||||
|
) {
|
||||||
|
this.updateChart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected buildOption(): Record<string, any> {
|
||||||
|
const isDark = !this.goBright;
|
||||||
|
const seriesColors = getEchartsSeriesColors(this.goBright);
|
||||||
|
|
||||||
|
const seriesData = this.series.map((s, index) => {
|
||||||
|
const color = s.color || seriesColors[index % seriesColors.length];
|
||||||
|
return {
|
||||||
|
name: s.name,
|
||||||
|
value: s.values,
|
||||||
|
itemStyle: { color },
|
||||||
|
lineStyle: { color, width: 2 },
|
||||||
|
areaStyle: this.fillArea ? { color, opacity: 0.15 } : undefined,
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 6,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
},
|
||||||
|
legend: this.showLegend && this.series.length > 1
|
||||||
|
? { bottom: 8, itemWidth: 10, itemHeight: 10 }
|
||||||
|
: { show: false },
|
||||||
|
radar: {
|
||||||
|
indicator: this.indicators.map((ind) => ({
|
||||||
|
name: ind.name,
|
||||||
|
max: ind.max,
|
||||||
|
})),
|
||||||
|
shape: 'polygon',
|
||||||
|
splitNumber: 4,
|
||||||
|
axisName: {
|
||||||
|
color: isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 35%)',
|
||||||
|
fontSize: 11,
|
||||||
|
},
|
||||||
|
splitArea: {
|
||||||
|
areaStyle: {
|
||||||
|
color: isDark
|
||||||
|
? ['hsl(0 0% 7%)', 'hsl(0 0% 9%)']
|
||||||
|
: ['hsl(0 0% 97%)', 'hsl(0 0% 95%)'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: isDark ? 'hsl(0 0% 16%)' : 'hsl(0 0% 88%)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: isDark ? 'hsl(0 0% 16%)' : 'hsl(0 0% 88%)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'radar',
|
||||||
|
data: seriesData,
|
||||||
|
emphasis: {
|
||||||
|
lineStyle: {
|
||||||
|
width: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
119
ts_web/elements/00group-chart/dees-chart-radar/demo.ts
Normal file
119
ts_web/elements/00group-chart/dees-chart-radar/demo.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { html, css, cssManager } from '@design.estate/dees-element';
|
||||||
|
import type { DeesChartRadar } from './component.js';
|
||||||
|
import '@design.estate/dees-wcctools/demotools';
|
||||||
|
import './component.js';
|
||||||
|
|
||||||
|
export const demoFunc = () => {
|
||||||
|
const indicators = [
|
||||||
|
{ name: 'Latency', max: 100 },
|
||||||
|
{ name: 'Throughput', max: 100 },
|
||||||
|
{ name: 'Availability', max: 100 },
|
||||||
|
{ name: 'Error Rate', max: 100 },
|
||||||
|
{ name: 'Saturation', max: 100 },
|
||||||
|
{ name: 'Security', max: 100 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const series = [
|
||||||
|
{ name: 'Service A', values: [85, 90, 99, 12, 45, 78] },
|
||||||
|
{ name: 'Service B', values: [70, 65, 95, 28, 60, 90] },
|
||||||
|
];
|
||||||
|
|
||||||
|
const singleIndicators = [
|
||||||
|
{ name: 'Speed', max: 10 },
|
||||||
|
{ name: 'Reliability', max: 10 },
|
||||||
|
{ name: 'Comfort', max: 10 },
|
||||||
|
{ name: 'Safety', max: 10 },
|
||||||
|
{ name: 'Cost', max: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const singleSeries = [
|
||||||
|
{ name: 'Rating', values: [8.5, 9.2, 7.0, 9.5, 6.0] },
|
||||||
|
];
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||||
|
const compChart = elementArg.querySelector('#comparison-chart') as DeesChartRadar;
|
||||||
|
const singleChart = elementArg.querySelector('#single-chart') as DeesChartRadar;
|
||||||
|
|
||||||
|
const buttons = elementArg.querySelectorAll('dees-button');
|
||||||
|
buttons.forEach((button: any) => {
|
||||||
|
const text = button.text?.trim();
|
||||||
|
if (text === 'Randomize') {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
compChart.series = series.map((s) => ({
|
||||||
|
...s,
|
||||||
|
values: s.values.map(() => Math.round(20 + Math.random() * 80)),
|
||||||
|
}));
|
||||||
|
singleChart.series = singleSeries.map((s) => ({
|
||||||
|
...s,
|
||||||
|
values: s.values.map(() => Math.round((2 + Math.random() * 8) * 10) / 10),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
<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-radar
|
||||||
|
id="comparison-chart"
|
||||||
|
.label=${'Service Health Comparison'}
|
||||||
|
.indicators=${indicators}
|
||||||
|
.series=${series}
|
||||||
|
></dees-chart-radar>
|
||||||
|
|
||||||
|
<dees-chart-radar
|
||||||
|
id="single-chart"
|
||||||
|
.label=${'Product Rating'}
|
||||||
|
.indicators=${singleIndicators}
|
||||||
|
.series=${singleSeries}
|
||||||
|
.fillArea=${true}
|
||||||
|
></dees-chart-radar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
Radar chart for multi-dimensional comparison •
|
||||||
|
Supports multiple overlay series and configurable fill •
|
||||||
|
Click 'Randomize' to update data
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dees-demowrapper>
|
||||||
|
`;
|
||||||
|
};
|
||||||
1
ts_web/elements/00group-chart/dees-chart-radar/index.ts
Normal file
1
ts_web/elements/00group-chart/dees-chart-radar/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './component.js';
|
||||||
7
ts_web/elements/00group-chart/dees-chart-radar/styles.ts
Normal file
7
ts_web/elements/00group-chart/dees-chart-radar/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 radarStyles = [
|
||||||
|
...echartsBaseStyles,
|
||||||
|
css``,
|
||||||
|
];
|
||||||
13
ts_web/elements/00group-chart/dees-chart-radar/template.ts
Normal file
13
ts_web/elements/00group-chart/dees-chart-radar/template.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { html, type TemplateResult } from '@design.estate/dees-element';
|
||||||
|
import type { DeesChartRadar } from './component.js';
|
||||||
|
|
||||||
|
export const renderChartRadar = (component: DeesChartRadar): TemplateResult => {
|
||||||
|
return html`
|
||||||
|
<dees-tile>
|
||||||
|
<div slot="header" class="chartHeader">
|
||||||
|
<span class="chartLabel">${component.label}</span>
|
||||||
|
</div>
|
||||||
|
<div class="chartContainer"></div>
|
||||||
|
</dees-tile>
|
||||||
|
`;
|
||||||
|
};
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
// Chart Components
|
// Chart Components
|
||||||
export * from './dees-chart-area/index.js';
|
export * from './dees-chart-area/index.js';
|
||||||
|
export * from './dees-chart-bar/index.js';
|
||||||
|
export * from './dees-chart-donut/index.js';
|
||||||
|
export * from './dees-chart-gauge/index.js';
|
||||||
export * from './dees-chart-log/index.js';
|
export * from './dees-chart-log/index.js';
|
||||||
|
export * from './dees-chart-radar/index.js';
|
||||||
|
|||||||
@@ -74,6 +74,28 @@ export interface ILightweightChartsBundle {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal type for an ECharts instance (loaded from CDN)
|
||||||
|
*/
|
||||||
|
export interface IEchartsInstance {
|
||||||
|
setOption(option: Record<string, any>, notMerge?: boolean): void;
|
||||||
|
resize(opts?: { width?: number; height?: number }): void;
|
||||||
|
dispose(): void;
|
||||||
|
on(eventName: string, handler: (...args: any[]) => void): void;
|
||||||
|
off(eventName: string, handler?: (...args: any[]) => void): void;
|
||||||
|
getOption(): Record<string, any>;
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bundle type for Apache ECharts
|
||||||
|
*/
|
||||||
|
export interface IEchartsBundle {
|
||||||
|
init: (dom: HTMLElement, theme?: string | object | null, opts?: Record<string, any>) => IEchartsInstance;
|
||||||
|
dispose: (chart: IEchartsInstance | HTMLElement | string) => void;
|
||||||
|
getInstanceByDom: (dom: HTMLElement) => IEchartsInstance | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bundle type for Tiptap editor and extensions
|
* Bundle type for Tiptap editor and extensions
|
||||||
*/
|
*/
|
||||||
@@ -108,6 +130,7 @@ export class DeesServiceLibLoader {
|
|||||||
private xtermSearchAddonLib: IXtermSearchAddonBundle | null = null;
|
private xtermSearchAddonLib: IXtermSearchAddonBundle | null = null;
|
||||||
private highlightJsLib: HLJSApi | null = null;
|
private highlightJsLib: HLJSApi | null = null;
|
||||||
private lightweightChartsLib: ILightweightChartsBundle | null = null;
|
private lightweightChartsLib: ILightweightChartsBundle | null = null;
|
||||||
|
private echartsLib: IEchartsBundle | null = null;
|
||||||
private tiptapLib: ITiptapBundle | null = null;
|
private tiptapLib: ITiptapBundle | null = null;
|
||||||
|
|
||||||
// Loading promises to prevent duplicate concurrent loads
|
// Loading promises to prevent duplicate concurrent loads
|
||||||
@@ -116,6 +139,7 @@ export class DeesServiceLibLoader {
|
|||||||
private xtermSearchAddonLoadingPromise: Promise<IXtermSearchAddonBundle> | null = null;
|
private xtermSearchAddonLoadingPromise: Promise<IXtermSearchAddonBundle> | null = null;
|
||||||
private highlightJsLoadingPromise: Promise<HLJSApi> | null = null;
|
private highlightJsLoadingPromise: Promise<HLJSApi> | null = null;
|
||||||
private lightweightChartsLoadingPromise: Promise<ILightweightChartsBundle> | null = null;
|
private lightweightChartsLoadingPromise: Promise<ILightweightChartsBundle> | null = null;
|
||||||
|
private echartsLoadingPromise: Promise<IEchartsBundle> | null = null;
|
||||||
private tiptapLoadingPromise: Promise<ITiptapBundle> | null = null;
|
private tiptapLoadingPromise: Promise<ITiptapBundle> | null = null;
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
@@ -296,6 +320,34 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
|
|||||||
return this.lightweightChartsLoadingPromise;
|
return this.lightweightChartsLoadingPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load Apache ECharts from CDN
|
||||||
|
* @returns Promise resolving to ECharts bundle
|
||||||
|
*/
|
||||||
|
public async loadEcharts(): Promise<IEchartsBundle> {
|
||||||
|
if (this.echartsLib) {
|
||||||
|
return this.echartsLib;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.echartsLoadingPromise) {
|
||||||
|
return this.echartsLoadingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.echartsLoadingPromise = (async () => {
|
||||||
|
const url = `${CDN_BASE}/echarts@${CDN_VERSIONS.echarts}/+esm`;
|
||||||
|
const module = await import(/* @vite-ignore */ url);
|
||||||
|
|
||||||
|
this.echartsLib = {
|
||||||
|
init: module.init,
|
||||||
|
dispose: module.dispose,
|
||||||
|
getInstanceByDom: module.getInstanceByDom,
|
||||||
|
};
|
||||||
|
return this.echartsLib;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return this.echartsLoadingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load Tiptap rich text editor and extensions from CDN
|
* Load Tiptap rich text editor and extensions from CDN
|
||||||
* @returns Promise resolving to Tiptap bundle with Editor and extensions
|
* @returns Promise resolving to Tiptap bundle with Editor and extensions
|
||||||
@@ -348,6 +400,7 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
|
|||||||
this.loadXtermSearchAddon(),
|
this.loadXtermSearchAddon(),
|
||||||
this.loadHighlightJs(),
|
this.loadHighlightJs(),
|
||||||
this.loadLightweightCharts(),
|
this.loadLightweightCharts(),
|
||||||
|
this.loadEcharts(),
|
||||||
this.loadTiptap(),
|
this.loadTiptap(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -355,7 +408,7 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
|
|||||||
/**
|
/**
|
||||||
* Check if a specific library is already loaded
|
* Check if a specific library is already loaded
|
||||||
*/
|
*/
|
||||||
public isLoaded(library: 'xterm' | 'xtermFitAddon' | 'xtermSearchAddon' | 'highlightJs' | 'lightweightCharts' | 'tiptap'): boolean {
|
public isLoaded(library: 'xterm' | 'xtermFitAddon' | 'xtermSearchAddon' | 'highlightJs' | 'lightweightCharts' | 'echarts' | 'tiptap'): boolean {
|
||||||
switch (library) {
|
switch (library) {
|
||||||
case 'xterm':
|
case 'xterm':
|
||||||
return this.xtermLib !== null;
|
return this.xtermLib !== null;
|
||||||
@@ -367,6 +420,8 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
|
|||||||
return this.highlightJsLib !== null;
|
return this.highlightJsLib !== null;
|
||||||
case 'lightweightCharts':
|
case 'lightweightCharts':
|
||||||
return this.lightweightChartsLib !== null;
|
return this.lightweightChartsLib !== null;
|
||||||
|
case 'echarts':
|
||||||
|
return this.echartsLib !== null;
|
||||||
case 'tiptap':
|
case 'tiptap':
|
||||||
return this.tiptapLib !== null;
|
return this.tiptapLib !== null;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export { DeesServiceLibLoader } from './DeesServiceLibLoader.js';
|
export { DeesServiceLibLoader } from './DeesServiceLibLoader.js';
|
||||||
export type { IXtermBundle, IXtermFitAddonBundle, IXtermSearchAddonBundle, IXtermSearchAddon, ITiptapBundle, ILightweightChartsBundle } from './DeesServiceLibLoader.js';
|
export type { IXtermBundle, IXtermFitAddonBundle, IXtermSearchAddonBundle, IXtermSearchAddon, ITiptapBundle, ILightweightChartsBundle, IEchartsBundle, IEchartsInstance } from './DeesServiceLibLoader.js';
|
||||||
export { CDN_BASE, CDN_VERSIONS } from './versions.js';
|
export { CDN_BASE, CDN_VERSIONS } from './versions.js';
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const CDN_VERSIONS = {
|
|||||||
xtermAddonFit: '0.8.0',
|
xtermAddonFit: '0.8.0',
|
||||||
xtermAddonSearch: '0.13.0',
|
xtermAddonSearch: '0.13.0',
|
||||||
highlightJs: '11.11.1',
|
highlightJs: '11.11.1',
|
||||||
|
echarts: '5.6.0',
|
||||||
lightweightCharts: '5.1.0',
|
lightweightCharts: '5.1.0',
|
||||||
tiptap: '2.27.2',
|
tiptap: '2.27.2',
|
||||||
fontawesome: '7.2.0',
|
fontawesome: '7.2.0',
|
||||||
|
|||||||
Reference in New Issue
Block a user