// Battery Bar // Purpose: Defines the Battery Bar module and the local helpers/components used in this file. // Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit. import React from 'react'; import './styles.css'; import { WARN_DISPLAY_PERCENT } from '../../lib/battery.js'; const WARN_FLASH_MS = 1600; const URGENT_FLASH_MS = 800; function classNames(...values) { return values.filter(Boolean).join(' '); } function BatteryBar({ visual, orientation = 'horizontal', variant = 'inline', compact = false, showLabel, className = '', }) { const isBackground = variant === 'background'; const isVertical = orientation === 'vertical'; const resolvedShowLabel = showLabel ?? !isBackground; if (!visual?.available) { if (isBackground) { const warnPercent = WARN_DISPLAY_PERCENT; const markerClass = classNames( 'absolute z-10', isVertical ? 'left-0 right-0 h-[2px]' : 'top-0 bottom-0 w-[2px]', 'bg-black/70', ); const markerStyle = isVertical ? { bottom: `${warnPercent}%` } : { left: `${warnPercent}%` }; const containerClass = classNames('absolute inset-0 pointer-events-none', className); return (
); } if (isVertical) return null; return

Battery telemetry unavailable

; } const percentDisplay = visual.percentDisplay ?? 0; const percent = Math.max(0, Math.min(100, percentDisplay)); const warnPercent = visual.warnDisplayPercent ?? WARN_DISPLAY_PERCENT; const isUrgent = Boolean(visual.urgentActive); const isWarn = Boolean(visual.warnActive); const animationDuration = isUrgent ? `${URGENT_FLASH_MS}ms` : isWarn ? `${WARN_FLASH_MS}ms` : undefined; const fillColor = isUrgent || isWarn ? 'bg-red-500' : 'bg-emerald-500'; const fillTone = isBackground ? isUrgent || isWarn ? 'bg-red-500/45' : 'bg-emerald-500/45' : fillColor; const baseTone = isUrgent || isWarn ? 'bg-red-900/35' : 'bg-slate-900/35'; const baseToneStrong = isUrgent || isWarn ? 'bg-red-900/45' : 'bg-slate-900/35'; const containerClass = classNames( isBackground ? 'absolute inset-0 pointer-events-none' : 'relative pointer-events-auto', isVertical && !isBackground ? 'flex flex-col items-center justify-center' : 'w-full', className, ); if (isBackground) { const fillStyle = isVertical ? { height: `${percent}%` } : { width: `${percent}%` }; const fillClass = classNames( 'absolute', isVertical ? 'bottom-0 left-0 right-0' : 'left-0 top-0 bottom-0', fillTone, 'transition-[width,height]', isUrgent && 'battery-urgent-flash', ); const markerClass = classNames( 'absolute z-10', isVertical ? 'left-0 right-0 h-[2px]' : 'top-0 bottom-0 w-[2px]', 'bg-black/70', isWarn && !isUrgent && 'battery-tick-warn', ); const markerStyle = isVertical ? { bottom: `${warnPercent}%` } : { left: `${warnPercent}%` }; return (
); } const barShellClass = classNames( 'relative overflow-hidden rounded-sm border border-slate-800', isUrgent || isWarn ? baseToneStrong : 'bg-zinc-900/90', isUrgent && 'battery-urgent-flash', isVertical ? 'w-full flex-1' : compact ? 'h-4' : 'h-5', ); const fillStyle = isVertical ? { height: `${percent}%` } : { width: `${percent}%` }; const fillClass = classNames( 'absolute', isVertical ? 'bottom-0 left-0 right-0' : 'left-0 top-0 bottom-0', fillTone, 'transition-[width,height]', isUrgent && 'battery-urgent-flash', ); const markerClass = classNames( 'absolute z-10', isVertical ? 'left-0 right-0 h-[2px]' : 'top-0 bottom-0 w-[2px]', 'bg-black/70', isWarn && !isUrgent && 'battery-tick-warn', ); const markerStyle = isVertical ? { bottom: `${warnPercent}%` } : { left: `${warnPercent}%` }; const labelText = `Battery ${percentDisplay}%`; const labelClass = classNames( 'pointer-events-none absolute inset-0 flex items-center justify-center text-center font-semibold', isVertical ? (compact ? 'text-[0.55rem]' : 'text-[0.65rem]') : 'text-xs', ); const labelPillClass = classNames( 'rounded px-1.5 py-0.5', isBackground ? 'bg-black/30 text-slate-100' : 'bg-black/50 text-slate-100', ); return (
{resolvedShowLabel && !isVertical ? (
{labelText}
) : null} {resolvedShowLabel && isVertical ? (
{percentDisplay}%
) : null}
); } export default React.memo(BatteryBar);