WebberUI

String Pinboard

紅線釘板版面:卡片像釘在軟木板上的線索,彼此以會自然下垂的紅線連接,拖動即時改變鬆緊與垂度。

載入預覽⋯

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

0.28
2
320
0.06
<StringPinboard />

安裝

npx shadcn@latest add https://webberui.com/r/string-pinboard.json

或在 components.json 設定 registries 後,改用 @webberui/string-pinboard 安裝。

安裝依賴後,從 registry JSON(/r/string-pinboard.jsonfiles[0].content)複製 string-pinboard.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion clsx tailwind-merge

使用

PinCard 擺放線索卡,connections 定義卡片之間的紅線。拖動任一張卡,所有相連的線會即時改變鬆緊與垂度。

import { StringPinboard, PinCard } from "@/components/ui/string-pinboard";

<StringPinboard
  connections={[
    ["idea", "design"],
    ["design", "ship"],
  ]}
  variant="cork"
  height={360}
>
  <PinCard id="idea" x={18} y={20} label="點子">
    構想
  </PinCard>
  <PinCard id="design" x={52} y={16} label="設計">
    設計
  </PinCard>
  <PinCard id="ship" x={76} y={64} label="上線">
    上線
  </PinCard>
</StringPinboard>;

connections 也可用物件形式覆寫單一條線的顏色:

connections={[{ from: "idea", to: "design", color: "#0ea5e9" }]}

Props

StringPinboard

Prop型別預設值說明
childrenReactNode放入 PinCard 子元件
connectionsPinConnection[][]卡片間的紅線;[from, to] 速記或 { from, to, color }
positionsRecord<string, PinPosition>受控座標,外部狀態同步進釘板(仍可拖曳)
defaultPositionsRecord<string, PinPosition>非受控初始座標,覆寫 PinCard 自身的 x/y
onPositionsChange(positions) => void任一卡片移動後觸發,帶入全部最新座標
variant"cork" | "paper" | "slate""cork"釘板表面款式
stringColorstring"#e11d48"紅線與圖釘顏色
sagnumber0.28線的鬆弛程度(0–1),越大垂得越深、拉遠時越快繃直
stringWidthnumber2線寬(px)
heightnumber400釘板高度(px)
staggernumber0.06卡片依序進場的間隔(秒)

PinCard

Prop型別預設值說明
idstring唯一識別 key,連線配對與座標記錄用
xnumber50初始 X 座標(百分比 0–100,指向卡片頂端圖釘)
ynumber50初始 Y 座標(百分比 0–100,指向卡片頂端圖釘)
labelstringid顯示名稱,用於無障礙標籤
childrenReactNode卡片內容

細節

  • 懸鏈線垂度:每條線在初次量測時記下「靜止繩長」(以釘板對角線比例儲存),繩長固定不隨拖曳改變。當兩卡拉遠、弦長接近繩長時,線逐漸繃直;靠近時垂度增大,貼近真實掛繩的手感。
  • 座標系統:卡片座標以百分比(0–100)表示,指向卡片頂端的圖釘;卡片自圖釘垂掛而下,因此縮放釘板時版面等比例維持。
  • 受控/非受控雙模式:不傳 positions 時由元件自行記錄座標;傳入 positions 則外部可驅動版面,拖曳與鍵盤移動仍會即時反映並透過 onPositionsChange 回報。
  • 表面款式cork 軟木斑點、paper 方格紙格線、slate 純石板,皆自適應深淺色。

可及性

  • 每張卡片為 role="button"、可鍵盤聚焦,帶 aria-roledescription="可拖曳的線索卡" 與含連線數的 aria-label
  • 聚焦卡片後,方向鍵可上下左右微調位置,移動同樣透過 onPositionsChange 回報。
  • 使用者系統開啟「減少動態效果」時,停用卡片進場與紅線描繪動畫,直接呈現最終版面;拖曳與鍵盤操作不受影響。
  • 拖曳以指標事件實作並支援觸控(touch-action: none),監聽器、requestAnimationFrameResizeObserver 於卸載時全數清理。

On this page