@ishibashi0112/spreadsheet-grid 0.37.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/useCellContextMenuController.d.ts +4 -4
- package/dist/hooks/useColumnAutosizeRunner.d.ts +2 -2
- package/dist/hooks/useColumnHeaderDragController.d.ts +2 -2
- package/dist/hooks/useColumnMenuController.d.ts +5 -5
- package/dist/hooks/useColumnSelectOptionsCollector.d.ts +2 -2
- package/dist/hooks/useController.d.ts +3 -1
- package/dist/hooks/useFilterPopoverController.d.ts +8 -8
- package/dist/hooks/useGlobalFilteredOrder.d.ts +3 -3
- package/dist/hooks/useGridBarContext.d.ts +1 -1
- package/dist/hooks/useGridClipboardController.d.ts +1 -1
- package/dist/hooks/useGridEditController.d.ts +4 -4
- package/dist/hooks/useGridHistoryController.d.ts +1 -1
- package/dist/hooks/useGridKeyboardInteractions.d.ts +1 -1
- package/dist/hooks/useGridPointerInteractions.d.ts +8 -8
- package/dist/hooks/useGridStore.d.ts +1 -1
- package/dist/hooks/useGridViewportSync.d.ts +1 -1
- package/dist/hooks/useResolvedGridTheme.d.ts +1 -1
- package/dist/hooks/useRowDragController.d.ts +2 -2
- package/dist/hooks/useServerSideRowModel.d.ts +3 -3
- package/dist/hooks/useToolPanelController.d.ts +2 -2
- package/dist/index.cjs +1 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2696 -8531
- package/dist/model/gridTypes.d.ts +2 -2
- package/dist/testing/index.d.ts +1 -5
- package/dist/testing.cjs +1 -1
- package/dist/testing.js +1 -51
- package/dist/view/ColumnChooserPanel.d.ts +1 -1
- package/dist/view/ColumnFilterPopover.d.ts +5 -5
- package/dist/view/GridBodyLayer.d.ts +2 -2
- package/dist/view/GridDetailLayer.d.ts +1 -1
- package/dist/view/GridHeaderRow.d.ts +2 -2
- package/dist/view/GridScrollHint.d.ts +2 -2
- package/package.json +8 -28
- package/README.md +0 -582
- package/dist/controllers/clipboardController.d.ts +0 -31
- package/dist/controllers/columnAutosizeRunner.d.ts +0 -20
- package/dist/controllers/columnHeaderDragController.d.ts +0 -44
- package/dist/controllers/columnMenuController.d.ts +0 -49
- package/dist/controllers/contextMenuController.d.ts +0 -30
- package/dist/controllers/editController.d.ts +0 -27
- package/dist/controllers/filterPopoverController.d.ts +0 -53
- package/dist/controllers/globalFilteredOrder.d.ts +0 -38
- package/dist/controllers/historyController.d.ts +0 -28
- package/dist/controllers/keyboardController.d.ts +0 -36
- package/dist/controllers/panelHeaderDragController.d.ts +0 -21
- package/dist/controllers/pointerInteractionsController.d.ts +0 -74
- package/dist/controllers/popoverSupport.d.ts +0 -18
- package/dist/controllers/rowDragController.d.ts +0 -40
- package/dist/controllers/selectOptionsCollector.d.ts +0 -31
- package/dist/controllers/serverSideRowModel.d.ts +0 -36
- package/dist/controllers/systemColorSchemeStore.d.ts +0 -5
- package/dist/controllers/toolPanelController.d.ts +0 -36
- package/dist/controllers/tooltipController.d.ts +0 -6
- package/dist/controllers/viewportSyncController.d.ts +0 -30
- package/dist/logic/aggregation.d.ts +0 -13
- package/dist/logic/autoScrollGeometry.d.ts +0 -27
- package/dist/logic/autoSizeOnData.d.ts +0 -10
- package/dist/logic/cellOverflowTooltip.d.ts +0 -5
- package/dist/logic/checkboxEditor.d.ts +0 -12
- package/dist/logic/chunkedLoop.d.ts +0 -8
- package/dist/logic/clearCells.d.ts +0 -31
- package/dist/logic/columnAutosize.d.ts +0 -18
- package/dist/logic/columnFlex.d.ts +0 -4
- package/dist/logic/columnReset.d.ts +0 -7
- package/dist/logic/contextMenuTarget.d.ts +0 -4
- package/dist/logic/cx.d.ts +0 -1
- package/dist/logic/dateFilterCondition.d.ts +0 -26
- package/dist/logic/dateFilterPresets.d.ts +0 -13
- package/dist/logic/dateFilterTree.d.ts +0 -17
- package/dist/logic/datePickerCalendar.d.ts +0 -12
- package/dist/logic/detailRow.d.ts +0 -33
- package/dist/logic/domGuards.d.ts +0 -7
- package/dist/logic/editorValues.d.ts +0 -5
- package/dist/logic/exportCsv.d.ts +0 -12
- package/dist/logic/exportData.d.ts +0 -9
- package/dist/logic/exportScope.d.ts +0 -3
- package/dist/logic/filterPopoverLayout.d.ts +0 -17
- package/dist/logic/filterPopoverOutsideClick.d.ts +0 -2
- package/dist/logic/filterSummary.d.ts +0 -3
- package/dist/logic/filtering.d.ts +0 -33
- package/dist/logic/geometry.d.ts +0 -72
- package/dist/logic/gridState.d.ts +0 -13
- package/dist/logic/grouping.d.ts +0 -34
- package/dist/logic/history.d.ts +0 -16
- package/dist/logic/inferFilterType.d.ts +0 -19
- package/dist/logic/numberFilterCondition.d.ts +0 -21
- package/dist/logic/panelDragGeometry.d.ts +0 -13
- package/dist/logic/rowHeightStore.d.ts +0 -13
- package/dist/logic/rowReorder.d.ts +0 -18
- package/dist/logic/rowSelection.d.ts +0 -16
- package/dist/logic/scrollHint.d.ts +0 -41
- package/dist/logic/scrollTargets.d.ts +0 -22
- package/dist/logic/selectEditorState.d.ts +0 -28
- package/dist/logic/selectOptions.d.ts +0 -9
- package/dist/logic/serverSideBlocks.d.ts +0 -2
- package/dist/logic/serverSideCache.d.ts +0 -14
- package/dist/logic/serverSideEdits.d.ts +0 -16
- package/dist/logic/serverSideQuery.d.ts +0 -7
- package/dist/logic/setFilterSearch.d.ts +0 -14
- package/dist/logic/setFilterSelection.d.ts +0 -5
- package/dist/logic/slotDom.d.ts +0 -8
- package/dist/logic/slotProps.d.ts +0 -18
- package/dist/logic/sorting.d.ts +0 -11
- package/dist/logic/textFilterCondition.d.ts +0 -18
- package/dist/logic/theme.d.ts +0 -3
- package/dist/logic/tooltipGeometry.d.ts +0 -19
- package/dist/logic/validation.d.ts +0 -14
- package/dist/logic/valueFormatters.d.ts +0 -9
- package/dist/logic/valueStore.d.ts +0 -6
- package/dist/logic/verticalGeometry.d.ts +0 -58
- package/dist/model/gridActions.d.ts +0 -125
- package/dist/model/gridReducer.d.ts +0 -4
- package/dist/model/gridSelectors.d.ts +0 -40
- package/dist/model/gridStore.d.ts +0 -24
- package/dist/model/gridTypes.core.d.ts +0 -796
- package/dist/utils/clipboard.d.ts +0 -29
- package/dist/utils/excelColumnName.d.ts +0 -1
- package/dist/utils/permissions.d.ts +0 -4
- package/dist/utils/scheduler.d.ts +0 -1
package/README.md
DELETED
|
@@ -1,582 +0,0 @@
|
|
|
1
|
-
# @ishibashi0112/spreadsheet-grid
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@ishibashi0112/spreadsheet-grid)
|
|
4
|
-
[](./LICENSE)
|
|
5
|
-
|
|
6
|
-
A high-performance, virtualized spreadsheet / data grid for **React 19**, written in TypeScript.
|
|
7
|
-
|
|
8
|
-
高性能な仮想化スプレッドシート/データグリッド(**React 19**・TypeScript 製)。
|
|
9
|
-
|
|
10
|
-
**English** | [日本語](#日本語)
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Features
|
|
15
|
-
|
|
16
|
-
- Scroll-space virtualization that handles up to ~1,000,000 rows.
|
|
17
|
-
- Three-pane pinned columns (left / center / right) via sticky positioning.
|
|
18
|
-
- Sorting, per-column filters (`text` / `number` / `date` / `select` / `set` / `custom`), and a global filter.
|
|
19
|
-
- In-cell editing and clipboard copy / paste, with range selection, keyboard navigation, and Delete / Backspace to clear selected cells. Cell editing is IME-aware (composing Enter never commits the cell).
|
|
20
|
-
- Built-in cell editor types via `column.editor` — `text` (default), `number` (`min` / `max` / `step`), `select` (dropdown with keyboard navigation & type-ahead; static or per-row options), `date` (native date picker), `checkbox` (direct toggle by click / Space, no edit session, custom checked/unchecked value mapping), and `custom` (render your own editor with `ctx.commit` / `ctx.cancel`; committing a non-string value bypasses parsing). Built-in editors auto-supply a default string→value parser (an explicit `parseClipboardValue` always wins) so paste and clear stay type-consistent.
|
|
21
|
-
- Cell validation via `column.validate` — default `'mark'` mode accepts the value but flags the cell (background + corner marker + hover tooltip message, derived at render time so it always matches `rows`, including after undo or external replacement); opt-in `'reject'` mode (`validationMode: 'reject'`) refuses invalid writes: the editor keeps editing with an inline error bubble, paste / clear skip the offending cells. `getInvalidCells()` on the imperative handle scans all rows for a pre-save check. Mark visibility is controlled declaratively with the `showValidationMarks` prop (default `true`) — flip it from state to show marks only on submit; `getInvalidCells()` and `'reject'` keep working regardless.
|
|
22
|
-
- Undo / redo for grid edits (cell edits, paste, clear) — `Ctrl/Cmd+Z`, `Ctrl/Cmd+Shift+Z` / `Ctrl/Cmd+Y`, plus `undo()` / `redo()` / `canUndo()` / `canRedo()` on the imperative handle and an `onUndoRedoStateChange` callback for toolbars. Restores the edited cell's active cell & selection and scrolls it back into view. History is snapshot-based with structural sharing, capped by `undoHistoryLimit` (default 100), and clears automatically when `rows` is replaced externally (client-side row model only).
|
|
23
|
-
- Optional auto-height rows for wrapped, variable-height content.
|
|
24
|
-
- Auto-fit column widths to content on data load — `autoSizeColumns="onMount"` (once, on first data) or `"onDataChange"` (every time `rows` changes, e.g. after a form submit). Same engine as the column menu's "Autosize All Columns"; opt individual columns out with `suppressAutoSize`.
|
|
25
|
-
- Full-text tooltip on truncated cells — `showCellOverflowTooltip` shows the full value on hover, but only when the cell is actually clipped (…).
|
|
26
|
-
- Japanese-aware line wrapping — per-column `wordBreak` / `lineBreak`, including `wordBreak: 'auto-phrase'` for phrase-based breaks on Chromium (BudouX). Cross-browser BudouX recipe in the API reference.
|
|
27
|
-
- External height control via `height` / `maxHeight` (e.g. `height="100%"` to follow the parent's height).
|
|
28
|
-
- Both **client-side** (`rows`) and **server-side** (`dataSource`, SSRM) row models — server-side includes query forwarding (filter / sort / global filter), soft refresh (`refreshServerSide()`), load-error retry UI, and cell-edit write-back via `dataSource.updateRows` with optimistic updates and automatic rollback on failure.
|
|
29
|
-
- Themeable with CSS custom properties (`--ssg-*`, defined at zero specificity so your overrides always win). Base styles are plain unlayered CSS with single-class specificity, so they survive CSS resets such as Tailwind Preflight; a cascade-layers variant (`style.layer.css`) is also shipped. `className` / `style` / `classNames` slots cover every visible part (25 slots), and every slot accepts either a class string or `{ className, style }` — the shape returned by StyleX's `stylex.props()`.
|
|
30
|
-
- Styled tooltips out of the box — action hints and truncated-text previews use a custom dark-chip tooltip (no browser-default `title` look). Add `data-ssg-tooltip="text"` to your own elements (custom cells, headers) to get the same tooltip; colors are themeable via `--ssg-tooltip-*` tokens.
|
|
31
|
-
- Built-in dark theme — `theme="light" | "dark" | "auto"` switches the grid, every popover / panel / menu, the drag ghost and tooltips through a single token preset. `"auto"` follows `prefers-color-scheme`; with class-based dark frameworks (Mantine / HeroUI / Tailwind) pass your resolved color scheme instead.
|
|
32
|
-
- Toggle the top / bottom bars and their parts via props — whole bars (`showTopBar` / `showBottomBar`), the default top bar's summary chips and global-filter input, and the Rows/Columns counts in each bar.
|
|
33
|
-
- Filter management panel — review every active column filter in one place (jump to the column & edit, clear one / all, add new), opened from the column menu, the default top bar's clickable Filters chip, or `openFilterManager()` on the imperative handle. An optional filter chip bar (`showFilterChipBar`) keeps active filters visible right below the top bar.
|
|
34
|
-
- Built-in CSV export (`downloadCsv` / `exportCsv`), plus a library-agnostic `getExportData()` for Excel / XLSX / ODS — feed the shaped data (filter/sort/visible-order aware) to your own writer such as [hucre](https://github.com/productdevbook/hucre), ExcelJS, or SheetJS. No spreadsheet library is bundled; multi-sheet is composed on your side. See [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md).
|
|
35
|
-
- Export scopes: `'view'` (default — every filtered/sorted view row, scroll-independent), `'raw'` (every source row, ignoring filter & sort), `'rendered'` (only the rows currently rendered by virtualization — scroll-dependent), `'selection'`. Legacy `'all'` / `'visible'` keep working as deprecated aliases of `'view'` / `'rendered'`.
|
|
36
|
-
- Touch-friendly basics — on touch devices a tap selects a cell, a double tap starts editing (works even where the browser never fires `dblclick`), and swipes scroll instead of starting a range selection. The column menu (`⋮`) is always visible on hover-less devices.
|
|
37
|
-
- TypeScript-first, fully controlled API.
|
|
38
|
-
|
|
39
|
-
## Installation
|
|
40
|
-
|
|
41
|
-
```sh
|
|
42
|
-
npm install @ishibashi0112/spreadsheet-grid
|
|
43
|
-
# pnpm add @ishibashi0112/spreadsheet-grid
|
|
44
|
-
# yarn add @ishibashi0112/spreadsheet-grid
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Requires **react** and **react-dom** `>= 19` as peer dependencies (install them in your app if you have not already). `@tanstack/virtual-core` (the framework-agnostic core of TanStack Virtual) is a regular dependency and is installed automatically; the React adapter is bundled with the grid.
|
|
48
|
-
|
|
49
|
-
## Styles
|
|
50
|
-
|
|
51
|
-
The grid ships its CSS as a separate file. Import it once (for example, in your app entry):
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
import '@ishibashi0112/spreadsheet-grid/style.css'
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
The base styles are plain (unlayered) CSS scoped to `.ssg-*` classes, and all design tokens are defined at zero specificity (`:where(.ssg-root)`), so your token overrides always win regardless of import order.
|
|
58
|
-
|
|
59
|
-
### Using with Tailwind CSS / HeroUI / Mantine
|
|
60
|
-
|
|
61
|
-
- **Tailwind CSS v3 (and HeroUI on v3)** — works out of the box. Preflight cannot break the grid: its element/universal resets lose to the grid's class selectors by specificity.
|
|
62
|
-
- **Tailwind CSS v4 (and HeroUI on v4)** — works out of the box. If you additionally want Tailwind utilities to override grid defaults without the `!` modifier, put the grid CSS into a cascade layer below `utilities`:
|
|
63
|
-
|
|
64
|
-
```css
|
|
65
|
-
@import 'tailwindcss';
|
|
66
|
-
@import '@ishibashi0112/spreadsheet-grid/style.css' layer(components);
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Alternatively, use the pre-layered variant `style.layer.css` (everything wrapped in `@layer ssg-base`) and declare the layer order yourself:
|
|
70
|
-
|
|
71
|
-
```css
|
|
72
|
-
@layer theme, base, ssg-base, components, utilities;
|
|
73
|
-
@import 'tailwindcss';
|
|
74
|
-
@import '@ishibashi0112/spreadsheet-grid/style.layer.css';
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
- **Mantine** — works out of the box (no class-name or reset conflicts; grid popovers use `z-index: 1000`, above Mantine's default modal z-index).
|
|
78
|
-
- **StyleX** — works out of the box (no resets, `x`-prefixed atomic classes). Pass `stylex.props(...)` straight into `classNames.*`, `cellClassName`, `getRowClassName` or `detailRow.className` — every slot accepts `string | { className, style }`, so StyleX dynamic styles (delivered through the `style` object) reach the element. StyleX atomic classes and the grid's base classes share the same specificity, so load the grid CSS before StyleX's output, or use `style.layer.css` (required when StyleX's `useLayers` option is on). Bridge tokens with `stylex.create({ grid: { '--ssg-accent': vars.accent } })` on the `root` slot.
|
|
79
|
-
|
|
80
|
-
To override a grid default reliably in plain CSS, chain your class with the grid's base class so it wins by specificity, independent of import order:
|
|
81
|
-
|
|
82
|
-
```css
|
|
83
|
-
.ssg-body-cell.my-warn-cell {
|
|
84
|
-
background-color: #fff7ed;
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Quick start
|
|
89
|
-
|
|
90
|
-
```tsx
|
|
91
|
-
import { useState } from 'react'
|
|
92
|
-
import { SpreadsheetGrid, type GridColumn } from '@ishibashi0112/spreadsheet-grid'
|
|
93
|
-
import '@ishibashi0112/spreadsheet-grid/style.css'
|
|
94
|
-
|
|
95
|
-
type Row = { id: number; name: string; qty: number }
|
|
96
|
-
|
|
97
|
-
const columns: GridColumn<Row>[] = [
|
|
98
|
-
{ key: 'name', title: 'Name', width: 200, editable: true, filterType: 'text' },
|
|
99
|
-
{ key: 'qty', title: 'Qty', width: 120, editable: true, filterType: 'number' },
|
|
100
|
-
]
|
|
101
|
-
|
|
102
|
-
export function Example() {
|
|
103
|
-
const [rows, setRows] = useState<Row[]>([
|
|
104
|
-
{ id: 1, name: 'Apple', qty: 3 },
|
|
105
|
-
{ id: 2, name: 'Banana', qty: 5 },
|
|
106
|
-
])
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<SpreadsheetGrid
|
|
110
|
-
rows={rows}
|
|
111
|
-
columns={columns}
|
|
112
|
-
onRowsChange={setRows}
|
|
113
|
-
rowKeyGetter={(row) => row.id}
|
|
114
|
-
/>
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
`rows` and `onRowsChange` make the grid a controlled component. A column needs at least `key` and `width`.
|
|
120
|
-
|
|
121
|
-
## Cell editors & validation
|
|
122
|
-
|
|
123
|
-
Pick an editor per column with `column.editor`, and validate values with `column.validate`:
|
|
124
|
-
|
|
125
|
-
```tsx
|
|
126
|
-
const columns: GridColumn<Row>[] = [
|
|
127
|
-
{
|
|
128
|
-
key: 'name', title: 'Name', width: 200,
|
|
129
|
-
// 'reject' refuses invalid writes: the editor keeps editing with an error
|
|
130
|
-
// bubble; paste / Delete skip the offending cells.
|
|
131
|
-
validate: ({ value }) => (String(value ?? '').length > 0 ? true : 'Name is required'),
|
|
132
|
-
validationMode: 'reject',
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
key: 'qty', title: 'Qty', width: 120, align: 'right',
|
|
136
|
-
editor: { type: 'number', min: 0, step: 1 },
|
|
137
|
-
// Default 'mark' mode: invalid values are accepted but flagged on the cell
|
|
138
|
-
// (background + corner marker + tooltip message on hover).
|
|
139
|
-
validate: ({ value }) =>
|
|
140
|
-
typeof value === 'number' && value >= 0 ? true : 'Enter a number ≥ 0',
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
key: 'status', title: 'Status', width: 140,
|
|
144
|
-
editor: { type: 'select', options: [
|
|
145
|
-
{ value: 'active', label: 'Active' },
|
|
146
|
-
{ value: 'hold', label: 'On hold' },
|
|
147
|
-
] },
|
|
148
|
-
},
|
|
149
|
-
{ key: 'shippedAt', title: 'Shipped', width: 140, editor: { type: 'date' } },
|
|
150
|
-
// Direct toggle by click / Space — no edit session. Values default to true / false;
|
|
151
|
-
// map them with checkedValue / uncheckedValue (e.g. '1' / '0').
|
|
152
|
-
{ key: 'done', title: 'Done', width: 90, align: 'center', editor: { type: 'checkbox' } },
|
|
153
|
-
// Bring your own editor: render anything, call ctx.commit(value) / ctx.cancel().
|
|
154
|
-
// Committing a non-string value bypasses parsing and writes the domain value as-is.
|
|
155
|
-
{ key: 'amount', title: 'Amount', width: 140,
|
|
156
|
-
editor: { type: 'custom', render: (ctx) => <MyAmountEditor ctx={ctx} /> } },
|
|
157
|
-
]
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Before saving, collect every invalid cell (client-side row model):
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
const invalid = gridRef.current?.getInvalidCells()
|
|
164
|
-
// -> [{ rowKey, sourceRowIndex, columnKey, message }, ...]
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
Marks are shown in real time by default. For the classic "validate on submit" flow, keep the rules on the columns and toggle mark visibility from state — `showValidationMarks={showErrors}` (default `true`). `getInvalidCells()` and `'reject'` are unaffected by the toggle. See the recipe in [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md).
|
|
168
|
-
|
|
169
|
-
## Sizing
|
|
170
|
-
|
|
171
|
-
By default the grid caps its height at `480px` (`max-height`) and scrolls when the content is taller. Pass `height` to take explicit control — use `height="100%"` to follow the parent's height, or a pixel value:
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
<div style={{ height: 600, minHeight: 0 }}>
|
|
175
|
-
<SpreadsheetGrid rows={rows} columns={columns} height="100%" />
|
|
176
|
-
</div>
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
For `height="100%"` to work, the parent must have a resolved height (its ancestors are sized, and a flex child needs `min-height: 0`). This is standard CSS the library can't resolve for you. `maxHeight` sets an upper bound and can be combined with `height` (explicit height, capped at `maxHeight`).
|
|
180
|
-
|
|
181
|
-
### Auto-height rows
|
|
182
|
-
|
|
183
|
-
Variable row height needs **two switches, both required**: the grid prop `autoHeight` (the master switch, default `false`) **and** at least one column with `autoHeight: true` (that column wraps and drives the row height). A cell grows only when `grid autoHeight && column.autoHeight` are both true. Auto-height is active only up to **50,000 rows**; beyond that it falls back to uniform `rowHeight`. `estimateRowHeight` is the placeholder for off-screen (not-yet-measured) rows — not a cap. See [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md) for details.
|
|
184
|
-
|
|
185
|
-
```tsx
|
|
186
|
-
<SpreadsheetGrid
|
|
187
|
-
rows={rows}
|
|
188
|
-
columns={[{ key: 'note', title: 'Note', width: 320, autoHeight: true }]}
|
|
189
|
-
autoHeight // master switch — without it, the column's autoHeight is ignored
|
|
190
|
-
/>
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Auto-sizing columns
|
|
194
|
-
|
|
195
|
-
Set `autoSizeColumns` to fit column widths to their content when data arrives — no imperative calls or effects needed on your side:
|
|
196
|
-
|
|
197
|
-
```tsx
|
|
198
|
-
// Refit every time a new result set replaces `rows` (e.g. after a form submit).
|
|
199
|
-
<SpreadsheetGrid rows={rows} columns={columns} autoSizeColumns="onDataChange" />
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
`'onMount'` fits once on first data; `'onDataChange'` refits whenever the `rows` reference changes; `false` (default) does nothing. It reuses the same measurement as the column menu's "Autosize All Columns", so per-column opt-outs apply: columns with `suppressAutoSize: true` (and `autoHeight: true` columns) keep their `width`. The trigger only reacts to `rows` — filtering, sorting and column reordering do **not** refit — and it writes to internal widths without calling `onColumnsChange`, so it coexists with controlled `columns`. Server-side (`dataSource`) is not supported. See [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md) for details.
|
|
203
|
-
|
|
204
|
-
### Density
|
|
205
|
-
|
|
206
|
-
Set `density` to switch the overall sizing with one prop — `'compact' | 'standard' | 'comfortable'` (default `'standard'`, identical to previous versions):
|
|
207
|
-
|
|
208
|
-
```tsx
|
|
209
|
-
<SpreadsheetGrid rows={rows} columns={columns} density="compact" />
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
The preset drives the default `rowHeight` / `headerHeight` (compact: 28/32, standard: 36/40, comfortable: 44/48 — explicit props always win) and switches sizing tokens (cell horizontal padding, bar padding, icon-button size, relative cell font scale) via a root modifier class. Individual tokens (e.g. `--ssg-cell-pad-x`) can still be overridden for fine-tuning. Popovers/menus are not affected.
|
|
213
|
-
|
|
214
|
-
## Server-side mode (SSRM)
|
|
215
|
-
|
|
216
|
-
Pass a `dataSource` instead of `rows` to switch to server-side mode. The grid keeps the full scroll height for the total row count and fetches only the blocks near the viewport:
|
|
217
|
-
|
|
218
|
-
```tsx
|
|
219
|
-
<SpreadsheetGrid
|
|
220
|
-
columns={columns}
|
|
221
|
-
dataSource={{
|
|
222
|
-
async getRows({ startIndex, endIndex, query, signal }) {
|
|
223
|
-
// Apply `query` (filters / sort) on the server and return only [startIndex, endIndex).
|
|
224
|
-
const { rows, totalRowCount } = await fetchPage({ startIndex, endIndex, query, signal })
|
|
225
|
-
return { rows, totalRowCount }
|
|
226
|
-
},
|
|
227
|
-
// Optional: enables cell editing in server-side mode (optimistic update + rollback on failure).
|
|
228
|
-
async updateRows({ updates }) {
|
|
229
|
-
await patchRows(updates) // updates: [{ rowKey, rowIndex, row, previousRow, changes }]
|
|
230
|
-
},
|
|
231
|
-
}}
|
|
232
|
-
/>
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Sorting, column filters, and the global filter stay enabled and are forwarded to the server through `query`. Cell edits (editor commit / paste / Delete-clear / checkbox) are written back through `updateRows` with optimistic updates — the grid rolls the cells back and shows a dismissible error bar if the write fails (`onServerSideWriteError` fires for toasts/logging). Without `updateRows`, server-side cells are read-only. Row add/remove has no write-back API; apply it on the server and call `refreshServerSide()`. See the [API reference](./src/components/spreadsheet-grid/API_REFERENCE.md) for the full `getRows` / `updateRows` contracts, the filter wire format, and `serverSideRefreshToken`.
|
|
236
|
-
|
|
237
|
-
## Styling & theming
|
|
238
|
-
|
|
239
|
-
- Override the CSS variables on `.ssg-root` (or scope them via the `className` prop):
|
|
240
|
-
|
|
241
|
-
```css
|
|
242
|
-
.ssg-root {
|
|
243
|
-
--ssg-accent: #16a34a;
|
|
244
|
-
--ssg-radius: 4px;
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
- Use the `classNames` prop for per-part slots (root / toolbar / statusBar / header & body rows and cells / group & detail rows / popover / menuItem / tooltip / dragGhost / checkbox / cellEditor / emptyState / filterChipBar / errorBar / scrollHint / overlays), `cellClassName` per column, and `getRowClassName` per row. Each accepts a class string or `{ className, style }`; the root also takes a `style` prop. Inline `style` is applied to the part's element while the grid keeps the last word on positioning (`left` / `top` / `width` / `height` / `transform`). Token overrides always apply (tokens are defined at zero specificity). For property overrides, chain with the base class (e.g. `.ssg-body-cell.my-class`) to win regardless of import order — see the Styles section above.
|
|
249
|
-
|
|
250
|
-
### Dark theme
|
|
251
|
-
|
|
252
|
-
Pass `theme` to switch the whole surface — the grid itself, every popover / panel / menu (they are portalled to `document.body` and carry the theme class themselves), the column drag ghost and tooltips:
|
|
253
|
-
|
|
254
|
-
```tsx
|
|
255
|
-
<SpreadsheetGrid theme="dark" columns={columns} rows={rows} />
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
- `"light"` (default) / `"dark"` — explicit. `"auto"` follows the OS / browser `prefers-color-scheme` and updates live.
|
|
259
|
-
- `color-scheme` is set accordingly, so native scrollbars and `<select>` controls follow the theme too.
|
|
260
|
-
- The dark preset only redefines color tokens (`.ssg-theme-dark`); sizing tokens (radius, paddings) are theme-independent.
|
|
261
|
-
|
|
262
|
-
**With Mantine / HeroUI / Tailwind (class-based dark):** the page's actual theme may not match `prefers-color-scheme`, so pass your resolved color scheme instead of `"auto"`:
|
|
263
|
-
|
|
264
|
-
```tsx
|
|
265
|
-
// Mantine
|
|
266
|
-
import { useComputedColorScheme } from '@mantine/core';
|
|
267
|
-
const colorScheme = useComputedColorScheme('light'); // 'light' | 'dark'
|
|
268
|
-
<SpreadsheetGrid theme={colorScheme} ... />
|
|
269
|
-
|
|
270
|
-
// HeroUI / Tailwind (next-themes)
|
|
271
|
-
import { useTheme } from 'next-themes';
|
|
272
|
-
const { resolvedTheme } = useTheme();
|
|
273
|
-
<SpreadsheetGrid theme={resolvedTheme === 'dark' ? 'dark' : 'light'} ... />
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
**Customizing dark colors:** override tokens under `.ssg-theme-dark` — the class is present on the grid root and on every portal root, so one rule covers all surfaces:
|
|
277
|
-
|
|
278
|
-
```css
|
|
279
|
-
.ssg-theme-dark {
|
|
280
|
-
--ssg-cell-bg: #0d0d0f;
|
|
281
|
-
--ssg-panel-bg: #1b1c20;
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
Note: a plain `.ssg-root { --ssg-* }` override wins over **both** themes (theme presets are defined at zero specificity). To target light only, scope it with `.ssg-root:not(.ssg-theme-dark)`.
|
|
286
|
-
|
|
287
|
-
## API reference
|
|
288
|
-
|
|
289
|
-
The full prop and type reference lives in [`src/components/spreadsheet-grid/API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md).
|
|
290
|
-
|
|
291
|
-
## License
|
|
292
|
-
|
|
293
|
-
[MIT](./LICENSE) © 2026 Yuki Sakakibara
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
## 日本語
|
|
298
|
-
|
|
299
|
-
[English](#ishibashi0112spreadsheet-grid)
|
|
300
|
-
|
|
301
|
-
**React 19** 製の高性能な仮想化スプレッドシート/データグリッドです。TypeScript で書かれています。
|
|
302
|
-
|
|
303
|
-
### 特徴
|
|
304
|
-
|
|
305
|
-
- スクロール空間の仮想化により最大 100 万行規模に対応。
|
|
306
|
-
- `position: sticky` による 3 ペイン固定列(左 / 中央 / 右)。
|
|
307
|
-
- ソート、列ごとのフィルター(`text` / `number` / `date` / `select` / `set` / `custom`)、グローバルフィルター。
|
|
308
|
-
- セル内編集とクリップボードのコピー/貼り付け、範囲選択、キーボード操作、Delete / Backspace による選択セルのクリア。セル編集は IME 対応(変換確定の Enter でセルが確定されない)。
|
|
309
|
-
- `column.editor` による組み込みエディタ種別 — `text`(既定)/ `number`(`min` / `max` / `step`)/ `select`(キーボード操作・タイプアヘッド付きドロップダウン。候補は静的配列 or 行依存関数)/ `date`(ネイティブ日付ピッカー)/ `checkbox`(クリック / Space の直接トグル。編集セッションなし、checked/unchecked の値マッピング可)/ `custom`(`ctx.commit` / `ctx.cancel` で自作エディタを差し込み。非 string の commit はパースをバイパス)。組み込みエディタは「文字列 → 値」の既定パーサを自動供給し(明示の `parseClipboardValue` が常に優先)、貼り付け・クリアでも型が揃います。
|
|
310
|
-
- `column.validate` によるセル検証 — 既定の `'mark'` モードは値を受け入れつつセルへ invalid 表示(背景 + 右上マーカー + ホバーでメッセージ。表示時導出のため undo や外部差し替え後も常に `rows` と整合)。`validationMode: 'reject'` で不正な書き込み自体を拒否(エディタはエラーバブル表示で編集継続、貼り付け / クリアは該当セルのみスキップ)。ハンドルの `getInvalidCells()` で保存前の全行チェックができます。マークの表示可否は `showValidationMarks` prop(既定 `true`)で宣言的に切り替えられ、「送信時にだけマークを出す」UX を state 1 つで実現できます(`getInvalidCells()` / `'reject'` は表示状態と無関係に機能)。
|
|
311
|
-
- グリッド編集の undo / redo(セル編集・貼り付け・クリア)— `Ctrl/Cmd+Z`、`Ctrl/Cmd+Shift+Z` / `Ctrl/Cmd+Y` に加え、ハンドルの `undo()` / `redo()` / `canUndo()` / `canRedo()` とツールバー向けの `onUndoRedoStateChange` コールバック。編集時のアクティブセル・選択範囲まで復元し、画面外なら可視位置へスクロールで追従。履歴は構造共有のスナップショット方式で `undoHistoryLimit`(既定 100)まで保持し、`rows` が外部から差し替えられたときは自動破棄(クライアントサイド行モデル専用)。
|
|
312
|
-
- 折り返し・可変行高に対応する auto-height 行(任意)。
|
|
313
|
-
- データ投入時に列幅を内容へ自動フィット — `autoSizeColumns="onMount"`(初回にデータが載った一度きり)/ `"onDataChange"`(`rows` が変わるたび。フォーム送信結果の差し替え等)。列メニュー「すべての列の幅を自動調整」と同一エンジンで、列個別の除外は `suppressAutoSize`。
|
|
314
|
-
- 省略(…)セルの全文ツールチップ — `showCellOverflowTooltip` でホバー時に全文表示(実際にクリップされているセルのみ)。
|
|
315
|
-
- 日本語対応の折り返し — 列ごとの `wordBreak` / `lineBreak`。`wordBreak: 'auto-phrase'` で Chromium(Chrome / Edge)の文節折り返し(BudouX)。クロスブラウザの BudouX レシピは API リファレンス参照。
|
|
316
|
-
- `height` / `maxHeight` によるスクロールコンテナ高さの外部制御(`height="100%"` で親要素の高さに追従)。
|
|
317
|
-
- **クライアントサイド**(`rows`)と**サーバーサイド**(`dataSource`、SSRM)の両行モデル — サーバーサイドはクエリ送出(フィルター / ソート / グローバルフィルター)、ソフトリフレッシュ(`refreshServerSide()`)、取得失敗の再試行 UI に加え、`dataSource.updateRows` によるセル編集の書き戻し(楽観更新 + 失敗時の自動ロールバック)まで対応。
|
|
318
|
-
- CSS カスタムプロパティ(`--ssg-*`。特異度 0 で定義され、利用側の上書きが常に勝ちます)によるテーマ設定。基底スタイルは未レイヤーの単一クラス特異度で、Tailwind Preflight などの CSS リセットに壊されません。カスケードレイヤー版(`style.layer.css`)も同梱。`className` / `style` / `classNames` スロットは可視パーツを網羅(25 スロット)し、各スロットは class 文字列でも `{ className, style }`(StyleX の `stylex.props()` の戻り値と同形)でも受け付けます。
|
|
319
|
-
- スタイル付きツールチップを標準装備 — 操作ヒントや切り詰めテキストの全文表示は、ブラウザ標準の `title` ではなくダークチップのカスタムツールチップで表示。利用側の要素(カスタムセルやヘッダー)にも `data-ssg-tooltip="文言"` を付けるだけで同じ見た目になります。配色は `--ssg-tooltip-*` トークンで調整可。
|
|
320
|
-
- ダークテーマを標準装備 — `theme="light" | "dark" | "auto"` で、グリッド本体・全ポップオーバー / パネル / メニュー・ドラッグゴースト・ツールチップをトークンプリセット 1 つで一括切替。`"auto"` は `prefers-color-scheme` に追従(Mantine / HeroUI / Tailwind のクラスベース dark 運用では、解決済みのカラースキームを渡す使い方を推奨)。
|
|
321
|
-
- トップ / ボトムバーとその構成要素(バー全体〔`showTopBar` / `showBottomBar`〕、既定トップバーの summary chips・グローバルフィルター入力、各バーの Rows/Columns 件数)を props で表示制御。
|
|
322
|
-
- フィルター管理パネル — 適用中の列フィルターを 1 箇所で確認・操作(該当列へジャンプして編集 / 個別・全クリア / 追加)。列メニュー、既定トップバーの Filters chip クリック、ハンドルの `openFilterManager()` から開けます。トップバー直下に常時表示するフィルターチップバー(`showFilterChipBar`)もオプションで利用可。
|
|
323
|
-
- CSV エクスポート(`downloadCsv` / `exportCsv`)を内蔵。Excel / XLSX / ODS はライブラリ非依存の `getExportData()` で、整形済みデータ(フィルター/ソート/可視列順を反映)を [hucre](https://github.com/productdevbook/hucre) / ExcelJS / SheetJS など任意の writer へ流す方式。xlsx ライブラリは同梱せず、マルチシートは利用側で合成。詳細は [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md)。
|
|
324
|
-
- エクスポート scope: `'view'`(既定=フィルター/ソート後の全ビュー行。スクロール位置に非依存)/ `'raw'`(フィルター/ソート無視の全ソース行)/ `'rendered'`(描画中の行のみ=スクロール位置に依存)/ `'selection'`(選択範囲)。旧 `'all'` / `'visible'` は `'view'` / `'rendered'` の deprecated エイリアスとして従来どおり動作。
|
|
325
|
-
- タッチ操作の基本対応 — タッチ端末ではタップでセル選択、ダブルタップで編集開始(`dblclick` を発火しないブラウザでも動作)、スワイプは範囲選択にならずスクロール。列メニュー(`⋮`)はホバー不可の端末で常時表示。
|
|
326
|
-
- TypeScript ファースト、完全 controlled な API。
|
|
327
|
-
|
|
328
|
-
### インストール
|
|
329
|
-
|
|
330
|
-
```sh
|
|
331
|
-
npm install @ishibashi0112/spreadsheet-grid
|
|
332
|
-
# pnpm add @ishibashi0112/spreadsheet-grid
|
|
333
|
-
# yarn add @ishibashi0112/spreadsheet-grid
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
peer 依存として **react** / **react-dom** `>= 19` が必要です(未導入なら利用側で入れてください)。`@tanstack/virtual-core`(TanStack Virtual のフレームワーク非依存コア)は通常依存として自動的に入ります。React 向けアダプタはグリッド側に同梱しています。
|
|
337
|
-
|
|
338
|
-
### スタイル
|
|
339
|
-
|
|
340
|
-
CSS は別ファイルとして同梱されます。アプリのエントリ等で 1 度だけ import してください:
|
|
341
|
-
|
|
342
|
-
```ts
|
|
343
|
-
import '@ishibashi0112/spreadsheet-grid/style.css'
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
基底スタイルは `.ssg-*` クラスにスコープした未レイヤーの素の CSS で、デザイントークンはすべて特異度 0(`:where(.ssg-root)`)で定義されています。トークンの上書きは読み込み順に依らず必ず勝ちます。
|
|
347
|
-
|
|
348
|
-
#### Tailwind CSS / HeroUI / Mantine との共存
|
|
349
|
-
|
|
350
|
-
- **Tailwind CSS v3(HeroUI の v3 世代)** — そのままで動作します。preflight(要素 / `*` セレクタのリセット)は本グリッドのクラスセレクタに特異度で負けるため、グリッドを壊せません。
|
|
351
|
-
- **Tailwind CSS v4(HeroUI の v4 世代)** — そのままで動作します。さらに Tailwind ユーティリティで `!` 修飾子なしにグリッド既定を上書きしたい場合は、グリッド CSS を `utilities` より下のレイヤーへ入れてください:
|
|
352
|
-
|
|
353
|
-
```css
|
|
354
|
-
@import 'tailwindcss';
|
|
355
|
-
@import '@ishibashi0112/spreadsheet-grid/style.css' layer(components);
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
もしくは全体を `@layer ssg-base` に包んだ `style.layer.css` を使い、レイヤー順を自分で宣言します:
|
|
359
|
-
|
|
360
|
-
```css
|
|
361
|
-
@layer theme, base, ssg-base, components, utilities;
|
|
362
|
-
@import 'tailwindcss';
|
|
363
|
-
@import '@ishibashi0112/spreadsheet-grid/style.layer.css';
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
- **Mantine** — そのままで動作します(クラス名・リセットの衝突なし。グリッドの popover は `z-index: 1000` で Mantine の既定モーダルより前面)。
|
|
367
|
-
- **StyleX** — そのままで動作します(reset を持たず、生成クラスは接頭辞 `x` で衝突しません)。`classNames.*` / `cellClassName` / `getRowClassName` / `detailRow.className` へ `stylex.props(...)` の戻り値をそのまま渡せます(全スロットが `string | { className, style }` を受けるため、`style` 側で届く StyleX の動的スタイルも欠落しません)。StyleX の atomic クラスと基底クラスは同特異度のため、グリッド CSS を先に・StyleX の出力を後に読み込むか、`style.layer.css` を使ってください(StyleX 側で `useLayers` を使う場合は必須)。トークンは `root` スロットに `stylex.create({ grid: { '--ssg-accent': vars.accent } })` を渡して橋渡しできます。
|
|
368
|
-
|
|
369
|
-
素の CSS でグリッド既定を確実に上書きするには、基底クラスと連結して特異度で勝たせてください(読み込み順に依存しません):
|
|
370
|
-
|
|
371
|
-
```css
|
|
372
|
-
.ssg-body-cell.my-warn-cell {
|
|
373
|
-
background-color: #fff7ed;
|
|
374
|
-
}
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
### クイックスタート
|
|
378
|
-
|
|
379
|
-
```tsx
|
|
380
|
-
import { useState } from 'react'
|
|
381
|
-
import { SpreadsheetGrid, type GridColumn } from '@ishibashi0112/spreadsheet-grid'
|
|
382
|
-
import '@ishibashi0112/spreadsheet-grid/style.css'
|
|
383
|
-
|
|
384
|
-
type Row = { id: number; name: string; qty: number }
|
|
385
|
-
|
|
386
|
-
const columns: GridColumn<Row>[] = [
|
|
387
|
-
{ key: 'name', title: '名前', width: 200, editable: true, filterType: 'text' },
|
|
388
|
-
{ key: 'qty', title: '数量', width: 120, editable: true, filterType: 'number' },
|
|
389
|
-
]
|
|
390
|
-
|
|
391
|
-
export function Example() {
|
|
392
|
-
const [rows, setRows] = useState<Row[]>([
|
|
393
|
-
{ id: 1, name: 'りんご', qty: 3 },
|
|
394
|
-
{ id: 2, name: 'バナナ', qty: 5 },
|
|
395
|
-
])
|
|
396
|
-
|
|
397
|
-
return (
|
|
398
|
-
<SpreadsheetGrid
|
|
399
|
-
rows={rows}
|
|
400
|
-
columns={columns}
|
|
401
|
-
onRowsChange={setRows}
|
|
402
|
-
rowKeyGetter={(row) => row.id}
|
|
403
|
-
/>
|
|
404
|
-
)
|
|
405
|
-
}
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
`rows` と `onRowsChange` でグリッドは controlled になります。列には最低限 `key` と `width` が必要です。
|
|
409
|
-
|
|
410
|
-
### セルエディタとバリデーション
|
|
411
|
-
|
|
412
|
-
列ごとに `column.editor` でエディタ種別を選び、`column.validate` で値を検証できます:
|
|
413
|
-
|
|
414
|
-
```tsx
|
|
415
|
-
const columns: GridColumn<Row>[] = [
|
|
416
|
-
{
|
|
417
|
-
key: 'name', title: '品名', width: 200,
|
|
418
|
-
// 'reject' は不正な書き込み自体を拒否: エディタはエラーバブル表示で編集継続、
|
|
419
|
-
// 貼り付け / Delete は該当セルのみスキップされます。
|
|
420
|
-
validate: ({ value }) => (String(value ?? '').length > 0 ? true : '品名は必須です'),
|
|
421
|
-
validationMode: 'reject',
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
key: 'qty', title: '数量', width: 120, align: 'right',
|
|
425
|
-
editor: { type: 'number', min: 0, step: 1 },
|
|
426
|
-
// 既定の 'mark' モード: 不正値も一旦入り、セルに invalid 表示
|
|
427
|
-
// (背景 + 右上マーカー + ホバーでメッセージ)が付きます。
|
|
428
|
-
validate: ({ value }) =>
|
|
429
|
-
typeof value === 'number' && value >= 0 ? true : '0 以上の数値を入力してください',
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
key: 'status', title: '状態', width: 140,
|
|
433
|
-
editor: { type: 'select', options: [
|
|
434
|
-
{ value: '有効', label: '有効' },
|
|
435
|
-
{ value: '保留', label: '保留' },
|
|
436
|
-
] },
|
|
437
|
-
},
|
|
438
|
-
{ key: 'shippedAt', title: '出荷日', width: 140, editor: { type: 'date' } },
|
|
439
|
-
// クリック / Space の直接トグル(編集セッションなし)。値は既定 true / false、
|
|
440
|
-
// checkedValue / uncheckedValue でマッピング可(例: '1' / '0')。
|
|
441
|
-
{ key: 'done', title: '完了', width: 90, align: 'center', editor: { type: 'checkbox' } },
|
|
442
|
-
// 自作エディタ: 任意の UI を描画し、ctx.commit(value) / ctx.cancel() を呼びます。
|
|
443
|
-
// 非 string の commit はパースをバイパスしてドメイン値をそのまま書き込みます。
|
|
444
|
-
{ key: 'amount', title: '金額', width: 140,
|
|
445
|
-
editor: { type: 'custom', render: (ctx) => <MyAmountEditor ctx={ctx} /> } },
|
|
446
|
-
]
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
保存前に invalid セルを一括取得できます(クライアントサイド行モデル):
|
|
450
|
-
|
|
451
|
-
```ts
|
|
452
|
-
const invalid = gridRef.current?.getInvalidCells()
|
|
453
|
-
// -> [{ rowKey, sourceRowIndex, columnKey, message }, ...]
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
マークは既定でリアルタイム表示です。「送信時にまとめて検証」の定番フローは、ルールを列に定義したまま `showValidationMarks={showErrors}`(既定 `true`)を state で切り替えて実現します。`getInvalidCells()` と `'reject'` は表示状態の影響を受けません。完結したコード例は [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md) のレシピを参照してください。
|
|
457
|
-
|
|
458
|
-
### サイズ(高さ)
|
|
459
|
-
|
|
460
|
-
既定ではグリッドの高さは `480px`(`max-height`)で頭打ちになり、中身がそれより高いとスクロールします。`height` を渡すと高さを明示制御できます。`height="100%"` で親要素の高さに追従、`number` で px 指定です:
|
|
461
|
-
|
|
462
|
-
```tsx
|
|
463
|
-
<div style={{ height: 600, minHeight: 0 }}>
|
|
464
|
-
<SpreadsheetGrid rows={rows} columns={columns} height="100%" />
|
|
465
|
-
</div>
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
`height="100%"` を効かせるには、**親要素が確定高さを持つ**必要があります(祖先まで高さが確定している/flex 子なら `min-height: 0` が必要)。これは CSS の一般則のため本ライブラリ側では解決できません。`maxHeight` は高さの上限で、`height` と併用できます(明示高さ+上限)。
|
|
469
|
-
|
|
470
|
-
#### 可変行高(auto-height)
|
|
471
|
-
|
|
472
|
-
行高を可変にするには**2つのスイッチが両方必要**です。グリッド props の `autoHeight`(大本のスイッチ・既定 `false`)と、**少なくとも1列に `column.autoHeight: true`**(その列が折り返して行高を駆動)。セルが可変になるのは「グリッド `autoHeight` && 列 `autoHeight`」が両方 true のときだけです。有効なのは **50,000 行以内**で、超えると uniform 行高(`rowHeight`)へフォールバックします。`estimateRowHeight` は画面外(未測定)行の推定値で、上限ではありません。詳細は [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md)。
|
|
473
|
-
|
|
474
|
-
```tsx
|
|
475
|
-
<SpreadsheetGrid
|
|
476
|
-
rows={rows}
|
|
477
|
-
columns={[{ key: 'note', title: '備考', width: 320, autoHeight: true }]}
|
|
478
|
-
autoHeight // 大本のスイッチ(これが無いと列側 autoHeight は無視される)
|
|
479
|
-
/>
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
#### 列幅の自動調整(autoSizeColumns)
|
|
483
|
-
|
|
484
|
-
`autoSizeColumns` を渡すと、データ投入時に列幅を内容へ自動フィットします(利用側でトークンや effect は不要):
|
|
485
|
-
|
|
486
|
-
```tsx
|
|
487
|
-
// フォーム送信結果などで rows を丸ごと差し替えるたびに合わせ直す。
|
|
488
|
-
<SpreadsheetGrid rows={rows} columns={columns} autoSizeColumns="onDataChange" />
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
`'onMount'` は初回にデータが載った一度きり、`'onDataChange'` は `rows`(参照)が変わるたび、`false`(既定)は無効です。計測は列メニュー「すべての列の幅を自動調整」と同一エンジンのため、列個別の除外がそのまま効きます — `suppressAutoSize: true` の列(および `autoHeight: true` の列)は `width` を維持します。発火 signal は `rows` のみで、フィルター / ソート / 列並べ替えでは**再フィットしません**。フィット幅は内部の列幅 state に反映され `onColumnsChange` を呼ばないため、controlled な `columns` とも競合しません。serverSide(`dataSource`)では無効です。詳細は [`API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md)。
|
|
492
|
-
|
|
493
|
-
#### 密度(density)
|
|
494
|
-
|
|
495
|
-
`density` プロップ 1 つで全体のサイズ感を切り替えられます — `'compact' | 'standard' | 'comfortable'`(既定 `'standard'` = 従来と同値):
|
|
496
|
-
|
|
497
|
-
```tsx
|
|
498
|
-
<SpreadsheetGrid rows={rows} columns={columns} density="compact" />
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
プリセットは `rowHeight` / `headerHeight` の既定値(compact: 28/32・standard: 36/40・comfortable: 44/48。明示 prop が常に優先)と、寸法トークン(セル横 padding・バー padding・アイコンボタン寸法・セル文字の相対拡縮)を root 修飾子経由で一括切替します。個別の微調整はトークン(例: `--ssg-cell-pad-x`)の上書きで可能です。popover / メニューは対象外です。
|
|
502
|
-
|
|
503
|
-
### サーバーサイドモード(SSRM)
|
|
504
|
-
|
|
505
|
-
`rows` の代わりに `dataSource` を渡すとサーバーサイドモードになります。総行数ぶんのスクロール高さを保ったまま、可視窓に近いブロックだけを取得します:
|
|
506
|
-
|
|
507
|
-
```tsx
|
|
508
|
-
<SpreadsheetGrid
|
|
509
|
-
columns={columns}
|
|
510
|
-
dataSource={{
|
|
511
|
-
async getRows({ startIndex, endIndex, query, signal }) {
|
|
512
|
-
// query(フィルター/ソート)をサーバで適用し、[startIndex, endIndex) のみ返す。
|
|
513
|
-
const { rows, totalRowCount } = await fetchPage({ startIndex, endIndex, query, signal })
|
|
514
|
-
return { rows, totalRowCount }
|
|
515
|
-
},
|
|
516
|
-
// 任意: serverSide のセル編集を有効化(楽観更新 + 失敗時ロールバック)。
|
|
517
|
-
async updateRows({ updates }) {
|
|
518
|
-
await patchRows(updates) // updates: [{ rowKey, rowIndex, row, previousRow, changes }]
|
|
519
|
-
},
|
|
520
|
-
}}
|
|
521
|
-
/>
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
ソート・列フィルター・グローバルフィルターは有効なまま `query` 経由でサーバへ送られます。セル編集(エディタ確定 / ペースト / Delete クリア / checkbox)は `updateRows` で楽観更新つきの書き戻しになり、保存失敗時はセルが自動で元に戻って保存失敗バーが表示されます(`onServerSideWriteError` でトースト / ログ通知も可)。`updateRows` 未指定の serverSide セルは読み取り専用です。行の追加削除は書き戻し API を持たないため、サーバへ反映後に `refreshServerSide()` を呼ぶ運用にしてください。`getRows` / `updateRows` の契約、フィルターの wire format、`serverSideRefreshToken` の詳細は [API リファレンス](./src/components/spreadsheet-grid/API_REFERENCE.md) を参照してください。
|
|
525
|
-
|
|
526
|
-
### スタイリング / テーマ
|
|
527
|
-
|
|
528
|
-
- `.ssg-root` 上で CSS 変数を上書きします(`className` prop でスコープも可能):
|
|
529
|
-
|
|
530
|
-
```css
|
|
531
|
-
.ssg-root {
|
|
532
|
-
--ssg-accent: #16a34a;
|
|
533
|
-
--ssg-radius: 4px;
|
|
534
|
-
}
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
- パーツ別の class は `classNames` prop、列単位は `cellClassName`、行単位は `getRowClassName` で付与できます。トークン上書きは常に効きます(特異度 0 で定義)。プロパティ上書きは基底クラスとの連結(例: `.ssg-body-cell.my-class`)で読み込み順に依らず確実になります — 上記「スタイル」参照。
|
|
538
|
-
|
|
539
|
-
#### ダークテーマ
|
|
540
|
-
|
|
541
|
-
`theme` を渡すだけで全サーフェス — グリッド本体・全ポップオーバー / パネル / メニュー(`document.body` 直下のポータルですが、自身がテーマクラスを保持します)・列ドラッグゴースト・ツールチップ — が一括で切り替わります:
|
|
542
|
-
|
|
543
|
-
```tsx
|
|
544
|
-
<SpreadsheetGrid theme="dark" columns={columns} rows={rows} />
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
- `"light"`(既定)/ `"dark"` は明示指定。`"auto"` は OS / ブラウザの `prefers-color-scheme` に追従し、設定変更にもライブで反応します。
|
|
548
|
-
- `color-scheme` も併せて切り替わるため、ネイティブのスクロールバーや `<select>` もテーマに揃います。
|
|
549
|
-
- ダークプリセットが上書きするのは色トークンのみ(`.ssg-theme-dark`)。寸法トークン(radius / padding 等)はテーマ非依存です。
|
|
550
|
-
|
|
551
|
-
**Mantine / HeroUI / Tailwind(クラスベース dark)との連動:** ページの実テーマと `prefers-color-scheme` は一致しないことがあるため、`"auto"` ではなく利用側カラースキームの解決値を渡してください:
|
|
552
|
-
|
|
553
|
-
```tsx
|
|
554
|
-
// Mantine
|
|
555
|
-
import { useComputedColorScheme } from '@mantine/core';
|
|
556
|
-
const colorScheme = useComputedColorScheme('light'); // 'light' | 'dark'
|
|
557
|
-
<SpreadsheetGrid theme={colorScheme} ... />
|
|
558
|
-
|
|
559
|
-
// HeroUI / Tailwind(next-themes)
|
|
560
|
-
import { useTheme } from 'next-themes';
|
|
561
|
-
const { resolvedTheme } = useTheme();
|
|
562
|
-
<SpreadsheetGrid theme={resolvedTheme === 'dark' ? 'dark' : 'light'} ... />
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
**ダーク時の色調整:** `.ssg-theme-dark` 配下でトークンを上書きします。このクラスはグリッド root と全ポータル root に付与されるため、1 ルールで全サーフェスに効きます:
|
|
566
|
-
|
|
567
|
-
```css
|
|
568
|
-
.ssg-theme-dark {
|
|
569
|
-
--ssg-cell-bg: #0d0d0f;
|
|
570
|
-
--ssg-panel-bg: #1b1c20;
|
|
571
|
-
}
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
注意: 素の `.ssg-root { --ssg-* }` 上書きは**両テーマ**に勝ちます(テーマプリセットは特異度 0 で定義)。ライトのみを対象にしたい場合は `.ssg-root:not(.ssg-theme-dark)` でスコープしてください。
|
|
575
|
-
|
|
576
|
-
### API リファレンス
|
|
577
|
-
|
|
578
|
-
prop と型の完全なリファレンスは [`src/components/spreadsheet-grid/API_REFERENCE.md`](./src/components/spreadsheet-grid/API_REFERENCE.md) にあります。
|
|
579
|
-
|
|
580
|
-
### ライセンス
|
|
581
|
-
|
|
582
|
-
[MIT](./LICENSE) © 2026 Yuki Sakakibara
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { type GridUiAction } from '../model/gridActions';
|
|
2
|
-
import type { GridColumn, GridSelection, GridUiState, RowModel, SpreadsheetGridProps } from '../model/gridTypes';
|
|
3
|
-
import type { ServerSideCellEditInput } from '../logic/serverSideEdits';
|
|
4
|
-
export type ClipboardEventLike = {
|
|
5
|
-
clipboardData: {
|
|
6
|
-
getData: (type: string) => string;
|
|
7
|
-
} | null;
|
|
8
|
-
preventDefault: () => void;
|
|
9
|
-
};
|
|
10
|
-
export type ClipboardControllerArgs<T extends object> = {
|
|
11
|
-
rows: T[];
|
|
12
|
-
rowModel: RowModel<T>;
|
|
13
|
-
visibleColumns: GridColumn<T>[];
|
|
14
|
-
uiState: GridUiState;
|
|
15
|
-
readOnly: boolean;
|
|
16
|
-
canEditCell: SpreadsheetGridProps<T>['canEditCell'];
|
|
17
|
-
createRow?: () => T;
|
|
18
|
-
createOverflowColumn?: (columnIndex: number) => GridColumn<T>;
|
|
19
|
-
onRowsChange?: (nextRows: T[]) => void;
|
|
20
|
-
onColumnsChange?: (nextColumns: GridColumn<T>[]) => void;
|
|
21
|
-
applyServerSideCellEdits?: (edits: ServerSideCellEditInput<T>[]) => number;
|
|
22
|
-
isRowExportable?: SpreadsheetGridProps<T>['isRowExportable'];
|
|
23
|
-
dispatch: (action: GridUiAction) => void;
|
|
24
|
-
};
|
|
25
|
-
export type ClipboardController<T extends object> = {
|
|
26
|
-
update: (args: ClipboardControllerArgs<T>) => void;
|
|
27
|
-
handleCopy: () => Promise<void>;
|
|
28
|
-
handlePaste: (event: ClipboardEventLike) => void;
|
|
29
|
-
};
|
|
30
|
-
export declare const computeIsWholeGridSelected: (selection: GridSelection, viewRowCount: number, columnCount: number) => boolean;
|
|
31
|
-
export declare const createClipboardController: <T extends object>() => ClipboardController<T>;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { gridActions } from '../model/gridActions';
|
|
2
|
-
import type { GridColumn, RowModel } from '../model/gridTypes';
|
|
3
|
-
type ReadonlyRef<V> = {
|
|
4
|
-
readonly current: V;
|
|
5
|
-
};
|
|
6
|
-
export type ColumnAutosizeRunnerArgs<T> = {
|
|
7
|
-
rowModelRef: ReadonlyRef<RowModel<T>>;
|
|
8
|
-
gridRootRef: ReadonlyRef<HTMLElement | null>;
|
|
9
|
-
columnWidthsRef: ReadonlyRef<Record<string, number>>;
|
|
10
|
-
dispatch: (action: ReturnType<typeof gridActions.syncColumnWidths>) => void;
|
|
11
|
-
};
|
|
12
|
-
export type ColumnAutosizeRunner<T> = {
|
|
13
|
-
update: (args: ColumnAutosizeRunnerArgs<T>) => void;
|
|
14
|
-
runAutosize: (columns: GridColumn<T>[]) => Promise<void>;
|
|
15
|
-
subscribe: (listener: () => void) => () => void;
|
|
16
|
-
getSnapshot: () => boolean;
|
|
17
|
-
dispose: () => void;
|
|
18
|
-
};
|
|
19
|
-
export declare const createColumnAutosizeRunner: <T>() => ColumnAutosizeRunner<T>;
|
|
20
|
-
export {};
|