feat(dees-chart-area): Enhance chart component with dynamic datasets, real-time updates, and improved demo features
This commit is contained in:
@ -1,21 +1,285 @@
|
||||
import { html } from '@design.estate/dees-element';
|
||||
import { html, css } from '@design.estate/dees-element';
|
||||
import type { DeesChartArea } from './dees-chart-area.js';
|
||||
|
||||
export const demoFunc = () => {
|
||||
let chartElement: DeesChartArea;
|
||||
let intervalId: number;
|
||||
let currentDataset = 'system';
|
||||
|
||||
// Get element reference after render
|
||||
setTimeout(() => {
|
||||
const charts = document.querySelectorAll('dees-chart-area');
|
||||
if (charts.length > 0) {
|
||||
chartElement = charts[charts.length - 1] as DeesChartArea;
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Y-axis formatters for different datasets
|
||||
const formatters = {
|
||||
system: (val: number) => `${val}%`,
|
||||
network: (val: number) => `${val} Mbps`,
|
||||
sales: (val: number) => `$${val.toLocaleString()}`,
|
||||
};
|
||||
|
||||
// Different datasets to showcase
|
||||
const datasets = {
|
||||
system: {
|
||||
label: 'System Usage (%)',
|
||||
series: [
|
||||
{
|
||||
name: 'CPU',
|
||||
data: [
|
||||
{ x: new Date(Date.now() - 300000).toISOString(), y: 25 },
|
||||
{ x: new Date(Date.now() - 240000).toISOString(), y: 30 },
|
||||
{ x: new Date(Date.now() - 180000).toISOString(), y: 28 },
|
||||
{ x: new Date(Date.now() - 120000).toISOString(), y: 35 },
|
||||
{ x: new Date(Date.now() - 60000).toISOString(), y: 32 },
|
||||
{ x: new Date().toISOString(), y: 38 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Memory',
|
||||
data: [
|
||||
{ x: new Date(Date.now() - 300000).toISOString(), y: 45 },
|
||||
{ x: new Date(Date.now() - 240000).toISOString(), y: 48 },
|
||||
{ x: new Date(Date.now() - 180000).toISOString(), y: 46 },
|
||||
{ x: new Date(Date.now() - 120000).toISOString(), y: 52 },
|
||||
{ x: new Date(Date.now() - 60000).toISOString(), y: 50 },
|
||||
{ x: new Date().toISOString(), y: 55 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
network: {
|
||||
label: 'Network Traffic (Mbps)',
|
||||
series: [
|
||||
{
|
||||
name: 'Download',
|
||||
data: [
|
||||
{ x: new Date(Date.now() - 300000).toISOString(), y: 120 },
|
||||
{ x: new Date(Date.now() - 240000).toISOString(), y: 150 },
|
||||
{ x: new Date(Date.now() - 180000).toISOString(), y: 180 },
|
||||
{ x: new Date(Date.now() - 120000).toISOString(), y: 165 },
|
||||
{ x: new Date(Date.now() - 60000).toISOString(), y: 190 },
|
||||
{ x: new Date().toISOString(), y: 175 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
data: [
|
||||
{ x: new Date(Date.now() - 300000).toISOString(), y: 25 },
|
||||
{ x: new Date(Date.now() - 240000).toISOString(), y: 30 },
|
||||
{ x: new Date(Date.now() - 180000).toISOString(), y: 35 },
|
||||
{ x: new Date(Date.now() - 120000).toISOString(), y: 28 },
|
||||
{ x: new Date(Date.now() - 60000).toISOString(), y: 32 },
|
||||
{ x: new Date().toISOString(), y: 40 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
sales: {
|
||||
label: 'Sales Analytics',
|
||||
series: [
|
||||
{
|
||||
name: 'Revenue',
|
||||
data: [
|
||||
{ x: '2025-01-01', y: 45000 },
|
||||
{ x: '2025-01-02', y: 52000 },
|
||||
{ x: '2025-01-03', y: 48000 },
|
||||
{ x: '2025-01-04', y: 61000 },
|
||||
{ x: '2025-01-05', y: 58000 },
|
||||
{ x: '2025-01-06', y: 65000 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Profit',
|
||||
data: [
|
||||
{ x: '2025-01-01', y: 12000 },
|
||||
{ x: '2025-01-02', y: 14000 },
|
||||
{ x: '2025-01-03', y: 11000 },
|
||||
{ x: '2025-01-04', y: 18000 },
|
||||
{ x: '2025-01-05', y: 16000 },
|
||||
{ x: '2025-01-06', y: 20000 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// Generate random value within range
|
||||
const getRandomValue = (min: number, max: number) => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
|
||||
// Add real-time data
|
||||
const addRealtimeData = () => {
|
||||
if (!chartElement) return;
|
||||
|
||||
const dataset = datasets[currentDataset];
|
||||
const newTimestamp = new Date().toISOString();
|
||||
|
||||
// Generate new data points based on dataset type
|
||||
let newData: any[][] = [];
|
||||
|
||||
if (currentDataset === 'system') {
|
||||
newData = [
|
||||
[{ x: newTimestamp, y: getRandomValue(25, 45) }], // CPU
|
||||
[{ x: newTimestamp, y: getRandomValue(45, 65) }], // Memory
|
||||
];
|
||||
} else if (currentDataset === 'network') {
|
||||
newData = [
|
||||
[{ x: newTimestamp, y: getRandomValue(100, 250) }], // Download
|
||||
[{ x: newTimestamp, y: getRandomValue(20, 50) }], // Upload
|
||||
];
|
||||
}
|
||||
|
||||
// Keep only last 10 data points
|
||||
const currentSeries = chartElement.series.map((series, index) => ({
|
||||
...series,
|
||||
data: [...series.data.slice(-9), ...(newData[index] || [])],
|
||||
}));
|
||||
|
||||
chartElement.series = currentSeries;
|
||||
};
|
||||
|
||||
// Switch dataset
|
||||
const switchDataset = (name: string) => {
|
||||
currentDataset = name;
|
||||
if (chartElement) {
|
||||
const dataset = datasets[name];
|
||||
chartElement.label = dataset.label;
|
||||
chartElement.series = dataset.series;
|
||||
chartElement.yAxisFormatter = formatters[name];
|
||||
}
|
||||
};
|
||||
|
||||
// Start/stop real-time updates
|
||||
const startRealtime = () => {
|
||||
if (!intervalId && (currentDataset === 'system' || currentDataset === 'network')) {
|
||||
intervalId = window.setInterval(() => addRealtimeData(), 2000);
|
||||
}
|
||||
};
|
||||
|
||||
const stopRealtime = () => {
|
||||
if (intervalId) {
|
||||
window.clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
// Randomize current data
|
||||
const randomizeData = () => {
|
||||
if (!chartElement) return;
|
||||
|
||||
const currentSeries = chartElement.series.map(series => ({
|
||||
...series,
|
||||
data: series.data.map(point => ({
|
||||
...point,
|
||||
y: typeof point.y === 'number'
|
||||
? point.y * (0.8 + Math.random() * 0.4) // +/- 20% variation
|
||||
: point.y,
|
||||
})),
|
||||
}));
|
||||
|
||||
chartElement.series = currentSeries;
|
||||
};
|
||||
|
||||
return html`
|
||||
<style>
|
||||
.demoBox {
|
||||
position: relative;
|
||||
background: #000000;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
${css`
|
||||
.demoBox {
|
||||
position: relative;
|
||||
background: #000000;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 40px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.control-section {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
font-family: 'Geist Sans', sans-serif;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #666;
|
||||
font-size: 11px;
|
||||
font-family: 'Geist Sans', sans-serif;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<div class="demoBox">
|
||||
<dees-chart-area
|
||||
.label=${'System Usage'}
|
||||
></dees-chart-area>
|
||||
<div class="controls">
|
||||
<div class="control-section">
|
||||
<span class="section-label">Dataset:</span>
|
||||
<dees-button
|
||||
@clicked=${() => switchDataset('system')}
|
||||
type=${currentDataset === 'system' ? 'highlighted' : 'normal'}
|
||||
>System Usage</dees-button>
|
||||
<dees-button
|
||||
@clicked=${() => switchDataset('network')}
|
||||
type=${currentDataset === 'network' ? 'highlighted' : 'normal'}
|
||||
>Network Traffic</dees-button>
|
||||
<dees-button
|
||||
@clicked=${() => switchDataset('sales')}
|
||||
type=${currentDataset === 'sales' ? 'highlighted' : 'normal'}
|
||||
>Sales Data</dees-button>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<span class="section-label">Real-time:</span>
|
||||
<dees-button @clicked=${() => startRealtime()}>Start Live</dees-button>
|
||||
<dees-button @clicked=${() => stopRealtime()}>Stop Live</dees-button>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<span class="section-label">Actions:</span>
|
||||
<dees-button @clicked=${() => randomizeData()}>Randomize Values</dees-button>
|
||||
<dees-button @clicked=${() => addRealtimeData()}>Add Point</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<dees-chart-area
|
||||
.label=${datasets[currentDataset].label}
|
||||
.series=${datasets[currentDataset].series}
|
||||
.yAxisFormatter=${formatters[currentDataset]}
|
||||
></dees-chart-area>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
Real-time updates work with System Usage and Network Traffic datasets •
|
||||
Chart updates every 2 seconds when live mode is active •
|
||||
Try switching datasets and randomizing values
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
@ -32,6 +32,12 @@ export class DeesChartArea extends DeesElement {
|
||||
@property()
|
||||
public label: string = 'Untitled Chart';
|
||||
|
||||
@property({ type: Array })
|
||||
public series: ApexAxisChartSeries = [];
|
||||
|
||||
@property({ type: Function })
|
||||
public yAxisFormatter: (value: number) => string = (val) => `${val} Mbps`;
|
||||
|
||||
private resizeObserver: ResizeObserver;
|
||||
private resizeTimeout: number;
|
||||
|
||||
@ -144,29 +150,32 @@ export class DeesChartArea extends DeesElement {
|
||||
const initialWidth = mainbox.clientWidth - paddingLeft - paddingRight;
|
||||
const initialHeight = mainbox.offsetHeight - paddingTop - paddingBottom;
|
||||
|
||||
// Use provided series data or default demo data
|
||||
const chartSeries = this.series.length > 0 ? this.series : [
|
||||
{
|
||||
name: 'cpu',
|
||||
data: [
|
||||
{ x: '2025-01-15T03:00:00', y: 25 },
|
||||
{ x: '2025-01-15T07:00:00', y: 30 },
|
||||
{ x: '2025-01-15T11:00:00', y: 20 },
|
||||
{ x: '2025-01-15T15:00:00', y: 35 },
|
||||
{ x: '2025-01-15T19:00:00', y: 25 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'memory',
|
||||
data: [
|
||||
{ x: '2025-01-15T03:00:00', y: 10 },
|
||||
{ x: '2025-01-15T07:00:00', y: 12 },
|
||||
{ x: '2025-01-15T11:00:00', y: 10 },
|
||||
{ x: '2025-01-15T15:00:00', y: 30 },
|
||||
{ x: '2025-01-15T19:00:00', y: 40 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
var options: ApexCharts.ApexOptions = {
|
||||
series: [
|
||||
{
|
||||
name: 'cpu',
|
||||
data: [
|
||||
{ x: '2025-01-15T03:00:00', y: 25 },
|
||||
{ x: '2025-01-15T07:00:00', y: 30 },
|
||||
{ x: '2025-01-15T11:00:00', y: 20 },
|
||||
{ x: '2025-01-15T15:00:00', y: 35 },
|
||||
{ x: '2025-01-15T19:00:00', y: 25 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'memory',
|
||||
data: [
|
||||
{ x: '2025-01-15T03:00:00', y: 10 },
|
||||
{ x: '2025-01-15T07:00:00', y: 12 },
|
||||
{ x: '2025-01-15T11:00:00', y: 10 },
|
||||
{ x: '2025-01-15T15:00:00', y: 30 },
|
||||
{ x: '2025-01-15T19:00:00', y: 40 },
|
||||
],
|
||||
},
|
||||
],
|
||||
series: chartSeries,
|
||||
chart: {
|
||||
width: initialWidth || 100, // Use actual width or fallback
|
||||
height: initialHeight || 100, // Use actual height or fallback
|
||||
@ -209,9 +218,7 @@ export class DeesChartArea extends DeesElement {
|
||||
yaxis: {
|
||||
min: 0,
|
||||
labels: {
|
||||
formatter: function (val: number) {
|
||||
return `${val} Mbps`; // Format Y-axis labels
|
||||
},
|
||||
formatter: this.yAxisFormatter,
|
||||
style: {
|
||||
colors: '#9e9e9e', // Label color
|
||||
fontSize: '12px',
|
||||
@ -285,6 +292,46 @@ export class DeesChartArea extends DeesElement {
|
||||
await this.resizeChart();
|
||||
}
|
||||
|
||||
public async updated(changedProperties: Map<string, any>) {
|
||||
super.updated(changedProperties);
|
||||
|
||||
// Update chart if series data changes
|
||||
if (changedProperties.has('series') && this.chart && this.series.length > 0) {
|
||||
await this.updateSeries(this.series);
|
||||
}
|
||||
|
||||
// Update y-axis formatter if it changes
|
||||
if (changedProperties.has('yAxisFormatter') && this.chart) {
|
||||
await this.chart.updateOptions({
|
||||
yaxis: {
|
||||
labels: {
|
||||
formatter: this.yAxisFormatter,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async updateSeries(newSeries: ApexAxisChartSeries) {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.chart.updateSeries(newSeries, true);
|
||||
}
|
||||
|
||||
public async appendData(seriesIndex: number, newData: any[]) {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSeries = [...this.series];
|
||||
if (currentSeries[seriesIndex]) {
|
||||
currentSeries[seriesIndex].data = [...currentSeries[seriesIndex].data, ...newData];
|
||||
await this.updateSeries(currentSeries);
|
||||
}
|
||||
}
|
||||
|
||||
public async resizeChart() {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
|
Reference in New Issue
Block a user