Roadmap Lane Board
Now / Next / Later 三欄泳道路線圖,項目卡以 shared layout 動畫在欄間晉升搬移,支援唯讀展示與互動拖曳。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
176
260
<RoadmapLaneBoard />
安裝
npx shadcn@latest add https://webberui.com/r/roadmap-lane-board.json或在 components.json 設定 registries 後,改用 @webberui/roadmap-lane-board 安裝。
安裝依賴後,從 registry JSON(/r/roadmap-lane-board.json 的 files[0].content)複製 roadmap-lane-board.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
以資料驅動:lanes 定義由左至右的泳道,items 以 laneId 歸屬到各欄。開啟 interactive 即可拖曳與用箭頭按鈕搬移。
import { RoadmapLaneBoard, type RoadmapItem } from "@/components/ui/roadmap-lane-board";
const items: RoadmapItem[] = [
{ id: "auth", laneId: "now", title: "重構登入流程", tag: "Eng" },
{ id: "billing", laneId: "next", title: "用量計費", tag: "Growth" },
{ id: "ai", laneId: "later", title: "AI 週報摘要", tag: "AI" },
];
export function Roadmap() {
const [list, setList] = React.useState(items);
return (
<RoadmapLaneBoard items={list} onItemsChange={setList} interactive />
);
}唯讀展示只需省略 interactive 並傳入 defaultItems:
<RoadmapLaneBoard defaultItems={items} />自訂泳道與狀態色帶:
<RoadmapLaneBoard
lanes={[
{ id: "todo", label: "待辦", accent: "#64748b" },
{ id: "doing", label: "進行中", accent: "#f59e0b" },
{ id: "done", label: "已完成", accent: "#10b981" },
]}
defaultItems={items}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
lanes | RoadmapLane[] | Now / Next / Later | 泳道定義,決定欄的內容與由左至右順序 |
items | RoadmapItem[] | — | 受控:目前所有項目 |
defaultItems | RoadmapItem[] | [] | 非受控初始項目 |
onItemsChange | (items: RoadmapItem[]) => void | — | 項目集合變更時觸發 |
onItemMove | (itemId, fromLaneId, toLaneId) => void | — | 單一項目跨欄搬移時觸發(同欄重排不觸發) |
interactive | boolean | false | 開啟拖曳與晉升按鈕;false 為唯讀展示 |
laneMinWidth | number | 176 | 每欄最小寬度(px),欄過窄時整體水平捲動 |
laneMaxHeight | number | 260 | 各欄內容區最大高度(px),超過時該欄垂直捲動 |
className | string | — | 外層容器樣式 |
RoadmapLane
| 欄位 | 型別 | 說明 |
|---|---|---|
id | string | 泳道唯一 id,對應 RoadmapItem.laneId |
label | string | 欄首顯示名稱 |
accent | string | 狀態色帶顏色(任意 CSS 色值),省略時依欄序取預設色盤 |
RoadmapItem
| 欄位 | 型別 | 說明 |
|---|---|---|
id | string | 項目唯一 id,欄間搬移的 shared layout 配對鍵 |
laneId | string | 目前所在泳道 id |
title | string | 卡片標題 |
description | React.ReactNode | 卡片描述(選填) |
tag | string | 右上角小標籤(選填) |
細節
- 每張卡片帶有穩定的
layoutId,跨欄搬移時 Motion 以 shared layout 動畫從舊位置滑向新欄,其餘卡片同步 FLIP 補位。 - 拖曳採原生 HTML5 drag-and-drop:懸停時依指標 Y 座標算出欄內落點並高亮目標欄,放開後插入對應位置。
- 受控/非受控雙模式:傳入
items+onItemsChange由外部主導,或僅傳defaultItems交由元件內部管理。 - 各欄數量徽章即時反映該欄項目數;欄首色帶可依業務語意(狀態、優先級)自訂顏色。
可及性
- 每欄為獨立
section,aria-label帶欄名與項目數;欄內為role="list"、卡片為role="listitem"。 - 觸控與鍵盤使用者可用卡片上的箭頭按鈕晉升/延後項目,按鈕帶明確
aria-label(如「將『用量計費』移至 Later」),到達首/尾欄時自動停用。 - 跨欄搬移後以
aria-live="polite"宣告最新狀態,輔助科技即時朗讀。 - 使用者系統開啟「減少動態效果」時停用 shared layout 與 FLIP 動畫,搬移瞬間到位,功能不受影響。