Shove Reflow Grid
推擠重排網格:切換分類時新進卡片擠進隊伍推開鄰居再彈回,離場卡片被擠出邊緣翻轉掉落。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
128
<ShoveReflowGrid />
安裝
npx shadcn@latest add https://webberui.com/r/shove-reflow-grid.json或在 components.json 設定 registries 後,改用 @webberui/shove-reflow-grid 安裝。
安裝依賴後,從 registry JSON(/r/shove-reflow-grid.json 的 files[0].content)複製 shove-reflow-grid.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
import {
ShoveReflowGrid,
type ShoveReflowItem,
} from "@/components/ui/shove-reflow-grid";
const items: ShoveReflowItem[] = [
{ id: "apple", categories: ["水果"], content: <Card>🍎 蘋果</Card> },
{ id: "carrot", categories: ["蔬菜"], content: <Card>🥕 紅蘿蔔</Card> },
{ id: "cake", categories: ["甜點"], content: <Card>🍰 蛋糕</Card> },
];
<ShoveReflowGrid items={items} intensity="normal" />;未提供 filters 時,篩選列會自 items 的 categories 去重推導,並在最前面補上「全部」。
受控模式
想用自訂的分類切換 UI(例如頁籤或側欄),把 showFilterBar 關掉,改用 value 與 onValueChange 由外部驅動;ALL_CATEGORY 常數代表「顯示全部」:
import { ShoveReflowGrid, ALL_CATEGORY } from "@/components/ui/shove-reflow-grid";
const [category, setCategory] = React.useState(ALL_CATEGORY);
<ShoveReflowGrid
items={items}
showFilterBar={false}
value={category}
onValueChange={setCategory}
/>;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
items | ShoveReflowItem[] | — | 網格項目,每項含 id、categories?、content |
filters | ShoveReflowFilter[] | 自 items 推導 | 篩選選項;未提供時去重推導並前置「全部」 |
value | string | — | 受控:目前選中的分類值 |
defaultValue | string | ALL_CATEGORY | 非受控:初始選中的分類值 |
onValueChange | (value: string) => void | — | 選中分類變更時的回呼 |
intensity | "subtle" | "normal" | "vigorous" | "normal" | 推擠力道,影響回位彈性與掉落距離 |
minItemWidth | number | 128 | 卡片最小寬度(px),格線以 auto-fill 依此換行 |
showFilterBar | boolean | true | 是否顯示內建篩選列 |
allLabel | React.ReactNode | "全部" | 「全部」選項的文案 |
itemClassName | string | — | 每張卡片外層 li 的 className |
ShoveReflowItem 的 id 必須穩定且唯一——它是重排(FLIP)動畫配對前後位置的依據。
細節
- 推擠讓位:以 Motion 的
layoutFLIP 搭配AnimatePresence的popLayout模式。新進卡片以較小的縮放「擠進」格線,其餘卡片同步用 spring 讓出位置;阻尼偏低使其回位時略微過衝,形成「推開再彈回」的體感。 - 擠出掉落:離場卡片被
popLayout凍結在原位,接著以加速緩動往下墜、翻轉、縮小淡出,方向依id推導而不整齊劃一,像被人群擠出邊緣。 - 力道分級:
intensity一次調整回位 spring 的剛性/阻尼、新進起始縮放、離場掉落距離與翻轉角度。vigorous最彈跳誇張,subtle最克制。 - 響應式格線:以
grid-template-columns: repeat(auto-fill, minmax(minItemWidth, 1fr))自動換行,欄數隨容器寬度變化。
可及性
- 篩選列為
role="toolbar",各選項是原生<button>並以aria-pressed表達選中狀態。 - 篩選列採 roving tabindex:以 ← →(或 ↑ ↓)在選項間游走並即時套用,Home / End 跳至頭尾。
- 內建
aria-live="polite"狀態區,切換分類時朗讀目前顯示的項目數。 - 使用者系統開啟「減少動態效果」時,停用推擠、掉落與重排動畫,僅以極短的淡入淡出切換,不改動 DOM 結構。