71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
|
|
import { useState } from "react";
|
|||
|
|
import { useI18n } from "../../i18n/I18nContext";
|
|||
|
|
import { METER_META, type UsageMeter } from "../../lib/usageMeter";
|
|||
|
|
|
|||
|
|
export type MeterBarRow = {
|
|||
|
|
meter: UsageMeter;
|
|||
|
|
credits: number;
|
|||
|
|
count: number;
|
|||
|
|
soft_cap: number;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
type Props = {
|
|||
|
|
rows: MeterBarRow[];
|
|||
|
|
unlimited?: boolean;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 分項用量:條長=相對自己上限;hover 才露出點數。
|
|||
|
|
*/
|
|||
|
|
export function UsageMeterBars({ rows, unlimited = false }: Props) {
|
|||
|
|
const { t } = useI18n();
|
|||
|
|
const [active, setActive] = useState<UsageMeter | null>(null);
|
|||
|
|
const ordered = rows
|
|||
|
|
.slice()
|
|||
|
|
.sort((a, b) => METER_META[a.meter].order - METER_META[b.meter].order);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<ul className="hb-usage-mbars" onMouseLeave={() => setActive(null)}>
|
|||
|
|
{ordered.map((row) => {
|
|||
|
|
const label = t(`usage.meter.${row.meter}`);
|
|||
|
|
const pct =
|
|||
|
|
row.soft_cap > 0
|
|||
|
|
? Math.min(100, Math.round((row.count / row.soft_cap) * 100))
|
|||
|
|
: 0;
|
|||
|
|
const on = active === row.meter;
|
|||
|
|
const over = row.soft_cap > 0 && row.count > row.soft_cap;
|
|||
|
|
return (
|
|||
|
|
<li key={row.meter}>
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
className={`hb-usage-mbar${on ? " is-on" : ""}${over ? " is-over" : ""}`}
|
|||
|
|
onMouseEnter={() => setActive(row.meter)}
|
|||
|
|
onFocus={() => setActive(row.meter)}
|
|||
|
|
aria-label={t("usage.meter.barAria", {
|
|||
|
|
label,
|
|||
|
|
count: row.count,
|
|||
|
|
credits: row.credits,
|
|||
|
|
cap: row.soft_cap,
|
|||
|
|
})}
|
|||
|
|
>
|
|||
|
|
<span className="hb-usage-mbar__label">{label}</span>
|
|||
|
|
<span className="hb-usage-mbar__track" aria-hidden>
|
|||
|
|
<span
|
|||
|
|
className="hb-usage-mbar__fill"
|
|||
|
|
style={{ width: `${Math.max(pct, row.count > 0 ? 3 : 0)}%` }}
|
|||
|
|
/>
|
|||
|
|
</span>
|
|||
|
|
<span className="hb-usage-mbar__val">
|
|||
|
|
<strong>{row.count}</strong>
|
|||
|
|
<span className="text-muted">/{row.soft_cap}</span>
|
|||
|
|
{on ? <span className="hb-usage-mbar__pts">{row.credits}pt</span> : null}
|
|||
|
|
{unlimited && over ? <span className="hb-usage-mbar__inf">∞</span> : null}
|
|||
|
|
</span>
|
|||
|
|
</button>
|
|||
|
|
</li>
|
|||
|
|
);
|
|||
|
|
})}
|
|||
|
|
</ul>
|
|||
|
|
);
|
|||
|
|
}
|