@a3s-lab/office 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/COLLABORATION_ROADMAP.md +15 -5
  2. package/README.md +28 -2
  3. package/dist/0~6090.js +7 -8
  4. package/dist/0~spreadsheet-editor.js +2347 -406
  5. package/dist/2180.js +370 -34
  6. package/dist/4104.js +657 -22
  7. package/dist/5184.js +1 -1
  8. package/dist/8715.js +156 -156
  9. package/dist/internal/collaboration/office-spreadsheet-collaboration-records.d.ts +2 -0
  10. package/dist/internal/collaboration/office-spreadsheet-collaboration-validation-support.d.ts +6 -0
  11. package/dist/internal/features/work/editors/spreadsheet-auto-filter.d.ts +1 -0
  12. package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +59 -1
  13. package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +12 -0
  14. package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +4 -2
  15. package/dist/internal/features/work/editors/spreadsheet-font-size-command.d.ts +18 -0
  16. package/dist/internal/features/work/editors/spreadsheet-font-size.d.ts +4 -0
  17. package/dist/internal/features/work/editors/spreadsheet-table-command.d.ts +3 -0
  18. package/dist/internal/features/work/editors/spreadsheet-table-conversion.d.ts +7 -0
  19. package/dist/internal/features/work/editors/spreadsheet-table-dialog.d.ts +8 -0
  20. package/dist/internal/features/work/editors/spreadsheet-table-limits.d.ts +1 -0
  21. package/dist/internal/features/work/editors/spreadsheet-table-reconciliation.d.ts +21 -0
  22. package/dist/internal/features/work/editors/spreadsheet-table-render.d.ts +16 -0
  23. package/dist/internal/features/work/editors/spreadsheet-table-ribbon.d.ts +8 -0
  24. package/dist/internal/features/work/editors/spreadsheet-table-style.d.ts +31 -0
  25. package/dist/internal/features/work/editors/spreadsheet-table.d.ts +55 -0
  26. package/dist/internal/features/work/editors/use-spreadsheet-table.d.ts +23 -0
  27. package/dist/internal/features/work/work-types.d.ts +100 -0
  28. package/dist/internal/features/work/work-xlsx-interop.d.ts +2 -0
  29. package/dist/internal/features/work/work-xlsx-table-filters.d.ts +3 -0
  30. package/dist/internal/features/work/work-xlsx-tables.d.ts +4 -0
  31. package/dist/office-kernel.wasm +0 -0
  32. package/dist/styles.css +204 -3
  33. package/dist/work-spreadsheet-package-scan.worker.js +3 -2
  34. package/docs/latest/en/browser-editor-architecture.md +43 -0
  35. package/package.json +6 -1
@@ -1,7 +1,7 @@
1
1
  import { __webpack_require__ } from "./0~rslib-runtime.js";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Workbook } from "@fortune-sheet/react";
4
- import { AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BadgeJapaneseYen, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Globe2, Grid2X2, Grid3X3, Hash, Italic, KeyRound, Layers2, Link2, Link2Off, ListChecks, ListFilter, LocateFixed, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, Settings2, ShieldCheck, Sigma, SortAsc, SortDesc, Strikethrough, SwatchBook, Table2, TableProperties, Trash2, Underline, Undo2, WrapText, X } from "lucide-react";
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, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Globe2, Grid2X2, Grid3X3, Hash, Italic, KeyRound, Layers2, Link2, Link2Off, ListChecks, ListFilter, LocateFixed, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, Settings2, ShieldCheck, Sigma, SortAsc, SortDesc, Strikethrough, SwatchBook, Table2, TableProperties, Trash2, Underline, Undo2, WrapText, X } from "lucide-react";
5
5
  import { Fragment as external_react_Fragment, cloneElement, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
6
  import { genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
7
7
  import { freezeImportedSpreadsheetCell, withoutEditableRange, spreadsheetMatrixProfile, normalizeSpreadsheetConditionalVisualOptions, effectiveSpreadsheetPageSetup, attachSpreadsheetShownCommentCells, withEditableRange, sameSpreadsheetHistoryValue, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, isSpreadsheetConditionalComparisonOperator, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, normalizeSpreadsheetDateValidationBoundary, sheetProtectionAuthority, editableRangeCellCount, unlockedCellCount, registerDerivedSpreadsheetMatrix, editableRangeRequiresCredentials, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSheetProtectionAuthority, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, spreadsheetConditionalComparisonNeedsUpperValue, drawSpreadsheetConditionalIcon, replaceOfficeSpreadsheetCollaboration as replaceWorkOfficeSpreadsheetCollaboration, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, spreadsheetConditionalThresholdsEqual } from "./2180.js";
@@ -10,13 +10,13 @@ import { moveOfficeMenuFocus, stepOfficeZoom, useOfficeRemoteParticipants, Popov
10
10
  import { useOfficeTaskPaneEscape, OfficeCheckbox, CommittedOfficeNumberField, useOfficeDialog, useOfficeTaskPaneModal, createOfficeKernelClient, officeFontFamilies, CollectionState, OfficeNumberField, handleOfficeTaskPaneKeyDown } from "./0~7048.js";
11
11
  import { isOfficeShortcutBlocked, OfficeColorPicker } from "./0~3635.js";
12
12
  import { createOfficeEditorExtension, useOfficeEditorRuntime, useOfficeEditorKeyboardShortcuts } from "./0~3557.js";
13
+ import { OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, createOfficeId as createWorkId, isOfficeKernelSpreadsheetError } from "./5184.js";
13
14
  import { showToast } from "./6489.js";
14
15
  import { unsupportedSpreadsheetFormulaFunctions } from "./0~work-spreadsheet-formula-support.js";
15
16
  import { WorkspaceContextMenu, isWorkspaceContextMenuKeyboardEvent, WorkOfficeZoomControls, Tabs, WorkOfficeStatusBar, Dialog, WorkOfficeRibbon, workspaceContextMenuPosition, OfficeSelect, WorkOfficeRibbonGroup, WorkOfficeRibbonButton, WorkOfficePreviewBar } from "./0~4595.js";
16
17
  import { useOfficeDraft, InlineNotice } from "./0~345.js";
17
18
  import { spreadsheetAgentMenuItems, SpreadsheetChartSeriesStyleEditor, normalizeOptionalOfficeNumber, normalizeRequiredOfficeNumber, SpreadsheetErrorBarEditor, useOfficeHistory, spreadsheetAgentSelection, SpreadsheetTrendlineEditor, applySpreadsheetAgentProposalChanges } from "./0~3539.js";
18
19
  import { useOfficeEditorFocusOrigin, useOfficeCollaborationLocationNavigator, StateView } from "./432.js";
19
- import { OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, createOfficeId as createWorkId, isOfficeKernelSpreadsheetError } from "./5184.js";
20
20
  var spreadsheet_editor_namespaceObject = {};
21
21
  __webpack_require__.r(spreadsheet_editor_namespaceObject);
22
22
  __webpack_require__.d(spreadsheet_editor_namespaceObject, {
@@ -1373,6 +1373,355 @@ const spreadsheetFontSizes = [
1373
1373
  48,
1374
1374
  72
1375
1375
  ];
1376
+ function nextSpreadsheetFontSize(current, direction) {
1377
+ if (!Number.isFinite(current) || current < 1 || current > 409) return null;
1378
+ return 'grow' === direction ? spreadsheetFontSizes.find((size)=>size > current) ?? null : [
1379
+ ...spreadsheetFontSizes
1380
+ ].reverse().find((size)=>size < current) ?? null;
1381
+ }
1382
+ function canApplySpreadsheetTableStructureChange(sheet, change) {
1383
+ if (!sheet || !validSpreadsheetTableStructureChange(change)) return false;
1384
+ for (const table of sheet.tables ?? []){
1385
+ if ('delete' === change.kind && 'row' === change.axis && spreadsheetTableDeleteRemovesSemanticRow(table, change.start, change.end)) return false;
1386
+ if (!transformSpreadsheetTableStructure(table, change)) return false;
1387
+ }
1388
+ return true;
1389
+ }
1390
+ function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations = []) {
1391
+ return sheets.map((sheet, index)=>{
1392
+ const source = sourceSheetForReconciliation(sheet, sourceSheets, index);
1393
+ if (!source?.tables?.length) return sheet;
1394
+ const sheetOperations = operations.filter((operation)=>operation.id === source.id);
1395
+ let tables = source.tables;
1396
+ const reconcileColumns = new Set();
1397
+ if (!operations.length) for (const table of tables)reconcileColumns.add(table.id);
1398
+ for (const operation of sheetOperations){
1399
+ const structure = spreadsheetTableStructureChangeFromOperation(operation);
1400
+ if (structure) {
1401
+ const transformed = [];
1402
+ for (const table of tables){
1403
+ const next = transformSpreadsheetTableStructure(table, structure);
1404
+ if (next) {
1405
+ transformed.push(next);
1406
+ if (next !== table) reconcileColumns.add(table.id);
1407
+ }
1408
+ }
1409
+ tables = transformed;
1410
+ continue;
1411
+ }
1412
+ const coordinate = spreadsheetCellCoordinateFromOperation(operation);
1413
+ if (!coordinate) continue;
1414
+ const table = tables.find((candidate)=>candidate.headerRow && candidate.range.row[0] === coordinate.row && coordinate.column >= candidate.range.column[0] && coordinate.column <= candidate.range.column[1]);
1415
+ if (table) reconcileColumns.add(table.id);
1416
+ }
1417
+ const updates = [];
1418
+ const readCell = spreadsheetSheetCellReader(sheet);
1419
+ tables = tables.map((table)=>{
1420
+ if (!reconcileColumns.has(table.id)) return table;
1421
+ const columns = canonicalSpreadsheetTableColumns(table, readCell);
1422
+ if (table.headerRow) for (const [offset, column] of columns.entries()){
1423
+ const cellColumn = table.range.column[0] + offset;
1424
+ if (spreadsheetCellText(readCell(table.range.row[0], cellColumn)) !== column.name) updates.push({
1425
+ column: cellColumn,
1426
+ name: column.name,
1427
+ row: table.range.row[0]
1428
+ });
1429
+ }
1430
+ return sameSpreadsheetTableColumns(table.columns, columns) ? table : {
1431
+ ...table,
1432
+ columns
1433
+ };
1434
+ });
1435
+ const withHeaders = stampSpreadsheetTableHeaderCells(sheet, updates);
1436
+ return {
1437
+ ...withHeaders,
1438
+ tables: tables.length ? tables : void 0
1439
+ };
1440
+ });
1441
+ }
1442
+ function transformSpreadsheetTableStructure(table, change) {
1443
+ if (!validSpreadsheetTableStructureChange(change)) return null;
1444
+ const axis = 'row' === change.axis ? table.range.row : table.range.column;
1445
+ const transformed = 'insert' === change.kind ? transformSpreadsheetTableAxisForInsertion(axis, change) : transformSpreadsheetTableAxisForDeletion(axis, change);
1446
+ if (!transformed) return null;
1447
+ const range = {
1448
+ row: 'row' === change.axis ? transformed.axis : [
1449
+ ...table.range.row
1450
+ ],
1451
+ column: 'column' === change.axis ? transformed.axis : [
1452
+ ...table.range.column
1453
+ ]
1454
+ };
1455
+ const height = range.row[1] - range.row[0] + 1;
1456
+ if (height <= Number(table.headerRow) + Number(table.totalsRow)) return null;
1457
+ if ('column' !== change.axis || !transformed.inside) {
1458
+ 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;
1459
+ return {
1460
+ ...table,
1461
+ range
1462
+ };
1463
+ }
1464
+ if ('insert' === change.kind) {
1465
+ const columns = [
1466
+ ...table.columns
1467
+ ];
1468
+ columns.splice(transformed.offset, 0, ...Array.from({
1469
+ length: change.count
1470
+ }, ()=>({
1471
+ name: ''
1472
+ })));
1473
+ return {
1474
+ ...table,
1475
+ range,
1476
+ columns,
1477
+ filters: table.filters.map((filter)=>filter.column >= transformed.offset ? {
1478
+ ...filter,
1479
+ column: filter.column + change.count
1480
+ } : filter)
1481
+ };
1482
+ }
1483
+ if (!('removed' in transformed)) return null;
1484
+ const removed = transformed.removed;
1485
+ if ('number' != typeof removed) return null;
1486
+ const columns = [
1487
+ ...table.columns
1488
+ ];
1489
+ columns.splice(transformed.offset, removed);
1490
+ if (!columns.length) return null;
1491
+ return {
1492
+ ...table,
1493
+ range,
1494
+ columns,
1495
+ filters: filtersAfterSpreadsheetTableColumnDeletion(table.filters, transformed.offset, removed)
1496
+ };
1497
+ }
1498
+ function transformSpreadsheetTableAxisForInsertion(axis, change) {
1499
+ const start = axis[0] ?? 0;
1500
+ const end = axis[1] ?? start;
1501
+ const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
1502
+ if (insertion <= start) return {
1503
+ axis: [
1504
+ start + change.count,
1505
+ end + change.count
1506
+ ],
1507
+ inside: false,
1508
+ offset: 0
1509
+ };
1510
+ if (insertion <= end) return {
1511
+ axis: [
1512
+ start,
1513
+ end + change.count
1514
+ ],
1515
+ inside: true,
1516
+ offset: insertion - start
1517
+ };
1518
+ return {
1519
+ axis: [
1520
+ start,
1521
+ end
1522
+ ],
1523
+ inside: false,
1524
+ offset: 0
1525
+ };
1526
+ }
1527
+ function transformSpreadsheetTableAxisForDeletion(axis, change) {
1528
+ const start = axis[0] ?? 0;
1529
+ const end = axis[1] ?? start;
1530
+ const count = change.end - change.start + 1;
1531
+ if (change.end < start) return {
1532
+ axis: [
1533
+ start - count,
1534
+ end - count
1535
+ ],
1536
+ inside: false,
1537
+ offset: 0,
1538
+ removed: 0
1539
+ };
1540
+ if (change.start > end) return {
1541
+ axis: [
1542
+ start,
1543
+ end
1544
+ ],
1545
+ inside: false,
1546
+ offset: 0,
1547
+ removed: 0
1548
+ };
1549
+ const overlapStart = Math.max(start, change.start);
1550
+ const overlapEnd = Math.min(end, change.end);
1551
+ const removed = overlapEnd - overlapStart + 1;
1552
+ const remaining = end - start + 1 - removed;
1553
+ if (remaining <= 0) return null;
1554
+ const deletedBeforeStart = Math.max(0, Math.min(change.end, start - 1) - change.start + 1);
1555
+ const nextStart = start - deletedBeforeStart;
1556
+ return {
1557
+ axis: [
1558
+ nextStart,
1559
+ nextStart + remaining - 1
1560
+ ],
1561
+ inside: true,
1562
+ offset: overlapStart - start,
1563
+ removed
1564
+ };
1565
+ }
1566
+ function filtersAfterSpreadsheetTableColumnDeletion(filters, offset, removed) {
1567
+ const end = offset + removed - 1;
1568
+ return filters.flatMap((filter)=>{
1569
+ if (filter.column >= offset && filter.column <= end) return [];
1570
+ return [
1571
+ filter.column > end ? {
1572
+ ...filter,
1573
+ column: filter.column - removed
1574
+ } : filter
1575
+ ];
1576
+ });
1577
+ }
1578
+ function canonicalSpreadsheetTableColumns(table, readCell) {
1579
+ const width = table.range.column[1] - table.range.column[0] + 1;
1580
+ const observed = new Set();
1581
+ return Array.from({
1582
+ length: width
1583
+ }, (_, offset)=>{
1584
+ const raw = table.headerRow ? spreadsheetCellText(readCell(table.range.row[0], table.range.column[0] + offset)).trim() : table.columns[offset]?.name.trim() ?? '';
1585
+ const base = validSpreadsheetTableColumnName(raw) ? raw : `Column${offset + 1}`;
1586
+ let candidate = base;
1587
+ let suffix = 2;
1588
+ while(observed.has(candidate.toLocaleLowerCase())){
1589
+ const suffixText = String(suffix);
1590
+ candidate = `${truncateSpreadsheetTableColumnName(base, 255 - suffixText.length)}${suffixText}`;
1591
+ suffix += 1;
1592
+ }
1593
+ observed.add(candidate.toLocaleLowerCase());
1594
+ return {
1595
+ name: candidate
1596
+ };
1597
+ });
1598
+ }
1599
+ function spreadsheetTableDeleteRemovesSemanticRow(table, start, end) {
1600
+ 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]);
1601
+ }
1602
+ function spreadsheetTableStructureChangeFromOperation(operation) {
1603
+ if (!spreadsheet_table_reconciliation_isRecord(operation.value)) return null;
1604
+ if ('insertRowCol' === operation.op) {
1605
+ const { count, direction, index, type } = operation.value;
1606
+ const change = {
1607
+ axis: 'column' === type ? 'column' : 'row',
1608
+ count: Number(count),
1609
+ direction: 'rightbottom' === direction ? 'rightbottom' : 'lefttop',
1610
+ index: Number(index),
1611
+ kind: 'insert'
1612
+ };
1613
+ return ('column' === type || 'row' === type) && ('lefttop' === direction || 'rightbottom' === direction) && validSpreadsheetTableStructureChange(change) ? change : null;
1614
+ }
1615
+ if ('deleteRowCol' === operation.op) {
1616
+ const { end, start, type } = operation.value;
1617
+ const change = {
1618
+ axis: 'column' === type ? 'column' : 'row',
1619
+ end: Number(end),
1620
+ kind: 'delete',
1621
+ start: Number(start)
1622
+ };
1623
+ return ('column' === type || 'row' === type) && validSpreadsheetTableStructureChange(change) ? change : null;
1624
+ }
1625
+ return null;
1626
+ }
1627
+ function spreadsheetCellCoordinateFromOperation(operation) {
1628
+ if ('data' !== operation.path[0] || operation.path.length < 3) return null;
1629
+ const row = operation.path[1];
1630
+ const column = operation.path[2];
1631
+ return Number.isSafeInteger(row) && Number(row) >= 0 && Number.isSafeInteger(column) && Number(column) >= 0 ? {
1632
+ column: Number(column),
1633
+ row: Number(row)
1634
+ } : null;
1635
+ }
1636
+ function validSpreadsheetTableStructureChange(change) {
1637
+ if ('insert' === change.kind) return Number.isSafeInteger(change.index) && change.index >= 0 && Number.isSafeInteger(change.count) && change.count > 0;
1638
+ return Number.isSafeInteger(change.start) && change.start >= 0 && Number.isSafeInteger(change.end) && change.end >= change.start;
1639
+ }
1640
+ function sourceSheetForReconciliation(sheet, sourceSheets, index) {
1641
+ return (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
1642
+ }
1643
+ function spreadsheetSheetCellReader(sheet) {
1644
+ if (void 0 !== sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
1645
+ const cells = new Map((sheet.celldata ?? []).map((entry)=>[
1646
+ `${entry.r}:${entry.c}`,
1647
+ entry.v
1648
+ ]));
1649
+ return (row, column)=>cells.get(`${row}:${column}`) ?? null;
1650
+ }
1651
+ function stampSpreadsheetTableHeaderCells(sheet, updates) {
1652
+ if (!updates.length) return sheet;
1653
+ if (void 0 !== sheet.data) {
1654
+ const data = [];
1655
+ data.length = sheet.data.length;
1656
+ for (const [row, value] of sparseArrayEntries(sheet.data))data[row] = value;
1657
+ const mutableRows = new Map();
1658
+ for (const update of updates){
1659
+ let row = mutableRows.get(update.row);
1660
+ if (!row) {
1661
+ const source = data[update.row];
1662
+ row = [];
1663
+ row.length = source?.length ?? 0;
1664
+ for (const column of sparseArrayIndexes(source))row[column] = source?.[column];
1665
+ mutableRows.set(update.row, row);
1666
+ data[update.row] = row;
1667
+ }
1668
+ row[update.column] = stampSpreadsheetTableHeaderCell(row[update.column], update.name);
1669
+ }
1670
+ return {
1671
+ ...sheet,
1672
+ data
1673
+ };
1674
+ }
1675
+ const celldata = [
1676
+ ...sheet.celldata ?? []
1677
+ ];
1678
+ const indexes = new Map(celldata.map((entry, index)=>[
1679
+ `${entry.r}:${entry.c}`,
1680
+ index
1681
+ ]));
1682
+ for (const update of updates){
1683
+ const key = `${update.row}:${update.column}`;
1684
+ const index = indexes.get(key);
1685
+ const source = void 0 === index ? void 0 : celldata[index];
1686
+ const entry = {
1687
+ r: update.row,
1688
+ c: update.column,
1689
+ v: stampSpreadsheetTableHeaderCell(source?.v, update.name)
1690
+ };
1691
+ if (void 0 === index) {
1692
+ indexes.set(key, celldata.length);
1693
+ celldata.push(entry);
1694
+ } else celldata[index] = entry;
1695
+ }
1696
+ return {
1697
+ ...sheet,
1698
+ celldata
1699
+ };
1700
+ }
1701
+ function stampSpreadsheetTableHeaderCell(cell, name) {
1702
+ return {
1703
+ ...cell ?? {},
1704
+ m: name,
1705
+ v: name
1706
+ };
1707
+ }
1708
+ function spreadsheetCellText(cell) {
1709
+ const value = cell?.m ?? cell?.v;
1710
+ return null == value ? '' : String(value);
1711
+ }
1712
+ function validSpreadsheetTableColumnName(name) {
1713
+ const characters = Array.from(name);
1714
+ return name.trim() === name && characters.length >= 1 && characters.length <= 255 && !characters.some((character)=>/\p{Cc}/u.test(character) || '\uFFFE' === character || '\uFFFF' === character);
1715
+ }
1716
+ function truncateSpreadsheetTableColumnName(value, maximum) {
1717
+ return Array.from(value).slice(0, maximum).join('');
1718
+ }
1719
+ function sameSpreadsheetTableColumns(left, right) {
1720
+ return left.length === right.length && left.every((column, index)=>column.name === right[index]?.name);
1721
+ }
1722
+ function spreadsheet_table_reconciliation_isRecord(value) {
1723
+ return Boolean(value && 'object' == typeof value && !Array.isArray(value));
1724
+ }
1376
1725
  function spreadsheetSelectionReference(selection) {
1377
1726
  const rowStart = Math.min(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
1378
1727
  const rowEnd = Math.max(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
@@ -1622,7 +1971,7 @@ function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = []) {
1622
1971
  incremental: true,
1623
1972
  operationCount: operations.length
1624
1973
  });
1625
- return incremental.sheets;
1974
+ return reconcileSpreadsheetTablesAfterFortune(incremental.sheets, sourceSheets, operations);
1626
1975
  }
1627
1976
  const projected = sheets.map((sheet, index)=>{
1628
1977
  const source = (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
@@ -1651,7 +2000,7 @@ function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = []) {
1651
2000
  incremental: false,
1652
2001
  operationCount: operations.length
1653
2002
  });
1654
- return projected;
2003
+ return reconcileSpreadsheetTablesAfterFortune(projected, sourceSheets, operations);
1655
2004
  }
1656
2005
  function recordSpreadsheetControlledProjectionMeasure(start, end, detail) {
1657
2006
  try {
@@ -2289,6 +2638,12 @@ const spreadsheetRibbonTabs = [
2289
2638
  label: '视图'
2290
2639
  }
2291
2640
  ];
2641
+ const spreadsheetTableDesignRibbonTab = {
2642
+ id: 'tableDesign',
2643
+ label: '表格设计',
2644
+ compactLabel: '设计',
2645
+ contextual: true
2646
+ };
2292
2647
  const spreadsheetCommandCatalog = {
2293
2648
  undo: {
2294
2649
  id: 'history.undo',
@@ -2456,6 +2811,40 @@ const spreadsheetCommandCatalog = {
2456
2811
  ]
2457
2812
  }
2458
2813
  },
2814
+ growFont: {
2815
+ id: 'font.grow',
2816
+ label: '增大字号',
2817
+ location: {
2818
+ area: 'ribbon',
2819
+ tab: 'home',
2820
+ group: 'font'
2821
+ },
2822
+ shortcut: {
2823
+ label: 'Cmd/Ctrl+Shift+. 或 Cmd/Ctrl+]',
2824
+ aria: 'Control+Shift+. Meta+Shift+. Control+] Meta+]',
2825
+ editor: [
2826
+ 'Mod-Shift-.',
2827
+ 'Mod-]'
2828
+ ]
2829
+ }
2830
+ },
2831
+ shrinkFont: {
2832
+ id: 'font.shrink',
2833
+ label: '减小字号',
2834
+ location: {
2835
+ area: 'ribbon',
2836
+ tab: 'home',
2837
+ group: 'font'
2838
+ },
2839
+ shortcut: {
2840
+ label: 'Cmd/Ctrl+Shift+, 或 Cmd/Ctrl+[',
2841
+ aria: 'Control+Shift+, Meta+Shift+, Control+[ Meta+[',
2842
+ editor: [
2843
+ 'Mod-Shift-,',
2844
+ 'Mod-['
2845
+ ]
2846
+ }
2847
+ },
2459
2848
  numberFormatGeneral: {
2460
2849
  id: 'number.general',
2461
2850
  label: spreadsheetNumberFormatPresetLabels.general,
@@ -2672,6 +3061,14 @@ const spreadsheetCommandCatalog = {
2672
3061
  area: 'ribbon',
2673
3062
  tab: 'home',
2674
3063
  group: 'font'
3064
+ },
3065
+ shortcut: {
3066
+ label: 'Cmd/Ctrl+Shift+_',
3067
+ aria: 'Control+Shift+_ Meta+Shift+_',
3068
+ editor: [
3069
+ 'Mod-Shift-_',
3070
+ 'Mod-Shift-Minus'
3071
+ ]
2675
3072
  }
2676
3073
  },
2677
3074
  borderAll: {
@@ -2690,6 +3087,14 @@ const spreadsheetCommandCatalog = {
2690
3087
  area: 'ribbon',
2691
3088
  tab: 'home',
2692
3089
  group: 'font'
3090
+ },
3091
+ shortcut: {
3092
+ label: 'Cmd/Ctrl+Shift+&',
3093
+ aria: 'Control+Shift+& Meta+Shift+&',
3094
+ editor: [
3095
+ 'Mod-Shift-&',
3096
+ 'Mod-Shift-7'
3097
+ ]
2693
3098
  }
2694
3099
  },
2695
3100
  borderInside: {
@@ -3058,6 +3463,22 @@ const spreadsheetCommandCatalog = {
3058
3463
  group: 'charts'
3059
3464
  }
3060
3465
  },
3466
+ table: {
3467
+ id: 'insert.table',
3468
+ label: '表格',
3469
+ location: {
3470
+ area: 'ribbon',
3471
+ tab: 'insert',
3472
+ group: 'tables'
3473
+ },
3474
+ shortcut: {
3475
+ label: 'Cmd/Ctrl+T',
3476
+ aria: 'Control+T Meta+T',
3477
+ editor: [
3478
+ 'Mod-t'
3479
+ ]
3480
+ }
3481
+ },
3061
3482
  hyperlink: {
3062
3483
  id: 'insert.hyperlink',
3063
3484
  label: '超链接',
@@ -3477,6 +3898,8 @@ function spreadsheet_cell_clear_isRecord(value) {
3477
3898
  return Boolean(value && 'object' == typeof value && !Array.isArray(value));
3478
3899
  }
3479
3900
  function createSpreadsheetCellBorderExtension() {
3901
+ const outsideShortcuts = spreadsheetCommandCatalog.borderOutside.shortcut.editor;
3902
+ const noneShortcuts = spreadsheetCommandCatalog.borderNone.shortcut.editor;
3480
3903
  return createOfficeEditorExtension({
3481
3904
  name: 'spreadsheetCellBorders',
3482
3905
  addCommands: ()=>({
@@ -3484,6 +3907,28 @@ function createSpreadsheetCellBorderExtension() {
3484
3907
  canExecute: canSetSelectedCellBorders,
3485
3908
  execute: setSelectedCellBorders
3486
3909
  }
3910
+ }),
3911
+ addKeyboardShortcuts: ()=>({
3912
+ [outsideShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3913
+ target: 'outside',
3914
+ color: '#000000',
3915
+ style: 'thin'
3916
+ }),
3917
+ [outsideShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3918
+ target: 'outside',
3919
+ color: '#000000',
3920
+ style: 'thin'
3921
+ }),
3922
+ [noneShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3923
+ target: 'none',
3924
+ color: '#000000',
3925
+ style: 'thin'
3926
+ }),
3927
+ [noneShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3928
+ target: 'none',
3929
+ color: '#000000',
3930
+ style: 'thin'
3931
+ })
3487
3932
  })
3488
3933
  });
3489
3934
  }
@@ -3491,15 +3936,22 @@ function canSetSelectedCellBorders(context, format) {
3491
3936
  return Boolean(context.editable && context.workbook && context.targetSheetId && canSetSpreadsheetCellBorders(context.content, context.targetSheetId, liveSpreadsheetBorderRange(context), format));
3492
3937
  }
3493
3938
  function setSelectedCellBorders(context, format) {
3494
- if (!canSetSelectedCellBorders(context, format)) return false;
3495
- const next = setSpreadsheetCellBorders(context.content, context.targetSheetId, liveSpreadsheetBorderRange(context), format);
3939
+ if (!context.editable || !context.workbook || !context.targetSheetId) return false;
3940
+ const selection = context.workbook.getSelection()?.at(-1) ?? context.fallbackRange;
3941
+ const range = spreadsheetSingleRange(selection);
3942
+ if (!canSetSpreadsheetCellBorders(context.content, context.targetSheetId, range, format)) return false;
3943
+ const next = setSpreadsheetCellBorders(context.content, context.targetSheetId, range, format);
3496
3944
  if (!next) return false;
3497
- context.onChange(next);
3945
+ context.onChange(spreadsheetContentWithSelection(next, context.targetSheetId, selection));
3498
3946
  return true;
3499
3947
  }
3500
3948
  function liveSpreadsheetBorderRange(context) {
3501
3949
  return spreadsheetSingleRange(context.workbook?.getSelection()?.at(-1) ?? context.fallbackRange);
3502
3950
  }
3951
+ function runSpreadsheetBorderShortcut(event, canExecute, execute, format) {
3952
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute(format)) return false;
3953
+ return execute(format);
3954
+ }
3503
3955
  const MAX_SPREADSHEET_PER_CELL_BORDER_CELLS = 10000;
3504
3956
  function setSpreadsheetCellBordersPerCell(content, sheetId, range, formats) {
3505
3957
  const normalizedRange = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
@@ -4354,76 +4806,227 @@ function spreadsheetSelectedCellFillPlan(context, direction) {
4354
4806
  const sheet = context.content.sheets.find((candidate)=>candidate.id === context.targetSheetId);
4355
4807
  return canApplySpreadsheetCellFill(sheet, plan) ? plan : null;
4356
4808
  }
4357
- const spreadsheet_go_to_spreadsheetMaximumRows = 1048576;
4358
- const spreadsheet_go_to_spreadsheetMaximumColumns = 16384;
4359
- const spreadsheetGoToMaximumInputLength = 512;
4360
- function resolveSpreadsheetGoToTarget(content, activeSheetId, input) {
4361
- const value = input.trim().replace(/^=/, '').trim();
4362
- if (!value) return spreadsheetGoToError('empty');
4363
- if (value.length > spreadsheetGoToMaximumInputLength) return spreadsheetGoToError('invalid-reference');
4364
- const activeSheet = content.sheets.find((sheet)=>sheet.id === activeSheetId);
4365
- if (!activeSheet) return spreadsheetGoToError('sheet-not-found');
4366
- const qualified = splitSpreadsheetQualifiedReference(value);
4367
- if (!qualified) return spreadsheetGoToError('invalid-reference');
4368
- const requestedSheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : activeSheet;
4369
- if (qualified.qualifier && !requestedSheet) return spreadsheetGoToError('sheet-not-found');
4370
- if (requestedSheet?.hide === 1) return spreadsheetGoToError('hidden-sheet');
4371
- const direct = resolveSpreadsheetDirectRange(content, requestedSheet ?? activeSheet, qualified);
4372
- if ('error' === direct.kind) return spreadsheetGoToError(direct.code);
4373
- if ('match' === direct.kind) return spreadsheetGoToSuccess(direct.value, 'reference');
4374
- const named = findSpreadsheetGoToName(content, qualified.reference, requestedSheet ?? activeSheet, Boolean(qualified.qualifier));
4375
- if ('error' === named.kind) return spreadsheetGoToError(named.code);
4376
- if (!named.value) return spreadsheetGoToError('invalid-reference');
4377
- const fallbackSheet = named.value.scopeSheetId ? content.sheets.find((sheet)=>sheet.id === named.value?.scopeSheetId) : requestedSheet ?? activeSheet;
4378
- if (!fallbackSheet) return spreadsheetGoToError('sheet-not-found');
4379
- if (1 === fallbackSheet.hide) return spreadsheetGoToError('hidden-sheet');
4380
- const namedReference = splitSpreadsheetQualifiedReference(named.value.reference.trim().replace(/^=/, '').trim());
4381
- if (!namedReference) return spreadsheetGoToError('unsupported-name');
4382
- const resolved = resolveSpreadsheetDirectRange(content, fallbackSheet, namedReference);
4383
- if ('error' === resolved.kind) return spreadsheetGoToError(resolved.code);
4384
- if ('match' !== resolved.kind) return spreadsheetGoToError('unsupported-name');
4385
- return spreadsheetGoToSuccess(resolved.value, 'named-range', named.value.name);
4386
- }
4387
- function spreadsheetGoToValidationMessage(content, activeSheetId, input) {
4388
- const resolution = resolveSpreadsheetGoToTarget(content, activeSheetId, input);
4389
- return resolution.ok ? null : resolution.message;
4809
+ const MAX_SPREADSHEET_FONT_SIZE_CELLS = 10000;
4810
+ function createSpreadsheetFontSizeExtension() {
4811
+ const growShortcuts = spreadsheetCommandCatalog.growFont.shortcut.editor;
4812
+ const shrinkShortcuts = spreadsheetCommandCatalog.shrinkFont.shortcut.editor;
4813
+ return createOfficeEditorExtension({
4814
+ name: 'spreadsheetFontSize',
4815
+ addCommands: ()=>({
4816
+ adjustFontSize: {
4817
+ canExecute: canAdjustSelectedFontSize,
4818
+ execute: adjustSelectedFontSize
4819
+ }
4820
+ }),
4821
+ addKeyboardShortcuts: ()=>({
4822
+ [growShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'grow'),
4823
+ [growShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'grow'),
4824
+ [shrinkShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'shrink'),
4825
+ [shrinkShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'shrink')
4826
+ })
4827
+ });
4390
4828
  }
4391
- function resolveSpreadsheetDirectRange(content, fallbackSheet, qualified) {
4392
- const sheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : fallbackSheet;
4393
- if (!sheet) return {
4394
- kind: 'error',
4395
- code: 'sheet-not-found'
4396
- };
4397
- if (1 === sheet.hide) return {
4398
- kind: 'error',
4399
- code: 'hidden-sheet'
4400
- };
4401
- const ranges = parseSpreadsheetCellRanges(qualified.reference);
4402
- if (!ranges) return {
4403
- kind: 'miss'
4404
- };
4405
- if (1 !== ranges.length) return {
4406
- kind: 'error',
4407
- code: 'multiple-ranges'
4408
- };
4409
- const range = ranges[0];
4410
- if (!range || !spreadsheetGoToRangeIsBounded(sheet, range)) return {
4411
- kind: 'error',
4412
- code: 'out-of-bounds'
4413
- };
4414
- return {
4415
- kind: 'match',
4416
- value: {
4417
- range,
4418
- sheet
4829
+ function spreadsheetFontSizeApiCalls(cells, range, sheetId, direction) {
4830
+ const normalizedRange = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4831
+ if (!normalizedRange || !sheetId || !isSpreadsheetFontSizeDirection(direction) || spreadsheet_cell_range_spreadsheetCellRangeArea(normalizedRange) > MAX_SPREADSHEET_FONT_SIZE_CELLS) return [];
4832
+ const rectangles = [];
4833
+ let active = new Map();
4834
+ const rowCount = normalizedRange.row[1] - normalizedRange.row[0] + 1;
4835
+ const columnCount = normalizedRange.column[1] - normalizedRange.column[0] + 1;
4836
+ for(let rowOffset = 0; rowOffset < rowCount; rowOffset += 1){
4837
+ const row = normalizedRange.row[0] + rowOffset;
4838
+ const runs = spreadsheetFontSizeRowRuns(cells[rowOffset], row, normalizedRange.column[0], columnCount, direction);
4839
+ const nextActive = new Map();
4840
+ for (const run of runs){
4841
+ const signature = `${run.key}:${run.range.column[0]}:${run.range.column[1]}`;
4842
+ const previous = active.get(signature);
4843
+ if (previous && previous.range.row[1] === row - 1) {
4844
+ previous.range.row[1] = row;
4845
+ nextActive.set(signature, previous);
4846
+ } else nextActive.set(signature, run);
4419
4847
  }
4420
- };
4848
+ for (const [signature, run] of active)if (!nextActive.has(signature)) rectangles.push(run);
4849
+ active = nextActive;
4850
+ }
4851
+ rectangles.push(...active.values());
4852
+ rectangles.sort((left, right)=>left.range.row[0] - right.range.row[0] || left.range.column[0] - right.range.column[0] || left.range.row[1] - right.range.row[1] || left.range.column[1] - right.range.column[1]);
4853
+ return rectangles.map(({ range: targetRange, size })=>({
4854
+ name: 'setCellFormatByRange',
4855
+ args: [
4856
+ 'fs',
4857
+ size,
4858
+ targetRange,
4859
+ {
4860
+ id: sheetId
4861
+ }
4862
+ ]
4863
+ }));
4421
4864
  }
4422
- function findSpreadsheetGoToName(content, input, scopeSheet, qualified) {
4423
- const normalized = input.trim().toLocaleLowerCase();
4424
- const matching = (content.namedRanges ?? []).filter((range)=>range.name.trim().toLocaleLowerCase() === normalized);
4425
- const scoped = matching.filter((range)=>range.scopeSheetId === scopeSheet.id);
4426
- const workbook = matching.filter((range)=>!range.scopeSheetId);
4865
+ function spreadsheetFontSizeRowRuns(cells, row, firstColumn, columnCount, direction) {
4866
+ const runs = [];
4867
+ let current = null;
4868
+ for(let columnOffset = 0; columnOffset < columnCount; columnOffset += 1){
4869
+ const column = firstColumn + columnOffset;
4870
+ const size = adjustedSpreadsheetFontSize(cells?.[columnOffset], direction);
4871
+ if (null === size) {
4872
+ if (current) runs.push(current);
4873
+ current = null;
4874
+ continue;
4875
+ }
4876
+ const key = String(size);
4877
+ if (current && current.key === key) {
4878
+ current.range.column[1] = column;
4879
+ continue;
4880
+ }
4881
+ if (current) runs.push(current);
4882
+ current = {
4883
+ key,
4884
+ range: {
4885
+ row: [
4886
+ row,
4887
+ row
4888
+ ],
4889
+ column: [
4890
+ column,
4891
+ column
4892
+ ]
4893
+ },
4894
+ size
4895
+ };
4896
+ }
4897
+ if (current) runs.push(current);
4898
+ return runs;
4899
+ }
4900
+ function adjustedSpreadsheetFontSize(cell, direction) {
4901
+ const size = cell?.fs === void 0 ? 10 : Number(cell.fs);
4902
+ return nextSpreadsheetFontSize(size, direction);
4903
+ }
4904
+ function canAdjustSelectedFontSize(context, direction) {
4905
+ const workbook = context.workbook;
4906
+ const sheetId = context.targetSheetId;
4907
+ if (!context.editable || !workbook || !sheetId || sheetId !== context.activeSheetId || !isSpreadsheetFontSizeDirection(direction)) return false;
4908
+ const range = liveSpreadsheetFontSizeRange(context);
4909
+ if (spreadsheet_cell_range_spreadsheetCellRangeArea(range) > MAX_SPREADSHEET_FONT_SIZE_CELLS) return false;
4910
+ try {
4911
+ const cells = workbook.getCellsByRange(range, {
4912
+ id: sheetId
4913
+ });
4914
+ return canAdjustSpreadsheetFontSizeCells(range.row[1] - range.row[0] + 1, range.column[1] - range.column[0] + 1, (rowOffset, columnOffset)=>cells[rowOffset]?.[columnOffset] ?? null, direction);
4915
+ } catch {
4916
+ return false;
4917
+ }
4918
+ }
4919
+ function adjustSelectedFontSize(context, direction) {
4920
+ const workbook = context.workbook;
4921
+ const sheetId = context.targetSheetId;
4922
+ if (!context.editable || !workbook || !sheetId || sheetId !== context.activeSheetId || !isSpreadsheetFontSizeDirection(direction)) return false;
4923
+ const range = liveSpreadsheetFontSizeRange(context);
4924
+ if (spreadsheet_cell_range_spreadsheetCellRangeArea(range) > MAX_SPREADSHEET_FONT_SIZE_CELLS) return false;
4925
+ try {
4926
+ const cells = workbook.getCellsByRange(range, {
4927
+ id: sheetId
4928
+ });
4929
+ const calls = spreadsheetFontSizeApiCalls(cells, range, sheetId, direction);
4930
+ if (!calls.length) return false;
4931
+ workbook.batchCallApis(calls);
4932
+ return true;
4933
+ } catch {
4934
+ return false;
4935
+ }
4936
+ }
4937
+ function canAdjustSpreadsheetFontSizeCells(rowCount, columnCount, cellAt, direction) {
4938
+ for(let rowOffset = 0; rowOffset < rowCount; rowOffset += 1)for(let columnOffset = 0; columnOffset < columnCount; columnOffset += 1)if (null !== adjustedSpreadsheetFontSize(cellAt(rowOffset, columnOffset), direction)) return true;
4939
+ return false;
4940
+ }
4941
+ function liveSpreadsheetFontSizeRange(context) {
4942
+ return spreadsheet_cell_range_normalizeSpreadsheetCellRange(spreadsheetSingleRange(context.workbook?.getSelection()?.at(-1) ?? context.fallbackRange)) ?? {
4943
+ row: [
4944
+ 0,
4945
+ 0
4946
+ ],
4947
+ column: [
4948
+ 0,
4949
+ 0
4950
+ ]
4951
+ };
4952
+ }
4953
+ function isSpreadsheetFontSizeDirection(value) {
4954
+ return 'grow' === value || 'shrink' === value;
4955
+ }
4956
+ function runSpreadsheetFontSizeShortcut(event, canExecute, execute, direction) {
4957
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute(direction)) return false;
4958
+ return execute(direction);
4959
+ }
4960
+ const spreadsheet_go_to_spreadsheetMaximumRows = 1048576;
4961
+ const spreadsheet_go_to_spreadsheetMaximumColumns = 16384;
4962
+ const spreadsheetGoToMaximumInputLength = 512;
4963
+ function resolveSpreadsheetGoToTarget(content, activeSheetId, input) {
4964
+ const value = input.trim().replace(/^=/, '').trim();
4965
+ if (!value) return spreadsheetGoToError('empty');
4966
+ if (value.length > spreadsheetGoToMaximumInputLength) return spreadsheetGoToError('invalid-reference');
4967
+ const activeSheet = content.sheets.find((sheet)=>sheet.id === activeSheetId);
4968
+ if (!activeSheet) return spreadsheetGoToError('sheet-not-found');
4969
+ const qualified = splitSpreadsheetQualifiedReference(value);
4970
+ if (!qualified) return spreadsheetGoToError('invalid-reference');
4971
+ const requestedSheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : activeSheet;
4972
+ if (qualified.qualifier && !requestedSheet) return spreadsheetGoToError('sheet-not-found');
4973
+ if (requestedSheet?.hide === 1) return spreadsheetGoToError('hidden-sheet');
4974
+ const direct = resolveSpreadsheetDirectRange(content, requestedSheet ?? activeSheet, qualified);
4975
+ if ('error' === direct.kind) return spreadsheetGoToError(direct.code);
4976
+ if ('match' === direct.kind) return spreadsheetGoToSuccess(direct.value, 'reference');
4977
+ const named = findSpreadsheetGoToName(content, qualified.reference, requestedSheet ?? activeSheet, Boolean(qualified.qualifier));
4978
+ if ('error' === named.kind) return spreadsheetGoToError(named.code);
4979
+ if (!named.value) return spreadsheetGoToError('invalid-reference');
4980
+ const fallbackSheet = named.value.scopeSheetId ? content.sheets.find((sheet)=>sheet.id === named.value?.scopeSheetId) : requestedSheet ?? activeSheet;
4981
+ if (!fallbackSheet) return spreadsheetGoToError('sheet-not-found');
4982
+ if (1 === fallbackSheet.hide) return spreadsheetGoToError('hidden-sheet');
4983
+ const namedReference = splitSpreadsheetQualifiedReference(named.value.reference.trim().replace(/^=/, '').trim());
4984
+ if (!namedReference) return spreadsheetGoToError('unsupported-name');
4985
+ const resolved = resolveSpreadsheetDirectRange(content, fallbackSheet, namedReference);
4986
+ if ('error' === resolved.kind) return spreadsheetGoToError(resolved.code);
4987
+ if ('match' !== resolved.kind) return spreadsheetGoToError('unsupported-name');
4988
+ return spreadsheetGoToSuccess(resolved.value, 'named-range', named.value.name);
4989
+ }
4990
+ function spreadsheetGoToValidationMessage(content, activeSheetId, input) {
4991
+ const resolution = resolveSpreadsheetGoToTarget(content, activeSheetId, input);
4992
+ return resolution.ok ? null : resolution.message;
4993
+ }
4994
+ function resolveSpreadsheetDirectRange(content, fallbackSheet, qualified) {
4995
+ const sheet = qualified.qualifier ? spreadsheetSheetByName(content, qualified.qualifier) : fallbackSheet;
4996
+ if (!sheet) return {
4997
+ kind: 'error',
4998
+ code: 'sheet-not-found'
4999
+ };
5000
+ if (1 === sheet.hide) return {
5001
+ kind: 'error',
5002
+ code: 'hidden-sheet'
5003
+ };
5004
+ const ranges = parseSpreadsheetCellRanges(qualified.reference);
5005
+ if (!ranges) return {
5006
+ kind: 'miss'
5007
+ };
5008
+ if (1 !== ranges.length) return {
5009
+ kind: 'error',
5010
+ code: 'multiple-ranges'
5011
+ };
5012
+ const range = ranges[0];
5013
+ if (!range || !spreadsheetGoToRangeIsBounded(sheet, range)) return {
5014
+ kind: 'error',
5015
+ code: 'out-of-bounds'
5016
+ };
5017
+ return {
5018
+ kind: 'match',
5019
+ value: {
5020
+ range,
5021
+ sheet
5022
+ }
5023
+ };
5024
+ }
5025
+ function findSpreadsheetGoToName(content, input, scopeSheet, qualified) {
5026
+ const normalized = input.trim().toLocaleLowerCase();
5027
+ const matching = (content.namedRanges ?? []).filter((range)=>range.name.trim().toLocaleLowerCase() === normalized);
5028
+ const scoped = matching.filter((range)=>range.scopeSheetId === scopeSheet.id);
5029
+ const workbook = matching.filter((range)=>!range.scopeSheetId);
4427
5030
  const preferred = scoped.length ? scoped : workbook;
4428
5031
  if (preferred.length > 1) return {
4429
5032
  kind: 'error',
@@ -5048,11 +5651,15 @@ function canApplySpreadsheetCellMerge(sheet, range, command) {
5048
5651
  const merged = spreadsheetMergesIntersectingRange(sheet, normalized);
5049
5652
  if ('unmerge-cells' === command || 'unmerge-and-fill' === command) return merged.length > 0;
5050
5653
  if (merged.length > 0) return false;
5654
+ if (spreadsheetMergeIntersectsTable(sheet, normalized)) return false;
5051
5655
  const rowCount = normalized.row[1] - normalized.row[0] + 1;
5052
5656
  const columnCount = normalized.column[1] - normalized.column[0] + 1;
5053
5657
  if ('merge-across' === command) return columnCount > 1;
5054
5658
  return rowCount * columnCount > 1;
5055
5659
  }
5660
+ function spreadsheetMergeIntersectsTable(sheet, range) {
5661
+ return (sheet.tables ?? []).some((table)=>table.range.row[0] <= range.row[1] && table.range.row[1] >= range.row[0] && table.range.column[0] <= range.column[1] && table.range.column[1] >= range.column[0]);
5662
+ }
5056
5663
  function spreadsheetCellMergeApiCalls(sheet, sheetId, range, command) {
5057
5664
  if (!canApplySpreadsheetCellMerge(sheet, range, command) || !sheet) return [];
5058
5665
  const normalized = normalizeSpreadsheetCellMergeRange(range);
@@ -5943,54 +6550,946 @@ function orderedSpreadsheetSheets(sheets) {
5943
6550
  index
5944
6551
  })).sort((left, right)=>(left.sheet.order ?? left.index) - (right.sheet.order ?? right.index) || left.index - right.index).map(({ sheet })=>sheet);
5945
6552
  }
5946
- function nextSpreadsheetSheetIdentity(sheets) {
5947
- const ids = new Set(sheets.map((sheet)=>sheet.id));
5948
- const names = new Set(sheets.map((sheet)=>sheet.name));
5949
- let ordinal = sheets.length + 1;
5950
- while(ids.has(`sheet-${ordinal}`) || names.has(`工作表 ${ordinal}`))ordinal += 1;
6553
+ function nextSpreadsheetSheetIdentity(sheets) {
6554
+ const ids = new Set(sheets.map((sheet)=>sheet.id));
6555
+ const names = new Set(sheets.map((sheet)=>sheet.name));
6556
+ let ordinal = sheets.length + 1;
6557
+ while(ids.has(`sheet-${ordinal}`) || names.has(`工作表 ${ordinal}`))ordinal += 1;
6558
+ return {
6559
+ id: `sheet-${ordinal}`,
6560
+ name: `工作表 ${ordinal}`
6561
+ };
6562
+ }
6563
+ function uniqueSpreadsheetSheetName(sheets, preferred) {
6564
+ const names = new Set(sheets.map((sheet)=>sheet.name));
6565
+ if (!names.has(preferred)) return preferred;
6566
+ let suffix = 2;
6567
+ while(names.has(`${preferred} ${suffix}`))suffix += 1;
6568
+ return `${preferred} ${suffix}`;
6569
+ }
6570
+ function spreadsheetSelectionAfterStructureDeletion(selection, axis, extent, start, end) {
6571
+ const deletedCount = Math.max(1, end - start + 1);
6572
+ const remainingExtent = Math.max(1, extent - deletedCount);
6573
+ const focus = Math.min(Math.max(0, start), remainingExtent - 1);
6574
+ if ('row' === axis) return {
6575
+ ...selection,
6576
+ row: [
6577
+ focus,
6578
+ focus
6579
+ ],
6580
+ column: [
6581
+ ...selection.column
6582
+ ],
6583
+ ...void 0 === selection.row_focus ? {} : {
6584
+ row_focus: focus
6585
+ }
6586
+ };
6587
+ return {
6588
+ ...selection,
6589
+ row: [
6590
+ ...selection.row
6591
+ ],
6592
+ column: [
6593
+ focus,
6594
+ focus
6595
+ ],
6596
+ ...void 0 === selection.column_focus ? {} : {
6597
+ column_focus: focus
6598
+ }
6599
+ };
6600
+ }
6601
+ function spreadsheetAutoFilterRange(sheet, selection) {
6602
+ const range = spreadsheetSelectionOrCurrentRegion(sheet, selection);
6603
+ if (!range || !validSpreadsheetAutoFilterRange(sheet, range)) return null;
6604
+ return range;
6605
+ }
6606
+ function spreadsheetSelectionOrCurrentRegion(sheet, selection) {
6607
+ if (sheet.isPivotTable || sheet.pivotTables?.length) return null;
6608
+ const normalized = normalizeAutoFilterSelection(selection);
6609
+ return normalized.row[0] !== normalized.row[1] ? normalized : spreadsheetCurrentRegion(sheet, normalized);
6610
+ }
6611
+ function spreadsheetAutoFilterHeaderColumn(sheet, selection) {
6612
+ const range = normalizedFilterSelection(sheet?.filter_select);
6613
+ if (!sheet || !range) return null;
6614
+ const row = spreadsheet_auto_filter_finiteIndex(selection.row_focus, selection.row[0]);
6615
+ const column = spreadsheet_auto_filter_finiteIndex(selection.column_focus, selection.column[0]);
6616
+ if (row !== range.row[0] || column < range.column[0] || column > range.column[1]) return null;
6617
+ return column;
6618
+ }
6619
+ function toggleSpreadsheetAutoFilter(content, sheetId, selection) {
6620
+ const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
6621
+ const sheet = content.sheets[sheetIndex];
6622
+ if (!sheet) return null;
6623
+ const nextSheet = sheet.filter_select ? spreadsheetSheetWithoutAutoFilter(sheet) : spreadsheetSheetWithAutoFilter(sheet, selection);
6624
+ if (!nextSheet) return null;
6625
+ const sheets = [
6626
+ ...content.sheets
6627
+ ];
6628
+ sheets[sheetIndex] = {
6629
+ ...nextSheet,
6630
+ luckysheet_select_save: [
6631
+ finiteSpreadsheetSelection(selection)
6632
+ ]
6633
+ };
6634
+ return {
6635
+ ...content,
6636
+ sheets
6637
+ };
6638
+ }
6639
+ function spreadsheetSheetWithAutoFilter(sheet, selection) {
6640
+ const range = spreadsheetAutoFilterRange(sheet, selection);
6641
+ if (!range) return null;
6642
+ return {
6643
+ ...sheet,
6644
+ filter: {},
6645
+ filter_select: {
6646
+ row: [
6647
+ ...range.row
6648
+ ],
6649
+ column: [
6650
+ ...range.column
6651
+ ]
6652
+ }
6653
+ };
6654
+ }
6655
+ function spreadsheetSheetWithoutAutoFilter(sheet) {
6656
+ const hiddenByFilter = spreadsheetFilterHiddenRows(sheet.filter);
6657
+ const rowhidden = {
6658
+ ...sheet.config?.rowhidden
6659
+ };
6660
+ for (const row of hiddenByFilter)delete rowhidden[row];
6661
+ const config = sheet.config ? {
6662
+ ...sheet.config,
6663
+ rowhidden
6664
+ } : void 0;
6665
+ const { filter: _filter, filter_select: _selection, ...remaining } = sheet;
6666
+ return {
6667
+ ...remaining,
6668
+ config
6669
+ };
6670
+ }
6671
+ function spreadsheetFilterHiddenRows(filter) {
6672
+ const rows = new Set();
6673
+ for (const value of Object.values(filter ?? {})){
6674
+ if (!value || 'object' != typeof value) continue;
6675
+ const rowhidden = value.rowhidden;
6676
+ if (rowhidden && 'object' == typeof rowhidden) for (const row of Object.keys(rowhidden))rows.add(row);
6677
+ }
6678
+ return rows;
6679
+ }
6680
+ function spreadsheetCurrentRegion(sheet, selection) {
6681
+ const cells = spreadsheetCellReader(sheet);
6682
+ const bounds = spreadsheetUsedBounds(sheet);
6683
+ let startRow = selection.row[0];
6684
+ let endRow = selection.row[1];
6685
+ let startColumn = selection.column[0];
6686
+ let endColumn = selection.column[1];
6687
+ if (startRow > bounds.lastRow || startColumn > bounds.lastColumn || !spreadsheet_auto_filter_spreadsheetCellHasContent(cells(startRow, startColumn))) return null;
6688
+ let changed = true;
6689
+ while(changed){
6690
+ changed = false;
6691
+ while(startColumn > 0 && spreadsheetColumnHasContent(cells, startColumn - 1, startRow, endRow)){
6692
+ startColumn -= 1;
6693
+ changed = true;
6694
+ }
6695
+ while(endColumn < bounds.lastColumn && spreadsheetColumnHasContent(cells, endColumn + 1, startRow, endRow)){
6696
+ endColumn += 1;
6697
+ changed = true;
6698
+ }
6699
+ while(startRow > 0 && spreadsheetRowHasContent(cells, startRow - 1, startColumn, endColumn)){
6700
+ startRow -= 1;
6701
+ changed = true;
6702
+ }
6703
+ while(endRow < bounds.lastRow && spreadsheetRowHasContent(cells, endRow + 1, startColumn, endColumn)){
6704
+ endRow += 1;
6705
+ changed = true;
6706
+ }
6707
+ }
6708
+ return {
6709
+ row: [
6710
+ startRow,
6711
+ endRow
6712
+ ],
6713
+ column: [
6714
+ startColumn,
6715
+ endColumn
6716
+ ]
6717
+ };
6718
+ }
6719
+ function validSpreadsheetAutoFilterRange(sheet, range) {
6720
+ if (range.row[1] <= range.row[0]) return false;
6721
+ if (spreadsheetAutoFilterIntersectsTable(sheet, range)) return false;
6722
+ const cells = spreadsheetCellReader(sheet);
6723
+ if (!spreadsheetRowHasContent(cells, range.row[0], range.column[0], range.column[1])) return false;
6724
+ let dataFound = false;
6725
+ for(let row = range.row[0] + 1; row <= range.row[1]; row += 1)if (spreadsheetRowHasContent(cells, row, range.column[0], range.column[1])) {
6726
+ dataFound = true;
6727
+ break;
6728
+ }
6729
+ return dataFound && !spreadsheetRangeIntersectsMerge(sheet, range);
6730
+ }
6731
+ function spreadsheetAutoFilterIntersectsTable(sheet, range) {
6732
+ return (sheet.tables ?? []).some((table)=>table.range.row[0] <= range.row[1] && table.range.row[1] >= range.row[0] && table.range.column[0] <= range.column[1] && table.range.column[1] >= range.column[0]);
6733
+ }
6734
+ function spreadsheetRangeIntersectsMerge(sheet, range) {
6735
+ return Object.values(sheet.config?.merge ?? {}).some((merge)=>{
6736
+ const endRow = merge.r + Math.max(merge.rs, 1) - 1;
6737
+ const endColumn = merge.c + Math.max(merge.cs, 1) - 1;
6738
+ return merge.r <= range.row[1] && endRow >= range.row[0] && merge.c <= range.column[1] && endColumn >= range.column[0];
6739
+ });
6740
+ }
6741
+ function spreadsheetRowHasContent(cellAt, row, startColumn, endColumn) {
6742
+ for(let column = startColumn; column <= endColumn; column += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
6743
+ return false;
6744
+ }
6745
+ function spreadsheetColumnHasContent(cellAt, column, startRow, endRow) {
6746
+ for(let row = startRow; row <= endRow; row += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
6747
+ return false;
6748
+ }
6749
+ function spreadsheet_auto_filter_spreadsheetCellHasContent(cell) {
6750
+ if (!cell) return false;
6751
+ return [
6752
+ cell.v,
6753
+ cell.m,
6754
+ cell.f
6755
+ ].some((value)=>null != value && '' !== value);
6756
+ }
6757
+ function spreadsheetCellReader(sheet) {
6758
+ if (sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
6759
+ const cells = new Map((sheet.celldata ?? []).map((cell)=>[
6760
+ `${cell.r}:${cell.c}`,
6761
+ cell.v
6762
+ ]));
6763
+ return (row, column)=>cells.get(`${row}:${column}`) ?? null;
6764
+ }
6765
+ function spreadsheetUsedBounds(sheet) {
6766
+ let lastRow = Math.max((sheet.data?.length ?? 1) - 1, 0);
6767
+ let lastColumn = Math.max(sparseMatrixColumnCount(sheet.data) - 1, 0);
6768
+ for (const cell of sheet.celldata ?? []){
6769
+ lastRow = Math.max(lastRow, cell.r);
6770
+ lastColumn = Math.max(lastColumn, cell.c);
6771
+ }
6772
+ return {
6773
+ lastColumn,
6774
+ lastRow
6775
+ };
6776
+ }
6777
+ function normalizeAutoFilterSelection(selection) {
6778
+ const startRow = spreadsheet_auto_filter_finiteIndex(selection.row[0], 0);
6779
+ const endRow = spreadsheet_auto_filter_finiteIndex(selection.row[1], startRow);
6780
+ const startColumn = spreadsheet_auto_filter_finiteIndex(selection.column[0], 0);
6781
+ const endColumn = spreadsheet_auto_filter_finiteIndex(selection.column[1], startColumn);
6782
+ return {
6783
+ row: [
6784
+ Math.min(startRow, endRow),
6785
+ Math.max(startRow, endRow)
6786
+ ],
6787
+ column: [
6788
+ Math.min(startColumn, endColumn),
6789
+ Math.max(startColumn, endColumn)
6790
+ ]
6791
+ };
6792
+ }
6793
+ function normalizedFilterSelection(selection) {
6794
+ if (!selection?.row?.length || !selection.column?.length) return null;
6795
+ return normalizeAutoFilterSelection(selection);
6796
+ }
6797
+ function spreadsheet_auto_filter_finiteIndex(value, fallback) {
6798
+ return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
6799
+ }
6800
+ const ACCENTS = [
6801
+ '#64748b',
6802
+ '#2563eb',
6803
+ '#0f766e',
6804
+ '#7c3aed',
6805
+ '#c2410c',
6806
+ '#be123c',
6807
+ '#15803d'
6808
+ ];
6809
+ const DARK_ACCENTS = [
6810
+ '#1e293b',
6811
+ '#1e3a8a',
6812
+ '#134e4a',
6813
+ '#4c1d95',
6814
+ '#7c2d12',
6815
+ '#881337',
6816
+ '#14532d',
6817
+ '#0f172a',
6818
+ '#312e81',
6819
+ '#3f3f46',
6820
+ '#422006'
6821
+ ];
6822
+ let cachedChoices = null;
6823
+ function spreadsheetTableStyleChoices() {
6824
+ cachedChoices ??= Object.freeze([
6825
+ ...tableStyleFamilyChoices('light', 21),
6826
+ ...tableStyleFamilyChoices('medium', 28),
6827
+ ...tableStyleFamilyChoices('dark', 11)
6828
+ ]);
6829
+ return cachedChoices;
6830
+ }
6831
+ function spreadsheetTableStylePalette(style) {
6832
+ if ('none' === style.family) return null;
6833
+ const maximum = 'light' === style.family ? 21 : 'medium' === style.family ? 28 : 11;
6834
+ if (!Number.isInteger(style.number) || style.number < 1 || style.number > maximum) return null;
6835
+ if ('light' === style.family) return lightPalette(style.number);
6836
+ if ('medium' === style.family) return mediumPalette(style.number);
6837
+ return darkPalette(style.number);
6838
+ }
6839
+ function createSpreadsheetTableRenderResolver(tables) {
6840
+ const renderable = tables.filter((table)=>spreadsheetTableStylePalette(table.style)).slice().sort((left, right)=>left.range.row[0] - right.range.row[0] || left.range.column[0] - right.range.column[0]);
6841
+ let cachedRow = -1;
6842
+ let rowTables = [];
6843
+ return (row, column)=>{
6844
+ if (row !== cachedRow) {
6845
+ cachedRow = row;
6846
+ rowTables = renderable.filter((table)=>row >= table.range.row[0] && row <= table.range.row[1]);
6847
+ }
6848
+ const table = rowTables.find((candidate)=>column >= candidate.range.column[0] && column <= candidate.range.column[1]);
6849
+ return table ? spreadsheetTableCellRenderStyle(table, row, column) : null;
6850
+ };
6851
+ }
6852
+ function spreadsheetTableCellRenderStyle(table, row, column) {
6853
+ const palette = spreadsheetTableStylePalette(table.style);
6854
+ if (!palette) return null;
6855
+ const header = table.headerRow && row === table.range.row[0];
6856
+ const totals = table.totalsRow && row === table.range.row[1];
6857
+ const dataStart = table.range.row[0] + Number(table.headerRow);
6858
+ const dataIndex = Math.max(0, row - dataStart);
6859
+ const secondary = table.showRowStripes && dataIndex % 2 === 1;
6860
+ let background = header ? palette.header : totals ? palette.total : secondary ? palette.secondaryRow : palette.primaryRow;
6861
+ if (!header && !totals && table.showColumnStripes && (column - table.range.column[0]) % 2 === 1) background = mixHex(background, palette.stripeColumn, 0.42);
6862
+ const firstColumn = column === table.range.column[0];
6863
+ const lastColumn = column === table.range.column[1];
6864
+ return {
6865
+ background,
6866
+ bold: header || totals || table.showFirstColumn && firstColumn || table.showLastColumn && lastColumn,
6867
+ borderColor: palette.border,
6868
+ role: header ? 'header' : totals ? 'totals' : 'body',
6869
+ tableId: table.id,
6870
+ textColor: header ? palette.headerText : totals ? palette.totalText : palette.text
6871
+ };
6872
+ }
6873
+ function tableStyleFamilyChoices(family, count) {
6874
+ return Array.from({
6875
+ length: count
6876
+ }, (_, index)=>{
6877
+ const number = index + 1;
6878
+ const style = {
6879
+ family,
6880
+ number
6881
+ };
6882
+ return {
6883
+ label: `${familyLabel(family)} ${number}`,
6884
+ ooxmlName: `TableStyle${capitalize(family)}${number}`,
6885
+ palette: spreadsheetTableStylePalette(style),
6886
+ style
6887
+ };
6888
+ });
6889
+ }
6890
+ function lightPalette(number) {
6891
+ const accent = ACCENTS[(number - 1) % ACCENTS.length] ?? ACCENTS[0];
6892
+ const variant = Math.floor((number - 1) / ACCENTS.length);
6893
+ const headerWeight = [
6894
+ 0.84,
6895
+ 0.73,
6896
+ 0.62
6897
+ ][variant] ?? 0.72;
6898
+ return {
6899
+ border: mixHex(accent, '#ffffff', 0.5),
6900
+ header: mixHex(accent, '#ffffff', headerWeight),
6901
+ headerText: mixHex(accent, '#0f172a', 0.38),
6902
+ primaryRow: '#ffffff',
6903
+ secondaryRow: mixHex(accent, '#ffffff', 0.9 - 0.025 * variant),
6904
+ stripeColumn: mixHex(accent, '#ffffff', 0.82),
6905
+ text: '#1f2937',
6906
+ total: mixHex(accent, '#ffffff', 0.78),
6907
+ totalText: '#172033'
6908
+ };
6909
+ }
6910
+ function mediumPalette(number) {
6911
+ const accent = ACCENTS[(number - 1) % ACCENTS.length] ?? ACCENTS[0];
6912
+ const variant = Math.floor((number - 1) / ACCENTS.length);
6913
+ const header = mixHex(accent, '#111827', 0.08 * variant);
6914
+ return {
6915
+ border: mixHex(accent, '#ffffff', 0.34),
6916
+ header,
6917
+ headerText: '#ffffff',
6918
+ primaryRow: '#ffffff',
6919
+ secondaryRow: mixHex(accent, '#ffffff', 0.86 - 0.02 * variant),
6920
+ stripeColumn: mixHex(accent, '#ffffff', 0.76),
6921
+ text: '#172033',
6922
+ total: mixHex(accent, '#ffffff', 0.72),
6923
+ totalText: mixHex(accent, '#111827', 0.48)
6924
+ };
6925
+ }
6926
+ function darkPalette(number) {
6927
+ const accent = DARK_ACCENTS[number - 1] ?? DARK_ACCENTS[0];
6928
+ return {
6929
+ border: mixHex(accent, '#ffffff', 0.32),
6930
+ header: accent,
6931
+ headerText: '#ffffff',
6932
+ primaryRow: mixHex(accent, '#ffffff', 0.9),
6933
+ secondaryRow: mixHex(accent, '#ffffff', 0.78),
6934
+ stripeColumn: mixHex(accent, '#ffffff', 0.68),
6935
+ text: '#172033',
6936
+ total: mixHex(accent, '#ffffff', 0.62),
6937
+ totalText: mixHex(accent, '#111827', 0.34)
6938
+ };
6939
+ }
6940
+ function mixHex(left, right, rightWeight) {
6941
+ const weight = Math.min(1, Math.max(0, rightWeight));
6942
+ const leftRgb = parseHex(left);
6943
+ const rightRgb = parseHex(right);
6944
+ return `#${leftRgb.map((value, index)=>Math.round(value * (1 - weight) + (rightRgb[index] ?? value) * weight).toString(16).padStart(2, '0')).join('')}`;
6945
+ }
6946
+ function parseHex(value) {
6947
+ const hex = value.replace(/^#/, '');
6948
+ return [
6949
+ 0,
6950
+ 2,
6951
+ 4
6952
+ ].map((offset)=>Number.parseInt(hex.slice(offset, offset + 2), 16));
6953
+ }
6954
+ function familyLabel(family) {
6955
+ if ('light' === family) return '浅色';
6956
+ if ('medium' === family) return '中等';
6957
+ return '深色';
6958
+ }
6959
+ function capitalize(value) {
6960
+ return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
6961
+ }
6962
+ const MAX_TABLE_CONVERSION_DENSE_FOOTPRINT = 1000000;
6963
+ function canMaterializeSpreadsheetTableAppearance(table) {
6964
+ return 'none' === table.style.family || spreadsheet_cell_range_spreadsheetCellRangeArea(table.range) <= 100000;
6965
+ }
6966
+ function materializeSpreadsheetTableAppearance(content, sheetId, table) {
6967
+ if (!canMaterializeSpreadsheetTableAppearance(table)) return null;
6968
+ if ('none' === table.style.family) return content;
6969
+ const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
6970
+ const sheet = content.sheets[sheetIndex];
6971
+ const palette = spreadsheetTableStylePalette(table.style);
6972
+ if (!sheet || !palette) return null;
6973
+ const resolve = createSpreadsheetTableRenderResolver([
6974
+ table
6975
+ ]);
6976
+ const styledSheet = shouldMaterializeSpreadsheetTableAsSparseCells(sheet, table) ? materializeSpreadsheetTableSparseCells(sheet, table, resolve) : materializeSpreadsheetTableMatrix(sheet, table, resolve);
6977
+ const sheets = [
6978
+ ...content.sheets
6979
+ ];
6980
+ sheets[sheetIndex] = styledSheet;
6981
+ return setSpreadsheetCellBorders({
6982
+ ...content,
6983
+ sheets
6984
+ }, sheetId, table.range, {
6985
+ target: 'all',
6986
+ color: palette.border,
6987
+ style: 'thin'
6988
+ });
6989
+ }
6990
+ function shouldMaterializeSpreadsheetTableAsSparseCells(sheet, table) {
6991
+ if (void 0 === sheet.data) return true;
6992
+ const [startRow, endRow] = table.range.row;
6993
+ const requiredRowLength = table.range.column[1] + 1;
6994
+ let footprint = 0;
6995
+ let presentTableRows = 0;
6996
+ for (const [rowIndex, row] of sparseArrayEntries(sheet.data)){
6997
+ if (rowIndex >= startRow && rowIndex <= endRow) {
6998
+ presentTableRows += 1;
6999
+ footprint += Math.max(row.length, requiredRowLength);
7000
+ } else footprint += row.length;
7001
+ if (footprint > MAX_TABLE_CONVERSION_DENSE_FOOTPRINT) return true;
7002
+ }
7003
+ footprint += (endRow - startRow + 1 - presentTableRows) * requiredRowLength;
7004
+ return footprint > MAX_TABLE_CONVERSION_DENSE_FOOTPRINT;
7005
+ }
7006
+ function materializeSpreadsheetTableMatrix(sheet, table, resolve) {
7007
+ const source = sheet.data ?? [];
7008
+ const data = [];
7009
+ data.length = Math.max(source.length, table.range.row[1] + 1);
7010
+ for (const [row, value] of sparseArrayEntries(source))data[row] = value;
7011
+ for(let row = table.range.row[0]; row <= table.range.row[1]; row += 1){
7012
+ const sourceRow = source[row];
7013
+ const values = [];
7014
+ values.length = Math.max(sourceRow?.length ?? 0, table.range.column[1] + 1);
7015
+ for (const column of sparseArrayIndexes(sourceRow))values[column] = sourceRow?.[column];
7016
+ for(let column = table.range.column[0]; column <= table.range.column[1]; column += 1){
7017
+ const style = resolve(row, column);
7018
+ if (style) values[column] = spreadsheetCellWithTableAppearance(values[column], style);
7019
+ }
7020
+ data[row] = values;
7021
+ }
7022
+ return {
7023
+ ...sheet,
7024
+ data
7025
+ };
7026
+ }
7027
+ function materializeSpreadsheetTableSparseCells(sheet, table, resolve) {
7028
+ const celldata = [];
7029
+ if (void 0 !== sheet.data) for (const [row, values] of sparseArrayEntries(sheet.data))for (const column of sparseArrayIndexes(values)){
7030
+ const cell = values[column];
7031
+ if (null != cell) celldata.push({
7032
+ c: column,
7033
+ r: row,
7034
+ v: cell
7035
+ });
7036
+ }
7037
+ else celldata.push(...sheet.celldata ?? []);
7038
+ const indexes = new Map(celldata.map((entry, index)=>[
7039
+ `${entry.r}:${entry.c}`,
7040
+ index
7041
+ ]));
7042
+ for(let row = table.range.row[0]; row <= table.range.row[1]; row += 1)for(let column = table.range.column[0]; column <= table.range.column[1]; column += 1){
7043
+ const style = resolve(row, column);
7044
+ if (!style) continue;
7045
+ const key = `${row}:${column}`;
7046
+ const index = indexes.get(key);
7047
+ const cell = spreadsheetCellWithTableAppearance(void 0 === index ? void 0 : celldata[index]?.v, style);
7048
+ if (void 0 === index) {
7049
+ indexes.set(key, celldata.length);
7050
+ celldata.push({
7051
+ c: column,
7052
+ r: row,
7053
+ v: cell
7054
+ });
7055
+ } else celldata[index] = {
7056
+ c: column,
7057
+ r: row,
7058
+ v: cell
7059
+ };
7060
+ }
7061
+ celldata.sort((left, right)=>left.r - right.r || left.c - right.c);
7062
+ const { data: _data, ...metadata } = sheet;
7063
+ return {
7064
+ ...metadata,
7065
+ celldata
7066
+ };
7067
+ }
7068
+ function spreadsheetCellWithTableAppearance(cell, style) {
7069
+ return {
7070
+ ...cell ?? {},
7071
+ bg: style.background,
7072
+ fc: style.textColor,
7073
+ ...style.bold ? {
7074
+ bl: 1
7075
+ } : {}
7076
+ };
7077
+ }
7078
+ function createSpreadsheetTableDialogSource(content, target) {
7079
+ const sheet = content.sheets.find((candidate)=>candidate.id === target.sheetId);
7080
+ if (!sheet) return null;
7081
+ const range = spreadsheetSelectionOrCurrentRegion(sheet, target.selection);
7082
+ if (!range) return null;
7083
+ let request = {
7084
+ headerRow: true,
7085
+ name: nextSpreadsheetTableName(content),
7086
+ range,
7087
+ sheetId: target.sheetId
7088
+ };
7089
+ if (!validateSpreadsheetTableRequest(content, request).ok) {
7090
+ request = {
7091
+ ...request,
7092
+ headerRow: false
7093
+ };
7094
+ if (!validateSpreadsheetTableRequest(content, request).ok) return null;
7095
+ }
7096
+ const rangeReference = formatSpreadsheetCellRanges([
7097
+ range
7098
+ ]);
7099
+ return {
7100
+ name: request.name,
7101
+ range,
7102
+ rangeReference,
7103
+ sheetId: target.sheetId,
7104
+ sheetName: sheet.name,
7105
+ value: {
7106
+ headerRow: request.headerRow,
7107
+ rangeReference
7108
+ }
7109
+ };
7110
+ }
7111
+ function spreadsheetTableRangeFromText(value) {
7112
+ const ranges = parseSpreadsheetCellRanges(value);
7113
+ return ranges?.length === 1 ? ranges[0] ?? null : null;
7114
+ }
7115
+ function validateSpreadsheetTableRequest(content, request) {
7116
+ const sheet = content.sheets.find((candidate)=>candidate.id === request.sheetId);
7117
+ if (!sheet) return tableError('sheet-not-found');
7118
+ if (sheet.isPivotTable || sheet.pivotTables?.length) return tableError('pivot-table');
7119
+ const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(request.range);
7120
+ if (!range) return tableError('invalid-range');
7121
+ const name = request.name.trim();
7122
+ if (!validSpreadsheetTableName(name)) return tableError('invalid-name');
7123
+ if (spreadsheetTableNameExists(content, name)) return tableError('name-conflict');
7124
+ const bounds = spreadsheetSheetBounds(sheet);
7125
+ if (range.row[1] > bounds.lastRow || range.column[1] > bounds.lastColumn) return tableError('out-of-bounds');
7126
+ const height = range.row[1] - range.row[0] + 1;
7127
+ if (height <= Number(request.headerRow)) return tableError('invalid-range');
7128
+ if (spreadsheet_cell_range_spreadsheetCellRangeArea(range) > 100000) return tableError('range-too-large');
7129
+ if (spreadsheetTableIntersectsMerge(sheet, range)) return tableError('merged-range');
7130
+ if ((sheet.tables ?? []).some((table)=>spreadsheetCellRangesIntersect(table.range, range))) return tableError('table-overlap');
7131
+ const autoFilter = spreadsheet_cell_range_normalizeSpreadsheetCellRange(sheet.filter_select ?? {
7132
+ row: [],
7133
+ column: []
7134
+ });
7135
+ if (autoFilter && spreadsheetCellRangesIntersect(autoFilter, range)) return tableError('auto-filter-overlap');
7136
+ if (!canMutateSpreadsheetCellRanges(sheet, [
7137
+ range
7138
+ ])) return tableError('protected-range');
7139
+ const style = request.style ?? {
7140
+ family: 'medium',
7141
+ number: 2
7142
+ };
7143
+ if (!validSpreadsheetTableStyle(style)) return tableError('invalid-style');
7144
+ const columns = spreadsheetTableColumnNames(sheet, range, request.headerRow);
7145
+ if (!columns.length || columns.some((column)=>!validTableColumnName(column))) return tableError('invalid-column-name');
7146
+ return {
7147
+ columns,
7148
+ name,
7149
+ ok: true,
7150
+ range,
7151
+ sheet
7152
+ };
7153
+ }
7154
+ function applySpreadsheetTable(content, request) {
7155
+ const validation = validateSpreadsheetTableRequest(content, request);
7156
+ if (!validation.ok) return null;
7157
+ const style = request.style ?? {
7158
+ family: 'medium',
7159
+ number: 2
7160
+ };
7161
+ const table = {
7162
+ id: createWorkId('spreadsheet-table'),
7163
+ name: validation.name,
7164
+ range: cloneRange(validation.range),
7165
+ columns: validation.columns.map((name)=>({
7166
+ name
7167
+ })),
7168
+ filters: [],
7169
+ headerRow: request.headerRow,
7170
+ totalsRow: false,
7171
+ style,
7172
+ showFirstColumn: false,
7173
+ showLastColumn: false,
7174
+ showRowStripes: 'none' !== style.family,
7175
+ showColumnStripes: false
7176
+ };
7177
+ const nextSheet = request.headerRow ? stampSpreadsheetTableHeaders(validation.sheet, validation.range, validation.columns) : validation.sheet;
7178
+ return replaceSpreadsheetTableSheet(content, {
7179
+ ...nextSheet,
7180
+ tables: [
7181
+ ...nextSheet.tables ?? [],
7182
+ table
7183
+ ]
7184
+ });
7185
+ }
7186
+ function updateSpreadsheetTable(content, sheetId, tableId, patch) {
7187
+ const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
7188
+ const table = sheet?.tables?.find((candidate)=>candidate.id === tableId);
7189
+ if (!sheet || !table) return null;
7190
+ const name = patch.name?.trim() ?? table.name;
7191
+ if (!validSpreadsheetTableName(name) || spreadsheetTableNameExists(content, name, tableId)) return null;
7192
+ const style = patch.style ?? table.style;
7193
+ if (!validSpreadsheetTableStyle(style)) return null;
7194
+ const showFirstColumn = patch.showFirstColumn ?? table.showFirstColumn;
7195
+ const showLastColumn = patch.showLastColumn ?? table.showLastColumn;
7196
+ const showRowStripes = patch.showRowStripes ?? table.showRowStripes;
7197
+ const showColumnStripes = patch.showColumnStripes ?? table.showColumnStripes;
7198
+ if ('none' === style.family && (showFirstColumn || showLastColumn || showRowStripes || showColumnStripes)) return null;
7199
+ const tables = sheet.tables?.map((candidate)=>candidate.id === tableId ? {
7200
+ ...candidate,
7201
+ name,
7202
+ ...void 0 === patch.name ? {} : {
7203
+ displayName: void 0
7204
+ },
7205
+ style,
7206
+ showFirstColumn,
7207
+ showLastColumn,
7208
+ showRowStripes,
7209
+ showColumnStripes
7210
+ } : candidate);
7211
+ return replaceSpreadsheetTableSheet(content, {
7212
+ ...sheet,
7213
+ tables
7214
+ });
7215
+ }
7216
+ function convertSpreadsheetTableToRange(content, sheetId, tableId) {
7217
+ const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
7218
+ const table = sheet?.tables?.find((candidate)=>candidate.id === tableId);
7219
+ if (!sheet || !table || spreadsheetTableHasStructuredReferences(content, table)) return null;
7220
+ const remaining = sheet.tables?.filter((candidate)=>candidate.id !== tableId);
7221
+ const withoutTable = replaceSpreadsheetTableSheet(content, {
7222
+ ...sheet,
7223
+ tables: remaining?.length ? remaining : void 0
7224
+ });
7225
+ return materializeSpreadsheetTableAppearance(withoutTable, sheetId, table);
7226
+ }
7227
+ function spreadsheetTableAtCell(sheet, row, column) {
7228
+ if (!sheet) return null;
7229
+ return (sheet.tables ?? []).find((table)=>spreadsheetCellRangeContains(table.range, row, column)) ?? null;
7230
+ }
7231
+ function spreadsheetTableFailureMessage(content, request) {
7232
+ const validation = validateSpreadsheetTableRequest(content, request);
7233
+ return validation.ok ? null : validation.message;
7234
+ }
7235
+ function nextSpreadsheetTableName(content) {
7236
+ for(let index = 1; index <= 65536; index += 1){
7237
+ const candidate = `Table${index}`;
7238
+ if (!spreadsheetTableNameExists(content, candidate)) return candidate;
7239
+ }
7240
+ return 'Table65536';
7241
+ }
7242
+ function spreadsheetTableNameExists(content, name, exceptTableId) {
7243
+ const normalized = name.toLocaleLowerCase();
7244
+ return content.sheets.some((sheet)=>(sheet.tables ?? []).some((table)=>table.id !== exceptTableId && [
7245
+ table.name,
7246
+ table.displayName
7247
+ ].some((candidate)=>candidate?.toLocaleLowerCase() === normalized))) || (content.namedRanges ?? []).some((range)=>range.name.trim().toLocaleLowerCase() === normalized);
7248
+ }
7249
+ function validSpreadsheetTableName(name) {
7250
+ return isValidSpreadsheetDefinedName(name) && !name.startsWith('_xlnm.') && ![
7251
+ 'r',
7252
+ 'c'
7253
+ ].includes(name.toLocaleLowerCase());
7254
+ }
7255
+ function validSpreadsheetTableStyle(style) {
7256
+ if ('none' === style.family) return true;
7257
+ if (!Number.isInteger(style.number)) return false;
7258
+ if ('light' === style.family) return style.number >= 1 && style.number <= 21;
7259
+ if ('medium' === style.family) return style.number >= 1 && style.number <= 28;
7260
+ return style.number >= 1 && style.number <= 11;
7261
+ }
7262
+ function spreadsheetTableColumnNames(sheet, range, headerRow) {
7263
+ const observed = new Set();
7264
+ const names = [];
7265
+ for(let column = range.column[0]; column <= range.column[1]; column += 1){
7266
+ const raw = headerRow ? spreadsheet_table_spreadsheetCellText(sheet, range.row[0], column).trim() : '';
7267
+ const base = validTableColumnName(raw) ? raw : `Column${column - range.column[0] + 1}`;
7268
+ let candidate = base;
7269
+ let suffix = 2;
7270
+ while(observed.has(candidate.toLocaleLowerCase())){
7271
+ const suffixText = String(suffix);
7272
+ candidate = `${truncateTableColumnName(base, 255 - suffixText.length)}${suffixText}`;
7273
+ suffix += 1;
7274
+ }
7275
+ observed.add(candidate.toLocaleLowerCase());
7276
+ names.push(candidate);
7277
+ }
7278
+ return names;
7279
+ }
7280
+ function validTableColumnName(name) {
7281
+ const characters = Array.from(name);
7282
+ return name.trim() === name && characters.length >= 1 && characters.length <= 255 && !characters.some((character)=>/\p{Cc}/u.test(character) || '\uFFFE' === character || '\uFFFF' === character);
7283
+ }
7284
+ function truncateTableColumnName(value, maximum) {
7285
+ return Array.from(value).slice(0, maximum).join('');
7286
+ }
7287
+ function spreadsheet_table_spreadsheetCellText(sheet, row, column) {
7288
+ const cell = sheet.data?.[row]?.[column] ?? sheet.celldata?.find((candidate)=>candidate.r === row && candidate.c === column)?.v;
7289
+ const value = cell?.m ?? cell?.v;
7290
+ return null == value ? '' : String(value);
7291
+ }
7292
+ function stampSpreadsheetTableHeaders(sheet, range, names) {
7293
+ if (void 0 !== sheet.data) {
7294
+ const data = cloneSparseMatrixShell(sheet.data);
7295
+ const sourceRow = sheet.data[range.row[0]];
7296
+ const row = cloneSparseRow(sourceRow);
7297
+ for (const [offset, name] of names.entries()){
7298
+ const column = range.column[0] + offset;
7299
+ row[column] = stampHeaderCell(sourceRow?.[column], name);
7300
+ }
7301
+ data[range.row[0]] = row;
7302
+ return {
7303
+ ...sheet,
7304
+ data
7305
+ };
7306
+ }
7307
+ const byCoordinate = new Map((sheet.celldata ?? []).map((entry)=>[
7308
+ `${entry.r}_${entry.c}`,
7309
+ entry
7310
+ ]));
7311
+ for (const [offset, name] of names.entries()){
7312
+ const column = range.column[0] + offset;
7313
+ const key = `${range.row[0]}_${column}`;
7314
+ const source = byCoordinate.get(key);
7315
+ byCoordinate.set(key, {
7316
+ r: range.row[0],
7317
+ c: column,
7318
+ v: stampHeaderCell(source?.v, name)
7319
+ });
7320
+ }
7321
+ return {
7322
+ ...sheet,
7323
+ celldata: [
7324
+ ...byCoordinate.values()
7325
+ ]
7326
+ };
7327
+ }
7328
+ function cloneSparseMatrixShell(source) {
7329
+ const data = [];
7330
+ data.length = source.length;
7331
+ for (const [row, value] of sparseArrayEntries(source))data[row] = value;
7332
+ return data;
7333
+ }
7334
+ function cloneSparseRow(source) {
7335
+ const row = [];
7336
+ if (!source) return row;
7337
+ row.length = source.length;
7338
+ for (const column of sparseArrayIndexes(source))row[column] = source[column];
7339
+ return row;
7340
+ }
7341
+ function stampHeaderCell(source, name) {
7342
+ return {
7343
+ ...source ?? {},
7344
+ m: name,
7345
+ v: name
7346
+ };
7347
+ }
7348
+ function spreadsheetTableIntersectsMerge(sheet, range) {
7349
+ return Object.values(sheet.config?.merge ?? {}).some((merge)=>spreadsheetCellRangesIntersect(range, {
7350
+ row: [
7351
+ merge.r,
7352
+ merge.r + Math.max(1, merge.rs) - 1
7353
+ ],
7354
+ column: [
7355
+ merge.c,
7356
+ merge.c + Math.max(1, merge.cs) - 1
7357
+ ]
7358
+ }));
7359
+ }
7360
+ function replaceSpreadsheetTableSheet(content, nextSheet) {
5951
7361
  return {
5952
- id: `sheet-${ordinal}`,
5953
- name: `工作表 ${ordinal}`
7362
+ ...content,
7363
+ sheets: content.sheets.map((sheet)=>sheet.id === nextSheet.id ? nextSheet : sheet)
5954
7364
  };
5955
7365
  }
5956
- function uniqueSpreadsheetSheetName(sheets, preferred) {
5957
- const names = new Set(sheets.map((sheet)=>sheet.name));
5958
- if (!names.has(preferred)) return preferred;
5959
- let suffix = 2;
5960
- while(names.has(`${preferred} ${suffix}`))suffix += 1;
5961
- return `${preferred} ${suffix}`;
7366
+ function spreadsheetTableHasStructuredReferences(content, table) {
7367
+ const aliases = [
7368
+ table.name,
7369
+ table.displayName
7370
+ ].filter((value)=>Boolean(value));
7371
+ const patterns = aliases.map((alias)=>new RegExp(`${escapeRegExp(alias)}\\s*\\[`, 'i'));
7372
+ const matches = (value)=>'string' == typeof value && patterns.some((pattern)=>pattern.test(value));
7373
+ for (const sheet of content.sheets){
7374
+ for (const [, row] of sparseArrayEntries(sheet.data))for (const [, cell] of sparseArrayEntries(row))if (matches(cell?.f)) return true;
7375
+ for (const entry of sheet.celldata ?? [])if (matches(entry.v?.f)) return true;
7376
+ if (Object.values(sheet.formulaMetadata?.sourceFormulas ?? {}).some(matches) || matches(JSON.stringify(sheet.dataValidationRanges ?? [])) || matches(JSON.stringify(sheet.luckysheet_conditionformat_save ?? [])) || matches(JSON.stringify(sheet.charts ?? []))) return true;
7377
+ }
7378
+ return (content.namedRanges ?? []).some((range)=>matches(range.reference));
5962
7379
  }
5963
- function spreadsheetSelectionAfterStructureDeletion(selection, axis, extent, start, end) {
5964
- const deletedCount = Math.max(1, end - start + 1);
5965
- const remainingExtent = Math.max(1, extent - deletedCount);
5966
- const focus = Math.min(Math.max(0, start), remainingExtent - 1);
5967
- if ('row' === axis) return {
5968
- ...selection,
7380
+ function escapeRegExp(value) {
7381
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7382
+ }
7383
+ function cloneRange(range) {
7384
+ return {
5969
7385
  row: [
5970
- focus,
5971
- focus
7386
+ ...range.row
5972
7387
  ],
5973
7388
  column: [
5974
- ...selection.column
5975
- ],
5976
- ...void 0 === selection.row_focus ? {} : {
5977
- row_focus: focus
5978
- }
7389
+ ...range.column
7390
+ ]
7391
+ };
7392
+ }
7393
+ function tableError(code) {
7394
+ const messages = {
7395
+ 'auto-filter-overlap': '表格区域不能与工作表自动筛选区域重叠。',
7396
+ 'invalid-column-name': '表格列名必须唯一且不超过 255 个字符。',
7397
+ 'invalid-name': '表格名称必须是有效且不类似单元格引用的标识符。',
7398
+ 'invalid-range': '表格至少需要一行数据。',
7399
+ 'invalid-style': '表格样式身份无效。',
7400
+ 'merged-range': '请先取消表格区域内的合并单元格。',
7401
+ 'name-conflict': '表格名称已被其他表格或名称使用。',
7402
+ 'out-of-bounds': '表格区域超出当前工作表边界。',
7403
+ 'pivot-table': '数据透视表工作表不能创建普通表格。',
7404
+ 'protected-range': '受保护或只读区域不能创建表格。',
7405
+ 'range-too-large': `一次最多可创建 ${100000..toLocaleString()} 个单元格的表格。`,
7406
+ 'sheet-not-found': '找不到目标工作表。',
7407
+ 'table-overlap': '表格区域不能与其他表格重叠。'
5979
7408
  };
5980
7409
  return {
5981
- ...selection,
5982
- row: [
5983
- ...selection.row
5984
- ],
5985
- column: [
5986
- focus,
5987
- focus
5988
- ],
5989
- ...void 0 === selection.column_focus ? {} : {
5990
- column_focus: focus
5991
- }
7410
+ code,
7411
+ message: messages[code],
7412
+ ok: false
7413
+ };
7414
+ }
7415
+ function createSpreadsheetTableExtension() {
7416
+ return createOfficeEditorExtension({
7417
+ name: 'spreadsheetTables',
7418
+ addCommands: ()=>({
7419
+ openTable: {
7420
+ canExecute: canOpenSpreadsheetTable,
7421
+ execute: openSpreadsheetTable
7422
+ },
7423
+ applyTable: {
7424
+ canExecute: (context, request)=>context.editable && validateSpreadsheetTableRequest(context.content, request).ok,
7425
+ execute: applySpreadsheetTableCommand
7426
+ },
7427
+ updateTable: {
7428
+ canExecute: canUpdateSpreadsheetTable,
7429
+ execute: updateSpreadsheetTableCommand
7430
+ },
7431
+ convertTableToRange: {
7432
+ canExecute: canConvertSpreadsheetTableToRange,
7433
+ execute: convertSpreadsheetTableToRangeCommand
7434
+ }
7435
+ }),
7436
+ addKeyboardShortcuts: ()=>({
7437
+ [spreadsheetCommandCatalog.table.shortcut.editor["0"]]: ({ can, commands }, event)=>runSpreadsheetTableShortcut(event, can.openTable, commands.openTable)
7438
+ })
7439
+ });
7440
+ }
7441
+ function canOpenSpreadsheetTable(context) {
7442
+ const target = liveSpreadsheetTableTarget(context);
7443
+ const sheet = context.content.sheets.find((candidate)=>candidate.id === target?.sheetId);
7444
+ const focus = target?.selection;
7445
+ const row = focus?.row_focus ?? focus?.row[0] ?? 0;
7446
+ const column = focus?.column_focus ?? focus?.column[0] ?? 0;
7447
+ return Boolean(context.editable && context.table.canOpen && context.workbook && target && target.sheetId === context.activeSheetId && sheet && !sheet.isPivotTable && !sheet.pivotTables?.length && !spreadsheetTableAtCell(sheet, row, column));
7448
+ }
7449
+ function openSpreadsheetTable(context) {
7450
+ const target = liveSpreadsheetTableTarget(context);
7451
+ return Boolean(target && canOpenSpreadsheetTable(context) && context.table.open(target));
7452
+ }
7453
+ function applySpreadsheetTableCommand(context, request) {
7454
+ if (!context.editable) return false;
7455
+ const next = applySpreadsheetTable(context.content, request);
7456
+ if (!next) return false;
7457
+ context.onChange(next);
7458
+ return true;
7459
+ }
7460
+ function canUpdateSpreadsheetTable(context, sheetId, tableId, patch) {
7461
+ return Boolean(context.editable && updateSpreadsheetTable(context.content, sheetId, tableId, patch));
7462
+ }
7463
+ function updateSpreadsheetTableCommand(context, sheetId, tableId, patch) {
7464
+ if (!context.editable) return false;
7465
+ const next = updateSpreadsheetTable(context.content, sheetId, tableId, patch);
7466
+ if (!next) return false;
7467
+ context.onChange(next);
7468
+ return true;
7469
+ }
7470
+ function canConvertSpreadsheetTableToRange(context, sheetId, tableId) {
7471
+ return Boolean(context.editable && context.content.sheets.find((sheet)=>sheet.id === sheetId)?.tables?.some((table)=>table.id === tableId && canMaterializeSpreadsheetTableAppearance(table)));
7472
+ }
7473
+ function convertSpreadsheetTableToRangeCommand(context, sheetId, tableId) {
7474
+ if (!context.editable) return false;
7475
+ const next = convertSpreadsheetTableToRange(context.content, sheetId, tableId);
7476
+ if (!next) return false;
7477
+ context.onChange(next);
7478
+ return true;
7479
+ }
7480
+ function liveSpreadsheetTableTarget(context) {
7481
+ if (!context.workbook || !context.targetSheetId) return null;
7482
+ const selection = context.workbook.getSelection()?.at(-1) ?? context.fallbackRange;
7483
+ return {
7484
+ sheetId: context.targetSheetId,
7485
+ selection: finiteSpreadsheetSelection(selection)
5992
7486
  };
5993
7487
  }
7488
+ function runSpreadsheetTableShortcut(event, canExecute, execute) {
7489
+ if (event.repeat || event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target)) return false;
7490
+ if (!event.repeat && canExecute()) execute();
7491
+ return true;
7492
+ }
5994
7493
  function createSpreadsheetEditorExtensions() {
5995
7494
  return [
5996
7495
  createOfficeEditorExtension({
@@ -6007,12 +7506,14 @@ function createSpreadsheetEditorExtensions() {
6007
7506
  })
6008
7507
  }),
6009
7508
  createSpreadsheetCellFormatExtension(),
7509
+ createSpreadsheetFontSizeExtension(),
6010
7510
  createSpreadsheetCellBorderExtension(),
6011
7511
  createSpreadsheetCellStyleExtension(),
6012
7512
  createSpreadsheetNumberFormatExtension(),
6013
7513
  createSpreadsheetNavigationExtension(),
6014
7514
  createSpreadsheetDataValidationExtension(),
6015
7515
  createSpreadsheetHyperlinkExtension(),
7516
+ createSpreadsheetTableExtension(),
6016
7517
  createSpreadsheetAutoSumExtension(),
6017
7518
  createSpreadsheetCellFillExtension(),
6018
7519
  createOfficeEditorExtension({
@@ -6283,9 +7784,16 @@ function canInsertSelectedStructure(context, axis, _position) {
6283
7784
  const [start, end] = spreadsheetStructureRange(range, axis);
6284
7785
  const count = end - start + 1;
6285
7786
  const maximum = 'row' === axis ? 10000 : 1000;
6286
- return spreadsheetStructureExtent(context, axis) + count < maximum;
7787
+ return spreadsheetStructureExtent(context, axis) + count < maximum && canApplySpreadsheetTableStructureChange(context.content.sheets.find((sheet)=>sheet.id === context.targetSheetId), {
7788
+ axis,
7789
+ count,
7790
+ direction: 'before' === _position ? 'lefttop' : 'rightbottom',
7791
+ index: 'before' === _position ? start : end,
7792
+ kind: 'insert'
7793
+ });
6287
7794
  }
6288
7795
  function insertSelectedStructure(context, axis, position) {
7796
+ if (!canInsertSelectedStructure(context, axis, position)) return false;
6289
7797
  if (!context.workbook || !context.targetSheetId) return false;
6290
7798
  const [start, end] = spreadsheetStructureRange(liveRange(context), axis);
6291
7799
  const before = 'before' === position;
@@ -6301,9 +7809,15 @@ function insertSelectedStructure(context, axis, position) {
6301
7809
  function canDeleteSelectedStructure(context, axis) {
6302
7810
  if (!canEditSelectedCells(context)) return false;
6303
7811
  const [start, end] = spreadsheetStructureRange(liveRange(context), axis);
6304
- return end - start + 1 < spreadsheetStructureExtent(context, axis);
7812
+ return end - start + 1 < spreadsheetStructureExtent(context, axis) && canApplySpreadsheetTableStructureChange(context.content.sheets.find((sheet)=>sheet.id === context.targetSheetId), {
7813
+ axis,
7814
+ end,
7815
+ kind: 'delete',
7816
+ start
7817
+ });
6305
7818
  }
6306
7819
  function deleteSelectedStructure(context, axis) {
7820
+ if (!canDeleteSelectedStructure(context, axis)) return false;
6307
7821
  if (!context.workbook || !context.targetSheetId) return false;
6308
7822
  const range = liveRange(context);
6309
7823
  const [start, end] = spreadsheetStructureRange(range, axis);
@@ -7571,6 +9085,7 @@ function SpreadsheetBorderRibbon({ can, commands }) {
7571
9085
  color
7572
9086
  };
7573
9087
  const currentDefinition = spreadsheetBorderTargetOptions.find((option)=>option.target === target)?.definition;
9088
+ const currentShortcut = currentDefinition && 'shortcut' in currentDefinition ? currentDefinition.shortcut : void 0;
7574
9089
  const currentLabel = currentDefinition?.label ?? '所有框线';
7575
9090
  const styleLabel = spreadsheetBorderStyleOptions.find((option)=>option.value === style)?.label;
7576
9091
  const menuDisabled = spreadsheetBorderTargetOptions.every((option)=>!can.setSelectedCellBorders({
@@ -7593,7 +9108,8 @@ function SpreadsheetBorderRibbon({ can, commands }) {
7593
9108
  type: "button",
7594
9109
  className: "work-spreadsheet-border-primary",
7595
9110
  "aria-label": currentLabel,
7596
- title: `${currentLabel}(${styleLabel},${color.toUpperCase()})`,
9111
+ "aria-keyshortcuts": currentShortcut?.aria,
9112
+ title: `${currentLabel}(${styleLabel},${color.toUpperCase()}${currentShortcut ? `;${currentShortcut.label}` : ''})`,
7597
9113
  disabled: !can.setSelectedCellBorders(format),
7598
9114
  onClick: ()=>commands.setSelectedCellBorders(format),
7599
9115
  children: /*#__PURE__*/ jsx(SpreadsheetBorderGlyph, {
@@ -7627,11 +9143,14 @@ function SpreadsheetBorderRibbon({ can, commands }) {
7627
9143
  ...format,
7628
9144
  target: option
7629
9145
  };
9146
+ const shortcut = 'shortcut' in definition ? definition.shortcut : void 0;
7630
9147
  return /*#__PURE__*/ jsxs("button", {
7631
9148
  type: "button",
7632
9149
  role: "menuitemradio",
7633
9150
  tabIndex: 0 === index ? 0 : -1,
7634
9151
  "aria-checked": option === target,
9152
+ "aria-label": definition.label,
9153
+ "aria-keyshortcuts": shortcut?.aria,
7635
9154
  disabled: !can.setSelectedCellBorders(next),
7636
9155
  onClick: ()=>{
7637
9156
  setTarget(option);
@@ -7643,7 +9162,11 @@ function SpreadsheetBorderRibbon({ can, commands }) {
7643
9162
  target: option
7644
9163
  }),
7645
9164
  /*#__PURE__*/ jsx("span", {
9165
+ className: "work-spreadsheet-border-target-label",
7646
9166
  children: definition.label
9167
+ }),
9168
+ shortcut && /*#__PURE__*/ jsx("kbd", {
9169
+ children: shortcut.label
7647
9170
  })
7648
9171
  ]
7649
9172
  }, definition.id);
@@ -7783,6 +9306,8 @@ function SpreadsheetFontRibbonGroup({ can, commands, toolbarCell }) {
7783
9306
  const strike = 1 === Number(toolbarCell?.cl);
7784
9307
  const textColor = 'string' == typeof toolbarCell?.fc ? toolbarCell.fc : '#172033';
7785
9308
  const fillColor = 'string' == typeof toolbarCell?.bg ? toolbarCell.bg : '#ffffff';
9309
+ const growFont = spreadsheetCommandCatalog.growFont;
9310
+ const shrinkFont = spreadsheetCommandCatalog.shrinkFont;
7786
9311
  return /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
7787
9312
  label: "字体",
7788
9313
  priority: "high",
@@ -7802,6 +9327,28 @@ function SpreadsheetFontRibbonGroup({ can, commands, toolbarCell }) {
7802
9327
  options: spreadsheetFontSizeOptions(toolbarCell?.fs),
7803
9328
  onValueChange: (value)=>commands.setCellFormat('fs', Number(value))
7804
9329
  }),
9330
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
9331
+ label: growFont.label,
9332
+ title: `${growFont.label}(${growFont.shortcut.label})`,
9333
+ "aria-keyshortcuts": growFont.shortcut.aria,
9334
+ displayLabel: false,
9335
+ disabled: !can.adjustFontSize('grow'),
9336
+ onClick: ()=>commands.adjustFontSize('grow'),
9337
+ children: /*#__PURE__*/ jsx(AArrowUp, {
9338
+ size: 15
9339
+ })
9340
+ }),
9341
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
9342
+ label: shrinkFont.label,
9343
+ title: `${shrinkFont.label}(${shrinkFont.shortcut.label})`,
9344
+ "aria-keyshortcuts": shrinkFont.shortcut.aria,
9345
+ displayLabel: false,
9346
+ disabled: !can.adjustFontSize('shrink'),
9347
+ onClick: ()=>commands.adjustFontSize('shrink'),
9348
+ children: /*#__PURE__*/ jsx(AArrowDown, {
9349
+ size: 15
9350
+ })
9351
+ }),
7805
9352
  /*#__PURE__*/ jsx(SpreadsheetFontToggle, {
7806
9353
  active: bold,
7807
9354
  attribute: "bl",
@@ -8654,89 +10201,350 @@ function SpreadsheetPrintSettingsPanel({ content, onChange }) {
8654
10201
  /*#__PURE__*/ jsxs("div", {
8655
10202
  className: "actions",
8656
10203
  children: [
8657
- error && /*#__PURE__*/ jsx(InlineNotice, {
8658
- className: "work-office-form-error",
8659
- tone: "danger",
8660
- role: "alert",
8661
- children: error
8662
- }),
8663
- /*#__PURE__*/ jsx(button_Button, {
8664
- tone: "secondary",
8665
- disabled: dirty || !savedArea && !savedTitles && !savedPageBreaks && !savedPageSetup,
8666
- onClick: clearSettings,
8667
- children: "清除"
8668
- }),
8669
- /*#__PURE__*/ jsx(button_Button, {
8670
- tone: "secondary",
8671
- disabled: !dirty,
8672
- onClick: cancelDraft,
8673
- children: "取消更改"
10204
+ error && /*#__PURE__*/ jsx(InlineNotice, {
10205
+ className: "work-office-form-error",
10206
+ tone: "danger",
10207
+ role: "alert",
10208
+ children: error
10209
+ }),
10210
+ /*#__PURE__*/ jsx(button_Button, {
10211
+ tone: "secondary",
10212
+ disabled: dirty || !savedArea && !savedTitles && !savedPageBreaks && !savedPageSetup,
10213
+ onClick: clearSettings,
10214
+ children: "清除"
10215
+ }),
10216
+ /*#__PURE__*/ jsx(button_Button, {
10217
+ tone: "secondary",
10218
+ disabled: !dirty,
10219
+ onClick: cancelDraft,
10220
+ children: "取消更改"
10221
+ }),
10222
+ /*#__PURE__*/ jsx(button_Button, {
10223
+ type: "submit",
10224
+ tone: "primary",
10225
+ disabled: !sheetId || !dirty,
10226
+ children: "保存打印设置"
10227
+ })
10228
+ ]
10229
+ })
10230
+ ]
10231
+ });
10232
+ }
10233
+ function spreadsheetPrintSettingsDraft(content, sheetId, empty = false) {
10234
+ const area = empty ? void 0 : content.printAreas?.find((item)=>item.sheetId === sheetId);
10235
+ const titles = empty ? void 0 : content.printTitles?.find((item)=>item.sheetId === sheetId);
10236
+ const pageBreaks = empty ? void 0 : content.pageBreaks?.find((item)=>item.sheetId === sheetId);
10237
+ const pageSetup = empty ? void 0 : content.pageSetups?.find((item)=>item.sheetId === sheetId);
10238
+ return {
10239
+ reference: area?.reference ?? '',
10240
+ titleRows: titles?.rows ?? '',
10241
+ titleColumns: titles?.columns ?? '',
10242
+ rowPageBreaks: formatSpreadsheetRowPageBreaks(pageBreaks?.rows),
10243
+ columnPageBreaks: formatSpreadsheetColumnPageBreaks(pageBreaks?.columns),
10244
+ pageSetup: effectiveSpreadsheetPageSetup(pageSetup)
10245
+ };
10246
+ }
10247
+ function spreadsheetMaximumRow(sheet) {
10248
+ return Math.max(0, (sheet?.row ?? 1) - 1, (sheet?.data?.length ?? 1) - 1);
10249
+ }
10250
+ function spreadsheetMaximumColumn(sheet) {
10251
+ let maximum = Math.max(0, (sheet?.column ?? 1) - 1);
10252
+ for (const row of sheet?.data ?? [])maximum = Math.max(maximum, row.length - 1);
10253
+ return maximum;
10254
+ }
10255
+ function PageMarginField({ label, value, onChange }) {
10256
+ return /*#__PURE__*/ jsxs("div", {
10257
+ className: "work-office-field",
10258
+ children: [
10259
+ /*#__PURE__*/ jsx("span", {
10260
+ children: label
10261
+ }),
10262
+ /*#__PURE__*/ jsx(CommittedOfficeNumberField, {
10263
+ ariaLabel: label,
10264
+ min: 0,
10265
+ max: 100,
10266
+ step: 0.01,
10267
+ value: value,
10268
+ normalizeValue: (nextValue)=>normalizePrintDecimal(nextValue, 0, 100),
10269
+ onValueCommit: onChange
10270
+ })
10271
+ ]
10272
+ });
10273
+ }
10274
+ function normalizePrintInteger(value, minimum, maximum) {
10275
+ if (!value.trim()) return null;
10276
+ const number = Number(value);
10277
+ return Number.isFinite(number) ? Math.min(maximum, Math.max(minimum, Math.round(number))) : null;
10278
+ }
10279
+ function normalizePrintDecimal(value, minimum, maximum) {
10280
+ if (!value.trim()) return null;
10281
+ const number = Number(value);
10282
+ return Number.isFinite(number) ? Math.min(maximum, Math.max(minimum, Math.round(100 * number) / 100)) : null;
10283
+ }
10284
+ function validPageSetup(pageSetup) {
10285
+ const validInteger = (value, minimum, maximum)=>Number.isInteger(value) && value >= minimum && value <= maximum;
10286
+ return validInteger(pageSetup.scale, 10, 400) && validInteger(pageSetup.fitToWidth, 0, 32767) && validInteger(pageSetup.fitToHeight, 0, 32767) && validInteger(pageSetup.pageNumberStart, 1, 32767) && Object.values(pageSetup.margins).every((margin)=>Number.isFinite(margin) && margin >= 0 && margin <= 100);
10287
+ }
10288
+ function SpreadsheetTableDesignRibbon({ can, commands, sheetId, table }) {
10289
+ return /*#__PURE__*/ jsxs(Fragment, {
10290
+ children: [
10291
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
10292
+ label: "属性",
10293
+ priority: "high",
10294
+ children: [
10295
+ /*#__PURE__*/ jsx(SpreadsheetTableNameControl, {
10296
+ can: can,
10297
+ commands: commands,
10298
+ sheetId: sheetId,
10299
+ table: table
10300
+ }),
10301
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
10302
+ label: "转换为区域",
10303
+ disabled: !can.convertTableToRange(sheetId, table.id),
10304
+ onClick: ()=>{
10305
+ if (!commands.convertTableToRange(sheetId, table.id)) showToast('无法转换为区域。请先移除引用此表格的结构化引用。', 'error');
10306
+ },
10307
+ children: /*#__PURE__*/ jsx(TableProperties, {
10308
+ size: 19
10309
+ })
10310
+ })
10311
+ ]
10312
+ }),
10313
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
10314
+ label: "表格样式",
10315
+ priority: "high",
10316
+ children: /*#__PURE__*/ jsx(SpreadsheetTableStyleGallery, {
10317
+ commands: commands,
10318
+ sheetId: sheetId,
10319
+ table: table
10320
+ })
10321
+ }),
10322
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
10323
+ label: "表格样式选项",
10324
+ children: [
10325
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10326
+ label: "首列",
10327
+ active: table.showFirstColumn,
10328
+ icon: /*#__PURE__*/ jsx(Columns3, {
10329
+ size: 17
10330
+ }),
10331
+ disabled: !can.updateTable(sheetId, table.id, {
10332
+ showFirstColumn: !table.showFirstColumn
10333
+ }),
10334
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10335
+ showFirstColumn: !table.showFirstColumn
10336
+ })
10337
+ }),
10338
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10339
+ label: "末列",
10340
+ active: table.showLastColumn,
10341
+ icon: /*#__PURE__*/ jsx(Columns3, {
10342
+ size: 17
10343
+ }),
10344
+ disabled: !can.updateTable(sheetId, table.id, {
10345
+ showLastColumn: !table.showLastColumn
10346
+ }),
10347
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10348
+ showLastColumn: !table.showLastColumn
10349
+ })
10350
+ }),
10351
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10352
+ label: "行条纹",
10353
+ active: table.showRowStripes,
10354
+ icon: /*#__PURE__*/ jsx(Rows3, {
10355
+ size: 17
10356
+ }),
10357
+ disabled: !can.updateTable(sheetId, table.id, {
10358
+ showRowStripes: !table.showRowStripes
10359
+ }),
10360
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10361
+ showRowStripes: !table.showRowStripes
10362
+ })
10363
+ }),
10364
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10365
+ label: "列条纹",
10366
+ active: table.showColumnStripes,
10367
+ icon: /*#__PURE__*/ jsx(Columns3, {
10368
+ size: 17
10369
+ }),
10370
+ disabled: !can.updateTable(sheetId, table.id, {
10371
+ showColumnStripes: !table.showColumnStripes
10372
+ }),
10373
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10374
+ showColumnStripes: !table.showColumnStripes
10375
+ })
10376
+ })
10377
+ ]
10378
+ })
10379
+ ]
10380
+ });
10381
+ }
10382
+ function SpreadsheetTableNameControl({ can, commands, sheetId, table }) {
10383
+ const [name, setName] = useState(table.name);
10384
+ useEffect(()=>setName(table.name), [
10385
+ table.id,
10386
+ table.name
10387
+ ]);
10388
+ const commit = ()=>{
10389
+ const candidate = name.trim();
10390
+ if (!candidate || candidate === table.name) return void setName(table.name);
10391
+ const patch = {
10392
+ name: candidate
10393
+ };
10394
+ if (!can.updateTable(sheetId, table.id, patch) || !commands.updateTable(sheetId, table.id, patch)) {
10395
+ setName(table.name);
10396
+ showToast('表格名称无效或已被使用。', 'error');
10397
+ }
10398
+ };
10399
+ return /*#__PURE__*/ jsxs("label", {
10400
+ className: "work-spreadsheet-table-name",
10401
+ children: [
10402
+ /*#__PURE__*/ jsx("span", {
10403
+ children: "表格名称"
10404
+ }),
10405
+ /*#__PURE__*/ jsx("input", {
10406
+ type: "text",
10407
+ "aria-label": "表格名称",
10408
+ autoCapitalize: "none",
10409
+ spellCheck: false,
10410
+ value: name,
10411
+ onBlur: commit,
10412
+ onChange: (event)=>setName(event.currentTarget.value),
10413
+ onKeyDown: (event)=>{
10414
+ if ('Enter' === event.key) {
10415
+ event.preventDefault();
10416
+ commit();
10417
+ } else if ('Escape' === event.key) {
10418
+ event.preventDefault();
10419
+ setName(table.name);
10420
+ }
10421
+ }
10422
+ })
10423
+ ]
10424
+ });
10425
+ }
10426
+ function SpreadsheetTableStyleGallery({ commands, sheetId, table }) {
10427
+ const choices = spreadsheetTableStyleChoices();
10428
+ const selected = choices.find((choice)=>spreadsheetTableUsesStyle(table.style, choice.style));
10429
+ const families = [
10430
+ {
10431
+ id: 'light',
10432
+ label: '浅色'
10433
+ },
10434
+ {
10435
+ id: 'medium',
10436
+ label: '中等'
10437
+ },
10438
+ {
10439
+ id: 'dark',
10440
+ label: '深色'
10441
+ }
10442
+ ];
10443
+ return /*#__PURE__*/ jsx(Popover, {
10444
+ label: "表格样式",
10445
+ panelLabel: "表格样式库",
10446
+ panelRole: "menu",
10447
+ portal: true,
10448
+ className: "work-spreadsheet-table-style-root",
10449
+ panelClassName: "work-spreadsheet-table-style-menu",
10450
+ focusFirstOnOpen: true,
10451
+ onPanelKeyDown: moveSpreadsheetTableStyleFocus,
10452
+ trigger: (triggerProps, { open })=>/*#__PURE__*/ jsxs("button", {
10453
+ ...triggerProps,
10454
+ type: "button",
10455
+ className: `with-label work-spreadsheet-table-style-trigger${open ? ' active' : ''}`,
10456
+ title: `表格样式(当前:${selected?.label ?? '无'})`,
10457
+ children: [
10458
+ /*#__PURE__*/ jsx(SpreadsheetTableStylePreview, {
10459
+ choice: selected ?? choices[0]
8674
10460
  }),
8675
- /*#__PURE__*/ jsx(button_Button, {
8676
- type: "submit",
8677
- tone: "primary",
8678
- disabled: !sheetId || !dirty,
8679
- children: "保存打印设置"
10461
+ /*#__PURE__*/ jsx("span", {
10462
+ children: "表格样式"
8680
10463
  })
8681
10464
  ]
10465
+ }),
10466
+ children: (close)=>/*#__PURE__*/ jsx(Fragment, {
10467
+ children: families.map((family)=>/*#__PURE__*/ jsxs("fieldset", {
10468
+ className: "work-spreadsheet-table-style-family",
10469
+ "data-office-menu-grid": true,
10470
+ "aria-label": family.label,
10471
+ children: [
10472
+ /*#__PURE__*/ jsx("span", {
10473
+ children: family.label
10474
+ }),
10475
+ /*#__PURE__*/ jsx("div", {
10476
+ children: choices.filter((choice)=>choice.style.family === family.id).map((choice)=>{
10477
+ const checked = spreadsheetTableUsesStyle(table.style, choice.style);
10478
+ return /*#__PURE__*/ jsx("button", {
10479
+ type: "button",
10480
+ role: "menuitemradio",
10481
+ tabIndex: -1,
10482
+ "aria-label": `应用表格样式:${choice.label}`,
10483
+ "aria-checked": checked,
10484
+ title: choice.label,
10485
+ onClick: ()=>{
10486
+ close();
10487
+ commands.updateTable(sheetId, table.id, {
10488
+ style: choice.style
10489
+ });
10490
+ },
10491
+ children: /*#__PURE__*/ jsx(SpreadsheetTableStylePreview, {
10492
+ choice: choice
10493
+ })
10494
+ }, choice.ooxmlName);
10495
+ })
10496
+ })
10497
+ ]
10498
+ }, family.id))
8682
10499
  })
8683
- ]
8684
10500
  });
8685
10501
  }
8686
- function spreadsheetPrintSettingsDraft(content, sheetId, empty = false) {
8687
- const area = empty ? void 0 : content.printAreas?.find((item)=>item.sheetId === sheetId);
8688
- const titles = empty ? void 0 : content.printTitles?.find((item)=>item.sheetId === sheetId);
8689
- const pageBreaks = empty ? void 0 : content.pageBreaks?.find((item)=>item.sheetId === sheetId);
8690
- const pageSetup = empty ? void 0 : content.pageSetups?.find((item)=>item.sheetId === sheetId);
8691
- return {
8692
- reference: area?.reference ?? '',
8693
- titleRows: titles?.rows ?? '',
8694
- titleColumns: titles?.columns ?? '',
8695
- rowPageBreaks: formatSpreadsheetRowPageBreaks(pageBreaks?.rows),
8696
- columnPageBreaks: formatSpreadsheetColumnPageBreaks(pageBreaks?.columns),
8697
- pageSetup: effectiveSpreadsheetPageSetup(pageSetup)
8698
- };
8699
- }
8700
- function spreadsheetMaximumRow(sheet) {
8701
- return Math.max(0, (sheet?.row ?? 1) - 1, (sheet?.data?.length ?? 1) - 1);
8702
- }
8703
- function spreadsheetMaximumColumn(sheet) {
8704
- let maximum = Math.max(0, (sheet?.column ?? 1) - 1);
8705
- for (const row of sheet?.data ?? [])maximum = Math.max(maximum, row.length - 1);
8706
- return maximum;
8707
- }
8708
- function PageMarginField({ label, value, onChange }) {
8709
- return /*#__PURE__*/ jsxs("div", {
8710
- className: "work-office-field",
10502
+ function moveSpreadsheetTableStyleFocus(event) {
10503
+ const active = document.activeElement;
10504
+ const group = [
10505
+ ...event.currentTarget.querySelectorAll('[data-office-menu-grid]')
10506
+ ].find((candidate)=>candidate.contains(active));
10507
+ const grid = group?.querySelector(':scope > div');
10508
+ const template = grid ? getComputedStyle(grid).gridTemplateColumns : '';
10509
+ const repeated = template.match(/^repeat\(\s*(\d+)/)?.[1];
10510
+ const resolved = template.split(/\s+/).filter((value)=>value && 'none' !== value).length;
10511
+ const columns = repeated ? Number(repeated) : resolved || 7;
10512
+ return moveOfficeGridMenuFocus(event, Math.max(1, columns));
10513
+ }
10514
+ function spreadsheetTableUsesStyle(tableStyle, choice) {
10515
+ return 'none' !== tableStyle.family && tableStyle.family === choice.family && tableStyle.number === choice.number;
10516
+ }
10517
+ function SpreadsheetTableStylePreview({ choice }) {
10518
+ return /*#__PURE__*/ jsxs("span", {
10519
+ className: "work-spreadsheet-table-style-preview",
10520
+ "aria-hidden": "true",
8711
10521
  children: [
8712
- /*#__PURE__*/ jsx("span", {
8713
- children: label
10522
+ /*#__PURE__*/ jsx("i", {
10523
+ style: {
10524
+ backgroundColor: choice.palette.header
10525
+ }
8714
10526
  }),
8715
- /*#__PURE__*/ jsx(CommittedOfficeNumberField, {
8716
- ariaLabel: label,
8717
- min: 0,
8718
- max: 100,
8719
- step: 0.01,
8720
- value: value,
8721
- normalizeValue: (nextValue)=>normalizePrintDecimal(nextValue, 0, 100),
8722
- onValueCommit: onChange
10527
+ /*#__PURE__*/ jsx("i", {
10528
+ style: {
10529
+ backgroundColor: choice.palette.primaryRow
10530
+ }
10531
+ }),
10532
+ /*#__PURE__*/ jsx("i", {
10533
+ style: {
10534
+ backgroundColor: choice.palette.secondaryRow
10535
+ }
8723
10536
  })
8724
10537
  ]
8725
10538
  });
8726
10539
  }
8727
- function normalizePrintInteger(value, minimum, maximum) {
8728
- if (!value.trim()) return null;
8729
- const number = Number(value);
8730
- return Number.isFinite(number) ? Math.min(maximum, Math.max(minimum, Math.round(number))) : null;
8731
- }
8732
- function normalizePrintDecimal(value, minimum, maximum) {
8733
- if (!value.trim()) return null;
8734
- const number = Number(value);
8735
- return Number.isFinite(number) ? Math.min(maximum, Math.max(minimum, Math.round(100 * number) / 100)) : null;
8736
- }
8737
- function validPageSetup(pageSetup) {
8738
- const validInteger = (value, minimum, maximum)=>Number.isInteger(value) && value >= minimum && value <= maximum;
8739
- return validInteger(pageSetup.scale, 10, 400) && validInteger(pageSetup.fitToWidth, 0, 32767) && validInteger(pageSetup.fitToHeight, 0, 32767) && validInteger(pageSetup.pageNumberStart, 1, 32767) && Object.values(pageSetup.margins).every((margin)=>Number.isFinite(margin) && margin >= 0 && margin <= 100);
10540
+ function SpreadsheetTableOption({ active, disabled, icon, label, onToggle }) {
10541
+ return /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
10542
+ label: label,
10543
+ active: active,
10544
+ disabled: disabled,
10545
+ onClick: onToggle,
10546
+ children: icon
10547
+ });
8740
10548
  }
8741
10549
  const defaultSpreadsheetFreezePanesSelection = {
8742
10550
  row: [
@@ -8750,7 +10558,11 @@ const defaultSpreadsheetFreezePanesSelection = {
8750
10558
  row_focus: 0,
8751
10559
  column_focus: 0
8752
10560
  };
8753
- function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, commands, content, fileActions, findOpen, formatPainterMode = null, freezePanesActive = false, freezePanesSelection = defaultSpreadsheetFreezePanesSelection, gridLinesVisible, panelId, onTabChange, onTogglePanel, panel, toolbarCell, toolbarCellBorders }) {
10561
+ function SpreadsheetEditorRibbon({ activeTab, activeTable = null, activeTableSheetId, autoFilterActive = false, can, commands, content, fileActions, findOpen, formatPainterMode = null, freezePanesActive = false, freezePanesSelection = defaultSpreadsheetFreezePanesSelection, gridLinesVisible, panelId, onTabChange, onTogglePanel, panel, toolbarCell, toolbarCellBorders }) {
10562
+ const ribbonTabs = activeTable ? [
10563
+ ...spreadsheetRibbonTabs,
10564
+ spreadsheetTableDesignRibbonTab
10565
+ ] : spreadsheetRibbonTabs;
8754
10566
  const formulaCount = useMemo(()=>spreadsheetFormulaCount(content), [
8755
10567
  content
8756
10568
  ]);
@@ -8759,7 +10571,7 @@ function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, com
8759
10571
  ]);
8760
10572
  return /*#__PURE__*/ jsx(WorkOfficeRibbon, {
8761
10573
  ariaLabel: "表格功能区",
8762
- tabs: spreadsheetRibbonTabs,
10574
+ tabs: ribbonTabs,
8763
10575
  defaultTab: "home",
8764
10576
  activeTab: activeTab,
8765
10577
  onTabChange: onTabChange,
@@ -8991,6 +10803,20 @@ function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, com
8991
10803
  }),
8992
10804
  insert: /*#__PURE__*/ jsxs(Fragment, {
8993
10805
  children: [
10806
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
10807
+ label: "表格",
10808
+ priority: "high",
10809
+ children: /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
10810
+ label: spreadsheetCommandCatalog.table.label,
10811
+ title: `${spreadsheetCommandCatalog.table.label}(${spreadsheetCommandCatalog.table.shortcut.label})`,
10812
+ "aria-keyshortcuts": spreadsheetCommandCatalog.table.shortcut.aria,
10813
+ disabled: !can.openTable(),
10814
+ onClick: commands.openTable,
10815
+ children: /*#__PURE__*/ jsx(Table2, {
10816
+ size: 19
10817
+ })
10818
+ })
10819
+ }),
8994
10820
  /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
8995
10821
  label: "链接",
8996
10822
  priority: "high",
@@ -9188,7 +11014,13 @@ function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, com
9188
11014
  })
9189
11015
  })
9190
11016
  ]
9191
- })
11017
+ }),
11018
+ tableDesign: activeTable && activeTableSheetId ? /*#__PURE__*/ jsx(SpreadsheetTableDesignRibbon, {
11019
+ can: can,
11020
+ commands: commands,
11021
+ sheetId: activeTableSheetId,
11022
+ table: activeTable
11023
+ }) : null
9192
11024
  }
9193
11025
  });
9194
11026
  }
@@ -12086,6 +13918,59 @@ function SheetMenuButton({ icon, label, danger = false, disabled = false, onClic
12086
13918
  ]
12087
13919
  });
12088
13920
  }
13921
+ const pendingContextRestores = new WeakMap();
13922
+ function beginSpreadsheetTableCellRender(_cell, tableStyle, conditionalStyle, context) {
13923
+ if (tableStyle) context.fillStyle = tableStyle.background;
13924
+ if (conditionalStyle?.cellColor) context.fillStyle = conditionalStyle.cellColor;
13925
+ if (!tableStyle || pendingContextRestores.has(context) || 'function' != typeof context.fillText) return;
13926
+ const fillText = context.fillText;
13927
+ pendingContextRestores.set(context, {
13928
+ fillText,
13929
+ fillTextOwned: Object.hasOwn(context, 'fillText')
13930
+ });
13931
+ const textColor = conditionalStyle?.textColor ?? tableStyle.textColor;
13932
+ context.fillText = (text, x, y, maxWidth)=>{
13933
+ const previousFillStyle = context.fillStyle;
13934
+ const previousFont = context.font;
13935
+ context.fillStyle = textColor;
13936
+ if (tableStyle.bold) context.font = boldSpreadsheetTableCanvasFont(context.font);
13937
+ try {
13938
+ if (void 0 === maxWidth) fillText.call(context, text, x, y);
13939
+ else fillText.call(context, text, x, y, maxWidth);
13940
+ } finally{
13941
+ context.fillStyle = previousFillStyle;
13942
+ context.font = previousFont;
13943
+ }
13944
+ };
13945
+ }
13946
+ function finishSpreadsheetTableCellRender(_cell, cellInfo, tableStyle, context) {
13947
+ if (tableStyle) drawSpreadsheetTableCellBorder(cellInfo, tableStyle, context);
13948
+ const restore = pendingContextRestores.get(context);
13949
+ if (!restore) return;
13950
+ if (restore.fillTextOwned) context.fillText = restore.fillText;
13951
+ else delete context.fillText;
13952
+ pendingContextRestores.delete(context);
13953
+ }
13954
+ function boldSpreadsheetTableCanvasFont(font) {
13955
+ return /(?:^|\s)(?:bold|[6-9]00)(?:\s|$)/i.test(font) ? font : `bold ${font}`;
13956
+ }
13957
+ function drawSpreadsheetTableCellBorder(cellInfo, tableStyle, context) {
13958
+ const left = Math.round(cellInfo.startX) + 0.5;
13959
+ const top = Math.round(cellInfo.startY) + 0.5;
13960
+ const right = Math.round(cellInfo.endX) - 0.5;
13961
+ const bottom = Math.round(cellInfo.endY) - 0.5;
13962
+ context.save();
13963
+ context.beginPath();
13964
+ context.strokeStyle = tableStyle.borderColor;
13965
+ context.lineWidth = 'header' === tableStyle.role ? 1 : 0.75;
13966
+ context.moveTo(left, top);
13967
+ context.lineTo(right, top);
13968
+ context.lineTo(right, bottom);
13969
+ context.lineTo(left, bottom);
13970
+ context.closePath();
13971
+ context.stroke();
13972
+ context.restore();
13973
+ }
12089
13974
  function validateSpreadsheetChartSeriesTrendlines(series, seriesIndex) {
12090
13975
  for (const [trendlineIndex, trendline] of (series.trendlines ?? []).entries()){
12091
13976
  const prefix = `系列 ${seriesIndex + 1} 的趋势线 ${trendlineIndex + 1}`;
@@ -16195,220 +18080,28 @@ function NamedRangeManager({ content, onChange }) {
16195
18080
  onClick: cancelDraft,
16196
18081
  children: "取消更改"
16197
18082
  }),
16198
- /*#__PURE__*/ jsx(button_Button, {
16199
- type: "submit",
16200
- tone: "primary",
16201
- disabled: Boolean(draft.id) && !dirty,
16202
- children: "保存名称"
16203
- })
16204
- ]
16205
- })
16206
- ]
16207
- })
16208
- ]
16209
- });
16210
- }
16211
- function rangeDraft(range) {
16212
- return {
16213
- id: range?.id,
16214
- name: range?.name ?? '',
16215
- reference: range?.reference ?? '',
16216
- scopeSheetId: range?.scopeSheetId ?? '',
16217
- comment: range?.comment ?? ''
16218
- };
16219
- }
16220
- function spreadsheetAutoFilterRange(sheet, selection) {
16221
- if (sheet.isPivotTable || sheet.pivotTables?.length) return null;
16222
- const normalized = normalizeAutoFilterSelection(selection);
16223
- const range = normalized.row[0] !== normalized.row[1] ? normalized : spreadsheetCurrentRegion(sheet, normalized);
16224
- if (!range || !validSpreadsheetAutoFilterRange(sheet, range)) return null;
16225
- return range;
16226
- }
16227
- function spreadsheetAutoFilterHeaderColumn(sheet, selection) {
16228
- const range = normalizedFilterSelection(sheet?.filter_select);
16229
- if (!sheet || !range) return null;
16230
- const row = spreadsheet_auto_filter_finiteIndex(selection.row_focus, selection.row[0]);
16231
- const column = spreadsheet_auto_filter_finiteIndex(selection.column_focus, selection.column[0]);
16232
- if (row !== range.row[0] || column < range.column[0] || column > range.column[1]) return null;
16233
- return column;
16234
- }
16235
- function toggleSpreadsheetAutoFilter(content, sheetId, selection) {
16236
- const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
16237
- const sheet = content.sheets[sheetIndex];
16238
- if (!sheet) return null;
16239
- const nextSheet = sheet.filter_select ? spreadsheetSheetWithoutAutoFilter(sheet) : spreadsheetSheetWithAutoFilter(sheet, selection);
16240
- if (!nextSheet) return null;
16241
- const sheets = [
16242
- ...content.sheets
16243
- ];
16244
- sheets[sheetIndex] = {
16245
- ...nextSheet,
16246
- luckysheet_select_save: [
16247
- finiteSpreadsheetSelection(selection)
16248
- ]
16249
- };
16250
- return {
16251
- ...content,
16252
- sheets
16253
- };
16254
- }
16255
- function spreadsheetSheetWithAutoFilter(sheet, selection) {
16256
- const range = spreadsheetAutoFilterRange(sheet, selection);
16257
- if (!range) return null;
16258
- return {
16259
- ...sheet,
16260
- filter: {},
16261
- filter_select: {
16262
- row: [
16263
- ...range.row
16264
- ],
16265
- column: [
16266
- ...range.column
16267
- ]
16268
- }
16269
- };
16270
- }
16271
- function spreadsheetSheetWithoutAutoFilter(sheet) {
16272
- const hiddenByFilter = spreadsheetFilterHiddenRows(sheet.filter);
16273
- const rowhidden = {
16274
- ...sheet.config?.rowhidden
16275
- };
16276
- for (const row of hiddenByFilter)delete rowhidden[row];
16277
- const config = sheet.config ? {
16278
- ...sheet.config,
16279
- rowhidden
16280
- } : void 0;
16281
- const { filter: _filter, filter_select: _selection, ...remaining } = sheet;
16282
- return {
16283
- ...remaining,
16284
- config
16285
- };
16286
- }
16287
- function spreadsheetFilterHiddenRows(filter) {
16288
- const rows = new Set();
16289
- for (const value of Object.values(filter ?? {})){
16290
- if (!value || 'object' != typeof value) continue;
16291
- const rowhidden = value.rowhidden;
16292
- if (rowhidden && 'object' == typeof rowhidden) for (const row of Object.keys(rowhidden))rows.add(row);
16293
- }
16294
- return rows;
16295
- }
16296
- function spreadsheetCurrentRegion(sheet, selection) {
16297
- const cells = spreadsheetCellReader(sheet);
16298
- const bounds = spreadsheetUsedBounds(sheet);
16299
- let startRow = selection.row[0];
16300
- let endRow = selection.row[1];
16301
- let startColumn = selection.column[0];
16302
- let endColumn = selection.column[1];
16303
- if (startRow > bounds.lastRow || startColumn > bounds.lastColumn || !spreadsheet_auto_filter_spreadsheetCellHasContent(cells(startRow, startColumn))) return null;
16304
- let changed = true;
16305
- while(changed){
16306
- changed = false;
16307
- while(startColumn > 0 && spreadsheetColumnHasContent(cells, startColumn - 1, startRow, endRow)){
16308
- startColumn -= 1;
16309
- changed = true;
16310
- }
16311
- while(endColumn < bounds.lastColumn && spreadsheetColumnHasContent(cells, endColumn + 1, startRow, endRow)){
16312
- endColumn += 1;
16313
- changed = true;
16314
- }
16315
- while(startRow > 0 && spreadsheetRowHasContent(cells, startRow - 1, startColumn, endColumn)){
16316
- startRow -= 1;
16317
- changed = true;
16318
- }
16319
- while(endRow < bounds.lastRow && spreadsheetRowHasContent(cells, endRow + 1, startColumn, endColumn)){
16320
- endRow += 1;
16321
- changed = true;
16322
- }
16323
- }
16324
- return {
16325
- row: [
16326
- startRow,
16327
- endRow
16328
- ],
16329
- column: [
16330
- startColumn,
16331
- endColumn
18083
+ /*#__PURE__*/ jsx(button_Button, {
18084
+ type: "submit",
18085
+ tone: "primary",
18086
+ disabled: Boolean(draft.id) && !dirty,
18087
+ children: "保存名称"
18088
+ })
18089
+ ]
18090
+ })
18091
+ ]
18092
+ })
16332
18093
  ]
16333
- };
16334
- }
16335
- function validSpreadsheetAutoFilterRange(sheet, range) {
16336
- if (range.row[1] <= range.row[0]) return false;
16337
- const cells = spreadsheetCellReader(sheet);
16338
- if (!spreadsheetRowHasContent(cells, range.row[0], range.column[0], range.column[1])) return false;
16339
- let dataFound = false;
16340
- for(let row = range.row[0] + 1; row <= range.row[1]; row += 1)if (spreadsheetRowHasContent(cells, row, range.column[0], range.column[1])) {
16341
- dataFound = true;
16342
- break;
16343
- }
16344
- return dataFound && !spreadsheetRangeIntersectsMerge(sheet, range);
16345
- }
16346
- function spreadsheetRangeIntersectsMerge(sheet, range) {
16347
- return Object.values(sheet.config?.merge ?? {}).some((merge)=>{
16348
- const endRow = merge.r + Math.max(merge.rs, 1) - 1;
16349
- const endColumn = merge.c + Math.max(merge.cs, 1) - 1;
16350
- return merge.r <= range.row[1] && endRow >= range.row[0] && merge.c <= range.column[1] && endColumn >= range.column[0];
16351
18094
  });
16352
18095
  }
16353
- function spreadsheetRowHasContent(cellAt, row, startColumn, endColumn) {
16354
- for(let column = startColumn; column <= endColumn; column += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
16355
- return false;
16356
- }
16357
- function spreadsheetColumnHasContent(cellAt, column, startRow, endRow) {
16358
- for(let row = startRow; row <= endRow; row += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
16359
- return false;
16360
- }
16361
- function spreadsheet_auto_filter_spreadsheetCellHasContent(cell) {
16362
- if (!cell) return false;
16363
- return [
16364
- cell.v,
16365
- cell.m,
16366
- cell.f
16367
- ].some((value)=>null != value && '' !== value);
16368
- }
16369
- function spreadsheetCellReader(sheet) {
16370
- if (sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
16371
- const cells = new Map((sheet.celldata ?? []).map((cell)=>[
16372
- `${cell.r}:${cell.c}`,
16373
- cell.v
16374
- ]));
16375
- return (row, column)=>cells.get(`${row}:${column}`) ?? null;
16376
- }
16377
- function spreadsheetUsedBounds(sheet) {
16378
- let lastRow = Math.max((sheet.data?.length ?? 1) - 1, 0);
16379
- let lastColumn = Math.max(sparseMatrixColumnCount(sheet.data) - 1, 0);
16380
- for (const cell of sheet.celldata ?? []){
16381
- lastRow = Math.max(lastRow, cell.r);
16382
- lastColumn = Math.max(lastColumn, cell.c);
16383
- }
16384
- return {
16385
- lastColumn,
16386
- lastRow
16387
- };
16388
- }
16389
- function normalizeAutoFilterSelection(selection) {
16390
- const startRow = spreadsheet_auto_filter_finiteIndex(selection.row[0], 0);
16391
- const endRow = spreadsheet_auto_filter_finiteIndex(selection.row[1], startRow);
16392
- const startColumn = spreadsheet_auto_filter_finiteIndex(selection.column[0], 0);
16393
- const endColumn = spreadsheet_auto_filter_finiteIndex(selection.column[1], startColumn);
18096
+ function rangeDraft(range) {
16394
18097
  return {
16395
- row: [
16396
- Math.min(startRow, endRow),
16397
- Math.max(startRow, endRow)
16398
- ],
16399
- column: [
16400
- Math.min(startColumn, endColumn),
16401
- Math.max(startColumn, endColumn)
16402
- ]
18098
+ id: range?.id,
18099
+ name: range?.name ?? '',
18100
+ reference: range?.reference ?? '',
18101
+ scopeSheetId: range?.scopeSheetId ?? '',
18102
+ comment: range?.comment ?? ''
16403
18103
  };
16404
18104
  }
16405
- function normalizedFilterSelection(selection) {
16406
- if (!selection?.row?.length || !selection.column?.length) return null;
16407
- return normalizeAutoFilterSelection(selection);
16408
- }
16409
- function spreadsheet_auto_filter_finiteIndex(value, fallback) {
16410
- return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
16411
- }
16412
18105
  function useSpreadsheetAutoFilter({ canvasRef, content, editable, mountRevision, onChange, selection, sheetId, workbook = null }) {
16413
18106
  const contentRef = useRef(content);
16414
18107
  const editableRef = useRef(editable);
@@ -19984,6 +21677,206 @@ function spreadsheetHyperlinkValidationMessage(content, source, value) {
19984
21677
  function spreadsheetHyperlinkFailureMessage(content, source, value, fallback) {
19985
21678
  return spreadsheetHyperlinkValidationMessage(content, source, value) ?? fallback;
19986
21679
  }
21680
+ function SpreadsheetTableDialog({ source, restoreFocusTarget, onApply, onClose, onValidate }) {
21681
+ const [value, setValue] = useState(source.value);
21682
+ const [touched, setTouched] = useState(false);
21683
+ const formId = useId();
21684
+ const validationError = onValidate(value);
21685
+ const visibleError = touched ? validationError : null;
21686
+ const submit = (event)=>{
21687
+ event.preventDefault();
21688
+ setTouched(true);
21689
+ if (validationError) return;
21690
+ if (onApply(value)) onClose();
21691
+ };
21692
+ return /*#__PURE__*/ jsx(Dialog, {
21693
+ title: "创建表格",
21694
+ description: source.sheetName,
21695
+ className: "work-spreadsheet-table-dialog",
21696
+ restoreFocusTarget: restoreFocusTarget,
21697
+ onClose: onClose,
21698
+ footer: /*#__PURE__*/ jsxs(Fragment, {
21699
+ children: [
21700
+ /*#__PURE__*/ jsx(button_Button, {
21701
+ tone: "quiet",
21702
+ onClick: onClose,
21703
+ children: "取消"
21704
+ }),
21705
+ /*#__PURE__*/ jsx(button_Button, {
21706
+ tone: "primary",
21707
+ type: "submit",
21708
+ form: formId,
21709
+ disabled: Boolean(validationError),
21710
+ children: "确定"
21711
+ })
21712
+ ]
21713
+ }),
21714
+ children: /*#__PURE__*/ jsxs("form", {
21715
+ id: formId,
21716
+ onSubmit: submit,
21717
+ children: [
21718
+ /*#__PURE__*/ jsxs("div", {
21719
+ className: "work-spreadsheet-table-scope",
21720
+ children: [
21721
+ /*#__PURE__*/ jsx("span", {
21722
+ "aria-hidden": "true",
21723
+ children: /*#__PURE__*/ jsx(Table2, {
21724
+ size: 18
21725
+ })
21726
+ }),
21727
+ /*#__PURE__*/ jsxs("div", {
21728
+ children: [
21729
+ /*#__PURE__*/ jsx("strong", {
21730
+ children: source.name
21731
+ }),
21732
+ /*#__PURE__*/ jsx("small", {
21733
+ children: "将连续数据区域转换为原生工作表表格"
21734
+ })
21735
+ ]
21736
+ })
21737
+ ]
21738
+ }),
21739
+ /*#__PURE__*/ jsx(field_Field, {
21740
+ label: "表格区域",
21741
+ required: true,
21742
+ error: visibleError ?? void 0,
21743
+ description: "输入一个连续区域,例如 A1:C20。",
21744
+ children: /*#__PURE__*/ jsx("input", {
21745
+ type: "text",
21746
+ autoCapitalize: "none",
21747
+ autoFocus: true,
21748
+ spellCheck: false,
21749
+ value: value.rangeReference,
21750
+ onBlur: ()=>setTouched(true),
21751
+ onChange: (event)=>{
21752
+ const rangeReference = event.currentTarget.value;
21753
+ setValue((current)=>({
21754
+ ...current,
21755
+ rangeReference
21756
+ }));
21757
+ setTouched(true);
21758
+ }
21759
+ })
21760
+ }),
21761
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
21762
+ ariaLabel: "表包含标题",
21763
+ checked: value.headerRow,
21764
+ onCheckedChange: (headerRow)=>setValue((current)=>({
21765
+ ...current,
21766
+ headerRow
21767
+ })),
21768
+ children: "表包含标题"
21769
+ })
21770
+ ]
21771
+ })
21772
+ });
21773
+ }
21774
+ function useSpreadsheetTable({ commandsRef, contentRef, focusGrid, getGridFocusTarget, getLiveSelection, preview }) {
21775
+ const [source, setSource] = useState(null);
21776
+ const invokerRef = useRef(null);
21777
+ const selectionRef = useRef(null);
21778
+ const applyingRef = useRef(false);
21779
+ const appliedRef = useRef(false);
21780
+ useEffect(()=>{
21781
+ if (!preview) return;
21782
+ setSource(null);
21783
+ invokerRef.current = null;
21784
+ selectionRef.current = null;
21785
+ applyingRef.current = false;
21786
+ appliedRef.current = false;
21787
+ }, [
21788
+ preview
21789
+ ]);
21790
+ const open = useCallback((target)=>{
21791
+ if (preview || source) return false;
21792
+ const nextSource = createSpreadsheetTableDialogSource(contentRef.current, target);
21793
+ if (!nextSource) {
21794
+ showToast('请选择包含标题和至少一行数据的连续、未合并区域。', 'error');
21795
+ return false;
21796
+ }
21797
+ const grid = getGridFocusTarget();
21798
+ invokerRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : grid;
21799
+ selectionRef.current = {
21800
+ sheetId: target.sheetId,
21801
+ selection: finiteSpreadsheetSelection(getLiveSelection() ?? target.selection)
21802
+ };
21803
+ appliedRef.current = false;
21804
+ setSource(nextSource);
21805
+ return true;
21806
+ }, [
21807
+ contentRef,
21808
+ getGridFocusTarget,
21809
+ getLiveSelection,
21810
+ preview,
21811
+ source
21812
+ ]);
21813
+ const close = useCallback(()=>{
21814
+ const invoker = invokerRef.current;
21815
+ const grid = getGridFocusTarget();
21816
+ const restoreGrid = appliedRef.current || invoker === grid;
21817
+ setSource(null);
21818
+ requestAnimationFrame(()=>{
21819
+ if (restoreGrid || !invoker?.isConnected) focusGrid(invoker);
21820
+ else invoker.focus({
21821
+ preventScroll: true
21822
+ });
21823
+ if (invokerRef.current === invoker) invokerRef.current = null;
21824
+ selectionRef.current = null;
21825
+ appliedRef.current = false;
21826
+ });
21827
+ }, [
21828
+ focusGrid,
21829
+ getGridFocusTarget
21830
+ ]);
21831
+ const commandPort = useMemo(()=>({
21832
+ canOpen: !preview && null === source,
21833
+ open
21834
+ }), [
21835
+ open,
21836
+ preview,
21837
+ source
21838
+ ]);
21839
+ const selectionForChange = useCallback(()=>applyingRef.current ? selectionRef.current : null, []);
21840
+ return {
21841
+ commandPort,
21842
+ selectionForChange,
21843
+ dialog: source ? /*#__PURE__*/ jsx(SpreadsheetTableDialog, {
21844
+ source: source,
21845
+ restoreFocusTarget: ()=>appliedRef.current ? getGridFocusTarget() : invokerRef.current?.isConnected ? invokerRef.current : getGridFocusTarget(),
21846
+ onApply: (value)=>{
21847
+ const range = spreadsheetTableRangeFromText(value.rangeReference);
21848
+ if (!range) return false;
21849
+ applyingRef.current = true;
21850
+ let handled = false;
21851
+ try {
21852
+ handled = commandsRef.current?.applyTable({
21853
+ headerRow: value.headerRow,
21854
+ name: source.name,
21855
+ range,
21856
+ sheetId: source.sheetId
21857
+ }) ?? false;
21858
+ appliedRef.current = handled;
21859
+ } finally{
21860
+ applyingRef.current = false;
21861
+ }
21862
+ if (!handled) showToast(spreadsheetTableValidationMessage(contentRef.current, source, value) ?? '无法创建表格。', 'error');
21863
+ return handled;
21864
+ },
21865
+ onClose: close,
21866
+ onValidate: (value)=>spreadsheetTableValidationMessage(contentRef.current, source, value)
21867
+ }) : null
21868
+ };
21869
+ }
21870
+ function spreadsheetTableValidationMessage(content, source, value) {
21871
+ const range = spreadsheetTableRangeFromText(value.rangeReference);
21872
+ if (!range) return '请输入一个连续区域。';
21873
+ return spreadsheetTableFailureMessage(content, {
21874
+ headerRow: value.headerRow,
21875
+ name: source.name,
21876
+ range,
21877
+ sheetId: source.sheetId
21878
+ });
21879
+ }
19987
21880
  const MAXIMUM_BUFFERED_SPREADSHEET_OPERATIONS = 10001;
19988
21881
  function useSpreadsheetWorkbookSync(content, synchronizeExternalWorkbook) {
19989
21882
  const mountedContentRef = useRef(content);
@@ -20117,6 +22010,14 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20117
22010
  getLiveSelections: getSpreadsheetDataValidationLiveSelections,
20118
22011
  preview
20119
22012
  });
22013
+ const spreadsheetTable = useSpreadsheetTable({
22014
+ commandsRef: spreadsheetCommandsRef,
22015
+ contentRef,
22016
+ focusGrid: focusSpreadsheetDialogGrid,
22017
+ getGridFocusTarget: getSpreadsheetDialogGridFocusTarget,
22018
+ getLiveSelection: getSpreadsheetHyperlinkLiveSelection,
22019
+ preview
22020
+ });
20120
22021
  const officeDialog = useOfficeDialog();
20121
22022
  const [findOpen, setFindOpen] = useState(false);
20122
22023
  const [findFocusRequest, setFindFocusRequest] = useState(0);
@@ -20149,6 +22050,16 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20149
22050
  ]);
20150
22051
  const conditionalStylesBySheetRef = useRef(conditionalStylesBySheet);
20151
22052
  conditionalStylesBySheetRef.current = conditionalStylesBySheet;
22053
+ const tableStylesBySheet = useMemo(()=>new Map(materializedContent.sheets.flatMap((sheet)=>sheet.id ? [
22054
+ [
22055
+ sheet.id,
22056
+ createSpreadsheetTableRenderResolver(sheet.tables ?? [])
22057
+ ]
22058
+ ] : [])), [
22059
+ materializedContent.sheets
22060
+ ]);
22061
+ const tableStylesBySheetRef = useRef(tableStylesBySheet);
22062
+ tableStylesBySheetRef.current = tableStylesBySheet;
20152
22063
  useEffect(()=>{
20153
22064
  setPreviewActiveSheetId(preview ? contentActiveSheetId : null);
20154
22065
  }, [
@@ -20259,21 +22170,28 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20259
22170
  endColumn: Math.max(selection.column[0], selection.column[1])
20260
22171
  }).length);
20261
22172
  },
20262
- beforeRenderCell: (_cell, cellInfo, context)=>{
22173
+ beforeRenderCell: (cell, cellInfo, context)=>{
22174
+ const tableStyle = tableStylesBySheetRef.current.get(activeSheetIdRef.current)?.(cellInfo.row, cellInfo.column) ?? null;
20263
22175
  const style = conditionalStylesBySheetRef.current.get(activeSheetIdRef.current)?.get(`${cellInfo.row}_${cellInfo.column}`);
20264
- if (style?.cellColor) context.fillStyle = style.cellColor;
22176
+ beginSpreadsheetTableCellRender(cell, tableStyle, style, context);
20265
22177
  return true;
20266
22178
  },
20267
22179
  afterRenderCell: (cell, cellInfo, context)=>{
22180
+ const tableStyle = tableStylesBySheetRef.current.get(activeSheetIdRef.current)?.(cellInfo.row, cellInfo.column) ?? null;
20268
22181
  const style = conditionalStylesBySheetRef.current.get(activeSheetIdRef.current)?.get(`${cellInfo.row}_${cellInfo.column}`);
20269
- if (!style?.icon && !style?.dataBar) return;
20270
- const background = style.cellColor ?? ('string' == typeof cell?.bg ? cell.bg : '#ffffff');
20271
- if (style.dataBar) drawSpreadsheetConditionalDataBar(context, cellInfo, {
20272
- ...style.dataBar,
20273
- showValue: style.dataBar.showValue && (style.icon?.showValue ?? true)
20274
- }, cell, background, style.textColor);
20275
- if (style.icon) drawSpreadsheetConditionalIcon(context, cellInfo, style.icon, background, style.dataBar ? false : !style.icon.showValue);
20276
- if (cell?.ps) drawSpreadsheetCommentMarker(context, cellInfo);
22182
+ try {
22183
+ if (style?.icon || style?.dataBar) {
22184
+ const background = style.cellColor ?? tableStyle?.background ?? ('string' == typeof cell?.bg ? cell.bg : '#ffffff');
22185
+ if (style.dataBar) drawSpreadsheetConditionalDataBar(context, cellInfo, {
22186
+ ...style.dataBar,
22187
+ showValue: style.dataBar.showValue && (style.icon?.showValue ?? true)
22188
+ }, cell, background, style.textColor);
22189
+ if (style.icon) drawSpreadsheetConditionalIcon(context, cellInfo, style.icon, background, style.dataBar ? false : !style.icon.showValue);
22190
+ if (cell?.ps) drawSpreadsheetCommentMarker(context, cellInfo);
22191
+ }
22192
+ } finally{
22193
+ finishSpreadsheetTableCellRender(cell, cellInfo, tableStyle, context);
22194
+ }
20277
22195
  }
20278
22196
  }), [
20279
22197
  collaborationView
@@ -20393,6 +22311,17 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20393
22311
  const toolbarColumn = toolbarSelection.column_focus ?? toolbarSelection.column[0];
20394
22312
  const toolbarCell = spreadsheetCellAt(toolbarSheet, toolbarRow, toolbarColumn);
20395
22313
  const toolbarCellBorders = spreadsheetCellBordersAt(toolbarSheet, toolbarRow, toolbarColumn);
22314
+ const toolbarContentSheet = materializedContent.sheets.find((sheet)=>sheet.id === toolbarSheetId);
22315
+ const activeTable = spreadsheetTableAtCell(toolbarContentSheet, toolbarRow, toolbarColumn);
22316
+ const activeTableId = activeTable?.id ?? null;
22317
+ useEffect(()=>{
22318
+ setRibbonTab((current)=>{
22319
+ if (activeTableId) return 'tableDesign' === current ? current : 'tableDesign';
22320
+ return 'tableDesign' === current ? 'home' : current;
22321
+ });
22322
+ }, [
22323
+ activeTableId
22324
+ ]);
20396
22325
  const selectedRange = spreadsheetSingleRange(toolbarSelection);
20397
22326
  const multipleCellsSelected = selectedRange.row[0] !== selectedRange.row[1] || selectedRange.column[0] !== selectedRange.column[1];
20398
22327
  const selectionSummary = multipleCellsSelected ? spreadsheetSelectionSummary(toolbarSheet, toolbarSelection) : null;
@@ -20633,12 +22562,18 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20633
22562
  onChange: (next)=>{
20634
22563
  const formatCellsSelection = formatCellsApplyingRef.current ? formatCellsSelectionRef.current : null;
20635
22564
  const dataValidationSelection = spreadsheetDataValidation.selectionForChange();
22565
+ const tableSelection = spreadsheetTable.selectionForChange();
20636
22566
  const hyperlinkSelection = spreadsheetHyperlink.selectionForChange();
20637
22567
  const preservedSelection = formatCellsSelection ? {
20638
22568
  sheetId: formatCellsSelection.sheetId,
20639
22569
  selections: [
20640
22570
  formatCellsSelection.selection
20641
22571
  ]
22572
+ } : tableSelection ? {
22573
+ sheetId: tableSelection.sheetId,
22574
+ selections: [
22575
+ tableSelection.selection
22576
+ ]
20642
22577
  } : dataValidationSelection ?? (hyperlinkSelection ? {
20643
22578
  sheetId: hyperlinkSelection.sheetId,
20644
22579
  selections: [
@@ -20650,6 +22585,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20650
22585
  onChange(controlled);
20651
22586
  },
20652
22587
  selection: selectionState,
22588
+ table: spreadsheetTable.commandPort,
20653
22589
  targetSheetGridSize,
20654
22590
  targetSheetId: toolbarSheetId,
20655
22591
  toolbarCell,
@@ -20853,6 +22789,8 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20853
22789
  }),
20854
22790
  !preview && /*#__PURE__*/ jsx(SpreadsheetEditorRibbon, {
20855
22791
  activeTab: ribbonTab,
22792
+ activeTable: activeTable,
22793
+ activeTableSheetId: activeTable ? toolbarSheetId : void 0,
20856
22794
  autoFilterActive: autoFilterActive,
20857
22795
  can: spreadsheetCan,
20858
22796
  commands: spreadsheetRibbonCommands,
@@ -21075,6 +23013,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
21075
23013
  }),
21076
23014
  spreadsheetDataValidation.dialog,
21077
23015
  spreadsheetHyperlink.dialog,
23016
+ spreadsheetTable.dialog,
21078
23017
  spreadsheetClipboard.dialogSource && /*#__PURE__*/ jsx(SpreadsheetPasteSpecialDialog, {
21079
23018
  source: spreadsheetClipboard.dialogSource,
21080
23019
  restoreFocusTarget: spreadsheetClipboard.restoreDialogFocusTarget,
@@ -21182,11 +23121,13 @@ function spreadsheetCommandsWithGridFocus(commands, restoreFocus) {
21182
23121
  return {
21183
23122
  ...commands,
21184
23123
  adjustDecimalPlaces: afterSuccessfulCommand(commands.adjustDecimalPlaces),
23124
+ adjustFontSize: afterSuccessfulCommand(commands.adjustFontSize),
21185
23125
  applyAutoSum: afterSuccessfulCommand(commands.applyAutoSum),
21186
23126
  activateFormatPainter: afterSuccessfulCommand(commands.activateFormatPainter),
21187
23127
  cancelFormatPainter: afterSuccessfulCommand(commands.cancelFormatPainter),
21188
23128
  clearSelectedCells: afterSuccessfulCommand(commands.clearSelectedCells),
21189
23129
  copySelection: afterSuccessfulCommand(commands.copySelection),
23130
+ convertTableToRange: afterSuccessfulCommand(commands.convertTableToRange),
21190
23131
  cutSelection: afterSuccessfulCommand(commands.cutSelection),
21191
23132
  deleteSelectedStructure: afterSuccessfulCommand(commands.deleteSelectedStructure),
21192
23133
  fillSelectedCells: afterSuccessfulCommand(commands.fillSelectedCells),