WebberUI

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.jsonfiles[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 時,篩選列會自 itemscategories 去重推導,並在最前面補上「全部」。

受控模式

想用自訂的分類切換 UI(例如頁籤或側欄),把 showFilterBar 關掉,改用 valueonValueChange 由外部驅動;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型別預設值說明
itemsShoveReflowItem[]網格項目,每項含 idcategories?content
filtersShoveReflowFilter[]items 推導篩選選項;未提供時去重推導並前置「全部」
valuestring受控:目前選中的分類值
defaultValuestringALL_CATEGORY非受控:初始選中的分類值
onValueChange(value: string) => void選中分類變更時的回呼
intensity"subtle" | "normal" | "vigorous""normal"推擠力道,影響回位彈性與掉落距離
minItemWidthnumber128卡片最小寬度(px),格線以 auto-fill 依此換行
showFilterBarbooleantrue是否顯示內建篩選列
allLabelReact.ReactNode"全部"「全部」選項的文案
itemClassNamestring每張卡片外層 li 的 className

ShoveReflowItemid 必須穩定且唯一——它是重排(FLIP)動畫配對前後位置的依據。

細節

  • 推擠讓位:以 Motion 的 layout FLIP 搭配 AnimatePresencepopLayout 模式。新進卡片以較小的縮放「擠進」格線,其餘卡片同步用 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 結構。

On this page