Enhance dashboard chart animations with improved element selection, faster update speed, and robust error handling
This commit is contained in:
@@ -14,16 +14,30 @@ class DashboardAnimator {
|
||||
|
||||
// Chart dimensions
|
||||
this.barChartHeight = 120;
|
||||
this.chartUpdateSpeed = 2000; // Base cycle speed in ms (controls smoothness)
|
||||
this.chartUpdateSpeed = 1500; // Faster: 1.5s cycle for constant movement
|
||||
|
||||
// Cache DOM elements
|
||||
this.barsGroup1 = svg.querySelectorAll('#bars-group-1 .bar');
|
||||
this.barsGroup2 = svg.querySelectorAll('#bars-group-2 .bar');
|
||||
this.valuesGroup1 = svg.querySelectorAll('#values-group-1 .chart-value');
|
||||
this.valuesGroup2 = svg.querySelectorAll('#values-group-2 .chart-value');
|
||||
this.linePath = svg.getElementById('line-path');
|
||||
this.lineFill = svg.getElementById('line-fill');
|
||||
this.pieSegments = svg.querySelectorAll('.pie-segment');
|
||||
// Cache DOM elements - try multiple selector methods for robustness
|
||||
this.barsGroup1 = this.svg.querySelectorAll('#bars-group-1 rect.bar');
|
||||
this.barsGroup2 = this.svg.querySelectorAll('#bars-group-2 rect.bar');
|
||||
this.valuesGroup1 = this.svg.querySelectorAll('#values-group-1 text.chart-value');
|
||||
this.valuesGroup2 = this.svg.querySelectorAll('#values-group-2 text.chart-value');
|
||||
this.linePath = this.svg.querySelector('#line-path');
|
||||
this.lineFill = this.svg.querySelector('#line-fill');
|
||||
this.pieSegments = this.svg.querySelectorAll('g.pie-segment');
|
||||
|
||||
// Debug: Check if elements were found
|
||||
const hasElements = this.barsGroup1.length > 0 || this.linePath || this.pieSegments.length > 0;
|
||||
if (!hasElements) {
|
||||
console.warn('[DashboardAnimator] No chart elements found. Debugging info:');
|
||||
console.warn(' SVG element:', this.svg);
|
||||
console.warn(' bars-group-1:', this.barsGroup1.length);
|
||||
console.warn(' line-path:', !!this.linePath);
|
||||
console.warn(' pie-segment:', this.pieSegments.length);
|
||||
// Try direct getElementById as fallback
|
||||
console.warn(' Testing getElementById on document:', document.getElementById('bars-group-1'));
|
||||
} else {
|
||||
console.log('[DashboardAnimator] ✓ Found elements - Bars:', this.barsGroup1.length, 'Line:', !!this.linePath, 'Pie:', this.pieSegments.length);
|
||||
}
|
||||
|
||||
// Noise generators for smooth, realistic data
|
||||
this.noisePhase1 = Math.random() * Math.PI * 2;
|
||||
@@ -36,9 +50,20 @@ class DashboardAnimator {
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.isReducedMotion) {
|
||||
if (!this.isReducedMotion && (this.barsGroup1.length || this.linePath || this.pieSegments.length)) {
|
||||
console.log('[DashboardAnimator] ✓ Starting animation loop');
|
||||
// Visual indicator that animator started
|
||||
this.svg.style.cssText = 'border: 2px solid green; border-radius: 4px;';
|
||||
setTimeout(() => {
|
||||
this.svg.style.border = '';
|
||||
}, 500);
|
||||
this.animate();
|
||||
this.attachHoverListeners();
|
||||
} else if (this.isReducedMotion) {
|
||||
console.log('[DashboardAnimator] Motion preference: reduced');
|
||||
} else {
|
||||
console.log('[DashboardAnimator] ✗ No animatable elements found');
|
||||
this.svg.style.cssText = 'border: 2px solid red; border-radius: 4px;';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,14 +97,20 @@ class DashboardAnimator {
|
||||
* Update bar chart with smooth animation
|
||||
*/
|
||||
updateBars(barsElements, valuesElements, maxValue = 100, isPercent = true) {
|
||||
if (!barsElements || barsElements.length === 0) return;
|
||||
|
||||
barsElements.forEach((bar, index) => {
|
||||
const value = this.generateValue(this.noisePhase1, index, maxValue);
|
||||
const heightPercent = (value / maxValue) * 100;
|
||||
const height = (heightPercent / 100) * this.barChartHeight;
|
||||
|
||||
bar.setAttribute('height', height);
|
||||
// For SVG bars to grow upward, adjust y position inversely
|
||||
const y = this.barChartHeight - height;
|
||||
|
||||
if (valuesElements[index]) {
|
||||
bar.setAttribute('height', height);
|
||||
bar.setAttribute('y', y);
|
||||
|
||||
if (valuesElements && valuesElements[index]) {
|
||||
valuesElements[index].textContent = isPercent ?
|
||||
Math.round(value) + '%' :
|
||||
Math.round(value);
|
||||
@@ -92,6 +123,8 @@ class DashboardAnimator {
|
||||
* Update line graph with smooth curve
|
||||
*/
|
||||
updateLineGraph() {
|
||||
if (!this.linePath || !this.lineFill) return;
|
||||
|
||||
const time = (Date.now() - this.startTime) / this.chartUpdateSpeed;
|
||||
const points = [];
|
||||
|
||||
@@ -119,6 +152,8 @@ class DashboardAnimator {
|
||||
* Update pie chart with smooth segment rotation
|
||||
*/
|
||||
updatePieChart() {
|
||||
if (!this.pieSegments || this.pieSegments.length === 0) return;
|
||||
|
||||
const time = (Date.now() - this.startTime) / (this.chartUpdateSpeed * 0.5);
|
||||
const baseRotation = (time * 15) % 360; // Slow rotation
|
||||
|
||||
@@ -134,10 +169,14 @@ class DashboardAnimator {
|
||||
*/
|
||||
animate = () => {
|
||||
if (!this.isPaused) {
|
||||
this.updateBars(this.barsGroup1, this.valuesGroup1, 100, true);
|
||||
this.updateBars(this.barsGroup2, this.valuesGroup2, 5000, false);
|
||||
this.updateLineGraph();
|
||||
this.updatePieChart();
|
||||
try {
|
||||
this.updateBars(this.barsGroup1, this.valuesGroup1, 100, true);
|
||||
this.updateBars(this.barsGroup2, this.valuesGroup2, 5000, false);
|
||||
this.updateLineGraph();
|
||||
this.updatePieChart();
|
||||
} catch (err) {
|
||||
console.error('[DashboardAnimator] Animation error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
this.animationId = requestAnimationFrame(this.animate);
|
||||
@@ -189,36 +228,84 @@ class DashboardAnimator {
|
||||
*/
|
||||
function initDashboardAnimation() {
|
||||
const dashboardCharts = document.querySelectorAll('.dashboard-chart');
|
||||
console.log('[initDashboardAnimation] Found', dashboardCharts.length, 'dashboard charts');
|
||||
|
||||
if (!dashboardCharts.length) return;
|
||||
|
||||
const observerOptions = {
|
||||
root: null,
|
||||
rootMargin: '50px',
|
||||
threshold: 0.1
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
if (!entry.target._dashboardAnimator) {
|
||||
entry.target._dashboardAnimator = new DashboardAnimator(entry.target);
|
||||
}
|
||||
} else {
|
||||
// Pause animation when out of view for performance
|
||||
if (entry.target._dashboardAnimator) {
|
||||
entry.target._dashboardAnimator.pause();
|
||||
dashboardCharts.forEach((chart, idx) => {
|
||||
// Check if element is likely in viewport
|
||||
const rect = chart.getBoundingClientRect();
|
||||
const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
|
||||
console.log('[initDashboardAnimation] Chart', idx, 'visible:', isVisible, 'rect:', rect);
|
||||
|
||||
if (isVisible || !chart._dashboardAnimator) {
|
||||
// Start animation immediately if visible or if not set up yet
|
||||
if (!chart._dashboardAnimator) {
|
||||
console.log('[initDashboardAnimation] Creating DashboardAnimator for chart', idx);
|
||||
try {
|
||||
chart._dashboardAnimator = new DashboardAnimator(chart);
|
||||
console.log('[initDashboardAnimation] ✓ DashboardAnimator created successfully');
|
||||
} catch (err) {
|
||||
console.error('[initDashboardAnimation] ✗ Failed to create DashboardAnimator:', err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
}
|
||||
});
|
||||
|
||||
dashboardCharts.forEach(chart => observer.observe(chart));
|
||||
// Also set up IntersectionObserver for when elements come into view
|
||||
if ('IntersectionObserver' in window) {
|
||||
const observerOptions = {
|
||||
root: null,
|
||||
rootMargin: '100px',
|
||||
threshold: 0.05
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
console.log('[IntersectionObserver]', entry.isIntersecting ? 'Intersecting' : 'Not intersecting');
|
||||
if (entry.isIntersecting) {
|
||||
if (!entry.target._dashboardAnimator) {
|
||||
console.log('[IntersectionObserver] Creating new DashboardAnimator');
|
||||
try {
|
||||
entry.target._dashboardAnimator = new DashboardAnimator(entry.target);
|
||||
} catch (err) {
|
||||
console.error('[IntersectionObserver] Failed:', err);
|
||||
}
|
||||
} else {
|
||||
entry.target._dashboardAnimator.resume();
|
||||
}
|
||||
} else {
|
||||
if (entry.target._dashboardAnimator) {
|
||||
entry.target._dashboardAnimator.pause();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
dashboardCharts.forEach(chart => observer.observe(chart));
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initDashboardAnimation);
|
||||
} else {
|
||||
initDashboardAnimation();
|
||||
console.log('[Dashboard Animator] Script loaded, readyState:', document.readyState);
|
||||
|
||||
function tryInitialize() {
|
||||
console.log('[Dashboard Animator] tryInitialize called');
|
||||
const charts = document.querySelectorAll('.dashboard-chart');
|
||||
console.log('[Dashboard Animator] Found', charts.length, 'dashboard charts on page');
|
||||
if (charts.length > 0) {
|
||||
console.log('[Dashboard Animator] Found charts, initializing now...');
|
||||
initDashboardAnimation();
|
||||
} else {
|
||||
console.log('[Dashboard Animator] No charts found yet, retrying in 100ms...');
|
||||
setTimeout(tryInitialize, 100);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
console.log('[Dashboard Animator] DOM still loading, waiting for DOMContentLoaded');
|
||||
document.addEventListener('DOMContentLoaded', tryInitialize);
|
||||
} else {
|
||||
console.log('[Dashboard Animator] DOM already loaded, initializing immediately');
|
||||
tryInitialize();
|
||||
}
|
||||
|
||||
@@ -1347,86 +1347,77 @@ function oribi_render_platform_row( $a ) {
|
||||
// Render dashboard chart animation
|
||||
$visual_html = '<div class="dashboard-chart-container" data-dashboard-container="true"><svg viewBox="0 0 800 400" xmlns="http://www.w3.org/2000/svg" class="dashboard-chart" aria-label="Real-time dashboard with animated charts">
|
||||
<defs>
|
||||
<style>
|
||||
.dashboard-chart { background: transparent; }
|
||||
.chart-title { font-size: 14px; font-weight: 600; fill: var(--color-heading); }
|
||||
.chart-label { font-size: 12px; fill: var(--color-text-muted); }
|
||||
.chart-value { font-size: 13px; font-weight: 500; fill: var(--color-text); }
|
||||
.bar { fill: url(#barGradient); transition: height 0.3s ease; transform-origin: bottom; }
|
||||
.line-path { stroke: var(--color-accent); fill: none; stroke-width: 2.5; stroke-linecap: round; }
|
||||
.pie-segment { transition: transform 0.4s ease; }
|
||||
</style>
|
||||
<linearGradient id="barGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:var(--color-primary);stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:var(--color-accent);stop-opacity:0.7" />
|
||||
<stop offset="0%" style="stop-color:#004225;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#4CAF50;stop-opacity:0.8" />
|
||||
</linearGradient>
|
||||
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:var(--color-accent);stop-opacity:0.3" />
|
||||
<stop offset="100%" style="stop-color:var(--color-accent);stop-opacity:0" />
|
||||
<stop offset="0%" style="stop-color:#4CAF50;stop-opacity:0.3" />
|
||||
<stop offset="100%" style="stop-color:#4CAF50;stop-opacity:0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="bar-charts" transform="translate(10, 10)">
|
||||
<text x="0" y="0" class="chart-title">Performance</text>
|
||||
<text x="0" y="0" style="font-size:14px;font-weight:600;fill:#333333">Performance</text>
|
||||
<g id="bars-group-1" transform="translate(0, 25)">
|
||||
<rect class="bar" x="0" y="0" width="18" height="0" data-index="0" data-label="API"/>
|
||||
<rect class="bar" x="25" y="0" width="18" height="0" data-index="1" data-label="Cache"/>
|
||||
<rect class="bar" x="50" y="0" width="18" height="0" data-index="2" data-label="DB"/>
|
||||
<rect class="bar" x="75" y="0" width="18" height="0" data-index="3" data-label="Queue"/>
|
||||
<rect class="bar" x="100" y="0" width="18" height="0" data-index="4" data-label="Worker"/>
|
||||
<rect class="bar" x="0" y="0" width="18" height="0" fill="url(#barGradient)" data-index="0" data-label="API"/>
|
||||
<rect class="bar" x="25" y="0" width="18" height="0" fill="url(#barGradient)" data-index="1" data-label="Cache"/>
|
||||
<rect class="bar" x="50" y="0" width="18" height="0" fill="url(#barGradient)" data-index="2" data-label="DB"/>
|
||||
<rect class="bar" x="75" y="0" width="18" height="0" fill="url(#barGradient)" data-index="3" data-label="Queue"/>
|
||||
<rect class="bar" x="100" y="0" width="18" height="0" fill="url(#barGradient)" data-index="4" data-label="Worker"/>
|
||||
</g>
|
||||
<g transform="translate(0, 145)">
|
||||
<text x="0" y="0" class="chart-label">API</text>
|
||||
<text x="25" y="0" class="chart-label">Cache</text>
|
||||
<text x="50" y="0" class="chart-label">DB</text>
|
||||
<text x="75" y="0" class="chart-label">Queue</text>
|
||||
<text x="100" y="0" class="chart-label">Worker</text>
|
||||
<text x="0" y="0" style="font-size:12px;fill:#666666">API</text>
|
||||
<text x="25" y="0" style="font-size:12px;fill:#666666">Cache</text>
|
||||
<text x="50" y="0" style="font-size:12px;fill:#666666">DB</text>
|
||||
<text x="75" y="0" style="font-size:12px;fill:#666666">Queue</text>
|
||||
<text x="100" y="0" style="font-size:12px;fill:#666666">Worker</text>
|
||||
</g>
|
||||
<g id="values-group-1" transform="translate(0, 160)">
|
||||
<text x="9" y="0" class="chart-value" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="34" y="0" class="chart-value" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="59" y="0" class="chart-value" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="84" y="0" class="chart-value" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="109" y="0" class="chart-value" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="9" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="34" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="59" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="84" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0%</text>
|
||||
<text x="109" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0%</text>
|
||||
</g>
|
||||
<text x="150" y="0" class="chart-title">Requests/sec</text>
|
||||
<text x="150" y="0" style="font-size:14px;font-weight:600;fill:#333333">Requests/sec</text>
|
||||
<g id="bars-group-2" transform="translate(150, 25)">
|
||||
<rect class="bar" x="0" y="0" width="18" height="0" data-index="0" data-label="Read"/>
|
||||
<rect class="bar" x="25" y="0" width="18" height="0" data-index="1" data-label="Write"/>
|
||||
<rect class="bar" x="50" y="0" width="18" height="0" data-index="2" data-label="Update"/>
|
||||
<rect class="bar" x="75" y="0" width="18" height="0" data-index="3" data-label="Delete"/>
|
||||
<rect class="bar" x="0" y="0" width="18" height="0" fill="url(#barGradient)" data-index="0" data-label="Read"/>
|
||||
<rect class="bar" x="25" y="0" width="18" height="0" fill="url(#barGradient)" data-index="1" data-label="Write"/>
|
||||
<rect class="bar" x="50" y="0" width="18" height="0" fill="url(#barGradient)" data-index="2" data-label="Update"/>
|
||||
<rect class="bar" x="75" y="0" width="18" height="0" fill="url(#barGradient)" data-index="3" data-label="Delete"/>
|
||||
</g>
|
||||
<g transform="translate(150, 145)">
|
||||
<text x="0" y="0" class="chart-label">Read</text>
|
||||
<text x="25" y="0" class="chart-label">Write</text>
|
||||
<text x="50" y="0" class="chart-label">Update</text>
|
||||
<text x="75" y="0" class="chart-label">Delete</text>
|
||||
<text x="0" y="0" style="font-size:12px;fill:#666666">Read</text>
|
||||
<text x="25" y="0" style="font-size:12px;fill:#666666">Write</text>
|
||||
<text x="50" y="0" style="font-size:12px;fill:#666666">Update</text>
|
||||
<text x="75" y="0" style="font-size:12px;fill:#666666">Delete</text>
|
||||
</g>
|
||||
<g id="values-group-2" transform="translate(150, 160)">
|
||||
<text x="9" y="0" class="chart-value" text-anchor="middle" data-value="0">0</text>
|
||||
<text x="34" y="0" class="chart-value" text-anchor="middle" data-value="0">0</text>
|
||||
<text x="59" y="0" class="chart-value" text-anchor="middle" data-value="0">0</text>
|
||||
<text x="84" y="0" class="chart-value" text-anchor="middle" data-value="0">0</text>
|
||||
<text x="9" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0</text>
|
||||
<text x="34" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0</text>
|
||||
<text x="59" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0</text>
|
||||
<text x="84" y="0" style="font-size:13px;font-weight:500;fill:#333333" text-anchor="middle" data-value="0">0</text>
|
||||
</g>
|
||||
</g>
|
||||
<g id="line-graph" transform="translate(320, 10)">
|
||||
<text x="0" y="0" class="chart-title">Traffic Trend</text>
|
||||
<text x="0" y="0" style="font-size:14px;font-weight:600;fill:#333333">Traffic Trend</text>
|
||||
<g transform="translate(0, 25)">
|
||||
<line x1="0" y1="0" x2="200" y2="0" stroke="var(--color-border)" stroke-width="0.5"/>
|
||||
<line x1="0" y1="40" x2="200" y2="40" stroke="var(--color-border)" stroke-width="0.5"/>
|
||||
<line x1="0" y1="80" x2="200" y2="80" stroke="var(--color-border)" stroke-width="0.5"/>
|
||||
<line x1="0" y1="120" x2="200" y2="120" stroke="var(--color-border)" stroke-width="0.5"/>
|
||||
<line x1="0" y1="0" x2="200" y2="0" stroke="#E0E0E0" stroke-width="0.5"/>
|
||||
<line x1="0" y1="40" x2="200" y2="40" stroke="#E0E0E0" stroke-width="0.5"/>
|
||||
<line x1="0" y1="80" x2="200" y2="80" stroke="#E0E0E0" stroke-width="0.5"/>
|
||||
<line x1="0" y1="120" x2="200" y2="120" stroke="#E0E0E0" stroke-width="0.5"/>
|
||||
</g>
|
||||
<path id="line-path" class="line-path" d="M0,80 Q50,60 100,70 T200,50" stroke-dasharray="200" stroke-dashoffset="200"/>
|
||||
<path id="line-path" d="M0,80 Q50,60 100,70 T200,50" stroke="#4CAF50" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-dasharray="200" stroke-dashoffset="200"/>
|
||||
<path id="line-fill" d="M0,80 Q50,60 100,70 T200,50 L200,160 Q100,140 0,160 Z" fill="url(#lineGradient)"/>
|
||||
</g>
|
||||
<g id="pie-chart" transform="translate(580, 10)">
|
||||
<text x="60" y="0" class="chart-title" text-anchor="middle">Distribution</text>
|
||||
<text x="60" y="0" style="font-size:14px;font-weight:600;fill:#333333" text-anchor="middle">Distribution</text>
|
||||
<g transform="translate(60, 80)">
|
||||
<g class="pie-segment" id="pie-seg-1" transform="rotate(0)">
|
||||
<path d="M 0,0 L 0,-45 A 45,45 0 0,1 31.82,-31.82 Z" fill="var(--color-primary)" opacity="0.9"/>
|
||||
<path d="M 0,0 L 0,-45 A 45,45 0 0,1 31.82,-31.82 Z" fill="#004225" opacity="0.9"/>
|
||||
</g>
|
||||
<g class="pie-segment" id="pie-seg-2" transform="rotate(90)">
|
||||
<path d="M 0,0 L 0,-45 A 45,45 0 0,1 31.82,-31.82 Z" fill="var(--color-accent)" opacity="0.8"/>
|
||||
<path d="M 0,0 L 0,-45 A 45,45 0 0,1 31.82,-31.82 Z" fill="#4CAF50" opacity="0.8"/>
|
||||
</g>
|
||||
<g class="pie-segment" id="pie-seg-3" transform="rotate(180)">
|
||||
<path d="M 0,0 L 0,-45 A 45,45 0 0,1 31.82,-31.82 Z" fill="#f59e0b" opacity="0.7"/>
|
||||
@@ -1434,18 +1425,18 @@ function oribi_render_platform_row( $a ) {
|
||||
<g class="pie-segment" id="pie-seg-4" transform="rotate(270)">
|
||||
<path d="M 0,0 L 0,-45 A 45,45 0 0,1 31.82,-31.82 Z" fill="#ef4444" opacity="0.7"/>
|
||||
</g>
|
||||
<circle cx="0" cy="0" r="18" fill="var(--color-bg)" stroke="var(--color-border)" stroke-width="1"/>
|
||||
<text x="0" y="5" text-anchor="middle" class="chart-value">100%</text>
|
||||
<circle cx="0" cy="0" r="18" fill="#ffffff" stroke="#E0E0E0" stroke-width="1"/>
|
||||
<text x="0" y="5" text-anchor="middle" style="font-size:13px;font-weight:500;fill:#333333">100%</text>
|
||||
</g>
|
||||
<g transform="translate(0, 210)">
|
||||
<rect x="0" y="0" width="8" height="8" fill="var(--color-primary)"/>
|
||||
<text x="12" y="7" class="chart-label">Service A</text>
|
||||
<rect x="0" y="15" width="8" height="8" fill="var(--color-accent)"/>
|
||||
<text x="12" y="22" class="chart-label">Service B</text>
|
||||
<rect x="0" y="0" width="8" height="8" fill="#004225"/>
|
||||
<text x="12" y="7" style="font-size:12px;fill:#666666">Service A</text>
|
||||
<rect x="0" y="15" width="8" height="8" fill="#4CAF50"/>
|
||||
<text x="12" y="22" style="font-size:12px;fill:#666666">Service B</text>
|
||||
<rect x="100" y="0" width="8" height="8" fill="#f59e0b"/>
|
||||
<text x="112" y="7" class="chart-label">Service C</text>
|
||||
<text x="112" y="7" style="font-size:12px;fill:#666666">Service C</text>
|
||||
<rect x="100" y="15" width="8" height="8" fill="#ef4444"/>
|
||||
<text x="112" y="22" class="chart-label">Service D</text>
|
||||
<text x="112" y="22" style="font-size:12px;fill:#666666">Service D</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg></div>';
|
||||
|
||||
Reference in New Issue
Block a user