@aiquants/spreadsheet 0.15.0 → 0.16.1
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 +105 -0
- package/README.md +209 -23
- package/dist/index.cjs +8 -8
- package/dist/index.d.cts +123 -5
- package/dist/index.d.ts +123 -5
- package/dist/index.js +4315 -4166
- package/dist/styles/spreadsheet.css +1 -1
- package/dist/styles/spreadsheet.standalone.css +1 -1
- package/dist/tsv.cjs +8 -1
- package/dist/tsv.d.cts +79 -4
- package/dist/tsv.d.ts +79 -4
- package/dist/tsv.js +85 -3
- package/dist/view-BkwCUzTl.cjs +1 -0
- package/dist/view-DycJZi_t.js +41 -0
- package/dist/view.cjs +1 -1
- package/dist/view.d.cts +31 -7
- package/dist/view.d.ts +31 -7
- package/dist/view.js +12 -12
- package/package.json +3 -3
- package/src/Spreadsheet.tsx +110 -14
- package/src/clipboard/tsv.ts +236 -89
- package/src/clipboard/useClipboard.ts +3 -2
- package/src/commands/useCommands.ts +43 -1
- package/src/grid/GridBody.tsx +51 -6
- package/src/header/resizeGates.ts +34 -0
- package/src/header/useHeaderResize.ts +83 -16
- package/src/index.ts +1 -1
- package/src/keyboard/shortcuts.ts +67 -0
- package/src/model/dataAdapter.ts +74 -0
- package/src/overlay/SelectionOverlay.tsx +22 -2
- package/src/styles/spreadsheet.css +193 -123
- package/src/tsv.ts +17 -10
- package/src/view.ts +67 -14
- package/dist/tsv-B6TDExlk.js +0 -91
- package/dist/tsv-BHXbWK5W.cjs +0 -8
package/dist/view.d.ts
CHANGED
|
@@ -13,9 +13,22 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export declare const clampSpreadsheetZoom: (value: number) => number;
|
|
15
15
|
|
|
16
|
+
/** Column index (0-based) to Excel-style name (0 → A, 26 → AA, 16383 → XFD). / 列 index → 列名。 */
|
|
17
|
+
export declare const columnIndexToName: (index: number) => string;
|
|
18
|
+
|
|
16
19
|
/** The height of the column-header band in CSS px (`--aqsp-col-header-height` in the stylesheet). 列見出しの帯の高さ (CSS px。スタイルシートの `--aqsp-col-header-height`)。 */
|
|
17
20
|
export declare const SPREADSHEET_COLUMN_HEADER_HEIGHT_PX = 24;
|
|
18
21
|
|
|
22
|
+
/** The default column width in CSS px, the width of every column the host does not size. 既定の列幅 (CSS px。ホストが寸法を決めない列の幅)。 */
|
|
23
|
+
export declare const SPREADSHEET_DEFAULT_COLUMN_WIDTH_PX = 100;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The default of `maxOperationCells` (ADR-12): the largest number of cells one operation (paste, fill, clear, format)
|
|
27
|
+
* may touch before the grid refuses it.
|
|
28
|
+
* `maxOperationCells` の既定値 (ADR-12)。1 つの操作 (貼り付け・フィル・消去・書式) が触れてよいセル数の上限で、超えるとグリッドが拒む。
|
|
29
|
+
*/
|
|
30
|
+
export declare const SPREADSHEET_MAX_OPERATION_CELLS = 100000;
|
|
31
|
+
|
|
19
32
|
/** The width of the row-header band in CSS px (`--aqsp-row-header-width` in the stylesheet). 行見出しの帯の幅 (CSS px。スタイルシートの `--aqsp-row-header-width`)。 */
|
|
20
33
|
export declare const SPREADSHEET_ROW_HEADER_WIDTH_PX = 64;
|
|
21
34
|
|
|
@@ -33,16 +46,27 @@ export declare const SPREADSHEET_ZOOM_MAX = 4;
|
|
|
33
46
|
export declare const SPREADSHEET_ZOOM_MIN = 0.25;
|
|
34
47
|
|
|
35
48
|
/**
|
|
36
|
-
* The
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* 拡大縮小の操作 (Alt+ホイールと Ctrl+Alt+= / -)
|
|
40
|
-
* (
|
|
41
|
-
|
|
49
|
+
* The zoom ladder of the gestures (Alt+Wheel and Ctrl+Alt+= / -): the geometric series `2^(k/4)` for every integer `k`
|
|
50
|
+
* from −8 to 8, rounded to hundredths, so it runs from SPREADSHEET_ZOOM_MIN (0.25) through 1 to SPREADSHEET_ZOOM_MAX (4)
|
|
51
|
+
* in steps of about 19 % and every power of two is a step.
|
|
52
|
+
* 拡大縮小の操作 (Alt+ホイールと Ctrl+Alt+= / -) の段。−8 から 8 までの整数 `k` の等比数列 `2^(k/4)` を 100 分の 1 に丸めた
|
|
53
|
+
* もので、SPREADSHEET_ZOOM_MIN (0.25) から 1 を通って SPREADSHEET_ZOOM_MAX (4) まで約 19 % ずつ進み、2 の累乗はどれも段に乗る。
|
|
54
|
+
*/
|
|
55
|
+
export declare const SPREADSHEET_ZOOM_STEPS: readonly number[];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The next step of SPREADSHEET_ZOOM_STEPS in the travel direction, from any finite value, including a value off the
|
|
59
|
+
* ladder (a host's 1.25 goes to 1.41 up and 1.19 down). The input is clamped first, as clampSpreadsheetZoom does, and
|
|
60
|
+
* the step is taken from the clamped value: 9 zooms out to 3.36 and 0.01 zooms in to 0.3, and at a bound the result stays
|
|
61
|
+
* at that bound (in from SPREADSHEET_ZOOM_MAX, out from SPREADSHEET_ZOOM_MIN). A non-finite value fails fast.
|
|
62
|
+
* 進む向きの SPREADSHEET_ZOOM_STEPS の次の段を返す処理。段の外の値 (ホストの 1.25 は上で 1.41、下で 1.19) を含む、どの有限の
|
|
63
|
+
* 値からでも求める。入力を先に clampSpreadsheetZoom と同じく範囲へ切り、切った値から一歩進む (9 からの縮小は 3.36、0.01 からの
|
|
64
|
+
* 拡大は 0.3)。端では端に留まる (SPREADSHEET_ZOOM_MAX からの拡大、SPREADSHEET_ZOOM_MIN からの縮小)。有限でない値はすぐに投げる。
|
|
42
65
|
*
|
|
43
66
|
* @param value - The current zoom. 今の拡大率。
|
|
44
67
|
* @param direction - 1 to zoom in, -1 to zoom out. 拡大は 1、縮小は -1。
|
|
45
|
-
* @returns The next
|
|
68
|
+
* @returns The next ladder step in that direction from the clamped input. 範囲へ切った入力からのその向きの次の段。
|
|
69
|
+
* @throws {RangeError} When the value is not finite. 有限でない場合。
|
|
46
70
|
*/
|
|
47
71
|
export declare const stepSpreadsheetZoom: (value: number, direction: 1 | -1) => number;
|
|
48
72
|
|
package/dist/view.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
if (!Number.isFinite(E))
|
|
3
|
-
throw new RangeError(`[spreadsheet] zoom must be a finite number (got ${E})`);
|
|
4
|
-
return Math.min(4, Math.max(0.25, E));
|
|
5
|
-
}, r = (E, t) => t > 0 ? (Math.floor(E * 10 + 1e-6) + 1) / 10 : (Math.ceil(E * 10 - 1e-6) - 1) / 10;
|
|
1
|
+
import { f as _, b as a, d as s, g as A, S as T, h as H, i as P, j as R, a as D, c as O, s as e } from "./view-DycJZi_t.js";
|
|
6
2
|
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
_ as SPREADSHEET_COLUMN_HEADER_HEIGHT_PX,
|
|
4
|
+
a as SPREADSHEET_DEFAULT_COLUMN_WIDTH_PX,
|
|
5
|
+
s as SPREADSHEET_MAX_OPERATION_CELLS,
|
|
6
|
+
A as SPREADSHEET_ROW_HEADER_WIDTH_PX,
|
|
7
|
+
T as SPREADSHEET_SCROLLBAR_SIZE_PX,
|
|
8
|
+
H as SPREADSHEET_ZOOM_MAX,
|
|
9
|
+
P as SPREADSHEET_ZOOM_MIN,
|
|
10
|
+
R as SPREADSHEET_ZOOM_STEPS,
|
|
11
|
+
D as clampSpreadsheetZoom,
|
|
12
|
+
O as columnIndexToName,
|
|
13
|
+
e as stepSpreadsheetZoom
|
|
14
14
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiquants/spreadsheet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Excel-grade spreadsheet grid for React: multi-cell selection, TSV/HTML clipboard, in-place editing on top of @aiquants/virtualscroll",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"LICENSE.md"
|
|
57
57
|
],
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@aiquants/virtualscroll": "^3.22.
|
|
59
|
+
"@aiquants/virtualscroll": "^3.22.1",
|
|
60
60
|
"react": "^19.2.7",
|
|
61
61
|
"react-dom": "^19.2.7"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@aiquants/virtualscroll": "3.22.
|
|
64
|
+
"@aiquants/virtualscroll": "3.22.1",
|
|
65
65
|
"@playwright/test": "^1.59.1",
|
|
66
66
|
"@tailwindcss/cli": "^4.1.18",
|
|
67
67
|
"@testing-library/jest-dom": "^6.9.1",
|
package/src/Spreadsheet.tsx
CHANGED
|
@@ -63,7 +63,7 @@ import { RowHeader } from "./header/RowHeader.tsx"
|
|
|
63
63
|
import { useHeaderResize } from "./header/useHeaderResize.ts"
|
|
64
64
|
import { useHeaderSelection } from "./header/useHeaderSelection.ts"
|
|
65
65
|
import { createEnsureCellVisible } from "./keyboard/ensureCellVisible.ts"
|
|
66
|
-
import { type ShortcutOverrides, type SpreadsheetCommand, validateShortcuts } from "./keyboard/shortcuts.ts"
|
|
66
|
+
import { RESIZE_COMMANDS, type ShortcutOverrides, type SpreadsheetCommand, toAriaKeyShortcut, validateShortcuts } from "./keyboard/shortcuts.ts"
|
|
67
67
|
import { type CommandDispatch, type MoveActiveInput, useGridKeyboard } from "./keyboard/useGridKeyboard.ts"
|
|
68
68
|
import { useAxisModel } from "./model/axisModel.ts"
|
|
69
69
|
import { type CellCommentInput, normalizeAdapterHyperlink } from "./model/cellAnnotations.ts"
|
|
@@ -71,7 +71,7 @@ import { type CellCoord, type CellRange, clampCoord, makeRange, rangeSize, type
|
|
|
71
71
|
import { type CellBorder, type CellFormat, type CellFormatPatch, formatCellValue } from "./model/cellFormat.ts"
|
|
72
72
|
import { type EditPaint, IDLE_EDIT_PAINT, normalizeColor, resolveEditPaint } from "./model/cellPaint.ts"
|
|
73
73
|
import { buildCfIndex, type CfIndex, type CfMemo, createCfMemo, resolvePaintedFormat } from "./model/conditionalFormat.ts"
|
|
74
|
-
import { type CellEdit, type EditContext, type EditRejection, type EditSource, isReadOnlyCell, lazyCellText, type ReadOnlyRejection, readOnlyDeclared, resolveCellAlign, type SpreadsheetDataAdapter } from "./model/dataAdapter.ts"
|
|
74
|
+
import { type CellEdit, type EditContext, type EditRejection, type EditSource, isReadOnlyCell, lazyCellText, type ReadOnlyRejection, readOnlyAdapterView, readOnlyDeclared, resolveCellAlign, type SpreadsheetDataAdapter } from "./model/dataAdapter.ts"
|
|
75
75
|
import { type DataValidationRule, type ValidationVerdict, validateCellText } from "./model/dataValidation.ts"
|
|
76
76
|
import type { ReferenceAxis } from "./model/formulaRefs.ts"
|
|
77
77
|
import { colToViewportX, rowToViewportY } from "./model/frozenPartition.ts"
|
|
@@ -85,7 +85,7 @@ import { clampSelectionToGrid, reduceActiveCycle, reduceSelectionExtend, reduceS
|
|
|
85
85
|
import { useAutoScroller } from "./selection/useAutoScroller.ts"
|
|
86
86
|
import { type PointDragOwner, usePointDrag } from "./selection/usePointDrag.ts"
|
|
87
87
|
import { useSelectionEngine } from "./selection/useSelectionEngine.ts"
|
|
88
|
-
import { clampSpreadsheetZoom, stepSpreadsheetZoom } from "./view.ts"
|
|
88
|
+
import { clampSpreadsheetZoom, SPREADSHEET_DEFAULT_COLUMN_WIDTH_PX, SPREADSHEET_MAX_OPERATION_CELLS, stepSpreadsheetZoom } from "./view.ts"
|
|
89
89
|
|
|
90
90
|
export type { SelectionState } from "./selection/selectionState.ts"
|
|
91
91
|
|
|
@@ -187,6 +187,15 @@ export type SpreadsheetProps = {
|
|
|
187
187
|
* 位置に依らず常時可視で、可視化の縦成分は no-op。飛行中の変更は全ドラッグを取り消す。
|
|
188
188
|
*/
|
|
189
189
|
frozenRows?: number
|
|
190
|
+
/**
|
|
191
|
+
* The leading rows whose cells are column headers (default 0): their cells take `role="columnheader"` and the
|
|
192
|
+
* stylesheet's header-row paint (`--aqsp-header-row-*`), so assistive technology reads them as the headers of the
|
|
193
|
+
* columns below. An integer in [0, frozenRows]: header rows are always frozen rows; anything else is a RangeError
|
|
194
|
+
* during render.
|
|
195
|
+
* 先頭の見出しの行数 (既定 0)。そのセルは `role="columnheader"` とスタイルシートの見出し行の塗り (`--aqsp-header-row-*`)
|
|
196
|
+
* を受け、支援技術は下の列の見出しとして読む。[0, frozenRows] の整数 (見出しの行は常に凍結行)。それ以外は描画中の RangeError。
|
|
197
|
+
*/
|
|
198
|
+
headerRows?: number
|
|
190
199
|
/**
|
|
191
200
|
* Freezes the LAST N columns at the right edge (v0.9 — the VirtualGrid frozenTrailingCols
|
|
192
201
|
* pass-through; LIVE prop, default 0). Validation SSOT is the engine's fail-fast contract
|
|
@@ -382,6 +391,26 @@ export type SpreadsheetProps = {
|
|
|
382
391
|
* region は溢れない。
|
|
383
392
|
*/
|
|
384
393
|
formatReadOnlyAnnouncement?: (rejection: ReadOnlyRejection) => string
|
|
394
|
+
/**
|
|
395
|
+
* Makes the whole grid read-only (default false): `aria-readonly="true"` on the `role="grid"` element and on the
|
|
396
|
+
* hidden input, and every cell is read-only whatever the adapter's `isCellReadOnly` says, so every write is refused
|
|
397
|
+
* through `onReadOnlyRejected` / `formatReadOnlyAnnouncement` and the cell announcement's `readOnly` is true. View
|
|
398
|
+
* changes stay available: selection, copy, scrolling, zoom, resizing, hiding and highlighting.
|
|
399
|
+
* グリッド全体を読み取り専用にする (既定 false)。`role="grid"` の要素と隠しの入力に `aria-readonly="true"` を付け、アダプタの
|
|
400
|
+
* `isCellReadOnly` に関わらず全てのセルを読み取り専用にする (書き込みは全て `onReadOnlyRejected` / `formatReadOnlyAnnouncement`
|
|
401
|
+
* で拒まれ、セルの読み上げの `readOnly` は true)。見た目の変更 (選択・コピー・スクロール・拡大率・寸法・非表示・強調) は使える。
|
|
402
|
+
*/
|
|
403
|
+
readOnly?: boolean
|
|
404
|
+
/**
|
|
405
|
+
* The id of the element that names the grid (a caption or heading of the host): the `role="grid"` element and the
|
|
406
|
+
* focusable root (the Escape landing point) both take `aria-labelledby` with it instead of the built-in
|
|
407
|
+
* `labels.rootAriaLabel`. A non-empty id without whitespace; anything else is a RangeError during render. Omitted:
|
|
408
|
+
* the root and the grid both carry `labels.rootAriaLabel` as `aria-label`.
|
|
409
|
+
* グリッドに名前を与える要素の id (ホストの表題や見出し)。`role="grid"` の要素とフォーカスを受けるルート (Escape の着地点) は
|
|
410
|
+
* 組み込みの `labels.rootAriaLabel` の代わりにこれを `aria-labelledby` に持つ。空白を含まない空でない id で、それ以外は描画中の
|
|
411
|
+
* RangeError。省略するとルートとグリッドの両方が `labels.rootAriaLabel` を `aria-label` に持つ。
|
|
412
|
+
*/
|
|
413
|
+
labelledBy?: string
|
|
385
414
|
/**
|
|
386
415
|
* Paste outcome summary (v0.10 plan §3.1): the menu channel mirrors `paste(options)`'s
|
|
387
416
|
* resolve value 1:1 (read refusals and the write-time re-gate included); the event channel
|
|
@@ -446,6 +475,15 @@ export type SpreadsheetProps = {
|
|
|
446
475
|
* コマンドの AT 通知 (opt-in、v0.8 §1.9)。`state` はトグル後状態 (非トグルは null)。live region へ。
|
|
447
476
|
*/
|
|
448
477
|
formatCommandAnnouncement?: (ctx: { command: SpreadsheetCommand; state: boolean | null }) => string
|
|
478
|
+
/**
|
|
479
|
+
* Live-region text after a keyboard resize (increaseColumnWidth / decreaseColumnWidth / increaseRowHeight /
|
|
480
|
+
* decreaseRowHeight / autoFitColumnWidth): the active track's axis, index and new size in CSS px. Omit for silence
|
|
481
|
+
* (no built-in strings); it shares the announcement debounce.
|
|
482
|
+
* キーボードの寸法の変更 (increaseColumnWidth / decreaseColumnWidth / increaseRowHeight / decreaseRowHeight /
|
|
483
|
+
* autoFitColumnWidth) の後の読み上げ文言。アクティブなトラックの軸・番号・新しい寸法 (CSS px) を受ける。省略で無音
|
|
484
|
+
* (組み込み文言なし)。読み上げのデバウンスを共有する。
|
|
485
|
+
*/
|
|
486
|
+
formatResizeAnnouncement?: (ctx: { command: SpreadsheetCommand; axis: "row" | "column"; index: number; size: number }) => string
|
|
449
487
|
/**
|
|
450
488
|
* UI chrome language ("en" | "ja"; default "en"): selects the label catalog, the built-in
|
|
451
489
|
* data-validation messages, and is forwarded to the grid scrollbars (arrow labels,
|
|
@@ -585,6 +623,34 @@ export type SpreadsheetProps = {
|
|
|
585
623
|
* 本層所有)。undefined は宣言不在信号 (エンジン既定) で対象外。非整数・負値は RangeError (メッセージは
|
|
586
624
|
* 生の入力を映す — 数値は Infinity / NaN 込みでそのまま、文字列は引用符つき — JS ホストの "5" は引用符で映る)。
|
|
587
625
|
*/
|
|
626
|
+
/**
|
|
627
|
+
* Validates `headerRows` during render: an integer in [0, frozenRows], since header rows are always frozen rows.
|
|
628
|
+
* 描画中に `headerRows` を確かめる処理。見出しの行は常に凍結行なので [0, frozenRows] の整数。
|
|
629
|
+
*
|
|
630
|
+
* @param headerRows - The prop. prop。
|
|
631
|
+
* @param frozenRows - The frozen leading rows. 先頭の凍結行数。
|
|
632
|
+
* @throws {RangeError} When the value is not an integer in [0, frozenRows]. [0, frozenRows] の整数でない場合。
|
|
633
|
+
*/
|
|
634
|
+
const validateHeaderRows = (headerRows: number, frozenRows: number): void => {
|
|
635
|
+
// 0 は凍結行数を問わず有効: 凍結行数そのものの検証はエンジン (VirtualGrid) の単層に任せ、ここで先に投げない
|
|
636
|
+
if (!Number.isInteger(headerRows) || headerRows < 0 || (headerRows > 0 && headerRows > frozenRows)) {
|
|
637
|
+
throw new RangeError(`[spreadsheet] headerRows must be a whole number from 0 to frozenRows = ${frozenRows} (got ${headerRows})`)
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Validates `labelledBy` during render: omitted, or one non-empty id without whitespace.
|
|
643
|
+
* 描画中に `labelledBy` を確かめる処理。省略か、空白を含まない空でない id 1 つ。
|
|
644
|
+
*
|
|
645
|
+
* @param labelledBy - The prop. prop。
|
|
646
|
+
* @throws {RangeError} When the value is empty or holds whitespace. 空か空白を含む場合。
|
|
647
|
+
*/
|
|
648
|
+
const validateLabelledBy = (labelledBy: string | undefined): void => {
|
|
649
|
+
if (labelledBy !== undefined && (typeof labelledBy !== "string" || labelledBy === "" || /\s/u.test(labelledBy))) {
|
|
650
|
+
throw new RangeError(`[spreadsheet] labelledBy must be one non-empty id without whitespace (got ${JSON.stringify(labelledBy)})`)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
588
654
|
const validateOverscan = (name: "overscanRows" | "overscanCols", value: number | undefined): number | undefined => {
|
|
589
655
|
if (value !== undefined && !(Number.isInteger(value) && value >= 0)) {
|
|
590
656
|
throw new RangeError(`[spreadsheet] ${name} must be a non-negative integer (got ${typeof value === "string" ? JSON.stringify(value) : String(value)})`)
|
|
@@ -622,8 +688,6 @@ type SpreadsheetTestProps = {
|
|
|
622
688
|
}
|
|
623
689
|
|
|
624
690
|
const DEFAULT_ROW_HEIGHT = 24
|
|
625
|
-
const DEFAULT_COLUMN_WIDTH = 100
|
|
626
|
-
const DEFAULT_MAX_OPERATION_CELLS = 100_000
|
|
627
691
|
/** Live region debounce in ms (§6.9-9). / live region デバウンス (ms)。 */
|
|
628
692
|
const ANNOUNCE_DEBOUNCE_MS = 200
|
|
629
693
|
/** Default custom colour slots (v0.8 §1.9) for `fillColorCustom1..5` / `fontColorCustom1..5`. / カスタム色 5 スロットの既定 (v0.8 §1.9)。 */
|
|
@@ -744,9 +808,12 @@ const INITIAL_SELECTION: SelectionState = {
|
|
|
744
808
|
*/
|
|
745
809
|
export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & SpreadsheetTestProps>(function Spreadsheet(
|
|
746
810
|
{
|
|
747
|
-
adapter,
|
|
811
|
+
adapter: hostAdapter,
|
|
812
|
+
headerRows = 0,
|
|
813
|
+
readOnly = false,
|
|
814
|
+
labelledBy,
|
|
748
815
|
defaultRowHeight = DEFAULT_ROW_HEIGHT,
|
|
749
|
-
defaultColumnWidth =
|
|
816
|
+
defaultColumnWidth = SPREADSHEET_DEFAULT_COLUMN_WIDTH_PX,
|
|
750
817
|
frozenColumns = 0,
|
|
751
818
|
frozenRows = 0,
|
|
752
819
|
frozenTrailingColumns = 0,
|
|
@@ -767,12 +834,13 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
767
834
|
onReadOnlyRejected,
|
|
768
835
|
formatReadOnlyAnnouncement,
|
|
769
836
|
dataVersion,
|
|
770
|
-
maxOperationCells =
|
|
837
|
+
maxOperationCells = SPREADSHEET_MAX_OPERATION_CELLS,
|
|
771
838
|
enableFillHandle = true,
|
|
772
839
|
enableResizeUI = true,
|
|
773
840
|
formatCellAnnouncement,
|
|
774
841
|
formatSelectionAnnouncement,
|
|
775
842
|
formatCommandAnnouncement,
|
|
843
|
+
formatResizeAnnouncement,
|
|
776
844
|
locale,
|
|
777
845
|
labels,
|
|
778
846
|
shortcuts,
|
|
@@ -802,6 +870,9 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
802
870
|
}: SpreadsheetProps & SpreadsheetTestProps,
|
|
803
871
|
ref,
|
|
804
872
|
): ReactNode {
|
|
873
|
+
// 全体の読み取り専用はアダプタのビュー 1 つで表す: 全ての読み取り専用のゲートと塗りはアダプタの宣言だけを読むため、
|
|
874
|
+
// ゲートごとに旗を通さずに全ての書き込みが同じ経路で拒まれる。同一性はシート切替の判定 (hostAdapter) に使わない
|
|
875
|
+
const adapter = useMemo(() => (readOnly ? readOnlyAdapterView(hostAdapter) : hostAdapter), [hostAdapter, readOnly])
|
|
805
876
|
// §6.9-1: フックは常に呼び、空グリッドはレンダー分岐でのみ返す (indexAt fail-fast の前提)
|
|
806
877
|
const rowAxis = useAxisModel(adapter.rowCount, defaultRowHeight)
|
|
807
878
|
const colAxis = useAxisModel(adapter.colCount, defaultColumnWidth)
|
|
@@ -822,6 +893,15 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
822
893
|
const scrollLabels = useValueStableScrollLabels(pickEngineLabels(resolvedLabels))
|
|
823
894
|
// コマンド表: validateShortcuts の 5 条件 (+ 未知キー) は consumer の設定誤り — render 中に表面化
|
|
824
895
|
const commandTable = useMemo(() => validateShortcuts(shortcuts), [shortcuts])
|
|
896
|
+
// 寸法のキーボード操作を隠しの入力の aria-keyshortcuts で示す (再束縛の後の実効チョード。束縛が無ければ属性を出さない)
|
|
897
|
+
const resizeKeyShortcuts = useMemo(() => {
|
|
898
|
+
if (!enableResizeUI) {
|
|
899
|
+
return undefined
|
|
900
|
+
}
|
|
901
|
+
const chords = [...commandTable].filter(([, command]) => RESIZE_COMMANDS.includes(command)).sort(([, a], [, b]) => RESIZE_COMMANDS.indexOf(a) - RESIZE_COMMANDS.indexOf(b))
|
|
902
|
+
const names = chords.map(([chord]) => toAriaKeyShortcut(chord)).filter((name): name is string => name !== null)
|
|
903
|
+
return names.length === 0 ? undefined : names.join(" ")
|
|
904
|
+
}, [commandTable, enableResizeUI])
|
|
825
905
|
// カスタム色スロット (各軸ちょうど 5 本の #rrggbb)
|
|
826
906
|
const resolvedColors = useMemo(() => ({ fill: resolveColorSlots("fill", colors === undefined ? undefined : colors.fill), font: resolveColorSlots("font", colors === undefined ? undefined : colors.font) }), [colors])
|
|
827
907
|
// 帯パレットの prop 時検証 (identity 安定): 宣言時だけ検証し、未宣言は undefined のまま — overlay へ渡す
|
|
@@ -831,6 +911,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
831
911
|
if (adapter.getConditionalFormats !== undefined && adapter.getCellFormat === undefined) {
|
|
832
912
|
throw new RangeError("[spreadsheet] getConditionalFormats requires getCellFormat")
|
|
833
913
|
}
|
|
914
|
+
validateHeaderRows(headerRows, frozenRows)
|
|
915
|
+
validateLabelledBy(labelledBy)
|
|
834
916
|
|
|
835
917
|
// ---- renderVersion (editVersion 購読 + dataVersion) と CF 索引 / memo (v0.8 §2.1) ----
|
|
836
918
|
// GridBody と同じ合流購読 (rAF 合流) — 親が先に描画するため、子のスライスが読む .current は常に新
|
|
@@ -1838,6 +1920,7 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1838
1920
|
adapter,
|
|
1839
1921
|
// autofit の採用条件 = renderCell 未指定 (カスタム DOM は measureText 計測を保証できない §10)
|
|
1840
1922
|
autofitEnabled: renderCell === undefined,
|
|
1923
|
+
headerRows,
|
|
1841
1924
|
defaultRowHeight,
|
|
1842
1925
|
defaultColumnWidth,
|
|
1843
1926
|
maxOperationCells,
|
|
@@ -2083,6 +2166,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2083
2166
|
}, [rowAxis, colAxis, ensureCellVisible])
|
|
2084
2167
|
const commands = useCommands({
|
|
2085
2168
|
adapter,
|
|
2169
|
+
// キーボードの寸法の変更は帯のリサイズ UI と同じ可否 (enableResizeUI が false なら利用者は寸法を変えない)
|
|
2170
|
+
trackResize: enableResizeUI && !isEmpty ? headerResizeMachine : undefined,
|
|
2086
2171
|
getSelection,
|
|
2087
2172
|
commitEdits,
|
|
2088
2173
|
getActive,
|
|
@@ -2112,6 +2197,7 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2112
2197
|
now,
|
|
2113
2198
|
announce,
|
|
2114
2199
|
formatCommandAnnouncement,
|
|
2200
|
+
formatResizeAnnouncement,
|
|
2115
2201
|
keyboardCellRect,
|
|
2116
2202
|
// v0.10: G8–G13 / G15 の拒否漏斗 (§6.4)
|
|
2117
2203
|
rejectReadOnly,
|
|
@@ -2447,10 +2533,11 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2447
2533
|
// 旧グリッド座標のレンジは新軸で out-of-bounds になり得て、repaintOverlay の sizeOf が
|
|
2448
2534
|
// 例外を投げる (Fenwick は範囲外 index で fail-fast) — クランプでなくシート切替の意味論
|
|
2449
2535
|
// どおり全リセット (Excel のシート切替と同じく選択は持ち越さない)
|
|
2450
|
-
|
|
2536
|
+
// 判定はホストのアダプタの同一性で行う (readOnly の切替が作るビューの交代はシート切替ではない)
|
|
2537
|
+
const prevAdapterRef = useRef(hostAdapter)
|
|
2451
2538
|
useLayoutEffect(() => {
|
|
2452
|
-
if (prevAdapterRef.current !==
|
|
2453
|
-
prevAdapterRef.current =
|
|
2539
|
+
if (prevAdapterRef.current !== hostAdapter) {
|
|
2540
|
+
prevAdapterRef.current = hostAdapter
|
|
2454
2541
|
// 飛行中ドラッグは復元なしで取り消す (各機械の dragBeforeRef は旧シート座標 — 復元経路
|
|
2455
2542
|
// (Escape / lostpointercapture) から新軸へ範囲外選択が漏れるのを断つ)
|
|
2456
2543
|
engine.cancelDrag()
|
|
@@ -2490,7 +2577,7 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2490
2577
|
// effect 化する将来変更でこの順序が実防壁になるため維持する
|
|
2491
2578
|
onAdapterSwitchRef.current()
|
|
2492
2579
|
}
|
|
2493
|
-
}, [
|
|
2580
|
+
}, [hostAdapter, isEmpty, setSelection, engine, colHeaderMachine, rowHeaderMachine, headerResizeMachine, fillMachine, pointDrag, viewStore, editor.cancelEdit])
|
|
2494
2581
|
|
|
2495
2582
|
// ---- 行数 / 列数の縮小 (同一 adapter — 絞り込み / カテゴリ / 期間の変更で明細が減る) は保持中の
|
|
2496
2583
|
// 座標を新グリッドへ畳む (ADR-33) ----
|
|
@@ -2696,12 +2783,15 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2696
2783
|
|
|
2697
2784
|
// live region は読み上げ prop のどれか 1 つでも宣言されたときだけ描く (コマンド通知も同じ領域へ)
|
|
2698
2785
|
// v0.10: formatReadOnlyAnnouncement も live region の存在条件 (拒否読み上げは announce() 経由 — §6.4)
|
|
2699
|
-
const hasAnnouncement = formatCellAnnouncement !== undefined || formatSelectionAnnouncement !== undefined || formatCommandAnnouncement !== undefined || formatReadOnlyAnnouncement !== undefined
|
|
2786
|
+
const hasAnnouncement = formatCellAnnouncement !== undefined || formatSelectionAnnouncement !== undefined || formatCommandAnnouncement !== undefined || formatReadOnlyAnnouncement !== undefined || formatResizeAnnouncement !== undefined
|
|
2787
|
+
|
|
2788
|
+
// 名前は labelledBy があればホストの要素 (aria-labelledby)、無ければ組み込みの名前 (aria-label) — グリッドも同じ規則で名前を持つ
|
|
2789
|
+
const rootName = labelledBy === undefined ? { "aria-label": resolvedLabels.rootAriaLabel } : { "aria-labelledby": labelledBy }
|
|
2700
2790
|
|
|
2701
2791
|
return (
|
|
2702
2792
|
// biome-ignore lint/a11y/useSemanticElements: fieldset はフォーム意味論であり不適。role="group" は Escape 脱出の着地点に現在地名を与えるための選択 (§6.9-7 / §10-4)
|
|
2703
2793
|
// biome-ignore lint/a11y/noNoninteractiveTabindex: tabIndex=0 は WCAG 2.1.2 の脱出着地点 + グリッド再進入口 (§10-4) — 意図的な tab stop
|
|
2704
|
-
<div ref={rootRef} className={className ? `aqsp-root ${className}` : "aqsp-root"} role="group"
|
|
2794
|
+
<div ref={rootRef} className={className ? `aqsp-root ${className}` : "aqsp-root"} role="group" {...rootName} tabIndex={0} onFocus={onRootFocus} onBlur={onRootBlur} data-aqsp-root="" data-testid={testId}>
|
|
2705
2795
|
{/* ズームラッパー (v0.3): 視覚は transform スケール、内側レイアウトは 100%/z の論理 px —
|
|
2706
2796
|
virtualscroll の実測 (ResizeObserver / clientHeight) は論理値のまま。z=1 は無スタイル
|
|
2707
2797
|
(v0.2 と byte 同一の DOM 幾何 — 既存全ゲートの不変性) */}
|
|
@@ -2783,6 +2873,10 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2783
2873
|
frozenRows={frozenRows}
|
|
2784
2874
|
frozenTrailingColumns={frozenTrailingColumns}
|
|
2785
2875
|
frozenTrailingRows={frozenTrailingRows}
|
|
2876
|
+
headerRows={headerRows}
|
|
2877
|
+
labelledBy={labelledBy}
|
|
2878
|
+
gridLabel={labelledBy === undefined ? resolvedLabels.rootAriaLabel : undefined}
|
|
2879
|
+
readOnly={readOnly}
|
|
2786
2880
|
tapScrollCircleOptions={tapScrollCircleOptions}
|
|
2787
2881
|
overscanRows={validateOverscan("overscanRows", overscanRows)}
|
|
2788
2882
|
overscanCols={validateOverscan("overscanCols", overscanCols)}
|
|
@@ -2825,6 +2919,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2825
2919
|
trailingCols={frozenTrailingColumns}
|
|
2826
2920
|
trailingRows={frozenTrailingRows}
|
|
2827
2921
|
sinkAriaLabel={resolvedLabels.sinkAriaLabel}
|
|
2922
|
+
sinkReadOnly={readOnly}
|
|
2923
|
+
sinkKeyShortcuts={resizeKeyShortcuts}
|
|
2828
2924
|
onSinkMouseDown={onSinkMouseDown}
|
|
2829
2925
|
editPaintRef={editPaintRef}
|
|
2830
2926
|
highlightRef={highlightRef}
|