互動編排

便利貼牆

軟木板上的彩色便利貼:拖曳、新增、揉掉刪除。

這是 WebberUI Pro 元件

下方的安裝指令需要授權才能執行,未授權時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

一面 CSS 紋理軟木板,貼滿帶著微旋轉與膠帶/圖釘裝飾的彩色便利貼:可自由拖曳移動、雙擊編輯文字、按下 ✕ 便揉成一團紙球丟出,右下角一鍵新增自動配色的新便利貼,適合待辦牆、腦力激盪板等趣味場景。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add "https://webberui.com/r/sticky-note-board.json?t=<安裝 token>"

還沒有專案,或安裝遇到問題?看快速開始 →

Sticky Note Board 是用 React、Motion 與 Tailwind CSS 做的便利貼牆(便條紙看板)元件:便利貼可拖曳、雙擊編輯、新增與刪除,每次異動都會經 onChange 回傳完整陣列,方便你自行儲存。

Playground

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

300
編輯中的呼叫預設屬性
<StickyNoteBoard/>

完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。

適用情境

  • 團隊工具裡的腦力激盪板、回顧會議(retro)看板
  • 個人儀表板上的待辦或備忘區
  • 活動頁、課程平台讓使用者留言的互動牆
  • 產品導覽或作品集中,用來展示「可以動手玩」的互動區塊
  • 不適合:需要欄位分組與排序的工作流程,例如依狀態分欄的任務看板,請改用專門的看板或清單元件

安裝

npx webberui@latest add "https://webberui.com/r/sticky-note-board.json?t=<安裝 token>"

或在 components.json 設定 registries 後,改用 @webberui/sticky-note-board 安裝。

使用

import { StickyNoteBoard, type StickyNote } from "@/components/ui/sticky-note-board";

const notes: StickyNote[] = [
  { id: "milk", text: "下班買牛奶 🥛", x: 24, y: 22, color: 0 },
  { id: "gym", text: "週三 19:00 健身房", x: 200, y: 120, color: 2 },
];

<StickyNoteBoard notes={notes} height={400} onChange={(next) => console.log(next)} />

範例

存到 localStorage

notes 只作為初始資料,所以先在 client 讀出儲存的內容,再渲染看板;之後每次 onChange 都寫回去。

"use client";

import * as React from "react";
import { StickyNoteBoard, type StickyNote } from "@/components/ui/sticky-note-board";

const KEY = "my-board-notes";

export function PersistentBoard() {
  const [initial, setInitial] = React.useState<StickyNote[] | null>(null);

  React.useEffect(() => {
    const saved = localStorage.getItem(KEY);
    setInitial(saved ? (JSON.parse(saved) as StickyNote[]) : []);
  }, []);

  if (!initial) return null;

  return (
    <StickyNoteBoard
      notes={initial}
      onChange={(next) => localStorage.setItem(KEY, JSON.stringify(next))}
    />
  );
}

同步到後端

拖曳結束、編輯提交、新增或刪除都會觸發 onChange,可以直接把最新陣列送到 API。

<StickyNoteBoard
  notes={notesFromServer}
  height={480}
  addLabel="新增想法"
  onChange={(next) => {
    void fetch("/api/boards/retro-42", {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ notes: next }),
    });
  }}
/>

固定顏色、隱藏新增按鈕

用 color 指定色票索引(0–5 依序為黃、粉、綠、藍、橘、紫),並以 showAdd={false} 隱藏新增按鈕,只讓使用者整理既有的便利貼。

const notes: StickyNote[] = [
  { id: "bug", text: "登入頁在 Safari 跑版", x: 24, y: 24, color: 1 },
  { id: "idea", text: "結帳頁加上進度條", x: 200, y: 40, color: 2 },
  { id: "todo", text: "週五前更新文件", x: 110, y: 170, color: 3 },
];

<StickyNoteBoard notes={notes} showAdd={false} height={320} />

Props

Prop型別預設值說明
notesStickyNote[][]初始便利貼資料;之後由元件內部管理,異動經 onChange 回報
onChange(notes: StickyNote[]) => void—拖曳、編輯、新增、揉掉任一異動後,回傳最新的便利貼陣列
heightnumber380軟木板高度(px)
addLabelstring"新增便利貼"新增按鈕的文字
showAddbooleantrue是否顯示右下角的新增按鈕
classNamestring—透傳到最外層(木框)容器

StickyNote 型別

欄位型別說明
idstring唯一識別碼,同時作為旋轉角度與圖釘/膠帶樣式的決定性雜湊種子
textstring便利貼上的文字內容
xnumber相對板面左上角的 X 座標(px)
ynumber相對板面左上角的 Y 座標(px)
colornumber色票索引 0–5(黃/粉/綠/藍/橘/紫),未指定時依 id 雜湊自動配色

可及性

  • 每張便利貼皆可鍵盤聚焦:Enter 進入編輯、方向鍵移動(Shift 加速)、Delete / Backspace 揉掉刪除
  • 便利貼以 role="group" 搭配 aria-label 朗讀內容,揉掉按鈕有明確的 aria-label
  • 編輯中 Esc 取消還原原文、Cmd/Ctrl + Enter 或失焦即提交
  • 使用者系統開啟「減少動態效果」時,進場、拖起放大與揉紙動畫皆改為單純淡入淡出
  • 旋轉角度與裝飾樣式由 id 決定性雜湊產生,SSR 與 client 渲染結果一致

常見問題

可以在 Next.js App Router 使用嗎?

可以。元件檔案開頭已有 "use client",在 Server Component 頁面中照常 import 即可。便利貼的旋轉角度與裝飾由 id 決定性雜湊產生,SSR 與 client 渲染結果一致。

為什麼改了 notes 之後畫面沒有更新?

notes 只在第一次渲染時讀入,之後由元件內部管理狀態。要整個換成新資料,請改變元件的 key 讓它重新掛載。

便利貼的位置怎麼存?

每張便利貼的 x、y 是相對板面左上角的像素座標,拖曳結束或用方向鍵移動後會四捨五入成整數,透過 onChange 回傳。把回傳的陣列原樣存起來,下次當成 notes 傳入即可還原。

可以用鍵盤操作嗎?

可以。每張便利貼都能聚焦:Enter 編輯、方向鍵移動(按住 Shift 每次移動較多)、Delete 或 Backspace 刪除;編輯時 Esc 取消、Cmd/Ctrl + Enter 提交。

需要哪些依賴?

motion、lucide-react、clsx 與 tailwind-merge。軟木板紋理是純 CSS 漸層,不需要圖片檔。