feat(dees-statsgrid): add multiPercentage tile type to stats grid

This commit is contained in:
2026-01-06 00:45:10 +00:00
parent e71efd409b
commit c7bff04ae5
5 changed files with 128 additions and 14 deletions

View File

@@ -1,5 +1,14 @@
# Changelog # Changelog
## 2026-01-06 - 3.33.0 - feat(dees-statsgrid)
add multiPercentage tile type to stats grid
- Add new 'multiPercentage' type to IStatsTile (percentages: [{label, value, color?}])
- Implement renderMultiPercentage() to render up to 3 percentage items with label, value and colored progress bars
- Add CSS styles for multi-percentage layout, bars, labels and values
- Update demo to replace 'Error Rate' tile with a 'Resource Usage' multiPercentage example (CPU, Memory, Disk)
- Change is additive and backward-compatible with existing tile types
## 2026-01-04 - 3.32.0 - feat(demo) ## 2026-01-04 - 3.32.0 - feat(demo)
add demoGroup metadata to components and update related dependencies add demoGroup metadata to components and update related dependencies

View File

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@design.estate/dees-catalog', name: '@design.estate/dees-catalog',
version: '3.32.0', version: '3.33.0',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
} }

View File

@@ -64,14 +64,16 @@ class DemoDashboardView extends DeesElement {
color: '#8b5cf6' color: '#8b5cf6'
}, },
{ {
id: 'errors', id: 'resources',
title: 'Error Rate', title: 'Resource Usage',
value: 0.12, value: '',
unit: '%', type: 'multiPercentage',
type: 'number', icon: 'lucide:server',
icon: 'lucide:alertTriangle', percentages: [
description: 'Last 24 hours', { label: 'CPU', value: 67, color: '#3b82f6' },
color: '#f59e0b' { label: 'Memory', value: 84, color: '#8b5cf6' },
{ label: 'Disk', value: 45, color: '#10b981' }
]
}, },
{ {
id: 'requests', id: 'requests',

View File

@@ -29,7 +29,7 @@ export interface IStatsTile {
title: string; title: string;
value: number | string; value: number | string;
unit?: string; unit?: string;
type: 'number' | 'gauge' | 'percentage' | 'trend' | 'text'; type: 'number' | 'gauge' | 'percentage' | 'trend' | 'text' | 'multiPercentage';
// For gauge type // For gauge type
gaugeOptions?: { gaugeOptions?: {
@@ -41,6 +41,13 @@ export interface IStatsTile {
// For trend type // For trend type
trendData?: number[]; trendData?: number[];
// For multiPercentage type
percentages?: Array<{
label: string;
value: number;
color?: string;
}>;
// Visual customization // Visual customization
color?: string; color?: string;
icon?: string; icon?: string;
@@ -273,6 +280,9 @@ export class DeesStatsGrid extends DeesElement {
.percentage-wrapper { .percentage-wrapper {
width: 100%; width: 100%;
position: relative; position: relative;
display: flex;
flex-direction: column;
flex: 1;
} }
.percentage-value { .percentage-value {
@@ -290,6 +300,7 @@ export class DeesStatsGrid extends DeesElement {
background: ${cssManager.bdTheme('#e8e8e8', '#1a1a1a')}; background: ${cssManager.bdTheme('#e8e8e8', '#1a1a1a')};
border-radius: 3px; border-radius: 3px;
overflow: hidden; overflow: hidden;
margin-top: auto;
} }
.percentage-fill { .percentage-fill {
@@ -299,6 +310,62 @@ export class DeesStatsGrid extends DeesElement {
border-radius: 3px; border-radius: 3px;
} }
/* Multi Percentage Styles */
.multi-percentage-wrapper {
width: 100%;
display: flex;
flex-direction: column;
flex: 1;
}
.multi-percentage-items {
display: flex;
flex-direction: column;
gap: 12px;
margin-top: auto;
}
.multi-percentage-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.multi-percentage-header {
display: flex;
justify-content: space-between;
align-items: baseline;
}
.multi-percentage-label {
font-size: 11px;
font-weight: 500;
color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')};
letter-spacing: -0.01em;
}
.multi-percentage-value {
font-size: 13px;
font-weight: 600;
color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')};
letter-spacing: -0.01em;
}
.multi-percentage-bar {
width: 100%;
height: 4px;
background: ${cssManager.bdTheme('#e8e8e8', '#1a1a1a')};
border-radius: 2px;
overflow: hidden;
}
.multi-percentage-fill {
height: 100%;
background: ${cssManager.bdTheme('#333333', '#e0e0e0')};
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 2px;
}
/* Trend Styles */ /* Trend Styles */
.trend-container { .trend-container {
width: 100%; width: 100%;
@@ -465,6 +532,9 @@ export class DeesStatsGrid extends DeesElement {
case 'trend': case 'trend':
return this.renderTrend(tile); return this.renderTrend(tile);
case 'multiPercentage':
return this.renderMultiPercentage(tile);
case 'text': case 'text':
return html` return html`
<div class="text-value" style="${tile.color ? `color: ${tile.color}` : ''}"> <div class="text-value" style="${tile.color ? `color: ${tile.color}` : ''}">
@@ -596,6 +666,39 @@ export class DeesStatsGrid extends DeesElement {
`; `;
} }
private renderMultiPercentage(tile: IStatsTile): TemplateResult {
if (!tile.percentages || tile.percentages.length === 0) {
return html`<div class="tile-value">${tile.value}</div>`;
}
// Limit to 3 percentages
const items = tile.percentages.slice(0, 3);
return html`
<div class="multi-percentage-wrapper">
<div class="multi-percentage-items">
${items.map(item => {
const percentage = Math.min(100, Math.max(0, item.value));
return html`
<div class="multi-percentage-item">
<div class="multi-percentage-header">
<span class="multi-percentage-label">${item.label}</span>
<span class="multi-percentage-value">${percentage}%</span>
</div>
<div class="multi-percentage-bar">
<div
class="multi-percentage-fill"
style="width: ${percentage}%; ${item.color ? `background: ${item.color}` : ''}"
></div>
</div>
</div>
`;
})}
</div>
</div>
`;
}
private async handleGridAction(action: plugins.tsclass.website.IMenuItem) { private async handleGridAction(action: plugins.tsclass.website.IMenuItem) {
if (action.action) { if (action.action) {
await action.action(); await action.action();