Enhance StoragePanel with improved volume color coding and metric display
Added a color coding system for volume segments in the StoragePanel to visually distinguish between owned, foreign, and free storage. Updated the Metric component to include a swatch for better visual association with the corresponding volume. Enhanced comments for clarity and improved the display logic for free storage, ensuring users can easily identify low space conditions.
This commit is contained in:
@@ -10,6 +10,14 @@ import { Card, CardContent } from '@/shared/ui/card'
|
|||||||
import { getStorageStats } from './api'
|
import { getStorageStats } from './api'
|
||||||
import { formatBytes, percentOf } from './format'
|
import { formatBytes, percentOf } from './format'
|
||||||
|
|
||||||
|
/** Доли тома: своё, чужое и свободное. Свободное краснеет у порога — там его и надо заметить. */
|
||||||
|
const VOLUME_COLORS = {
|
||||||
|
ours: 'bg-primary',
|
||||||
|
foreign: 'bg-muted-foreground/50',
|
||||||
|
free: 'bg-emerald-500/25',
|
||||||
|
lowFree: 'bg-destructive/50',
|
||||||
|
}
|
||||||
|
|
||||||
/** Цвета областей — те же и в полосе, и в списке: полоса без легенды не читается. */
|
/** Цвета областей — те же и в полосе, и в списке: полоса без легенды не читается. */
|
||||||
const AREA_COLORS: Record<StorageArea, string> = {
|
const AREA_COLORS: Record<StorageArea, string> = {
|
||||||
Assets: 'bg-emerald-500',
|
Assets: 'bg-emerald-500',
|
||||||
@@ -101,18 +109,24 @@ export function StoragePanel() {
|
|||||||
|
|
||||||
{volumeKnown ? (
|
{volumeKnown ? (
|
||||||
<>
|
<>
|
||||||
{/* Полоса тома: своё, чужое, свободное. */}
|
{/* Полоса тома: своё, чужое, свободное. Свободное — такая же доля, а не пустой
|
||||||
|
остаток полосы: иначе непонятно, кончился ли диск или просто кончилась заливка. */}
|
||||||
<div className="flex h-4 overflow-hidden rounded-full bg-muted/40">
|
<div className="flex h-4 overflow-hidden rounded-full bg-muted/40">
|
||||||
<div
|
<div
|
||||||
className="bg-primary"
|
className={VOLUME_COLORS.ours}
|
||||||
style={{ width: `${percentOf(storage, total)}%` }}
|
style={{ width: `${percentOf(storage, total)}%` }}
|
||||||
title={`${t('admin.storage.ours')} · ${formatBytes(storage)}`}
|
title={`${t('admin.storage.ours')} · ${formatBytes(storage)}`}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="bg-muted-foreground/50"
|
className={VOLUME_COLORS.foreign}
|
||||||
style={{ width: `${percentOf(foreign, total)}%` }}
|
style={{ width: `${percentOf(foreign, total)}%` }}
|
||||||
title={`${t('admin.storage.foreign')} · ${formatBytes(foreign)}`}
|
title={`${t('admin.storage.foreign')} · ${formatBytes(foreign)}`}
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
className={lowSpace ? VOLUME_COLORS.lowFree : VOLUME_COLORS.free}
|
||||||
|
style={{ width: `${percentOf(free, total)}%` }}
|
||||||
|
title={`${t('admin.storage.free')} · ${formatBytes(free)}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-3 text-sm sm:grid-cols-4">
|
<div className="grid gap-3 text-sm sm:grid-cols-4">
|
||||||
@@ -122,17 +136,20 @@ export function StoragePanel() {
|
|||||||
value={formatBytes(storage)}
|
value={formatBytes(storage)}
|
||||||
hint={`${percentOf(storage, total).toFixed(1)}%`}
|
hint={`${percentOf(storage, total).toFixed(1)}%`}
|
||||||
accent="text-primary"
|
accent="text-primary"
|
||||||
|
swatch={VOLUME_COLORS.ours}
|
||||||
/>
|
/>
|
||||||
<Metric
|
<Metric
|
||||||
label={t('admin.storage.foreign')}
|
label={t('admin.storage.foreign')}
|
||||||
value={formatBytes(foreign)}
|
value={formatBytes(foreign)}
|
||||||
hint={t('admin.storage.foreignHint')}
|
hint={t('admin.storage.foreignHint')}
|
||||||
|
swatch={VOLUME_COLORS.foreign}
|
||||||
/>
|
/>
|
||||||
<Metric
|
<Metric
|
||||||
label={t('admin.storage.free')}
|
label={t('admin.storage.free')}
|
||||||
value={formatBytes(free)}
|
value={formatBytes(free)}
|
||||||
hint={`${percentOf(free, total).toFixed(1)}%`}
|
hint={`${percentOf(free, total).toFixed(1)}%`}
|
||||||
accent={lowSpace ? 'text-destructive' : 'text-emerald-500'}
|
accent={lowSpace ? 'text-destructive' : 'text-emerald-500'}
|
||||||
|
swatch={lowSpace ? VOLUME_COLORS.lowFree : VOLUME_COLORS.free}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -196,10 +213,21 @@ function Metric({
|
|||||||
value,
|
value,
|
||||||
hint,
|
hint,
|
||||||
accent,
|
accent,
|
||||||
}: Readonly<{ label: string; value: string; hint?: string; accent?: string }>) {
|
swatch,
|
||||||
|
}: Readonly<{
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
hint?: string
|
||||||
|
accent?: string
|
||||||
|
/** Цвет доли на полосе тома: без него подпись и сегмент не связать глазами. */
|
||||||
|
swatch?: string
|
||||||
|
}>) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="text-xs uppercase tracking-wide text-muted-foreground">{label}</span>
|
<span className="flex items-center gap-1.5 text-xs uppercase tracking-wide text-muted-foreground">
|
||||||
|
{swatch && <span className={cn('h-2.5 w-2.5 shrink-0 rounded-sm', swatch)} />}
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
<span className={cn('font-mono text-lg tabular-nums', accent)}>{value}</span>
|
<span className={cn('font-mono text-lg tabular-nums', accent)}>{value}</span>
|
||||||
{hint && <span className="text-xs text-muted-foreground">{hint}</span>}
|
{hint && <span className="text-xs text-muted-foreground">{hint}</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user