@a3s-lab/office 0.43.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +30 -1
  2. package/dist/0~1403.js +1 -1
  3. package/dist/0~2330.js +2 -2
  4. package/dist/0~3539.js +2 -2
  5. package/dist/0~3557.js +1 -1
  6. package/dist/{0~5024.js → 0~4042.js} +111 -3
  7. package/dist/0~4980.js +1 -1
  8. package/dist/0~7828.js +1 -1
  9. package/dist/{0~7048.js → 0~7831.js} +2 -547
  10. package/dist/0~document-editor.js +154 -5
  11. package/dist/{0~3635.js → 0~office-color-picker.js} +1 -12
  12. package/dist/0~pdf-viewer.js +22 -8
  13. package/dist/0~presentation-editor.js +4 -3
  14. package/dist/0~spreadsheet-editor.js +200 -3669
  15. package/dist/0~work-docx-export.js +11 -8
  16. package/dist/0~work-docx-import.js +11 -5
  17. package/dist/0~work-office-diagnostics.js +14 -3
  18. package/dist/0~work-pptx-export.js +1 -1
  19. package/dist/0~work-pptx-import.js +1 -1
  20. package/dist/0~work-presentation-charts.js +1 -1
  21. package/dist/3818.js +1 -1
  22. package/dist/4166.js +1 -1
  23. package/dist/4174.js +112 -4
  24. package/dist/4560.js +2 -2
  25. package/dist/{5416.js → 610.js} +4335 -410
  26. package/dist/{4121.js → 6968.js} +622 -13
  27. package/dist/7060.js +1 -1
  28. package/dist/{4650.js → 816.js} +12 -1
  29. package/dist/8715.js +1 -1
  30. package/dist/9333.js +17 -17
  31. package/dist/core.js +7 -5
  32. package/dist/index.js +2 -2
  33. package/dist/internal/features/work/editors/document-picture-properties-dialog-model.d.ts +4 -1
  34. package/dist/internal/features/work/editors/spreadsheet-data-validation-list.d.ts +44 -0
  35. package/dist/internal/features/work/editors/spreadsheet-data-validation.d.ts +2 -1
  36. package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +1 -1
  37. package/dist/internal/features/work/work-document-image-layout.d.ts +14 -0
  38. package/dist/internal/features/work/work-docx-export-image.d.ts +2 -1
  39. package/dist/internal/features/work/work-docx-image-layout-import.d.ts +2 -1
  40. package/dist/internal/features/work/work-docx-image-transform.d.ts +17 -0
  41. package/dist/internal/features/work/work-types.d.ts +4 -0
  42. package/dist/office-kernel.wasm +0 -0
  43. package/dist/styles.css +51 -3
  44. package/docs/latest/en/browser-editor-architecture.md +7 -3
  45. package/package.json +1 -1
@@ -3,19 +3,19 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Workbook } from "@fortune-sheet/react";
4
4
  import { AArrowDown, AArrowUp, AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BadgeJapaneseYen, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, CalendarClock, CalendarDays, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Clock, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Globe2, Grid2X2, Grid3X3, Hash, Info, Italic, KeyRound, Layers2, Link2, Link2Off, ListChecks, ListFilter, ListOrdered, ListPlus, LocateFixed, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, Settings2, ShieldCheck, Sigma, SlidersHorizontal, SortAsc, SortDesc, Strikethrough, SwatchBook, Table2, TableProperties, Trash2, TriangleAlert, Underline, Undo2, WrapText, X } from "lucide-react";
5
5
  import { Fragment as external_react_Fragment, cloneElement, createElement, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
- import { Parser, SUPPORTED_FORMULAS } from "@fortune-sheet/formula-parser";
7
6
  import { convertSpanToShareString, genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
8
- import { useOfficeTaskPaneEscape, OfficeCheckbox, useOfficeDialog, CommittedOfficeNumberField, useOfficeTaskPaneModal, createOfficeKernelClient, officeFontFamilies, CollectionState, OfficeNumberField, handleOfficeTaskPaneKeyDown, normalizeFormulaForFortuneParser } from "./0~7048.js";
9
- 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, isSpreadsheetConditionalIconSetName, spreadsheetConditionalThresholdsEqual, spreadsheetConditionalIconSetCount, spreadsheetConditionalIconAppearance } from "./9333.js";
10
- 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";
11
- import { workSpreadsheetFilterHiddenRows, xlsxRichTextCellText, spreadsheetTextOrientationFromAngle, spreadsheetCellValueWithDiagonalBorder, spreadsheetUnderlineCellValue, workSpreadsheetAutoFilterCriteriaEntries, spreadsheetTableTotalsFunctionLabel, withDefaultSpreadsheetTableTotalsLabel, normalizeXlsxRichTextEditSource, spreadsheetTableTotalsColumnPatch, SPREADSHEET_TABLE_TOTALS_FUNCTIONS, normalizeXlsxRichTextCell, activeXlsxNativeFill, normalizeSpreadsheetDateValidationBoundary, spreadsheetTextOrientationChoiceFromCell, parseSpreadsheetSortCustomList, workSpreadsheetFilterTextCharacters, sameXlsxRichTextRunStyle, spreadsheetSortCustomListMatchKey, createSpreadsheetSortCustomList, reconcileSpreadsheetTableTotalsColumns, spreadsheetTextOrientationFromCell, patchSpreadsheetRichTextFontRuns, synchronizeSpreadsheetTableTotalsRow, workSpreadsheetAutoFilterManuallyHiddenRows, normalizedWorkSpreadsheetAutoFilterRange, workSpreadsheetSheetWithoutAutoFilterCriteria, isSpreadsheetTextOrientationId, reconcileSpreadsheetTableCalculatedColumns, spreadsheetVisibleTextRotationFromCell, spreadsheetTextOrientationFromChoice, normalizeWorkSpreadsheetFilterCriteria, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, normalizeSpreadsheetDataValidationErrorStyle, workSpreadsheetAutoFilterCriteria, normalizeXlsxRichTextColor, isHighSurrogate, xlsxNativeFillCellKeys, workSpreadsheetSheetWithReappliedAutoFilterCriteria, workSpreadsheetSheetWithAutoFilterCriteria, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, boundedSpreadsheetDataValidationText, spreadsheetUnderlineStyle, workSpreadsheetCellIsDate, workSpreadsheetAutoFilterOwnedRows, deleteXlsxNativeFills, spreadsheetTextOrientationCellStyle, normalizeXlsxRichTextRun, spreadsheetDiagonalBorderFromCellValue, isLowSurrogate, mergeSpreadsheetSortCustomLists, spreadsheetTableTotalsRowHasContent, validateSpreadsheetSortCustomList, normalizeStoredSpreadsheetSortCustomLists, coalesceXlsxRichTextRuns, fillSpreadsheetTableCalculatedColumns, replaceOfficeSpreadsheetCollaboration as replaceWorkOfficeSpreadsheetCollaboration, validXlsxRichText, spreadsheetSortCustomListsEqual, isSpreadsheetUnderlineStyle } from "./5416.js";
7
+ import { spreadsheetCellRangeContains, isSpreadsheetCellEditorTarget, spreadsheetSortAppearanceFields, finiteSpreadsheetSelection, withDefaultSpreadsheetTableTotalsLabel, SPREADSHEET_TABLE_TOTALS_FUNCTIONS, normalizeSpreadsheetDateValidationBoundary, createSpreadsheetSortDirectAppearanceRows, createSpreadsheetSortRangePlan, spreadsheetTextOrientationChoiceFromCell, spreadsheetSheetsForFortune, selectSpreadsheetFormulaBarContents, evaluateSpreadsheetLocalFormula, normalizeSpreadsheetCellRange as spreadsheet_cell_range_normalizeSpreadsheetCellRange, spreadsheetCellRangesIntersect, parseSpreadsheetSortCustomList, subtractSpreadsheetCellRange, workSpreadsheetFilterTextCharacters, spreadsheetSheetsWithFiniteSelections, spreadsheetTextOrientationFromCell, captureSpreadsheetRichTextPaste, patchSpreadsheetRichTextFontRuns, withSpreadsheetCellFillFormat, spreadsheetCellRangeArea as spreadsheet_cell_range_spreadsheetCellRangeArea, normalizeSpreadsheetDependentListFormula, spreadsheetSortAppearanceTargetsEqual, normalizeWorkSpreadsheetFilterCriteria, spreadsheet_editor_support_spreadsheetCellAt, isSpreadsheetTextOrientationId, spreadsheetSortAppearanceTargetValue, reconcileSpreadsheetTableCalculatedColumns, workSpreadsheetAutoFilterCriteria, spreadsheetSortCellMatchesAppearance, spreadsheetSelectionSummary, normalizeXlsxRichTextColor, resolveSpreadsheetGoToTarget, createSpreadsheetSortDialogSource, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, boundedSpreadsheetDataValidationText, workSpreadsheetCellIsDate, scopeSpreadsheetKeyboardSelection, captureSpreadsheetRichTextDomSelection, spreadsheetSortAppearanceRowsMatch, spreadsheetTableTotalsRowHasContent, spreadsheetDiagonalBorderFromCellValue, reconcileSpreadsheetFiltersAfterSort, parseSpreadsheetCellRange, spreadsheetFontSizeOptions, spreadsheetCellFillFormat, normalizeStoredSpreadsheetSortCustomLists, spreadsheetContentWithSelection, spreadsheetConditionalFormatStyles, replaceOfficeSpreadsheetCollaboration as replaceWorkOfficeSpreadsheetCollaboration, spreadsheetSortCustomListsEqual, isSpreadsheetUnderlineStyle, spreadsheetLocalFormulaBoolean, xlsxRichTextCellText, spreadsheetTextOrientationFromAngle, spreadsheetCellValueWithDiagonalBorder, spreadsheetUnderlineCellValue, resolveSpreadsheetDependentListReference, spreadsheetFilterReconciliationIsBounded, validateSpreadsheetSortRequest, spreadsheetTableTotalsFunctionLabel, spreadsheetCurrentRegion, createSpreadsheetSortTextComparator, normalizeXlsxRichTextEditSource, spreadsheetTableTotalsColumnPatch, spreadsheetSheetsFromFortune, spreadsheetSortAppearanceTargetLabel, activeXlsxNativeFill, isSpreadsheetDependentListFormula, spreadsheetSortRowsMatchRange, spreadsheetCellRangesEqual, sameSpreadsheetHistoryContent, spreadsheetSheetBounds, isSpreadsheetNativeTextUndoTarget, isSpreadsheetCellEditingTarget, runSpreadsheetSelectionMoveShortcut, spreadsheetGoToValidationMessage, spreadsheetSortCustomListMatchKey, createSpreadsheetSortCustomList, spreadsheetRowHasContent, workSpreadsheetAutoFilterManuallyHiddenRows, normalizedWorkSpreadsheetAutoFilterRange, nextSpreadsheetFontSize, spreadsheetSelectionReference, workSpreadsheetSheetWithoutAutoFilterCriteria, spreadsheetVisibleTextRotationFromCell, spreadsheetTextOrientationFromChoice, isSpreadsheetGridKeyboardTarget, synchronizeSpreadsheetTableTotalsRow, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, sameSpreadsheetWorkbookState, normalizeSpreadsheetDataValidationErrorStyle, moveSpreadsheetKeyboardSelection, spreadsheetSortAppearanceTargets, createSpreadsheetSortAppearanceRows, xlsxNativeFillCellKeys, workSpreadsheetSheetWithAutoFilterCriteria, MAX_SPREADSHEET_SORT_CELLS, runSpreadsheetSelectionScopeShortcut, spreadsheetUnderlineStyle, spreadsheetSortError, deleteXlsxNativeFills, spreadsheetTextOrientationCellStyle, parseSpreadsheetSortAppearanceTargetValue, normalizeSpreadsheetCellFillFormat, createSpreadsheetSortRangeDialogSource, spreadsheetSortRowsFromSheet, spreadsheetSelectionContainsFocus, mergeSpreadsheetSortCustomLists, spreadsheetFontFamilyOptions, canApplySpreadsheetTableStructureChange, spreadsheetSingleRange, sameSpreadsheetWorkbookStateAfterOperations, spreadsheetSheetCellReader, coalesceXlsxRichTextRuns, spreadsheetContentWithSelections, spreadsheetSortOwnedRangeForExactRange } from "./610.js";
12
8
  import { moveOfficeMenuFocus, stepOfficeZoom, useOfficeRemoteParticipants, Popover, Dialog, officePresenceColor, useOfficePublishPresenceLocation, button_Button, icon_button_IconButton, moveOfficeGridMenuFocus, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom } from "./0~8136.js";
13
- import { isOfficeKernelSpreadsheetError, normalizeCssColor, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, createOfficeId as createWorkId } from "./4121.js";
14
- import { isOfficeShortcutBlocked, OfficeColorPicker } from "./0~3635.js";
9
+ 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";
15
10
  import { createOfficeEditorExtension, useOfficeEditorRuntime, useOfficeEditorKeyboardShortcuts } from "./0~3557.js";
11
+ import { isOfficeShortcutBlocked } from "./816.js";
12
+ import { withoutEditableRange, editableRangeRequiresCredentials, spreadsheetMatrixProfile, normalizeSheetProtectionAuthority, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, defaultSpreadsheetDataBarOptions, spreadsheetProtectionKey, withSheetProtection, normalizeSpreadsheetConditionalVisualOptions, defaultSpreadsheetColorScaleThresholds, protectedSheetCount, normalizeSpreadsheetConditionalIconSetFormat, SPREADSHEET_CONDITIONAL_ICON_SETS, effectiveSpreadsheetPageSetup, withSheetSelectionPermissions, withEditableRange, spreadsheetConditionalComparisonNeedsUpperValue, isSpreadsheetConditionalComparisonOperator, drawSpreadsheetConditionalIcon, defaultSpreadsheetConditionalIconThresholds, xlsxPatternFillTypes, sheetProtectionAuthority, editableRangeCellCount, unlockedCellCount, spreadsheetConditionalThresholdsEqual } from "./9333.js";
13
+ import { isOfficeKernelSpreadsheetError, normalizeCssColor, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, createOfficeId as createWorkId } from "./6968.js";
16
14
  import { showToast } from "./6489.js";
17
15
  import { unsupportedSpreadsheetFormulaFunctions } from "./0~work-spreadsheet-formula-support.js";
18
16
  import { WorkspaceContextMenu, isWorkspaceContextMenuKeyboardEvent, WorkOfficeZoomControls, Tabs, WorkOfficeStatusBar, WorkOfficeRibbon, workspaceContextMenuPosition, OfficeSelect, WorkOfficeRibbonGroup, WorkOfficeRibbonButton, WorkOfficePreviewBar } from "./0~9073.js";
17
+ import { OfficeColorPicker } from "./0~office-color-picker.js";
18
+ import { useOfficeTaskPaneEscape, OfficeCheckbox, CommittedOfficeNumberField, useOfficeTaskPaneModal, useOfficeDialog, createOfficeKernelClient, CollectionState, OfficeNumberField, handleOfficeTaskPaneKeyDown } from "./0~7831.js";
19
19
  import { useOfficeDraft, InlineNotice } from "./0~345.js";
20
20
  import { spreadsheetAgentMenuItems, SpreadsheetChartSeriesStyleEditor, normalizeOptionalOfficeNumber, normalizeRequiredOfficeNumber, SpreadsheetErrorBarEditor, useOfficeHistory, spreadsheetAgentSelection, SpreadsheetTrendlineEditor, applySpreadsheetAgentProposalChanges } from "./0~3539.js";
21
21
  import { useOfficeEditorFocusOrigin, useOfficeCollaborationLocationNavigator, StateView } from "./432.js";
@@ -89,546 +89,6 @@ function drawCellValue(context, bounds, cell, conditionalTextColor) {
89
89
  context.fillText(String(value), bounds.startX + 4, bounds.startY + height / 2, bounds.endX - bounds.startX - 8);
90
90
  }
91
91
  }
92
- function parseSpreadsheetConditionalColor(value) {
93
- const hexadecimal = /^#?([0-9a-f]{6})$/i.exec(value.trim());
94
- if (hexadecimal) return {
95
- red: Number.parseInt(hexadecimal[1].slice(0, 2), 16),
96
- green: Number.parseInt(hexadecimal[1].slice(2, 4), 16),
97
- blue: Number.parseInt(hexadecimal[1].slice(4, 6), 16)
98
- };
99
- const rgb = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(value);
100
- if (!rgb) return null;
101
- return {
102
- red: boundedColorChannel(rgb[1]),
103
- green: boundedColorChannel(rgb[2]),
104
- blue: boundedColorChannel(rgb[3])
105
- };
106
- }
107
- function interpolateSpreadsheetConditionalColor(start, end, amount) {
108
- return {
109
- red: Math.round(start.red + (end.red - start.red) * amount),
110
- green: Math.round(start.green + (end.green - start.green) * amount),
111
- blue: Math.round(start.blue + (end.blue - start.blue) * amount)
112
- };
113
- }
114
- function spreadsheetConditionalCssColor(color) {
115
- return `rgb(${color.red}, ${color.green}, ${color.blue})`;
116
- }
117
- function spreadsheetConditionalRatio(value, minimum, maximum) {
118
- if (maximum === minimum) return 0;
119
- return Math.max(0, Math.min(1, (value - minimum) / (maximum - minimum)));
120
- }
121
- function boundedColorChannel(value) {
122
- return Math.max(0, Math.min(255, Number(value)));
123
- }
124
- function spreadsheetConditionalDataBar(color, value, minimum, maximum, minLength, maxLength, showValue) {
125
- if (minimum < 0 && maximum > 0) {
126
- const axis = -minimum / (maximum - minimum) * 100;
127
- const ratio = value < 0 ? Math.abs(value / minimum) : value > 0 ? value / maximum : 0;
128
- const width = 0 === value ? 0 : displayLength(ratio, minLength, maxLength) * (value < 0 ? axis / 100 : (100 - axis) / 100);
129
- return {
130
- color,
131
- startPercent: value < 0 ? axis - width : axis,
132
- widthPercent: width,
133
- axisPercent: axis,
134
- showValue
135
- };
136
- }
137
- if (maximum <= 0) {
138
- const ratio = minimum === maximum ? 0 === value ? 0 : 1 : Math.max(0, Math.min(1, (maximum - value) / (maximum - minimum)));
139
- const width = 0 === value ? 0 : displayLength(ratio, minLength, maxLength);
140
- return {
141
- color,
142
- startPercent: 100 - width,
143
- widthPercent: width,
144
- axisPercent: 100,
145
- showValue
146
- };
147
- }
148
- const ratio = minimum === maximum ? 0 === value ? 0 : 1 : Math.max(0, Math.min(1, (value - minimum) / (maximum - minimum)));
149
- const width = 0 === value ? 0 : displayLength(ratio, minLength, maxLength);
150
- return {
151
- color,
152
- startPercent: 0,
153
- widthPercent: width,
154
- axisPercent: 0,
155
- showValue
156
- };
157
- }
158
- function displayLength(valueRatio, minLength, maxLength) {
159
- const normalized = Math.max(0, Math.min(1, valueRatio));
160
- return minLength + (maxLength - minLength) * normalized;
161
- }
162
- const MAX_SPREADSHEET_LOCAL_FORMULA_LENGTH = 255;
163
- const work_spreadsheet_local_formula_MAX_SPREADSHEET_LOCAL_FORMULA_REFERENCED_CELLS = 1024;
164
- const MAX_SHEET_ROW = 1048576;
165
- const MAX_SHEET_COLUMN = 16384;
166
- const LOCAL_FORMULA_FUNCTIONS = new Set(SUPPORTED_FORMULAS.map((name)=>name.toUpperCase()));
167
- function evaluateSpreadsheetLocalFormula(formula, options) {
168
- const source = formula.trim().replace(/^=/, '').trim();
169
- if (!source) return unsupportedLocalFormula('公式为空。');
170
- if (Array.from(source).length > MAX_SPREADSHEET_LOCAL_FORMULA_LENGTH) return unsupportedLocalFormula('公式超过 255 个字符。');
171
- const currentSheet = options.sheets.find((candidate)=>candidate.id === options.sheetId);
172
- if (!currentSheet) return unsupportedLocalFormula('找不到公式所在的工作表。');
173
- const evaluator = new SpreadsheetLocalFormulaEvaluator(options, currentSheet);
174
- return evaluator.evaluate(source);
175
- }
176
- function spreadsheetLocalFormulaBoolean(value) {
177
- if ('boolean' == typeof value) return value;
178
- if ('number' == typeof value) return Number.isFinite(value) && 0 !== value;
179
- return 'string' == typeof value && 'TRUE' === value.trim().toUpperCase();
180
- }
181
- class SpreadsheetLocalFormulaEvaluator {
182
- options;
183
- currentSheet;
184
- sheetsByName = new Map();
185
- anchor;
186
- referencedCells = 0;
187
- unsupportedMessage;
188
- celldataBySheet = new Map();
189
- constructor(options, currentSheet){
190
- this.options = options;
191
- this.currentSheet = currentSheet;
192
- this.anchor = {
193
- row: options.anchorRow ?? options.row,
194
- column: options.anchorColumn ?? options.column
195
- };
196
- for (const sheet of options.sheets)this.sheetsByName.set(sheet.name.trim().toLocaleLowerCase(), sheet);
197
- }
198
- evaluate(formula) {
199
- const parser = new Parser();
200
- parser.setFunction('ROW', (parameters)=>parameters.length ? null : this.options.row + 1).setFunction('COLUMN', (parameters)=>parameters.length ? null : this.options.column + 1);
201
- let unsupportedFunction;
202
- parser.on('callFunction', (name)=>{
203
- const normalized = name.toUpperCase();
204
- if (!LOCAL_FORMULA_FUNCTIONS.has(normalized)) unsupportedFunction ??= normalized;
205
- });
206
- parser.on('callCellValue', (coordinate, _parserOptions, done)=>{
207
- const resolved = this.resolveCoordinate(this.currentSheet, coordinate);
208
- if (!resolved) return void done(null);
209
- done(this.readCell(resolved));
210
- });
211
- parser.on('callRangeValue', (start, end, _parserOptions, done)=>{
212
- done(this.readRange(start, end));
213
- });
214
- const parsed = parser.parse(normalizeFormulaForFortuneParser(formula), {
215
- sheetId: this.currentSheet.id
216
- });
217
- if (unsupportedFunction) return unsupportedLocalFormula(`公式函数“${unsupportedFunction}”无法在浏览器本地求值。`);
218
- if (this.unsupportedMessage) return unsupportedLocalFormula(this.unsupportedMessage);
219
- if (parsed.error || void 0 === parsed.result) return unsupportedLocalFormula('公式无法求值,请检查函数、引用和括号。');
220
- return {
221
- supported: true,
222
- value: parsed.result
223
- };
224
- }
225
- resolveCoordinate(fallbackSheet, coordinate) {
226
- const sheet = coordinate.sheetName ? this.sheetsByName.get(coordinate.sheetName.trim().toLocaleLowerCase()) : fallbackSheet;
227
- if (!sheet) {
228
- this.unsupportedMessage = '公式引用了不存在的工作表。';
229
- return null;
230
- }
231
- if (coordinate.row.index < 0 || coordinate.column.index < 0) {
232
- this.unsupportedMessage = '公式暂不支持整行或整列引用,请改用有限区域。';
233
- return null;
234
- }
235
- const row = coordinate.row.isAbsolute ? coordinate.row.index : this.options.row + (coordinate.row.index - this.anchor.row);
236
- const column = coordinate.column.isAbsolute ? coordinate.column.index : this.options.column + (coordinate.column.index - this.anchor.column);
237
- if (row < 0 || column < 0 || row >= MAX_SHEET_ROW || column >= MAX_SHEET_COLUMN) {
238
- this.unsupportedMessage = '公式引用超出了工作表边界。';
239
- return null;
240
- }
241
- return {
242
- sheet,
243
- row,
244
- column
245
- };
246
- }
247
- readRange(start, end) {
248
- const startCoordinate = this.resolveCoordinate(this.currentSheet, start);
249
- const rangeSheet = start.sheetName ? this.sheetsByName.get(start.sheetName.trim().toLocaleLowerCase()) : this.currentSheet;
250
- const endCoordinate = rangeSheet ? this.resolveCoordinate(rangeSheet, end) : null;
251
- if (!startCoordinate || !endCoordinate || startCoordinate.sheet.id !== endCoordinate.sheet.id) {
252
- this.unsupportedMessage ??= '公式区域引用必须位于同一工作表。';
253
- return [];
254
- }
255
- const rowStart = Math.min(startCoordinate.row, endCoordinate.row);
256
- const rowEnd = Math.max(startCoordinate.row, endCoordinate.row);
257
- const columnStart = Math.min(startCoordinate.column, endCoordinate.column);
258
- const columnEnd = Math.max(startCoordinate.column, endCoordinate.column);
259
- const area = (rowEnd - rowStart + 1) * (columnEnd - columnStart + 1);
260
- if (area <= 0 || area > work_spreadsheet_local_formula_MAX_SPREADSHEET_LOCAL_FORMULA_REFERENCED_CELLS - this.referencedCells) {
261
- this.unsupportedMessage = '公式一次最多读取 1,024 个单元格。';
262
- return [];
263
- }
264
- const values = [];
265
- for(let row = rowStart; row <= rowEnd; row += 1){
266
- const valuesRow = [];
267
- for(let column = columnStart; column <= columnEnd; column += 1)valuesRow.push(this.readCell({
268
- sheet: startCoordinate.sheet,
269
- row,
270
- column
271
- }));
272
- values.push(valuesRow);
273
- }
274
- return values;
275
- }
276
- readCell(coordinate) {
277
- this.referencedCells += 1;
278
- if (this.referencedCells > work_spreadsheet_local_formula_MAX_SPREADSHEET_LOCAL_FORMULA_REFERENCED_CELLS) {
279
- this.unsupportedMessage = '公式一次最多读取 1,024 个单元格。';
280
- return null;
281
- }
282
- if (this.options.useProposedValue && coordinate.sheet.id === this.currentSheet.id && coordinate.row === this.options.row && coordinate.column === this.options.column) return this.options.proposedValue;
283
- const cell = this.cellAt(coordinate.sheet, coordinate.row, coordinate.column);
284
- if (!cell) return null;
285
- if ('string' == typeof cell.f && cell.f.length > 0 && void 0 === cell.v) {
286
- this.unsupportedMessage ??= '公式引用了尚未计算的公式单元格。';
287
- return null;
288
- }
289
- return cell.v ?? null;
290
- }
291
- cellAt(sheet, row, column) {
292
- if (void 0 !== sheet.data) return sheet.data[row]?.[column] ?? null;
293
- let cells = this.celldataBySheet.get(sheet.id);
294
- if (!cells) {
295
- cells = new Map();
296
- for (const entry of sheet.celldata ?? []){
297
- const value = entry.v && 'object' == typeof entry.v && 'v' in entry.v ? entry.v : {
298
- v: entry.v
299
- };
300
- cells.set(`${entry.r}_${entry.c}`, value);
301
- }
302
- this.celldataBySheet.set(sheet.id, cells);
303
- }
304
- return cells.get(`${row}_${column}`) ?? null;
305
- }
306
- }
307
- function unsupportedLocalFormula(message) {
308
- return {
309
- supported: false,
310
- value: false,
311
- message
312
- };
313
- }
314
- const MAX_SPREADSHEET_CONDITIONAL_FORMULA_SCAN_CELLS = 10000;
315
- function spreadsheetConditionalFormatStyles(sheet, formulaSheets) {
316
- const styles = new Map();
317
- const data = sheet.data ?? [];
318
- const rules = conditionalRules(sheet);
319
- if (!data.length && !rules.some(isFormulaConditionalRule)) return styles;
320
- const blocked = new Set();
321
- for (const rule of rules){
322
- const matched = new Set();
323
- if ('colorGradation' === rule.type) applyColorScale(styles, data, rule, blocked, matched);
324
- else if ('dataBar' === rule.type) applyDataBars(styles, data, rule, blocked, matched);
325
- else if ('icons' === rule.type) applyIconSet(styles, data, rule, blocked, matched);
326
- else applyDefaultRule(styles, data, rule, blocked, matched, formulaSheets, sheet);
327
- if (rule.stopIfTrue) for (const key of matched)blocked.add(key);
328
- }
329
- return styles;
330
- }
331
- function isFormulaConditionalRule(rule) {
332
- return 'default' === rule.type && 'formula' === rule.conditionName;
333
- }
334
- function applyDefaultRule(styles, data, rule, blocked, matched, formulaSheets, currentSheet) {
335
- if ('default' !== rule.type || !isConditionalFormatStyle(rule.format) || !rule.conditionName) return;
336
- const values = (rule.conditionValue ?? []).map(String);
337
- const style = rule.format;
338
- if ('formula' === rule.conditionName && values[0]) {
339
- for (const range of rule.cellrange)forEachFormulaCell(data, [
340
- range
341
- ], ({ row, column })=>{
342
- if (evaluateFormula(values[0], row, column, range, currentSheet, formulaSheets)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
343
- });
344
- return;
345
- }
346
- if ('duplicateValue' === rule.conditionName) {
347
- for (const range of rule.cellrange){
348
- const positions = cellPositions(data, [
349
- range
350
- ]).filter(({ row, column })=>null != work_spreadsheet_conditional_format_cellValue(data, row, column));
351
- const occurrences = new Map();
352
- for (const { row, column } of positions){
353
- const key = comparableKey(work_spreadsheet_conditional_format_cellValue(data, row, column));
354
- occurrences.set(key, (occurrences.get(key) ?? 0) + 1);
355
- }
356
- const unique = '1' === values[0];
357
- for (const { row, column } of positions){
358
- const count = occurrences.get(comparableKey(work_spreadsheet_conditional_format_cellValue(data, row, column))) ?? 0;
359
- if (unique && 1 === count || !unique && count > 1) applyDifferentialStyle(styles, blocked, matched, row, column, style);
360
- }
361
- }
362
- return;
363
- }
364
- if ([
365
- 'top10',
366
- 'top10_percent',
367
- 'last10',
368
- 'last10_percent'
369
- ].includes(rule.conditionName)) {
370
- for (const range of rule.cellrange)applyRankRule(styles, data, range, rule.conditionName, values[0], style, blocked, matched);
371
- return;
372
- }
373
- if ('aboveAverage' === rule.conditionName || 'belowAverage' === rule.conditionName) {
374
- for (const range of rule.cellrange)applyAverageRule(styles, data, range, rule.conditionName, style, blocked, matched);
375
- return;
376
- }
377
- forEachCell(data, rule.cellrange, ({ row, column })=>{
378
- const current = work_spreadsheet_conditional_format_cellValue(data, row, column);
379
- if (matchesCondition(current, rule.conditionName, values)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
380
- });
381
- }
382
- function applyRankRule(styles, data, range, conditionName, sourceRank, style, blocked, matched) {
383
- const positions = cellPositions(data, [
384
- range
385
- ]);
386
- const numbers = positions.flatMap(({ row, column })=>{
387
- const value = numericCellValue(data, row, column);
388
- return null === value ? [] : [
389
- value
390
- ];
391
- });
392
- if (!numbers.length) return;
393
- numbers.sort((left, right)=>right - left);
394
- const requested = Math.max(1, Math.trunc(Number(sourceRank) || 10));
395
- const count = conditionName.endsWith('_percent') ? Math.max(1, Math.ceil(Math.min(100, requested) / 100 * numbers.length)) : Math.min(numbers.length, requested);
396
- const bottom = conditionName.startsWith('last');
397
- const threshold = bottom ? numbers[numbers.length - count] : numbers[count - 1];
398
- for (const { row, column } of positions){
399
- const value = numericCellValue(data, row, column);
400
- if (null !== value && (bottom ? value <= threshold : value >= threshold)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
401
- }
402
- }
403
- function applyAverageRule(styles, data, range, conditionName, style, blocked, matched) {
404
- const positions = cellPositions(data, [
405
- range
406
- ]);
407
- const values = positions.flatMap(({ row, column })=>{
408
- const value = numericCellValue(data, row, column);
409
- return null === value ? [] : [
410
- value
411
- ];
412
- });
413
- if (!values.length) return;
414
- const average = values.reduce((sum, value)=>sum + value, 0) / values.length;
415
- for (const { row, column } of positions){
416
- const value = numericCellValue(data, row, column);
417
- if (null !== value && ('aboveAverage' === conditionName ? value > average : value < average)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
418
- }
419
- }
420
- function applyColorScale(styles, data, rule, blocked, matched) {
421
- if (!Array.isArray(rule.format) || rule.format.length < 2) return;
422
- const positions = cellPositions(data, rule.cellrange);
423
- const numericPositions = positions.flatMap((position)=>{
424
- const value = numericCellValue(data, position.row, position.column);
425
- return null === value ? [] : [
426
- {
427
- ...position,
428
- value
429
- }
430
- ];
431
- });
432
- if (!numericPositions.length) return;
433
- const colors = rule.format.slice(0, 3).map(parseSpreadsheetConditionalColor);
434
- if (colors.some((color)=>!color)) return;
435
- const values = numericPositions.map(({ value })=>value);
436
- const options = rule.visualOptions ? normalizeSpreadsheetConditionalVisualOptions(rule.visualOptions, colors.length, 'colorScale') : {
437
- thresholds: defaultSpreadsheetColorScaleThresholds(colors.length)
438
- };
439
- if (!options) return;
440
- const points = options.thresholds.map((threshold)=>spreadsheetConditionalThresholdValue(threshold, values));
441
- if (points.some((point)=>null === point)) return;
442
- const minimum = points[0];
443
- const midpoint = 3 === points.length ? points[1] : null;
444
- const maximum = points.at(-1);
445
- const maximumColor = colors[0];
446
- const midpointColor = 3 === colors.length ? colors[1] : null;
447
- const minimumColor = colors.at(-1);
448
- for (const { row, column, value } of numericPositions){
449
- let color;
450
- color = maximum === minimum ? midpointColor ?? minimumColor : midpointColor && null !== midpoint && value <= midpoint ? interpolateSpreadsheetConditionalColor(minimumColor, midpointColor, spreadsheetConditionalRatio(value, minimum, midpoint)) : midpointColor && null !== midpoint ? interpolateSpreadsheetConditionalColor(midpointColor, maximumColor, spreadsheetConditionalRatio(value, midpoint, maximum)) : interpolateSpreadsheetConditionalColor(minimumColor, maximumColor, spreadsheetConditionalRatio(value, minimum, maximum));
451
- applyConditionalStyle(styles, blocked, matched, row, column, {
452
- cellColor: spreadsheetConditionalCssColor(color)
453
- });
454
- }
455
- }
456
- function applyDataBars(styles, data, rule, blocked, matched) {
457
- if (!isConditionalFormatStyle(rule.format)) return;
458
- const color = rule.format.cellColor;
459
- if (!color) return;
460
- const positions = cellPositions(data, rule.cellrange);
461
- const numericPositions = positions.flatMap((position)=>{
462
- const value = numericCellValue(data, position.row, position.column);
463
- return null === value ? [] : [
464
- {
465
- ...position,
466
- value
467
- }
468
- ];
469
- });
470
- if (!numericPositions.length) return;
471
- const values = numericPositions.map(({ value })=>value);
472
- const options = rule.visualOptions ? normalizeSpreadsheetConditionalVisualOptions(rule.visualOptions, 2, 'dataBar') : defaultSpreadsheetDataBarOptions();
473
- if (!options) return;
474
- const points = options.thresholds.map((threshold)=>spreadsheetConditionalThresholdValue(threshold, values));
475
- if (points.some((point)=>null === point)) return;
476
- const minimum = points[0];
477
- const maximum = points[1];
478
- for (const { row, column, value } of numericPositions)applyConditionalStyle(styles, blocked, matched, row, column, {
479
- dataBar: spreadsheetConditionalDataBar(color, value, minimum, maximum, options.minLength ?? 10, options.maxLength ?? 90, false !== options.showValue)
480
- });
481
- }
482
- function applyIconSet(styles, data, rule, blocked, matched) {
483
- if ('icons' !== rule.type) return;
484
- const format = normalizeSpreadsheetConditionalIconSetFormat(rule.format);
485
- if (!format) return;
486
- const numericPositions = cellPositions(data, rule.cellrange).flatMap((position)=>{
487
- const value = numericCellValue(data, position.row, position.column);
488
- return null === value ? [] : [
489
- {
490
- ...position,
491
- value
492
- }
493
- ];
494
- });
495
- const values = numericPositions.map(({ value })=>value);
496
- for (const { row, column, value } of numericPositions){
497
- const icon = spreadsheetConditionalIconForValue(format, value, values);
498
- if (icon) applyConditionalStyle(styles, blocked, matched, row, column, {
499
- icon
500
- });
501
- }
502
- }
503
- function matchesCondition(value, name, conditions) {
504
- if (null == value) return false;
505
- if ('textContains' === name) return String(value).toLowerCase().includes((conditions[0] ?? '').toLowerCase());
506
- if ('equal' === name) return 0 === compareValues(value, conditions[0]);
507
- if ('notEqual' === name) return 0 !== compareValues(value, conditions[0]);
508
- if ('greaterThan' === name) return compareValues(value, conditions[0]) > 0;
509
- if ('greaterThanOrEqual' === name) return compareValues(value, conditions[0]) >= 0;
510
- if ('lessThan' === name) return compareValues(value, conditions[0]) < 0;
511
- if ('lessThanOrEqual' === name) return compareValues(value, conditions[0]) <= 0;
512
- if ('between' === name || 'notBetween' === name) {
513
- const lower = Math.min(Number(conditions[0]), Number(conditions[1]));
514
- const upper = Math.max(Number(conditions[0]), Number(conditions[1]));
515
- if (!Number.isFinite(lower) || !Number.isFinite(upper)) return false;
516
- const between = Number(value) >= lower && Number(value) <= upper;
517
- return 'between' === name ? between : !between;
518
- }
519
- return false;
520
- }
521
- function evaluateFormula(source, row, column, baseRange, currentSheet, formulaSheets) {
522
- const sheets = [
523
- toFormulaSheet(currentSheet),
524
- ...(formulaSheets ?? []).filter((sheet)=>sheet.id && sheet.id !== currentSheet.id).map(toFormulaSheet)
525
- ];
526
- const result = evaluateSpreadsheetLocalFormula(source, {
527
- sheets,
528
- sheetId: currentSheet.id ?? '',
529
- row,
530
- column,
531
- anchorRow: baseRange.row[0],
532
- anchorColumn: baseRange.column[0]
533
- });
534
- return result.supported && spreadsheetLocalFormulaBoolean(result.value);
535
- }
536
- function applyDifferentialStyle(styles, blocked, matched, row, column, style) {
537
- const patch = {};
538
- if (style.textColor) patch.textColor = style.textColor;
539
- if (style.cellColor) patch.cellColor = style.cellColor;
540
- applyConditionalStyle(styles, blocked, matched, row, column, patch);
541
- }
542
- function applyConditionalStyle(styles, blocked, matched, row, column, patch) {
543
- const key = cellKey(row, column);
544
- if (blocked.has(key)) return;
545
- matched.add(key);
546
- if (Object.keys(patch).length) patchCellStyle(styles, row, column, patch);
547
- }
548
- function patchCellStyle(styles, row, column, patch) {
549
- const key = cellKey(row, column);
550
- styles.set(key, {
551
- ...patch,
552
- ...styles.get(key)
553
- });
554
- }
555
- function conditionalRules(sheet) {
556
- if (!Array.isArray(sheet.luckysheet_conditionformat_save)) return [];
557
- return sheet.luckysheet_conditionformat_save.filter(isConditionalRule);
558
- }
559
- function isConditionalRule(value) {
560
- if (!value || 'object' != typeof value) return false;
561
- const rule = value;
562
- return [
563
- 'default',
564
- 'colorGradation',
565
- 'dataBar',
566
- 'icons'
567
- ].includes(String(rule.type)) && Array.isArray(rule.cellrange);
568
- }
569
- function isConditionalFormatStyle(value) {
570
- return Boolean(value) && !Array.isArray(value) && 'object' == typeof value && !('iconSet' in value) && 'textColor' in value && 'cellColor' in value;
571
- }
572
- function cellPositions(data, ranges) {
573
- const positions = [];
574
- const seen = new Set();
575
- forEachCell(data, ranges, (position)=>{
576
- const key = cellKey(position.row, position.column);
577
- if (!seen.has(key)) {
578
- seen.add(key);
579
- positions.push(position);
580
- }
581
- });
582
- return positions;
583
- }
584
- function cellKey(row, column) {
585
- return `${row}_${column}`;
586
- }
587
- function forEachCell(data, ranges, visit) {
588
- for (const [row, cells] of sparseArrayEntries(data))for (const [column] of sparseArrayEntries(cells))if (ranges.some((range)=>row >= range.row[0] && row <= range.row[1] && column >= range.column[0] && column <= range.column[1])) visit({
589
- row,
590
- column
591
- });
592
- }
593
- function forEachFormulaCell(data, ranges, visit) {
594
- for (const range of ranges){
595
- const area = (range.row[1] - range.row[0] + 1) * (range.column[1] - range.column[0] + 1);
596
- if (area > 0 && area <= MAX_SPREADSHEET_CONDITIONAL_FORMULA_SCAN_CELLS) {
597
- for(let row = range.row[0]; row <= range.row[1]; row += 1)for(let column = range.column[0]; column <= range.column[1]; column += 1)visit({
598
- row,
599
- column
600
- });
601
- continue;
602
- }
603
- forEachCell(data, [
604
- range
605
- ], visit);
606
- }
607
- }
608
- function work_spreadsheet_conditional_format_cellValue(data, row, column) {
609
- return data[row]?.[column]?.v ?? null;
610
- }
611
- function numericCellValue(data, row, column) {
612
- const value = work_spreadsheet_conditional_format_cellValue(data, row, column);
613
- return 'number' == typeof value && Number.isFinite(value) ? value : null;
614
- }
615
- function comparableKey(value) {
616
- return `${typeof value}:${String(value)}`;
617
- }
618
- function compareValues(left, right) {
619
- const leftNumber = Number(left);
620
- const rightNumber = Number(right);
621
- if (String(left).trim() && String(right).trim() && Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) return leftNumber === rightNumber ? 0 : leftNumber > rightNumber ? 1 : -1;
622
- return String(left).localeCompare(String(right));
623
- }
624
- function toFormulaSheet(sheet) {
625
- return {
626
- id: sheet.id ?? '',
627
- name: sheet.name,
628
- data: sheet.data,
629
- celldata: sheet.celldata
630
- };
631
- }
632
92
  function drawSpreadsheetDiagonalDownBorder(context, bounds, line) {
633
93
  drawSpreadsheetDiagonalBorder(context, bounds, line, 'down');
634
94
  }
@@ -939,102 +399,6 @@ function restoreSpreadsheetNativeFillContext(context) {
939
399
  else delete context.fillRect;
940
400
  pendingContextRestores.delete(context);
941
401
  }
942
- function spreadsheet_cell_range_normalizeSpreadsheetCellRange(value) {
943
- const row = normalizeSpreadsheetCellRangeAxis(value.row);
944
- const column = normalizeSpreadsheetCellRangeAxis(value.column);
945
- return row && column ? {
946
- row,
947
- column
948
- } : null;
949
- }
950
- function parseSpreadsheetCellRange(value) {
951
- if (!isRecord(value)) return null;
952
- const row = normalizeSpreadsheetCellRangeAxis(value.row);
953
- const column = normalizeSpreadsheetCellRangeAxis(value.column);
954
- return row && column ? {
955
- row,
956
- column
957
- } : null;
958
- }
959
- function subtractSpreadsheetCellRange(source, removed) {
960
- const top = Math.max(source.row[0], removed.row[0]);
961
- const bottom = Math.min(source.row[1], removed.row[1]);
962
- const left = Math.max(source.column[0], removed.column[0]);
963
- const right = Math.min(source.column[1], removed.column[1]);
964
- if (top > bottom || left > right) return [
965
- source
966
- ];
967
- const remaining = [];
968
- if (source.row[0] < top) remaining.push({
969
- row: [
970
- source.row[0],
971
- top - 1
972
- ],
973
- column: [
974
- ...source.column
975
- ]
976
- });
977
- if (bottom < source.row[1]) remaining.push({
978
- row: [
979
- bottom + 1,
980
- source.row[1]
981
- ],
982
- column: [
983
- ...source.column
984
- ]
985
- });
986
- if (source.column[0] < left) remaining.push({
987
- row: [
988
- top,
989
- bottom
990
- ],
991
- column: [
992
- source.column[0],
993
- left - 1
994
- ]
995
- });
996
- if (right < source.column[1]) remaining.push({
997
- row: [
998
- top,
999
- bottom
1000
- ],
1001
- column: [
1002
- right + 1,
1003
- source.column[1]
1004
- ]
1005
- });
1006
- return remaining;
1007
- }
1008
- function spreadsheetCellRangesIntersect(left, right) {
1009
- return !(left.row[1] < right.row[0] || right.row[1] < left.row[0] || left.column[1] < right.column[0] || right.column[1] < left.column[0]);
1010
- }
1011
- function spreadsheetCellRangesEqual(left, right) {
1012
- const normalizedLeft = spreadsheet_cell_range_normalizeSpreadsheetCellRange(left);
1013
- const normalizedRight = spreadsheet_cell_range_normalizeSpreadsheetCellRange(right);
1014
- return Boolean(normalizedLeft && normalizedRight && normalizedLeft.row[0] === normalizedRight.row[0] && normalizedLeft.row[1] === normalizedRight.row[1] && normalizedLeft.column[0] === normalizedRight.column[0] && normalizedLeft.column[1] === normalizedRight.column[1]);
1015
- }
1016
- function spreadsheetCellRangeContains(range, row, column) {
1017
- return row >= range.row[0] && row <= range.row[1] && column >= range.column[0] && column <= range.column[1];
1018
- }
1019
- function spreadsheet_cell_range_spreadsheetCellRangeArea(range) {
1020
- return (range.row[1] - range.row[0] + 1) * (range.column[1] - range.column[0] + 1);
1021
- }
1022
- function normalizeSpreadsheetCellRangeAxis(value) {
1023
- if (!Array.isArray(value)) return null;
1024
- const first = finiteSpreadsheetCellIndex(value[0]);
1025
- const second = finiteSpreadsheetCellIndex(value[1]);
1026
- if (null === first || null === second) return null;
1027
- return [
1028
- Math.min(first, second),
1029
- Math.max(first, second)
1030
- ];
1031
- }
1032
- function finiteSpreadsheetCellIndex(value) {
1033
- return 'number' == typeof value && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
1034
- }
1035
- function isRecord(value) {
1036
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
1037
- }
1038
402
  const spreadsheetCellBorderTargets = [
1039
403
  'top',
1040
404
  'bottom',
@@ -1132,9 +496,9 @@ function spreadsheetCellBordersAt(sheet, row, column) {
1132
496
  const borders = {};
1133
497
  const source = Array.isArray(sheet?.config?.borderInfo) ? sheet.config.borderInfo : [];
1134
498
  for (const candidate of source){
1135
- if (!spreadsheet_cell_border_isRecord(candidate)) continue;
499
+ if (!isRecord(candidate)) continue;
1136
500
  if ('cell' === candidate.rangeType) {
1137
- if (!spreadsheet_cell_border_isRecord(candidate.value)) continue;
501
+ if (!isRecord(candidate.value)) continue;
1138
502
  if (finiteSpreadsheetBorderIndex(candidate.value.row_index) !== row || finiteSpreadsheetBorderIndex(candidate.value.col_index) !== column) continue;
1139
503
  applyResolvedCellBorderLine(borders, 'left', candidate.value.l);
1140
504
  applyResolvedCellBorderLine(borders, 'right', candidate.value.r);
@@ -1156,9 +520,9 @@ function spreadsheetRenderableDiagonalBorders(sheet) {
1156
520
  const borders = new Map();
1157
521
  const source = Array.isArray(sheet.config?.borderInfo) ? sheet.config.borderInfo : [];
1158
522
  for (const candidate of source){
1159
- if (!spreadsheet_cell_border_isRecord(candidate)) continue;
523
+ if (!isRecord(candidate)) continue;
1160
524
  if ('cell' === candidate.rangeType) {
1161
- if (!spreadsheet_cell_border_isRecord(candidate.value)) continue;
525
+ if (!isRecord(candidate.value)) continue;
1162
526
  const row = finiteSpreadsheetBorderIndex(candidate.value.row_index);
1163
527
  const column = finiteSpreadsheetBorderIndex(candidate.value.col_index);
1164
528
  if (null === row || null === column) continue;
@@ -1237,11 +601,11 @@ function spreadsheetBorderInfoIsWritable(sheet) {
1237
601
  function compactSpreadsheetBorderInfo(formats, range, appliedType) {
1238
602
  const supersedesAll = 'border-all' === appliedType || 'border-none' === appliedType;
1239
603
  return formats.flatMap((format)=>{
1240
- if (!spreadsheet_cell_border_isRecord(format)) return [
604
+ if (!isRecord(format)) return [
1241
605
  format
1242
606
  ];
1243
607
  if ('cell' === format.rangeType) {
1244
- if (!spreadsheet_cell_border_isRecord(format.value)) return [
608
+ if (!isRecord(format.value)) return [
1245
609
  format
1246
610
  ];
1247
611
  const row = finiteSpreadsheetBorderIndex(format.value.row_index);
@@ -1328,9 +692,9 @@ function spreadsheetDiagonalBorderDirectionsInRange(sheet, selection) {
1328
692
  const directions = new Map();
1329
693
  const source = Array.isArray(sheet.config?.borderInfo) ? sheet.config.borderInfo : [];
1330
694
  for (const candidate of source){
1331
- if (!spreadsheet_cell_border_isRecord(candidate)) continue;
695
+ if (!isRecord(candidate)) continue;
1332
696
  if ('cell' === candidate.rangeType) {
1333
- if (!spreadsheet_cell_border_isRecord(candidate.value)) continue;
697
+ if (!isRecord(candidate.value)) continue;
1334
698
  const row = finiteSpreadsheetBorderIndex(candidate.value.row_index);
1335
699
  const column = finiteSpreadsheetBorderIndex(candidate.value.col_index);
1336
700
  if (null === row || null === column || !spreadsheetCellRangeContains(selection, row, column)) continue;
@@ -1388,7 +752,7 @@ function contentWithSpreadsheetBorderInfo(content, sheetIndex, sheet, borderInfo
1388
752
  }
1389
753
  function applyResolvedCellBorderLine(borders, side, value) {
1390
754
  if (null === value) return void delete borders[side];
1391
- if (!spreadsheet_cell_border_isRecord(value)) return;
755
+ if (!isRecord(value)) return;
1392
756
  const color = 'string' == typeof value.color ? normalizeSpreadsheetBorderColor(value.color) : null;
1393
757
  const style = 'string' == typeof value.style ? value.style : null;
1394
758
  if (color && style) borders[side] = {
@@ -1408,7 +772,7 @@ function applyResolvedCellDiagonalBorders(borders, value) {
1408
772
  if (diagonal.up) borders.diagonalUp = line;
1409
773
  }
1410
774
  function resolvedCellBorderLine(value) {
1411
- if (!spreadsheet_cell_border_isRecord(value)) return null;
775
+ if (!isRecord(value)) return null;
1412
776
  const color = 'string' == typeof value.color ? normalizeSpreadsheetBorderColor(value.color) : null;
1413
777
  const style = 'string' == typeof value.style ? value.style : null;
1414
778
  return color && style ? {
@@ -1497,7 +861,7 @@ function isSpreadsheetNativeBorderType(value) {
1497
861
  function finiteSpreadsheetBorderIndex(value) {
1498
862
  return 'number' == typeof value && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
1499
863
  }
1500
- function spreadsheet_cell_border_isRecord(value) {
864
+ function isRecord(value) {
1501
865
  return Boolean(value && 'object' == typeof value && !Array.isArray(value));
1502
866
  }
1503
867
  function useSpreadsheetCollaborationPresenceProjection({ content, workbook }) {
@@ -3167,2823 +2531,97 @@ function spreadsheetAutoSumNativeFunction(functionName) {
3167
2531
  case 'average':
3168
2532
  return 'AVERAGE';
3169
2533
  case 'count':
3170
- return 'COUNT';
3171
- case 'max':
3172
- return 'MAX';
3173
- case 'min':
3174
- return 'MIN';
3175
- default:
3176
- return null;
3177
- }
3178
- }
3179
- function canMutateSpreadsheetCellRange(sheet, range) {
3180
- return canMutateSpreadsheetCellRanges(sheet, [
3181
- range
3182
- ]);
3183
- }
3184
- function canMutateSpreadsheetCellRanges(sheet, ranges) {
3185
- if (!sheet || 0 === ranges.length || ranges.some((range)=>!spreadsheetCellMutationRangeIsValid(range))) return false;
3186
- if (sheet.isPivotTable || sheet.pivotTable || sheet.pivotTables?.length) return false;
3187
- const merges = Object.values(sheet.config?.merge ?? {});
3188
- if (merges.some((merge)=>{
3189
- const mergeRange = spreadsheetNativeMergeRange(merge);
3190
- return mergeRange && ranges.some((range)=>spreadsheetCellRangesIntersect(mergeRange, range));
3191
- })) return false;
3192
- const cellAt = createSpreadsheetMutationCellReader(sheet);
3193
- for (const range of ranges){
3194
- for(let row = range.row[0]; row <= range.row[1]; row += 1)if (sheet.config?.rowReadOnly?.[row]) return false;
3195
- for(let column = range.column[0]; column <= range.column[1]; column += 1)if (sheet.config?.colReadOnly?.[column]) return false;
3196
- for(let row = range.row[0]; row <= range.row[1]; row += 1)for(let column = range.column[0]; column <= range.column[1]; column += 1)if (spreadsheetCellIsLocked(sheet, cellAt, row, column)) return false;
3197
- }
3198
- return true;
3199
- }
3200
- function spreadsheetCellMutationRangeIsValid(range) {
3201
- return Number.isSafeInteger(range.row[0]) && Number.isSafeInteger(range.row[1]) && Number.isSafeInteger(range.column[0]) && Number.isSafeInteger(range.column[1]) && range.row[0] >= 0 && range.row[0] <= range.row[1] && range.column[0] >= 0 && range.column[0] <= range.column[1];
3202
- }
3203
- function spreadsheetNativeMergeRange(value) {
3204
- if (!spreadsheet_cell_mutation_guard_isRecord(value)) return null;
3205
- const row = finiteIndex(value.r);
3206
- const column = finiteIndex(value.c);
3207
- const rowSpan = positiveSpan(value.rs);
3208
- const columnSpan = positiveSpan(value.cs);
3209
- if (null === row || null === column || null === rowSpan || null === columnSpan || !Number.isSafeInteger(row + rowSpan - 1) || !Number.isSafeInteger(column + columnSpan - 1)) return null;
3210
- return {
3211
- row: [
3212
- row,
3213
- row + rowSpan - 1
3214
- ],
3215
- column: [
3216
- column,
3217
- column + columnSpan - 1
3218
- ]
3219
- };
3220
- }
3221
- function spreadsheetCellIsLocked(sheet, cellAt, row, column) {
3222
- const cell = cellAt(row, column);
3223
- if (cell?.lo !== void 0 && null !== cell.lo) return Boolean(cell.lo);
3224
- const authority = sheet.config?.authority;
3225
- if (!spreadsheet_cell_mutation_guard_isRecord(authority)) return false;
3226
- const ranges = authority.cellProtectionRanges;
3227
- if (Array.isArray(ranges)) for(let index = ranges.length - 1; index >= 0; index -= 1){
3228
- const candidate = ranges[index];
3229
- if (!spreadsheet_cell_mutation_guard_isRecord(candidate)) continue;
3230
- const protectedRange = parseSpreadsheetCellRange(candidate.range);
3231
- if (protectedRange && row >= protectedRange.row[0] && row <= protectedRange.row[1] && column >= protectedRange.column[0] && column <= protectedRange.column[1]) return false !== candidate.locked;
3232
- }
3233
- return void 0 !== authority.sheet && 0 !== authority.sheet;
3234
- }
3235
- function createSpreadsheetMutationCellReader(sheet) {
3236
- let sparseRows = null;
3237
- const sparseCell = (row, column)=>{
3238
- if (!sparseRows) {
3239
- sparseRows = new Map();
3240
- for (const entry of sheet.celldata ?? []){
3241
- let sparseRow = sparseRows.get(entry.r);
3242
- if (!sparseRow) {
3243
- sparseRow = new Map();
3244
- sparseRows.set(entry.r, sparseRow);
3245
- }
3246
- sparseRow.set(entry.c, entry.v);
3247
- }
3248
- }
3249
- return sparseRows.get(row)?.get(column);
3250
- };
3251
- return (row, column)=>sheet.data?.[row]?.[column] ?? sparseCell(row, column);
3252
- }
3253
- function finiteIndex(value) {
3254
- return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : null;
3255
- }
3256
- function positiveSpan(value) {
3257
- return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : null;
3258
- }
3259
- function spreadsheet_cell_mutation_guard_isRecord(value) {
3260
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
3261
- }
3262
- function reconcileSpreadsheetRichTextCellEdit(previous, current, formattedPaste) {
3263
- if (current.f) return current;
3264
- const source = formattedPaste ? normalizeXlsxRichTextEditSource(previous) : previous ? normalizeXlsxRichTextCell(previous) : null;
3265
- if (!source) return current;
3266
- const live = liveRichTextValue(current);
3267
- if (!live || '' === live.text) return current;
3268
- const replacement = richTextReplacement(source.text, live.text);
3269
- const sourceRuns = indexRichTextRuns(source);
3270
- const formattedPasteRuns = formattedPaste ? reconstructFormattedPasteRuns(source, sourceRuns, live.text, formattedPaste) : null;
3271
- const runs = formattedPasteRuns ?? (live.rich && live.text === source.text ? restoreSemanticColorOrigins(live.rich.runs, sourceRuns, replacement) : reconstructPlainTextRuns(sourceRuns, replacement));
3272
- const normalizedRuns = coalesceXlsxRichTextRuns(runs);
3273
- if (!normalizedRuns.length || normalizedRuns.length > 512 || normalizedRuns.map((run)=>run.v).join('') !== live.text) return current;
3274
- return spreadsheetRichTextCellWithRuns(previous, current, live.text, normalizedRuns);
3275
- }
3276
- function restoreSpreadsheetRichTextCellRuns(previous, current) {
3277
- if (!previous || current.f) return current;
3278
- const source = normalizeXlsxRichTextCell(previous);
3279
- const live = liveRichTextValue(current);
3280
- if (!source || !live || source.text !== live.text) return current;
3281
- return spreadsheetRichTextCellWithRuns(previous, current, source.text, source.runs);
3282
- }
3283
- function spreadsheetRichTextCellWithRuns(previous, current, text, runs) {
3284
- const next = {
3285
- ...current,
3286
- ct: {
3287
- ...previous?.ct,
3288
- ...current.ct,
3289
- s: runs.map((run)=>({
3290
- ...run
3291
- })),
3292
- t: 'inlineStr'
3293
- },
3294
- v: text
3295
- };
3296
- delete next.f;
3297
- delete next.m;
3298
- return next;
3299
- }
3300
- function sameSpreadsheetRichTextCellText(previous, current) {
3301
- if (!previous || current.f) return false;
3302
- const source = normalizeXlsxRichTextCell(previous);
3303
- const live = liveRichTextValue(current);
3304
- return Boolean(source && live && source.text === live.text);
3305
- }
3306
- function liveRichTextValue(cell) {
3307
- const rich = normalizeXlsxRichTextCell(cell);
3308
- if ('string' == typeof cell.v && cell.v !== rich?.text) return validBoundedText(cell.v) ? {
3309
- rich: null,
3310
- text: cell.v
3311
- } : null;
3312
- if (rich) return {
3313
- rich,
3314
- text: rich.text
3315
- };
3316
- return 'string' == typeof cell.v && validBoundedText(cell.v) ? {
3317
- rich: null,
3318
- text: cell.v
3319
- } : null;
3320
- }
3321
- function validBoundedText(value) {
3322
- return value.length <= 32767 && validXlsxRichText(value);
3323
- }
3324
- function richTextReplacement(previous, current) {
3325
- let left = 0;
3326
- let right = 0;
3327
- while(left < previous.length && left < current.length && previous[left] === current[left])left += 1;
3328
- if (left > 0 && isHighSurrogate(previous.charCodeAt(left - 1))) left -= 1;
3329
- while(right + left < previous.length && right + left < current.length && previous[previous.length - right - 1] === current[current.length - right - 1])right += 1;
3330
- if (right > 0 && isLowSurrogate(previous.charCodeAt(previous.length - right))) right -= 1;
3331
- return {
3332
- index: left,
3333
- insert: current.slice(left, current.length - right),
3334
- remove: previous.length - left - right
3335
- };
3336
- }
3337
- function indexRichTextRuns(source) {
3338
- let offset = 0;
3339
- return source.runs.map((run)=>{
3340
- const indexed = {
3341
- end: offset + run.v.length,
3342
- run,
3343
- start: offset
3344
- };
3345
- offset = indexed.end;
3346
- return indexed;
3347
- });
3348
- }
3349
- function reconstructPlainTextRuns(source, replacement) {
3350
- const runs = [];
3351
- appendSourceRange(runs, source, 0, replacement.index);
3352
- if (replacement.insert) {
3353
- const inherited = insertionSourceRun(source, replacement);
3354
- if (inherited) runs.push({
3355
- ...inherited.run,
3356
- v: replacement.insert
3357
- });
3358
- }
3359
- const sourceLength = source.at(-1)?.end ?? 0;
3360
- appendSourceRange(runs, source, replacement.index + replacement.remove, sourceLength);
3361
- return runs;
3362
- }
3363
- function reconstructFormattedPasteRuns(source, sourceRuns, currentText, intent) {
3364
- if (!validRichTextPasteSelection(source.text, intent.start, intent.end) || 'string' != typeof intent.text || !intent.text || intent.text.length > 32767 || !validXlsxRichText(intent.text) || !Array.isArray(intent.runs) || !intent.runs.length || intent.runs.length > 512) return null;
3365
- const pastedRuns = [];
3366
- for (const candidate of intent.runs){
3367
- const run = normalizeXlsxRichTextRun(candidate);
3368
- if (!run) return null;
3369
- if (run.v) pastedRuns.push(run);
3370
- }
3371
- if (!pastedRuns.length || pastedRuns.map((run)=>run.v).join('') !== intent.text) return null;
3372
- const expectedText = source.text.slice(0, intent.start) + intent.text + source.text.slice(intent.end);
3373
- if (expectedText !== currentText || expectedText.length > 32767 || !validXlsxRichText(expectedText)) return null;
3374
- const runs = [];
3375
- appendSourceRange(runs, sourceRuns, 0, intent.start);
3376
- runs.push(...pastedRuns);
3377
- appendSourceRange(runs, sourceRuns, intent.end, source.text.length);
3378
- const normalized = coalesceXlsxRichTextRuns(runs);
3379
- return normalized.length > 0 && normalized.length <= 512 ? normalized : null;
3380
- }
3381
- function validRichTextPasteSelection(text, start, end) {
3382
- return Boolean(Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end && end <= text.length && !splitsSurrogatePair(text, start) && !splitsSurrogatePair(text, end));
3383
- }
3384
- function splitsSurrogatePair(text, offset) {
3385
- if (offset <= 0 || offset >= text.length) return false;
3386
- return isHighSurrogate(text.charCodeAt(offset - 1)) && isLowSurrogate(text.charCodeAt(offset));
3387
- }
3388
- function appendSourceRange(target, source, start, end) {
3389
- if (start >= end) return;
3390
- for (const indexed of source){
3391
- const overlapStart = Math.max(start, indexed.start);
3392
- const overlapEnd = Math.min(end, indexed.end);
3393
- if (!(overlapStart >= overlapEnd)) target.push({
3394
- ...indexed.run,
3395
- v: indexed.run.v.slice(overlapStart - indexed.start, overlapEnd - indexed.start)
3396
- });
3397
- }
3398
- }
3399
- function restoreSemanticColorOrigins(current, source, replacement) {
3400
- const runs = [];
3401
- let absoluteOffset = 0;
3402
- for (const currentRun of current){
3403
- let localOffset = 0;
3404
- while(localOffset < currentRun.v.length){
3405
- const width = codePointWidth(currentRun.v, localOffset);
3406
- const origin = semanticOriginAt(source, replacement, absoluteOffset + localOffset);
3407
- const next = {
3408
- ...currentRun,
3409
- v: currentRun.v.slice(localOffset, localOffset + width)
3410
- };
3411
- delete next.a3sXlsxColorOrigin;
3412
- if (origin && currentRun.fc && xlsxSemanticColorMatchesValue(origin, currentRun.fc)) next.a3sXlsxColorOrigin = origin;
3413
- runs.push(next);
3414
- localOffset += width;
3415
- }
3416
- absoluteOffset += currentRun.v.length;
3417
- }
3418
- return runs;
3419
- }
3420
- function semanticOriginAt(source, replacement, currentOffset) {
3421
- let indexed;
3422
- indexed = currentOffset < replacement.index ? sourceRunAt(source, currentOffset) : currentOffset >= replacement.index + replacement.insert.length ? sourceRunAt(source, currentOffset - replacement.insert.length + replacement.remove) : insertionSourceRun(source, replacement);
3423
- return normalizeXlsxSemanticColorOrigin(indexed?.run.a3sXlsxColorOrigin);
3424
- }
3425
- function insertionSourceRun(source, replacement) {
3426
- const sourceLength = source.at(-1)?.end ?? 0;
3427
- if (!sourceLength) return;
3428
- if (replacement.remove > 0 && replacement.index < sourceLength) return sourceRunAt(source, replacement.index);
3429
- if (replacement.index > 0) return sourceRunAt(source, replacement.index - 1);
3430
- return sourceRunAt(source, 0);
3431
- }
3432
- function sourceRunAt(source, offset) {
3433
- let left = 0;
3434
- let right = source.length - 1;
3435
- while(left <= right){
3436
- const middle = left + Math.floor((right - left) / 2);
3437
- const indexed = source[middle];
3438
- if (!indexed) break;
3439
- if (offset < indexed.start) right = middle - 1;
3440
- else {
3441
- if (!(offset >= indexed.end)) return indexed;
3442
- left = middle + 1;
3443
- }
3444
- }
3445
- }
3446
- function codePointWidth(value, offset) {
3447
- return isHighSurrogate(value.charCodeAt(offset)) && isLowSurrogate(value.charCodeAt(offset + 1)) ? 2 : 1;
3448
- }
3449
- const spreadsheetFontSizes = [
3450
- 9,
3451
- 10,
3452
- 11,
3453
- 12,
3454
- 14,
3455
- 16,
3456
- 18,
3457
- 20,
3458
- 22,
3459
- 24,
3460
- 28,
3461
- 36,
3462
- 48,
3463
- 72
3464
- ];
3465
- function nextSpreadsheetFontSize(current, direction) {
3466
- if (!Number.isFinite(current) || current < 1 || current > 409) return null;
3467
- return 'grow' === direction ? spreadsheetFontSizes.find((size)=>size > current) ?? null : [
3468
- ...spreadsheetFontSizes
3469
- ].reverse().find((size)=>size < current) ?? null;
3470
- }
3471
- function captureSpreadsheetRichTextDomSelection({ expandedOnly = false, root, target }) {
3472
- const selection = window.getSelection();
3473
- if (!root || !selection || 1 !== selection.rangeCount) return null;
3474
- const range = selection.getRangeAt(0);
3475
- if (expandedOnly && range.collapsed) return null;
3476
- const targetNode = target instanceof Node ? target : null;
3477
- const editor = spreadsheetRichTextEditor(targetNode ?? range.commonAncestorContainer);
3478
- if (!editor || !root.contains(editor) || !editorContainsRange(editor, range)) return null;
3479
- const start = spreadsheetTextOffsetAtPoint(editor, range.startContainer, range.startOffset);
3480
- const end = spreadsheetTextOffsetAtPoint(editor, range.endContainer, range.endOffset);
3481
- return null === start || null === end || start > end ? null : {
3482
- editor,
3483
- end,
3484
- start
3485
- };
3486
- }
3487
- function spreadsheetRichTextEditor(node) {
3488
- const element = node instanceof Element ? node : node.parentElement;
3489
- const editor = element?.closest('.luckysheet-cell-input, .fortune-fx-input');
3490
- return editor?.isContentEditable || editor?.getAttribute('contenteditable') === 'true' ? editor : null;
3491
- }
3492
- function editorContainsRange(editor, range) {
3493
- return (range.startContainer === editor || editor.contains(range.startContainer)) && (range.endContainer === editor || editor.contains(range.endContainer));
3494
- }
3495
- function spreadsheetTextOffsetAtPoint(editor, node, offset) {
3496
- try {
3497
- const prefix = document.createRange();
3498
- prefix.selectNodeContents(editor);
3499
- prefix.setEnd(node, offset);
3500
- return prefix.toString().length;
3501
- } catch {
3502
- return null;
3503
- }
3504
- }
3505
- const MAX_SPREADSHEET_RICH_TEXT_CLIPBOARD_HTML_CHARACTERS = 256000;
3506
- const pendingPastes = new WeakMap();
3507
- const ignoredClipboardElements = new Set([
3508
- 'HEAD',
3509
- 'META',
3510
- 'NOSCRIPT',
3511
- 'SCRIPT',
3512
- 'STYLE',
3513
- 'TITLE'
3514
- ]);
3515
- const blockClipboardElements = new Set([
3516
- 'ADDRESS',
3517
- 'BLOCKQUOTE',
3518
- 'DIV',
3519
- 'H1',
3520
- 'H2',
3521
- 'H3',
3522
- 'H4',
3523
- 'H5',
3524
- 'H6',
3525
- 'LI',
3526
- 'P',
3527
- 'PRE'
3528
- ]);
3529
- function parseSpreadsheetRichTextClipboard(html, plainText) {
3530
- const text = normalizeClipboardText(plainText);
3531
- if (!html || html.length > MAX_SPREADSHEET_RICH_TEXT_CLIPBOARD_HTML_CHARACTERS || !text || text.length > 32767 || !validXlsxRichText(text)) return null;
3532
- const template = document.createElement('template');
3533
- template.innerHTML = html;
3534
- const runs = [];
3535
- const state = {
3536
- characterCount: 0,
3537
- invalid: false
3538
- };
3539
- for (const child of template.content.childNodes){
3540
- collectClipboardRuns(child, {}, runs, state);
3541
- if (state.invalid) return null;
3542
- }
3543
- trimTrailingClipboardNewline(runs, text);
3544
- const normalized = coalesceXlsxRichTextRuns(runs);
3545
- if (!normalized.length || normalized.length > 512 || normalized.map((run)=>run.v).join('') !== text || !normalized.some(spreadsheetRichTextRunHasFormatting)) return null;
3546
- return {
3547
- runs: normalized,
3548
- text
3549
- };
3550
- }
3551
- function captureSpreadsheetRichTextPaste({ clipboardData, content, root, selection, sheetId, target }) {
3552
- const domSelection = captureSpreadsheetRichTextDomSelection({
3553
- root,
3554
- target
3555
- });
3556
- const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
3557
- if (!domSelection || !sheet || !selection) return false;
3558
- const row = focusedAxisIndex(selection.row_focus, selection.row);
3559
- const column = focusedAxisIndex(selection.column_focus, selection.column);
3560
- const cell = spreadsheetCellAt(sheet, row, column);
3561
- const source = normalizeXlsxRichTextEditSource(cell);
3562
- const key = spreadsheetRichTextPasteKey(row, column);
3563
- if (!source || domSelection.editor.textContent !== source.text || domSelection.end > source.text.length) {
3564
- clearPendingPaste(sheet, key);
3565
- return false;
3566
- }
3567
- const parsed = parseSpreadsheetRichTextClipboard(clipboardData.getData('text/html'), clipboardData.getData('text/plain'));
3568
- if (!parsed) {
3569
- clearPendingPaste(sheet, key);
3570
- return false;
3571
- }
3572
- stageSpreadsheetRichTextPaste(sheet, row, column, source.text, {
3573
- end: domSelection.end,
3574
- runs: parsed.runs,
3575
- start: domSelection.start,
3576
- text: parsed.text
3577
- });
3578
- return true;
3579
- }
3580
- function stageSpreadsheetRichTextPaste(sheet, row, column, sourceText, intent) {
3581
- if (!validCoordinate(row) || !validCoordinate(column)) return;
3582
- const key = spreadsheetRichTextPasteKey(row, column);
3583
- const entries = pendingPastes.get(sheet) ?? new Map();
3584
- pendingPastes.set(sheet, entries);
3585
- entries.set(key, {
3586
- intent: {
3587
- ...intent,
3588
- runs: intent.runs.map((run)=>({
3589
- ...run
3590
- }))
3591
- },
3592
- sourceText
3593
- });
3594
- }
3595
- function takeSpreadsheetRichTextPaste(sheet, row, column, current) {
3596
- const intent = peekSpreadsheetRichTextPaste(sheet, row, column, current);
3597
- if (intent) consumeSpreadsheetRichTextPaste(sheet, row, column);
3598
- return intent;
3599
- }
3600
- function peekSpreadsheetRichTextPaste(sheet, row, column, current) {
3601
- const entries = pendingPastes.get(sheet);
3602
- const key = spreadsheetRichTextPasteKey(row, column);
3603
- const pending = entries?.get(key);
3604
- if (!entries || !pending) return;
3605
- const currentText = spreadsheetRichTextCellText(current);
3606
- if (currentText === pending.sourceText) return;
3607
- return null === currentText ? void 0 : pending.intent;
3608
- }
3609
- function consumeSpreadsheetRichTextPaste(sheet, row, column) {
3610
- clearPendingPaste(sheet, spreadsheetRichTextPasteKey(row, column));
3611
- }
3612
- function collectClipboardRuns(node, inherited, runs, state) {
3613
- if (state.invalid) return;
3614
- if (node.nodeType === Node.TEXT_NODE) return void appendClipboardText(runs, inherited, node.textContent ?? '', state);
3615
- if (!(node instanceof HTMLElement) || ignoredClipboardElements.has(node.tagName)) return;
3616
- const style = spreadsheetClipboardElementStyle(node, inherited);
3617
- if ('BR' === node.tagName) return void appendClipboardText(runs, style, '\n', state);
3618
- for (const child of node.childNodes){
3619
- collectClipboardRuns(child, style, runs, state);
3620
- if (state.invalid) return;
3621
- }
3622
- if (blockClipboardElements.has(node.tagName) && hasFollowingClipboardContent(node)) appendClipboardText(runs, style, '\n', state);
3623
- }
3624
- function appendClipboardText(runs, style, source, state) {
3625
- const value = normalizeClipboardText(source);
3626
- if (!value) return;
3627
- state.characterCount += value.length;
3628
- if (state.characterCount > 32767 || !validXlsxRichText(value)) {
3629
- state.invalid = true;
3630
- return;
3631
- }
3632
- const run = {
3633
- ...style,
3634
- v: value
3635
- };
3636
- const previous = runs.at(-1);
3637
- if (previous && sameXlsxRichTextRunStyle(previous, run)) previous.v += value;
3638
- else runs.push(run);
3639
- if (runs.length > 512) state.invalid = true;
3640
- }
3641
- function spreadsheetClipboardElementStyle(element, inherited) {
3642
- const style = {
3643
- ...inherited
3644
- };
3645
- if (element.matches('b, strong')) style.bl = 1;
3646
- if (element.matches('i, em')) style.it = 1;
3647
- if (element.matches('s, strike, del')) style.cl = 1;
3648
- if (element.matches('u')) style.un = 1;
3649
- const declarations = cssDeclarations(element.getAttribute('style'));
3650
- applyFontWeight(style, declarations.get('font-weight'));
3651
- applyToggle(style, 'it', declarations.get('font-style'), 'italic');
3652
- applyTextDecoration(style, declarations);
3653
- applyFontFamily(style, declarations.get('font-family'));
3654
- applyFontSize(style, declarations.get('font-size'));
3655
- applyFontColor(style, declarations.get('color'));
3656
- applyFortuneToggle(style, 'cl', declarations.get('lucky-strike'));
3657
- applyFortuneUnderline(style, declarations.get('lucky-underline'));
3658
- if (declarations.get('border-bottom')?.trim().toLowerCase() !== 'none') {
3659
- if (declarations.has('border-bottom')) style.un = 1;
3660
- }
3661
- if ('FONT' === element.tagName) {
3662
- applyFontFamily(style, element.getAttribute('face') ?? void 0);
3663
- applyFontColor(style, element.getAttribute('color') ?? void 0);
3664
- applyHtmlFontSize(style, element.getAttribute('size'));
3665
- }
3666
- return style;
3667
- }
3668
- function cssDeclarations(value) {
3669
- const declarations = new Map();
3670
- for (const candidate of value?.split(';') ?? []){
3671
- const separator = candidate.indexOf(':');
3672
- if (separator <= 0) continue;
3673
- const key = candidate.slice(0, separator).trim().toLowerCase();
3674
- const declaration = candidate.slice(separator + 1).trim();
3675
- if (key && declaration) declarations.set(key, declaration);
3676
- }
3677
- return declarations;
3678
- }
3679
- function applyFontWeight(style, value) {
3680
- const normalized = value?.trim().toLowerCase();
3681
- if (!normalized) return;
3682
- if ('bold' === normalized || 'bolder' === normalized) style.bl = 1;
3683
- else if ('normal' === normalized || 'lighter' === normalized) style.bl = 0;
3684
- else if (/^\d+$/.test(normalized)) style.bl = Number(normalized) >= 600 ? 1 : 0;
3685
- }
3686
- function applyToggle(style, key, value, enabled) {
3687
- const normalized = value?.trim().toLowerCase();
3688
- if (!normalized) return;
3689
- style[key] = normalized === enabled || 'oblique' === normalized ? 1 : 0;
3690
- }
3691
- function applyTextDecoration(style, declarations) {
3692
- const value = declarations.get('text-decoration-line') ?? declarations.get('text-decoration');
3693
- const normalized = value?.toLowerCase();
3694
- if (!normalized) return;
3695
- if (normalized.includes('none')) {
3696
- style.cl = 0;
3697
- style.un = 0;
3698
- return;
3699
- }
3700
- if (normalized.includes('line-through')) style.cl = 1;
3701
- if (normalized.includes('underline')) style.un = 'double' === declarations.get('text-decoration-style') ? 2 : 1;
3702
- }
3703
- function applyFontFamily(style, value) {
3704
- const family = value?.split(',')[0]?.trim().replace(/^(['"])(.*)\1$/, '$2');
3705
- if (family && family.length <= 128) style.ff = family;
3706
- }
3707
- function applyFontSize(style, value) {
3708
- const match = /^([\d.]+)\s*(pt|px)$/i.exec(value?.trim() ?? '');
3709
- if (!match) return;
3710
- const number = Number(match[1]);
3711
- const size = match[2]?.toLowerCase() === 'px' ? 0.75 * number : number;
3712
- if (Number.isFinite(size) && size >= 1 && size <= 409) style.fs = Math.round(100 * size) / 100;
3713
- }
3714
- function applyFontColor(style, value) {
3715
- const color = normalizeCssColor(value);
3716
- if (color && 'transparent' !== color) style.fc = color;
3717
- }
3718
- function applyFortuneToggle(style, key, value) {
3719
- const toggle = Number(value);
3720
- if (Number.isSafeInteger(toggle) && (0 === toggle || 1 === toggle)) style[key] = toggle;
3721
- }
3722
- function applyFortuneUnderline(style, value) {
3723
- const underline = Number(value);
3724
- if (Number.isSafeInteger(underline) && underline >= 0 && underline <= 4) style.un = underline;
3725
- }
3726
- function applyHtmlFontSize(style, value) {
3727
- const index = Number(value);
3728
- const size = [
3729
- 0,
3730
- 8,
3731
- 10,
3732
- 12,
3733
- 14,
3734
- 18,
3735
- 24,
3736
- 36
3737
- ][index];
3738
- if (size) style.fs = size;
3739
- }
3740
- function spreadsheetRichTextRunHasFormatting(run) {
3741
- return [
3742
- 'bl',
3743
- 'cl',
3744
- 'fc',
3745
- 'ff',
3746
- 'fs',
3747
- 'it',
3748
- 'un'
3749
- ].some((key)=>void 0 !== run[key]);
3750
- }
3751
- function trimTrailingClipboardNewline(runs, expectedText) {
3752
- const actual = runs.map((run)=>run.v).join('');
3753
- if (!actual.endsWith('\n') || expectedText.endsWith('\n')) return;
3754
- const last = runs.at(-1);
3755
- if (!last) return;
3756
- last.v = last.v.slice(0, -1);
3757
- if (!last.v) runs.pop();
3758
- }
3759
- function hasFollowingClipboardContent(element) {
3760
- let sibling = element.nextSibling;
3761
- while(sibling){
3762
- if (sibling.nodeType === Node.TEXT_NODE) {
3763
- if (sibling.textContent) return true;
3764
- } else if (sibling instanceof HTMLElement && !ignoredClipboardElements.has(sibling.tagName)) return true;
3765
- sibling = sibling.nextSibling;
3766
- }
3767
- return false;
3768
- }
3769
- function spreadsheetRichTextCellText(cell) {
3770
- const rich = normalizeXlsxRichTextCell(cell);
3771
- if ('string' == typeof cell.v && cell.v !== rich?.text) return cell.v;
3772
- return rich?.text ?? ('string' == typeof cell.v ? cell.v : null);
3773
- }
3774
- function spreadsheetCellAt(sheet, row, column) {
3775
- return sheet.data?.[row]?.[column] ?? sheet.celldata?.find((candidate)=>candidate.r === row && candidate.c === column)?.v ?? null;
3776
- }
3777
- function focusedAxisIndex(value, axis) {
3778
- const minimum = spreadsheet_rich_text_paste_finiteIndex(axis[0], 0);
3779
- const maximum = spreadsheet_rich_text_paste_finiteIndex(axis[1], minimum);
3780
- const focus = spreadsheet_rich_text_paste_finiteIndex(value, minimum);
3781
- return Math.min(Math.max(minimum, maximum), Math.max(Math.min(minimum, maximum), focus));
3782
- }
3783
- function spreadsheet_rich_text_paste_finiteIndex(value, fallback) {
3784
- return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
3785
- }
3786
- function normalizeClipboardText(value) {
3787
- return value.replace(/\r\n?/g, '\n').replace(/\u00a0/g, ' ');
3788
- }
3789
- function clearPendingPaste(sheet, key) {
3790
- const entries = pendingPastes.get(sheet);
3791
- entries?.delete(key);
3792
- if (entries && !entries.size) pendingPastes.delete(sheet);
3793
- }
3794
- function spreadsheetRichTextPasteKey(row, column) {
3795
- return `${row}:${column}`;
3796
- }
3797
- function validCoordinate(value) {
3798
- return Number.isSafeInteger(value) && value >= 0;
3799
- }
3800
- const MAXIMUM_INCREMENTAL_SPREADSHEET_OPERATIONS = 10000;
3801
- function spreadsheetCellOperationKey(row, column) {
3802
- return `${row}:${column}`;
3803
- }
3804
- function spreadsheetCellOperationCoordinates(operations) {
3805
- if (operations.length > MAXIMUM_INCREMENTAL_SPREADSHEET_OPERATIONS) return null;
3806
- const coordinatesBySheet = new Map();
3807
- for (const operation of operations){
3808
- if ('insertRowCol' === operation.op || 'deleteRowCol' === operation.op || 'addSheet' === operation.op || 'deleteSheet' === operation.op || 0 === operation.path.length || !operation.id) return null;
3809
- const root = operation.path[0];
3810
- if ('id' === root || 'celldata' === root) return null;
3811
- if ('data' !== root) continue;
3812
- const row = operation.path[1];
3813
- const column = operation.path[2];
3814
- if (operation.path.length < 3 || !isSpreadsheetCoordinate(row) || !isSpreadsheetCoordinate(column)) return null;
3815
- const coordinates = coordinatesBySheet.get(operation.id) ?? new Map();
3816
- coordinatesBySheet.set(operation.id, coordinates);
3817
- coordinates.set(spreadsheetCellOperationKey(row, column), {
3818
- column,
3819
- row
3820
- });
3821
- }
3822
- return coordinatesBySheet;
3823
- }
3824
- function projectSpreadsheetSheetsFromFortuneOperations(sheets, sourceSheets, operations) {
3825
- if (0 === operations.length || sheets.length !== sourceSheets.length) return null;
3826
- const changedSheets = spreadsheetSheetsById(sheets);
3827
- const controlledSheets = spreadsheetSheetsById(sourceSheets);
3828
- if (!changedSheets || !controlledSheets) return null;
3829
- if (changedSheets.size !== sheets.length || controlledSheets.size !== sourceSheets.length) return null;
3830
- const coordinatesBySheet = spreadsheetCellOperationCoordinates(operations);
3831
- if (!coordinatesBySheet) return null;
3832
- for (const operation of operations)if (!operation.id || !changedSheets.has(operation.id) || !controlledSheets.has(operation.id)) return null;
3833
- let affectedCellCount = 0;
3834
- const projectedSheets = [];
3835
- const pendingRichTextPastes = [];
3836
- for (const sheet of sheets){
3837
- const id = sheet.id;
3838
- if (!id) return null;
3839
- const source = controlledSheets.get(id);
3840
- if (!source) return null;
3841
- const coordinates = coordinatesBySheet.get(id);
3842
- const { celldata: _cellData, data: _data, ...metadata } = sheet;
3843
- if (!coordinates?.size) {
3844
- projectedSheets.push(void 0 !== source.data ? {
3845
- ...metadata,
3846
- data: source.data
3847
- } : {
3848
- ...metadata,
3849
- celldata: source.celldata ?? []
3850
- });
3851
- continue;
3852
- }
3853
- if (void 0 === source.data || void 0 === sheet.data || !spreadsheetMatrixProfile(source.data)?.fortuneReady) return null;
3854
- const projection = projectSpreadsheetMatrixCells(sheet, source, coordinates.values(), pendingRichTextPastes);
3855
- if (!projection) return null;
3856
- affectedCellCount += projection.affectedCellCount;
3857
- projectedSheets.push({
3858
- ...metadata,
3859
- data: projection.data
3860
- });
3861
- }
3862
- for (const pending of pendingRichTextPastes)consumeSpreadsheetRichTextPaste(pending.sheet, pending.row, pending.column);
3863
- return {
3864
- affectedCellCount,
3865
- sheets: projectedSheets
3866
- };
3867
- }
3868
- function projectSpreadsheetMatrixCells(changed, source, coordinates, pendingRichTextPastes) {
3869
- if (!changed.data || !source.data) return null;
3870
- const data = source.data.slice();
3871
- data.length = Math.max(data.length, changed.data.length, positiveSpreadsheetDimension(changed.row));
3872
- const mutableRows = new Map();
3873
- const profileChanges = [];
3874
- for (const { column, row } of coordinates){
3875
- const changedRow = changed.data[row];
3876
- const sourceRow = source.data[row];
3877
- const currentCell = changedRow?.[column];
3878
- const formattedPaste = currentCell ? peekSpreadsheetRichTextPaste(source, row, column, currentCell) : void 0;
3879
- const nextCell = null == currentCell ? currentCell : cloneControlledSpreadsheetCell(currentCell, sourceRow?.[column], formattedPaste);
3880
- if (formattedPaste) pendingRichTextPastes.push({
3881
- column,
3882
- row,
3883
- sheet: source
3884
- });
3885
- let nextRow = mutableRows.get(row);
3886
- if (!nextRow) {
3887
- nextRow = sourceRow?.slice() ?? [];
3888
- nextRow.length = Math.max(nextRow.length, changedRow?.length ?? 0, positiveSpreadsheetDimension(changed.column));
3889
- mutableRows.set(row, nextRow);
3890
- data[row] = nextRow;
3891
- }
3892
- if (null == nextCell) delete nextRow[column];
3893
- else nextRow[column] = nextCell;
3894
- profileChanges.push({
3895
- column,
3896
- current: nextCell,
3897
- previous: sourceRow?.[column],
3898
- row
3899
- });
3900
- }
3901
- if (!registerDerivedSpreadsheetMatrix(data, source.data, profileChanges)) return null;
3902
- return {
3903
- affectedCellCount: profileChanges.length,
3904
- data
3905
- };
3906
- }
3907
- function cloneControlledSpreadsheetCell(cell, previous, formattedPaste) {
3908
- return freezeImportedSpreadsheetCell(structuredClone(reconcileSpreadsheetRichTextCellEdit(previous, cell, formattedPaste)));
3909
- }
3910
- function spreadsheetSheetsById(sheets) {
3911
- const byId = new Map();
3912
- for (const sheet of sheets){
3913
- if (!sheet.id || byId.has(sheet.id)) return null;
3914
- byId.set(sheet.id, sheet);
3915
- }
3916
- return byId;
3917
- }
3918
- function isSpreadsheetCoordinate(value) {
3919
- return Number.isSafeInteger(value) && Number(value) >= 0;
3920
- }
3921
- function positiveSpreadsheetDimension(value) {
3922
- return 'number' == typeof value && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
3923
- }
3924
- function spreadsheetCurrentRegion(sheet, seed) {
3925
- const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(seed);
3926
- if (!range) return null;
3927
- const cells = spreadsheetSheetCellReader(sheet);
3928
- const bounds = spreadsheetCurrentRegionUsedBounds(sheet);
3929
- let startRow = range.row[0];
3930
- let endRow = range.row[1];
3931
- let startColumn = range.column[0];
3932
- let endColumn = range.column[1];
3933
- if (startRow > bounds.lastRow || startColumn > bounds.lastColumn || !spreadsheet_current_region_spreadsheetCellHasContent(cells(startRow, startColumn))) return null;
3934
- let changed = true;
3935
- while(changed){
3936
- changed = false;
3937
- while(startColumn > 0 && spreadsheetCurrentRegionColumnHasContent(cells, startColumn - 1, startRow, endRow)){
3938
- startColumn -= 1;
3939
- changed = true;
3940
- }
3941
- while(endColumn < bounds.lastColumn && spreadsheetCurrentRegionColumnHasContent(cells, endColumn + 1, startRow, endRow)){
3942
- endColumn += 1;
3943
- changed = true;
3944
- }
3945
- while(startRow > 0 && spreadsheetRowHasContent(cells, startRow - 1, startColumn, endColumn)){
3946
- startRow -= 1;
3947
- changed = true;
3948
- }
3949
- while(endRow < bounds.lastRow && spreadsheetRowHasContent(cells, endRow + 1, startColumn, endColumn)){
3950
- endRow += 1;
3951
- changed = true;
3952
- }
3953
- }
3954
- return {
3955
- row: [
3956
- startRow,
3957
- endRow
3958
- ],
3959
- column: [
3960
- startColumn,
3961
- endColumn
3962
- ]
3963
- };
3964
- }
3965
- function spreadsheetRowHasContent(cellAt, row, startColumn, endColumn) {
3966
- for(let column = startColumn; column <= endColumn; column += 1)if (spreadsheet_current_region_spreadsheetCellHasContent(cellAt(row, column))) return true;
3967
- return false;
3968
- }
3969
- function spreadsheetCurrentRegionColumnHasContent(cellAt, column, startRow, endRow) {
3970
- for(let row = startRow; row <= endRow; row += 1)if (spreadsheet_current_region_spreadsheetCellHasContent(cellAt(row, column))) return true;
3971
- return false;
3972
- }
3973
- function spreadsheet_current_region_spreadsheetCellHasContent(cell) {
3974
- if (!cell) return false;
3975
- return [
3976
- cell.v,
3977
- cell.m,
3978
- cell.f
3979
- ].some((value)=>null != value && '' !== value);
3980
- }
3981
- function spreadsheetSheetCellReader(sheet) {
3982
- if (sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
3983
- const cells = new Map((sheet.celldata ?? []).map((cell)=>[
3984
- `${cell.r}:${cell.c}`,
3985
- cell.v
3986
- ]));
3987
- return (row, column)=>cells.get(`${row}:${column}`) ?? null;
3988
- }
3989
- function spreadsheetCurrentRegionUsedBounds(sheet) {
3990
- let lastRow = Math.max((sheet.data?.length ?? 1) - 1, 0);
3991
- let lastColumn = Math.max(sparseMatrixColumnCount(sheet.data) - 1, 0);
3992
- for (const cell of sheet.celldata ?? []){
3993
- lastRow = Math.max(lastRow, cell.r);
3994
- lastColumn = Math.max(lastColumn, cell.c);
3995
- }
3996
- return {
3997
- lastColumn,
3998
- lastRow
3999
- };
4000
- }
4001
- function spreadsheetCellFillFormat(cell) {
4002
- const nativeFill = activeXlsxNativeFill(cell);
4003
- if (nativeFill) return nativeFill;
4004
- const color = normalizeSpreadsheetFillColor(cell?.bg);
4005
- return color ? {
4006
- color,
4007
- kind: 'solid'
4008
- } : {
4009
- kind: 'none'
4010
- };
4011
- }
4012
- function normalizeSpreadsheetCellFillFormat(value) {
4013
- if (!spreadsheet_cell_fill_format_isRecord(value)) return null;
4014
- if ('none' === value.kind) return {
4015
- kind: 'none'
4016
- };
4017
- if ('solid' === value.kind) {
4018
- const color = normalizeSpreadsheetFillColor(value.color);
4019
- return color ? {
4020
- color,
4021
- kind: 'solid'
4022
- } : null;
4023
- }
4024
- if ('pattern' === value.kind) {
4025
- const fill = normalizeXlsxPatternFill(value.value);
4026
- return fill ? {
4027
- kind: 'pattern',
4028
- value: fill
4029
- } : null;
4030
- }
4031
- if ('gradient' === value.kind) {
4032
- const fill = normalizeXlsxGradientFill(value.value);
4033
- return fill ? {
4034
- kind: 'gradient',
4035
- value: fill
4036
- } : null;
4037
- }
4038
- return null;
4039
- }
4040
- function withSpreadsheetCellFillFormat(cell, fill) {
4041
- const normalized = normalizeSpreadsheetCellFillFormat(fill);
4042
- if (!normalized) return null;
4043
- const next = {
4044
- ...cell
4045
- };
4046
- deleteXlsxNativeFills(next);
4047
- if ('none' === normalized.kind) {
4048
- delete next.bg;
4049
- return next;
4050
- }
4051
- if ('solid' === normalized.kind) {
4052
- next.bg = normalized.color;
4053
- return next;
4054
- }
4055
- if ('pattern' === normalized.kind) {
4056
- next.bg = normalized.value.backgroundColor;
4057
- return withXlsxPatternFill(next, normalized.value);
4058
- }
4059
- next.bg = xlsxGradientFillFallbackColor(normalized.value);
4060
- return withXlsxGradientFill(next, normalized.value);
4061
- }
4062
- function normalizeSpreadsheetFillColor(value) {
4063
- if ('string' != typeof value) return null;
4064
- const color = value.trim().toLowerCase();
4065
- if (/^#[0-9a-f]{6}$/.test(color)) return color;
4066
- if (!/^#[0-9a-f]{3}$/.test(color)) return null;
4067
- return `#${[
4068
- ...color.slice(1)
4069
- ].map((character)=>character.repeat(2)).join('')}`;
4070
- }
4071
- function spreadsheet_cell_fill_format_isRecord(value) {
4072
- return 'object' == typeof value && null !== value && !Array.isArray(value);
4073
- }
4074
- function createSpreadsheetSortAppearanceRows(sheet, range, rows) {
4075
- const data = liveSpreadsheetSortMatrix(sheet, range, rows);
4076
- const conditional = spreadsheetConditionalFormatStyles({
4077
- ...sheet,
4078
- data
4079
- });
4080
- return rows.map((row, rowOffset)=>row.map((cell, columnOffset)=>{
4081
- const direct = spreadsheetSortDirectCellAppearance(cell);
4082
- const style = conditional.get(`${range.row[0] + rowOffset}_${range.column[0] + columnOffset}`);
4083
- const conditionalCellColor = normalizeSpreadsheetSortColor(style?.cellColor);
4084
- const conditionalFontColor = normalizeSpreadsheetSortColor(style?.textColor);
4085
- return {
4086
- cellColor: void 0 !== conditionalCellColor ? conditionalCellColor : direct.cellColor,
4087
- fontColor: void 0 !== conditionalFontColor ? conditionalFontColor : direct.fontColor,
4088
- icon: normalizeSpreadsheetSortIcon(style?.icon) ?? null
4089
- };
4090
- }));
4091
- }
4092
- function createSpreadsheetSortDirectAppearanceRows(rows) {
4093
- return rows.map((row)=>row.map(spreadsheetSortDirectCellAppearance));
4094
- }
4095
- function spreadsheetSortAppearanceFields(rows, range, orientation, hasHeader) {
4096
- const vertical = 'top-to-bottom' === orientation;
4097
- const fieldCount = vertical ? range.column[1] - range.column[0] + 1 : range.row[1] - range.row[0] + 1;
4098
- return Array.from({
4099
- length: fieldCount
4100
- }, (_, offset)=>{
4101
- const cellColors = [];
4102
- const fontColors = [];
4103
- const icons = [];
4104
- const seenCellColors = new Set();
4105
- const seenFontColors = new Set();
4106
- const seenIcons = new Set();
4107
- const appearances = vertical ? rows.slice(hasHeader ? 1 : 0).map((row)=>row[offset]) : rows[offset] ?? [];
4108
- for (const appearance of appearances)if (appearance) {
4109
- if (void 0 !== appearance.cellColor) appendUniqueColor(cellColors, seenCellColors, appearance.cellColor);
4110
- appendUniqueColor(fontColors, seenFontColors, appearance.fontColor);
4111
- if (appearance.icon) {
4112
- const key = spreadsheetSortIconTargetKey(appearance.icon);
4113
- if (!seenIcons.has(key)) {
4114
- seenIcons.add(key);
4115
- icons.push({
4116
- ...appearance.icon
4117
- });
4118
- }
4119
- }
4120
- }
4121
- return {
4122
- index: (vertical ? range.column[0] : range.row[0]) + offset,
4123
- cellColors,
4124
- fontColors,
4125
- icons
4126
- };
4127
- });
4128
- }
4129
- function normalizeSpreadsheetSortColor(value) {
4130
- if (null === value) return null;
4131
- if ('string' != typeof value) return;
4132
- const source = value.trim();
4133
- const shorthand = /^#([0-9a-f]{3})$/iu.exec(source);
4134
- const parsed = parseSpreadsheetConditionalColor(shorthand ? `#${[
4135
- ...shorthand[1]
4136
- ].map((character)=>character.repeat(2)).join('')}` : source);
4137
- return parsed ? spreadsheetSortHexColor(parsed) : void 0;
4138
- }
4139
- function normalizeSpreadsheetSortIcon(value) {
4140
- if (!value || 'object' != typeof value || Array.isArray(value)) return null;
4141
- const source = value;
4142
- if (!isSpreadsheetConditionalIconSetName(source.iconSet) || !Number.isSafeInteger(source.index) || source.index < 0 || source.index >= spreadsheetConditionalIconSetCount(source.iconSet)) return null;
4143
- return {
4144
- iconSet: source.iconSet,
4145
- index: source.index
4146
- };
4147
- }
4148
- function spreadsheetSortAppearanceTargetValue(target) {
4149
- if ('icon' === target.kind) return `icon:${target.icon.iconSet}:${target.icon.index}`;
4150
- const value = target.color ?? ('cell-color' === target.kind ? 'none' : 'automatic');
4151
- return `${target.kind}:${value}`;
4152
- }
4153
- function parseSpreadsheetSortAppearanceTargetValue(value) {
4154
- if (value.startsWith('cell-color:')) {
4155
- const source = value.slice(11);
4156
- if ('none' === source) return {
4157
- kind: 'cell-color',
4158
- color: null
4159
- };
4160
- const color = normalizeSpreadsheetSortColor(source);
4161
- return 'string' == typeof color ? {
4162
- kind: 'cell-color',
4163
- color
4164
- } : null;
4165
- }
4166
- if (value.startsWith('font-color:')) {
4167
- const source = value.slice(11);
4168
- if ('automatic' === source) return {
4169
- kind: 'font-color',
4170
- color: null
4171
- };
4172
- const color = normalizeSpreadsheetSortColor(source);
4173
- return 'string' == typeof color ? {
4174
- kind: 'font-color',
4175
- color
4176
- } : null;
4177
- }
4178
- const icon = /^icon:([^:]+):(\d+)$/u.exec(value);
4179
- if (!icon) return null;
4180
- const normalized = normalizeSpreadsheetSortIcon({
4181
- iconSet: icon[1],
4182
- index: Number(icon[2])
4183
- });
4184
- return normalized ? {
4185
- kind: 'icon',
4186
- icon: normalized
4187
- } : null;
4188
- }
4189
- function spreadsheetSortAppearanceTargetLabel(target) {
4190
- if ('cell-color' === target.kind) return target.color ? `单元格颜色 ${target.color.toUpperCase()}` : '无单元格颜色';
4191
- if ('font-color' === target.kind) return target.color ? `字体颜色 ${target.color.toUpperCase()}` : '自动字体颜色';
4192
- const count = spreadsheetConditionalIconSetCount(target.icon.iconSet);
4193
- const appearance = spreadsheetConditionalIconAppearance({
4194
- ...target.icon,
4195
- count,
4196
- showValue: true
4197
- });
4198
- return `${appearance.glyph} ${appearance.label}`;
4199
- }
4200
- function spreadsheetSortAppearanceTargets(field, kind) {
4201
- if (!field) return [];
4202
- if ('cell-color' === kind) return field.cellColors.map((color)=>({
4203
- kind,
4204
- color
4205
- }));
4206
- if ('font-color' === kind) return field.fontColors.map((color)=>({
4207
- kind,
4208
- color
4209
- }));
4210
- return field.icons.map((icon)=>({
4211
- kind,
4212
- icon: {
4213
- ...icon
4214
- }
4215
- }));
4216
- }
4217
- function spreadsheetSortAppearanceTargetsEqual(left, right) {
4218
- return spreadsheetSortAppearanceTargetValue(left) === spreadsheetSortAppearanceTargetValue(right);
4219
- }
4220
- function spreadsheetSortCellMatchesAppearance(appearance, target) {
4221
- if (!appearance) return false;
4222
- if ('cell-color' === target.kind) return appearance.cellColor === target.color;
4223
- if ('font-color' === target.kind) return appearance.fontColor === target.color;
4224
- return Boolean(appearance.icon && spreadsheetSortIconTargetKey(appearance.icon) === spreadsheetSortIconTargetKey(target.icon));
4225
- }
4226
- function spreadsheetSortDirectCellAppearance(cell) {
4227
- const fill = spreadsheetCellFillFormat(cell);
4228
- const fontColor = normalizeSpreadsheetSortColor(cell?.fc);
4229
- return {
4230
- cellColor: 'solid' === fill.kind ? fill.color : 'none' === fill.kind ? null : void 0,
4231
- fontColor: 'string' == typeof fontColor ? fontColor : null,
4232
- icon: null
4233
- };
4234
- }
4235
- function liveSpreadsheetSortMatrix(sheet, range, rows) {
4236
- const data = cloneSparseMatrix(sheet.data);
4237
- for (const entry of sheet.celldata ?? []){
4238
- const row = data[entry.r] ? [
4239
- ...data[entry.r]
4240
- ] : [];
4241
- if (void 0 === row[entry.c]) row[entry.c] = entry.v;
4242
- data[entry.r] = row;
4243
- }
4244
- for(let rowOffset = 0; rowOffset < rows.length; rowOffset += 1){
4245
- const absoluteRow = range.row[0] + rowOffset;
4246
- const target = data[absoluteRow] ? [
4247
- ...data[absoluteRow]
4248
- ] : [];
4249
- const source = rows[rowOffset] ?? [];
4250
- for(let columnOffset = 0; columnOffset < source.length; columnOffset += 1)target[range.column[0] + columnOffset] = source[columnOffset] ?? null;
4251
- data[absoluteRow] = target;
4252
- }
4253
- return data;
4254
- }
4255
- function appendUniqueColor(target, seen, color) {
4256
- const key = color ?? 'none';
4257
- if (seen.has(key)) return;
4258
- seen.add(key);
4259
- target.push(color);
4260
- }
4261
- function spreadsheetSortIconTargetKey(target) {
4262
- return `${target.iconSet}:${target.index}`;
4263
- }
4264
- function spreadsheetSortHexColor(color) {
4265
- return `#${[
4266
- color.red,
4267
- color.green,
4268
- color.blue
4269
- ].map((channel)=>channel.toString(16).padStart(2, '0')).join('')}`;
4270
- }
4271
- const DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS = Object.freeze({
4272
- caseSensitive: false,
4273
- textMethod: 'pinyin'
4274
- });
4275
- const SPREADSHEET_SORT_COLLATION_PROBES = Object.freeze({
4276
- pinyin: Object.freeze([
4277
- '阿',
4278
- '丁',
4279
- '王',
4280
- '赵'
4281
- ]),
4282
- stroke: Object.freeze([
4283
- '丁',
4284
- '王',
4285
- '安',
4286
- '阿',
4287
- '赵'
4288
- ])
4289
- });
4290
- function isSpreadsheetSortTextMethod(value) {
4291
- return 'pinyin' === value || 'stroke' === value;
4292
- }
4293
- function createSpreadsheetSortTextComparator(options) {
4294
- try {
4295
- const collator = new Intl.Collator(`zh-CN-u-co-${options.textMethod}`, {
4296
- caseFirst: 'lower',
4297
- numeric: false,
4298
- sensitivity: options.caseSensitive ? 'case' : 'base',
4299
- usage: 'sort'
4300
- });
4301
- if (!spreadsheetSortCollatorSupportsMethod(collator, options.textMethod)) return null;
4302
- return collator.compare;
4303
- } catch {
4304
- return null;
4305
- }
4306
- }
4307
- function spreadsheetSortCollatorSupportsMethod(collator, textMethod) {
4308
- const probe = SPREADSHEET_SORT_COLLATION_PROBES[textMethod];
4309
- for(let index = 1; index < probe.length; index += 1){
4310
- const previous = probe[index - 1];
4311
- const current = probe[index];
4312
- if (void 0 === previous || void 0 === current) return false;
4313
- if (collator.compare(previous, current) >= 0) return false;
4314
- }
4315
- return true;
4316
- }
4317
- const MAX_SPREADSHEET_SORT_KEYS = 64;
4318
- const MAX_SPREADSHEET_SORT_CELLS = 1000000;
4319
- const MAX_SPREADSHEET_SORT_HEADER_LENGTH = 48;
4320
- function createSpreadsheetSortRangePlan(sheet, selectedRange) {
4321
- const selected = spreadsheet_cell_range_normalizeSpreadsheetCellRange(selectedRange);
4322
- if (!selected) return null;
4323
- const owned = spreadsheetSortOwnedRangeContaining(sheet, selected);
4324
- if (owned) return {
4325
- selectedRange: selected,
4326
- expandedRange: spreadsheetCellRangesEqual(owned.range, selected) ? null : owned.range
4327
- };
4328
- const currentRegion = spreadsheetCurrentRegion(sheet, selected);
4329
- return {
4330
- selectedRange: selected,
4331
- expandedRange: currentRegion && !spreadsheetCellRangesEqual(currentRegion, selected) ? currentRegion : null
4332
- };
4333
- }
4334
- function createSpreadsheetSortRangeDialogSource(sheetName, request) {
4335
- if (!request.expanded) return null;
4336
- return {
4337
- sheetName,
4338
- selectedRangeReference: formatSpreadsheetCellRanges([
4339
- request.selected.range
4340
- ]),
4341
- expandedRangeReference: formatSpreadsheetCellRanges([
4342
- request.expanded.range
4343
- ]),
4344
- canSortSelection: request.selected.available,
4345
- canSortExpandedRange: request.expanded.available,
4346
- ...request.expanded.scope ? {
4347
- ownedScope: request.expanded.scope
4348
- } : {}
4349
- };
4350
- }
4351
- function createSpreadsheetSortDialogSource(sheetId, sheetName, target, rows, customLists = [], appearanceRows) {
4352
- const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(target.range);
4353
- if (!range || spreadsheetSortRangeError(range)) return null;
4354
- const width = range.column[1] - range.column[0] + 1;
4355
- const height = range.row[1] - range.row[0] + 1;
4356
- if (!spreadsheetSortRowsMatchRange(rows, range)) return null;
4357
- const hasHeader = height > 2 && spreadsheetSortRowsHaveHeader(rows);
4358
- const resolvedHasHeader = target.scope?.hasHeader ?? hasHeader;
4359
- const activeColumn = spreadsheetSortActiveIndex(target.activeColumn, range.column);
4360
- const activeRow = spreadsheetSortActiveIndex(target.activeRow, range.row);
4361
- const header = rows[0] ?? [];
4362
- const resolvedAppearanceRows = appearanceRows && spreadsheetSortAppearanceRowsMatch(rows, appearanceRows) ? appearanceRows : createSpreadsheetSortDirectAppearanceRows(rows);
4363
- return {
4364
- sheetId,
4365
- sheetName,
4366
- range,
4367
- rangeReference: formatSpreadsheetCellRanges([
4368
- range
4369
- ]),
4370
- activeRow,
4371
- appearanceRows: resolvedAppearanceRows,
4372
- customLists: mergeSpreadsheetSortCustomLists(customLists),
4373
- ...target.scope ? {
4374
- scope: target.scope
4375
- } : {},
4376
- columns: Array.from({
4377
- length: width
4378
- }, (_, offset)=>{
4379
- const index = range.column[0] + offset;
4380
- const name = spreadsheetSortHeaderText(header[offset]);
4381
- const coordinate = spreadsheetSortColumnLabel(index);
4382
- return {
4383
- index,
4384
- label: name ? `${coordinate}(${name})` : coordinate
4385
- };
4386
- }),
4387
- rows: Array.from({
4388
- length: height
4389
- }, (_, offset)=>{
4390
- const index = range.row[0] + offset;
4391
- return {
4392
- index,
4393
- label: `行 ${index + 1}`
4394
- };
4395
- }),
4396
- value: {
4397
- orientation: 'top-to-bottom',
4398
- ...DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS,
4399
- hasHeader: resolvedHasHeader,
4400
- keys: [
4401
- {
4402
- index: activeColumn,
4403
- direction: 'ascending'
4404
- }
4405
- ]
4406
- }
4407
- };
4408
- }
4409
- function spreadsheetSortRowsMatchRange(rows, range) {
4410
- const normalized = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4411
- if (!normalized) return false;
4412
- const width = normalized.column[1] - normalized.column[0] + 1;
4413
- const height = normalized.row[1] - normalized.row[0] + 1;
4414
- return rows.length === height && rows.every((row)=>row.length === width);
4415
- }
4416
- function spreadsheetSortRowsFromSheet(sheet, range) {
4417
- const normalized = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4418
- if (!normalized || spreadsheetSortRangeError(normalized)) return null;
4419
- const width = normalized.column[1] - normalized.column[0] + 1;
4420
- const height = normalized.row[1] - normalized.row[0] + 1;
4421
- const rows = Array.from({
4422
- length: height
4423
- }, ()=>Array(width).fill(null));
4424
- for(let row = normalized.row[0]; row <= normalized.row[1]; row += 1){
4425
- const source = sheet.data?.[row];
4426
- if (!source) continue;
4427
- const target = rows[row - normalized.row[0]];
4428
- for(let column = normalized.column[0]; column <= normalized.column[1]; column += 1)target[column - normalized.column[0]] = source[column] ?? null;
4429
- }
4430
- for (const entry of sheet.celldata ?? []){
4431
- if (entry.r < normalized.row[0] || entry.r > normalized.row[1] || entry.c < normalized.column[0] || entry.c > normalized.column[1]) continue;
4432
- const row = rows[entry.r - normalized.row[0]];
4433
- const column = entry.c - normalized.column[0];
4434
- if (null === row[column]) row[column] = entry.v ?? null;
4435
- }
4436
- return rows;
4437
- }
4438
- function spreadsheetSortOwnedRangeForExactRange(sheet, range) {
4439
- const normalized = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4440
- if (!normalized) return null;
4441
- const matches = spreadsheetSortOwnedRanges(sheet).filter((candidate)=>spreadsheetCellRangesEqual(candidate.range, normalized));
4442
- return 1 === matches.length ? matches[0] : null;
4443
- }
4444
- function spreadsheetSortOwnedRangeContaining(sheet, range) {
4445
- const matches = spreadsheetSortOwnedRanges(sheet).filter((candidate)=>spreadsheetSortRangeContains(candidate.range, range));
4446
- return 1 === matches.length ? matches[0] : null;
4447
- }
4448
- function spreadsheetSortOwnedRanges(sheet) {
4449
- const ranges = [];
4450
- const autoFilter = spreadsheet_cell_range_normalizeSpreadsheetCellRange(sheet.filter_select ?? {
4451
- row: [],
4452
- column: []
4453
- });
4454
- if (autoFilter && autoFilter.row[1] > autoFilter.row[0]) ranges.push({
4455
- range: autoFilter,
4456
- scope: {
4457
- kind: 'auto-filter',
4458
- hasHeader: true
4459
- }
4460
- });
4461
- for (const table of sheet.tables ?? []){
4462
- const tableRange = spreadsheet_cell_range_normalizeSpreadsheetCellRange(table.range);
4463
- if (!tableRange || !table.id || 0 === table.columns.length) continue;
4464
- const width = tableRange.column[1] - tableRange.column[0] + 1;
4465
- if (width !== table.columns.length) continue;
4466
- const endRow = tableRange.row[1] - Number(table.totalsRow);
4467
- if (!(endRow <= tableRange.row[0])) ranges.push({
4468
- range: {
4469
- row: [
4470
- tableRange.row[0],
4471
- endRow
4472
- ],
4473
- column: [
4474
- ...tableRange.column
4475
- ]
4476
- },
4477
- scope: {
4478
- kind: 'table',
4479
- tableId: table.id,
4480
- hasHeader: table.headerRow
4481
- }
4482
- });
4483
- }
4484
- return ranges;
4485
- }
4486
- function spreadsheetSortRangeContains(outer, inner) {
4487
- return outer.row[0] <= inner.row[0] && outer.row[1] >= inner.row[1] && outer.column[0] <= inner.column[0] && outer.column[1] >= inner.column[1];
4488
- }
4489
- function validateSpreadsheetSortRequest(request) {
4490
- const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(request.range);
4491
- if (!range || 'string' != typeof request.sheetId || !request.sheetId.trim()) return spreadsheetSortError('invalid-range');
4492
- const scope = normalizeSpreadsheetSortOwnedScope(request.scope);
4493
- if (void 0 !== request.scope && !scope) return spreadsheetSortError('invalid-scope');
4494
- if ('top-to-bottom' !== request.orientation && 'left-to-right' !== request.orientation) return spreadsheetSortError('invalid-orientation');
4495
- const caseSensitive = void 0 === request.caseSensitive ? DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS.caseSensitive : request.caseSensitive;
4496
- if ('boolean' != typeof caseSensitive) return spreadsheetSortError('invalid-case-sensitivity');
4497
- const textMethod = void 0 === request.textMethod ? DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS.textMethod : request.textMethod;
4498
- if (!isSpreadsheetSortTextMethod(textMethod)) return spreadsheetSortError('invalid-text-method');
4499
- if ('left-to-right' === request.orientation && request.hasHeader) return spreadsheetSortError('invalid-header');
4500
- const rangeError = spreadsheetSortRangeError(range);
4501
- if (rangeError) return rangeError;
4502
- if (!Array.isArray(request.keys) || !request.keys.length) return spreadsheetSortError('missing-key');
4503
- if (request.keys.length > MAX_SPREADSHEET_SORT_KEYS) return spreadsheetSortError('too-many-keys');
4504
- const keyRange = 'top-to-bottom' === request.orientation ? range.column : range.row;
4505
- const outOfRangeCode = 'top-to-bottom' === request.orientation ? 'column-out-of-range' : 'row-out-of-range';
4506
- const keys = [];
4507
- for (const key of request.keys){
4508
- if (!Number.isSafeInteger(key.index) || key.index < keyRange[0] || key.index > keyRange[1]) return spreadsheetSortError(outOfRangeCode);
4509
- if (void 0 !== key.sortOn) {
4510
- if ('first' !== key.position && 'last' !== key.position || void 0 !== key.customList || void 0 !== key.direction) return spreadsheetSortError('invalid-appearance');
4511
- if ('cell-color' === key.sortOn || 'font-color' === key.sortOn) {
4512
- const color = normalizeSpreadsheetSortColor(key.color);
4513
- if (void 0 === color || void 0 !== key.icon) return spreadsheetSortError('invalid-appearance');
4514
- const normalized = {
4515
- index: key.index,
4516
- sortOn: key.sortOn,
4517
- color,
4518
- position: key.position
4519
- };
4520
- if (!appendSpreadsheetSortKey(keys, normalized)) return spreadsheetSortError('duplicate-key');
4521
- continue;
4522
- }
4523
- if ('icon' === key.sortOn && void 0 === key.color) {
4524
- const icon = normalizeSpreadsheetSortIcon(key.icon);
4525
- if (icon) {
4526
- const normalized = {
4527
- index: key.index,
4528
- sortOn: 'icon',
4529
- icon,
4530
- position: key.position
4531
- };
4532
- if (!appendSpreadsheetSortKey(keys, normalized)) return spreadsheetSortError('duplicate-key');
4533
- continue;
4534
- }
4535
- }
4536
- return spreadsheetSortError('invalid-appearance');
4537
- }
4538
- if (void 0 !== key.customList) {
4539
- if (void 0 !== key.direction) return spreadsheetSortError('invalid-custom-list');
4540
- const customList = validateSpreadsheetSortCustomList(key.customList);
4541
- if (!customList.ok) return spreadsheetSortError('invalid-custom-list');
4542
- if (!appendSpreadsheetSortKey(keys, {
4543
- index: key.index,
4544
- customList: customList.entries
4545
- })) return spreadsheetSortError('duplicate-key');
4546
- continue;
4547
- }
4548
- if ('ascending' !== key.direction && 'descending' !== key.direction) return spreadsheetSortError('invalid-direction');
4549
- if (!appendSpreadsheetSortKey(keys, {
4550
- index: key.index,
4551
- direction: key.direction
4552
- })) return spreadsheetSortError('duplicate-key');
4553
- }
4554
- if ('top-to-bottom' === request.orientation) {
4555
- const rowCount = range.row[1] - range.row[0] + 1;
4556
- if (rowCount - (request.hasHeader ? 1 : 0) < 2) return spreadsheetSortError('not-enough-rows');
4557
- } else if (range.column[1] - range.column[0] + 1 < 2) return spreadsheetSortError('not-enough-columns');
4558
- return {
4559
- ok: true,
4560
- request: {
4561
- sheetId: request.sheetId,
4562
- range,
4563
- orientation: request.orientation,
4564
- caseSensitive,
4565
- textMethod,
4566
- hasHeader: Boolean(request.hasHeader),
4567
- keys,
4568
- ...scope ? {
4569
- scope
4570
- } : {}
4571
- }
4572
- };
4573
- }
4574
- function spreadsheetSortAppearanceRowsMatch(rows, appearances) {
4575
- return rows.length === appearances.length && rows.every((row, index)=>row.length === (appearances[index]?.length ?? -1));
4576
- }
4577
- function spreadsheetSortError(code) {
4578
- const messages = {
4579
- 'column-out-of-range': '排序列必须位于当前选定区域内。',
4580
- 'duplicate-key': '同一行或列不能重复使用相同的值或外观排序条件。',
4581
- 'formula-reference-out-of-range': '排序会使相对公式引用超出工作表范围,因此未应用任何更改。',
4582
- 'invalid-appearance': '外观排序条件或当前颜色/图标快照无效,因此未应用任何更改。',
4583
- 'invalid-case-sensitivity': '请选择有效的大小写比较规则。',
4584
- 'invalid-custom-list': '自定义排序序列无效,请检查项目数量、长度和重复项。',
4585
- 'invalid-direction': '请选择有效的升序或降序次序。',
4586
- 'invalid-header': '按行排序会移动所有选定列,不能保留标题列。',
4587
- 'invalid-matrix': '排序只能应用到已完整读取的矩形区域。',
4588
- 'invalid-orientation': '请选择有效的按列或按行排序方向。',
4589
- 'invalid-range': '请选择一个有效的连续单元格区域。',
4590
- 'invalid-scope': '排序区域的表格或筛选所有权已变化,请重新打开排序。',
4591
- 'invalid-text-method': '请选择拼音排序或笔画排序。',
4592
- 'missing-key': '请至少添加一个排序条件。',
4593
- 'not-enough-columns': '当前区域没有足够的数据列可供按行排序。',
4594
- 'not-enough-rows': '当前区域没有足够的数据行可供排序。',
4595
- 'range-too-large': `一次最多可排序 ${MAX_SPREADSHEET_SORT_CELLS.toLocaleString('en-US')} 个单元格。`,
4596
- 'row-out-of-range': '排序行必须位于当前选定区域内。',
4597
- 'too-many-keys': `一次最多可设置 ${MAX_SPREADSHEET_SORT_KEYS} 个排序条件。`,
4598
- 'unsupported-text-method': '当前运行环境不支持所选中文排序方法,因此未应用任何更改。',
4599
- 'unsupported-linked-cell': '当前区域包含坐标关联的超链接,尚不能安全地随排序移动。'
4600
- };
4601
- return {
4602
- ok: false,
4603
- code,
4604
- message: messages[code]
4605
- };
4606
- }
4607
- function normalizeSpreadsheetSortOwnedScope(value) {
4608
- if (void 0 === value) return null;
4609
- if (!value || 'object' != typeof value || Array.isArray(value)) return null;
4610
- if ('auto-filter' === value.kind && true === value.hasHeader && Object.keys(value).every((key)=>'kind' === key || 'hasHeader' === key)) return {
4611
- kind: 'auto-filter',
4612
- hasHeader: true
4613
- };
4614
- if ('table' === value.kind && 'string' == typeof value.tableId && Boolean(value.tableId.trim()) && 'boolean' == typeof value.hasHeader && Object.keys(value).every((key)=>'kind' === key || 'tableId' === key || 'hasHeader' === key)) return {
4615
- kind: 'table',
4616
- tableId: value.tableId,
4617
- hasHeader: value.hasHeader
4618
- };
4619
- return null;
4620
- }
4621
- function spreadsheetSortRangeError(range) {
4622
- const area = spreadsheet_cell_range_spreadsheetCellRangeArea(range);
4623
- if (!Number.isSafeInteger(area) || area > MAX_SPREADSHEET_SORT_CELLS) return spreadsheetSortError('range-too-large');
4624
- return null;
4625
- }
4626
- function spreadsheetSortRowsHaveHeader(rows) {
4627
- const first = rows[0];
4628
- if (!first?.length) return false;
4629
- const labels = first.map(spreadsheetSortHeaderText);
4630
- return labels.every(Boolean) && new Set(labels.map((label)=>label.toLocaleLowerCase())).size === labels.length;
4631
- }
4632
- function appendSpreadsheetSortKey(keys, key) {
4633
- const identity = spreadsheetSortKeyIdentity(key);
4634
- if (keys.some((candidate)=>spreadsheetSortKeyIdentity(candidate) === identity)) return false;
4635
- keys.push(key);
4636
- return true;
4637
- }
4638
- function spreadsheetSortKeyIdentity(key) {
4639
- if ('cell-color' === key.sortOn || 'font-color' === key.sortOn) return `${key.index}:${spreadsheetSortAppearanceTargetValue({
4640
- kind: key.sortOn,
4641
- color: key.color
4642
- })}`;
4643
- if ('icon' === key.sortOn) return `${key.index}:${spreadsheetSortAppearanceTargetValue({
4644
- kind: 'icon',
4645
- icon: key.icon
4646
- })}`;
4647
- return `${key.index}:values`;
4648
- }
4649
- function spreadsheetSortActiveIndex(active, range) {
4650
- return active >= range[0] && active <= range[1] ? active : range[0];
4651
- }
4652
- function spreadsheetSortHeaderText(cell) {
4653
- if (cell?.f || 'string' != typeof cell?.v) return '';
4654
- const value = cell.v;
4655
- return Array.from(value.trim()).slice(0, MAX_SPREADSHEET_SORT_HEADER_LENGTH).join('');
4656
- }
4657
- function spreadsheetSortColumnLabel(column) {
4658
- return formatSpreadsheetCellRanges([
4659
- {
4660
- row: [
4661
- 0,
4662
- 0
4663
- ],
4664
- column: [
4665
- column,
4666
- column
4667
- ]
4668
- }
4669
- ]).replace(/\d+$/, '');
4670
- }
4671
- function reconcileSpreadsheetFiltersAfterFortune(sheets, sourceSheets, operations = [], context = {
4672
- now: new Date()
4673
- }) {
4674
- const coordinates = spreadsheetCellOperationCoordinates(operations);
4675
- return sheets.map((sheet, index)=>{
4676
- const source = spreadsheet_filter_reconciliation_sourceSheet(sheet, sourceSheets, index);
4677
- if (!source || !spreadsheetSheetHasCriteria(source) || !spreadsheetFilterReconciliationIsBounded(source) || !spreadsheetFilterRangesWereEdited(source, operations, coordinates)) return sheet;
4678
- const previousTableOwnedRows = spreadsheetTableFilterOwnedRows(source, context);
4679
- const manuallyHiddenRows = workSpreadsheetAutoFilterManuallyHiddenRows(source);
4680
- for (const row of previousTableOwnedRows)manuallyHiddenRows.delete(row);
4681
- let next = sheet;
4682
- if (normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select) && workSpreadsheetAutoFilterCriteriaEntries(source).length) next = workSpreadsheetSheetWithReappliedAutoFilterCriteria(sheet, source, manuallyHiddenRows, context);
4683
- const hiddenRows = new Set(manuallyHiddenRows);
4684
- for (const row of workSpreadsheetAutoFilterOwnedRows(next))hiddenRows.add(row);
4685
- for (const row of spreadsheetTableFilterOwnedRows(next, context))hiddenRows.add(row);
4686
- const rowhidden = Object.fromEntries([
4687
- ...hiddenRows
4688
- ].sort(compareSpreadsheetRowKeys).map((row)=>[
4689
- row,
4690
- 0
4691
- ]));
4692
- return {
4693
- ...next,
4694
- config: {
4695
- ...next.config ?? {},
4696
- rowhidden
4697
- }
4698
- };
4699
- });
4700
- }
4701
- function reconcileSpreadsheetFiltersAfterSort(sheet, source, range, sourceIndexes, context = {
4702
- now: new Date()
4703
- }) {
4704
- if (!spreadsheetFilterReconciliationIsBounded(source) || !spreadsheetSortSourceIndexesAreValid(range, sourceIndexes)) return null;
4705
- const reconciled = reconcileSpreadsheetFiltersAfterFortune([
4706
- sheet
4707
- ], [
4708
- source
4709
- ], [], context)[0];
4710
- if (!reconciled) return null;
4711
- let filter = reconciled.filter;
4712
- const autoFilter = normalizedWorkSpreadsheetAutoFilterRange(source.filter_select);
4713
- if (autoFilter && autoFilter.row[0] === range.row[0] && autoFilter.row[1] === range.row[1] && autoFilter.column[0] === range.column[0] && autoFilter.column[1] === range.column[1]) filter = spreadsheetFilterWithRemappedOpaqueEntries(reconciled, source, range, sourceIndexes);
4714
- const manuallyHiddenRows = workSpreadsheetAutoFilterManuallyHiddenRows(source);
4715
- for (const row of spreadsheetTableFilterOwnedRows(source, context))manuallyHiddenRows.delete(row);
4716
- const withFilter = {
4717
- ...reconciled,
4718
- filter
4719
- };
4720
- const hiddenRows = new Set(manuallyHiddenRows);
4721
- for (const row of workSpreadsheetAutoFilterOwnedRows(withFilter))hiddenRows.add(row);
4722
- for (const row of spreadsheetTableFilterOwnedRows(withFilter, context))hiddenRows.add(row);
4723
- const rowhidden = Object.fromEntries([
4724
- ...hiddenRows
4725
- ].sort(compareSpreadsheetRowKeys).map((row)=>[
4726
- row,
4727
- 0
4728
- ]));
4729
- return {
4730
- ...sheet,
4731
- filter,
4732
- config: {
4733
- ...sheet.config ?? {},
4734
- rowhidden
4735
- }
4736
- };
4737
- }
4738
- function spreadsheetFilterReconciliationIsBounded(sheet) {
4739
- let cellVisits = 0;
4740
- const autoFilter = normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select);
4741
- if (autoFilter) {
4742
- const filterCount = Object.keys(sheet.filter ?? {}).length;
4743
- cellVisits += (autoFilter.row[1] - autoFilter.row[0]) * Math.max(filterCount, workSpreadsheetAutoFilterCriteriaEntries(sheet).length);
4744
- }
4745
- for (const table of sheet.tables ?? []){
4746
- const range = spreadsheetTableFilterRange(table);
4747
- if (range) {
4748
- cellVisits += (range.row[1] - range.row[0]) * table.filters.length;
4749
- if (!Number.isSafeInteger(cellVisits) || cellVisits > MAX_SPREADSHEET_SORT_CELLS) return false;
4750
- }
4751
- }
4752
- return Number.isSafeInteger(cellVisits) && cellVisits <= MAX_SPREADSHEET_SORT_CELLS;
4753
- }
4754
- function spreadsheetFilterWithRemappedOpaqueEntries(reconciled, source, range, sourceIndexes) {
4755
- const next = {
4756
- ...reconciled.filter ?? {}
4757
- };
4758
- const typedColumns = new Set(workSpreadsheetAutoFilterCriteriaEntries(source).map((entry)=>String(entry.column)));
4759
- for (const [column, entry] of Object.entries(source.filter ?? {}))if (!typedColumns.has(column) && spreadsheet_filter_reconciliation_isRecord(entry)) next[column] = {
4760
- ...entry,
4761
- rowhidden: spreadsheetRemappedFilterRows(entry.rowhidden, range, sourceIndexes)
4762
- };
4763
- return next;
4764
- }
4765
- function spreadsheetRemappedFilterRows(value, range, sourceIndexes) {
4766
- const sourceRows = spreadsheet_filter_reconciliation_isRecord(value) ? value : {};
4767
- const rowhidden = {};
4768
- const dataStart = range.row[0] + 1;
4769
- sourceIndexes.forEach((sourceIndex, targetIndex)=>{
4770
- if (Object.hasOwn(sourceRows, String(dataStart + sourceIndex))) rowhidden[String(dataStart + targetIndex)] = 0;
4771
- });
4772
- return rowhidden;
4773
- }
4774
- function spreadsheetSortSourceIndexesAreValid(range, sourceIndexes) {
4775
- const count = range.row[1] - range.row[0];
4776
- return sourceIndexes.length === count && sourceIndexes.every((index)=>Number.isSafeInteger(index) && index >= 0 && index < count) && new Set(sourceIndexes).size === count;
4777
- }
4778
- function spreadsheetSheetHasCriteria(sheet) {
4779
- return Boolean(workSpreadsheetAutoFilterCriteriaEntries(sheet).length || sheet.tables?.some((table)=>table.filters.length));
4780
- }
4781
- function spreadsheetFilterRangesWereEdited(sheet, operations, coordinates) {
4782
- if (!operations.length) return true;
4783
- if (!sheet.id) return false;
4784
- const sheetCoordinates = coordinates?.get(sheet.id);
4785
- if (sheetCoordinates) {
4786
- const ranges = spreadsheetFilterRanges(sheet);
4787
- return [
4788
- ...sheetCoordinates.values()
4789
- ].some(({ column, row })=>ranges.some((range)=>row >= range.row[0] && row <= range.row[1] && column >= range.column[0] && column <= range.column[1]));
4790
- }
4791
- return operations.some((operation)=>operation.id === sheet.id);
4792
- }
4793
- function spreadsheetFilterRanges(sheet) {
4794
- const ranges = [];
4795
- const autoFilter = normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select);
4796
- if (autoFilter && workSpreadsheetAutoFilterCriteriaEntries(sheet).length) ranges.push(autoFilter);
4797
- for (const table of sheet.tables ?? []){
4798
- if (!table.filters.length) continue;
4799
- const range = spreadsheetTableFilterRange(table);
4800
- if (range) ranges.push(range);
4801
- }
4802
- return ranges;
4803
- }
4804
- function spreadsheetTableFilterOwnedRows(sheet, context) {
4805
- const hidden = new Set();
4806
- for (const table of sheet.tables ?? []){
4807
- const range = spreadsheetTableFilterRange(table);
4808
- if (range) for (const filter of table.filters){
4809
- const column = range.column[0] + filter.column;
4810
- const rowhidden = workSpreadsheetFilterHiddenRows(sheet, range, column, filter.criteria, context);
4811
- for (const row of Object.keys(rowhidden ?? {}))hidden.add(row);
4812
- }
4813
- }
4814
- return hidden;
4815
- }
4816
- function spreadsheetTableFilterRange(table) {
4817
- if (!table.headerRow || !table.filters.length) return null;
4818
- const rowStart = spreadsheet_filter_reconciliation_finiteIndex(table.range.row[0]);
4819
- const rowEnd = spreadsheet_filter_reconciliation_finiteIndex(table.range.row[1]);
4820
- const columnStart = spreadsheet_filter_reconciliation_finiteIndex(table.range.column[0]);
4821
- const columnEnd = spreadsheet_filter_reconciliation_finiteIndex(table.range.column[1]);
4822
- if (null === rowStart || null === rowEnd || null === columnStart || null === columnEnd) return null;
4823
- const end = Math.max(rowStart, rowEnd) - Number(table.totalsRow);
4824
- const start = Math.min(rowStart, rowEnd);
4825
- if (end <= start) return null;
4826
- return {
4827
- row: [
4828
- start,
4829
- end
4830
- ],
4831
- column: [
4832
- Math.min(columnStart, columnEnd),
4833
- Math.max(columnStart, columnEnd)
4834
- ]
4835
- };
4836
- }
4837
- function spreadsheet_filter_reconciliation_sourceSheet(sheet, sources, index) {
4838
- return sources.find((candidate)=>candidate.id === sheet.id) ?? sources[index];
4839
- }
4840
- function spreadsheet_filter_reconciliation_finiteIndex(value) {
4841
- return 'number' == typeof value && Number.isSafeInteger(value) && value >= 0 ? value : null;
4842
- }
4843
- function compareSpreadsheetRowKeys(left, right) {
4844
- return Number(left) - Number(right);
4845
- }
4846
- function spreadsheet_filter_reconciliation_isRecord(value) {
4847
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
4848
- }
4849
- function canApplySpreadsheetTableStructureChange(sheet, change) {
4850
- if (!sheet || !validSpreadsheetTableStructureChange(change)) return false;
4851
- for (const table of sheet.tables ?? []){
4852
- if ('delete' === change.kind && 'row' === change.axis && spreadsheetTableDeleteRemovesSemanticRow(table, change.start, change.end)) return false;
4853
- if (!transformSpreadsheetTableStructure(table, change)) return false;
4854
- }
4855
- return true;
4856
- }
4857
- function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations = []) {
4858
- return sheets.map((sheet, index)=>{
4859
- const source = sourceSheetForReconciliation(sheet, sourceSheets, index);
4860
- if (!source?.tables?.length) return sheet;
4861
- const sheetOperations = operations.filter((operation)=>operation.id === source.id);
4862
- let tables = source.tables;
4863
- const reconcileColumns = new Set();
4864
- const calculatedColumnRows = new Map();
4865
- const editedTotalsColumns = new Map();
4866
- if (!operations.length) for (const table of tables)reconcileColumns.add(table.id);
4867
- for (const operation of sheetOperations){
4868
- const structure = spreadsheetTableStructureChangeFromOperation(operation);
4869
- if (structure) {
4870
- transformCalculatedColumnRows(calculatedColumnRows, structure);
4871
- const transformed = [];
4872
- for (const table of tables){
4873
- if ('row' === structure.axis && 'insert' === structure.kind) {
4874
- const insertedRows = rowsInsertedIntoSpreadsheetTableBody(table, structure);
4875
- if (insertedRows.length) {
4876
- const rows = calculatedColumnRows.get(table.id) ?? new Set();
4877
- for (const row of insertedRows)rows.add(row);
4878
- calculatedColumnRows.set(table.id, rows);
4879
- }
4880
- }
4881
- const next = transformSpreadsheetTableStructure(table, structure);
4882
- if (next) {
4883
- transformed.push(next);
4884
- if (next !== table) reconcileColumns.add(table.id);
4885
- }
4886
- }
4887
- tables = transformed;
4888
- continue;
4889
- }
4890
- const coordinate = spreadsheetCellCoordinateFromOperation(operation);
4891
- if (!coordinate) continue;
4892
- const table = tables.find((candidate)=>spreadsheetTableContainsCell(candidate, coordinate.row, coordinate.column));
4893
- if (table) {
4894
- reconcileColumns.add(table.id);
4895
- if (table.totalsRow && coordinate.row === table.range.row[1]) {
4896
- const offset = coordinate.column - table.range.column[0];
4897
- if (offset >= 0 && offset < table.columns.length) {
4898
- const offsets = editedTotalsColumns.get(table.id) ?? new Set();
4899
- offsets.add(offset);
4900
- editedTotalsColumns.set(table.id, offsets);
4901
- }
4902
- }
4903
- }
4904
- }
4905
- const updates = [];
4906
- const readCell = spreadsheet_table_reconciliation_spreadsheetSheetCellReader(sheet);
4907
- tables = tables.map((table)=>{
4908
- if (!reconcileColumns.has(table.id)) return table;
4909
- const columns = canonicalSpreadsheetTableColumns(table, readCell);
4910
- const calculatedColumns = reconcileSpreadsheetTableCalculatedColumns(sheet, {
4911
- ...table,
4912
- columns
4913
- });
4914
- const reconciledColumns = reconcileSpreadsheetTableTotalsColumns(sheet, {
4915
- ...table,
4916
- columns: calculatedColumns
4917
- }, editedTotalsColumns.get(table.id));
4918
- if (table.headerRow) for (const [offset, column] of reconciledColumns.entries()){
4919
- const cellColumn = table.range.column[0] + offset;
4920
- if (spreadsheetCellText(readCell(table.range.row[0], cellColumn)) !== column.name) updates.push({
4921
- column: cellColumn,
4922
- name: column.name,
4923
- row: table.range.row[0]
4924
- });
4925
- }
4926
- return sameSpreadsheetTableColumns(table.columns, reconciledColumns) ? table : {
4927
- ...table,
4928
- columns: reconciledColumns
4929
- };
4930
- });
4931
- let withCalculatedColumns = sheet;
4932
- for (const table of tables){
4933
- const rows = calculatedColumnRows.get(table.id);
4934
- if (rows?.size) withCalculatedColumns = fillSpreadsheetTableCalculatedColumns(withCalculatedColumns, table, [
4935
- ...rows
4936
- ]);
4937
- }
4938
- const withHeaders = stampSpreadsheetTableHeaderCells(withCalculatedColumns, updates);
4939
- let withTotals = withHeaders;
4940
- for (const table of tables){
4941
- const previous = source.tables.find((candidate)=>candidate.id === table.id);
4942
- withTotals = synchronizeSpreadsheetTableTotalsRow(withTotals, previous, table);
4943
- }
4944
- return {
4945
- ...withTotals,
4946
- tables: tables.length ? tables : void 0
4947
- };
4948
- });
4949
- }
4950
- function transformSpreadsheetTableStructure(table, change) {
4951
- if (!validSpreadsheetTableStructureChange(change)) return null;
4952
- const axis = 'row' === change.axis ? table.range.row : table.range.column;
4953
- const transformed = 'insert' === change.kind ? transformSpreadsheetTableAxisForInsertion(axis, change) : transformSpreadsheetTableAxisForDeletion(axis, change);
4954
- if (!transformed) return null;
4955
- const range = {
4956
- row: 'row' === change.axis ? transformed.axis : [
4957
- ...table.range.row
4958
- ],
4959
- column: 'column' === change.axis ? transformed.axis : [
4960
- ...table.range.column
4961
- ]
4962
- };
4963
- const height = range.row[1] - range.row[0] + 1;
4964
- if (height <= Number(table.headerRow) + Number(table.totalsRow)) return null;
4965
- if ('column' !== change.axis || !transformed.inside) {
4966
- if (range.row[0] === table.range.row[0] && range.row[1] === table.range.row[1] && range.column[0] === table.range.column[0] && range.column[1] === table.range.column[1]) return table;
4967
- return {
4968
- ...table,
4969
- range
4970
- };
4971
- }
4972
- if ('insert' === change.kind) {
4973
- const columns = [
4974
- ...table.columns
4975
- ];
4976
- columns.splice(transformed.offset, 0, ...Array.from({
4977
- length: change.count
4978
- }, ()=>({
4979
- name: ''
4980
- })));
4981
- return {
4982
- ...table,
4983
- range,
4984
- columns,
4985
- filters: table.filters.map((filter)=>filter.column >= transformed.offset ? {
4986
- ...filter,
4987
- column: filter.column + change.count
4988
- } : filter)
4989
- };
4990
- }
4991
- if (!('removed' in transformed)) return null;
4992
- const removed = transformed.removed;
4993
- if ('number' != typeof removed) return null;
4994
- const columns = [
4995
- ...table.columns
4996
- ];
4997
- columns.splice(transformed.offset, removed);
4998
- if (!columns.length) return null;
4999
- return {
5000
- ...table,
5001
- range,
5002
- columns,
5003
- filters: filtersAfterSpreadsheetTableColumnDeletion(table.filters, transformed.offset, removed)
5004
- };
5005
- }
5006
- function spreadsheetTableContainsCell(table, row, column) {
5007
- return row >= table.range.row[0] && row <= table.range.row[1] && column >= table.range.column[0] && column <= table.range.column[1];
5008
- }
5009
- function rowsInsertedIntoSpreadsheetTableBody(table, change) {
5010
- if ('row' !== change.axis) return [];
5011
- const transformed = transformSpreadsheetTableAxisForInsertion(table.range.row, change);
5012
- if (!transformed.inside) return [];
5013
- const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
5014
- const bodyStart = table.range.row[0] + Number(table.headerRow);
5015
- if (insertion < bodyStart) return [];
5016
- return Array.from({
5017
- length: change.count
5018
- }, (_, offset)=>insertion + offset);
5019
- }
5020
- function transformCalculatedColumnRows(rowsByTable, change) {
5021
- if ('row' !== change.axis || !rowsByTable.size) return;
5022
- if ('insert' === change.kind) {
5023
- const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
5024
- for (const [tableId, rows] of rowsByTable){
5025
- const next = new Set();
5026
- for (const row of rows)next.add(row >= insertion ? row + change.count : row);
5027
- rowsByTable.set(tableId, next);
5028
- }
5029
- return;
5030
- }
5031
- const count = change.end - change.start + 1;
5032
- for (const [tableId, rows] of rowsByTable){
5033
- const next = new Set();
5034
- for (const row of rows)if (row < change.start) next.add(row);
5035
- else if (row > change.end) next.add(row - count);
5036
- rowsByTable.set(tableId, next);
5037
- }
5038
- }
5039
- function transformSpreadsheetTableAxisForInsertion(axis, change) {
5040
- const start = axis[0] ?? 0;
5041
- const end = axis[1] ?? start;
5042
- const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
5043
- if (insertion <= start) return {
5044
- axis: [
5045
- start + change.count,
5046
- end + change.count
5047
- ],
5048
- inside: false,
5049
- offset: 0
5050
- };
5051
- if (insertion <= end) return {
5052
- axis: [
5053
- start,
5054
- end + change.count
5055
- ],
5056
- inside: true,
5057
- offset: insertion - start
5058
- };
5059
- return {
5060
- axis: [
5061
- start,
5062
- end
5063
- ],
5064
- inside: false,
5065
- offset: 0
5066
- };
5067
- }
5068
- function transformSpreadsheetTableAxisForDeletion(axis, change) {
5069
- const start = axis[0] ?? 0;
5070
- const end = axis[1] ?? start;
5071
- const count = change.end - change.start + 1;
5072
- if (change.end < start) return {
5073
- axis: [
5074
- start - count,
5075
- end - count
5076
- ],
5077
- inside: false,
5078
- offset: 0,
5079
- removed: 0
5080
- };
5081
- if (change.start > end) return {
5082
- axis: [
5083
- start,
5084
- end
5085
- ],
5086
- inside: false,
5087
- offset: 0,
5088
- removed: 0
5089
- };
5090
- const overlapStart = Math.max(start, change.start);
5091
- const overlapEnd = Math.min(end, change.end);
5092
- const removed = overlapEnd - overlapStart + 1;
5093
- const remaining = end - start + 1 - removed;
5094
- if (remaining <= 0) return null;
5095
- const deletedBeforeStart = Math.max(0, Math.min(change.end, start - 1) - change.start + 1);
5096
- const nextStart = start - deletedBeforeStart;
5097
- return {
5098
- axis: [
5099
- nextStart,
5100
- nextStart + remaining - 1
5101
- ],
5102
- inside: true,
5103
- offset: overlapStart - start,
5104
- removed
5105
- };
5106
- }
5107
- function filtersAfterSpreadsheetTableColumnDeletion(filters, offset, removed) {
5108
- const end = offset + removed - 1;
5109
- return filters.flatMap((filter)=>{
5110
- if (filter.column >= offset && filter.column <= end) return [];
5111
- return [
5112
- filter.column > end ? {
5113
- ...filter,
5114
- column: filter.column - removed
5115
- } : filter
5116
- ];
5117
- });
5118
- }
5119
- function canonicalSpreadsheetTableColumns(table, readCell) {
5120
- const width = table.range.column[1] - table.range.column[0] + 1;
5121
- const observed = new Set();
5122
- return Array.from({
5123
- length: width
5124
- }, (_, offset)=>{
5125
- const raw = table.headerRow ? spreadsheetCellText(readCell(table.range.row[0], table.range.column[0] + offset)).trim() : table.columns[offset]?.name.trim() ?? '';
5126
- const base = validSpreadsheetTableColumnName(raw) ? raw : `Column${offset + 1}`;
5127
- let candidate = base;
5128
- let suffix = 2;
5129
- while(observed.has(candidate.toLocaleLowerCase())){
5130
- const suffixText = String(suffix);
5131
- candidate = `${truncateSpreadsheetTableColumnName(base, 255 - suffixText.length)}${suffixText}`;
5132
- suffix += 1;
5133
- }
5134
- observed.add(candidate.toLocaleLowerCase());
5135
- return {
5136
- ...table.columns[offset] ?? {},
5137
- name: candidate
5138
- };
5139
- });
5140
- }
5141
- function spreadsheetTableDeleteRemovesSemanticRow(table, start, end) {
5142
- return Boolean(table.headerRow && start <= table.range.row[0] && end >= table.range.row[0] || table.totalsRow && start <= table.range.row[1] && end >= table.range.row[1]);
5143
- }
5144
- function spreadsheetTableStructureChangeFromOperation(operation) {
5145
- if (!spreadsheet_table_reconciliation_isRecord(operation.value)) return null;
5146
- if ('insertRowCol' === operation.op) {
5147
- const { count, direction, index, type } = operation.value;
5148
- const change = {
5149
- axis: 'column' === type ? 'column' : 'row',
5150
- count: Number(count),
5151
- direction: 'rightbottom' === direction ? 'rightbottom' : 'lefttop',
5152
- index: Number(index),
5153
- kind: 'insert'
5154
- };
5155
- return ('column' === type || 'row' === type) && ('lefttop' === direction || 'rightbottom' === direction) && validSpreadsheetTableStructureChange(change) ? change : null;
5156
- }
5157
- if ('deleteRowCol' === operation.op) {
5158
- const { end, start, type } = operation.value;
5159
- const change = {
5160
- axis: 'column' === type ? 'column' : 'row',
5161
- end: Number(end),
5162
- kind: 'delete',
5163
- start: Number(start)
5164
- };
5165
- return ('column' === type || 'row' === type) && validSpreadsheetTableStructureChange(change) ? change : null;
5166
- }
5167
- return null;
5168
- }
5169
- function spreadsheetCellCoordinateFromOperation(operation) {
5170
- if ('data' !== operation.path[0] || operation.path.length < 3) return null;
5171
- const row = operation.path[1];
5172
- const column = operation.path[2];
5173
- return Number.isSafeInteger(row) && Number(row) >= 0 && Number.isSafeInteger(column) && Number(column) >= 0 ? {
5174
- column: Number(column),
5175
- row: Number(row)
5176
- } : null;
5177
- }
5178
- function validSpreadsheetTableStructureChange(change) {
5179
- if ('insert' === change.kind) return Number.isSafeInteger(change.index) && change.index >= 0 && Number.isSafeInteger(change.count) && change.count > 0;
5180
- return Number.isSafeInteger(change.start) && change.start >= 0 && Number.isSafeInteger(change.end) && change.end >= change.start;
5181
- }
5182
- function sourceSheetForReconciliation(sheet, sourceSheets, index) {
5183
- return (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
5184
- }
5185
- function spreadsheet_table_reconciliation_spreadsheetSheetCellReader(sheet) {
5186
- if (void 0 !== sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
5187
- const cells = new Map((sheet.celldata ?? []).map((entry)=>[
5188
- `${entry.r}:${entry.c}`,
5189
- entry.v
5190
- ]));
5191
- return (row, column)=>cells.get(`${row}:${column}`) ?? null;
5192
- }
5193
- function stampSpreadsheetTableHeaderCells(sheet, updates) {
5194
- if (!updates.length) return sheet;
5195
- if (void 0 !== sheet.data) {
5196
- const data = [];
5197
- data.length = sheet.data.length;
5198
- for (const [row, value] of sparseArrayEntries(sheet.data))data[row] = value;
5199
- const mutableRows = new Map();
5200
- for (const update of updates){
5201
- let row = mutableRows.get(update.row);
5202
- if (!row) {
5203
- const source = data[update.row];
5204
- row = [];
5205
- row.length = source?.length ?? 0;
5206
- for (const column of sparseArrayIndexes(source))row[column] = source?.[column];
5207
- mutableRows.set(update.row, row);
5208
- data[update.row] = row;
5209
- }
5210
- row[update.column] = stampSpreadsheetTableHeaderCell(row[update.column], update.name);
5211
- }
5212
- return {
5213
- ...sheet,
5214
- data
5215
- };
5216
- }
5217
- const celldata = [
5218
- ...sheet.celldata ?? []
5219
- ];
5220
- const indexes = new Map(celldata.map((entry, index)=>[
5221
- `${entry.r}:${entry.c}`,
5222
- index
5223
- ]));
5224
- for (const update of updates){
5225
- const key = `${update.row}:${update.column}`;
5226
- const index = indexes.get(key);
5227
- const source = void 0 === index ? void 0 : celldata[index];
5228
- const entry = {
5229
- r: update.row,
5230
- c: update.column,
5231
- v: stampSpreadsheetTableHeaderCell(source?.v, update.name)
5232
- };
5233
- if (void 0 === index) {
5234
- indexes.set(key, celldata.length);
5235
- celldata.push(entry);
5236
- } else celldata[index] = entry;
5237
- }
5238
- return {
5239
- ...sheet,
5240
- celldata
5241
- };
5242
- }
5243
- function stampSpreadsheetTableHeaderCell(cell, name) {
5244
- return {
5245
- ...cell ?? {},
5246
- m: name,
5247
- v: name
5248
- };
5249
- }
5250
- function spreadsheetCellText(cell) {
5251
- const value = cell?.m ?? cell?.v;
5252
- return null == value ? '' : String(value);
5253
- }
5254
- function validSpreadsheetTableColumnName(name) {
5255
- const characters = Array.from(name);
5256
- return name.trim() === name && characters.length >= 1 && characters.length <= 255 && !characters.some((character)=>/\p{Cc}/u.test(character) || '\uFFFE' === character || '\uFFFF' === character);
5257
- }
5258
- function truncateSpreadsheetTableColumnName(value, maximum) {
5259
- return Array.from(value).slice(0, maximum).join('');
5260
- }
5261
- function sameSpreadsheetTableColumns(left, right) {
5262
- return left.length === right.length && left.every((column, index)=>column.name === right[index]?.name && column.calculatedFormula === right[index]?.calculatedFormula && column.totalsFunction === right[index]?.totalsFunction && column.totalsLabel === right[index]?.totalsLabel && column.totalsFormula === right[index]?.totalsFormula);
5263
- }
5264
- function spreadsheet_table_reconciliation_isRecord(value) {
5265
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
5266
- }
5267
- function spreadsheetSelectionReference(selection) {
5268
- const rowStart = Math.min(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
5269
- const rowEnd = Math.max(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
5270
- const columnStart = Math.min(selection.column[0] ?? 0, selection.column[1] ?? selection.column[0] ?? 0);
5271
- const columnEnd = Math.max(selection.column[0] ?? 0, selection.column[1] ?? selection.column[0] ?? 0);
5272
- const start = `${spreadsheetColumnLabel(columnStart)}${rowStart + 1}`;
5273
- const end = `${spreadsheetColumnLabel(columnEnd)}${rowEnd + 1}`;
5274
- return start === end ? start : `${start}:${end}`;
5275
- }
5276
- function spreadsheetSingleRange(selection) {
5277
- return {
5278
- row: finiteSpreadsheetSelectionAxis(selection.row),
5279
- column: finiteSpreadsheetSelectionAxis(selection.column)
5280
- };
5281
- }
5282
- function spreadsheet_editor_support_spreadsheetCellAt(sheet, row, column) {
5283
- if (!sheet) return null;
5284
- const safeRow = finiteSpreadsheetIndex(row, 0);
5285
- const safeColumn = finiteSpreadsheetIndex(column, 0);
5286
- return sheet.data?.[safeRow]?.[safeColumn] ?? sheet.celldata?.find((entry)=>entry.r === safeRow && entry.c === safeColumn)?.v ?? null;
5287
- }
5288
- function spreadsheetSelectionSummary(sheet, selection) {
5289
- const summary = {
5290
- average: null,
5291
- nonEmptyCount: 0,
5292
- numericCount: 0,
5293
- sum: null
5294
- };
5295
- if (!sheet) return summary;
5296
- const range = spreadsheetSingleRange(selection);
5297
- let sum = 0;
5298
- const collect = (cell)=>{
5299
- if (!cell || !spreadsheet_editor_support_spreadsheetCellHasContent(cell)) return;
5300
- summary.nonEmptyCount += 1;
5301
- if ('number' != typeof cell.v || !Number.isFinite(cell.v)) return;
5302
- summary.numericCount += 1;
5303
- sum += cell.v;
5304
- };
5305
- if (sheet.data?.length) {
5306
- const rowStart = range.row[0] ?? 0;
5307
- const rowEnd = Math.min(range.row[1] ?? rowStart, sheet.data.length - 1);
5308
- const columnStart = range.column[0] ?? 0;
5309
- const columnEnd = range.column[1] ?? columnStart;
5310
- for (const rowIndex of sparseArrayIndexes(sheet.data)){
5311
- if (rowIndex < rowStart || rowIndex > rowEnd) continue;
5312
- const row = sheet.data[rowIndex];
5313
- if (row) {
5314
- for (const columnIndex of sparseArrayIndexes(row))if (!(columnIndex < columnStart) && !(columnIndex > columnEnd)) collect(row[columnIndex]);
5315
- }
5316
- }
5317
- } else {
5318
- const rowStart = range.row[0] ?? 0;
5319
- const rowEnd = range.row[1] ?? rowStart;
5320
- const columnStart = range.column[0] ?? 0;
5321
- const columnEnd = range.column[1] ?? columnStart;
5322
- for (const entry of sheet.celldata ?? [])if (!(entry.r < rowStart) && !(entry.r > rowEnd) && !(entry.c < columnStart) && !(entry.c > columnEnd)) collect(entry.v);
5323
- }
5324
- if (summary.numericCount > 0) {
5325
- summary.sum = sum;
5326
- summary.average = sum / summary.numericCount;
5327
- }
5328
- return summary;
5329
- }
5330
- function spreadsheetFontSizeOptions(current) {
5331
- const values = [
5332
- ...spreadsheetFontSizes
5333
- ];
5334
- if (current && !values.includes(current)) values.push(current);
5335
- return values.sort((left, right)=>left - right).map((value)=>({
5336
- value: String(value),
5337
- label: String(value)
5338
- }));
5339
- }
5340
- function spreadsheetFontFamilyOptions(current) {
5341
- const options = officeFontFamilies.map(({ cssFamily, group, label, name })=>({
5342
- value: name,
5343
- group,
5344
- label,
5345
- previewStyle: {
5346
- fontFamily: cssFamily
5347
- },
5348
- searchText: `${name} ${label}`
5349
- }));
5350
- const normalizedCurrent = current?.trim();
5351
- if (normalizedCurrent && !options.some(({ value })=>value === normalizedCurrent)) options.push({
5352
- value: normalizedCurrent,
5353
- group: '文档字体',
5354
- label: normalizedCurrent,
5355
- previewStyle: {
5356
- fontFamily: normalizedCurrent
5357
- },
5358
- searchText: normalizedCurrent
5359
- });
5360
- return options;
5361
- }
5362
- function spreadsheetSheetsWithFiniteSelections(sheets) {
5363
- return sheets.map((sheet)=>({
5364
- ...sheet,
5365
- luckysheet_select_save: (sheet.luckysheet_select_save?.length ? sheet.luckysheet_select_save : [
5366
- void 0
5367
- ]).map(finiteSpreadsheetSelection)
5368
- }));
5369
- }
5370
- function spreadsheetSheetsForFortune(sheets) {
5371
- const startedAt = spreadsheetProjectionNow();
5372
- let populatedCellCount = 0;
5373
- const projectedSheets = sheets.map((sheet)=>{
5374
- const { celldata: sourceCellData, data, ...sourceMetadata } = sheet;
5375
- const projected = structuredClone(sourceMetadata);
5376
- const profile = spreadsheetMatrixProfile(data);
5377
- if (data && profile?.fortuneReady) {
5378
- populatedCellCount += profile.populatedCellCount;
5379
- return {
5380
- ...projected,
5381
- data
5382
- };
5383
- }
5384
- const fortuneProjection = spreadsheetMatrixForFortune({
5385
- ...projected,
5386
- ...void 0 === data ? {} : {
5387
- data
5388
- },
5389
- ...void 0 === sourceCellData ? {} : {
5390
- celldata: sourceCellData
5391
- }
5392
- }, projected.config?.merge);
5393
- populatedCellCount += fortuneProjection.populatedCellCount;
5394
- return {
5395
- ...projected,
5396
- data: fortuneProjection.data
5397
- };
5398
- });
5399
- recordSpreadsheetProjectionMeasure(startedAt, spreadsheetProjectionNow(), {
5400
- populatedCellCount,
5401
- sheetCount: sheets.length
5402
- });
5403
- return projectedSheets;
5404
- }
5405
- function spreadsheetMatrixForFortune(sheet, merges) {
5406
- const declaredRows = spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.row);
5407
- const declaredColumns = spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.column);
5408
- const hasDeclaredGrid = declaredRows > 0 && declaredColumns > 0;
5409
- let rowCount = hasDeclaredGrid ? declaredRows : 60;
5410
- let columnCount = hasDeclaredGrid ? declaredColumns : 26;
5411
- let populatedCellCount = 0;
5412
- const shownCommentCells = [];
5413
- const projected = [];
5414
- const setSourceCell = (rowIndex, columnIndex, cell)=>{
5415
- rowCount = Math.max(rowCount, rowIndex + 1);
5416
- columnCount = Math.max(columnCount, columnIndex + 1);
5417
- const row = projected[rowIndex] ?? [];
5418
- projected[rowIndex] = row;
5419
- row[columnIndex] = cloneSpreadsheetCellForFortune(cell);
5420
- if (cell.ps?.isShow) shownCommentCells.push({
5421
- c: columnIndex,
5422
- r: rowIndex
5423
- });
5424
- populatedCellCount += 1;
5425
- };
5426
- if (void 0 !== sheet.data) {
5427
- rowCount = Math.max(rowCount, sheet.data.length);
5428
- for (const rowIndex of sparseArrayIndexes(sheet.data)){
5429
- const sourceRow = sheet.data[rowIndex];
5430
- if (sourceRow) {
5431
- columnCount = Math.max(columnCount, sourceRow.length);
5432
- for (const columnIndex of sparseArrayIndexes(sourceRow)){
5433
- const cell = sourceRow[columnIndex];
5434
- if (null != cell) setSourceCell(rowIndex, columnIndex, cell);
5435
- }
5436
- }
5437
- }
5438
- } else for (const entry of sheet.celldata ?? [])if (null != entry.v) setSourceCell(entry.r, entry.c, entry.v);
5439
- for (const merge of Object.values(merges ?? {})){
5440
- rowCount = Math.max(rowCount, merge.r + merge.rs);
5441
- columnCount = Math.max(columnCount, merge.c + merge.cs);
5442
- for(let rowIndex = merge.r; rowIndex < merge.r + merge.rs; rowIndex += 1){
5443
- const row = projected[rowIndex] ?? [];
5444
- projected[rowIndex] = row;
5445
- for(let columnIndex = merge.c; columnIndex < merge.c + merge.cs; columnIndex += 1)row[columnIndex] = Object.freeze({
5446
- ...row[columnIndex] ?? {},
5447
- mc: rowIndex === merge.r && columnIndex === merge.c ? {
5448
- ...merge
5449
- } : {
5450
- r: merge.r,
5451
- c: merge.c
5452
- }
5453
- });
5454
- }
5455
- }
5456
- projected.length = rowCount;
5457
- projected[0] ??= [];
5458
- for (const rowIndex of sparseArrayIndexes(projected)){
5459
- const row = projected[rowIndex];
5460
- if (row) {
5461
- row.length = Math.max(row.length, columnCount);
5462
- Object.freeze(row);
5463
- }
5464
- }
5465
- attachSpreadsheetShownCommentCells(projected, Object.freeze(shownCommentCells.map((cell)=>Object.freeze(cell))));
5466
- Object.freeze(projected);
5467
- return {
5468
- data: projected,
5469
- populatedCellCount
5470
- };
5471
- }
5472
- function cloneSpreadsheetCellForFortune(cell) {
5473
- const clone = {
5474
- ...cell
5475
- };
5476
- if (cell.mc) clone.mc = {
5477
- ...cell.mc
5478
- };
5479
- if (cell.ct) clone.ct = {
5480
- ...cell.ct,
5481
- ...void 0 === cell.ct.s ? {} : {
5482
- s: structuredClone(cell.ct.s)
5483
- }
5484
- };
5485
- if (cell.ps) clone.ps = {
5486
- ...cell.ps
5487
- };
5488
- if (cell.hl) clone.hl = {
5489
- ...cell.hl
5490
- };
5491
- if (cell.spl && 'object' == typeof cell.spl) clone.spl = structuredClone(cell.spl);
5492
- Object.freeze(clone);
5493
- return clone;
5494
- }
5495
- function spreadsheetProjectionNow() {
5496
- return globalThis.performance?.now?.() ?? Date.now();
5497
- }
5498
- function recordSpreadsheetProjectionMeasure(start, end, detail) {
5499
- try {
5500
- globalThis.performance?.measure('a3s-office.spreadsheet.fortune-projection', {
5501
- detail,
5502
- end,
5503
- start
5504
- });
5505
- } catch {}
5506
- }
5507
- function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = [], dateSystem = '1900') {
5508
- const startedAt = spreadsheetProjectionNow();
5509
- const filterContext = {
5510
- dateSystem,
5511
- now: new Date()
5512
- };
5513
- const incremental = projectSpreadsheetSheetsFromFortuneOperations(sheets, sourceSheets, operations);
5514
- if (incremental) {
5515
- recordSpreadsheetControlledProjectionMeasure(startedAt, spreadsheetProjectionNow(), {
5516
- affectedCellCount: incremental.affectedCellCount,
5517
- incremental: true,
5518
- operationCount: operations.length
5519
- });
5520
- return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(incremental.sheets, sourceSheets, operations), sourceSheets, operations, filterContext);
5521
- }
5522
- const operationCoordinatesBySheet = spreadsheetCellOperationCoordinates(operations);
5523
- const projected = sheets.map((sheet, index)=>{
5524
- const source = (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
5525
- const operationCoordinates = sheet.id ? operationCoordinatesBySheet?.get(sheet.id) : void 0;
5526
- const cells = spreadsheetPopulatedCellData(sheet).map((entry)=>{
5527
- if (!source || !entry.v) return entry;
5528
- const previous = spreadsheet_editor_support_spreadsheetCellAt(source, entry.r, entry.c);
5529
- const exactCellOperation = operationCoordinates?.has(spreadsheetCellOperationKey(entry.r, entry.c));
5530
- const textStableNonStructuralCallback = null !== operationCoordinatesBySheet && sameSpreadsheetRichTextCellText(previous, entry.v);
5531
- if (!exactCellOperation && !textStableNonStructuralCallback) return entry;
5532
- const reconciled = exactCellOperation ? reconcileSpreadsheetRichTextCellEdit(previous, entry.v, takeSpreadsheetRichTextPaste(source, entry.r, entry.c, entry.v)) : restoreSpreadsheetRichTextCellRuns(previous, entry.v);
5533
- return reconciled === entry.v ? entry : {
5534
- ...entry,
5535
- v: reconciled
5536
- };
5537
- });
5538
- const { celldata: _cellData, data: _data, ...metadata } = sheet;
5539
- const useMatrix = source ? void 0 !== source.data : void 0 !== sheet.data;
5540
- if (!useMatrix) return {
5541
- ...metadata,
5542
- celldata: cells
5543
- };
5544
- const data = [];
5545
- data.length = Math.max(sheet.data?.length ?? 0, source?.data?.length ?? 0, spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.row));
5546
- for (const { r: rowIndex, c: columnIndex, v: cell } of cells){
5547
- const row = data[rowIndex] ?? [];
5548
- data[rowIndex] = row;
5549
- row.length = Math.max(row.length, sheet.data?.[rowIndex]?.length ?? 0, source?.data?.[rowIndex]?.length ?? 0, spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.column));
5550
- row[columnIndex] = cell;
5551
- }
5552
- return {
5553
- ...metadata,
5554
- data
5555
- };
5556
- });
5557
- recordSpreadsheetControlledProjectionMeasure(startedAt, spreadsheetProjectionNow(), {
5558
- affectedCellCount: 0,
5559
- incremental: false,
5560
- operationCount: operations.length
5561
- });
5562
- return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(projected, sourceSheets, operations), sourceSheets, operations, filterContext);
5563
- }
5564
- function recordSpreadsheetControlledProjectionMeasure(start, end, detail) {
5565
- try {
5566
- globalThis.performance?.measure('a3s-office.spreadsheet.controlled-projection', {
5567
- detail,
5568
- end,
5569
- start
5570
- });
5571
- } catch {}
5572
- }
5573
- function finiteSpreadsheetSelection(selection) {
5574
- const row = finiteSpreadsheetSelectionAxis(selection?.row);
5575
- const column = finiteSpreadsheetSelectionAxis(selection?.column);
5576
- return {
5577
- ...selection,
5578
- row,
5579
- column,
5580
- row_focus: finiteSpreadsheetFocus(selection?.row_focus, row),
5581
- column_focus: finiteSpreadsheetFocus(selection?.column_focus, column)
5582
- };
5583
- }
5584
- function sameSpreadsheetWorkbookState(changed, rendered) {
5585
- return changed.length === rendered.length && changed.every((sheet, index)=>sameSpreadsheetSheetState(sheet, rendered[index]));
5586
- }
5587
- function sameSpreadsheetWorkbookStateAfterOperations(changed, rendered, operations) {
5588
- if (0 === operations.length || operations.length > 10000) return null;
5589
- if (changed.length !== rendered.length || !changed.every((sheet, index)=>sameSpreadsheetHistoryValue(spreadsheetSheetMetadata(sheet), spreadsheetSheetMetadata(rendered[index])))) return false;
5590
- const changedById = spreadsheet_editor_support_spreadsheetSheetsById(changed);
5591
- const renderedById = spreadsheet_editor_support_spreadsheetSheetsById(rendered);
5592
- if (!changedById || !renderedById) return null;
5593
- const coordinates = new Map();
5594
- for (const operation of operations){
5595
- if ('insertRowCol' === operation.op || 'deleteRowCol' === operation.op || 'addSheet' === operation.op || 'deleteSheet' === operation.op || 0 === operation.path.length) return false;
5596
- if (!operation.id || !changedById.has(operation.id) || !renderedById.has(operation.id)) return null;
5597
- if ('data' !== operation.path[0]) continue;
5598
- const row = operation.path[1];
5599
- const column = operation.path[2];
5600
- if (operation.path.length < 3 || !spreadsheet_editor_support_isSpreadsheetCoordinate(row) || !spreadsheet_editor_support_isSpreadsheetCoordinate(column)) return null;
5601
- coordinates.set(`${operation.id}:${row}:${column}`, {
5602
- column,
5603
- row,
5604
- sheetId: operation.id
5605
- });
5606
- }
5607
- for (const { column, row, sheetId } of coordinates.values())if (!sameSpreadsheetHistoryValue(spreadsheet_editor_support_spreadsheetCellAt(changedById.get(sheetId), row, column), spreadsheet_editor_support_spreadsheetCellAt(renderedById.get(sheetId), row, column))) return false;
5608
- return true;
5609
- }
5610
- function sameSpreadsheetHistoryContent(left, right) {
5611
- if (left === right) return true;
5612
- const { sheets: leftSheets, ...leftMetadata } = left;
5613
- const { sheets: rightSheets, ...rightMetadata } = right;
5614
- return sameSpreadsheetHistoryValue(leftMetadata, rightMetadata) && leftSheets.length === rightSheets.length && leftSheets.every((sheet, index)=>sameSpreadsheetSheetState(sheet, rightSheets[index]));
5615
- }
5616
- function spreadsheetContentWithSelection(content, sheetId, selection) {
5617
- return spreadsheetContentWithSelections(content, sheetId, selection ? [
5618
- selection
5619
- ] : []);
5620
- }
5621
- function spreadsheetContentWithSelections(content, sheetId, selections) {
5622
- if (!sheetId || !selections.length) return content;
5623
- const nextSelections = selections.map(finiteSpreadsheetSelection);
5624
- let changed = false;
5625
- const sheets = content.sheets.map((sheet)=>{
5626
- if (sheet.id !== sheetId) return sheet;
5627
- changed = true;
5628
- return {
5629
- ...sheet,
5630
- luckysheet_select_save: nextSelections
5631
- };
5632
- });
5633
- return changed ? {
5634
- ...content,
5635
- sheets
5636
- } : content;
5637
- }
5638
- function isSpreadsheetNativeTextUndoTarget(target) {
5639
- if (!(target instanceof HTMLElement)) return false;
5640
- if (isSpreadsheetCellEditingTarget(target)) return true;
5641
- if (target.closest('.fortune-container')) return false;
5642
- return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target.isContentEditable || Boolean(target.closest('[contenteditable="true"]'));
5643
- }
5644
- function isSpreadsheetCellEditingTarget(target) {
5645
- if (!isSpreadsheetCellEditorTarget(target)) return false;
5646
- if (!(target instanceof Element)) return false;
5647
- const formulaInput = target.closest('.fortune-fx-input');
5648
- if (formulaInput) return true;
5649
- const cellInput = target.closest('.luckysheet-cell-input');
5650
- if (!cellInput) return false;
5651
- const inputBox = cellInput.closest('.luckysheet-input-box');
5652
- if (!inputBox) return true;
5653
- const zIndex = Number.parseInt(inputBox.style.zIndex || getComputedStyle(inputBox).zIndex, 10);
5654
- return !Number.isFinite(zIndex) || zIndex >= 0;
5655
- }
5656
- function isSpreadsheetCellEditorTarget(target) {
5657
- return target instanceof Element && Boolean(target.closest('.fortune-fx-input, .luckysheet-cell-input'));
5658
- }
5659
- function spreadsheetFormulaBarSelectAllTarget(event) {
5660
- if (event.defaultPrevented || event.repeat || event.altKey || event.shiftKey || !(event.metaKey || event.ctrlKey) || 'a' !== event.key.toLocaleLowerCase() || !(event.target instanceof Element)) return null;
5661
- const formulaBar = event.target.closest('.fortune-fx-input');
5662
- return formulaBar instanceof HTMLElement ? formulaBar : null;
5663
- }
5664
- function selectSpreadsheetFormulaBarContents(event) {
5665
- const formulaBar = spreadsheetFormulaBarSelectAllTarget(event);
5666
- const selection = window.getSelection();
5667
- if (!formulaBar || !selection) return false;
5668
- const range = document.createRange();
5669
- range.selectNodeContents(formulaBar);
5670
- selection.removeAllRanges();
5671
- selection.addRange(range);
5672
- return true;
5673
- }
5674
- function spreadsheetColumnLabel(column) {
5675
- let value = Math.max(0, column) + 1;
5676
- let label = '';
5677
- while(value > 0){
5678
- value -= 1;
5679
- label = String.fromCharCode(65 + value % 26) + label;
5680
- value = Math.floor(value / 26);
5681
- }
5682
- return label;
5683
- }
5684
- function spreadsheet_editor_support_spreadsheetCellHasContent(cell) {
5685
- if ('string' == typeof cell.f && cell.f.trim()) return true;
5686
- if (null === cell.v || void 0 === cell.v) return false;
5687
- return 'string' != typeof cell.v || cell.v.trim().length > 0;
5688
- }
5689
- function finiteSpreadsheetSelectionAxis(axis) {
5690
- const first = finiteSpreadsheetIndex(axis?.[0], 0);
5691
- const second = finiteSpreadsheetIndex(axis?.[1], first);
5692
- return [
5693
- Math.min(first, second),
5694
- Math.max(first, second)
5695
- ];
5696
- }
5697
- function finiteSpreadsheetFocus(value, axis) {
5698
- const focus = finiteSpreadsheetIndex(value, axis[0] ?? 0);
5699
- return Math.min(axis[1] ?? focus, Math.max(axis[0] ?? focus, focus));
5700
- }
5701
- function finiteSpreadsheetIndex(value, fallback) {
5702
- return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
5703
- }
5704
- function spreadsheet_editor_support_isSpreadsheetCoordinate(value) {
5705
- return Number.isSafeInteger(value) && Number(value) >= 0;
5706
- }
5707
- function spreadsheet_editor_support_spreadsheetSheetsById(sheets) {
5708
- const byId = new Map();
5709
- for (const sheet of sheets){
5710
- if (!sheet.id || byId.has(sheet.id)) return null;
5711
- byId.set(sheet.id, sheet);
5712
- }
5713
- return byId;
5714
- }
5715
- function spreadsheetSheetMetadata(sheet) {
5716
- if (!sheet) return null;
5717
- const { celldata: _cellData, calcChain: _calculationChain, data: _data, dynamicArray_compute: _dynamicArrayComputation, luckysheet_select_save: _selection, luckysheet_selection_range: _range, zoomRatio, ...content } = sheet;
5718
- return {
5719
- ...content,
5720
- ...void 0 === zoomRatio || 1 === zoomRatio ? {} : {
5721
- zoomRatio
5722
- }
5723
- };
5724
- }
5725
- function sameSpreadsheetSheetState(left, right) {
5726
- return Boolean(right && sameSpreadsheetHistoryValue(spreadsheetSheetMetadata(left), spreadsheetSheetMetadata(right)) && sameSpreadsheetCellStorage(left, right));
5727
- }
5728
- function sameSpreadsheetCellStorage(left, right) {
5729
- if (void 0 !== left.data && void 0 !== right.data) {
5730
- const leftProfile = spreadsheetMatrixProfile(left.data);
5731
- const rightProfile = spreadsheetMatrixProfile(right.data);
5732
- if (leftProfile && rightProfile && leftProfile.historyRoot === rightProfile.historyRoot) return leftProfile.historyState === rightProfile.historyState;
5733
- return sameSpreadsheetDataMatrices(left.data, right.data);
5734
- }
5735
- if (void 0 === left.data && void 0 === right.data) {
5736
- if (left.celldata === right.celldata) return true;
2534
+ return 'COUNT';
2535
+ case 'max':
2536
+ return 'MAX';
2537
+ case 'min':
2538
+ return 'MIN';
2539
+ default:
2540
+ return null;
5737
2541
  }
5738
- return sameSpreadsheetHistoryValue(spreadsheetPopulatedCellData(left), spreadsheetPopulatedCellData(right));
5739
2542
  }
5740
- function sameSpreadsheetDataMatrices(left, right) {
5741
- if (left === right) return true;
5742
- for (const rowIndex of sparseArrayIndexes(left))if (!sameSpreadsheetDataRows(left[rowIndex], right[rowIndex])) return false;
5743
- for (const rowIndex of sparseArrayIndexes(right))if (!Object.hasOwn(left, rowIndex) && !sameSpreadsheetDataRows(void 0, right[rowIndex])) return false;
5744
- return true;
2543
+ function canMutateSpreadsheetCellRange(sheet, range) {
2544
+ return canMutateSpreadsheetCellRanges(sheet, [
2545
+ range
2546
+ ]);
5745
2547
  }
5746
- function sameSpreadsheetDataRows(left, right) {
5747
- if (left === right) return true;
5748
- for (const columnIndex of sparseArrayIndexes(left ?? [])){
5749
- const leftCell = left?.[columnIndex];
5750
- if (null == leftCell) continue;
5751
- const rightCell = right?.[columnIndex];
5752
- if (null == rightCell || !sameSpreadsheetHistoryValue(leftCell, rightCell)) return false;
2548
+ function canMutateSpreadsheetCellRanges(sheet, ranges) {
2549
+ if (!sheet || 0 === ranges.length || ranges.some((range)=>!spreadsheetCellMutationRangeIsValid(range))) return false;
2550
+ if (sheet.isPivotTable || sheet.pivotTable || sheet.pivotTables?.length) return false;
2551
+ const merges = Object.values(sheet.config?.merge ?? {});
2552
+ if (merges.some((merge)=>{
2553
+ const mergeRange = spreadsheetNativeMergeRange(merge);
2554
+ return mergeRange && ranges.some((range)=>spreadsheetCellRangesIntersect(mergeRange, range));
2555
+ })) return false;
2556
+ const cellAt = createSpreadsheetMutationCellReader(sheet);
2557
+ for (const range of ranges){
2558
+ for(let row = range.row[0]; row <= range.row[1]; row += 1)if (sheet.config?.rowReadOnly?.[row]) return false;
2559
+ for(let column = range.column[0]; column <= range.column[1]; column += 1)if (sheet.config?.colReadOnly?.[column]) return false;
2560
+ for(let row = range.row[0]; row <= range.row[1]; row += 1)for(let column = range.column[0]; column <= range.column[1]; column += 1)if (spreadsheetCellIsLocked(sheet, cellAt, row, column)) return false;
5753
2561
  }
5754
- for (const columnIndex of sparseArrayIndexes(right ?? []))if (right?.[columnIndex] != null && left?.[columnIndex] == null) return false;
5755
2562
  return true;
5756
2563
  }
5757
- function spreadsheetPopulatedCellData(sheet) {
5758
- const cells = [];
5759
- if (void 0 !== sheet.data) for (const rowIndex of sparseArrayIndexes(sheet.data)){
5760
- const row = sheet.data[rowIndex];
5761
- if (row) for (const columnIndex of sparseArrayIndexes(row)){
5762
- const cell = row[columnIndex];
5763
- if (null != cell) cells.push({
5764
- r: rowIndex,
5765
- c: columnIndex,
5766
- v: cell
5767
- });
5768
- }
5769
- }
5770
- else for (const cell of sheet.celldata ?? [])if (null != cell.v) cells.push(cell);
5771
- return cells.sort((left, right)=>left.r - right.r || left.c - right.c);
5772
- }
5773
- function spreadsheet_editor_support_positiveSpreadsheetDimension(value) {
5774
- return 'number' == typeof value && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
5775
- }
5776
- const spreadsheetKeyboardPageRows = 20;
5777
- const spreadsheetDefaultRows = 60;
5778
- const spreadsheetDefaultColumns = 26;
5779
- function moveSpreadsheetKeyboardSelection(sheet, selection, move, extend) {
5780
- const bounds = spreadsheetSheetBounds(sheet);
5781
- const current = normalizeSpreadsheetKeyboardSelection(selection, bounds);
5782
- let targetRow = current.rowFocus;
5783
- let targetColumn = current.columnFocus;
5784
- switch(move){
5785
- case 'up':
5786
- targetRow -= 1;
5787
- break;
5788
- case 'down':
5789
- targetRow += 1;
5790
- break;
5791
- case 'left':
5792
- targetColumn -= 1;
5793
- break;
5794
- case 'right':
5795
- targetColumn += 1;
5796
- break;
5797
- case 'next-cell':
5798
- if (targetColumn < bounds.lastColumn) targetColumn += 1;
5799
- else if (targetRow < bounds.lastRow) {
5800
- targetRow += 1;
5801
- targetColumn = 0;
5802
- }
5803
- break;
5804
- case 'previous-cell':
5805
- if (targetColumn > 0) targetColumn -= 1;
5806
- else if (targetRow > 0) {
5807
- targetRow -= 1;
5808
- targetColumn = bounds.lastColumn;
5809
- }
5810
- break;
5811
- case 'page-up':
5812
- targetRow -= spreadsheetKeyboardPageRows;
5813
- break;
5814
- case 'page-down':
5815
- targetRow += spreadsheetKeyboardPageRows;
5816
- break;
5817
- case 'row-start':
5818
- targetColumn = 0;
5819
- break;
5820
- case 'sheet-start':
5821
- targetRow = 0;
5822
- targetColumn = 0;
5823
- break;
5824
- case 'used-end':
5825
- {
5826
- const usedEnd = spreadsheetUsedRangeEnd(sheet);
5827
- targetRow = usedEnd.row;
5828
- targetColumn = usedEnd.column;
5829
- break;
5830
- }
5831
- }
5832
- targetRow = clampSpreadsheetIndex(targetRow, bounds.lastRow);
5833
- targetColumn = clampSpreadsheetIndex(targetColumn, bounds.lastColumn);
5834
- if (!extend) return {
5835
- row: [
5836
- targetRow,
5837
- targetRow
5838
- ],
5839
- column: [
5840
- targetColumn,
5841
- targetColumn
5842
- ],
5843
- row_focus: targetRow,
5844
- column_focus: targetColumn
5845
- };
5846
- const rowAnchor = selectionAnchor(current.startRow, current.endRow, current.rowFocus);
5847
- const columnAnchor = selectionAnchor(current.startColumn, current.endColumn, current.columnFocus);
5848
- return {
5849
- row: [
5850
- Math.min(rowAnchor, targetRow),
5851
- Math.max(rowAnchor, targetRow)
5852
- ],
5853
- column: [
5854
- Math.min(columnAnchor, targetColumn),
5855
- Math.max(columnAnchor, targetColumn)
5856
- ],
5857
- row_focus: targetRow,
5858
- column_focus: targetColumn
5859
- };
2564
+ function spreadsheetCellMutationRangeIsValid(range) {
2565
+ return Number.isSafeInteger(range.row[0]) && Number.isSafeInteger(range.row[1]) && Number.isSafeInteger(range.column[0]) && Number.isSafeInteger(range.column[1]) && range.row[0] >= 0 && range.row[0] <= range.row[1] && range.column[0] >= 0 && range.column[0] <= range.column[1];
5860
2566
  }
5861
- function scopeSpreadsheetKeyboardSelection(sheet, selection, scope) {
5862
- const bounds = spreadsheetSheetBounds(sheet);
5863
- const current = normalizeSpreadsheetKeyboardSelection(selection, bounds);
5864
- if ('column' === scope) return {
5865
- row: [
5866
- 0,
5867
- bounds.lastRow
5868
- ],
5869
- column: [
5870
- current.columnFocus,
5871
- current.columnFocus
5872
- ],
5873
- row_focus: current.rowFocus,
5874
- column_focus: current.columnFocus
5875
- };
5876
- if ('row' === scope) return {
5877
- row: [
5878
- current.rowFocus,
5879
- current.rowFocus
5880
- ],
5881
- column: [
5882
- 0,
5883
- bounds.lastColumn
5884
- ],
5885
- row_focus: current.rowFocus,
5886
- column_focus: current.columnFocus
5887
- };
2567
+ function spreadsheetNativeMergeRange(value) {
2568
+ if (!spreadsheet_cell_mutation_guard_isRecord(value)) return null;
2569
+ const row = finiteIndex(value.r);
2570
+ const column = finiteIndex(value.c);
2571
+ const rowSpan = positiveSpan(value.rs);
2572
+ const columnSpan = positiveSpan(value.cs);
2573
+ if (null === row || null === column || null === rowSpan || null === columnSpan || !Number.isSafeInteger(row + rowSpan - 1) || !Number.isSafeInteger(column + columnSpan - 1)) return null;
5888
2574
  return {
5889
2575
  row: [
5890
- 0,
5891
- bounds.lastRow
2576
+ row,
2577
+ row + rowSpan - 1
5892
2578
  ],
5893
2579
  column: [
5894
- 0,
5895
- bounds.lastColumn
5896
- ],
5897
- row_focus: current.rowFocus,
5898
- column_focus: current.columnFocus
5899
- };
5900
- }
5901
- function isSpreadsheetGridKeyboardTarget(target) {
5902
- if (null === target) return true;
5903
- return target instanceof Element && Boolean(target.closest('.fortune-container'));
5904
- }
5905
- function runSpreadsheetSelectionMoveShortcut(event, canExecute, execute, move, extend) {
5906
- if (event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute(move, extend)) return false;
5907
- return execute(move, extend);
5908
- }
5909
- function runSpreadsheetSelectionScopeShortcut(event, canExecute, execute, scope) {
5910
- if (event.repeat || event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute(scope)) return false;
5911
- return execute(scope);
5912
- }
5913
- function spreadsheetSelectionContainsFocus(selection, focus) {
5914
- const firstRow = selection.row[0] ?? 0;
5915
- const secondRow = selection.row[1] ?? firstRow;
5916
- const firstColumn = selection.column[0] ?? 0;
5917
- const secondColumn = selection.column[1] ?? firstColumn;
5918
- return focus.row >= Math.min(firstRow, secondRow) && focus.row <= Math.max(firstRow, secondRow) && focus.column >= Math.min(firstColumn, secondColumn) && focus.column <= Math.max(firstColumn, secondColumn);
5919
- }
5920
- function spreadsheetSheetBounds(sheet) {
5921
- const dataRows = sheet.data?.length ?? 0;
5922
- const dataColumns = sparseMatrixColumnCount(sheet.data);
5923
- return {
5924
- lastRow: spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(sheet.row, dataRows, spreadsheetDefaultRows) - 1,
5925
- lastColumn: spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(sheet.column, dataColumns, spreadsheetDefaultColumns) - 1
2580
+ column,
2581
+ column + columnSpan - 1
2582
+ ]
5926
2583
  };
5927
2584
  }
5928
- function spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(configured, dataDimension, fallback) {
5929
- const value = Number(configured);
5930
- if (Number.isFinite(value) && value > 0) return Math.max(1, Math.trunc(value));
5931
- if (dataDimension > 0) return dataDimension;
5932
- return fallback;
2585
+ function spreadsheetCellIsLocked(sheet, cellAt, row, column) {
2586
+ const cell = cellAt(row, column);
2587
+ if (cell?.lo !== void 0 && null !== cell.lo) return Boolean(cell.lo);
2588
+ const authority = sheet.config?.authority;
2589
+ if (!spreadsheet_cell_mutation_guard_isRecord(authority)) return false;
2590
+ const ranges = authority.cellProtectionRanges;
2591
+ if (Array.isArray(ranges)) for(let index = ranges.length - 1; index >= 0; index -= 1){
2592
+ const candidate = ranges[index];
2593
+ if (!spreadsheet_cell_mutation_guard_isRecord(candidate)) continue;
2594
+ const protectedRange = parseSpreadsheetCellRange(candidate.range);
2595
+ if (protectedRange && row >= protectedRange.row[0] && row <= protectedRange.row[1] && column >= protectedRange.column[0] && column <= protectedRange.column[1]) return false !== candidate.locked;
2596
+ }
2597
+ return void 0 !== authority.sheet && 0 !== authority.sheet;
5933
2598
  }
5934
- function spreadsheetUsedRangeEnd(sheet) {
5935
- const bounds = spreadsheetSheetBounds(sheet);
5936
- let row = 0;
5937
- let column = 0;
5938
- sheet.data?.forEach((cells, rowIndex)=>{
5939
- cells?.forEach((cell, columnIndex)=>{
5940
- if (!spreadsheetCellHasValue(cell)) return;
5941
- row = Math.max(row, rowIndex);
5942
- column = Math.max(column, columnIndex);
5943
- });
5944
- });
5945
- sheet.celldata?.forEach((cell)=>{
5946
- if (!spreadsheetCellHasValue(cell.v)) return;
5947
- row = Math.max(row, Number(cell.r) || 0);
5948
- column = Math.max(column, Number(cell.c) || 0);
5949
- });
5950
- return {
5951
- row: clampSpreadsheetIndex(row, bounds.lastRow),
5952
- column: clampSpreadsheetIndex(column, bounds.lastColumn)
2599
+ function createSpreadsheetMutationCellReader(sheet) {
2600
+ let sparseRows = null;
2601
+ const sparseCell = (row, column)=>{
2602
+ if (!sparseRows) {
2603
+ sparseRows = new Map();
2604
+ for (const entry of sheet.celldata ?? []){
2605
+ let sparseRow = sparseRows.get(entry.r);
2606
+ if (!sparseRow) {
2607
+ sparseRow = new Map();
2608
+ sparseRows.set(entry.r, sparseRow);
2609
+ }
2610
+ sparseRow.set(entry.c, entry.v);
2611
+ }
2612
+ }
2613
+ return sparseRows.get(row)?.get(column);
5953
2614
  };
2615
+ return (row, column)=>sheet.data?.[row]?.[column] ?? sparseCell(row, column);
5954
2616
  }
5955
- function spreadsheetCellHasValue(cell) {
5956
- if (!cell) return false;
5957
- if ('string' == typeof cell.f && cell.f.trim()) return true;
5958
- const value = cell.v;
5959
- if (null != value) return 'string' != typeof value || value.trim().length > 0;
5960
- return 'string' == typeof cell.m && cell.m.trim().length > 0;
5961
- }
5962
- function normalizeSpreadsheetKeyboardSelection(selection, bounds) {
5963
- const firstRow = clampSpreadsheetIndex(selection.row[0], bounds.lastRow);
5964
- const secondRow = clampSpreadsheetIndex(selection.row[1] ?? firstRow, bounds.lastRow);
5965
- const firstColumn = clampSpreadsheetIndex(selection.column[0], bounds.lastColumn);
5966
- const secondColumn = clampSpreadsheetIndex(selection.column[1] ?? firstColumn, bounds.lastColumn);
5967
- const startRow = Math.min(firstRow, secondRow);
5968
- const endRow = Math.max(firstRow, secondRow);
5969
- const startColumn = Math.min(firstColumn, secondColumn);
5970
- const endColumn = Math.max(firstColumn, secondColumn);
5971
- return {
5972
- startRow,
5973
- endRow,
5974
- startColumn,
5975
- endColumn,
5976
- rowFocus: clampSpreadsheetIndex(selection.row_focus ?? secondRow, bounds.lastRow),
5977
- columnFocus: clampSpreadsheetIndex(selection.column_focus ?? secondColumn, bounds.lastColumn)
5978
- };
2617
+ function finiteIndex(value) {
2618
+ return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : null;
5979
2619
  }
5980
- function clampSpreadsheetIndex(value, maximum) {
5981
- const numeric = Number(value);
5982
- const finite = Number.isFinite(numeric) ? Math.trunc(numeric) : 0;
5983
- return Math.min(maximum, Math.max(0, finite));
2620
+ function positiveSpan(value) {
2621
+ return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : null;
5984
2622
  }
5985
- function selectionAnchor(start, end, focus) {
5986
- return focus === start ? end : start;
2623
+ function spreadsheet_cell_mutation_guard_isRecord(value) {
2624
+ return Boolean(value && 'object' == typeof value && !Array.isArray(value));
5987
2625
  }
5988
2626
  function createSpreadsheetAutoSumExtension() {
5989
2627
  return createOfficeEditorExtension({
@@ -7104,10 +3742,10 @@ function formatMatrixCells(sheet, range, patch) {
7104
3742
  }
7105
3743
  function formatSparseCells(sheet, range, patch) {
7106
3744
  const byCoordinate = new Map();
7107
- for (const entry of sheet.celldata ?? [])byCoordinate.set(spreadsheet_cell_format_cellKey(entry.r, entry.c), entry);
3745
+ for (const entry of sheet.celldata ?? [])byCoordinate.set(cellKey(entry.r, entry.c), entry);
7108
3746
  let materialized = false;
7109
3747
  for(let row = range.row[0]; row <= range.row[1]; row += 1)for(let column = range.column[0]; column <= range.column[1]; column += 1){
7110
- const key = spreadsheet_cell_format_cellKey(row, column);
3748
+ const key = cellKey(row, column);
7111
3749
  const entry = byCoordinate.get(key);
7112
3750
  const next = formatCell(entry?.v ?? null, patch);
7113
3751
  if (next) {
@@ -7265,7 +3903,7 @@ function normalizeColor(value) {
7265
3903
  ...color.slice(1)
7266
3904
  ].map((character)=>character.repeat(2)).join('')}`;
7267
3905
  }
7268
- function spreadsheet_cell_format_cellKey(row, column) {
3906
+ function cellKey(row, column) {
7269
3907
  return `${row}:${column}`;
7270
3908
  }
7271
3909
  const spreadsheetFormatCellsTabs = [
@@ -7972,185 +4610,6 @@ function runSpreadsheetCopyFromAboveShortcut(event, kind, canExecute, execute) {
7972
4610
  if (event.repeat || event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute(kind)) return false;
7973
4611
  return execute(kind);
7974
4612
  }
7975
- const spreadsheet_go_to_spreadsheetMaximumRows = 1048576;
7976
- const spreadsheet_go_to_spreadsheetMaximumColumns = 16384;
7977
- const spreadsheetGoToMaximumInputLength = 512;
7978
- function resolveSpreadsheetGoToTarget(content, activeSheetId, input) {
7979
- const value = input.trim().replace(/^=/, '').trim();
7980
- if (!value) return spreadsheetGoToError('empty');
7981
- if (value.length > spreadsheetGoToMaximumInputLength) return spreadsheetGoToError('invalid-reference');
7982
- const activeSheet = content.sheets.find((sheet)=>sheet.id === activeSheetId);
7983
- if (!activeSheet) return spreadsheetGoToError('sheet-not-found');
7984
- const qualified = splitSpreadsheetQualifiedReference(value);
7985
- if (!qualified) return spreadsheetGoToError('invalid-reference');
7986
- const requestedSheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : activeSheet;
7987
- if (qualified.qualifier && !requestedSheet) return spreadsheetGoToError('sheet-not-found');
7988
- if (requestedSheet?.hide === 1) return spreadsheetGoToError('hidden-sheet');
7989
- const direct = resolveSpreadsheetDirectRange(content, requestedSheet ?? activeSheet, qualified);
7990
- if ('error' === direct.kind) return spreadsheetGoToError(direct.code);
7991
- if ('match' === direct.kind) return spreadsheetGoToSuccess(direct.value, 'reference');
7992
- const named = findSpreadsheetGoToName(content, qualified.reference, requestedSheet ?? activeSheet, Boolean(qualified.qualifier));
7993
- if ('error' === named.kind) return spreadsheetGoToError(named.code);
7994
- if (!named.value) return spreadsheetGoToError('invalid-reference');
7995
- const fallbackSheet = named.value.scopeSheetId ? content.sheets.find((sheet)=>sheet.id === named.value?.scopeSheetId) : requestedSheet ?? activeSheet;
7996
- if (!fallbackSheet) return spreadsheetGoToError('sheet-not-found');
7997
- if (1 === fallbackSheet.hide) return spreadsheetGoToError('hidden-sheet');
7998
- const namedReference = splitSpreadsheetQualifiedReference(named.value.reference.trim().replace(/^=/, '').trim());
7999
- if (!namedReference) return spreadsheetGoToError('unsupported-name');
8000
- const resolved = resolveSpreadsheetDirectRange(content, fallbackSheet, namedReference);
8001
- if ('error' === resolved.kind) return spreadsheetGoToError(resolved.code);
8002
- if ('match' !== resolved.kind) return spreadsheetGoToError('unsupported-name');
8003
- return spreadsheetGoToSuccess(resolved.value, 'named-range', named.value.name);
8004
- }
8005
- function spreadsheetGoToValidationMessage(content, activeSheetId, input) {
8006
- const resolution = resolveSpreadsheetGoToTarget(content, activeSheetId, input);
8007
- return resolution.ok ? null : resolution.message;
8008
- }
8009
- function resolveSpreadsheetDirectRange(content, fallbackSheet, qualified) {
8010
- const sheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : fallbackSheet;
8011
- if (!sheet) return {
8012
- kind: 'error',
8013
- code: 'sheet-not-found'
8014
- };
8015
- if (1 === sheet.hide) return {
8016
- kind: 'error',
8017
- code: 'hidden-sheet'
8018
- };
8019
- const ranges = parseSpreadsheetCellRanges(qualified.reference);
8020
- if (!ranges) return {
8021
- kind: 'miss'
8022
- };
8023
- if (1 !== ranges.length) return {
8024
- kind: 'error',
8025
- code: 'multiple-ranges'
8026
- };
8027
- const range = ranges[0];
8028
- if (!range || !spreadsheetGoToRangeIsBounded(sheet, range)) return {
8029
- kind: 'error',
8030
- code: 'out-of-bounds'
8031
- };
8032
- return {
8033
- kind: 'match',
8034
- value: {
8035
- range,
8036
- sheet
8037
- }
8038
- };
8039
- }
8040
- function findSpreadsheetGoToName(content, input, scopeSheet, qualified) {
8041
- const normalized = input.trim().toLocaleLowerCase();
8042
- const matching = (content.namedRanges ?? []).filter((range)=>range.name.trim().toLocaleLowerCase() === normalized);
8043
- const scoped = matching.filter((range)=>range.scopeSheetId === scopeSheet.id);
8044
- const workbook = matching.filter((range)=>!range.scopeSheetId);
8045
- const preferred = scoped.length ? scoped : workbook;
8046
- if (preferred.length > 1) return {
8047
- kind: 'error',
8048
- code: 'ambiguous-name'
8049
- };
8050
- if (!preferred.length && !qualified) return {
8051
- kind: 'match',
8052
- value: null
8053
- };
8054
- return {
8055
- kind: 'match',
8056
- value: preferred[0] ?? null
8057
- };
8058
- }
8059
- function spreadsheetGoToRangeIsBounded(sheet, range) {
8060
- const bounds = spreadsheetSheetBounds(sheet);
8061
- return range.row[0] >= 0 && range.row[1] < spreadsheet_go_to_spreadsheetMaximumRows && range.row[1] <= bounds.lastRow && range.column[0] >= 0 && range.column[1] < spreadsheet_go_to_spreadsheetMaximumColumns && range.column[1] <= bounds.lastColumn;
8062
- }
8063
- function spreadsheetGoToSuccess(resolved, source, name) {
8064
- const reference = formatSpreadsheetCellRanges([
8065
- resolved.range
8066
- ]);
8067
- return {
8068
- ok: true,
8069
- target: {
8070
- displayReference: `${quoteSpreadsheetSheetName(resolved.sheet.name)}!${reference}`,
8071
- name,
8072
- range: resolved.range,
8073
- selection: {
8074
- row: [
8075
- ...resolved.range.row
8076
- ],
8077
- column: [
8078
- ...resolved.range.column
8079
- ],
8080
- row_focus: resolved.range.row[0],
8081
- column_focus: resolved.range.column[0]
8082
- },
8083
- sheetId: resolved.sheet.id ?? '',
8084
- source
8085
- }
8086
- };
8087
- }
8088
- function spreadsheetGoToError(code) {
8089
- const messages = {
8090
- 'ambiguous-name': '同一作用域中存在多个同名区域,无法确定定位目标。',
8091
- empty: '请输入单元格、连续区域或已定义名称。',
8092
- 'hidden-sheet': '不能定位到隐藏工作表。',
8093
- 'invalid-reference': '请输入有效的 A1 单元格、连续区域或已定义名称。',
8094
- 'multiple-ranges': '一次只能定位到一个连续区域。',
8095
- 'out-of-bounds': '引用超出了目标工作表的有效边界。',
8096
- 'sheet-not-found': '找不到引用的工作表。',
8097
- 'unsupported-name': '该名称不是可定位的连续单元格区域。'
8098
- };
8099
- return {
8100
- ok: false,
8101
- code,
8102
- message: messages[code]
8103
- };
8104
- }
8105
- function splitSpreadsheetQualifiedReference(value) {
8106
- let quoted = false;
8107
- let separator = -1;
8108
- for(let index = 0; index < value.length; index += 1){
8109
- const character = value[index];
8110
- if ("'" === character) {
8111
- if (quoted && "'" === value[index + 1]) {
8112
- index += 1;
8113
- continue;
8114
- }
8115
- quoted = !quoted;
8116
- continue;
8117
- }
8118
- if ('!' === character && !quoted) {
8119
- if (separator >= 0) return null;
8120
- separator = index;
8121
- }
8122
- }
8123
- if (quoted) return null;
8124
- if (separator < 0) return {
8125
- qualifier: null,
8126
- reference: value.trim()
8127
- };
8128
- const rawQualifier = value.slice(0, separator).trim();
8129
- const reference = value.slice(separator + 1).trim();
8130
- if (!rawQualifier || !reference) return null;
8131
- const qualifier = unquoteSpreadsheetSheetName(rawQualifier);
8132
- return qualifier ? {
8133
- qualifier,
8134
- reference
8135
- } : null;
8136
- }
8137
- function unquoteSpreadsheetSheetName(value) {
8138
- if (!value.startsWith("'")) return value.includes("'") ? null : value;
8139
- if (!value.endsWith("'") || value.length < 2) return null;
8140
- const inner = value.slice(1, -1);
8141
- for(let index = 0; index < inner.length; index += 1)if ("'" === inner[index]) {
8142
- if ("'" !== inner[index + 1]) return null;
8143
- index += 1;
8144
- }
8145
- return inner.replaceAll("''", "'");
8146
- }
8147
- function spreadsheetSheetByName(content, name) {
8148
- const normalized = name.trim().toLocaleLowerCase();
8149
- return content.sheets.find((sheet)=>sheet.name.trim().toLocaleLowerCase() === normalized);
8150
- }
8151
- function quoteSpreadsheetSheetName(name) {
8152
- return `'${name.replaceAll("'", "''")}'`;
8153
- }
8154
4613
  const MAX_SPREADSHEET_DATA_VALIDATION_CELLS = 10000;
8155
4614
  const defaultSpreadsheetDataValidationValue = {
8156
4615
  allowBlank: true,
@@ -8275,7 +4734,7 @@ function normalizeSpreadsheetDataValidationItem(content, sheet, ranges, value) {
8275
4734
  const operators = spreadsheetDataValidationOperators(value.type);
8276
4735
  const type2 = 'dropdown' === value.type || 'custom' === value.type ? '' : value.type2;
8277
4736
  if ('dropdown' !== value.type && 'custom' !== value.type && !operators.includes(type2)) return spreadsheetDataValidationError('invalid-operator');
8278
- const normalizedValues = normalizeSpreadsheetDataValidationValues(content, sheet, value.type, type2, value.value1, value.value2);
4737
+ const normalizedValues = normalizeSpreadsheetDataValidationValues(content, sheet, ranges, value.type, type2, value.value1, value.value2);
8279
4738
  if (!normalizedValues.ok) return normalizedValues;
8280
4739
  return {
8281
4740
  ok: true,
@@ -8300,11 +4759,11 @@ function normalizeSpreadsheetDataValidationItem(content, sheet, ranges, value) {
8300
4759
  }
8301
4760
  };
8302
4761
  }
8303
- function normalizeSpreadsheetDataValidationValues(content, sheet, type, type2, rawValue1, rawValue2) {
4762
+ function normalizeSpreadsheetDataValidationValues(content, sheet, ranges, type, type2, rawValue1, rawValue2) {
8304
4763
  const value1 = rawValue1.trim();
8305
4764
  const value2 = rawValue2.trim();
8306
4765
  if (!value1) return spreadsheetDataValidationError('missing-value');
8307
- if ('dropdown' === type) return normalizeSpreadsheetListValidation(content, sheet, value1);
4766
+ if ('dropdown' === type) return normalizeSpreadsheetListValidation(content, sheet, ranges, value1);
8308
4767
  if ('custom' === type) {
8309
4768
  const formula = value1.replace(/^=/, '').trim();
8310
4769
  if (!formula || Array.from(formula).length > 255 || /[\u0000-\u001f\u007f]/u.test(formula)) return spreadsheetDataValidationError('invalid-custom-formula');
@@ -8338,7 +4797,20 @@ function normalizeSpreadsheetDataValidationValues(content, sheet, type, type2, r
8338
4797
  value2: null === second ? '' : String(second)
8339
4798
  };
8340
4799
  }
8341
- function normalizeSpreadsheetListValidation(content, sheet, value) {
4800
+ function normalizeSpreadsheetListValidation(content, sheet, ranges, value) {
4801
+ if (isSpreadsheetDependentListFormula(value)) {
4802
+ const normalized = normalizeSpreadsheetDependentListFormula(value);
4803
+ if (!normalized.ok || !normalized.formula) return spreadsheetDataValidationError('invalid-list-formula');
4804
+ for (const range of ranges){
4805
+ const resolved = resolveSpreadsheetDependentListReference(content, sheet.id ?? '', range.row[0], range.column[0], range.row[0], range.column[0], normalized.formula);
4806
+ if (!resolved.ok) return spreadsheetDataValidationError('invalid-list-formula');
4807
+ }
4808
+ return {
4809
+ ok: true,
4810
+ value1: normalized.formula,
4811
+ value2: ''
4812
+ };
4813
+ }
8342
4814
  const looksLikeReference = value.startsWith('=') || value.includes('!') || /^\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?$/i.test(value) || (content.namedRanges ?? []).some((range)=>range.name.trim().toLocaleLowerCase() === value.toLocaleLowerCase());
8343
4815
  if (looksLikeReference) {
8344
4816
  const resolved = resolveSpreadsheetGoToTarget(content, sheet.id ?? '', value);
@@ -8607,6 +5079,7 @@ function spreadsheetDataValidationError(code) {
8607
5079
  const messages = {
8608
5080
  'invalid-custom-formula': '自定义公式必须是 255 个字符以内的本地公式,且不能包含控制字符。',
8609
5081
  'invalid-date': '请输入有效的日期、Excel 日期序号或 DATE(...) 表达式。',
5082
+ 'invalid-list-formula': '动态下拉公式必须是本地 =INDIRECT(单元格或文本拼接),并解析为当前工作簿内的一行或一列区域。',
8610
5083
  'invalid-list-source': '请输入不超过 255 个字符的逗号分隔列表,或有效的单行/单列区域。',
8611
5084
  'invalid-number': '请输入有效的数字;整数验证不能使用小数边界。',
8612
5085
  'invalid-operator': '请选择与当前验证类型匹配的数据条件。',
@@ -10209,7 +6682,7 @@ function translateFormulaSegment(segment, rowOffset, columnOffset) {
10209
6682
  valid = false;
10210
6683
  return match;
10211
6684
  }
10212
- return `${prefix}${sheet ?? ''}${absoluteColumn}${spreadsheet_paste_special_cell_spreadsheetColumnLabel(translatedColumn)}${absoluteRow}${translatedRow}`;
6685
+ return `${prefix}${sheet ?? ''}${absoluteColumn}${spreadsheetColumnLabel(translatedColumn)}${absoluteRow}${translatedRow}`;
10213
6686
  });
10214
6687
  next = next.replace(new RegExp(String.raw`(^|[^A-Za-z0-9_.])(${sheetPrefix})?(\$?)([A-Za-z]{1,3}):(\$?)([A-Za-z]{1,3})(?![A-Za-z0-9_])`, 'g'), (match, prefix, sheet, leftAbsolute, left, rightAbsolute, right)=>{
10215
6688
  const leftColumn = leftAbsolute ? spreadsheetColumnIndex(left) : spreadsheetColumnIndex(left) + columnOffset;
@@ -10218,7 +6691,7 @@ function translateFormulaSegment(segment, rowOffset, columnOffset) {
10218
6691
  valid = false;
10219
6692
  return match;
10220
6693
  }
10221
- return `${prefix}${sheet ?? ''}${leftAbsolute}${spreadsheet_paste_special_cell_spreadsheetColumnLabel(leftColumn)}:${rightAbsolute}${spreadsheet_paste_special_cell_spreadsheetColumnLabel(rightColumn)}`;
6694
+ return `${prefix}${sheet ?? ''}${leftAbsolute}${spreadsheetColumnLabel(leftColumn)}:${rightAbsolute}${spreadsheetColumnLabel(rightColumn)}`;
10222
6695
  });
10223
6696
  next = next.replace(new RegExp(String.raw`(^|[^A-Za-z0-9_.])(${sheetPrefix})?(\$?)([1-9]\d{0,6}):(\$?)([1-9]\d{0,6})(?![A-Za-z0-9_])`, 'g'), (match, prefix, sheet, topAbsolute, top, bottomAbsolute, bottom)=>{
10224
6697
  const topRow = topAbsolute ? Number(top) : Number(top) + rowOffset;
@@ -10236,7 +6709,7 @@ function spreadsheetColumnIndex(value) {
10236
6709
  for (const character of value.toUpperCase())index = 26 * index + character.charCodeAt(0) - 64;
10237
6710
  return index - 1;
10238
6711
  }
10239
- function spreadsheet_paste_special_cell_spreadsheetColumnLabel(index) {
6712
+ function spreadsheetColumnLabel(index) {
10240
6713
  let value = index + 1;
10241
6714
  let label = '';
10242
6715
  while(value > 0){
@@ -11448,7 +7921,7 @@ function spreadsheetTableColumnNames(sheet, range, headerRow) {
11448
7921
  const observed = new Set();
11449
7922
  const names = [];
11450
7923
  for(let column = range.column[0]; column <= range.column[1]; column += 1){
11451
- const raw = headerRow ? spreadsheet_table_spreadsheetCellText(sheet, range.row[0], column).trim() : '';
7924
+ const raw = headerRow ? spreadsheetCellText(sheet, range.row[0], column).trim() : '';
11452
7925
  const base = validTableColumnName(raw) ? raw : `Column${column - range.column[0] + 1}`;
11453
7926
  let candidate = base;
11454
7927
  let suffix = 2;
@@ -11469,7 +7942,7 @@ function validTableColumnName(name) {
11469
7942
  function truncateTableColumnName(value, maximum) {
11470
7943
  return Array.from(value).slice(0, maximum).join('');
11471
7944
  }
11472
- function spreadsheet_table_spreadsheetCellText(sheet, row, column) {
7945
+ function spreadsheetCellText(sheet, row, column) {
11473
7946
  const cell = sheet.data?.[row]?.[column] ?? sheet.celldata?.find((candidate)=>candidate.r === row && candidate.c === column)?.v;
11474
7947
  const value = cell?.m ?? cell?.v;
11475
7948
  return null == value ? '' : String(value);
@@ -18732,9 +15205,9 @@ function appendRunSegment(target, source, absoluteStart, absoluteEnd, sourceStar
18732
15205
  });
18733
15206
  }
18734
15207
  function validTextSelection(text, selection) {
18735
- return Boolean(Number.isInteger(selection.start) && Number.isInteger(selection.end) && selection.start >= 0 && selection.start < selection.end && selection.end <= text.length && !spreadsheet_rich_text_selection_format_splitsSurrogatePair(text, selection.start) && !spreadsheet_rich_text_selection_format_splitsSurrogatePair(text, selection.end));
15208
+ return Boolean(Number.isInteger(selection.start) && Number.isInteger(selection.end) && selection.start >= 0 && selection.start < selection.end && selection.end <= text.length && !splitsSurrogatePair(text, selection.start) && !splitsSurrogatePair(text, selection.end));
18736
15209
  }
18737
- function spreadsheet_rich_text_selection_format_splitsSurrogatePair(text, offset) {
15210
+ function splitsSurrogatePair(text, offset) {
18738
15211
  if (offset <= 0 || offset >= text.length) return false;
18739
15212
  const previous = text.charCodeAt(offset - 1);
18740
15213
  const next = text.charCodeAt(offset);
@@ -18887,8 +15360,8 @@ function captureSpreadsheetRichTextSelection({ content, root, selection, sheetId
18887
15360
  });
18888
15361
  if (!sheetId || !selection || !domSelection) return null;
18889
15362
  const { editor, end, start } = domSelection;
18890
- const row = spreadsheet_rich_text_selection_controller_focusedAxisIndex(selection.row_focus, selection.row);
18891
- const column = spreadsheet_rich_text_selection_controller_focusedAxisIndex(selection.column_focus, selection.column);
15363
+ const row = focusedAxisIndex(selection.row_focus, selection.row);
15364
+ const column = focusedAxisIndex(selection.column_focus, selection.column);
18892
15365
  const source = spreadsheet_rich_text_selection_controller_sheetCellAt(content, sheetId, row, column);
18893
15366
  if (!source) return null;
18894
15367
  const sourceCell = liveSpreadsheetTextCell(editor, source);
@@ -18991,7 +15464,7 @@ function currentSpreadsheetTextEditor(snapshot) {
18991
15464
  function editorText(editor) {
18992
15465
  return editor.textContent ?? '';
18993
15466
  }
18994
- function spreadsheet_rich_text_selection_controller_focusedAxisIndex(value, axis) {
15467
+ function focusedAxisIndex(value, axis) {
18995
15468
  const minimum = spreadsheet_rich_text_selection_controller_finiteIndex(axis[0], 0);
18996
15469
  const maximum = spreadsheet_rich_text_selection_controller_finiteIndex(axis[1], minimum);
18997
15470
  const focus = spreadsheet_rich_text_selection_controller_finiteIndex(value, minimum);
@@ -27006,25 +23479,42 @@ function SpreadsheetDataValidationDialog({ source, restoreFocusTarget, onApply,
27006
23479
  })
27007
23480
  ]
27008
23481
  }),
27009
- 'dropdown' === value.type ? /*#__PURE__*/ jsx(field_Field, {
27010
- label: "来源",
27011
- required: true,
27012
- description: "输入逗号分隔项目,或单行/单列区域,例如 Ready,Blocked 或 Lists!A1:A8。",
27013
- error: visibleError ?? void 0,
27014
- children: /*#__PURE__*/ jsx("input", {
27015
- type: "text",
27016
- "aria-label": "来源",
27017
- autoCapitalize: "none",
27018
- spellCheck: false,
27019
- value: value.value1,
27020
- onBlur: ()=>setTouched(true),
27021
- onChange: (event)=>{
27022
- update({
27023
- value1: event.currentTarget.value
27024
- });
27025
- setTouched(true);
27026
- }
27027
- })
23482
+ 'dropdown' === value.type ? /*#__PURE__*/ jsxs("div", {
23483
+ className: "work-spreadsheet-data-validation-list-source",
23484
+ children: [
23485
+ /*#__PURE__*/ jsx(field_Field, {
23486
+ label: "来源",
23487
+ required: true,
23488
+ description: isSpreadsheetDependentListFormula(value.value1) ? '动态来源支持 =INDIRECT(单元格或文本拼接),驱动单元格需返回区域或已定义名称。' : '输入逗号分隔项目,或单行/单列区域,例如 Ready,Blocked 或 Lists!A1:A8。',
23489
+ error: visibleError ?? void 0,
23490
+ children: /*#__PURE__*/ jsx("input", {
23491
+ type: "text",
23492
+ "aria-label": "来源",
23493
+ autoCapitalize: "none",
23494
+ spellCheck: false,
23495
+ value: value.value1,
23496
+ onBlur: ()=>setTouched(true),
23497
+ onChange: (event)=>{
23498
+ update({
23499
+ value1: event.currentTarget.value
23500
+ });
23501
+ setTouched(true);
23502
+ }
23503
+ })
23504
+ }),
23505
+ isSpreadsheetDependentListFormula(value.value1) && /*#__PURE__*/ jsxs("p", {
23506
+ className: "work-spreadsheet-data-validation-formula-note",
23507
+ children: [
23508
+ /*#__PURE__*/ jsx(Sigma, {
23509
+ size: 14,
23510
+ "aria-hidden": "true"
23511
+ }),
23512
+ /*#__PURE__*/ jsx("span", {
23513
+ children: "每个受验证单元格会按相对引用重新解析来源;空驱动值会显示为空列表,引用范围限制为本地有限区域。"
23514
+ })
23515
+ ]
23516
+ })
23517
+ ]
27028
23518
  }) : 'custom' === value.type ? /*#__PURE__*/ jsx(SpreadsheetDataValidationCustomFormulaField, {
27029
23519
  value: value.value1,
27030
23520
  error: visibleError,
@@ -30316,6 +26806,46 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
30316
26806
  const formatCellsInvokerRef = useRef(null);
30317
26807
  const formatCellsSelectionRef = useRef(null);
30318
26808
  const formatCellsApplyingRef = useRef(false);
26809
+ useEffect(()=>{
26810
+ const container = spreadsheetCanvasRef.current;
26811
+ if (!container || preview || !selectionState) return;
26812
+ const frame = requestAnimationFrame(()=>{
26813
+ const button = container.querySelector('#luckysheet-dataVerification-dropdown-btn');
26814
+ const selectedCell = container.querySelector('#luckysheet-cell-selected');
26815
+ const cellArea = selectedCell?.closest('.fortune-cell-area');
26816
+ const sheet = materializedContent.sheets.find((candidate)=>candidate.id === selectionState.sheetId);
26817
+ const row = selectionState.selection.row[0];
26818
+ const column = selectionState.selection.column[0];
26819
+ const item = sheet && null != row && null != column ? spreadsheetDataValidationItemAt(sheet, row, column) : null;
26820
+ if (!button || !selectedCell || !cellArea || !item || 'dropdown' !== item.type || false === item.showDropdownArrow) {
26821
+ if (button) button.style.display = 'none';
26822
+ return;
26823
+ }
26824
+ button.setAttribute('role', 'button');
26825
+ button.setAttribute('aria-label', '打开下拉列表');
26826
+ button.setAttribute('aria-haspopup', 'listbox');
26827
+ button.setAttribute('title', '打开下拉列表');
26828
+ const cellBounds = selectedCell.getBoundingClientRect();
26829
+ const areaBounds = cellArea.getBoundingClientRect();
26830
+ const buttonWidth = 20;
26831
+ const buttonHeight = 20;
26832
+ const maxLeft = Math.max(0, areaBounds.width - buttonWidth);
26833
+ const maxTop = Math.max(0, areaBounds.height - buttonHeight);
26834
+ const left = Math.max(0, Math.min(cellBounds.right - areaBounds.left - buttonWidth, maxLeft));
26835
+ const top = Math.max(0, Math.min(cellBounds.top - areaBounds.top + (cellBounds.height - buttonHeight) / 2 - 2, maxTop));
26836
+ button.style.display = 'block';
26837
+ button.style.maxWidth = `${Math.max(0, cellBounds.width)}px`;
26838
+ button.style.maxHeight = `${Math.max(0, cellBounds.height)}px`;
26839
+ button.style.left = `${left}px`;
26840
+ button.style.top = `${top}px`;
26841
+ });
26842
+ return ()=>cancelAnimationFrame(frame);
26843
+ }, [
26844
+ materializedContent,
26845
+ preview,
26846
+ selectionState,
26847
+ workbookMountRevision
26848
+ ]);
30319
26849
  const focusSpreadsheetDialogGrid = useCallback((focusOrigin)=>focusSpreadsheetGrid(spreadsheetCanvasRef.current, {
30320
26850
  focusOrigin
30321
26851
  }), []);
@@ -30712,7 +27242,8 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
30712
27242
  materializedContent,
30713
27243
  requestedWorkbookSelection
30714
27244
  ]);
30715
- const workbookSheets = useMemo(()=>spreadsheetSheetsForFortune(renderedWorkbookSheets), [
27245
+ const workbookSheets = useMemo(()=>spreadsheetSheetsForFortune(renderedWorkbookSheets, materializedContent.namedRanges), [
27246
+ materializedContent.namedRanges,
30716
27247
  renderedWorkbookSheets
30717
27248
  ]);
30718
27249
  const displayedWorkbookSheets = useMemo(()=>preview ? workbookSheets.map((sheet)=>({