資料視覺敘事

試算表網格

可編輯的試算表網格:方向鍵移動選取、雙擊或 Enter 編輯、公式列顯示原始內容、支援簡易公式,首列首欄凍結。

這是 WebberUI Pro 元件

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

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

一張可以直接打字的小型試算表:方向鍵移動、Shift+方向鍵框選、雙擊或 Enter 編輯,頂部公式列顯示作用格的原始內容(公式就顯示 =B2*C2,而不是結果)。內建一個自寫的小公式引擎,支援四則運算、儲存格參照、範圍與 SUM/AVERAGE/MIN/MAX/COUNT,並以依賴圖只重算受影響的格。選取框以 spring 滑到新位置,提交後該格與所有依賴它的公式格一起閃一下,改一個數字就看得出連動到哪裡。注音、倉頡等輸入法的第一鍵也能直接開始編輯,不會遺失。

把這個效果裝進你的專案

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

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

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

Playground

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

20
8
0
600
編輯中的呼叫預設屬性
<SpreadsheetGrid/>

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

安裝

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

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

使用

import * as React from "react";
import { SpreadsheetGrid } from "@/components/ui/spreadsheet-grid";

export function Budget() {
  const [cells, setCells] = React.useState<Record<string, string>>({
    A1: "品項",
    B1: "數量",
    C1: "單價",
    D1: "小計",
    A2: "便當",
    B2: "32",
    C2: "110",
    D2: "=B2*C2",
    A3: "合計",
    D3: "=SUM(D2:D2)",
  });

  return (
    <SpreadsheetGrid
      rows={20}
      cols={6}
      value={cells}
      onChange={setCells}
      columnWidths={{ A: 140 }}
      frozenRows={1}
      frozenCols={1}
      onSelectionChange={(s) => console.log(s.range, s.display)}
    />
  );
}

Props

Prop型別預設值說明
rowsnumber20資料列數(不含欄標頭)
colsnumber8欄數,欄名依序為 A、B、C…Z、AA
valueRecord<string, string>—受控的儲存格內容;鍵為 A1 位址、值為原始輸入,以 = 開頭為公式
defaultValueRecord<string, string>—非受控模式的初始內容
onChange(value: Record<string, string>) => void—任何儲存格變動時回呼,參數為整份新內容;清成空字串的格會被移除
columnWidthsRecord<string, number>—各欄寬度(px),鍵為欄字母;未指定的欄為 96。拖曳欄標頭右緣可調整(40–480)
onColumnResize(column: string, width: number) => void—拖曳調整欄寬、放開時回呼
onSelectionChange(selection: SpreadsheetSelection) => void—作用格或選取範圍改變時回呼
frozenRowsnumber1除欄標頭外,前 N 列資料捲動時固定在上方
frozenColsnumber0除列號外,前 N 欄資料捲動時固定在左側
readOnlybooleanfalse唯讀:可選取與複製,不能編輯、清除或貼上
formatNumber(value: number) => stringzh-TW 千分位數字顯示格式;預設最多 10 位小數
maxHeightnumber360網格捲動區域的最大高度(px)
labelstring"試算表"網格的無障礙名稱
classNamestring—併入最外層容器

SpreadsheetSelection

欄位型別預設值說明
activestring—作用格位址,例如 B3
rangestring—選取範圍,例如 B3:D5;單格時同 active
rawstring—作用格的原始輸入
displaystring—作用格的顯示值(公式為計算結果或錯誤代碼)

另外具名匯出 SpreadsheetCellValue、SpreadsheetErrorCode 與 SpreadsheetGridProps 型別。

CSS 變數

欄位型別預設值說明
--wb-spreadsheet-grid-stiffnessnumber600選取框 spring 的 stiffness(damping 固定 40)
--wb-spreadsheet-grid-flashtime400ms提交後強調色閃爍的淡出時間
--wb-spreadsheet-grid-accentcolor#2563eb選取框與閃爍的強調色

細節

  • 公式:以 = 開頭。支援數字、儲存格參照($ 絕對參照記號可寫但會被忽略)、範圍 A1:B3、+ - * /、括號與負號,以及 SUM、AVERAGE、MIN、MAX、COUNT(不分大小寫,參數以逗號分隔)。範圍內的文字與空白會被略過;在四則運算中,空白視為 0、文字回 #VALUE!
  • 錯誤代碼:參照超出網格為 #REF!、除以零為 #DIV/0!、未知函式或名稱為 #NAME?、循環參照為 #CYCLE!、語法錯誤為 #ERROR!;錯誤會往依賴它的格傳遞,並以 rose 色顯示
  • 只重算受影響的格:元件維護一張依賴圖,內容改變時只重算被改的格與遞移依賴它們的公式;rows/cols 改變時才整張重建。解析結果依原始字串快取
  • 鍵盤:方向鍵移動、Shift+方向鍵延伸範圍、Ctrl/⌘+方向鍵跳到邊界、Home/End 到列首尾;Tab/Shift+Tab 左右移;Enter 開始編輯、編輯中 Enter 提交後下移(Shift+Enter 上移);F2 編輯;直接打字覆蓋原內容;Esc 取消;Delete/Backspace 清空選取範圍;Ctrl/⌘+A 全選
  • 複製貼上:Ctrl/⌘+C 以 TSV 複製選取範圍的原始內容(公式保留為公式文字,不做相對參照位移);Ctrl/⌘+V 從選取範圍左上角貼上 TSV,超出網格的部分會被截掉。使用 copy/paste 事件的 clipboardData,不需要剪貼簿權限
  • 滑鼠:點擊選格、拖曳或 Shift+點擊框選、雙擊編輯;點欄標頭選整欄、點列號選整列、點左上角全選
  • 凍結:欄標頭與列號永遠固定;frozenRows/frozenCols 再額外固定前 N 列/欄資料
  • 效能:所有儲存格直接渲染,rows × cols 建議在 2,000 格以內;更大的資料請在外層分頁,元件不做虛擬捲動
  • columnWidths 同步:拖曳後的寬度保留在元件內部;傳入新的 columnWidths 物件時會覆蓋回 prop 值,因此請用 state 或常數保存,不要每次 render 建立新物件

可及性

  • 網格為 role="grid",帶 aria-rowcount/aria-colcount/aria-multiselectable;每列 role="row"、欄標頭 role="columnheader"、列號 role="rowheader",儲存格 role="gridcell" 並帶 aria-rowindex、aria-colindex 與 aria-selected
  • roving tabindex:只有作用格 tabIndex=0,Tab 進入網格即落在作用格;焦點實際交給作用格內的隱藏輸入框,aria-label 會讀出「儲存格 B3:值」
  • 公式列輸入框的無障礙名稱為「儲存格 B3 內容」,在第一欄按 Shift+Tab 會回到公式列,在最後一欄按 Tab 會離開網格
  • 所有 Enter 提交都會略過 IME 組字中的按鍵(含 Safari 的 keyCode 229),注音選字不會誤送出;直接用輸入法打字時,compositionstart 就會進入編輯
  • 狀態列(選取範圍、加總、平均、計數)為 aria-live="polite"
  • 使用者系統開啟「減少動態效果」時,選取框直接跳到新位置,不播 spring,也不顯示提交後的閃爍