@coco-vexel/coco-office-editor 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +510 -2
- package/THIRD_PARTY_NOTICES.md +212 -0
- package/dist/index.cjs +26401 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +26192 -0
- package/dist/index.js.map +1 -0
- package/dist/model-DB6aN6uJ.d.cts +321 -0
- package/dist/model-DB6aN6uJ.d.ts +321 -0
- package/dist/native-B6B649CC.d.ts +1111 -0
- package/dist/native-Ct9nN06K.d.cts +1111 -0
- package/dist/ppt-native.cjs +5334 -0
- package/dist/ppt-native.cjs.map +1 -0
- package/dist/ppt-native.d.cts +2 -0
- package/dist/ppt-native.d.ts +2 -0
- package/dist/ppt-native.js +5332 -0
- package/dist/ppt-native.js.map +1 -0
- package/dist/ppt.cjs +5426 -0
- package/dist/ppt.cjs.map +1 -0
- package/dist/ppt.d.cts +306 -0
- package/dist/ppt.d.ts +306 -0
- package/dist/ppt.js +5410 -0
- package/dist/ppt.js.map +1 -0
- package/dist/spreadsheet.cjs +4276 -0
- package/dist/spreadsheet.cjs.map +1 -0
- package/dist/spreadsheet.d.cts +676 -0
- package/dist/spreadsheet.d.ts +676 -0
- package/dist/spreadsheet.js +4213 -0
- package/dist/spreadsheet.js.map +1 -0
- package/dist/word-editing.cjs +812 -0
- package/dist/word-editing.cjs.map +1 -0
- package/dist/word-editing.d.cts +149 -0
- package/dist/word-editing.d.ts +149 -0
- package/dist/word-editing.js +803 -0
- package/dist/word-editing.js.map +1 -0
- package/dist/word-native.cjs +13400 -0
- package/dist/word-native.cjs.map +1 -0
- package/dist/word-native.d.cts +4 -0
- package/dist/word-native.d.ts +4 -0
- package/dist/word-native.js +13293 -0
- package/dist/word-native.js.map +1 -0
- package/dist/word-prosemirror.cjs +489 -0
- package/dist/word-prosemirror.cjs.map +1 -0
- package/dist/word-prosemirror.d.cts +21 -0
- package/dist/word-prosemirror.d.ts +21 -0
- package/dist/word-prosemirror.js +483 -0
- package/dist/word-prosemirror.js.map +1 -0
- package/dist/word.cjs +16764 -0
- package/dist/word.cjs.map +1 -0
- package/dist/word.d.cts +84 -0
- package/dist/word.d.ts +84 -0
- package/dist/word.js +16632 -0
- package/dist/word.js.map +1 -0
- package/package.json +106 -3
- package/src/styles/editor.css +1507 -0
|
@@ -0,0 +1,4213 @@
|
|
|
1
|
+
import { parseCellAddress, parseRangeAddress, formatCellAddress, formatRangeAddress, shiftFormulaReferences, ExcelWorkbook, SpreadsheetFormulaEngine, diffExcelWorkbookModels, rewriteFormulaForRowSort, columnIndexToName } from '@coco-vexel/coco-office/excel';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defProps = Object.defineProperties;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
9
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
10
|
+
var __spreadValues = (a, b) => {
|
|
11
|
+
for (var prop in b || (b = {}))
|
|
12
|
+
if (__hasOwnProp.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
if (__getOwnPropSymbols)
|
|
15
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
16
|
+
if (__propIsEnum.call(b, prop))
|
|
17
|
+
__defNormalProp(a, prop, b[prop]);
|
|
18
|
+
}
|
|
19
|
+
return a;
|
|
20
|
+
};
|
|
21
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
22
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
23
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
24
|
+
|
|
25
|
+
// src/spreadsheet/types.ts
|
|
26
|
+
function rowColumnKey(row, column) {
|
|
27
|
+
return `${row}:${column}`;
|
|
28
|
+
}
|
|
29
|
+
__name(rowColumnKey, "rowColumnKey");
|
|
30
|
+
function buildSpreadsheetSheetRenderModel(sheet, styles, options = {}) {
|
|
31
|
+
var _a, _b, _c;
|
|
32
|
+
const cellsByAddress = /* @__PURE__ */ new Map();
|
|
33
|
+
const cellsByRowColumn = /* @__PURE__ */ new Map();
|
|
34
|
+
const rowMetaByIndex = /* @__PURE__ */ new Map();
|
|
35
|
+
const columnMetaByIndex = /* @__PURE__ */ new Map();
|
|
36
|
+
const mergeMasterByAddress = /* @__PURE__ */ new Map();
|
|
37
|
+
const mergeFollowerByAddress = /* @__PURE__ */ new Map();
|
|
38
|
+
let maxRow = (_a = options.minimumRows) != null ? _a : 100;
|
|
39
|
+
let maxColumn = (_b = options.minimumColumns) != null ? _b : 26;
|
|
40
|
+
for (const row of sheet.rows) {
|
|
41
|
+
rowMetaByIndex.set(row.index, row);
|
|
42
|
+
maxRow = Math.max(maxRow, row.index);
|
|
43
|
+
}
|
|
44
|
+
for (const column of sheet.columns) {
|
|
45
|
+
for (let index = column.min; index <= column.max; index++) {
|
|
46
|
+
columnMetaByIndex.set(index, column);
|
|
47
|
+
}
|
|
48
|
+
maxColumn = Math.max(maxColumn, column.max);
|
|
49
|
+
}
|
|
50
|
+
for (const cell of sheet.cells) {
|
|
51
|
+
const renderCell = toRenderCell(cell, styles, options.formulaResults);
|
|
52
|
+
cellsByAddress.set(cell.address, renderCell);
|
|
53
|
+
cellsByRowColumn.set(rowColumnKey(cell.row, cell.column), renderCell);
|
|
54
|
+
maxRow = Math.max(maxRow, cell.row);
|
|
55
|
+
maxColumn = Math.max(maxColumn, cell.column);
|
|
56
|
+
}
|
|
57
|
+
for (const [address, result] of (_c = options.formulaResults) != null ? _c : []) {
|
|
58
|
+
if (cellsByAddress.has(address) || result.error) continue;
|
|
59
|
+
const parsed = parseCellAddress(address);
|
|
60
|
+
const virtualCell = {
|
|
61
|
+
address,
|
|
62
|
+
row: parsed.row,
|
|
63
|
+
column: parsed.column,
|
|
64
|
+
kind: "formula",
|
|
65
|
+
value: null
|
|
66
|
+
};
|
|
67
|
+
const renderCell = toRenderCell(virtualCell, styles, options.formulaResults);
|
|
68
|
+
cellsByAddress.set(address, renderCell);
|
|
69
|
+
cellsByRowColumn.set(rowColumnKey(parsed.row, parsed.column), renderCell);
|
|
70
|
+
maxRow = Math.max(maxRow, parsed.row);
|
|
71
|
+
maxColumn = Math.max(maxColumn, parsed.column);
|
|
72
|
+
}
|
|
73
|
+
for (const merge of sheet.mergedRanges) {
|
|
74
|
+
const range = parseRangeAddress(merge.address);
|
|
75
|
+
const endRow = range.startRow + range.rowCount - 1;
|
|
76
|
+
const endColumn = range.startColumn + range.columnCount - 1;
|
|
77
|
+
const renderRange = {
|
|
78
|
+
startRow: range.startRow,
|
|
79
|
+
startColumn: range.startColumn,
|
|
80
|
+
endRow,
|
|
81
|
+
endColumn
|
|
82
|
+
};
|
|
83
|
+
const masterAddress = formatCellAddress(range.startRow, range.startColumn);
|
|
84
|
+
const info = {
|
|
85
|
+
address: merge.address,
|
|
86
|
+
range: renderRange,
|
|
87
|
+
masterAddress,
|
|
88
|
+
source: merge
|
|
89
|
+
};
|
|
90
|
+
mergeMasterByAddress.set(masterAddress, info);
|
|
91
|
+
maxRow = Math.max(maxRow, endRow);
|
|
92
|
+
maxColumn = Math.max(maxColumn, endColumn);
|
|
93
|
+
for (let row = range.startRow; row <= endRow; row++) {
|
|
94
|
+
for (let column = range.startColumn; column <= endColumn; column++) {
|
|
95
|
+
const address = formatCellAddress(row, column);
|
|
96
|
+
if (address !== masterAddress) mergeFollowerByAddress.set(address, masterAddress);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
sheetId: sheet.id,
|
|
102
|
+
sheetName: sheet.name,
|
|
103
|
+
source: sheet,
|
|
104
|
+
styles,
|
|
105
|
+
cellsByAddress,
|
|
106
|
+
cellsByRowColumn,
|
|
107
|
+
rowMetaByIndex,
|
|
108
|
+
columnMetaByIndex,
|
|
109
|
+
mergeMasterByAddress,
|
|
110
|
+
mergeFollowerByAddress,
|
|
111
|
+
maxRow,
|
|
112
|
+
maxColumn
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
__name(buildSpreadsheetSheetRenderModel, "buildSpreadsheetSheetRenderModel");
|
|
116
|
+
function getRenderCell(model, row, column) {
|
|
117
|
+
return model.cellsByRowColumn.get(rowColumnKey(row, column));
|
|
118
|
+
}
|
|
119
|
+
__name(getRenderCell, "getRenderCell");
|
|
120
|
+
function toRenderCell(cell, styles, formulaResults) {
|
|
121
|
+
var _a, _b;
|
|
122
|
+
const formulaResult = formulaResults == null ? void 0 : formulaResults.get(cell.address);
|
|
123
|
+
const resultValue = formulaResult && !formulaResult.error ? formulaResult.value : void 0;
|
|
124
|
+
const value = Array.isArray(resultValue) ? (_b = (_a = resultValue[0]) == null ? void 0 : _a[0]) != null ? _b : null : resultValue != null ? resultValue : cell.value;
|
|
125
|
+
return {
|
|
126
|
+
row: cell.row,
|
|
127
|
+
column: cell.column,
|
|
128
|
+
address: cell.address,
|
|
129
|
+
kind: cell.kind,
|
|
130
|
+
value,
|
|
131
|
+
displayText: cellToDisplayText(cell, styles, value, formulaResult),
|
|
132
|
+
formula: cell.formula,
|
|
133
|
+
styleId: cell.styleId,
|
|
134
|
+
source: cell
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
__name(toRenderCell, "toRenderCell");
|
|
138
|
+
function cellToDisplayText(cell, styles, value, formulaResult) {
|
|
139
|
+
if (formulaResult == null ? void 0 : formulaResult.error) return formulaResult.displayText;
|
|
140
|
+
if (value !== null) return formatSpreadsheetDisplayValue(value, numberFormatCodeForCell(cell, styles));
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
__name(cellToDisplayText, "cellToDisplayText");
|
|
144
|
+
function numberFormatCodeForCell(cell, styles) {
|
|
145
|
+
var _a, _b;
|
|
146
|
+
return (_b = styles.cellFormats[(_a = cell.styleId) != null ? _a : 0]) == null ? void 0 : _b.numberFormatCode;
|
|
147
|
+
}
|
|
148
|
+
__name(numberFormatCodeForCell, "numberFormatCodeForCell");
|
|
149
|
+
function formatSpreadsheetDisplayValue(value, formatCode) {
|
|
150
|
+
if (value === null) return "";
|
|
151
|
+
if (typeof value !== "number" || !formatCode) return String(value);
|
|
152
|
+
const normalized = formatCode.toLowerCase();
|
|
153
|
+
if (normalized.includes("%")) return `${formatFixedNumber(value * 100, decimalPlaces(formatCode))}%`;
|
|
154
|
+
if (normalized.includes("$")) return `$${formatFixedNumber(value, decimalPlaces(formatCode), true)}`;
|
|
155
|
+
if (looksLikeDateFormat(normalized)) return formatExcelSerialDate(value);
|
|
156
|
+
if (formatCode.includes(",")) return formatFixedNumber(value, decimalPlaces(formatCode), true);
|
|
157
|
+
if (formatCode.includes(".")) return formatFixedNumber(value, decimalPlaces(formatCode));
|
|
158
|
+
return String(value);
|
|
159
|
+
}
|
|
160
|
+
__name(formatSpreadsheetDisplayValue, "formatSpreadsheetDisplayValue");
|
|
161
|
+
function decimalPlaces(formatCode) {
|
|
162
|
+
var _a, _b;
|
|
163
|
+
const match = /\.(0+)/.exec(formatCode);
|
|
164
|
+
return (_b = (_a = match == null ? void 0 : match[1]) == null ? void 0 : _a.length) != null ? _b : 0;
|
|
165
|
+
}
|
|
166
|
+
__name(decimalPlaces, "decimalPlaces");
|
|
167
|
+
function formatFixedNumber(value, digits, grouping = false) {
|
|
168
|
+
return new Intl.NumberFormat("en-US", {
|
|
169
|
+
minimumFractionDigits: digits,
|
|
170
|
+
maximumFractionDigits: digits,
|
|
171
|
+
useGrouping: grouping
|
|
172
|
+
}).format(value);
|
|
173
|
+
}
|
|
174
|
+
__name(formatFixedNumber, "formatFixedNumber");
|
|
175
|
+
function looksLikeDateFormat(formatCode) {
|
|
176
|
+
return /(^|[^a-z])y{2,4}([^a-z]|$)/.test(formatCode) && /(^|[^a-z])d{1,2}([^a-z]|$)/.test(formatCode);
|
|
177
|
+
}
|
|
178
|
+
__name(looksLikeDateFormat, "looksLikeDateFormat");
|
|
179
|
+
function formatExcelSerialDate(serial) {
|
|
180
|
+
if (!Number.isFinite(serial)) return String(serial);
|
|
181
|
+
const epoch = Date.UTC(1899, 11, 30);
|
|
182
|
+
const date = new Date(epoch + Math.round(serial) * 864e5);
|
|
183
|
+
const year = date.getUTCFullYear();
|
|
184
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
185
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
186
|
+
return `${year}-${month}-${day}`;
|
|
187
|
+
}
|
|
188
|
+
__name(formatExcelSerialDate, "formatExcelSerialDate");
|
|
189
|
+
var DEFAULT_ROW_HEIGHT = 22;
|
|
190
|
+
var DEFAULT_COLUMN_WIDTH = 96;
|
|
191
|
+
var DEFAULT_ROW_HEADER_WIDTH = 48;
|
|
192
|
+
var DEFAULT_COLUMN_HEADER_HEIGHT = 24;
|
|
193
|
+
var EXCEL_POINTS_TO_CSS_PIXELS = 96 / 72;
|
|
194
|
+
var EXCEL_MAX_DIGIT_WIDTH_PX = 7;
|
|
195
|
+
var EXCEL_COLUMN_PADDING_PX = 5;
|
|
196
|
+
var _SpreadsheetLayout = class _SpreadsheetLayout {
|
|
197
|
+
constructor(model, options = {}) {
|
|
198
|
+
__publicField(this, "model", model);
|
|
199
|
+
__publicField(this, "defaultRowHeight");
|
|
200
|
+
__publicField(this, "defaultColumnWidth");
|
|
201
|
+
__publicField(this, "rowHeaderWidth");
|
|
202
|
+
__publicField(this, "columnHeaderHeight");
|
|
203
|
+
__publicField(this, "rowCount");
|
|
204
|
+
__publicField(this, "columnCount");
|
|
205
|
+
__publicField(this, "rowOffsets");
|
|
206
|
+
__publicField(this, "columnOffsets");
|
|
207
|
+
__publicField(this, "rowHeights");
|
|
208
|
+
__publicField(this, "columnWidths");
|
|
209
|
+
var _a, _b, _c, _d, _e, _f;
|
|
210
|
+
this.defaultRowHeight = (_a = options.defaultRowHeight) != null ? _a : DEFAULT_ROW_HEIGHT;
|
|
211
|
+
this.defaultColumnWidth = (_b = options.defaultColumnWidth) != null ? _b : DEFAULT_COLUMN_WIDTH;
|
|
212
|
+
this.rowHeaderWidth = (_c = options.rowHeaderWidth) != null ? _c : DEFAULT_ROW_HEADER_WIDTH;
|
|
213
|
+
this.columnHeaderHeight = (_d = options.columnHeaderHeight) != null ? _d : DEFAULT_COLUMN_HEADER_HEIGHT;
|
|
214
|
+
this.rowCount = Math.max((_e = options.rowCount) != null ? _e : 0, model.maxRow);
|
|
215
|
+
this.columnCount = Math.max((_f = options.columnCount) != null ? _f : 0, model.maxColumn);
|
|
216
|
+
this.rowHeights = buildRowHeights(model, this.rowCount, this.defaultRowHeight);
|
|
217
|
+
this.columnWidths = buildColumnWidths(model, this.columnCount, this.defaultColumnWidth);
|
|
218
|
+
this.rowOffsets = buildOffsets(this.rowHeights);
|
|
219
|
+
this.columnOffsets = buildOffsets(this.columnWidths);
|
|
220
|
+
}
|
|
221
|
+
totalWidth() {
|
|
222
|
+
var _a;
|
|
223
|
+
return (_a = this.columnOffsets[this.columnOffsets.length - 1]) != null ? _a : 0;
|
|
224
|
+
}
|
|
225
|
+
totalHeight() {
|
|
226
|
+
var _a;
|
|
227
|
+
return (_a = this.rowOffsets[this.rowOffsets.length - 1]) != null ? _a : 0;
|
|
228
|
+
}
|
|
229
|
+
getColumnWidth(column) {
|
|
230
|
+
var _a;
|
|
231
|
+
return (_a = this.columnWidths[column - 1]) != null ? _a : this.defaultColumnWidth;
|
|
232
|
+
}
|
|
233
|
+
getRowHeight(row) {
|
|
234
|
+
var _a;
|
|
235
|
+
return (_a = this.rowHeights[row - 1]) != null ? _a : this.defaultRowHeight;
|
|
236
|
+
}
|
|
237
|
+
getColumnX(column) {
|
|
238
|
+
var _a;
|
|
239
|
+
return (_a = this.columnOffsets[Math.max(0, column - 1)]) != null ? _a : 0;
|
|
240
|
+
}
|
|
241
|
+
getRowY(row) {
|
|
242
|
+
var _a;
|
|
243
|
+
return (_a = this.rowOffsets[Math.max(0, row - 1)]) != null ? _a : 0;
|
|
244
|
+
}
|
|
245
|
+
getCellLogicalRect(row, column) {
|
|
246
|
+
return {
|
|
247
|
+
x: this.getColumnX(column),
|
|
248
|
+
y: this.getRowY(row),
|
|
249
|
+
width: this.getColumnWidth(column),
|
|
250
|
+
height: this.getRowHeight(row)
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
getRangeLogicalRect(range) {
|
|
254
|
+
const x = this.getColumnX(range.startColumn);
|
|
255
|
+
const y = this.getRowY(range.startRow);
|
|
256
|
+
const endX = this.getColumnX(range.endColumn) + this.getColumnWidth(range.endColumn);
|
|
257
|
+
const endY = this.getRowY(range.endRow) + this.getRowHeight(range.endRow);
|
|
258
|
+
return { x, y, width: endX - x, height: endY - y };
|
|
259
|
+
}
|
|
260
|
+
getVisibleRange(originX, originY, size, minimumRow = 1, minimumColumn = 1) {
|
|
261
|
+
const startColumn = Math.max(minimumColumn, this.columnAt(originX));
|
|
262
|
+
const startRow = Math.max(minimumRow, this.rowAt(originY));
|
|
263
|
+
const endColumn = Math.max(
|
|
264
|
+
startColumn,
|
|
265
|
+
Math.min(this.columnCount, this.columnAt(originX + size.width) + 1)
|
|
266
|
+
);
|
|
267
|
+
const endRow = Math.max(
|
|
268
|
+
startRow,
|
|
269
|
+
Math.min(this.rowCount, this.rowAt(originY + size.height) + 1)
|
|
270
|
+
);
|
|
271
|
+
return { startRow, startColumn, endRow, endColumn };
|
|
272
|
+
}
|
|
273
|
+
rowAt(logicalY) {
|
|
274
|
+
return clampIndex(binarySearchOffset(this.rowOffsets, logicalY), this.rowCount);
|
|
275
|
+
}
|
|
276
|
+
columnAt(logicalX) {
|
|
277
|
+
return clampIndex(binarySearchOffset(this.columnOffsets, logicalX), this.columnCount);
|
|
278
|
+
}
|
|
279
|
+
hitTestLogical(logicalX, logicalY) {
|
|
280
|
+
const column = this.columnAt(logicalX);
|
|
281
|
+
const row = this.rowAt(logicalY);
|
|
282
|
+
if (row < 1 || column < 1 || row > this.rowCount || column > this.columnCount) {
|
|
283
|
+
return { kind: "none" };
|
|
284
|
+
}
|
|
285
|
+
return { kind: "cell", row, column, address: formatCellAddress(row, column) };
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
__name(_SpreadsheetLayout, "SpreadsheetLayout");
|
|
289
|
+
var SpreadsheetLayout = _SpreadsheetLayout;
|
|
290
|
+
function buildRowHeights(model, rowCount, defaultHeight) {
|
|
291
|
+
const heights = [];
|
|
292
|
+
for (let row = 1; row <= rowCount; row++) {
|
|
293
|
+
const meta = model.rowMetaByIndex.get(row);
|
|
294
|
+
heights.push((meta == null ? void 0 : meta.hidden) ? 0 : (meta == null ? void 0 : meta.height) === void 0 ? defaultHeight : meta.height * EXCEL_POINTS_TO_CSS_PIXELS);
|
|
295
|
+
}
|
|
296
|
+
return heights;
|
|
297
|
+
}
|
|
298
|
+
__name(buildRowHeights, "buildRowHeights");
|
|
299
|
+
function buildColumnWidths(model, columnCount, defaultWidth) {
|
|
300
|
+
const widths = [];
|
|
301
|
+
for (let column = 1; column <= columnCount; column++) {
|
|
302
|
+
const meta = model.columnMetaByIndex.get(column);
|
|
303
|
+
widths.push((meta == null ? void 0 : meta.hidden) ? 0 : (meta == null ? void 0 : meta.width) === void 0 ? defaultWidth : excelColumnWidthToCssPixels(meta.width));
|
|
304
|
+
}
|
|
305
|
+
return widths;
|
|
306
|
+
}
|
|
307
|
+
__name(buildColumnWidths, "buildColumnWidths");
|
|
308
|
+
function excelColumnWidthToCssPixels(width) {
|
|
309
|
+
if (!Number.isFinite(width) || width <= 0) return 0;
|
|
310
|
+
return Math.floor(
|
|
311
|
+
(256 * width + Math.floor(128 / EXCEL_MAX_DIGIT_WIDTH_PX)) / 256 * EXCEL_MAX_DIGIT_WIDTH_PX
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
__name(excelColumnWidthToCssPixels, "excelColumnWidthToCssPixels");
|
|
315
|
+
function cssPixelsToExcelColumnWidth(width) {
|
|
316
|
+
if (!Number.isFinite(width) || width <= 0) return 0;
|
|
317
|
+
return Math.floor((width - EXCEL_COLUMN_PADDING_PX) / EXCEL_MAX_DIGIT_WIDTH_PX * 256) / 256;
|
|
318
|
+
}
|
|
319
|
+
__name(cssPixelsToExcelColumnWidth, "cssPixelsToExcelColumnWidth");
|
|
320
|
+
function buildOffsets(sizes) {
|
|
321
|
+
var _a;
|
|
322
|
+
const offsets = [0];
|
|
323
|
+
for (const size of sizes) offsets.push(((_a = offsets[offsets.length - 1]) != null ? _a : 0) + size);
|
|
324
|
+
return offsets;
|
|
325
|
+
}
|
|
326
|
+
__name(buildOffsets, "buildOffsets");
|
|
327
|
+
function binarySearchOffset(offsets, value) {
|
|
328
|
+
var _a;
|
|
329
|
+
if (value <= 0) return 1;
|
|
330
|
+
let low = 0;
|
|
331
|
+
let high = offsets.length - 1;
|
|
332
|
+
while (low < high) {
|
|
333
|
+
const mid = Math.floor((low + high + 1) / 2);
|
|
334
|
+
if (((_a = offsets[mid]) != null ? _a : 0) <= value) low = mid;
|
|
335
|
+
else high = mid - 1;
|
|
336
|
+
}
|
|
337
|
+
return low + 1;
|
|
338
|
+
}
|
|
339
|
+
__name(binarySearchOffset, "binarySearchOffset");
|
|
340
|
+
function clampIndex(index, max) {
|
|
341
|
+
return Math.max(1, Math.min(max, index));
|
|
342
|
+
}
|
|
343
|
+
__name(clampIndex, "clampIndex");
|
|
344
|
+
|
|
345
|
+
// src/spreadsheet/viewport.ts
|
|
346
|
+
var _SpreadsheetViewportManager = class _SpreadsheetViewportManager {
|
|
347
|
+
constructor(layout, options) {
|
|
348
|
+
__publicField(this, "layout", layout);
|
|
349
|
+
__publicField(this, "scroll");
|
|
350
|
+
__publicField(this, "freeze");
|
|
351
|
+
__publicField(this, "viewportSize");
|
|
352
|
+
__publicField(this, "viewports");
|
|
353
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
354
|
+
this.viewportSize = options.viewportSize;
|
|
355
|
+
this.freeze = {
|
|
356
|
+
frozenRows: Math.max(0, (_b = (_a = options.freeze) == null ? void 0 : _a.frozenRows) != null ? _b : 0),
|
|
357
|
+
frozenColumns: Math.max(0, (_d = (_c = options.freeze) == null ? void 0 : _c.frozenColumns) != null ? _d : 0)
|
|
358
|
+
};
|
|
359
|
+
this.scroll = clampScroll(layout, this.freeze, options.viewportSize, {
|
|
360
|
+
x: (_f = (_e = options.scroll) == null ? void 0 : _e.x) != null ? _f : 0,
|
|
361
|
+
y: (_h = (_g = options.scroll) == null ? void 0 : _g.y) != null ? _h : 0
|
|
362
|
+
});
|
|
363
|
+
this.viewports = computeViewports(layout, this.viewportSize, this.scroll, this.freeze);
|
|
364
|
+
}
|
|
365
|
+
hitTest(point) {
|
|
366
|
+
const viewport = this.findViewport(point);
|
|
367
|
+
if (!viewport) return { kind: "none" };
|
|
368
|
+
if (viewport.kind === "corner") return { kind: "corner" };
|
|
369
|
+
const logicalX = point.x - viewport.screenRect.x + viewport.originX;
|
|
370
|
+
const logicalY = point.y - viewport.screenRect.y + viewport.originY;
|
|
371
|
+
if (viewport.kind === "columnHeaderScrollable" || viewport.kind === "columnHeaderFrozen") {
|
|
372
|
+
return { kind: "columnHeader", column: this.layout.columnAt(logicalX) };
|
|
373
|
+
}
|
|
374
|
+
if (viewport.kind === "rowHeaderScrollable" || viewport.kind === "rowHeaderFrozen") {
|
|
375
|
+
return { kind: "rowHeader", row: this.layout.rowAt(logicalY) };
|
|
376
|
+
}
|
|
377
|
+
return this.layout.hitTestLogical(logicalX, logicalY);
|
|
378
|
+
}
|
|
379
|
+
findViewport(point) {
|
|
380
|
+
for (let index = this.viewports.length - 1; index >= 0; index--) {
|
|
381
|
+
const viewport = this.viewports[index];
|
|
382
|
+
if (viewport && containsPoint(viewport.screenRect, point)) return viewport;
|
|
383
|
+
}
|
|
384
|
+
return void 0;
|
|
385
|
+
}
|
|
386
|
+
cellScreenRect(row, column, viewport) {
|
|
387
|
+
const logicalRect = this.layout.getCellLogicalRect(row, column);
|
|
388
|
+
return {
|
|
389
|
+
x: viewport.screenRect.x + logicalRect.x - viewport.originX,
|
|
390
|
+
y: viewport.screenRect.y + logicalRect.y - viewport.originY,
|
|
391
|
+
width: logicalRect.width,
|
|
392
|
+
height: logicalRect.height
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
__name(_SpreadsheetViewportManager, "SpreadsheetViewportManager");
|
|
397
|
+
var SpreadsheetViewportManager = _SpreadsheetViewportManager;
|
|
398
|
+
function freezeStateFromSheet(sheet) {
|
|
399
|
+
var _a;
|
|
400
|
+
const pane = (_a = sheet.sheetViews[0]) == null ? void 0 : _a.pane;
|
|
401
|
+
return freezeStateFromPane(pane);
|
|
402
|
+
}
|
|
403
|
+
__name(freezeStateFromSheet, "freezeStateFromSheet");
|
|
404
|
+
function freezeStateFromPane(pane) {
|
|
405
|
+
var _a, _b;
|
|
406
|
+
if (!pane || pane.state !== "frozen") return { frozenRows: 0, frozenColumns: 0 };
|
|
407
|
+
return {
|
|
408
|
+
frozenRows: Math.max(0, Math.floor((_a = pane.ySplit) != null ? _a : 0)),
|
|
409
|
+
frozenColumns: Math.max(0, Math.floor((_b = pane.xSplit) != null ? _b : 0))
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
__name(freezeStateFromPane, "freezeStateFromPane");
|
|
413
|
+
function clampScroll(layout, freeze, viewportSize, scroll) {
|
|
414
|
+
const frozenWidth = layout.getColumnX(freeze.frozenColumns + 1);
|
|
415
|
+
const frozenHeight = layout.getRowY(freeze.frozenRows + 1);
|
|
416
|
+
const mainWidth = Math.max(0, viewportSize.width - layout.rowHeaderWidth - frozenWidth);
|
|
417
|
+
const mainHeight = Math.max(0, viewportSize.height - layout.columnHeaderHeight - frozenHeight);
|
|
418
|
+
const maxX = Math.max(0, layout.totalWidth() - frozenWidth - mainWidth);
|
|
419
|
+
const maxY = Math.max(0, layout.totalHeight() - frozenHeight - mainHeight);
|
|
420
|
+
return {
|
|
421
|
+
x: Math.max(0, Math.min(maxX, scroll.x)),
|
|
422
|
+
y: Math.max(0, Math.min(maxY, scroll.y))
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
__name(clampScroll, "clampScroll");
|
|
426
|
+
function computeViewports(layout, viewportSize, scroll, freeze) {
|
|
427
|
+
const headerW = layout.rowHeaderWidth;
|
|
428
|
+
const headerH = layout.columnHeaderHeight;
|
|
429
|
+
const frozenW = layout.getColumnX(freeze.frozenColumns + 1);
|
|
430
|
+
const frozenH = layout.getRowY(freeze.frozenRows + 1);
|
|
431
|
+
const scrollableX = frozenW + scroll.x;
|
|
432
|
+
const scrollableY = frozenH + scroll.y;
|
|
433
|
+
const bodyW = Math.max(0, viewportSize.width - headerW);
|
|
434
|
+
const bodyH = Math.max(0, viewportSize.height - headerH);
|
|
435
|
+
const mainX = headerW + frozenW;
|
|
436
|
+
const mainY = headerH + frozenH;
|
|
437
|
+
const mainW = Math.max(0, viewportSize.width - mainX);
|
|
438
|
+
const mainH = Math.max(0, viewportSize.height - mainY);
|
|
439
|
+
const viewports = [];
|
|
440
|
+
viewports.push({
|
|
441
|
+
kind: "corner",
|
|
442
|
+
screenRect: { x: 0, y: 0, width: headerW, height: headerH },
|
|
443
|
+
originX: 0,
|
|
444
|
+
originY: 0,
|
|
445
|
+
rows: { start: 1, end: 0 },
|
|
446
|
+
columns: { start: 1, end: 0 }
|
|
447
|
+
});
|
|
448
|
+
if (freeze.frozenColumns > 0) {
|
|
449
|
+
viewports.push({
|
|
450
|
+
kind: "columnHeaderFrozen",
|
|
451
|
+
screenRect: { x: headerW, y: 0, width: Math.min(frozenW, bodyW), height: headerH },
|
|
452
|
+
originX: 0,
|
|
453
|
+
originY: 0,
|
|
454
|
+
rows: { start: 1, end: 0 },
|
|
455
|
+
columns: { start: 1, end: freeze.frozenColumns }
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
viewports.push({
|
|
459
|
+
kind: "columnHeaderScrollable",
|
|
460
|
+
screenRect: {
|
|
461
|
+
x: headerW + frozenW,
|
|
462
|
+
y: 0,
|
|
463
|
+
width: Math.max(0, viewportSize.width - headerW - frozenW),
|
|
464
|
+
height: headerH
|
|
465
|
+
},
|
|
466
|
+
originX: scrollableX,
|
|
467
|
+
originY: 0,
|
|
468
|
+
rows: { start: 1, end: 0 },
|
|
469
|
+
columns: visibleColumns(layout, scrollableX, mainW, freeze.frozenColumns + 1)
|
|
470
|
+
});
|
|
471
|
+
if (freeze.frozenRows > 0) {
|
|
472
|
+
viewports.push({
|
|
473
|
+
kind: "rowHeaderFrozen",
|
|
474
|
+
screenRect: { x: 0, y: headerH, width: headerW, height: Math.min(frozenH, bodyH) },
|
|
475
|
+
originX: 0,
|
|
476
|
+
originY: 0,
|
|
477
|
+
rows: { start: 1, end: freeze.frozenRows },
|
|
478
|
+
columns: { start: 1, end: 0 }
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
viewports.push({
|
|
482
|
+
kind: "rowHeaderScrollable",
|
|
483
|
+
screenRect: {
|
|
484
|
+
x: 0,
|
|
485
|
+
y: headerH + frozenH,
|
|
486
|
+
width: headerW,
|
|
487
|
+
height: Math.max(0, viewportSize.height - headerH - frozenH)
|
|
488
|
+
},
|
|
489
|
+
originX: 0,
|
|
490
|
+
originY: scrollableY,
|
|
491
|
+
rows: visibleRows(layout, scrollableY, mainH, freeze.frozenRows + 1),
|
|
492
|
+
columns: { start: 1, end: 0 }
|
|
493
|
+
});
|
|
494
|
+
if (freeze.frozenRows > 0 && freeze.frozenColumns > 0) {
|
|
495
|
+
viewports.push({
|
|
496
|
+
kind: "frozenTopLeft",
|
|
497
|
+
screenRect: { x: headerW, y: headerH, width: frozenW, height: frozenH },
|
|
498
|
+
originX: 0,
|
|
499
|
+
originY: 0,
|
|
500
|
+
rows: { start: 1, end: freeze.frozenRows },
|
|
501
|
+
columns: { start: 1, end: freeze.frozenColumns }
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
if (freeze.frozenRows > 0) {
|
|
505
|
+
viewports.push({
|
|
506
|
+
kind: "frozenTop",
|
|
507
|
+
screenRect: { x: mainX, y: headerH, width: mainW, height: frozenH },
|
|
508
|
+
originX: scrollableX,
|
|
509
|
+
originY: 0,
|
|
510
|
+
rows: { start: 1, end: freeze.frozenRows },
|
|
511
|
+
columns: visibleColumns(layout, scrollableX, mainW, freeze.frozenColumns + 1)
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
if (freeze.frozenColumns > 0) {
|
|
515
|
+
viewports.push({
|
|
516
|
+
kind: "frozenLeft",
|
|
517
|
+
screenRect: { x: headerW, y: mainY, width: frozenW, height: mainH },
|
|
518
|
+
originX: 0,
|
|
519
|
+
originY: scrollableY,
|
|
520
|
+
rows: visibleRows(layout, scrollableY, mainH, freeze.frozenRows + 1),
|
|
521
|
+
columns: { start: 1, end: freeze.frozenColumns }
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
viewports.push({
|
|
525
|
+
kind: "main",
|
|
526
|
+
screenRect: { x: mainX, y: mainY, width: mainW, height: mainH },
|
|
527
|
+
originX: scrollableX,
|
|
528
|
+
originY: scrollableY,
|
|
529
|
+
rows: visibleRows(layout, scrollableY, mainH, freeze.frozenRows + 1),
|
|
530
|
+
columns: visibleColumns(layout, scrollableX, mainW, freeze.frozenColumns + 1)
|
|
531
|
+
});
|
|
532
|
+
return viewports.filter((viewport) => viewport.screenRect.width > 0 && viewport.screenRect.height > 0);
|
|
533
|
+
}
|
|
534
|
+
__name(computeViewports, "computeViewports");
|
|
535
|
+
function visibleRows(layout, originY, height, minimumRow) {
|
|
536
|
+
const range = layout.getVisibleRange(0, originY, { width: 1, height }, minimumRow, 1);
|
|
537
|
+
return { start: range.startRow, end: range.endRow };
|
|
538
|
+
}
|
|
539
|
+
__name(visibleRows, "visibleRows");
|
|
540
|
+
function visibleColumns(layout, originX, width, minimumColumn) {
|
|
541
|
+
const range = layout.getVisibleRange(originX, 0, { width, height: 1 }, 1, minimumColumn);
|
|
542
|
+
return { start: range.startColumn, end: range.endColumn };
|
|
543
|
+
}
|
|
544
|
+
__name(visibleColumns, "visibleColumns");
|
|
545
|
+
function containsPoint(rect, point) {
|
|
546
|
+
return point.x >= rect.x && point.y >= rect.y && point.x < rect.x + rect.width && point.y < rect.y + rect.height;
|
|
547
|
+
}
|
|
548
|
+
__name(containsPoint, "containsPoint");
|
|
549
|
+
|
|
550
|
+
// src/spreadsheet/style-resolver.ts
|
|
551
|
+
function resolveSpreadsheetCellStyle(styles, styleId, cell) {
|
|
552
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
553
|
+
const format = (_a = styles.cellFormats[styleId != null ? styleId : 0]) != null ? _a : {};
|
|
554
|
+
const font = (_c = styles.fonts[(_b = format.fontId) != null ? _b : 0]) != null ? _c : {};
|
|
555
|
+
const fontSize = (_d = font.size) != null ? _d : 11;
|
|
556
|
+
const fill = (_f = styles.fills[(_e = format.fillId) != null ? _e : 0]) != null ? _f : {};
|
|
557
|
+
const border = (_h = styles.borders[(_g = format.borderId) != null ? _g : 0]) != null ? _h : {};
|
|
558
|
+
return {
|
|
559
|
+
font: canvasFont(font.bold, font.italic, fontSize, (_i = font.name) != null ? _i : "Arial"),
|
|
560
|
+
fontSize,
|
|
561
|
+
underline: font.underline === true,
|
|
562
|
+
fontColor: (_k = (_j = font.color) == null ? void 0 : _j.rgb) != null ? _k : "#111827",
|
|
563
|
+
fillColor: (_m = (_l = fill.foregroundColor) == null ? void 0 : _l.rgb) != null ? _m : "#FFFFFF",
|
|
564
|
+
wrapText: ((_n = format.alignment) == null ? void 0 : _n.wrapText) === true,
|
|
565
|
+
border,
|
|
566
|
+
horizontalAlign: resolveHorizontalAlign(format, cell),
|
|
567
|
+
verticalAlign: resolveVerticalAlign(format)
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
__name(resolveSpreadsheetCellStyle, "resolveSpreadsheetCellStyle");
|
|
571
|
+
function resolveSpreadsheetRangeBorder(model, range) {
|
|
572
|
+
var _a, _b, _c, _d;
|
|
573
|
+
const border = {};
|
|
574
|
+
for (let column = range.startColumn; column <= range.endColumn; column++) {
|
|
575
|
+
(_a = border.top) != null ? _a : border.top = cellBorderSide(model, range.startRow, column, "top");
|
|
576
|
+
(_b = border.bottom) != null ? _b : border.bottom = cellBorderSide(model, range.endRow, column, "bottom");
|
|
577
|
+
}
|
|
578
|
+
for (let row = range.startRow; row <= range.endRow; row++) {
|
|
579
|
+
(_c = border.left) != null ? _c : border.left = cellBorderSide(model, row, range.startColumn, "left");
|
|
580
|
+
(_d = border.right) != null ? _d : border.right = cellBorderSide(model, row, range.endColumn, "right");
|
|
581
|
+
}
|
|
582
|
+
return border;
|
|
583
|
+
}
|
|
584
|
+
__name(resolveSpreadsheetRangeBorder, "resolveSpreadsheetRangeBorder");
|
|
585
|
+
function cellBorderSide(model, row, column, side) {
|
|
586
|
+
var _a, _b, _c, _d;
|
|
587
|
+
const cell = getRenderCell(model, row, column);
|
|
588
|
+
const format = (_b = model.styles.cellFormats[(_a = cell == null ? void 0 : cell.styleId) != null ? _a : 0]) != null ? _b : {};
|
|
589
|
+
return (_d = model.styles.borders[(_c = format.borderId) != null ? _c : 0]) == null ? void 0 : _d[side];
|
|
590
|
+
}
|
|
591
|
+
__name(cellBorderSide, "cellBorderSide");
|
|
592
|
+
function canvasFont(bold, italic, size, family) {
|
|
593
|
+
const parts = [];
|
|
594
|
+
if (italic) parts.push("italic");
|
|
595
|
+
if (bold) parts.push("600");
|
|
596
|
+
parts.push(`${size}px`);
|
|
597
|
+
parts.push(quoteFontFamily(family));
|
|
598
|
+
return parts.join(" ");
|
|
599
|
+
}
|
|
600
|
+
__name(canvasFont, "canvasFont");
|
|
601
|
+
function quoteFontFamily(family) {
|
|
602
|
+
return /\s/.test(family) ? JSON.stringify(family) : family;
|
|
603
|
+
}
|
|
604
|
+
__name(quoteFontFamily, "quoteFontFamily");
|
|
605
|
+
function resolveHorizontalAlign(format, cell) {
|
|
606
|
+
var _a;
|
|
607
|
+
switch ((_a = format.alignment) == null ? void 0 : _a.horizontal) {
|
|
608
|
+
case "left":
|
|
609
|
+
return "left";
|
|
610
|
+
case "center":
|
|
611
|
+
return "center";
|
|
612
|
+
case "right":
|
|
613
|
+
return "right";
|
|
614
|
+
}
|
|
615
|
+
if ((cell == null ? void 0 : cell.kind) === "number") return "right";
|
|
616
|
+
if ((cell == null ? void 0 : cell.kind) === "formula" && typeof cell.value === "number") return "right";
|
|
617
|
+
if ((cell == null ? void 0 : cell.kind) === "boolean" || (cell == null ? void 0 : cell.kind) === "error") return "center";
|
|
618
|
+
return "left";
|
|
619
|
+
}
|
|
620
|
+
__name(resolveHorizontalAlign, "resolveHorizontalAlign");
|
|
621
|
+
function resolveVerticalAlign(format) {
|
|
622
|
+
var _a;
|
|
623
|
+
switch ((_a = format.alignment) == null ? void 0 : _a.vertical) {
|
|
624
|
+
case "top":
|
|
625
|
+
return "top";
|
|
626
|
+
case "center":
|
|
627
|
+
case "middle":
|
|
628
|
+
return "middle";
|
|
629
|
+
case "bottom":
|
|
630
|
+
return "bottom";
|
|
631
|
+
default:
|
|
632
|
+
return "bottom";
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
__name(resolveVerticalAlign, "resolveVerticalAlign");
|
|
636
|
+
var _SpreadsheetCanvasRenderer = class _SpreadsheetCanvasRenderer {
|
|
637
|
+
constructor(options) {
|
|
638
|
+
__publicField(this, "options", options);
|
|
639
|
+
__publicField(this, "baseCtx");
|
|
640
|
+
__publicField(this, "headerCtx");
|
|
641
|
+
__publicField(this, "overlayCtx");
|
|
642
|
+
__publicField(this, "cssWidth", 1);
|
|
643
|
+
__publicField(this, "cssHeight", 1);
|
|
644
|
+
__publicField(this, "scaleX", 1);
|
|
645
|
+
__publicField(this, "scaleY", 1);
|
|
646
|
+
__publicField(this, "headerFiltersEnabled", true);
|
|
647
|
+
__publicField(this, "headerFilterColumns", null);
|
|
648
|
+
__publicField(this, "activeHeaderFilterColumns", /* @__PURE__ */ new Set());
|
|
649
|
+
this.baseCtx = get2dContext(options.baseCanvas);
|
|
650
|
+
this.headerCtx = get2dContext(options.headerCanvas);
|
|
651
|
+
this.overlayCtx = get2dContext(options.overlayCanvas);
|
|
652
|
+
}
|
|
653
|
+
resize(width, height, devicePixelRatio = globalThis.devicePixelRatio || 1) {
|
|
654
|
+
this.cssWidth = Math.max(1, width);
|
|
655
|
+
this.cssHeight = Math.max(1, height);
|
|
656
|
+
const ratio = Math.max(1, devicePixelRatio || 1);
|
|
657
|
+
for (const canvas of [
|
|
658
|
+
this.options.baseCanvas,
|
|
659
|
+
this.options.headerCanvas,
|
|
660
|
+
this.options.overlayCanvas
|
|
661
|
+
]) {
|
|
662
|
+
canvas.style.width = `${this.cssWidth}px`;
|
|
663
|
+
canvas.style.height = `${this.cssHeight}px`;
|
|
664
|
+
canvas.width = Math.max(1, Math.ceil(this.cssWidth * ratio));
|
|
665
|
+
canvas.height = Math.max(1, Math.ceil(this.cssHeight * ratio));
|
|
666
|
+
const ctx = get2dContext(canvas);
|
|
667
|
+
this.scaleX = canvas.width / this.cssWidth;
|
|
668
|
+
this.scaleY = canvas.height / this.cssHeight;
|
|
669
|
+
ctx.setTransform(this.scaleX, 0, 0, this.scaleY, 0, 0);
|
|
670
|
+
ctx.imageSmoothingEnabled = false;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
setHeaderFiltersEnabled(enabled) {
|
|
674
|
+
this.headerFiltersEnabled = enabled;
|
|
675
|
+
}
|
|
676
|
+
setHeaderFilterColumns(columns) {
|
|
677
|
+
this.headerFilterColumns = columns === null ? null : new Set(columns);
|
|
678
|
+
}
|
|
679
|
+
setActiveHeaderFilterColumns(columns) {
|
|
680
|
+
this.activeHeaderFilterColumns = new Set(columns);
|
|
681
|
+
}
|
|
682
|
+
render(layout, viewportManager) {
|
|
683
|
+
this.clearAll(layout, viewportManager);
|
|
684
|
+
this.drawCells(layout, viewportManager);
|
|
685
|
+
this.drawHeaders(layout, viewportManager);
|
|
686
|
+
}
|
|
687
|
+
clearOverlay() {
|
|
688
|
+
clearCanvas(this.overlayCtx, this.options.overlayCanvas);
|
|
689
|
+
}
|
|
690
|
+
renderSelection(layout, viewportManager, range, options = {}) {
|
|
691
|
+
this.clearOverlay();
|
|
692
|
+
const ctx = this.overlayCtx;
|
|
693
|
+
for (const viewport of viewportManager.viewports) {
|
|
694
|
+
if (!isCellViewport(viewport)) continue;
|
|
695
|
+
const intersection = intersectRange(range, {
|
|
696
|
+
startRow: viewport.rows.start,
|
|
697
|
+
endRow: viewport.rows.end,
|
|
698
|
+
startColumn: viewport.columns.start,
|
|
699
|
+
endColumn: viewport.columns.end
|
|
700
|
+
});
|
|
701
|
+
if (!intersection) continue;
|
|
702
|
+
const logical = layout.getRangeLogicalRect(intersection);
|
|
703
|
+
const rect = logicalRectToScreen(logical, viewport);
|
|
704
|
+
withClip(ctx, viewport.screenRect, () => {
|
|
705
|
+
ctx.fillStyle = "rgba(37, 99, 235, 0.08)";
|
|
706
|
+
ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
707
|
+
ctx.strokeStyle = "#2563EB";
|
|
708
|
+
ctx.lineWidth = 2 / this.scaleX;
|
|
709
|
+
ctx.setLineDash([]);
|
|
710
|
+
strokeCrispRect(ctx, insetRect(rect, 1), this.scaleX, this.scaleY);
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
if (options.showFillHandle) this.drawFillHandle(layout, viewportManager, range);
|
|
714
|
+
}
|
|
715
|
+
fillHandleRect(layout, viewportManager, range) {
|
|
716
|
+
for (const viewport of viewportManager.viewports) {
|
|
717
|
+
if (!isCellViewport(viewport)) continue;
|
|
718
|
+
if (range.endRow < viewport.rows.start || range.endRow > viewport.rows.end || range.endColumn < viewport.columns.start || range.endColumn > viewport.columns.end) {
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
const cell = viewportManager.cellScreenRect(range.endRow, range.endColumn, viewport);
|
|
722
|
+
const size = 7;
|
|
723
|
+
return {
|
|
724
|
+
x: cell.x + cell.width - size / 2,
|
|
725
|
+
y: cell.y + cell.height - size / 2,
|
|
726
|
+
width: size,
|
|
727
|
+
height: size
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
renderCopiedRange(layout, viewportManager, range) {
|
|
733
|
+
this.renderDashedRange(layout, viewportManager, range, {
|
|
734
|
+
stroke: "#1D4ED8"
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
renderDashedRange(layout, viewportManager, range, options) {
|
|
738
|
+
const ctx = this.overlayCtx;
|
|
739
|
+
for (const viewport of viewportManager.viewports) {
|
|
740
|
+
if (!isCellViewport(viewport)) continue;
|
|
741
|
+
const intersection = intersectRange(range, {
|
|
742
|
+
startRow: viewport.rows.start,
|
|
743
|
+
endRow: viewport.rows.end,
|
|
744
|
+
startColumn: viewport.columns.start,
|
|
745
|
+
endColumn: viewport.columns.end
|
|
746
|
+
});
|
|
747
|
+
if (!intersection) continue;
|
|
748
|
+
const logical = layout.getRangeLogicalRect(intersection);
|
|
749
|
+
const rect = logicalRectToScreen(logical, viewport);
|
|
750
|
+
withClip(ctx, viewport.screenRect, () => {
|
|
751
|
+
ctx.save();
|
|
752
|
+
if (options.fill) {
|
|
753
|
+
ctx.fillStyle = options.fill;
|
|
754
|
+
ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
755
|
+
}
|
|
756
|
+
ctx.strokeStyle = options.stroke;
|
|
757
|
+
ctx.lineWidth = 2 / this.scaleX;
|
|
758
|
+
ctx.setLineDash([4, 3]);
|
|
759
|
+
ctx.lineDashOffset = 0;
|
|
760
|
+
strokeCrispRect(ctx, insetRect(rect, 2), this.scaleX, this.scaleY);
|
|
761
|
+
ctx.restore();
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
clearAll(layout, viewportManager) {
|
|
766
|
+
clearCanvas(this.baseCtx, this.options.baseCanvas);
|
|
767
|
+
clearCanvas(this.headerCtx, this.options.headerCanvas);
|
|
768
|
+
clearCanvas(this.overlayCtx, this.options.overlayCanvas);
|
|
769
|
+
}
|
|
770
|
+
drawFillHandle(layout, viewportManager, range) {
|
|
771
|
+
const rect = this.fillHandleRect(layout, viewportManager, range);
|
|
772
|
+
if (!rect) return;
|
|
773
|
+
const ctx = this.overlayCtx;
|
|
774
|
+
ctx.save();
|
|
775
|
+
ctx.fillStyle = "#2563EB";
|
|
776
|
+
ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
777
|
+
ctx.strokeStyle = "#FFFFFF";
|
|
778
|
+
ctx.lineWidth = 1 / this.scaleX;
|
|
779
|
+
strokeCrispRect(ctx, insetRect(rect, 0.5), this.scaleX, this.scaleY);
|
|
780
|
+
ctx.restore();
|
|
781
|
+
}
|
|
782
|
+
drawCells(layout, viewportManager) {
|
|
783
|
+
const ctx = this.baseCtx;
|
|
784
|
+
ctx.save();
|
|
785
|
+
ctx.fillStyle = "#FFFFFF";
|
|
786
|
+
ctx.fillRect(0, 0, this.cssWidth, this.cssHeight);
|
|
787
|
+
ctx.restore();
|
|
788
|
+
for (const viewport of viewportManager.viewports) {
|
|
789
|
+
if (!isCellViewport(viewport)) continue;
|
|
790
|
+
withClip(ctx, viewport.screenRect, () => {
|
|
791
|
+
for (let row = viewport.rows.start; row <= viewport.rows.end; row++) {
|
|
792
|
+
for (let column = viewport.columns.start; column <= viewport.columns.end; column++) {
|
|
793
|
+
const address = formatCellAddress(row, column);
|
|
794
|
+
const mergeMaster = layout.model.mergeFollowerByAddress.get(address);
|
|
795
|
+
if (mergeMaster) continue;
|
|
796
|
+
this.drawCell(ctx, layout, viewportManager, viewport, row, column, {
|
|
797
|
+
drawBorder: false,
|
|
798
|
+
reserveTrailingSpace: this.headerFiltersEnabled && row === 1
|
|
799
|
+
});
|
|
800
|
+
if (this.headerFiltersEnabled && row === 1 && (this.headerFilterColumns === null || this.headerFilterColumns.has(column))) {
|
|
801
|
+
const logical = layout.getCellLogicalRect(row, column);
|
|
802
|
+
const rect = {
|
|
803
|
+
x: viewport.screenRect.x + logical.x - viewport.originX,
|
|
804
|
+
y: viewport.screenRect.y + logical.y - viewport.originY,
|
|
805
|
+
width: logical.width,
|
|
806
|
+
height: logical.height
|
|
807
|
+
};
|
|
808
|
+
drawFilterIcon(
|
|
809
|
+
ctx,
|
|
810
|
+
rect,
|
|
811
|
+
this.scaleX,
|
|
812
|
+
this.scaleY,
|
|
813
|
+
this.activeHeaderFilterColumns.has(column)
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
for (let row = viewport.rows.start; row <= viewport.rows.end; row++) {
|
|
819
|
+
for (let column = viewport.columns.start; column <= viewport.columns.end; column++) {
|
|
820
|
+
const address = formatCellAddress(row, column);
|
|
821
|
+
const mergeMaster = layout.model.mergeFollowerByAddress.get(address);
|
|
822
|
+
if (mergeMaster) continue;
|
|
823
|
+
this.drawCell(ctx, layout, viewportManager, viewport, row, column, {
|
|
824
|
+
drawContent: false
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
drawCell(ctx, layout, viewportManager, viewport, row, column, options = {}) {
|
|
832
|
+
const cell = getRenderCell(layout.model, row, column);
|
|
833
|
+
const merge = cell ? layout.model.mergeMasterByAddress.get(cell.address) : void 0;
|
|
834
|
+
const rect = merge ? logicalRectToScreen(layout.getRangeLogicalRect(merge.range), viewport) : viewportManager.cellScreenRect(row, column, viewport);
|
|
835
|
+
const style = resolveSpreadsheetCellStyle(layout.model.styles, cell == null ? void 0 : cell.styleId, cell);
|
|
836
|
+
const border = merge ? resolveSpreadsheetRangeBorder(layout.model, merge.range) : style.border;
|
|
837
|
+
if (options.drawContent !== false) {
|
|
838
|
+
ctx.fillStyle = style.fillColor;
|
|
839
|
+
ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
840
|
+
ctx.strokeStyle = "#E5E7EB";
|
|
841
|
+
ctx.lineWidth = 1 / this.scaleX;
|
|
842
|
+
strokeCrispRect(ctx, rect, this.scaleX, this.scaleY);
|
|
843
|
+
}
|
|
844
|
+
if (options.drawBorder !== false) drawCellBorder(ctx, rect, border, this.scaleX, this.scaleY);
|
|
845
|
+
if (options.drawContent === false || !(cell == null ? void 0 : cell.displayText)) return;
|
|
846
|
+
ctx.save();
|
|
847
|
+
ctx.font = style.font;
|
|
848
|
+
ctx.fillStyle = style.fontColor;
|
|
849
|
+
ctx.textAlign = style.horizontalAlign;
|
|
850
|
+
ctx.textBaseline = "middle";
|
|
851
|
+
const padding = 6;
|
|
852
|
+
const trailingSpace = options.reserveTrailingSpace && rect.width >= 28 ? 22 : 0;
|
|
853
|
+
const contentRect = {
|
|
854
|
+
x: rect.x,
|
|
855
|
+
y: rect.y,
|
|
856
|
+
width: Math.max(1, rect.width - trailingSpace),
|
|
857
|
+
height: rect.height
|
|
858
|
+
};
|
|
859
|
+
const textX = style.horizontalAlign === "right" ? contentRect.x + contentRect.width - padding : style.horizontalAlign === "center" ? contentRect.x + contentRect.width / 2 : contentRect.x + padding;
|
|
860
|
+
withClip(ctx, contentRect, () => {
|
|
861
|
+
const lines = style.wrapText || cell.displayText.includes("\n") ? wrapCanvasText(ctx, cell.displayText, Math.max(1, contentRect.width - padding * 2)) : [cell.displayText];
|
|
862
|
+
const lineHeight = style.fontSize * 1.2;
|
|
863
|
+
const blockHeight = lines.length * lineHeight;
|
|
864
|
+
const firstTextY = textBlockStartY(rect, style, padding, blockHeight, lineHeight);
|
|
865
|
+
for (let index = 0; index < lines.length; index++) {
|
|
866
|
+
const line = lines[index];
|
|
867
|
+
const lineY = firstTextY + index * lineHeight;
|
|
868
|
+
ctx.fillText(line, textX, lineY);
|
|
869
|
+
if (style.underline) drawTextUnderline(ctx, line, textX, lineY, style, this.scaleX, this.scaleY);
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
ctx.restore();
|
|
873
|
+
}
|
|
874
|
+
drawHeaders(layout, viewportManager) {
|
|
875
|
+
const ctx = this.headerCtx;
|
|
876
|
+
for (const viewport of viewportManager.viewports) {
|
|
877
|
+
withClip(ctx, viewport.screenRect, () => {
|
|
878
|
+
if (viewport.kind === "corner") {
|
|
879
|
+
drawHeaderRect(ctx, viewport.screenRect, "", this.scaleX, this.scaleY);
|
|
880
|
+
} else if (viewport.kind === "columnHeaderFrozen" || viewport.kind === "columnHeaderScrollable") {
|
|
881
|
+
for (let column = viewport.columns.start; column <= viewport.columns.end; column++) {
|
|
882
|
+
const logical = layout.getCellLogicalRect(1, column);
|
|
883
|
+
const rect = {
|
|
884
|
+
x: viewport.screenRect.x + logical.x - viewport.originX,
|
|
885
|
+
y: viewport.screenRect.y,
|
|
886
|
+
width: logical.width,
|
|
887
|
+
height: viewport.screenRect.height
|
|
888
|
+
};
|
|
889
|
+
drawHeaderRect(ctx, rect, columnIndexToName(column), this.scaleX, this.scaleY);
|
|
890
|
+
}
|
|
891
|
+
} else if (viewport.kind === "rowHeaderFrozen" || viewport.kind === "rowHeaderScrollable") {
|
|
892
|
+
for (let row = viewport.rows.start; row <= viewport.rows.end; row++) {
|
|
893
|
+
const logical = layout.getCellLogicalRect(row, 1);
|
|
894
|
+
const rect = {
|
|
895
|
+
x: viewport.screenRect.x,
|
|
896
|
+
y: viewport.screenRect.y + logical.y - viewport.originY,
|
|
897
|
+
width: viewport.screenRect.width,
|
|
898
|
+
height: logical.height
|
|
899
|
+
};
|
|
900
|
+
drawHeaderRect(ctx, rect, String(row), this.scaleX, this.scaleY);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
__name(_SpreadsheetCanvasRenderer, "SpreadsheetCanvasRenderer");
|
|
908
|
+
var SpreadsheetCanvasRenderer = _SpreadsheetCanvasRenderer;
|
|
909
|
+
function drawFilterIcon(ctx, rect, scaleX, scaleY, active = false) {
|
|
910
|
+
if (rect.width < 28) return;
|
|
911
|
+
const centerX = rect.x + rect.width - 10;
|
|
912
|
+
const centerY = rect.y + rect.height / 2;
|
|
913
|
+
ctx.save();
|
|
914
|
+
ctx.strokeStyle = active ? "#2563EB" : "#64748B";
|
|
915
|
+
ctx.lineWidth = 1 / scaleX;
|
|
916
|
+
ctx.lineCap = "round";
|
|
917
|
+
ctx.beginPath();
|
|
918
|
+
ctx.moveTo(centerX - 4, centerY - 3);
|
|
919
|
+
ctx.lineTo(centerX + 4, centerY - 3);
|
|
920
|
+
ctx.lineTo(centerX + 1, centerY + 1);
|
|
921
|
+
ctx.lineTo(centerX + 1, centerY + 4);
|
|
922
|
+
ctx.lineTo(centerX - 1, centerY + 3);
|
|
923
|
+
ctx.lineTo(centerX - 1, centerY + 1);
|
|
924
|
+
ctx.closePath();
|
|
925
|
+
ctx.stroke();
|
|
926
|
+
ctx.restore();
|
|
927
|
+
}
|
|
928
|
+
__name(drawFilterIcon, "drawFilterIcon");
|
|
929
|
+
function intersectRange(a, b) {
|
|
930
|
+
const startRow = Math.max(a.startRow, b.startRow);
|
|
931
|
+
const startColumn = Math.max(a.startColumn, b.startColumn);
|
|
932
|
+
const endRow = Math.min(a.endRow, b.endRow);
|
|
933
|
+
const endColumn = Math.min(a.endColumn, b.endColumn);
|
|
934
|
+
if (startRow > endRow || startColumn > endColumn) return null;
|
|
935
|
+
return { startRow, startColumn, endRow, endColumn };
|
|
936
|
+
}
|
|
937
|
+
__name(intersectRange, "intersectRange");
|
|
938
|
+
function get2dContext(canvas) {
|
|
939
|
+
const ctx = canvas.getContext("2d");
|
|
940
|
+
if (!ctx) throw new Error("SpreadsheetCanvasRenderer: 2D canvas context is unavailable");
|
|
941
|
+
return ctx;
|
|
942
|
+
}
|
|
943
|
+
__name(get2dContext, "get2dContext");
|
|
944
|
+
function clearCanvas(ctx, canvas) {
|
|
945
|
+
const width = Number.parseFloat(canvas.style.width) || canvas.width;
|
|
946
|
+
const height = Number.parseFloat(canvas.style.height) || canvas.height;
|
|
947
|
+
ctx.clearRect(0, 0, width, height);
|
|
948
|
+
}
|
|
949
|
+
__name(clearCanvas, "clearCanvas");
|
|
950
|
+
function withClip(ctx, rect, draw) {
|
|
951
|
+
ctx.save();
|
|
952
|
+
ctx.beginPath();
|
|
953
|
+
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
954
|
+
ctx.clip();
|
|
955
|
+
draw();
|
|
956
|
+
ctx.restore();
|
|
957
|
+
}
|
|
958
|
+
__name(withClip, "withClip");
|
|
959
|
+
function isCellViewport(viewport) {
|
|
960
|
+
return viewport.kind === "main" || viewport.kind === "frozenTop" || viewport.kind === "frozenLeft" || viewport.kind === "frozenTopLeft";
|
|
961
|
+
}
|
|
962
|
+
__name(isCellViewport, "isCellViewport");
|
|
963
|
+
function logicalRectToScreen(rect, viewport) {
|
|
964
|
+
return {
|
|
965
|
+
x: viewport.screenRect.x + rect.x - viewport.originX,
|
|
966
|
+
y: viewport.screenRect.y + rect.y - viewport.originY,
|
|
967
|
+
width: rect.width,
|
|
968
|
+
height: rect.height
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
__name(logicalRectToScreen, "logicalRectToScreen");
|
|
972
|
+
function drawHeaderRect(ctx, rect, text, scaleX, scaleY) {
|
|
973
|
+
ctx.fillStyle = "#F3F4F6";
|
|
974
|
+
ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
975
|
+
ctx.strokeStyle = "#D1D5DB";
|
|
976
|
+
ctx.lineWidth = 1 / scaleX;
|
|
977
|
+
strokeCrispRect(ctx, rect, scaleX, scaleY);
|
|
978
|
+
if (!text) return;
|
|
979
|
+
ctx.font = "12px Arial";
|
|
980
|
+
ctx.fillStyle = "#374151";
|
|
981
|
+
ctx.textAlign = "center";
|
|
982
|
+
ctx.textBaseline = "middle";
|
|
983
|
+
ctx.fillText(text, rect.x + rect.width / 2, rect.y + rect.height / 2);
|
|
984
|
+
}
|
|
985
|
+
__name(drawHeaderRect, "drawHeaderRect");
|
|
986
|
+
function drawCellBorder(ctx, rect, border, scaleX, scaleY) {
|
|
987
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
988
|
+
if (!border.left && !border.right && !border.top && !border.bottom) return;
|
|
989
|
+
ctx.save();
|
|
990
|
+
ctx.lineWidth = 1 / scaleX;
|
|
991
|
+
ctx.setLineDash([]);
|
|
992
|
+
if (border.top) {
|
|
993
|
+
ctx.strokeStyle = (_b = (_a = border.top.color) == null ? void 0 : _a.rgb) != null ? _b : "#111827";
|
|
994
|
+
drawCrispLine(ctx, rect.x, rect.y, rect.x + rect.width, rect.y, scaleX, scaleY);
|
|
995
|
+
}
|
|
996
|
+
if (border.right) {
|
|
997
|
+
ctx.strokeStyle = (_d = (_c = border.right.color) == null ? void 0 : _c.rgb) != null ? _d : "#111827";
|
|
998
|
+
drawCrispLine(ctx, rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height, scaleX, scaleY);
|
|
999
|
+
}
|
|
1000
|
+
if (border.bottom) {
|
|
1001
|
+
ctx.strokeStyle = (_f = (_e = border.bottom.color) == null ? void 0 : _e.rgb) != null ? _f : "#111827";
|
|
1002
|
+
drawCrispLine(ctx, rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height, scaleX, scaleY);
|
|
1003
|
+
}
|
|
1004
|
+
if (border.left) {
|
|
1005
|
+
ctx.strokeStyle = (_h = (_g = border.left.color) == null ? void 0 : _g.rgb) != null ? _h : "#111827";
|
|
1006
|
+
drawCrispLine(ctx, rect.x, rect.y, rect.x, rect.y + rect.height, scaleX, scaleY);
|
|
1007
|
+
}
|
|
1008
|
+
ctx.restore();
|
|
1009
|
+
}
|
|
1010
|
+
__name(drawCellBorder, "drawCellBorder");
|
|
1011
|
+
function textBlockStartY(rect, style, padding, blockHeight, lineHeight) {
|
|
1012
|
+
if (style.verticalAlign === "top") return rect.y + padding + lineHeight / 2;
|
|
1013
|
+
if (style.verticalAlign === "bottom") return rect.y + rect.height - padding - blockHeight + lineHeight / 2;
|
|
1014
|
+
return rect.y + (rect.height - blockHeight) / 2 + lineHeight / 2;
|
|
1015
|
+
}
|
|
1016
|
+
__name(textBlockStartY, "textBlockStartY");
|
|
1017
|
+
function wrapCanvasText(ctx, text, maxWidth) {
|
|
1018
|
+
const lines = [];
|
|
1019
|
+
for (const paragraph of text.split(/\r?\n/)) {
|
|
1020
|
+
if (paragraph === "") {
|
|
1021
|
+
lines.push("");
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
let line = "";
|
|
1025
|
+
for (const character of paragraph) {
|
|
1026
|
+
const candidate = line + character;
|
|
1027
|
+
if (line && ctx.measureText(candidate).width > maxWidth) {
|
|
1028
|
+
lines.push(line);
|
|
1029
|
+
line = character;
|
|
1030
|
+
} else {
|
|
1031
|
+
line = candidate;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
if (line) lines.push(line);
|
|
1035
|
+
}
|
|
1036
|
+
return lines.length > 0 ? lines : [""];
|
|
1037
|
+
}
|
|
1038
|
+
__name(wrapCanvasText, "wrapCanvasText");
|
|
1039
|
+
function drawCrispLine(ctx, x1, y1, x2, y2, scaleX, scaleY) {
|
|
1040
|
+
ctx.beginPath();
|
|
1041
|
+
ctx.moveTo(snapStrokeCoordinate(x1, scaleX), snapStrokeCoordinate(y1, scaleY));
|
|
1042
|
+
ctx.lineTo(snapStrokeCoordinate(x2, scaleX), snapStrokeCoordinate(y2, scaleY));
|
|
1043
|
+
ctx.stroke();
|
|
1044
|
+
}
|
|
1045
|
+
__name(drawCrispLine, "drawCrispLine");
|
|
1046
|
+
function drawTextUnderline(ctx, text, textX, textY, style, scaleX, scaleY) {
|
|
1047
|
+
const metrics = ctx.measureText(text);
|
|
1048
|
+
const width = metrics.width;
|
|
1049
|
+
const startX = style.horizontalAlign === "right" ? textX - width : style.horizontalAlign === "center" ? textX - width / 2 : textX;
|
|
1050
|
+
const underlineOffset = Math.max(2, (metrics.actualBoundingBoxDescent || style.fontSize * 0.18) + 1);
|
|
1051
|
+
ctx.save();
|
|
1052
|
+
ctx.strokeStyle = style.fontColor;
|
|
1053
|
+
ctx.lineWidth = 1 / scaleX;
|
|
1054
|
+
drawCrispLine(ctx, startX, textY + underlineOffset, startX + width, textY + underlineOffset, scaleX, scaleY);
|
|
1055
|
+
ctx.restore();
|
|
1056
|
+
}
|
|
1057
|
+
__name(drawTextUnderline, "drawTextUnderline");
|
|
1058
|
+
function insetRect(rect, inset) {
|
|
1059
|
+
return {
|
|
1060
|
+
x: rect.x + inset,
|
|
1061
|
+
y: rect.y + inset,
|
|
1062
|
+
width: Math.max(0, rect.width - inset * 2),
|
|
1063
|
+
height: Math.max(0, rect.height - inset * 2)
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
__name(insetRect, "insetRect");
|
|
1067
|
+
function strokeCrispRect(ctx, rect, scaleX, scaleY) {
|
|
1068
|
+
const x = snapStrokeCoordinate(rect.x, scaleX);
|
|
1069
|
+
const y = snapStrokeCoordinate(rect.y, scaleY);
|
|
1070
|
+
const right = snapStrokeCoordinate(rect.x + rect.width, scaleX);
|
|
1071
|
+
const bottom = snapStrokeCoordinate(rect.y + rect.height, scaleY);
|
|
1072
|
+
ctx.strokeRect(x, y, Math.max(0, right - x), Math.max(0, bottom - y));
|
|
1073
|
+
}
|
|
1074
|
+
__name(strokeCrispRect, "strokeCrispRect");
|
|
1075
|
+
function snapStrokeCoordinate(value, scale) {
|
|
1076
|
+
return (Math.round(value * scale) + 0.5) / scale;
|
|
1077
|
+
}
|
|
1078
|
+
__name(snapStrokeCoordinate, "snapStrokeCoordinate");
|
|
1079
|
+
function createSelection(row, column) {
|
|
1080
|
+
return {
|
|
1081
|
+
kind: "cell",
|
|
1082
|
+
active: { row, column },
|
|
1083
|
+
range: { startRow: row, startColumn: column, endRow: row, endColumn: column }
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
__name(createSelection, "createSelection");
|
|
1087
|
+
function createRowSelection(row, bounds) {
|
|
1088
|
+
return {
|
|
1089
|
+
kind: "row",
|
|
1090
|
+
active: { row, column: 1 },
|
|
1091
|
+
range: { startRow: row, startColumn: 1, endRow: row, endColumn: bounds.columnCount }
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
__name(createRowSelection, "createRowSelection");
|
|
1095
|
+
function createRowRangeSelection(startRow, endRow, bounds) {
|
|
1096
|
+
const range = normalizeSelectionRange({
|
|
1097
|
+
startRow,
|
|
1098
|
+
startColumn: 1,
|
|
1099
|
+
endRow,
|
|
1100
|
+
endColumn: bounds.columnCount
|
|
1101
|
+
});
|
|
1102
|
+
return {
|
|
1103
|
+
kind: "row",
|
|
1104
|
+
active: { row: range.startRow, column: 1 },
|
|
1105
|
+
range
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
__name(createRowRangeSelection, "createRowRangeSelection");
|
|
1109
|
+
function createColumnSelection(column, bounds) {
|
|
1110
|
+
return {
|
|
1111
|
+
kind: "column",
|
|
1112
|
+
active: { row: 1, column },
|
|
1113
|
+
range: { startRow: 1, startColumn: column, endRow: bounds.rowCount, endColumn: column }
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
__name(createColumnSelection, "createColumnSelection");
|
|
1117
|
+
function createColumnRangeSelection(startColumn, endColumn, bounds) {
|
|
1118
|
+
const range = normalizeSelectionRange({
|
|
1119
|
+
startRow: 1,
|
|
1120
|
+
startColumn,
|
|
1121
|
+
endRow: bounds.rowCount,
|
|
1122
|
+
endColumn
|
|
1123
|
+
});
|
|
1124
|
+
return {
|
|
1125
|
+
kind: "column",
|
|
1126
|
+
active: { row: 1, column: range.startColumn },
|
|
1127
|
+
range
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
__name(createColumnRangeSelection, "createColumnRangeSelection");
|
|
1131
|
+
function normalizeSelectionRange(range) {
|
|
1132
|
+
return {
|
|
1133
|
+
startRow: Math.min(range.startRow, range.endRow),
|
|
1134
|
+
startColumn: Math.min(range.startColumn, range.endColumn),
|
|
1135
|
+
endRow: Math.max(range.startRow, range.endRow),
|
|
1136
|
+
endColumn: Math.max(range.startColumn, range.endColumn)
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
__name(normalizeSelectionRange, "normalizeSelectionRange");
|
|
1140
|
+
function extendSelection(selection, row, column, bounds) {
|
|
1141
|
+
var _a, _b;
|
|
1142
|
+
if (selection.kind === "row") {
|
|
1143
|
+
return {
|
|
1144
|
+
kind: "row",
|
|
1145
|
+
active: selection.active,
|
|
1146
|
+
range: normalizeSelectionRange({
|
|
1147
|
+
startRow: selection.active.row,
|
|
1148
|
+
startColumn: 1,
|
|
1149
|
+
endRow: row,
|
|
1150
|
+
endColumn: (_a = bounds == null ? void 0 : bounds.columnCount) != null ? _a : selection.range.endColumn
|
|
1151
|
+
})
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
if (selection.kind === "column") {
|
|
1155
|
+
return {
|
|
1156
|
+
kind: "column",
|
|
1157
|
+
active: selection.active,
|
|
1158
|
+
range: normalizeSelectionRange({
|
|
1159
|
+
startRow: 1,
|
|
1160
|
+
startColumn: selection.active.column,
|
|
1161
|
+
endRow: (_b = bounds == null ? void 0 : bounds.rowCount) != null ? _b : selection.range.endRow,
|
|
1162
|
+
endColumn: column
|
|
1163
|
+
})
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
return {
|
|
1167
|
+
kind: "cell",
|
|
1168
|
+
active: selection.active,
|
|
1169
|
+
range: normalizeSelectionRange({
|
|
1170
|
+
startRow: selection.active.row,
|
|
1171
|
+
startColumn: selection.active.column,
|
|
1172
|
+
endRow: row,
|
|
1173
|
+
endColumn: column
|
|
1174
|
+
})
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
__name(extendSelection, "extendSelection");
|
|
1178
|
+
function moveSelection(selection, deltaRow, deltaColumn, bounds) {
|
|
1179
|
+
return createSelection(
|
|
1180
|
+
clamp(selection.active.row + deltaRow, 1, bounds.rowCount),
|
|
1181
|
+
clamp(selection.active.column + deltaColumn, 1, bounds.columnCount)
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
__name(moveSelection, "moveSelection");
|
|
1185
|
+
function expandSelectionForMergedRanges(selection, model) {
|
|
1186
|
+
var _a;
|
|
1187
|
+
if (selection.kind !== "cell") return selection;
|
|
1188
|
+
const active = (_a = mergeMasterPointForCell(selection.active.row, selection.active.column, model)) != null ? _a : selection.active;
|
|
1189
|
+
return __spreadProps(__spreadValues({}, selection), { active, range: expandRangeForMergedRanges(selection.range, model) });
|
|
1190
|
+
}
|
|
1191
|
+
__name(expandSelectionForMergedRanges, "expandSelectionForMergedRanges");
|
|
1192
|
+
function expandRangeForMergedRanges(range, model) {
|
|
1193
|
+
let expanded = normalizeSelectionRange(range);
|
|
1194
|
+
let changed = true;
|
|
1195
|
+
while (changed) {
|
|
1196
|
+
changed = false;
|
|
1197
|
+
for (const merge of model.mergeMasterByAddress.values()) {
|
|
1198
|
+
if (!rangesIntersect(expanded, merge.range)) continue;
|
|
1199
|
+
const next = {
|
|
1200
|
+
startRow: Math.min(expanded.startRow, merge.range.startRow),
|
|
1201
|
+
startColumn: Math.min(expanded.startColumn, merge.range.startColumn),
|
|
1202
|
+
endRow: Math.max(expanded.endRow, merge.range.endRow),
|
|
1203
|
+
endColumn: Math.max(expanded.endColumn, merge.range.endColumn)
|
|
1204
|
+
};
|
|
1205
|
+
changed = next.startRow !== expanded.startRow || next.startColumn !== expanded.startColumn || next.endRow !== expanded.endRow || next.endColumn !== expanded.endColumn;
|
|
1206
|
+
expanded = next;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return expanded;
|
|
1210
|
+
}
|
|
1211
|
+
__name(expandRangeForMergedRanges, "expandRangeForMergedRanges");
|
|
1212
|
+
function selectionAfterRowDeletion(selection, deletedStartRow, deletedCount, bounds, model) {
|
|
1213
|
+
const nextRow = clamp(positionAfterDeletion(selection.active.row, deletedStartRow, deletedCount), 1, bounds.rowCount);
|
|
1214
|
+
const nextColumn = clamp(selection.active.column, 1, bounds.columnCount);
|
|
1215
|
+
if (selection.kind === "row") {
|
|
1216
|
+
return createRowSelection(nextRow, { columnCount: bounds.columnCount });
|
|
1217
|
+
}
|
|
1218
|
+
return expandSelectionForMergedRanges(createSelection(nextRow, nextColumn), model);
|
|
1219
|
+
}
|
|
1220
|
+
__name(selectionAfterRowDeletion, "selectionAfterRowDeletion");
|
|
1221
|
+
function selectionAfterColumnDeletion(selection, deletedStartColumn, deletedCount, bounds, model) {
|
|
1222
|
+
const nextRow = clamp(selection.active.row, 1, bounds.rowCount);
|
|
1223
|
+
const nextColumn = clamp(positionAfterDeletion(selection.active.column, deletedStartColumn, deletedCount), 1, bounds.columnCount);
|
|
1224
|
+
if (selection.kind === "column") {
|
|
1225
|
+
return createColumnSelection(nextColumn, { rowCount: bounds.rowCount });
|
|
1226
|
+
}
|
|
1227
|
+
return expandSelectionForMergedRanges(createSelection(nextRow, nextColumn), model);
|
|
1228
|
+
}
|
|
1229
|
+
__name(selectionAfterColumnDeletion, "selectionAfterColumnDeletion");
|
|
1230
|
+
function positionAfterDeletion(position, deletedStart, deletedCount) {
|
|
1231
|
+
const deletedEnd = deletedStart + deletedCount - 1;
|
|
1232
|
+
if (position < deletedStart) return position;
|
|
1233
|
+
if (position > deletedEnd) return position - deletedCount;
|
|
1234
|
+
return deletedStart;
|
|
1235
|
+
}
|
|
1236
|
+
__name(positionAfterDeletion, "positionAfterDeletion");
|
|
1237
|
+
function rangesIntersect(a, b) {
|
|
1238
|
+
return !(a.endRow < b.startRow || a.startRow > b.endRow || a.endColumn < b.startColumn || a.startColumn > b.endColumn);
|
|
1239
|
+
}
|
|
1240
|
+
__name(rangesIntersect, "rangesIntersect");
|
|
1241
|
+
function mergeMasterPointForCell(row, column, model) {
|
|
1242
|
+
const masterAddress = model.mergeFollowerByAddress.get(formatCellAddress(row, column));
|
|
1243
|
+
if (!masterAddress) return null;
|
|
1244
|
+
const merge = model.mergeMasterByAddress.get(masterAddress);
|
|
1245
|
+
if (!merge) return null;
|
|
1246
|
+
return { row: merge.range.startRow, column: merge.range.startColumn };
|
|
1247
|
+
}
|
|
1248
|
+
__name(mergeMasterPointForCell, "mergeMasterPointForCell");
|
|
1249
|
+
function clamp(value, min, max) {
|
|
1250
|
+
return Math.max(min, Math.min(max, value));
|
|
1251
|
+
}
|
|
1252
|
+
__name(clamp, "clamp");
|
|
1253
|
+
function buildSpreadsheetClipboardPayload(model, range) {
|
|
1254
|
+
const text = serializeRangeToTsv(model, range);
|
|
1255
|
+
return {
|
|
1256
|
+
text,
|
|
1257
|
+
values: parseTsv(text),
|
|
1258
|
+
styleIds: rangeStyleIdsForClipboard(model, range),
|
|
1259
|
+
merges: mergedRangesForClipboard(model, range)
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
__name(buildSpreadsheetClipboardPayload, "buildSpreadsheetClipboardPayload");
|
|
1263
|
+
function serializeRangeToTsv(model, range) {
|
|
1264
|
+
const lines = [];
|
|
1265
|
+
for (let row = range.startRow; row <= range.endRow; row++) {
|
|
1266
|
+
const values = [];
|
|
1267
|
+
for (let column = range.startColumn; column <= range.endColumn; column++) {
|
|
1268
|
+
values.push(escapeTsvValue(clipboardCellText(model, row, column)));
|
|
1269
|
+
}
|
|
1270
|
+
lines.push(values.join(" "));
|
|
1271
|
+
}
|
|
1272
|
+
return lines.join("\n");
|
|
1273
|
+
}
|
|
1274
|
+
__name(serializeRangeToTsv, "serializeRangeToTsv");
|
|
1275
|
+
function parseTsv(text) {
|
|
1276
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").filter((line, index, lines) => !(index === lines.length - 1 && line === "")).map((line) => line.split(" ").map(coerceClipboardValue));
|
|
1277
|
+
}
|
|
1278
|
+
__name(parseTsv, "parseTsv");
|
|
1279
|
+
function rangeAddressForMatrix(startRow, startColumn, values) {
|
|
1280
|
+
const rowCount = Math.max(1, values.length);
|
|
1281
|
+
const columnCount = Math.max(1, values.reduce((max, row) => Math.max(max, row.length), 0));
|
|
1282
|
+
return formatRangeAddress(startRow, startColumn, rowCount, columnCount);
|
|
1283
|
+
}
|
|
1284
|
+
__name(rangeAddressForMatrix, "rangeAddressForMatrix");
|
|
1285
|
+
function escapeTsvValue(value) {
|
|
1286
|
+
return value.replace(/\t/g, " ").replace(/\r?\n/g, " ");
|
|
1287
|
+
}
|
|
1288
|
+
__name(escapeTsvValue, "escapeTsvValue");
|
|
1289
|
+
function clipboardCellText(model, row, column) {
|
|
1290
|
+
var _a, _b;
|
|
1291
|
+
const address = formatCellAddress(row, column);
|
|
1292
|
+
if (model.mergeFollowerByAddress.has(address)) return "";
|
|
1293
|
+
return (_b = (_a = getRenderCell(model, row, column)) == null ? void 0 : _a.displayText) != null ? _b : "";
|
|
1294
|
+
}
|
|
1295
|
+
__name(clipboardCellText, "clipboardCellText");
|
|
1296
|
+
function mergedRangesForClipboard(model, range) {
|
|
1297
|
+
const merges = [];
|
|
1298
|
+
for (const merge of model.mergeMasterByAddress.values()) {
|
|
1299
|
+
if (!rangeContainsRange(range, merge.range)) continue;
|
|
1300
|
+
merges.push({
|
|
1301
|
+
startRowOffset: merge.range.startRow - range.startRow,
|
|
1302
|
+
startColumnOffset: merge.range.startColumn - range.startColumn,
|
|
1303
|
+
rowCount: merge.range.endRow - merge.range.startRow + 1,
|
|
1304
|
+
columnCount: merge.range.endColumn - merge.range.startColumn + 1
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
return merges;
|
|
1308
|
+
}
|
|
1309
|
+
__name(mergedRangesForClipboard, "mergedRangesForClipboard");
|
|
1310
|
+
function rangeStyleIdsForClipboard(model, range) {
|
|
1311
|
+
var _a;
|
|
1312
|
+
const styleIds = [];
|
|
1313
|
+
for (let row = range.startRow; row <= range.endRow; row++) {
|
|
1314
|
+
const line = [];
|
|
1315
|
+
for (let column = range.startColumn; column <= range.endColumn; column++) {
|
|
1316
|
+
line.push((_a = getRenderCell(model, row, column)) == null ? void 0 : _a.styleId);
|
|
1317
|
+
}
|
|
1318
|
+
styleIds.push(line);
|
|
1319
|
+
}
|
|
1320
|
+
return styleIds;
|
|
1321
|
+
}
|
|
1322
|
+
__name(rangeStyleIdsForClipboard, "rangeStyleIdsForClipboard");
|
|
1323
|
+
function rangeContainsRange(outer, inner) {
|
|
1324
|
+
return inner.startRow >= outer.startRow && inner.endRow <= outer.endRow && inner.startColumn >= outer.startColumn && inner.endColumn <= outer.endColumn;
|
|
1325
|
+
}
|
|
1326
|
+
__name(rangeContainsRange, "rangeContainsRange");
|
|
1327
|
+
function coerceClipboardValue(value) {
|
|
1328
|
+
if (value === "") return null;
|
|
1329
|
+
const number = Number(value);
|
|
1330
|
+
if (Number.isFinite(number) && value.trim() !== "") return number;
|
|
1331
|
+
if (value === "TRUE") return true;
|
|
1332
|
+
if (value === "FALSE") return false;
|
|
1333
|
+
return value;
|
|
1334
|
+
}
|
|
1335
|
+
__name(coerceClipboardValue, "coerceClipboardValue");
|
|
1336
|
+
function buildSpreadsheetAutoFillPlan(model, sourceRange, targetRange) {
|
|
1337
|
+
var _a;
|
|
1338
|
+
const source = normalizeRange(sourceRange);
|
|
1339
|
+
const target = normalizeRange(targetRange);
|
|
1340
|
+
const direction = resolveAutoFillDirection(source, target);
|
|
1341
|
+
if (rangeTouchesMergedCells(source, model) || rangeTouchesMergedCells(target, model)) {
|
|
1342
|
+
throw new Error("Auto fill does not support merged cells");
|
|
1343
|
+
}
|
|
1344
|
+
const cells = [];
|
|
1345
|
+
for (let row = target.startRow; row <= target.endRow; row++) {
|
|
1346
|
+
for (let column = target.startColumn; column <= target.endColumn; column++) {
|
|
1347
|
+
if (isInsideRange(row, column, source)) continue;
|
|
1348
|
+
const sourceRow = direction === "down" ? source.startRow + modulo(row - source.startRow, heightOf(source)) : row;
|
|
1349
|
+
const sourceColumn = direction === "right" ? source.startColumn + modulo(column - source.startColumn, widthOf(source)) : column;
|
|
1350
|
+
const sourceCell = getRenderCell(model, sourceRow, sourceColumn);
|
|
1351
|
+
const sequenceValue = autoFillSequenceValue(model, source, direction, row, column);
|
|
1352
|
+
const formula = (sourceCell == null ? void 0 : sourceCell.formula) ? shiftFormulaReferences(sourceCell.formula, row - sourceRow, column - sourceColumn) : void 0;
|
|
1353
|
+
cells.push({
|
|
1354
|
+
row,
|
|
1355
|
+
column,
|
|
1356
|
+
value: formula ? null : (_a = sequenceValue != null ? sequenceValue : sourceCell == null ? void 0 : sourceCell.source.value) != null ? _a : null,
|
|
1357
|
+
formula,
|
|
1358
|
+
styleId: sourceCell == null ? void 0 : sourceCell.styleId
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
return { direction, source, target, cells };
|
|
1363
|
+
}
|
|
1364
|
+
__name(buildSpreadsheetAutoFillPlan, "buildSpreadsheetAutoFillPlan");
|
|
1365
|
+
function supportsSpreadsheetAutoFill(model, sourceRange) {
|
|
1366
|
+
return !rangeTouchesMergedCells(normalizeRange(sourceRange), model);
|
|
1367
|
+
}
|
|
1368
|
+
__name(supportsSpreadsheetAutoFill, "supportsSpreadsheetAutoFill");
|
|
1369
|
+
function resolveAutoFillDirection(source, target) {
|
|
1370
|
+
const fillsDown = target.startRow === source.startRow && target.startColumn === source.startColumn && target.endColumn === source.endColumn && target.endRow > source.endRow;
|
|
1371
|
+
if (fillsDown) return "down";
|
|
1372
|
+
const fillsRight = target.startRow === source.startRow && target.endRow === source.endRow && target.startColumn === source.startColumn && target.endColumn > source.endColumn;
|
|
1373
|
+
if (fillsRight) return "right";
|
|
1374
|
+
throw new Error("Auto fill target must extend the source selection down or right");
|
|
1375
|
+
}
|
|
1376
|
+
__name(resolveAutoFillDirection, "resolveAutoFillDirection");
|
|
1377
|
+
function autoFillSequenceValue(model, source, direction, row, column) {
|
|
1378
|
+
const length = direction === "down" ? heightOf(source) : widthOf(source);
|
|
1379
|
+
if (length < 2) return void 0;
|
|
1380
|
+
const values = Array.from({ length }, (_, index) => {
|
|
1381
|
+
const sourceRow = direction === "down" ? source.startRow + index : row;
|
|
1382
|
+
const sourceColumn = direction === "right" ? source.startColumn + index : column;
|
|
1383
|
+
const cell = getRenderCell(model, sourceRow, sourceColumn);
|
|
1384
|
+
return (cell == null ? void 0 : cell.formula) ? void 0 : cell == null ? void 0 : cell.source.value;
|
|
1385
|
+
});
|
|
1386
|
+
if (!values.every((value) => typeof value === "number" && Number.isFinite(value))) {
|
|
1387
|
+
return void 0;
|
|
1388
|
+
}
|
|
1389
|
+
const step = values[1] - values[0];
|
|
1390
|
+
if (!values.every((value, index) => index === 0 || value === values[0] + step * index)) return void 0;
|
|
1391
|
+
const position = direction === "down" ? row - source.startRow : column - source.startColumn;
|
|
1392
|
+
return values[0] + step * position;
|
|
1393
|
+
}
|
|
1394
|
+
__name(autoFillSequenceValue, "autoFillSequenceValue");
|
|
1395
|
+
function rangeTouchesMergedCells(range, model) {
|
|
1396
|
+
for (const merge of model.mergeMasterByAddress.values()) {
|
|
1397
|
+
if (rangesIntersect2(range, merge.range)) return true;
|
|
1398
|
+
}
|
|
1399
|
+
return false;
|
|
1400
|
+
}
|
|
1401
|
+
__name(rangeTouchesMergedCells, "rangeTouchesMergedCells");
|
|
1402
|
+
function normalizeRange(range) {
|
|
1403
|
+
return {
|
|
1404
|
+
startRow: Math.min(range.startRow, range.endRow),
|
|
1405
|
+
startColumn: Math.min(range.startColumn, range.endColumn),
|
|
1406
|
+
endRow: Math.max(range.startRow, range.endRow),
|
|
1407
|
+
endColumn: Math.max(range.startColumn, range.endColumn)
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
__name(normalizeRange, "normalizeRange");
|
|
1411
|
+
function heightOf(range) {
|
|
1412
|
+
return range.endRow - range.startRow + 1;
|
|
1413
|
+
}
|
|
1414
|
+
__name(heightOf, "heightOf");
|
|
1415
|
+
function widthOf(range) {
|
|
1416
|
+
return range.endColumn - range.startColumn + 1;
|
|
1417
|
+
}
|
|
1418
|
+
__name(widthOf, "widthOf");
|
|
1419
|
+
function isInsideRange(row, column, range) {
|
|
1420
|
+
return row >= range.startRow && row <= range.endRow && column >= range.startColumn && column <= range.endColumn;
|
|
1421
|
+
}
|
|
1422
|
+
__name(isInsideRange, "isInsideRange");
|
|
1423
|
+
function rangesIntersect2(a, b) {
|
|
1424
|
+
return !(a.endRow < b.startRow || a.startRow > b.endRow || a.endColumn < b.startColumn || a.startColumn > b.endColumn);
|
|
1425
|
+
}
|
|
1426
|
+
__name(rangesIntersect2, "rangesIntersect");
|
|
1427
|
+
function modulo(value, divisor) {
|
|
1428
|
+
return (value % divisor + divisor) % divisor;
|
|
1429
|
+
}
|
|
1430
|
+
__name(modulo, "modulo");
|
|
1431
|
+
async function setSpreadsheetCellValue(workbook, sheetName, address, value) {
|
|
1432
|
+
await workbook.worksheets.getItem(sheetName).getCell(address).setValue(value);
|
|
1433
|
+
}
|
|
1434
|
+
__name(setSpreadsheetCellValue, "setSpreadsheetCellValue");
|
|
1435
|
+
async function setSpreadsheetCellFormula(workbook, sheetName, address, formula) {
|
|
1436
|
+
await workbook.worksheets.getItem(sheetName).getCell(address).setFormula(formula);
|
|
1437
|
+
}
|
|
1438
|
+
__name(setSpreadsheetCellFormula, "setSpreadsheetCellFormula");
|
|
1439
|
+
async function setSpreadsheetRangeValues(workbook, sheetName, startRow, startColumn, values) {
|
|
1440
|
+
const rowCount = values.length;
|
|
1441
|
+
const columnCount = values.reduce((max, row) => Math.max(max, row.length), 0);
|
|
1442
|
+
const normalized = values.map((row) => {
|
|
1443
|
+
const next = row.slice();
|
|
1444
|
+
while (next.length < columnCount) next.push(null);
|
|
1445
|
+
return next;
|
|
1446
|
+
});
|
|
1447
|
+
await workbook.worksheets.getItem(sheetName).getRange(formatRangeAddress(startRow, startColumn, rowCount, columnCount)).setValues(normalized);
|
|
1448
|
+
}
|
|
1449
|
+
__name(setSpreadsheetRangeValues, "setSpreadsheetRangeValues");
|
|
1450
|
+
async function clearSpreadsheetRange(workbook, sheetName, startRow, startColumn, rowCount, columnCount) {
|
|
1451
|
+
await workbook.worksheets.getItem(sheetName).getRange(formatRangeAddress(startRow, startColumn, rowCount, columnCount)).clear();
|
|
1452
|
+
}
|
|
1453
|
+
__name(clearSpreadsheetRange, "clearSpreadsheetRange");
|
|
1454
|
+
async function setSpreadsheetRowHeight(workbook, sheetName, row, height) {
|
|
1455
|
+
await workbook.worksheets.getItem(sheetName).getRange(formatCellAddress(row, 1)).setRowHeight(height);
|
|
1456
|
+
}
|
|
1457
|
+
__name(setSpreadsheetRowHeight, "setSpreadsheetRowHeight");
|
|
1458
|
+
async function setSpreadsheetColumnWidth(workbook, sheetName, column, width) {
|
|
1459
|
+
await workbook.worksheets.getItem(sheetName).getRange(formatCellAddress(1, column)).setColumnWidth(cssPixelsToExcelColumnWidth(width));
|
|
1460
|
+
}
|
|
1461
|
+
__name(setSpreadsheetColumnWidth, "setSpreadsheetColumnWidth");
|
|
1462
|
+
|
|
1463
|
+
// src/spreadsheet/search.ts
|
|
1464
|
+
function buildSpreadsheetReplacePlan(model, options) {
|
|
1465
|
+
const query = options.query.trim();
|
|
1466
|
+
if (!query) return [];
|
|
1467
|
+
const matches = [];
|
|
1468
|
+
const cells = [...model.cellsByAddress.values()].sort((a, b) => a.row - b.row || a.column - b.column);
|
|
1469
|
+
for (const cell of cells) {
|
|
1470
|
+
if (!searchableCellText(cell, options.matchCase === true).includes(normalizeSearchText(query, options.matchCase === true))) continue;
|
|
1471
|
+
if (cell.formula) {
|
|
1472
|
+
const formulaQuery = query.startsWith("=") ? query.slice(1) : query;
|
|
1473
|
+
matches.push({ address: cell.address, formula: replaceText(cell.formula, formulaQuery, options.replacement, options.matchCase === true) });
|
|
1474
|
+
} else {
|
|
1475
|
+
const source = cell.value === null ? "" : String(cell.value);
|
|
1476
|
+
matches.push({ address: cell.address, value: replaceText(source, query, options.replacement, options.matchCase === true) });
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return matches;
|
|
1480
|
+
}
|
|
1481
|
+
__name(buildSpreadsheetReplacePlan, "buildSpreadsheetReplacePlan");
|
|
1482
|
+
function findSpreadsheetMatches(model, options) {
|
|
1483
|
+
const query = normalizeSearchText(options.query, options.matchCase === true);
|
|
1484
|
+
if (!query) return [];
|
|
1485
|
+
const matches = [];
|
|
1486
|
+
const cells = [...model.cellsByAddress.values()].sort((a, b) => a.row - b.row || a.column - b.column);
|
|
1487
|
+
for (const cell of cells) {
|
|
1488
|
+
const haystack = searchableCellText(cell, options.matchCase === true);
|
|
1489
|
+
if (!haystack.includes(query)) continue;
|
|
1490
|
+
matches.push({ row: cell.row, column: cell.column, address: cell.address, text: cell.displayText });
|
|
1491
|
+
}
|
|
1492
|
+
return matches;
|
|
1493
|
+
}
|
|
1494
|
+
__name(findSpreadsheetMatches, "findSpreadsheetMatches");
|
|
1495
|
+
function nextSpreadsheetMatch(matches, active, direction = "forward") {
|
|
1496
|
+
var _a, _b, _c, _d;
|
|
1497
|
+
if (matches.length === 0) return null;
|
|
1498
|
+
if (direction === "backward") {
|
|
1499
|
+
return (_b = (_a = [...matches].reverse().find((match) => compareCellPosition(match, active) < 0)) != null ? _a : matches[matches.length - 1]) != null ? _b : null;
|
|
1500
|
+
}
|
|
1501
|
+
return (_d = (_c = matches.find((match) => compareCellPosition(match, active) > 0)) != null ? _c : matches[0]) != null ? _d : null;
|
|
1502
|
+
}
|
|
1503
|
+
__name(nextSpreadsheetMatch, "nextSpreadsheetMatch");
|
|
1504
|
+
function rangeForSpreadsheetMatch(match) {
|
|
1505
|
+
return {
|
|
1506
|
+
startRow: match.row,
|
|
1507
|
+
startColumn: match.column,
|
|
1508
|
+
endRow: match.row,
|
|
1509
|
+
endColumn: match.column
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
__name(rangeForSpreadsheetMatch, "rangeForSpreadsheetMatch");
|
|
1513
|
+
function searchableCellText(cell, matchCase) {
|
|
1514
|
+
const parts = [
|
|
1515
|
+
cell.displayText,
|
|
1516
|
+
rawCellValueText(cell.value),
|
|
1517
|
+
cell.formula ? `=${cell.formula}` : ""
|
|
1518
|
+
].filter(Boolean);
|
|
1519
|
+
return normalizeSearchText(parts.join("\n"), matchCase);
|
|
1520
|
+
}
|
|
1521
|
+
__name(searchableCellText, "searchableCellText");
|
|
1522
|
+
function rawCellValueText(value) {
|
|
1523
|
+
return value === null ? "" : String(value);
|
|
1524
|
+
}
|
|
1525
|
+
__name(rawCellValueText, "rawCellValueText");
|
|
1526
|
+
function normalizeSearchText(value, matchCase) {
|
|
1527
|
+
const text = value.trim();
|
|
1528
|
+
return matchCase ? text : text.toLocaleLowerCase();
|
|
1529
|
+
}
|
|
1530
|
+
__name(normalizeSearchText, "normalizeSearchText");
|
|
1531
|
+
function compareCellPosition(a, b) {
|
|
1532
|
+
return a.row === b.row ? a.column - b.column : a.row - b.row;
|
|
1533
|
+
}
|
|
1534
|
+
__name(compareCellPosition, "compareCellPosition");
|
|
1535
|
+
function replaceText(source, query, replacement, matchCase) {
|
|
1536
|
+
if (!query) return source;
|
|
1537
|
+
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1538
|
+
return source.replace(new RegExp(escaped, matchCase ? "g" : "gi"), replacement);
|
|
1539
|
+
}
|
|
1540
|
+
__name(replaceText, "replaceText");
|
|
1541
|
+
|
|
1542
|
+
// src/spreadsheet/filter.ts
|
|
1543
|
+
function spreadsheetFilterValues(model, range, column) {
|
|
1544
|
+
var _a, _b;
|
|
1545
|
+
const values = /* @__PURE__ */ new Set();
|
|
1546
|
+
for (let row = range.startRow + 1; row <= range.endRow; row += 1) {
|
|
1547
|
+
values.add((_b = (_a = getRenderCell(model, row, column)) == null ? void 0 : _a.displayText) != null ? _b : "");
|
|
1548
|
+
}
|
|
1549
|
+
return [...values].sort((left, right) => left.localeCompare(right, void 0, { numeric: true }));
|
|
1550
|
+
}
|
|
1551
|
+
__name(spreadsheetFilterValues, "spreadsheetFilterValues");
|
|
1552
|
+
function spreadsheetVisibleFilterValues(model, range, column) {
|
|
1553
|
+
var _a, _b, _c;
|
|
1554
|
+
const visibleValues = /* @__PURE__ */ new Set();
|
|
1555
|
+
for (let row = range.startRow + 1; row <= range.endRow; row += 1) {
|
|
1556
|
+
if ((_a = model.rowMetaByIndex.get(row)) == null ? void 0 : _a.hidden) continue;
|
|
1557
|
+
visibleValues.add((_c = (_b = getRenderCell(model, row, column)) == null ? void 0 : _b.displayText) != null ? _c : "");
|
|
1558
|
+
}
|
|
1559
|
+
return [...visibleValues].sort((left, right) => left.localeCompare(right, void 0, { numeric: true }));
|
|
1560
|
+
}
|
|
1561
|
+
__name(spreadsheetVisibleFilterValues, "spreadsheetVisibleFilterValues");
|
|
1562
|
+
function spreadsheetRowsForFilter(model, range, column, value) {
|
|
1563
|
+
var _a, _b;
|
|
1564
|
+
const values = new Set(typeof value === "string" ? [value] : value);
|
|
1565
|
+
const hiddenRows = [];
|
|
1566
|
+
for (let row = range.startRow + 1; row <= range.endRow; row += 1) {
|
|
1567
|
+
if (!values.has((_b = (_a = getRenderCell(model, row, column)) == null ? void 0 : _a.displayText) != null ? _b : "")) hiddenRows.push(row);
|
|
1568
|
+
}
|
|
1569
|
+
return hiddenRows;
|
|
1570
|
+
}
|
|
1571
|
+
__name(spreadsheetRowsForFilter, "spreadsheetRowsForFilter");
|
|
1572
|
+
function spreadsheetRowsForColumnFilters(model, range, filters) {
|
|
1573
|
+
const allowedByColumn = filters.map((filter) => [filter.column, new Set(filter.values)]);
|
|
1574
|
+
const hiddenRows = [];
|
|
1575
|
+
for (let row = range.startRow + 1; row <= range.endRow; row += 1) {
|
|
1576
|
+
const matchesAll = allowedByColumn.every(
|
|
1577
|
+
([column, values]) => {
|
|
1578
|
+
var _a, _b;
|
|
1579
|
+
return values.has((_b = (_a = getRenderCell(model, row, column)) == null ? void 0 : _a.displayText) != null ? _b : "");
|
|
1580
|
+
}
|
|
1581
|
+
);
|
|
1582
|
+
if (!matchesAll) hiddenRows.push(row);
|
|
1583
|
+
}
|
|
1584
|
+
return hiddenRows;
|
|
1585
|
+
}
|
|
1586
|
+
__name(spreadsheetRowsForColumnFilters, "spreadsheetRowsForColumnFilters");
|
|
1587
|
+
function spreadsheetSortRows(model, range, column, direction) {
|
|
1588
|
+
const rows = Array.from({ length: range.endRow - range.startRow }, (_, index) => range.startRow + index + 1);
|
|
1589
|
+
rows.sort((leftRow, rightRow) => {
|
|
1590
|
+
var _a, _b, _c, _d;
|
|
1591
|
+
const left = (_b = (_a = getRenderCell(model, leftRow, column)) == null ? void 0 : _a.displayText) != null ? _b : "";
|
|
1592
|
+
const right = (_d = (_c = getRenderCell(model, rightRow, column)) == null ? void 0 : _c.displayText) != null ? _d : "";
|
|
1593
|
+
if (left === "" && right !== "") return 1;
|
|
1594
|
+
if (left !== "" && right === "") return -1;
|
|
1595
|
+
const comparison = left.localeCompare(right, void 0, { numeric: true, sensitivity: "base" });
|
|
1596
|
+
return (direction === "desc" ? -comparison : comparison) || leftRow - rightRow;
|
|
1597
|
+
});
|
|
1598
|
+
return rows;
|
|
1599
|
+
}
|
|
1600
|
+
__name(spreadsheetSortRows, "spreadsheetSortRows");
|
|
1601
|
+
|
|
1602
|
+
// src/spreadsheet/runtime.ts
|
|
1603
|
+
var _SpreadsheetEditorRuntime = class _SpreadsheetEditorRuntime {
|
|
1604
|
+
constructor(state) {
|
|
1605
|
+
__publicField(this, "state", state);
|
|
1606
|
+
__publicField(this, "activeTransaction", null);
|
|
1607
|
+
__publicField(this, "activeTransactionSource", null);
|
|
1608
|
+
__publicField(this, "transactionNeedsRefresh", false);
|
|
1609
|
+
}
|
|
1610
|
+
static async fromZip(bytes, options = {}) {
|
|
1611
|
+
const workbook = await ExcelWorkbook.fromZip(bytes, options);
|
|
1612
|
+
return _SpreadsheetEditorRuntime.fromWorkbook(workbook);
|
|
1613
|
+
}
|
|
1614
|
+
static async fromWorkbook(workbook) {
|
|
1615
|
+
const model = await workbook.toModel();
|
|
1616
|
+
const firstSheet = model.sheets[0];
|
|
1617
|
+
if (!firstSheet) throw new Error("SpreadsheetEditorRuntime: workbook has no sheets");
|
|
1618
|
+
const formulaEngine = new SpreadsheetFormulaEngine(firstSheet, { sheets: model.sheets });
|
|
1619
|
+
const formulaResults = formulaEngine.calculate();
|
|
1620
|
+
return new _SpreadsheetEditorRuntime({
|
|
1621
|
+
workbook,
|
|
1622
|
+
model,
|
|
1623
|
+
activeSheetName: firstSheet.name,
|
|
1624
|
+
formulaResults,
|
|
1625
|
+
formulaEngine,
|
|
1626
|
+
activeSheet: buildSpreadsheetSheetRenderModel(firstSheet, model.styles, { formulaResults })
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
workbook() {
|
|
1630
|
+
return this.state.workbook;
|
|
1631
|
+
}
|
|
1632
|
+
model() {
|
|
1633
|
+
return this.state.model;
|
|
1634
|
+
}
|
|
1635
|
+
activeSheetName() {
|
|
1636
|
+
return this.state.activeSheetName;
|
|
1637
|
+
}
|
|
1638
|
+
activeSheet() {
|
|
1639
|
+
return this.state.activeSheet;
|
|
1640
|
+
}
|
|
1641
|
+
canUndo() {
|
|
1642
|
+
return this.state.workbook.history().canUndo();
|
|
1643
|
+
}
|
|
1644
|
+
canRedo() {
|
|
1645
|
+
return this.state.workbook.history().canRedo();
|
|
1646
|
+
}
|
|
1647
|
+
async undo() {
|
|
1648
|
+
const result = await this.state.workbook.history().undo();
|
|
1649
|
+
if (!result) return null;
|
|
1650
|
+
await this.refresh();
|
|
1651
|
+
return result;
|
|
1652
|
+
}
|
|
1653
|
+
async redo() {
|
|
1654
|
+
const result = await this.state.workbook.history().redo();
|
|
1655
|
+
if (!result) return null;
|
|
1656
|
+
await this.refresh();
|
|
1657
|
+
return result;
|
|
1658
|
+
}
|
|
1659
|
+
async runUserTransaction(options, run) {
|
|
1660
|
+
return this.runScopedTransaction(
|
|
1661
|
+
"user",
|
|
1662
|
+
(apply) => this.state.workbook.history().runGroup(__spreadProps(__spreadValues({}, options), { source: "user" }), apply),
|
|
1663
|
+
run
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
async runAgentTransaction(options, run) {
|
|
1667
|
+
return this.runScopedTransaction(
|
|
1668
|
+
"agent",
|
|
1669
|
+
(apply) => this.state.workbook.history().runAgentGroup(options, apply),
|
|
1670
|
+
() => run(this)
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
async previewAgentTransaction(options, run) {
|
|
1674
|
+
var _a;
|
|
1675
|
+
const beforeModel = cloneWorkbookModel(this.state.model);
|
|
1676
|
+
const previewWorkbook = await ExcelWorkbook.fromModel(beforeModel, { mode: "inline" });
|
|
1677
|
+
const previewRuntime = await _SpreadsheetEditorRuntime.fromWorkbook(previewWorkbook);
|
|
1678
|
+
await previewRuntime.setActiveSheet(this.state.activeSheetName);
|
|
1679
|
+
try {
|
|
1680
|
+
const result = await previewRuntime.runAgentTransaction(options, run);
|
|
1681
|
+
const afterModel = cloneWorkbookModel(previewRuntime.model());
|
|
1682
|
+
const diff = diffExcelWorkbookModels(beforeModel, afterModel);
|
|
1683
|
+
return {
|
|
1684
|
+
result,
|
|
1685
|
+
summary: (_a = result == null ? void 0 : result.summary) != null ? _a : null,
|
|
1686
|
+
diff,
|
|
1687
|
+
diffSummary: summarizeSpreadsheetPreviewDiff(diff),
|
|
1688
|
+
beforeModel,
|
|
1689
|
+
afterModel,
|
|
1690
|
+
activeSheetName: previewRuntime.activeSheetName()
|
|
1691
|
+
};
|
|
1692
|
+
} finally {
|
|
1693
|
+
await previewRuntime.dispose();
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
async setActiveSheet(sheetName) {
|
|
1697
|
+
const sheet = this.state.model.sheets.find((item) => item.name === sheetName);
|
|
1698
|
+
if (!sheet) throw new Error(`SpreadsheetEditorRuntime: sheet not found: ${sheetName}`);
|
|
1699
|
+
const formulaEngine = new SpreadsheetFormulaEngine(sheet, { sheets: this.state.model.sheets });
|
|
1700
|
+
const formulaResults = formulaEngine.calculate();
|
|
1701
|
+
this.state.activeSheetName = sheet.name;
|
|
1702
|
+
this.state.formulaResults = formulaResults;
|
|
1703
|
+
this.state.formulaEngine = formulaEngine;
|
|
1704
|
+
this.state.activeSheet = buildSpreadsheetSheetRenderModel(sheet, this.state.model.styles, {
|
|
1705
|
+
formulaResults
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
async addSheet(name) {
|
|
1709
|
+
await this.runWorksheetStructureMutation(
|
|
1710
|
+
(tx) => tx.addSheet(name),
|
|
1711
|
+
() => this.state.workbook.worksheets.add(name).then(() => void 0)
|
|
1712
|
+
);
|
|
1713
|
+
await this.setActiveSheet(name);
|
|
1714
|
+
}
|
|
1715
|
+
async renameSheet(sheetName, name) {
|
|
1716
|
+
const wasActive = sheetName === this.state.activeSheetName;
|
|
1717
|
+
await this.runWorksheetStructureMutation(
|
|
1718
|
+
(tx) => tx.renameSheet(sheetName, name),
|
|
1719
|
+
() => this.state.workbook.worksheets.getItem(sheetName).rename(name)
|
|
1720
|
+
);
|
|
1721
|
+
if (wasActive) await this.setActiveSheet(name);
|
|
1722
|
+
}
|
|
1723
|
+
async copySheet(sheetName, newName) {
|
|
1724
|
+
await this.runWorksheetStructureMutation(
|
|
1725
|
+
(tx) => tx.copySheet(sheetName, newName),
|
|
1726
|
+
() => this.state.workbook.worksheets.copy(sheetName, newName).then(() => void 0)
|
|
1727
|
+
);
|
|
1728
|
+
await this.setActiveSheet(newName);
|
|
1729
|
+
}
|
|
1730
|
+
async deleteSheet(sheetName) {
|
|
1731
|
+
var _a;
|
|
1732
|
+
const index = this.state.model.sheets.findIndex((sheet) => sheet.name === sheetName);
|
|
1733
|
+
if (index === -1) throw new Error(`SpreadsheetEditorRuntime: sheet not found: ${sheetName}`);
|
|
1734
|
+
if (this.state.model.sheets.length === 1) {
|
|
1735
|
+
throw new Error("SpreadsheetEditorRuntime: cannot delete the last worksheet");
|
|
1736
|
+
}
|
|
1737
|
+
const nextActive = (_a = this.state.model.sheets[index + 1]) != null ? _a : this.state.model.sheets[index - 1];
|
|
1738
|
+
const wasActive = sheetName === this.state.activeSheetName;
|
|
1739
|
+
await this.runWorksheetStructureMutation(
|
|
1740
|
+
(tx) => tx.deleteSheet(sheetName),
|
|
1741
|
+
() => this.state.workbook.worksheets.deleteItem(sheetName)
|
|
1742
|
+
);
|
|
1743
|
+
if (wasActive && nextActive) await this.setActiveSheet(nextActive.name);
|
|
1744
|
+
}
|
|
1745
|
+
async moveSheet(sheetName, toIndex) {
|
|
1746
|
+
await this.runWorksheetStructureMutation(
|
|
1747
|
+
(tx) => tx.moveSheet(sheetName, toIndex),
|
|
1748
|
+
() => this.state.workbook.worksheets.move(sheetName, toIndex)
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
async refresh() {
|
|
1752
|
+
var _a;
|
|
1753
|
+
const model = await this.state.workbook.toModel();
|
|
1754
|
+
const sheet = (_a = model.sheets.find((item) => item.name === this.state.activeSheetName)) != null ? _a : model.sheets[0];
|
|
1755
|
+
if (!sheet) throw new Error("SpreadsheetEditorRuntime: workbook has no sheets");
|
|
1756
|
+
const formulaEngine = new SpreadsheetFormulaEngine(sheet, { sheets: model.sheets });
|
|
1757
|
+
const formulaResults = formulaEngine.calculate();
|
|
1758
|
+
this.state = __spreadProps(__spreadValues({}, this.state), {
|
|
1759
|
+
model,
|
|
1760
|
+
activeSheetName: sheet.name,
|
|
1761
|
+
formulaResults,
|
|
1762
|
+
formulaEngine,
|
|
1763
|
+
activeSheet: buildSpreadsheetSheetRenderModel(sheet, model.styles, { formulaResults })
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
async setCellValue(address, value, historyOptions = {}) {
|
|
1767
|
+
const targetAddress = this.editableCellAddress(address);
|
|
1768
|
+
await this.runUserMutation(this.cellEditHistoryGroup(`Set ${targetAddress}`, targetAddress, historyOptions), async (tx) => {
|
|
1769
|
+
await tx.setCellValue(this.state.activeSheetName, targetAddress, value);
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
async setCellFormula(address, formula, historyOptions = {}) {
|
|
1773
|
+
const targetAddress = this.editableCellAddress(address);
|
|
1774
|
+
await this.runUserMutation(this.cellEditHistoryGroup(`Set ${targetAddress} formula`, targetAddress, historyOptions), async (tx) => {
|
|
1775
|
+
await tx.setCellFormula(this.state.activeSheetName, targetAddress, formula);
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
async replaceAll(query, replacement, matchCase = false) {
|
|
1779
|
+
const plan = buildSpreadsheetReplacePlan(this.state.activeSheet, { query, replacement, matchCase });
|
|
1780
|
+
if (plan.length === 0) return 0;
|
|
1781
|
+
await this.runUserMutation({ label: `Replace ${query}` }, async (tx) => {
|
|
1782
|
+
var _a;
|
|
1783
|
+
for (const target of plan) {
|
|
1784
|
+
if (target.formula !== void 0) {
|
|
1785
|
+
await tx.setCellFormula(this.state.activeSheetName, target.address, target.formula);
|
|
1786
|
+
} else {
|
|
1787
|
+
await tx.setCellValue(this.state.activeSheetName, target.address, (_a = target.value) != null ? _a : null);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
return plan.length;
|
|
1792
|
+
}
|
|
1793
|
+
async filterRows(startRow, endRow, column, value) {
|
|
1794
|
+
const range = { startRow, startColumn: column, endRow, endColumn: column };
|
|
1795
|
+
const hiddenRows = value === null ? [] : spreadsheetRowsForFilter(this.state.activeSheet, __spreadProps(__spreadValues({}, range), { startColumn: 1, endColumn: this.state.activeSheet.maxColumn }), column, value);
|
|
1796
|
+
await this.runUserMutation({ label: value === null ? "Clear filter" : "Filter rows" }, async (tx) => {
|
|
1797
|
+
await tx.runSnapshot("Filter rows", "spreadsheet row filter", async () => {
|
|
1798
|
+
await this.state.workbook.executeCode(
|
|
1799
|
+
`
|
|
1800
|
+
const sheet = workbook.worksheets.getItem(args.sheetName);
|
|
1801
|
+
const hidden = new Set(args.hiddenRows);
|
|
1802
|
+
for (let index = args.startRow + 1; index <= args.endRow; index += 1) {
|
|
1803
|
+
let row = sheet.model.rows.find((item) => item.index === index);
|
|
1804
|
+
if (hidden.has(index)) {
|
|
1805
|
+
row ??= { index };
|
|
1806
|
+
row.hidden = true;
|
|
1807
|
+
if (!sheet.model.rows.includes(row)) sheet.model.rows.push(row);
|
|
1808
|
+
} else if (row) {
|
|
1809
|
+
delete row.hidden;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
sheet.model.rows.sort((left, right) => left.index - right.index);
|
|
1813
|
+
`,
|
|
1814
|
+
{ sheetName: this.state.activeSheetName, startRow, endRow, hiddenRows }
|
|
1815
|
+
);
|
|
1816
|
+
});
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
async filterRowsByColumns(startRow, endRow, filters) {
|
|
1820
|
+
const range = {
|
|
1821
|
+
startRow,
|
|
1822
|
+
endRow,
|
|
1823
|
+
endColumn: this.state.activeSheet.maxColumn
|
|
1824
|
+
};
|
|
1825
|
+
const hiddenRows = spreadsheetRowsForColumnFilters(this.state.activeSheet, range, filters);
|
|
1826
|
+
await this.runUserMutation({ label: "Filter rows" }, async (tx) => {
|
|
1827
|
+
await tx.runSnapshot("Filter rows", "spreadsheet row filter", async () => {
|
|
1828
|
+
await this.state.workbook.executeCode(
|
|
1829
|
+
`
|
|
1830
|
+
const sheet = workbook.worksheets.getItem(args.sheetName);
|
|
1831
|
+
const hidden = new Set(args.hiddenRows);
|
|
1832
|
+
for (let index = args.startRow + 1; index <= args.endRow; index += 1) {
|
|
1833
|
+
let row = sheet.model.rows.find((item) => item.index === index);
|
|
1834
|
+
if (hidden.has(index)) {
|
|
1835
|
+
row ??= { index };
|
|
1836
|
+
row.hidden = true;
|
|
1837
|
+
if (!sheet.model.rows.includes(row)) sheet.model.rows.push(row);
|
|
1838
|
+
} else if (row) {
|
|
1839
|
+
delete row.hidden;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
sheet.model.rows.sort((left, right) => left.index - right.index);
|
|
1843
|
+
`,
|
|
1844
|
+
{ sheetName: this.state.activeSheetName, startRow, endRow, hiddenRows }
|
|
1845
|
+
);
|
|
1846
|
+
});
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
async sortRows(startRow, endRow, column, direction) {
|
|
1850
|
+
const range = { startRow, endRow, endColumn: this.state.activeSheet.maxColumn };
|
|
1851
|
+
const orderedRows = spreadsheetSortRows(this.state.activeSheet, range, column, direction);
|
|
1852
|
+
const rowMap = new Map(orderedRows.map((sourceRow, index) => [sourceRow, startRow + index + 1]));
|
|
1853
|
+
const formulaUpdates = this.state.activeSheet.source.cells.filter((cell) => cell.formula).map((cell) => {
|
|
1854
|
+
var _a;
|
|
1855
|
+
return [
|
|
1856
|
+
cell.address,
|
|
1857
|
+
rewriteFormulaForRowSort((_a = cell.formula) != null ? _a : "", rowMap, { sheetName: this.state.activeSheetName })
|
|
1858
|
+
];
|
|
1859
|
+
});
|
|
1860
|
+
await this.runUserMutation({ label: direction === "asc" ? "Sort ascending" : "Sort descending" }, async (tx) => {
|
|
1861
|
+
await tx.runSnapshot("Sort rows", "spreadsheet row sort", async () => {
|
|
1862
|
+
await this.state.workbook.executeCode(
|
|
1863
|
+
`
|
|
1864
|
+
const sheet = workbook.worksheets.getItem(args.sheetName);
|
|
1865
|
+
const rowMap = new Map(args.rowMap);
|
|
1866
|
+
const formulas = new Map(args.formulas);
|
|
1867
|
+
function formatCellAddress(row, column) {
|
|
1868
|
+
let value = column;
|
|
1869
|
+
let letters = '';
|
|
1870
|
+
while (value > 0) {
|
|
1871
|
+
const remainder = (value - 1) % 26;
|
|
1872
|
+
letters = String.fromCharCode(65 + remainder) + letters;
|
|
1873
|
+
value = Math.floor((value - 1) / 26);
|
|
1874
|
+
}
|
|
1875
|
+
return letters + row;
|
|
1876
|
+
}
|
|
1877
|
+
for (const cell of sheet.model.cells) {
|
|
1878
|
+
const nextFormula = formulas.get(cell.address);
|
|
1879
|
+
if (nextFormula !== undefined) cell.formula = nextFormula;
|
|
1880
|
+
const nextRow = rowMap.get(cell.row);
|
|
1881
|
+
if (nextRow === undefined) continue;
|
|
1882
|
+
cell.row = nextRow;
|
|
1883
|
+
cell.address = formatCellAddress(cell.row, cell.column);
|
|
1884
|
+
}
|
|
1885
|
+
for (const row of sheet.model.rows) {
|
|
1886
|
+
const nextIndex = rowMap.get(row.index);
|
|
1887
|
+
if (nextIndex !== undefined) row.index = nextIndex;
|
|
1888
|
+
}
|
|
1889
|
+
sheet.model.cells.sort((left, right) => left.row - right.row || left.column - right.column);
|
|
1890
|
+
sheet.model.rows.sort((left, right) => left.index - right.index);
|
|
1891
|
+
`,
|
|
1892
|
+
{ sheetName: this.state.activeSheetName, rowMap: [...rowMap.entries()], formulas: formulaUpdates }
|
|
1893
|
+
);
|
|
1894
|
+
});
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
async setRangeValues(startRow, startColumn, values) {
|
|
1898
|
+
const rowCount = values.length;
|
|
1899
|
+
const columnCount = values.reduce((max, row) => Math.max(max, row.length), 0);
|
|
1900
|
+
const normalized = values.map((row) => {
|
|
1901
|
+
const next = row.slice();
|
|
1902
|
+
while (next.length < columnCount) next.push(null);
|
|
1903
|
+
return next;
|
|
1904
|
+
});
|
|
1905
|
+
await this.runUserMutation({ label: "Set range values" }, async (tx) => {
|
|
1906
|
+
await tx.setRangeValues(
|
|
1907
|
+
this.state.activeSheetName,
|
|
1908
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
1909
|
+
normalized
|
|
1910
|
+
);
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
async autoFillRange(sourceRange, targetRange) {
|
|
1914
|
+
const plan = buildSpreadsheetAutoFillPlan(this.state.activeSheet, sourceRange, targetRange);
|
|
1915
|
+
if (plan.cells.length === 0) return;
|
|
1916
|
+
await this.runUserMutation({ label: "Auto fill" }, async (tx) => {
|
|
1917
|
+
for (const cell of plan.cells) {
|
|
1918
|
+
const address = formatCellAddress(cell.row, cell.column);
|
|
1919
|
+
if (cell.formula) await tx.setCellFormula(this.state.activeSheetName, address, cell.formula);
|
|
1920
|
+
else await tx.setCellValue(this.state.activeSheetName, address, cell.value);
|
|
1921
|
+
await tx.setRangeStyleId(this.state.activeSheetName, address, cell.styleId);
|
|
1922
|
+
}
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
async pasteRangeValues(startRow, startColumn, values, merges = [], styleIds = []) {
|
|
1926
|
+
await this.pasteRange(startRow, startColumn, values, { mode: "all", merges, styleIds });
|
|
1927
|
+
}
|
|
1928
|
+
async pasteRange(startRow, startColumn, values, options = {}) {
|
|
1929
|
+
var _a;
|
|
1930
|
+
const mode = (_a = options.mode) != null ? _a : "all";
|
|
1931
|
+
const rowCount = values.length;
|
|
1932
|
+
const columnCount = values.reduce((max, row) => Math.max(max, row.length), 0);
|
|
1933
|
+
const normalized = values.map((row) => {
|
|
1934
|
+
const next = row.slice();
|
|
1935
|
+
while (next.length < columnCount) next.push(null);
|
|
1936
|
+
return next;
|
|
1937
|
+
});
|
|
1938
|
+
const effectiveRowCount = Math.max(1, rowCount);
|
|
1939
|
+
const effectiveColumnCount = Math.max(1, columnCount);
|
|
1940
|
+
const targetRange = {
|
|
1941
|
+
startRow,
|
|
1942
|
+
startColumn,
|
|
1943
|
+
endRow: startRow + effectiveRowCount - 1,
|
|
1944
|
+
endColumn: startColumn + effectiveColumnCount - 1
|
|
1945
|
+
};
|
|
1946
|
+
const existingMerges = [...this.state.activeSheet.mergeMasterByAddress.values()].filter((merge) => rangesIntersect3(targetRange, merge.range)).map((merge) => merge.address);
|
|
1947
|
+
await this.runUserMutation({ label: pasteHistoryLabel(mode) }, async (tx) => {
|
|
1948
|
+
var _a2, _b;
|
|
1949
|
+
if (mode !== "formats") {
|
|
1950
|
+
for (const address of existingMerges) await tx.unmergeRange(this.state.activeSheetName, address);
|
|
1951
|
+
await tx.setRangeValues(
|
|
1952
|
+
this.state.activeSheetName,
|
|
1953
|
+
formatRangeAddress(startRow, startColumn, effectiveRowCount, effectiveColumnCount),
|
|
1954
|
+
normalized
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
if (mode !== "values") {
|
|
1958
|
+
await applySpreadsheetPasteStyles(
|
|
1959
|
+
tx,
|
|
1960
|
+
this.state.activeSheetName,
|
|
1961
|
+
startRow,
|
|
1962
|
+
startColumn,
|
|
1963
|
+
effectiveColumnCount,
|
|
1964
|
+
(_a2 = options.styleIds) != null ? _a2 : []
|
|
1965
|
+
);
|
|
1966
|
+
}
|
|
1967
|
+
if (mode === "all") await applySpreadsheetPasteMerges(tx, this.state.activeSheetName, startRow, startColumn, (_b = options.merges) != null ? _b : []);
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
async clearRange(startRow, startColumn, rowCount, columnCount) {
|
|
1971
|
+
await this.runUserMutation({ label: "Clear range" }, async (tx) => {
|
|
1972
|
+
await tx.clearRange(
|
|
1973
|
+
this.state.activeSheetName,
|
|
1974
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount)
|
|
1975
|
+
);
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
async setRowHeight(row, height) {
|
|
1979
|
+
await this.runUserMutation({ label: "Set row height" }, async (tx) => {
|
|
1980
|
+
await tx.setRowHeight(this.state.activeSheetName, row, height);
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
async setColumnWidth(column, width) {
|
|
1984
|
+
await this.runUserMutation({ label: "Set column width" }, async (tx) => {
|
|
1985
|
+
await tx.setColumnWidth(this.state.activeSheetName, column, cssPixelsToExcelColumnWidth(width));
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
async setFreezePanes(frozenRows, frozenColumns) {
|
|
1989
|
+
const rows = normalizeFrozenPaneCount(frozenRows);
|
|
1990
|
+
const columns = normalizeFrozenPaneCount(frozenColumns);
|
|
1991
|
+
const views = spreadsheetSheetViewsWithFrozenPane(this.state.activeSheet.source.sheetViews, rows, columns);
|
|
1992
|
+
await this.runUserMutation({ label: rows || columns ? "Freeze panes" : "Unfreeze panes" }, async (tx) => {
|
|
1993
|
+
await tx.setSheetViews(this.state.activeSheetName, views);
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
async insertRows(rowIndex, count = 1) {
|
|
1997
|
+
await this.runUserMutation({ label: "Insert rows" }, async (tx) => {
|
|
1998
|
+
await tx.insertRows(this.state.activeSheetName, rowIndex, count);
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
async deleteRows(rowIndex, count = 1) {
|
|
2002
|
+
await this.runUserMutation({ label: "Delete rows" }, async (tx) => {
|
|
2003
|
+
await tx.deleteRows(this.state.activeSheetName, rowIndex, count);
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
async insertColumns(columnIndex, count = 1) {
|
|
2007
|
+
await this.runUserMutation({ label: "Insert columns" }, async (tx) => {
|
|
2008
|
+
await tx.insertColumns(this.state.activeSheetName, columnIndex, count);
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
async deleteColumns(columnIndex, count = 1) {
|
|
2012
|
+
await this.runUserMutation({ label: "Delete columns" }, async (tx) => {
|
|
2013
|
+
await tx.deleteColumns(this.state.activeSheetName, columnIndex, count);
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
async setRangeBold(startRow, startColumn, rowCount, columnCount, value) {
|
|
2017
|
+
await this.runUserMutation({ label: "Set bold" }, async (tx) => {
|
|
2018
|
+
await tx.setRangeBold(
|
|
2019
|
+
this.state.activeSheetName,
|
|
2020
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2021
|
+
value
|
|
2022
|
+
);
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
async setRangeItalic(startRow, startColumn, rowCount, columnCount, value) {
|
|
2026
|
+
await this.runUserMutation({ label: "Set italic" }, async (tx) => {
|
|
2027
|
+
await tx.setRangeItalic(
|
|
2028
|
+
this.state.activeSheetName,
|
|
2029
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2030
|
+
value
|
|
2031
|
+
);
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
async setRangeUnderline(startRow, startColumn, rowCount, columnCount, value) {
|
|
2035
|
+
await this.runUserMutation({ label: "Set underline" }, async (tx) => {
|
|
2036
|
+
await tx.setRangeUnderline(
|
|
2037
|
+
this.state.activeSheetName,
|
|
2038
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2039
|
+
value
|
|
2040
|
+
);
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
async setRangeFontColor(startRow, startColumn, rowCount, columnCount, color) {
|
|
2044
|
+
await this.runUserMutation({ label: "Set font color" }, async (tx) => {
|
|
2045
|
+
await tx.setRangeFontColor(
|
|
2046
|
+
this.state.activeSheetName,
|
|
2047
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2048
|
+
color
|
|
2049
|
+
);
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
async setRangeFillColor(startRow, startColumn, rowCount, columnCount, color) {
|
|
2053
|
+
await this.runUserMutation({ label: "Set fill color" }, async (tx) => {
|
|
2054
|
+
await tx.setRangeFillColor(
|
|
2055
|
+
this.state.activeSheetName,
|
|
2056
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2057
|
+
color
|
|
2058
|
+
);
|
|
2059
|
+
});
|
|
2060
|
+
}
|
|
2061
|
+
async setRangeHorizontalAlign(startRow, startColumn, rowCount, columnCount, horizontal) {
|
|
2062
|
+
await this.runUserMutation({ label: "Set horizontal align" }, async (tx) => {
|
|
2063
|
+
await tx.setRangeHorizontalAlign(
|
|
2064
|
+
this.state.activeSheetName,
|
|
2065
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2066
|
+
horizontal
|
|
2067
|
+
);
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
async setRangeVerticalAlign(startRow, startColumn, rowCount, columnCount, vertical) {
|
|
2071
|
+
await this.runUserMutation({ label: "Set vertical align" }, async (tx) => {
|
|
2072
|
+
await tx.setRangeVerticalAlign(
|
|
2073
|
+
this.state.activeSheetName,
|
|
2074
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2075
|
+
vertical
|
|
2076
|
+
);
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
async setRangeNumberFormat(startRow, startColumn, rowCount, columnCount, code) {
|
|
2080
|
+
await this.runUserMutation({ label: "Set number format" }, async (tx) => {
|
|
2081
|
+
await tx.setRangeNumberFormat(
|
|
2082
|
+
this.state.activeSheetName,
|
|
2083
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2084
|
+
code
|
|
2085
|
+
);
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
async clearRangeFormat(startRow, startColumn, rowCount, columnCount) {
|
|
2089
|
+
await this.runUserMutation({ label: "Clear format" }, async (tx) => {
|
|
2090
|
+
await tx.clearRangeFormat(
|
|
2091
|
+
this.state.activeSheetName,
|
|
2092
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount)
|
|
2093
|
+
);
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
async mergeRange(startRow, startColumn, rowCount, columnCount) {
|
|
2097
|
+
const address = formatRangeAddress(startRow, startColumn, rowCount, columnCount);
|
|
2098
|
+
const masterStyleId = await this.cellStyleId(formatCellAddress(startRow, startColumn));
|
|
2099
|
+
await this.runUserMutation({ label: "Merge range" }, async (tx) => {
|
|
2100
|
+
await tx.setRangeStyleId(this.state.activeSheetName, address, masterStyleId);
|
|
2101
|
+
await tx.mergeRange(this.state.activeSheetName, address);
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
async unmergeRanges(addresses) {
|
|
2105
|
+
if (addresses.length === 0) return;
|
|
2106
|
+
const merges = addresses.map((address) => {
|
|
2107
|
+
const merge = [...this.state.activeSheet.mergeMasterByAddress.values()].find((item) => item.address === address);
|
|
2108
|
+
const masterAddress = merge ? formatCellAddress(merge.range.startRow, merge.range.startColumn) : masterAddressForRange(address);
|
|
2109
|
+
return { address, masterAddress };
|
|
2110
|
+
});
|
|
2111
|
+
await this.runUserMutation({ label: "Unmerge ranges" }, async (tx) => {
|
|
2112
|
+
for (const merge of merges) {
|
|
2113
|
+
await tx.setRangeStyleId(this.state.activeSheetName, merge.address, await this.cellStyleId(merge.masterAddress));
|
|
2114
|
+
await tx.unmergeRange(this.state.activeSheetName, merge.address);
|
|
2115
|
+
}
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
async setRangeBorder(startRow, startColumn, rowCount, columnCount, mode) {
|
|
2119
|
+
if (mode === "clear") {
|
|
2120
|
+
await this.runUserMutation({ label: "Clear border" }, async (tx) => {
|
|
2121
|
+
await tx.setRangeBorder(
|
|
2122
|
+
this.state.activeSheetName,
|
|
2123
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2124
|
+
{}
|
|
2125
|
+
);
|
|
2126
|
+
});
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
const side = spreadsheetDefaultBorderSide();
|
|
2130
|
+
if (mode === "all") {
|
|
2131
|
+
await this.runUserMutation({ label: "Set all borders" }, async (tx) => {
|
|
2132
|
+
await tx.setRangeBorder(
|
|
2133
|
+
this.state.activeSheetName,
|
|
2134
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2135
|
+
{ left: side, right: side, top: side, bottom: side }
|
|
2136
|
+
);
|
|
2137
|
+
});
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
await this.runUserMutation({ label: `Set ${mode} borders` }, async (tx) => {
|
|
2141
|
+
await tx.setRangeBorder(
|
|
2142
|
+
this.state.activeSheetName,
|
|
2143
|
+
formatRangeAddress(startRow, startColumn, rowCount, columnCount),
|
|
2144
|
+
{}
|
|
2145
|
+
);
|
|
2146
|
+
for (let rowOffset = 0; rowOffset < rowCount; rowOffset++) {
|
|
2147
|
+
for (let columnOffset = 0; columnOffset < columnCount; columnOffset++) {
|
|
2148
|
+
const border = {};
|
|
2149
|
+
if (mode === "outer") {
|
|
2150
|
+
if (rowOffset === 0) border.top = side;
|
|
2151
|
+
if (rowOffset === rowCount - 1) border.bottom = side;
|
|
2152
|
+
if (columnOffset === 0) border.left = side;
|
|
2153
|
+
if (columnOffset === columnCount - 1) border.right = side;
|
|
2154
|
+
} else {
|
|
2155
|
+
if (columnOffset < columnCount - 1) border.right = side;
|
|
2156
|
+
if (rowOffset < rowCount - 1) border.bottom = side;
|
|
2157
|
+
}
|
|
2158
|
+
if (Object.keys(border).length === 0) continue;
|
|
2159
|
+
await tx.setRangeBorder(
|
|
2160
|
+
this.state.activeSheetName,
|
|
2161
|
+
formatCellAddress(startRow + rowOffset, startColumn + columnOffset),
|
|
2162
|
+
border
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
async toZip() {
|
|
2169
|
+
return this.state.workbook.toZip();
|
|
2170
|
+
}
|
|
2171
|
+
async moveChart(chartIndex, position) {
|
|
2172
|
+
await this.runUserMutation({ label: "Move chart" }, async (tx) => {
|
|
2173
|
+
await tx.runSnapshot("Move chart", "spreadsheet chart position", async () => {
|
|
2174
|
+
const sheetModel = this.state.model.sheets.find((sheet) => sheet.name === this.state.activeSheetName);
|
|
2175
|
+
const chart = sheetModel == null ? void 0 : sheetModel.charts[chartIndex];
|
|
2176
|
+
if (!sheetModel || !chart) return;
|
|
2177
|
+
const drawingIndex = sheetModel.drawings.findIndex((drawing2) => drawing2.path === chart.drawingPath);
|
|
2178
|
+
const drawing = drawingIndex >= 0 ? sheetModel.drawings[drawingIndex] : sheetModel.drawings[0];
|
|
2179
|
+
if (!drawing) return;
|
|
2180
|
+
const anchorIndex = drawing.anchors.findIndex(
|
|
2181
|
+
(anchor2) => {
|
|
2182
|
+
var _a;
|
|
2183
|
+
return ((_a = anchor2.object) == null ? void 0 : _a.kind) === "chart" && (!chart.relationshipId || anchor2.object.relationshipId === chart.relationshipId);
|
|
2184
|
+
}
|
|
2185
|
+
);
|
|
2186
|
+
if (anchorIndex < 0) return;
|
|
2187
|
+
const drawings = await this.state.workbook.worksheets.getItem(this.state.activeSheetName).drawings.items();
|
|
2188
|
+
const coreDrawing = drawings[drawingIndex >= 0 ? drawingIndex : 0];
|
|
2189
|
+
const anchor = coreDrawing == null ? void 0 : coreDrawing.anchors.getItem(anchorIndex);
|
|
2190
|
+
if (!anchor) return;
|
|
2191
|
+
anchor.from.setRow(position.from.row);
|
|
2192
|
+
anchor.from.setColumn(position.from.column);
|
|
2193
|
+
anchor.from.setRowOffset(position.from.rowOffset);
|
|
2194
|
+
anchor.from.setColumnOffset(position.from.columnOffset);
|
|
2195
|
+
if (position.to) {
|
|
2196
|
+
anchor.to.setRow(position.to.row);
|
|
2197
|
+
anchor.to.setColumn(position.to.column);
|
|
2198
|
+
anchor.to.setRowOffset(position.to.rowOffset);
|
|
2199
|
+
anchor.to.setColumnOffset(position.to.columnOffset);
|
|
2200
|
+
}
|
|
2201
|
+
});
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
async dispose() {
|
|
2205
|
+
await this.state.workbook.dispose();
|
|
2206
|
+
}
|
|
2207
|
+
editableCellAddress(address) {
|
|
2208
|
+
var _a;
|
|
2209
|
+
const cell = parseCellAddress(address);
|
|
2210
|
+
const normalized = formatCellAddress(cell.row, cell.column);
|
|
2211
|
+
return (_a = this.state.activeSheet.mergeFollowerByAddress.get(normalized)) != null ? _a : normalized;
|
|
2212
|
+
}
|
|
2213
|
+
rebuildActiveSheetRenderModel() {
|
|
2214
|
+
this.state.formulaResults = this.state.formulaEngine.calculate();
|
|
2215
|
+
this.state.activeSheet = buildSpreadsheetSheetRenderModel(
|
|
2216
|
+
this.state.activeSheet.source,
|
|
2217
|
+
this.state.model.styles,
|
|
2218
|
+
{ formulaResults: this.state.formulaResults }
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
async runUserMutation(options, mutate) {
|
|
2222
|
+
if (this.activeTransaction) {
|
|
2223
|
+
await mutate(this.activeTransaction);
|
|
2224
|
+
this.transactionNeedsRefresh = true;
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
await this.runScopedTransaction(
|
|
2228
|
+
"user",
|
|
2229
|
+
(apply) => this.state.workbook.history().runGroup(__spreadProps(__spreadValues({}, options), { source: "user" }), apply),
|
|
2230
|
+
async () => {
|
|
2231
|
+
if (!this.activeTransaction) throw new Error("SpreadsheetEditorRuntime: transaction not active");
|
|
2232
|
+
await mutate(this.activeTransaction);
|
|
2233
|
+
this.transactionNeedsRefresh = true;
|
|
2234
|
+
}
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
2237
|
+
async runWorksheetStructureMutation(recordAgentMutation, mutate) {
|
|
2238
|
+
if (this.activeTransactionSource === "agent" && this.activeTransaction) {
|
|
2239
|
+
await recordAgentMutation(this.activeTransaction);
|
|
2240
|
+
this.transactionNeedsRefresh = true;
|
|
2241
|
+
await this.refresh();
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
await mutate();
|
|
2245
|
+
await this.refresh();
|
|
2246
|
+
}
|
|
2247
|
+
async cellStyleId(address) {
|
|
2248
|
+
var _a;
|
|
2249
|
+
return (_a = (await this.state.workbook.worksheets.getItem(this.state.activeSheetName).getRange(address).styleIds())[0]) == null ? void 0 : _a[0];
|
|
2250
|
+
}
|
|
2251
|
+
async runScopedTransaction(source, execute, run) {
|
|
2252
|
+
if (this.activeTransaction) {
|
|
2253
|
+
await run();
|
|
2254
|
+
this.transactionNeedsRefresh = true;
|
|
2255
|
+
return null;
|
|
2256
|
+
}
|
|
2257
|
+
const previousNeedsRefresh = this.transactionNeedsRefresh;
|
|
2258
|
+
this.transactionNeedsRefresh = false;
|
|
2259
|
+
try {
|
|
2260
|
+
const result = await execute(async (transaction) => {
|
|
2261
|
+
this.activeTransaction = transaction;
|
|
2262
|
+
this.activeTransactionSource = source;
|
|
2263
|
+
try {
|
|
2264
|
+
await run();
|
|
2265
|
+
} finally {
|
|
2266
|
+
this.activeTransaction = null;
|
|
2267
|
+
this.activeTransactionSource = null;
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
const shouldRefresh = this.transactionNeedsRefresh || result !== null;
|
|
2271
|
+
if (shouldRefresh) await this.refresh();
|
|
2272
|
+
return result;
|
|
2273
|
+
} finally {
|
|
2274
|
+
this.activeTransaction = null;
|
|
2275
|
+
this.activeTransactionSource = null;
|
|
2276
|
+
this.transactionNeedsRefresh = previousNeedsRefresh;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
cellEditHistoryGroup(label, address, options) {
|
|
2280
|
+
var _a;
|
|
2281
|
+
const base = { label };
|
|
2282
|
+
if (!options.mergeUndo) return base;
|
|
2283
|
+
return __spreadProps(__spreadValues({}, base), {
|
|
2284
|
+
mergeKey: `edit:${this.state.activeSheetName}!${address}`,
|
|
2285
|
+
mergeWindowMs: (_a = options.mergeWindowMs) != null ? _a : 1e3
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2288
|
+
range(startRow, startColumn, rowCount, columnCount) {
|
|
2289
|
+
return this.state.workbook.worksheets.getItem(this.state.activeSheetName).getRange(formatRangeAddress(startRow, startColumn, rowCount, columnCount));
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
2292
|
+
__name(_SpreadsheetEditorRuntime, "SpreadsheetEditorRuntime");
|
|
2293
|
+
var SpreadsheetEditorRuntime = _SpreadsheetEditorRuntime;
|
|
2294
|
+
function spreadsheetDefaultBorderSide() {
|
|
2295
|
+
return { style: "thin", color: { rgb: "#111827" } };
|
|
2296
|
+
}
|
|
2297
|
+
__name(spreadsheetDefaultBorderSide, "spreadsheetDefaultBorderSide");
|
|
2298
|
+
function normalizeFrozenPaneCount(value) {
|
|
2299
|
+
if (!Number.isFinite(value)) return 0;
|
|
2300
|
+
return Math.max(0, Math.floor(value));
|
|
2301
|
+
}
|
|
2302
|
+
__name(normalizeFrozenPaneCount, "normalizeFrozenPaneCount");
|
|
2303
|
+
function spreadsheetSheetViewsWithFrozenPane(sheetViews, frozenRows, frozenColumns) {
|
|
2304
|
+
var _a;
|
|
2305
|
+
if (!frozenRows && !frozenColumns) {
|
|
2306
|
+
return sheetViews.map((view, index) => __spreadProps(__spreadValues({}, view), {
|
|
2307
|
+
pane: index === 0 ? void 0 : view.pane ? __spreadValues({}, view.pane) : void 0,
|
|
2308
|
+
selections: view.selections.map((selection) => __spreadValues({}, selection))
|
|
2309
|
+
}));
|
|
2310
|
+
}
|
|
2311
|
+
const currentView = (_a = sheetViews[0]) != null ? _a : { workbookViewId: 0, selections: [] };
|
|
2312
|
+
const pane = {
|
|
2313
|
+
xSplit: frozenColumns || void 0,
|
|
2314
|
+
ySplit: frozenRows || void 0,
|
|
2315
|
+
topLeftCell: formatCellAddress(frozenRows + 1, frozenColumns + 1),
|
|
2316
|
+
activePane: frozenRows && frozenColumns ? "bottomRight" : frozenRows ? "bottomLeft" : "topRight",
|
|
2317
|
+
state: "frozen"
|
|
2318
|
+
};
|
|
2319
|
+
return [
|
|
2320
|
+
__spreadProps(__spreadValues({}, currentView), {
|
|
2321
|
+
pane,
|
|
2322
|
+
selections: currentView.selections.map((selection) => __spreadValues({}, selection))
|
|
2323
|
+
}),
|
|
2324
|
+
...sheetViews.slice(1).map((view) => __spreadProps(__spreadValues({}, view), {
|
|
2325
|
+
pane: view.pane ? __spreadValues({}, view.pane) : void 0,
|
|
2326
|
+
selections: view.selections.map((selection) => __spreadValues({}, selection))
|
|
2327
|
+
}))
|
|
2328
|
+
];
|
|
2329
|
+
}
|
|
2330
|
+
__name(spreadsheetSheetViewsWithFrozenPane, "spreadsheetSheetViewsWithFrozenPane");
|
|
2331
|
+
async function applySpreadsheetPasteStyles(tx, sheetName, startRow, startColumn, columnCount, styleIds) {
|
|
2332
|
+
for (let rowOffset = 0; rowOffset < styleIds.length; rowOffset++) {
|
|
2333
|
+
const styleRow = styleIds[rowOffset];
|
|
2334
|
+
if (!styleRow) continue;
|
|
2335
|
+
for (let columnOffset = 0; columnOffset < columnCount; columnOffset++) {
|
|
2336
|
+
await tx.setRangeStyleId(
|
|
2337
|
+
sheetName,
|
|
2338
|
+
formatCellAddress(startRow + rowOffset, startColumn + columnOffset),
|
|
2339
|
+
styleRow[columnOffset]
|
|
2340
|
+
);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
__name(applySpreadsheetPasteStyles, "applySpreadsheetPasteStyles");
|
|
2345
|
+
async function applySpreadsheetPasteMerges(tx, sheetName, startRow, startColumn, merges) {
|
|
2346
|
+
for (const merge of merges) {
|
|
2347
|
+
await tx.mergeRange(
|
|
2348
|
+
sheetName,
|
|
2349
|
+
formatRangeAddress(
|
|
2350
|
+
startRow + merge.startRowOffset,
|
|
2351
|
+
startColumn + merge.startColumnOffset,
|
|
2352
|
+
merge.rowCount,
|
|
2353
|
+
merge.columnCount
|
|
2354
|
+
)
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
__name(applySpreadsheetPasteMerges, "applySpreadsheetPasteMerges");
|
|
2359
|
+
function pasteHistoryLabel(mode) {
|
|
2360
|
+
switch (mode) {
|
|
2361
|
+
case "values":
|
|
2362
|
+
return "Paste values";
|
|
2363
|
+
case "formats":
|
|
2364
|
+
return "Paste formats";
|
|
2365
|
+
default:
|
|
2366
|
+
return "Paste range";
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
__name(pasteHistoryLabel, "pasteHistoryLabel");
|
|
2370
|
+
function cloneWorkbookModel(model) {
|
|
2371
|
+
return JSON.parse(JSON.stringify(model));
|
|
2372
|
+
}
|
|
2373
|
+
__name(cloneWorkbookModel, "cloneWorkbookModel");
|
|
2374
|
+
function summarizeSpreadsheetPreviewDiff(diff) {
|
|
2375
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2376
|
+
const cellChanges = {
|
|
2377
|
+
total: 0,
|
|
2378
|
+
added: 0,
|
|
2379
|
+
modified: 0,
|
|
2380
|
+
deleted: 0
|
|
2381
|
+
};
|
|
2382
|
+
const sampleCells = [];
|
|
2383
|
+
for (const sheet of diff.sheets) {
|
|
2384
|
+
for (const cell of sheet.cells) {
|
|
2385
|
+
cellChanges.total += 1;
|
|
2386
|
+
cellChanges[cell.kind] += 1;
|
|
2387
|
+
if (sampleCells.length >= 20) continue;
|
|
2388
|
+
sampleCells.push({
|
|
2389
|
+
sheetName: (_b = (_a = sheet.afterName) != null ? _a : sheet.beforeName) != null ? _b : sheet.sheetId,
|
|
2390
|
+
address: cell.address,
|
|
2391
|
+
kind: cell.kind,
|
|
2392
|
+
before: (_d = (_c = cell.before) == null ? void 0 : _c.value) != null ? _d : null,
|
|
2393
|
+
after: (_f = (_e = cell.after) == null ? void 0 : _e.value) != null ? _f : null
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
return {
|
|
2398
|
+
changed: diff.changed,
|
|
2399
|
+
changedSheets: diff.sheets.length,
|
|
2400
|
+
sheetNames: diff.sheets.map((sheet) => {
|
|
2401
|
+
var _a2, _b2;
|
|
2402
|
+
return (_b2 = (_a2 = sheet.afterName) != null ? _a2 : sheet.beforeName) != null ? _b2 : sheet.sheetId;
|
|
2403
|
+
}),
|
|
2404
|
+
cellChanges,
|
|
2405
|
+
structuralChanged: diff.sheets.some((sheet) => sheet.structuralChanged),
|
|
2406
|
+
objectChanged: diff.sheets.some((sheet) => sheet.objectChanged),
|
|
2407
|
+
sampleCells
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
__name(summarizeSpreadsheetPreviewDiff, "summarizeSpreadsheetPreviewDiff");
|
|
2411
|
+
function rangesIntersect3(a, b) {
|
|
2412
|
+
return !(a.endRow < b.startRow || a.startRow > b.endRow || a.endColumn < b.startColumn || a.startColumn > b.endColumn);
|
|
2413
|
+
}
|
|
2414
|
+
__name(rangesIntersect3, "rangesIntersect");
|
|
2415
|
+
function masterAddressForRange(address) {
|
|
2416
|
+
const range = parseRangeAddress(address);
|
|
2417
|
+
return formatCellAddress(range.startRow, range.startColumn);
|
|
2418
|
+
}
|
|
2419
|
+
__name(masterAddressForRange, "masterAddressForRange");
|
|
2420
|
+
|
|
2421
|
+
// src/spreadsheet/agent-action.ts
|
|
2422
|
+
async function previewSpreadsheetAgentAction(runtime, action, context = {}) {
|
|
2423
|
+
return {
|
|
2424
|
+
action,
|
|
2425
|
+
context,
|
|
2426
|
+
transaction: await runtime.previewAgentTransaction(
|
|
2427
|
+
agentActionTransactionOptions(action),
|
|
2428
|
+
(targetRuntime) => action.run(targetRuntime, context)
|
|
2429
|
+
)
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
__name(previewSpreadsheetAgentAction, "previewSpreadsheetAgentAction");
|
|
2433
|
+
async function applySpreadsheetAgentAction(runtime, action, context = {}) {
|
|
2434
|
+
return {
|
|
2435
|
+
action,
|
|
2436
|
+
context,
|
|
2437
|
+
transaction: await runtime.runAgentTransaction(
|
|
2438
|
+
agentActionTransactionOptions(action),
|
|
2439
|
+
(targetRuntime) => action.run(targetRuntime, context)
|
|
2440
|
+
)
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
__name(applySpreadsheetAgentAction, "applySpreadsheetAgentAction");
|
|
2444
|
+
function agentActionTransactionOptions(action) {
|
|
2445
|
+
var _a;
|
|
2446
|
+
return {
|
|
2447
|
+
label: action.label,
|
|
2448
|
+
actor: action.actor,
|
|
2449
|
+
description: action.description,
|
|
2450
|
+
agentRunId: action.agentRunId,
|
|
2451
|
+
agentStepId: action.agentStepId,
|
|
2452
|
+
agentActionId: (_a = action.agentActionId) != null ? _a : action.id,
|
|
2453
|
+
metadata: action.metadata
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
__name(agentActionTransactionOptions, "agentActionTransactionOptions");
|
|
2457
|
+
|
|
2458
|
+
// src/spreadsheet/editing.ts
|
|
2459
|
+
var _SpreadsheetCellEditor = class _SpreadsheetCellEditor {
|
|
2460
|
+
constructor(options) {
|
|
2461
|
+
__publicField(this, "options", options);
|
|
2462
|
+
this.options.textarea.addEventListener("keydown", (event) => {
|
|
2463
|
+
if (event.key === "Escape") {
|
|
2464
|
+
event.preventDefault();
|
|
2465
|
+
this.cancel();
|
|
2466
|
+
} else if (event.key === "Enter" && !event.shiftKey) {
|
|
2467
|
+
event.preventDefault();
|
|
2468
|
+
void this.commit();
|
|
2469
|
+
}
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
open(rect, value) {
|
|
2473
|
+
const textarea = this.options.textarea;
|
|
2474
|
+
textarea.value = value;
|
|
2475
|
+
textarea.style.display = "block";
|
|
2476
|
+
textarea.style.position = "absolute";
|
|
2477
|
+
textarea.style.left = `${rect.x}px`;
|
|
2478
|
+
textarea.style.top = `${rect.y}px`;
|
|
2479
|
+
textarea.style.width = `${Math.max(1, rect.width)}px`;
|
|
2480
|
+
textarea.style.height = `${Math.max(1, rect.height)}px`;
|
|
2481
|
+
textarea.focus();
|
|
2482
|
+
textarea.select();
|
|
2483
|
+
}
|
|
2484
|
+
async commit() {
|
|
2485
|
+
const value = this.options.textarea.value;
|
|
2486
|
+
this.close();
|
|
2487
|
+
await this.options.onCommit(value);
|
|
2488
|
+
}
|
|
2489
|
+
cancel() {
|
|
2490
|
+
var _a, _b;
|
|
2491
|
+
this.close();
|
|
2492
|
+
(_b = (_a = this.options).onCancel) == null ? void 0 : _b.call(_a);
|
|
2493
|
+
}
|
|
2494
|
+
close() {
|
|
2495
|
+
this.options.textarea.style.display = "none";
|
|
2496
|
+
}
|
|
2497
|
+
};
|
|
2498
|
+
__name(_SpreadsheetCellEditor, "SpreadsheetCellEditor");
|
|
2499
|
+
var SpreadsheetCellEditor = _SpreadsheetCellEditor;
|
|
2500
|
+
|
|
2501
|
+
// src/spreadsheet/scrollbar.ts
|
|
2502
|
+
function applyWheelDelta(scroll, deltaX, deltaY) {
|
|
2503
|
+
return {
|
|
2504
|
+
x: Math.max(0, scroll.x + deltaX),
|
|
2505
|
+
y: Math.max(0, scroll.y + deltaY)
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
__name(applyWheelDelta, "applyWheelDelta");
|
|
2509
|
+
function computeScrollbarState(layout, viewportSize, freeze, scroll, scrollbarThickness = 12) {
|
|
2510
|
+
const frozenWidth = layout.getColumnX(freeze.frozenColumns + 1);
|
|
2511
|
+
const frozenHeight = layout.getRowY(freeze.frozenRows + 1);
|
|
2512
|
+
const horizontalTrackLength = Math.max(
|
|
2513
|
+
0,
|
|
2514
|
+
viewportSize.width - layout.rowHeaderWidth - frozenWidth - scrollbarThickness
|
|
2515
|
+
);
|
|
2516
|
+
const verticalTrackLength = Math.max(
|
|
2517
|
+
0,
|
|
2518
|
+
viewportSize.height - layout.columnHeaderHeight - frozenHeight - scrollbarThickness
|
|
2519
|
+
);
|
|
2520
|
+
const horizontalViewport = horizontalTrackLength;
|
|
2521
|
+
const verticalViewport = verticalTrackLength;
|
|
2522
|
+
const horizontalContent = Math.max(0, layout.totalWidth() - frozenWidth);
|
|
2523
|
+
const verticalContent = Math.max(0, layout.totalHeight() - frozenHeight);
|
|
2524
|
+
return {
|
|
2525
|
+
horizontal: computeAxisScrollbar(horizontalContent, horizontalViewport, horizontalTrackLength, scroll.x),
|
|
2526
|
+
vertical: computeAxisScrollbar(verticalContent, verticalViewport, verticalTrackLength, scroll.y)
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
__name(computeScrollbarState, "computeScrollbarState");
|
|
2530
|
+
function scrollFromThumbPosition(geometry, thumbStart) {
|
|
2531
|
+
if (geometry.maxScroll <= 0) return 0;
|
|
2532
|
+
const maxThumbStart = Math.max(0, geometry.trackLength - geometry.thumbLength);
|
|
2533
|
+
if (maxThumbStart <= 0) return 0;
|
|
2534
|
+
const clamped = Math.max(0, Math.min(maxThumbStart, thumbStart));
|
|
2535
|
+
return clamped / maxThumbStart * geometry.maxScroll;
|
|
2536
|
+
}
|
|
2537
|
+
__name(scrollFromThumbPosition, "scrollFromThumbPosition");
|
|
2538
|
+
function computeAxisScrollbar(contentLength, viewportLength, trackLength, scrollOffset) {
|
|
2539
|
+
const maxScroll = Math.max(0, contentLength - viewportLength);
|
|
2540
|
+
if (trackLength <= 0 || contentLength <= 0 || maxScroll <= 0) {
|
|
2541
|
+
return { trackLength, thumbStart: 0, thumbLength: trackLength, maxScroll: 0 };
|
|
2542
|
+
}
|
|
2543
|
+
const thumbLength = Math.max(24, Math.min(trackLength, viewportLength / contentLength * trackLength));
|
|
2544
|
+
const maxThumbStart = Math.max(0, trackLength - thumbLength);
|
|
2545
|
+
const thumbStart = maxScroll <= 0 ? 0 : Math.max(0, Math.min(maxScroll, scrollOffset)) / maxScroll * maxThumbStart;
|
|
2546
|
+
return { trackLength, thumbStart, thumbLength, maxScroll };
|
|
2547
|
+
}
|
|
2548
|
+
__name(computeAxisScrollbar, "computeAxisScrollbar");
|
|
2549
|
+
|
|
2550
|
+
// src/spreadsheet/resize.ts
|
|
2551
|
+
var SPREADSHEET_COLUMN_RESIZE_MIN = 24;
|
|
2552
|
+
var SPREADSHEET_ROW_RESIZE_MIN = 12;
|
|
2553
|
+
var SPREADSHEET_RESIZE_HIT_TOLERANCE = 4;
|
|
2554
|
+
function hitSpreadsheetResizeHandle(layout, viewports, point) {
|
|
2555
|
+
for (const viewport of viewports) {
|
|
2556
|
+
if (viewport.kind === "columnHeaderFrozen" || viewport.kind === "columnHeaderScrollable") {
|
|
2557
|
+
const handle = hitSpreadsheetColumnResizeHandle(layout, viewport, point);
|
|
2558
|
+
if (handle) return handle;
|
|
2559
|
+
}
|
|
2560
|
+
if (viewport.kind === "rowHeaderFrozen" || viewport.kind === "rowHeaderScrollable") {
|
|
2561
|
+
const handle = hitSpreadsheetRowResizeHandle(layout, viewport, point);
|
|
2562
|
+
if (handle) return handle;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
__name(hitSpreadsheetResizeHandle, "hitSpreadsheetResizeHandle");
|
|
2568
|
+
function startSpreadsheetResizeDrag(handle, event) {
|
|
2569
|
+
return __spreadProps(__spreadValues({}, handle), {
|
|
2570
|
+
startClientX: event.clientX,
|
|
2571
|
+
startClientY: event.clientY,
|
|
2572
|
+
currentSize: handle.startSize,
|
|
2573
|
+
currentGuidePosition: handle.guidePosition
|
|
2574
|
+
});
|
|
2575
|
+
}
|
|
2576
|
+
__name(startSpreadsheetResizeDrag, "startSpreadsheetResizeDrag");
|
|
2577
|
+
function updateSpreadsheetResizeDrag(drag, event) {
|
|
2578
|
+
const delta = drag.kind === "column" ? event.clientX - drag.startClientX : event.clientY - drag.startClientY;
|
|
2579
|
+
const currentSize = Math.max(drag.minSize, Math.round(drag.startSize + delta));
|
|
2580
|
+
return __spreadProps(__spreadValues({}, drag), {
|
|
2581
|
+
currentSize,
|
|
2582
|
+
currentGuidePosition: drag.guidePosition + currentSize - drag.startSize
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
__name(updateSpreadsheetResizeDrag, "updateSpreadsheetResizeDrag");
|
|
2586
|
+
function hitSpreadsheetColumnResizeHandle(layout, viewport, point) {
|
|
2587
|
+
if (!containsSpreadsheetPoint(viewport.screenRect, point, SPREADSHEET_RESIZE_HIT_TOLERANCE)) return null;
|
|
2588
|
+
for (let column = viewport.columns.start; column <= viewport.columns.end; column++) {
|
|
2589
|
+
const logical = layout.getCellLogicalRect(1, column);
|
|
2590
|
+
const left = viewport.screenRect.x + logical.x - viewport.originX;
|
|
2591
|
+
const right = left + logical.width;
|
|
2592
|
+
if (Math.abs(point.x - right) <= SPREADSHEET_RESIZE_HIT_TOLERANCE) {
|
|
2593
|
+
return {
|
|
2594
|
+
kind: "column",
|
|
2595
|
+
index: column,
|
|
2596
|
+
startSize: layout.getColumnWidth(column),
|
|
2597
|
+
guidePosition: right,
|
|
2598
|
+
minSize: SPREADSHEET_COLUMN_RESIZE_MIN
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
return null;
|
|
2603
|
+
}
|
|
2604
|
+
__name(hitSpreadsheetColumnResizeHandle, "hitSpreadsheetColumnResizeHandle");
|
|
2605
|
+
function hitSpreadsheetRowResizeHandle(layout, viewport, point) {
|
|
2606
|
+
if (!containsSpreadsheetPoint(viewport.screenRect, point, SPREADSHEET_RESIZE_HIT_TOLERANCE)) return null;
|
|
2607
|
+
for (let row = viewport.rows.start; row <= viewport.rows.end; row++) {
|
|
2608
|
+
const logical = layout.getCellLogicalRect(row, 1);
|
|
2609
|
+
const top = viewport.screenRect.y + logical.y - viewport.originY;
|
|
2610
|
+
const bottom = top + logical.height;
|
|
2611
|
+
if (Math.abs(point.y - bottom) <= SPREADSHEET_RESIZE_HIT_TOLERANCE) {
|
|
2612
|
+
return {
|
|
2613
|
+
kind: "row",
|
|
2614
|
+
index: row,
|
|
2615
|
+
startSize: layout.getRowHeight(row),
|
|
2616
|
+
guidePosition: bottom,
|
|
2617
|
+
minSize: SPREADSHEET_ROW_RESIZE_MIN
|
|
2618
|
+
};
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
return null;
|
|
2622
|
+
}
|
|
2623
|
+
__name(hitSpreadsheetRowResizeHandle, "hitSpreadsheetRowResizeHandle");
|
|
2624
|
+
function containsSpreadsheetPoint(rect, point, tolerance = 0) {
|
|
2625
|
+
return point.x >= rect.x - tolerance && point.y >= rect.y - tolerance && point.x <= rect.x + rect.width + tolerance && point.y <= rect.y + rect.height + tolerance;
|
|
2626
|
+
}
|
|
2627
|
+
__name(containsSpreadsheetPoint, "containsSpreadsheetPoint");
|
|
2628
|
+
|
|
2629
|
+
// src/spreadsheet/format-state.ts
|
|
2630
|
+
function summarizeSpreadsheetSelectionFormat(model, selection) {
|
|
2631
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2632
|
+
const summary = createEmptyFormatSummary();
|
|
2633
|
+
const range = normalizeSelectionRange(selection.range);
|
|
2634
|
+
for (let row = range.startRow; row <= range.endRow; row++) {
|
|
2635
|
+
for (let column = range.startColumn; column <= range.endColumn; column++) {
|
|
2636
|
+
const format = cellFormatAt(model, row, column);
|
|
2637
|
+
const font = (_b = model.styles.fonts[(_a = format.fontId) != null ? _a : 0]) != null ? _b : {};
|
|
2638
|
+
const fill = (_d = model.styles.fills[(_c = format.fillId) != null ? _c : 0]) != null ? _d : {};
|
|
2639
|
+
const border = (_f = model.styles.borders[(_e = format.borderId) != null ? _e : 0]) != null ? _f : {};
|
|
2640
|
+
collectSummaryValue(summary.bold, font.bold === true);
|
|
2641
|
+
collectSummaryValue(summary.italic, font.italic === true);
|
|
2642
|
+
collectSummaryValue(summary.underline, font.underline === true);
|
|
2643
|
+
collectSummaryValue(summary.fontColor, font.color);
|
|
2644
|
+
collectSummaryValue(summary.fillColor, fill.foregroundColor);
|
|
2645
|
+
collectSummaryValue(summary.numberFormatCode, format.numberFormatCode);
|
|
2646
|
+
collectSummaryValue(summary.horizontal, normalizeHorizontalAlign(format));
|
|
2647
|
+
collectSummaryValue(summary.vertical, normalizeVerticalAlign(format));
|
|
2648
|
+
collectSummaryValue(summary.border, border);
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
return {
|
|
2652
|
+
font: {
|
|
2653
|
+
bold: summaryValue(summary.bold),
|
|
2654
|
+
italic: summaryValue(summary.italic),
|
|
2655
|
+
underline: summaryValue(summary.underline),
|
|
2656
|
+
color: summaryValue(summary.fontColor)
|
|
2657
|
+
},
|
|
2658
|
+
fill: {
|
|
2659
|
+
foregroundColor: summaryValue(summary.fillColor)
|
|
2660
|
+
},
|
|
2661
|
+
numberFormatCode: summaryValue(summary.numberFormatCode),
|
|
2662
|
+
alignment: {
|
|
2663
|
+
horizontal: summaryValue(summary.horizontal),
|
|
2664
|
+
vertical: summaryValue(summary.vertical)
|
|
2665
|
+
},
|
|
2666
|
+
border: summaryValue(summary.border)
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
__name(summarizeSpreadsheetSelectionFormat, "summarizeSpreadsheetSelectionFormat");
|
|
2670
|
+
function createEmptyFormatSummary() {
|
|
2671
|
+
return {
|
|
2672
|
+
bold: emptySummary(),
|
|
2673
|
+
italic: emptySummary(),
|
|
2674
|
+
underline: emptySummary(),
|
|
2675
|
+
fontColor: emptySummary(),
|
|
2676
|
+
fillColor: emptySummary(),
|
|
2677
|
+
numberFormatCode: emptySummary(),
|
|
2678
|
+
horizontal: emptySummary(),
|
|
2679
|
+
vertical: emptySummary(),
|
|
2680
|
+
border: emptySummary()
|
|
2681
|
+
};
|
|
2682
|
+
}
|
|
2683
|
+
__name(createEmptyFormatSummary, "createEmptyFormatSummary");
|
|
2684
|
+
function emptySummary() {
|
|
2685
|
+
return { hasValue: false, value: void 0, mixed: false };
|
|
2686
|
+
}
|
|
2687
|
+
__name(emptySummary, "emptySummary");
|
|
2688
|
+
function collectSummaryValue(summary, value) {
|
|
2689
|
+
if (!summary.hasValue) {
|
|
2690
|
+
summary.hasValue = true;
|
|
2691
|
+
summary.value = value;
|
|
2692
|
+
return;
|
|
2693
|
+
}
|
|
2694
|
+
if (!formatValuesEqual(summary.value, value)) summary.mixed = true;
|
|
2695
|
+
}
|
|
2696
|
+
__name(collectSummaryValue, "collectSummaryValue");
|
|
2697
|
+
function summaryValue(summary) {
|
|
2698
|
+
return summary.mixed ? "mixed" : summary.value;
|
|
2699
|
+
}
|
|
2700
|
+
__name(summaryValue, "summaryValue");
|
|
2701
|
+
function cellFormatAt(model, row, column) {
|
|
2702
|
+
var _a, _b;
|
|
2703
|
+
const cell = getRenderCell(model, row, column);
|
|
2704
|
+
return (_b = model.styles.cellFormats[(_a = cell == null ? void 0 : cell.styleId) != null ? _a : 0]) != null ? _b : {};
|
|
2705
|
+
}
|
|
2706
|
+
__name(cellFormatAt, "cellFormatAt");
|
|
2707
|
+
function normalizeHorizontalAlign(format) {
|
|
2708
|
+
var _a, _b;
|
|
2709
|
+
return ((_a = format.alignment) == null ? void 0 : _a.horizontal) === "center" || ((_b = format.alignment) == null ? void 0 : _b.horizontal) === "right" ? format.alignment.horizontal : "left";
|
|
2710
|
+
}
|
|
2711
|
+
__name(normalizeHorizontalAlign, "normalizeHorizontalAlign");
|
|
2712
|
+
function normalizeVerticalAlign(format) {
|
|
2713
|
+
var _a, _b;
|
|
2714
|
+
if (((_a = format.alignment) == null ? void 0 : _a.vertical) === "top" || ((_b = format.alignment) == null ? void 0 : _b.vertical) === "center") {
|
|
2715
|
+
return format.alignment.vertical;
|
|
2716
|
+
}
|
|
2717
|
+
return "bottom";
|
|
2718
|
+
}
|
|
2719
|
+
__name(normalizeVerticalAlign, "normalizeVerticalAlign");
|
|
2720
|
+
function formatValuesEqual(a, b) {
|
|
2721
|
+
return JSON.stringify(a != null ? a : null) === JSON.stringify(b != null ? b : null);
|
|
2722
|
+
}
|
|
2723
|
+
__name(formatValuesEqual, "formatValuesEqual");
|
|
2724
|
+
async function renderSpreadsheetEditor(host, bytes, options = {}) {
|
|
2725
|
+
const controller = new NativeSpreadsheetEditor(host, options);
|
|
2726
|
+
await controller.loadWorkbook(bytes);
|
|
2727
|
+
return controller;
|
|
2728
|
+
}
|
|
2729
|
+
__name(renderSpreadsheetEditor, "renderSpreadsheetEditor");
|
|
2730
|
+
var _NativeSpreadsheetEditor = class _NativeSpreadsheetEditor {
|
|
2731
|
+
constructor(host, options) {
|
|
2732
|
+
__publicField(this, "host", host);
|
|
2733
|
+
__publicField(this, "options", options);
|
|
2734
|
+
__publicField(this, "renderRoot");
|
|
2735
|
+
__publicField(this, "runtimeValue", null);
|
|
2736
|
+
__publicField(this, "renderer", null);
|
|
2737
|
+
__publicField(this, "layout", null);
|
|
2738
|
+
__publicField(this, "viewport", null);
|
|
2739
|
+
__publicField(this, "scroll", { x: 0, y: 0 });
|
|
2740
|
+
__publicField(this, "scrollbarState", null);
|
|
2741
|
+
__publicField(this, "listeners", /* @__PURE__ */ new Set());
|
|
2742
|
+
__publicField(this, "stateListeners", /* @__PURE__ */ new Set());
|
|
2743
|
+
__publicField(this, "baseCanvas", document.createElement("canvas"));
|
|
2744
|
+
__publicField(this, "headerCanvas", document.createElement("canvas"));
|
|
2745
|
+
__publicField(this, "overlayCanvas", document.createElement("canvas"));
|
|
2746
|
+
__publicField(this, "unsupportedObjectLayer", document.createElement("div"));
|
|
2747
|
+
__publicField(this, "sheetTabs", document.createElement("div"));
|
|
2748
|
+
__publicField(this, "horizontalScrollbar", document.createElement("div"));
|
|
2749
|
+
__publicField(this, "verticalScrollbar", document.createElement("div"));
|
|
2750
|
+
__publicField(this, "horizontalThumb", document.createElement("div"));
|
|
2751
|
+
__publicField(this, "verticalThumb", document.createElement("div"));
|
|
2752
|
+
__publicField(this, "cellEditorElement", document.createElement("textarea"));
|
|
2753
|
+
__publicField(this, "resizeGuide", document.createElement("div"));
|
|
2754
|
+
__publicField(this, "contextMenu", document.createElement("div"));
|
|
2755
|
+
__publicField(this, "filterMenu", document.createElement("div"));
|
|
2756
|
+
__publicField(this, "cellEditor");
|
|
2757
|
+
__publicField(this, "resizeObserver");
|
|
2758
|
+
__publicField(this, "disposed", false);
|
|
2759
|
+
__publicField(this, "drag", null);
|
|
2760
|
+
__publicField(this, "selectionState", createSelection(1, 1));
|
|
2761
|
+
__publicField(this, "editingCell", null);
|
|
2762
|
+
__publicField(this, "editingCellStyle", null);
|
|
2763
|
+
__publicField(this, "clipboardPayload", null);
|
|
2764
|
+
__publicField(this, "selecting", false);
|
|
2765
|
+
__publicField(this, "resizeDrag", null);
|
|
2766
|
+
__publicField(this, "autoFillDrag", null);
|
|
2767
|
+
__publicField(this, "selectedChartIndex", null);
|
|
2768
|
+
__publicField(this, "chartDrag", null);
|
|
2769
|
+
__publicField(this, "columnFilters", /* @__PURE__ */ new Map());
|
|
2770
|
+
__publicField(this, "filterMenuColumn", null);
|
|
2771
|
+
__publicField(this, "copiedRange", null);
|
|
2772
|
+
__publicField(this, "formulaReferenceRanges", []);
|
|
2773
|
+
__publicField(this, "lastStateKey", "");
|
|
2774
|
+
__publicField(this, "transactionDepth", 0);
|
|
2775
|
+
__publicField(this, "transactionChanged", false);
|
|
2776
|
+
__publicField(this, "handleClick", /* @__PURE__ */ __name((event) => {
|
|
2777
|
+
if (this.eventPathContains(event, this.contextMenu) || this.eventPathContains(event, this.filterMenu)) return;
|
|
2778
|
+
if (this.filterMenu.style.display === "block") this.hideFilterMenu();
|
|
2779
|
+
if (this.editingCell) {
|
|
2780
|
+
if (this.eventPathContains(event, this.cellEditorElement)) return;
|
|
2781
|
+
this.cellEditor.cancel();
|
|
2782
|
+
}
|
|
2783
|
+
if (!this.viewport || !this.layout) return;
|
|
2784
|
+
const chartHit = this.chartAtPoint(this.eventPoint(event));
|
|
2785
|
+
if (chartHit) {
|
|
2786
|
+
this.selectedChartIndex = chartHit.chartIndex;
|
|
2787
|
+
this.render();
|
|
2788
|
+
event.preventDefault();
|
|
2789
|
+
return;
|
|
2790
|
+
}
|
|
2791
|
+
this.selectedChartIndex = null;
|
|
2792
|
+
const hit = this.viewport.hitTest(this.eventPoint(event));
|
|
2793
|
+
if (hit.kind !== "cell" && hit.kind !== "rowHeader" && hit.kind !== "columnHeader") return;
|
|
2794
|
+
if (hit.kind === "cell" && hit.row === 1 && this.isFilterIconHit(hit.column, event)) {
|
|
2795
|
+
this.openFilterMenu(hit.column, event.clientX, event.clientY);
|
|
2796
|
+
event.preventDefault();
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
if (hit.kind === "cell") {
|
|
2800
|
+
const model = this.runtime().activeSheet();
|
|
2801
|
+
this.selectionState = expandSelectionForMergedRanges(
|
|
2802
|
+
createSelection(hit.row, hit.column),
|
|
2803
|
+
model
|
|
2804
|
+
);
|
|
2805
|
+
}
|
|
2806
|
+
this.host.focus();
|
|
2807
|
+
this.render();
|
|
2808
|
+
}, "handleClick"));
|
|
2809
|
+
__publicField(this, "handleFilterMenuClick", /* @__PURE__ */ __name((event) => {
|
|
2810
|
+
var _a;
|
|
2811
|
+
const target = event.target;
|
|
2812
|
+
const action = (_a = target.closest("[data-filter-action]")) == null ? void 0 : _a.dataset.filterAction;
|
|
2813
|
+
if (!action || this.filterMenuColumn === null) return;
|
|
2814
|
+
if (action === "cancel") {
|
|
2815
|
+
this.hideFilterMenu();
|
|
2816
|
+
return;
|
|
2817
|
+
}
|
|
2818
|
+
const column = this.filterMenuColumn;
|
|
2819
|
+
if (action === "clear") {
|
|
2820
|
+
this.columnFilters.delete(column);
|
|
2821
|
+
} else if (action === "apply") {
|
|
2822
|
+
const values = Array.from(this.filterMenu.querySelectorAll("input[data-filter-value]:checked")).map((input) => {
|
|
2823
|
+
var _a2;
|
|
2824
|
+
return (_a2 = input.dataset.filterValue) != null ? _a2 : "";
|
|
2825
|
+
});
|
|
2826
|
+
this.columnFilters.set(column, values);
|
|
2827
|
+
}
|
|
2828
|
+
this.hideFilterMenu();
|
|
2829
|
+
void this.applyColumnFilters();
|
|
2830
|
+
}, "handleFilterMenuClick"));
|
|
2831
|
+
__publicField(this, "handleDoubleClick", /* @__PURE__ */ __name((event) => {
|
|
2832
|
+
if (!this.viewport || !this.layout) return;
|
|
2833
|
+
const hit = this.viewport.hitTest(this.eventPoint(event));
|
|
2834
|
+
if (hit.kind !== "cell") return;
|
|
2835
|
+
this.selectionState = expandSelectionForMergedRanges(createSelection(hit.row, hit.column), this.runtime().activeSheet());
|
|
2836
|
+
this.openCellEditor();
|
|
2837
|
+
event.preventDefault();
|
|
2838
|
+
}, "handleDoubleClick"));
|
|
2839
|
+
__publicField(this, "handlePointerDown", /* @__PURE__ */ __name((event) => {
|
|
2840
|
+
var _a;
|
|
2841
|
+
if (this.editingCell) {
|
|
2842
|
+
if (this.eventPathContains(event, this.cellEditorElement)) return;
|
|
2843
|
+
this.cellEditor.cancel();
|
|
2844
|
+
}
|
|
2845
|
+
if (event.button !== 0 || !this.viewport || !this.layout) return;
|
|
2846
|
+
const point = this.eventPoint(event);
|
|
2847
|
+
const chartHit = this.chartAtPoint(point);
|
|
2848
|
+
if (chartHit) {
|
|
2849
|
+
this.selectedChartIndex = chartHit.chartIndex;
|
|
2850
|
+
this.chartDrag = {
|
|
2851
|
+
chartIndex: chartHit.chartIndex,
|
|
2852
|
+
pointer: point,
|
|
2853
|
+
startRect: chartHit.logicalRect,
|
|
2854
|
+
previewRect: chartHit.logicalRect
|
|
2855
|
+
};
|
|
2856
|
+
this.host.setPointerCapture(event.pointerId);
|
|
2857
|
+
this.render();
|
|
2858
|
+
event.preventDefault();
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
this.selectedChartIndex = null;
|
|
2862
|
+
const resizeHandle = hitSpreadsheetResizeHandle(this.layout, this.viewport.viewports, point);
|
|
2863
|
+
if (resizeHandle) {
|
|
2864
|
+
this.resizeDrag = startSpreadsheetResizeDrag(resizeHandle, event);
|
|
2865
|
+
this.host.setPointerCapture(event.pointerId);
|
|
2866
|
+
this.host.style.cursor = resizeHandle.kind === "column" ? "col-resize" : "row-resize";
|
|
2867
|
+
this.updateResizeGuide(this.resizeDrag);
|
|
2868
|
+
event.preventDefault();
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
const selectionRange = normalizeSelectionRange(this.selectionState.range);
|
|
2872
|
+
const fillHandle = (_a = this.renderer) == null ? void 0 : _a.fillHandleRect(this.layout, this.viewport, selectionRange);
|
|
2873
|
+
if (fillHandle && containsPoint2(fillHandle, point) && supportsSpreadsheetAutoFill(this.runtime().activeSheet(), selectionRange)) {
|
|
2874
|
+
this.autoFillDrag = { source: selectionRange, preview: null };
|
|
2875
|
+
this.host.setPointerCapture(event.pointerId);
|
|
2876
|
+
event.preventDefault();
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
const hit = this.viewport.hitTest(point);
|
|
2880
|
+
if (hit.kind !== "cell" && hit.kind !== "rowHeader" && hit.kind !== "columnHeader") return;
|
|
2881
|
+
const model = this.runtime().activeSheet();
|
|
2882
|
+
if (hit.kind === "cell") {
|
|
2883
|
+
this.selectionState = expandSelectionForMergedRanges(createSelection(hit.row, hit.column), model);
|
|
2884
|
+
} else if (hit.kind === "rowHeader") {
|
|
2885
|
+
this.selectionState = createRowSelection(hit.row, { columnCount: this.layout.columnCount });
|
|
2886
|
+
} else {
|
|
2887
|
+
this.selectionState = createColumnSelection(hit.column, { rowCount: this.layout.rowCount });
|
|
2888
|
+
}
|
|
2889
|
+
this.selecting = true;
|
|
2890
|
+
this.host.setPointerCapture(event.pointerId);
|
|
2891
|
+
this.render();
|
|
2892
|
+
event.preventDefault();
|
|
2893
|
+
}, "handlePointerDown"));
|
|
2894
|
+
__publicField(this, "handlePointerMove", /* @__PURE__ */ __name((event) => {
|
|
2895
|
+
if (!this.viewport || !this.layout) return;
|
|
2896
|
+
if (this.chartDrag) {
|
|
2897
|
+
const point = this.eventPoint(event);
|
|
2898
|
+
const deltaX = point.x - this.chartDrag.pointer.x;
|
|
2899
|
+
const deltaY = point.y - this.chartDrag.pointer.y;
|
|
2900
|
+
this.chartDrag.previewRect = __spreadProps(__spreadValues({}, this.chartDrag.startRect), {
|
|
2901
|
+
x: Math.max(0, this.chartDrag.startRect.x + deltaX),
|
|
2902
|
+
y: Math.max(0, this.chartDrag.startRect.y + deltaY)
|
|
2903
|
+
});
|
|
2904
|
+
this.host.style.cursor = "grabbing";
|
|
2905
|
+
this.render();
|
|
2906
|
+
event.preventDefault();
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
if (!this.resizeDrag && !this.autoFillDrag && !this.selecting && !this.editingCell) {
|
|
2910
|
+
const resizeHandle = hitSpreadsheetResizeHandle(this.layout, this.viewport.viewports, this.eventPoint(event));
|
|
2911
|
+
this.host.style.cursor = resizeHandle ? resizeHandle.kind === "column" ? "col-resize" : "row-resize" : "";
|
|
2912
|
+
}
|
|
2913
|
+
if (this.resizeDrag) {
|
|
2914
|
+
this.resizeDrag = updateSpreadsheetResizeDrag(this.resizeDrag, event);
|
|
2915
|
+
this.updateResizeGuide(this.resizeDrag);
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
if (this.autoFillDrag) {
|
|
2919
|
+
const hit2 = this.viewport.hitTest(this.eventPoint(event));
|
|
2920
|
+
this.autoFillDrag.preview = autoFillPreviewRangeForHit(this.autoFillDrag.source, hit2);
|
|
2921
|
+
this.render();
|
|
2922
|
+
event.preventDefault();
|
|
2923
|
+
return;
|
|
2924
|
+
}
|
|
2925
|
+
if (!this.selecting) return;
|
|
2926
|
+
const hit = this.viewport.hitTest(this.eventPoint(event));
|
|
2927
|
+
if (hit.kind === "none" || hit.kind === "corner") return;
|
|
2928
|
+
const model = this.runtime().activeSheet();
|
|
2929
|
+
if (hit.kind === "cell") {
|
|
2930
|
+
this.selectionState = expandSelectionForMergedRanges(
|
|
2931
|
+
extendSelection(this.selectionState, hit.row, hit.column, {
|
|
2932
|
+
rowCount: this.layout.rowCount,
|
|
2933
|
+
columnCount: this.layout.columnCount
|
|
2934
|
+
}),
|
|
2935
|
+
model
|
|
2936
|
+
);
|
|
2937
|
+
} else if (hit.kind === "rowHeader" && this.selectionState.kind === "row") {
|
|
2938
|
+
this.selectionState = extendSelection(this.selectionState, hit.row, 1, { columnCount: this.layout.columnCount, rowCount: this.layout.rowCount });
|
|
2939
|
+
} else if (hit.kind === "columnHeader" && this.selectionState.kind === "column") {
|
|
2940
|
+
this.selectionState = extendSelection(this.selectionState, 1, hit.column, { columnCount: this.layout.columnCount, rowCount: this.layout.rowCount });
|
|
2941
|
+
}
|
|
2942
|
+
this.render();
|
|
2943
|
+
event.preventDefault();
|
|
2944
|
+
}, "handlePointerMove"));
|
|
2945
|
+
__publicField(this, "handlePointerUp", /* @__PURE__ */ __name((event) => {
|
|
2946
|
+
if (this.chartDrag) {
|
|
2947
|
+
const completed = this.chartDrag;
|
|
2948
|
+
this.chartDrag = null;
|
|
2949
|
+
this.host.style.cursor = "";
|
|
2950
|
+
if (this.host.hasPointerCapture(event.pointerId)) this.host.releasePointerCapture(event.pointerId);
|
|
2951
|
+
if (completed.previewRect.x === completed.startRect.x && completed.previewRect.y === completed.startRect.y) {
|
|
2952
|
+
this.render();
|
|
2953
|
+
event.preventDefault();
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
const anchor = this.chartAnchorAtIndex(completed.chartIndex);
|
|
2957
|
+
if (anchor && this.layout) {
|
|
2958
|
+
const next = chartAnchorForLogicalRect(this.layout, completed.previewRect, anchor);
|
|
2959
|
+
void this.runtime().moveChart(completed.chartIndex, next).then(async () => {
|
|
2960
|
+
this.render();
|
|
2961
|
+
await this.emitChange();
|
|
2962
|
+
});
|
|
2963
|
+
} else {
|
|
2964
|
+
this.render();
|
|
2965
|
+
}
|
|
2966
|
+
event.preventDefault();
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
if (this.resizeDrag) {
|
|
2970
|
+
const completed = updateSpreadsheetResizeDrag(this.resizeDrag, event);
|
|
2971
|
+
this.resizeDrag = null;
|
|
2972
|
+
this.host.style.cursor = "";
|
|
2973
|
+
this.hideResizeGuide();
|
|
2974
|
+
if (this.host.hasPointerCapture(event.pointerId)) this.host.releasePointerCapture(event.pointerId);
|
|
2975
|
+
void this.commitResizeDrag(completed);
|
|
2976
|
+
event.preventDefault();
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
if (this.autoFillDrag) {
|
|
2980
|
+
const completed = this.autoFillDrag;
|
|
2981
|
+
this.autoFillDrag = null;
|
|
2982
|
+
if (this.host.hasPointerCapture(event.pointerId)) this.host.releasePointerCapture(event.pointerId);
|
|
2983
|
+
if (completed.preview) {
|
|
2984
|
+
void this.runtime().autoFillRange(completed.source, completed.preview).then(async () => {
|
|
2985
|
+
this.selectionState = __spreadProps(__spreadValues({}, this.selectionState), { range: completed.preview });
|
|
2986
|
+
this.render();
|
|
2987
|
+
await this.emitChange();
|
|
2988
|
+
});
|
|
2989
|
+
} else {
|
|
2990
|
+
this.render();
|
|
2991
|
+
}
|
|
2992
|
+
event.preventDefault();
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
if (!this.selecting) return;
|
|
2996
|
+
this.selecting = false;
|
|
2997
|
+
if (this.host.hasPointerCapture(event.pointerId)) this.host.releasePointerCapture(event.pointerId);
|
|
2998
|
+
this.host.style.cursor = "";
|
|
2999
|
+
}, "handlePointerUp"));
|
|
3000
|
+
__publicField(this, "handlePointerLeave", /* @__PURE__ */ __name(() => {
|
|
3001
|
+
if (!this.resizeDrag && !this.autoFillDrag && !this.chartDrag) this.host.style.cursor = "";
|
|
3002
|
+
}, "handlePointerLeave"));
|
|
3003
|
+
__publicField(this, "handleKeyDown", /* @__PURE__ */ __name((event) => {
|
|
3004
|
+
if (this.editingCell) return;
|
|
3005
|
+
const { row, column } = this.selectionState.active;
|
|
3006
|
+
if (event.key === "F2" || event.key === "Enter") {
|
|
3007
|
+
event.preventDefault();
|
|
3008
|
+
this.openCellEditor();
|
|
3009
|
+
return;
|
|
3010
|
+
}
|
|
3011
|
+
const deltas = {
|
|
3012
|
+
ArrowUp: [-1, 0],
|
|
3013
|
+
ArrowDown: [1, 0],
|
|
3014
|
+
ArrowLeft: [0, -1],
|
|
3015
|
+
ArrowRight: [0, 1]
|
|
3016
|
+
};
|
|
3017
|
+
const delta = deltas[event.key];
|
|
3018
|
+
if (!delta) return;
|
|
3019
|
+
const model = this.runtime().activeSheet();
|
|
3020
|
+
this.selectionState = createSelection(
|
|
3021
|
+
Math.max(1, Math.min(model.maxRow, row + delta[0])),
|
|
3022
|
+
Math.max(1, Math.min(model.maxColumn, column + delta[1]))
|
|
3023
|
+
);
|
|
3024
|
+
event.preventDefault();
|
|
3025
|
+
this.render();
|
|
3026
|
+
}, "handleKeyDown"));
|
|
3027
|
+
__publicField(this, "handleCopy", /* @__PURE__ */ __name((event) => {
|
|
3028
|
+
var _a;
|
|
3029
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3030
|
+
const payload = buildSpreadsheetClipboardPayload(this.runtime().activeSheet(), range);
|
|
3031
|
+
this.clipboardPayload = payload;
|
|
3032
|
+
this.copiedRange = range;
|
|
3033
|
+
(_a = event.clipboardData) == null ? void 0 : _a.setData("text/plain", payload.text);
|
|
3034
|
+
event.preventDefault();
|
|
3035
|
+
}, "handleCopy"));
|
|
3036
|
+
__publicField(this, "handlePaste", /* @__PURE__ */ __name((event) => {
|
|
3037
|
+
var _a, _b, _c, _d, _e;
|
|
3038
|
+
const text = (_b = (_a = event.clipboardData) == null ? void 0 : _a.getData("text/plain")) != null ? _b : "";
|
|
3039
|
+
if (!text) return;
|
|
3040
|
+
event.preventDefault();
|
|
3041
|
+
const values = ((_c = this.clipboardPayload) == null ? void 0 : _c.text) === text ? this.clipboardPayload.values : parseTsv(text);
|
|
3042
|
+
void this.runtime().pasteRange(this.selectionState.active.row, this.selectionState.active.column, values, {
|
|
3043
|
+
mode: "all",
|
|
3044
|
+
merges: ((_d = this.clipboardPayload) == null ? void 0 : _d.text) === text ? this.clipboardPayload.merges : [],
|
|
3045
|
+
styleIds: ((_e = this.clipboardPayload) == null ? void 0 : _e.text) === text ? this.clipboardPayload.styleIds : []
|
|
3046
|
+
}).then(async () => {
|
|
3047
|
+
const pastedSelection = expandSelectionForMergedRanges({
|
|
3048
|
+
kind: "cell",
|
|
3049
|
+
active: this.selectionState.active,
|
|
3050
|
+
range: {
|
|
3051
|
+
startRow: this.selectionState.active.row,
|
|
3052
|
+
startColumn: this.selectionState.active.column,
|
|
3053
|
+
endRow: this.selectionState.active.row + values.length - 1,
|
|
3054
|
+
endColumn: this.selectionState.active.column + Math.max(1, values.reduce((max, row) => Math.max(max, row.length), 0)) - 1
|
|
3055
|
+
}
|
|
3056
|
+
}, this.runtime().activeSheet());
|
|
3057
|
+
this.selectionState = pastedSelection;
|
|
3058
|
+
this.copiedRange = normalizeSelectionRange(pastedSelection.range);
|
|
3059
|
+
this.render();
|
|
3060
|
+
await this.emitChange();
|
|
3061
|
+
});
|
|
3062
|
+
}, "handlePaste"));
|
|
3063
|
+
__publicField(this, "handleContextMenu", /* @__PURE__ */ __name((event) => {
|
|
3064
|
+
if (!this.viewport || !this.layout) return;
|
|
3065
|
+
const chartHit = this.chartAtPoint(this.eventPoint(event));
|
|
3066
|
+
if (chartHit) {
|
|
3067
|
+
this.selectedChartIndex = chartHit.chartIndex;
|
|
3068
|
+
this.hideContextMenu();
|
|
3069
|
+
this.hideFilterMenu();
|
|
3070
|
+
this.render();
|
|
3071
|
+
event.preventDefault();
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
const hit = this.viewport.hitTest(this.eventPoint(event));
|
|
3075
|
+
if (hit.kind === "cell" && !this.selectionContainsCell(hit.row, hit.column)) {
|
|
3076
|
+
this.selectionState = expandSelectionForMergedRanges(createSelection(hit.row, hit.column), this.runtime().activeSheet());
|
|
3077
|
+
this.render();
|
|
3078
|
+
}
|
|
3079
|
+
if (hit.kind !== "cell" && hit.kind !== "rowHeader" && hit.kind !== "columnHeader") return;
|
|
3080
|
+
event.preventDefault();
|
|
3081
|
+
this.renderContextMenu(event.clientX, event.clientY, hit.kind === "columnHeader" ? hit.column : void 0);
|
|
3082
|
+
}, "handleContextMenu"));
|
|
3083
|
+
__publicField(this, "handleDocumentPointerDown", /* @__PURE__ */ __name((event) => {
|
|
3084
|
+
const path = event.composedPath();
|
|
3085
|
+
if (path.includes(this.contextMenu) || path.includes(this.filterMenu)) return;
|
|
3086
|
+
this.hideContextMenu();
|
|
3087
|
+
this.hideFilterMenu();
|
|
3088
|
+
}, "handleDocumentPointerDown"));
|
|
3089
|
+
__publicField(this, "handleWheel", /* @__PURE__ */ __name((event) => {
|
|
3090
|
+
if (!this.runtimeValue) return;
|
|
3091
|
+
event.preventDefault();
|
|
3092
|
+
const next = applyWheelDelta(this.scroll, event.deltaX, event.deltaY);
|
|
3093
|
+
this.setScroll(next.x, next.y);
|
|
3094
|
+
}, "handleWheel"));
|
|
3095
|
+
var _a;
|
|
3096
|
+
host.replaceChildren();
|
|
3097
|
+
this.renderRoot = options.styleIsolation === "none" ? host : (_a = host.shadowRoot) != null ? _a : host.attachShadow({ mode: "open" });
|
|
3098
|
+
this.renderRoot.replaceChildren();
|
|
3099
|
+
if (this.renderRoot instanceof ShadowRoot) {
|
|
3100
|
+
const style = document.createElement("style");
|
|
3101
|
+
style.textContent = `
|
|
3102
|
+
:host { display: block; position: relative; min-width: 0; min-height: 0; }
|
|
3103
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
3104
|
+
button, input, textarea, select { font: 13px Arial, sans-serif; line-height: normal; }
|
|
3105
|
+
button { margin: 0; }
|
|
3106
|
+
`;
|
|
3107
|
+
this.renderRoot.appendChild(style);
|
|
3108
|
+
}
|
|
3109
|
+
host.classList.add("coco-spreadsheet-editor-host");
|
|
3110
|
+
Object.assign(host.style, { position: "relative", overflow: "hidden" });
|
|
3111
|
+
for (const canvas of [this.baseCanvas, this.headerCanvas, this.overlayCanvas]) {
|
|
3112
|
+
Object.assign(canvas.style, { position: "absolute", inset: "0", display: "block" });
|
|
3113
|
+
this.renderRoot.appendChild(canvas);
|
|
3114
|
+
}
|
|
3115
|
+
this.unsupportedObjectLayer.className = "coco-spreadsheet-unsupported-object-layer";
|
|
3116
|
+
Object.assign(this.unsupportedObjectLayer.style, {
|
|
3117
|
+
position: "absolute",
|
|
3118
|
+
inset: "0",
|
|
3119
|
+
zIndex: "4",
|
|
3120
|
+
overflow: "hidden",
|
|
3121
|
+
pointerEvents: "none"
|
|
3122
|
+
});
|
|
3123
|
+
this.renderRoot.appendChild(this.unsupportedObjectLayer);
|
|
3124
|
+
this.cellEditorElement.className = "coco-spreadsheet-cell-editor";
|
|
3125
|
+
Object.assign(this.cellEditorElement.style, {
|
|
3126
|
+
position: "absolute",
|
|
3127
|
+
zIndex: "20",
|
|
3128
|
+
display: "none",
|
|
3129
|
+
resize: "none",
|
|
3130
|
+
overflow: "hidden",
|
|
3131
|
+
padding: "2px 4px",
|
|
3132
|
+
border: "2px solid #2563eb",
|
|
3133
|
+
outline: "none",
|
|
3134
|
+
font: "13px Arial, sans-serif",
|
|
3135
|
+
lineHeight: "normal",
|
|
3136
|
+
whiteSpace: "pre-wrap",
|
|
3137
|
+
overflowWrap: "break-word"
|
|
3138
|
+
});
|
|
3139
|
+
this.renderRoot.appendChild(this.cellEditorElement);
|
|
3140
|
+
this.resizeGuide.className = "coco-spreadsheet-resize-guide";
|
|
3141
|
+
Object.assign(this.resizeGuide.style, {
|
|
3142
|
+
position: "absolute",
|
|
3143
|
+
zIndex: "25",
|
|
3144
|
+
display: "none",
|
|
3145
|
+
pointerEvents: "none",
|
|
3146
|
+
background: "#2563eb"
|
|
3147
|
+
});
|
|
3148
|
+
this.renderRoot.appendChild(this.resizeGuide);
|
|
3149
|
+
this.contextMenu.className = "coco-spreadsheet-context-menu";
|
|
3150
|
+
Object.assign(this.contextMenu.style, {
|
|
3151
|
+
position: "fixed",
|
|
3152
|
+
zIndex: "50",
|
|
3153
|
+
display: "none",
|
|
3154
|
+
minWidth: "160px",
|
|
3155
|
+
padding: "4px",
|
|
3156
|
+
background: "#ffffff",
|
|
3157
|
+
border: "1px solid #cbd5e1",
|
|
3158
|
+
borderRadius: "6px",
|
|
3159
|
+
boxShadow: "0 8px 24px rgba(15, 23, 42, 0.16)"
|
|
3160
|
+
});
|
|
3161
|
+
this.renderRoot.appendChild(this.contextMenu);
|
|
3162
|
+
this.filterMenu.className = "coco-spreadsheet-filter-menu";
|
|
3163
|
+
Object.assign(this.filterMenu.style, {
|
|
3164
|
+
position: "fixed",
|
|
3165
|
+
zIndex: "60",
|
|
3166
|
+
display: "none",
|
|
3167
|
+
width: "240px",
|
|
3168
|
+
maxHeight: "360px",
|
|
3169
|
+
overflow: "auto",
|
|
3170
|
+
padding: "8px",
|
|
3171
|
+
background: "#ffffff",
|
|
3172
|
+
border: "1px solid #cbd5e1",
|
|
3173
|
+
borderRadius: "6px",
|
|
3174
|
+
boxShadow: "0 8px 24px rgba(15, 23, 42, 0.16)"
|
|
3175
|
+
});
|
|
3176
|
+
this.renderRoot.appendChild(this.filterMenu);
|
|
3177
|
+
this.cellEditor = new SpreadsheetCellEditor({
|
|
3178
|
+
textarea: this.cellEditorElement,
|
|
3179
|
+
onCommit: /* @__PURE__ */ __name(async (value) => this.commitCellEdit(value), "onCommit"),
|
|
3180
|
+
onCancel: /* @__PURE__ */ __name(() => {
|
|
3181
|
+
this.editingCell = null;
|
|
3182
|
+
this.editingCellStyle = null;
|
|
3183
|
+
this.formulaReferenceRanges = [];
|
|
3184
|
+
this.render();
|
|
3185
|
+
}, "onCancel")
|
|
3186
|
+
});
|
|
3187
|
+
this.cellEditorElement.addEventListener("input", () => {
|
|
3188
|
+
if (!this.editingCell) return;
|
|
3189
|
+
this.formulaReferenceRanges = formulaReferenceRangesFromText(this.cellEditorElement.value);
|
|
3190
|
+
this.render();
|
|
3191
|
+
});
|
|
3192
|
+
this.configureScrollbar(this.horizontalScrollbar, this.horizontalThumb, true);
|
|
3193
|
+
this.configureScrollbar(this.verticalScrollbar, this.verticalThumb, false);
|
|
3194
|
+
this.renderRoot.append(this.horizontalScrollbar, this.verticalScrollbar);
|
|
3195
|
+
this.sheetTabs.className = "coco-spreadsheet-editor-tabs";
|
|
3196
|
+
if (options.showSheetTabs !== false) this.renderRoot.appendChild(this.sheetTabs);
|
|
3197
|
+
this.renderer = new SpreadsheetCanvasRenderer({
|
|
3198
|
+
baseCanvas: this.baseCanvas,
|
|
3199
|
+
headerCanvas: this.headerCanvas,
|
|
3200
|
+
overlayCanvas: this.overlayCanvas
|
|
3201
|
+
});
|
|
3202
|
+
host.addEventListener("wheel", this.handleWheel, { passive: false });
|
|
3203
|
+
host.addEventListener("click", this.handleClick);
|
|
3204
|
+
host.addEventListener("dblclick", this.handleDoubleClick);
|
|
3205
|
+
host.addEventListener("pointerdown", this.handlePointerDown);
|
|
3206
|
+
host.addEventListener("pointermove", this.handlePointerMove);
|
|
3207
|
+
host.addEventListener("pointerup", this.handlePointerUp);
|
|
3208
|
+
host.addEventListener("pointerleave", this.handlePointerLeave);
|
|
3209
|
+
host.addEventListener("keydown", this.handleKeyDown);
|
|
3210
|
+
host.addEventListener("copy", this.handleCopy);
|
|
3211
|
+
host.addEventListener("paste", this.handlePaste);
|
|
3212
|
+
host.addEventListener("contextmenu", this.handleContextMenu);
|
|
3213
|
+
document.addEventListener("pointerdown", this.handleDocumentPointerDown);
|
|
3214
|
+
this.filterMenu.addEventListener("click", this.handleFilterMenuClick);
|
|
3215
|
+
host.tabIndex = 0;
|
|
3216
|
+
this.resizeObserver = new ResizeObserver(() => this.render());
|
|
3217
|
+
this.resizeObserver.observe(host);
|
|
3218
|
+
}
|
|
3219
|
+
runtime() {
|
|
3220
|
+
if (!this.runtimeValue) throw new Error("SpreadsheetEditor: workbook is not loaded");
|
|
3221
|
+
return this.runtimeValue;
|
|
3222
|
+
}
|
|
3223
|
+
selection() {
|
|
3224
|
+
return this.selectionState;
|
|
3225
|
+
}
|
|
3226
|
+
cellState(address) {
|
|
3227
|
+
var _a, _b, _c, _d;
|
|
3228
|
+
const model = this.runtime().activeSheet();
|
|
3229
|
+
const cell = model.cellsByAddress.get(address);
|
|
3230
|
+
if (!cell) {
|
|
3231
|
+
const match = address.match(/^([A-Z]+)(\d+)$/i);
|
|
3232
|
+
if (!match) throw new Error(`SpreadsheetEditor: invalid cell address ${address}`);
|
|
3233
|
+
const column = columnNumberFromLetters(match[1]);
|
|
3234
|
+
const row = Number(match[2]);
|
|
3235
|
+
return { address: address.toUpperCase(), row, column, value: null, displayText: "" };
|
|
3236
|
+
}
|
|
3237
|
+
const format = model.styles.cellFormats[(_a = cell.styleId) != null ? _a : 0];
|
|
3238
|
+
return {
|
|
3239
|
+
address: cell.address,
|
|
3240
|
+
row: cell.row,
|
|
3241
|
+
column: cell.column,
|
|
3242
|
+
value: cell.value,
|
|
3243
|
+
displayText: cell.displayText,
|
|
3244
|
+
formula: cell.formula,
|
|
3245
|
+
styleId: cell.styleId,
|
|
3246
|
+
format: format ? {
|
|
3247
|
+
font: model.styles.fonts[(_b = format.fontId) != null ? _b : 0],
|
|
3248
|
+
fill: model.styles.fills[(_c = format.fillId) != null ? _c : 0],
|
|
3249
|
+
border: model.styles.borders[(_d = format.borderId) != null ? _d : 0],
|
|
3250
|
+
alignment: format.alignment,
|
|
3251
|
+
numberFormatCode: format.numberFormatCode
|
|
3252
|
+
} : void 0
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
freezeState() {
|
|
3256
|
+
return freezeStateFromSheet(this.runtime().activeSheet().source);
|
|
3257
|
+
}
|
|
3258
|
+
filterState() {
|
|
3259
|
+
return new Map([...this.columnFilters.entries()].map(([column, values]) => [column, [...values]]));
|
|
3260
|
+
}
|
|
3261
|
+
activeSheetName() {
|
|
3262
|
+
return this.runtime().activeSheetName();
|
|
3263
|
+
}
|
|
3264
|
+
async setActiveSheet(name) {
|
|
3265
|
+
await this.runtime().setActiveSheet(name);
|
|
3266
|
+
this.scroll = { x: 0, y: 0 };
|
|
3267
|
+
this.selectionState = createSelection(1, 1);
|
|
3268
|
+
this.render();
|
|
3269
|
+
this.renderTabs();
|
|
3270
|
+
}
|
|
3271
|
+
async addSheet(name) {
|
|
3272
|
+
await this.runtime().addSheet(name);
|
|
3273
|
+
this.scroll = { x: 0, y: 0 };
|
|
3274
|
+
this.selectionState = createSelection(1, 1);
|
|
3275
|
+
this.renderTabs();
|
|
3276
|
+
this.render();
|
|
3277
|
+
await this.emitChange();
|
|
3278
|
+
}
|
|
3279
|
+
async renameSheet(sheetName, name) {
|
|
3280
|
+
await this.runtime().renameSheet(sheetName, name);
|
|
3281
|
+
this.renderTabs();
|
|
3282
|
+
this.render();
|
|
3283
|
+
await this.emitChange();
|
|
3284
|
+
}
|
|
3285
|
+
async copySheet(sheetName, newName) {
|
|
3286
|
+
await this.runtime().copySheet(sheetName, newName);
|
|
3287
|
+
this.scroll = { x: 0, y: 0 };
|
|
3288
|
+
this.selectionState = createSelection(1, 1);
|
|
3289
|
+
this.renderTabs();
|
|
3290
|
+
this.render();
|
|
3291
|
+
await this.emitChange();
|
|
3292
|
+
}
|
|
3293
|
+
async deleteSheet(sheetName) {
|
|
3294
|
+
await this.runtime().deleteSheet(sheetName);
|
|
3295
|
+
this.scroll = { x: 0, y: 0 };
|
|
3296
|
+
this.selectionState = createSelection(1, 1);
|
|
3297
|
+
this.renderTabs();
|
|
3298
|
+
this.render();
|
|
3299
|
+
await this.emitChange();
|
|
3300
|
+
}
|
|
3301
|
+
async moveSheet(sheetName, toIndex) {
|
|
3302
|
+
await this.runtime().moveSheet(sheetName, toIndex);
|
|
3303
|
+
this.renderTabs();
|
|
3304
|
+
this.render();
|
|
3305
|
+
await this.emitChange();
|
|
3306
|
+
}
|
|
3307
|
+
async setCellValue(address, value) {
|
|
3308
|
+
await this.runtime().setCellValue(address, value, { mergeUndo: true });
|
|
3309
|
+
this.render();
|
|
3310
|
+
await this.emitChange();
|
|
3311
|
+
}
|
|
3312
|
+
async setCellFormula(address, formula) {
|
|
3313
|
+
await this.runtime().setCellFormula(address, formula, { mergeUndo: true });
|
|
3314
|
+
this.render();
|
|
3315
|
+
await this.emitChange();
|
|
3316
|
+
}
|
|
3317
|
+
async formatSelection(kind) {
|
|
3318
|
+
await this.applyFormat(kind);
|
|
3319
|
+
}
|
|
3320
|
+
async setSelectionFontColor(color) {
|
|
3321
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3322
|
+
await this.runtime().setRangeFontColor(
|
|
3323
|
+
range.startRow,
|
|
3324
|
+
range.startColumn,
|
|
3325
|
+
range.endRow - range.startRow + 1,
|
|
3326
|
+
range.endColumn - range.startColumn + 1,
|
|
3327
|
+
color
|
|
3328
|
+
);
|
|
3329
|
+
this.render();
|
|
3330
|
+
await this.emitChange();
|
|
3331
|
+
}
|
|
3332
|
+
async setSelectionFillColor(color) {
|
|
3333
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3334
|
+
await this.runtime().setRangeFillColor(
|
|
3335
|
+
range.startRow,
|
|
3336
|
+
range.startColumn,
|
|
3337
|
+
range.endRow - range.startRow + 1,
|
|
3338
|
+
range.endColumn - range.startColumn + 1,
|
|
3339
|
+
color
|
|
3340
|
+
);
|
|
3341
|
+
this.render();
|
|
3342
|
+
await this.emitChange();
|
|
3343
|
+
}
|
|
3344
|
+
async setSelectionHorizontalAlign(alignment) {
|
|
3345
|
+
await this.applyAlignment(alignment);
|
|
3346
|
+
}
|
|
3347
|
+
async setSelectionVerticalAlign(alignment) {
|
|
3348
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3349
|
+
await this.runtime().setRangeVerticalAlign(
|
|
3350
|
+
range.startRow,
|
|
3351
|
+
range.startColumn,
|
|
3352
|
+
range.endRow - range.startRow + 1,
|
|
3353
|
+
range.endColumn - range.startColumn + 1,
|
|
3354
|
+
alignment
|
|
3355
|
+
);
|
|
3356
|
+
this.render();
|
|
3357
|
+
await this.emitChange();
|
|
3358
|
+
}
|
|
3359
|
+
async setSelectionNumberFormat(code) {
|
|
3360
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3361
|
+
await this.runtime().setRangeNumberFormat(
|
|
3362
|
+
range.startRow,
|
|
3363
|
+
range.startColumn,
|
|
3364
|
+
range.endRow - range.startRow + 1,
|
|
3365
|
+
range.endColumn - range.startColumn + 1,
|
|
3366
|
+
code
|
|
3367
|
+
);
|
|
3368
|
+
this.render();
|
|
3369
|
+
await this.emitChange();
|
|
3370
|
+
}
|
|
3371
|
+
async setSelectionBorder(mode) {
|
|
3372
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3373
|
+
await this.runtime().setRangeBorder(
|
|
3374
|
+
range.startRow,
|
|
3375
|
+
range.startColumn,
|
|
3376
|
+
range.endRow - range.startRow + 1,
|
|
3377
|
+
range.endColumn - range.startColumn + 1,
|
|
3378
|
+
mode
|
|
3379
|
+
);
|
|
3380
|
+
this.render();
|
|
3381
|
+
await this.emitChange();
|
|
3382
|
+
}
|
|
3383
|
+
async loadWorkbook(bytes) {
|
|
3384
|
+
this.assertAlive();
|
|
3385
|
+
const previous = this.runtimeValue;
|
|
3386
|
+
this.runtimeValue = null;
|
|
3387
|
+
this.scroll = { x: 0, y: 0 };
|
|
3388
|
+
if (previous) await previous.dispose();
|
|
3389
|
+
this.runtimeValue = await SpreadsheetEditorRuntime.fromZip(bytes, {
|
|
3390
|
+
workbookId: this.options.workbookId,
|
|
3391
|
+
mode: "inline"
|
|
3392
|
+
});
|
|
3393
|
+
this.renderTabs();
|
|
3394
|
+
this.render();
|
|
3395
|
+
}
|
|
3396
|
+
async toZip() {
|
|
3397
|
+
return this.runtime().toZip();
|
|
3398
|
+
}
|
|
3399
|
+
canUndo() {
|
|
3400
|
+
return this.runtime().canUndo();
|
|
3401
|
+
}
|
|
3402
|
+
canRedo() {
|
|
3403
|
+
return this.runtime().canRedo();
|
|
3404
|
+
}
|
|
3405
|
+
async undo() {
|
|
3406
|
+
await this.runtime().undo();
|
|
3407
|
+
this.render();
|
|
3408
|
+
await this.emitChange();
|
|
3409
|
+
}
|
|
3410
|
+
async redo() {
|
|
3411
|
+
await this.runtime().redo();
|
|
3412
|
+
this.render();
|
|
3413
|
+
await this.emitChange();
|
|
3414
|
+
}
|
|
3415
|
+
subscribe(listener) {
|
|
3416
|
+
this.listeners.add(listener);
|
|
3417
|
+
return () => this.listeners.delete(listener);
|
|
3418
|
+
}
|
|
3419
|
+
subscribeState(listener) {
|
|
3420
|
+
this.stateListeners.add(listener);
|
|
3421
|
+
return () => this.stateListeners.delete(listener);
|
|
3422
|
+
}
|
|
3423
|
+
async runTransaction(options, run) {
|
|
3424
|
+
this.transactionDepth += 1;
|
|
3425
|
+
try {
|
|
3426
|
+
await this.runtime().runUserTransaction(options, () => run(this));
|
|
3427
|
+
} finally {
|
|
3428
|
+
this.transactionDepth -= 1;
|
|
3429
|
+
if (this.transactionDepth === 0 && this.transactionChanged) {
|
|
3430
|
+
this.transactionChanged = false;
|
|
3431
|
+
this.render();
|
|
3432
|
+
await this.emitChange();
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
destroy() {
|
|
3437
|
+
if (this.disposed) return;
|
|
3438
|
+
this.disposed = true;
|
|
3439
|
+
this.resizeObserver.disconnect();
|
|
3440
|
+
this.host.removeEventListener("wheel", this.handleWheel);
|
|
3441
|
+
this.host.removeEventListener("click", this.handleClick);
|
|
3442
|
+
this.host.removeEventListener("dblclick", this.handleDoubleClick);
|
|
3443
|
+
this.host.removeEventListener("pointerdown", this.handlePointerDown);
|
|
3444
|
+
this.host.removeEventListener("pointermove", this.handlePointerMove);
|
|
3445
|
+
this.host.removeEventListener("pointerup", this.handlePointerUp);
|
|
3446
|
+
this.host.removeEventListener("pointerleave", this.handlePointerLeave);
|
|
3447
|
+
this.host.removeEventListener("keydown", this.handleKeyDown);
|
|
3448
|
+
this.host.removeEventListener("copy", this.handleCopy);
|
|
3449
|
+
this.host.removeEventListener("paste", this.handlePaste);
|
|
3450
|
+
this.host.removeEventListener("contextmenu", this.handleContextMenu);
|
|
3451
|
+
document.removeEventListener("pointerdown", this.handleDocumentPointerDown);
|
|
3452
|
+
this.contextMenu.remove();
|
|
3453
|
+
this.filterMenu.remove();
|
|
3454
|
+
this.unsupportedObjectLayer.remove();
|
|
3455
|
+
this.host.replaceChildren();
|
|
3456
|
+
const runtime = this.runtimeValue;
|
|
3457
|
+
this.runtimeValue = null;
|
|
3458
|
+
void (runtime == null ? void 0 : runtime.dispose());
|
|
3459
|
+
}
|
|
3460
|
+
render() {
|
|
3461
|
+
var _a;
|
|
3462
|
+
const runtime = this.runtimeValue;
|
|
3463
|
+
const renderer = this.renderer;
|
|
3464
|
+
if (!runtime || !renderer || this.disposed) return;
|
|
3465
|
+
const rect = this.host.getBoundingClientRect();
|
|
3466
|
+
const width = Math.max(1, rect.width);
|
|
3467
|
+
const height = Math.max(1, rect.height - (this.options.showSheetTabs === false ? 0 : 40));
|
|
3468
|
+
const layout = new SpreadsheetLayout(runtime.activeSheet());
|
|
3469
|
+
const viewportSize = { width: Math.max(1, width - 12), height: Math.max(1, height - 12) };
|
|
3470
|
+
const viewport = new SpreadsheetViewportManager(layout, {
|
|
3471
|
+
viewportSize,
|
|
3472
|
+
scroll: this.scroll,
|
|
3473
|
+
freeze: freezeStateFromSheet(runtime.activeSheet().source)
|
|
3474
|
+
});
|
|
3475
|
+
this.layout = layout;
|
|
3476
|
+
this.viewport = viewport;
|
|
3477
|
+
this.scroll = viewport.scroll;
|
|
3478
|
+
this.scrollbarState = computeScrollbarState(
|
|
3479
|
+
layout,
|
|
3480
|
+
viewportSize,
|
|
3481
|
+
freezeStateFromSheet(runtime.activeSheet().source),
|
|
3482
|
+
this.scroll
|
|
3483
|
+
);
|
|
3484
|
+
renderer.resize(width, height);
|
|
3485
|
+
renderer.setHeaderFiltersEnabled(this.options.enableHeaderFilters === true);
|
|
3486
|
+
renderer.setHeaderFilterColumns(this.options.enableHeaderFilters === true ? Array.from({ length: layout.columnCount }, (_, index) => index + 1) : null);
|
|
3487
|
+
renderer.setActiveHeaderFilterColumns(Array.from(this.columnFilters.keys()));
|
|
3488
|
+
renderer.render(layout, viewport);
|
|
3489
|
+
const selectionRange = normalizeSelectionRange(this.selectionState.range);
|
|
3490
|
+
renderer.renderSelection(layout, viewport, selectionRange, {
|
|
3491
|
+
showFillHandle: supportsSpreadsheetAutoFill(runtime.activeSheet(), selectionRange)
|
|
3492
|
+
});
|
|
3493
|
+
this.updateCellEditorPosition();
|
|
3494
|
+
if ((_a = this.autoFillDrag) == null ? void 0 : _a.preview) {
|
|
3495
|
+
renderer.renderDashedRange(layout, viewport, this.autoFillDrag.preview, {
|
|
3496
|
+
stroke: "#16A34A",
|
|
3497
|
+
fill: "rgba(34, 197, 94, 0.08)"
|
|
3498
|
+
});
|
|
3499
|
+
}
|
|
3500
|
+
for (const reference of this.formulaReferenceRanges) {
|
|
3501
|
+
renderer.renderDashedRange(layout, viewport, reference.range, { stroke: reference.stroke });
|
|
3502
|
+
}
|
|
3503
|
+
if (this.copiedRange) renderer.renderCopiedRange(layout, viewport, this.copiedRange);
|
|
3504
|
+
this.renderUnsupportedChartNotices(layout, viewport);
|
|
3505
|
+
this.emitStateIfChanged(runtime.activeSheetName());
|
|
3506
|
+
this.updateScrollbars();
|
|
3507
|
+
}
|
|
3508
|
+
renderUnsupportedChartNotices(layout, viewport) {
|
|
3509
|
+
var _a, _b, _c;
|
|
3510
|
+
this.unsupportedObjectLayer.replaceChildren();
|
|
3511
|
+
const sheet = (_a = this.runtimeValue) == null ? void 0 : _a.activeSheet().source;
|
|
3512
|
+
if (!sheet || sheet.charts.length === 0) return;
|
|
3513
|
+
for (let chartIndex = 0; chartIndex < sheet.charts.length; chartIndex += 1) {
|
|
3514
|
+
const chart = sheet.charts[chartIndex];
|
|
3515
|
+
if (!chart) continue;
|
|
3516
|
+
const anchor = findChartAnchor(sheet.drawings, chart);
|
|
3517
|
+
const logicalRect = ((_b = this.chartDrag) == null ? void 0 : _b.chartIndex) === chartIndex ? this.chartDrag.previewRect : chartLogicalRect(layout, anchor);
|
|
3518
|
+
if (!logicalRect) continue;
|
|
3519
|
+
for (const fragment of chartScreenFragments(logicalRect, viewport)) {
|
|
3520
|
+
const notice = document.createElement("div");
|
|
3521
|
+
notice.className = "coco-spreadsheet-unsupported-chart";
|
|
3522
|
+
Object.assign(notice.style, {
|
|
3523
|
+
position: "absolute",
|
|
3524
|
+
left: `${fragment.x}px`,
|
|
3525
|
+
top: `${fragment.y}px`,
|
|
3526
|
+
width: `${Math.max(1, fragment.width)}px`,
|
|
3527
|
+
height: `${Math.max(1, fragment.height)}px`,
|
|
3528
|
+
display: "flex",
|
|
3529
|
+
flexDirection: "column",
|
|
3530
|
+
alignItems: "center",
|
|
3531
|
+
justifyContent: "center",
|
|
3532
|
+
gap: "6px",
|
|
3533
|
+
padding: "12px",
|
|
3534
|
+
boxSizing: "border-box",
|
|
3535
|
+
overflow: "hidden",
|
|
3536
|
+
background: "rgba(248, 250, 252, 0.94)",
|
|
3537
|
+
color: "#475569",
|
|
3538
|
+
textAlign: "center",
|
|
3539
|
+
font: "13px Arial, sans-serif",
|
|
3540
|
+
lineHeight: "1.4",
|
|
3541
|
+
cursor: ((_c = this.chartDrag) == null ? void 0 : _c.chartIndex) === chartIndex ? "grabbing" : "move",
|
|
3542
|
+
border: this.selectedChartIndex === chartIndex ? "2px solid #2563eb" : "1px dashed #94a3b8",
|
|
3543
|
+
boxShadow: this.selectedChartIndex === chartIndex ? "0 0 0 1px rgba(37, 99, 235, 0.18)" : "none"
|
|
3544
|
+
});
|
|
3545
|
+
const title = document.createElement("strong");
|
|
3546
|
+
title.textContent = chart.title ? `\u56FE\u8868\uFF1A${chart.title}` : "\u56FE\u8868";
|
|
3547
|
+
title.style.color = "#334155";
|
|
3548
|
+
const message = document.createElement("span");
|
|
3549
|
+
message.textContent = "\u5F53\u524D\u7F16\u8F91\u5668\u6682\u4E0D\u652F\u6301\u56FE\u8868\u6E32\u67D3";
|
|
3550
|
+
const suggestion = document.createElement("span");
|
|
3551
|
+
suggestion.textContent = "\u8BF7\u4F7F\u7528 Excel \u6216 WPS \u6253\u5F00\u6587\u4EF6\u67E5\u770B";
|
|
3552
|
+
notice.append(title, message, suggestion);
|
|
3553
|
+
this.unsupportedObjectLayer.appendChild(notice);
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
chartAnchorAtIndex(chartIndex) {
|
|
3558
|
+
var _a;
|
|
3559
|
+
const sheet = (_a = this.runtimeValue) == null ? void 0 : _a.activeSheet().source;
|
|
3560
|
+
const chart = sheet == null ? void 0 : sheet.charts[chartIndex];
|
|
3561
|
+
return chart ? findChartAnchor(sheet.drawings, chart) : void 0;
|
|
3562
|
+
}
|
|
3563
|
+
chartAtPoint(point) {
|
|
3564
|
+
var _a;
|
|
3565
|
+
const sheet = (_a = this.runtimeValue) == null ? void 0 : _a.activeSheet().source;
|
|
3566
|
+
if (!sheet || !this.layout || !this.viewport) return null;
|
|
3567
|
+
for (let chartIndex = sheet.charts.length - 1; chartIndex >= 0; chartIndex -= 1) {
|
|
3568
|
+
const chart = sheet.charts[chartIndex];
|
|
3569
|
+
if (!chart) continue;
|
|
3570
|
+
const anchor = findChartAnchor(sheet.drawings, chart);
|
|
3571
|
+
const logicalRect = chartLogicalRect(this.layout, anchor);
|
|
3572
|
+
if (!logicalRect) continue;
|
|
3573
|
+
if (chartScreenFragments(logicalRect, this.viewport).some((fragment) => containsPoint2(fragment, point))) {
|
|
3574
|
+
return { chartIndex, logicalRect };
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
return null;
|
|
3578
|
+
}
|
|
3579
|
+
updateResizeGuide(drag) {
|
|
3580
|
+
if (drag.kind === "column") {
|
|
3581
|
+
Object.assign(this.resizeGuide.style, {
|
|
3582
|
+
display: "block",
|
|
3583
|
+
left: `${Math.round(drag.currentGuidePosition)}px`,
|
|
3584
|
+
top: "0",
|
|
3585
|
+
bottom: "0",
|
|
3586
|
+
width: "2px",
|
|
3587
|
+
height: ""
|
|
3588
|
+
});
|
|
3589
|
+
} else {
|
|
3590
|
+
Object.assign(this.resizeGuide.style, {
|
|
3591
|
+
display: "block",
|
|
3592
|
+
left: "0",
|
|
3593
|
+
top: `${Math.round(drag.currentGuidePosition)}px`,
|
|
3594
|
+
right: "0",
|
|
3595
|
+
height: "2px",
|
|
3596
|
+
width: ""
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
hideResizeGuide() {
|
|
3601
|
+
this.resizeGuide.style.display = "none";
|
|
3602
|
+
}
|
|
3603
|
+
renderContextMenu(x, y, columnHeader) {
|
|
3604
|
+
const items = [
|
|
3605
|
+
{ label: "Undo", disabled: !this.canUndo(), action: /* @__PURE__ */ __name(() => this.undo(), "action") },
|
|
3606
|
+
{ label: "Redo", disabled: !this.canRedo(), action: /* @__PURE__ */ __name(() => this.redo(), "action") },
|
|
3607
|
+
{ label: "Copy", action: /* @__PURE__ */ __name(() => this.copySelection(), "action") },
|
|
3608
|
+
{ label: "Paste", action: /* @__PURE__ */ __name(() => this.pasteFromClipboard(), "action") },
|
|
3609
|
+
{ label: "Clear cells", action: /* @__PURE__ */ __name(() => this.clearSelection(), "action") },
|
|
3610
|
+
{ label: "Clear format", action: /* @__PURE__ */ __name(() => this.clearSelectionFormat(), "action") },
|
|
3611
|
+
{ label: "Bold", action: /* @__PURE__ */ __name(() => this.applyFormat("bold"), "action") },
|
|
3612
|
+
{ label: "Italic", action: /* @__PURE__ */ __name(() => this.applyFormat("italic"), "action") },
|
|
3613
|
+
{ label: "Underline", action: /* @__PURE__ */ __name(() => this.applyFormat("underline"), "action") },
|
|
3614
|
+
{ label: "Align left", action: /* @__PURE__ */ __name(() => this.applyAlignment("left"), "action") },
|
|
3615
|
+
{ label: "Align center", action: /* @__PURE__ */ __name(() => this.applyAlignment("center"), "action") },
|
|
3616
|
+
{ label: "Align right", action: /* @__PURE__ */ __name(() => this.applyAlignment("right"), "action") },
|
|
3617
|
+
{ label: "Merge cells", action: /* @__PURE__ */ __name(() => this.mergeSelection(), "action") },
|
|
3618
|
+
{ label: "Unmerge cells", action: /* @__PURE__ */ __name(() => this.unmergeSelection(), "action") },
|
|
3619
|
+
{ label: "Freeze above and left", action: /* @__PURE__ */ __name(() => this.freezeSelection(), "action") }
|
|
3620
|
+
];
|
|
3621
|
+
if (columnHeader !== void 0) {
|
|
3622
|
+
items.unshift({
|
|
3623
|
+
label: "Filter column",
|
|
3624
|
+
action: /* @__PURE__ */ __name(() => this.openFilterMenu(columnHeader, x, y), "action")
|
|
3625
|
+
});
|
|
3626
|
+
items.splice(
|
|
3627
|
+
1,
|
|
3628
|
+
0,
|
|
3629
|
+
{ label: "Sort A-Z", action: /* @__PURE__ */ __name(() => this.sortColumn(columnHeader, "asc"), "action") },
|
|
3630
|
+
{ label: "Sort Z-A", action: /* @__PURE__ */ __name(() => this.sortColumn(columnHeader, "desc"), "action") }
|
|
3631
|
+
);
|
|
3632
|
+
}
|
|
3633
|
+
this.contextMenu.replaceChildren();
|
|
3634
|
+
for (const item of items) {
|
|
3635
|
+
const button = document.createElement("button");
|
|
3636
|
+
button.type = "button";
|
|
3637
|
+
button.textContent = item.label;
|
|
3638
|
+
button.disabled = item.disabled === true;
|
|
3639
|
+
Object.assign(button.style, {
|
|
3640
|
+
display: "block",
|
|
3641
|
+
width: "100%",
|
|
3642
|
+
padding: "6px 10px",
|
|
3643
|
+
border: "0",
|
|
3644
|
+
background: "transparent",
|
|
3645
|
+
textAlign: "left",
|
|
3646
|
+
cursor: button.disabled ? "default" : "pointer"
|
|
3647
|
+
});
|
|
3648
|
+
button.addEventListener("mouseenter", () => {
|
|
3649
|
+
if (!button.disabled) button.style.background = "#eff6ff";
|
|
3650
|
+
});
|
|
3651
|
+
button.addEventListener("mouseleave", () => {
|
|
3652
|
+
button.style.background = "transparent";
|
|
3653
|
+
});
|
|
3654
|
+
button.addEventListener("click", () => {
|
|
3655
|
+
this.hideContextMenu();
|
|
3656
|
+
void item.action();
|
|
3657
|
+
});
|
|
3658
|
+
this.contextMenu.appendChild(button);
|
|
3659
|
+
}
|
|
3660
|
+
Object.assign(this.contextMenu.style, { left: `${x}px`, top: `${y}px`, display: "block" });
|
|
3661
|
+
}
|
|
3662
|
+
openFilterMenu(column, x, y) {
|
|
3663
|
+
var _a;
|
|
3664
|
+
if (this.filterMenu.style.display === "block" && this.filterMenuColumn === column) {
|
|
3665
|
+
this.hideFilterMenu();
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
const model = this.runtime().activeSheet();
|
|
3669
|
+
const range = { startRow: 1, endRow: model.maxRow, endColumn: model.maxColumn };
|
|
3670
|
+
const selected = new Set((_a = this.columnFilters.get(column)) != null ? _a : spreadsheetFilterValues(model, range, column));
|
|
3671
|
+
this.filterMenuColumn = column;
|
|
3672
|
+
this.filterMenu.replaceChildren();
|
|
3673
|
+
const title = document.createElement("strong");
|
|
3674
|
+
title.textContent = `Filter ${column}`;
|
|
3675
|
+
title.style.display = "block";
|
|
3676
|
+
title.style.marginBottom = "6px";
|
|
3677
|
+
this.filterMenu.appendChild(title);
|
|
3678
|
+
for (const value of spreadsheetFilterValues(model, range, column)) {
|
|
3679
|
+
const label = document.createElement("label");
|
|
3680
|
+
label.style.display = "block";
|
|
3681
|
+
label.style.padding = "3px 0";
|
|
3682
|
+
const input = document.createElement("input");
|
|
3683
|
+
input.type = "checkbox";
|
|
3684
|
+
input.checked = selected.has(value);
|
|
3685
|
+
input.dataset.filterValue = value;
|
|
3686
|
+
input.style.marginRight = "8px";
|
|
3687
|
+
label.append(input, value === "" ? "(blank)" : value);
|
|
3688
|
+
this.filterMenu.appendChild(label);
|
|
3689
|
+
}
|
|
3690
|
+
const actions = document.createElement("div");
|
|
3691
|
+
actions.style.display = "flex";
|
|
3692
|
+
actions.style.gap = "6px";
|
|
3693
|
+
actions.style.marginTop = "8px";
|
|
3694
|
+
for (const [label, action] of [["Apply", "apply"], ["Clear filter", "clear"], ["Cancel", "cancel"]]) {
|
|
3695
|
+
const button = document.createElement("button");
|
|
3696
|
+
button.type = "button";
|
|
3697
|
+
button.textContent = label;
|
|
3698
|
+
button.dataset.filterAction = action;
|
|
3699
|
+
actions.appendChild(button);
|
|
3700
|
+
}
|
|
3701
|
+
this.filterMenu.appendChild(actions);
|
|
3702
|
+
Object.assign(this.filterMenu.style, { left: `${x}px`, top: `${y}px`, display: "block" });
|
|
3703
|
+
}
|
|
3704
|
+
hideFilterMenu() {
|
|
3705
|
+
this.filterMenu.style.display = "none";
|
|
3706
|
+
this.filterMenuColumn = null;
|
|
3707
|
+
}
|
|
3708
|
+
isFilterIconHit(column, event) {
|
|
3709
|
+
if (!this.layout || !this.viewport) return false;
|
|
3710
|
+
const point = this.eventPoint(event);
|
|
3711
|
+
const viewport = this.viewport.findViewport(point);
|
|
3712
|
+
if (!viewport) return false;
|
|
3713
|
+
const rect = this.viewport.cellScreenRect(1, column, viewport);
|
|
3714
|
+
return rect.width >= 28 && point.x >= rect.x + rect.width - 22 && point.x <= rect.x + rect.width;
|
|
3715
|
+
}
|
|
3716
|
+
async applyColumnFilters() {
|
|
3717
|
+
const model = this.runtime().activeSheet();
|
|
3718
|
+
const filters = Array.from(this.columnFilters.entries()).filter(([, values]) => values.length > 0).map(([column, values]) => ({ column, values }));
|
|
3719
|
+
await this.runtime().filterRowsByColumns(1, model.maxRow, filters);
|
|
3720
|
+
this.render();
|
|
3721
|
+
await this.emitChange();
|
|
3722
|
+
}
|
|
3723
|
+
async sortColumn(column, direction) {
|
|
3724
|
+
const model = this.runtime().activeSheet();
|
|
3725
|
+
await this.runtime().sortRows(1, model.maxRow, column, direction);
|
|
3726
|
+
this.render();
|
|
3727
|
+
await this.emitChange();
|
|
3728
|
+
}
|
|
3729
|
+
async sortActiveColumn(direction) {
|
|
3730
|
+
await this.sortColumn(this.selectionState.active.column, direction);
|
|
3731
|
+
}
|
|
3732
|
+
async filterColumn(column, values) {
|
|
3733
|
+
if (values === null) this.columnFilters.delete(column);
|
|
3734
|
+
else this.columnFilters.set(column, [...values]);
|
|
3735
|
+
await this.applyColumnFilters();
|
|
3736
|
+
}
|
|
3737
|
+
hideContextMenu() {
|
|
3738
|
+
this.contextMenu.style.display = "none";
|
|
3739
|
+
}
|
|
3740
|
+
selectionContainsCell(row, column) {
|
|
3741
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3742
|
+
return row >= range.startRow && row <= range.endRow && column >= range.startColumn && column <= range.endColumn;
|
|
3743
|
+
}
|
|
3744
|
+
async copySelection() {
|
|
3745
|
+
const payload = buildSpreadsheetClipboardPayload(this.runtime().activeSheet(), normalizeSelectionRange(this.selectionState.range));
|
|
3746
|
+
this.clipboardPayload = payload;
|
|
3747
|
+
try {
|
|
3748
|
+
await navigator.clipboard.writeText(payload.text);
|
|
3749
|
+
} catch (e) {
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
async pasteFromClipboard() {
|
|
3753
|
+
var _a;
|
|
3754
|
+
let text = "";
|
|
3755
|
+
try {
|
|
3756
|
+
text = await navigator.clipboard.readText();
|
|
3757
|
+
} catch (e) {
|
|
3758
|
+
return;
|
|
3759
|
+
}
|
|
3760
|
+
if (!text) return;
|
|
3761
|
+
const event = new ClipboardEvent("paste", { clipboardData: new DataTransfer() });
|
|
3762
|
+
(_a = event.clipboardData) == null ? void 0 : _a.setData("text/plain", text);
|
|
3763
|
+
this.handlePaste(event);
|
|
3764
|
+
}
|
|
3765
|
+
async clearSelection() {
|
|
3766
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3767
|
+
await this.runtime().clearRange(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
3768
|
+
this.render();
|
|
3769
|
+
await this.emitChange();
|
|
3770
|
+
}
|
|
3771
|
+
async clearSelectionFormat() {
|
|
3772
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3773
|
+
await this.runtime().clearRangeFormat(range.startRow, range.startColumn, range.endRow - range.startRow + 1, range.endColumn - range.startColumn + 1);
|
|
3774
|
+
this.render();
|
|
3775
|
+
await this.emitChange();
|
|
3776
|
+
}
|
|
3777
|
+
async applyFormat(kind) {
|
|
3778
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3779
|
+
const rows = range.endRow - range.startRow + 1;
|
|
3780
|
+
const columns = range.endColumn - range.startColumn + 1;
|
|
3781
|
+
if (kind === "bold") await this.runtime().setRangeBold(range.startRow, range.startColumn, rows, columns, true);
|
|
3782
|
+
if (kind === "italic") await this.runtime().setRangeItalic(range.startRow, range.startColumn, rows, columns, true);
|
|
3783
|
+
if (kind === "underline") await this.runtime().setRangeUnderline(range.startRow, range.startColumn, rows, columns, true);
|
|
3784
|
+
this.render();
|
|
3785
|
+
await this.emitChange();
|
|
3786
|
+
}
|
|
3787
|
+
async applyAlignment(alignment) {
|
|
3788
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3789
|
+
await this.runtime().setRangeHorizontalAlign(
|
|
3790
|
+
range.startRow,
|
|
3791
|
+
range.startColumn,
|
|
3792
|
+
range.endRow - range.startRow + 1,
|
|
3793
|
+
range.endColumn - range.startColumn + 1,
|
|
3794
|
+
alignment
|
|
3795
|
+
);
|
|
3796
|
+
this.render();
|
|
3797
|
+
await this.emitChange();
|
|
3798
|
+
}
|
|
3799
|
+
async mergeSelection() {
|
|
3800
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3801
|
+
if (range.startRow === range.endRow && range.startColumn === range.endColumn) return;
|
|
3802
|
+
await this.runtime().mergeRange(
|
|
3803
|
+
range.startRow,
|
|
3804
|
+
range.startColumn,
|
|
3805
|
+
range.endRow - range.startRow + 1,
|
|
3806
|
+
range.endColumn - range.startColumn + 1
|
|
3807
|
+
);
|
|
3808
|
+
this.selectionState = expandSelectionForMergedRanges(this.selectionState, this.runtime().activeSheet());
|
|
3809
|
+
this.render();
|
|
3810
|
+
await this.emitChange();
|
|
3811
|
+
}
|
|
3812
|
+
async unmergeSelection() {
|
|
3813
|
+
const range = normalizeSelectionRange(this.selectionState.range);
|
|
3814
|
+
const addresses = [...this.runtime().activeSheet().mergeMasterByAddress.values()].filter((merge) => rangesIntersect4(range, merge.range)).map((merge) => merge.address);
|
|
3815
|
+
if (addresses.length === 0) return;
|
|
3816
|
+
await this.runtime().unmergeRanges(addresses);
|
|
3817
|
+
this.render();
|
|
3818
|
+
await this.emitChange();
|
|
3819
|
+
}
|
|
3820
|
+
async freezeSelection() {
|
|
3821
|
+
const active = this.selectionState.active;
|
|
3822
|
+
await this.runtime().setFreezePanes(Math.max(0, active.row - 1), Math.max(0, active.column - 1));
|
|
3823
|
+
this.render();
|
|
3824
|
+
await this.emitChange();
|
|
3825
|
+
}
|
|
3826
|
+
async commitResizeDrag(drag) {
|
|
3827
|
+
if (drag.currentSize === drag.startSize) {
|
|
3828
|
+
this.render();
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
if (drag.kind === "column") await this.runtime().setColumnWidth(drag.index, drag.currentSize);
|
|
3832
|
+
else await this.runtime().setRowHeight(drag.index, drag.currentSize * 72 / 96);
|
|
3833
|
+
this.render();
|
|
3834
|
+
await this.emitChange();
|
|
3835
|
+
}
|
|
3836
|
+
openCellEditor() {
|
|
3837
|
+
var _a, _b;
|
|
3838
|
+
if (!this.layout || !this.viewport) return;
|
|
3839
|
+
const cell = this.selectionState.active;
|
|
3840
|
+
const viewport = this.viewport.findViewport({
|
|
3841
|
+
x: this.layout.rowHeaderWidth + 1,
|
|
3842
|
+
y: this.layout.columnHeaderHeight + 1
|
|
3843
|
+
});
|
|
3844
|
+
const renderCell = getRenderCell(this.runtime().activeSheet(), cell.row, cell.column);
|
|
3845
|
+
const cellStyle = resolveSpreadsheetCellStyle(this.runtime().activeSheet().styles, renderCell == null ? void 0 : renderCell.styleId, renderCell);
|
|
3846
|
+
Object.assign(this.cellEditorElement.style, {
|
|
3847
|
+
font: cellStyle.font,
|
|
3848
|
+
color: cellStyle.fontColor,
|
|
3849
|
+
textDecoration: cellStyle.underline ? "underline" : "none",
|
|
3850
|
+
textAlign: cellStyle.horizontalAlign
|
|
3851
|
+
});
|
|
3852
|
+
const targetViewport = (_a = this.viewport.viewports.find((item) => item.kind === "main")) != null ? _a : viewport;
|
|
3853
|
+
if (!targetViewport) return;
|
|
3854
|
+
const editRange = normalizeSelectionRange(this.selectionState.range);
|
|
3855
|
+
const startRect = this.viewport.cellScreenRect(editRange.startRow, editRange.startColumn, targetViewport);
|
|
3856
|
+
const endRect = this.viewport.cellScreenRect(editRange.endRow, editRange.endColumn, targetViewport);
|
|
3857
|
+
const rect = {
|
|
3858
|
+
x: startRect.x,
|
|
3859
|
+
y: startRect.y,
|
|
3860
|
+
width: endRect.x + endRect.width - startRect.x,
|
|
3861
|
+
height: endRect.y + endRect.height - startRect.y
|
|
3862
|
+
};
|
|
3863
|
+
this.editingCell = cell;
|
|
3864
|
+
this.editingCellStyle = cellStyle;
|
|
3865
|
+
const value = (renderCell == null ? void 0 : renderCell.formula) ? `=${renderCell.formula}` : (_b = renderCell == null ? void 0 : renderCell.displayText) != null ? _b : "";
|
|
3866
|
+
this.formulaReferenceRanges = formulaReferenceRangesFromText(value);
|
|
3867
|
+
this.cellEditor.open(rect, value);
|
|
3868
|
+
this.alignCellEditor(rect, cellStyle);
|
|
3869
|
+
this.render();
|
|
3870
|
+
}
|
|
3871
|
+
async commitCellEdit(value) {
|
|
3872
|
+
const cell = this.editingCell;
|
|
3873
|
+
this.editingCell = null;
|
|
3874
|
+
this.editingCellStyle = null;
|
|
3875
|
+
this.formulaReferenceRanges = [];
|
|
3876
|
+
if (!cell) return;
|
|
3877
|
+
const address = formatCellAddress(cell.row, cell.column);
|
|
3878
|
+
if (value.startsWith("=")) await this.runtime().setCellFormula(address, value.slice(1), { mergeUndo: true });
|
|
3879
|
+
else await this.runtime().setCellValue(address, coerceEditorValue(value), { mergeUndo: true });
|
|
3880
|
+
this.render();
|
|
3881
|
+
await this.emitChange();
|
|
3882
|
+
}
|
|
3883
|
+
eventPoint(event) {
|
|
3884
|
+
const rect = this.host.getBoundingClientRect();
|
|
3885
|
+
return { x: event.clientX - rect.left, y: event.clientY - rect.top };
|
|
3886
|
+
}
|
|
3887
|
+
eventPathContains(event, node) {
|
|
3888
|
+
return event.composedPath().includes(node);
|
|
3889
|
+
}
|
|
3890
|
+
updateCellEditorPosition() {
|
|
3891
|
+
const cell = this.editingCell;
|
|
3892
|
+
const layout = this.layout;
|
|
3893
|
+
const viewportManager = this.viewport;
|
|
3894
|
+
if (!cell || !layout || !viewportManager) return;
|
|
3895
|
+
const targetViewport = viewportManager.viewports.find(
|
|
3896
|
+
(viewport) => viewport.kind !== "columnHeaderFrozen" && viewport.kind !== "columnHeaderScrollable" && viewport.kind !== "rowHeaderFrozen" && viewport.kind !== "rowHeaderScrollable" && cell.row >= viewport.rows.start && cell.row <= viewport.rows.end && cell.column >= viewport.columns.start && cell.column <= viewport.columns.end
|
|
3897
|
+
);
|
|
3898
|
+
if (!targetViewport) {
|
|
3899
|
+
this.cellEditorElement.style.display = "none";
|
|
3900
|
+
return;
|
|
3901
|
+
}
|
|
3902
|
+
const editRange = normalizeSelectionRange(this.selectionState.range);
|
|
3903
|
+
const startRect = viewportManager.cellScreenRect(editRange.startRow, editRange.startColumn, targetViewport);
|
|
3904
|
+
const endRect = viewportManager.cellScreenRect(editRange.endRow, editRange.endColumn, targetViewport);
|
|
3905
|
+
const rect = {
|
|
3906
|
+
x: startRect.x,
|
|
3907
|
+
y: startRect.y,
|
|
3908
|
+
width: endRect.x + endRect.width - startRect.x,
|
|
3909
|
+
height: endRect.y + endRect.height - startRect.y
|
|
3910
|
+
};
|
|
3911
|
+
this.cellEditorElement.style.left = `${rect.x}px`;
|
|
3912
|
+
this.cellEditorElement.style.top = `${rect.y}px`;
|
|
3913
|
+
this.cellEditorElement.style.width = `${Math.max(1, rect.width)}px`;
|
|
3914
|
+
this.cellEditorElement.style.height = `${Math.max(1, rect.height)}px`;
|
|
3915
|
+
this.cellEditorElement.style.display = "block";
|
|
3916
|
+
if (this.editingCellStyle) this.alignCellEditor(rect, this.editingCellStyle);
|
|
3917
|
+
}
|
|
3918
|
+
alignCellEditor(rect, style) {
|
|
3919
|
+
const textarea = this.cellEditorElement;
|
|
3920
|
+
const paddingX = 4;
|
|
3921
|
+
const paddingY = 2;
|
|
3922
|
+
textarea.style.padding = `${paddingY}px ${paddingX}px`;
|
|
3923
|
+
const extraHeight = Math.max(0, rect.height - textarea.scrollHeight);
|
|
3924
|
+
const topPadding = style.verticalAlign === "bottom" ? paddingY + extraHeight : style.verticalAlign === "middle" ? paddingY + extraHeight / 2 : paddingY;
|
|
3925
|
+
textarea.style.paddingTop = `${topPadding}px`;
|
|
3926
|
+
}
|
|
3927
|
+
async emitChange() {
|
|
3928
|
+
var _a, _b;
|
|
3929
|
+
if (this.transactionDepth > 0) {
|
|
3930
|
+
this.transactionChanged = true;
|
|
3931
|
+
return;
|
|
3932
|
+
}
|
|
3933
|
+
const event = { kind: "workbookChange", bytes: await this.toZip() };
|
|
3934
|
+
for (const listener of this.listeners) listener(event);
|
|
3935
|
+
await ((_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, event));
|
|
3936
|
+
}
|
|
3937
|
+
emitStateIfChanged(activeSheetName) {
|
|
3938
|
+
var _a, _b, _c, _d;
|
|
3939
|
+
const selection = this.selectionState;
|
|
3940
|
+
const selectionKey = `${selection.kind}:${selection.active.row}:${selection.active.column}:${selection.range.startRow}:${selection.range.startColumn}:${selection.range.endRow}:${selection.range.endColumn}:${(_b = (_a = this.editingCell) == null ? void 0 : _a.row) != null ? _b : 0}:${(_d = (_c = this.editingCell) == null ? void 0 : _c.column) != null ? _d : 0}`;
|
|
3941
|
+
const stateKey = `${activeSheetName}|${selectionKey}`;
|
|
3942
|
+
if (stateKey === this.lastStateKey) return;
|
|
3943
|
+
this.lastStateKey = stateKey;
|
|
3944
|
+
const event = {
|
|
3945
|
+
kind: "stateChange",
|
|
3946
|
+
activeSheetName,
|
|
3947
|
+
selection,
|
|
3948
|
+
editingCell: this.editingCell
|
|
3949
|
+
};
|
|
3950
|
+
for (const listener of this.stateListeners) listener(event);
|
|
3951
|
+
}
|
|
3952
|
+
renderTabs() {
|
|
3953
|
+
if (this.options.showSheetTabs === false || !this.runtimeValue) return;
|
|
3954
|
+
this.sheetTabs.replaceChildren();
|
|
3955
|
+
Object.assign(this.sheetTabs.style, {
|
|
3956
|
+
position: "absolute",
|
|
3957
|
+
left: "0",
|
|
3958
|
+
right: "0",
|
|
3959
|
+
bottom: "0",
|
|
3960
|
+
height: "40px",
|
|
3961
|
+
display: "flex",
|
|
3962
|
+
alignItems: "center",
|
|
3963
|
+
gap: "8px",
|
|
3964
|
+
padding: "0 12px",
|
|
3965
|
+
background: "#eef1f5",
|
|
3966
|
+
borderTop: "1px solid #e2e8f0",
|
|
3967
|
+
boxSizing: "border-box",
|
|
3968
|
+
overflowX: "auto",
|
|
3969
|
+
overflowY: "hidden"
|
|
3970
|
+
});
|
|
3971
|
+
for (const sheet of this.runtimeValue.model().sheets) {
|
|
3972
|
+
const button = document.createElement("button");
|
|
3973
|
+
button.type = "button";
|
|
3974
|
+
button.textContent = sheet.name;
|
|
3975
|
+
button.dataset.active = String(sheet.name === this.runtimeValue.activeSheetName());
|
|
3976
|
+
Object.assign(button.style, {
|
|
3977
|
+
height: "32px",
|
|
3978
|
+
minWidth: "64px",
|
|
3979
|
+
padding: "0 14px",
|
|
3980
|
+
border: "0",
|
|
3981
|
+
borderTop: sheet.name === this.runtimeValue.activeSheetName() ? "2px solid #2563eb" : "2px solid transparent",
|
|
3982
|
+
borderRadius: "3px 3px 0 0",
|
|
3983
|
+
background: sheet.name === this.runtimeValue.activeSheetName() ? "#ffffff" : "transparent",
|
|
3984
|
+
color: sheet.name === this.runtimeValue.activeSheetName() ? "#1d4ed8" : "#475569",
|
|
3985
|
+
font: "13px Arial, sans-serif",
|
|
3986
|
+
fontWeight: sheet.name === this.runtimeValue.activeSheetName() ? "600" : "400",
|
|
3987
|
+
cursor: "pointer",
|
|
3988
|
+
whiteSpace: "nowrap"
|
|
3989
|
+
});
|
|
3990
|
+
button.addEventListener("mouseenter", () => {
|
|
3991
|
+
if (button.dataset.active !== "true") button.style.background = "#e2e8f0";
|
|
3992
|
+
});
|
|
3993
|
+
button.addEventListener("mouseleave", () => {
|
|
3994
|
+
if (button.dataset.active !== "true") button.style.background = "transparent";
|
|
3995
|
+
});
|
|
3996
|
+
button.addEventListener("click", () => void this.setActiveSheet(sheet.name));
|
|
3997
|
+
this.sheetTabs.appendChild(button);
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
configureScrollbar(track, thumb, horizontal) {
|
|
4001
|
+
track.className = "coco-spreadsheet-editor-scrollbar";
|
|
4002
|
+
Object.assign(track.style, __spreadValues({
|
|
4003
|
+
position: "absolute",
|
|
4004
|
+
zIndex: "10",
|
|
4005
|
+
background: "#f1f5f9",
|
|
4006
|
+
border: "1px solid #cbd5e1",
|
|
4007
|
+
cursor: "pointer"
|
|
4008
|
+
}, horizontal ? { left: "48px", bottom: "40px", height: "12px" } : { top: "24px", right: "0", bottom: "52px", width: "12px" }));
|
|
4009
|
+
thumb.className = "coco-spreadsheet-editor-scrollbar-thumb";
|
|
4010
|
+
Object.assign(thumb.style, { position: "absolute", background: "#94a3b8", borderRadius: "999px" });
|
|
4011
|
+
track.appendChild(thumb);
|
|
4012
|
+
track.addEventListener("pointerdown", (event) => {
|
|
4013
|
+
var _a;
|
|
4014
|
+
const geometry = (_a = this.scrollbarState) == null ? void 0 : _a[horizontal ? "horizontal" : "vertical"];
|
|
4015
|
+
if (!geometry || geometry.maxScroll <= 0) return;
|
|
4016
|
+
const rect = track.getBoundingClientRect();
|
|
4017
|
+
const pointer = horizontal ? event.clientX - rect.left : event.clientY - rect.top;
|
|
4018
|
+
this.drag = { axis: horizontal ? "horizontal" : "vertical", pointer, thumb: geometry.thumbStart };
|
|
4019
|
+
track.setPointerCapture(event.pointerId);
|
|
4020
|
+
this.setScroll(horizontal ? scrollFromThumbPosition(geometry, pointer - geometry.thumbLength / 2) : this.scroll.x, horizontal ? this.scroll.y : scrollFromThumbPosition(geometry, pointer - geometry.thumbLength / 2));
|
|
4021
|
+
});
|
|
4022
|
+
track.addEventListener("pointermove", (event) => {
|
|
4023
|
+
var _a;
|
|
4024
|
+
const drag = this.drag;
|
|
4025
|
+
const geometry = (_a = this.scrollbarState) == null ? void 0 : _a[horizontal ? "horizontal" : "vertical"];
|
|
4026
|
+
if (!drag || !geometry || drag.axis !== (horizontal ? "horizontal" : "vertical")) return;
|
|
4027
|
+
const rect = track.getBoundingClientRect();
|
|
4028
|
+
const pointer = horizontal ? event.clientX - rect.left : event.clientY - rect.top;
|
|
4029
|
+
const next = scrollFromThumbPosition(geometry, drag.thumb + pointer - drag.pointer);
|
|
4030
|
+
this.setScroll(horizontal ? next : this.scroll.x, horizontal ? this.scroll.y : next);
|
|
4031
|
+
});
|
|
4032
|
+
track.addEventListener("pointerup", () => {
|
|
4033
|
+
this.drag = null;
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
updateScrollbars() {
|
|
4037
|
+
const state = this.scrollbarState;
|
|
4038
|
+
const layout = this.layout;
|
|
4039
|
+
const viewport = this.viewport;
|
|
4040
|
+
if (!state || !layout || !viewport) return;
|
|
4041
|
+
const horizontal = state.horizontal;
|
|
4042
|
+
const vertical = state.vertical;
|
|
4043
|
+
this.horizontalScrollbar.style.display = horizontal.maxScroll > 0 ? "block" : "none";
|
|
4044
|
+
this.verticalScrollbar.style.display = vertical.maxScroll > 0 ? "block" : "none";
|
|
4045
|
+
Object.assign(this.horizontalScrollbar.style, { left: `${layout.rowHeaderWidth + layout.getColumnX(viewport.freeze.frozenColumns + 1)}px`, width: `${horizontal.trackLength}px` });
|
|
4046
|
+
Object.assign(this.horizontalThumb.style, { left: `${horizontal.thumbStart}px`, width: `${horizontal.thumbLength}px`, top: "1px", bottom: "1px" });
|
|
4047
|
+
Object.assign(this.verticalScrollbar.style, { top: `${layout.columnHeaderHeight + layout.getRowY(viewport.freeze.frozenRows + 1)}px`, height: `${vertical.trackLength}px` });
|
|
4048
|
+
Object.assign(this.verticalThumb.style, { top: `${vertical.thumbStart}px`, height: `${vertical.thumbLength}px`, left: "1px", right: "1px" });
|
|
4049
|
+
}
|
|
4050
|
+
setScroll(x, y) {
|
|
4051
|
+
this.scroll = { x, y };
|
|
4052
|
+
this.render();
|
|
4053
|
+
}
|
|
4054
|
+
assertAlive() {
|
|
4055
|
+
if (this.disposed) throw new Error("SpreadsheetEditor: editor has been destroyed");
|
|
4056
|
+
}
|
|
4057
|
+
};
|
|
4058
|
+
__name(_NativeSpreadsheetEditor, "NativeSpreadsheetEditor");
|
|
4059
|
+
var NativeSpreadsheetEditor = _NativeSpreadsheetEditor;
|
|
4060
|
+
function coerceEditorValue(value) {
|
|
4061
|
+
if (value === "") return null;
|
|
4062
|
+
if (value === "TRUE") return true;
|
|
4063
|
+
if (value === "FALSE") return false;
|
|
4064
|
+
const number = Number(value);
|
|
4065
|
+
return Number.isFinite(number) && value.trim() !== "" ? number : value;
|
|
4066
|
+
}
|
|
4067
|
+
__name(coerceEditorValue, "coerceEditorValue");
|
|
4068
|
+
function containsPoint2(rect, point) {
|
|
4069
|
+
return point.x >= rect.x && point.y >= rect.y && point.x <= rect.x + rect.width && point.y <= rect.y + rect.height;
|
|
4070
|
+
}
|
|
4071
|
+
__name(containsPoint2, "containsPoint");
|
|
4072
|
+
function findChartAnchor(drawings, chart) {
|
|
4073
|
+
var _a;
|
|
4074
|
+
const drawing = drawings.find((item) => item.path === chart.drawingPath);
|
|
4075
|
+
const anchors = (_a = drawing == null ? void 0 : drawing.anchors) != null ? _a : drawings.flatMap((item) => item.anchors);
|
|
4076
|
+
return anchors.find(
|
|
4077
|
+
(anchor) => {
|
|
4078
|
+
var _a2;
|
|
4079
|
+
return ((_a2 = anchor.object) == null ? void 0 : _a2.kind) === "chart" && (!chart.relationshipId || anchor.object.relationshipId === chart.relationshipId);
|
|
4080
|
+
}
|
|
4081
|
+
);
|
|
4082
|
+
}
|
|
4083
|
+
__name(findChartAnchor, "findChartAnchor");
|
|
4084
|
+
function chartLogicalRect(layout, anchor) {
|
|
4085
|
+
var _a, _b;
|
|
4086
|
+
if (!anchor || anchor.kind === "absolute" || !anchor.from) return null;
|
|
4087
|
+
const startColumn = markerIndex(anchor.from.column);
|
|
4088
|
+
const startRow = markerIndex(anchor.from.row);
|
|
4089
|
+
const endColumn = anchor.to ? markerIndex(anchor.to.column) : startColumn + 5;
|
|
4090
|
+
const endRow = anchor.to ? markerIndex(anchor.to.row) : startRow + 12;
|
|
4091
|
+
const startX = layout.getColumnX(startColumn) + markerOffset(anchor.from.columnOffset);
|
|
4092
|
+
const startY = layout.getRowY(startRow) + markerOffset(anchor.from.rowOffset);
|
|
4093
|
+
const endX = layout.getColumnX(endColumn) + markerOffset((_a = anchor.to) == null ? void 0 : _a.columnOffset);
|
|
4094
|
+
const endY = layout.getRowY(endRow) + markerOffset((_b = anchor.to) == null ? void 0 : _b.rowOffset);
|
|
4095
|
+
return {
|
|
4096
|
+
x: startX,
|
|
4097
|
+
y: startY,
|
|
4098
|
+
width: Math.max(1, endX - startX),
|
|
4099
|
+
height: Math.max(1, endY - startY)
|
|
4100
|
+
};
|
|
4101
|
+
}
|
|
4102
|
+
__name(chartLogicalRect, "chartLogicalRect");
|
|
4103
|
+
function chartScreenFragments(logicalRect, viewport) {
|
|
4104
|
+
const fragments = [];
|
|
4105
|
+
for (const item of viewport.viewports) {
|
|
4106
|
+
if (!isBodyViewport(item)) continue;
|
|
4107
|
+
const visibleLeft = item.originX;
|
|
4108
|
+
const visibleTop = item.originY;
|
|
4109
|
+
const visibleRight = item.originX + item.screenRect.width;
|
|
4110
|
+
const visibleBottom = item.originY + item.screenRect.height;
|
|
4111
|
+
const left = Math.max(logicalRect.x, visibleLeft);
|
|
4112
|
+
const top = Math.max(logicalRect.y, visibleTop);
|
|
4113
|
+
const right = Math.min(logicalRect.x + logicalRect.width, visibleRight);
|
|
4114
|
+
const bottom = Math.min(logicalRect.y + logicalRect.height, visibleBottom);
|
|
4115
|
+
if (right <= left || bottom <= top) continue;
|
|
4116
|
+
fragments.push({
|
|
4117
|
+
x: item.screenRect.x + left - item.originX,
|
|
4118
|
+
y: item.screenRect.y + top - item.originY,
|
|
4119
|
+
width: right - left,
|
|
4120
|
+
height: bottom - top
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4123
|
+
return fragments;
|
|
4124
|
+
}
|
|
4125
|
+
__name(chartScreenFragments, "chartScreenFragments");
|
|
4126
|
+
function chartAnchorForLogicalRect(layout, rect, original) {
|
|
4127
|
+
const marker = /* @__PURE__ */ __name((x, y) => {
|
|
4128
|
+
const column = layout.columnAt(Math.max(0, x));
|
|
4129
|
+
const row = layout.rowAt(Math.max(0, y));
|
|
4130
|
+
return {
|
|
4131
|
+
column: column - 1,
|
|
4132
|
+
row: row - 1,
|
|
4133
|
+
columnOffset: Math.round((x - layout.getColumnX(column)) * 9525),
|
|
4134
|
+
rowOffset: Math.round((y - layout.getRowY(row)) * 9525)
|
|
4135
|
+
};
|
|
4136
|
+
}, "marker");
|
|
4137
|
+
const from = marker(rect.x, rect.y);
|
|
4138
|
+
const to = original.to ? marker(rect.x + rect.width, rect.y + rect.height) : void 0;
|
|
4139
|
+
return { from, to };
|
|
4140
|
+
}
|
|
4141
|
+
__name(chartAnchorForLogicalRect, "chartAnchorForLogicalRect");
|
|
4142
|
+
function isBodyViewport(viewport) {
|
|
4143
|
+
return viewport.kind === "main" || viewport.kind === "frozenTop" || viewport.kind === "frozenLeft" || viewport.kind === "frozenTopLeft";
|
|
4144
|
+
}
|
|
4145
|
+
__name(isBodyViewport, "isBodyViewport");
|
|
4146
|
+
function markerIndex(value) {
|
|
4147
|
+
return Math.max(1, Math.floor(value != null ? value : 0) + 1);
|
|
4148
|
+
}
|
|
4149
|
+
__name(markerIndex, "markerIndex");
|
|
4150
|
+
function markerOffset(value) {
|
|
4151
|
+
return Number.isFinite(value) ? (value != null ? value : 0) / 9525 : 0;
|
|
4152
|
+
}
|
|
4153
|
+
__name(markerOffset, "markerOffset");
|
|
4154
|
+
function columnNumberFromLetters(letters) {
|
|
4155
|
+
let value = 0;
|
|
4156
|
+
for (const character of letters.toUpperCase()) {
|
|
4157
|
+
value = value * 26 + character.charCodeAt(0) - 64;
|
|
4158
|
+
}
|
|
4159
|
+
return value;
|
|
4160
|
+
}
|
|
4161
|
+
__name(columnNumberFromLetters, "columnNumberFromLetters");
|
|
4162
|
+
function autoFillPreviewRangeForHit(source, hit) {
|
|
4163
|
+
if (hit.kind !== "cell") return null;
|
|
4164
|
+
const down = Math.max(0, hit.row - source.endRow);
|
|
4165
|
+
const right = Math.max(0, hit.column - source.endColumn);
|
|
4166
|
+
if (!down && !right) return null;
|
|
4167
|
+
if (down >= right) return __spreadProps(__spreadValues({}, source), { endRow: source.endRow + down });
|
|
4168
|
+
return __spreadProps(__spreadValues({}, source), { endColumn: source.endColumn + right });
|
|
4169
|
+
}
|
|
4170
|
+
__name(autoFillPreviewRangeForHit, "autoFillPreviewRangeForHit");
|
|
4171
|
+
function rangesIntersect4(left, right) {
|
|
4172
|
+
return !(left.endRow < right.startRow || left.startRow > right.endRow || left.endColumn < right.startColumn || left.startColumn > right.endColumn);
|
|
4173
|
+
}
|
|
4174
|
+
__name(rangesIntersect4, "rangesIntersect");
|
|
4175
|
+
function formulaReferenceRangesFromText(text) {
|
|
4176
|
+
var _a;
|
|
4177
|
+
if (!text.startsWith("=")) return [];
|
|
4178
|
+
const references = (_a = text.match(/(?:\$?[A-Z]{1,3}\$?\d+)(?::(?:\$?[A-Z]{1,3}\$?\d+))?/g)) != null ? _a : [];
|
|
4179
|
+
const colors = ["#DC2626", "#16A34A", "#7C3AED", "#EA580C", "#0891B2"];
|
|
4180
|
+
return references.flatMap((reference, index) => {
|
|
4181
|
+
var _a2, _b, _c;
|
|
4182
|
+
const parts = reference.split(":");
|
|
4183
|
+
const start = (_a2 = parts[0]) != null ? _a2 : "";
|
|
4184
|
+
const end = (_b = parts[1]) != null ? _b : start;
|
|
4185
|
+
const first = formulaReferenceCell(start);
|
|
4186
|
+
const last = formulaReferenceCell(end);
|
|
4187
|
+
if (!first || !last) return [];
|
|
4188
|
+
return [{
|
|
4189
|
+
range: normalizeSelectionRange({
|
|
4190
|
+
startRow: first.row,
|
|
4191
|
+
startColumn: first.column,
|
|
4192
|
+
endRow: last.row,
|
|
4193
|
+
endColumn: last.column
|
|
4194
|
+
}),
|
|
4195
|
+
stroke: (_c = colors[index % colors.length]) != null ? _c : "#DC2626"
|
|
4196
|
+
}];
|
|
4197
|
+
});
|
|
4198
|
+
}
|
|
4199
|
+
__name(formulaReferenceRangesFromText, "formulaReferenceRangesFromText");
|
|
4200
|
+
function formulaReferenceCell(value) {
|
|
4201
|
+
var _a, _b;
|
|
4202
|
+
const match = value.replace(/\$/g, "").match(/^([A-Z]{1,3})(\d+)$/);
|
|
4203
|
+
if (!match) return null;
|
|
4204
|
+
let column = 0;
|
|
4205
|
+
for (const character of (_a = match[1]) != null ? _a : "") column = column * 26 + character.charCodeAt(0) - 64;
|
|
4206
|
+
const row = Number((_b = match[2]) != null ? _b : "");
|
|
4207
|
+
return Number.isFinite(row) && row > 0 && column > 0 ? { row, column } : null;
|
|
4208
|
+
}
|
|
4209
|
+
__name(formulaReferenceCell, "formulaReferenceCell");
|
|
4210
|
+
|
|
4211
|
+
export { SPREADSHEET_COLUMN_RESIZE_MIN, SPREADSHEET_RESIZE_HIT_TOLERANCE, SPREADSHEET_ROW_RESIZE_MIN, SpreadsheetCanvasRenderer, SpreadsheetCellEditor, SpreadsheetEditorRuntime, SpreadsheetLayout, SpreadsheetViewportManager, applySpreadsheetAgentAction, applyWheelDelta, buildSpreadsheetAutoFillPlan, buildSpreadsheetClipboardPayload, buildSpreadsheetReplacePlan, buildSpreadsheetSheetRenderModel, clampScroll, clearSpreadsheetRange, computeScrollbarState, createColumnRangeSelection, createColumnSelection, createRowRangeSelection, createRowSelection, createSelection, cssPixelsToExcelColumnWidth, excelColumnWidthToCssPixels, expandRangeForMergedRanges, expandSelectionForMergedRanges, extendSelection, findSpreadsheetMatches, formatSpreadsheetDisplayValue, freezeStateFromPane, freezeStateFromSheet, getRenderCell, hitSpreadsheetResizeHandle, moveSelection, nextSpreadsheetMatch, normalizeSelectionRange, parseTsv, previewSpreadsheetAgentAction, rangeAddressForMatrix, rangeForSpreadsheetMatch, renderSpreadsheetEditor, resolveSpreadsheetCellStyle, resolveSpreadsheetRangeBorder, rowColumnKey, scrollFromThumbPosition, selectionAfterColumnDeletion, selectionAfterRowDeletion, serializeRangeToTsv, setSpreadsheetCellFormula, setSpreadsheetCellValue, setSpreadsheetColumnWidth, setSpreadsheetRangeValues, setSpreadsheetRowHeight, spreadsheetFilterValues, spreadsheetRowsForColumnFilters, spreadsheetRowsForFilter, spreadsheetSortRows, spreadsheetVisibleFilterValues, startSpreadsheetResizeDrag, summarizeSpreadsheetSelectionFormat, supportsSpreadsheetAutoFill, updateSpreadsheetResizeDrag };
|
|
4212
|
+
//# sourceMappingURL=spreadsheet.js.map
|
|
4213
|
+
//# sourceMappingURL=spreadsheet.js.map
|