@aiquants/spreadsheet 0.13.0 → 0.14.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/CHANGELOG.md +38 -8
- package/README.md +33 -6
- package/dist/index.cjs +13 -13
- package/dist/index.d.cts +12 -11
- package/dist/index.d.ts +12 -11
- package/dist/index.js +981 -976
- package/dist/styles/spreadsheet.standalone.css +1 -1
- package/dist/view.cjs +1 -0
- package/dist/view.d.cts +49 -0
- package/dist/view.d.ts +49 -0
- package/dist/view.js +14 -0
- package/package.json +20 -7
- package/src/Spreadsheet.tsx +20 -50
- package/src/grid/GridBody.tsx +7 -6
- package/src/header/ColumnHeader.tsx +10 -6
- package/src/header/RowHeader.tsx +4 -3
- package/src/model/labels.ts +23 -22
- package/src/model/viewStore.ts +1 -4
- package/src/overlay/SelectionOverlay.tsx +3 -3
- package/src/popover/placement.ts +4 -4
- package/src/styles/spreadsheet.css +1 -1
- package/src/view.ts +62 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@aiquants/spreadsheet` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.14.0 (2026-10-26)
|
|
6
|
+
|
|
7
|
+
グリッドの固定の寸法と拡大率の規則を、グリッドを読み込まない副パス `@aiquants/spreadsheet/view` として公開する。グリッド自身も
|
|
8
|
+
同じ定義を読むので、描画と操作の動作は変わらない。
|
|
9
|
+
|
|
10
|
+
- **新機能 — `@aiquants/spreadsheet/view`**: 列見出しの帯の高さ `SPREADSHEET_COLUMN_HEADER_HEIGHT_PX` (24)、行見出しの帯の幅
|
|
11
|
+
`SPREADSHEET_ROW_HEADER_WIDTH_PX` (64)、スクロールバーの太さ `SPREADSHEET_SCROLLBAR_SIZE_PX` (12)、拡大率の範囲
|
|
12
|
+
`SPREADSHEET_ZOOM_MIN` / `SPREADSHEET_ZOOM_MAX` (0.25 / 4)、範囲へ切る `clampSpreadsheetZoom` (有限でない値は `RangeError`)、
|
|
13
|
+
操作と同じ 0.1 刻みの `stepSpreadsheetZoom` (範囲では切らない) を公開する。React も `@aiquants/virtualscroll` も読まないため、
|
|
14
|
+
グリッドを遅延で読み込むホストが、読み込む前に箱の大きさを決めたり (サーバー描画の代わりの表など)、自前の拡大縮小のボタンを
|
|
15
|
+
グリッドと同じ数で動かしたりできる。
|
|
16
|
+
- **定義は 1 か所**: グリッドの拡大率の切り詰めと刻み、スクロールバーの太さ (内部の `SCROLLBAR_WIDTH` は廃止) は `view` の定義を
|
|
17
|
+
読む。列見出しの帯の高さと行見出しの帯の幅は、スタイルシートのカスタムプロパティ (`--aqsp-col-header-height` /
|
|
18
|
+
`--aqsp-row-header-width`) と同じ値であることを試験で確かめる。
|
|
19
|
+
- **peer の下限が上がる**: `@aiquants/virtualscroll` の peer の範囲が `^3.22.0` になる (ワークスペースの版から公開時に展開する範囲。
|
|
20
|
+
0.13.1 は `^3.8.0`)。コードが 3.22 の機能を使うわけではないが、それより古い virtualscroll と組み合わせる利用者は peer の警告を
|
|
21
|
+
受けるので、virtualscroll を 3.22 へ上げる。
|
|
22
|
+
- **検証 — 走行台帳 (census の錨は本未リリース節)**: 単体 / 統合 **93 ファイル 4,094 テスト** 全緑 —
|
|
23
|
+
0.13.0 出荷時の 92 ファイル 4,083 に v0.14 進行分 +11 (新規 `view` 10、既存 `viewStore` −1 (スクロールバーの太さの試験を
|
|
24
|
+
`view` へ移した)、0.13.0 / 0.13.1 節の出荷時凍結ピンで `hxCensus` +2)。barrel (`src/index.ts`) の export は変わらない。
|
|
25
|
+
`tsc --noEmit` / biome 0。
|
|
26
|
+
|
|
27
|
+
## 0.13.1 (2026-10-03)
|
|
28
|
+
|
|
29
|
+
公開物から社内の作業資料への参照を除き、公開の経路に漏洩ガードを通す。API と描画の動作は変わらない。
|
|
30
|
+
|
|
31
|
+
- **公開物 — 社内資料への参照を除去**: 同梱の CHANGELOG にあった社内の設計・報告文書への参照 (tarball の中ではリンク切れになり、
|
|
32
|
+
作業資料の存在を明かしていた) を、参照先が決めていた内容の記述へ改めた。
|
|
33
|
+
- **公開の経路 — 漏洩ガード**: `prepublishOnly` と `publish:patch` / `minor` / `major` が、詰めた tarball を公開漏洩ガードにかけてから送る。
|
|
34
|
+
- **peer の下限が上がる**: `@aiquants/virtualscroll` の peer の範囲が `^3.8.0` になる (ワークスペースの版から公開時に展開する範囲。
|
|
35
|
+
0.13.0 は `^3.7.0`)。コードが 3.8 の機能を使うわけではないが、3.7 系と組み合わせる利用者は peer の警告を受けるので、virtualscroll を 3.8 へ上げる。
|
|
36
|
+
|
|
5
37
|
## 0.13.0 (2026-09-24)
|
|
6
38
|
|
|
7
39
|
v0.13 は UI クロームの多言語化の列車 — 英語 (`"en"`) を既定、日本語 (`"ja"`) を第 2 言語とし、ラベル・組込み
|
|
@@ -136,7 +168,7 @@ v0.11 はエンジン素通し 3 プロップ (`overscanRows` / `overscanCols` /
|
|
|
136
168
|
## 0.10.0 (2026-09-22)
|
|
137
169
|
|
|
138
170
|
v0.10 は読み取り専用セル (isCellReadOnly) — 統治文書は
|
|
139
|
-
|
|
171
|
+
v0.10 実装計画 (以下「v0.10 計画 §N」)。
|
|
140
172
|
同乗スコープはタップサークル素通し (H2) と貼り付けフィードバック (onPasteResolved)。
|
|
141
173
|
|
|
142
174
|
- **新機能 — 読み取り専用セル (ADR-27..31)**: `SpreadsheetDataAdapter.isCellReadOnly?: (row, col) => boolean`
|
|
@@ -190,7 +222,7 @@ v0.10 は読み取り専用セル (isCellReadOnly) — 統治文書は
|
|
|
190
222
|
## 0.9.0 (2026-09-18)
|
|
191
223
|
|
|
192
224
|
v0.9 は末尾凍結 (trailing freeze) と強い凍結分割線 (freeze divider) — 統治文書は
|
|
193
|
-
|
|
225
|
+
v0.9 実装計画。エンジンは
|
|
194
226
|
`@aiquants/virtualscroll` 3.5.0 (`frozenTrailingCols` / `frozenTrailingRows` + `getFrozenSize()` 10 フィールド)。
|
|
195
227
|
|
|
196
228
|
- **新機能 — 末尾凍結 (ADR-18 / ADR-19)**: `SpreadsheetProps.frozenTrailingColumns` / `frozenTrailingRows`
|
|
@@ -241,7 +273,7 @@ v0.9 は末尾凍結 (trailing freeze) と強い凍結分割線 (freeze divider)
|
|
|
241
273
|
|
|
242
274
|
v0.8 はリッチセル — 宣言式のセル書式 / 注釈 / 検証 / 条件付き書式、Excel / Google Sheets クリップボード相互運用、
|
|
243
275
|
再バインド可能コマンド、ポップオーバー層、強調帯、数式参照ポインティング。統治文書は
|
|
244
|
-
|
|
276
|
+
v0.8 実装計画 (以下「計画 §N」)、横断仕様は
|
|
245
277
|
新設の分冊 [08-rich-cells](<./docs/specs/2026.09.05 [AI] 08-rich-cells.md>)。未宣言能力 + 既定 props は v0.7 と
|
|
246
278
|
**DOM byte 同一** (値フォールバックではなく `adapter.x === undefined` の能力分岐 — `richCells.spec.tsx` /
|
|
247
279
|
`popovers.spec.tsx` / `highlightBands.spec.tsx` の whole-root 封印)、後方互換 shim は置かない (クリーンカットオーバー —
|
|
@@ -955,8 +987,7 @@ v0.8 はリッチセル — 宣言式のセル書式 / 注釈 / 検証 / 条件
|
|
|
955
987
|
|
|
956
988
|
## 0.6.0 (未リリース)
|
|
957
989
|
|
|
958
|
-
v0.6 は列凍結 (0.5.0) の**行軸への完全対称化**。本節の § は v0.6
|
|
959
|
-
(`docs/plans/2026.09.02 [AI] spreadsheet-v0.6-frozen-rows-plan.md`) を指す。
|
|
990
|
+
v0.6 は列凍結 (0.5.0) の**行軸への完全対称化**。本節の § は v0.6 実装プランを指す。
|
|
960
991
|
|
|
961
992
|
- **新機能 — 行凍結 (`frozenRows`)**: 先頭 N 行を上端に凍結するライブ prop (v0.6 計画 —
|
|
962
993
|
VirtualGrid `frozenLeadingRows` (virtualscroll 3.4.0 初出) の消費)。検証はエンジンの
|
|
@@ -1221,8 +1252,7 @@ v0.3 機能トリオ (要望改善)。本節の § は v0.2 実装プランを
|
|
|
1221
1252
|
## 0.2.0 (未リリース)
|
|
1222
1253
|
|
|
1223
1254
|
ヘッダーリサイズ / unhide UI・フィルハンドル・非表示トラック・複数レンジ操作・キーボード拡張を
|
|
1224
|
-
柱とするリリース。本節の § 番号は v0.2
|
|
1225
|
-
(`docs/plans/2026.08.24 [AI] spreadsheet-v0.2-implementation-plan.md`) を指す
|
|
1255
|
+
柱とするリリース。本節の § 番号は v0.2 実装プランを指す
|
|
1226
1256
|
(0.1.0 節の § は v0.1 プランのまま)。
|
|
1227
1257
|
|
|
1228
1258
|
- **修正 — 実ブラウザで dblclick autofit / 復元が不到達だった問題**: リサイズドラッグの
|
|
@@ -1295,7 +1325,7 @@ v0.3 機能トリオ (要望改善)。本節の § は v0.2 実装プランを
|
|
|
1295
1325
|
ヘッダーリサイズ / fill) が press / cleanup の repaint エッジ規律を共有 (§8.2)
|
|
1296
1326
|
- **品質**: 敵対的レビュー 9 ラウンド (V2-A R1〜R4 / V2-B R1(B)〜R5(B) — 双方の最終ラウンドで
|
|
1297
1327
|
5 レンズ全員 astonished)、変異検証台帳 1 冊追加
|
|
1298
|
-
(
|
|
1328
|
+
(安定 ID [M71]〜[M134]。
|
|
1299
1329
|
[M120] の bump 面は測定等価の反証記録として残置)、単体 / 統合 464・E2E 54・
|
|
1300
1330
|
tsc / biome / markdownlint 全 0
|
|
1301
1331
|
|
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ deep-reach proof route.
|
|
|
50
50
|
pnpm add @aiquants/spreadsheet @aiquants/virtualscroll
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
`react` / `react-dom` (`^19.2`) and `@aiquants/virtualscroll` (
|
|
53
|
+
`react` / `react-dom` (`^19.2`) and `@aiquants/virtualscroll` are peer dependencies (the ranges are `peerDependencies` in the published `package.json`; the code itself needs virtualscroll 3.7.0 or later, whose label catalogs back the `locale` prop).
|
|
54
54
|
|
|
55
55
|
> **Browser floor**: the built-in cell display formats (v0.7) require `Intl.NumberFormat` v3
|
|
56
56
|
> (string mathematical-value input + `signDisplay: "negative"`) — **Chrome/Edge 106+, Firefox 116+,
|
|
@@ -458,8 +458,8 @@ keyboard input — this is what makes IME composition, clipboard events, and 16M
|
|
|
458
458
|
- `labels.sinkAriaLabel` labels the input surface (its catalog value explains the Escape hand-off — in English by default, in
|
|
459
459
|
Japanese under `locale="ja"`); `labels.rootAriaLabel` labels the grid container (`"Spreadsheet"` / `"スプレッドシート"`). Both
|
|
460
460
|
live in the `labels` prop (v0.8 — the former `sinkAriaLabel` / `rootAriaLabel` props were folded into it; together with the 19
|
|
461
|
-
popover strings they are the 21 package-owned keys, and the
|
|
462
|
-
empty state). The `locale` prop picks the catalog (`SPREADSHEET_LABEL_CATALOGS[locale]`), and an omitted or `undefined` key
|
|
461
|
+
popover strings they are the 21 package-owned keys, and the engine keys (every `VIRTUAL_SCROLL_LABEL_KEYS` entry) name the
|
|
462
|
+
grid scrollbars' chrome, such as the arrows and edge pills, and the empty state). The `locale` prop picks the catalog (`SPREADSHEET_LABEL_CATALOGS[locale]`), and an omitted or `undefined` key
|
|
463
463
|
keeps that catalog's value. Switching `locale` localizes both names together; if you override them, **override both**, and a
|
|
464
464
|
replacement `sinkAriaLabel` **must keep the Escape-exit notice** — the word `Escape` must appear, otherwise the grid throws a `RangeError` while rendering: Escape moves
|
|
465
465
|
focus from the sink to the root, and without the notice (or with an unlabeled landing point) screen-reader users are stranded
|
|
@@ -574,7 +574,7 @@ the zoom keys shadow that chord (the zoom token space `Ctrl+Alt` and `Ctrl+Alt+S
|
|
|
574
574
|
`preventDefault`ed are respected and ignored. In controlled mode, gestures step from the last *requested* value: a parent that rejects a
|
|
575
575
|
request without re-rendering leaves the next detent continuing from the rejected value until any re-render re-syncs it —
|
|
576
576
|
enforce a tighter range by clamping in `onZoomChange` and re-rendering with the clamped value. Wheel/key steps land on the nearest 0.1 detent in the travel direction
|
|
577
|
-
(from the 0.25 bound the first step up is 0.3); Alt+Wheel with no vertical component (tilt wheel / horizontal pan) is
|
|
577
|
+
(from the 0.25 bound the first step up is 0.3; the same step is `stepSpreadsheetZoom` of `@aiquants/spreadsheet/view`); Alt+Wheel with no vertical component (tilt wheel / horizontal pan) is
|
|
578
578
|
passed through to the browser.
|
|
579
579
|
|
|
580
580
|
Navigation is **visible-track aware**: arrows and Shift-arrows step to the next *visible* row/column (a hidden run is
|
|
@@ -734,7 +734,7 @@ The grid's own UI chrome ships in English (`"en"`, the default) and Japanese (`"
|
|
|
734
734
|
| `renderCell` | `(ctx: RenderCellContext) => ReactNode` — `RenderCellContext = { row; col; text; displayText }` (exported) | Custom cell rendering. **Must be identity-stable** (`useCallback`) — an inline arrow defeats the cell memo and re-renders every visible cell each pass. **Per-cell opt-out** (v0.12 — ADR-32): return `undefined` and that one cell falls back to the package default rendering, value-format engine included (only `undefined` opts out — `null`, `false` and any other ReactNode are custom output, i.e. an empty custom cell). `ctx.text` is the raw canonical string; `ctx.displayText` is the value-format display — the exact string the default rendering, announcements, the HTML clipboard and autofit share, so a custom cell that renders it can never drift from what gets copied. Autofit stays a whole-grid no-op while `renderCell` is set. |
|
|
735
735
|
| `maxOperationCells` | `number` | Bulk-operation ceiling (default 100,000). Row fan-out resize gates at 1/10 of it and autofit at 1/4 — see "Operation limits". |
|
|
736
736
|
| `enableResizeUI` | `boolean` | Default `true`. `false` removes resize strips **and unhide markers** — hidden tracks then have no in-package restore path (imperative API only). |
|
|
737
|
-
| `zoom` / `defaultZoom` / `onZoomChange` | `number` / `number` / `(zoom: number) => void` | Package zoom (v0.3): visual transform scale, logic stays in logical px. Controlled (`zoom`) or uncontrolled (`defaultZoom`, default 1). Gestures — Alt+Wheel and Ctrl+Alt+`=` / `+` / `-` / `0` (reset; the four keys match the produced character, Shift-agnostic; AltGraph-state chords are never claimed — legacy-shape defense in depth, see Keymap notes) — request via `onZoomChange`; Ctrl+Wheel is deliberately unbound (the browser owns it). Clamped to [0.25,
|
|
737
|
+
| `zoom` / `defaultZoom` / `onZoomChange` | `number` / `number` / `(zoom: number) => void` | Package zoom (v0.3): visual transform scale, logic stays in logical px. Controlled (`zoom`) or uncontrolled (`defaultZoom`, default 1). Gestures — Alt+Wheel and Ctrl+Alt+`=` / `+` / `-` / `0` (reset; the four keys match the produced character, Shift-agnostic; AltGraph-state chords are never claimed — legacy-shape defense in depth, see Keymap notes) — request via `onZoomChange`; Ctrl+Wheel is deliberately unbound (the browser owns it). Clamped to [`SPREADSHEET_ZOOM_MIN`, `SPREADSHEET_ZOOM_MAX`] (0.25–4, `clampSpreadsheetZoom` of `@aiquants/spreadsheet/view`); a non-finite value (NaN / ±Infinity) throws a `RangeError` (fail-fast — no silent substitution). |
|
|
738
738
|
| `enableFillHandle` | `boolean` | Default `true`. Fill writes through `applyEdits` — disable for read-only adapters. |
|
|
739
739
|
| `onSelectionChange` | `(selection: SelectionState) => void` | Fires on confirmed changes only — never on mount, and never mid-drag with one documented exception: the header subtract reservation converts to a real press on the first movement that both really changes the pointer coordinates and lands on a different header index (in-index jitter and synthetic same-position moves keep the reservation), firing the deferred press replace mid-drag (total fire count matches v0.1). |
|
|
740
740
|
| `onOperationRejected` | `(op, cellCount, reason) => void` | `op: SpreadsheetOperation` (`"copy" \| "cut" \| "delete" \| "paste" \| "fill" \| "resize" \| "format" \| "calc" \| "annotate"` — the three v0.8 names widen the callback's parameter, a 0.x-breaking change for consumers that annotated a narrower `op`), `reason: "limit" \| "bounds"` — count definitions and per-op ceilings under "Operation limits". |
|
|
@@ -744,7 +744,7 @@ The grid's own UI chrome ships in English (`"en"`, the default) and Japanese (`"
|
|
|
744
744
|
| `onUndoRequest` / `onRedoRequest` | `() => void` | `Ctrl+Z` / `Ctrl+Y` / `Ctrl+Shift+Z` relays. |
|
|
745
745
|
| `formatCellAnnouncement` / `formatSelectionAnnouncement` | formatter | Opt-in screen-reader announcements (see above). |
|
|
746
746
|
| `formatCommandAnnouncement` | `(ctx: { command: SpreadsheetCommand; state: boolean \| null }) => string` | Opt-in (v0.8) live-region text after a successful format command; `state` is the toggle's post-state (`bold` / `italic` / `underline` / `strikethrough` / colour toggles), `null` for non-toggle commands. Also makes the live region render on its own. |
|
|
747
|
-
| `labels` | `SpreadsheetLabelOverrides` (any subset of the
|
|
747
|
+
| `labels` | `SpreadsheetLabelOverrides` (any subset of the `SpreadsheetLabels` keys) | v0.8 — replaces the `sinkAriaLabel` / `rootAriaLabel` props (folded in) and names every UI chrome string: the engine keys shared with `@aiquants/virtualscroll` (every entry of the installed engine's `VIRTUAL_SCROLL_LABEL_KEYS`, such as `scrollUp`, `scrollDown`, `scrollToTop`, `noItems` — they reach the grid scrollbars and the empty state) and the 21 package keys (`sinkAriaLabel`, `rootAriaLabel`, `noteTitle`, `noteSave`, `noteEscapeHint`, `noteRejectedHint`, `noteViewerHint` (v0.10 — the note hint in the read-only viewer mode, which never promises a save), `commentTitle`, `commentReply`, `commentPost`, `commentCount` (string or `(n) => string`), `linkTitle`, `linkTextField`, `linkUrlField`, `linkOpen`, `linkSave`, `listTitle`, `listEmpty`, `listMore` (string or `(n) => string` — the validation-list picker mounts at most 500 options and announces the hidden-match count on this row), `messageTitle`, `popoverClose`). Omitted / `undefined` keys keep the value of the `locale` catalog (`SPREADSHEET_LABEL_CATALOGS[locale]`); an unknown key (including the removed `validationRejected` / `indicatorNote` / `indicatorComment` / `indicatorInvalid`) or a blank / non-string value throws a `RangeError` during render; `sinkAriaLabel` must mention `Escape` (`RangeError` during render otherwise). |
|
|
748
748
|
| `locale` | `SpreadsheetLocale` (`"en" \| "ja"`) | v0.13 — the UI chrome language, default `"en"`: selects the label catalog, the built-in validation messages (`onValidationRejected`'s `message` and the message popover) and the grid scrollbars' arrow names / empty state. Any other value (`"fr"`, `"ja-JP"`, `"EN"`, `""`, `null`) throws a `RangeError` during render, even on an empty grid — there is no language negotiation. Unrelated to `formats.locale`. See "Localization". |
|
|
749
749
|
| `shortcuts` | `ShortcutOverrides` = `{ [command]?: readonly string[] \| null }` | v0.8 — rebinds command chords (array = replace the default list, `null` = unbind); validated during render with `RangeError`s (see "Command shortcuts"). Identity-stable — consulted through a ref. |
|
|
750
750
|
| `colors` | `{ fill?: readonly string[]; font?: readonly string[] }` | v0.8 — the five `#rrggbb` slots of `fillColorCustom1..5` / `fontColorCustom1..5` (exactly 5 six-digit hex strings per axis, `RangeError` otherwise); an omitted axis uses `DEFAULT_CUSTOM_COLORS` (`#FF0000` / `#00FF00` / `#0000FF` / `#FFFF00` / `#FF00FF`, exported). |
|
|
@@ -817,6 +817,33 @@ that fails to parse `"unreadable"` — each fires `onClipboardUnavailable` and r
|
|
|
817
817
|
called; `"empty"` means nothing was written (an own payload whose identity was suppressed, every cell skipped, a mode without cell
|
|
818
818
|
aspects such as `columnWidths` — sizes may still have been applied).
|
|
819
819
|
|
|
820
|
+
## View geometry and zoom rules (`@aiquants/spreadsheet/view`)
|
|
821
|
+
|
|
822
|
+
`@aiquants/spreadsheet/view` publishes the grid's fixed geometry and its zoom rules without loading the grid (no React, no
|
|
823
|
+
`@aiquants/virtualscroll`): a host can size a box for the grid before the grid itself is loaded (a lazily imported grid, a
|
|
824
|
+
server-rendered placeholder) and drive the zoom from its own controls with exactly the numbers the grid uses. The grid reads the
|
|
825
|
+
same definitions.
|
|
826
|
+
|
|
827
|
+
| Export | Value / contract |
|
|
828
|
+
| :--- | :--- |
|
|
829
|
+
| `SPREADSHEET_COLUMN_HEADER_HEIGHT_PX` | `24` — the column-header band (`--aqsp-col-header-height`) |
|
|
830
|
+
| `SPREADSHEET_ROW_HEADER_WIDTH_PX` | `64` — the row-header band (`--aqsp-row-header-width`) |
|
|
831
|
+
| `SPREADSHEET_SCROLLBAR_SIZE_PX` | `12` — each scroll bar, reserved on both axes whether or not the grid scrolls |
|
|
832
|
+
| `SPREADSHEET_ZOOM_MIN` / `SPREADSHEET_ZOOM_MAX` | `0.25` / `4` — the bounds of `zoom` |
|
|
833
|
+
| `clampSpreadsheetZoom(value)` | Clamps into the bounds without quantizing; a non-finite value throws a `RangeError` |
|
|
834
|
+
| `stepSpreadsheetZoom(value, direction)` | The gestures' 0.1 detent step (`1` in, `-1` out), unclamped: from an off-grid value it lands on the nearest detent in the travel direction (0.25 → 0.3 / 0.2) |
|
|
835
|
+
|
|
836
|
+
At zoom 1 with uniform rows, a box that shows `n` rows exactly is
|
|
837
|
+
`SPREADSHEET_COLUMN_HEADER_HEIGHT_PX + n × defaultRowHeight + SPREADSHEET_SCROLLBAR_SIZE_PX` px tall. Zoom buttons of a host that
|
|
838
|
+
controls `zoom` step the same way the gestures do:
|
|
839
|
+
|
|
840
|
+
```tsx
|
|
841
|
+
import { clampSpreadsheetZoom, stepSpreadsheetZoom } from "@aiquants/spreadsheet/view"
|
|
842
|
+
|
|
843
|
+
const zoomIn = () => setZoom((zoom) => clampSpreadsheetZoom(stepSpreadsheetZoom(zoom, 1)))
|
|
844
|
+
// <Spreadsheet zoom={zoom} onZoomChange={setZoom} … />
|
|
845
|
+
```
|
|
846
|
+
|
|
820
847
|
## Clipboard utilities (public)
|
|
821
848
|
|
|
822
849
|
`parseTsv` / `serializeTsv` / `estimatePasteCellCount` / `expandPaste` are exported for building custom paste UIs and
|