@a3s-lab/office 0.42.0 → 0.44.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 (42) hide show
  1. package/README.md +33 -1
  2. package/dist/0~1403.js +1 -1
  3. package/dist/0~2330.js +1 -1
  4. package/dist/0~3539.js +2 -2
  5. package/dist/0~3557.js +1 -1
  6. package/dist/0~4980.js +1 -1
  7. package/dist/0~5024.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 +4 -3
  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 +314 -3617
  15. package/dist/0~work-docx-export.js +1 -1
  16. package/dist/0~work-docx-import.js +1 -1
  17. package/dist/0~work-office-diagnostics.js +1 -1
  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 +3 -3
  24. package/dist/4560.js +2 -2
  25. package/dist/{5416.js → 610.js} +4335 -410
  26. package/dist/{4121.js → 6968.js} +875 -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/spreadsheet-conditional-format-model.d.ts +6 -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-spreadsheet-conditional-format.d.ts +3 -1
  38. package/dist/internal/features/work/work-spreadsheet-local-formula.d.ts +43 -0
  39. package/dist/internal/features/work/work-types.d.ts +4 -0
  40. package/dist/office-kernel.wasm +0 -0
  41. package/dist/styles.css +13 -1
  42. package/package.json +4 -1
@@ -4,18 +4,18 @@ 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
6
  import { convertSpanToShareString, genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
7
- import { Parser, SUPPORTED_FORMULAS } from "@fortune-sheet/formula-parser";
8
- 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";
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";
10
- 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";
11
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";
12
- import { isOfficeKernelSpreadsheetError, normalizeCssColor, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, createOfficeId as createWorkId } from "./4121.js";
13
- import { useOfficeTaskPaneEscape, OfficeCheckbox, CommittedOfficeNumberField, useOfficeDialog, useOfficeTaskPaneModal, createOfficeKernelClient, officeFontFamilies, CollectionState, OfficeNumberField, handleOfficeTaskPaneKeyDown, normalizeFormulaForFortuneParser } from "./0~7048.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,395 +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
- function spreadsheetConditionalFormatStyles(sheet) {
163
- const styles = new Map();
164
- const data = sheet.data ?? [];
165
- if (!data.length) return styles;
166
- const blocked = new Set();
167
- for (const rule of conditionalRules(sheet)){
168
- const matched = new Set();
169
- if ('colorGradation' === rule.type) applyColorScale(styles, data, rule, blocked, matched);
170
- else if ('dataBar' === rule.type) applyDataBars(styles, data, rule, blocked, matched);
171
- else if ('icons' === rule.type) applyIconSet(styles, data, rule, blocked, matched);
172
- else applyDefaultRule(styles, data, rule, blocked, matched);
173
- if (rule.stopIfTrue) for (const key of matched)blocked.add(key);
174
- }
175
- return styles;
176
- }
177
- function applyDefaultRule(styles, data, rule, blocked, matched) {
178
- if ('default' !== rule.type || !isConditionalFormatStyle(rule.format) || !rule.conditionName) return;
179
- const values = (rule.conditionValue ?? []).map(String);
180
- const style = rule.format;
181
- if ('formula' === rule.conditionName && values[0]) {
182
- for (const range of rule.cellrange)forEachCell(data, [
183
- range
184
- ], ({ row, column })=>{
185
- if (evaluateFormula(values[0], row, column, range, data)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
186
- });
187
- return;
188
- }
189
- if ('duplicateValue' === rule.conditionName) {
190
- for (const range of rule.cellrange){
191
- const positions = cellPositions(data, [
192
- range
193
- ]).filter(({ row, column })=>null != work_spreadsheet_conditional_format_cellValue(data, row, column));
194
- const occurrences = new Map();
195
- for (const { row, column } of positions){
196
- const key = comparableKey(work_spreadsheet_conditional_format_cellValue(data, row, column));
197
- occurrences.set(key, (occurrences.get(key) ?? 0) + 1);
198
- }
199
- const unique = '1' === values[0];
200
- for (const { row, column } of positions){
201
- const count = occurrences.get(comparableKey(work_spreadsheet_conditional_format_cellValue(data, row, column))) ?? 0;
202
- if (unique && 1 === count || !unique && count > 1) applyDifferentialStyle(styles, blocked, matched, row, column, style);
203
- }
204
- }
205
- return;
206
- }
207
- if ([
208
- 'top10',
209
- 'top10_percent',
210
- 'last10',
211
- 'last10_percent'
212
- ].includes(rule.conditionName)) {
213
- for (const range of rule.cellrange)applyRankRule(styles, data, range, rule.conditionName, values[0], style, blocked, matched);
214
- return;
215
- }
216
- if ('aboveAverage' === rule.conditionName || 'belowAverage' === rule.conditionName) {
217
- for (const range of rule.cellrange)applyAverageRule(styles, data, range, rule.conditionName, style, blocked, matched);
218
- return;
219
- }
220
- forEachCell(data, rule.cellrange, ({ row, column })=>{
221
- const current = work_spreadsheet_conditional_format_cellValue(data, row, column);
222
- if (matchesCondition(current, rule.conditionName, values)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
223
- });
224
- }
225
- function applyRankRule(styles, data, range, conditionName, sourceRank, style, blocked, matched) {
226
- const positions = cellPositions(data, [
227
- range
228
- ]);
229
- const numbers = positions.flatMap(({ row, column })=>{
230
- const value = numericCellValue(data, row, column);
231
- return null === value ? [] : [
232
- value
233
- ];
234
- });
235
- if (!numbers.length) return;
236
- numbers.sort((left, right)=>right - left);
237
- const requested = Math.max(1, Math.trunc(Number(sourceRank) || 10));
238
- const count = conditionName.endsWith('_percent') ? Math.max(1, Math.ceil(Math.min(100, requested) / 100 * numbers.length)) : Math.min(numbers.length, requested);
239
- const bottom = conditionName.startsWith('last');
240
- const threshold = bottom ? numbers[numbers.length - count] : numbers[count - 1];
241
- for (const { row, column } of positions){
242
- const value = numericCellValue(data, row, column);
243
- if (null !== value && (bottom ? value <= threshold : value >= threshold)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
244
- }
245
- }
246
- function applyAverageRule(styles, data, range, conditionName, style, blocked, matched) {
247
- const positions = cellPositions(data, [
248
- range
249
- ]);
250
- const values = positions.flatMap(({ row, column })=>{
251
- const value = numericCellValue(data, row, column);
252
- return null === value ? [] : [
253
- value
254
- ];
255
- });
256
- if (!values.length) return;
257
- const average = values.reduce((sum, value)=>sum + value, 0) / values.length;
258
- for (const { row, column } of positions){
259
- const value = numericCellValue(data, row, column);
260
- if (null !== value && ('aboveAverage' === conditionName ? value > average : value < average)) applyDifferentialStyle(styles, blocked, matched, row, column, style);
261
- }
262
- }
263
- function applyColorScale(styles, data, rule, blocked, matched) {
264
- if (!Array.isArray(rule.format) || rule.format.length < 2) return;
265
- const positions = cellPositions(data, rule.cellrange);
266
- const numericPositions = positions.flatMap((position)=>{
267
- const value = numericCellValue(data, position.row, position.column);
268
- return null === value ? [] : [
269
- {
270
- ...position,
271
- value
272
- }
273
- ];
274
- });
275
- if (!numericPositions.length) return;
276
- const colors = rule.format.slice(0, 3).map(parseSpreadsheetConditionalColor);
277
- if (colors.some((color)=>!color)) return;
278
- const values = numericPositions.map(({ value })=>value);
279
- const options = rule.visualOptions ? normalizeSpreadsheetConditionalVisualOptions(rule.visualOptions, colors.length, 'colorScale') : {
280
- thresholds: defaultSpreadsheetColorScaleThresholds(colors.length)
281
- };
282
- if (!options) return;
283
- const points = options.thresholds.map((threshold)=>spreadsheetConditionalThresholdValue(threshold, values));
284
- if (points.some((point)=>null === point)) return;
285
- const minimum = points[0];
286
- const midpoint = 3 === points.length ? points[1] : null;
287
- const maximum = points.at(-1);
288
- const maximumColor = colors[0];
289
- const midpointColor = 3 === colors.length ? colors[1] : null;
290
- const minimumColor = colors.at(-1);
291
- for (const { row, column, value } of numericPositions){
292
- let color;
293
- 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));
294
- applyConditionalStyle(styles, blocked, matched, row, column, {
295
- cellColor: spreadsheetConditionalCssColor(color)
296
- });
297
- }
298
- }
299
- function applyDataBars(styles, data, rule, blocked, matched) {
300
- if (!isConditionalFormatStyle(rule.format)) return;
301
- const color = rule.format.cellColor;
302
- if (!color) return;
303
- const positions = cellPositions(data, rule.cellrange);
304
- const numericPositions = positions.flatMap((position)=>{
305
- const value = numericCellValue(data, position.row, position.column);
306
- return null === value ? [] : [
307
- {
308
- ...position,
309
- value
310
- }
311
- ];
312
- });
313
- if (!numericPositions.length) return;
314
- const values = numericPositions.map(({ value })=>value);
315
- const options = rule.visualOptions ? normalizeSpreadsheetConditionalVisualOptions(rule.visualOptions, 2, 'dataBar') : defaultSpreadsheetDataBarOptions();
316
- if (!options) return;
317
- const points = options.thresholds.map((threshold)=>spreadsheetConditionalThresholdValue(threshold, values));
318
- if (points.some((point)=>null === point)) return;
319
- const minimum = points[0];
320
- const maximum = points[1];
321
- for (const { row, column, value } of numericPositions)applyConditionalStyle(styles, blocked, matched, row, column, {
322
- dataBar: spreadsheetConditionalDataBar(color, value, minimum, maximum, options.minLength ?? 10, options.maxLength ?? 90, false !== options.showValue)
323
- });
324
- }
325
- function applyIconSet(styles, data, rule, blocked, matched) {
326
- if ('icons' !== rule.type) return;
327
- const format = normalizeSpreadsheetConditionalIconSetFormat(rule.format);
328
- if (!format) return;
329
- const numericPositions = cellPositions(data, rule.cellrange).flatMap((position)=>{
330
- const value = numericCellValue(data, position.row, position.column);
331
- return null === value ? [] : [
332
- {
333
- ...position,
334
- value
335
- }
336
- ];
337
- });
338
- const values = numericPositions.map(({ value })=>value);
339
- for (const { row, column, value } of numericPositions){
340
- const icon = spreadsheetConditionalIconForValue(format, value, values);
341
- if (icon) applyConditionalStyle(styles, blocked, matched, row, column, {
342
- icon
343
- });
344
- }
345
- }
346
- function matchesCondition(value, name, conditions) {
347
- if (null == value) return false;
348
- if ('textContains' === name) return String(value).toLowerCase().includes((conditions[0] ?? '').toLowerCase());
349
- if ('equal' === name) return 0 === compareValues(value, conditions[0]);
350
- if ('notEqual' === name) return 0 !== compareValues(value, conditions[0]);
351
- if ('greaterThan' === name) return compareValues(value, conditions[0]) > 0;
352
- if ('greaterThanOrEqual' === name) return compareValues(value, conditions[0]) >= 0;
353
- if ('lessThan' === name) return compareValues(value, conditions[0]) < 0;
354
- if ('lessThanOrEqual' === name) return compareValues(value, conditions[0]) <= 0;
355
- if ('between' === name || 'notBetween' === name) {
356
- const lower = Math.min(Number(conditions[0]), Number(conditions[1]));
357
- const upper = Math.max(Number(conditions[0]), Number(conditions[1]));
358
- if (!Number.isFinite(lower) || !Number.isFinite(upper)) return false;
359
- const between = Number(value) >= lower && Number(value) <= upper;
360
- return 'between' === name ? between : !between;
361
- }
362
- return false;
363
- }
364
- function evaluateFormula(source, row, column, baseRange, data) {
365
- const formula = source.trim().replace(/^=/, '').replace(/\s+/g, '');
366
- if (/^TRUE\(\)?$/i.test(formula)) return true;
367
- if (/^FALSE\(\)?$/i.test(formula)) return false;
368
- const parity = /^MOD\((ROW|COLUMN)\(\),(\d+)\)(=|<>|>=|<=|>|<)(-?\d+)$/i.exec(formula);
369
- if (parity) {
370
- const value = 'ROW' === parity[1].toUpperCase() ? row + 1 : column + 1;
371
- return compareWithOperator(value % Number(parity[2]), parity[3], Number(parity[4]));
372
- }
373
- const blank = /^(NOT\()?ISBLANK\((\$?)([A-Z]{1,3})(\$?)([1-9]\d*)\)\)?$/i.exec(formula);
374
- if (blank) {
375
- const target = formulaReference(blank[2], blank[3], blank[4], blank[5], row, column, baseRange);
376
- const empty = null == work_spreadsheet_conditional_format_cellValue(data, target.row, target.column) || '' === work_spreadsheet_conditional_format_cellValue(data, target.row, target.column);
377
- return blank[1] ? !empty : empty;
378
- }
379
- const comparison = /^(\$?)([A-Z]{1,3})(\$?)([1-9]\d*)(=|<>|>=|<=|>|<)(?:"((?:[^"]|"")*)"|(-?\d+(?:\.\d+)?))$/i.exec(formula);
380
- if (!comparison) return false;
381
- const target = formulaReference(comparison[1], comparison[2], comparison[3], comparison[4], row, column, baseRange);
382
- const left = work_spreadsheet_conditional_format_cellValue(data, target.row, target.column);
383
- const right = void 0 !== comparison[6] ? comparison[6].replaceAll('""', '"') : Number(comparison[7]);
384
- return compareWithOperator(left, comparison[5], right);
385
- }
386
- function formulaReference(absoluteColumn, columnName, absoluteRow, rowNumber, row, column, baseRange) {
387
- const sourceColumn = decodeColumn(columnName);
388
- const sourceRow = Number(rowNumber) - 1;
389
- return {
390
- row: absoluteRow ? sourceRow : sourceRow + row - baseRange.row[0],
391
- column: absoluteColumn ? sourceColumn : sourceColumn + column - baseRange.column[0]
392
- };
393
- }
394
- function applyDifferentialStyle(styles, blocked, matched, row, column, style) {
395
- const patch = {};
396
- if (style.textColor) patch.textColor = style.textColor;
397
- if (style.cellColor) patch.cellColor = style.cellColor;
398
- applyConditionalStyle(styles, blocked, matched, row, column, patch);
399
- }
400
- function applyConditionalStyle(styles, blocked, matched, row, column, patch) {
401
- const key = cellKey(row, column);
402
- if (blocked.has(key)) return;
403
- matched.add(key);
404
- if (Object.keys(patch).length) patchCellStyle(styles, row, column, patch);
405
- }
406
- function patchCellStyle(styles, row, column, patch) {
407
- const key = cellKey(row, column);
408
- styles.set(key, {
409
- ...patch,
410
- ...styles.get(key)
411
- });
412
- }
413
- function conditionalRules(sheet) {
414
- if (!Array.isArray(sheet.luckysheet_conditionformat_save)) return [];
415
- return sheet.luckysheet_conditionformat_save.filter(isConditionalRule);
416
- }
417
- function isConditionalRule(value) {
418
- if (!value || 'object' != typeof value) return false;
419
- const rule = value;
420
- return [
421
- 'default',
422
- 'colorGradation',
423
- 'dataBar',
424
- 'icons'
425
- ].includes(String(rule.type)) && Array.isArray(rule.cellrange);
426
- }
427
- function isConditionalFormatStyle(value) {
428
- return Boolean(value) && !Array.isArray(value) && 'object' == typeof value && !('iconSet' in value) && 'textColor' in value && 'cellColor' in value;
429
- }
430
- function cellPositions(data, ranges) {
431
- const positions = [];
432
- const seen = new Set();
433
- forEachCell(data, ranges, (position)=>{
434
- const key = cellKey(position.row, position.column);
435
- if (!seen.has(key)) {
436
- seen.add(key);
437
- positions.push(position);
438
- }
439
- });
440
- return positions;
441
- }
442
- function cellKey(row, column) {
443
- return `${row}_${column}`;
444
- }
445
- function forEachCell(data, ranges, visit) {
446
- 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({
447
- row,
448
- column
449
- });
450
- }
451
- function work_spreadsheet_conditional_format_cellValue(data, row, column) {
452
- return data[row]?.[column]?.v ?? null;
453
- }
454
- function numericCellValue(data, row, column) {
455
- const value = work_spreadsheet_conditional_format_cellValue(data, row, column);
456
- return 'number' == typeof value && Number.isFinite(value) ? value : null;
457
- }
458
- function comparableKey(value) {
459
- return `${typeof value}:${String(value)}`;
460
- }
461
- function compareValues(left, right) {
462
- const leftNumber = Number(left);
463
- const rightNumber = Number(right);
464
- if (String(left).trim() && String(right).trim() && Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) return leftNumber === rightNumber ? 0 : leftNumber > rightNumber ? 1 : -1;
465
- return String(left).localeCompare(String(right));
466
- }
467
- function compareWithOperator(left, operator, right) {
468
- const comparison = compareValues(left, right);
469
- if ('=' === operator) return 0 === comparison;
470
- if ('<>' === operator) return 0 !== comparison;
471
- if ('>' === operator) return comparison > 0;
472
- if ('<' === operator) return comparison < 0;
473
- if ('>=' === operator) return comparison >= 0;
474
- return '<=' === operator && comparison <= 0;
475
- }
476
- function decodeColumn(value) {
477
- let result = 0;
478
- for (const character of value.toUpperCase())result = 26 * result + character.charCodeAt(0) - 64;
479
- return result - 1;
480
- }
481
92
  function drawSpreadsheetDiagonalDownBorder(context, bounds, line) {
482
93
  drawSpreadsheetDiagonalBorder(context, bounds, line, 'down');
483
94
  }
@@ -788,102 +399,6 @@ function restoreSpreadsheetNativeFillContext(context) {
788
399
  else delete context.fillRect;
789
400
  pendingContextRestores.delete(context);
790
401
  }
791
- function spreadsheet_cell_range_normalizeSpreadsheetCellRange(value) {
792
- const row = normalizeSpreadsheetCellRangeAxis(value.row);
793
- const column = normalizeSpreadsheetCellRangeAxis(value.column);
794
- return row && column ? {
795
- row,
796
- column
797
- } : null;
798
- }
799
- function parseSpreadsheetCellRange(value) {
800
- if (!isRecord(value)) return null;
801
- const row = normalizeSpreadsheetCellRangeAxis(value.row);
802
- const column = normalizeSpreadsheetCellRangeAxis(value.column);
803
- return row && column ? {
804
- row,
805
- column
806
- } : null;
807
- }
808
- function subtractSpreadsheetCellRange(source, removed) {
809
- const top = Math.max(source.row[0], removed.row[0]);
810
- const bottom = Math.min(source.row[1], removed.row[1]);
811
- const left = Math.max(source.column[0], removed.column[0]);
812
- const right = Math.min(source.column[1], removed.column[1]);
813
- if (top > bottom || left > right) return [
814
- source
815
- ];
816
- const remaining = [];
817
- if (source.row[0] < top) remaining.push({
818
- row: [
819
- source.row[0],
820
- top - 1
821
- ],
822
- column: [
823
- ...source.column
824
- ]
825
- });
826
- if (bottom < source.row[1]) remaining.push({
827
- row: [
828
- bottom + 1,
829
- source.row[1]
830
- ],
831
- column: [
832
- ...source.column
833
- ]
834
- });
835
- if (source.column[0] < left) remaining.push({
836
- row: [
837
- top,
838
- bottom
839
- ],
840
- column: [
841
- source.column[0],
842
- left - 1
843
- ]
844
- });
845
- if (right < source.column[1]) remaining.push({
846
- row: [
847
- top,
848
- bottom
849
- ],
850
- column: [
851
- right + 1,
852
- source.column[1]
853
- ]
854
- });
855
- return remaining;
856
- }
857
- function spreadsheetCellRangesIntersect(left, right) {
858
- 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]);
859
- }
860
- function spreadsheetCellRangesEqual(left, right) {
861
- const normalizedLeft = spreadsheet_cell_range_normalizeSpreadsheetCellRange(left);
862
- const normalizedRight = spreadsheet_cell_range_normalizeSpreadsheetCellRange(right);
863
- 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]);
864
- }
865
- function spreadsheetCellRangeContains(range, row, column) {
866
- return row >= range.row[0] && row <= range.row[1] && column >= range.column[0] && column <= range.column[1];
867
- }
868
- function spreadsheet_cell_range_spreadsheetCellRangeArea(range) {
869
- return (range.row[1] - range.row[0] + 1) * (range.column[1] - range.column[0] + 1);
870
- }
871
- function normalizeSpreadsheetCellRangeAxis(value) {
872
- if (!Array.isArray(value)) return null;
873
- const first = finiteSpreadsheetCellIndex(value[0]);
874
- const second = finiteSpreadsheetCellIndex(value[1]);
875
- if (null === first || null === second) return null;
876
- return [
877
- Math.min(first, second),
878
- Math.max(first, second)
879
- ];
880
- }
881
- function finiteSpreadsheetCellIndex(value) {
882
- return 'number' == typeof value && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
883
- }
884
- function isRecord(value) {
885
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
886
- }
887
402
  const spreadsheetCellBorderTargets = [
888
403
  'top',
889
404
  'bottom',
@@ -981,9 +496,9 @@ function spreadsheetCellBordersAt(sheet, row, column) {
981
496
  const borders = {};
982
497
  const source = Array.isArray(sheet?.config?.borderInfo) ? sheet.config.borderInfo : [];
983
498
  for (const candidate of source){
984
- if (!spreadsheet_cell_border_isRecord(candidate)) continue;
499
+ if (!isRecord(candidate)) continue;
985
500
  if ('cell' === candidate.rangeType) {
986
- if (!spreadsheet_cell_border_isRecord(candidate.value)) continue;
501
+ if (!isRecord(candidate.value)) continue;
987
502
  if (finiteSpreadsheetBorderIndex(candidate.value.row_index) !== row || finiteSpreadsheetBorderIndex(candidate.value.col_index) !== column) continue;
988
503
  applyResolvedCellBorderLine(borders, 'left', candidate.value.l);
989
504
  applyResolvedCellBorderLine(borders, 'right', candidate.value.r);
@@ -1005,9 +520,9 @@ function spreadsheetRenderableDiagonalBorders(sheet) {
1005
520
  const borders = new Map();
1006
521
  const source = Array.isArray(sheet.config?.borderInfo) ? sheet.config.borderInfo : [];
1007
522
  for (const candidate of source){
1008
- if (!spreadsheet_cell_border_isRecord(candidate)) continue;
523
+ if (!isRecord(candidate)) continue;
1009
524
  if ('cell' === candidate.rangeType) {
1010
- if (!spreadsheet_cell_border_isRecord(candidate.value)) continue;
525
+ if (!isRecord(candidate.value)) continue;
1011
526
  const row = finiteSpreadsheetBorderIndex(candidate.value.row_index);
1012
527
  const column = finiteSpreadsheetBorderIndex(candidate.value.col_index);
1013
528
  if (null === row || null === column) continue;
@@ -1086,11 +601,11 @@ function spreadsheetBorderInfoIsWritable(sheet) {
1086
601
  function compactSpreadsheetBorderInfo(formats, range, appliedType) {
1087
602
  const supersedesAll = 'border-all' === appliedType || 'border-none' === appliedType;
1088
603
  return formats.flatMap((format)=>{
1089
- if (!spreadsheet_cell_border_isRecord(format)) return [
604
+ if (!isRecord(format)) return [
1090
605
  format
1091
606
  ];
1092
607
  if ('cell' === format.rangeType) {
1093
- if (!spreadsheet_cell_border_isRecord(format.value)) return [
608
+ if (!isRecord(format.value)) return [
1094
609
  format
1095
610
  ];
1096
611
  const row = finiteSpreadsheetBorderIndex(format.value.row_index);
@@ -1177,9 +692,9 @@ function spreadsheetDiagonalBorderDirectionsInRange(sheet, selection) {
1177
692
  const directions = new Map();
1178
693
  const source = Array.isArray(sheet.config?.borderInfo) ? sheet.config.borderInfo : [];
1179
694
  for (const candidate of source){
1180
- if (!spreadsheet_cell_border_isRecord(candidate)) continue;
695
+ if (!isRecord(candidate)) continue;
1181
696
  if ('cell' === candidate.rangeType) {
1182
- if (!spreadsheet_cell_border_isRecord(candidate.value)) continue;
697
+ if (!isRecord(candidate.value)) continue;
1183
698
  const row = finiteSpreadsheetBorderIndex(candidate.value.row_index);
1184
699
  const column = finiteSpreadsheetBorderIndex(candidate.value.col_index);
1185
700
  if (null === row || null === column || !spreadsheetCellRangeContains(selection, row, column)) continue;
@@ -1237,7 +752,7 @@ function contentWithSpreadsheetBorderInfo(content, sheetIndex, sheet, borderInfo
1237
752
  }
1238
753
  function applyResolvedCellBorderLine(borders, side, value) {
1239
754
  if (null === value) return void delete borders[side];
1240
- if (!spreadsheet_cell_border_isRecord(value)) return;
755
+ if (!isRecord(value)) return;
1241
756
  const color = 'string' == typeof value.color ? normalizeSpreadsheetBorderColor(value.color) : null;
1242
757
  const style = 'string' == typeof value.style ? value.style : null;
1243
758
  if (color && style) borders[side] = {
@@ -1257,7 +772,7 @@ function applyResolvedCellDiagonalBorders(borders, value) {
1257
772
  if (diagonal.up) borders.diagonalUp = line;
1258
773
  }
1259
774
  function resolvedCellBorderLine(value) {
1260
- if (!spreadsheet_cell_border_isRecord(value)) return null;
775
+ if (!isRecord(value)) return null;
1261
776
  const color = 'string' == typeof value.color ? normalizeSpreadsheetBorderColor(value.color) : null;
1262
777
  const style = 'string' == typeof value.style ? value.style : null;
1263
778
  return color && style ? {
@@ -1346,7 +861,7 @@ function isSpreadsheetNativeBorderType(value) {
1346
861
  function finiteSpreadsheetBorderIndex(value) {
1347
862
  return 'number' == typeof value && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
1348
863
  }
1349
- function spreadsheet_cell_border_isRecord(value) {
864
+ function isRecord(value) {
1350
865
  return Boolean(value && 'object' == typeof value && !Array.isArray(value));
1351
866
  }
1352
867
  function useSpreadsheetCollaborationPresenceProjection({ content, workbook }) {
@@ -3058,2781 +2573,55 @@ function spreadsheetNativeMergeRange(value) {
3058
2573
  if (null === row || null === column || null === rowSpan || null === columnSpan || !Number.isSafeInteger(row + rowSpan - 1) || !Number.isSafeInteger(column + columnSpan - 1)) return null;
3059
2574
  return {
3060
2575
  row: [
3061
- row,
3062
- row + rowSpan - 1
3063
- ],
3064
- column: [
3065
- column,
3066
- column + columnSpan - 1
3067
- ]
3068
- };
3069
- }
3070
- function spreadsheetCellIsLocked(sheet, cellAt, row, column) {
3071
- const cell = cellAt(row, column);
3072
- if (cell?.lo !== void 0 && null !== cell.lo) return Boolean(cell.lo);
3073
- const authority = sheet.config?.authority;
3074
- if (!spreadsheet_cell_mutation_guard_isRecord(authority)) return false;
3075
- const ranges = authority.cellProtectionRanges;
3076
- if (Array.isArray(ranges)) for(let index = ranges.length - 1; index >= 0; index -= 1){
3077
- const candidate = ranges[index];
3078
- if (!spreadsheet_cell_mutation_guard_isRecord(candidate)) continue;
3079
- const protectedRange = parseSpreadsheetCellRange(candidate.range);
3080
- if (protectedRange && row >= protectedRange.row[0] && row <= protectedRange.row[1] && column >= protectedRange.column[0] && column <= protectedRange.column[1]) return false !== candidate.locked;
3081
- }
3082
- return void 0 !== authority.sheet && 0 !== authority.sheet;
3083
- }
3084
- function createSpreadsheetMutationCellReader(sheet) {
3085
- let sparseRows = null;
3086
- const sparseCell = (row, column)=>{
3087
- if (!sparseRows) {
3088
- sparseRows = new Map();
3089
- for (const entry of sheet.celldata ?? []){
3090
- let sparseRow = sparseRows.get(entry.r);
3091
- if (!sparseRow) {
3092
- sparseRow = new Map();
3093
- sparseRows.set(entry.r, sparseRow);
3094
- }
3095
- sparseRow.set(entry.c, entry.v);
3096
- }
3097
- }
3098
- return sparseRows.get(row)?.get(column);
3099
- };
3100
- return (row, column)=>sheet.data?.[row]?.[column] ?? sparseCell(row, column);
3101
- }
3102
- function finiteIndex(value) {
3103
- return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : null;
3104
- }
3105
- function positiveSpan(value) {
3106
- return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : null;
3107
- }
3108
- function spreadsheet_cell_mutation_guard_isRecord(value) {
3109
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
3110
- }
3111
- function reconcileSpreadsheetRichTextCellEdit(previous, current, formattedPaste) {
3112
- if (current.f) return current;
3113
- const source = formattedPaste ? normalizeXlsxRichTextEditSource(previous) : previous ? normalizeXlsxRichTextCell(previous) : null;
3114
- if (!source) return current;
3115
- const live = liveRichTextValue(current);
3116
- if (!live || '' === live.text) return current;
3117
- const replacement = richTextReplacement(source.text, live.text);
3118
- const sourceRuns = indexRichTextRuns(source);
3119
- const formattedPasteRuns = formattedPaste ? reconstructFormattedPasteRuns(source, sourceRuns, live.text, formattedPaste) : null;
3120
- const runs = formattedPasteRuns ?? (live.rich && live.text === source.text ? restoreSemanticColorOrigins(live.rich.runs, sourceRuns, replacement) : reconstructPlainTextRuns(sourceRuns, replacement));
3121
- const normalizedRuns = coalesceXlsxRichTextRuns(runs);
3122
- if (!normalizedRuns.length || normalizedRuns.length > 512 || normalizedRuns.map((run)=>run.v).join('') !== live.text) return current;
3123
- return spreadsheetRichTextCellWithRuns(previous, current, live.text, normalizedRuns);
3124
- }
3125
- function restoreSpreadsheetRichTextCellRuns(previous, current) {
3126
- if (!previous || current.f) return current;
3127
- const source = normalizeXlsxRichTextCell(previous);
3128
- const live = liveRichTextValue(current);
3129
- if (!source || !live || source.text !== live.text) return current;
3130
- return spreadsheetRichTextCellWithRuns(previous, current, source.text, source.runs);
3131
- }
3132
- function spreadsheetRichTextCellWithRuns(previous, current, text, runs) {
3133
- const next = {
3134
- ...current,
3135
- ct: {
3136
- ...previous?.ct,
3137
- ...current.ct,
3138
- s: runs.map((run)=>({
3139
- ...run
3140
- })),
3141
- t: 'inlineStr'
3142
- },
3143
- v: text
3144
- };
3145
- delete next.f;
3146
- delete next.m;
3147
- return next;
3148
- }
3149
- function sameSpreadsheetRichTextCellText(previous, current) {
3150
- if (!previous || current.f) return false;
3151
- const source = normalizeXlsxRichTextCell(previous);
3152
- const live = liveRichTextValue(current);
3153
- return Boolean(source && live && source.text === live.text);
3154
- }
3155
- function liveRichTextValue(cell) {
3156
- const rich = normalizeXlsxRichTextCell(cell);
3157
- if ('string' == typeof cell.v && cell.v !== rich?.text) return validBoundedText(cell.v) ? {
3158
- rich: null,
3159
- text: cell.v
3160
- } : null;
3161
- if (rich) return {
3162
- rich,
3163
- text: rich.text
3164
- };
3165
- return 'string' == typeof cell.v && validBoundedText(cell.v) ? {
3166
- rich: null,
3167
- text: cell.v
3168
- } : null;
3169
- }
3170
- function validBoundedText(value) {
3171
- return value.length <= 32767 && validXlsxRichText(value);
3172
- }
3173
- function richTextReplacement(previous, current) {
3174
- let left = 0;
3175
- let right = 0;
3176
- while(left < previous.length && left < current.length && previous[left] === current[left])left += 1;
3177
- if (left > 0 && isHighSurrogate(previous.charCodeAt(left - 1))) left -= 1;
3178
- while(right + left < previous.length && right + left < current.length && previous[previous.length - right - 1] === current[current.length - right - 1])right += 1;
3179
- if (right > 0 && isLowSurrogate(previous.charCodeAt(previous.length - right))) right -= 1;
3180
- return {
3181
- index: left,
3182
- insert: current.slice(left, current.length - right),
3183
- remove: previous.length - left - right
3184
- };
3185
- }
3186
- function indexRichTextRuns(source) {
3187
- let offset = 0;
3188
- return source.runs.map((run)=>{
3189
- const indexed = {
3190
- end: offset + run.v.length,
3191
- run,
3192
- start: offset
3193
- };
3194
- offset = indexed.end;
3195
- return indexed;
3196
- });
3197
- }
3198
- function reconstructPlainTextRuns(source, replacement) {
3199
- const runs = [];
3200
- appendSourceRange(runs, source, 0, replacement.index);
3201
- if (replacement.insert) {
3202
- const inherited = insertionSourceRun(source, replacement);
3203
- if (inherited) runs.push({
3204
- ...inherited.run,
3205
- v: replacement.insert
3206
- });
3207
- }
3208
- const sourceLength = source.at(-1)?.end ?? 0;
3209
- appendSourceRange(runs, source, replacement.index + replacement.remove, sourceLength);
3210
- return runs;
3211
- }
3212
- function reconstructFormattedPasteRuns(source, sourceRuns, currentText, intent) {
3213
- 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;
3214
- const pastedRuns = [];
3215
- for (const candidate of intent.runs){
3216
- const run = normalizeXlsxRichTextRun(candidate);
3217
- if (!run) return null;
3218
- if (run.v) pastedRuns.push(run);
3219
- }
3220
- if (!pastedRuns.length || pastedRuns.map((run)=>run.v).join('') !== intent.text) return null;
3221
- const expectedText = source.text.slice(0, intent.start) + intent.text + source.text.slice(intent.end);
3222
- if (expectedText !== currentText || expectedText.length > 32767 || !validXlsxRichText(expectedText)) return null;
3223
- const runs = [];
3224
- appendSourceRange(runs, sourceRuns, 0, intent.start);
3225
- runs.push(...pastedRuns);
3226
- appendSourceRange(runs, sourceRuns, intent.end, source.text.length);
3227
- const normalized = coalesceXlsxRichTextRuns(runs);
3228
- return normalized.length > 0 && normalized.length <= 512 ? normalized : null;
3229
- }
3230
- function validRichTextPasteSelection(text, start, end) {
3231
- return Boolean(Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end && end <= text.length && !splitsSurrogatePair(text, start) && !splitsSurrogatePair(text, end));
3232
- }
3233
- function splitsSurrogatePair(text, offset) {
3234
- if (offset <= 0 || offset >= text.length) return false;
3235
- return isHighSurrogate(text.charCodeAt(offset - 1)) && isLowSurrogate(text.charCodeAt(offset));
3236
- }
3237
- function appendSourceRange(target, source, start, end) {
3238
- if (start >= end) return;
3239
- for (const indexed of source){
3240
- const overlapStart = Math.max(start, indexed.start);
3241
- const overlapEnd = Math.min(end, indexed.end);
3242
- if (!(overlapStart >= overlapEnd)) target.push({
3243
- ...indexed.run,
3244
- v: indexed.run.v.slice(overlapStart - indexed.start, overlapEnd - indexed.start)
3245
- });
3246
- }
3247
- }
3248
- function restoreSemanticColorOrigins(current, source, replacement) {
3249
- const runs = [];
3250
- let absoluteOffset = 0;
3251
- for (const currentRun of current){
3252
- let localOffset = 0;
3253
- while(localOffset < currentRun.v.length){
3254
- const width = codePointWidth(currentRun.v, localOffset);
3255
- const origin = semanticOriginAt(source, replacement, absoluteOffset + localOffset);
3256
- const next = {
3257
- ...currentRun,
3258
- v: currentRun.v.slice(localOffset, localOffset + width)
3259
- };
3260
- delete next.a3sXlsxColorOrigin;
3261
- if (origin && currentRun.fc && xlsxSemanticColorMatchesValue(origin, currentRun.fc)) next.a3sXlsxColorOrigin = origin;
3262
- runs.push(next);
3263
- localOffset += width;
3264
- }
3265
- absoluteOffset += currentRun.v.length;
3266
- }
3267
- return runs;
3268
- }
3269
- function semanticOriginAt(source, replacement, currentOffset) {
3270
- let indexed;
3271
- indexed = currentOffset < replacement.index ? sourceRunAt(source, currentOffset) : currentOffset >= replacement.index + replacement.insert.length ? sourceRunAt(source, currentOffset - replacement.insert.length + replacement.remove) : insertionSourceRun(source, replacement);
3272
- return normalizeXlsxSemanticColorOrigin(indexed?.run.a3sXlsxColorOrigin);
3273
- }
3274
- function insertionSourceRun(source, replacement) {
3275
- const sourceLength = source.at(-1)?.end ?? 0;
3276
- if (!sourceLength) return;
3277
- if (replacement.remove > 0 && replacement.index < sourceLength) return sourceRunAt(source, replacement.index);
3278
- if (replacement.index > 0) return sourceRunAt(source, replacement.index - 1);
3279
- return sourceRunAt(source, 0);
3280
- }
3281
- function sourceRunAt(source, offset) {
3282
- let left = 0;
3283
- let right = source.length - 1;
3284
- while(left <= right){
3285
- const middle = left + Math.floor((right - left) / 2);
3286
- const indexed = source[middle];
3287
- if (!indexed) break;
3288
- if (offset < indexed.start) right = middle - 1;
3289
- else {
3290
- if (!(offset >= indexed.end)) return indexed;
3291
- left = middle + 1;
3292
- }
3293
- }
3294
- }
3295
- function codePointWidth(value, offset) {
3296
- return isHighSurrogate(value.charCodeAt(offset)) && isLowSurrogate(value.charCodeAt(offset + 1)) ? 2 : 1;
3297
- }
3298
- const spreadsheetFontSizes = [
3299
- 9,
3300
- 10,
3301
- 11,
3302
- 12,
3303
- 14,
3304
- 16,
3305
- 18,
3306
- 20,
3307
- 22,
3308
- 24,
3309
- 28,
3310
- 36,
3311
- 48,
3312
- 72
3313
- ];
3314
- function nextSpreadsheetFontSize(current, direction) {
3315
- if (!Number.isFinite(current) || current < 1 || current > 409) return null;
3316
- return 'grow' === direction ? spreadsheetFontSizes.find((size)=>size > current) ?? null : [
3317
- ...spreadsheetFontSizes
3318
- ].reverse().find((size)=>size < current) ?? null;
3319
- }
3320
- function captureSpreadsheetRichTextDomSelection({ expandedOnly = false, root, target }) {
3321
- const selection = window.getSelection();
3322
- if (!root || !selection || 1 !== selection.rangeCount) return null;
3323
- const range = selection.getRangeAt(0);
3324
- if (expandedOnly && range.collapsed) return null;
3325
- const targetNode = target instanceof Node ? target : null;
3326
- const editor = spreadsheetRichTextEditor(targetNode ?? range.commonAncestorContainer);
3327
- if (!editor || !root.contains(editor) || !editorContainsRange(editor, range)) return null;
3328
- const start = spreadsheetTextOffsetAtPoint(editor, range.startContainer, range.startOffset);
3329
- const end = spreadsheetTextOffsetAtPoint(editor, range.endContainer, range.endOffset);
3330
- return null === start || null === end || start > end ? null : {
3331
- editor,
3332
- end,
3333
- start
3334
- };
3335
- }
3336
- function spreadsheetRichTextEditor(node) {
3337
- const element = node instanceof Element ? node : node.parentElement;
3338
- const editor = element?.closest('.luckysheet-cell-input, .fortune-fx-input');
3339
- return editor?.isContentEditable || editor?.getAttribute('contenteditable') === 'true' ? editor : null;
3340
- }
3341
- function editorContainsRange(editor, range) {
3342
- return (range.startContainer === editor || editor.contains(range.startContainer)) && (range.endContainer === editor || editor.contains(range.endContainer));
3343
- }
3344
- function spreadsheetTextOffsetAtPoint(editor, node, offset) {
3345
- try {
3346
- const prefix = document.createRange();
3347
- prefix.selectNodeContents(editor);
3348
- prefix.setEnd(node, offset);
3349
- return prefix.toString().length;
3350
- } catch {
3351
- return null;
3352
- }
3353
- }
3354
- const MAX_SPREADSHEET_RICH_TEXT_CLIPBOARD_HTML_CHARACTERS = 256000;
3355
- const pendingPastes = new WeakMap();
3356
- const ignoredClipboardElements = new Set([
3357
- 'HEAD',
3358
- 'META',
3359
- 'NOSCRIPT',
3360
- 'SCRIPT',
3361
- 'STYLE',
3362
- 'TITLE'
3363
- ]);
3364
- const blockClipboardElements = new Set([
3365
- 'ADDRESS',
3366
- 'BLOCKQUOTE',
3367
- 'DIV',
3368
- 'H1',
3369
- 'H2',
3370
- 'H3',
3371
- 'H4',
3372
- 'H5',
3373
- 'H6',
3374
- 'LI',
3375
- 'P',
3376
- 'PRE'
3377
- ]);
3378
- function parseSpreadsheetRichTextClipboard(html, plainText) {
3379
- const text = normalizeClipboardText(plainText);
3380
- if (!html || html.length > MAX_SPREADSHEET_RICH_TEXT_CLIPBOARD_HTML_CHARACTERS || !text || text.length > 32767 || !validXlsxRichText(text)) return null;
3381
- const template = document.createElement('template');
3382
- template.innerHTML = html;
3383
- const runs = [];
3384
- const state = {
3385
- characterCount: 0,
3386
- invalid: false
3387
- };
3388
- for (const child of template.content.childNodes){
3389
- collectClipboardRuns(child, {}, runs, state);
3390
- if (state.invalid) return null;
3391
- }
3392
- trimTrailingClipboardNewline(runs, text);
3393
- const normalized = coalesceXlsxRichTextRuns(runs);
3394
- if (!normalized.length || normalized.length > 512 || normalized.map((run)=>run.v).join('') !== text || !normalized.some(spreadsheetRichTextRunHasFormatting)) return null;
3395
- return {
3396
- runs: normalized,
3397
- text
3398
- };
3399
- }
3400
- function captureSpreadsheetRichTextPaste({ clipboardData, content, root, selection, sheetId, target }) {
3401
- const domSelection = captureSpreadsheetRichTextDomSelection({
3402
- root,
3403
- target
3404
- });
3405
- const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
3406
- if (!domSelection || !sheet || !selection) return false;
3407
- const row = focusedAxisIndex(selection.row_focus, selection.row);
3408
- const column = focusedAxisIndex(selection.column_focus, selection.column);
3409
- const cell = spreadsheetCellAt(sheet, row, column);
3410
- const source = normalizeXlsxRichTextEditSource(cell);
3411
- const key = spreadsheetRichTextPasteKey(row, column);
3412
- if (!source || domSelection.editor.textContent !== source.text || domSelection.end > source.text.length) {
3413
- clearPendingPaste(sheet, key);
3414
- return false;
3415
- }
3416
- const parsed = parseSpreadsheetRichTextClipboard(clipboardData.getData('text/html'), clipboardData.getData('text/plain'));
3417
- if (!parsed) {
3418
- clearPendingPaste(sheet, key);
3419
- return false;
3420
- }
3421
- stageSpreadsheetRichTextPaste(sheet, row, column, source.text, {
3422
- end: domSelection.end,
3423
- runs: parsed.runs,
3424
- start: domSelection.start,
3425
- text: parsed.text
3426
- });
3427
- return true;
3428
- }
3429
- function stageSpreadsheetRichTextPaste(sheet, row, column, sourceText, intent) {
3430
- if (!validCoordinate(row) || !validCoordinate(column)) return;
3431
- const key = spreadsheetRichTextPasteKey(row, column);
3432
- const entries = pendingPastes.get(sheet) ?? new Map();
3433
- pendingPastes.set(sheet, entries);
3434
- entries.set(key, {
3435
- intent: {
3436
- ...intent,
3437
- runs: intent.runs.map((run)=>({
3438
- ...run
3439
- }))
3440
- },
3441
- sourceText
3442
- });
3443
- }
3444
- function takeSpreadsheetRichTextPaste(sheet, row, column, current) {
3445
- const intent = peekSpreadsheetRichTextPaste(sheet, row, column, current);
3446
- if (intent) consumeSpreadsheetRichTextPaste(sheet, row, column);
3447
- return intent;
3448
- }
3449
- function peekSpreadsheetRichTextPaste(sheet, row, column, current) {
3450
- const entries = pendingPastes.get(sheet);
3451
- const key = spreadsheetRichTextPasteKey(row, column);
3452
- const pending = entries?.get(key);
3453
- if (!entries || !pending) return;
3454
- const currentText = spreadsheetRichTextCellText(current);
3455
- if (currentText === pending.sourceText) return;
3456
- return null === currentText ? void 0 : pending.intent;
3457
- }
3458
- function consumeSpreadsheetRichTextPaste(sheet, row, column) {
3459
- clearPendingPaste(sheet, spreadsheetRichTextPasteKey(row, column));
3460
- }
3461
- function collectClipboardRuns(node, inherited, runs, state) {
3462
- if (state.invalid) return;
3463
- if (node.nodeType === Node.TEXT_NODE) return void appendClipboardText(runs, inherited, node.textContent ?? '', state);
3464
- if (!(node instanceof HTMLElement) || ignoredClipboardElements.has(node.tagName)) return;
3465
- const style = spreadsheetClipboardElementStyle(node, inherited);
3466
- if ('BR' === node.tagName) return void appendClipboardText(runs, style, '\n', state);
3467
- for (const child of node.childNodes){
3468
- collectClipboardRuns(child, style, runs, state);
3469
- if (state.invalid) return;
3470
- }
3471
- if (blockClipboardElements.has(node.tagName) && hasFollowingClipboardContent(node)) appendClipboardText(runs, style, '\n', state);
3472
- }
3473
- function appendClipboardText(runs, style, source, state) {
3474
- const value = normalizeClipboardText(source);
3475
- if (!value) return;
3476
- state.characterCount += value.length;
3477
- if (state.characterCount > 32767 || !validXlsxRichText(value)) {
3478
- state.invalid = true;
3479
- return;
3480
- }
3481
- const run = {
3482
- ...style,
3483
- v: value
3484
- };
3485
- const previous = runs.at(-1);
3486
- if (previous && sameXlsxRichTextRunStyle(previous, run)) previous.v += value;
3487
- else runs.push(run);
3488
- if (runs.length > 512) state.invalid = true;
3489
- }
3490
- function spreadsheetClipboardElementStyle(element, inherited) {
3491
- const style = {
3492
- ...inherited
3493
- };
3494
- if (element.matches('b, strong')) style.bl = 1;
3495
- if (element.matches('i, em')) style.it = 1;
3496
- if (element.matches('s, strike, del')) style.cl = 1;
3497
- if (element.matches('u')) style.un = 1;
3498
- const declarations = cssDeclarations(element.getAttribute('style'));
3499
- applyFontWeight(style, declarations.get('font-weight'));
3500
- applyToggle(style, 'it', declarations.get('font-style'), 'italic');
3501
- applyTextDecoration(style, declarations);
3502
- applyFontFamily(style, declarations.get('font-family'));
3503
- applyFontSize(style, declarations.get('font-size'));
3504
- applyFontColor(style, declarations.get('color'));
3505
- applyFortuneToggle(style, 'cl', declarations.get('lucky-strike'));
3506
- applyFortuneUnderline(style, declarations.get('lucky-underline'));
3507
- if (declarations.get('border-bottom')?.trim().toLowerCase() !== 'none') {
3508
- if (declarations.has('border-bottom')) style.un = 1;
3509
- }
3510
- if ('FONT' === element.tagName) {
3511
- applyFontFamily(style, element.getAttribute('face') ?? void 0);
3512
- applyFontColor(style, element.getAttribute('color') ?? void 0);
3513
- applyHtmlFontSize(style, element.getAttribute('size'));
3514
- }
3515
- return style;
3516
- }
3517
- function cssDeclarations(value) {
3518
- const declarations = new Map();
3519
- for (const candidate of value?.split(';') ?? []){
3520
- const separator = candidate.indexOf(':');
3521
- if (separator <= 0) continue;
3522
- const key = candidate.slice(0, separator).trim().toLowerCase();
3523
- const declaration = candidate.slice(separator + 1).trim();
3524
- if (key && declaration) declarations.set(key, declaration);
3525
- }
3526
- return declarations;
3527
- }
3528
- function applyFontWeight(style, value) {
3529
- const normalized = value?.trim().toLowerCase();
3530
- if (!normalized) return;
3531
- if ('bold' === normalized || 'bolder' === normalized) style.bl = 1;
3532
- else if ('normal' === normalized || 'lighter' === normalized) style.bl = 0;
3533
- else if (/^\d+$/.test(normalized)) style.bl = Number(normalized) >= 600 ? 1 : 0;
3534
- }
3535
- function applyToggle(style, key, value, enabled) {
3536
- const normalized = value?.trim().toLowerCase();
3537
- if (!normalized) return;
3538
- style[key] = normalized === enabled || 'oblique' === normalized ? 1 : 0;
3539
- }
3540
- function applyTextDecoration(style, declarations) {
3541
- const value = declarations.get('text-decoration-line') ?? declarations.get('text-decoration');
3542
- const normalized = value?.toLowerCase();
3543
- if (!normalized) return;
3544
- if (normalized.includes('none')) {
3545
- style.cl = 0;
3546
- style.un = 0;
3547
- return;
3548
- }
3549
- if (normalized.includes('line-through')) style.cl = 1;
3550
- if (normalized.includes('underline')) style.un = 'double' === declarations.get('text-decoration-style') ? 2 : 1;
3551
- }
3552
- function applyFontFamily(style, value) {
3553
- const family = value?.split(',')[0]?.trim().replace(/^(['"])(.*)\1$/, '$2');
3554
- if (family && family.length <= 128) style.ff = family;
3555
- }
3556
- function applyFontSize(style, value) {
3557
- const match = /^([\d.]+)\s*(pt|px)$/i.exec(value?.trim() ?? '');
3558
- if (!match) return;
3559
- const number = Number(match[1]);
3560
- const size = match[2]?.toLowerCase() === 'px' ? 0.75 * number : number;
3561
- if (Number.isFinite(size) && size >= 1 && size <= 409) style.fs = Math.round(100 * size) / 100;
3562
- }
3563
- function applyFontColor(style, value) {
3564
- const color = normalizeCssColor(value);
3565
- if (color && 'transparent' !== color) style.fc = color;
3566
- }
3567
- function applyFortuneToggle(style, key, value) {
3568
- const toggle = Number(value);
3569
- if (Number.isSafeInteger(toggle) && (0 === toggle || 1 === toggle)) style[key] = toggle;
3570
- }
3571
- function applyFortuneUnderline(style, value) {
3572
- const underline = Number(value);
3573
- if (Number.isSafeInteger(underline) && underline >= 0 && underline <= 4) style.un = underline;
3574
- }
3575
- function applyHtmlFontSize(style, value) {
3576
- const index = Number(value);
3577
- const size = [
3578
- 0,
3579
- 8,
3580
- 10,
3581
- 12,
3582
- 14,
3583
- 18,
3584
- 24,
3585
- 36
3586
- ][index];
3587
- if (size) style.fs = size;
3588
- }
3589
- function spreadsheetRichTextRunHasFormatting(run) {
3590
- return [
3591
- 'bl',
3592
- 'cl',
3593
- 'fc',
3594
- 'ff',
3595
- 'fs',
3596
- 'it',
3597
- 'un'
3598
- ].some((key)=>void 0 !== run[key]);
3599
- }
3600
- function trimTrailingClipboardNewline(runs, expectedText) {
3601
- const actual = runs.map((run)=>run.v).join('');
3602
- if (!actual.endsWith('\n') || expectedText.endsWith('\n')) return;
3603
- const last = runs.at(-1);
3604
- if (!last) return;
3605
- last.v = last.v.slice(0, -1);
3606
- if (!last.v) runs.pop();
3607
- }
3608
- function hasFollowingClipboardContent(element) {
3609
- let sibling = element.nextSibling;
3610
- while(sibling){
3611
- if (sibling.nodeType === Node.TEXT_NODE) {
3612
- if (sibling.textContent) return true;
3613
- } else if (sibling instanceof HTMLElement && !ignoredClipboardElements.has(sibling.tagName)) return true;
3614
- sibling = sibling.nextSibling;
3615
- }
3616
- return false;
3617
- }
3618
- function spreadsheetRichTextCellText(cell) {
3619
- const rich = normalizeXlsxRichTextCell(cell);
3620
- if ('string' == typeof cell.v && cell.v !== rich?.text) return cell.v;
3621
- return rich?.text ?? ('string' == typeof cell.v ? cell.v : null);
3622
- }
3623
- function spreadsheetCellAt(sheet, row, column) {
3624
- return sheet.data?.[row]?.[column] ?? sheet.celldata?.find((candidate)=>candidate.r === row && candidate.c === column)?.v ?? null;
3625
- }
3626
- function focusedAxisIndex(value, axis) {
3627
- const minimum = spreadsheet_rich_text_paste_finiteIndex(axis[0], 0);
3628
- const maximum = spreadsheet_rich_text_paste_finiteIndex(axis[1], minimum);
3629
- const focus = spreadsheet_rich_text_paste_finiteIndex(value, minimum);
3630
- return Math.min(Math.max(minimum, maximum), Math.max(Math.min(minimum, maximum), focus));
3631
- }
3632
- function spreadsheet_rich_text_paste_finiteIndex(value, fallback) {
3633
- return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
3634
- }
3635
- function normalizeClipboardText(value) {
3636
- return value.replace(/\r\n?/g, '\n').replace(/\u00a0/g, ' ');
3637
- }
3638
- function clearPendingPaste(sheet, key) {
3639
- const entries = pendingPastes.get(sheet);
3640
- entries?.delete(key);
3641
- if (entries && !entries.size) pendingPastes.delete(sheet);
3642
- }
3643
- function spreadsheetRichTextPasteKey(row, column) {
3644
- return `${row}:${column}`;
3645
- }
3646
- function validCoordinate(value) {
3647
- return Number.isSafeInteger(value) && value >= 0;
3648
- }
3649
- const MAXIMUM_INCREMENTAL_SPREADSHEET_OPERATIONS = 10000;
3650
- function spreadsheetCellOperationKey(row, column) {
3651
- return `${row}:${column}`;
3652
- }
3653
- function spreadsheetCellOperationCoordinates(operations) {
3654
- if (operations.length > MAXIMUM_INCREMENTAL_SPREADSHEET_OPERATIONS) return null;
3655
- const coordinatesBySheet = new Map();
3656
- for (const operation of operations){
3657
- if ('insertRowCol' === operation.op || 'deleteRowCol' === operation.op || 'addSheet' === operation.op || 'deleteSheet' === operation.op || 0 === operation.path.length || !operation.id) return null;
3658
- const root = operation.path[0];
3659
- if ('id' === root || 'celldata' === root) return null;
3660
- if ('data' !== root) continue;
3661
- const row = operation.path[1];
3662
- const column = operation.path[2];
3663
- if (operation.path.length < 3 || !isSpreadsheetCoordinate(row) || !isSpreadsheetCoordinate(column)) return null;
3664
- const coordinates = coordinatesBySheet.get(operation.id) ?? new Map();
3665
- coordinatesBySheet.set(operation.id, coordinates);
3666
- coordinates.set(spreadsheetCellOperationKey(row, column), {
3667
- column,
3668
- row
3669
- });
3670
- }
3671
- return coordinatesBySheet;
3672
- }
3673
- function projectSpreadsheetSheetsFromFortuneOperations(sheets, sourceSheets, operations) {
3674
- if (0 === operations.length || sheets.length !== sourceSheets.length) return null;
3675
- const changedSheets = spreadsheetSheetsById(sheets);
3676
- const controlledSheets = spreadsheetSheetsById(sourceSheets);
3677
- if (!changedSheets || !controlledSheets) return null;
3678
- if (changedSheets.size !== sheets.length || controlledSheets.size !== sourceSheets.length) return null;
3679
- const coordinatesBySheet = spreadsheetCellOperationCoordinates(operations);
3680
- if (!coordinatesBySheet) return null;
3681
- for (const operation of operations)if (!operation.id || !changedSheets.has(operation.id) || !controlledSheets.has(operation.id)) return null;
3682
- let affectedCellCount = 0;
3683
- const projectedSheets = [];
3684
- const pendingRichTextPastes = [];
3685
- for (const sheet of sheets){
3686
- const id = sheet.id;
3687
- if (!id) return null;
3688
- const source = controlledSheets.get(id);
3689
- if (!source) return null;
3690
- const coordinates = coordinatesBySheet.get(id);
3691
- const { celldata: _cellData, data: _data, ...metadata } = sheet;
3692
- if (!coordinates?.size) {
3693
- projectedSheets.push(void 0 !== source.data ? {
3694
- ...metadata,
3695
- data: source.data
3696
- } : {
3697
- ...metadata,
3698
- celldata: source.celldata ?? []
3699
- });
3700
- continue;
3701
- }
3702
- if (void 0 === source.data || void 0 === sheet.data || !spreadsheetMatrixProfile(source.data)?.fortuneReady) return null;
3703
- const projection = projectSpreadsheetMatrixCells(sheet, source, coordinates.values(), pendingRichTextPastes);
3704
- if (!projection) return null;
3705
- affectedCellCount += projection.affectedCellCount;
3706
- projectedSheets.push({
3707
- ...metadata,
3708
- data: projection.data
3709
- });
3710
- }
3711
- for (const pending of pendingRichTextPastes)consumeSpreadsheetRichTextPaste(pending.sheet, pending.row, pending.column);
3712
- return {
3713
- affectedCellCount,
3714
- sheets: projectedSheets
3715
- };
3716
- }
3717
- function projectSpreadsheetMatrixCells(changed, source, coordinates, pendingRichTextPastes) {
3718
- if (!changed.data || !source.data) return null;
3719
- const data = source.data.slice();
3720
- data.length = Math.max(data.length, changed.data.length, positiveSpreadsheetDimension(changed.row));
3721
- const mutableRows = new Map();
3722
- const profileChanges = [];
3723
- for (const { column, row } of coordinates){
3724
- const changedRow = changed.data[row];
3725
- const sourceRow = source.data[row];
3726
- const currentCell = changedRow?.[column];
3727
- const formattedPaste = currentCell ? peekSpreadsheetRichTextPaste(source, row, column, currentCell) : void 0;
3728
- const nextCell = null == currentCell ? currentCell : cloneControlledSpreadsheetCell(currentCell, sourceRow?.[column], formattedPaste);
3729
- if (formattedPaste) pendingRichTextPastes.push({
3730
- column,
3731
- row,
3732
- sheet: source
3733
- });
3734
- let nextRow = mutableRows.get(row);
3735
- if (!nextRow) {
3736
- nextRow = sourceRow?.slice() ?? [];
3737
- nextRow.length = Math.max(nextRow.length, changedRow?.length ?? 0, positiveSpreadsheetDimension(changed.column));
3738
- mutableRows.set(row, nextRow);
3739
- data[row] = nextRow;
3740
- }
3741
- if (null == nextCell) delete nextRow[column];
3742
- else nextRow[column] = nextCell;
3743
- profileChanges.push({
3744
- column,
3745
- current: nextCell,
3746
- previous: sourceRow?.[column],
3747
- row
3748
- });
3749
- }
3750
- if (!registerDerivedSpreadsheetMatrix(data, source.data, profileChanges)) return null;
3751
- return {
3752
- affectedCellCount: profileChanges.length,
3753
- data
3754
- };
3755
- }
3756
- function cloneControlledSpreadsheetCell(cell, previous, formattedPaste) {
3757
- return freezeImportedSpreadsheetCell(structuredClone(reconcileSpreadsheetRichTextCellEdit(previous, cell, formattedPaste)));
3758
- }
3759
- function spreadsheetSheetsById(sheets) {
3760
- const byId = new Map();
3761
- for (const sheet of sheets){
3762
- if (!sheet.id || byId.has(sheet.id)) return null;
3763
- byId.set(sheet.id, sheet);
3764
- }
3765
- return byId;
3766
- }
3767
- function isSpreadsheetCoordinate(value) {
3768
- return Number.isSafeInteger(value) && Number(value) >= 0;
3769
- }
3770
- function positiveSpreadsheetDimension(value) {
3771
- return 'number' == typeof value && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
3772
- }
3773
- function spreadsheetCurrentRegion(sheet, seed) {
3774
- const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(seed);
3775
- if (!range) return null;
3776
- const cells = spreadsheetSheetCellReader(sheet);
3777
- const bounds = spreadsheetCurrentRegionUsedBounds(sheet);
3778
- let startRow = range.row[0];
3779
- let endRow = range.row[1];
3780
- let startColumn = range.column[0];
3781
- let endColumn = range.column[1];
3782
- if (startRow > bounds.lastRow || startColumn > bounds.lastColumn || !spreadsheet_current_region_spreadsheetCellHasContent(cells(startRow, startColumn))) return null;
3783
- let changed = true;
3784
- while(changed){
3785
- changed = false;
3786
- while(startColumn > 0 && spreadsheetCurrentRegionColumnHasContent(cells, startColumn - 1, startRow, endRow)){
3787
- startColumn -= 1;
3788
- changed = true;
3789
- }
3790
- while(endColumn < bounds.lastColumn && spreadsheetCurrentRegionColumnHasContent(cells, endColumn + 1, startRow, endRow)){
3791
- endColumn += 1;
3792
- changed = true;
3793
- }
3794
- while(startRow > 0 && spreadsheetRowHasContent(cells, startRow - 1, startColumn, endColumn)){
3795
- startRow -= 1;
3796
- changed = true;
3797
- }
3798
- while(endRow < bounds.lastRow && spreadsheetRowHasContent(cells, endRow + 1, startColumn, endColumn)){
3799
- endRow += 1;
3800
- changed = true;
3801
- }
3802
- }
3803
- return {
3804
- row: [
3805
- startRow,
3806
- endRow
3807
- ],
3808
- column: [
3809
- startColumn,
3810
- endColumn
3811
- ]
3812
- };
3813
- }
3814
- function spreadsheetRowHasContent(cellAt, row, startColumn, endColumn) {
3815
- for(let column = startColumn; column <= endColumn; column += 1)if (spreadsheet_current_region_spreadsheetCellHasContent(cellAt(row, column))) return true;
3816
- return false;
3817
- }
3818
- function spreadsheetCurrentRegionColumnHasContent(cellAt, column, startRow, endRow) {
3819
- for(let row = startRow; row <= endRow; row += 1)if (spreadsheet_current_region_spreadsheetCellHasContent(cellAt(row, column))) return true;
3820
- return false;
3821
- }
3822
- function spreadsheet_current_region_spreadsheetCellHasContent(cell) {
3823
- if (!cell) return false;
3824
- return [
3825
- cell.v,
3826
- cell.m,
3827
- cell.f
3828
- ].some((value)=>null != value && '' !== value);
3829
- }
3830
- function spreadsheetSheetCellReader(sheet) {
3831
- if (sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
3832
- const cells = new Map((sheet.celldata ?? []).map((cell)=>[
3833
- `${cell.r}:${cell.c}`,
3834
- cell.v
3835
- ]));
3836
- return (row, column)=>cells.get(`${row}:${column}`) ?? null;
3837
- }
3838
- function spreadsheetCurrentRegionUsedBounds(sheet) {
3839
- let lastRow = Math.max((sheet.data?.length ?? 1) - 1, 0);
3840
- let lastColumn = Math.max(sparseMatrixColumnCount(sheet.data) - 1, 0);
3841
- for (const cell of sheet.celldata ?? []){
3842
- lastRow = Math.max(lastRow, cell.r);
3843
- lastColumn = Math.max(lastColumn, cell.c);
3844
- }
3845
- return {
3846
- lastColumn,
3847
- lastRow
3848
- };
3849
- }
3850
- function spreadsheetCellFillFormat(cell) {
3851
- const nativeFill = activeXlsxNativeFill(cell);
3852
- if (nativeFill) return nativeFill;
3853
- const color = normalizeSpreadsheetFillColor(cell?.bg);
3854
- return color ? {
3855
- color,
3856
- kind: 'solid'
3857
- } : {
3858
- kind: 'none'
3859
- };
3860
- }
3861
- function normalizeSpreadsheetCellFillFormat(value) {
3862
- if (!spreadsheet_cell_fill_format_isRecord(value)) return null;
3863
- if ('none' === value.kind) return {
3864
- kind: 'none'
3865
- };
3866
- if ('solid' === value.kind) {
3867
- const color = normalizeSpreadsheetFillColor(value.color);
3868
- return color ? {
3869
- color,
3870
- kind: 'solid'
3871
- } : null;
3872
- }
3873
- if ('pattern' === value.kind) {
3874
- const fill = normalizeXlsxPatternFill(value.value);
3875
- return fill ? {
3876
- kind: 'pattern',
3877
- value: fill
3878
- } : null;
3879
- }
3880
- if ('gradient' === value.kind) {
3881
- const fill = normalizeXlsxGradientFill(value.value);
3882
- return fill ? {
3883
- kind: 'gradient',
3884
- value: fill
3885
- } : null;
3886
- }
3887
- return null;
3888
- }
3889
- function withSpreadsheetCellFillFormat(cell, fill) {
3890
- const normalized = normalizeSpreadsheetCellFillFormat(fill);
3891
- if (!normalized) return null;
3892
- const next = {
3893
- ...cell
3894
- };
3895
- deleteXlsxNativeFills(next);
3896
- if ('none' === normalized.kind) {
3897
- delete next.bg;
3898
- return next;
3899
- }
3900
- if ('solid' === normalized.kind) {
3901
- next.bg = normalized.color;
3902
- return next;
3903
- }
3904
- if ('pattern' === normalized.kind) {
3905
- next.bg = normalized.value.backgroundColor;
3906
- return withXlsxPatternFill(next, normalized.value);
3907
- }
3908
- next.bg = xlsxGradientFillFallbackColor(normalized.value);
3909
- return withXlsxGradientFill(next, normalized.value);
3910
- }
3911
- function normalizeSpreadsheetFillColor(value) {
3912
- if ('string' != typeof value) return null;
3913
- const color = value.trim().toLowerCase();
3914
- if (/^#[0-9a-f]{6}$/.test(color)) return color;
3915
- if (!/^#[0-9a-f]{3}$/.test(color)) return null;
3916
- return `#${[
3917
- ...color.slice(1)
3918
- ].map((character)=>character.repeat(2)).join('')}`;
3919
- }
3920
- function spreadsheet_cell_fill_format_isRecord(value) {
3921
- return 'object' == typeof value && null !== value && !Array.isArray(value);
3922
- }
3923
- function createSpreadsheetSortAppearanceRows(sheet, range, rows) {
3924
- const data = liveSpreadsheetSortMatrix(sheet, range, rows);
3925
- const conditional = spreadsheetConditionalFormatStyles({
3926
- ...sheet,
3927
- data
3928
- });
3929
- return rows.map((row, rowOffset)=>row.map((cell, columnOffset)=>{
3930
- const direct = spreadsheetSortDirectCellAppearance(cell);
3931
- const style = conditional.get(`${range.row[0] + rowOffset}_${range.column[0] + columnOffset}`);
3932
- const conditionalCellColor = normalizeSpreadsheetSortColor(style?.cellColor);
3933
- const conditionalFontColor = normalizeSpreadsheetSortColor(style?.textColor);
3934
- return {
3935
- cellColor: void 0 !== conditionalCellColor ? conditionalCellColor : direct.cellColor,
3936
- fontColor: void 0 !== conditionalFontColor ? conditionalFontColor : direct.fontColor,
3937
- icon: normalizeSpreadsheetSortIcon(style?.icon) ?? null
3938
- };
3939
- }));
3940
- }
3941
- function createSpreadsheetSortDirectAppearanceRows(rows) {
3942
- return rows.map((row)=>row.map(spreadsheetSortDirectCellAppearance));
3943
- }
3944
- function spreadsheetSortAppearanceFields(rows, range, orientation, hasHeader) {
3945
- const vertical = 'top-to-bottom' === orientation;
3946
- const fieldCount = vertical ? range.column[1] - range.column[0] + 1 : range.row[1] - range.row[0] + 1;
3947
- return Array.from({
3948
- length: fieldCount
3949
- }, (_, offset)=>{
3950
- const cellColors = [];
3951
- const fontColors = [];
3952
- const icons = [];
3953
- const seenCellColors = new Set();
3954
- const seenFontColors = new Set();
3955
- const seenIcons = new Set();
3956
- const appearances = vertical ? rows.slice(hasHeader ? 1 : 0).map((row)=>row[offset]) : rows[offset] ?? [];
3957
- for (const appearance of appearances)if (appearance) {
3958
- if (void 0 !== appearance.cellColor) appendUniqueColor(cellColors, seenCellColors, appearance.cellColor);
3959
- appendUniqueColor(fontColors, seenFontColors, appearance.fontColor);
3960
- if (appearance.icon) {
3961
- const key = spreadsheetSortIconTargetKey(appearance.icon);
3962
- if (!seenIcons.has(key)) {
3963
- seenIcons.add(key);
3964
- icons.push({
3965
- ...appearance.icon
3966
- });
3967
- }
3968
- }
3969
- }
3970
- return {
3971
- index: (vertical ? range.column[0] : range.row[0]) + offset,
3972
- cellColors,
3973
- fontColors,
3974
- icons
3975
- };
3976
- });
3977
- }
3978
- function normalizeSpreadsheetSortColor(value) {
3979
- if (null === value) return null;
3980
- if ('string' != typeof value) return;
3981
- const source = value.trim();
3982
- const shorthand = /^#([0-9a-f]{3})$/iu.exec(source);
3983
- const parsed = parseSpreadsheetConditionalColor(shorthand ? `#${[
3984
- ...shorthand[1]
3985
- ].map((character)=>character.repeat(2)).join('')}` : source);
3986
- return parsed ? spreadsheetSortHexColor(parsed) : void 0;
3987
- }
3988
- function normalizeSpreadsheetSortIcon(value) {
3989
- if (!value || 'object' != typeof value || Array.isArray(value)) return null;
3990
- const source = value;
3991
- if (!isSpreadsheetConditionalIconSetName(source.iconSet) || !Number.isSafeInteger(source.index) || source.index < 0 || source.index >= spreadsheetConditionalIconSetCount(source.iconSet)) return null;
3992
- return {
3993
- iconSet: source.iconSet,
3994
- index: source.index
3995
- };
3996
- }
3997
- function spreadsheetSortAppearanceTargetValue(target) {
3998
- if ('icon' === target.kind) return `icon:${target.icon.iconSet}:${target.icon.index}`;
3999
- const value = target.color ?? ('cell-color' === target.kind ? 'none' : 'automatic');
4000
- return `${target.kind}:${value}`;
4001
- }
4002
- function parseSpreadsheetSortAppearanceTargetValue(value) {
4003
- if (value.startsWith('cell-color:')) {
4004
- const source = value.slice(11);
4005
- if ('none' === source) return {
4006
- kind: 'cell-color',
4007
- color: null
4008
- };
4009
- const color = normalizeSpreadsheetSortColor(source);
4010
- return 'string' == typeof color ? {
4011
- kind: 'cell-color',
4012
- color
4013
- } : null;
4014
- }
4015
- if (value.startsWith('font-color:')) {
4016
- const source = value.slice(11);
4017
- if ('automatic' === source) return {
4018
- kind: 'font-color',
4019
- color: null
4020
- };
4021
- const color = normalizeSpreadsheetSortColor(source);
4022
- return 'string' == typeof color ? {
4023
- kind: 'font-color',
4024
- color
4025
- } : null;
4026
- }
4027
- const icon = /^icon:([^:]+):(\d+)$/u.exec(value);
4028
- if (!icon) return null;
4029
- const normalized = normalizeSpreadsheetSortIcon({
4030
- iconSet: icon[1],
4031
- index: Number(icon[2])
4032
- });
4033
- return normalized ? {
4034
- kind: 'icon',
4035
- icon: normalized
4036
- } : null;
4037
- }
4038
- function spreadsheetSortAppearanceTargetLabel(target) {
4039
- if ('cell-color' === target.kind) return target.color ? `单元格颜色 ${target.color.toUpperCase()}` : '无单元格颜色';
4040
- if ('font-color' === target.kind) return target.color ? `字体颜色 ${target.color.toUpperCase()}` : '自动字体颜色';
4041
- const count = spreadsheetConditionalIconSetCount(target.icon.iconSet);
4042
- const appearance = spreadsheetConditionalIconAppearance({
4043
- ...target.icon,
4044
- count,
4045
- showValue: true
4046
- });
4047
- return `${appearance.glyph} ${appearance.label}`;
4048
- }
4049
- function spreadsheetSortAppearanceTargets(field, kind) {
4050
- if (!field) return [];
4051
- if ('cell-color' === kind) return field.cellColors.map((color)=>({
4052
- kind,
4053
- color
4054
- }));
4055
- if ('font-color' === kind) return field.fontColors.map((color)=>({
4056
- kind,
4057
- color
4058
- }));
4059
- return field.icons.map((icon)=>({
4060
- kind,
4061
- icon: {
4062
- ...icon
4063
- }
4064
- }));
4065
- }
4066
- function spreadsheetSortAppearanceTargetsEqual(left, right) {
4067
- return spreadsheetSortAppearanceTargetValue(left) === spreadsheetSortAppearanceTargetValue(right);
4068
- }
4069
- function spreadsheetSortCellMatchesAppearance(appearance, target) {
4070
- if (!appearance) return false;
4071
- if ('cell-color' === target.kind) return appearance.cellColor === target.color;
4072
- if ('font-color' === target.kind) return appearance.fontColor === target.color;
4073
- return Boolean(appearance.icon && spreadsheetSortIconTargetKey(appearance.icon) === spreadsheetSortIconTargetKey(target.icon));
4074
- }
4075
- function spreadsheetSortDirectCellAppearance(cell) {
4076
- const fill = spreadsheetCellFillFormat(cell);
4077
- const fontColor = normalizeSpreadsheetSortColor(cell?.fc);
4078
- return {
4079
- cellColor: 'solid' === fill.kind ? fill.color : 'none' === fill.kind ? null : void 0,
4080
- fontColor: 'string' == typeof fontColor ? fontColor : null,
4081
- icon: null
4082
- };
4083
- }
4084
- function liveSpreadsheetSortMatrix(sheet, range, rows) {
4085
- const data = cloneSparseMatrix(sheet.data);
4086
- for (const entry of sheet.celldata ?? []){
4087
- const row = data[entry.r] ? [
4088
- ...data[entry.r]
4089
- ] : [];
4090
- if (void 0 === row[entry.c]) row[entry.c] = entry.v;
4091
- data[entry.r] = row;
4092
- }
4093
- for(let rowOffset = 0; rowOffset < rows.length; rowOffset += 1){
4094
- const absoluteRow = range.row[0] + rowOffset;
4095
- const target = data[absoluteRow] ? [
4096
- ...data[absoluteRow]
4097
- ] : [];
4098
- const source = rows[rowOffset] ?? [];
4099
- for(let columnOffset = 0; columnOffset < source.length; columnOffset += 1)target[range.column[0] + columnOffset] = source[columnOffset] ?? null;
4100
- data[absoluteRow] = target;
4101
- }
4102
- return data;
4103
- }
4104
- function appendUniqueColor(target, seen, color) {
4105
- const key = color ?? 'none';
4106
- if (seen.has(key)) return;
4107
- seen.add(key);
4108
- target.push(color);
4109
- }
4110
- function spreadsheetSortIconTargetKey(target) {
4111
- return `${target.iconSet}:${target.index}`;
4112
- }
4113
- function spreadsheetSortHexColor(color) {
4114
- return `#${[
4115
- color.red,
4116
- color.green,
4117
- color.blue
4118
- ].map((channel)=>channel.toString(16).padStart(2, '0')).join('')}`;
4119
- }
4120
- const DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS = Object.freeze({
4121
- caseSensitive: false,
4122
- textMethod: 'pinyin'
4123
- });
4124
- const SPREADSHEET_SORT_COLLATION_PROBES = Object.freeze({
4125
- pinyin: Object.freeze([
4126
- '阿',
4127
- '丁',
4128
- '王',
4129
- '赵'
4130
- ]),
4131
- stroke: Object.freeze([
4132
- '丁',
4133
- '王',
4134
- '安',
4135
- '阿',
4136
- '赵'
4137
- ])
4138
- });
4139
- function isSpreadsheetSortTextMethod(value) {
4140
- return 'pinyin' === value || 'stroke' === value;
4141
- }
4142
- function createSpreadsheetSortTextComparator(options) {
4143
- try {
4144
- const collator = new Intl.Collator(`zh-CN-u-co-${options.textMethod}`, {
4145
- caseFirst: 'lower',
4146
- numeric: false,
4147
- sensitivity: options.caseSensitive ? 'case' : 'base',
4148
- usage: 'sort'
4149
- });
4150
- if (!spreadsheetSortCollatorSupportsMethod(collator, options.textMethod)) return null;
4151
- return collator.compare;
4152
- } catch {
4153
- return null;
4154
- }
4155
- }
4156
- function spreadsheetSortCollatorSupportsMethod(collator, textMethod) {
4157
- const probe = SPREADSHEET_SORT_COLLATION_PROBES[textMethod];
4158
- for(let index = 1; index < probe.length; index += 1){
4159
- const previous = probe[index - 1];
4160
- const current = probe[index];
4161
- if (void 0 === previous || void 0 === current) return false;
4162
- if (collator.compare(previous, current) >= 0) return false;
4163
- }
4164
- return true;
4165
- }
4166
- const MAX_SPREADSHEET_SORT_KEYS = 64;
4167
- const MAX_SPREADSHEET_SORT_CELLS = 1000000;
4168
- const MAX_SPREADSHEET_SORT_HEADER_LENGTH = 48;
4169
- function createSpreadsheetSortRangePlan(sheet, selectedRange) {
4170
- const selected = spreadsheet_cell_range_normalizeSpreadsheetCellRange(selectedRange);
4171
- if (!selected) return null;
4172
- const owned = spreadsheetSortOwnedRangeContaining(sheet, selected);
4173
- if (owned) return {
4174
- selectedRange: selected,
4175
- expandedRange: spreadsheetCellRangesEqual(owned.range, selected) ? null : owned.range
4176
- };
4177
- const currentRegion = spreadsheetCurrentRegion(sheet, selected);
4178
- return {
4179
- selectedRange: selected,
4180
- expandedRange: currentRegion && !spreadsheetCellRangesEqual(currentRegion, selected) ? currentRegion : null
4181
- };
4182
- }
4183
- function createSpreadsheetSortRangeDialogSource(sheetName, request) {
4184
- if (!request.expanded) return null;
4185
- return {
4186
- sheetName,
4187
- selectedRangeReference: formatSpreadsheetCellRanges([
4188
- request.selected.range
4189
- ]),
4190
- expandedRangeReference: formatSpreadsheetCellRanges([
4191
- request.expanded.range
4192
- ]),
4193
- canSortSelection: request.selected.available,
4194
- canSortExpandedRange: request.expanded.available,
4195
- ...request.expanded.scope ? {
4196
- ownedScope: request.expanded.scope
4197
- } : {}
4198
- };
4199
- }
4200
- function createSpreadsheetSortDialogSource(sheetId, sheetName, target, rows, customLists = [], appearanceRows) {
4201
- const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(target.range);
4202
- if (!range || spreadsheetSortRangeError(range)) return null;
4203
- const width = range.column[1] - range.column[0] + 1;
4204
- const height = range.row[1] - range.row[0] + 1;
4205
- if (!spreadsheetSortRowsMatchRange(rows, range)) return null;
4206
- const hasHeader = height > 2 && spreadsheetSortRowsHaveHeader(rows);
4207
- const resolvedHasHeader = target.scope?.hasHeader ?? hasHeader;
4208
- const activeColumn = spreadsheetSortActiveIndex(target.activeColumn, range.column);
4209
- const activeRow = spreadsheetSortActiveIndex(target.activeRow, range.row);
4210
- const header = rows[0] ?? [];
4211
- const resolvedAppearanceRows = appearanceRows && spreadsheetSortAppearanceRowsMatch(rows, appearanceRows) ? appearanceRows : createSpreadsheetSortDirectAppearanceRows(rows);
4212
- return {
4213
- sheetId,
4214
- sheetName,
4215
- range,
4216
- rangeReference: formatSpreadsheetCellRanges([
4217
- range
4218
- ]),
4219
- activeRow,
4220
- appearanceRows: resolvedAppearanceRows,
4221
- customLists: mergeSpreadsheetSortCustomLists(customLists),
4222
- ...target.scope ? {
4223
- scope: target.scope
4224
- } : {},
4225
- columns: Array.from({
4226
- length: width
4227
- }, (_, offset)=>{
4228
- const index = range.column[0] + offset;
4229
- const name = spreadsheetSortHeaderText(header[offset]);
4230
- const coordinate = spreadsheetSortColumnLabel(index);
4231
- return {
4232
- index,
4233
- label: name ? `${coordinate}(${name})` : coordinate
4234
- };
4235
- }),
4236
- rows: Array.from({
4237
- length: height
4238
- }, (_, offset)=>{
4239
- const index = range.row[0] + offset;
4240
- return {
4241
- index,
4242
- label: `行 ${index + 1}`
4243
- };
4244
- }),
4245
- value: {
4246
- orientation: 'top-to-bottom',
4247
- ...DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS,
4248
- hasHeader: resolvedHasHeader,
4249
- keys: [
4250
- {
4251
- index: activeColumn,
4252
- direction: 'ascending'
4253
- }
4254
- ]
4255
- }
4256
- };
4257
- }
4258
- function spreadsheetSortRowsMatchRange(rows, range) {
4259
- const normalized = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4260
- if (!normalized) return false;
4261
- const width = normalized.column[1] - normalized.column[0] + 1;
4262
- const height = normalized.row[1] - normalized.row[0] + 1;
4263
- return rows.length === height && rows.every((row)=>row.length === width);
4264
- }
4265
- function spreadsheetSortRowsFromSheet(sheet, range) {
4266
- const normalized = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4267
- if (!normalized || spreadsheetSortRangeError(normalized)) return null;
4268
- const width = normalized.column[1] - normalized.column[0] + 1;
4269
- const height = normalized.row[1] - normalized.row[0] + 1;
4270
- const rows = Array.from({
4271
- length: height
4272
- }, ()=>Array(width).fill(null));
4273
- for(let row = normalized.row[0]; row <= normalized.row[1]; row += 1){
4274
- const source = sheet.data?.[row];
4275
- if (!source) continue;
4276
- const target = rows[row - normalized.row[0]];
4277
- for(let column = normalized.column[0]; column <= normalized.column[1]; column += 1)target[column - normalized.column[0]] = source[column] ?? null;
4278
- }
4279
- for (const entry of sheet.celldata ?? []){
4280
- if (entry.r < normalized.row[0] || entry.r > normalized.row[1] || entry.c < normalized.column[0] || entry.c > normalized.column[1]) continue;
4281
- const row = rows[entry.r - normalized.row[0]];
4282
- const column = entry.c - normalized.column[0];
4283
- if (null === row[column]) row[column] = entry.v ?? null;
4284
- }
4285
- return rows;
4286
- }
4287
- function spreadsheetSortOwnedRangeForExactRange(sheet, range) {
4288
- const normalized = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4289
- if (!normalized) return null;
4290
- const matches = spreadsheetSortOwnedRanges(sheet).filter((candidate)=>spreadsheetCellRangesEqual(candidate.range, normalized));
4291
- return 1 === matches.length ? matches[0] : null;
4292
- }
4293
- function spreadsheetSortOwnedRangeContaining(sheet, range) {
4294
- const matches = spreadsheetSortOwnedRanges(sheet).filter((candidate)=>spreadsheetSortRangeContains(candidate.range, range));
4295
- return 1 === matches.length ? matches[0] : null;
4296
- }
4297
- function spreadsheetSortOwnedRanges(sheet) {
4298
- const ranges = [];
4299
- const autoFilter = spreadsheet_cell_range_normalizeSpreadsheetCellRange(sheet.filter_select ?? {
4300
- row: [],
4301
- column: []
4302
- });
4303
- if (autoFilter && autoFilter.row[1] > autoFilter.row[0]) ranges.push({
4304
- range: autoFilter,
4305
- scope: {
4306
- kind: 'auto-filter',
4307
- hasHeader: true
4308
- }
4309
- });
4310
- for (const table of sheet.tables ?? []){
4311
- const tableRange = spreadsheet_cell_range_normalizeSpreadsheetCellRange(table.range);
4312
- if (!tableRange || !table.id || 0 === table.columns.length) continue;
4313
- const width = tableRange.column[1] - tableRange.column[0] + 1;
4314
- if (width !== table.columns.length) continue;
4315
- const endRow = tableRange.row[1] - Number(table.totalsRow);
4316
- if (!(endRow <= tableRange.row[0])) ranges.push({
4317
- range: {
4318
- row: [
4319
- tableRange.row[0],
4320
- endRow
4321
- ],
4322
- column: [
4323
- ...tableRange.column
4324
- ]
4325
- },
4326
- scope: {
4327
- kind: 'table',
4328
- tableId: table.id,
4329
- hasHeader: table.headerRow
4330
- }
4331
- });
4332
- }
4333
- return ranges;
4334
- }
4335
- function spreadsheetSortRangeContains(outer, inner) {
4336
- 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];
4337
- }
4338
- function validateSpreadsheetSortRequest(request) {
4339
- const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(request.range);
4340
- if (!range || 'string' != typeof request.sheetId || !request.sheetId.trim()) return spreadsheetSortError('invalid-range');
4341
- const scope = normalizeSpreadsheetSortOwnedScope(request.scope);
4342
- if (void 0 !== request.scope && !scope) return spreadsheetSortError('invalid-scope');
4343
- if ('top-to-bottom' !== request.orientation && 'left-to-right' !== request.orientation) return spreadsheetSortError('invalid-orientation');
4344
- const caseSensitive = void 0 === request.caseSensitive ? DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS.caseSensitive : request.caseSensitive;
4345
- if ('boolean' != typeof caseSensitive) return spreadsheetSortError('invalid-case-sensitivity');
4346
- const textMethod = void 0 === request.textMethod ? DEFAULT_SPREADSHEET_SORT_TEXT_OPTIONS.textMethod : request.textMethod;
4347
- if (!isSpreadsheetSortTextMethod(textMethod)) return spreadsheetSortError('invalid-text-method');
4348
- if ('left-to-right' === request.orientation && request.hasHeader) return spreadsheetSortError('invalid-header');
4349
- const rangeError = spreadsheetSortRangeError(range);
4350
- if (rangeError) return rangeError;
4351
- if (!Array.isArray(request.keys) || !request.keys.length) return spreadsheetSortError('missing-key');
4352
- if (request.keys.length > MAX_SPREADSHEET_SORT_KEYS) return spreadsheetSortError('too-many-keys');
4353
- const keyRange = 'top-to-bottom' === request.orientation ? range.column : range.row;
4354
- const outOfRangeCode = 'top-to-bottom' === request.orientation ? 'column-out-of-range' : 'row-out-of-range';
4355
- const keys = [];
4356
- for (const key of request.keys){
4357
- if (!Number.isSafeInteger(key.index) || key.index < keyRange[0] || key.index > keyRange[1]) return spreadsheetSortError(outOfRangeCode);
4358
- if (void 0 !== key.sortOn) {
4359
- if ('first' !== key.position && 'last' !== key.position || void 0 !== key.customList || void 0 !== key.direction) return spreadsheetSortError('invalid-appearance');
4360
- if ('cell-color' === key.sortOn || 'font-color' === key.sortOn) {
4361
- const color = normalizeSpreadsheetSortColor(key.color);
4362
- if (void 0 === color || void 0 !== key.icon) return spreadsheetSortError('invalid-appearance');
4363
- const normalized = {
4364
- index: key.index,
4365
- sortOn: key.sortOn,
4366
- color,
4367
- position: key.position
4368
- };
4369
- if (!appendSpreadsheetSortKey(keys, normalized)) return spreadsheetSortError('duplicate-key');
4370
- continue;
4371
- }
4372
- if ('icon' === key.sortOn && void 0 === key.color) {
4373
- const icon = normalizeSpreadsheetSortIcon(key.icon);
4374
- if (icon) {
4375
- const normalized = {
4376
- index: key.index,
4377
- sortOn: 'icon',
4378
- icon,
4379
- position: key.position
4380
- };
4381
- if (!appendSpreadsheetSortKey(keys, normalized)) return spreadsheetSortError('duplicate-key');
4382
- continue;
4383
- }
4384
- }
4385
- return spreadsheetSortError('invalid-appearance');
4386
- }
4387
- if (void 0 !== key.customList) {
4388
- if (void 0 !== key.direction) return spreadsheetSortError('invalid-custom-list');
4389
- const customList = validateSpreadsheetSortCustomList(key.customList);
4390
- if (!customList.ok) return spreadsheetSortError('invalid-custom-list');
4391
- if (!appendSpreadsheetSortKey(keys, {
4392
- index: key.index,
4393
- customList: customList.entries
4394
- })) return spreadsheetSortError('duplicate-key');
4395
- continue;
4396
- }
4397
- if ('ascending' !== key.direction && 'descending' !== key.direction) return spreadsheetSortError('invalid-direction');
4398
- if (!appendSpreadsheetSortKey(keys, {
4399
- index: key.index,
4400
- direction: key.direction
4401
- })) return spreadsheetSortError('duplicate-key');
4402
- }
4403
- if ('top-to-bottom' === request.orientation) {
4404
- const rowCount = range.row[1] - range.row[0] + 1;
4405
- if (rowCount - (request.hasHeader ? 1 : 0) < 2) return spreadsheetSortError('not-enough-rows');
4406
- } else if (range.column[1] - range.column[0] + 1 < 2) return spreadsheetSortError('not-enough-columns');
4407
- return {
4408
- ok: true,
4409
- request: {
4410
- sheetId: request.sheetId,
4411
- range,
4412
- orientation: request.orientation,
4413
- caseSensitive,
4414
- textMethod,
4415
- hasHeader: Boolean(request.hasHeader),
4416
- keys,
4417
- ...scope ? {
4418
- scope
4419
- } : {}
4420
- }
4421
- };
4422
- }
4423
- function spreadsheetSortAppearanceRowsMatch(rows, appearances) {
4424
- return rows.length === appearances.length && rows.every((row, index)=>row.length === (appearances[index]?.length ?? -1));
4425
- }
4426
- function spreadsheetSortError(code) {
4427
- const messages = {
4428
- 'column-out-of-range': '排序列必须位于当前选定区域内。',
4429
- 'duplicate-key': '同一行或列不能重复使用相同的值或外观排序条件。',
4430
- 'formula-reference-out-of-range': '排序会使相对公式引用超出工作表范围,因此未应用任何更改。',
4431
- 'invalid-appearance': '外观排序条件或当前颜色/图标快照无效,因此未应用任何更改。',
4432
- 'invalid-case-sensitivity': '请选择有效的大小写比较规则。',
4433
- 'invalid-custom-list': '自定义排序序列无效,请检查项目数量、长度和重复项。',
4434
- 'invalid-direction': '请选择有效的升序或降序次序。',
4435
- 'invalid-header': '按行排序会移动所有选定列,不能保留标题列。',
4436
- 'invalid-matrix': '排序只能应用到已完整读取的矩形区域。',
4437
- 'invalid-orientation': '请选择有效的按列或按行排序方向。',
4438
- 'invalid-range': '请选择一个有效的连续单元格区域。',
4439
- 'invalid-scope': '排序区域的表格或筛选所有权已变化,请重新打开排序。',
4440
- 'invalid-text-method': '请选择拼音排序或笔画排序。',
4441
- 'missing-key': '请至少添加一个排序条件。',
4442
- 'not-enough-columns': '当前区域没有足够的数据列可供按行排序。',
4443
- 'not-enough-rows': '当前区域没有足够的数据行可供排序。',
4444
- 'range-too-large': `一次最多可排序 ${MAX_SPREADSHEET_SORT_CELLS.toLocaleString('en-US')} 个单元格。`,
4445
- 'row-out-of-range': '排序行必须位于当前选定区域内。',
4446
- 'too-many-keys': `一次最多可设置 ${MAX_SPREADSHEET_SORT_KEYS} 个排序条件。`,
4447
- 'unsupported-text-method': '当前运行环境不支持所选中文排序方法,因此未应用任何更改。',
4448
- 'unsupported-linked-cell': '当前区域包含坐标关联的超链接,尚不能安全地随排序移动。'
4449
- };
4450
- return {
4451
- ok: false,
4452
- code,
4453
- message: messages[code]
4454
- };
4455
- }
4456
- function normalizeSpreadsheetSortOwnedScope(value) {
4457
- if (void 0 === value) return null;
4458
- if (!value || 'object' != typeof value || Array.isArray(value)) return null;
4459
- if ('auto-filter' === value.kind && true === value.hasHeader && Object.keys(value).every((key)=>'kind' === key || 'hasHeader' === key)) return {
4460
- kind: 'auto-filter',
4461
- hasHeader: true
4462
- };
4463
- 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 {
4464
- kind: 'table',
4465
- tableId: value.tableId,
4466
- hasHeader: value.hasHeader
4467
- };
4468
- return null;
4469
- }
4470
- function spreadsheetSortRangeError(range) {
4471
- const area = spreadsheet_cell_range_spreadsheetCellRangeArea(range);
4472
- if (!Number.isSafeInteger(area) || area > MAX_SPREADSHEET_SORT_CELLS) return spreadsheetSortError('range-too-large');
4473
- return null;
4474
- }
4475
- function spreadsheetSortRowsHaveHeader(rows) {
4476
- const first = rows[0];
4477
- if (!first?.length) return false;
4478
- const labels = first.map(spreadsheetSortHeaderText);
4479
- return labels.every(Boolean) && new Set(labels.map((label)=>label.toLocaleLowerCase())).size === labels.length;
4480
- }
4481
- function appendSpreadsheetSortKey(keys, key) {
4482
- const identity = spreadsheetSortKeyIdentity(key);
4483
- if (keys.some((candidate)=>spreadsheetSortKeyIdentity(candidate) === identity)) return false;
4484
- keys.push(key);
4485
- return true;
4486
- }
4487
- function spreadsheetSortKeyIdentity(key) {
4488
- if ('cell-color' === key.sortOn || 'font-color' === key.sortOn) return `${key.index}:${spreadsheetSortAppearanceTargetValue({
4489
- kind: key.sortOn,
4490
- color: key.color
4491
- })}`;
4492
- if ('icon' === key.sortOn) return `${key.index}:${spreadsheetSortAppearanceTargetValue({
4493
- kind: 'icon',
4494
- icon: key.icon
4495
- })}`;
4496
- return `${key.index}:values`;
4497
- }
4498
- function spreadsheetSortActiveIndex(active, range) {
4499
- return active >= range[0] && active <= range[1] ? active : range[0];
4500
- }
4501
- function spreadsheetSortHeaderText(cell) {
4502
- if (cell?.f || 'string' != typeof cell?.v) return '';
4503
- const value = cell.v;
4504
- return Array.from(value.trim()).slice(0, MAX_SPREADSHEET_SORT_HEADER_LENGTH).join('');
4505
- }
4506
- function spreadsheetSortColumnLabel(column) {
4507
- return formatSpreadsheetCellRanges([
4508
- {
4509
- row: [
4510
- 0,
4511
- 0
4512
- ],
4513
- column: [
4514
- column,
4515
- column
4516
- ]
4517
- }
4518
- ]).replace(/\d+$/, '');
4519
- }
4520
- function reconcileSpreadsheetFiltersAfterFortune(sheets, sourceSheets, operations = [], context = {
4521
- now: new Date()
4522
- }) {
4523
- const coordinates = spreadsheetCellOperationCoordinates(operations);
4524
- return sheets.map((sheet, index)=>{
4525
- const source = spreadsheet_filter_reconciliation_sourceSheet(sheet, sourceSheets, index);
4526
- if (!source || !spreadsheetSheetHasCriteria(source) || !spreadsheetFilterReconciliationIsBounded(source) || !spreadsheetFilterRangesWereEdited(source, operations, coordinates)) return sheet;
4527
- const previousTableOwnedRows = spreadsheetTableFilterOwnedRows(source, context);
4528
- const manuallyHiddenRows = workSpreadsheetAutoFilterManuallyHiddenRows(source);
4529
- for (const row of previousTableOwnedRows)manuallyHiddenRows.delete(row);
4530
- let next = sheet;
4531
- if (normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select) && workSpreadsheetAutoFilterCriteriaEntries(source).length) next = workSpreadsheetSheetWithReappliedAutoFilterCriteria(sheet, source, manuallyHiddenRows, context);
4532
- const hiddenRows = new Set(manuallyHiddenRows);
4533
- for (const row of workSpreadsheetAutoFilterOwnedRows(next))hiddenRows.add(row);
4534
- for (const row of spreadsheetTableFilterOwnedRows(next, context))hiddenRows.add(row);
4535
- const rowhidden = Object.fromEntries([
4536
- ...hiddenRows
4537
- ].sort(compareSpreadsheetRowKeys).map((row)=>[
4538
- row,
4539
- 0
4540
- ]));
4541
- return {
4542
- ...next,
4543
- config: {
4544
- ...next.config ?? {},
4545
- rowhidden
4546
- }
4547
- };
4548
- });
4549
- }
4550
- function reconcileSpreadsheetFiltersAfterSort(sheet, source, range, sourceIndexes, context = {
4551
- now: new Date()
4552
- }) {
4553
- if (!spreadsheetFilterReconciliationIsBounded(source) || !spreadsheetSortSourceIndexesAreValid(range, sourceIndexes)) return null;
4554
- const reconciled = reconcileSpreadsheetFiltersAfterFortune([
4555
- sheet
4556
- ], [
4557
- source
4558
- ], [], context)[0];
4559
- if (!reconciled) return null;
4560
- let filter = reconciled.filter;
4561
- const autoFilter = normalizedWorkSpreadsheetAutoFilterRange(source.filter_select);
4562
- 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);
4563
- const manuallyHiddenRows = workSpreadsheetAutoFilterManuallyHiddenRows(source);
4564
- for (const row of spreadsheetTableFilterOwnedRows(source, context))manuallyHiddenRows.delete(row);
4565
- const withFilter = {
4566
- ...reconciled,
4567
- filter
4568
- };
4569
- const hiddenRows = new Set(manuallyHiddenRows);
4570
- for (const row of workSpreadsheetAutoFilterOwnedRows(withFilter))hiddenRows.add(row);
4571
- for (const row of spreadsheetTableFilterOwnedRows(withFilter, context))hiddenRows.add(row);
4572
- const rowhidden = Object.fromEntries([
4573
- ...hiddenRows
4574
- ].sort(compareSpreadsheetRowKeys).map((row)=>[
4575
- row,
4576
- 0
4577
- ]));
4578
- return {
4579
- ...sheet,
4580
- filter,
4581
- config: {
4582
- ...sheet.config ?? {},
4583
- rowhidden
4584
- }
4585
- };
4586
- }
4587
- function spreadsheetFilterReconciliationIsBounded(sheet) {
4588
- let cellVisits = 0;
4589
- const autoFilter = normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select);
4590
- if (autoFilter) {
4591
- const filterCount = Object.keys(sheet.filter ?? {}).length;
4592
- cellVisits += (autoFilter.row[1] - autoFilter.row[0]) * Math.max(filterCount, workSpreadsheetAutoFilterCriteriaEntries(sheet).length);
4593
- }
4594
- for (const table of sheet.tables ?? []){
4595
- const range = spreadsheetTableFilterRange(table);
4596
- if (range) {
4597
- cellVisits += (range.row[1] - range.row[0]) * table.filters.length;
4598
- if (!Number.isSafeInteger(cellVisits) || cellVisits > MAX_SPREADSHEET_SORT_CELLS) return false;
4599
- }
4600
- }
4601
- return Number.isSafeInteger(cellVisits) && cellVisits <= MAX_SPREADSHEET_SORT_CELLS;
4602
- }
4603
- function spreadsheetFilterWithRemappedOpaqueEntries(reconciled, source, range, sourceIndexes) {
4604
- const next = {
4605
- ...reconciled.filter ?? {}
4606
- };
4607
- const typedColumns = new Set(workSpreadsheetAutoFilterCriteriaEntries(source).map((entry)=>String(entry.column)));
4608
- for (const [column, entry] of Object.entries(source.filter ?? {}))if (!typedColumns.has(column) && spreadsheet_filter_reconciliation_isRecord(entry)) next[column] = {
4609
- ...entry,
4610
- rowhidden: spreadsheetRemappedFilterRows(entry.rowhidden, range, sourceIndexes)
4611
- };
4612
- return next;
4613
- }
4614
- function spreadsheetRemappedFilterRows(value, range, sourceIndexes) {
4615
- const sourceRows = spreadsheet_filter_reconciliation_isRecord(value) ? value : {};
4616
- const rowhidden = {};
4617
- const dataStart = range.row[0] + 1;
4618
- sourceIndexes.forEach((sourceIndex, targetIndex)=>{
4619
- if (Object.hasOwn(sourceRows, String(dataStart + sourceIndex))) rowhidden[String(dataStart + targetIndex)] = 0;
4620
- });
4621
- return rowhidden;
4622
- }
4623
- function spreadsheetSortSourceIndexesAreValid(range, sourceIndexes) {
4624
- const count = range.row[1] - range.row[0];
4625
- return sourceIndexes.length === count && sourceIndexes.every((index)=>Number.isSafeInteger(index) && index >= 0 && index < count) && new Set(sourceIndexes).size === count;
4626
- }
4627
- function spreadsheetSheetHasCriteria(sheet) {
4628
- return Boolean(workSpreadsheetAutoFilterCriteriaEntries(sheet).length || sheet.tables?.some((table)=>table.filters.length));
4629
- }
4630
- function spreadsheetFilterRangesWereEdited(sheet, operations, coordinates) {
4631
- if (!operations.length) return true;
4632
- if (!sheet.id) return false;
4633
- const sheetCoordinates = coordinates?.get(sheet.id);
4634
- if (sheetCoordinates) {
4635
- const ranges = spreadsheetFilterRanges(sheet);
4636
- return [
4637
- ...sheetCoordinates.values()
4638
- ].some(({ column, row })=>ranges.some((range)=>row >= range.row[0] && row <= range.row[1] && column >= range.column[0] && column <= range.column[1]));
4639
- }
4640
- return operations.some((operation)=>operation.id === sheet.id);
4641
- }
4642
- function spreadsheetFilterRanges(sheet) {
4643
- const ranges = [];
4644
- const autoFilter = normalizedWorkSpreadsheetAutoFilterRange(sheet.filter_select);
4645
- if (autoFilter && workSpreadsheetAutoFilterCriteriaEntries(sheet).length) ranges.push(autoFilter);
4646
- for (const table of sheet.tables ?? []){
4647
- if (!table.filters.length) continue;
4648
- const range = spreadsheetTableFilterRange(table);
4649
- if (range) ranges.push(range);
4650
- }
4651
- return ranges;
4652
- }
4653
- function spreadsheetTableFilterOwnedRows(sheet, context) {
4654
- const hidden = new Set();
4655
- for (const table of sheet.tables ?? []){
4656
- const range = spreadsheetTableFilterRange(table);
4657
- if (range) for (const filter of table.filters){
4658
- const column = range.column[0] + filter.column;
4659
- const rowhidden = workSpreadsheetFilterHiddenRows(sheet, range, column, filter.criteria, context);
4660
- for (const row of Object.keys(rowhidden ?? {}))hidden.add(row);
4661
- }
4662
- }
4663
- return hidden;
4664
- }
4665
- function spreadsheetTableFilterRange(table) {
4666
- if (!table.headerRow || !table.filters.length) return null;
4667
- const rowStart = spreadsheet_filter_reconciliation_finiteIndex(table.range.row[0]);
4668
- const rowEnd = spreadsheet_filter_reconciliation_finiteIndex(table.range.row[1]);
4669
- const columnStart = spreadsheet_filter_reconciliation_finiteIndex(table.range.column[0]);
4670
- const columnEnd = spreadsheet_filter_reconciliation_finiteIndex(table.range.column[1]);
4671
- if (null === rowStart || null === rowEnd || null === columnStart || null === columnEnd) return null;
4672
- const end = Math.max(rowStart, rowEnd) - Number(table.totalsRow);
4673
- const start = Math.min(rowStart, rowEnd);
4674
- if (end <= start) return null;
4675
- return {
4676
- row: [
4677
- start,
4678
- end
4679
- ],
4680
- column: [
4681
- Math.min(columnStart, columnEnd),
4682
- Math.max(columnStart, columnEnd)
4683
- ]
4684
- };
4685
- }
4686
- function spreadsheet_filter_reconciliation_sourceSheet(sheet, sources, index) {
4687
- return sources.find((candidate)=>candidate.id === sheet.id) ?? sources[index];
4688
- }
4689
- function spreadsheet_filter_reconciliation_finiteIndex(value) {
4690
- return 'number' == typeof value && Number.isSafeInteger(value) && value >= 0 ? value : null;
4691
- }
4692
- function compareSpreadsheetRowKeys(left, right) {
4693
- return Number(left) - Number(right);
4694
- }
4695
- function spreadsheet_filter_reconciliation_isRecord(value) {
4696
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
4697
- }
4698
- function canApplySpreadsheetTableStructureChange(sheet, change) {
4699
- if (!sheet || !validSpreadsheetTableStructureChange(change)) return false;
4700
- for (const table of sheet.tables ?? []){
4701
- if ('delete' === change.kind && 'row' === change.axis && spreadsheetTableDeleteRemovesSemanticRow(table, change.start, change.end)) return false;
4702
- if (!transformSpreadsheetTableStructure(table, change)) return false;
4703
- }
4704
- return true;
4705
- }
4706
- function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations = []) {
4707
- return sheets.map((sheet, index)=>{
4708
- const source = sourceSheetForReconciliation(sheet, sourceSheets, index);
4709
- if (!source?.tables?.length) return sheet;
4710
- const sheetOperations = operations.filter((operation)=>operation.id === source.id);
4711
- let tables = source.tables;
4712
- const reconcileColumns = new Set();
4713
- const calculatedColumnRows = new Map();
4714
- const editedTotalsColumns = new Map();
4715
- if (!operations.length) for (const table of tables)reconcileColumns.add(table.id);
4716
- for (const operation of sheetOperations){
4717
- const structure = spreadsheetTableStructureChangeFromOperation(operation);
4718
- if (structure) {
4719
- transformCalculatedColumnRows(calculatedColumnRows, structure);
4720
- const transformed = [];
4721
- for (const table of tables){
4722
- if ('row' === structure.axis && 'insert' === structure.kind) {
4723
- const insertedRows = rowsInsertedIntoSpreadsheetTableBody(table, structure);
4724
- if (insertedRows.length) {
4725
- const rows = calculatedColumnRows.get(table.id) ?? new Set();
4726
- for (const row of insertedRows)rows.add(row);
4727
- calculatedColumnRows.set(table.id, rows);
4728
- }
4729
- }
4730
- const next = transformSpreadsheetTableStructure(table, structure);
4731
- if (next) {
4732
- transformed.push(next);
4733
- if (next !== table) reconcileColumns.add(table.id);
4734
- }
4735
- }
4736
- tables = transformed;
4737
- continue;
4738
- }
4739
- const coordinate = spreadsheetCellCoordinateFromOperation(operation);
4740
- if (!coordinate) continue;
4741
- const table = tables.find((candidate)=>spreadsheetTableContainsCell(candidate, coordinate.row, coordinate.column));
4742
- if (table) {
4743
- reconcileColumns.add(table.id);
4744
- if (table.totalsRow && coordinate.row === table.range.row[1]) {
4745
- const offset = coordinate.column - table.range.column[0];
4746
- if (offset >= 0 && offset < table.columns.length) {
4747
- const offsets = editedTotalsColumns.get(table.id) ?? new Set();
4748
- offsets.add(offset);
4749
- editedTotalsColumns.set(table.id, offsets);
4750
- }
4751
- }
4752
- }
4753
- }
4754
- const updates = [];
4755
- const readCell = spreadsheet_table_reconciliation_spreadsheetSheetCellReader(sheet);
4756
- tables = tables.map((table)=>{
4757
- if (!reconcileColumns.has(table.id)) return table;
4758
- const columns = canonicalSpreadsheetTableColumns(table, readCell);
4759
- const calculatedColumns = reconcileSpreadsheetTableCalculatedColumns(sheet, {
4760
- ...table,
4761
- columns
4762
- });
4763
- const reconciledColumns = reconcileSpreadsheetTableTotalsColumns(sheet, {
4764
- ...table,
4765
- columns: calculatedColumns
4766
- }, editedTotalsColumns.get(table.id));
4767
- if (table.headerRow) for (const [offset, column] of reconciledColumns.entries()){
4768
- const cellColumn = table.range.column[0] + offset;
4769
- if (spreadsheetCellText(readCell(table.range.row[0], cellColumn)) !== column.name) updates.push({
4770
- column: cellColumn,
4771
- name: column.name,
4772
- row: table.range.row[0]
4773
- });
4774
- }
4775
- return sameSpreadsheetTableColumns(table.columns, reconciledColumns) ? table : {
4776
- ...table,
4777
- columns: reconciledColumns
4778
- };
4779
- });
4780
- let withCalculatedColumns = sheet;
4781
- for (const table of tables){
4782
- const rows = calculatedColumnRows.get(table.id);
4783
- if (rows?.size) withCalculatedColumns = fillSpreadsheetTableCalculatedColumns(withCalculatedColumns, table, [
4784
- ...rows
4785
- ]);
4786
- }
4787
- const withHeaders = stampSpreadsheetTableHeaderCells(withCalculatedColumns, updates);
4788
- let withTotals = withHeaders;
4789
- for (const table of tables){
4790
- const previous = source.tables.find((candidate)=>candidate.id === table.id);
4791
- withTotals = synchronizeSpreadsheetTableTotalsRow(withTotals, previous, table);
4792
- }
4793
- return {
4794
- ...withTotals,
4795
- tables: tables.length ? tables : void 0
4796
- };
4797
- });
4798
- }
4799
- function transformSpreadsheetTableStructure(table, change) {
4800
- if (!validSpreadsheetTableStructureChange(change)) return null;
4801
- const axis = 'row' === change.axis ? table.range.row : table.range.column;
4802
- const transformed = 'insert' === change.kind ? transformSpreadsheetTableAxisForInsertion(axis, change) : transformSpreadsheetTableAxisForDeletion(axis, change);
4803
- if (!transformed) return null;
4804
- const range = {
4805
- row: 'row' === change.axis ? transformed.axis : [
4806
- ...table.range.row
4807
- ],
4808
- column: 'column' === change.axis ? transformed.axis : [
4809
- ...table.range.column
4810
- ]
4811
- };
4812
- const height = range.row[1] - range.row[0] + 1;
4813
- if (height <= Number(table.headerRow) + Number(table.totalsRow)) return null;
4814
- if ('column' !== change.axis || !transformed.inside) {
4815
- 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;
4816
- return {
4817
- ...table,
4818
- range
4819
- };
4820
- }
4821
- if ('insert' === change.kind) {
4822
- const columns = [
4823
- ...table.columns
4824
- ];
4825
- columns.splice(transformed.offset, 0, ...Array.from({
4826
- length: change.count
4827
- }, ()=>({
4828
- name: ''
4829
- })));
4830
- return {
4831
- ...table,
4832
- range,
4833
- columns,
4834
- filters: table.filters.map((filter)=>filter.column >= transformed.offset ? {
4835
- ...filter,
4836
- column: filter.column + change.count
4837
- } : filter)
4838
- };
4839
- }
4840
- if (!('removed' in transformed)) return null;
4841
- const removed = transformed.removed;
4842
- if ('number' != typeof removed) return null;
4843
- const columns = [
4844
- ...table.columns
4845
- ];
4846
- columns.splice(transformed.offset, removed);
4847
- if (!columns.length) return null;
4848
- return {
4849
- ...table,
4850
- range,
4851
- columns,
4852
- filters: filtersAfterSpreadsheetTableColumnDeletion(table.filters, transformed.offset, removed)
4853
- };
4854
- }
4855
- function spreadsheetTableContainsCell(table, row, column) {
4856
- return row >= table.range.row[0] && row <= table.range.row[1] && column >= table.range.column[0] && column <= table.range.column[1];
4857
- }
4858
- function rowsInsertedIntoSpreadsheetTableBody(table, change) {
4859
- if ('row' !== change.axis) return [];
4860
- const transformed = transformSpreadsheetTableAxisForInsertion(table.range.row, change);
4861
- if (!transformed.inside) return [];
4862
- const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
4863
- const bodyStart = table.range.row[0] + Number(table.headerRow);
4864
- if (insertion < bodyStart) return [];
4865
- return Array.from({
4866
- length: change.count
4867
- }, (_, offset)=>insertion + offset);
4868
- }
4869
- function transformCalculatedColumnRows(rowsByTable, change) {
4870
- if ('row' !== change.axis || !rowsByTable.size) return;
4871
- if ('insert' === change.kind) {
4872
- const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
4873
- for (const [tableId, rows] of rowsByTable){
4874
- const next = new Set();
4875
- for (const row of rows)next.add(row >= insertion ? row + change.count : row);
4876
- rowsByTable.set(tableId, next);
4877
- }
4878
- return;
4879
- }
4880
- const count = change.end - change.start + 1;
4881
- for (const [tableId, rows] of rowsByTable){
4882
- const next = new Set();
4883
- for (const row of rows)if (row < change.start) next.add(row);
4884
- else if (row > change.end) next.add(row - count);
4885
- rowsByTable.set(tableId, next);
4886
- }
4887
- }
4888
- function transformSpreadsheetTableAxisForInsertion(axis, change) {
4889
- const start = axis[0] ?? 0;
4890
- const end = axis[1] ?? start;
4891
- const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
4892
- if (insertion <= start) return {
4893
- axis: [
4894
- start + change.count,
4895
- end + change.count
4896
- ],
4897
- inside: false,
4898
- offset: 0
4899
- };
4900
- if (insertion <= end) return {
4901
- axis: [
4902
- start,
4903
- end + change.count
4904
- ],
4905
- inside: true,
4906
- offset: insertion - start
4907
- };
4908
- return {
4909
- axis: [
4910
- start,
4911
- end
4912
- ],
4913
- inside: false,
4914
- offset: 0
4915
- };
4916
- }
4917
- function transformSpreadsheetTableAxisForDeletion(axis, change) {
4918
- const start = axis[0] ?? 0;
4919
- const end = axis[1] ?? start;
4920
- const count = change.end - change.start + 1;
4921
- if (change.end < start) return {
4922
- axis: [
4923
- start - count,
4924
- end - count
4925
- ],
4926
- inside: false,
4927
- offset: 0,
4928
- removed: 0
4929
- };
4930
- if (change.start > end) return {
4931
- axis: [
4932
- start,
4933
- end
4934
- ],
4935
- inside: false,
4936
- offset: 0,
4937
- removed: 0
4938
- };
4939
- const overlapStart = Math.max(start, change.start);
4940
- const overlapEnd = Math.min(end, change.end);
4941
- const removed = overlapEnd - overlapStart + 1;
4942
- const remaining = end - start + 1 - removed;
4943
- if (remaining <= 0) return null;
4944
- const deletedBeforeStart = Math.max(0, Math.min(change.end, start - 1) - change.start + 1);
4945
- const nextStart = start - deletedBeforeStart;
4946
- return {
4947
- axis: [
4948
- nextStart,
4949
- nextStart + remaining - 1
4950
- ],
4951
- inside: true,
4952
- offset: overlapStart - start,
4953
- removed
4954
- };
4955
- }
4956
- function filtersAfterSpreadsheetTableColumnDeletion(filters, offset, removed) {
4957
- const end = offset + removed - 1;
4958
- return filters.flatMap((filter)=>{
4959
- if (filter.column >= offset && filter.column <= end) return [];
4960
- return [
4961
- filter.column > end ? {
4962
- ...filter,
4963
- column: filter.column - removed
4964
- } : filter
4965
- ];
4966
- });
4967
- }
4968
- function canonicalSpreadsheetTableColumns(table, readCell) {
4969
- const width = table.range.column[1] - table.range.column[0] + 1;
4970
- const observed = new Set();
4971
- return Array.from({
4972
- length: width
4973
- }, (_, offset)=>{
4974
- const raw = table.headerRow ? spreadsheetCellText(readCell(table.range.row[0], table.range.column[0] + offset)).trim() : table.columns[offset]?.name.trim() ?? '';
4975
- const base = validSpreadsheetTableColumnName(raw) ? raw : `Column${offset + 1}`;
4976
- let candidate = base;
4977
- let suffix = 2;
4978
- while(observed.has(candidate.toLocaleLowerCase())){
4979
- const suffixText = String(suffix);
4980
- candidate = `${truncateSpreadsheetTableColumnName(base, 255 - suffixText.length)}${suffixText}`;
4981
- suffix += 1;
4982
- }
4983
- observed.add(candidate.toLocaleLowerCase());
4984
- return {
4985
- ...table.columns[offset] ?? {},
4986
- name: candidate
4987
- };
4988
- });
4989
- }
4990
- function spreadsheetTableDeleteRemovesSemanticRow(table, start, end) {
4991
- 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]);
4992
- }
4993
- function spreadsheetTableStructureChangeFromOperation(operation) {
4994
- if (!spreadsheet_table_reconciliation_isRecord(operation.value)) return null;
4995
- if ('insertRowCol' === operation.op) {
4996
- const { count, direction, index, type } = operation.value;
4997
- const change = {
4998
- axis: 'column' === type ? 'column' : 'row',
4999
- count: Number(count),
5000
- direction: 'rightbottom' === direction ? 'rightbottom' : 'lefttop',
5001
- index: Number(index),
5002
- kind: 'insert'
5003
- };
5004
- return ('column' === type || 'row' === type) && ('lefttop' === direction || 'rightbottom' === direction) && validSpreadsheetTableStructureChange(change) ? change : null;
5005
- }
5006
- if ('deleteRowCol' === operation.op) {
5007
- const { end, start, type } = operation.value;
5008
- const change = {
5009
- axis: 'column' === type ? 'column' : 'row',
5010
- end: Number(end),
5011
- kind: 'delete',
5012
- start: Number(start)
5013
- };
5014
- return ('column' === type || 'row' === type) && validSpreadsheetTableStructureChange(change) ? change : null;
5015
- }
5016
- return null;
5017
- }
5018
- function spreadsheetCellCoordinateFromOperation(operation) {
5019
- if ('data' !== operation.path[0] || operation.path.length < 3) return null;
5020
- const row = operation.path[1];
5021
- const column = operation.path[2];
5022
- return Number.isSafeInteger(row) && Number(row) >= 0 && Number.isSafeInteger(column) && Number(column) >= 0 ? {
5023
- column: Number(column),
5024
- row: Number(row)
5025
- } : null;
5026
- }
5027
- function validSpreadsheetTableStructureChange(change) {
5028
- if ('insert' === change.kind) return Number.isSafeInteger(change.index) && change.index >= 0 && Number.isSafeInteger(change.count) && change.count > 0;
5029
- return Number.isSafeInteger(change.start) && change.start >= 0 && Number.isSafeInteger(change.end) && change.end >= change.start;
5030
- }
5031
- function sourceSheetForReconciliation(sheet, sourceSheets, index) {
5032
- return (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
5033
- }
5034
- function spreadsheet_table_reconciliation_spreadsheetSheetCellReader(sheet) {
5035
- if (void 0 !== sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
5036
- const cells = new Map((sheet.celldata ?? []).map((entry)=>[
5037
- `${entry.r}:${entry.c}`,
5038
- entry.v
5039
- ]));
5040
- return (row, column)=>cells.get(`${row}:${column}`) ?? null;
5041
- }
5042
- function stampSpreadsheetTableHeaderCells(sheet, updates) {
5043
- if (!updates.length) return sheet;
5044
- if (void 0 !== sheet.data) {
5045
- const data = [];
5046
- data.length = sheet.data.length;
5047
- for (const [row, value] of sparseArrayEntries(sheet.data))data[row] = value;
5048
- const mutableRows = new Map();
5049
- for (const update of updates){
5050
- let row = mutableRows.get(update.row);
5051
- if (!row) {
5052
- const source = data[update.row];
5053
- row = [];
5054
- row.length = source?.length ?? 0;
5055
- for (const column of sparseArrayIndexes(source))row[column] = source?.[column];
5056
- mutableRows.set(update.row, row);
5057
- data[update.row] = row;
5058
- }
5059
- row[update.column] = stampSpreadsheetTableHeaderCell(row[update.column], update.name);
5060
- }
5061
- return {
5062
- ...sheet,
5063
- data
5064
- };
5065
- }
5066
- const celldata = [
5067
- ...sheet.celldata ?? []
5068
- ];
5069
- const indexes = new Map(celldata.map((entry, index)=>[
5070
- `${entry.r}:${entry.c}`,
5071
- index
5072
- ]));
5073
- for (const update of updates){
5074
- const key = `${update.row}:${update.column}`;
5075
- const index = indexes.get(key);
5076
- const source = void 0 === index ? void 0 : celldata[index];
5077
- const entry = {
5078
- r: update.row,
5079
- c: update.column,
5080
- v: stampSpreadsheetTableHeaderCell(source?.v, update.name)
5081
- };
5082
- if (void 0 === index) {
5083
- indexes.set(key, celldata.length);
5084
- celldata.push(entry);
5085
- } else celldata[index] = entry;
5086
- }
5087
- return {
5088
- ...sheet,
5089
- celldata
5090
- };
5091
- }
5092
- function stampSpreadsheetTableHeaderCell(cell, name) {
5093
- return {
5094
- ...cell ?? {},
5095
- m: name,
5096
- v: name
5097
- };
5098
- }
5099
- function spreadsheetCellText(cell) {
5100
- const value = cell?.m ?? cell?.v;
5101
- return null == value ? '' : String(value);
5102
- }
5103
- function validSpreadsheetTableColumnName(name) {
5104
- const characters = Array.from(name);
5105
- return name.trim() === name && characters.length >= 1 && characters.length <= 255 && !characters.some((character)=>/\p{Cc}/u.test(character) || '\uFFFE' === character || '\uFFFF' === character);
5106
- }
5107
- function truncateSpreadsheetTableColumnName(value, maximum) {
5108
- return Array.from(value).slice(0, maximum).join('');
5109
- }
5110
- function sameSpreadsheetTableColumns(left, right) {
5111
- 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);
5112
- }
5113
- function spreadsheet_table_reconciliation_isRecord(value) {
5114
- return Boolean(value && 'object' == typeof value && !Array.isArray(value));
5115
- }
5116
- function spreadsheetSelectionReference(selection) {
5117
- const rowStart = Math.min(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
5118
- const rowEnd = Math.max(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
5119
- const columnStart = Math.min(selection.column[0] ?? 0, selection.column[1] ?? selection.column[0] ?? 0);
5120
- const columnEnd = Math.max(selection.column[0] ?? 0, selection.column[1] ?? selection.column[0] ?? 0);
5121
- const start = `${spreadsheetColumnLabel(columnStart)}${rowStart + 1}`;
5122
- const end = `${spreadsheetColumnLabel(columnEnd)}${rowEnd + 1}`;
5123
- return start === end ? start : `${start}:${end}`;
5124
- }
5125
- function spreadsheetSingleRange(selection) {
5126
- return {
5127
- row: finiteSpreadsheetSelectionAxis(selection.row),
5128
- column: finiteSpreadsheetSelectionAxis(selection.column)
5129
- };
5130
- }
5131
- function spreadsheet_editor_support_spreadsheetCellAt(sheet, row, column) {
5132
- if (!sheet) return null;
5133
- const safeRow = finiteSpreadsheetIndex(row, 0);
5134
- const safeColumn = finiteSpreadsheetIndex(column, 0);
5135
- return sheet.data?.[safeRow]?.[safeColumn] ?? sheet.celldata?.find((entry)=>entry.r === safeRow && entry.c === safeColumn)?.v ?? null;
5136
- }
5137
- function spreadsheetSelectionSummary(sheet, selection) {
5138
- const summary = {
5139
- average: null,
5140
- nonEmptyCount: 0,
5141
- numericCount: 0,
5142
- sum: null
5143
- };
5144
- if (!sheet) return summary;
5145
- const range = spreadsheetSingleRange(selection);
5146
- let sum = 0;
5147
- const collect = (cell)=>{
5148
- if (!cell || !spreadsheet_editor_support_spreadsheetCellHasContent(cell)) return;
5149
- summary.nonEmptyCount += 1;
5150
- if ('number' != typeof cell.v || !Number.isFinite(cell.v)) return;
5151
- summary.numericCount += 1;
5152
- sum += cell.v;
5153
- };
5154
- if (sheet.data?.length) {
5155
- const rowStart = range.row[0] ?? 0;
5156
- const rowEnd = Math.min(range.row[1] ?? rowStart, sheet.data.length - 1);
5157
- const columnStart = range.column[0] ?? 0;
5158
- const columnEnd = range.column[1] ?? columnStart;
5159
- for (const rowIndex of sparseArrayIndexes(sheet.data)){
5160
- if (rowIndex < rowStart || rowIndex > rowEnd) continue;
5161
- const row = sheet.data[rowIndex];
5162
- if (row) {
5163
- for (const columnIndex of sparseArrayIndexes(row))if (!(columnIndex < columnStart) && !(columnIndex > columnEnd)) collect(row[columnIndex]);
5164
- }
5165
- }
5166
- } else {
5167
- const rowStart = range.row[0] ?? 0;
5168
- const rowEnd = range.row[1] ?? rowStart;
5169
- const columnStart = range.column[0] ?? 0;
5170
- const columnEnd = range.column[1] ?? columnStart;
5171
- for (const entry of sheet.celldata ?? [])if (!(entry.r < rowStart) && !(entry.r > rowEnd) && !(entry.c < columnStart) && !(entry.c > columnEnd)) collect(entry.v);
5172
- }
5173
- if (summary.numericCount > 0) {
5174
- summary.sum = sum;
5175
- summary.average = sum / summary.numericCount;
5176
- }
5177
- return summary;
5178
- }
5179
- function spreadsheetFontSizeOptions(current) {
5180
- const values = [
5181
- ...spreadsheetFontSizes
5182
- ];
5183
- if (current && !values.includes(current)) values.push(current);
5184
- return values.sort((left, right)=>left - right).map((value)=>({
5185
- value: String(value),
5186
- label: String(value)
5187
- }));
5188
- }
5189
- function spreadsheetFontFamilyOptions(current) {
5190
- const options = officeFontFamilies.map(({ cssFamily, group, label, name })=>({
5191
- value: name,
5192
- group,
5193
- label,
5194
- previewStyle: {
5195
- fontFamily: cssFamily
5196
- },
5197
- searchText: `${name} ${label}`
5198
- }));
5199
- const normalizedCurrent = current?.trim();
5200
- if (normalizedCurrent && !options.some(({ value })=>value === normalizedCurrent)) options.push({
5201
- value: normalizedCurrent,
5202
- group: '文档字体',
5203
- label: normalizedCurrent,
5204
- previewStyle: {
5205
- fontFamily: normalizedCurrent
5206
- },
5207
- searchText: normalizedCurrent
5208
- });
5209
- return options;
5210
- }
5211
- function spreadsheetSheetsWithFiniteSelections(sheets) {
5212
- return sheets.map((sheet)=>({
5213
- ...sheet,
5214
- luckysheet_select_save: (sheet.luckysheet_select_save?.length ? sheet.luckysheet_select_save : [
5215
- void 0
5216
- ]).map(finiteSpreadsheetSelection)
5217
- }));
5218
- }
5219
- function spreadsheetSheetsForFortune(sheets) {
5220
- const startedAt = spreadsheetProjectionNow();
5221
- let populatedCellCount = 0;
5222
- const projectedSheets = sheets.map((sheet)=>{
5223
- const { celldata: sourceCellData, data, ...sourceMetadata } = sheet;
5224
- const projected = structuredClone(sourceMetadata);
5225
- const profile = spreadsheetMatrixProfile(data);
5226
- if (data && profile?.fortuneReady) {
5227
- populatedCellCount += profile.populatedCellCount;
5228
- return {
5229
- ...projected,
5230
- data
5231
- };
5232
- }
5233
- const fortuneProjection = spreadsheetMatrixForFortune({
5234
- ...projected,
5235
- ...void 0 === data ? {} : {
5236
- data
5237
- },
5238
- ...void 0 === sourceCellData ? {} : {
5239
- celldata: sourceCellData
5240
- }
5241
- }, projected.config?.merge);
5242
- populatedCellCount += fortuneProjection.populatedCellCount;
5243
- return {
5244
- ...projected,
5245
- data: fortuneProjection.data
5246
- };
5247
- });
5248
- recordSpreadsheetProjectionMeasure(startedAt, spreadsheetProjectionNow(), {
5249
- populatedCellCount,
5250
- sheetCount: sheets.length
5251
- });
5252
- return projectedSheets;
5253
- }
5254
- function spreadsheetMatrixForFortune(sheet, merges) {
5255
- const declaredRows = spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.row);
5256
- const declaredColumns = spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.column);
5257
- const hasDeclaredGrid = declaredRows > 0 && declaredColumns > 0;
5258
- let rowCount = hasDeclaredGrid ? declaredRows : 60;
5259
- let columnCount = hasDeclaredGrid ? declaredColumns : 26;
5260
- let populatedCellCount = 0;
5261
- const shownCommentCells = [];
5262
- const projected = [];
5263
- const setSourceCell = (rowIndex, columnIndex, cell)=>{
5264
- rowCount = Math.max(rowCount, rowIndex + 1);
5265
- columnCount = Math.max(columnCount, columnIndex + 1);
5266
- const row = projected[rowIndex] ?? [];
5267
- projected[rowIndex] = row;
5268
- row[columnIndex] = cloneSpreadsheetCellForFortune(cell);
5269
- if (cell.ps?.isShow) shownCommentCells.push({
5270
- c: columnIndex,
5271
- r: rowIndex
5272
- });
5273
- populatedCellCount += 1;
5274
- };
5275
- if (void 0 !== sheet.data) {
5276
- rowCount = Math.max(rowCount, sheet.data.length);
5277
- for (const rowIndex of sparseArrayIndexes(sheet.data)){
5278
- const sourceRow = sheet.data[rowIndex];
5279
- if (sourceRow) {
5280
- columnCount = Math.max(columnCount, sourceRow.length);
5281
- for (const columnIndex of sparseArrayIndexes(sourceRow)){
5282
- const cell = sourceRow[columnIndex];
5283
- if (null != cell) setSourceCell(rowIndex, columnIndex, cell);
5284
- }
5285
- }
5286
- }
5287
- } else for (const entry of sheet.celldata ?? [])if (null != entry.v) setSourceCell(entry.r, entry.c, entry.v);
5288
- for (const merge of Object.values(merges ?? {})){
5289
- rowCount = Math.max(rowCount, merge.r + merge.rs);
5290
- columnCount = Math.max(columnCount, merge.c + merge.cs);
5291
- for(let rowIndex = merge.r; rowIndex < merge.r + merge.rs; rowIndex += 1){
5292
- const row = projected[rowIndex] ?? [];
5293
- projected[rowIndex] = row;
5294
- for(let columnIndex = merge.c; columnIndex < merge.c + merge.cs; columnIndex += 1)row[columnIndex] = Object.freeze({
5295
- ...row[columnIndex] ?? {},
5296
- mc: rowIndex === merge.r && columnIndex === merge.c ? {
5297
- ...merge
5298
- } : {
5299
- r: merge.r,
5300
- c: merge.c
5301
- }
5302
- });
5303
- }
5304
- }
5305
- projected.length = rowCount;
5306
- projected[0] ??= [];
5307
- for (const rowIndex of sparseArrayIndexes(projected)){
5308
- const row = projected[rowIndex];
5309
- if (row) {
5310
- row.length = Math.max(row.length, columnCount);
5311
- Object.freeze(row);
5312
- }
5313
- }
5314
- attachSpreadsheetShownCommentCells(projected, Object.freeze(shownCommentCells.map((cell)=>Object.freeze(cell))));
5315
- Object.freeze(projected);
5316
- return {
5317
- data: projected,
5318
- populatedCellCount
5319
- };
5320
- }
5321
- function cloneSpreadsheetCellForFortune(cell) {
5322
- const clone = {
5323
- ...cell
5324
- };
5325
- if (cell.mc) clone.mc = {
5326
- ...cell.mc
5327
- };
5328
- if (cell.ct) clone.ct = {
5329
- ...cell.ct,
5330
- ...void 0 === cell.ct.s ? {} : {
5331
- s: structuredClone(cell.ct.s)
5332
- }
5333
- };
5334
- if (cell.ps) clone.ps = {
5335
- ...cell.ps
5336
- };
5337
- if (cell.hl) clone.hl = {
5338
- ...cell.hl
5339
- };
5340
- if (cell.spl && 'object' == typeof cell.spl) clone.spl = structuredClone(cell.spl);
5341
- Object.freeze(clone);
5342
- return clone;
5343
- }
5344
- function spreadsheetProjectionNow() {
5345
- return globalThis.performance?.now?.() ?? Date.now();
5346
- }
5347
- function recordSpreadsheetProjectionMeasure(start, end, detail) {
5348
- try {
5349
- globalThis.performance?.measure('a3s-office.spreadsheet.fortune-projection', {
5350
- detail,
5351
- end,
5352
- start
5353
- });
5354
- } catch {}
5355
- }
5356
- function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = [], dateSystem = '1900') {
5357
- const startedAt = spreadsheetProjectionNow();
5358
- const filterContext = {
5359
- dateSystem,
5360
- now: new Date()
5361
- };
5362
- const incremental = projectSpreadsheetSheetsFromFortuneOperations(sheets, sourceSheets, operations);
5363
- if (incremental) {
5364
- recordSpreadsheetControlledProjectionMeasure(startedAt, spreadsheetProjectionNow(), {
5365
- affectedCellCount: incremental.affectedCellCount,
5366
- incremental: true,
5367
- operationCount: operations.length
5368
- });
5369
- return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(incremental.sheets, sourceSheets, operations), sourceSheets, operations, filterContext);
5370
- }
5371
- const operationCoordinatesBySheet = spreadsheetCellOperationCoordinates(operations);
5372
- const projected = sheets.map((sheet, index)=>{
5373
- const source = (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
5374
- const operationCoordinates = sheet.id ? operationCoordinatesBySheet?.get(sheet.id) : void 0;
5375
- const cells = spreadsheetPopulatedCellData(sheet).map((entry)=>{
5376
- if (!source || !entry.v) return entry;
5377
- const previous = spreadsheet_editor_support_spreadsheetCellAt(source, entry.r, entry.c);
5378
- const exactCellOperation = operationCoordinates?.has(spreadsheetCellOperationKey(entry.r, entry.c));
5379
- const textStableNonStructuralCallback = null !== operationCoordinatesBySheet && sameSpreadsheetRichTextCellText(previous, entry.v);
5380
- if (!exactCellOperation && !textStableNonStructuralCallback) return entry;
5381
- const reconciled = exactCellOperation ? reconcileSpreadsheetRichTextCellEdit(previous, entry.v, takeSpreadsheetRichTextPaste(source, entry.r, entry.c, entry.v)) : restoreSpreadsheetRichTextCellRuns(previous, entry.v);
5382
- return reconciled === entry.v ? entry : {
5383
- ...entry,
5384
- v: reconciled
5385
- };
5386
- });
5387
- const { celldata: _cellData, data: _data, ...metadata } = sheet;
5388
- const useMatrix = source ? void 0 !== source.data : void 0 !== sheet.data;
5389
- if (!useMatrix) return {
5390
- ...metadata,
5391
- celldata: cells
5392
- };
5393
- const data = [];
5394
- data.length = Math.max(sheet.data?.length ?? 0, source?.data?.length ?? 0, spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.row));
5395
- for (const { r: rowIndex, c: columnIndex, v: cell } of cells){
5396
- const row = data[rowIndex] ?? [];
5397
- data[rowIndex] = row;
5398
- row.length = Math.max(row.length, sheet.data?.[rowIndex]?.length ?? 0, source?.data?.[rowIndex]?.length ?? 0, spreadsheet_editor_support_positiveSpreadsheetDimension(sheet.column));
5399
- row[columnIndex] = cell;
5400
- }
5401
- return {
5402
- ...metadata,
5403
- data
5404
- };
5405
- });
5406
- recordSpreadsheetControlledProjectionMeasure(startedAt, spreadsheetProjectionNow(), {
5407
- affectedCellCount: 0,
5408
- incremental: false,
5409
- operationCount: operations.length
5410
- });
5411
- return reconcileSpreadsheetFiltersAfterFortune(reconcileSpreadsheetTablesAfterFortune(projected, sourceSheets, operations), sourceSheets, operations, filterContext);
5412
- }
5413
- function recordSpreadsheetControlledProjectionMeasure(start, end, detail) {
5414
- try {
5415
- globalThis.performance?.measure('a3s-office.spreadsheet.controlled-projection', {
5416
- detail,
5417
- end,
5418
- start
5419
- });
5420
- } catch {}
5421
- }
5422
- function finiteSpreadsheetSelection(selection) {
5423
- const row = finiteSpreadsheetSelectionAxis(selection?.row);
5424
- const column = finiteSpreadsheetSelectionAxis(selection?.column);
5425
- return {
5426
- ...selection,
5427
- row,
5428
- column,
5429
- row_focus: finiteSpreadsheetFocus(selection?.row_focus, row),
5430
- column_focus: finiteSpreadsheetFocus(selection?.column_focus, column)
5431
- };
5432
- }
5433
- function sameSpreadsheetWorkbookState(changed, rendered) {
5434
- return changed.length === rendered.length && changed.every((sheet, index)=>sameSpreadsheetSheetState(sheet, rendered[index]));
5435
- }
5436
- function sameSpreadsheetWorkbookStateAfterOperations(changed, rendered, operations) {
5437
- if (0 === operations.length || operations.length > 10000) return null;
5438
- if (changed.length !== rendered.length || !changed.every((sheet, index)=>sameSpreadsheetHistoryValue(spreadsheetSheetMetadata(sheet), spreadsheetSheetMetadata(rendered[index])))) return false;
5439
- const changedById = spreadsheet_editor_support_spreadsheetSheetsById(changed);
5440
- const renderedById = spreadsheet_editor_support_spreadsheetSheetsById(rendered);
5441
- if (!changedById || !renderedById) return null;
5442
- const coordinates = new Map();
5443
- for (const operation of operations){
5444
- if ('insertRowCol' === operation.op || 'deleteRowCol' === operation.op || 'addSheet' === operation.op || 'deleteSheet' === operation.op || 0 === operation.path.length) return false;
5445
- if (!operation.id || !changedById.has(operation.id) || !renderedById.has(operation.id)) return null;
5446
- if ('data' !== operation.path[0]) continue;
5447
- const row = operation.path[1];
5448
- const column = operation.path[2];
5449
- if (operation.path.length < 3 || !spreadsheet_editor_support_isSpreadsheetCoordinate(row) || !spreadsheet_editor_support_isSpreadsheetCoordinate(column)) return null;
5450
- coordinates.set(`${operation.id}:${row}:${column}`, {
5451
- column,
5452
- row,
5453
- sheetId: operation.id
5454
- });
5455
- }
5456
- 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;
5457
- return true;
5458
- }
5459
- function sameSpreadsheetHistoryContent(left, right) {
5460
- if (left === right) return true;
5461
- const { sheets: leftSheets, ...leftMetadata } = left;
5462
- const { sheets: rightSheets, ...rightMetadata } = right;
5463
- return sameSpreadsheetHistoryValue(leftMetadata, rightMetadata) && leftSheets.length === rightSheets.length && leftSheets.every((sheet, index)=>sameSpreadsheetSheetState(sheet, rightSheets[index]));
5464
- }
5465
- function spreadsheetContentWithSelection(content, sheetId, selection) {
5466
- return spreadsheetContentWithSelections(content, sheetId, selection ? [
5467
- selection
5468
- ] : []);
5469
- }
5470
- function spreadsheetContentWithSelections(content, sheetId, selections) {
5471
- if (!sheetId || !selections.length) return content;
5472
- const nextSelections = selections.map(finiteSpreadsheetSelection);
5473
- let changed = false;
5474
- const sheets = content.sheets.map((sheet)=>{
5475
- if (sheet.id !== sheetId) return sheet;
5476
- changed = true;
5477
- return {
5478
- ...sheet,
5479
- luckysheet_select_save: nextSelections
5480
- };
5481
- });
5482
- return changed ? {
5483
- ...content,
5484
- sheets
5485
- } : content;
5486
- }
5487
- function isSpreadsheetNativeTextUndoTarget(target) {
5488
- if (!(target instanceof HTMLElement)) return false;
5489
- if (isSpreadsheetCellEditingTarget(target)) return true;
5490
- if (target.closest('.fortune-container')) return false;
5491
- return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target.isContentEditable || Boolean(target.closest('[contenteditable="true"]'));
5492
- }
5493
- function isSpreadsheetCellEditingTarget(target) {
5494
- if (!isSpreadsheetCellEditorTarget(target)) return false;
5495
- if (!(target instanceof Element)) return false;
5496
- const formulaInput = target.closest('.fortune-fx-input');
5497
- if (formulaInput) return true;
5498
- const cellInput = target.closest('.luckysheet-cell-input');
5499
- if (!cellInput) return false;
5500
- const inputBox = cellInput.closest('.luckysheet-input-box');
5501
- if (!inputBox) return true;
5502
- const zIndex = Number.parseInt(inputBox.style.zIndex || getComputedStyle(inputBox).zIndex, 10);
5503
- return !Number.isFinite(zIndex) || zIndex >= 0;
5504
- }
5505
- function isSpreadsheetCellEditorTarget(target) {
5506
- return target instanceof Element && Boolean(target.closest('.fortune-fx-input, .luckysheet-cell-input'));
5507
- }
5508
- function spreadsheetFormulaBarSelectAllTarget(event) {
5509
- if (event.defaultPrevented || event.repeat || event.altKey || event.shiftKey || !(event.metaKey || event.ctrlKey) || 'a' !== event.key.toLocaleLowerCase() || !(event.target instanceof Element)) return null;
5510
- const formulaBar = event.target.closest('.fortune-fx-input');
5511
- return formulaBar instanceof HTMLElement ? formulaBar : null;
5512
- }
5513
- function selectSpreadsheetFormulaBarContents(event) {
5514
- const formulaBar = spreadsheetFormulaBarSelectAllTarget(event);
5515
- const selection = window.getSelection();
5516
- if (!formulaBar || !selection) return false;
5517
- const range = document.createRange();
5518
- range.selectNodeContents(formulaBar);
5519
- selection.removeAllRanges();
5520
- selection.addRange(range);
5521
- return true;
5522
- }
5523
- function spreadsheetColumnLabel(column) {
5524
- let value = Math.max(0, column) + 1;
5525
- let label = '';
5526
- while(value > 0){
5527
- value -= 1;
5528
- label = String.fromCharCode(65 + value % 26) + label;
5529
- value = Math.floor(value / 26);
5530
- }
5531
- return label;
5532
- }
5533
- function spreadsheet_editor_support_spreadsheetCellHasContent(cell) {
5534
- if ('string' == typeof cell.f && cell.f.trim()) return true;
5535
- if (null === cell.v || void 0 === cell.v) return false;
5536
- return 'string' != typeof cell.v || cell.v.trim().length > 0;
5537
- }
5538
- function finiteSpreadsheetSelectionAxis(axis) {
5539
- const first = finiteSpreadsheetIndex(axis?.[0], 0);
5540
- const second = finiteSpreadsheetIndex(axis?.[1], first);
5541
- return [
5542
- Math.min(first, second),
5543
- Math.max(first, second)
5544
- ];
5545
- }
5546
- function finiteSpreadsheetFocus(value, axis) {
5547
- const focus = finiteSpreadsheetIndex(value, axis[0] ?? 0);
5548
- return Math.min(axis[1] ?? focus, Math.max(axis[0] ?? focus, focus));
5549
- }
5550
- function finiteSpreadsheetIndex(value, fallback) {
5551
- return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
5552
- }
5553
- function spreadsheet_editor_support_isSpreadsheetCoordinate(value) {
5554
- return Number.isSafeInteger(value) && Number(value) >= 0;
5555
- }
5556
- function spreadsheet_editor_support_spreadsheetSheetsById(sheets) {
5557
- const byId = new Map();
5558
- for (const sheet of sheets){
5559
- if (!sheet.id || byId.has(sheet.id)) return null;
5560
- byId.set(sheet.id, sheet);
5561
- }
5562
- return byId;
5563
- }
5564
- function spreadsheetSheetMetadata(sheet) {
5565
- if (!sheet) return null;
5566
- const { celldata: _cellData, calcChain: _calculationChain, data: _data, dynamicArray_compute: _dynamicArrayComputation, luckysheet_select_save: _selection, luckysheet_selection_range: _range, zoomRatio, ...content } = sheet;
5567
- return {
5568
- ...content,
5569
- ...void 0 === zoomRatio || 1 === zoomRatio ? {} : {
5570
- zoomRatio
5571
- }
5572
- };
5573
- }
5574
- function sameSpreadsheetSheetState(left, right) {
5575
- return Boolean(right && sameSpreadsheetHistoryValue(spreadsheetSheetMetadata(left), spreadsheetSheetMetadata(right)) && sameSpreadsheetCellStorage(left, right));
5576
- }
5577
- function sameSpreadsheetCellStorage(left, right) {
5578
- if (void 0 !== left.data && void 0 !== right.data) {
5579
- const leftProfile = spreadsheetMatrixProfile(left.data);
5580
- const rightProfile = spreadsheetMatrixProfile(right.data);
5581
- if (leftProfile && rightProfile && leftProfile.historyRoot === rightProfile.historyRoot) return leftProfile.historyState === rightProfile.historyState;
5582
- return sameSpreadsheetDataMatrices(left.data, right.data);
5583
- }
5584
- if (void 0 === left.data && void 0 === right.data) {
5585
- if (left.celldata === right.celldata) return true;
5586
- }
5587
- return sameSpreadsheetHistoryValue(spreadsheetPopulatedCellData(left), spreadsheetPopulatedCellData(right));
5588
- }
5589
- function sameSpreadsheetDataMatrices(left, right) {
5590
- if (left === right) return true;
5591
- for (const rowIndex of sparseArrayIndexes(left))if (!sameSpreadsheetDataRows(left[rowIndex], right[rowIndex])) return false;
5592
- for (const rowIndex of sparseArrayIndexes(right))if (!Object.hasOwn(left, rowIndex) && !sameSpreadsheetDataRows(void 0, right[rowIndex])) return false;
5593
- return true;
5594
- }
5595
- function sameSpreadsheetDataRows(left, right) {
5596
- if (left === right) return true;
5597
- for (const columnIndex of sparseArrayIndexes(left ?? [])){
5598
- const leftCell = left?.[columnIndex];
5599
- if (null == leftCell) continue;
5600
- const rightCell = right?.[columnIndex];
5601
- if (null == rightCell || !sameSpreadsheetHistoryValue(leftCell, rightCell)) return false;
5602
- }
5603
- for (const columnIndex of sparseArrayIndexes(right ?? []))if (right?.[columnIndex] != null && left?.[columnIndex] == null) return false;
5604
- return true;
5605
- }
5606
- function spreadsheetPopulatedCellData(sheet) {
5607
- const cells = [];
5608
- if (void 0 !== sheet.data) for (const rowIndex of sparseArrayIndexes(sheet.data)){
5609
- const row = sheet.data[rowIndex];
5610
- if (row) for (const columnIndex of sparseArrayIndexes(row)){
5611
- const cell = row[columnIndex];
5612
- if (null != cell) cells.push({
5613
- r: rowIndex,
5614
- c: columnIndex,
5615
- v: cell
5616
- });
5617
- }
5618
- }
5619
- else for (const cell of sheet.celldata ?? [])if (null != cell.v) cells.push(cell);
5620
- return cells.sort((left, right)=>left.r - right.r || left.c - right.c);
5621
- }
5622
- function spreadsheet_editor_support_positiveSpreadsheetDimension(value) {
5623
- return 'number' == typeof value && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
5624
- }
5625
- const spreadsheetKeyboardPageRows = 20;
5626
- const spreadsheetDefaultRows = 60;
5627
- const spreadsheetDefaultColumns = 26;
5628
- function moveSpreadsheetKeyboardSelection(sheet, selection, move, extend) {
5629
- const bounds = spreadsheetSheetBounds(sheet);
5630
- const current = normalizeSpreadsheetKeyboardSelection(selection, bounds);
5631
- let targetRow = current.rowFocus;
5632
- let targetColumn = current.columnFocus;
5633
- switch(move){
5634
- case 'up':
5635
- targetRow -= 1;
5636
- break;
5637
- case 'down':
5638
- targetRow += 1;
5639
- break;
5640
- case 'left':
5641
- targetColumn -= 1;
5642
- break;
5643
- case 'right':
5644
- targetColumn += 1;
5645
- break;
5646
- case 'next-cell':
5647
- if (targetColumn < bounds.lastColumn) targetColumn += 1;
5648
- else if (targetRow < bounds.lastRow) {
5649
- targetRow += 1;
5650
- targetColumn = 0;
5651
- }
5652
- break;
5653
- case 'previous-cell':
5654
- if (targetColumn > 0) targetColumn -= 1;
5655
- else if (targetRow > 0) {
5656
- targetRow -= 1;
5657
- targetColumn = bounds.lastColumn;
5658
- }
5659
- break;
5660
- case 'page-up':
5661
- targetRow -= spreadsheetKeyboardPageRows;
5662
- break;
5663
- case 'page-down':
5664
- targetRow += spreadsheetKeyboardPageRows;
5665
- break;
5666
- case 'row-start':
5667
- targetColumn = 0;
5668
- break;
5669
- case 'sheet-start':
5670
- targetRow = 0;
5671
- targetColumn = 0;
5672
- break;
5673
- case 'used-end':
5674
- {
5675
- const usedEnd = spreadsheetUsedRangeEnd(sheet);
5676
- targetRow = usedEnd.row;
5677
- targetColumn = usedEnd.column;
5678
- break;
5679
- }
5680
- }
5681
- targetRow = clampSpreadsheetIndex(targetRow, bounds.lastRow);
5682
- targetColumn = clampSpreadsheetIndex(targetColumn, bounds.lastColumn);
5683
- if (!extend) return {
5684
- row: [
5685
- targetRow,
5686
- targetRow
5687
- ],
5688
- column: [
5689
- targetColumn,
5690
- targetColumn
5691
- ],
5692
- row_focus: targetRow,
5693
- column_focus: targetColumn
5694
- };
5695
- const rowAnchor = selectionAnchor(current.startRow, current.endRow, current.rowFocus);
5696
- const columnAnchor = selectionAnchor(current.startColumn, current.endColumn, current.columnFocus);
5697
- return {
5698
- row: [
5699
- Math.min(rowAnchor, targetRow),
5700
- Math.max(rowAnchor, targetRow)
5701
- ],
5702
- column: [
5703
- Math.min(columnAnchor, targetColumn),
5704
- Math.max(columnAnchor, targetColumn)
5705
- ],
5706
- row_focus: targetRow,
5707
- column_focus: targetColumn
5708
- };
5709
- }
5710
- function scopeSpreadsheetKeyboardSelection(sheet, selection, scope) {
5711
- const bounds = spreadsheetSheetBounds(sheet);
5712
- const current = normalizeSpreadsheetKeyboardSelection(selection, bounds);
5713
- if ('column' === scope) return {
5714
- row: [
5715
- 0,
5716
- bounds.lastRow
5717
- ],
5718
- column: [
5719
- current.columnFocus,
5720
- current.columnFocus
5721
- ],
5722
- row_focus: current.rowFocus,
5723
- column_focus: current.columnFocus
5724
- };
5725
- if ('row' === scope) return {
5726
- row: [
5727
- current.rowFocus,
5728
- current.rowFocus
5729
- ],
5730
- column: [
5731
- 0,
5732
- bounds.lastColumn
5733
- ],
5734
- row_focus: current.rowFocus,
5735
- column_focus: current.columnFocus
5736
- };
5737
- return {
5738
- row: [
5739
- 0,
5740
- bounds.lastRow
5741
- ],
5742
- column: [
5743
- 0,
5744
- bounds.lastColumn
5745
- ],
5746
- row_focus: current.rowFocus,
5747
- column_focus: current.columnFocus
5748
- };
5749
- }
5750
- function isSpreadsheetGridKeyboardTarget(target) {
5751
- if (null === target) return true;
5752
- return target instanceof Element && Boolean(target.closest('.fortune-container'));
5753
- }
5754
- function runSpreadsheetSelectionMoveShortcut(event, canExecute, execute, move, extend) {
5755
- if (event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute(move, extend)) return false;
5756
- return execute(move, extend);
5757
- }
5758
- function runSpreadsheetSelectionScopeShortcut(event, canExecute, execute, scope) {
5759
- if (event.repeat || event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute(scope)) return false;
5760
- return execute(scope);
5761
- }
5762
- function spreadsheetSelectionContainsFocus(selection, focus) {
5763
- const firstRow = selection.row[0] ?? 0;
5764
- const secondRow = selection.row[1] ?? firstRow;
5765
- const firstColumn = selection.column[0] ?? 0;
5766
- const secondColumn = selection.column[1] ?? firstColumn;
5767
- 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);
5768
- }
5769
- function spreadsheetSheetBounds(sheet) {
5770
- const dataRows = sheet.data?.length ?? 0;
5771
- const dataColumns = sparseMatrixColumnCount(sheet.data);
5772
- return {
5773
- lastRow: spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(sheet.row, dataRows, spreadsheetDefaultRows) - 1,
5774
- lastColumn: spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(sheet.column, dataColumns, spreadsheetDefaultColumns) - 1
2576
+ row,
2577
+ row + rowSpan - 1
2578
+ ],
2579
+ column: [
2580
+ column,
2581
+ column + columnSpan - 1
2582
+ ]
5775
2583
  };
5776
2584
  }
5777
- function spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(configured, dataDimension, fallback) {
5778
- const value = Number(configured);
5779
- if (Number.isFinite(value) && value > 0) return Math.max(1, Math.trunc(value));
5780
- if (dataDimension > 0) return dataDimension;
5781
- 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;
5782
2598
  }
5783
- function spreadsheetUsedRangeEnd(sheet) {
5784
- const bounds = spreadsheetSheetBounds(sheet);
5785
- let row = 0;
5786
- let column = 0;
5787
- sheet.data?.forEach((cells, rowIndex)=>{
5788
- cells?.forEach((cell, columnIndex)=>{
5789
- if (!spreadsheetCellHasValue(cell)) return;
5790
- row = Math.max(row, rowIndex);
5791
- column = Math.max(column, columnIndex);
5792
- });
5793
- });
5794
- sheet.celldata?.forEach((cell)=>{
5795
- if (!spreadsheetCellHasValue(cell.v)) return;
5796
- row = Math.max(row, Number(cell.r) || 0);
5797
- column = Math.max(column, Number(cell.c) || 0);
5798
- });
5799
- return {
5800
- row: clampSpreadsheetIndex(row, bounds.lastRow),
5801
- 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);
5802
2614
  };
2615
+ return (row, column)=>sheet.data?.[row]?.[column] ?? sparseCell(row, column);
5803
2616
  }
5804
- function spreadsheetCellHasValue(cell) {
5805
- if (!cell) return false;
5806
- if ('string' == typeof cell.f && cell.f.trim()) return true;
5807
- const value = cell.v;
5808
- if (null != value) return 'string' != typeof value || value.trim().length > 0;
5809
- return 'string' == typeof cell.m && cell.m.trim().length > 0;
5810
- }
5811
- function normalizeSpreadsheetKeyboardSelection(selection, bounds) {
5812
- const firstRow = clampSpreadsheetIndex(selection.row[0], bounds.lastRow);
5813
- const secondRow = clampSpreadsheetIndex(selection.row[1] ?? firstRow, bounds.lastRow);
5814
- const firstColumn = clampSpreadsheetIndex(selection.column[0], bounds.lastColumn);
5815
- const secondColumn = clampSpreadsheetIndex(selection.column[1] ?? firstColumn, bounds.lastColumn);
5816
- const startRow = Math.min(firstRow, secondRow);
5817
- const endRow = Math.max(firstRow, secondRow);
5818
- const startColumn = Math.min(firstColumn, secondColumn);
5819
- const endColumn = Math.max(firstColumn, secondColumn);
5820
- return {
5821
- startRow,
5822
- endRow,
5823
- startColumn,
5824
- endColumn,
5825
- rowFocus: clampSpreadsheetIndex(selection.row_focus ?? secondRow, bounds.lastRow),
5826
- columnFocus: clampSpreadsheetIndex(selection.column_focus ?? secondColumn, bounds.lastColumn)
5827
- };
2617
+ function finiteIndex(value) {
2618
+ return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : null;
5828
2619
  }
5829
- function clampSpreadsheetIndex(value, maximum) {
5830
- const numeric = Number(value);
5831
- const finite = Number.isFinite(numeric) ? Math.trunc(numeric) : 0;
5832
- return Math.min(maximum, Math.max(0, finite));
2620
+ function positiveSpan(value) {
2621
+ return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : null;
5833
2622
  }
5834
- function selectionAnchor(start, end, focus) {
5835
- return focus === start ? end : start;
2623
+ function spreadsheet_cell_mutation_guard_isRecord(value) {
2624
+ return Boolean(value && 'object' == typeof value && !Array.isArray(value));
5836
2625
  }
5837
2626
  function createSpreadsheetAutoSumExtension() {
5838
2627
  return createOfficeEditorExtension({
@@ -6953,10 +3742,10 @@ function formatMatrixCells(sheet, range, patch) {
6953
3742
  }
6954
3743
  function formatSparseCells(sheet, range, patch) {
6955
3744
  const byCoordinate = new Map();
6956
- 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);
6957
3746
  let materialized = false;
6958
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){
6959
- const key = spreadsheet_cell_format_cellKey(row, column);
3748
+ const key = cellKey(row, column);
6960
3749
  const entry = byCoordinate.get(key);
6961
3750
  const next = formatCell(entry?.v ?? null, patch);
6962
3751
  if (next) {
@@ -7114,7 +3903,7 @@ function normalizeColor(value) {
7114
3903
  ...color.slice(1)
7115
3904
  ].map((character)=>character.repeat(2)).join('')}`;
7116
3905
  }
7117
- function spreadsheet_cell_format_cellKey(row, column) {
3906
+ function cellKey(row, column) {
7118
3907
  return `${row}:${column}`;
7119
3908
  }
7120
3909
  const spreadsheetFormatCellsTabs = [
@@ -7821,185 +4610,6 @@ function runSpreadsheetCopyFromAboveShortcut(event, kind, canExecute, execute) {
7821
4610
  if (event.repeat || event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target) || !canExecute(kind)) return false;
7822
4611
  return execute(kind);
7823
4612
  }
7824
- const spreadsheet_go_to_spreadsheetMaximumRows = 1048576;
7825
- const spreadsheet_go_to_spreadsheetMaximumColumns = 16384;
7826
- const spreadsheetGoToMaximumInputLength = 512;
7827
- function resolveSpreadsheetGoToTarget(content, activeSheetId, input) {
7828
- const value = input.trim().replace(/^=/, '').trim();
7829
- if (!value) return spreadsheetGoToError('empty');
7830
- if (value.length > spreadsheetGoToMaximumInputLength) return spreadsheetGoToError('invalid-reference');
7831
- const activeSheet = content.sheets.find((sheet)=>sheet.id === activeSheetId);
7832
- if (!activeSheet) return spreadsheetGoToError('sheet-not-found');
7833
- const qualified = splitSpreadsheetQualifiedReference(value);
7834
- if (!qualified) return spreadsheetGoToError('invalid-reference');
7835
- const requestedSheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : activeSheet;
7836
- if (qualified.qualifier && !requestedSheet) return spreadsheetGoToError('sheet-not-found');
7837
- if (requestedSheet?.hide === 1) return spreadsheetGoToError('hidden-sheet');
7838
- const direct = resolveSpreadsheetDirectRange(content, requestedSheet ?? activeSheet, qualified);
7839
- if ('error' === direct.kind) return spreadsheetGoToError(direct.code);
7840
- if ('match' === direct.kind) return spreadsheetGoToSuccess(direct.value, 'reference');
7841
- const named = findSpreadsheetGoToName(content, qualified.reference, requestedSheet ?? activeSheet, Boolean(qualified.qualifier));
7842
- if ('error' === named.kind) return spreadsheetGoToError(named.code);
7843
- if (!named.value) return spreadsheetGoToError('invalid-reference');
7844
- const fallbackSheet = named.value.scopeSheetId ? content.sheets.find((sheet)=>sheet.id === named.value?.scopeSheetId) : requestedSheet ?? activeSheet;
7845
- if (!fallbackSheet) return spreadsheetGoToError('sheet-not-found');
7846
- if (1 === fallbackSheet.hide) return spreadsheetGoToError('hidden-sheet');
7847
- const namedReference = splitSpreadsheetQualifiedReference(named.value.reference.trim().replace(/^=/, '').trim());
7848
- if (!namedReference) return spreadsheetGoToError('unsupported-name');
7849
- const resolved = resolveSpreadsheetDirectRange(content, fallbackSheet, namedReference);
7850
- if ('error' === resolved.kind) return spreadsheetGoToError(resolved.code);
7851
- if ('match' !== resolved.kind) return spreadsheetGoToError('unsupported-name');
7852
- return spreadsheetGoToSuccess(resolved.value, 'named-range', named.value.name);
7853
- }
7854
- function spreadsheetGoToValidationMessage(content, activeSheetId, input) {
7855
- const resolution = resolveSpreadsheetGoToTarget(content, activeSheetId, input);
7856
- return resolution.ok ? null : resolution.message;
7857
- }
7858
- function resolveSpreadsheetDirectRange(content, fallbackSheet, qualified) {
7859
- const sheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : fallbackSheet;
7860
- if (!sheet) return {
7861
- kind: 'error',
7862
- code: 'sheet-not-found'
7863
- };
7864
- if (1 === sheet.hide) return {
7865
- kind: 'error',
7866
- code: 'hidden-sheet'
7867
- };
7868
- const ranges = parseSpreadsheetCellRanges(qualified.reference);
7869
- if (!ranges) return {
7870
- kind: 'miss'
7871
- };
7872
- if (1 !== ranges.length) return {
7873
- kind: 'error',
7874
- code: 'multiple-ranges'
7875
- };
7876
- const range = ranges[0];
7877
- if (!range || !spreadsheetGoToRangeIsBounded(sheet, range)) return {
7878
- kind: 'error',
7879
- code: 'out-of-bounds'
7880
- };
7881
- return {
7882
- kind: 'match',
7883
- value: {
7884
- range,
7885
- sheet
7886
- }
7887
- };
7888
- }
7889
- function findSpreadsheetGoToName(content, input, scopeSheet, qualified) {
7890
- const normalized = input.trim().toLocaleLowerCase();
7891
- const matching = (content.namedRanges ?? []).filter((range)=>range.name.trim().toLocaleLowerCase() === normalized);
7892
- const scoped = matching.filter((range)=>range.scopeSheetId === scopeSheet.id);
7893
- const workbook = matching.filter((range)=>!range.scopeSheetId);
7894
- const preferred = scoped.length ? scoped : workbook;
7895
- if (preferred.length > 1) return {
7896
- kind: 'error',
7897
- code: 'ambiguous-name'
7898
- };
7899
- if (!preferred.length && !qualified) return {
7900
- kind: 'match',
7901
- value: null
7902
- };
7903
- return {
7904
- kind: 'match',
7905
- value: preferred[0] ?? null
7906
- };
7907
- }
7908
- function spreadsheetGoToRangeIsBounded(sheet, range) {
7909
- const bounds = spreadsheetSheetBounds(sheet);
7910
- 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;
7911
- }
7912
- function spreadsheetGoToSuccess(resolved, source, name) {
7913
- const reference = formatSpreadsheetCellRanges([
7914
- resolved.range
7915
- ]);
7916
- return {
7917
- ok: true,
7918
- target: {
7919
- displayReference: `${quoteSpreadsheetSheetName(resolved.sheet.name)}!${reference}`,
7920
- name,
7921
- range: resolved.range,
7922
- selection: {
7923
- row: [
7924
- ...resolved.range.row
7925
- ],
7926
- column: [
7927
- ...resolved.range.column
7928
- ],
7929
- row_focus: resolved.range.row[0],
7930
- column_focus: resolved.range.column[0]
7931
- },
7932
- sheetId: resolved.sheet.id ?? '',
7933
- source
7934
- }
7935
- };
7936
- }
7937
- function spreadsheetGoToError(code) {
7938
- const messages = {
7939
- 'ambiguous-name': '同一作用域中存在多个同名区域,无法确定定位目标。',
7940
- empty: '请输入单元格、连续区域或已定义名称。',
7941
- 'hidden-sheet': '不能定位到隐藏工作表。',
7942
- 'invalid-reference': '请输入有效的 A1 单元格、连续区域或已定义名称。',
7943
- 'multiple-ranges': '一次只能定位到一个连续区域。',
7944
- 'out-of-bounds': '引用超出了目标工作表的有效边界。',
7945
- 'sheet-not-found': '找不到引用的工作表。',
7946
- 'unsupported-name': '该名称不是可定位的连续单元格区域。'
7947
- };
7948
- return {
7949
- ok: false,
7950
- code,
7951
- message: messages[code]
7952
- };
7953
- }
7954
- function splitSpreadsheetQualifiedReference(value) {
7955
- let quoted = false;
7956
- let separator = -1;
7957
- for(let index = 0; index < value.length; index += 1){
7958
- const character = value[index];
7959
- if ("'" === character) {
7960
- if (quoted && "'" === value[index + 1]) {
7961
- index += 1;
7962
- continue;
7963
- }
7964
- quoted = !quoted;
7965
- continue;
7966
- }
7967
- if ('!' === character && !quoted) {
7968
- if (separator >= 0) return null;
7969
- separator = index;
7970
- }
7971
- }
7972
- if (quoted) return null;
7973
- if (separator < 0) return {
7974
- qualifier: null,
7975
- reference: value.trim()
7976
- };
7977
- const rawQualifier = value.slice(0, separator).trim();
7978
- const reference = value.slice(separator + 1).trim();
7979
- if (!rawQualifier || !reference) return null;
7980
- const qualifier = unquoteSpreadsheetSheetName(rawQualifier);
7981
- return qualifier ? {
7982
- qualifier,
7983
- reference
7984
- } : null;
7985
- }
7986
- function unquoteSpreadsheetSheetName(value) {
7987
- if (!value.startsWith("'")) return value.includes("'") ? null : value;
7988
- if (!value.endsWith("'") || value.length < 2) return null;
7989
- const inner = value.slice(1, -1);
7990
- for(let index = 0; index < inner.length; index += 1)if ("'" === inner[index]) {
7991
- if ("'" !== inner[index + 1]) return null;
7992
- index += 1;
7993
- }
7994
- return inner.replaceAll("''", "'");
7995
- }
7996
- function spreadsheetSheetByName(content, name) {
7997
- const normalized = name.trim().toLocaleLowerCase();
7998
- return content.sheets.find((sheet)=>sheet.name.trim().toLocaleLowerCase() === normalized);
7999
- }
8000
- function quoteSpreadsheetSheetName(name) {
8001
- return `'${name.replaceAll("'", "''")}'`;
8002
- }
8003
4613
  const MAX_SPREADSHEET_DATA_VALIDATION_CELLS = 10000;
8004
4614
  const defaultSpreadsheetDataValidationValue = {
8005
4615
  allowBlank: true,
@@ -8124,7 +4734,7 @@ function normalizeSpreadsheetDataValidationItem(content, sheet, ranges, value) {
8124
4734
  const operators = spreadsheetDataValidationOperators(value.type);
8125
4735
  const type2 = 'dropdown' === value.type || 'custom' === value.type ? '' : value.type2;
8126
4736
  if ('dropdown' !== value.type && 'custom' !== value.type && !operators.includes(type2)) return spreadsheetDataValidationError('invalid-operator');
8127
- 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);
8128
4738
  if (!normalizedValues.ok) return normalizedValues;
8129
4739
  return {
8130
4740
  ok: true,
@@ -8149,11 +4759,11 @@ function normalizeSpreadsheetDataValidationItem(content, sheet, ranges, value) {
8149
4759
  }
8150
4760
  };
8151
4761
  }
8152
- function normalizeSpreadsheetDataValidationValues(content, sheet, type, type2, rawValue1, rawValue2) {
4762
+ function normalizeSpreadsheetDataValidationValues(content, sheet, ranges, type, type2, rawValue1, rawValue2) {
8153
4763
  const value1 = rawValue1.trim();
8154
4764
  const value2 = rawValue2.trim();
8155
4765
  if (!value1) return spreadsheetDataValidationError('missing-value');
8156
- if ('dropdown' === type) return normalizeSpreadsheetListValidation(content, sheet, value1);
4766
+ if ('dropdown' === type) return normalizeSpreadsheetListValidation(content, sheet, ranges, value1);
8157
4767
  if ('custom' === type) {
8158
4768
  const formula = value1.replace(/^=/, '').trim();
8159
4769
  if (!formula || Array.from(formula).length > 255 || /[\u0000-\u001f\u007f]/u.test(formula)) return spreadsheetDataValidationError('invalid-custom-formula');
@@ -8187,7 +4797,20 @@ function normalizeSpreadsheetDataValidationValues(content, sheet, type, type2, r
8187
4797
  value2: null === second ? '' : String(second)
8188
4798
  };
8189
4799
  }
8190
- 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
+ }
8191
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());
8192
4815
  if (looksLikeReference) {
8193
4816
  const resolved = resolveSpreadsheetGoToTarget(content, sheet.id ?? '', value);
@@ -8456,6 +5079,7 @@ function spreadsheetDataValidationError(code) {
8456
5079
  const messages = {
8457
5080
  'invalid-custom-formula': '自定义公式必须是 255 个字符以内的本地公式,且不能包含控制字符。',
8458
5081
  'invalid-date': '请输入有效的日期、Excel 日期序号或 DATE(...) 表达式。',
5082
+ 'invalid-list-formula': '动态下拉公式必须是本地 =INDIRECT(单元格或文本拼接),并解析为当前工作簿内的一行或一列区域。',
8459
5083
  'invalid-list-source': '请输入不超过 255 个字符的逗号分隔列表,或有效的单行/单列区域。',
8460
5084
  'invalid-number': '请输入有效的数字;整数验证不能使用小数边界。',
8461
5085
  'invalid-operator': '请选择与当前验证类型匹配的数据条件。',
@@ -10058,7 +6682,7 @@ function translateFormulaSegment(segment, rowOffset, columnOffset) {
10058
6682
  valid = false;
10059
6683
  return match;
10060
6684
  }
10061
- return `${prefix}${sheet ?? ''}${absoluteColumn}${spreadsheet_paste_special_cell_spreadsheetColumnLabel(translatedColumn)}${absoluteRow}${translatedRow}`;
6685
+ return `${prefix}${sheet ?? ''}${absoluteColumn}${spreadsheetColumnLabel(translatedColumn)}${absoluteRow}${translatedRow}`;
10062
6686
  });
10063
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)=>{
10064
6688
  const leftColumn = leftAbsolute ? spreadsheetColumnIndex(left) : spreadsheetColumnIndex(left) + columnOffset;
@@ -10067,7 +6691,7 @@ function translateFormulaSegment(segment, rowOffset, columnOffset) {
10067
6691
  valid = false;
10068
6692
  return match;
10069
6693
  }
10070
- 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)}`;
10071
6695
  });
10072
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)=>{
10073
6697
  const topRow = topAbsolute ? Number(top) : Number(top) + rowOffset;
@@ -10085,7 +6709,7 @@ function spreadsheetColumnIndex(value) {
10085
6709
  for (const character of value.toUpperCase())index = 26 * index + character.charCodeAt(0) - 64;
10086
6710
  return index - 1;
10087
6711
  }
10088
- function spreadsheet_paste_special_cell_spreadsheetColumnLabel(index) {
6712
+ function spreadsheetColumnLabel(index) {
10089
6713
  let value = index + 1;
10090
6714
  let label = '';
10091
6715
  while(value > 0){
@@ -11297,7 +7921,7 @@ function spreadsheetTableColumnNames(sheet, range, headerRow) {
11297
7921
  const observed = new Set();
11298
7922
  const names = [];
11299
7923
  for(let column = range.column[0]; column <= range.column[1]; column += 1){
11300
- const raw = headerRow ? spreadsheet_table_spreadsheetCellText(sheet, range.row[0], column).trim() : '';
7924
+ const raw = headerRow ? spreadsheetCellText(sheet, range.row[0], column).trim() : '';
11301
7925
  const base = validTableColumnName(raw) ? raw : `Column${column - range.column[0] + 1}`;
11302
7926
  let candidate = base;
11303
7927
  let suffix = 2;
@@ -11318,7 +7942,7 @@ function validTableColumnName(name) {
11318
7942
  function truncateTableColumnName(value, maximum) {
11319
7943
  return Array.from(value).slice(0, maximum).join('');
11320
7944
  }
11321
- function spreadsheet_table_spreadsheetCellText(sheet, row, column) {
7945
+ function spreadsheetCellText(sheet, row, column) {
11322
7946
  const cell = sheet.data?.[row]?.[column] ?? sheet.celldata?.find((candidate)=>candidate.r === row && candidate.c === column)?.v;
11323
7947
  const value = cell?.m ?? cell?.v;
11324
7948
  return null == value ? '' : String(value);
@@ -13073,6 +9697,11 @@ function newConditionalRuleDraft(sheetId) {
13073
9697
  comparisonCellColor: DEFAULT_COLORS.comparisonCell,
13074
9698
  comparisonUseTextColor: true,
13075
9699
  comparisonUseCellColor: true,
9700
+ formula: '=A1>0',
9701
+ formulaTextColor: DEFAULT_COLORS.comparisonText,
9702
+ formulaCellColor: DEFAULT_COLORS.comparisonCell,
9703
+ formulaUseTextColor: true,
9704
+ formulaUseCellColor: true,
13076
9705
  scaleSize: '3',
13077
9706
  minimumColor: DEFAULT_COLORS.minimum,
13078
9707
  midpointColor: DEFAULT_COLORS.midpoint,
@@ -13104,7 +9733,7 @@ function conditionalRuleDraftForRule(sheetId, rule) {
13104
9733
  draft.comparisonUseCellColor = Boolean(style.cellColor);
13105
9734
  draft.comparisonTextColor = colorInputValue(style.textColor, DEFAULT_COLORS.comparisonText);
13106
9735
  draft.comparisonCellColor = colorInputValue(style.cellColor, DEFAULT_COLORS.comparisonCell);
13107
- } else if (isToolbarConditionalRule(rule)) {
9736
+ } else if ('default' !== rule.type || 'formula' !== rule.conditionName || Array.isArray(rule.format)) if (isToolbarConditionalRule(rule)) {
13108
9737
  draft.type = 'toolbarRule';
13109
9738
  draft.preservedRule = rule;
13110
9739
  } else if ('colorGradation' === rule.type && Array.isArray(rule.format)) {
@@ -13139,6 +9768,16 @@ function conditionalRuleDraftForRule(sheetId, rule) {
13139
9768
  draft.barMaxLength = String(options.maxLength ?? 90);
13140
9769
  }
13141
9770
  }
9771
+ else {
9772
+ const style = rule.format;
9773
+ const values = (rule.conditionValue ?? []).map(String);
9774
+ draft.type = 'formula';
9775
+ draft.formula = values[0] ?? '';
9776
+ draft.formulaUseTextColor = Boolean(style.textColor);
9777
+ draft.formulaUseCellColor = Boolean(style.cellColor);
9778
+ draft.formulaTextColor = colorInputValue(style.textColor, DEFAULT_COLORS.comparisonText);
9779
+ draft.formulaCellColor = colorInputValue(style.cellColor, DEFAULT_COLORS.comparisonCell);
9780
+ }
13142
9781
  return draft;
13143
9782
  }
13144
9783
  function buildConditionalRule(draft) {
@@ -13179,6 +9818,34 @@ function buildConditionalRule(draft) {
13179
9818
  value
13180
9819
  ]
13181
9820
  };
9821
+ } else if ('formula' === draft.type) {
9822
+ const formula = draft.formula.trim();
9823
+ if (!formula) return {
9824
+ error: '请输入条件格式公式。'
9825
+ };
9826
+ const formulaSource = formula.replace(/^=/, '').trim();
9827
+ if (!formulaSource) return {
9828
+ error: '请输入条件格式公式,例如 =A2>0。'
9829
+ };
9830
+ if (Array.from(formulaSource).length > 255) return {
9831
+ error: '条件格式公式不能超过 255 个字符。'
9832
+ };
9833
+ if (!draft.formulaUseTextColor && !draft.formulaUseCellColor) return {
9834
+ error: '至少启用一种文字或填充颜色。'
9835
+ };
9836
+ rule = {
9837
+ type: 'default',
9838
+ cellrange,
9839
+ format: {
9840
+ textColor: draft.formulaUseTextColor ? draft.formulaTextColor : null,
9841
+ cellColor: draft.formulaUseCellColor ? draft.formulaCellColor : null
9842
+ },
9843
+ conditionName: 'formula',
9844
+ conditionRange: [],
9845
+ conditionValue: [
9846
+ `=${formulaSource}`
9847
+ ]
9848
+ };
13182
9849
  } else if ('dataBar' === draft.type) {
13183
9850
  const thresholds = savedVisualThresholds(draft.barThresholds);
13184
9851
  const minLength = Number(draft.barMinLength);
@@ -14429,7 +11096,7 @@ function formatSpreadsheetRowPageBreaks(breaks) {
14429
11096
  return normalizedBreaks(breaks).map((row)=>String(row + 1)).join(', ');
14430
11097
  }
14431
11098
  function parseSpreadsheetColumnPageBreaks(value, maximumColumn) {
14432
- return parsePageBreakList(value, maximumColumn, work_spreadsheet_page_breaks_decodeColumn);
11099
+ return parsePageBreakList(value, maximumColumn, decodeColumn);
14433
11100
  }
14434
11101
  function formatSpreadsheetColumnPageBreaks(breaks) {
14435
11102
  return normalizedBreaks(breaks).map(encodeColumn).join(', ');
@@ -14449,7 +11116,7 @@ function parsePageBreakList(value, maximum, decode) {
14449
11116
  function normalizedBreaks(breaks) {
14450
11117
  return Array.from(new Set((breaks ?? []).filter((value)=>Number.isSafeInteger(value) && value > 0))).sort((left, right)=>left - right);
14451
11118
  }
14452
- function work_spreadsheet_page_breaks_decodeColumn(token) {
11119
+ function decodeColumn(token) {
14453
11120
  const match = /^\$?([A-Z]+)$/i.exec(token);
14454
11121
  if (!match) return null;
14455
11122
  let value = 0;
@@ -18538,9 +15205,9 @@ function appendRunSegment(target, source, absoluteStart, absoluteEnd, sourceStar
18538
15205
  });
18539
15206
  }
18540
15207
  function validTextSelection(text, selection) {
18541
- 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));
18542
15209
  }
18543
- function spreadsheet_rich_text_selection_format_splitsSurrogatePair(text, offset) {
15210
+ function splitsSurrogatePair(text, offset) {
18544
15211
  if (offset <= 0 || offset >= text.length) return false;
18545
15212
  const previous = text.charCodeAt(offset - 1);
18546
15213
  const next = text.charCodeAt(offset);
@@ -18693,8 +15360,8 @@ function captureSpreadsheetRichTextSelection({ content, root, selection, sheetId
18693
15360
  });
18694
15361
  if (!sheetId || !selection || !domSelection) return null;
18695
15362
  const { editor, end, start } = domSelection;
18696
- const row = spreadsheet_rich_text_selection_controller_focusedAxisIndex(selection.row_focus, selection.row);
18697
- 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);
18698
15365
  const source = spreadsheet_rich_text_selection_controller_sheetCellAt(content, sheetId, row, column);
18699
15366
  if (!source) return null;
18700
15367
  const sourceCell = liveSpreadsheetTextCell(editor, source);
@@ -18797,7 +15464,7 @@ function currentSpreadsheetTextEditor(snapshot) {
18797
15464
  function editorText(editor) {
18798
15465
  return editor.textContent ?? '';
18799
15466
  }
18800
- function spreadsheet_rich_text_selection_controller_focusedAxisIndex(value, axis) {
15467
+ function focusedAxisIndex(value, axis) {
18801
15468
  const minimum = spreadsheet_rich_text_selection_controller_finiteIndex(axis[0], 0);
18802
15469
  const maximum = spreadsheet_rich_text_selection_controller_finiteIndex(axis[1], minimum);
18803
15470
  const focus = spreadsheet_rich_text_selection_controller_finiteIndex(value, minimum);
@@ -21598,6 +18265,10 @@ function SpreadsheetConditionalFormatPanel({ content, onChange }) {
21598
18265
  value: 'icons',
21599
18266
  label: '图标集'
21600
18267
  },
18268
+ {
18269
+ value: 'formula',
18270
+ label: '自定义公式'
18271
+ },
21601
18272
  ...'toolbarRule' === draft.type ? [
21602
18273
  {
21603
18274
  value: 'toolbarRule',
@@ -21657,6 +18328,76 @@ function SpreadsheetConditionalFormatPanel({ content, onChange }) {
21657
18328
  ...draft,
21658
18329
  ...patch
21659
18330
  })
18331
+ }) : 'formula' === draft.type ? /*#__PURE__*/ jsxs(Fragment, {
18332
+ children: [
18333
+ /*#__PURE__*/ jsxs("div", {
18334
+ className: "work-office-field reference",
18335
+ children: [
18336
+ /*#__PURE__*/ jsx("span", {
18337
+ children: "公式"
18338
+ }),
18339
+ /*#__PURE__*/ jsx(office_text_field_OfficeTextField, {
18340
+ "aria-label": "条件格式公式",
18341
+ value: draft.formula,
18342
+ maxLength: 256,
18343
+ placeholder: "=A2>0",
18344
+ onChange: (event)=>setDraft({
18345
+ ...draft,
18346
+ formula: event.target.value
18347
+ })
18348
+ }),
18349
+ /*#__PURE__*/ jsxs("small", {
18350
+ children: [
18351
+ "以应用范围左上角为相对锚点;只读取本地缓存值。",
18352
+ ' ',
18353
+ Array.from(draft.formula.replace(/^=/, '')).length,
18354
+ "/",
18355
+ 255
18356
+ ]
18357
+ })
18358
+ ]
18359
+ }),
18360
+ /*#__PURE__*/ jsx("p", {
18361
+ className: "work-office-field-hint",
18362
+ children: "支持常用 Excel 函数、相对/绝对引用和有限区域;无法本地求值时保持原样。"
18363
+ }),
18364
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
18365
+ className: "toggle",
18366
+ ariaLabel: "公式规则使用文字颜色",
18367
+ checked: draft.formulaUseTextColor,
18368
+ onCheckedChange: (formulaUseTextColor)=>setDraft({
18369
+ ...draft,
18370
+ formulaUseTextColor
18371
+ }),
18372
+ children: "使用文字颜色"
18373
+ }),
18374
+ draft.formulaUseTextColor && /*#__PURE__*/ jsx(ColorField, {
18375
+ label: "文字颜色",
18376
+ value: draft.formulaTextColor,
18377
+ onChange: (formulaTextColor)=>setDraft({
18378
+ ...draft,
18379
+ formulaTextColor
18380
+ })
18381
+ }),
18382
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
18383
+ className: "toggle",
18384
+ ariaLabel: "公式规则使用填充颜色",
18385
+ checked: draft.formulaUseCellColor,
18386
+ onCheckedChange: (formulaUseCellColor)=>setDraft({
18387
+ ...draft,
18388
+ formulaUseCellColor
18389
+ }),
18390
+ children: "使用填充颜色"
18391
+ }),
18392
+ draft.formulaUseCellColor && /*#__PURE__*/ jsx(ColorField, {
18393
+ label: "填充颜色",
18394
+ value: draft.formulaCellColor,
18395
+ onChange: (formulaCellColor)=>setDraft({
18396
+ ...draft,
18397
+ formulaCellColor
18398
+ })
18399
+ })
18400
+ ]
21660
18401
  }) : 'colorGradation' === draft.type ? /*#__PURE__*/ jsxs(Fragment, {
21661
18402
  children: [
21662
18403
  /*#__PURE__*/ jsxs("div", {
@@ -26738,25 +23479,42 @@ function SpreadsheetDataValidationDialog({ source, restoreFocusTarget, onApply,
26738
23479
  })
26739
23480
  ]
26740
23481
  }),
26741
- 'dropdown' === value.type ? /*#__PURE__*/ jsx(field_Field, {
26742
- label: "来源",
26743
- required: true,
26744
- description: "输入逗号分隔项目,或单行/单列区域,例如 Ready,Blocked 或 Lists!A1:A8。",
26745
- error: visibleError ?? void 0,
26746
- children: /*#__PURE__*/ jsx("input", {
26747
- type: "text",
26748
- "aria-label": "来源",
26749
- autoCapitalize: "none",
26750
- spellCheck: false,
26751
- value: value.value1,
26752
- onBlur: ()=>setTouched(true),
26753
- onChange: (event)=>{
26754
- update({
26755
- value1: event.currentTarget.value
26756
- });
26757
- setTouched(true);
26758
- }
26759
- })
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
+ ]
26760
23518
  }) : 'custom' === value.type ? /*#__PURE__*/ jsx(SpreadsheetDataValidationCustomFormulaField, {
26761
23519
  value: value.value1,
26762
23520
  error: visibleError,
@@ -27185,7 +23943,6 @@ function cloneSpreadsheetDataValidationItem(item) {
27185
23943
  };
27186
23944
  }
27187
23945
  const MAX_SPREADSHEET_CUSTOM_VALIDATION_FORMULA_LENGTH = 255;
27188
- const MAX_SPREADSHEET_CUSTOM_VALIDATION_REFERENCED_CELLS = 1024;
27189
23946
  function evaluateSpreadsheetCustomValidation(content, sheetId, row, column, item, value) {
27190
23947
  const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
27191
23948
  if (!sheet) return unsupportedCustomValidation('找不到自定义公式所在的工作表。');
@@ -27200,122 +23957,30 @@ function evaluateSpreadsheetCustomValidation(content, sheetId, row, column, item
27200
23957
  if (Array.from(formula).length > MAX_SPREADSHEET_CUSTOM_VALIDATION_FORMULA_LENGTH) return unsupportedCustomValidation('自定义公式超过 255 个字符。');
27201
23958
  const ranges = parseSpreadsheetCellRanges(item.rangeTxt);
27202
23959
  const anchor = spreadsheetCustomValidationAnchor(ranges, row, column);
27203
- const evaluator = new SpreadsheetCustomFormulaEvaluator(content, sheet, row, column, anchor, normalizedProposed);
27204
- return evaluator.evaluate(formula);
27205
- }
27206
- class SpreadsheetCustomFormulaEvaluator {
27207
- content;
27208
- currentSheet;
27209
- targetRow;
27210
- targetColumn;
27211
- anchor;
27212
- proposedValue;
27213
- sheetsByName = new Map();
27214
- referencedCells = 0;
27215
- unsupportedMessage;
27216
- constructor(content, currentSheet, targetRow, targetColumn, anchor, proposedValue){
27217
- this.content = content;
27218
- this.currentSheet = currentSheet;
27219
- this.targetRow = targetRow;
27220
- this.targetColumn = targetColumn;
27221
- this.anchor = anchor;
27222
- this.proposedValue = proposedValue;
27223
- for (const sheet of content.sheets)this.sheetsByName.set(sheet.name.trim().toLocaleLowerCase(), sheet);
27224
- }
27225
- evaluate(formula) {
27226
- const allowedFunctions = new Set(SUPPORTED_FORMULAS.map((name)=>name.toUpperCase()));
27227
- const parser = new Parser();
27228
- parser.setFunction('ROW', (parameters)=>parameters.length ? null : this.targetRow + 1).setFunction('COLUMN', (parameters)=>parameters.length ? null : this.targetColumn + 1);
27229
- let unsupportedFunction;
27230
- parser.on('callFunction', (name)=>{
27231
- const normalized = name.toUpperCase();
27232
- if (!allowedFunctions.has(normalized)) unsupportedFunction ??= normalized;
27233
- });
27234
- parser.on('callCellValue', (coordinate, _options, done)=>{
27235
- const resolved = this.resolveCoordinate(this.currentSheet, coordinate);
27236
- if (!resolved) return void done(null);
27237
- done(this.readCell(resolved));
27238
- });
27239
- parser.on('callRangeValue', (start, end, _options, done)=>{
27240
- done(this.readRange(start, end));
27241
- });
27242
- const parsed = parser.parse(normalizeFormulaForFortuneParser(formula), {
27243
- sheetId: this.currentSheet.id
27244
- });
27245
- if (unsupportedFunction) return unsupportedCustomValidation(`自定义公式函数“${unsupportedFunction}”无法在浏览器本地求值。`);
27246
- if (this.unsupportedMessage) return unsupportedCustomValidation(this.unsupportedMessage);
27247
- if (parsed.error) return unsupportedCustomValidation('自定义公式无法求值,请检查函数、引用和括号。');
27248
- return {
27249
- supported: true,
27250
- valid: spreadsheetCustomValidationBoolean(parsed.result)
27251
- };
27252
- }
27253
- resolveCoordinate(fallbackSheet, coordinate) {
27254
- const sheet = coordinate.sheetName ? this.sheetsByName.get(coordinate.sheetName.trim().toLocaleLowerCase()) : fallbackSheet;
27255
- if (!sheet) {
27256
- this.unsupportedMessage = '自定义公式引用了不存在的工作表。';
27257
- return null;
27258
- }
27259
- if (coordinate.row.index < 0 || coordinate.column.index < 0) {
27260
- this.unsupportedMessage = '自定义公式暂不支持整行或整列引用,请改用有限区域。';
27261
- return null;
27262
- }
27263
- const resolvedRow = coordinate.row.isAbsolute ? coordinate.row.index : this.targetRow + (coordinate.row.index - this.anchor.row);
27264
- const resolvedColumn = coordinate.column.isAbsolute ? coordinate.column.index : this.targetColumn + (coordinate.column.index - this.anchor.column);
27265
- if (resolvedRow < 0 || resolvedColumn < 0 || resolvedRow >= 1048576 || resolvedColumn >= 16384) {
27266
- this.unsupportedMessage = '自定义公式引用超出了工作表边界。';
27267
- return null;
27268
- }
27269
- return {
27270
- sheet,
27271
- row: resolvedRow,
27272
- column: resolvedColumn
27273
- };
27274
- }
27275
- readRange(start, end) {
27276
- const startCoordinate = this.resolveCoordinate(this.currentSheet, start);
27277
- const rangeSheet = start.sheetName ? this.sheetsByName.get(start.sheetName.trim().toLocaleLowerCase()) : this.currentSheet;
27278
- const endCoordinate = rangeSheet ? this.resolveCoordinate(rangeSheet, end) : null;
27279
- if (!startCoordinate || !endCoordinate || startCoordinate.sheet.id !== endCoordinate.sheet.id) {
27280
- this.unsupportedMessage ??= '自定义公式区域引用必须位于同一工作表。';
27281
- return [];
27282
- }
27283
- const rowStart = Math.min(startCoordinate.row, endCoordinate.row);
27284
- const rowEnd = Math.max(startCoordinate.row, endCoordinate.row);
27285
- const columnStart = Math.min(startCoordinate.column, endCoordinate.column);
27286
- const columnEnd = Math.max(startCoordinate.column, endCoordinate.column);
27287
- const area = (rowEnd - rowStart + 1) * (columnEnd - columnStart + 1);
27288
- if (area <= 0 || area > MAX_SPREADSHEET_CUSTOM_VALIDATION_REFERENCED_CELLS - this.referencedCells) {
27289
- this.unsupportedMessage = '自定义公式一次最多读取 1,024 个单元格。';
27290
- return [];
27291
- }
27292
- const values = [];
27293
- for(let currentRow = rowStart; currentRow <= rowEnd; currentRow += 1){
27294
- const row = [];
27295
- for(let currentColumn = columnStart; currentColumn <= columnEnd; currentColumn += 1)row.push(this.readCell({
27296
- sheet: startCoordinate.sheet,
27297
- row: currentRow,
27298
- column: currentColumn
27299
- }));
27300
- values.push(row);
27301
- }
27302
- return values;
27303
- }
27304
- readCell(coordinate) {
27305
- this.referencedCells += 1;
27306
- if (this.referencedCells > MAX_SPREADSHEET_CUSTOM_VALIDATION_REFERENCED_CELLS) {
27307
- this.unsupportedMessage = '自定义公式一次最多读取 1,024 个单元格。';
27308
- return null;
27309
- }
27310
- if (coordinate.sheet.id === this.currentSheet.id && coordinate.row === this.targetRow && coordinate.column === this.targetColumn) return this.proposedValue;
27311
- const cell = spreadsheetCustomValidationCellAt(coordinate.sheet, coordinate.row, coordinate.column);
27312
- if (!cell) return null;
27313
- if (cell.f && void 0 === cell.v) {
27314
- this.unsupportedMessage ??= '自定义公式引用了尚未计算的公式单元格。';
27315
- return null;
27316
- }
27317
- return cell.v ?? null;
27318
- }
23960
+ const result = evaluateSpreadsheetLocalFormula(formula, {
23961
+ sheets: content.sheets.flatMap((candidate)=>candidate.id ? [
23962
+ {
23963
+ ...candidate,
23964
+ id: candidate.id
23965
+ }
23966
+ ] : []),
23967
+ sheetId,
23968
+ row,
23969
+ column,
23970
+ anchorRow: anchor?.row,
23971
+ anchorColumn: anchor?.column,
23972
+ proposedValue: normalizedProposed,
23973
+ useProposedValue: true
23974
+ });
23975
+ if (!result.supported) return {
23976
+ supported: false,
23977
+ valid: false,
23978
+ message: result.message ? `自定义${result.message}` : result.message
23979
+ };
23980
+ return {
23981
+ supported: true,
23982
+ valid: spreadsheetLocalFormulaBoolean(result.value)
23983
+ };
27319
23984
  }
27320
23985
  function spreadsheetCustomValidationAnchor(ranges, row, column) {
27321
23986
  const range = ranges?.find((candidate)=>spreadsheetCellRangeContains(candidate, row, column));
@@ -27324,10 +23989,6 @@ function spreadsheetCustomValidationAnchor(ranges, row, column) {
27324
23989
  column: range?.column[0] ?? column
27325
23990
  };
27326
23991
  }
27327
- function spreadsheetCustomValidationCellAt(sheet, row, column) {
27328
- if (void 0 !== sheet.data) return sheet.data[row]?.[column] ?? null;
27329
- return sheet.celldata?.find((entry)=>entry.r === row && entry.c === column)?.v ?? null;
27330
- }
27331
23992
  function spreadsheetCustomValidationValue(value) {
27332
23993
  if (!value || 'object' != typeof value || Array.isArray(value)) return value;
27333
23994
  if ('v' in value) return value.v;
@@ -27336,11 +23997,6 @@ function spreadsheetCustomValidationValue(value) {
27336
23997
  function spreadsheetCustomValidationBlank(value) {
27337
23998
  return null == value || 'string' == typeof value && '' === value.trim();
27338
23999
  }
27339
- function spreadsheetCustomValidationBoolean(value) {
27340
- if ('boolean' == typeof value) return value;
27341
- if ('number' == typeof value) return Number.isFinite(value) && 0 !== value;
27342
- return 'string' == typeof value && 'TRUE' === value.trim().toUpperCase();
27343
- }
27344
24000
  function unsupportedCustomValidation(message) {
27345
24001
  return {
27346
24002
  supported: false,
@@ -30150,6 +26806,46 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
30150
26806
  const formatCellsInvokerRef = useRef(null);
30151
26807
  const formatCellsSelectionRef = useRef(null);
30152
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
+ ]);
30153
26849
  const focusSpreadsheetDialogGrid = useCallback((focusOrigin)=>focusSpreadsheetGrid(spreadsheetCanvasRef.current, {
30154
26850
  focusOrigin
30155
26851
  }), []);
@@ -30267,7 +26963,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
30267
26963
  const conditionalStylesBySheet = useMemo(()=>new Map(materializedContent.sheets.flatMap((sheet)=>sheet.id ? [
30268
26964
  [
30269
26965
  sheet.id,
30270
- spreadsheetConditionalFormatStyles(sheet)
26966
+ spreadsheetConditionalFormatStyles(sheet, materializedContent.sheets)
30271
26967
  ]
30272
26968
  ] : [])), [
30273
26969
  materializedContent.sheets
@@ -30546,7 +27242,8 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
30546
27242
  materializedContent,
30547
27243
  requestedWorkbookSelection
30548
27244
  ]);
30549
- const workbookSheets = useMemo(()=>spreadsheetSheetsForFortune(renderedWorkbookSheets), [
27245
+ const workbookSheets = useMemo(()=>spreadsheetSheetsForFortune(renderedWorkbookSheets, materializedContent.namedRanges), [
27246
+ materializedContent.namedRanges,
30550
27247
  renderedWorkbookSheets
30551
27248
  ]);
30552
27249
  const displayedWorkbookSheets = useMemo(()=>preview ? workbookSheets.map((sheet)=>({