@a3s-lab/office 0.35.0 → 0.36.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.
@@ -6,7 +6,7 @@ import { Fragment as external_react_Fragment, cloneElement, memo, useCallback, u
6
6
  import { convertSpanToShareString, genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
7
7
  import { freezeImportedSpreadsheetCell, normalizeXlsxSemanticColorOrigin, spreadsheetMatrixProfile, withXlsxPatternFill, withoutEditableRange, normalizeSpreadsheetConditionalVisualOptions, xlsxSemanticColorMatchesValue, withXlsxGradientFill, effectiveSpreadsheetPageSetup, attachSpreadsheetShownCommentCells, normalizeXlsxPatternFill, withEditableRange, sameSpreadsheetHistoryValue, isSpreadsheetConditionalComparisonOperator, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, xlsxPatternFillTypes, sheetProtectionAuthority, normalizeXlsxGradientFill, xlsxGradientFillFallbackColor, editableRangeCellCount, unlockedCellCount, registerDerivedSpreadsheetMatrix, editableRangeRequiresCredentials, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSheetProtectionAuthority, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, spreadsheetConditionalComparisonNeedsUpperValue, drawSpreadsheetConditionalIcon, spreadsheetConditionalThresholdsEqual } from "./9333.js";
8
8
  import { workSpreadsheetChartSupportsGrouping, spreadsheetFormulaRangesForSelection, createSpreadsheetChartFromSelection, normalizeWorkSpreadsheetDataLabelPosition, normalizeSpreadsheetPrintTitleColumns, normalizeSpreadsheetPrintTitleRows, formulaHasStructuredReference, normalizeWorkSpreadsheetErrorBars, defaultPivotValueCaption, workSpreadsheetChartSupportsBarSpacing, normalizeWorkSpreadsheetChartGrouping, spreadsheetGridSize, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetScatterStyle, cloneSparseMatrix, spreadsheetPivotAggregationLabel, workSpreadsheetChartAxisIsCategoryAxis, reconcileSpreadsheetChartPreviews, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetChartLegendPosition, spreadsheetChartCount, workSpreadsheetChartTypeLabel, sparseArrayEntries, normalizeWorkSpreadsheetTrendline, effectiveSpreadsheetCalculationSettings, spreadsheetPivotIntersects, normalizeWorkSpreadsheetDataLabels, spreadsheetPivotOutputContains, normalizeSpreadsheetPrintArea, spreadsheetCellAddress, formatSpreadsheetCellRanges, normalizeWorkSpreadsheetChartLayout, spreadsheetFormulaRangeForCell, workSpreadsheetChartAxisIsValueAxis, normalizeWorkSpreadsheetChartSmoothLines, spreadsheetPivotFilterValueKey, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetChartSeriesStyle, volatileSpreadsheetFormulaFunctions, sparseArrayIndexes, normalizeWorkSpreadsheetBubbleSizeRepresents, workSpreadsheetChartSupportsTrendlines, normalizeWorkSpreadsheetChartLegendOverlay, parseSpreadsheetChartReference, workSpreadsheetChartGroupingIsStacked, normalizeWorkSpreadsheetChartGapWidth, workSpreadsheetChartSupportsSmoothLines, refreshSpreadsheetPivotTables, createSpreadsheetPivotFromSelection, reconcileSpreadsheetPivots, spreadsheetPivotCount, sparseMatrixColumnCount, workSpreadsheetChartSupportsErrorBars, formulaHasExternalReference, isValidSpreadsheetDefinedName, spreadsheetPivotValidation, deleteSpreadsheetPivotTable, workSpreadsheetChartAxisDefaultLabelPosition, workSpreadsheetChartSupportsSeriesAnalysis, parseSpreadsheetCellRanges, spreadsheetPivotFilterItems, spreadsheetPivotFields, normalizeWorkSpreadsheetChartOverlap, spreadsheetFormulaRangeConflict, spreadsheetSheetsWithChartPreviews, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, workSpreadsheetChartSupportsAxes, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetRadarStyle } from "./8715.js";
9
- import { spreadsheetTextOrientationFromChoice, isSpreadsheetTextOrientationId, xlsxRichTextCellText, spreadsheetTextOrientationFromAngle, spreadsheetCellValueWithDiagonalBorder, spreadsheetUnderlineCellValue, normalizeSpreadsheetDataValidationErrorStyle, normalizeXlsxRichTextEditSource, normalizeXlsxRichTextColor, isHighSurrogate, xlsxNativeFillCellKeys, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, boundedSpreadsheetDataValidationText, normalizeSpreadsheetDateValidationBoundary, normalizeXlsxRichTextCell, activeXlsxNativeFill, spreadsheetUnderlineStyle, deleteXlsxNativeFills, spreadsheetTextOrientationCellStyle, spreadsheetTextOrientationChoiceFromCell, normalizeXlsxRichTextRun, spreadsheetDiagonalBorderFromCellValue, sameXlsxRichTextRunStyle, isLowSurrogate, spreadsheetTextOrientationFromCell, coalesceXlsxRichTextRuns, patchSpreadsheetRichTextFontRuns, replaceOfficeSpreadsheetCollaboration as replaceWorkOfficeSpreadsheetCollaboration, validXlsxRichText, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, isSpreadsheetUnderlineStyle, spreadsheetVisibleTextRotationFromCell } from "./3266.js";
9
+ import { reconcileSpreadsheetTableCalculatedColumns, spreadsheetTextOrientationFromChoice, xlsxRichTextCellText, isSpreadsheetTextOrientationId, spreadsheetTextOrientationFromAngle, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, spreadsheetCellValueWithDiagonalBorder, spreadsheetUnderlineCellValue, normalizeSpreadsheetDataValidationErrorStyle, normalizeXlsxRichTextEditSource, normalizeXlsxRichTextColor, isHighSurrogate, xlsxNativeFillCellKeys, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, boundedSpreadsheetDataValidationText, normalizeXlsxRichTextCell, activeXlsxNativeFill, spreadsheetUnderlineStyle, normalizeSpreadsheetDateValidationBoundary, deleteXlsxNativeFills, spreadsheetTextOrientationCellStyle, spreadsheetTextOrientationChoiceFromCell, normalizeXlsxRichTextRun, spreadsheetDiagonalBorderFromCellValue, sameXlsxRichTextRunStyle, isLowSurrogate, spreadsheetTextOrientationFromCell, coalesceXlsxRichTextRuns, patchSpreadsheetRichTextFontRuns, fillSpreadsheetTableCalculatedColumns, validXlsxRichText, replaceOfficeSpreadsheetCollaboration as replaceWorkOfficeSpreadsheetCollaboration, isSpreadsheetUnderlineStyle, spreadsheetVisibleTextRotationFromCell } from "./3266.js";
10
10
  import { moveOfficeMenuFocus, stepOfficeZoom, useOfficeRemoteParticipants, Popover, Dialog, officePresenceColor, useOfficePublishPresenceLocation, button_Button, icon_button_IconButton, moveOfficeGridMenuFocus, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom } from "./0~8136.js";
11
11
  import { isOfficeKernelSpreadsheetError, normalizeCssColor, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, createOfficeId as createWorkId } from "./4121.js";
12
12
  import { useOfficeTaskPaneEscape, OfficeCheckbox, CommittedOfficeNumberField, useOfficeDialog, useOfficeTaskPaneModal, createOfficeKernelClient, officeFontFamilies, CollectionState, OfficeNumberField, handleOfficeTaskPaneKeyDown } from "./0~7048.js";
@@ -3769,12 +3769,22 @@ function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations
3769
3769
  const sheetOperations = operations.filter((operation)=>operation.id === source.id);
3770
3770
  let tables = source.tables;
3771
3771
  const reconcileColumns = new Set();
3772
+ const calculatedColumnRows = new Map();
3772
3773
  if (!operations.length) for (const table of tables)reconcileColumns.add(table.id);
3773
3774
  for (const operation of sheetOperations){
3774
3775
  const structure = spreadsheetTableStructureChangeFromOperation(operation);
3775
3776
  if (structure) {
3777
+ transformCalculatedColumnRows(calculatedColumnRows, structure);
3776
3778
  const transformed = [];
3777
3779
  for (const table of tables){
3780
+ if ('row' === structure.axis && 'insert' === structure.kind) {
3781
+ const insertedRows = rowsInsertedIntoSpreadsheetTableBody(table, structure);
3782
+ if (insertedRows.length) {
3783
+ const rows = calculatedColumnRows.get(table.id) ?? new Set();
3784
+ for (const row of insertedRows)rows.add(row);
3785
+ calculatedColumnRows.set(table.id, rows);
3786
+ }
3787
+ }
3778
3788
  const next = transformSpreadsheetTableStructure(table, structure);
3779
3789
  if (next) {
3780
3790
  transformed.push(next);
@@ -3786,7 +3796,7 @@ function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations
3786
3796
  }
3787
3797
  const coordinate = spreadsheetCellCoordinateFromOperation(operation);
3788
3798
  if (!coordinate) continue;
3789
- const table = tables.find((candidate)=>candidate.headerRow && candidate.range.row[0] === coordinate.row && coordinate.column >= candidate.range.column[0] && coordinate.column <= candidate.range.column[1]);
3799
+ const table = tables.find((candidate)=>spreadsheetTableContainsCell(candidate, coordinate.row, coordinate.column));
3790
3800
  if (table) reconcileColumns.add(table.id);
3791
3801
  }
3792
3802
  const updates = [];
@@ -3794,7 +3804,11 @@ function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations
3794
3804
  tables = tables.map((table)=>{
3795
3805
  if (!reconcileColumns.has(table.id)) return table;
3796
3806
  const columns = canonicalSpreadsheetTableColumns(table, readCell);
3797
- if (table.headerRow) for (const [offset, column] of columns.entries()){
3807
+ const calculatedColumns = reconcileSpreadsheetTableCalculatedColumns(sheet, {
3808
+ ...table,
3809
+ columns
3810
+ });
3811
+ if (table.headerRow) for (const [offset, column] of calculatedColumns.entries()){
3798
3812
  const cellColumn = table.range.column[0] + offset;
3799
3813
  if (spreadsheetCellText(readCell(table.range.row[0], cellColumn)) !== column.name) updates.push({
3800
3814
  column: cellColumn,
@@ -3802,12 +3816,19 @@ function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations
3802
3816
  row: table.range.row[0]
3803
3817
  });
3804
3818
  }
3805
- return sameSpreadsheetTableColumns(table.columns, columns) ? table : {
3819
+ return sameSpreadsheetTableColumns(table.columns, calculatedColumns) ? table : {
3806
3820
  ...table,
3807
- columns
3821
+ columns: calculatedColumns
3808
3822
  };
3809
3823
  });
3810
- const withHeaders = stampSpreadsheetTableHeaderCells(sheet, updates);
3824
+ let withCalculatedColumns = sheet;
3825
+ for (const table of tables){
3826
+ const rows = calculatedColumnRows.get(table.id);
3827
+ if (rows?.size) withCalculatedColumns = fillSpreadsheetTableCalculatedColumns(withCalculatedColumns, table, [
3828
+ ...rows
3829
+ ]);
3830
+ }
3831
+ const withHeaders = stampSpreadsheetTableHeaderCells(withCalculatedColumns, updates);
3811
3832
  return {
3812
3833
  ...withHeaders,
3813
3834
  tables: tables.length ? tables : void 0
@@ -3870,6 +3891,39 @@ function transformSpreadsheetTableStructure(table, change) {
3870
3891
  filters: filtersAfterSpreadsheetTableColumnDeletion(table.filters, transformed.offset, removed)
3871
3892
  };
3872
3893
  }
3894
+ function spreadsheetTableContainsCell(table, row, column) {
3895
+ return row >= table.range.row[0] && row <= table.range.row[1] && column >= table.range.column[0] && column <= table.range.column[1];
3896
+ }
3897
+ function rowsInsertedIntoSpreadsheetTableBody(table, change) {
3898
+ if ('row' !== change.axis) return [];
3899
+ const transformed = transformSpreadsheetTableAxisForInsertion(table.range.row, change);
3900
+ if (!transformed.inside) return [];
3901
+ const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
3902
+ const bodyStart = table.range.row[0] + Number(table.headerRow);
3903
+ if (insertion < bodyStart) return [];
3904
+ return Array.from({
3905
+ length: change.count
3906
+ }, (_, offset)=>insertion + offset);
3907
+ }
3908
+ function transformCalculatedColumnRows(rowsByTable, change) {
3909
+ if ('row' !== change.axis || !rowsByTable.size) return;
3910
+ if ('insert' === change.kind) {
3911
+ const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
3912
+ for (const [tableId, rows] of rowsByTable){
3913
+ const next = new Set();
3914
+ for (const row of rows)next.add(row >= insertion ? row + change.count : row);
3915
+ rowsByTable.set(tableId, next);
3916
+ }
3917
+ return;
3918
+ }
3919
+ const count = change.end - change.start + 1;
3920
+ for (const [tableId, rows] of rowsByTable){
3921
+ const next = new Set();
3922
+ for (const row of rows)if (row < change.start) next.add(row);
3923
+ else if (row > change.end) next.add(row - count);
3924
+ rowsByTable.set(tableId, next);
3925
+ }
3926
+ }
3873
3927
  function transformSpreadsheetTableAxisForInsertion(axis, change) {
3874
3928
  const start = axis[0] ?? 0;
3875
3929
  const end = axis[1] ?? start;
@@ -4092,7 +4146,7 @@ function truncateSpreadsheetTableColumnName(value, maximum) {
4092
4146
  return Array.from(value).slice(0, maximum).join('');
4093
4147
  }
4094
4148
  function sameSpreadsheetTableColumns(left, right) {
4095
- return left.length === right.length && left.every((column, index)=>column.name === right[index]?.name);
4149
+ return left.length === right.length && left.every((column, index)=>column.name === right[index]?.name && column.calculatedFormula === right[index]?.calculatedFormula);
4096
4150
  }
4097
4151
  function spreadsheet_table_reconciliation_isRecord(value) {
4098
4152
  return Boolean(value && 'object' == typeof value && !Array.isArray(value));
@@ -9378,7 +9432,7 @@ function applySpreadsheetTable(content, request) {
9378
9432
  family: 'medium',
9379
9433
  number: 2
9380
9434
  };
9381
- const table = {
9435
+ const tableWithoutCalculatedColumns = {
9382
9436
  id: createWorkId('spreadsheet-table'),
9383
9437
  name: validation.name,
9384
9438
  range: cloneRange(validation.range),
@@ -9394,6 +9448,10 @@ function applySpreadsheetTable(content, request) {
9394
9448
  showRowStripes: 'none' !== style.family,
9395
9449
  showColumnStripes: false
9396
9450
  };
9451
+ const table = {
9452
+ ...tableWithoutCalculatedColumns,
9453
+ columns: reconcileSpreadsheetTableCalculatedColumns(validation.sheet, tableWithoutCalculatedColumns)
9454
+ };
9397
9455
  const nextSheet = request.headerRow ? stampSpreadsheetTableHeaders(validation.sheet, validation.range, validation.columns) : validation.sheet;
9398
9456
  return replaceSpreadsheetTableSheet(content, {
9399
9457
  ...nextSheet,
@@ -10444,9 +10502,9 @@ function createSpreadsheetFormulaDiagnostics(content, { summary, locations }) {
10444
10502
  });
10445
10503
  if (summary.structuredReferenceCells) diagnostics.push({
10446
10504
  code: 'formula.structured-references',
10447
- severity: 'warning',
10505
+ severity: 'info',
10448
10506
  title: '结构化引用',
10449
- message: `${summary.structuredReferenceCells} 个公式使用表格结构化引用;公式会原样保存,但当前计算引擎不解析 Excel 表对象。`,
10507
+ message: `${summary.structuredReferenceCells} 个公式使用表格结构化引用;浏览器内核支持有界表名、显示名称、连续列范围以及常见标题、数据、汇总和当前行选择。不连续、外部或高级形式会保留公式与缓存值,交由兼容应用重算。`,
10450
10508
  locations: locations.structured
10451
10509
  });
10452
10510
  if (summary.unsupportedFunctions.length) diagnostics.push({
@@ -21908,10 +21966,15 @@ function compileSpreadsheetKernelWorkbook(sheets, fallbackCells = []) {
21908
21966
  cellCount += cells.length;
21909
21967
  sheetIds.add(id);
21910
21968
  sheetNames.add(normalizedName);
21969
+ const tables = spreadsheetKernelTables(sheet.tables);
21970
+ if (null === tables) return null;
21911
21971
  sourceSheets.push({
21912
21972
  id,
21913
21973
  name,
21914
- cells
21974
+ cells,
21975
+ ...tables.length ? {
21976
+ tables
21977
+ } : {}
21915
21978
  });
21916
21979
  }
21917
21980
  const compiled = sourceSheets.map((sheet)=>({
@@ -21954,7 +22017,8 @@ function spreadsheetKernelSourceState(sheets) {
21954
22017
  sourceCells,
21955
22018
  structureKey: JSON.stringify(sheets.map((sheet)=>({
21956
22019
  id: sheet.id,
21957
- name: sheet.name
22020
+ name: sheet.name,
22021
+ tables: sheet.tables ?? []
21958
22022
  })))
21959
22023
  };
21960
22024
  }
@@ -21972,7 +22036,18 @@ function spreadsheetStructureKey(sheets) {
21972
22036
  names.add(normalizedName);
21973
22037
  structure.push({
21974
22038
  id,
21975
- name
22039
+ name,
22040
+ tables: (sheet.tables ?? []).map((table)=>({
22041
+ name: table.name,
22042
+ displayName: table.displayName,
22043
+ startRow: table.range.row[0],
22044
+ endRow: table.range.row[1],
22045
+ startColumn: table.range.column[0],
22046
+ endColumn: table.range.column[1],
22047
+ columns: table.columns.map((column)=>column.name),
22048
+ headerRow: table.headerRow,
22049
+ totalsRow: table.totalsRow
22050
+ }))
21976
22051
  });
21977
22052
  }
21978
22053
  return JSON.stringify(structure);
@@ -22055,6 +22130,49 @@ function spreadsheetKernelInputCell(cell, fallback) {
22055
22130
  const { formula: _formula, ...cachedCell } = cell;
22056
22131
  return cachedCell;
22057
22132
  }
22133
+ function spreadsheetKernelTables(tables) {
22134
+ if (!tables?.length) return [];
22135
+ const names = new Map();
22136
+ const ranges = [];
22137
+ for (const [tableIndex, table] of tables.entries()){
22138
+ const [startRow, endRow] = table.range.row;
22139
+ const [startColumn, endColumn] = table.range.column;
22140
+ if (!table.name?.trim() || utf8ByteLength(table.name) > SPREADSHEET_KERNEL_MAX_IDENTIFIER_BYTES || void 0 !== table.displayName && (!table.displayName.trim() || utf8ByteLength(table.displayName) > SPREADSHEET_KERNEL_MAX_IDENTIFIER_BYTES) || !isSpreadsheetCoordinateIndex(startRow, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) || !isSpreadsheetCoordinateIndex(endRow, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) || !isSpreadsheetCoordinateIndex(startColumn, 16384) || !isSpreadsheetCoordinateIndex(endColumn, 16384) || startRow > endRow || startColumn > endColumn || table.columns.length !== endColumn - startColumn + 1) return null;
22141
+ for (const alias of [
22142
+ table.name,
22143
+ table.displayName
22144
+ ]){
22145
+ if (!alias) continue;
22146
+ const normalized = alias.toLocaleLowerCase();
22147
+ const previous = names.get(normalized);
22148
+ if (void 0 !== previous && previous !== tableIndex) return null;
22149
+ names.set(normalized, tableIndex);
22150
+ }
22151
+ const columnNames = new Set();
22152
+ for (const column of table.columns){
22153
+ if (!column.name?.trim() || utf8ByteLength(column.name) > SPREADSHEET_KERNEL_MAX_IDENTIFIER_BYTES) return null;
22154
+ const normalized = column.name.toLocaleLowerCase();
22155
+ if (columnNames.has(normalized)) return null;
22156
+ columnNames.add(normalized);
22157
+ }
22158
+ const next = {
22159
+ name: table.name,
22160
+ ...table.displayName ? {
22161
+ displayName: table.displayName
22162
+ } : {},
22163
+ startRow,
22164
+ endRow,
22165
+ startColumn,
22166
+ endColumn,
22167
+ columns: table.columns.map((column)=>column.name),
22168
+ headerRow: Boolean(table.headerRow),
22169
+ totalsRow: Boolean(table.totalsRow)
22170
+ };
22171
+ if (ranges.some((previous)=>next.startRow <= previous.endRow && next.endRow >= previous.startRow && next.startColumn <= previous.endColumn && next.endColumn >= previous.startColumn)) return null;
22172
+ ranges.push(next);
22173
+ }
22174
+ return ranges;
22175
+ }
22058
22176
  function spreadsheetSheetsWithKernelChanges(sheets, changes) {
22059
22177
  const changesBySheet = new Map();
22060
22178
  for (const change of changes.values()){
@@ -22313,7 +22431,7 @@ function spreadsheetCalculationOps(sheets, calculatedCells) {
22313
22431
  });
22314
22432
  }
22315
22433
  function sameSpreadsheetStructure(previous, current) {
22316
- return previous.length === current.length && previous.every((sheet, index)=>sheet.id === current[index]?.id && sheet.name === current[index]?.name);
22434
+ return previous.length === current.length && previous.every((sheet, index)=>sheet.id === current[index]?.id && sheet.name === current[index]?.name && JSON.stringify(sheet.tables ?? []) === JSON.stringify(current[index]?.tables ?? []));
22317
22435
  }
22318
22436
  function cellWithCalculatedValue(cell, value) {
22319
22437
  const next = {
@@ -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,178 @@
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
+ }
8
176
  function requiredCoordinate(value, maximum, label, sheetId) {
9
177
  if (!Number.isSafeInteger(value) || value < 0 || value >= maximum) invalidWorkOfficeSpreadsheetInput(`a valid ${label} coordinate in sheet '${sheetId}'`);
10
178
  return value;
@@ -258,18 +426,29 @@ function requiredSpreadsheetTableColumns(value, width, label) {
258
426
  const names = new Set();
259
427
  return value.map((candidate)=>{
260
428
  const record = requiredInputRecord(candidate, `${label} column`);
261
- assertExactRecordKeys(record, [
262
- 'name'
429
+ assertOptionalRecordKeys(record, [
430
+ 'name',
431
+ 'calculatedFormula'
263
432
  ], `column for ${label}`);
264
433
  const name = requiredTableColumnName(record.name, label);
265
434
  const normalized = name.toLocaleLowerCase();
266
435
  if (names.has(normalized)) invalidWorkOfficeSpreadsheetInput(`unique column names for ${label}`);
267
436
  names.add(normalized);
268
- return {
437
+ if (void 0 === record.calculatedFormula) return {
269
438
  name
270
439
  };
440
+ const calculatedFormula = normalizeSpreadsheetTableCalculatedFormula(record.calculatedFormula);
441
+ if (!calculatedFormula || calculatedFormula !== record.calculatedFormula) invalidWorkOfficeSpreadsheetInput(`a bounded structured calculated-column formula for ${label}`);
442
+ return {
443
+ name,
444
+ calculatedFormula
445
+ };
271
446
  });
272
447
  }
448
+ function assertOptionalRecordKeys(record, keys, label) {
449
+ const allowed = new Set(keys);
450
+ if (!Object.hasOwn(record, keys[0] ?? '') || Object.keys(record).some((key)=>!allowed.has(key))) invalidWorkOfficeSpreadsheetInput(`a complete ${label} record without unknown fields`);
451
+ }
273
452
  function requiredSpreadsheetTableFilters(value, width, label) {
274
453
  if (!Array.isArray(value)) invalidWorkOfficeSpreadsheetInput(`an array of filters for ${label}`);
275
454
  const columns = new Set();
@@ -744,7 +923,7 @@ function assertCompatibleValue(previous, next, shared, label) {
744
923
  function spreadsheetCells(sheet) {
745
924
  const result = new Map();
746
925
  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);
926
+ 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
927
  return result;
749
928
  }
750
929
  for (const entry of sheet.celldata ?? [])if (null !== entry.v) result.set(`${entry.r}:${entry.c}`, entry.v);
@@ -854,7 +1033,7 @@ function spreadsheetCellEntries(sheet) {
854
1033
  if (!sheet) return [];
855
1034
  const entries = [];
856
1035
  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({
1036
+ 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
1037
  cell,
859
1038
  column,
860
1039
  coordinate: encodedCoordinate(row, column),
@@ -2216,12 +2395,12 @@ function xlsxRichTextCellText(cell) {
2216
2395
  return normalizeXlsxRichTextCell(cell)?.text ?? null;
2217
2396
  }
2218
2397
  function sheetHasXlsxRichTextCells(sheet) {
2219
- for (const [, row] of sparseArrayEntries(sheet.data))for (const [, cell] of sparseArrayEntries(row))if (cell && normalizeXlsxRichTextCell(cell)) return true;
2398
+ for (const [, row] of spreadsheet_sparse_sparseArrayEntries(sheet.data))for (const [, cell] of spreadsheet_sparse_sparseArrayEntries(row))if (cell && normalizeXlsxRichTextCell(cell)) return true;
2220
2399
  return false;
2221
2400
  }
2222
2401
  function xlsxRichTextStyleOrigins(sheet) {
2223
2402
  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 ?? []){
2403
+ 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
2404
  const fontColor = normalizeXlsxSemanticColorOrigin(run.a3sXlsxColorOrigin);
2226
2405
  if (fontColor && run.fc && xlsxSemanticColorMatchesValue(fontColor, run.fc)) origins.push({
2227
2406
  fontColor
@@ -2241,7 +2420,7 @@ function writeXlsxRichTextCells(worksheet, sheet, semanticPalette) {
2241
2420
  }));
2242
2421
  let remainingCells = 10000;
2243
2422
  let remainingRuns = 100000;
2244
- for (const [row, values] of sparseArrayEntries(sheet.data))for (const [column, cell] of sparseArrayEntries(values)){
2423
+ for (const [row, values] of spreadsheet_sparse_sparseArrayEntries(sheet.data))for (const [column, cell] of spreadsheet_sparse_sparseArrayEntries(values)){
2245
2424
  if (remainingCells <= 0 || remainingRuns <= 0) return;
2246
2425
  if (!cell) continue;
2247
2426
  const richText = normalizeXlsxRichTextCell(cell);
@@ -2384,4 +2563,4 @@ function work_xlsx_rich_text_nonNegativeInteger(value) {
2384
2563
  function work_xlsx_rich_text_isRecord(value) {
2385
2564
  return 'object' == typeof value && null !== value && !Array.isArray(value);
2386
2565
  }
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 };
2566
+ export { activeXlsxNativeFill, activeXlsxSemanticColorOrigin, applyImportedXlsxRichText, boundedSpreadsheetDataValidationText, coalesceXlsxRichTextRuns, createWorkOfficeSpreadsheetCollaborationBinding as createOfficeSpreadsheetCollaborationBinding, createXlsxRichTextReadContext, deleteXlsxNativeFills, directXlsxAlignment, directXlsxFontStyle, fillSpreadsheetTableCalculatedColumns, hasXlsxDirectFontStyle, initializeWorkOfficeSpreadsheetCollaboration as initializeOfficeSpreadsheetCollaboration, isHighSurrogate, isLowSurrogate, isSpreadsheetTextOrientationId, isSpreadsheetUnderlineStyle, normalizeSpreadsheetDataValidationErrorStyle, normalizeSpreadsheetDateValidationBoundary, normalizeSpreadsheetTableCalculatedFormula, normalizeXlsxRichTextCell, normalizeXlsxRichTextColor, normalizeXlsxRichTextEditSource, normalizeXlsxRichTextRun, patchSpreadsheetRichTextFontRuns, readWorkOfficeSpreadsheetCollaboration as readOfficeSpreadsheetCollaboration, readXlsxRichTextCells, reconcileSpreadsheetTableCalculatedColumns, 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 };