メーターMeterBeta
容量、達成率、しきい値を値と最大値の関係で示すメーターです。
プレビュー
重量積載率2,100kg / 3,000kg(70%)
現在2,100kg
反映後2,450kg
状態とバリエーション
追加予定のプレビュー
incoming は未確定の追加量を斜線で重ね、反映後の超過も先に示します。
重量積載率2,100kg / 3,000kg(70%)
現在2,100kg
反映後2,450kg
目標値と充足率
direction を切り替えると、同じバーを稼働率や充足率にも使えます。
稼働率(目標 90%)86%
受注カバー率87%
予算消化¥2,340,000 / ¥3,000,000(78%)
中立ゲージ(満席率・稼働率)
時間で埋まっていくゲージ(満席率など)には direction="neutral"。判定色を付けず(primary 一定)、target は参照マーカーのみ。低い値でも赤にならないので、サービス開始直後の誤警報を防ぎます。(higher-is-better では 29% が赤くなってしまいます)
満席率(2 / 7卓)2卓 / 7卓(29%)
target=6 はマーカー。低い値でも赤くならない(primary 一定)。
表セル向け
size="inline" は一覧表の中で幅を取りすぎない薄い表示です。
| D-01 | |
|---|---|
| D-02 | |
| D-03 |
プロパティ
表は横にスクロールできます
| プロパティ | 型 | 初期値 | 説明 |
|---|---|---|---|
| value | number | - | 現在値です。 |
| max | number | 100 | 範囲の上限です。0 以下の値は安全な既定値に正規化されます。 |
| incoming | number | - | 追加予定分を斜線の上乗せとして表示し、反映後のトーン判定にも使います。 |
| thresholds | { warning?: number; over?: number } | { warning: 0.8, over: 1 } | higher-is-worse で warning / destructive に切り替える割合です。 |
| direction | "higher-is-worse" | "higher-is-better" | "fill-is-good" | "neutral" | "higher-is-worse" | トーンの意味論を選びます。higher-is-worse=満杯に近いほど warning/destructive。higher-is-better=target 以上が success・未達で赤。fill-is-good=max に近いほど success。neutral=判定色なし(primary 一定)+target はマーカーのみ(満席率・稼働率など時間で埋まるゲージ向け・低い値を赤にしない)。(#343) |
| target | number | - | 目標線です。higher-is-better ではトーン判定にも使います。neutral では参照マーカーのみ(色判定なし)。 |
| tone | SemanticTone | - | 自動判定を使わず正準トーンを直接指定します。default は muted の見た目に正規化されます。 |
| label | ReactNode | - | meter のアクセシブル名として使われるラベルです。 |
| valueText | string | - | 支援技術に読み上げる値の説明を上書きします。 |
| unit / formatValue | string / (value) => string | - | 表示値の単位または独自フォーマットです。 |
| size | "default" | "sm" | "inline" | "default" | 通常、やや小さめ、表セル向けインラインのサイズです。 |
| showValue | boolean | true | 数値の読み取り表示を出すかどうかです。 |
使い方
import * as React from "react";
import { Button, Meter } from "@gunjo/ui";
const MAX_WEIGHT = 3000;
const NEXT_WEIGHT = 350;
export function ShipmentCapacityMeter() {
const [loaded, setLoaded] = React.useState(2100);
const [previewIncoming, setPreviewIncoming] = React.useState(false);
const projected = Math.min(MAX_WEIGHT, loaded + NEXT_WEIGHT);
return (
<div className="flex w-full max-w-lg flex-col gap-5 rounded-lg border bg-card p-4">
<Meter
label="重量積載率"
value={loaded}
max={MAX_WEIGHT}
incoming={previewIncoming ? NEXT_WEIGHT : undefined}
thresholds={{ warning: 0.8, over: 1 }}
formatValue={(value) => value.toLocaleString("ja-JP") + "kg"}
/>
<div className="flex flex-wrap items-center gap-2">
<Button
type="button"
size="sm"
variant="outline"
onMouseEnter={() => setPreviewIncoming(true)}
onMouseLeave={() => setPreviewIncoming(false)}
onFocus={() => setPreviewIncoming(true)}
onBlur={() => setPreviewIncoming(false)}
onClick={() => {
setLoaded(projected);
setPreviewIncoming(false);
}}
>
次の出荷を積む(+350kg)
</Button>
<Button type="button" size="sm" variant="ghost" onClick={() => setLoaded(2100)}>
リセット
</Button>
</div>
<div className="grid grid-cols-2 gap-2 text-sm">
<div className="rounded-md border bg-muted/30 px-3 py-2">
<span className="text-muted-foreground">現在</span>
<span className="mt-1 block font-medium tabular-nums text-foreground">{loaded.toLocaleString("ja-JP")}kg</span>
</div>
<div className={previewIncoming ? "rounded-md border border-primary-border bg-primary-subtle px-3 py-2" : "rounded-md border bg-muted/30 px-3 py-2"}>
<span className={previewIncoming ? "text-primary-subtle-foreground/80" : "text-muted-foreground"}>反映後</span>
<span className={previewIncoming ? "mt-1 block font-medium tabular-nums text-primary-subtle-foreground" : "mt-1 block font-medium tabular-nums text-foreground"}>{projected.toLocaleString("ja-JP")}kg</span>
</div>
</div>
</div>
);
}