@a3s-lab/office 0.34.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));
@@ -5315,6 +5369,70 @@ function spreadsheetCellFillRangeIsSafe(range) {
5315
5369
  function sameSpreadsheetCellRange(left, right) {
5316
5370
  return left.row[0] === right.row[0] && left.row[1] === right.row[1] && left.column[0] === right.column[0] && left.column[1] === right.column[1];
5317
5371
  }
5372
+ function canEditSpreadsheetSelection(context) {
5373
+ return Boolean(context.editable && context.workbook && context.targetSheetId);
5374
+ }
5375
+ function spreadsheetLiveCommandRange(context) {
5376
+ const selection = spreadsheetLiveCommandSelection(context);
5377
+ return spreadsheetSingleRange(selection ?? context.fallbackRange);
5378
+ }
5379
+ function spreadsheetLiveCommandSelection(context) {
5380
+ return spreadsheetLiveCommandSelections(context)?.at(-1);
5381
+ }
5382
+ function spreadsheetLiveCommandSelections(context) {
5383
+ const shadow = context.selectionRef?.current;
5384
+ if (shadow?.sheetId === context.targetSheetId) return [
5385
+ shadow.selection
5386
+ ];
5387
+ return context.workbook?.getSelection();
5388
+ }
5389
+ function acceptSpreadsheetSelectionChange(selectionRef, sheetId, selection) {
5390
+ const nextSelection = finiteSpreadsheetSelection(selection);
5391
+ const requested = selectionRef.requested;
5392
+ if (requested && (requested.sheetId !== sheetId || !sameSpreadsheetSelectionValue(requested.selection, nextSelection))) return null;
5393
+ const next = {
5394
+ sheetId,
5395
+ selection: nextSelection
5396
+ };
5397
+ selectionRef.current = next;
5398
+ return next;
5399
+ }
5400
+ function rememberSpreadsheetCommandSelection(context, selection) {
5401
+ if (!context.selectionRef || !context.targetSheetId) return;
5402
+ const next = {
5403
+ ...selection,
5404
+ row: [
5405
+ ...selection.row
5406
+ ],
5407
+ column: [
5408
+ ...selection.column
5409
+ ]
5410
+ };
5411
+ context.selectionRef.current = {
5412
+ sheetId: context.targetSheetId,
5413
+ selection: next
5414
+ };
5415
+ context.selectionRef.requested = {
5416
+ sheetId: context.targetSheetId,
5417
+ selection: {
5418
+ ...next,
5419
+ row: [
5420
+ ...next.row
5421
+ ],
5422
+ column: [
5423
+ ...next.column
5424
+ ]
5425
+ }
5426
+ };
5427
+ }
5428
+ function releaseSpreadsheetSelectionRequest(selectionRef) {
5429
+ selectionRef.requested = null;
5430
+ }
5431
+ function sameSpreadsheetSelectionValue(left, right) {
5432
+ const normalizedLeft = finiteSpreadsheetSelection(left);
5433
+ const normalizedRight = finiteSpreadsheetSelection(right);
5434
+ return normalizedLeft.row[0] === normalizedRight.row[0] && normalizedLeft.row[1] === normalizedRight.row[1] && normalizedLeft.column[0] === normalizedRight.column[0] && normalizedLeft.column[1] === normalizedRight.column[1] && normalizedLeft.row_focus === normalizedRight.row_focus && normalizedLeft.column_focus === normalizedRight.column_focus;
5435
+ }
5318
5436
  function createSpreadsheetCellFillExtension() {
5319
5437
  return createOfficeEditorExtension({
5320
5438
  name: 'spreadsheetCellFill',
@@ -5365,9 +5483,11 @@ function materializeSpreadsheetCellFillRows(sheet, plan) {
5365
5483
  }
5366
5484
  function spreadsheetSelectedCellFillPlan(context, direction) {
5367
5485
  if (!context.editable || !context.workbook || !context.targetSheetId || context.targetSheetId !== context.activeSheetId) return null;
5368
- const selections = context.workbook.getSelection();
5486
+ const selections = spreadsheetLiveCommandSelections(context);
5369
5487
  if (selections?.length !== 1) return null;
5370
- const plan = planSpreadsheetCellFill(selections[0], direction);
5488
+ const selection = selections[0];
5489
+ if (!selection) return null;
5490
+ const plan = planSpreadsheetCellFill(selection, direction);
5371
5491
  if (!plan) return null;
5372
5492
  const sheet = context.content.sheets.find((candidate)=>candidate.id === context.targetSheetId);
5373
5493
  return canApplySpreadsheetCellFill(sheet, plan) ? plan : null;
@@ -6323,13 +6443,6 @@ function applySelectedCellStyle(context, preset) {
6323
6443
  function liveSpreadsheetCellStyleRange(context) {
6324
6444
  return spreadsheetSingleRange(context.workbook?.getSelection()?.at(-1) ?? context.fallbackRange);
6325
6445
  }
6326
- function canEditSpreadsheetSelection(context) {
6327
- return Boolean(context.editable && context.workbook && context.targetSheetId);
6328
- }
6329
- function spreadsheetLiveCommandRange(context) {
6330
- const selection = context.workbook?.getSelection()?.at(-1);
6331
- return spreadsheetSingleRange(selection ?? context.fallbackRange);
6332
- }
6333
6446
  const MAX_SPREADSHEET_ROWS = 1048576;
6334
6447
  const spreadsheetPasteContentOptions = [
6335
6448
  {
@@ -8493,7 +8606,7 @@ function updateSpreadsheetSelection(context, storage, update) {
8493
8606
  if (!context.workbook || !context.targetSheetId) return false;
8494
8607
  const sheet = context.content.sheets.find((candidate)=>candidate.id === context.targetSheetId);
8495
8608
  if (!sheet) return false;
8496
- const liveSelection = context.workbook.getSelection()?.at(-1) ?? context.fallbackRange;
8609
+ const liveSelection = spreadsheetLiveCommandSelection(context) ?? context.fallbackRange;
8497
8610
  const rememberedFocus = storage.focus;
8498
8611
  const selection = rememberedFocus && spreadsheetSelectionContainsFocus(liveSelection, rememberedFocus) ? {
8499
8612
  ...liveSelection,
@@ -8507,6 +8620,7 @@ function updateSpreadsheetSelection(context, storage, update) {
8507
8620
  ], {
8508
8621
  id: context.targetSheetId
8509
8622
  });
8623
+ rememberSpreadsheetCommandSelection(context, next);
8510
8624
  storage.focus = {
8511
8625
  row: next.row_focus ?? next.row[1] ?? next.row[0] ?? 0,
8512
8626
  column: next.column_focus ?? next.column[1] ?? next.column[0] ?? 0
@@ -9318,7 +9432,7 @@ function applySpreadsheetTable(content, request) {
9318
9432
  family: 'medium',
9319
9433
  number: 2
9320
9434
  };
9321
- const table = {
9435
+ const tableWithoutCalculatedColumns = {
9322
9436
  id: createWorkId('spreadsheet-table'),
9323
9437
  name: validation.name,
9324
9438
  range: cloneRange(validation.range),
@@ -9334,6 +9448,10 @@ function applySpreadsheetTable(content, request) {
9334
9448
  showRowStripes: 'none' !== style.family,
9335
9449
  showColumnStripes: false
9336
9450
  };
9451
+ const table = {
9452
+ ...tableWithoutCalculatedColumns,
9453
+ columns: reconcileSpreadsheetTableCalculatedColumns(validation.sheet, tableWithoutCalculatedColumns)
9454
+ };
9337
9455
  const nextSheet = request.headerRow ? stampSpreadsheetTableHeaders(validation.sheet, validation.range, validation.columns) : validation.sheet;
9338
9456
  return replaceSpreadsheetTableSheet(content, {
9339
9457
  ...nextSheet,
@@ -10010,6 +10128,16 @@ function pasteCells(context, values) {
10010
10128
  ], {
10011
10129
  id: context.targetSheetId
10012
10130
  });
10131
+ rememberSpreadsheetCommandSelection(context, {
10132
+ row: [
10133
+ ...range.row
10134
+ ],
10135
+ column: [
10136
+ ...range.column
10137
+ ],
10138
+ row_focus: range.row[0],
10139
+ column_focus: range.column[0]
10140
+ });
10013
10141
  } catch {}
10014
10142
  syncSpreadsheetFormulaBar(context, data[0][0]);
10015
10143
  return true;
@@ -10374,9 +10502,9 @@ function createSpreadsheetFormulaDiagnostics(content, { summary, locations }) {
10374
10502
  });
10375
10503
  if (summary.structuredReferenceCells) diagnostics.push({
10376
10504
  code: 'formula.structured-references',
10377
- severity: 'warning',
10505
+ severity: 'info',
10378
10506
  title: '结构化引用',
10379
- message: `${summary.structuredReferenceCells} 个公式使用表格结构化引用;公式会原样保存,但当前计算引擎不解析 Excel 表对象。`,
10507
+ message: `${summary.structuredReferenceCells} 个公式使用表格结构化引用;浏览器内核支持有界表名、显示名称、连续列范围以及常见标题、数据、汇总和当前行选择。不连续、外部或高级形式会保留公式与缓存值,交由兼容应用重算。`,
10380
10508
  locations: locations.structured
10381
10509
  });
10382
10510
  if (summary.unsupportedFunctions.length) diagnostics.push({
@@ -21838,10 +21966,15 @@ function compileSpreadsheetKernelWorkbook(sheets, fallbackCells = []) {
21838
21966
  cellCount += cells.length;
21839
21967
  sheetIds.add(id);
21840
21968
  sheetNames.add(normalizedName);
21969
+ const tables = spreadsheetKernelTables(sheet.tables);
21970
+ if (null === tables) return null;
21841
21971
  sourceSheets.push({
21842
21972
  id,
21843
21973
  name,
21844
- cells
21974
+ cells,
21975
+ ...tables.length ? {
21976
+ tables
21977
+ } : {}
21845
21978
  });
21846
21979
  }
21847
21980
  const compiled = sourceSheets.map((sheet)=>({
@@ -21884,7 +22017,8 @@ function spreadsheetKernelSourceState(sheets) {
21884
22017
  sourceCells,
21885
22018
  structureKey: JSON.stringify(sheets.map((sheet)=>({
21886
22019
  id: sheet.id,
21887
- name: sheet.name
22020
+ name: sheet.name,
22021
+ tables: sheet.tables ?? []
21888
22022
  })))
21889
22023
  };
21890
22024
  }
@@ -21902,7 +22036,18 @@ function spreadsheetStructureKey(sheets) {
21902
22036
  names.add(normalizedName);
21903
22037
  structure.push({
21904
22038
  id,
21905
- 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
+ }))
21906
22051
  });
21907
22052
  }
21908
22053
  return JSON.stringify(structure);
@@ -21985,6 +22130,49 @@ function spreadsheetKernelInputCell(cell, fallback) {
21985
22130
  const { formula: _formula, ...cachedCell } = cell;
21986
22131
  return cachedCell;
21987
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
+ }
21988
22176
  function spreadsheetSheetsWithKernelChanges(sheets, changes) {
21989
22177
  const changesBySheet = new Map();
21990
22178
  for (const change of changes.values()){
@@ -22243,7 +22431,7 @@ function spreadsheetCalculationOps(sheets, calculatedCells) {
22243
22431
  });
22244
22432
  }
22245
22433
  function sameSpreadsheetStructure(previous, current) {
22246
- 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 ?? []));
22247
22435
  }
22248
22436
  function cellWithCalculatedValue(cell, value) {
22249
22437
  const next = {
@@ -25395,6 +25583,60 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
25395
25583
  const editorFocusOrigin = useOfficeEditorFocusOrigin();
25396
25584
  const workbookRef = useRef(null);
25397
25585
  const projectedWorkbookSheetsRef = useRef([]);
25586
+ useLayoutEffect(()=>{
25587
+ const container = spreadsheetCanvasRef.current;
25588
+ if (!container || preview) return;
25589
+ let pendingEditor = null;
25590
+ let pointerEnteredEditor = false;
25591
+ const moveEditorCaretToEnd = (editor)=>{
25592
+ if (document.activeElement !== editor || !editor.isConnected || !editor.textContent) return false;
25593
+ const selection = document.getSelection();
25594
+ if (!selection) return false;
25595
+ const range = document.createRange();
25596
+ range.selectNodeContents(editor);
25597
+ range.collapse(false);
25598
+ selection.removeAllRanges();
25599
+ selection.addRange(range);
25600
+ return true;
25601
+ };
25602
+ const handlePointerDown = (event)=>{
25603
+ pointerEnteredEditor = event.target instanceof Element && Boolean(event.target.closest('.luckysheet-cell-input'));
25604
+ };
25605
+ const handleFocusIn = (event)=>{
25606
+ const target = event.target;
25607
+ const editor = target instanceof Element ? target.closest('.luckysheet-cell-input') : null;
25608
+ if (!editor || pointerEnteredEditor) {
25609
+ pendingEditor = null;
25610
+ pointerEnteredEditor = false;
25611
+ return;
25612
+ }
25613
+ pendingEditor = editor;
25614
+ queueMicrotask(()=>{
25615
+ if (pendingEditor === editor && moveEditorCaretToEnd(editor)) pendingEditor = null;
25616
+ });
25617
+ };
25618
+ const observer = new MutationObserver(()=>{
25619
+ const editor = pendingEditor;
25620
+ if (!editor || document.activeElement !== editor) return;
25621
+ if (!editor.textContent) return;
25622
+ moveEditorCaretToEnd(editor);
25623
+ pendingEditor = null;
25624
+ });
25625
+ container.addEventListener('pointerdown', handlePointerDown, true);
25626
+ container.addEventListener('focusin', handleFocusIn, true);
25627
+ observer.observe(container, {
25628
+ characterData: true,
25629
+ childList: true,
25630
+ subtree: true
25631
+ });
25632
+ return ()=>{
25633
+ container.removeEventListener('pointerdown', handlePointerDown, true);
25634
+ container.removeEventListener('focusin', handleFocusIn, true);
25635
+ observer.disconnect();
25636
+ };
25637
+ }, [
25638
+ preview
25639
+ ]);
25398
25640
  const spreadsheetZoomRef = useRef(100);
25399
25641
  const [workbookInstance, setWorkbookInstance] = useState(null);
25400
25642
  const bindWorkbookInstance = useCallback((instance)=>{
@@ -25413,6 +25655,10 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
25413
25655
  const [panel, setPanel] = useState(null);
25414
25656
  const panelTriggerRef = useRef(null);
25415
25657
  const [selectionState, setSelectionState] = useState(null);
25658
+ const selectionStateRef = useRef({
25659
+ current: null,
25660
+ requested: null
25661
+ });
25416
25662
  const formatPainterSelectionHandlerRef = useRef((_sheetId, _selection)=>void 0);
25417
25663
  const [contextMenu, setContextMenu] = useState(null);
25418
25664
  const [formatCellsDialog, setFormatCellsDialog] = useState(null);
@@ -25588,15 +25834,16 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
25588
25834
  collaborationView?.activateSheet(id);
25589
25835
  if (previewRef.current) setPreviewActiveSheetId(id);
25590
25836
  else setNavigationActiveSheetId(id);
25837
+ selectionStateRef.current.current = null;
25838
+ releaseSpreadsheetSelectionRequest(selectionStateRef.current);
25591
25839
  setSelectionState(null);
25592
25840
  },
25593
25841
  afterSelectionChange: (sheetId, selection)=>{
25594
- collaborationView?.select(sheetId, selection);
25595
- setSelectionState({
25596
- sheetId,
25597
- selection
25598
- });
25599
- formatPainterSelectionHandlerRef.current(sheetId, selection);
25842
+ const next = acceptSpreadsheetSelectionChange(selectionStateRef.current, sheetId, selection);
25843
+ if (!next) return;
25844
+ collaborationView?.select(sheetId, next.selection);
25845
+ setSelectionState(next);
25846
+ formatPainterSelectionHandlerRef.current(sheetId, next.selection);
25600
25847
  },
25601
25848
  beforeUpdateCell: (row, column)=>{
25602
25849
  const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === activeSheetIdRef.current);
@@ -25876,6 +26123,8 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
25876
26123
  if (!sheet) return false;
25877
26124
  activeSheetIdRef.current = sheetId;
25878
26125
  if (collaborationView && !collaborationView.activateSheet(sheetId)) return false;
26126
+ selectionStateRef.current.current = null;
26127
+ releaseSpreadsheetSelectionRequest(selectionStateRef.current);
25879
26128
  setSelectionState(null);
25880
26129
  if (previewRef.current) setPreviewActiveSheetId(sheetId);
25881
26130
  try {
@@ -25910,7 +26159,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
25910
26159
  targetRow: activeCell.row,
25911
26160
  targetColumn: activeCell.column
25912
26161
  });
25913
- setSelectionState({
26162
+ const nextSelection = {
25914
26163
  sheetId,
25915
26164
  selection: {
25916
26165
  row: [
@@ -25922,7 +26171,21 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
25922
26171
  row_focus: activeCell.row,
25923
26172
  column_focus: activeCell.column
25924
26173
  }
25925
- });
26174
+ };
26175
+ selectionStateRef.current.current = nextSelection;
26176
+ selectionStateRef.current.requested = {
26177
+ sheetId,
26178
+ selection: {
26179
+ ...nextSelection.selection,
26180
+ row: [
26181
+ ...nextSelection.selection.row
26182
+ ],
26183
+ column: [
26184
+ ...nextSelection.selection.column
26185
+ ]
26186
+ }
26187
+ };
26188
+ setSelectionState(nextSelection);
25926
26189
  } catch {
25927
26190
  return false;
25928
26191
  }
@@ -26043,6 +26306,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
26043
26306
  toggle: (attribute)=>richTextSelectionRef.current?.toggle(contentRef.current, acceptSpreadsheetCommandChange, attribute) ?? false
26044
26307
  },
26045
26308
  selection: selectionState,
26309
+ selectionRef: selectionStateRef.current,
26046
26310
  table: spreadsheetTable.commandPort,
26047
26311
  targetSheetGridSize,
26048
26312
  targetSheetId: toolbarSheetId,
@@ -26150,6 +26414,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
26150
26414
  if ('Escape' === event.key && isSpreadsheetCellEditingTarget(event.target)) requestAnimationFrame(()=>focusSpreadsheetGrid(spreadsheetCanvasRef.current));
26151
26415
  };
26152
26416
  const handleSpreadsheetKeyDownCapture = (event)=>{
26417
+ releaseSpreadsheetSelectionRequest(selectionStateRef.current);
26153
26418
  if ('Alt' === event.key && !event.repeat && event.target instanceof Element && event.target.closest('.fortune-sheet-overlay') && reserveAutoFilterAltKey()) {
26154
26419
  event.preventDefault();
26155
26420
  event.stopPropagation();
@@ -26158,6 +26423,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
26158
26423
  handleSpreadsheetEditingEscape(event);
26159
26424
  };
26160
26425
  const handleSpreadsheetPointerDownCapture = (event)=>{
26426
+ releaseSpreadsheetSelectionRequest(selectionStateRef.current);
26161
26427
  const controller = richTextSelectionRef.current;
26162
26428
  if (!controller) return;
26163
26429
  if (!isSpreadsheetRichTextFormatPointerTarget(event.target)) return void controller.clear();
@@ -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(', ')}.`));