ooxml-excel-editor 1.3.3 → 1.11.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 (51) hide show
  1. package/CHANGELOG.md +283 -0
  2. package/README.md +52 -11
  3. package/dist/chunks/index-CeTZbV_m.js +13673 -0
  4. package/dist/chunks/{index.es-D9BGYyEt.js → index.es-CNm6wZK8.js} +1 -1
  5. package/dist/chunks/{index.es-n6H_ncuE.js → index.es-DF0q70BO.js} +1 -1
  6. package/dist/chunks/{jspdf.es.min-B6-ocR7J.js → jspdf.es.min-65KuNx_3.js} +2 -2
  7. package/dist/chunks/{jspdf.es.min-Dbn0akWf.js → jspdf.es.min-Bo8KrqZO.js} +2 -2
  8. package/dist/chunks/plugin-overlay-C_fL02Qc.js +12128 -0
  9. package/dist/chunks/{toolbar-icons-fOm95ASq.js → toolbar-icons-BjwdJDiN.js} +60 -65
  10. package/dist/components/ExcelViewer.vue.d.ts +46 -1
  11. package/dist/components/FindBar.vue.d.ts +8 -0
  12. package/dist/core/edit/autofill.d.ts +10 -0
  13. package/dist/core/edit/clipboard-html.d.ts +12 -0
  14. package/dist/core/edit/clipboard-snapshot.d.ts +76 -0
  15. package/dist/core/edit/commands.d.ts +9 -1
  16. package/dist/core/edit/context-menu.d.ts +14 -1
  17. package/dist/core/edit/data-validation.d.ts +15 -0
  18. package/dist/core/edit/edit-controller.d.ts +33 -2
  19. package/dist/core/edit/editor-context.d.ts +5 -2
  20. package/dist/core/edit/paste-behavior.d.ts +33 -0
  21. package/dist/core/edit/types.d.ts +26 -0
  22. package/dist/core/export/exporter.d.ts +2 -0
  23. package/dist/core/export/pivot-tables.d.ts +17 -0
  24. package/dist/core/export/xlsx-writer.d.ts +6 -0
  25. package/dist/core/index.d.ts +4 -2
  26. package/dist/core/model/mutations.d.ts +4 -0
  27. package/dist/core/model/types.d.ts +113 -2
  28. package/dist/core/parser/pivot-parser.d.ts +3 -0
  29. package/dist/core/plugin.d.ts +69 -5
  30. package/dist/core/render/canvas-renderer.d.ts +28 -0
  31. package/dist/core/render/pivot-toggle.d.ts +13 -0
  32. package/dist/core/viewer/comment-dialog-host.d.ts +16 -0
  33. package/dist/core/viewer/conditional-format-dialog-host.d.ts +30 -0
  34. package/dist/core/viewer/controller.d.ts +140 -8
  35. package/dist/core/viewer/number-format-dialog-host.d.ts +25 -0
  36. package/dist/core/viewer/overlay-manager.d.ts +1 -0
  37. package/dist/core/viewer/paste-config-host.d.ts +12 -0
  38. package/dist/core/viewer/pivot-dialog-host.d.ts +48 -0
  39. package/dist/core/viewer/readonly-prompt-host.d.ts +23 -0
  40. package/dist/core/viewer/validation-prompt-host.d.ts +25 -0
  41. package/dist/core.js +67 -64
  42. package/dist/index.d.ts +2 -2
  43. package/dist/index.js +1110 -925
  44. package/dist/react/ExcelViewer.d.ts +41 -4
  45. package/dist/react.js +835 -628
  46. package/dist/style.css +1 -1
  47. package/dist/vue2.css +1 -1
  48. package/dist/vue2.js +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunks/index-6q8kSGQg.js +0 -10575
  51. package/dist/chunks/plugin-overlay-BUrPrpT2.js +0 -9146
@@ -1,7 +1,7 @@
1
1
  var S = Object.defineProperty;
2
2
  var T = Object.getOwnPropertySymbols;
3
3
  var E = Object.prototype.hasOwnProperty, H = Object.prototype.propertyIsEnumerable;
4
- var j = (e, r, t) => r in e ? S(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t, f = (e, r) => {
4
+ var j = (e, r, t) => r in e ? S(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t, w = (e, r) => {
5
5
  for (var t in r || (r = {}))
6
6
  E.call(r, t) && j(e, t, r[t]);
7
7
  if (T)
@@ -9,22 +9,8 @@ var j = (e, r, t) => r in e ? S(e, r, { enumerable: !0, configurable: !0, writab
9
9
  H.call(r, t) && j(e, t, r[t]);
10
10
  return e;
11
11
  };
12
- import { i as R, p as G } from "./plugin-overlay-BUrPrpT2.js";
13
- const N = Array(17).fill("#000000");
14
- function F() {
15
- return {
16
- font: { name: "Calibri", size: 11, bold: !1, italic: !1, underline: !1, strike: !1, color: "#000000" },
17
- fill: { type: "none" },
18
- borders: {},
19
- hAlign: "general",
20
- vAlign: "bottom",
21
- wrapText: !1,
22
- shrinkToFit: !1,
23
- textRotation: 0,
24
- indent: 0,
25
- numFmt: "General"
26
- };
27
- }
12
+ import { m as N, i as R, p as G } from "./plugin-overlay-C_fL02Qc.js";
13
+ const O = Array(17).fill("#000000");
28
14
  function V(e, r) {
29
15
  return {
30
16
  name: e,
@@ -32,7 +18,7 @@ function V(e, r) {
32
18
  state: "visible",
33
19
  dimension: { rows: 0, cols: 0 },
34
20
  cells: /* @__PURE__ */ new Map(),
35
- styles: [F()],
21
+ styles: [N()],
36
22
  merges: [],
37
23
  columns: /* @__PURE__ */ new Map(),
38
24
  rows: /* @__PURE__ */ new Map(),
@@ -45,10 +31,11 @@ function V(e, r) {
45
31
  charts: [],
46
32
  shapes: [],
47
33
  sparklines: [],
34
+ pivotTables: [],
48
35
  showGridLines: !0
49
36
  };
50
37
  }
51
- function O(e, r) {
38
+ function $(e, r) {
52
39
  if (e == null || e === "") return { raw: null, type: "empty" };
53
40
  if (typeof e == "number") return { raw: e, type: "number" };
54
41
  if (typeof e == "boolean") return { raw: e, type: "boolean" };
@@ -67,54 +54,54 @@ function O(e, r) {
67
54
  }
68
55
  return { raw: e, type: "string" };
69
56
  }
70
- function $(e, r) {
57
+ function D(e, r) {
71
58
  return r.map((t) => e[t]);
72
59
  }
73
60
  function L(e, r, t = {}) {
74
- var A, x, p, w, C, M;
75
- const s = (A = t.startRow) != null ? A : 0, c = (x = t.startCol) != null ? x : 0, y = (p = t.autoInfer) != null ? p : !0;
76
- let n = 0, h = 0, d = !1;
61
+ var b, x, p, f, M, C;
62
+ const s = (b = t.startRow) != null ? b : 0, c = (x = t.startCol) != null ? x : 0, y = (p = t.autoInfer) != null ? p : !0;
63
+ let l = 0, h = 0, m = !1;
77
64
  if (!r.length) return { lastRow: 0, lastCol: 0, wrote: !1 };
78
- const m = r[0], o = m !== null && typeof m == "object" && !Array.isArray(m), a = o ? (w = t.columns) != null ? w : Object.keys(m) : [];
65
+ const d = r[0], o = d !== null && typeof d == "object" && !Array.isArray(d), a = o ? (f = t.columns) != null ? f : Object.keys(d) : [];
79
66
  let g = s;
80
- if (o && ((C = t.headerRow) == null || C)) {
81
- for (let l = 0; l < a.length; l++) {
82
- const i = g, u = c + l;
83
- e.cells.set(R(i, u), { row: i, col: u, type: "string", raw: a[l], styleId: 0 }), n = Math.max(n, i), h = Math.max(h, u), d = !0;
67
+ if (o && ((M = t.headerRow) == null || M)) {
68
+ for (let n = 0; n < a.length; n++) {
69
+ const i = g, u = c + n;
70
+ e.cells.set(R(i, u), { row: i, col: u, type: "string", raw: a[n], styleId: 0 }), l = Math.max(l, i), h = Math.max(h, u), m = !0;
84
71
  }
85
72
  g++;
86
73
  }
87
- for (let l = 0; l < r.length; l++) {
88
- const i = r[l], u = Array.isArray(i) ? i : $(i, a);
74
+ for (let n = 0; n < r.length; n++) {
75
+ const i = r[n], u = Array.isArray(i) ? i : D(i, a);
89
76
  for (let k = 0; k < u.length; k++) {
90
- const I = g + l - (o && ((M = t.headerRow) == null || M), 0), b = c + k, z = O(u[k], y);
91
- z.type !== "empty" && (e.cells.set(R(I, b), { row: I, col: b, type: z.type, raw: z.raw, styleId: 0 }), n = Math.max(n, I), h = Math.max(h, b), d = !0);
77
+ const I = g + n - (o && ((C = t.headerRow) == null || C), 0), A = c + k, z = $(u[k], y);
78
+ z.type !== "empty" && (e.cells.set(R(I, A), { row: I, col: A, type: z.type, raw: z.raw, styleId: 0 }), l = Math.max(l, I), h = Math.max(h, A), m = !0);
92
79
  }
93
80
  }
94
- return { lastRow: n, lastCol: h, wrote: d };
81
+ return { lastRow: l, lastCol: h, wrote: m };
95
82
  }
96
- function B(e, r = {}) {
83
+ function v(e, r = {}) {
97
84
  var o, a;
98
- const t = r.themeColors && r.themeColors.length === 17 ? r.themeColors : N, s = (o = r.autoInfer) != null ? o : !0, c = (a = r.headerRow) != null ? a : !0;
85
+ const t = r.themeColors && r.themeColors.length === 17 ? r.themeColors : O, s = (o = r.autoInfer) != null ? o : !0, c = (a = r.headerRow) != null ? a : !0;
99
86
  if (e && typeof e == "object" && !Array.isArray(e) && Array.isArray(e.sheets))
100
87
  return { sheets: e.sheets.map((x, p) => {
101
88
  var i;
102
- const w = V(x.name || `Sheet${p + 1}`, p), { lastRow: C, lastCol: M, wrote: l } = L(w, (i = x.rows) != null ? i : [], { headerRow: c, autoInfer: s });
103
- return w.dimension = l ? { rows: C + 1, cols: M + 1 } : { rows: 0, cols: 0 }, w;
89
+ const f = V(x.name || `Sheet${p + 1}`, p), { lastRow: M, lastCol: C, wrote: n } = L(f, (i = x.rows) != null ? i : [], { headerRow: c, autoInfer: s });
90
+ return f.dimension = n ? { rows: M + 1, cols: C + 1 } : { rows: 0, cols: 0 }, f;
104
91
  }), activeSheet: 0, themeColors: t, date1904: !1 };
105
- const y = V(r.sheetName || "Sheet1", 0), n = e != null ? e : [], { lastRow: h, lastCol: d, wrote: m } = L(y, n, { headerRow: c, autoInfer: s });
106
- return y.dimension = m ? { rows: h + 1, cols: d + 1 } : { rows: 0, cols: 0 }, { sheets: [y], activeSheet: 0, themeColors: t, date1904: !1 };
92
+ const y = V(r.sheetName || "Sheet1", 0), l = e != null ? e : [], { lastRow: h, lastCol: m, wrote: d } = L(y, l, { headerRow: c, autoInfer: s });
93
+ return y.dimension = d ? { rows: h + 1, cols: m + 1 } : { rows: 0, cols: 0 }, { sheets: [y], activeSheet: 0, themeColors: t, date1904: !1 };
107
94
  }
108
- function _(e) {
95
+ function B(e) {
109
96
  return !!e && typeof e == "object" && Array.isArray(e.sheets) && typeof e.activeSheet == "number" && Array.isArray(e.themeColors);
110
97
  }
111
- function K(e, r) {
112
- var m;
98
+ function _(e, r) {
99
+ var d;
113
100
  const t = r.sheets[0], s = e.sheets[0];
114
101
  if (!t) return e;
115
102
  if (!s)
116
103
  return {
117
- sheets: [D(t, t.name)],
104
+ sheets: [F(t, t.name)],
118
105
  activeSheet: 0,
119
106
  themeColors: r.themeColors,
120
107
  date1904: e.date1904,
@@ -126,13 +113,13 @@ function K(e, r) {
126
113
  state: "visible",
127
114
  dimension: { rows: 0, cols: 0 },
128
115
  cells: /* @__PURE__ */ new Map(),
129
- styles: t.styles.map((o) => f({}, o)),
130
- merges: t.merges.map((o) => f({}, o)),
116
+ styles: t.styles.map((o) => w({}, o)),
117
+ merges: t.merges.map((o) => w({}, o)),
131
118
  columns: new Map(t.columns),
132
119
  rows: new Map(t.rows),
133
120
  defaultColWidth: t.defaultColWidth,
134
121
  defaultRowHeight: t.defaultRowHeight,
135
- freeze: f({}, t.freeze),
122
+ freeze: w({}, t.freeze),
136
123
  // 条件格式 / 数据验证 不带过来 —— 数据可能不在模板的目标列上, 套规则会误命中
137
124
  conditional: [],
138
125
  dataValidations: [],
@@ -141,9 +128,10 @@ function K(e, r) {
141
128
  charts: [],
142
129
  shapes: [],
143
130
  sparklines: [],
131
+ pivotTables: [],
144
132
  showGridLines: t.showGridLines
145
133
  };
146
- let y = 0, n = 0;
134
+ let y = 0, l = 0;
147
135
  for (const o of s.cells.values()) {
148
136
  const a = t.cells.get(R(o.row, o.col));
149
137
  c.cells.set(R(o.row, o.col), {
@@ -155,14 +143,14 @@ function K(e, r) {
155
143
  formula: o.formula,
156
144
  hyperlink: o.hyperlink,
157
145
  comment: o.comment,
158
- styleId: (m = a == null ? void 0 : a.styleId) != null ? m : 0,
146
+ styleId: (d = a == null ? void 0 : a.styleId) != null ? d : 0,
159
147
  dispImgId: o.dispImgId
160
- }), o.row > y && (y = o.row), o.col > n && (n = o.col);
148
+ }), o.row > y && (y = o.row), o.col > l && (l = o.col);
161
149
  }
162
- const h = t.columns.size ? Math.max(...t.columns.keys()) + 1 : 0, d = t.rows.size ? Math.max(...t.rows.keys()) + 1 : 0;
150
+ const h = t.columns.size ? Math.max(...t.columns.keys()) + 1 : 0, m = t.rows.size ? Math.max(...t.rows.keys()) + 1 : 0;
163
151
  return c.dimension = {
164
- rows: Math.max(s.dimension.rows, y + 1, d),
165
- cols: Math.max(s.dimension.cols, n + 1, h)
152
+ rows: Math.max(s.dimension.rows, y + 1, m),
153
+ cols: Math.max(s.dimension.cols, l + 1, h)
166
154
  }, {
167
155
  sheets: [c],
168
156
  activeSheet: 0,
@@ -171,33 +159,34 @@ function K(e, r) {
171
159
  cellImages: e.cellImages
172
160
  };
173
161
  }
174
- function D(e, r) {
162
+ function F(e, r) {
175
163
  return {
176
164
  name: r,
177
165
  index: 0,
178
166
  state: "visible",
179
167
  dimension: { rows: 0, cols: 0 },
180
168
  cells: /* @__PURE__ */ new Map(),
181
- styles: e.styles.map((t) => f({}, t)),
182
- merges: e.merges.map((t) => f({}, t)),
169
+ styles: e.styles.map((t) => w({}, t)),
170
+ merges: e.merges.map((t) => w({}, t)),
183
171
  columns: new Map(e.columns),
184
172
  rows: new Map(e.rows),
185
173
  defaultColWidth: e.defaultColWidth,
186
174
  defaultRowHeight: e.defaultRowHeight,
187
- freeze: f({}, e.freeze),
175
+ freeze: w({}, e.freeze),
188
176
  conditional: [],
189
177
  dataValidations: [],
190
178
  images: [],
191
179
  charts: [],
192
180
  shapes: [],
193
181
  sparklines: [],
182
+ pivotTables: [],
194
183
  showGridLines: e.showGridLines
195
184
  };
196
185
  }
197
- function Z(e, r) {
186
+ function K(e, r) {
198
187
  return G(e, r);
199
188
  }
200
- const q = {
189
+ const Z = {
201
190
  // 放大镜
202
191
  find: '<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>',
203
192
  // 漏斗
@@ -206,6 +195,12 @@ const q = {
206
195
  "clear-filter": '<path d="M3 5h18l-7 8.5V20l-4-2.2V13.5z"/><line x1="3" y1="3" x2="21" y2="21"/>',
207
196
  // 上下箭头(排序)
208
197
  sort: '<path d="M7 16l3 3 3-3"/><line x1="10" y1="19" x2="10" y2="5"/><path d="M17 8l-3-3-3 3"/><line x1="14" y1="5" x2="14" y2="19"/>',
198
+ // 透视表: 小表格 + 汇总箭头
199
+ "pivot-table": '<rect x="3" y="4" width="13" height="13" rx="1.5"/><line x1="3" y1="8" x2="16" y2="8"/><line x1="7" y1="4" x2="7" y2="17"/><path d="M14 14h7"/><path d="M18 11l3 3-3 3"/>',
200
+ // 条件格式: 渐变色块 + 小滴
201
+ "conditional-format": '<rect x="3" y="4" width="13" height="13" rx="1.5"/><path d="M3 9h13"/><path d="M3 13h13"/><circle cx="18.5" cy="15" r="3"/><path d="M18.5 9.5c1.2 1.4 2 2.7 2 3.8a2 2 0 0 1-4 0c0-1.1.8-2.4 2-3.8z"/>',
202
+ // 数字格式: 百分号 + 小数点(123)
203
+ "number-format": '<text x="2" y="11" font-size="9" font-family="sans-serif" fill="currentColor" stroke="none">123</text><circle cx="5" cy="17" r="1" fill="currentColor" stroke="none"/><path d="M21 6l-7 12"/><circle cx="14.5" cy="8" r="2"/><circle cx="20.5" cy="16" r="2"/>',
209
204
  // 下载(导出)
210
205
  export: '<line x1="12" y1="3" x2="12" y2="15"/><path d="M8 11l4 4 4-4"/><path d="M4 19h16"/>',
211
206
  // 放大镜带 +
@@ -225,14 +220,14 @@ const q = {
225
220
  // 下拉小箭头
226
221
  caret: '<path d="M6 9l6 6 6-6"/>'
227
222
  };
228
- function J(e) {
223
+ function q(e) {
229
224
  return '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + e + "</svg>";
230
225
  }
231
226
  export {
232
- q as T,
233
- K as a,
234
- _ as i,
235
- B as j,
236
- Z as p,
237
- J as s
227
+ Z as T,
228
+ _ as a,
229
+ B as i,
230
+ v as j,
231
+ K as p,
232
+ q as s
238
233
  };
@@ -7,6 +7,7 @@ import { ReadOptions } from '../core/model/data-access';
7
7
  import { ViewerTheme } from '../core/render/theme';
8
8
  import { ContextMenuBeforePayload, ContextMenuShowPayload, ContextMenuTransform } from '../core/viewer/controller';
9
9
  import { EditableTarget } from '../core/edit/types';
10
+ import { PasteBehavior } from '../core/edit/paste-behavior';
10
11
  import { FormulaEngineFactory } from '../core/formula/engine';
11
12
  import { CellChangePayload, DimChangePayload, DirtyChangePayload, ImageChangePayload, StructChangePayload } from '../core/edit/edit-controller';
12
13
  import { EditorResolver } from '../core/edit/editor-context';
@@ -58,6 +59,18 @@ type __VLS_Props = {
58
59
  toolbar?: boolean | Array<string | ToolbarItem>;
59
60
  /** 编辑总开关:默认 false = 只读(行为不变)。开启后才能进入编辑(E0:闸门) */
60
61
  editable?: boolean;
62
+ /**
63
+ * 透视表功能开关:默认 false = 关闭。开启后(还需 `editable`)工具栏 `pivot-table` 入口可见、
64
+ * `createPivotTable`/`openPivotTableDialog` 等 API 生效、导出 .xlsx 回注真实 OOXML 透视表零件
65
+ * (overlay 模式同时保留原文件透视表)。
66
+ */
67
+ pivotTable?: boolean;
68
+ /**
69
+ * 条件格式编辑开关:默认 false = 关闭(只读渲染)。开启后(还需 `editable`)工具栏 `conditional-format`
70
+ * 入口可见、`openConditionalFormatDialog`/`addConditionalRule` 等 API 生效、导出 .xlsx 回写条件格式
71
+ * (overlay 模式保留原件未编辑规则原样,只增改用户改的)。
72
+ */
73
+ conditionalFormat?: boolean;
61
74
  /** 按格只读判定:返回 true = 只读(cell 为空格时传 null) */
62
75
  cellReadOnly?: (cell: CellModel | null, pos: {
63
76
  row: number;
@@ -95,6 +108,13 @@ type __VLS_Props = {
95
108
  recalc?: boolean;
96
109
  /** 自定义/自研公式引擎工厂(可换引擎);不给则用默认 HyperFormula(需 npm i hyperformula) */
97
110
  formulaEngine?: FormulaEngineFactory;
111
+ /**
112
+ * 粘贴行为(默认 = 覆盖式 1:1)。控制 Ctrl+V / 右键粘贴时源各方面如何落目标(覆盖/合并/仅值)。
113
+ * 不传 = 默认;也可 `viewer.setPasteBehavior(cfg)` 运行时改、右键「选择性粘贴」逐次选预设。
114
+ */
115
+ pasteBehavior?: Partial<PasteBehavior>;
116
+ /** 粘贴撞只读格的内置提醒:'dialog'(默认,弹窗列出哪些格只读)/ 'toast'(气泡)/ 'none'(只发事件) */
117
+ readOnlyPrompt?: 'dialog' | 'toast' | 'none';
98
118
  /**
99
119
  * 内置导出进度遮罩(P1.5):默认 `true` —— 调 `viewer.downloadPdf` / `downloadImage` / `downloadXlsx` /
100
120
  * `print` / 选区图片批量转换 时,壳自动建 `AbortController` + 接 `onProgress` →
@@ -206,12 +226,34 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {
206
226
  setActiveSheet(index: number): void;
207
227
  getSelection(): MergeRange | null;
208
228
  setSelection(range: MergeRange): void;
229
+ scrollToCell(row: number, col: number, opts?: {
230
+ select?: boolean;
231
+ }): boolean;
209
232
  rectOf(row: number, col: number): import('../core/plugin').Rect | null;
210
233
  rectOfRange(range: MergeRange): import('../core/plugin').Rect | null;
211
234
  redraw(): void;
212
235
  isCellEditable(row: number, col: number): boolean;
213
236
  setEditableTargets(targets: EditableTarget | EditableTarget[] | undefined): void;
214
237
  getEditableTargets(): EditableTarget | EditableTarget[] | undefined;
238
+ sortActiveColumn(dir: "asc" | "desc"): boolean;
239
+ createPivotTable(opts: import('../core/plugin').CreatePivotTableOptions): boolean;
240
+ createPivotTableFromSelection(opts?: {
241
+ rowFieldIndex?: number;
242
+ valueFieldIndex?: number;
243
+ output?: import('../core/plugin').PivotOutput;
244
+ }): boolean;
245
+ openPivotTableDialog(): boolean;
246
+ getConditionalRules(): import('../core/model/types').ConditionalRule[];
247
+ addConditionalRule(rule: Partial<import('../core/model/types').ConditionalRule> & Pick<import('../core/model/types').ConditionalRule, "ranges" | "type">): string | false;
248
+ updateConditionalRule(ruleId: string, patch: Partial<import('../core/model/types').ConditionalRule>): boolean;
249
+ removeConditionalRule(ruleId: string): boolean;
250
+ setConditionalRules(rules: import('../core/model/types').ConditionalRule[]): boolean;
251
+ openConditionalFormatDialog(): boolean;
252
+ setSelectionNumberFormat(code: string): boolean;
253
+ openNumberFormatDialog(): boolean;
254
+ getCellComment(row: number, col: number): string;
255
+ setCellComment(row: number, col: number, comment: string): boolean;
256
+ openCommentEditor(row?: number, col?: number): boolean;
215
257
  exportImage(opts?: ImageExportOptions): Promise<Blob>;
216
258
  downloadImage(opts?: ImageExportOptions): Promise<void>;
217
259
  exportPdf(opts?: PdfExportOptions): Promise<Blob>;
@@ -266,7 +308,10 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {
266
308
  pasteRichHtml(html: string, at?: {
267
309
  row: number;
268
310
  col: number;
269
- }): boolean;
311
+ }, behaviorOverride?: Partial<PasteBehavior> | null): boolean;
312
+ getPasteBehavior(): PasteBehavior;
313
+ setPasteBehavior(cfg: Partial<PasteBehavior> | null): void;
314
+ openPasteConfigDialog(): boolean;
270
315
  pasteImageBlob(blob: Blob, at?: {
271
316
  row: number;
272
317
  col: number;
@@ -4,6 +4,8 @@ type __VLS_Props = {
4
4
  current: number;
5
5
  matchCase: boolean;
6
6
  wholeCell: boolean;
7
+ editable: boolean;
8
+ replace: string;
7
9
  };
8
10
  declare const _default: import('vue').DefineComponent<__VLS_Props, {
9
11
  focus: () => void | undefined;
@@ -12,15 +14,21 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {
12
14
  "update:query": (v: string) => any;
13
15
  "update:matchCase": (v: boolean) => any;
14
16
  "update:wholeCell": (v: boolean) => any;
17
+ "update:replace": (v: string) => any;
15
18
  next: () => any;
16
19
  prev: () => any;
20
+ "replace-one": () => any;
21
+ "replace-all": () => any;
17
22
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
18
23
  onClose?: (() => any) | undefined;
19
24
  "onUpdate:query"?: ((v: string) => any) | undefined;
20
25
  "onUpdate:matchCase"?: ((v: boolean) => any) | undefined;
21
26
  "onUpdate:wholeCell"?: ((v: boolean) => any) | undefined;
27
+ "onUpdate:replace"?: ((v: string) => any) | undefined;
22
28
  onNext?: (() => any) | undefined;
23
29
  onPrev?: (() => any) | undefined;
30
+ "onReplace-one"?: (() => any) | undefined;
31
+ "onReplace-all"?: (() => any) | undefined;
24
32
  }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
25
33
  inputEl: HTMLInputElement;
26
34
  }, HTMLDivElement>;
@@ -0,0 +1,10 @@
1
+ import { CellValue } from '../model/data-access';
2
+ /**
3
+ * 接续 source 产出 count 个新值。source 已按填充正方向排序(至少 1 个)。
4
+ *
5
+ * `ctrl` = 拖拽时是否按住 Ctrl,翻转"复制 ↔ 序列"(对齐 Excel/WPS):
6
+ * - 数值:普通 单个→复制 / ≥2→等差;Ctrl 单个→递增(+1) / ≥2→复制
7
+ * - 日期/星期月份/文本递增:普通→序列;Ctrl→复制
8
+ * - 纯文本:始终循环复制(Ctrl 无影响)
9
+ */
10
+ export declare function computeFillSeries(source: CellValue[], count: number, ctrl?: boolean): CellValue[];
@@ -17,7 +17,19 @@ export interface ParsedClipboard {
17
17
  col: number;
18
18
  dataUrl: string;
19
19
  }[];
20
+ /** 列宽(相对列 index → px;来自 <col width>);稀疏 */
21
+ colWidths: number[];
22
+ /** 行高(相对行 index → px;来自 <tr height>);稀疏 */
23
+ rowHeights: number[];
20
24
  }
25
+ /** mso-number-format 的值是 CSS 转义的 Excel 格式码(\0022→" \#→# \;→; \\(→\( …),解回真实格式码。 */
26
+ export declare function unescapeMsoNumFmt(v: string): string;
27
+ /**
28
+ * 从合并 CSS 串里解析 mso-number-format → Excel 格式码(值可能含转义的 \; ,故按引号串/到分号匹配)。
29
+ * **取最后一条**:合并串是 `td默认;类;内联`,按 CSS 层叠后写的覆盖——裸 `td` 默认常带 `mso-number-format:General`,
30
+ * 若取第一条会被它的 General 顶掉、丢掉后面类里的真实日期/货币格式码。
31
+ */
32
+ export declare function parseMsoNumberFormat(css: string): string | undefined;
21
33
  /**
22
34
  * 解析剪贴板 HTML → {values, styles, merges, images}。非浏览器环境 / 无 <table> 返 null(调用方回退 TSV)。
23
35
  */
@@ -0,0 +1,76 @@
1
+ import { AnchorCell, CellModel, CellStyle, CellValueType, MergeRange, RichTextRun, SheetModel, WorkbookModel } from '../model/types';
2
+ /** raw 是 Date 时序列化为 {__d: epochMs},反序列化还原。其余原样 JSON。 */
3
+ type ClipRaw = number | string | boolean | null | {
4
+ __d: number;
5
+ };
6
+ export interface ClipCell {
7
+ r: number;
8
+ c: number;
9
+ type: CellValueType;
10
+ raw: ClipRaw;
11
+ formula?: string;
12
+ hyperlink?: string;
13
+ comment?: string;
14
+ rich?: RichTextRun[];
15
+ dispImgId?: string;
16
+ style: CellStyle;
17
+ }
18
+ export interface ClipImage {
19
+ from: AnchorCell;
20
+ to?: AnchorCell;
21
+ extWidthEmu?: number;
22
+ extHeightEmu?: number;
23
+ editAs?: string;
24
+ mime: string;
25
+ b64: string;
26
+ }
27
+ export interface ClipCellImage {
28
+ id: string;
29
+ mime: string;
30
+ b64: string;
31
+ }
32
+ export interface ClipDim {
33
+ i: number;
34
+ height?: number;
35
+ width?: number;
36
+ hidden?: boolean;
37
+ custom?: boolean;
38
+ }
39
+ export interface ClipSnapshot {
40
+ v: 1;
41
+ rows: number;
42
+ cols: number;
43
+ cells: ClipCell[];
44
+ merges: MergeRange[];
45
+ images: ClipImage[];
46
+ cellImages: ClipCellImage[];
47
+ rowHeights: ClipDim[];
48
+ colWidths: ClipDim[];
49
+ }
50
+ /** 图片字节预算(原始字节):复制区图片总字节超此值 → 降级为"无图 1:1 复制",避免剪贴板超限/卡顿。 */
51
+ export declare const CLIP_IMAGE_BUDGET_BYTES: number;
52
+ /**
53
+ * 抓一段区域的完整模型快照(相对坐标)。
54
+ * `withImageBytes=false`:图片只记引用(id / 浮动序号),b64 留空 —— 给"图片字节走可见 `<img>`、
55
+ * 快照只引用"的瘦身传输用(避免图片被双重 base64),粘贴时由 parseSnapshotHtml 从 `<img>` 回填字节。
56
+ */
57
+ export declare function serializeSnapshot(sheet: SheetModel, wb: WorkbookModel, range: MergeRange, opts?: {
58
+ withImageBytes?: boolean;
59
+ }): ClipSnapshot;
60
+ /** ClipRaw → 运行时 raw(还原 Date)。 */
61
+ export declare function reviveClipRaw(raw: ClipRaw): CellModel['raw'];
62
+ export declare function encodeSnapshot(snap: ClipSnapshot): string;
63
+ export declare function decodeSnapshot(s: string | null | undefined): ClipSnapshot | null;
64
+ /**
65
+ * 把图片字节从可见 `<img data-clip-img="key">` 回填进瘦身快照(传输优化:图片只在 `<img>` 存一份)。
66
+ * key 约定:DISPIMG 单元格图 = `c:${id}`,浮动图 = `f:${序号}`(序号同 serializeSnapshot 的 images 顺序)。
67
+ * 已带 b64(完整快照路径)则原样保留。
68
+ */
69
+ export declare function reattachImages(snap: ClipSnapshot | null, imgB64: Map<string, string>): ClipSnapshot | null;
70
+ /** 降级:去掉所有图片(超字节预算时用)。DISPIMG 格中性化为空格(保样式),避免粘出引用不到的破图。 */
71
+ export declare function withoutImages(snap: ClipSnapshot): ClipSnapshot;
72
+ /** 从剪贴板 HTML 抽出本组件的 1:1 快照(`<table data-ooxml-clip="...">` + 回填 `<img>` 字节);非本组件复制返 null。 */
73
+ export declare function parseSnapshotHtml(html: string): ClipSnapshot | null;
74
+ export declare function bytesToB64(bytes: Uint8Array): string;
75
+ export declare function b64ToBytes(b64: string): Uint8Array;
76
+ export {};
@@ -1,4 +1,4 @@
1
- import { CellModel, CellStyleOverride, ColumnInfo, ImageAnchor, MergeRange, RowInfo, SheetModel, WorkbookModel } from '../model/types';
1
+ import { CellModel, CellStyleOverride, ColumnInfo, ConditionalRule, ImageAnchor, MergeRange, RowInfo, SheetModel, WorkbookModel } from '../model/types';
2
2
  import { CellValue } from '../model/data-access';
3
3
  import { StructOp } from '../model/structure';
4
4
  export type CellPos = {
@@ -64,6 +64,14 @@ export type EditCommand = {
64
64
  } | {
65
65
  kind: 'unmerge-cells';
66
66
  range: MergeRange;
67
+ } | {
68
+ kind: 'set-conditional';
69
+ rules: ConditionalRule[];
70
+ } | {
71
+ kind: 'set-comment';
72
+ row: number;
73
+ col: number;
74
+ comment: string;
67
75
  } | {
68
76
  kind: 'restore-merges';
69
77
  merges: MergeRange[];
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 右键上下文菜单宿主(框架无关 DOM)—— 挂到 document.body 的 fixed 菜单,点项执行 action + 关闭,
3
- * 点外部 / Esc 关闭,贴边自动翻转。Vue / React 壳只把 contextmenu 事件转给 controller,菜单逻辑全在这。
3
+ * 点外部 / Esc 关闭,贴边自动翻转。支持一级子菜单(`children`,悬停展开右侧 flyout)。
4
+ * Vue / React 壳只把 contextmenu 事件转给 controller,菜单逻辑全在这。
4
5
  */
5
6
  export interface MenuItem {
6
7
  label?: string;
@@ -8,12 +9,24 @@ export interface MenuItem {
8
9
  disabled?: boolean;
9
10
  /** true = 分隔线 */
10
11
  separator?: boolean;
12
+ /** 子菜单项(有则该行不执行 action,悬停展开右侧 flyout) */
13
+ children?: MenuItem[];
11
14
  }
12
15
  export declare class ContextMenuHost {
13
16
  private el;
17
+ private submenus;
18
+ private openSubRow;
19
+ private subCloseTimer;
14
20
  private cleanup;
15
21
  isOpen(): boolean;
16
22
  show(x: number, y: number, items: MenuItem[]): void;
23
+ /** 建一级菜单 DOM(不挂载、不定位);子菜单悬停时再建 flyout。 */
24
+ private buildMenu;
25
+ /** 在 row 右侧展开子菜单 flyout(贴边翻转到左侧);悬停 flyout 取消关闭,离开 flyout 延时关。 */
26
+ private openSubmenu;
27
+ private scheduleSubClose;
28
+ private cancelSubClose;
29
+ private closeAllSubmenus;
17
30
  close(): void;
18
31
  dispose(): void;
19
32
  }
@@ -0,0 +1,15 @@
1
+ import { DataValidationRule, SheetModel } from '../model/types';
2
+ import { CellValue } from '../model/data-access';
3
+ export interface ValidationViolation {
4
+ rule: DataValidationRule;
5
+ errorStyle: 'stop' | 'warning' | 'information';
6
+ title?: string;
7
+ message: string;
8
+ }
9
+ /** 找命中 (row,col) 的数据验证规则(Excel 一格至多一条;取首个包含该格的)。 */
10
+ export declare function findValidationRuleAt(sheet: SheetModel, row: number, col: number): DataValidationRule | undefined;
11
+ /**
12
+ * 校验一个值是否满足规则。返回 null = 合法(放行);返回 violation = 非法。
13
+ * 空值一律放行(允许清空 —— 拦截清空对预览/编辑组件太粗暴;allowBlank 只表"空值跳过其余校验")。
14
+ */
15
+ export declare function validateCellValue(rule: DataValidationRule, value: CellValue): ValidationViolation | null;
@@ -1,7 +1,9 @@
1
- import { CellStyleOverride, ColumnInfo, ImageAnchor, MergeRange, RowInfo, SheetModel, WorkbookModel } from '../model/types';
1
+ import { CellStyleOverride, ColumnInfo, ConditionalRule, ImageAnchor, MergeRange, RowInfo, SheetModel, WorkbookModel } from '../model/types';
2
2
  import { CellValue } from '../model/data-access';
3
3
  import { CellSnapshot } from '../model/snapshot';
4
+ import { PasteBehavior } from './paste-behavior';
4
5
  import { ParsedClipboard } from './clipboard-html';
6
+ import { ClipSnapshot } from './clipboard-snapshot';
5
7
  import { CellPos, DimAxis, EditCommand } from './commands';
6
8
  import { StructOp } from '../model/structure';
7
9
  import { FormulaEngineFactory } from '../formula/engine';
@@ -72,7 +74,12 @@ export declare class EditController {
72
74
  private engine;
73
75
  private engineFor;
74
76
  private warming;
77
+ private pasteBehavior;
75
78
  constructor(host: EditControllerHost);
79
+ /** 读当前粘贴行为(完整配置)。 */
80
+ getPasteBehavior(): PasteBehavior;
81
+ /** 设粘贴行为默认(缺项回落默认);影响 Ctrl+V / 右键「粘贴」。 */
82
+ setPasteBehavior(cfg: Partial<PasteBehavior> | null): void;
76
83
  /** 异步懒初始化引擎(开重算 + 有工厂 + 尚未为当前簿建好)。返回的 Promise 供测试 await;生产 fire-and-forget。 */
77
84
  warmEngine(): Promise<void>;
78
85
  /** 释放引擎(切簿/关重算/dispose)。 */
@@ -94,14 +101,38 @@ export declare class EditController {
94
101
  editCell(row: number, col: number, value: CellValue): boolean;
95
102
  /** 区域批量设值(2D,左上对齐 range.top/left);跳过只读格,返回是否有改动 */
96
103
  editRange(range: MergeRange, values: CellValue[][]): boolean;
104
+ /** 设/清单元格批注(1.11.0,单次撤销)。 */
105
+ setCellComment(row: number, col: number, comment: string): boolean;
106
+ /** 批量设一组散列格(自动填充用,1.10.0):跳过只读格,整体单次撤销。 */
107
+ setCellsBatch(cells: {
108
+ row: number;
109
+ col: number;
110
+ value: CellValue;
111
+ }[]): boolean;
112
+ /** 替换整张表的条件格式规则集(1.9.0,整体单次撤销)。规则对象不可变 → 直接换数组。 */
113
+ setConditionalRules(rules: ConditionalRule[]): boolean;
114
+ /** 合并区 m 是否与矩形 [top,left]-[bottom,right] 有交叠(用于清粘贴区内的目标原有合并)。 */
115
+ private _mergeOverlaps;
97
116
  /**
98
117
  * 富粘贴(Excel/WPS 复制的 HTML 解析后):值 + 样式 + 合并 + 图片,**整体单次撤销**。
99
118
  * start = 落点左上角。跳过只读格。一次 cloneWorkbook 快照 → 应用全部 → 压一条 restore-wb 逆。
119
+ * behaviorOverride = 右键「选择性粘贴」逐次预设(覆盖 this.pasteBehavior;缺省走默认配置)。
100
120
  */
101
121
  pasteRich(start: {
102
122
  row: number;
103
123
  col: number;
104
- }, parsed: ParsedClipboard): boolean;
124
+ }, parsed: ParsedClipboard, behaviorOverride?: Partial<PasteBehavior> | null): boolean;
125
+ /** 去重后发一次 paste 的 permission-denied(没只读格则不发)。 */
126
+ private _emitPasteDenied;
127
+ /**
128
+ * 1:1 保真粘贴:用复制时序列化进剪贴板的完整模型快照(本组件自己复制的,跨实例 Vue3/Vue2/React 通用)
129
+ * 覆盖式落到目标区 —— 完整还原 值/原始类型/数字格式/全部样式(含边框)/合并/图片(浮动 + DISPIMG)/行高列宽。
130
+ * 跟 pasteRich(外部 HTML 近似)同走命令栈(整体单次撤销)+ 前后快照 cell-change + 只读 permission-denied。
131
+ */
132
+ pasteSnapshot(start: {
133
+ row: number;
134
+ col: number;
135
+ }, snap: ClipSnapshot, behaviorOverride?: Partial<PasteBehavior> | null): boolean;
105
136
  /** 清空区域(跳过只读) */
106
137
  clearRange(range: MergeRange): boolean;
107
138
  /** 给区域套样式覆盖(E5;跳过只读格)。返回是否有改动。前后 style 不同 → 发 cell-change。 */
@@ -23,8 +23,11 @@ export interface CellEditorContext {
23
23
  permission: EditPermission;
24
24
  /** 进入编辑的初始文本(打字进入时为该字符;否则 undefined → editor 用 snapshot.text) */
25
25
  initialText?: string;
26
- /** 提交编辑(走命令栈 + 事件);move 指示提交后活动格移动方向 */
27
- commit(value: EditorCommitValue, move?: 'down' | 'right'): void;
26
+ /**
27
+ * 提交编辑(走命令栈 + 事件);move 指示提交后活动格移动方向。
28
+ * 返回 false = 提交被拒(如数据验证拦截),编辑器应保持打开让用户改正;void/true = 已提交。
29
+ */
30
+ commit(value: EditorCommitValue, move?: 'down' | 'right'): boolean | void;
28
31
  /** 取消编辑(不改模型) */
29
32
  cancel(): void;
30
33
  /**