メーター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
45%
D-02
90%
D-03
102%↑

プロパティ

表は横にスクロールできます
プロパティ初期値説明
valuenumber-現在値です。
maxnumber100範囲の上限です。0 以下の値は安全な既定値に正規化されます。
incomingnumber-追加予定分を斜線の上乗せとして表示し、反映後のトーン判定にも使います。
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)
targetnumber-目標線です。higher-is-better ではトーン判定にも使います。neutral では参照マーカーのみ(色判定なし)。
toneSemanticTone-自動判定を使わず正準トーンを直接指定します。default は muted の見た目に正規化されます。
labelReactNode-meter のアクセシブル名として使われるラベルです。
valueTextstring-支援技術に読み上げる値の説明を上書きします。
unit / formatValuestring / (value) => string-表示値の単位または独自フォーマットです。
size"default" | "sm" | "inline""default"通常、やや小さめ、表セル向けインラインのサイズです。
showValuebooleantrue数値の読み取り表示を出すかどうかです。

使い方

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>
  );
}

使用コンポーネント