@veltra/sheet 2.0.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.
Files changed (137) hide show
  1. package/dist/core/address.d.ts +60 -0
  2. package/dist/core/address.js +142 -0
  3. package/dist/core/address.js.map +1 -0
  4. package/dist/core/cell-store.d.ts +61 -0
  5. package/dist/core/cell-store.js +135 -0
  6. package/dist/core/cell-store.js.map +1 -0
  7. package/dist/core/command/default-registry.d.ts +10 -0
  8. package/dist/core/command/default-registry.js +18 -0
  9. package/dist/core/command/default-registry.js.map +1 -0
  10. package/dist/core/command/history.d.ts +64 -0
  11. package/dist/core/command/history.js +127 -0
  12. package/dist/core/command/history.js.map +1 -0
  13. package/dist/core/command/merge-cells.d.ts +21 -0
  14. package/dist/core/command/merge-cells.js +74 -0
  15. package/dist/core/command/merge-cells.js.map +1 -0
  16. package/dist/core/command/registry.d.ts +17 -0
  17. package/dist/core/command/registry.js +26 -0
  18. package/dist/core/command/registry.js.map +1 -0
  19. package/dist/core/command/set-cell-formula.d.ts +17 -0
  20. package/dist/core/command/set-cell-formula.js +29 -0
  21. package/dist/core/command/set-cell-formula.js.map +1 -0
  22. package/dist/core/command/set-cell-value.d.ts +22 -0
  23. package/dist/core/command/set-cell-value.js +35 -0
  24. package/dist/core/command/set-cell-value.js.map +1 -0
  25. package/dist/core/command/types.d.ts +68 -0
  26. package/dist/core/events.js +29 -0
  27. package/dist/core/events.js.map +1 -0
  28. package/dist/core/formula/ast.d.ts +58 -0
  29. package/dist/core/formula/ast.js +38 -0
  30. package/dist/core/formula/ast.js.map +1 -0
  31. package/dist/core/formula/dependency-graph.d.ts +70 -0
  32. package/dist/core/formula/dependency-graph.js +269 -0
  33. package/dist/core/formula/dependency-graph.js.map +1 -0
  34. package/dist/core/formula/errors.d.ts +25 -0
  35. package/dist/core/formula/errors.js +36 -0
  36. package/dist/core/formula/errors.js.map +1 -0
  37. package/dist/core/formula/evaluator.d.ts +41 -0
  38. package/dist/core/formula/evaluator.js +154 -0
  39. package/dist/core/formula/evaluator.js.map +1 -0
  40. package/dist/core/formula/functions.d.ts +30 -0
  41. package/dist/core/formula/functions.js +209 -0
  42. package/dist/core/formula/functions.js.map +1 -0
  43. package/dist/core/formula/parser.d.ts +7 -0
  44. package/dist/core/formula/parser.js +208 -0
  45. package/dist/core/formula/parser.js.map +1 -0
  46. package/dist/core/formula/tokenizer.d.ts +37 -0
  47. package/dist/core/formula/tokenizer.js +146 -0
  48. package/dist/core/formula/tokenizer.js.map +1 -0
  49. package/dist/core/merge-manager.d.ts +64 -0
  50. package/dist/core/merge-manager.js +111 -0
  51. package/dist/core/merge-manager.js.map +1 -0
  52. package/dist/core/selection.d.ts +31 -0
  53. package/dist/core/selection.js +62 -0
  54. package/dist/core/selection.js.map +1 -0
  55. package/dist/core/sheet.d.ts +105 -0
  56. package/dist/core/sheet.js +202 -0
  57. package/dist/core/sheet.js.map +1 -0
  58. package/dist/core/workbook.d.ts +43 -0
  59. package/dist/core/workbook.js +77 -0
  60. package/dist/core/workbook.js.map +1 -0
  61. package/dist/grid/sheet-grid.d.ts +59 -0
  62. package/dist/grid/sheet-grid.js +230 -0
  63. package/dist/grid/sheet-grid.js.map +1 -0
  64. package/dist/index.d.ts +28 -0
  65. package/dist/index.js +25 -0
  66. package/dist/tools/builtin.js +72 -0
  67. package/dist/tools/builtin.js.map +1 -0
  68. package/dist/tools/context.d.ts +57 -0
  69. package/dist/tools/context.js +43 -0
  70. package/dist/tools/context.js.map +1 -0
  71. package/dist/tools/registry.d.ts +68 -0
  72. package/dist/tools/registry.js +77 -0
  73. package/dist/tools/registry.js.map +1 -0
  74. package/dist/types/index.d.ts +2 -0
  75. package/dist/types/sheet.d.ts +43 -0
  76. package/dist/vue/index.d.ts +2 -0
  77. package/dist/vue/sheet.js +185 -0
  78. package/dist/vue/sheet.js.map +1 -0
  79. package/dist/vue/sheet.vue.d.ts +24 -0
  80. package/dist/vue/style.d.ts +1 -0
  81. package/dist/vue/style.js +1 -0
  82. package/dist/vue/style2.css +93 -0
  83. package/package.json +37 -0
  84. package/src/core/__test__/address.test.ts +113 -0
  85. package/src/core/__test__/cell-store.test.ts +97 -0
  86. package/src/core/__test__/command.test.ts +377 -0
  87. package/src/core/__test__/formula-adversarial.test.ts +265 -0
  88. package/src/core/__test__/formula-engine.test.ts +470 -0
  89. package/src/core/__test__/formula-functions.test.ts +178 -0
  90. package/src/core/__test__/formula-parser.test.ts +160 -0
  91. package/src/core/__test__/history.test.ts +225 -0
  92. package/src/core/__test__/merge-manager.test.ts +106 -0
  93. package/src/core/__test__/sheet.test.ts +142 -0
  94. package/src/core/address.ts +166 -0
  95. package/src/core/cell-store.ts +173 -0
  96. package/src/core/command/default-registry.ts +15 -0
  97. package/src/core/command/history.ts +167 -0
  98. package/src/core/command/index.ts +7 -0
  99. package/src/core/command/merge-cells.ts +74 -0
  100. package/src/core/command/registry.ts +30 -0
  101. package/src/core/command/set-cell-formula.ts +29 -0
  102. package/src/core/command/set-cell-value.ts +38 -0
  103. package/src/core/command/types.ts +73 -0
  104. package/src/core/events.ts +37 -0
  105. package/src/core/formula/ast.ts +66 -0
  106. package/src/core/formula/dependency-graph.ts +329 -0
  107. package/src/core/formula/errors.ts +41 -0
  108. package/src/core/formula/evaluator.ts +219 -0
  109. package/src/core/formula/functions.ts +266 -0
  110. package/src/core/formula/index.ts +7 -0
  111. package/src/core/formula/parser.ts +198 -0
  112. package/src/core/formula/tokenizer.ts +160 -0
  113. package/src/core/merge-manager.ts +158 -0
  114. package/src/core/selection.ts +66 -0
  115. package/src/core/sheet.ts +257 -0
  116. package/src/core/workbook.ts +99 -0
  117. package/src/env.d.ts +7 -0
  118. package/src/grid/__test__/canvas-mock.ts +49 -0
  119. package/src/grid/__test__/setup.ts +3 -0
  120. package/src/grid/__test__/sheet-grid-formula.test.ts +140 -0
  121. package/src/grid/__test__/sheet-grid.test.ts +177 -0
  122. package/src/grid/sheet-grid.ts +283 -0
  123. package/src/index.ts +16 -0
  124. package/src/tools/__test__/builtin.test.ts +128 -0
  125. package/src/tools/__test__/context.test.ts +157 -0
  126. package/src/tools/__test__/registry.test.ts +102 -0
  127. package/src/tools/builtin.ts +75 -0
  128. package/src/tools/context.ts +106 -0
  129. package/src/tools/index.ts +2 -0
  130. package/src/tools/registry.ts +129 -0
  131. package/src/types/index.ts +1 -0
  132. package/src/types/sheet.ts +41 -0
  133. package/src/vue/__test__/sheet-component.test.ts +267 -0
  134. package/src/vue/index.ts +1 -0
  135. package/src/vue/sheet.vue +195 -0
  136. package/src/vue/style.scss +94 -0
  137. package/src/vue/style.ts +1 -0
@@ -0,0 +1,64 @@
1
+ import { CellAddress, CellRange } from "./address.js";
2
+ //#region src/core/merge-manager.d.ts
3
+ /**
4
+ * 合并单元格管理器(只管几何,不管数据)。
5
+ *
6
+ * - 锚点恒为区域左上角,数据只存在锚点格(数据搬迁由 Sheet 编排)
7
+ * - merges:anchorKey → CellRange
8
+ * - coverIndex:coveredCellKey(不含锚点自身)→ anchorKey,O(1) 查询
9
+ */
10
+ /** 单元格在合并语义下的种类 */
11
+ type MergedCellKind = 'normal' | 'merged-anchor' | 'merged-covered';
12
+ interface CellInfo {
13
+ kind: MergedCellKind;
14
+ /** 锚点地址;普通格为自身 */
15
+ anchor: CellAddress;
16
+ /** 所在合并区域;普通格无 */
17
+ mergeRange?: CellRange;
18
+ }
19
+ interface MergeResult {
20
+ /** 最终生效的合并区域(与既有合并相交时取包围盒,可能大于入参) */
21
+ range: CellRange;
22
+ /** 本次被解除的既有合并 */
23
+ removed: CellRange[];
24
+ }
25
+ declare class MergeManager {
26
+ private merges;
27
+ private coverIndex;
28
+ /**
29
+ * 计算合并结果(纯查询,不修改状态):
30
+ * 1. 所有与 range 相交的既有合并将被解除
31
+ * 2. 新区域 = range 与被解除合并的包围盒
32
+ */
33
+ computeMerge(range: CellRange): MergeResult;
34
+ /** 合并区域(解除相交既有合并 + 包围盒,见 computeMerge) */
35
+ merge(range: CellRange): MergeResult;
36
+ /**
37
+ * 精确登记一个合并区域(不做相交解除与包围盒)。
38
+ * 低层接口,供命令系统回放补丁使用;调用方需保证不与既有合并相交。
39
+ */
40
+ addMerge(range: CellRange): void;
41
+ /** 精确移除一个合并区域(按锚点匹配);不存在时为空操作 */
42
+ removeMerge(range: CellRange): void;
43
+ /** 解除所有与 range 相交的合并,返回被解除的区域 */
44
+ unmerge(range: CellRange): CellRange[];
45
+ /** 地址解析到锚点:被覆盖格 → 锚点;锚点/普通格 → 自身 */
46
+ resolveAnchor(addr: CellAddress): CellAddress;
47
+ /** 是否在任意合并区域内(含锚点) */
48
+ isMerged(addr: CellAddress): boolean;
49
+ /** 是否为被覆盖的非锚点格 */
50
+ isCovered(addr: CellAddress): boolean;
51
+ /** 所在合并区域(锚点与被覆盖格均可查);不在任何合并内返回 undefined */
52
+ getMergeAt(addr: CellAddress): CellRange | undefined;
53
+ /** 合并语义下的单元格信息 */
54
+ getCellInfo(addr: CellAddress): CellInfo;
55
+ /** 全部合并区域(迭代 merges 的副本) */
56
+ getMerges(): CellRange[];
57
+ get size(): number;
58
+ clear(): void;
59
+ private register;
60
+ private unregister;
61
+ }
62
+ //#endregion
63
+ export { CellInfo, MergeManager, MergeResult, MergedCellKind };
64
+ //# sourceMappingURL=merge-manager.d.ts.map
@@ -0,0 +1,111 @@
1
+ import { boundingBox, cellKey, createRange, iterateRange, rangesIntersect } from "./address.js";
2
+ //#region src/core/merge-manager.ts
3
+ var MergeManager = class {
4
+ merges = /* @__PURE__ */ new Map();
5
+ coverIndex = /* @__PURE__ */ new Map();
6
+ /**
7
+ * 计算合并结果(纯查询,不修改状态):
8
+ * 1. 所有与 range 相交的既有合并将被解除
9
+ * 2. 新区域 = range 与被解除合并的包围盒
10
+ */
11
+ computeMerge(range) {
12
+ const removed = [];
13
+ for (const existing of this.merges.values()) if (rangesIntersect(existing, range)) removed.push(existing);
14
+ return {
15
+ range: removed.length > 0 ? boundingBox([range, ...removed]) : createRange(range.start, range.end),
16
+ removed
17
+ };
18
+ }
19
+ /** 合并区域(解除相交既有合并 + 包围盒,见 computeMerge) */
20
+ merge(range) {
21
+ const result = this.computeMerge(range);
22
+ for (const item of result.removed) this.unregister(item);
23
+ this.register(result.range);
24
+ return result;
25
+ }
26
+ /**
27
+ * 精确登记一个合并区域(不做相交解除与包围盒)。
28
+ * 低层接口,供命令系统回放补丁使用;调用方需保证不与既有合并相交。
29
+ */
30
+ addMerge(range) {
31
+ this.register(range);
32
+ }
33
+ /** 精确移除一个合并区域(按锚点匹配);不存在时为空操作 */
34
+ removeMerge(range) {
35
+ this.unregister(range);
36
+ }
37
+ /** 解除所有与 range 相交的合并,返回被解除的区域 */
38
+ unmerge(range) {
39
+ const removed = [];
40
+ for (const existing of this.merges.values()) if (rangesIntersect(existing, range)) removed.push(existing);
41
+ for (const item of removed) this.unregister(item);
42
+ return removed;
43
+ }
44
+ /** 地址解析到锚点:被覆盖格 → 锚点;锚点/普通格 → 自身 */
45
+ resolveAnchor(addr) {
46
+ const merge = this.getMergeAt(addr);
47
+ return merge ? { ...merge.start } : { ...addr };
48
+ }
49
+ /** 是否在任意合并区域内(含锚点) */
50
+ isMerged(addr) {
51
+ return this.getMergeAt(addr) !== void 0;
52
+ }
53
+ /** 是否为被覆盖的非锚点格 */
54
+ isCovered(addr) {
55
+ return this.coverIndex.has(cellKey(addr));
56
+ }
57
+ /** 所在合并区域(锚点与被覆盖格均可查);不在任何合并内返回 undefined */
58
+ getMergeAt(addr) {
59
+ const key = cellKey(addr);
60
+ const asAnchor = this.merges.get(key);
61
+ if (asAnchor) return asAnchor;
62
+ const anchorKey = this.coverIndex.get(key);
63
+ return anchorKey === void 0 ? void 0 : this.merges.get(anchorKey);
64
+ }
65
+ /** 合并语义下的单元格信息 */
66
+ getCellInfo(addr) {
67
+ const merge = this.getMergeAt(addr);
68
+ if (!merge) return {
69
+ kind: "normal",
70
+ anchor: { ...addr }
71
+ };
72
+ const anchor = { ...merge.start };
73
+ if (addr.row === anchor.row && addr.col === anchor.col) return {
74
+ kind: "merged-anchor",
75
+ anchor,
76
+ mergeRange: merge
77
+ };
78
+ return {
79
+ kind: "merged-covered",
80
+ anchor,
81
+ mergeRange: merge
82
+ };
83
+ }
84
+ /** 全部合并区域(迭代 merges 的副本) */
85
+ getMerges() {
86
+ return [...this.merges.values()].map((range) => ({
87
+ start: { ...range.start },
88
+ end: { ...range.end }
89
+ }));
90
+ }
91
+ get size() {
92
+ return this.merges.size;
93
+ }
94
+ clear() {
95
+ this.merges.clear();
96
+ this.coverIndex.clear();
97
+ }
98
+ register(range) {
99
+ const anchorKey = cellKey(range.start);
100
+ this.merges.set(anchorKey, range);
101
+ for (const addr of iterateRange(range)) if (addr.row !== range.start.row || addr.col !== range.start.col) this.coverIndex.set(cellKey(addr), anchorKey);
102
+ }
103
+ unregister(range) {
104
+ this.merges.delete(cellKey(range.start));
105
+ for (const addr of iterateRange(range)) if (addr.row !== range.start.row || addr.col !== range.start.col) this.coverIndex.delete(cellKey(addr));
106
+ }
107
+ };
108
+ //#endregion
109
+ export { MergeManager };
110
+
111
+ //# sourceMappingURL=merge-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-manager.js","names":[],"sources":["../../src/core/merge-manager.ts"],"sourcesContent":["import {\n boundingBox,\n cellKey,\n createRange,\n iterateRange,\n rangesIntersect,\n type CellAddress,\n type CellRange\n} from './address'\n\n/**\n * 合并单元格管理器(只管几何,不管数据)。\n *\n * - 锚点恒为区域左上角,数据只存在锚点格(数据搬迁由 Sheet 编排)\n * - merges:anchorKey → CellRange\n * - coverIndex:coveredCellKey(不含锚点自身)→ anchorKey,O(1) 查询\n */\n\n/** 单元格在合并语义下的种类 */\nexport type MergedCellKind = 'normal' | 'merged-anchor' | 'merged-covered'\n\nexport interface CellInfo {\n kind: MergedCellKind\n /** 锚点地址;普通格为自身 */\n anchor: CellAddress\n /** 所在合并区域;普通格无 */\n mergeRange?: CellRange\n}\n\nexport interface MergeResult {\n /** 最终生效的合并区域(与既有合并相交时取包围盒,可能大于入参) */\n range: CellRange\n /** 本次被解除的既有合并 */\n removed: CellRange[]\n}\n\nexport class MergeManager {\n private merges = new Map<number, CellRange>()\n private coverIndex = new Map<number, number>()\n\n /**\n * 计算合并结果(纯查询,不修改状态):\n * 1. 所有与 range 相交的既有合并将被解除\n * 2. 新区域 = range 与被解除合并的包围盒\n */\n computeMerge(range: CellRange): MergeResult {\n const removed: CellRange[] = []\n for (const existing of this.merges.values()) {\n if (rangesIntersect(existing, range)) removed.push(existing)\n }\n const finalRange =\n removed.length > 0 ? boundingBox([range, ...removed]) : createRange(range.start, range.end)\n return { range: finalRange, removed }\n }\n\n /** 合并区域(解除相交既有合并 + 包围盒,见 computeMerge) */\n merge(range: CellRange): MergeResult {\n const result = this.computeMerge(range)\n for (const item of result.removed) this.unregister(item)\n this.register(result.range)\n return result\n }\n\n /**\n * 精确登记一个合并区域(不做相交解除与包围盒)。\n * 低层接口,供命令系统回放补丁使用;调用方需保证不与既有合并相交。\n */\n addMerge(range: CellRange): void {\n this.register(range)\n }\n\n /** 精确移除一个合并区域(按锚点匹配);不存在时为空操作 */\n removeMerge(range: CellRange): void {\n this.unregister(range)\n }\n\n /** 解除所有与 range 相交的合并,返回被解除的区域 */\n unmerge(range: CellRange): CellRange[] {\n const removed: CellRange[] = []\n for (const existing of this.merges.values()) {\n if (rangesIntersect(existing, range)) removed.push(existing)\n }\n for (const item of removed) this.unregister(item)\n return removed\n }\n\n /** 地址解析到锚点:被覆盖格 → 锚点;锚点/普通格 → 自身 */\n resolveAnchor(addr: CellAddress): CellAddress {\n const merge = this.getMergeAt(addr)\n return merge ? { ...merge.start } : { ...addr }\n }\n\n /** 是否在任意合并区域内(含锚点) */\n isMerged(addr: CellAddress): boolean {\n return this.getMergeAt(addr) !== undefined\n }\n\n /** 是否为被覆盖的非锚点格 */\n isCovered(addr: CellAddress): boolean {\n return this.coverIndex.has(cellKey(addr))\n }\n\n /** 所在合并区域(锚点与被覆盖格均可查);不在任何合并内返回 undefined */\n getMergeAt(addr: CellAddress): CellRange | undefined {\n const key = cellKey(addr)\n const asAnchor = this.merges.get(key)\n if (asAnchor) return asAnchor\n const anchorKey = this.coverIndex.get(key)\n return anchorKey === undefined ? undefined : this.merges.get(anchorKey)\n }\n\n /** 合并语义下的单元格信息 */\n getCellInfo(addr: CellAddress): CellInfo {\n const merge = this.getMergeAt(addr)\n if (!merge) return { kind: 'normal', anchor: { ...addr } }\n const anchor = { ...merge.start }\n if (addr.row === anchor.row && addr.col === anchor.col) {\n return { kind: 'merged-anchor', anchor, mergeRange: merge }\n }\n return { kind: 'merged-covered', anchor, mergeRange: merge }\n }\n\n /** 全部合并区域(迭代 merges 的副本) */\n getMerges(): CellRange[] {\n return [...this.merges.values()].map((range) => ({\n start: { ...range.start },\n end: { ...range.end }\n }))\n }\n\n get size(): number {\n return this.merges.size\n }\n\n clear(): void {\n this.merges.clear()\n this.coverIndex.clear()\n }\n\n private register(range: CellRange): void {\n const anchorKey = cellKey(range.start)\n this.merges.set(anchorKey, range)\n for (const addr of iterateRange(range)) {\n if (addr.row !== range.start.row || addr.col !== range.start.col) {\n this.coverIndex.set(cellKey(addr), anchorKey)\n }\n }\n }\n\n private unregister(range: CellRange): void {\n this.merges.delete(cellKey(range.start))\n for (const addr of iterateRange(range)) {\n if (addr.row !== range.start.row || addr.col !== range.start.col) {\n this.coverIndex.delete(cellKey(addr))\n }\n }\n }\n}\n"],"mappings":";;AAoCA,IAAa,eAAb,MAA0B;CACxB,yBAAiB,IAAI,IAAuB;CAC5C,6BAAqB,IAAI,IAAoB;;;;;;CAO7C,aAAa,OAA+B;EAC1C,MAAM,UAAuB,CAAC;EAC9B,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,GACxC,IAAI,gBAAgB,UAAU,KAAK,GAAG,QAAQ,KAAK,QAAQ;EAI7D,OAAO;GAAE,OADP,QAAQ,SAAS,IAAI,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,YAAY,MAAM,OAAO,MAAM,GAAG;GAChE;EAAQ;CACtC;;CAGA,MAAM,OAA+B;EACnC,MAAM,SAAS,KAAK,aAAa,KAAK;EACtC,KAAK,MAAM,QAAQ,OAAO,SAAS,KAAK,WAAW,IAAI;EACvD,KAAK,SAAS,OAAO,KAAK;EAC1B,OAAO;CACT;;;;;CAMA,SAAS,OAAwB;EAC/B,KAAK,SAAS,KAAK;CACrB;;CAGA,YAAY,OAAwB;EAClC,KAAK,WAAW,KAAK;CACvB;;CAGA,QAAQ,OAA+B;EACrC,MAAM,UAAuB,CAAC;EAC9B,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,GACxC,IAAI,gBAAgB,UAAU,KAAK,GAAG,QAAQ,KAAK,QAAQ;EAE7D,KAAK,MAAM,QAAQ,SAAS,KAAK,WAAW,IAAI;EAChD,OAAO;CACT;;CAGA,cAAc,MAAgC;EAC5C,MAAM,QAAQ,KAAK,WAAW,IAAI;EAClC,OAAO,QAAQ,EAAE,GAAG,MAAM,MAAM,IAAI,EAAE,GAAG,KAAK;CAChD;;CAGA,SAAS,MAA4B;EACnC,OAAO,KAAK,WAAW,IAAI,MAAM,KAAA;CACnC;;CAGA,UAAU,MAA4B;EACpC,OAAO,KAAK,WAAW,IAAI,QAAQ,IAAI,CAAC;CAC1C;;CAGA,WAAW,MAA0C;EACnD,MAAM,MAAM,QAAQ,IAAI;EACxB,MAAM,WAAW,KAAK,OAAO,IAAI,GAAG;EACpC,IAAI,UAAU,OAAO;EACrB,MAAM,YAAY,KAAK,WAAW,IAAI,GAAG;EACzC,OAAO,cAAc,KAAA,IAAY,KAAA,IAAY,KAAK,OAAO,IAAI,SAAS;CACxE;;CAGA,YAAY,MAA6B;EACvC,MAAM,QAAQ,KAAK,WAAW,IAAI;EAClC,IAAI,CAAC,OAAO,OAAO;GAAE,MAAM;GAAU,QAAQ,EAAE,GAAG,KAAK;EAAE;EACzD,MAAM,SAAS,EAAE,GAAG,MAAM,MAAM;EAChC,IAAI,KAAK,QAAQ,OAAO,OAAO,KAAK,QAAQ,OAAO,KACjD,OAAO;GAAE,MAAM;GAAiB;GAAQ,YAAY;EAAM;EAE5D,OAAO;GAAE,MAAM;GAAkB;GAAQ,YAAY;EAAM;CAC7D;;CAGA,YAAyB;EACvB,OAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW;GAC/C,OAAO,EAAE,GAAG,MAAM,MAAM;GACxB,KAAK,EAAE,GAAG,MAAM,IAAI;EACtB,EAAE;CACJ;CAEA,IAAI,OAAe;EACjB,OAAO,KAAK,OAAO;CACrB;CAEA,QAAc;EACZ,KAAK,OAAO,MAAM;EAClB,KAAK,WAAW,MAAM;CACxB;CAEA,SAAiB,OAAwB;EACvC,MAAM,YAAY,QAAQ,MAAM,KAAK;EACrC,KAAK,OAAO,IAAI,WAAW,KAAK;EAChC,KAAK,MAAM,QAAQ,aAAa,KAAK,GACnC,IAAI,KAAK,QAAQ,MAAM,MAAM,OAAO,KAAK,QAAQ,MAAM,MAAM,KAC3D,KAAK,WAAW,IAAI,QAAQ,IAAI,GAAG,SAAS;CAGlD;CAEA,WAAmB,OAAwB;EACzC,KAAK,OAAO,OAAO,QAAQ,MAAM,KAAK,CAAC;EACvC,KAAK,MAAM,QAAQ,aAAa,KAAK,GACnC,IAAI,KAAK,QAAQ,MAAM,MAAM,OAAO,KAAK,QAAQ,MAAM,MAAM,KAC3D,KAAK,WAAW,OAAO,QAAQ,IAAI,CAAC;CAG1C;AACF"}
@@ -0,0 +1,31 @@
1
+ import { CellAddress, CellRange } from "./address.js";
2
+ //#region src/core/selection.d.ts
3
+ /**
4
+ * 选区模型:activeCell + ranges。
5
+ * activeCell 永远指向锚点(构造时注入 resolveAnchor)。
6
+ */
7
+ interface SelectionState {
8
+ /** 活动单元格(锚点语义);未选中时为 null */
9
+ activeCell: CellAddress | null;
10
+ /** 选中区域列表(本阶段单选区,预留多选区) */
11
+ ranges: CellRange[];
12
+ }
13
+ declare class SelectionModel {
14
+ private readonly resolveAnchor;
15
+ private state;
16
+ private emitter;
17
+ constructor(resolveAnchor: (addr: CellAddress) => CellAddress);
18
+ get activeCell(): CellAddress | null;
19
+ get ranges(): CellRange[];
20
+ /** 选中单格;被覆盖格自动解析到锚点 */
21
+ selectCell(addr: CellAddress): void;
22
+ /** 选中区域;activeCell 取区域起点的锚点 */
23
+ selectRange(range: CellRange): void;
24
+ getState(): SelectionState;
25
+ clear(): void;
26
+ on(handler: (state: SelectionState) => void): () => void;
27
+ private setState;
28
+ }
29
+ //#endregion
30
+ export { SelectionModel, SelectionState };
31
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1,62 @@
1
+ import { createRange } from "./address.js";
2
+ import { TypedEventEmitter } from "./events.js";
3
+ //#region src/core/selection.ts
4
+ var SelectionModel = class {
5
+ resolveAnchor;
6
+ state = {
7
+ activeCell: null,
8
+ ranges: []
9
+ };
10
+ emitter = new TypedEventEmitter();
11
+ constructor(resolveAnchor) {
12
+ this.resolveAnchor = resolveAnchor;
13
+ }
14
+ get activeCell() {
15
+ return this.state.activeCell;
16
+ }
17
+ get ranges() {
18
+ return this.state.ranges;
19
+ }
20
+ /** 选中单格;被覆盖格自动解析到锚点 */
21
+ selectCell(addr) {
22
+ const anchor = this.resolveAnchor(addr);
23
+ this.setState({
24
+ activeCell: anchor,
25
+ ranges: [createRange(anchor, anchor)]
26
+ });
27
+ }
28
+ /** 选中区域;activeCell 取区域起点的锚点 */
29
+ selectRange(range) {
30
+ const anchor = this.resolveAnchor(range.start);
31
+ this.setState({
32
+ activeCell: anchor,
33
+ ranges: [range]
34
+ });
35
+ }
36
+ getState() {
37
+ return {
38
+ activeCell: this.state.activeCell ? { ...this.state.activeCell } : null,
39
+ ranges: this.state.ranges.map((range) => ({
40
+ start: { ...range.start },
41
+ end: { ...range.end }
42
+ }))
43
+ };
44
+ }
45
+ clear() {
46
+ this.setState({
47
+ activeCell: null,
48
+ ranges: []
49
+ });
50
+ }
51
+ on(handler) {
52
+ return this.emitter.on("change", handler);
53
+ }
54
+ setState(state) {
55
+ this.state = state;
56
+ this.emitter.emit("change", this.getState());
57
+ }
58
+ };
59
+ //#endregion
60
+ export { SelectionModel };
61
+
62
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","names":[],"sources":["../../src/core/selection.ts"],"sourcesContent":["import { createRange, type CellAddress, type CellRange } from './address'\nimport { TypedEventEmitter } from './events'\n\n/**\n * 选区模型:activeCell + ranges。\n * activeCell 永远指向锚点(构造时注入 resolveAnchor)。\n */\n\nexport interface SelectionState {\n /** 活动单元格(锚点语义);未选中时为 null */\n activeCell: CellAddress | null\n /** 选中区域列表(本阶段单选区,预留多选区) */\n ranges: CellRange[]\n}\n\ntype SelectionEvents = { change: SelectionState }\n\nexport class SelectionModel {\n private state: SelectionState = { activeCell: null, ranges: [] }\n private emitter = new TypedEventEmitter<SelectionEvents>()\n\n constructor(private readonly resolveAnchor: (addr: CellAddress) => CellAddress) {}\n\n get activeCell(): CellAddress | null {\n return this.state.activeCell\n }\n\n get ranges(): CellRange[] {\n return this.state.ranges\n }\n\n /** 选中单格;被覆盖格自动解析到锚点 */\n selectCell(addr: CellAddress): void {\n const anchor = this.resolveAnchor(addr)\n this.setState({ activeCell: anchor, ranges: [createRange(anchor, anchor)] })\n }\n\n /** 选中区域;activeCell 取区域起点的锚点 */\n selectRange(range: CellRange): void {\n const anchor = this.resolveAnchor(range.start)\n this.setState({ activeCell: anchor, ranges: [range] })\n }\n\n getState(): SelectionState {\n return {\n activeCell: this.state.activeCell ? { ...this.state.activeCell } : null,\n ranges: this.state.ranges.map((range) => ({\n start: { ...range.start },\n end: { ...range.end }\n }))\n }\n }\n\n clear(): void {\n this.setState({ activeCell: null, ranges: [] })\n }\n\n on(handler: (state: SelectionState) => void): () => void {\n return this.emitter.on('change', handler)\n }\n\n private setState(state: SelectionState): void {\n this.state = state\n this.emitter.emit('change', this.getState())\n }\n}\n"],"mappings":";;;AAiBA,IAAa,iBAAb,MAA4B;CAIG;CAH7B,QAAgC;EAAE,YAAY;EAAM,QAAQ,CAAC;CAAE;CAC/D,UAAkB,IAAI,kBAAmC;CAEzD,YAAY,eAAoE;EAAnD,KAAA,gBAAA;CAAoD;CAEjF,IAAI,aAAiC;EACnC,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,SAAsB;EACxB,OAAO,KAAK,MAAM;CACpB;;CAGA,WAAW,MAAyB;EAClC,MAAM,SAAS,KAAK,cAAc,IAAI;EACtC,KAAK,SAAS;GAAE,YAAY;GAAQ,QAAQ,CAAC,YAAY,QAAQ,MAAM,CAAC;EAAE,CAAC;CAC7E;;CAGA,YAAY,OAAwB;EAClC,MAAM,SAAS,KAAK,cAAc,MAAM,KAAK;EAC7C,KAAK,SAAS;GAAE,YAAY;GAAQ,QAAQ,CAAC,KAAK;EAAE,CAAC;CACvD;CAEA,WAA2B;EACzB,OAAO;GACL,YAAY,KAAK,MAAM,aAAa,EAAE,GAAG,KAAK,MAAM,WAAW,IAAI;GACnE,QAAQ,KAAK,MAAM,OAAO,KAAK,WAAW;IACxC,OAAO,EAAE,GAAG,MAAM,MAAM;IACxB,KAAK,EAAE,GAAG,MAAM,IAAI;GACtB,EAAE;EACJ;CACF;CAEA,QAAc;EACZ,KAAK,SAAS;GAAE,YAAY;GAAM,QAAQ,CAAC;EAAE,CAAC;CAChD;CAEA,GAAG,SAAsD;EACvD,OAAO,KAAK,QAAQ,GAAG,UAAU,OAAO;CAC1C;CAEA,SAAiB,OAA6B;EAC5C,KAAK,QAAQ;EACb,KAAK,QAAQ,KAAK,UAAU,KAAK,SAAS,CAAC;CAC7C;AACF"}
@@ -0,0 +1,105 @@
1
+ import { CellAddress, CellRange } from "./address.js";
2
+ import { CellData, CellStore, CellValue } from "./cell-store.js";
3
+ import { CellInfo, MergeManager } from "./merge-manager.js";
4
+ import { SelectionModel, SelectionState } from "./selection.js";
5
+ import { HistoryManager, HistoryState } from "./command/history.js";
6
+ import { SetCellValueItem } from "./command/set-cell-value.js";
7
+ import { DependencyGraph } from "./formula/dependency-graph.js";
8
+ //#region src/core/sheet.d.ts
9
+ /**
10
+ * Sheet = cell-store + merge-manager + selection + history 的组合,统一操作入口。
11
+ *
12
+ * 语义约定:
13
+ * - `getCellData`:原始存储语义,被合并覆盖的非锚点格 → undefined
14
+ * - `getDisplayValue`:锚点解析语义,被覆盖格返回锚点的值
15
+ * - `setCellValue` / `selectCell`:内部先 resolveAnchor(用户操作永远落锚点)
16
+ * - 一切写操作(setCellValue / setCell / setCells / setCellFormula / mergeCells / unmergeCells)
17
+ * 都经命令系统执行(applyPatch 是唯一变更通道),天然获得 undo/redo 能力
18
+ * - 公式:`setCellValue` 识别 '=' 前缀走 `setCellFormula`;命令执行后自动增量重算,
19
+ * 重算派生变更(含跨表)并入同一 undo 单元
20
+ */
21
+ type SheetEvents = {
22
+ /** 单元格数据变化(含删除) */
23
+ 'cell-change': {
24
+ addr: CellAddress;
25
+ };
26
+ /** 合并结构变化(合并/取消合并) */
27
+ 'merge-change': {
28
+ range: CellRange;
29
+ };
30
+ /** 选区变化 */
31
+ 'selection-change': SelectionState;
32
+ /** 历史栈变化(undo/redo 可用状态,供工具栏按钮置灰) */
33
+ 'history-change': HistoryState;
34
+ };
35
+ declare class Sheet {
36
+ readonly store: CellStore;
37
+ readonly merges: MergeManager;
38
+ readonly selection: SelectionModel;
39
+ /** undo/redo 历史(命令系统) */
40
+ readonly history: HistoryManager;
41
+ /** 公式依赖图(工作簿内多 sheet 共享;独立 Sheet 自建) */
42
+ readonly formulaGraph: DependencyGraph;
43
+ name: string;
44
+ private emitter;
45
+ constructor(name?: string, formulaGraph?: DependencyGraph);
46
+ get rowCount(): number;
47
+ get colCount(): number;
48
+ /** 原始存储语义读取(被覆盖格 → undefined) */
49
+ getCellData(addr: CellAddress): CellData | undefined;
50
+ /** 锚点解析语义读取(被覆盖格 → 锚点的值) */
51
+ getDisplayValue(addr: CellAddress): CellValue | undefined;
52
+ /** 写入原始值(解析到锚点;空值 = 清除);'=' 前缀按公式处理;经命令执行,可撤销 */
53
+ setCellValue(addr: CellAddress, value: CellValue): void;
54
+ /** 写入完整 CellData(解析到锚点;空数据 = 清除);经命令执行,可撤销 */
55
+ setCell(addr: CellAddress, data?: CellData): void;
56
+ /**
57
+ * 写入公式(可带 '=' 前缀;空白公式 = 清除);经命令执行,可撤销。
58
+ * 公式原文存 CellData.f,计算缓存(v/t)由增量重算填充;解析失败 → #ERROR!。
59
+ */
60
+ setCellFormula(addr: CellAddress, formula: string): void;
61
+ /** 批量写入(一次调用 = 一个 undo 单元,供粘贴/填充复用) */
62
+ setCells(items: SetCellValueItem[]): void;
63
+ /** 合并语义下的单元格信息(普通格 / 合并锚点 / 被覆盖格) */
64
+ getCellInfo(addr: CellAddress): CellInfo;
65
+ /**
66
+ * 合并区域。值保留规则(同 Excel/univer):
67
+ * 仅保留包围盒内按行主序第一个有值格的值,写入新锚点,其余被覆盖格清空。
68
+ * @returns 最终生效的合并区域(与既有合并相交时取包围盒)
69
+ */
70
+ mergeCells(range: CellRange): CellRange;
71
+ /** 解除与 range 相交的所有合并;仅原锚点保留值(被覆盖格本就无值) */
72
+ unmergeCells(range: CellRange): void;
73
+ /** 经默认注册表执行命令;产生的 mutation 与公式重算派生 mutation 一并推入历史 */
74
+ executeCommand<R = void>(commandId: string, params: unknown): R | undefined;
75
+ /** 开启事务(事务内所有命令合并为一个 undo 单元;可嵌套,拍平到最外层) */
76
+ beginTransaction(): void;
77
+ /** 提交事务 */
78
+ commit(): void;
79
+ /** 回滚事务:还原缓冲中已应用的变更并放弃事务 */
80
+ rollback(): void;
81
+ /** 撤销一步;无可撤销时返回 false */
82
+ undo(): boolean;
83
+ /** 重做一步;无可重做时返回 false */
84
+ redo(): boolean;
85
+ get canUndo(): boolean;
86
+ get canRedo(): boolean;
87
+ getSelection(): SelectionState;
88
+ /** 选中单格(被覆盖格自动定位锚点) */
89
+ selectCell(addr: CellAddress): void;
90
+ selectRange(range: CellRange): void;
91
+ on<K extends keyof SheetEvents>(type: K, handler: (payload: SheetEvents[K]) => void): () => void;
92
+ off<K extends keyof SheetEvents>(type: K, handler: (payload: SheetEvents[K]) => void): void;
93
+ private readonly boundApplyPatch;
94
+ /** 应用补丁:命令执行与 undo/redo 回放共用的唯一变更通道 */
95
+ private applyPatch;
96
+ /**
97
+ * 命令执行后的公式增量重算:从命令 mutation 提取变更格 → 依赖图标脏 +
98
+ * 拓扑序重算 → 派生补丁立即应用,作为附加 mutation 并入同一 undo 单元。
99
+ * undo/redo 纯补丁回放(不重算),图状态由 applyPatch 内的 syncCell 维持。
100
+ */
101
+ private recalcAfterCommand;
102
+ }
103
+ //#endregion
104
+ export { Sheet, SheetEvents };
105
+ //# sourceMappingURL=sheet.d.ts.map
@@ -0,0 +1,202 @@
1
+ import { TypedEventEmitter } from "./events.js";
2
+ import { CellStore, inferCellType } from "./cell-store.js";
3
+ import { MergeManager } from "./merge-manager.js";
4
+ import { SelectionModel } from "./selection.js";
5
+ import { MergeCellsCommand, UnmergeCellsCommand } from "./command/merge-cells.js";
6
+ import { SetCellFormulaCommand } from "./command/set-cell-formula.js";
7
+ import { SetCellValueCommand } from "./command/set-cell-value.js";
8
+ import { defaultCommandRegistry } from "./command/default-registry.js";
9
+ import { HistoryManager } from "./command/history.js";
10
+ import { DependencyGraph } from "./formula/dependency-graph.js";
11
+ //#region src/core/sheet.ts
12
+ var Sheet = class {
13
+ store = new CellStore();
14
+ merges = new MergeManager();
15
+ selection;
16
+ /** undo/redo 历史(命令系统) */
17
+ history;
18
+ /** 公式依赖图(工作簿内多 sheet 共享;独立 Sheet 自建) */
19
+ formulaGraph;
20
+ name;
21
+ emitter = new TypedEventEmitter();
22
+ constructor(name = "Sheet1", formulaGraph) {
23
+ this.name = name;
24
+ this.selection = new SelectionModel((addr) => this.merges.resolveAnchor(addr));
25
+ this.selection.on((state) => this.emitter.emit("selection-change", state));
26
+ this.history = new HistoryManager(this.boundApplyPatch);
27
+ this.history.onChange((state) => this.emitter.emit("history-change", state));
28
+ this.formulaGraph = formulaGraph ?? new DependencyGraph();
29
+ this.formulaGraph.registerSheet(this);
30
+ }
31
+ get rowCount() {
32
+ return this.store.rowCount;
33
+ }
34
+ get colCount() {
35
+ return this.store.colCount;
36
+ }
37
+ /** 原始存储语义读取(被覆盖格 → undefined) */
38
+ getCellData(addr) {
39
+ return this.store.getCell(addr);
40
+ }
41
+ /** 锚点解析语义读取(被覆盖格 → 锚点的值) */
42
+ getDisplayValue(addr) {
43
+ const anchor = this.merges.resolveAnchor(addr);
44
+ return this.store.getCell(anchor)?.v ?? void 0;
45
+ }
46
+ /** 写入原始值(解析到锚点;空值 = 清除);'=' 前缀按公式处理;经命令执行,可撤销 */
47
+ setCellValue(addr, value) {
48
+ if (typeof value === "string" && value.startsWith("=")) {
49
+ this.setCellFormula(addr, value);
50
+ return;
51
+ }
52
+ const data = value == null || value === "" ? void 0 : {
53
+ v: value,
54
+ t: inferCellType(value)
55
+ };
56
+ this.setCells([{
57
+ addr,
58
+ data
59
+ }]);
60
+ }
61
+ /** 写入完整 CellData(解析到锚点;空数据 = 清除);经命令执行,可撤销 */
62
+ setCell(addr, data) {
63
+ this.setCells([{
64
+ addr,
65
+ data
66
+ }]);
67
+ }
68
+ /**
69
+ * 写入公式(可带 '=' 前缀;空白公式 = 清除);经命令执行,可撤销。
70
+ * 公式原文存 CellData.f,计算缓存(v/t)由增量重算填充;解析失败 → #ERROR!。
71
+ */
72
+ setCellFormula(addr, formula) {
73
+ const text = formula.startsWith("=") ? formula.slice(1) : formula;
74
+ if (text.trim() === "") {
75
+ this.setCells([{
76
+ addr,
77
+ data: void 0
78
+ }]);
79
+ return;
80
+ }
81
+ this.executeCommand(SetCellFormulaCommand.id, {
82
+ addr,
83
+ formula: text
84
+ });
85
+ }
86
+ /** 批量写入(一次调用 = 一个 undo 单元,供粘贴/填充复用) */
87
+ setCells(items) {
88
+ this.executeCommand(SetCellValueCommand.id, { items });
89
+ }
90
+ /** 合并语义下的单元格信息(普通格 / 合并锚点 / 被覆盖格) */
91
+ getCellInfo(addr) {
92
+ return this.merges.getCellInfo(addr);
93
+ }
94
+ /**
95
+ * 合并区域。值保留规则(同 Excel/univer):
96
+ * 仅保留包围盒内按行主序第一个有值格的值,写入新锚点,其余被覆盖格清空。
97
+ * @returns 最终生效的合并区域(与既有合并相交时取包围盒)
98
+ */
99
+ mergeCells(range) {
100
+ return this.executeCommand(MergeCellsCommand.id, { range });
101
+ }
102
+ /** 解除与 range 相交的所有合并;仅原锚点保留值(被覆盖格本就无值) */
103
+ unmergeCells(range) {
104
+ this.executeCommand(UnmergeCellsCommand.id, { range });
105
+ }
106
+ /** 经默认注册表执行命令;产生的 mutation 与公式重算派生 mutation 一并推入历史 */
107
+ executeCommand(commandId, params) {
108
+ const result = defaultCommandRegistry.execute({
109
+ sheet: this,
110
+ applyPatch: this.boundApplyPatch
111
+ }, commandId, params);
112
+ if (result && result.mutations.length > 0) {
113
+ const mutations = [...result.mutations];
114
+ const recalcMutation = this.recalcAfterCommand(result.mutations);
115
+ if (recalcMutation) mutations.push(recalcMutation);
116
+ this.history.push(mutations);
117
+ }
118
+ return result?.result;
119
+ }
120
+ /** 开启事务(事务内所有命令合并为一个 undo 单元;可嵌套,拍平到最外层) */
121
+ beginTransaction() {
122
+ this.history.beginTransaction();
123
+ }
124
+ /** 提交事务 */
125
+ commit() {
126
+ this.history.commit();
127
+ }
128
+ /** 回滚事务:还原缓冲中已应用的变更并放弃事务 */
129
+ rollback() {
130
+ this.history.rollback();
131
+ }
132
+ /** 撤销一步;无可撤销时返回 false */
133
+ undo() {
134
+ return this.history.undo();
135
+ }
136
+ /** 重做一步;无可重做时返回 false */
137
+ redo() {
138
+ return this.history.redo();
139
+ }
140
+ get canUndo() {
141
+ return this.history.canUndo;
142
+ }
143
+ get canRedo() {
144
+ return this.history.canRedo;
145
+ }
146
+ getSelection() {
147
+ return this.selection.getState();
148
+ }
149
+ /** 选中单格(被覆盖格自动定位锚点) */
150
+ selectCell(addr) {
151
+ this.selection.selectCell(addr);
152
+ }
153
+ selectRange(range) {
154
+ this.selection.selectRange(range);
155
+ }
156
+ on(type, handler) {
157
+ return this.emitter.on(type, handler);
158
+ }
159
+ off(type, handler) {
160
+ this.emitter.off(type, handler);
161
+ }
162
+ boundApplyPatch = (patch, direction) => {
163
+ (patch.kind === "cell" ? patch.sheet ?? this : this).applyPatch(patch, direction);
164
+ };
165
+ /** 应用补丁:命令执行与 undo/redo 回放共用的唯一变更通道 */
166
+ applyPatch(patch, direction) {
167
+ if (patch.kind === "cell") {
168
+ const data = direction === "redo" ? patch.after : patch.before;
169
+ const previous = direction === "redo" ? patch.before : patch.after;
170
+ this.store.setCell(patch.addr, data);
171
+ this.formulaGraph.syncCell(this, patch.addr, previous, data);
172
+ this.emitter.emit("cell-change", { addr: patch.addr });
173
+ return;
174
+ }
175
+ if (direction === "redo" ? patch.after : patch.before) this.merges.addMerge(patch.range);
176
+ else this.merges.removeMerge(patch.range);
177
+ this.emitter.emit("merge-change", { range: patch.range });
178
+ }
179
+ /**
180
+ * 命令执行后的公式增量重算:从命令 mutation 提取变更格 → 依赖图标脏 +
181
+ * 拓扑序重算 → 派生补丁立即应用,作为附加 mutation 并入同一 undo 单元。
182
+ * undo/redo 纯补丁回放(不重算),图状态由 applyPatch 内的 syncCell 维持。
183
+ */
184
+ recalcAfterCommand(mutations) {
185
+ const changed = [];
186
+ for (const mutation of mutations) for (const patch of mutation.redo) if (patch.kind === "cell") changed.push({
187
+ sheet: patch.sheet ?? this,
188
+ addr: patch.addr
189
+ });
190
+ const derived = this.formulaGraph.recalc(changed);
191
+ if (derived.length === 0) return void 0;
192
+ for (const patch of derived) this.boundApplyPatch(patch, "redo");
193
+ return {
194
+ redo: derived,
195
+ undo: [...derived].reverse()
196
+ };
197
+ }
198
+ };
199
+ //#endregion
200
+ export { Sheet };
201
+
202
+ //# sourceMappingURL=sheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheet.js","names":[],"sources":["../../src/core/sheet.ts"],"sourcesContent":["import type { CellAddress, CellRange } from './address'\nimport { inferCellType, CellStore, type CellData, type CellValue } from './cell-store'\nimport { defaultCommandRegistry } from './command/default-registry'\nimport { HistoryManager, type HistoryState } from './command/history'\nimport { MergeCellsCommand, UnmergeCellsCommand } from './command/merge-cells'\nimport { SetCellFormulaCommand } from './command/set-cell-formula'\nimport { SetCellValueCommand, type SetCellValueItem } from './command/set-cell-value'\nimport type { Mutation, Patch, PatchDirection } from './command/types'\nimport { TypedEventEmitter } from './events'\nimport { DependencyGraph } from './formula/dependency-graph'\nimport { MergeManager, type CellInfo } from './merge-manager'\nimport { SelectionModel, type SelectionState } from './selection'\n\n/**\n * Sheet = cell-store + merge-manager + selection + history 的组合,统一操作入口。\n *\n * 语义约定:\n * - `getCellData`:原始存储语义,被合并覆盖的非锚点格 → undefined\n * - `getDisplayValue`:锚点解析语义,被覆盖格返回锚点的值\n * - `setCellValue` / `selectCell`:内部先 resolveAnchor(用户操作永远落锚点)\n * - 一切写操作(setCellValue / setCell / setCells / setCellFormula / mergeCells / unmergeCells)\n * 都经命令系统执行(applyPatch 是唯一变更通道),天然获得 undo/redo 能力\n * - 公式:`setCellValue` 识别 '=' 前缀走 `setCellFormula`;命令执行后自动增量重算,\n * 重算派生变更(含跨表)并入同一 undo 单元\n */\n\nexport type SheetEvents = {\n /** 单元格数据变化(含删除) */\n 'cell-change': { addr: CellAddress }\n /** 合并结构变化(合并/取消合并) */\n 'merge-change': { range: CellRange }\n /** 选区变化 */\n 'selection-change': SelectionState\n /** 历史栈变化(undo/redo 可用状态,供工具栏按钮置灰) */\n 'history-change': HistoryState\n}\n\nexport class Sheet {\n readonly store = new CellStore()\n readonly merges = new MergeManager()\n readonly selection: SelectionModel\n /** undo/redo 历史(命令系统) */\n readonly history: HistoryManager\n /** 公式依赖图(工作簿内多 sheet 共享;独立 Sheet 自建) */\n readonly formulaGraph: DependencyGraph\n\n name: string\n\n private emitter = new TypedEventEmitter<SheetEvents>()\n\n constructor(name = 'Sheet1', formulaGraph?: DependencyGraph) {\n this.name = name\n this.selection = new SelectionModel((addr) => this.merges.resolveAnchor(addr))\n this.selection.on((state) => this.emitter.emit('selection-change', state))\n this.history = new HistoryManager(this.boundApplyPatch)\n this.history.onChange((state) => this.emitter.emit('history-change', state))\n this.formulaGraph = formulaGraph ?? new DependencyGraph()\n this.formulaGraph.registerSheet(this)\n }\n\n get rowCount(): number {\n return this.store.rowCount\n }\n\n get colCount(): number {\n return this.store.colCount\n }\n\n // ─── 单元格数据 ────────────────────────────────────────────\n\n /** 原始存储语义读取(被覆盖格 → undefined) */\n getCellData(addr: CellAddress): CellData | undefined {\n return this.store.getCell(addr)\n }\n\n /** 锚点解析语义读取(被覆盖格 → 锚点的值) */\n getDisplayValue(addr: CellAddress): CellValue | undefined {\n const anchor = this.merges.resolveAnchor(addr)\n return this.store.getCell(anchor)?.v ?? undefined\n }\n\n /** 写入原始值(解析到锚点;空值 = 清除);'=' 前缀按公式处理;经命令执行,可撤销 */\n setCellValue(addr: CellAddress, value: CellValue): void {\n if (typeof value === 'string' && value.startsWith('=')) {\n this.setCellFormula(addr, value)\n return\n }\n const data = value == null || value === '' ? undefined : { v: value, t: inferCellType(value) }\n this.setCells([{ addr, data }])\n }\n\n /** 写入完整 CellData(解析到锚点;空数据 = 清除);经命令执行,可撤销 */\n setCell(addr: CellAddress, data?: CellData): void {\n this.setCells([{ addr, data }])\n }\n\n /**\n * 写入公式(可带 '=' 前缀;空白公式 = 清除);经命令执行,可撤销。\n * 公式原文存 CellData.f,计算缓存(v/t)由增量重算填充;解析失败 → #ERROR!。\n */\n setCellFormula(addr: CellAddress, formula: string): void {\n const text = formula.startsWith('=') ? formula.slice(1) : formula\n if (text.trim() === '') {\n this.setCells([{ addr, data: undefined }])\n return\n }\n this.executeCommand(SetCellFormulaCommand.id, { addr, formula: text })\n }\n\n /** 批量写入(一次调用 = 一个 undo 单元,供粘贴/填充复用) */\n setCells(items: SetCellValueItem[]): void {\n this.executeCommand(SetCellValueCommand.id, { items })\n }\n\n // ─── 合并 ────────────────────────────────────────────────\n\n /** 合并语义下的单元格信息(普通格 / 合并锚点 / 被覆盖格) */\n getCellInfo(addr: CellAddress): CellInfo {\n return this.merges.getCellInfo(addr)\n }\n\n /**\n * 合并区域。值保留规则(同 Excel/univer):\n * 仅保留包围盒内按行主序第一个有值格的值,写入新锚点,其余被覆盖格清空。\n * @returns 最终生效的合并区域(与既有合并相交时取包围盒)\n */\n mergeCells(range: CellRange): CellRange {\n return this.executeCommand<CellRange>(MergeCellsCommand.id, { range })!\n }\n\n /** 解除与 range 相交的所有合并;仅原锚点保留值(被覆盖格本就无值) */\n unmergeCells(range: CellRange): void {\n this.executeCommand(UnmergeCellsCommand.id, { range })\n }\n\n // ─── 命令与历史 ──────────────────────────────────────────\n\n /** 经默认注册表执行命令;产生的 mutation 与公式重算派生 mutation 一并推入历史 */\n executeCommand<R = void>(commandId: string, params: unknown): R | undefined {\n const result = defaultCommandRegistry.execute<R>(\n { sheet: this, applyPatch: this.boundApplyPatch },\n commandId,\n params\n )\n if (result && result.mutations.length > 0) {\n const mutations = [...result.mutations]\n const recalcMutation = this.recalcAfterCommand(result.mutations)\n if (recalcMutation) mutations.push(recalcMutation)\n this.history.push(mutations)\n }\n return result?.result\n }\n\n /** 开启事务(事务内所有命令合并为一个 undo 单元;可嵌套,拍平到最外层) */\n beginTransaction(): void {\n this.history.beginTransaction()\n }\n\n /** 提交事务 */\n commit(): void {\n this.history.commit()\n }\n\n /** 回滚事务:还原缓冲中已应用的变更并放弃事务 */\n rollback(): void {\n this.history.rollback()\n }\n\n /** 撤销一步;无可撤销时返回 false */\n undo(): boolean {\n return this.history.undo()\n }\n\n /** 重做一步;无可重做时返回 false */\n redo(): boolean {\n return this.history.redo()\n }\n\n get canUndo(): boolean {\n return this.history.canUndo\n }\n\n get canRedo(): boolean {\n return this.history.canRedo\n }\n\n // ─── 选区 ────────────────────────────────────────────────\n\n getSelection(): SelectionState {\n return this.selection.getState()\n }\n\n /** 选中单格(被覆盖格自动定位锚点) */\n selectCell(addr: CellAddress): void {\n this.selection.selectCell(addr)\n }\n\n selectRange(range: CellRange): void {\n this.selection.selectRange(range)\n }\n\n // ─── 事件 ────────────────────────────────────────────────\n\n on<K extends keyof SheetEvents>(type: K, handler: (payload: SheetEvents[K]) => void): () => void {\n return this.emitter.on(type, handler)\n }\n\n off<K extends keyof SheetEvents>(type: K, handler: (payload: SheetEvents[K]) => void): void {\n this.emitter.off(type, handler)\n }\n\n // ─── 内部 ────────────────────────────────────────────────\n\n private readonly boundApplyPatch = (patch: Patch, direction: PatchDirection): void => {\n // 跨表重算的派生补丁路由到目标 sheet(回放经源 sheet 历史时同样按此路由)\n const target = patch.kind === 'cell' ? (patch.sheet ?? this) : this\n target.applyPatch(patch, direction)\n }\n\n /** 应用补丁:命令执行与 undo/redo 回放共用的唯一变更通道 */\n private applyPatch(patch: Patch, direction: PatchDirection): void {\n if (patch.kind === 'cell') {\n const data = direction === 'redo' ? patch.after : patch.before\n const previous = direction === 'redo' ? patch.before : patch.after\n this.store.setCell(patch.addr, data)\n // 公式依赖图同步(f 增/删/改 → 节点增/删/重建;undo/redo 回放同样覆盖)\n this.formulaGraph.syncCell(this, patch.addr, previous, data)\n this.emitter.emit('cell-change', { addr: patch.addr })\n return\n }\n const exists = direction === 'redo' ? patch.after : patch.before\n if (exists) {\n this.merges.addMerge(patch.range)\n } else {\n this.merges.removeMerge(patch.range)\n }\n this.emitter.emit('merge-change', { range: patch.range })\n }\n\n /**\n * 命令执行后的公式增量重算:从命令 mutation 提取变更格 → 依赖图标脏 +\n * 拓扑序重算 → 派生补丁立即应用,作为附加 mutation 并入同一 undo 单元。\n * undo/redo 纯补丁回放(不重算),图状态由 applyPatch 内的 syncCell 维持。\n */\n private recalcAfterCommand(mutations: Mutation[]): Mutation | undefined {\n const changed: { sheet: Sheet; addr: CellAddress }[] = []\n for (const mutation of mutations) {\n for (const patch of mutation.redo) {\n if (patch.kind === 'cell') changed.push({ sheet: patch.sheet ?? this, addr: patch.addr })\n }\n }\n const derived = this.formulaGraph.recalc(changed)\n if (derived.length === 0) return undefined\n for (const patch of derived) this.boundApplyPatch(patch, 'redo')\n return { redo: derived, undo: [...derived].reverse() }\n }\n}\n"],"mappings":"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"}