@aiquants/spreadsheet 0.8.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 +1103 -0
- package/LICENSE.md +155 -0
- package/README.md +817 -0
- package/dist/index.cjs +21 -0
- package/dist/index.d.cts +1960 -0
- package/dist/index.d.ts +1960 -0
- package/dist/index.js +7711 -0
- package/dist/styles/spreadsheet.css +2 -0
- package/dist/styles/spreadsheet.standalone.css +3 -0
- package/package.json +94 -0
- package/src/Spreadsheet.tsx +2313 -0
- package/src/clipboard/clipPayload.ts +1242 -0
- package/src/clipboard/fixtures/excel.fixture.ts +301 -0
- package/src/clipboard/fixtures/html.fixture.ts +90 -0
- package/src/clipboard/fixtures/sheets.fixture.ts +83 -0
- package/src/clipboard/htmlInterop.ts +1694 -0
- package/src/clipboard/numberFormatCodec.ts +870 -0
- package/src/clipboard/pasteSpecial.ts +608 -0
- package/src/clipboard/tiling.ts +370 -0
- package/src/clipboard/tsv.ts +186 -0
- package/src/clipboard/useClipboard.ts +1154 -0
- package/src/commands/selfCalc.ts +279 -0
- package/src/commands/useCommands.ts +946 -0
- package/src/editor/useCellEditor.ts +708 -0
- package/src/grid/CellView.tsx +51 -0
- package/src/grid/GridBody.tsx +489 -0
- package/src/header/ColumnHeader.tsx +167 -0
- package/src/header/RowHeader.tsx +144 -0
- package/src/header/columnName.ts +31 -0
- package/src/header/resizeGates.ts +146 -0
- package/src/header/resizeStrips.ts +79 -0
- package/src/header/useHeaderResize.ts +598 -0
- package/src/header/useHeaderSelection.ts +454 -0
- package/src/index.ts +122 -0
- package/src/keyboard/compositionGuard.ts +53 -0
- package/src/keyboard/ensureCellVisible.ts +77 -0
- package/src/keyboard/shortcuts.ts +718 -0
- package/src/keyboard/useGridKeyboard.ts +940 -0
- package/src/model/axisModel.ts +186 -0
- package/src/model/cellAnnotations.ts +92 -0
- package/src/model/cellCoord.ts +114 -0
- package/src/model/cellFormat.ts +562 -0
- package/src/model/cellPaint.ts +537 -0
- package/src/model/conditionalFormat.ts +805 -0
- package/src/model/dataAdapter.ts +478 -0
- package/src/model/dataValidation.ts +542 -0
- package/src/model/formulaRefs.ts +984 -0
- package/src/model/frozenPartition.ts +100 -0
- package/src/model/labels.ts +228 -0
- package/src/model/viewStore.ts +124 -0
- package/src/overlay/SelectionOverlay.tsx +1425 -0
- package/src/overlay/useFillHandle.ts +432 -0
- package/src/popover/CommentPopover.tsx +109 -0
- package/src/popover/LinkPopover.tsx +108 -0
- package/src/popover/ListPopover.tsx +161 -0
- package/src/popover/MessagePopover.tsx +36 -0
- package/src/popover/NotePopover.tsx +106 -0
- package/src/popover/NoteTip.tsx +26 -0
- package/src/popover/PopoverLayer.tsx +210 -0
- package/src/popover/placement.ts +138 -0
- package/src/popover/usePopovers.ts +642 -0
- package/src/selection/selectionState.ts +239 -0
- package/src/selection/useAutoScroller.ts +226 -0
- package/src/selection/usePointDrag.ts +273 -0
- package/src/selection/useSelectionEngine.ts +687 -0
- package/src/styles/components.entry.css +10 -0
- package/src/styles/spreadsheet.css +1029 -0
- package/src/styles/standalone.entry.css +9 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The single data boundary between the grid and the consumer (v0.8 — ADR-13
|
|
3
|
+
* rich cells / ADR-16 write vocabulary): the adapter type with its eight optional rich-cell
|
|
4
|
+
* capability reads, the per-cell PATCH the grid writes (CellPatch / CellEdit — an absent key is
|
|
5
|
+
* untouched, `null` clears), the discriminated write context every applyEdits receives
|
|
6
|
+
* (EditContext — one transaction id per gesture; the paste arm carries `sameSheet` and the
|
|
7
|
+
* pasted conditional-format inputs), the write-rejection context (EditRejection — what
|
|
8
|
+
* onEditRejected reports when applyEdits throws) and the four adapter-level pure helpers every
|
|
9
|
+
* consumer of the boundary shares: resolveCellAlign (the ONE alignment judgment, over the
|
|
10
|
+
* PAINTED format = base ⊕ conditional formats), lazyCellText (the once-only text thunk = the
|
|
11
|
+
* eighth read of a cell), resolveEditText (the editor seed — formula source or canonical text,
|
|
12
|
+
* fail-fast on a formula that does not start with "=") and aspectCapabilities (the write gate's
|
|
13
|
+
* single reader of the declared aspects). Every capability is a DECLARATION: absence is a code
|
|
14
|
+
* branch that reproduces the v0.7 behaviour exactly, never a value fallback.
|
|
15
|
+
* グリッドと consumer の唯一のデータ境界 (v0.8 — ADR-13 リッチセル / ADR-16 書込語彙): 8 つの
|
|
16
|
+
* 任意リッチセル能力読みを持つアダプタ型、グリッドが書くセル単位パッチ (CellPatch / CellEdit —
|
|
17
|
+
* 未指定キーは「触らない」、`null` は「消す」)、全 applyEdits が受け取る判別可能な書込文脈
|
|
18
|
+
* (EditContext — ジェスチャ毎に 1 トランザクション id、paste 枝は `sameSheet` と貼り付けた条件付き
|
|
19
|
+
* 書式入力を運ぶ)、書込拒否文脈 (EditRejection — applyEdits の throw を onEditRejected が報告する
|
|
20
|
+
* 形)、境界の全消費者が共有するアダプタ水準の純関数 4 つ: resolveCellAlign (唯一の揃え判定 —
|
|
21
|
+
* **塗り書式** = base ⊕ 条件付き書式に対して)、lazyCellText (遅延 1 回読みの text thunk = セル
|
|
22
|
+
* 8 読み目)、resolveEditText (エディタ初期文字列 — 数式ソースか正準文字列、"=" 非始まりの数式は
|
|
23
|
+
* fail-fast)、aspectCapabilities (書込ゲートの宣言済み側面の単一読み)。全能力は**宣言**であり、
|
|
24
|
+
* 未宣言は v0.7 の振る舞いを厳密に再現するコード分岐 — 値フォールバックではない。
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { CellComment, CellCommentInput } from "./cellAnnotations.ts"
|
|
28
|
+
import type { CellCoord, CellRange, SpreadsheetDirection } from "./cellCoord.ts"
|
|
29
|
+
import { type CellAlign, type CellFormat, resolveAlign } from "./cellFormat.ts"
|
|
30
|
+
import type { ConditionalFormatRule, ConditionalFormatRuleInput } from "./conditionalFormat.ts"
|
|
31
|
+
import type { DataValidationRule } from "./dataValidation.ts"
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* One per-cell PATCH (ADR-16): an ABSENT key leaves that aspect untouched, `null` CLEARS it, a
|
|
35
|
+
* value replaces it whole. `text` is the canonical string (a cleared cell is `text: ""` — text
|
|
36
|
+
* has no `null`); `format` is the complete CANONICAL CellFormat of the cell after the gesture
|
|
37
|
+
* (the command runner merges its patch with the base format BEFORE the adapter sees it — a
|
|
38
|
+
* CellFormatPatch never crosses this boundary; `null` removes every format key); `note` is the
|
|
39
|
+
* note text; `comments` carries the WHOLE thread: an input WITH `id` is an existing comment the
|
|
40
|
+
* consumer keeps verbatim, an input WITHOUT `id` is a new comment the consumer stamps with
|
|
41
|
+
* id / author / createdAt (the package never mints an id; `author` / `createdAt` travel only when a
|
|
42
|
+
* paste replays a foreign thread), `null` clears the thread; `hyperlink` is the normalized URL
|
|
43
|
+
* (normalizeHyperlink — one of http / https / mailto / tel); `validation` is the rule object.
|
|
44
|
+
* セル 1 件のパッチ (ADR-16): **未指定**キーはその側面を触らず、`null` は消去、値は丸ごと置換。
|
|
45
|
+
* `text` は正準文字列 (消去は `text: ""` — text に `null` は無い)。`format` はジェスチャ後のセルの
|
|
46
|
+
* **完全な正準** CellFormat (コマンド実行器はアダプタに渡す前に base 書式とパッチを合成する —
|
|
47
|
+
* CellFormatPatch はこの境界を跨がない。`null` は全書式キーの除去)。`note` はノート本文。
|
|
48
|
+
* `comments` は**スレッド全体**: `id` つき入力は consumer が verbatim に保持する既存コメント、`id`
|
|
49
|
+
* なし入力は consumer が id / author / createdAt を刻印する新規コメント (パッケージは採番しない。
|
|
50
|
+
* `author` / `createdAt` は paste が外部スレッドを再生するときだけ運ぶ)、`null` はスレッド消去。
|
|
51
|
+
* `hyperlink` は正規化済み URL (normalizeHyperlink — http / https / mailto / tel のいずれか)、
|
|
52
|
+
* `validation` は規則 object。
|
|
53
|
+
*/
|
|
54
|
+
export type CellPatch = { text?: string; format?: CellFormat | null; note?: string | null; comments?: readonly CellCommentInput[] | null; hyperlink?: string | null; validation?: DataValidationRule | null }
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* One cell write: a coordinate plus a {@link CellPatch}. A v0.7 text edit `{ row, col, text }`
|
|
58
|
+
* is still a CellEdit; a format-only edit omits `text` entirely.
|
|
59
|
+
* セル書込 1 件: 座標 + {@link CellPatch}。v0.7 のテキスト編集 `{ row, col, text }` もそのまま
|
|
60
|
+
* CellEdit であり、書式だけの編集は `text` を持たない。
|
|
61
|
+
*/
|
|
62
|
+
export type CellEdit = { row: number; col: number } & CellPatch
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Every write path of the grid (the union of the two EditContext arms' `source`; equal to
|
|
66
|
+
* `EditRejection["source"]`, sealed in dataAdapter.spec.ts): the editor commit ("edit" — also
|
|
67
|
+
* the idle insertDate / insertTime write), the ADR-12 write gestures ("paste" / "fill" /
|
|
68
|
+
* "delete") and the v0.8 command families ("format" = format commands / handle writes, "calc"
|
|
69
|
+
* = multiply / divide / sign-reversal self-calculation, "annotate" = note / comment / hyperlink /
|
|
70
|
+
* validation writes). A cut-move is the Ctrl+V gesture and is "paste" (Ctrl+X only serializes).
|
|
71
|
+
* グリッドの全書込経路 (EditContext 2 枝の `source` の和 = `EditRejection["source"]`、
|
|
72
|
+
* dataAdapter.spec.ts が封印): エディタ確定 ("edit" — 待機中の insertDate / insertTime 書込も)、
|
|
73
|
+
* ADR-12 の書込ジェスチャ ("paste" / "fill" / "delete")、v0.8 のコマンド族 ("format" = 書式
|
|
74
|
+
* コマンド / handle 書込、"calc" = 乗除 / 符号反転の自己計算、"annotate" = ノート / コメント /
|
|
75
|
+
* リンク / 検証の書込)。cut-move は Ctrl+V のジェスチャで "paste" (Ctrl+X は直列化のみ)。
|
|
76
|
+
*/
|
|
77
|
+
export type EditSource = "edit" | "paste" | "fill" | "delete" | "format" | "calc" | "annotate"
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Write context of every non-paste applyEdits: `transaction` is the id the grid allocated for
|
|
81
|
+
* this gesture (strictly increasing per grid instance, one id per applyEdits call — the
|
|
82
|
+
* consumer's undo groups by it) and `source` names the write path. Spelled as an explicit
|
|
83
|
+
* literal union (api-extractor cannot roll up `Exclude<EditSource, "paste">`); its agreement
|
|
84
|
+
* with EditSource is sealed by the type-parity guard in dataAdapter.spec.ts.
|
|
85
|
+
* paste 以外の全 applyEdits の書込文脈: `transaction` はグリッドがこのジェスチャに採番した id
|
|
86
|
+
* (グリッド実体毎に厳密増加、applyEdits 呼び出し 1 回につき 1 id — consumer の Undo はこれで
|
|
87
|
+
* 束ねる)、`source` は書込経路。明示リテラル union で綴る (api-extractor は
|
|
88
|
+
* `Exclude<EditSource, "paste">` を d.ts ロールアップできない)。EditSource との一致は
|
|
89
|
+
* dataAdapter.spec.ts の型パリティガードが封印する。
|
|
90
|
+
*/
|
|
91
|
+
export type PlainEditContext = { transaction: number; source: "edit" | "fill" | "delete" | "format" | "calc" | "annotate" }
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Write context of a paste (incl. a cut-move, which is the Ctrl+V gesture): `sameSheet` is
|
|
95
|
+
* true when the clip was copied from THIS adapter and false for a cross-sheet paste — and for
|
|
96
|
+
* the source-side clear of a cross-sheet cut, which the grid delivers to the SOURCE adapter with
|
|
97
|
+
* the same transaction id and `conditionalFormats: []` (a cut never moves or removes conditional
|
|
98
|
+
* formats from the source sheet — deviation row). `conditionalFormats` are the rules pasted with
|
|
99
|
+
* the cells, translated to the target ranges (`[]` when none; a CF-only paste has `edits: []`
|
|
100
|
+
* and a non-empty list); the consumer mints each new rule's id — `sourceRuleId` is the id on the
|
|
101
|
+
* source sheet.
|
|
102
|
+
* 貼り付け (Ctrl+V のジェスチャである cut-move を含む) の書込文脈: `sameSheet` はクリップが
|
|
103
|
+
* **この**アダプタからコピーされたとき true、跨ぎ貼り付けは false — 跨ぎ cut の元シート側の消去
|
|
104
|
+
* も false で、グリッドは**元**アダプタへ同じ transaction id と `conditionalFormats: []` で届ける
|
|
105
|
+
* (cut は元シートの条件付き書式を移動も削除もしない — 逸脱行)。`conditionalFormats` はセルと共に
|
|
106
|
+
* 貼り付けた規則を対象レンジへ平行移動したもの (規則なしは `[]`。CF だけの貼り付けは `edits: []`
|
|
107
|
+
* + 非空リスト)。新規則の id は consumer が採番 — `sourceRuleId` は元シートでの id。
|
|
108
|
+
*/
|
|
109
|
+
export type PasteEditContext = { transaction: number; source: "paste"; sameSheet: boolean; conditionalFormats: readonly ConditionalFormatRuleInput[] }
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The write context of applyEdits — a discriminated union on `source`: exactly the paste arm
|
|
113
|
+
* carries `sameSheet` / `conditionalFormats`. An EMPTY transaction (no edits and no conditional
|
|
114
|
+
* formats) never reaches the adapter.
|
|
115
|
+
* applyEdits の書込文脈 — `source` で判別する union: paste 枝だけが `sameSheet` /
|
|
116
|
+
* `conditionalFormats` を持つ。**空**トランザクション (edits も条件付き書式も無し) はアダプタへ
|
|
117
|
+
* 届かない。
|
|
118
|
+
*/
|
|
119
|
+
export type EditContext = PlainEditContext | PasteEditContext
|
|
120
|
+
|
|
121
|
+
/** Compile-time seal: the plain arm never carries "paste" (the discriminant stays unambiguous, so `context.source === "paste"` narrows to the paste arm alone). / コンパイル時封印: plain 枝は "paste" を持たない (判別子が曖昧にならず `context.source === "paste"` は paste 枝だけに絞れる)。 */
|
|
122
|
+
const plainContextNeverPaste: [Extract<PlainEditContext["source"], "paste">] extends [never] ? true : never = true
|
|
123
|
+
void plainContextNeverPaste
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Context of a rejected applyEdits (delivered with the thrown error by onEditRejected). The
|
|
127
|
+
* write path that was refused — the editor commit ("edit") or one of the SpreadsheetOperation
|
|
128
|
+
* WRITE vocabulary ("paste" / "fill" / "delete" / "format" / "calc" / "annotate", the same
|
|
129
|
+
* names onOperationRejected uses for the same gestures: a cut-move is the Ctrl+V gesture and is
|
|
130
|
+
* "paste" in both callbacks, because Ctrl+X only serializes — "cut" and "copy" never write,
|
|
131
|
+
* "resize" is geometry) — and, for an in-cell editing commit, the FATE of the session, so a
|
|
132
|
+
* consumer can tell the user the truth: "kept" (an in-grid commit: Enter / Tab / click — the
|
|
133
|
+
* session stays open with the typed value and focus, Escape cancels it, or the value can be
|
|
134
|
+
* fixed) versus "discarded" (a grid-exit focusout: the grid discards the session as this
|
|
135
|
+
* rejection completes — once the callback returns the typed value is dropped and the sink
|
|
136
|
+
* re-armed WITHOUT moving focus: it stays on the element the user chose, or on the hidden input
|
|
137
|
+
* after a window blur (alt-tab — a null relatedTarget takes the same path) — advising "press
|
|
138
|
+
* Escape" here would be wrong, and an idle Escape would hand focus out of the grid per WCAG
|
|
139
|
+
* 2.1.2). Non-editing writes carry no session. A discriminated union: impossible combinations
|
|
140
|
+
* do not type-check.
|
|
141
|
+
* applyEdits 拒否の文脈 (onEditRejected が throw されたエラーと共に届ける)。拒否された書込経路
|
|
142
|
+
* (エディタ確定の "edit"、または onOperationRejected が同じジェスチャに使うのと同じ
|
|
143
|
+
* SpreadsheetOperation の**書込**語彙 "paste" / "fill" / "delete" / "format" / "calc" /
|
|
144
|
+
* "annotate" — cut-move は Ctrl+V のジェスチャで両コールバックとも "paste"。Ctrl+X は直列化のみで
|
|
145
|
+
* "cut" と "copy" は書かず、"resize" は幾何) と、セル内編集の確定なら**セッションの行方** —
|
|
146
|
+
* "kept" (グリッド内確定: Enter / Tab / クリック — 入力値とフォーカスごとセッション維持、Escape で
|
|
147
|
+
* 取消か修正可能) と "discarded" (グリッド外 focusout: この拒否の完了と同時にグリッドが
|
|
148
|
+
* セッションを破棄する — コールバックが返った時点で入力値は消え、シンクはフォーカスを動かさずに
|
|
149
|
+
* 再武装される: フォーカスはユーザーが選んだ要素、またはウィンドウ blur (alt-tab — relatedTarget
|
|
150
|
+
* null も同じ経路) なら隠し入力のまま。ここで「Escape を押せ」と案内するのは誤りで、待機中の
|
|
151
|
+
* Escape は WCAG 2.1.2 のフォーカス脱出になる) を消費者が区別できる。非編集の書込はセッションを
|
|
152
|
+
* 持たない。判別可能 union — あり得ない組合せは型が拒む。
|
|
153
|
+
*/
|
|
154
|
+
// 明示リテラル union で宣言する (api-extractor は Extract<SpreadsheetOperation, ...> 等の lib 汎用型を
|
|
155
|
+
// d.ts ロールアップできず build が落ちる — ScrollToCellOptions と同じ制約)。非 edit の source 集合が
|
|
156
|
+
// SpreadsheetOperation の書込部分集合 (copy / cut / resize を除く) および EditSource と**集合として
|
|
157
|
+
// 一致**することは dataAdapter.spec.ts の型パリティガードが tsc で機械封印する (欠落・外来・非書込の
|
|
158
|
+
// 混入いずれも即赤)
|
|
159
|
+
export type EditRejection = { source: "edit"; editSession: "kept" | "discarded" } | { source: "paste" | "fill" | "delete" | "format" | "calc" | "annotate" }
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The single data boundary between the grid and the consumer. Reads via getCellText (renderCell
|
|
163
|
+
* — a SpreadsheetProps prop, not an adapter member — customizes only the presentation of that
|
|
164
|
+
* text) plus the optional capability reads, writes via applyEdits as ONE transaction per
|
|
165
|
+
* gesture (editor commit / Delete / paste / cut-clear / fill / format / calc / annotate). This
|
|
166
|
+
* seam is where the consumer implements undo (inverse patches grouped by `transaction`). Every
|
|
167
|
+
* optional member is a CAPABILITY DECLARATION ("what cannot be read cannot be written"): the
|
|
168
|
+
* grid enables a feature only when the read it depends on is declared, and an undeclared
|
|
169
|
+
* capability reproduces the v0.7 behaviour exactly (never a value fallback). Declaring
|
|
170
|
+
* getConditionalFormats without getCellFormat is a RangeError at mount (a painted format is
|
|
171
|
+
* base ⊕ CF and there is no base without getCellFormat).
|
|
172
|
+
* グリッドと consumer の唯一のデータ境界。読みは getCellText (renderCell は SpreadsheetProps の
|
|
173
|
+
* prop であってアダプタメンバーではない — その文字列の表現のみを差し替える) と任意の能力読み、
|
|
174
|
+
* 書きは applyEdits のジェスチャ毎 1 トランザクション (編集確定 / Delete / 貼り付け / 切り取り
|
|
175
|
+
* 清算 / フィル / 書式 / 自己計算 / 注釈)。consumer の Undo 実装点 (`transaction` で束ねる逆
|
|
176
|
+
* パッチ)。任意メンバーは全て**能力宣言** (「読めないものは書けない」): グリッドは依存する読みが
|
|
177
|
+
* 宣言されたときだけ機能を有効化し、未宣言能力は v0.7 の振る舞いを厳密に再現する (値フォール
|
|
178
|
+
* バックではない)。getCellFormat なしの getConditionalFormats 宣言はマウント時 RangeError
|
|
179
|
+
* (塗り書式は base ⊕ CF で、getCellFormat なしに base は無い)。
|
|
180
|
+
*/
|
|
181
|
+
export type SpreadsheetDataAdapter = {
|
|
182
|
+
rowCount: number
|
|
183
|
+
colCount: number
|
|
184
|
+
/** Canonical string used for TSV copy and the editor initial value. / コピーとエディタ初期値の正準文字列。 */
|
|
185
|
+
getCellText: (row: number, col: number) => string
|
|
186
|
+
/**
|
|
187
|
+
* Applies ONE transaction of per-cell patches. String-to-value interpretation is the
|
|
188
|
+
* consumer's job. Rejection = throw (grid state is preserved and onEditRejected fires with
|
|
189
|
+
* the {@link EditRejection} context). `context` carries the transaction id and the write path
|
|
190
|
+
* ({@link EditContext}); a paste additionally says whether the clip came from this adapter and
|
|
191
|
+
* which conditional formats travel with it. An empty transaction never arrives.
|
|
192
|
+
* セル単位パッチの 1 トランザクションを適用する。文字列→値の解釈は consumer の責務。拒否は
|
|
193
|
+
* throw (グリッド状態は保たれ、onEditRejected が {@link EditRejection} 文脈つきで通知される)。
|
|
194
|
+
* `context` はトランザクション id と書込経路 ({@link EditContext}) を運び、貼り付けはさらに
|
|
195
|
+
* クリップがこのアダプタ由来かと同伴する条件付き書式を伝える。空トランザクションは届かない。
|
|
196
|
+
* ❗ 契約: edits は**配列順に適用し、同一セルへの重複はキー単位で後勝ち (last-wins)** — 後の edit が
|
|
197
|
+
* 持つキーは前の edit の同じキーを上書きし、後の edit が持たないキーは前の edit の値のまま残る。
|
|
198
|
+
* cut の移動 (clear → 貼り付けの重ね積み) がこの順序意味論に依存する。逆パッチで Undo を実装する
|
|
199
|
+
* consumer は「最初に現れた edit の適用前値」を inverse として記録すること (参照実装は demo/edit)。
|
|
200
|
+
*/
|
|
201
|
+
applyEdits: (edits: readonly CellEdit[], context: EditContext) => void
|
|
202
|
+
/**
|
|
203
|
+
* OPTIONAL capability: computes the Excel Ctrl+Arrow jump target from `from` in
|
|
204
|
+
* `direction`. Absence = the grid keeps its v0.1 grid-edge jump (a capability declaration,
|
|
205
|
+
* NOT a value fallback). The consumer implements Excel semantics (non-empty cell with a
|
|
206
|
+
* non-empty neighbor: the end of the contiguous non-empty run; otherwise: the next
|
|
207
|
+
* non-empty cell in the direction; none: the grid edge) because only the store can jump
|
|
208
|
+
* data-aware in O(occupied) instead of O(cells). The grid validates the return fully
|
|
209
|
+
* fail-fast BEFORE dispatch (non-integer / out-of-axis / orthogonal drift on the reset
|
|
210
|
+
* path all throw RangeError, propagated uncaught — reference implementation: demo/edit).
|
|
211
|
+
* 任意能力: Excel の Ctrl+Arrow ジャンプ先を計算する。未宣言 = v0.1 のグリッド端ジャンプ
|
|
212
|
+
* 維持 (能力宣言であり値フォールバックではない)。Excel 意味論 (非空 + 隣接非空 → 連続
|
|
213
|
+
* 非空の末端 / それ以外 → 方向の次の非空セル / 無ければグリッド端) は consumer 責務 —
|
|
214
|
+
* データを知るストアだけが O(占有セル) で跳べる。戻り値はディスパッチ前に完全 fail-fast
|
|
215
|
+
* 検証 (非整数 / 軸域外 / reset 経路の直交ドリフトは RangeError を未捕捉で伝播)。
|
|
216
|
+
*/
|
|
217
|
+
findDataEdge?: (from: CellCoord, direction: SpreadsheetDirection) => CellCoord
|
|
218
|
+
/**
|
|
219
|
+
* OPTIONAL capability (v0.7, widened in v0.8): the cell's BASE display format — value
|
|
220
|
+
* format (undefined = raw text), horizontal alignment (undefined = auto BY THE ACTUAL VALUE:
|
|
221
|
+
* text that parses under the cell's numeric/date format — or as a strict decimal with no
|
|
222
|
+
* format — resolves right, labels resolve left), vertical alignment, wrap, colours, font
|
|
223
|
+
* traits, font family / size and per-side borders. Absence = the v0.1 plain-text,
|
|
224
|
+
* left-aligned rendering with no format command, no format paste aspect and no handle
|
|
225
|
+
* format write (a capability declaration, NOT a value fallback). The grid paints the
|
|
226
|
+
* PAINTED format (this base ⊕ conditional formats); the value format applies to the DISPLAY
|
|
227
|
+
* string only — editing keeps the raw getCellText (Excel semantics) and copy/TSV carry the raw
|
|
228
|
+
* value too (a DELIBERATE deviation from Excel, which copies the display — raw preserves the
|
|
229
|
+
* numeric round-trip). Autofit measures the FORMATTED display, and screen-reader
|
|
230
|
+
* announcements receive both the raw text and the display string. `renderCell` (a prop)
|
|
231
|
+
* takes precedence over the built-in VALUE format (raw text flows to the custom renderer),
|
|
232
|
+
* but the resolved ALIGNMENT and paint are cell properties that still apply under
|
|
233
|
+
* renderCell. Every format is fail-soft: a non-numeric value in a numeric format, an
|
|
234
|
+
* invalid date, an out-of-range Intl option or an unparseable colour renders / paints
|
|
235
|
+
* nothing for that key. Return a shared/interned CellFormat per column (or per cell object)
|
|
236
|
+
* to avoid per-cell allocation and to keep the paint intern stable; because return values
|
|
237
|
+
* (like getCellText) are only re-read on a re-slice, publish a live format change by bumping
|
|
238
|
+
* dataVersion (or swapping the adapter).
|
|
239
|
+
* 任意能力 (v0.7、v0.8 で拡張): セルの **base** 表示形式 — 値書式 (undefined = 素の文字列)・
|
|
240
|
+
* 水平揃え (undefined = **実際の値**による自動: 数値 / 日付書式の下で実際に解釈できる文字列と、
|
|
241
|
+
* 書式なしの厳密 10 進だけ右、ラベルは左)・垂直揃え・折返し・色・字体・フォント族 / サイズ・
|
|
242
|
+
* 辺毎の罫線。未宣言 = v0.1 の素テキスト左寄せ描画で、書式コマンドも書式の貼り付け側面も
|
|
243
|
+
* handle の書式書込も無い (能力宣言であり値フォールバックではない)。グリッドが塗るのは
|
|
244
|
+
* **塗り書式** (この base ⊕ 条件付き書式)。値書式は表示文字列にのみ適用し、編集は素の
|
|
245
|
+
* getCellText (Excel 意味論)、コピー / TSV も素の値を運ぶ (Excel は表示を写すため**意図的な
|
|
246
|
+
* 逸脱** — 素値は数値の往復を保つ)。autofit は**整形後の表示**を計測し、読み上げには素の
|
|
247
|
+
* text と表示文字列の両方を渡す。`renderCell` (prop) は組み込み「値書式」に優先する (素の
|
|
248
|
+
* text がカスタムレンダラーへ) が、解決済み「揃え」と塗りはセル固有属性として renderCell 下
|
|
249
|
+
* でも効く。全書式は fail-soft — 数値書式の非数値・無効な日付・範囲外 Intl オプション・解釈
|
|
250
|
+
* 不能な色はそのキーだけ何も描かない。列毎 (またはセル object 毎) に共有 CellFormat を返すと
|
|
251
|
+
* セル毎の確保を避け塗りの intern も安定する。戻り値は (getCellText 同様) 再スライス時のみ
|
|
252
|
+
* 再読込されるため、書式変更の反映は dataVersion 増分 (またはアダプタ差替) で行う。
|
|
253
|
+
*/
|
|
254
|
+
getCellFormat?: (row: number, col: number) => CellFormat | undefined
|
|
255
|
+
/**
|
|
256
|
+
* OPTIONAL capability (v0.8): the cell's formula SOURCE — a string that MUST start with
|
|
257
|
+
* "=" (anything else is a RangeError raised by resolveEditText before dispatch, the
|
|
258
|
+
* findDataEdge precedent); undefined = the cell holds a plain value. Declared, the editor
|
|
259
|
+
* seeds with the formula instead of getCellText, copy serializes formulas as R1C1 so a
|
|
260
|
+
* paste re-anchors them, the self-calculation commands wrap formula cells, and the
|
|
261
|
+
* formula-reference pointing gestures (click / Arrow / drag while typing after "=") exist.
|
|
262
|
+
* Absence = none of that: "=" is ordinary text and Arrow keys commit exactly as v0.7.
|
|
263
|
+
* 任意能力 (v0.8): セルの数式**ソース** — 必ず "=" で始まる文字列 (それ以外は resolveEditText が
|
|
264
|
+
* ディスパッチ前に RangeError — findDataEdge の前例)。undefined = 素の値のセル。宣言時、エディタ
|
|
265
|
+
* は getCellText でなく数式で初期化し、コピーは数式を R1C1 で直列化して貼り付けが再アンカー
|
|
266
|
+
* し、自己計算コマンドは数式セルをラップし、数式参照ポインティング ("=" 後のクリック / Arrow /
|
|
267
|
+
* ドラッグ) が存在する。未宣言 = いずれも無し: "=" は普通の文字で、Arrow は v0.7 どおり確定。
|
|
268
|
+
*/
|
|
269
|
+
getCellFormula?: (row: number, col: number) => string | undefined
|
|
270
|
+
/**
|
|
271
|
+
* OPTIONAL capability (v0.8): the cell's note text; undefined = no note. Declared, cells
|
|
272
|
+
* with a note carry the `data-aqsp-note` marker, the hover note tip and the note popover
|
|
273
|
+
* (Shift+F2) exist, and the note aspect of copy / paste / fill travels. Absence = no marker,
|
|
274
|
+
* no popover, the chord passes through, the aspect is stripped before tiling.
|
|
275
|
+
* 任意能力 (v0.8): セルのノート本文。undefined = ノート無し。宣言時、ノートつきセルは
|
|
276
|
+
* `data-aqsp-note` マーカーを持ち、ホバーの note tip と note ポップオーバー (Shift+F2) が存在し、
|
|
277
|
+
* コピー / 貼り付け / フィルの note 側面が運ばれる。未宣言 = マーカーもポップオーバーも無く、
|
|
278
|
+
* チョードは素通し、側面は tiling 前に除去。
|
|
279
|
+
*/
|
|
280
|
+
getCellNote?: (row: number, col: number) => string | undefined
|
|
281
|
+
/**
|
|
282
|
+
* OPTIONAL capability (v0.8): the cell's comment thread on the READ side — every field
|
|
283
|
+
* stamped by the consumer (id / author / createdAt / text); undefined or an empty array = no
|
|
284
|
+
* thread. Declared, threaded cells carry `data-aqsp-comment`, the comment popover
|
|
285
|
+
* (Ctrl+Alt+M) exists and a reply writes the whole thread back through CellPatch.comments
|
|
286
|
+
* (existing ids kept, the new input id-less). Absence = no marker / popover, the chord passes
|
|
287
|
+
* through, the aspect is stripped before tiling (fill never copies comments).
|
|
288
|
+
* 任意能力 (v0.8): セルのコメントスレッド (**読み側** — id / author / createdAt / text は全て
|
|
289
|
+
* consumer が刻印済み)。undefined または空配列 = スレッド無し。宣言時、スレッドつきセルは
|
|
290
|
+
* `data-aqsp-comment` を持ち、コメントポップオーバー (Ctrl+Alt+M) が存在し、返信は
|
|
291
|
+
* CellPatch.comments でスレッド全体を書き戻す (既存 id 保持、新規入力は id なし)。未宣言 =
|
|
292
|
+
* マーカー / ポップオーバー無し、チョード素通し、側面は tiling 前に除去 (フィルはコメントを写さない)。
|
|
293
|
+
*/
|
|
294
|
+
getCellComments?: (row: number, col: number) => readonly CellComment[] | undefined
|
|
295
|
+
/**
|
|
296
|
+
* OPTIONAL capability (v0.8): the cell's hyperlink URL; undefined = none. Every read is the
|
|
297
|
+
* trust boundary (ADR-10): the raw URL goes through `normalizeAdapterHyperlink`, so a URL
|
|
298
|
+
* outside the allowlist (http / https / mailto / tel) is treated as link-less at every seam —
|
|
299
|
+
* never navigated, never exported into a clip flavour, never re-committed through
|
|
300
|
+
* `CellPatch.hyperlink`. Declared, linked cells carry `data-aqsp-link`, Alt+Enter opens the
|
|
301
|
+
* link (onHyperlinkOpen or the link popover), Ctrl / Meta+click opens it too when
|
|
302
|
+
* onHyperlinkOpen is declared (spec 08 §11 D-53 — the selection stays frozen), Ctrl+K edits
|
|
303
|
+
* it, Delete clears the link together with the text (`hyperlink: null`), and the link aspect
|
|
304
|
+
* of copy / paste / fill travels. Absence = plain cells, the chords pass through, Delete
|
|
305
|
+
* writes text only.
|
|
306
|
+
* 任意能力 (v0.8): セルのハイパーリンク URL。undefined = 無し。全読み口が信頼境界 (ADR-10):
|
|
307
|
+
* 生 URL は `normalizeAdapterHyperlink` を通り、許可リスト外 (http / https / mailto / tel 以外)
|
|
308
|
+
* の URL は全シームでリンク無し扱い — 航行せず、クリップフレーバーへ書き出さず、
|
|
309
|
+
* `CellPatch.hyperlink` へ再確定しない。宣言時、リンクつきセルは `data-aqsp-link` を持ち、
|
|
310
|
+
* Alt+Enter がリンクを開き (onHyperlinkOpen または link ポップオーバー)、onHyperlinkOpen 宣言時は
|
|
311
|
+
* Ctrl / Meta+クリックでも開き (分冊 08 §11 D-53 — 選択は凍結)、Ctrl+K が編集し、Delete は
|
|
312
|
+
* テキストと共にリンクを消し (`hyperlink: null`)、コピー / 貼り付け / フィルの link 側面が
|
|
313
|
+
* 運ばれる。未宣言 = 素のセル、チョード素通し、Delete はテキストだけ書く。
|
|
314
|
+
*/
|
|
315
|
+
getCellHyperlink?: (row: number, col: number) => string | undefined
|
|
316
|
+
/**
|
|
317
|
+
* OPTIONAL capability (v0.8): the cell's data-validation rule; undefined = none. Declared,
|
|
318
|
+
* an editor commit is validated (strict rules reject and fire onValidationRejected,
|
|
319
|
+
* non-strict rules only notify), visibly invalid cells carry `data-aqsp-invalid` (custom
|
|
320
|
+
* formulas are never evaluated at paint), list rules get the dropdown (Alt+ArrowDown) and
|
|
321
|
+
* the validation aspect of copy / paste / fill travels. Return a STABLE rule object per cell
|
|
322
|
+
* (identity) so the list-value intern hits. Absence = every commit passes as v0.7.
|
|
323
|
+
* 任意能力 (v0.8): セルのデータ検証規則。undefined = 無し。宣言時、エディタ確定は検証され
|
|
324
|
+
* (strict は拒否して onValidationRejected、非 strict は通知のみ)、目に見えて無効なセルは
|
|
325
|
+
* `data-aqsp-invalid` を持ち (custom 数式は描画時に評価しない)、list 規則はドロップダウン
|
|
326
|
+
* (Alt+ArrowDown) を持ち、コピー / 貼り付け / フィルの validation 側面が運ばれる。セル毎に
|
|
327
|
+
* **安定した**規則 object (identity) を返すと list 値の intern が当たる。未宣言 = 全確定が
|
|
328
|
+
* v0.7 どおり通る。
|
|
329
|
+
*/
|
|
330
|
+
getCellValidation?: (row: number, col: number) => DataValidationRule | undefined
|
|
331
|
+
/**
|
|
332
|
+
* OPTIONAL capability (v0.8): the sheet-level conditional-format rules in PRIORITY order
|
|
333
|
+
* (array order = evaluation order; an earlier true rule wins per property, `stopIfTrue` ends
|
|
334
|
+
* the walk). Requires getCellFormat (declared alone ⇒ RangeError at mount). Return a stable
|
|
335
|
+
* array identity — the grid rebuilds its bucket index per (rules identity, renderVersion), so
|
|
336
|
+
* publish a rule change by bumping dataVersion. Declared, the CF aspect of copy / paste
|
|
337
|
+
* travels (pasted rules arrive as PasteEditContext.conditionalFormats). Absence = base
|
|
338
|
+
* formats paint as-is.
|
|
339
|
+
* 任意能力 (v0.8): シート水準の条件付き書式規則を**優先順** (配列順 = 評価順。先の真規則が
|
|
340
|
+
* プロパティ毎に勝ち、`stopIfTrue` で打ち切り) で返す。getCellFormat を要する (単独宣言は
|
|
341
|
+
* マウント時 RangeError)。配列 identity を安定させること — グリッドは (規則 identity,
|
|
342
|
+
* renderVersion) 毎にバケット索引を再構築するため、規則変更は dataVersion 増分で公開する。
|
|
343
|
+
* 宣言時、コピー / 貼り付けの CF 側面が運ばれる (貼り付けた規則は
|
|
344
|
+
* PasteEditContext.conditionalFormats で届く)。未宣言 = base 書式をそのまま塗る。
|
|
345
|
+
*/
|
|
346
|
+
getConditionalFormats?: () => readonly ConditionalFormatRule[]
|
|
347
|
+
/**
|
|
348
|
+
* OPTIONAL capability (v0.8): the numeric minimum / maximum over a range, for colorScale
|
|
349
|
+
* rules whose end points omit `value`; null = the range holds no numeric value. Memoised by
|
|
350
|
+
* the grid per (rule id, renderVersion). Absence = such rules are skipped whole (rules with
|
|
351
|
+
* explicit values still paint).
|
|
352
|
+
* 任意能力 (v0.8): レンジ内の数値の最小 / 最大 — `value` 省略の端点を持つ colorScale 規則用。
|
|
353
|
+
* null = 数値が無い。グリッドが (規則 id, renderVersion) 毎に memo する。未宣言 = そのような
|
|
354
|
+
* 規則は丸ごとスキップ (値明示の規則は塗る)。
|
|
355
|
+
*/
|
|
356
|
+
getRangeExtrema?: (range: CellRange) => { min: number; max: number } | null
|
|
357
|
+
/**
|
|
358
|
+
* OPTIONAL capability (v0.8): evaluates a formula string (passed VERBATIM, "=" included) in
|
|
359
|
+
* the context of `cell`; truth for a customFormula rule / custom validation = `true` or a
|
|
360
|
+
* finite non-zero number. Memoised by the grid per (rule id, row, col, renderVersion) for
|
|
361
|
+
* conditional formats; custom validation calls it at commit only (never at paint). Absence =
|
|
362
|
+
* customFormula rules are skipped and custom validation accepts.
|
|
363
|
+
* 任意能力 (v0.8): 数式文字列 ("=" を含め**そのまま**渡す) を `cell` の文脈で評価する。
|
|
364
|
+
* customFormula 規則 / custom 検証の真 = `true` または有限非 0 数。条件付き書式ではグリッドが
|
|
365
|
+
* (規則 id, row, col, renderVersion) 毎に memo し、custom 検証は確定時だけ呼ぶ (描画時は呼ばない)。
|
|
366
|
+
* 未宣言 = customFormula 規則はスキップ、custom 検証は通す。
|
|
367
|
+
*/
|
|
368
|
+
evaluateFormula?: (formula: string, cell: CellCoord) => string | number | boolean | null
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* The ONE alignment judgment every alignment consumer shares (cell props, the in-cell editor's
|
|
373
|
+
* text-align, and any future surface), over the PAINTED format (base ⊕ conditional formats)
|
|
374
|
+
* rather than the adapter: an explicit `align` wins outright; otherwise the alignment is auto BY
|
|
375
|
+
* THE ACTUAL VALUE, so the text is read through `readText` — the eighth read of a cell, shared
|
|
376
|
+
* with CF conditions and the invalid marker via {@link lazyCellText} — only then. The
|
|
377
|
+
* getCellFormat capability branch (undeclared = the v0.1 identity: no alignment attribute at
|
|
378
|
+
* all, which is NOT the same as "left") lives at the caller: a painted format has no adapter to
|
|
379
|
+
* inspect, so the caller skips this judgment entirely when the capability is absent.
|
|
380
|
+
* 揃えの全消費者 (セル属性・セル内エディタの text-align・将来の面) が共有する唯一の揃え判定 —
|
|
381
|
+
* アダプタでなく**塗り書式** (base ⊕ 条件付き書式) に対して: 明示 `align` は無条件に勝ち、
|
|
382
|
+
* それ以外は**実際の値**による自動判定なので、そのときだけ `readText` で text を読む (セル
|
|
383
|
+
* 8 読み目 — {@link lazyCellText} で CF 条件 / 無効マーカーと共有)。getCellFormat の能力分岐
|
|
384
|
+
* (未宣言 = v0.1 恒等: 揃え属性そのものを出さない — 「左」とは別物) は呼び手側にある: 塗り書式
|
|
385
|
+
* には調べるべきアダプタが無いので、未宣言なら呼び手がこの判定自体を省く。
|
|
386
|
+
*/
|
|
387
|
+
export const resolveCellAlign = (painted: CellFormat | undefined, readText: () => string): CellAlign => {
|
|
388
|
+
// 塗り書式なし = 揃えも値書式も未指定 → 実際の値で自動判定 (8 読み目)
|
|
389
|
+
if (painted === undefined) {
|
|
390
|
+
return resolveAlign(undefined, undefined, readText())
|
|
391
|
+
}
|
|
392
|
+
// 明示 align があれば text 読み取り不要 (自動判定だけが実際の値を必要とする)
|
|
393
|
+
return resolveAlign(painted.align, painted.value, painted.align === undefined ? readText() : "")
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Lazy once-only text thunk — the single construction point of a cell's eighth read, shared by
|
|
398
|
+
* alignment (align unset), conditional-format conditions and the visible-invalid check so the
|
|
399
|
+
* text is read at most ONCE per cell per slice. Nothing is read at construction; the first call
|
|
400
|
+
* reads getCellText and every later call returns that same string (an empty "" is a valid,
|
|
401
|
+
* cached value — the thunk never re-reads).
|
|
402
|
+
* 遅延 1 回読みの text thunk — セル 8 読み目の単一構築点。揃え (align 未設定)・条件付き書式の
|
|
403
|
+
* 条件・無効表示判定が共有し、text はスライス毎セル毎に最大 **1 回**しか読まれない。構築時には
|
|
404
|
+
* 読まず、初回呼び出しで getCellText を読み、以後は同じ文字列を返す (空文字 "" も正当なキャッシュ
|
|
405
|
+
* 値 — 再読はしない)。
|
|
406
|
+
*/
|
|
407
|
+
export const lazyCellText = (adapter: SpreadsheetDataAdapter, row: number, col: number): (() => string) => {
|
|
408
|
+
// 未読の番兵は null (空文字 "" は正当な値なので番兵に使わない)
|
|
409
|
+
let text: string | null = null
|
|
410
|
+
return () => {
|
|
411
|
+
if (text === null) {
|
|
412
|
+
text = adapter.getCellText(row, col)
|
|
413
|
+
}
|
|
414
|
+
return text
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Editor seed text: the formula source when the getCellFormula capability is declared and the
|
|
420
|
+
* cell holds one (it must start with "=" — anything else, including a non-string, is a
|
|
421
|
+
* RangeError before dispatch, the findDataEdge precedent), else the canonical text. A capability
|
|
422
|
+
* branch, not a value fallback: without the capability getCellFormula is never consulted, and
|
|
423
|
+
* with a formula getCellText is not read.
|
|
424
|
+
* エディタ初期文字列: getCellFormula 能力が宣言され、セルに数式があればその数式ソース ("=" 始まり
|
|
425
|
+
* 必須 — それ以外は非文字列を含めディスパッチ前に RangeError、findDataEdge の前例)、それ以外は
|
|
426
|
+
* 正準文字列。能力分岐であって値フォールバックではない: 未宣言なら getCellFormula には触れず、
|
|
427
|
+
* 数式があれば getCellText は読まない。
|
|
428
|
+
*/
|
|
429
|
+
export const resolveEditText = (adapter: SpreadsheetDataAdapter, row: number, col: number): { text: string; isFormula: boolean } => {
|
|
430
|
+
// 能力分岐: getCellFormula 未宣言なら「数式」という概念自体が無い (v0.7 恒等)
|
|
431
|
+
if (adapter.getCellFormula !== undefined) {
|
|
432
|
+
const formula = adapter.getCellFormula(row, col)
|
|
433
|
+
if (formula !== undefined) {
|
|
434
|
+
// "=" 始まり以外は consumer のバグ — findDataEdge と同じくディスパッチ前に fail-fast
|
|
435
|
+
if (typeof formula !== "string" || !formula.startsWith("=")) {
|
|
436
|
+
throw new RangeError(`[spreadsheet] getCellFormula(${row}, ${col}) must return a string starting with "=", received ${JSON.stringify(formula)}`)
|
|
437
|
+
}
|
|
438
|
+
return { text: formula, isFormula: true }
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return { text: adapter.getCellText(row, col), isFormula: false }
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* The declared aspect capabilities of an adapter — one boolean per rich-cell aspect, derived
|
|
446
|
+
* purely from which optional reads are declared ("what cannot be read cannot be written"):
|
|
447
|
+
* `format` = getCellFormat, `note` = getCellNote, `comments` = getCellComments, `hyperlink` =
|
|
448
|
+
* getCellHyperlink, `validation` = getCellValidation, `cf` = getConditionalFormats, `formula` =
|
|
449
|
+
* getCellFormula. findDataEdge is navigation, not an aspect.
|
|
450
|
+
* アダプタの宣言済み側面能力 — リッチセル側面毎に 1 真偽値、どの任意読みが宣言されているかだけ
|
|
451
|
+
* から導く (「読めないものは書けない」): `format` = getCellFormat、`note` = getCellNote、
|
|
452
|
+
* `comments` = getCellComments、`hyperlink` = getCellHyperlink、`validation` = getCellValidation、
|
|
453
|
+
* `cf` = getConditionalFormats、`formula` = getCellFormula。findDataEdge は航行であり側面ではない。
|
|
454
|
+
*/
|
|
455
|
+
export type AspectCapabilities = { format: boolean; note: boolean; comments: boolean; hyperlink: boolean; validation: boolean; cf: boolean; formula: boolean }
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Declared aspect capabilities (the write gate's single reader): the paste path strips every
|
|
459
|
+
* undeclared aspect before tiling, and commands and popovers are available only for declared
|
|
460
|
+
* aspects (their default chords pass through otherwise). A live read of the adapter (no memo)
|
|
461
|
+
* so the judgment is always the adapter's current declaration. The paint path (getCellProps)
|
|
462
|
+
* makes the same live judgment through direct member reads and never builds this snapshot —
|
|
463
|
+
* a per-cell allocation has no place on the paint hot path.
|
|
464
|
+
* 宣言済み側面能力 (書込ゲートの単一読み): 貼り付け経路は tiling 前に未宣言側面を除去し、
|
|
465
|
+
* コマンド / ポップオーバーは宣言済み側面でだけ利用可能 (それ以外は既定チョードが素通し)。
|
|
466
|
+
* アダプタの生読み (memo なし) なので判定は常にアダプタの現在の宣言。塗り経路 (getCellProps) は
|
|
467
|
+
* 同じ生判定をメンバー直接読みで行いこのスナップショットを組まない — セル毎割当は塗りの
|
|
468
|
+
* hot path に置かない。
|
|
469
|
+
*/
|
|
470
|
+
export const aspectCapabilities = (adapter: SpreadsheetDataAdapter): AspectCapabilities => ({
|
|
471
|
+
format: adapter.getCellFormat !== undefined,
|
|
472
|
+
note: adapter.getCellNote !== undefined,
|
|
473
|
+
comments: adapter.getCellComments !== undefined,
|
|
474
|
+
hyperlink: adapter.getCellHyperlink !== undefined,
|
|
475
|
+
validation: adapter.getCellValidation !== undefined,
|
|
476
|
+
cf: adapter.getConditionalFormats !== undefined,
|
|
477
|
+
formula: adapter.getCellFormula !== undefined,
|
|
478
|
+
})
|