@a3s-lab/office 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1950,7 +1950,7 @@ async function diagnoseXlsxFormulas(archive, importedFeatures, worksheetScans) {
1950
1950
  if (dataTables) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.data-tables', `${dataTables} what-if data table(s), input references, and cached results are preserved, but Work does not recalculate scenario tables in the browser.`));
1951
1951
  if (cachedErrors) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.cached-errors', `${cachedErrors} formula cell(s) contain cached Excel error values; the error type and displayed value are preserved in editing, preview, print, and export.`));
1952
1952
  if (externalReferences.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.external-references', `${externalReferences.length} formula cell(s) reference external workbooks; formulas and cached values are preserved without refreshing external files.`));
1953
- if (structuredReferences.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.structured-references', `${structuredReferences.length} formula cell(s) use structured table references; formulas round-trip unchanged, but the Work calculation engine does not resolve Excel table objects.`));
1953
+ if (structuredReferences.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.structured-references', `${structuredReferences.length} formula cell(s) use structured table references; the browser kernel resolves bounded table names, display names, contiguous column ranges, and common row selectors. Disjoint, external, and advanced forms keep their source formula and cached value for compatible recalculation.`, 'info'));
1954
1954
  if (unsupportedFunctions.size) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.unsupported-functions', `The current browser calculation engine does not evaluate ${Array.from(unsupportedFunctions).sort().join(', ')}; source formulas and cached results are preserved for compatible desktop recalculation.`));
1955
1955
  if (volatileFormulas.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.volatile', `${volatileFormulas.length} volatile formula cell(s) can change when the workbook is explicitly recalculated.`, 'info'));
1956
1956
  if (unsupportedFormulaAttributes.size) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.attributes', `Advanced formula attributes are not represented by the editable model and normalize on export: ${Array.from(unsupportedFormulaAttributes).sort().join(', ')}.`));
package/dist/3266.js CHANGED
@@ -1,10 +1,512 @@
1
+ import { formulaHasStructuredReference, sparseArrayEntries as spreadsheet_sparse_sparseArrayEntries, formulaHasExternalReference, spreadsheetFormulaFunctions, editableSpreadsheetFormula, isValidSpreadsheetDefinedName } from "./8715.js";
1
2
  import { workOfficeCollaborationJsonEqual, canonicalWorkOfficeCollaborationJson, isWorkOfficeCollaborationRecord, cloneWorkOfficeCollaborationJson } from "./4650.js";
2
3
  import { initializeWorkOfficeCollaborationMetadata, assertWorkOfficeCollaborationEditable, OfficeCollaborationError as WorkOfficeCollaborationError, readOfficeCollaborationMetadata as readWorkOfficeCollaborationMetadata, registerWorkOfficeCollaborationInitializer, markWorkOfficeCollaborationInitialized, assertWorkOfficeCollaborationOrigin } from "./9787.js";
3
4
  import { directChild, directChildren, descendants, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, attribute } from "./4121.js";
4
- import { isValidSpreadsheetDefinedName, sparseArrayEntries } from "./8715.js";
5
5
  import { patchWorkOfficeCollaborationFlatJsonMap, readWorkOfficeCollaborationFlatJsonMap } from "./7060.js";
6
6
  import { resolveXlsxColor, createXlsxColorResolver, normalizeXlsxSemanticColorOrigin, xlsxPatternFillKey, XLSX_PATTERN_FILL_CELL_KEY, xlsxCellAddress, xlsxSemanticColorMatchesValue, xlsxGradientFillSemanticColors, xlsxColorElementMatchesOrigin, decodeXlsxCellAddress, xlsxSemanticColorOriginSupported, deleteXlsxPatternFill, xlsxCellStyleOrigin, xlsxRgbColor, xlsxPatternFillSemanticColors, activeXlsxGradientFill, xlsxGradientFillKey, applyXlsxSemanticColorOrigin, deleteXlsxGradientFill, XLSX_GRADIENT_FILL_CELL_KEY, readXlsxSemanticColorOrigin, activeXlsxPatternFill } from "./9333.js";
7
7
  import * as __rspack_external_yjs from "yjs";
8
+ const MAX_SPREADSHEET_TABLE_CALCULATED_FORMULA_LENGTH = 8192;
9
+ const UNSAFE_SPREADSHEET_TABLE_CALCULATED_FUNCTIONS = new Set([
10
+ 'CALL',
11
+ 'DDE',
12
+ 'DDE.REQUEST',
13
+ 'DDE.POKE',
14
+ 'EXEC',
15
+ 'HYPERLINK',
16
+ 'INDIRECT',
17
+ 'OFFSET',
18
+ 'REGISTER.ID',
19
+ 'RTD',
20
+ 'WEBSERVICE'
21
+ ]);
22
+ function normalizeSpreadsheetTableCalculatedFormula(value) {
23
+ if ('string' != typeof value) return;
24
+ const trimmed = value.trim();
25
+ if (!trimmed) return;
26
+ const formula = trimmed.startsWith('=') ? trimmed : `=${trimmed}`;
27
+ if (formula.startsWith('==') || formula.length > MAX_SPREADSHEET_TABLE_CALCULATED_FORMULA_LENGTH || /[\u0000-\u001f\u007f]/u.test(formula) || formulaHasExternalReference(formula) || !formulaHasStructuredReference(formula) || spreadsheetFormulaFunctions(formula).some((name)=>UNSAFE_SPREADSHEET_TABLE_CALCULATED_FUNCTIONS.has(name))) return;
28
+ const editable = editableSpreadsheetFormula(formula).trim();
29
+ return editable || void 0;
30
+ }
31
+ function reconcileSpreadsheetTableCalculatedColumns(sheet, table) {
32
+ return table.columns.map((column, offset)=>{
33
+ const declared = normalizeSpreadsheetTableCalculatedFormula(column.calculatedFormula);
34
+ const observed = spreadsheetTableCurrentRowFormulas(sheet, table, offset);
35
+ const uniqueObserved = new Set(observed);
36
+ if (uniqueObserved.size > 1) return stripCalculatedFormula(column);
37
+ const inferred = uniqueObserved.values().next().value;
38
+ if (declared && inferred && declared !== inferred) return stripCalculatedFormula(column);
39
+ if (declared) return {
40
+ ...column,
41
+ calculatedFormula: declared
42
+ };
43
+ return inferred ? {
44
+ ...column,
45
+ calculatedFormula: inferred
46
+ } : stripCalculatedFormula(column);
47
+ });
48
+ }
49
+ function isSpreadsheetTableCalculatedFormula(value) {
50
+ const formula = normalizeSpreadsheetTableCalculatedFormula(value);
51
+ return Boolean(formula && hasCurrentRowReference(formula));
52
+ }
53
+ function fillSpreadsheetTableCalculatedColumns(sheet, table, rows) {
54
+ const targetRows = Array.from(new Set(rows.filter((row)=>Number.isSafeInteger(row) && row >= table.range.row[0] + Number(table.headerRow) && row <= table.range.row[1] - Number(table.totalsRow)))).sort((left, right)=>left - right);
55
+ if (!targetRows.length) return sheet;
56
+ const formulas = table.columns.map((column)=>isSpreadsheetTableCalculatedFormula(column.calculatedFormula) ? normalizeSpreadsheetTableCalculatedFormula(column.calculatedFormula) : void 0);
57
+ if (!formulas.some(Boolean)) return sheet;
58
+ if (void 0 !== sheet.data) {
59
+ const data = sheet.data.slice();
60
+ const mutableRows = new Map();
61
+ let changed = false;
62
+ for (const rowIndex of targetRows){
63
+ const sourceRow = data[rowIndex];
64
+ let row = mutableRows.get(rowIndex);
65
+ for(let offset = 0; offset < formulas.length; offset += 1){
66
+ const formula = formulas[offset];
67
+ if (!formula) continue;
68
+ const column = table.range.column[0] + offset;
69
+ const current = sourceRow?.[column] ?? null;
70
+ if (!spreadsheetTableCellIsEmpty(current)) continue;
71
+ row ??= cloneSpreadsheetTableRow(sourceRow);
72
+ mutableRows.set(rowIndex, row);
73
+ const styleSource = current ?? nearestSpreadsheetTableCell(sheet, table, rowIndex, column);
74
+ row[column] = spreadsheetCellWithCalculatedFormula(styleSource, formula);
75
+ changed = true;
76
+ }
77
+ if (row) data[rowIndex] = row;
78
+ }
79
+ return changed ? {
80
+ ...sheet,
81
+ data
82
+ } : sheet;
83
+ }
84
+ const entries = [
85
+ ...sheet.celldata ?? []
86
+ ];
87
+ const byCoordinate = new Map(entries.map((entry, index)=>[
88
+ `${entry.r}:${entry.c}`,
89
+ index
90
+ ]));
91
+ let changed = false;
92
+ for (const row of targetRows)for(let offset = 0; offset < formulas.length; offset += 1){
93
+ const formula = formulas[offset];
94
+ if (!formula) continue;
95
+ const column = table.range.column[0] + offset;
96
+ const key = `${row}:${column}`;
97
+ const index = byCoordinate.get(key);
98
+ const current = void 0 === index ? null : entries[index]?.v;
99
+ if (!spreadsheetTableCellIsEmpty(current)) continue;
100
+ const styleSource = current ?? nearestSpreadsheetTableCell(sheet, table, row, column);
101
+ const entry = {
102
+ r: row,
103
+ c: column,
104
+ v: spreadsheetCellWithCalculatedFormula(styleSource, formula)
105
+ };
106
+ if (void 0 === index) {
107
+ byCoordinate.set(key, entries.length);
108
+ entries.push(entry);
109
+ } else entries[index] = entry;
110
+ changed = true;
111
+ }
112
+ if (!changed) return sheet;
113
+ entries.sort((left, right)=>left.r - right.r || left.c - right.c);
114
+ return {
115
+ ...sheet,
116
+ celldata: entries
117
+ };
118
+ }
119
+ function spreadsheetTableCellAt(sheet, row, column) {
120
+ return sheet.data?.[row]?.[column] ?? sheet.celldata?.find((entry)=>entry.r === row && entry.c === column)?.v ?? null;
121
+ }
122
+ function spreadsheetTableCellIsEmpty(cell) {
123
+ return !cell?.f && (cell?.v === void 0 || null === cell.v || '' === cell.v) && (cell?.m === void 0 || null === cell.m || '' === cell.m);
124
+ }
125
+ function spreadsheetCellWithCalculatedFormula(source, formula) {
126
+ const { f: _formula, m: _display, v: _value, ...presentation } = source ?? {};
127
+ return {
128
+ ...presentation,
129
+ f: formula
130
+ };
131
+ }
132
+ function stripCalculatedFormula(column) {
133
+ if (void 0 === column.calculatedFormula) return column;
134
+ const { calculatedFormula: _formula, ...withoutFormula } = column;
135
+ return withoutFormula;
136
+ }
137
+ function hasCurrentRowReference(formula) {
138
+ const source = formula.replace(/"(?:[^"]|"")*"/g, '""');
139
+ return /\[@/i.test(source) || /\[#This Row\]/i.test(source);
140
+ }
141
+ function spreadsheetTableCurrentRowFormulas(sheet, table, columnOffset) {
142
+ const startRow = table.range.row[0] + Number(table.headerRow);
143
+ const endRow = table.range.row[1] - Number(table.totalsRow);
144
+ const column = table.range.column[0] + columnOffset;
145
+ const formulas = [];
146
+ for(let row = startRow; row <= endRow; row += 1){
147
+ const formula = normalizeSpreadsheetTableCalculatedFormula(spreadsheetTableCellAt(sheet, row, column)?.f);
148
+ if (formula && hasCurrentRowReference(formula)) formulas.push(formula);
149
+ }
150
+ return formulas;
151
+ }
152
+ function cloneSpreadsheetTableRow(source) {
153
+ const row = [];
154
+ if (!source) return row;
155
+ row.length = source.length;
156
+ for (const column of spreadsheet_sparse_sparseArrayEntries(source).map(([index])=>index))row[column] = source[column];
157
+ return row;
158
+ }
159
+ function nearestSpreadsheetTableCell(sheet, table, row, column) {
160
+ const start = table.range.row[0] + Number(table.headerRow);
161
+ const end = table.range.row[1] - Number(table.totalsRow);
162
+ for(let distance = 1; distance <= end - start; distance += 1){
163
+ const before = row - distance;
164
+ if (before >= start) {
165
+ const cell = spreadsheetTableCellAt(sheet, before, column);
166
+ if (cell) return cell;
167
+ }
168
+ const after = row + distance;
169
+ if (after <= end) {
170
+ const cell = spreadsheetTableCellAt(sheet, after, column);
171
+ if (cell) return cell;
172
+ }
173
+ }
174
+ return null;
175
+ }
176
+ const MAX_SPREADSHEET_TABLE_TOTALS_FORMULA_LENGTH = 8192;
177
+ const MAX_SPREADSHEET_TABLE_TOTALS_LABEL_LENGTH = 255;
178
+ const DEFAULT_SPREADSHEET_TABLE_TOTALS_LABEL = 'Total';
179
+ const SPREADSHEET_TABLE_TOTALS_FUNCTIONS = Object.freeze([
180
+ 'sum',
181
+ 'average',
182
+ 'count',
183
+ 'countNums',
184
+ 'max',
185
+ 'min',
186
+ 'stdDev',
187
+ 'stdDevP',
188
+ 'var',
189
+ 'varP',
190
+ 'custom'
191
+ ]);
192
+ const UNSAFE_TOTALS_FUNCTIONS = new Set([
193
+ 'CALL',
194
+ 'DDE',
195
+ 'DDE.REQUEST',
196
+ 'DDE.POKE',
197
+ 'EXEC',
198
+ 'HYPERLINK',
199
+ 'INDIRECT',
200
+ 'OFFSET',
201
+ 'REGISTER.ID',
202
+ 'RTD',
203
+ 'WEBSERVICE'
204
+ ]);
205
+ const SUBTOTAL_CODES = {
206
+ average: 101,
207
+ count: 103,
208
+ countNums: 102,
209
+ max: 104,
210
+ min: 105,
211
+ stdDev: 107,
212
+ stdDevP: 108,
213
+ sum: 109,
214
+ var: 110,
215
+ varP: 111
216
+ };
217
+ const OOXML_TOTALS_FUNCTIONS = {
218
+ average: 'average',
219
+ count: 'count',
220
+ countNums: 'countNums',
221
+ max: 'max',
222
+ min: 'min',
223
+ stdDev: 'stdDev',
224
+ stdDevP: 'stdDevp',
225
+ sum: 'sum',
226
+ var: 'var',
227
+ varP: 'varp'
228
+ };
229
+ const TOTALS_FUNCTION_LABELS = {
230
+ sum: '求和',
231
+ average: '平均值',
232
+ count: '计数',
233
+ countNums: '数值计数',
234
+ max: '最大值',
235
+ min: '最小值',
236
+ stdDev: '标准差',
237
+ stdDevP: '总体标准差',
238
+ var: '方差',
239
+ varP: '总体方差',
240
+ custom: '自定义'
241
+ };
242
+ function spreadsheetTableTotalsFunctionLabel(value) {
243
+ return value ? TOTALS_FUNCTION_LABELS[value] : '不汇总';
244
+ }
245
+ function normalizeSpreadsheetTableTotalsFunction(value) {
246
+ if ('string' != typeof value) return;
247
+ if ('none' === value || '' === value) return;
248
+ return SPREADSHEET_TABLE_TOTALS_FUNCTIONS.includes(value) ? value : void 0;
249
+ }
250
+ function normalizeSpreadsheetTableTotalsFormula(value) {
251
+ if ('string' != typeof value) return;
252
+ const trimmed = value.trim();
253
+ if (!trimmed) return;
254
+ const formula = trimmed.startsWith('=') ? trimmed : `=${trimmed}`;
255
+ if (formula.startsWith('==') || formula.length > MAX_SPREADSHEET_TABLE_TOTALS_FORMULA_LENGTH || /[\u0000-\u001f\u007f]/u.test(formula) || formulaHasExternalReference(formula) || spreadsheetFormulaFunctions(formula).some((name)=>UNSAFE_TOTALS_FUNCTIONS.has(name))) return;
256
+ const editable = editableSpreadsheetFormula(formula).trim();
257
+ return editable || void 0;
258
+ }
259
+ function normalizeSpreadsheetTableTotalsLabel(value) {
260
+ if ('string' != typeof value) return;
261
+ const label = value.trim();
262
+ if (!label || Array.from(label).length > MAX_SPREADSHEET_TABLE_TOTALS_LABEL_LENGTH || /[\u0000-\u001f\u007f]/u.test(label)) return;
263
+ return label;
264
+ }
265
+ function spreadsheetTableTotalsFunctionFromOoxml(value) {
266
+ if ('string' != typeof value) return;
267
+ const normalized = value.trim().toLowerCase();
268
+ if ('custom' === normalized) return 'custom';
269
+ const entry = Object.entries(OOXML_TOTALS_FUNCTIONS).find(([, token])=>token.toLowerCase() === normalized);
270
+ return entry?.[0];
271
+ }
272
+ function spreadsheetTableTotalsFunctionToOoxml(value) {
273
+ if (!value || 'custom' === value) return 'custom' === value ? 'custom' : void 0;
274
+ return OOXML_TOTALS_FUNCTIONS[value];
275
+ }
276
+ function spreadsheetTableTotalsFormula(table, columnOffset) {
277
+ const column = table.columns[columnOffset];
278
+ if (!column) return;
279
+ const custom = normalizeSpreadsheetTableTotalsFormula(column.totalsFormula);
280
+ if (custom) return custom;
281
+ const functionName = normalizeSpreadsheetTableTotalsFunction(column.totalsFunction);
282
+ if (!functionName || 'custom' === functionName) return;
283
+ const code = SUBTOTAL_CODES[functionName];
284
+ if (!code) return;
285
+ return `=SUBTOTAL(${code},${table.name}[${escapeStructuredColumnName(column.name)}])`;
286
+ }
287
+ function spreadsheetTableTotalsColumnPatch(columns, patches) {
288
+ if (void 0 === patches) return columns.map((column)=>({
289
+ ...column
290
+ }));
291
+ const entries = Array.isArray(patches) ? patches.flatMap((patch, offset)=>patch ? [
292
+ [
293
+ String(offset),
294
+ patch
295
+ ]
296
+ ] : []) : Object.entries(patches);
297
+ const next = columns.map((column)=>({
298
+ ...column
299
+ }));
300
+ for (const [rawOffset, patch] of entries){
301
+ const offset = Number(rawOffset);
302
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset >= next.length || !patch || 'object' != typeof patch || Array.isArray(patch)) return null;
303
+ const current = next[offset];
304
+ if (!current) return null;
305
+ const candidate = {
306
+ ...current
307
+ };
308
+ if (Object.hasOwn(patch, 'totalsFunction')) if (null === patch.totalsFunction || '' === patch.totalsFunction) delete candidate.totalsFunction;
309
+ else {
310
+ const functionName = normalizeSpreadsheetTableTotalsFunction(patch.totalsFunction);
311
+ if (!functionName) return null;
312
+ candidate.totalsFunction = functionName;
313
+ }
314
+ if (Object.hasOwn(patch, 'totalsLabel')) if (null === patch.totalsLabel || '' === patch.totalsLabel) delete candidate.totalsLabel;
315
+ else {
316
+ const label = normalizeSpreadsheetTableTotalsLabel(patch.totalsLabel);
317
+ if (!label) return null;
318
+ candidate.totalsLabel = label;
319
+ }
320
+ if (Object.hasOwn(patch, 'totalsFormula')) if (null === patch.totalsFormula || '' === patch.totalsFormula) delete candidate.totalsFormula;
321
+ else {
322
+ const formula = normalizeSpreadsheetTableTotalsFormula(patch.totalsFormula);
323
+ if (!formula) return null;
324
+ candidate.totalsFormula = formula;
325
+ }
326
+ if (candidate.totalsFormula) {
327
+ if (void 0 !== candidate.totalsFunction && 'custom' !== candidate.totalsFunction) return null;
328
+ candidate.totalsFunction = 'custom';
329
+ } else if ('custom' === candidate.totalsFunction) return null;
330
+ if (void 0 !== candidate.totalsFunction && 'custom' !== candidate.totalsFunction) delete candidate.totalsFormula;
331
+ if (void 0 !== candidate.totalsFunction && candidate.totalsLabel) delete candidate.totalsLabel;
332
+ next[offset] = candidate;
333
+ }
334
+ return next;
335
+ }
336
+ function withDefaultSpreadsheetTableTotalsLabel(columns, totalsRow) {
337
+ const next = columns.map((column)=>({
338
+ ...column
339
+ }));
340
+ if (!totalsRow || !next.length) return next;
341
+ const hasExplicitLabel = next.some((column)=>column.totalsLabel);
342
+ if (hasExplicitLabel) return next;
343
+ const first = next[0];
344
+ if (first && !first.totalsFunction && !first.totalsFormula) first.totalsLabel = DEFAULT_SPREADSHEET_TABLE_TOTALS_LABEL;
345
+ return next;
346
+ }
347
+ function spreadsheetTableTotalsRowHasContent(sheet, table, row = table.totalsRow ? table.range.row[1] : table.range.row[1] + 1) {
348
+ for(let column = table.range.column[0]; column <= table.range.column[1]; column += 1)if (!spreadsheetTableTotalsCellIsEmpty(spreadsheetTableTotalsCellAt(sheet, row, column))) return true;
349
+ return false;
350
+ }
351
+ function synchronizeSpreadsheetTableTotalsRow(sheet, previousTable, nextTable, options = {}) {
352
+ if (!nextTable.totalsRow) return sheet;
353
+ const force = true === options.force;
354
+ const previousRow = previousTable?.range.row[1];
355
+ const nextRow = nextTable.range.row[1];
356
+ const width = nextTable.range.column[1] - nextTable.range.column[0] + 1;
357
+ const updates = new Map();
358
+ for(let offset = 0; offset < width; offset += 1){
359
+ const column = nextTable.range.column[0] + offset;
360
+ const nextColumn = nextTable.columns[offset];
361
+ if (!nextColumn) continue;
362
+ const previousColumn = previousTable?.columns[offset];
363
+ const oldFormula = previousTable ? spreadsheetTableTotalsFormula(previousTable, offset) : void 0;
364
+ const newFormula = spreadsheetTableTotalsFormula(nextTable, offset);
365
+ const oldLabel = normalizeSpreadsheetTableTotalsLabel(previousColumn?.totalsLabel);
366
+ const newLabel = normalizeSpreadsheetTableTotalsLabel(nextColumn.totalsLabel);
367
+ const changed = force || void 0 === previousTable || previousTable.name !== nextTable.name || previousTable.range.row[1] !== nextTable.range.row[1] || previousColumn?.totalsFunction !== nextColumn.totalsFunction || previousColumn?.totalsFormula !== nextColumn.totalsFormula || previousColumn?.totalsLabel !== nextColumn.totalsLabel || previousColumn?.name !== nextColumn.name;
368
+ const current = spreadsheetTableTotalsCellAt(sheet, nextRow, column);
369
+ const previousCell = void 0 === previousRow ? null : spreadsheetTableTotalsCellAt(sheet, previousRow, column);
370
+ const styleSource = current ?? previousCell;
371
+ const empty = spreadsheetTableTotalsCellIsEmpty(current);
372
+ const currentFormula = normalizeSpreadsheetTableTotalsFormula(current?.f);
373
+ const generatedFormula = Boolean(oldFormula && currentFormula && formulasEqual(currentFormula, oldFormula));
374
+ const generatedLabel = Boolean(oldLabel && !current?.f && spreadsheetTableCellText(current) === oldLabel);
375
+ const replaceable = force || empty || changed && (generatedFormula || generatedLabel);
376
+ if (newFormula && replaceable) updates.set(column, spreadsheetCellWithTotalsFormula(styleSource, newFormula));
377
+ else if (newLabel && replaceable) updates.set(column, spreadsheetCellWithTotalsLabel(styleSource, newLabel));
378
+ else if (!newFormula && !newLabel && changed && (generatedFormula || generatedLabel)) updates.set(column, clearSpreadsheetTableTotalsCell(current));
379
+ }
380
+ return applySpreadsheetTableTotalsCellUpdates(sheet, nextRow, updates);
381
+ }
382
+ function reconcileSpreadsheetTableTotalsColumns(sheet, table, editedOffsets) {
383
+ return table.columns.map((column, offset)=>{
384
+ const candidate = {
385
+ ...column
386
+ };
387
+ const declaredFunction = normalizeSpreadsheetTableTotalsFunction(candidate.totalsFunction);
388
+ const declaredFormula = normalizeSpreadsheetTableTotalsFormula(candidate.totalsFormula);
389
+ if (declaredFunction) candidate.totalsFunction = declaredFunction;
390
+ else delete candidate.totalsFunction;
391
+ if (declaredFormula) candidate.totalsFormula = declaredFormula;
392
+ else delete candidate.totalsFormula;
393
+ const label = normalizeSpreadsheetTableTotalsLabel(candidate.totalsLabel);
394
+ if (label) candidate.totalsLabel = label;
395
+ else delete candidate.totalsLabel;
396
+ if (!table.totalsRow) return candidate;
397
+ const cell = spreadsheetTableTotalsCellAt(sheet, table.range.row[1], table.range.column[0] + offset);
398
+ if (editedOffsets && !editedOffsets.has(offset)) return candidate;
399
+ const cellFormula = normalizeSpreadsheetTableTotalsFormula(cell?.f);
400
+ const expected = spreadsheetTableTotalsFormula({
401
+ ...table,
402
+ columns: [
403
+ {
404
+ ...candidate
405
+ }
406
+ ]
407
+ }, 0);
408
+ if (cellFormula) {
409
+ if (expected && formulasEqual(cellFormula, expected)) return candidate;
410
+ delete candidate.totalsFunction;
411
+ delete candidate.totalsFormula;
412
+ candidate.totalsFunction = 'custom';
413
+ candidate.totalsFormula = cellFormula;
414
+ return candidate;
415
+ }
416
+ delete candidate.totalsFunction;
417
+ delete candidate.totalsFormula;
418
+ const cellLabel = normalizeSpreadsheetTableTotalsLabel(spreadsheetTableCellText(cell));
419
+ if (cellLabel) if (candidate.totalsLabel && candidate.totalsLabel !== cellLabel) delete candidate.totalsLabel;
420
+ else candidate.totalsLabel = cellLabel;
421
+ else delete candidate.totalsLabel;
422
+ return candidate;
423
+ });
424
+ }
425
+ function escapeStructuredColumnName(value) {
426
+ return value.replaceAll(']', ']]');
427
+ }
428
+ function formulasEqual(left, right) {
429
+ return left.trim().replace(/\s+/g, '').toLocaleLowerCase() === right.trim().replace(/\s+/g, '').toLocaleLowerCase();
430
+ }
431
+ function spreadsheetTableTotalsCellAt(sheet, row, column) {
432
+ return sheet.data?.[row]?.[column] ?? sheet.celldata?.find((entry)=>entry.r === row && entry.c === column)?.v ?? null;
433
+ }
434
+ function spreadsheetTableTotalsCellIsEmpty(cell) {
435
+ return !cell?.f && (cell?.v === void 0 || null === cell.v || '' === cell.v) && (cell?.m === void 0 || null === cell.m || '' === cell.m);
436
+ }
437
+ function spreadsheetTableCellText(cell) {
438
+ const value = cell?.m ?? cell?.v;
439
+ return null == value ? '' : String(value);
440
+ }
441
+ function spreadsheetCellWithTotalsFormula(source, formula) {
442
+ const { f: _formula, m: _display, v: _value, ...presentation } = source ?? {};
443
+ return {
444
+ ...presentation,
445
+ f: formula
446
+ };
447
+ }
448
+ function spreadsheetCellWithTotalsLabel(source, label) {
449
+ const { f: _formula, ...withoutFormula } = source ?? {};
450
+ return {
451
+ ...withoutFormula,
452
+ m: label,
453
+ v: label
454
+ };
455
+ }
456
+ function clearSpreadsheetTableTotalsCell(source) {
457
+ if (!source) return null;
458
+ const { f: _formula, m: _display, v: _value, ...presentation } = source;
459
+ return Object.keys(presentation).length ? presentation : null;
460
+ }
461
+ function applySpreadsheetTableTotalsCellUpdates(sheet, rowIndex, updates) {
462
+ if (!updates.size) return sheet;
463
+ if (void 0 !== sheet.data) {
464
+ const data = sheet.data.slice();
465
+ const source = data[rowIndex];
466
+ const row = source ? source.slice() : [];
467
+ for (const [column, cell] of updates)row[column] = cell;
468
+ data[rowIndex] = row;
469
+ return {
470
+ ...sheet,
471
+ data
472
+ };
473
+ }
474
+ const entries = [
475
+ ...sheet.celldata ?? []
476
+ ];
477
+ const indexes = new Map(entries.map((entry, index)=>[
478
+ `${entry.r}:${entry.c}`,
479
+ index
480
+ ]));
481
+ for (const [column, cell] of updates){
482
+ const key = `${rowIndex}:${column}`;
483
+ const index = indexes.get(key);
484
+ if (null === cell) {
485
+ if (void 0 !== index) entries.splice(index, 1);
486
+ if (void 0 !== index) {
487
+ indexes.clear();
488
+ entries.forEach((entry, nextIndex)=>{
489
+ indexes.set(`${entry.r}:${entry.c}`, nextIndex);
490
+ });
491
+ }
492
+ continue;
493
+ }
494
+ const entry = {
495
+ r: rowIndex,
496
+ c: column,
497
+ v: cell
498
+ };
499
+ if (void 0 === index) {
500
+ indexes.set(key, entries.length);
501
+ entries.push(entry);
502
+ } else entries[index] = entry;
503
+ }
504
+ entries.sort((left, right)=>left.r - right.r || left.c - right.c);
505
+ return {
506
+ ...sheet,
507
+ celldata: entries
508
+ };
509
+ }
8
510
  function requiredCoordinate(value, maximum, label, sheetId) {
9
511
  if (!Number.isSafeInteger(value) || value < 0 || value >= maximum) invalidWorkOfficeSpreadsheetInput(`a valid ${label} coordinate in sheet '${sheetId}'`);
10
512
  return value;
@@ -258,18 +760,55 @@ function requiredSpreadsheetTableColumns(value, width, label) {
258
760
  const names = new Set();
259
761
  return value.map((candidate)=>{
260
762
  const record = requiredInputRecord(candidate, `${label} column`);
261
- assertExactRecordKeys(record, [
262
- 'name'
763
+ assertOptionalRecordKeys(record, [
764
+ 'name',
765
+ 'calculatedFormula',
766
+ 'totalsFunction',
767
+ 'totalsLabel',
768
+ 'totalsFormula'
263
769
  ], `column for ${label}`);
264
770
  const name = requiredTableColumnName(record.name, label);
265
771
  const normalized = name.toLocaleLowerCase();
266
772
  if (names.has(normalized)) invalidWorkOfficeSpreadsheetInput(`unique column names for ${label}`);
267
773
  names.add(normalized);
268
- return {
774
+ let calculatedFormula;
775
+ if (void 0 !== record.calculatedFormula) {
776
+ calculatedFormula = normalizeSpreadsheetTableCalculatedFormula(record.calculatedFormula);
777
+ if (!calculatedFormula || calculatedFormula !== record.calculatedFormula) invalidWorkOfficeSpreadsheetInput(`a bounded structured calculated-column formula for ${label}`);
778
+ }
779
+ const totalsFunction = void 0 === record.totalsFunction ? void 0 : normalizeSpreadsheetTableTotalsFunction(record.totalsFunction);
780
+ if (void 0 !== record.totalsFunction && (!totalsFunction || totalsFunction !== record.totalsFunction)) invalidWorkOfficeSpreadsheetInput(`a supported totals-row function for ${label}`);
781
+ const totalsLabel = void 0 === record.totalsLabel ? void 0 : normalizeSpreadsheetTableTotalsLabel(record.totalsLabel);
782
+ if (void 0 !== record.totalsLabel && (!totalsLabel || totalsLabel !== record.totalsLabel)) invalidWorkOfficeSpreadsheetInput(`a bounded totals-row label for ${label}`);
783
+ const totalsFormula = void 0 === record.totalsFormula ? void 0 : normalizeSpreadsheetTableTotalsFormula(record.totalsFormula);
784
+ if (void 0 !== record.totalsFormula && (!totalsFormula || totalsFormula !== record.totalsFormula)) invalidWorkOfficeSpreadsheetInput(`a bounded totals-row formula for ${label}`);
785
+ if (totalsFormula && 'custom' !== totalsFunction) invalidWorkOfficeSpreadsheetInput(`custom totals-row formulas to declare the custom function for ${label}`);
786
+ if ('custom' === totalsFunction && !totalsFormula) invalidWorkOfficeSpreadsheetInput(`custom totals-row functions to include a formula for ${label}`);
787
+ if ((totalsFunction || totalsFormula) && totalsLabel) invalidWorkOfficeSpreadsheetInput(`totals-row labels not to share a cell with a formula for ${label}`);
788
+ if (!calculatedFormula && !totalsFunction && !totalsLabel && !totalsFormula) return {
269
789
  name
270
790
  };
791
+ return {
792
+ name,
793
+ ...calculatedFormula ? {
794
+ calculatedFormula
795
+ } : {},
796
+ ...totalsFunction ? {
797
+ totalsFunction
798
+ } : {},
799
+ ...totalsLabel ? {
800
+ totalsLabel
801
+ } : {},
802
+ ...totalsFormula ? {
803
+ totalsFormula
804
+ } : {}
805
+ };
271
806
  });
272
807
  }
808
+ function assertOptionalRecordKeys(record, keys, label) {
809
+ const allowed = new Set(keys);
810
+ if (!Object.hasOwn(record, keys[0] ?? '') || Object.keys(record).some((key)=>!allowed.has(key))) invalidWorkOfficeSpreadsheetInput(`a complete ${label} record without unknown fields`);
811
+ }
273
812
  function requiredSpreadsheetTableFilters(value, width, label) {
274
813
  if (!Array.isArray(value)) invalidWorkOfficeSpreadsheetInput(`an array of filters for ${label}`);
275
814
  const columns = new Set();
@@ -744,7 +1283,7 @@ function assertCompatibleValue(previous, next, shared, label) {
744
1283
  function spreadsheetCells(sheet) {
745
1284
  const result = new Map();
746
1285
  if (void 0 !== sheet.data) {
747
- for (const [row, values] of sparseArrayEntries(sheet.data))for (const [column, cell] of sparseArrayEntries(values))if (null !== cell) result.set(`${row}:${column}`, cell);
1286
+ for (const [row, values] of spreadsheet_sparse_sparseArrayEntries(sheet.data))for (const [column, cell] of spreadsheet_sparse_sparseArrayEntries(values))if (null !== cell) result.set(`${row}:${column}`, cell);
748
1287
  return result;
749
1288
  }
750
1289
  for (const entry of sheet.celldata ?? [])if (null !== entry.v) result.set(`${entry.r}:${entry.c}`, entry.v);
@@ -854,7 +1393,7 @@ function spreadsheetCellEntries(sheet) {
854
1393
  if (!sheet) return [];
855
1394
  const entries = [];
856
1395
  if (void 0 !== sheet.data) {
857
- for (const [row, values] of sparseArrayEntries(sheet.data))for (const [column, cell] of sparseArrayEntries(values))if (null !== cell) entries.push({
1396
+ for (const [row, values] of spreadsheet_sparse_sparseArrayEntries(sheet.data))for (const [column, cell] of spreadsheet_sparse_sparseArrayEntries(values))if (null !== cell) entries.push({
858
1397
  cell,
859
1398
  column,
860
1399
  coordinate: encodedCoordinate(row, column),
@@ -2216,12 +2755,12 @@ function xlsxRichTextCellText(cell) {
2216
2755
  return normalizeXlsxRichTextCell(cell)?.text ?? null;
2217
2756
  }
2218
2757
  function sheetHasXlsxRichTextCells(sheet) {
2219
- for (const [, row] of sparseArrayEntries(sheet.data))for (const [, cell] of sparseArrayEntries(row))if (cell && normalizeXlsxRichTextCell(cell)) return true;
2758
+ for (const [, row] of spreadsheet_sparse_sparseArrayEntries(sheet.data))for (const [, cell] of spreadsheet_sparse_sparseArrayEntries(row))if (cell && normalizeXlsxRichTextCell(cell)) return true;
2220
2759
  return false;
2221
2760
  }
2222
2761
  function xlsxRichTextStyleOrigins(sheet) {
2223
2762
  const origins = [];
2224
- for (const [, row] of sparseArrayEntries(sheet.data))for (const [, cell] of sparseArrayEntries(row))if (cell) for (const run of normalizeXlsxRichTextCell(cell)?.runs ?? []){
2763
+ for (const [, row] of spreadsheet_sparse_sparseArrayEntries(sheet.data))for (const [, cell] of spreadsheet_sparse_sparseArrayEntries(row))if (cell) for (const run of normalizeXlsxRichTextCell(cell)?.runs ?? []){
2225
2764
  const fontColor = normalizeXlsxSemanticColorOrigin(run.a3sXlsxColorOrigin);
2226
2765
  if (fontColor && run.fc && xlsxSemanticColorMatchesValue(fontColor, run.fc)) origins.push({
2227
2766
  fontColor
@@ -2241,7 +2780,7 @@ function writeXlsxRichTextCells(worksheet, sheet, semanticPalette) {
2241
2780
  }));
2242
2781
  let remainingCells = 10000;
2243
2782
  let remainingRuns = 100000;
2244
- for (const [row, values] of sparseArrayEntries(sheet.data))for (const [column, cell] of sparseArrayEntries(values)){
2783
+ for (const [row, values] of spreadsheet_sparse_sparseArrayEntries(sheet.data))for (const [column, cell] of spreadsheet_sparse_sparseArrayEntries(values)){
2245
2784
  if (remainingCells <= 0 || remainingRuns <= 0) return;
2246
2785
  if (!cell) continue;
2247
2786
  const richText = normalizeXlsxRichTextCell(cell);
@@ -2384,4 +2923,4 @@ function work_xlsx_rich_text_nonNegativeInteger(value) {
2384
2923
  function work_xlsx_rich_text_isRecord(value) {
2385
2924
  return 'object' == typeof value && null !== value && !Array.isArray(value);
2386
2925
  }
2387
- export { activeXlsxNativeFill, activeXlsxSemanticColorOrigin, applyImportedXlsxRichText, boundedSpreadsheetDataValidationText, coalesceXlsxRichTextRuns, createWorkOfficeSpreadsheetCollaborationBinding as createOfficeSpreadsheetCollaborationBinding, createXlsxRichTextReadContext, deleteXlsxNativeFills, directXlsxAlignment, directXlsxFontStyle, hasXlsxDirectFontStyle, initializeWorkOfficeSpreadsheetCollaboration as initializeOfficeSpreadsheetCollaboration, isHighSurrogate, isLowSurrogate, isSpreadsheetTextOrientationId, isSpreadsheetUnderlineStyle, normalizeSpreadsheetDataValidationErrorStyle, normalizeSpreadsheetDateValidationBoundary, normalizeXlsxRichTextCell, normalizeXlsxRichTextColor, normalizeXlsxRichTextEditSource, normalizeXlsxRichTextRun, patchSpreadsheetRichTextFontRuns, readWorkOfficeSpreadsheetCollaboration as readOfficeSpreadsheetCollaboration, readXlsxRichTextCells, replaceWorkOfficeSpreadsheetCollaboration as replaceOfficeSpreadsheetCollaboration, sameXlsxRichTextRunStyle, sheetHasXlsxRichTextCells, spreadsheetCellValueWithDiagonalBorder, spreadsheetDateValidationFormula, spreadsheetDiagonalBorderFromCellValue, spreadsheetExplicitTextOrientationFromCell, spreadsheetTextOrientationCellStyle, spreadsheetTextOrientationChoiceFromCell, spreadsheetTextOrientationFromAngle, spreadsheetTextOrientationFromCell, spreadsheetTextOrientationFromChoice, spreadsheetTextOrientationFromXlsx, spreadsheetUnderlineCellValue, spreadsheetUnderlineCellValueFromSheetJs, spreadsheetUnderlineCellValueFromXlsx, spreadsheetUnderlineStyle, spreadsheetVisibleTextRotationFromCell, validXlsxRichText, writeXlsxRichTextCells, xlsxAlignmentMatches, xlsxBooleanAttribute, xlsxBorderLineMatches, xlsxColorMatches, xlsxNativeFillCellKeys, xlsxNativeFillKey, xlsxNativeFillSemanticColors, xlsxRichTextCellText, xlsxRichTextStyleOrigins, xlsxStyleCollectionIndex, xlsxToggleEnabled, xlsxUnderlineStyle };
2926
+ export { SPREADSHEET_TABLE_TOTALS_FUNCTIONS, activeXlsxNativeFill, activeXlsxSemanticColorOrigin, applyImportedXlsxRichText, boundedSpreadsheetDataValidationText, coalesceXlsxRichTextRuns, createWorkOfficeSpreadsheetCollaborationBinding as createOfficeSpreadsheetCollaborationBinding, createXlsxRichTextReadContext, deleteXlsxNativeFills, directXlsxAlignment, directXlsxFontStyle, fillSpreadsheetTableCalculatedColumns, hasXlsxDirectFontStyle, initializeWorkOfficeSpreadsheetCollaboration as initializeOfficeSpreadsheetCollaboration, isHighSurrogate, isLowSurrogate, isSpreadsheetTextOrientationId, isSpreadsheetUnderlineStyle, normalizeSpreadsheetDataValidationErrorStyle, normalizeSpreadsheetDateValidationBoundary, normalizeSpreadsheetTableCalculatedFormula, normalizeSpreadsheetTableTotalsFormula, normalizeSpreadsheetTableTotalsLabel, normalizeXlsxRichTextCell, normalizeXlsxRichTextColor, normalizeXlsxRichTextEditSource, normalizeXlsxRichTextRun, patchSpreadsheetRichTextFontRuns, readWorkOfficeSpreadsheetCollaboration as readOfficeSpreadsheetCollaboration, readXlsxRichTextCells, reconcileSpreadsheetTableCalculatedColumns, reconcileSpreadsheetTableTotalsColumns, replaceWorkOfficeSpreadsheetCollaboration as replaceOfficeSpreadsheetCollaboration, sameXlsxRichTextRunStyle, sheetHasXlsxRichTextCells, spreadsheetCellValueWithDiagonalBorder, spreadsheetDateValidationFormula, spreadsheetDiagonalBorderFromCellValue, spreadsheetExplicitTextOrientationFromCell, spreadsheetTableTotalsColumnPatch, spreadsheetTableTotalsFunctionFromOoxml, spreadsheetTableTotalsFunctionLabel, spreadsheetTableTotalsFunctionToOoxml, spreadsheetTableTotalsRowHasContent, spreadsheetTextOrientationCellStyle, spreadsheetTextOrientationChoiceFromCell, spreadsheetTextOrientationFromAngle, spreadsheetTextOrientationFromCell, spreadsheetTextOrientationFromChoice, spreadsheetTextOrientationFromXlsx, spreadsheetUnderlineCellValue, spreadsheetUnderlineCellValueFromSheetJs, spreadsheetUnderlineCellValueFromXlsx, spreadsheetUnderlineStyle, spreadsheetVisibleTextRotationFromCell, synchronizeSpreadsheetTableTotalsRow, validXlsxRichText, withDefaultSpreadsheetTableTotalsLabel, writeXlsxRichTextCells, xlsxAlignmentMatches, xlsxBooleanAttribute, xlsxBorderLineMatches, xlsxColorMatches, xlsxNativeFillCellKeys, xlsxNativeFillKey, xlsxNativeFillSemanticColors, xlsxRichTextCellText, xlsxRichTextStyleOrigins, xlsxStyleCollectionIndex, xlsxToggleEnabled, xlsxUnderlineStyle };