|
| 1 | +import React, { useEffect, useState } from "react"; |
| 2 | +import styles from "./BatteryWidget.css"; |
| 3 | + |
| 4 | +export default function BatteryWidget() { |
| 5 | + const [percent, setPercent] = useState<number | null>(null); |
| 6 | + |
| 7 | + useEffect(() => { |
| 8 | + let cleanup: (() => void) | undefined; |
| 9 | + |
| 10 | + (async () => { |
| 11 | + const nav: any = navigator as any; |
| 12 | + if (!nav.getBattery) return; // non-Chromium browsers: show "—%" |
| 13 | + |
| 14 | + const mgr = await nav.getBattery(); |
| 15 | + |
| 16 | + const update = () => { |
| 17 | + const lvl = typeof mgr.level === "number" ? mgr.level : 0; // 0..1 |
| 18 | + setPercent(Math.round(lvl * 100)); |
| 19 | + }; |
| 20 | + |
| 21 | + update(); // initial value |
| 22 | + mgr.addEventListener("levelchange", update); |
| 23 | + |
| 24 | + cleanup = () => { |
| 25 | + mgr.removeEventListener("levelchange", update); |
| 26 | + }; |
| 27 | + })(); |
| 28 | + |
| 29 | + return () => cleanup?.(); |
| 30 | + }, []); |
| 31 | + |
| 32 | + const clamped = percent == null ? 0 : Math.max(0, Math.min(100, percent)); |
| 33 | + const tier = percent == null ? "neutral" : percent <= 20 ? "low" : "ok"; |
| 34 | + |
| 35 | + const INNER_MAX = 40; |
| 36 | + const innerWidth = Math.round((clamped / 100) * INNER_MAX); |
| 37 | + |
| 38 | + return ( |
| 39 | + <div className={`${styles.row} ${styles[tier]}`}> |
| 40 | + <span className={styles.value}> |
| 41 | + {percent != null ? `${percent}%` : "—%"} |
| 42 | + </span> |
| 43 | + |
| 44 | + <svg className={styles.icon} viewBox="0 0 52 24" aria-hidden="true"> |
| 45 | + {/* outline */} |
| 46 | + <rect |
| 47 | + x="1" |
| 48 | + y="3" |
| 49 | + width="44" |
| 50 | + height="18" |
| 51 | + rx="5" |
| 52 | + className={styles.body} |
| 53 | + /> |
| 54 | + {/* cap */} |
| 55 | + <rect x="46" y="8" width="5" height="8" rx="2" className={styles.cap} /> |
| 56 | + {/* fill */} |
| 57 | + <rect |
| 58 | + x="3" |
| 59 | + y="5" |
| 60 | + width={innerWidth} |
| 61 | + height="14" |
| 62 | + rx="4" |
| 63 | + className={styles.level} |
| 64 | + /> |
| 65 | + </svg> |
| 66 | + </div> |
| 67 | + ); |
| 68 | +} |
0 commit comments