feat(dees-statsgrid): add cpuCores tile type with column spanning, rendering, demo and docs
This commit is contained in:
@@ -24,12 +24,21 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ICpuCore {
|
||||
id: string | number;
|
||||
usage: number; // 0-100
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface IStatsTile {
|
||||
id: string;
|
||||
title: string;
|
||||
value: number | string;
|
||||
unit?: string;
|
||||
type: 'number' | 'gauge' | 'percentage' | 'trend' | 'text' | 'multiPercentage';
|
||||
type: 'number' | 'gauge' | 'percentage' | 'trend' | 'text' | 'multiPercentage' | 'cpuCores';
|
||||
|
||||
// Layout options
|
||||
columnSpan?: number; // Number of columns to span (default: 1)
|
||||
|
||||
// For gauge type
|
||||
gaugeOptions?: {
|
||||
@@ -48,6 +57,9 @@ export interface IStatsTile {
|
||||
color?: string;
|
||||
}>;
|
||||
|
||||
// For cpuCores type
|
||||
coresData?: ICpuCore[];
|
||||
|
||||
// Visual customization
|
||||
color?: string;
|
||||
icon?: string;
|
||||
@@ -366,6 +378,107 @@ export class DeesStatsGrid extends DeesElement {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* CPU Cores Styles */
|
||||
.cpu-cores-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.cpu-cores-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.cpu-cores-value {
|
||||
font-size: var(--value-font-size);
|
||||
font-weight: 600;
|
||||
color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')};
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.cpu-cores-unit {
|
||||
font-size: var(--unit-font-size);
|
||||
font-weight: 400;
|
||||
color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')};
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.cpu-cores-label {
|
||||
font-size: var(--label-font-size);
|
||||
font-weight: 500;
|
||||
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
|
||||
letter-spacing: -0.01em;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.cpu-cores-bars {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 3px;
|
||||
flex: 1;
|
||||
min-height: 60px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.cpu-core-bar-container {
|
||||
flex: 1;
|
||||
min-width: 6px;
|
||||
max-width: 24px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.cpu-core-bar-wrapper {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background: ${cssManager.bdTheme('#e8e8e8', '#1a1a1a')};
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
overflow: hidden;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.cpu-core-bar-fill {
|
||||
width: 100%;
|
||||
background: ${cssManager.bdTheme('#666666', '#888888')};
|
||||
transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.3s ease;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
.cpu-core-bar-fill.low {
|
||||
background: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')};
|
||||
}
|
||||
|
||||
.cpu-core-bar-fill.medium {
|
||||
background: ${cssManager.bdTheme('hsl(45.4 93.4% 47.5%)', 'hsl(45.4 93.4% 47.5%)')};
|
||||
}
|
||||
|
||||
.cpu-core-bar-fill.high {
|
||||
background: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 84.2% 60.2%)')};
|
||||
}
|
||||
|
||||
.cpu-core-label {
|
||||
font-size: 9px;
|
||||
font-weight: 500;
|
||||
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Trend Styles */
|
||||
.trend-container {
|
||||
width: 100%;
|
||||
@@ -488,10 +601,12 @@ export class DeesStatsGrid extends DeesElement {
|
||||
private renderTile(tile: IStatsTile): TemplateResult {
|
||||
const hasActions = tile.actions && tile.actions.length > 0;
|
||||
const clickable = hasActions && tile.actions.length === 1;
|
||||
const columnSpan = tile.columnSpan && tile.columnSpan > 1 ? tile.columnSpan : undefined;
|
||||
|
||||
return html`
|
||||
<div
|
||||
<div
|
||||
class="stats-tile ${clickable ? 'clickable' : ''}"
|
||||
style="${columnSpan ? `grid-column: span ${columnSpan}` : ''}"
|
||||
@click=${clickable ? () => this.handleTileAction(tile.actions![0], tile) : undefined}
|
||||
@contextmenu=${hasActions ? (e: MouseEvent) => this.showContextMenu(e, tile) : undefined}
|
||||
>
|
||||
@@ -535,6 +650,9 @@ export class DeesStatsGrid extends DeesElement {
|
||||
case 'multiPercentage':
|
||||
return this.renderMultiPercentage(tile);
|
||||
|
||||
case 'cpuCores':
|
||||
return this.renderCpuCores(tile);
|
||||
|
||||
case 'text':
|
||||
return html`
|
||||
<div class="text-value" style="${tile.color ? `color: ${tile.color}` : ''}">
|
||||
@@ -699,6 +817,51 @@ export class DeesStatsGrid extends DeesElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private renderCpuCores(tile: IStatsTile): TemplateResult {
|
||||
if (!tile.coresData || tile.coresData.length === 0) {
|
||||
return html`<div class="tile-value">${tile.value}</div>`;
|
||||
}
|
||||
|
||||
const cores = tile.coresData;
|
||||
const avgUsage = Math.round(cores.reduce((sum, core) => sum + core.usage, 0) / cores.length);
|
||||
|
||||
// Determine color class based on usage
|
||||
const getColorClass = (usage: number): string => {
|
||||
if (usage < 50) return 'low';
|
||||
if (usage < 80) return 'medium';
|
||||
return 'high';
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="cpu-cores-wrapper">
|
||||
<div class="cpu-cores-header">
|
||||
<span class="cpu-cores-value">${avgUsage}</span>
|
||||
<span class="cpu-cores-unit">%</span>
|
||||
<span class="cpu-cores-label">${cores.length} cores</span>
|
||||
</div>
|
||||
<div class="cpu-cores-bars">
|
||||
${cores.map(core => {
|
||||
const usage = Math.min(100, Math.max(0, core.usage));
|
||||
const colorClass = getColorClass(usage);
|
||||
return html`
|
||||
<div class="cpu-core-bar-container" title="Core ${core.label || core.id}: ${usage}%">
|
||||
<div class="cpu-core-bar-wrapper">
|
||||
<div
|
||||
class="cpu-core-bar-fill ${colorClass}"
|
||||
style="height: ${usage}%"
|
||||
></div>
|
||||
</div>
|
||||
${cores.length <= 16 ? html`
|
||||
<span class="cpu-core-label">${core.label || core.id}</span>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private async handleGridAction(action: plugins.tsclass.website.IMenuItem) {
|
||||
if (action.action) {
|
||||
await action.action();
|
||||
|
||||
Reference in New Issue
Block a user