レベル表示StatusLevelExperimental

軽い順に並べた段の定義から、いまが何段目かをチップと段バーで示す順序つきレベル表示です。

プレビュー

迂回4段階中 3段目色を落として見る

彩度をゼロにしても、段バーで何段目かが数えられます。

品98
品川駅港南口〜大井競馬場前

一部区間運休

運休4段階中 4段目
宿75
新宿駅西口〜三宅坂

迂回運行

迂回4段階中 3段目
都06
渋谷駅前〜新橋駅前

約15分遅れ

遅延4段階中 2段目
東22
東京駅丸の内北口〜錦糸町駅前

平常運転

平常運転4段階中 1段目

全体の運行状況は「運休」です(いちばん重い段に合わせています)。 重い順に並べ替えています。

状態とバリエーション

段のすべて

3段の混雑と4段の運行状況。段バーが同じ規則で伸びます。

空いています3段階中 1段目やや混雑3段階中 2段目混雑3段階中 3段目
平常運転4段階中 1段目遅延4段階中 2段目迂回4段階中 3段目運休4段階中 4段目

色を落としたとき

彩度をゼロにしても段バーは数えられます。読み上げには「4段階中 3段目」が乗ります。

平常運転4段階中 1段目遅延4段階中 2段目迂回4段階中 3段目運休4段階中 4段目

大きさと並び

sm / default / lg と、段バーの位置、バーだけの密な形です。

遅延4段階中 2段目遅延4段階中 2段目遅延4段階中 2段目
運休4段階中 4段目運休4段階中 4段目

プロパティ

表は横にスクロールできます
プロパティ初期値説明
levelsreadonly StatusLevelStep[]-段の定義です。軽い順(低い端から高い端へ)に並べます。index 0 が1段目になります。
valuestring | null | undefined-いまの段です。levels に無い値のときは unknownLabel を表示します。
showBarbooleantrue段バーを表示します。色を落としても段が読めるのはこのバーです。
showLabelbooleantrue文字チップを表示します。false にするとバーだけになり、ラベルは読み上げにだけ残ります。
barPosition"start" | "end""end"段バーをチップの前後どちらに置くかです。
size"sm" | "default" | "lg""default"チップと段バーの大きさです。
formatPosition(position, total, step) => string-読み上げ用の位置の言い回しです。既定は「4段階中 3段目」です。
unknownLabelReactNode"—"value が段に無いときの表示です。
chipClassNamestring-チップだけに足す className です。一覧の幅そろえに使います。
barClassNamestring-段バーだけに足す className です。
statusLevelIndex(levels, value) => number-いま何番目かを返す純関数です(見つからないときは -1)。
compareStatusLevel(levels, a, b) => number-並べ替え用の比較関数です。軽い順に並びます。
highestStatusLevel(levels, values) => value | undefined-いちばん重い段を返します。全体の状況の導出に使います。
statusLevelStep(levels, value) => StatusLevelStep | undefined-その段の定義そのものを返します。tone やラベルを引くのに使います。

使い方

import {
  ListCard,
  StatusLevel,
  compareStatusLevel,
  highestStatusLevel,
} from "@gunjo/ui";

// 段は軽い順に1回だけ書く。並べ替えも全体の導出もこの配列から引く。
const SERVICE_LEVELS = [
  { value: "normal", label: "平常運転", tone: "success" },
  { value: "delay", label: "遅延", tone: "warning" },
  { value: "detour", label: "迂回", tone: "warning" },
  { value: "suspended", label: "運休", tone: "destructive" },
] as const;

const ROUTES = [
  { id: "ke96", name: "品96 品川駅港南口〜八潮パークタウン", note: "平常どおり", level: "normal" },
  { id: "ke98", name: "品98 品川駅港南口〜大井競馬場前", note: "工事のため迂回", level: "detour" },
  { id: "ke99", name: "品99 品川駅高輪口〜五反田駅", note: "終日運休", level: "suspended" },
] as const;

export function ServiceStatusList() {
  // 重い順(運休が上)。
  const sorted = [...ROUTES].sort((a, b) =>
    compareStatusLevel(SERVICE_LEVELS, b.level, a.level),
  );
  // 全体の運行状況=いちばん重い段。
  const overall = highestStatusLevel(SERVICE_LEVELS, ROUTES.map((r) => r.level));

  return (
    <div className="flex flex-col gap-2">
      {sorted.map((route) => (
        <ListCard
          key={route.id}
          title={route.name}
          description={route.note}
          status={<StatusLevel levels={SERVICE_LEVELS} value={route.level} />}
        />
      ))}
      <p>全体:<StatusLevel levels={SERVICE_LEVELS} value={overall} /></p>
    </div>
  );
}

設計の判断

  • 色だけで段を伝えない。段バーを常に出しているのは、彩度をゼロにしても 「何段目か」を数えられるようにするためです。コールドテストで繰り返し出た粗さで、 UIXHERO の記事にも実例が載っています。
    AIが作った画面で、実際に起きた29の失敗 — 28. 状態の違いが、色だけに乗る
  • 段の定義は1か所だけに書く。並べ替えも「いちばん重い段」の導出も同じ 配列から引きます。定義が2か所に分かれると、片方が必ず古くなります。
  • 色に頼らない伝え方の考え方は資料にまとめてあります。
    色だけに頼らない

使用コンポーネント