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