座席表SeatMapExperimental
The 2-D selectable seat grid: rows × columns with aisle gaps (columns array with nulls for the aisle — 3-3 / 2-4-2 / 2-2 cabins), per-seat state (空席 / 予約済 / 確保中 / 選択中) and type (非常口 / 足元ゆったり / プレミアム / 窓側 / 通路側), controlled multi-select with a capacity cap (maxSelectable), and grid a11y: role=grid, arrow-key roving focus, aria-selected / aria-disabled, composed seat names (『12番A席、窓側、非常口座席、空席、¥1,500』). The seat/spot picker every booking flow needs — airline / 新幹線 seats, cinema, stadium, event hall, restaurant tables. State never rides on colour alone (selected shows a check, occupied an ×, plus an sr-only label). Owns its horizontal scroll so a wide cabin doesn't push the page on a phone. Controlled — pass selectedIds + onToggle. For a read-only intensity grid use HeatmapChart; this is the interactive picker.
プレビュー
Props
| プロパティ | 型 | 初期値 | 説明 |
|---|---|---|---|
| columns | (string | null)[] | - | 列IDの並び。null は通路(3-3 / 2-4-2 / 2-2)。 |
| seats | Seat[] | - | 座席。各 { id, row, col, state?, type?, position?, fee? }。 |
| Seat.state | "available" | "occupied" | "held" | "blocked" | "available" | 空席状況。selected は selectedIds から導出。blocked は空きスペース。 |
| Seat.type | string | - | 種別(非常口 / 足元ゆったり / プレミアム)。読み上げ名+特別席の見た目に反映。 |
| Seat.position | "window" | "aisle" | "middle" | - | 窓側 / 通路側。読み上げ名に反映。 |
| Seat.fee | number | - | 座席指定料。読み上げ名に反映。 |
| selectedIds | string[] | - | 選択中の座席ID(controlled)。 |
| maxSelectable | number | - | 選択上限。到達すると未選択席は操作不可に。 |
| onToggle | (seatId: string) => void | - | 座席のトグル(操作可能な空席のみ発火)。 |
| formatFee | (fee: number) => string | ¥1,500 | 読み上げ名の料金整形。 |
| showHeaders | boolean | true | 列ヘッダ+行番号の表示。 |
| hideLegend | boolean | - | 凡例を隠す。 |
Usage
import { SeatMap, type Seat } from "@gunjo/ui"
const columns = ["A", "B", "C", null, "D", "E", "F"] // null = 通路
<SeatMap
columns={columns}
seats={[
{ id: "12A", row: 12, col: "A", position: "window", type: "非常口座席", fee: 1500 },
{ id: "12B", row: 12, col: "B", state: "occupied" },
// …
]}
selectedIds={selected}
maxSelectable={2}
onToggle={(id) => toggle(id)}
/>