xiaodao-spreader 0.1.36 → 0.1.38

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.
Files changed (29) hide show
  1. package/README.ZH.md +85 -2
  2. package/README.md +83 -2
  3. package/dist/style.css +1 -1
  4. package/dist/types/components/spreader/components/data-validation-input-message.vue.d.ts +2 -2
  5. package/dist/types/components/spreader/components/dropdown.vue.d.ts +4 -1
  6. package/dist/types/components/spreader/components/note-overlay.vue.d.ts +47 -0
  7. package/dist/types/components/spreader/components/pickers/border-picker.vue.d.ts +3 -0
  8. package/dist/types/components/spreader/components/pickers/calc-picker.vue.d.ts +2 -0
  9. package/dist/types/components/spreader/components/pickers/color-picker.vue.d.ts +3 -0
  10. package/dist/types/components/spreader/components/pickers/conditional-format-menu.vue.d.ts +8 -0
  11. package/dist/types/components/spreader/components/pickers/data-validation-dropdown.vue.d.ts +5 -3
  12. package/dist/types/components/spreader/components/pickers/filter-popup.vue.d.ts +2 -0
  13. package/dist/types/components/spreader/components/pickers/merge-picker.vue.d.ts +3 -0
  14. package/dist/types/components/spreader/components/pickers/outline-picker.vue.d.ts +6 -0
  15. package/dist/types/components/spreader/components/pickers/sort-picker.vue.d.ts +3 -0
  16. package/dist/types/components/spreader/components/spreader.vue.d.ts +36 -0
  17. package/dist/types/components/spreader/components/tabbar.vue.d.ts +2 -0
  18. package/dist/types/components/spreader/components/toolbar.vue.d.ts +23 -0
  19. package/dist/types/components/spreader/composables/core-state.d.ts +19 -1
  20. package/dist/types/components/spreader/composables/interactions.d.ts +21 -0
  21. package/dist/types/components/spreader/composables/useFloatMenuPosition.d.ts +39 -0
  22. package/dist/types/components/spreader/core/constants.d.ts +2 -0
  23. package/dist/types/components/spreader/core/notes.d.ts +47 -0
  24. package/dist/types/components/spreader/core/outline-core.d.ts +2 -2
  25. package/dist/types/components/spreader/core/types.d.ts +26 -2
  26. package/dist/types/components/spreader/core/utils.d.ts +20 -0
  27. package/dist/xiaodao-spreader.es.js +8432 -8049
  28. package/dist/xiaodao-spreader.umd.js +9 -9
  29. package/package.json +1 -1
package/README.ZH.md CHANGED
@@ -35,6 +35,8 @@
35
35
  - **查找与替换**: 工具栏查找按钮或 `Ctrl/Cmd+F`(含 `Ctrl/Cmd+H`)打开;支持「当前工作表 / 整个工作簿 / 当前选区」三种范围,区分大小写、匹配整个单元格;高亮全部匹配并定位当前项、循环查找;单次与全部替换均接入撤销/重做,仅修改单元格原始 `value`(始终保持字符串),不改动格式 / 边框 / 合并
36
36
  - **条件格式**: 按工作表存储条件格式规则(`SheetState.conditionalFormats`)。工具栏「条件格式」下拉提供高亮单元格预设(大于 / 小于 / 介于 / 文本包含 / 重复 / 唯一 / 空白 / 非空白)以及「新建规则」对话框(支持单元格值条件和自定义公式条件)。规则渲染为基础单元格样式之上的临时覆盖层,绝不写回底层 `value` 或 `styleId`。规则优先级(数值越小优先级越高)、`stopIfTrue` 语义、单元格值变化时自动失效缓存。插入/删除行列时通过与自动填充相同的 `shiftFormulaRefs` 引擎自动调整规则范围与公式引用(`$B$2` 绝对引用保持固定)。内置「管理规则」对话框可编辑、排序、删除规则;「清除规则」子菜单支持清除全部或按选区清除。渲染时与基础样式正确合成背景色、文字颜色、粗体、斜体、下划线、删除线。*见 [条件格式](#条件格式)*
37
37
  - **数据验证**: 按工作表存储类 Excel 数据验证规则。工具栏「数据验证」按钮打开对话框,设置允许类型(任何值 / 列表 / 整数 / 小数 / 日期 / 时间 / 文本长度 / 自定义公式)、运算符(介于 / 不介于 / 等于 / 大于 ……)与条件值。列表验证在单元格内显示下拉,支持搜索与完整键盘操作(↑ ↓ / Home / End / PageUp / PageDown / Enter / Esc)。支持逐条输入信息提示与出错警告(停止 / 警告 / 信息):验证发生在值写入之前,停止级违规直接拒绝录入,警告/信息级由用户确认后继续。同一范围的多条规则必须全部通过。规则随 v-model 持久化、接入撤销/重做,并在行列增删时自动调整范围。复制/粘贴与自动填充走原子校验——出现停止级违规则整次取消。*见 [数据验证](#数据验证)*
38
+ - **行列分组与折叠**: 类 Excel 的行/列分组(Outline)。选中整行或整列后,工具栏「分组」下拉(位于排序与筛选之间)提供添加分组 / 取消分组 / 清除分组 / 全部展开 / 全部折叠,直接作用于选中轴;行/列右键菜单提供同构的「分组」子菜单。仅支持一层、互不相交的分组,嵌套或部分重叠会被校验拒绝并提示。分组区间在行号/列号带用交替背景色区分,± 折叠按钮浮动于表头带内,不占用独立 gutter。折叠的行/列不参与内容绘制;插入/删除行列时分组范围自动平移、被清空的分组自动删除。分组状态按工作表持久化(`rowOutlines` / `columnOutlines`),增删与折叠均接入撤销/重做。*见 [行列分组与折叠](#行列分组与折叠)*
39
+ - **单元格批注**: 类 Excel 的单元格批注(Note/Comment)。右键菜单新建 / 编辑 / 删除;单元格右上角红色三角指示器标识含批注;hover 悬停查看、点按编辑;批注独立于单元格值与样式,采用 Note Pool 存储(Sheet 级 notes 池 + cell.noteId 引用),便于排序 / 插入删除时随 cell 一起移动。批注支持多行文本、可选作者、创建 / 修改时间。可通过 `noteAuthor` prop 设置当前作者名。*见 [单元格批注](#单元格批注)*
38
40
  - **自动填充(填充柄)**: 类 Excel 的活动选区右下角填充柄,按住向上/下/左/右拖拽即可填充单元格:单值复制、数字/日期/文本数字自动续序列(如 `1,2 → 3,4,5`)、公式引用按相对/绝对/混合规则平移(如 `=A1*2 → =A2*2`)、源 `styleId` 经样式池复用。拖拽期间实时预览、边缘自动滚动、动态扩展工作表、冻结窗格兼容、单次拖拽仅产生一个撤销步骤,*见 [自动填充](#自动填充)*
39
41
 
40
42
  ### 交互体验
@@ -142,6 +144,7 @@ const myData = ref<SheetModelData[]>([
142
144
  | `height` | `number \| string` | - | 组件高度(像素;传 `string` 或省略则自适应) |
143
145
  | `theme` | `'light' \| 'dark'` | `'light'` | 主题模式 |
144
146
  | `locale` | `string` | `'zh-CN'` | 语言: `'zh-CN'` \| `'en-US'` |
147
+ | `noteAuthor` | `string` | `''`(空串,显示时回退为 i18n 占位符「未命名」) | 当前批注作者名;为空时存入批注的 author 字段为空字符串,显示时回退为 i18n 占位符 |
145
148
 
146
149
  ---
147
150
 
@@ -159,6 +162,8 @@ interface SheetModelData {
159
162
  cells: Record<string, {
160
163
  value: string;
161
164
  styleId?: number;
165
+ /** 批注引用:指向 Sheet 级 notes 池中的 CellNote */
166
+ noteId?: string;
162
167
  }>;
163
168
  merges?: Record<string, SelectionRange>;
164
169
  freeze?: { rows: number; cols: number };
@@ -168,6 +173,8 @@ interface SheetModelData {
168
173
  colCount?: number;
169
174
  /** 逻辑行数(0 基,不含)。省略时默认为 200。 */
170
175
  rowCount?: number;
176
+ /** 单元格批注池:按 noteId 存放 CellNote,cell.noteId 引用之;与 cell value/style 完全独立 */
177
+ notes?: Record<string, CellNote>;
171
178
  }
172
179
  ```
173
180
 
@@ -221,6 +228,7 @@ src/
221
228
  │ ├── tabbar.vue # Sheet 标签栏
222
229
  │ ├── dropdown.vue # 通用下拉组件
223
230
  │ ├── find-replace-bar.vue # 查找替换栏 UI
231
+ │ └── note-overlay.vue # 批注查看/编辑浮层(view + edit 双模)
224
232
  │ └── pickers/
225
233
  │ ├── colorPicker.vue # 文字 / 填充颜色选择器
226
234
  │ ├── borderPicker.vue # 边框选择器
@@ -236,6 +244,7 @@ src/
236
244
  │ ├── borders-merge.ts # 边框操作、合并操作、剪贴板、求和
237
245
  │ ├── sheets-ops.ts # 行列增删、多 Sheet、v-model 发射、主题、refs
238
246
  │ ├── find-replace.ts # 查找替换状态与交互(依赖 Vue)
247
+ │ └── notes.ts # 批注池管理:CRUD、持久化、Undo 集成
239
248
  │ └── interactions.ts # 渲染器、编辑栏、标签栏、右键菜单、滚动条、事件
240
249
  └── core/
241
250
  ├── constants.ts # 布局常量、国际化、主题调色板
@@ -479,6 +488,80 @@ CF 引擎的 `shiftFormulaRefsSafe` 复用公式引擎的 `shiftFormulaRefs` 函
479
488
 
480
489
  ---
481
490
 
491
+ ## 行列分组与折叠
492
+
493
+ 类 Excel 的行/列分组与折叠(`spreader/core/outline-core.ts` 纯函数引擎 + `composables/core-state.ts` 状态层 + `composables/interactions.ts` Canvas 绘制层)。
494
+
495
+ ### 数据模型
496
+
497
+ 分组按工作表、按轴存储于 `SheetState.rowOutlines` / `columnOutlines`(`DimensionOutline[]`,随 v-model 序列化)。每条分组包含稳定 `id`、`start` / `end`(0-based 闭区间)、`level`(恒为 1)、`collapsed`。
498
+
499
+ ### 校验规则
500
+
501
+ `validateGroup` 在创建前校验:至少 2 个连续行/列;与既有分组必须完全不相交(部分重叠提示「分组范围与现有分组交叉」);禁止嵌套(`MAX_OUTLINE_LEVEL = 1`,提示「分组不能嵌套,仅支持一层」)。取消分组要求选区完整覆盖至少一个分组,否则提示调整选区。
502
+
503
+ ### 渲染与折叠
504
+
505
+ 分组区间在行号/列号带用交替背景色区分;± 折叠按钮(约 9px)浮动于表头带内,不预留独立 gutter 分区。折叠的行/列在内容、边框、表头文字绘制时统一跳过(0 尺寸防残影),与滚动、冻结窗格兼容。
506
+
507
+ ### 入口
508
+
509
+ - 工具栏「分组」下拉(位于排序与筛选之间):仅在选中整行或整列时可用,菜单项(添加分组 / 取消分组 / 清除分组 / 全部展开 / 全部折叠)直接作用于选中轴,不再二次选择行/列
510
+ - 行/列右键菜单「分组」子菜单:按右键轴分发同构的菜单项
511
+
512
+ ### 传播与撤销
513
+
514
+ 插入/删除行列时分组范围经 `addOutlineForInsert` / `addOutlineForDelete` 平移,完全被清空的分组自动删除。分组的创建、取消、清除与折叠均产生撤销步骤;校验失败优先使用宿主注入的应用内提示(`showOutlineAlert` 钩子),未注入时回退 `window.alert`。单元测试见 `test/outline-core.test.ts`。
515
+
516
+ ---
517
+
518
+
519
+ ## 单元格批注
520
+
521
+ 类 Excel 的单元格批注(Note / Comment)功能,独立于单元格值与样式。核心引擎位于 `spreader/core/notes.ts`,纯逻辑实现;UI 浮层为 `components/note-overlay.vue`。
522
+
523
+ ### 数据模型
524
+
525
+ 批注采用 **Note Pool** 存储:Sheet 级 `notes` 池按 `id` 存放完整 `CellNote` 对象,单元格仅保存 `noteId` 引用。
526
+
527
+ ````typescript
528
+ interface CellNote {
529
+ id: string; // 稳定唯一 ID
530
+ text: string; // 批注正文(多行文本)
531
+ author?: string; // 作者(可选)
532
+ createdAt: number; // 创建时间(epoch ms)
533
+ updatedAt: number; // 最近修改时间(epoch ms)
534
+ }
535
+ ````
536
+
537
+ - **`CellData.noteId`**: 单元格上的批注引用,指向 `SheetState.notes` 池
538
+ - **`SheetModelData.notes`**: v-model 序列化时的批注池,键为 noteId
539
+
540
+ Note Pool 设计使得排序、插入 / 删除行列等操作移动单元格时,`noteId` 随 cell 数据一起搬运,批注不会丢失。
541
+
542
+ ### 交互入口
543
+
544
+ - **右键菜单**: 单元格右键菜单提供「新建批注 / 编辑批注 / 删除批注」三项;无批注时隐藏编辑 / 删除,有批注时隐藏新建
545
+ - **Shift+F2**: 在含批注的单元格上切换编辑浮层
546
+ - **hover 查看**: 鼠标悬停含批注的单元格时弹出只读查看浮层
547
+
548
+ ### 渲染
549
+
550
+ - 含批注的单元格右上角绘制 6px 红色三角指示器(Canvas 绘制,无 DOM 开销)
551
+ - 批注浮层 `note-overlay.vue` 为 Vue DOM 组件,`position: absolute` 驻留于 `.spreadsheet-wrapper` 内,被 `overflow: hidden` 裁剪到表格区域,不溢出覆盖 toolbar / 行列头 / 冻结区域 / 滚动条
552
+ - z-index 分层:非冻结区浮层在 freeze canvas(z-index: 1)之下(z-index: 0),冻结区浮层在 freeze canvas 之上(z-index: 2),滚动条 z-index: 3 永远在最上层
553
+
554
+ ### 作者名
555
+
556
+ 通过 `noteAuthor` prop 设置当前作者名。空串时存入批注的 `author` 字段为空字符串,显示时回退为 i18n 占位符(中文「未命名」/ 英文 Unnamed)。
557
+
558
+ ### 持久化与撤销
559
+
560
+ - 批注随 Sheet v-model(`notes` 字段)持久化,保存 / 加载 / 复制工作表均正确往返
561
+ - 新建 / 修改 / 删除批注各产生一个撤销步骤,与单元格值、样式、边框等操作共享同一撤销栈
562
+
563
+ ---
564
+
482
565
  ## 自动填充
483
566
 
484
567
  一套类 Excel 的填充柄机制(`spreader/core/autofill.ts` 纯引擎 + `composables/interactions.ts` 画布/交互层)。拖动活动选区右下角的小方块即可填充单元格,无 DOM 句柄,全部在 Canvas 上绘制。
@@ -703,11 +786,11 @@ x-spreader 以触屏为第一交互:每个鼠标操作都有对称的触屏路
703
786
 
704
787
  - [x] 冻结窗格(冻结行/列), *见 [冻结窗格](#功能特性)*
705
788
  - [x] 数据验证:下拉列表、输入约束(任何值 / 列表 / 整数 / 小数 / 日期 / 时间 / 文本长度 / 自定义公式),列表下拉支持搜索与键盘导航,输入信息与出错警告(停止 / 警告 / 信息),粘贴/自动填充原子校验,撤销重做与持久化;见 [数据验证](#数据验证)
706
- - [ ] 行列分组与折叠
789
+ - [x] 行列分组与折叠:*见 [行列分组与折叠](#行列分组与折叠)*
707
790
  - [x] 排序与筛选:按展示内容排序,含类 Excel「排序提醒」对话框(扩展选定区域 / 仅对选定区域排序);见 [排序与排序提醒](#功能特性)
708
791
  - [x] 自动筛选(AutoFilter):工具栏「筛选」/`Ctrl+Shift+L` 启用,表头下箭头、智能向下探测数据区、值/文本/数值/日期多类型筛选、清除单列与整体移除分离;见 [自动筛选(AutoFilter)](#自动筛选autofilter)
709
792
  - [x] 触屏与移动端交互:选择 / 右键菜单 / 筛选 / 列宽行高 / 框选 / 格式刷 / tab 菜单均补齐对称触屏路径,浮层支持点外部关闭;见 [触屏与移动端交互](#触屏与移动端交互)
710
- - [ ] 单元格批注
793
+ - [x] 单元格批注:Note Pool 存储、右键菜单 CRUD、hover 查看、编辑浮层、持久化与撤销;见 [单元格批注](#单元格批注)
711
794
  - [ ] 打印布局
712
795
 
713
796
  ### 远期
package/README.md CHANGED
@@ -34,7 +34,9 @@ A high-performance, canvas-based spreadsheet component for Vue 3: bringing an Ex
34
34
  - **Find & Replace**: Open via the toolbar find button or `Ctrl/Cmd+F` (also `Ctrl/Cmd+H`); three scopes: current sheet / entire workbook / current selection; match case and match entire cell; highlights all matches and locates the active one with wrap-around navigation; single and replace-all both integrate with undo/redo, mutating only the raw `value` (always kept a string), never format / border / merge
35
35
  - **Conditional Formatting**: Excel-style conditional formatting rules stored per-sheet. The toolbar "Conditional Formatting" dropdown offers preset rules (greater than / less than / between / text contains / duplicate / unique / blank / not blank) and a "New Rule" dialog with cell value and formula-based conditions. Rules render as temporary style overlays on top of the base cell style — no mutation of the underlying cell `value` or `styleId`. Rule priority (smaller number = higher), `stopIfTrue` semantics, and automatic cache invalidation on cell value change. Inserting / deleting rows or columns adjusts rule ranges and formula references via the same `shiftFormulaRefs` engine used by auto-fill. Includes a "Manage Rules" dialog for editing, reordering, and deleting rules; the "Clear Rules" sub-dialog lets you clear all rules or rules from selected ranges. Render-time style synthesis correctly composes CF background, font color, bold, italic, underline, and strikethrough with the base cell style. *see [Conditional Formatting](#conditional-formatting)*
36
36
  - **Data Validation**: Excel-style data validation rules stored per-sheet. The toolbar "Data Validation" button opens a dialog to set the allowed type (any value / list / whole number / decimal / date / time / text length / custom formula), the operator (between, not between, equal, greater than, …), and the criterion values. List validation shows an in-cell dropdown with search and full keyboard navigation (↑ ↓ / Home / End / PageUp / PageDown / Enter / Esc). Per-rule input message and error alert (Stop / Warning / Information) are supported; validation runs *before* the value is committed, so Stop-level violations reject the edit while Warning/Information let the user confirm. All rules on a range must pass. Rules persist through v-model, integrate with undo/redo, and adjust automatically on row/column insert/delete. Copy/paste and Auto Fill validate atomically — a Stop violation cancels the whole operation. *see [Data Validation](#data-validation)*
37
+ - **Cell Notes**: Excel-style cell notes (comments). Right-click menu to create / edit / delete; red triangle indicator at cell corner marks cells with notes; hover to preview, click to edit. Notes are independent from cell values and styles, stored via a Note Pool (Sheet-level notes pool + cell.noteId reference), so sort / insert-delete moves cells together with their notes. Notes support multi-line text, optional author, and create/update timestamps. Author name can be set via the noteAuthor prop. *see [Cell Notes](#cell-notes)*
37
38
  - **Auto Fill (Fill Handle)**: Excel-style fill handle at the bottom-right corner of the active selection. Drag it up/down/left/right to fill cells: single values copy, number/date/text-number sequences auto-continue (e.g. `1,2 → 3,4,5`), formula references adjust per relative/absolute/mixed rules (e.g. `=A1*2` → `=A2*2`), and source `styleId` is reused via the style pool. Live preview during drag, edge auto-scroll, dynamic sheet expansion, freeze-pane compatibility, and a single undo step per operation: *see [Auto Fill](#auto-fill)*
39
+ - **Row / Column Grouping & Collapsing**: Excel-style row/column outlines. Select whole rows or columns, then the toolbar "Group" dropdown (between Sort and Filter) offers Add Group / Ungroup / Clear Outline / Expand All / Collapse All, applied directly to the selected axis; row/column context menus expose a matching "Group" submenu. Only one level of non-overlapping groups is supported — nesting and partial overlap are rejected with a message. Group ranges get alternating background colors in the row/column header band; ± collapse buttons float inside the header band without reserving a separate gutter. Collapsed rows/columns are skipped during content rendering; inserting/deleting rows or columns shifts group ranges and auto-removes emptied groups. Groups persist per-sheet (`rowOutlines` / `columnOutlines`) through v-model, and every mutation integrates with undo/redo. *see [Row / Column Grouping & Collapsing](#row--column-grouping--collapsing)*
38
40
 
39
41
  ### Interaction
40
42
  - **Smart Selection**: Click, drag, Shift+Click, row/column header select, corner-cell select-all
@@ -139,6 +141,7 @@ const myData = ref<SheetModelData[]>([
139
141
  | `height` | `number \| string` | - | Component height (pixels; omit or pass `string` for responsive) |
140
142
  | `theme` | `'light' \| 'dark'` | `'light'` | Theme mode |
141
143
  | `locale` | `string` | `'zh-CN'` | Language: `'zh-CN'` \| `'en-US'` |
144
+ | `noteAuthor` | `string` | `''` | Current note author name; when empty, saved notes have empty author field that renders as i18n placeholder |
142
145
 
143
146
  ---
144
147
 
@@ -156,6 +159,8 @@ interface SheetModelData {
156
159
  cells: Record<string, {
157
160
  value: string;
158
161
  styleId?: number;
162
+ /** Note reference pointing to a CellNote in the Sheet-level notes pool */
163
+ noteId?: string;
159
164
  }>;
160
165
  merges?: Record<string, SelectionRange>;
161
166
  freeze?: { rows: number; cols: number };
@@ -165,6 +170,8 @@ interface SheetModelData {
165
170
  colCount?: number;
166
171
  /** Logic row count (0-based exclusive). Defaults to 200 when omitted. */
167
172
  rowCount?: number;
173
+ /** Cell notes pool: CellNote objects keyed by noteId; cell.noteId references into this pool */
174
+ notes?: Record<string, CellNote>;
168
175
  }
169
176
  ```
170
177
 
@@ -218,6 +225,7 @@ src/
218
225
  │ ├── tabbar.vue # Sheet tab bar
219
226
  │ ├── dropdown.vue # Generic dropdown component
220
227
  │ ├── find-replace-bar.vue # Find/replace bar UI
228
+ note-overlay.vue # Note view/edit overlay (dual-mode: view + edit)
221
229
  │ └── pickers/
222
230
  │ ├── colorPicker.vue # Text & fill color picker
223
231
  │ ├── borderPicker.vue # Border picker
@@ -233,6 +241,7 @@ src/
233
241
  │ ├── borders-merge.ts # Border ops, merge ops, clipboard, sum
234
242
  │ ├── sheets-ops.ts # Row/col ops, multi-sheet, v-model emit, theme, refs
235
243
  │ ├── find-replace.ts # Find/replace state & interaction (Vue-dependent)
244
+ notes.ts # Note pool management: CRUD, persistence, undo integration
236
245
  │ └── interactions.ts # Renderer, formula bar, tab bar, context menu, scrollbar, events
237
246
  └── core/
238
247
  ├── constants.ts # Layout constants, i18n, theme palettes
@@ -476,6 +485,78 @@ Rules persist through the sheet v-model (`dataValidations` field), round-trip th
476
485
 
477
486
  ---
478
487
 
488
+ ## Row / Column Grouping & Collapsing
489
+
490
+ Excel-style row/column grouping and collapsing (`spreader/core/outline-core.ts` pure engine + `composables/core-state.ts` state layer + `composables/interactions.ts` canvas rendering layer).
491
+
492
+ ### Data Model
493
+
494
+ Groups are stored per-sheet and per-axis in `SheetState.rowOutlines` / `columnOutlines` (`DimensionOutline[]`, serialized with v-model). Each group carries a stable `id`, `start` / `end` (0-based inclusive range), `level` (always 1), and `collapsed`.
495
+
496
+ ### Validation Rules
497
+
498
+ `validateGroup` checks before creation: at least 2 consecutive rows/columns; fully disjoint from existing groups (partial overlap reports "group range crosses existing groups"); nesting is rejected (`MAX_OUTLINE_LEVEL = 1`). Ungrouping requires the selection to fully cover at least one group, otherwise a hint is shown.
499
+
500
+ ### Rendering & Collapse
501
+
502
+ Group ranges get alternating background colors in the row/column header band; ± collapse buttons (~9px) float inside the header band without reserving a separate gutter. Collapsed rows/columns are skipped during content, border, and header-text rendering (0-size guard against ghost stripes), compatible with scrolling and frozen panes.
503
+
504
+ ### Entry Points
505
+
506
+ - Toolbar "Group" dropdown (between Sort and Filter): enabled only when whole rows or columns are selected; menu items (Add Group / Ungroup / Clear Outline / Expand All / Collapse All) act directly on the selected axis
507
+ - Row/column context menu "Group" submenu: dispatches the same items for the right-clicked axis
508
+
509
+ ### Propagation & Undo
510
+
511
+ Inserting/deleting rows or columns shifts group ranges via `addOutlineForInsert` / `addOutlineForDelete`; fully-emptied groups are auto-removed. Group create / ungroup / clear / collapse each produce one undo step. Validation failures prefer the host-injected in-app alert (`showOutlineAlert` hook), falling back to `window.alert`. Unit tests live in `test/outline-core.test.ts`.
512
+
513
+ ---
514
+
515
+ ## Cell Notes
516
+
517
+ Excel-style cell notes (comments), fully independent from cell values and styles. The engine lives in `spreader/core/notes.ts` — pure-logic, zero Vue/Canvas deps; the UI overlay is `components/note-overlay.vue`.
518
+
519
+ ### Data Model
520
+
521
+ Notes use a **Note Pool** storage pattern: each sheet maintains a `notes` pool keyed by stable `id`, and cells only store a `noteId` reference.
522
+
523
+ `` ypescript
524
+ interface CellNote {
525
+ id: string; // Stable unique ID
526
+ text: string; // Note body (multi-line text)
527
+ author?: string; // Optional author name
528
+ createdAt: number; // Creation time (epoch ms)
529
+ updatedAt: number; // Last modification time (epoch ms)
530
+ }
531
+ ``
532
+
533
+ - **`CellData.noteId`**: Per-cell reference pointing into `SheetState.notes`
534
+ - **`SheetModelData.notes`**: v-model serialized note pool, keyed by noteId
535
+
536
+ The Note Pool design ensures that sort, insert/delete row/column, and other cell-moving operations carry `noteId` together with the cell data — notes never get orphaned.
537
+
538
+ ### Entry Points
539
+
540
+ - **Context menu**: Cell right-click menu offers *New Note / Edit Note / Delete Note*; edit/delete are hidden when no note exists, new is hidden when one does
541
+ - **Shift+F2**: Toggle the edit overlay on a note-bearing cell
542
+ - **Hover preview**: A read-only view overlay pops up when the cursor hovers over a cell with a note
543
+
544
+ ### Rendering
545
+
546
+ - Cells with a note show a 6px red triangle indicator in the top-right corner, drawn on Canvas (zero DOM cost)
547
+ - The `note-overlay.vue` component is a Vue DOM overlay with `position: absolute` mounted inside `.spreadsheet-wrapper`, constrained by `overflow: hidden` to the table area — it never spills over the toolbar, row/column headers, frozen panes, or scrollbars
548
+ - Z-index layering: non-frozen overlays sit *below* the freeze canvas (z-index: 0 vs 1), frozen-region overlays sit *above* it (z-index: 2); scrollbars (z-index: 3) always stay on top for interactivity
549
+
550
+ ### Author Name
551
+
552
+ Set the current author via the `noteAuthor` prop. When empty, saved notes store an empty `author` field, and display falls back to an i18n placeholder (`Unnamed`).
553
+
554
+ ### Persistence & Undo
555
+
556
+ - Notes persist through the sheet v-model (`notes` field), round-tripping correctly through save / load / duplicate-sheet
557
+ - Creating / editing / deleting a note each produces one undo step, sharing the same undo stack as cell values, styles, and borders
558
+
559
+ ---
479
560
  ## Auto Fill
480
561
 
481
562
  An Excel-style fill-handle mechanism (`spreader/core/autofill.ts` pure engine + `composables/interactions.ts` canvas/interaction layer). Drag the small square at the bottom-right corner of the active selection to fill cells: no DOM handles, all drawn on Canvas.
@@ -700,11 +781,11 @@ x-spreader is touch-first: every mouse interaction has a symmetric touch path, v
700
781
 
701
782
  - [x] Frozen panes (freeze rows/columns): *see [Freeze Panes](#features)*
702
783
  - [x] Data Validation: dropdown lists, input constraints (any value / list / whole number / decimal / date / time / text length / custom formula), list dropdown with search & keyboard nav, input message and error alert (Stop/Warning/Information), atomic paste/auto-fill validation, undo/redo and persistence; see [Data Validation](#data-validation)
703
- - [ ] Column / row grouping and collapsing
784
+ - [x] Column / row grouping and collapsing: *see [Row / Column Grouping & Collapsing](#row--column-grouping--collapsing)*
704
785
  - [x] Sort & filter: sort by displayed content with Excel-style Sort Warning dialog (expand selection / current selection only); see [Sorting & Sort Warning](#features)
705
786
  - [x] Auto Filter (AutoFilter): toolbar "Filter" / `Ctrl+Shift+L` to enable, header drop-down arrows, intelligent downward data probing, value/text/number/date multi-type filters, separate clear-column vs remove-all; see [Auto Filter (AutoFilter)](#auto-filter-autofilter)
706
787
  - [x] Touch & mobile interaction: symmetric touch paths for select / context menu / filter / resize / range select / format brush / tab menu; popups close on outside tap; see [Touch & Mobile Interaction](#touch--mobile-interaction)
707
- - [ ] Cell comments / notes
788
+ - [x] Cell comments / notes: Note Pool storage, context-menu CRUD, hover preview, edit overlay, persistence & undo; see [Cell Notes](#cell-notes)
708
789
  - [ ] Print layout
709
790
 
710
791
  ### Long-term