From ed20e04e96c09e972ce6faec0d60ed681c7436b4 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Mon, 16 Jun 2025 22:23:22 +0000 Subject: [PATCH] fix(dees-catalog): update @design.estate/dees-wcctools dependency to version 1.0.98 for compatibility and enhance demo functionality with real-time data updates --- package.json | 2 +- pnpm-lock.yaml | 10 +- ts_web/elements/dees-chart-area.demo.ts | 319 ++++++++++++++++-------- ts_web/elements/dees-chart-area.ts | 168 ++++++++++++- 4 files changed, 386 insertions(+), 113 deletions(-) diff --git a/package.json b/package.json index c375521..7064a3b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "dependencies": { "@design.estate/dees-domtools": "^2.1.1", "@design.estate/dees-element": "^2.0.42", - "@design.estate/dees-wcctools": "^1.0.97", + "@design.estate/dees-wcctools": "^1.0.98", "@fortawesome/fontawesome-svg-core": "^6.7.2", "@fortawesome/free-brands-svg-icons": "^6.7.2", "@fortawesome/free-regular-svg-icons": "^6.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee725c0..22d0594 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^2.0.42 version: 2.0.42 '@design.estate/dees-wcctools': - specifier: ^1.0.97 - version: 1.0.97 + specifier: ^1.0.98 + version: 1.0.98 '@fortawesome/fontawesome-svg-core': specifier: ^6.7.2 version: 6.7.2 @@ -305,8 +305,8 @@ packages: '@design.estate/dees-element@2.0.42': resolution: {integrity: sha512-1PzHP6q/PtSiu4P0nCxjSeHtRHn62zoSouMy8JFW2h29FT/CSDVaTUAUqYqnvwE/U98aLNivWTmerZitDF7kBQ==} - '@design.estate/dees-wcctools@1.0.97': - resolution: {integrity: sha512-0jG6+xuh2kGyCww7oqIJ0OwQI7sMNM6g2pjyARQAzGPzB9khN7H+Yr2+uIF2/wcTc42kObRp/O35aI2TY2V1PA==} + '@design.estate/dees-wcctools@1.0.98': + resolution: {integrity: sha512-6EolTGBiXgF1wgr+KOeSXAIKpXqU95FU4vOJYPPEvb+e3ebFXCuL/B4UTFZYG3e1KuTZgxiaJ04L8ejm5HfTZA==} '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} @@ -5176,7 +5176,7 @@ snapshots: - supports-color - vue - '@design.estate/dees-wcctools@1.0.97': + '@design.estate/dees-wcctools@1.0.98': dependencies: '@design.estate/dees-domtools': 2.3.2 '@design.estate/dees-element': 2.0.42 diff --git a/ts_web/elements/dees-chart-area.demo.ts b/ts_web/elements/dees-chart-area.demo.ts index 0f54b4e..97aa610 100644 --- a/ts_web/elements/dees-chart-area.demo.ts +++ b/ts_web/elements/dees-chart-area.demo.ts @@ -40,9 +40,11 @@ export const demoFunc = () => { return html` { - // Get the chart element - const chartElement = elementArg.querySelector('dees-chart-area') as DeesChartArea; + // Get the chart elements + const chartElement = elementArg.querySelector('#main-chart') as DeesChartArea; + const connectionsChartElement = elementArg.querySelector('#connections-chart') as DeesChartArea; let intervalId: number; + let connectionsIntervalId: number; let currentDataset = 'system'; // Y-axis formatters for different datasets @@ -51,7 +53,35 @@ export const demoFunc = () => { network: (val: number) => `${val} Mbps`, sales: (val: number) => `$${val.toLocaleString()}`, }; + + // Time window configuration (in milliseconds) + const TIME_WINDOW = 2 * 60 * 1000; // 2 minutes + const UPDATE_INTERVAL = 1000; // 1 second + const DATA_POINT_INTERVAL = 5000; // Show data points every 5 seconds + + // Store previous values for smooth transitions + let previousValues = { + cpu: 30, + memory: 50, + download: 150, + upload: 30, + connections: 150 + }; + // Generate initial data points for time window + const generateInitialData = (baseValue: number, variance: number, interval: number = DATA_POINT_INTERVAL) => { + const data = []; + const now = Date.now(); + const pointCount = Math.floor(TIME_WINDOW / interval); + + for (let i = pointCount; i >= 0; i--) { + const timestamp = new Date(now - (i * interval)).toISOString(); + const value = baseValue + (Math.random() - 0.5) * variance; + data.push({ x: timestamp, y: Math.round(value) }); + } + return data; + }; + // Different datasets to showcase const datasets = { system: { @@ -59,25 +89,11 @@ export const demoFunc = () => { 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 }, - ], + data: generateInitialData(previousValues.cpu, 10), }, { 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 }, - ], + data: generateInitialData(previousValues.memory, 8), }, ], }, @@ -86,25 +102,11 @@ export const demoFunc = () => { 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 }, - ], + data: generateInitialData(previousValues.download, 30), }, { 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 }, - ], + data: generateInitialData(previousValues.upload, 10), }, ], }, @@ -137,40 +139,101 @@ export const demoFunc = () => { }, }; - // Generate random value within range - const getRandomValue = (min: number, max: number) => { - return Math.floor(Math.random() * (max - min + 1)) + min; + // Generate smooth value transitions + const getNextValue = (current: number, min: number, max: number, maxChange: number = 5) => { + // Add some randomness but keep it close to current value + const change = (Math.random() - 0.5) * maxChange * 2; + let newValue = current + change; + + // Apply some "pressure" to move towards center of range + const center = (min + max) / 2; + const pressure = (center - newValue) * 0.1; + newValue += pressure; + + // Ensure within bounds + newValue = Math.max(min, Math.min(max, newValue)); + return Math.round(newValue); }; + // Track time of last data point + let lastDataPointTime = Date.now(); + let connectionsLastUpdate = Date.now(); + // Add real-time data const addRealtimeData = () => { if (!chartElement) return; - const newTimestamp = new Date().toISOString(); + const now = Date.now(); - // Generate new data points based on dataset type - let newData: any[][] = []; + // Only add new data point every DATA_POINT_INTERVAL + const shouldAddPoint = (now - lastDataPointTime) >= DATA_POINT_INTERVAL; - 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 - ]; + if (shouldAddPoint) { + lastDataPointTime = now; + const newTimestamp = new Date(now).toISOString(); + + // Generate smooth transitions for new values + if (currentDataset === 'system') { + // Generate new values + previousValues.cpu = getNextValue(previousValues.cpu, 20, 50, 3); + previousValues.memory = getNextValue(previousValues.memory, 40, 70, 2); + + // Get current data and add new points + const currentSeries = chartElement.chartSeries.map((series, index) => ({ + name: series.name, + data: [ + ...(series.data as Array<{x: any; y: any}>), + index === 0 + ? { x: newTimestamp, y: previousValues.cpu } + : { x: newTimestamp, y: previousValues.memory } + ] + })); + + chartElement.updateSeries(currentSeries, false); + + } else if (currentDataset === 'network') { + // Generate new values + previousValues.download = getNextValue(previousValues.download, 100, 200, 10); + previousValues.upload = getNextValue(previousValues.upload, 20, 50, 5); + + // Get current data and add new points + const currentSeries = chartElement.chartSeries.map((series, index) => ({ + name: series.name, + data: [ + ...(series.data as Array<{x: any; y: any}>), + index === 0 + ? { x: newTimestamp, y: previousValues.download } + : { x: newTimestamp, y: previousValues.upload } + ] + })); + + chartElement.updateSeries(currentSeries, false); + } } + }; + + // Update connections chart data + const updateConnections = () => { + if (!connectionsChartElement) return; - // Keep only last 20 data points and update without animation - const currentSeries = chartElement.series.map((series, index) => ({ - ...series, - data: [...series.data.slice(-19), ...(newData[index] || [])], - })); + const now = Date.now(); + const newTimestamp = new Date(now).toISOString(); - // Update without animation for smoother real-time updates - chartElement.updateSeries(currentSeries, false); + // Generate new connections value with discrete changes + const change = Math.floor(Math.random() * 21) - 10; // -10 to +10 connections + previousValues.connections = Math.max(50, Math.min(300, previousValues.connections + change)); + + // Get current data and add new point + const currentSeries = connectionsChartElement.chartSeries; + const newData = [{ + name: currentSeries[0]?.name || 'Connections', + data: [ + ...(currentSeries[0]?.data as Array<{x: any; y: any}> || []), + { x: newTimestamp, y: previousValues.connections } + ] + }]; + + connectionsChartElement.updateSeries(newData, false); }; // Switch dataset @@ -180,21 +243,34 @@ export const demoFunc = () => { chartElement.label = dataset.label; chartElement.series = dataset.series; chartElement.yAxisFormatter = formatters[name]; + + // Set appropriate y-axis scaling + if (name === 'system') { + chartElement.yAxisScaling = 'percentage'; + chartElement.yAxisMax = 100; + } else if (name === 'network') { + chartElement.yAxisScaling = 'dynamic'; + } else { + chartElement.yAxisScaling = 'dynamic'; + } + + // Reset last data point time to get fresh data immediately + lastDataPointTime = Date.now() - DATA_POINT_INTERVAL; }; // Start/stop real-time updates const startRealtime = () => { if (!intervalId && (currentDataset === 'system' || currentDataset === 'network')) { - // Disable animations for real-time mode - chartElement.updateOptions({ - chart: { - animations: { - enabled: false - } - } - }, false, false); - - intervalId = window.setInterval(() => addRealtimeData(), 2000); + chartElement.realtimeMode = true; + // Only add data every 5 seconds, chart auto-scrolls independently + intervalId = window.setInterval(() => addRealtimeData(), DATA_POINT_INTERVAL); + } + + // Start connections updates + if (!connectionsIntervalId) { + connectionsChartElement.realtimeMode = true; + // Update connections every second + connectionsIntervalId = window.setInterval(() => updateConnections(), UPDATE_INTERVAL); } }; @@ -202,37 +278,35 @@ export const demoFunc = () => { if (intervalId) { window.clearInterval(intervalId); intervalId = null; - - // Re-enable animations when stopping real-time - chartElement.updateOptions({ - chart: { - animations: { - enabled: true, - speed: 400, - animateGradually: { - enabled: true, - delay: 150 - } - } - } - }, false, true); + chartElement.realtimeMode = false; + } + + // Stop connections updates + if (connectionsIntervalId) { + window.clearInterval(connectionsIntervalId); + connectionsIntervalId = null; + connectionsChartElement.realtimeMode = false; } }; - // Randomize current data + // Randomize current data (spike/drop simulation) const randomizeData = () => { - const currentSeries = chartElement.series.map(series => ({ - ...series, - data: series.data.map((point: any) => ({ - ...point, - y: typeof point.y === 'number' - ? Math.round(point.y * (0.8 + Math.random() * 0.4)) // +/- 20% variation - : point.y, - })), - })); + if (currentDataset === 'system') { + // Simulate CPU/Memory spike + previousValues.cpu = Math.random() > 0.5 ? 85 : 25; + previousValues.memory = Math.random() > 0.5 ? 80 : 45; + } else if (currentDataset === 'network') { + // Simulate network traffic spike + previousValues.download = Math.random() > 0.5 ? 250 : 100; + previousValues.upload = Math.random() > 0.5 ? 80 : 20; + } - // Update with animation for single updates - chartElement.updateSeries(currentSeries, true); + // Also spike connections + previousValues.connections = Math.random() > 0.5 ? 280 : 80; + + // Force immediate update by resetting timers + lastDataPointTime = 0; + connectionsLastUpdate = 0; }; // Wire up button click handlers @@ -249,10 +323,8 @@ export const demoFunc = () => { button.addEventListener('click', () => startRealtime()); } else if (text === 'Stop Live') { button.addEventListener('click', () => stopRealtime()); - } else if (text === 'Randomize Values') { + } else if (text === 'Spike Values') { button.addEventListener('click', () => randomizeData()); - } else if (text === 'Add Point') { - button.addEventListener('click', () => addRealtimeData()); } }); @@ -271,6 +343,18 @@ export const demoFunc = () => { }); }; + // Configure main chart with rolling window + chartElement.rollingWindow = TIME_WINDOW; + chartElement.realtimeMode = false; // Will be enabled when starting live updates + chartElement.yAxisScaling = 'percentage'; // Initial system dataset uses percentage + chartElement.yAxisMax = 100; + chartElement.autoScrollInterval = 1000; // Auto-scroll every second + + // Set initial time window + setTimeout(() => { + chartElement.updateTimeWindow(); + }, 100); + // Update button states when dataset changes const originalSwitchDataset = switchDataset; const switchDatasetWithButtonUpdate = (name: string) => { @@ -292,6 +376,27 @@ export const demoFunc = () => { button.addEventListener('click', () => switchDatasetWithButtonUpdate('sales')); } }); + + // Initialize connections chart with data + if (connectionsChartElement) { + const initialConnectionsData = generateInitialData(previousValues.connections, 30, UPDATE_INTERVAL); + connectionsChartElement.series = [{ + name: 'Connections', + data: initialConnectionsData + }]; + + // Configure connections chart + connectionsChartElement.rollingWindow = TIME_WINDOW; + connectionsChartElement.realtimeMode = false; // Will be enabled when starting live updates + connectionsChartElement.yAxisScaling = 'fixed'; + connectionsChartElement.yAxisMax = 350; + connectionsChartElement.autoScrollInterval = 1000; // Auto-scroll every second + + // Set initial time window + setTimeout(() => { + connectionsChartElement.updateTimeWindow(); + }, 100); + } }}>