@a3s-lab/office 0.15.0 → 0.16.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 (33) hide show
  1. package/COLLABORATION_ROADMAP.md +15 -5
  2. package/README.md +22 -1
  3. package/dist/0~6090.js +7 -8
  4. package/dist/0~spreadsheet-editor.js +2106 -438
  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 +21 -1
  13. package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +10 -0
  14. package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +4 -2
  15. package/dist/internal/features/work/editors/spreadsheet-table-command.d.ts +3 -0
  16. package/dist/internal/features/work/editors/spreadsheet-table-conversion.d.ts +7 -0
  17. package/dist/internal/features/work/editors/spreadsheet-table-dialog.d.ts +8 -0
  18. package/dist/internal/features/work/editors/spreadsheet-table-limits.d.ts +1 -0
  19. package/dist/internal/features/work/editors/spreadsheet-table-reconciliation.d.ts +21 -0
  20. package/dist/internal/features/work/editors/spreadsheet-table-render.d.ts +16 -0
  21. package/dist/internal/features/work/editors/spreadsheet-table-ribbon.d.ts +8 -0
  22. package/dist/internal/features/work/editors/spreadsheet-table-style.d.ts +31 -0
  23. package/dist/internal/features/work/editors/spreadsheet-table.d.ts +55 -0
  24. package/dist/internal/features/work/editors/use-spreadsheet-table.d.ts +23 -0
  25. package/dist/internal/features/work/work-types.d.ts +100 -0
  26. package/dist/internal/features/work/work-xlsx-interop.d.ts +2 -0
  27. package/dist/internal/features/work/work-xlsx-table-filters.d.ts +3 -0
  28. package/dist/internal/features/work/work-xlsx-tables.d.ts +4 -0
  29. package/dist/office-kernel.wasm +0 -0
  30. package/dist/styles.css +189 -0
  31. package/dist/work-spreadsheet-package-scan.worker.js +3 -2
  32. package/docs/latest/en/browser-editor-architecture.md +43 -0
  33. package/package.json +4 -1
@@ -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, {
@@ -1357,6 +1357,349 @@ function isSpreadsheetCoordinate(value) {
1357
1357
  function positiveSpreadsheetDimension(value) {
1358
1358
  return 'number' == typeof value && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
1359
1359
  }
1360
+ function canApplySpreadsheetTableStructureChange(sheet, change) {
1361
+ if (!sheet || !validSpreadsheetTableStructureChange(change)) return false;
1362
+ for (const table of sheet.tables ?? []){
1363
+ if ('delete' === change.kind && 'row' === change.axis && spreadsheetTableDeleteRemovesSemanticRow(table, change.start, change.end)) return false;
1364
+ if (!transformSpreadsheetTableStructure(table, change)) return false;
1365
+ }
1366
+ return true;
1367
+ }
1368
+ function reconcileSpreadsheetTablesAfterFortune(sheets, sourceSheets, operations = []) {
1369
+ return sheets.map((sheet, index)=>{
1370
+ const source = sourceSheetForReconciliation(sheet, sourceSheets, index);
1371
+ if (!source?.tables?.length) return sheet;
1372
+ const sheetOperations = operations.filter((operation)=>operation.id === source.id);
1373
+ let tables = source.tables;
1374
+ const reconcileColumns = new Set();
1375
+ if (!operations.length) for (const table of tables)reconcileColumns.add(table.id);
1376
+ for (const operation of sheetOperations){
1377
+ const structure = spreadsheetTableStructureChangeFromOperation(operation);
1378
+ if (structure) {
1379
+ const transformed = [];
1380
+ for (const table of tables){
1381
+ const next = transformSpreadsheetTableStructure(table, structure);
1382
+ if (next) {
1383
+ transformed.push(next);
1384
+ if (next !== table) reconcileColumns.add(table.id);
1385
+ }
1386
+ }
1387
+ tables = transformed;
1388
+ continue;
1389
+ }
1390
+ const coordinate = spreadsheetCellCoordinateFromOperation(operation);
1391
+ if (!coordinate) continue;
1392
+ 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]);
1393
+ if (table) reconcileColumns.add(table.id);
1394
+ }
1395
+ const updates = [];
1396
+ const readCell = spreadsheetSheetCellReader(sheet);
1397
+ tables = tables.map((table)=>{
1398
+ if (!reconcileColumns.has(table.id)) return table;
1399
+ const columns = canonicalSpreadsheetTableColumns(table, readCell);
1400
+ if (table.headerRow) for (const [offset, column] of columns.entries()){
1401
+ const cellColumn = table.range.column[0] + offset;
1402
+ if (spreadsheetCellText(readCell(table.range.row[0], cellColumn)) !== column.name) updates.push({
1403
+ column: cellColumn,
1404
+ name: column.name,
1405
+ row: table.range.row[0]
1406
+ });
1407
+ }
1408
+ return sameSpreadsheetTableColumns(table.columns, columns) ? table : {
1409
+ ...table,
1410
+ columns
1411
+ };
1412
+ });
1413
+ const withHeaders = stampSpreadsheetTableHeaderCells(sheet, updates);
1414
+ return {
1415
+ ...withHeaders,
1416
+ tables: tables.length ? tables : void 0
1417
+ };
1418
+ });
1419
+ }
1420
+ function transformSpreadsheetTableStructure(table, change) {
1421
+ if (!validSpreadsheetTableStructureChange(change)) return null;
1422
+ const axis = 'row' === change.axis ? table.range.row : table.range.column;
1423
+ const transformed = 'insert' === change.kind ? transformSpreadsheetTableAxisForInsertion(axis, change) : transformSpreadsheetTableAxisForDeletion(axis, change);
1424
+ if (!transformed) return null;
1425
+ const range = {
1426
+ row: 'row' === change.axis ? transformed.axis : [
1427
+ ...table.range.row
1428
+ ],
1429
+ column: 'column' === change.axis ? transformed.axis : [
1430
+ ...table.range.column
1431
+ ]
1432
+ };
1433
+ const height = range.row[1] - range.row[0] + 1;
1434
+ if (height <= Number(table.headerRow) + Number(table.totalsRow)) return null;
1435
+ if ('column' !== change.axis || !transformed.inside) {
1436
+ 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;
1437
+ return {
1438
+ ...table,
1439
+ range
1440
+ };
1441
+ }
1442
+ if ('insert' === change.kind) {
1443
+ const columns = [
1444
+ ...table.columns
1445
+ ];
1446
+ columns.splice(transformed.offset, 0, ...Array.from({
1447
+ length: change.count
1448
+ }, ()=>({
1449
+ name: ''
1450
+ })));
1451
+ return {
1452
+ ...table,
1453
+ range,
1454
+ columns,
1455
+ filters: table.filters.map((filter)=>filter.column >= transformed.offset ? {
1456
+ ...filter,
1457
+ column: filter.column + change.count
1458
+ } : filter)
1459
+ };
1460
+ }
1461
+ if (!('removed' in transformed)) return null;
1462
+ const removed = transformed.removed;
1463
+ if ('number' != typeof removed) return null;
1464
+ const columns = [
1465
+ ...table.columns
1466
+ ];
1467
+ columns.splice(transformed.offset, removed);
1468
+ if (!columns.length) return null;
1469
+ return {
1470
+ ...table,
1471
+ range,
1472
+ columns,
1473
+ filters: filtersAfterSpreadsheetTableColumnDeletion(table.filters, transformed.offset, removed)
1474
+ };
1475
+ }
1476
+ function transformSpreadsheetTableAxisForInsertion(axis, change) {
1477
+ const start = axis[0] ?? 0;
1478
+ const end = axis[1] ?? start;
1479
+ const insertion = change.index + ('rightbottom' === change.direction ? 1 : 0);
1480
+ if (insertion <= start) return {
1481
+ axis: [
1482
+ start + change.count,
1483
+ end + change.count
1484
+ ],
1485
+ inside: false,
1486
+ offset: 0
1487
+ };
1488
+ if (insertion <= end) return {
1489
+ axis: [
1490
+ start,
1491
+ end + change.count
1492
+ ],
1493
+ inside: true,
1494
+ offset: insertion - start
1495
+ };
1496
+ return {
1497
+ axis: [
1498
+ start,
1499
+ end
1500
+ ],
1501
+ inside: false,
1502
+ offset: 0
1503
+ };
1504
+ }
1505
+ function transformSpreadsheetTableAxisForDeletion(axis, change) {
1506
+ const start = axis[0] ?? 0;
1507
+ const end = axis[1] ?? start;
1508
+ const count = change.end - change.start + 1;
1509
+ if (change.end < start) return {
1510
+ axis: [
1511
+ start - count,
1512
+ end - count
1513
+ ],
1514
+ inside: false,
1515
+ offset: 0,
1516
+ removed: 0
1517
+ };
1518
+ if (change.start > end) return {
1519
+ axis: [
1520
+ start,
1521
+ end
1522
+ ],
1523
+ inside: false,
1524
+ offset: 0,
1525
+ removed: 0
1526
+ };
1527
+ const overlapStart = Math.max(start, change.start);
1528
+ const overlapEnd = Math.min(end, change.end);
1529
+ const removed = overlapEnd - overlapStart + 1;
1530
+ const remaining = end - start + 1 - removed;
1531
+ if (remaining <= 0) return null;
1532
+ const deletedBeforeStart = Math.max(0, Math.min(change.end, start - 1) - change.start + 1);
1533
+ const nextStart = start - deletedBeforeStart;
1534
+ return {
1535
+ axis: [
1536
+ nextStart,
1537
+ nextStart + remaining - 1
1538
+ ],
1539
+ inside: true,
1540
+ offset: overlapStart - start,
1541
+ removed
1542
+ };
1543
+ }
1544
+ function filtersAfterSpreadsheetTableColumnDeletion(filters, offset, removed) {
1545
+ const end = offset + removed - 1;
1546
+ return filters.flatMap((filter)=>{
1547
+ if (filter.column >= offset && filter.column <= end) return [];
1548
+ return [
1549
+ filter.column > end ? {
1550
+ ...filter,
1551
+ column: filter.column - removed
1552
+ } : filter
1553
+ ];
1554
+ });
1555
+ }
1556
+ function canonicalSpreadsheetTableColumns(table, readCell) {
1557
+ const width = table.range.column[1] - table.range.column[0] + 1;
1558
+ const observed = new Set();
1559
+ return Array.from({
1560
+ length: width
1561
+ }, (_, offset)=>{
1562
+ const raw = table.headerRow ? spreadsheetCellText(readCell(table.range.row[0], table.range.column[0] + offset)).trim() : table.columns[offset]?.name.trim() ?? '';
1563
+ const base = validSpreadsheetTableColumnName(raw) ? raw : `Column${offset + 1}`;
1564
+ let candidate = base;
1565
+ let suffix = 2;
1566
+ while(observed.has(candidate.toLocaleLowerCase())){
1567
+ const suffixText = String(suffix);
1568
+ candidate = `${truncateSpreadsheetTableColumnName(base, 255 - suffixText.length)}${suffixText}`;
1569
+ suffix += 1;
1570
+ }
1571
+ observed.add(candidate.toLocaleLowerCase());
1572
+ return {
1573
+ name: candidate
1574
+ };
1575
+ });
1576
+ }
1577
+ function spreadsheetTableDeleteRemovesSemanticRow(table, start, end) {
1578
+ 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]);
1579
+ }
1580
+ function spreadsheetTableStructureChangeFromOperation(operation) {
1581
+ if (!spreadsheet_table_reconciliation_isRecord(operation.value)) return null;
1582
+ if ('insertRowCol' === operation.op) {
1583
+ const { count, direction, index, type } = operation.value;
1584
+ const change = {
1585
+ axis: 'column' === type ? 'column' : 'row',
1586
+ count: Number(count),
1587
+ direction: 'rightbottom' === direction ? 'rightbottom' : 'lefttop',
1588
+ index: Number(index),
1589
+ kind: 'insert'
1590
+ };
1591
+ return ('column' === type || 'row' === type) && ('lefttop' === direction || 'rightbottom' === direction) && validSpreadsheetTableStructureChange(change) ? change : null;
1592
+ }
1593
+ if ('deleteRowCol' === operation.op) {
1594
+ const { end, start, type } = operation.value;
1595
+ const change = {
1596
+ axis: 'column' === type ? 'column' : 'row',
1597
+ end: Number(end),
1598
+ kind: 'delete',
1599
+ start: Number(start)
1600
+ };
1601
+ return ('column' === type || 'row' === type) && validSpreadsheetTableStructureChange(change) ? change : null;
1602
+ }
1603
+ return null;
1604
+ }
1605
+ function spreadsheetCellCoordinateFromOperation(operation) {
1606
+ if ('data' !== operation.path[0] || operation.path.length < 3) return null;
1607
+ const row = operation.path[1];
1608
+ const column = operation.path[2];
1609
+ return Number.isSafeInteger(row) && Number(row) >= 0 && Number.isSafeInteger(column) && Number(column) >= 0 ? {
1610
+ column: Number(column),
1611
+ row: Number(row)
1612
+ } : null;
1613
+ }
1614
+ function validSpreadsheetTableStructureChange(change) {
1615
+ if ('insert' === change.kind) return Number.isSafeInteger(change.index) && change.index >= 0 && Number.isSafeInteger(change.count) && change.count > 0;
1616
+ return Number.isSafeInteger(change.start) && change.start >= 0 && Number.isSafeInteger(change.end) && change.end >= change.start;
1617
+ }
1618
+ function sourceSheetForReconciliation(sheet, sourceSheets, index) {
1619
+ return (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
1620
+ }
1621
+ function spreadsheetSheetCellReader(sheet) {
1622
+ if (void 0 !== sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
1623
+ const cells = new Map((sheet.celldata ?? []).map((entry)=>[
1624
+ `${entry.r}:${entry.c}`,
1625
+ entry.v
1626
+ ]));
1627
+ return (row, column)=>cells.get(`${row}:${column}`) ?? null;
1628
+ }
1629
+ function stampSpreadsheetTableHeaderCells(sheet, updates) {
1630
+ if (!updates.length) return sheet;
1631
+ if (void 0 !== sheet.data) {
1632
+ const data = [];
1633
+ data.length = sheet.data.length;
1634
+ for (const [row, value] of sparseArrayEntries(sheet.data))data[row] = value;
1635
+ const mutableRows = new Map();
1636
+ for (const update of updates){
1637
+ let row = mutableRows.get(update.row);
1638
+ if (!row) {
1639
+ const source = data[update.row];
1640
+ row = [];
1641
+ row.length = source?.length ?? 0;
1642
+ for (const column of sparseArrayIndexes(source))row[column] = source?.[column];
1643
+ mutableRows.set(update.row, row);
1644
+ data[update.row] = row;
1645
+ }
1646
+ row[update.column] = stampSpreadsheetTableHeaderCell(row[update.column], update.name);
1647
+ }
1648
+ return {
1649
+ ...sheet,
1650
+ data
1651
+ };
1652
+ }
1653
+ const celldata = [
1654
+ ...sheet.celldata ?? []
1655
+ ];
1656
+ const indexes = new Map(celldata.map((entry, index)=>[
1657
+ `${entry.r}:${entry.c}`,
1658
+ index
1659
+ ]));
1660
+ for (const update of updates){
1661
+ const key = `${update.row}:${update.column}`;
1662
+ const index = indexes.get(key);
1663
+ const source = void 0 === index ? void 0 : celldata[index];
1664
+ const entry = {
1665
+ r: update.row,
1666
+ c: update.column,
1667
+ v: stampSpreadsheetTableHeaderCell(source?.v, update.name)
1668
+ };
1669
+ if (void 0 === index) {
1670
+ indexes.set(key, celldata.length);
1671
+ celldata.push(entry);
1672
+ } else celldata[index] = entry;
1673
+ }
1674
+ return {
1675
+ ...sheet,
1676
+ celldata
1677
+ };
1678
+ }
1679
+ function stampSpreadsheetTableHeaderCell(cell, name) {
1680
+ return {
1681
+ ...cell ?? {},
1682
+ m: name,
1683
+ v: name
1684
+ };
1685
+ }
1686
+ function spreadsheetCellText(cell) {
1687
+ const value = cell?.m ?? cell?.v;
1688
+ return null == value ? '' : String(value);
1689
+ }
1690
+ function validSpreadsheetTableColumnName(name) {
1691
+ const characters = Array.from(name);
1692
+ return name.trim() === name && characters.length >= 1 && characters.length <= 255 && !characters.some((character)=>/\p{Cc}/u.test(character) || '\uFFFE' === character || '\uFFFF' === character);
1693
+ }
1694
+ function truncateSpreadsheetTableColumnName(value, maximum) {
1695
+ return Array.from(value).slice(0, maximum).join('');
1696
+ }
1697
+ function sameSpreadsheetTableColumns(left, right) {
1698
+ return left.length === right.length && left.every((column, index)=>column.name === right[index]?.name);
1699
+ }
1700
+ function spreadsheet_table_reconciliation_isRecord(value) {
1701
+ return Boolean(value && 'object' == typeof value && !Array.isArray(value));
1702
+ }
1360
1703
  const spreadsheetFontSizes = [
1361
1704
  9,
1362
1705
  10,
@@ -1622,7 +1965,7 @@ function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = []) {
1622
1965
  incremental: true,
1623
1966
  operationCount: operations.length
1624
1967
  });
1625
- return incremental.sheets;
1968
+ return reconcileSpreadsheetTablesAfterFortune(incremental.sheets, sourceSheets, operations);
1626
1969
  }
1627
1970
  const projected = sheets.map((sheet, index)=>{
1628
1971
  const source = (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
@@ -1651,7 +1994,7 @@ function spreadsheetSheetsFromFortune(sheets, sourceSheets, operations = []) {
1651
1994
  incremental: false,
1652
1995
  operationCount: operations.length
1653
1996
  });
1654
- return projected;
1997
+ return reconcileSpreadsheetTablesAfterFortune(projected, sourceSheets, operations);
1655
1998
  }
1656
1999
  function recordSpreadsheetControlledProjectionMeasure(start, end, detail) {
1657
2000
  try {
@@ -2289,6 +2632,12 @@ const spreadsheetRibbonTabs = [
2289
2632
  label: '视图'
2290
2633
  }
2291
2634
  ];
2635
+ const spreadsheetTableDesignRibbonTab = {
2636
+ id: 'tableDesign',
2637
+ label: '表格设计',
2638
+ compactLabel: '设计',
2639
+ contextual: true
2640
+ };
2292
2641
  const spreadsheetCommandCatalog = {
2293
2642
  undo: {
2294
2643
  id: 'history.undo',
@@ -3058,6 +3407,22 @@ const spreadsheetCommandCatalog = {
3058
3407
  group: 'charts'
3059
3408
  }
3060
3409
  },
3410
+ table: {
3411
+ id: 'insert.table',
3412
+ label: '表格',
3413
+ location: {
3414
+ area: 'ribbon',
3415
+ tab: 'insert',
3416
+ group: 'tables'
3417
+ },
3418
+ shortcut: {
3419
+ label: 'Cmd/Ctrl+T',
3420
+ aria: 'Control+T Meta+T',
3421
+ editor: [
3422
+ 'Mod-t'
3423
+ ]
3424
+ }
3425
+ },
3061
3426
  hyperlink: {
3062
3427
  id: 'insert.hyperlink',
3063
3428
  label: '超链接',
@@ -5048,11 +5413,15 @@ function canApplySpreadsheetCellMerge(sheet, range, command) {
5048
5413
  const merged = spreadsheetMergesIntersectingRange(sheet, normalized);
5049
5414
  if ('unmerge-cells' === command || 'unmerge-and-fill' === command) return merged.length > 0;
5050
5415
  if (merged.length > 0) return false;
5416
+ if (spreadsheetMergeIntersectsTable(sheet, normalized)) return false;
5051
5417
  const rowCount = normalized.row[1] - normalized.row[0] + 1;
5052
5418
  const columnCount = normalized.column[1] - normalized.column[0] + 1;
5053
5419
  if ('merge-across' === command) return columnCount > 1;
5054
5420
  return rowCount * columnCount > 1;
5055
5421
  }
5422
+ function spreadsheetMergeIntersectsTable(sheet, range) {
5423
+ 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]);
5424
+ }
5056
5425
  function spreadsheetCellMergeApiCalls(sheet, sheetId, range, command) {
5057
5426
  if (!canApplySpreadsheetCellMerge(sheet, range, command) || !sheet) return [];
5058
5427
  const normalized = normalizeSpreadsheetCellMergeRange(range);
@@ -5991,147 +6360,1040 @@ function spreadsheetSelectionAfterStructureDeletion(selection, axis, extent, sta
5991
6360
  }
5992
6361
  };
5993
6362
  }
5994
- function createSpreadsheetEditorExtensions() {
5995
- return [
5996
- createOfficeEditorExtension({
5997
- name: 'spreadsheetDocument',
5998
- addCommands: ()=>({
5999
- setSpreadsheetContent: {
6000
- canExecute: (context)=>context.editable,
6001
- execute: (context, content)=>{
6002
- if (!context.editable) return false;
6003
- context.onChange(content);
6004
- return true;
6005
- }
6006
- }
6007
- })
6008
- }),
6009
- createSpreadsheetCellFormatExtension(),
6010
- createSpreadsheetCellBorderExtension(),
6011
- createSpreadsheetCellStyleExtension(),
6012
- createSpreadsheetNumberFormatExtension(),
6013
- createSpreadsheetNavigationExtension(),
6014
- createSpreadsheetDataValidationExtension(),
6015
- createSpreadsheetHyperlinkExtension(),
6016
- createSpreadsheetAutoSumExtension(),
6017
- createSpreadsheetCellFillExtension(),
6018
- createOfficeEditorExtension({
6019
- name: 'spreadsheetFormatPainter',
6020
- addCommands: ()=>({
6021
- activateFormatPainter: {
6022
- canExecute: ({ formatPainter })=>formatPainter.canActivate,
6023
- execute: ({ formatPainter }, mode)=>formatPainter.canActivate && formatPainter.activate(mode)
6024
- },
6025
- applyFormatPainter: {
6026
- canExecute: ({ formatPainter })=>formatPainter.active,
6027
- execute: ({ formatPainter }, target)=>formatPainter.active && formatPainter.applySelection(target)
6028
- },
6029
- cancelFormatPainter: {
6030
- canExecute: ({ formatPainter })=>formatPainter.active,
6031
- execute: ({ formatPainter })=>formatPainter.active && formatPainter.cancel()
6032
- }
6033
- })
6034
- }),
6035
- createOfficeEditorExtension({
6036
- name: 'spreadsheetAutoFilter',
6037
- addCommands: ()=>({
6038
- openAutoFilterMenu: {
6039
- canExecute: ({ autoFilter })=>autoFilter.canOpenMenu,
6040
- execute: ({ autoFilter })=>autoFilter.canOpenMenu && autoFilter.openMenu()
6041
- },
6042
- toggleAutoFilter: {
6043
- canExecute: ({ autoFilter })=>autoFilter.canToggle,
6044
- execute: ({ autoFilter })=>autoFilter.canToggle && autoFilter.toggle()
6045
- }
6046
- })
6047
- }),
6048
- createOfficeEditorExtension({
6049
- name: 'spreadsheetClipboard',
6050
- addCommands: ()=>({
6051
- copySelection: {
6052
- canExecute: ({ clipboard })=>clipboard.canCopySelection,
6053
- execute: ({ clipboard })=>clipboard.canCopySelection && clipboard.copySelection()
6054
- },
6055
- cutSelection: {
6056
- canExecute: ({ clipboard })=>clipboard.canCutSelection,
6057
- execute: ({ clipboard })=>clipboard.canCutSelection && clipboard.cutSelection()
6058
- },
6059
- pasteSelection: {
6060
- canExecute: ({ clipboard })=>clipboard.canPasteSelection,
6061
- execute: ({ clipboard })=>clipboard.canPasteSelection && clipboard.pasteSelection()
6062
- },
6063
- pasteSpecial: {
6064
- canExecute: ({ clipboard }, content)=>clipboard.canPasteSpecial(content),
6065
- execute: ({ clipboard }, content)=>clipboard.canPasteSpecial(content) && clipboard.pasteSpecial(content)
6066
- },
6067
- openPasteSpecial: {
6068
- canExecute: ({ clipboard })=>clipboard.canOpenPasteSpecial,
6069
- execute: ({ clipboard })=>clipboard.canOpenPasteSpecial && clipboard.openPasteSpecial()
6070
- }
6071
- })
6072
- }),
6073
- createOfficeEditorExtension({
6074
- name: 'spreadsheetHistory',
6075
- addCommands: ()=>({
6076
- redo: {
6077
- canExecute: (context)=>context.editable && (context.history?.canRedo ?? false),
6078
- execute: (context)=>context.history?.redo() ?? false
6079
- },
6080
- undo: {
6081
- canExecute: (context)=>context.editable && (context.history?.canUndo ?? false),
6082
- execute: (context)=>context.history?.undo() ?? false
6083
- }
6084
- })
6085
- }),
6086
- createOfficeEditorExtension({
6087
- name: 'spreadsheetSheets',
6088
- addCommands: ()=>({
6089
- activateSheet: {
6090
- canExecute: (context, sheetId)=>{
6091
- const sheet = context.content.sheets.find((candidate)=>candidate.id === sheetId);
6092
- return Boolean(sheet && (context.editable || context.view && 1 !== sheet.hide));
6093
- },
6094
- execute: (context, sheetId)=>{
6095
- const sheet = context.content.sheets.find((candidate)=>candidate.id === sheetId);
6096
- if (!sheet) return false;
6097
- if (context.view && 1 !== sheet.hide) return context.view?.activateSheet(sheetId) ?? false;
6098
- if (!context.editable) return false;
6099
- return applySpreadsheetSheetChange(context, activateSpreadsheetSheet(context.content, sheetId));
6100
- }
6101
- },
6102
- addSheet: {
6103
- canExecute: (context)=>context.editable,
6104
- execute: (context)=>applySpreadsheetSheetChange(context, addSpreadsheetSheet(context.content))
6105
- },
6106
- deleteSheet: {
6107
- canExecute: (context, sheetId)=>Boolean(context.editable && context.content.sheets.length > 1 && context.content.sheets.some((sheet)=>sheet.id === sheetId)),
6108
- execute: (context, sheetId)=>applySpreadsheetSheetChange(context, deleteSpreadsheetSheet(context.content, sheetId))
6109
- },
6110
- duplicateSheet: {
6111
- canExecute: (context, sheetId)=>context.editable && context.content.sheets.some((sheet)=>sheet.id === sheetId),
6112
- execute: (context, sheetId)=>applySpreadsheetSheetChange(context, duplicateSpreadsheetSheet(context.content, sheetId))
6113
- },
6114
- hideSheet: {
6115
- canExecute: (context, sheetId)=>context.editable && context.content.sheets.filter((sheet)=>1 !== sheet.hide).length > 1 && context.content.sheets.some((sheet)=>sheet.id === sheetId && 1 !== sheet.hide),
6116
- execute: (context, sheetId)=>applySpreadsheetSheetChange(context, hideSpreadsheetSheet(context.content, sheetId, true))
6117
- },
6118
- moveSheet: {
6119
- canExecute: (context, sheetId, direction)=>Boolean(context.editable && moveSpreadsheetSheet(context.content, sheetId, direction)),
6120
- execute: (context, sheetId, direction)=>applySpreadsheetSheetChange(context, moveSpreadsheetSheet(context.content, sheetId, direction))
6121
- },
6122
- renameSheet: {
6123
- canExecute: (context, sheetId, name)=>Boolean(context.editable && renameSpreadsheetSheet(context.content, sheetId, name)),
6124
- execute: (context, sheetId, name)=>applySpreadsheetSheetChange(context, renameSpreadsheetSheet(context.content, sheetId, name))
6125
- },
6126
- setSheetColor: {
6127
- canExecute: (context, sheetId, color)=>Boolean(context.editable && setSpreadsheetSheetColor(context.content, sheetId, color)),
6128
- execute: (context, sheetId, color)=>applySpreadsheetSheetChange(context, setSpreadsheetSheetColor(context.content, sheetId, color))
6129
- }
6130
- })
6131
- }),
6132
- createOfficeEditorExtension({
6133
- name: 'spreadsheetKeyboardShortcuts',
6134
- addKeyboardShortcuts: ()=>({
6363
+ function spreadsheetAutoFilterRange(sheet, selection) {
6364
+ const range = spreadsheetSelectionOrCurrentRegion(sheet, selection);
6365
+ if (!range || !validSpreadsheetAutoFilterRange(sheet, range)) return null;
6366
+ return range;
6367
+ }
6368
+ function spreadsheetSelectionOrCurrentRegion(sheet, selection) {
6369
+ if (sheet.isPivotTable || sheet.pivotTables?.length) return null;
6370
+ const normalized = normalizeAutoFilterSelection(selection);
6371
+ return normalized.row[0] !== normalized.row[1] ? normalized : spreadsheetCurrentRegion(sheet, normalized);
6372
+ }
6373
+ function spreadsheetAutoFilterHeaderColumn(sheet, selection) {
6374
+ const range = normalizedFilterSelection(sheet?.filter_select);
6375
+ if (!sheet || !range) return null;
6376
+ const row = spreadsheet_auto_filter_finiteIndex(selection.row_focus, selection.row[0]);
6377
+ const column = spreadsheet_auto_filter_finiteIndex(selection.column_focus, selection.column[0]);
6378
+ if (row !== range.row[0] || column < range.column[0] || column > range.column[1]) return null;
6379
+ return column;
6380
+ }
6381
+ function toggleSpreadsheetAutoFilter(content, sheetId, selection) {
6382
+ const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
6383
+ const sheet = content.sheets[sheetIndex];
6384
+ if (!sheet) return null;
6385
+ const nextSheet = sheet.filter_select ? spreadsheetSheetWithoutAutoFilter(sheet) : spreadsheetSheetWithAutoFilter(sheet, selection);
6386
+ if (!nextSheet) return null;
6387
+ const sheets = [
6388
+ ...content.sheets
6389
+ ];
6390
+ sheets[sheetIndex] = {
6391
+ ...nextSheet,
6392
+ luckysheet_select_save: [
6393
+ finiteSpreadsheetSelection(selection)
6394
+ ]
6395
+ };
6396
+ return {
6397
+ ...content,
6398
+ sheets
6399
+ };
6400
+ }
6401
+ function spreadsheetSheetWithAutoFilter(sheet, selection) {
6402
+ const range = spreadsheetAutoFilterRange(sheet, selection);
6403
+ if (!range) return null;
6404
+ return {
6405
+ ...sheet,
6406
+ filter: {},
6407
+ filter_select: {
6408
+ row: [
6409
+ ...range.row
6410
+ ],
6411
+ column: [
6412
+ ...range.column
6413
+ ]
6414
+ }
6415
+ };
6416
+ }
6417
+ function spreadsheetSheetWithoutAutoFilter(sheet) {
6418
+ const hiddenByFilter = spreadsheetFilterHiddenRows(sheet.filter);
6419
+ const rowhidden = {
6420
+ ...sheet.config?.rowhidden
6421
+ };
6422
+ for (const row of hiddenByFilter)delete rowhidden[row];
6423
+ const config = sheet.config ? {
6424
+ ...sheet.config,
6425
+ rowhidden
6426
+ } : void 0;
6427
+ const { filter: _filter, filter_select: _selection, ...remaining } = sheet;
6428
+ return {
6429
+ ...remaining,
6430
+ config
6431
+ };
6432
+ }
6433
+ function spreadsheetFilterHiddenRows(filter) {
6434
+ const rows = new Set();
6435
+ for (const value of Object.values(filter ?? {})){
6436
+ if (!value || 'object' != typeof value) continue;
6437
+ const rowhidden = value.rowhidden;
6438
+ if (rowhidden && 'object' == typeof rowhidden) for (const row of Object.keys(rowhidden))rows.add(row);
6439
+ }
6440
+ return rows;
6441
+ }
6442
+ function spreadsheetCurrentRegion(sheet, selection) {
6443
+ const cells = spreadsheetCellReader(sheet);
6444
+ const bounds = spreadsheetUsedBounds(sheet);
6445
+ let startRow = selection.row[0];
6446
+ let endRow = selection.row[1];
6447
+ let startColumn = selection.column[0];
6448
+ let endColumn = selection.column[1];
6449
+ if (startRow > bounds.lastRow || startColumn > bounds.lastColumn || !spreadsheet_auto_filter_spreadsheetCellHasContent(cells(startRow, startColumn))) return null;
6450
+ let changed = true;
6451
+ while(changed){
6452
+ changed = false;
6453
+ while(startColumn > 0 && spreadsheetColumnHasContent(cells, startColumn - 1, startRow, endRow)){
6454
+ startColumn -= 1;
6455
+ changed = true;
6456
+ }
6457
+ while(endColumn < bounds.lastColumn && spreadsheetColumnHasContent(cells, endColumn + 1, startRow, endRow)){
6458
+ endColumn += 1;
6459
+ changed = true;
6460
+ }
6461
+ while(startRow > 0 && spreadsheetRowHasContent(cells, startRow - 1, startColumn, endColumn)){
6462
+ startRow -= 1;
6463
+ changed = true;
6464
+ }
6465
+ while(endRow < bounds.lastRow && spreadsheetRowHasContent(cells, endRow + 1, startColumn, endColumn)){
6466
+ endRow += 1;
6467
+ changed = true;
6468
+ }
6469
+ }
6470
+ return {
6471
+ row: [
6472
+ startRow,
6473
+ endRow
6474
+ ],
6475
+ column: [
6476
+ startColumn,
6477
+ endColumn
6478
+ ]
6479
+ };
6480
+ }
6481
+ function validSpreadsheetAutoFilterRange(sheet, range) {
6482
+ if (range.row[1] <= range.row[0]) return false;
6483
+ if (spreadsheetAutoFilterIntersectsTable(sheet, range)) return false;
6484
+ const cells = spreadsheetCellReader(sheet);
6485
+ if (!spreadsheetRowHasContent(cells, range.row[0], range.column[0], range.column[1])) return false;
6486
+ let dataFound = false;
6487
+ for(let row = range.row[0] + 1; row <= range.row[1]; row += 1)if (spreadsheetRowHasContent(cells, row, range.column[0], range.column[1])) {
6488
+ dataFound = true;
6489
+ break;
6490
+ }
6491
+ return dataFound && !spreadsheetRangeIntersectsMerge(sheet, range);
6492
+ }
6493
+ function spreadsheetAutoFilterIntersectsTable(sheet, range) {
6494
+ 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]);
6495
+ }
6496
+ function spreadsheetRangeIntersectsMerge(sheet, range) {
6497
+ return Object.values(sheet.config?.merge ?? {}).some((merge)=>{
6498
+ const endRow = merge.r + Math.max(merge.rs, 1) - 1;
6499
+ const endColumn = merge.c + Math.max(merge.cs, 1) - 1;
6500
+ return merge.r <= range.row[1] && endRow >= range.row[0] && merge.c <= range.column[1] && endColumn >= range.column[0];
6501
+ });
6502
+ }
6503
+ function spreadsheetRowHasContent(cellAt, row, startColumn, endColumn) {
6504
+ for(let column = startColumn; column <= endColumn; column += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
6505
+ return false;
6506
+ }
6507
+ function spreadsheetColumnHasContent(cellAt, column, startRow, endRow) {
6508
+ for(let row = startRow; row <= endRow; row += 1)if (spreadsheet_auto_filter_spreadsheetCellHasContent(cellAt(row, column))) return true;
6509
+ return false;
6510
+ }
6511
+ function spreadsheet_auto_filter_spreadsheetCellHasContent(cell) {
6512
+ if (!cell) return false;
6513
+ return [
6514
+ cell.v,
6515
+ cell.m,
6516
+ cell.f
6517
+ ].some((value)=>null != value && '' !== value);
6518
+ }
6519
+ function spreadsheetCellReader(sheet) {
6520
+ if (sheet.data) return (row, column)=>sheet.data?.[row]?.[column] ?? null;
6521
+ const cells = new Map((sheet.celldata ?? []).map((cell)=>[
6522
+ `${cell.r}:${cell.c}`,
6523
+ cell.v
6524
+ ]));
6525
+ return (row, column)=>cells.get(`${row}:${column}`) ?? null;
6526
+ }
6527
+ function spreadsheetUsedBounds(sheet) {
6528
+ let lastRow = Math.max((sheet.data?.length ?? 1) - 1, 0);
6529
+ let lastColumn = Math.max(sparseMatrixColumnCount(sheet.data) - 1, 0);
6530
+ for (const cell of sheet.celldata ?? []){
6531
+ lastRow = Math.max(lastRow, cell.r);
6532
+ lastColumn = Math.max(lastColumn, cell.c);
6533
+ }
6534
+ return {
6535
+ lastColumn,
6536
+ lastRow
6537
+ };
6538
+ }
6539
+ function normalizeAutoFilterSelection(selection) {
6540
+ const startRow = spreadsheet_auto_filter_finiteIndex(selection.row[0], 0);
6541
+ const endRow = spreadsheet_auto_filter_finiteIndex(selection.row[1], startRow);
6542
+ const startColumn = spreadsheet_auto_filter_finiteIndex(selection.column[0], 0);
6543
+ const endColumn = spreadsheet_auto_filter_finiteIndex(selection.column[1], startColumn);
6544
+ return {
6545
+ row: [
6546
+ Math.min(startRow, endRow),
6547
+ Math.max(startRow, endRow)
6548
+ ],
6549
+ column: [
6550
+ Math.min(startColumn, endColumn),
6551
+ Math.max(startColumn, endColumn)
6552
+ ]
6553
+ };
6554
+ }
6555
+ function normalizedFilterSelection(selection) {
6556
+ if (!selection?.row?.length || !selection.column?.length) return null;
6557
+ return normalizeAutoFilterSelection(selection);
6558
+ }
6559
+ function spreadsheet_auto_filter_finiteIndex(value, fallback) {
6560
+ return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
6561
+ }
6562
+ const ACCENTS = [
6563
+ '#64748b',
6564
+ '#2563eb',
6565
+ '#0f766e',
6566
+ '#7c3aed',
6567
+ '#c2410c',
6568
+ '#be123c',
6569
+ '#15803d'
6570
+ ];
6571
+ const DARK_ACCENTS = [
6572
+ '#1e293b',
6573
+ '#1e3a8a',
6574
+ '#134e4a',
6575
+ '#4c1d95',
6576
+ '#7c2d12',
6577
+ '#881337',
6578
+ '#14532d',
6579
+ '#0f172a',
6580
+ '#312e81',
6581
+ '#3f3f46',
6582
+ '#422006'
6583
+ ];
6584
+ let cachedChoices = null;
6585
+ function spreadsheetTableStyleChoices() {
6586
+ cachedChoices ??= Object.freeze([
6587
+ ...tableStyleFamilyChoices('light', 21),
6588
+ ...tableStyleFamilyChoices('medium', 28),
6589
+ ...tableStyleFamilyChoices('dark', 11)
6590
+ ]);
6591
+ return cachedChoices;
6592
+ }
6593
+ function spreadsheetTableStylePalette(style) {
6594
+ if ('none' === style.family) return null;
6595
+ const maximum = 'light' === style.family ? 21 : 'medium' === style.family ? 28 : 11;
6596
+ if (!Number.isInteger(style.number) || style.number < 1 || style.number > maximum) return null;
6597
+ if ('light' === style.family) return lightPalette(style.number);
6598
+ if ('medium' === style.family) return mediumPalette(style.number);
6599
+ return darkPalette(style.number);
6600
+ }
6601
+ function createSpreadsheetTableRenderResolver(tables) {
6602
+ 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]);
6603
+ let cachedRow = -1;
6604
+ let rowTables = [];
6605
+ return (row, column)=>{
6606
+ if (row !== cachedRow) {
6607
+ cachedRow = row;
6608
+ rowTables = renderable.filter((table)=>row >= table.range.row[0] && row <= table.range.row[1]);
6609
+ }
6610
+ const table = rowTables.find((candidate)=>column >= candidate.range.column[0] && column <= candidate.range.column[1]);
6611
+ return table ? spreadsheetTableCellRenderStyle(table, row, column) : null;
6612
+ };
6613
+ }
6614
+ function spreadsheetTableCellRenderStyle(table, row, column) {
6615
+ const palette = spreadsheetTableStylePalette(table.style);
6616
+ if (!palette) return null;
6617
+ const header = table.headerRow && row === table.range.row[0];
6618
+ const totals = table.totalsRow && row === table.range.row[1];
6619
+ const dataStart = table.range.row[0] + Number(table.headerRow);
6620
+ const dataIndex = Math.max(0, row - dataStart);
6621
+ const secondary = table.showRowStripes && dataIndex % 2 === 1;
6622
+ let background = header ? palette.header : totals ? palette.total : secondary ? palette.secondaryRow : palette.primaryRow;
6623
+ if (!header && !totals && table.showColumnStripes && (column - table.range.column[0]) % 2 === 1) background = mixHex(background, palette.stripeColumn, 0.42);
6624
+ const firstColumn = column === table.range.column[0];
6625
+ const lastColumn = column === table.range.column[1];
6626
+ return {
6627
+ background,
6628
+ bold: header || totals || table.showFirstColumn && firstColumn || table.showLastColumn && lastColumn,
6629
+ borderColor: palette.border,
6630
+ role: header ? 'header' : totals ? 'totals' : 'body',
6631
+ tableId: table.id,
6632
+ textColor: header ? palette.headerText : totals ? palette.totalText : palette.text
6633
+ };
6634
+ }
6635
+ function tableStyleFamilyChoices(family, count) {
6636
+ return Array.from({
6637
+ length: count
6638
+ }, (_, index)=>{
6639
+ const number = index + 1;
6640
+ const style = {
6641
+ family,
6642
+ number
6643
+ };
6644
+ return {
6645
+ label: `${familyLabel(family)} ${number}`,
6646
+ ooxmlName: `TableStyle${capitalize(family)}${number}`,
6647
+ palette: spreadsheetTableStylePalette(style),
6648
+ style
6649
+ };
6650
+ });
6651
+ }
6652
+ function lightPalette(number) {
6653
+ const accent = ACCENTS[(number - 1) % ACCENTS.length] ?? ACCENTS[0];
6654
+ const variant = Math.floor((number - 1) / ACCENTS.length);
6655
+ const headerWeight = [
6656
+ 0.84,
6657
+ 0.73,
6658
+ 0.62
6659
+ ][variant] ?? 0.72;
6660
+ return {
6661
+ border: mixHex(accent, '#ffffff', 0.5),
6662
+ header: mixHex(accent, '#ffffff', headerWeight),
6663
+ headerText: mixHex(accent, '#0f172a', 0.38),
6664
+ primaryRow: '#ffffff',
6665
+ secondaryRow: mixHex(accent, '#ffffff', 0.9 - 0.025 * variant),
6666
+ stripeColumn: mixHex(accent, '#ffffff', 0.82),
6667
+ text: '#1f2937',
6668
+ total: mixHex(accent, '#ffffff', 0.78),
6669
+ totalText: '#172033'
6670
+ };
6671
+ }
6672
+ function mediumPalette(number) {
6673
+ const accent = ACCENTS[(number - 1) % ACCENTS.length] ?? ACCENTS[0];
6674
+ const variant = Math.floor((number - 1) / ACCENTS.length);
6675
+ const header = mixHex(accent, '#111827', 0.08 * variant);
6676
+ return {
6677
+ border: mixHex(accent, '#ffffff', 0.34),
6678
+ header,
6679
+ headerText: '#ffffff',
6680
+ primaryRow: '#ffffff',
6681
+ secondaryRow: mixHex(accent, '#ffffff', 0.86 - 0.02 * variant),
6682
+ stripeColumn: mixHex(accent, '#ffffff', 0.76),
6683
+ text: '#172033',
6684
+ total: mixHex(accent, '#ffffff', 0.72),
6685
+ totalText: mixHex(accent, '#111827', 0.48)
6686
+ };
6687
+ }
6688
+ function darkPalette(number) {
6689
+ const accent = DARK_ACCENTS[number - 1] ?? DARK_ACCENTS[0];
6690
+ return {
6691
+ border: mixHex(accent, '#ffffff', 0.32),
6692
+ header: accent,
6693
+ headerText: '#ffffff',
6694
+ primaryRow: mixHex(accent, '#ffffff', 0.9),
6695
+ secondaryRow: mixHex(accent, '#ffffff', 0.78),
6696
+ stripeColumn: mixHex(accent, '#ffffff', 0.68),
6697
+ text: '#172033',
6698
+ total: mixHex(accent, '#ffffff', 0.62),
6699
+ totalText: mixHex(accent, '#111827', 0.34)
6700
+ };
6701
+ }
6702
+ function mixHex(left, right, rightWeight) {
6703
+ const weight = Math.min(1, Math.max(0, rightWeight));
6704
+ const leftRgb = parseHex(left);
6705
+ const rightRgb = parseHex(right);
6706
+ return `#${leftRgb.map((value, index)=>Math.round(value * (1 - weight) + (rightRgb[index] ?? value) * weight).toString(16).padStart(2, '0')).join('')}`;
6707
+ }
6708
+ function parseHex(value) {
6709
+ const hex = value.replace(/^#/, '');
6710
+ return [
6711
+ 0,
6712
+ 2,
6713
+ 4
6714
+ ].map((offset)=>Number.parseInt(hex.slice(offset, offset + 2), 16));
6715
+ }
6716
+ function familyLabel(family) {
6717
+ if ('light' === family) return '浅色';
6718
+ if ('medium' === family) return '中等';
6719
+ return '深色';
6720
+ }
6721
+ function capitalize(value) {
6722
+ return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
6723
+ }
6724
+ const MAX_TABLE_CONVERSION_DENSE_FOOTPRINT = 1000000;
6725
+ function canMaterializeSpreadsheetTableAppearance(table) {
6726
+ return 'none' === table.style.family || spreadsheet_cell_range_spreadsheetCellRangeArea(table.range) <= 100000;
6727
+ }
6728
+ function materializeSpreadsheetTableAppearance(content, sheetId, table) {
6729
+ if (!canMaterializeSpreadsheetTableAppearance(table)) return null;
6730
+ if ('none' === table.style.family) return content;
6731
+ const sheetIndex = content.sheets.findIndex((sheet)=>sheet.id === sheetId);
6732
+ const sheet = content.sheets[sheetIndex];
6733
+ const palette = spreadsheetTableStylePalette(table.style);
6734
+ if (!sheet || !palette) return null;
6735
+ const resolve = createSpreadsheetTableRenderResolver([
6736
+ table
6737
+ ]);
6738
+ const styledSheet = shouldMaterializeSpreadsheetTableAsSparseCells(sheet, table) ? materializeSpreadsheetTableSparseCells(sheet, table, resolve) : materializeSpreadsheetTableMatrix(sheet, table, resolve);
6739
+ const sheets = [
6740
+ ...content.sheets
6741
+ ];
6742
+ sheets[sheetIndex] = styledSheet;
6743
+ return setSpreadsheetCellBorders({
6744
+ ...content,
6745
+ sheets
6746
+ }, sheetId, table.range, {
6747
+ target: 'all',
6748
+ color: palette.border,
6749
+ style: 'thin'
6750
+ });
6751
+ }
6752
+ function shouldMaterializeSpreadsheetTableAsSparseCells(sheet, table) {
6753
+ if (void 0 === sheet.data) return true;
6754
+ const [startRow, endRow] = table.range.row;
6755
+ const requiredRowLength = table.range.column[1] + 1;
6756
+ let footprint = 0;
6757
+ let presentTableRows = 0;
6758
+ for (const [rowIndex, row] of sparseArrayEntries(sheet.data)){
6759
+ if (rowIndex >= startRow && rowIndex <= endRow) {
6760
+ presentTableRows += 1;
6761
+ footprint += Math.max(row.length, requiredRowLength);
6762
+ } else footprint += row.length;
6763
+ if (footprint > MAX_TABLE_CONVERSION_DENSE_FOOTPRINT) return true;
6764
+ }
6765
+ footprint += (endRow - startRow + 1 - presentTableRows) * requiredRowLength;
6766
+ return footprint > MAX_TABLE_CONVERSION_DENSE_FOOTPRINT;
6767
+ }
6768
+ function materializeSpreadsheetTableMatrix(sheet, table, resolve) {
6769
+ const source = sheet.data ?? [];
6770
+ const data = [];
6771
+ data.length = Math.max(source.length, table.range.row[1] + 1);
6772
+ for (const [row, value] of sparseArrayEntries(source))data[row] = value;
6773
+ for(let row = table.range.row[0]; row <= table.range.row[1]; row += 1){
6774
+ const sourceRow = source[row];
6775
+ const values = [];
6776
+ values.length = Math.max(sourceRow?.length ?? 0, table.range.column[1] + 1);
6777
+ for (const column of sparseArrayIndexes(sourceRow))values[column] = sourceRow?.[column];
6778
+ for(let column = table.range.column[0]; column <= table.range.column[1]; column += 1){
6779
+ const style = resolve(row, column);
6780
+ if (style) values[column] = spreadsheetCellWithTableAppearance(values[column], style);
6781
+ }
6782
+ data[row] = values;
6783
+ }
6784
+ return {
6785
+ ...sheet,
6786
+ data
6787
+ };
6788
+ }
6789
+ function materializeSpreadsheetTableSparseCells(sheet, table, resolve) {
6790
+ const celldata = [];
6791
+ if (void 0 !== sheet.data) for (const [row, values] of sparseArrayEntries(sheet.data))for (const column of sparseArrayIndexes(values)){
6792
+ const cell = values[column];
6793
+ if (null != cell) celldata.push({
6794
+ c: column,
6795
+ r: row,
6796
+ v: cell
6797
+ });
6798
+ }
6799
+ else celldata.push(...sheet.celldata ?? []);
6800
+ const indexes = new Map(celldata.map((entry, index)=>[
6801
+ `${entry.r}:${entry.c}`,
6802
+ index
6803
+ ]));
6804
+ 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){
6805
+ const style = resolve(row, column);
6806
+ if (!style) continue;
6807
+ const key = `${row}:${column}`;
6808
+ const index = indexes.get(key);
6809
+ const cell = spreadsheetCellWithTableAppearance(void 0 === index ? void 0 : celldata[index]?.v, style);
6810
+ if (void 0 === index) {
6811
+ indexes.set(key, celldata.length);
6812
+ celldata.push({
6813
+ c: column,
6814
+ r: row,
6815
+ v: cell
6816
+ });
6817
+ } else celldata[index] = {
6818
+ c: column,
6819
+ r: row,
6820
+ v: cell
6821
+ };
6822
+ }
6823
+ celldata.sort((left, right)=>left.r - right.r || left.c - right.c);
6824
+ const { data: _data, ...metadata } = sheet;
6825
+ return {
6826
+ ...metadata,
6827
+ celldata
6828
+ };
6829
+ }
6830
+ function spreadsheetCellWithTableAppearance(cell, style) {
6831
+ return {
6832
+ ...cell ?? {},
6833
+ bg: style.background,
6834
+ fc: style.textColor,
6835
+ ...style.bold ? {
6836
+ bl: 1
6837
+ } : {}
6838
+ };
6839
+ }
6840
+ function createSpreadsheetTableDialogSource(content, target) {
6841
+ const sheet = content.sheets.find((candidate)=>candidate.id === target.sheetId);
6842
+ if (!sheet) return null;
6843
+ const range = spreadsheetSelectionOrCurrentRegion(sheet, target.selection);
6844
+ if (!range) return null;
6845
+ let request = {
6846
+ headerRow: true,
6847
+ name: nextSpreadsheetTableName(content),
6848
+ range,
6849
+ sheetId: target.sheetId
6850
+ };
6851
+ if (!validateSpreadsheetTableRequest(content, request).ok) {
6852
+ request = {
6853
+ ...request,
6854
+ headerRow: false
6855
+ };
6856
+ if (!validateSpreadsheetTableRequest(content, request).ok) return null;
6857
+ }
6858
+ const rangeReference = formatSpreadsheetCellRanges([
6859
+ range
6860
+ ]);
6861
+ return {
6862
+ name: request.name,
6863
+ range,
6864
+ rangeReference,
6865
+ sheetId: target.sheetId,
6866
+ sheetName: sheet.name,
6867
+ value: {
6868
+ headerRow: request.headerRow,
6869
+ rangeReference
6870
+ }
6871
+ };
6872
+ }
6873
+ function spreadsheetTableRangeFromText(value) {
6874
+ const ranges = parseSpreadsheetCellRanges(value);
6875
+ return ranges?.length === 1 ? ranges[0] ?? null : null;
6876
+ }
6877
+ function validateSpreadsheetTableRequest(content, request) {
6878
+ const sheet = content.sheets.find((candidate)=>candidate.id === request.sheetId);
6879
+ if (!sheet) return tableError('sheet-not-found');
6880
+ if (sheet.isPivotTable || sheet.pivotTables?.length) return tableError('pivot-table');
6881
+ const range = spreadsheet_cell_range_normalizeSpreadsheetCellRange(request.range);
6882
+ if (!range) return tableError('invalid-range');
6883
+ const name = request.name.trim();
6884
+ if (!validSpreadsheetTableName(name)) return tableError('invalid-name');
6885
+ if (spreadsheetTableNameExists(content, name)) return tableError('name-conflict');
6886
+ const bounds = spreadsheetSheetBounds(sheet);
6887
+ if (range.row[1] > bounds.lastRow || range.column[1] > bounds.lastColumn) return tableError('out-of-bounds');
6888
+ const height = range.row[1] - range.row[0] + 1;
6889
+ if (height <= Number(request.headerRow)) return tableError('invalid-range');
6890
+ if (spreadsheet_cell_range_spreadsheetCellRangeArea(range) > 100000) return tableError('range-too-large');
6891
+ if (spreadsheetTableIntersectsMerge(sheet, range)) return tableError('merged-range');
6892
+ if ((sheet.tables ?? []).some((table)=>spreadsheetCellRangesIntersect(table.range, range))) return tableError('table-overlap');
6893
+ const autoFilter = spreadsheet_cell_range_normalizeSpreadsheetCellRange(sheet.filter_select ?? {
6894
+ row: [],
6895
+ column: []
6896
+ });
6897
+ if (autoFilter && spreadsheetCellRangesIntersect(autoFilter, range)) return tableError('auto-filter-overlap');
6898
+ if (!canMutateSpreadsheetCellRanges(sheet, [
6899
+ range
6900
+ ])) return tableError('protected-range');
6901
+ const style = request.style ?? {
6902
+ family: 'medium',
6903
+ number: 2
6904
+ };
6905
+ if (!validSpreadsheetTableStyle(style)) return tableError('invalid-style');
6906
+ const columns = spreadsheetTableColumnNames(sheet, range, request.headerRow);
6907
+ if (!columns.length || columns.some((column)=>!validTableColumnName(column))) return tableError('invalid-column-name');
6908
+ return {
6909
+ columns,
6910
+ name,
6911
+ ok: true,
6912
+ range,
6913
+ sheet
6914
+ };
6915
+ }
6916
+ function applySpreadsheetTable(content, request) {
6917
+ const validation = validateSpreadsheetTableRequest(content, request);
6918
+ if (!validation.ok) return null;
6919
+ const style = request.style ?? {
6920
+ family: 'medium',
6921
+ number: 2
6922
+ };
6923
+ const table = {
6924
+ id: createWorkId('spreadsheet-table'),
6925
+ name: validation.name,
6926
+ range: cloneRange(validation.range),
6927
+ columns: validation.columns.map((name)=>({
6928
+ name
6929
+ })),
6930
+ filters: [],
6931
+ headerRow: request.headerRow,
6932
+ totalsRow: false,
6933
+ style,
6934
+ showFirstColumn: false,
6935
+ showLastColumn: false,
6936
+ showRowStripes: 'none' !== style.family,
6937
+ showColumnStripes: false
6938
+ };
6939
+ const nextSheet = request.headerRow ? stampSpreadsheetTableHeaders(validation.sheet, validation.range, validation.columns) : validation.sheet;
6940
+ return replaceSpreadsheetTableSheet(content, {
6941
+ ...nextSheet,
6942
+ tables: [
6943
+ ...nextSheet.tables ?? [],
6944
+ table
6945
+ ]
6946
+ });
6947
+ }
6948
+ function updateSpreadsheetTable(content, sheetId, tableId, patch) {
6949
+ const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
6950
+ const table = sheet?.tables?.find((candidate)=>candidate.id === tableId);
6951
+ if (!sheet || !table) return null;
6952
+ const name = patch.name?.trim() ?? table.name;
6953
+ if (!validSpreadsheetTableName(name) || spreadsheetTableNameExists(content, name, tableId)) return null;
6954
+ const style = patch.style ?? table.style;
6955
+ if (!validSpreadsheetTableStyle(style)) return null;
6956
+ const showFirstColumn = patch.showFirstColumn ?? table.showFirstColumn;
6957
+ const showLastColumn = patch.showLastColumn ?? table.showLastColumn;
6958
+ const showRowStripes = patch.showRowStripes ?? table.showRowStripes;
6959
+ const showColumnStripes = patch.showColumnStripes ?? table.showColumnStripes;
6960
+ if ('none' === style.family && (showFirstColumn || showLastColumn || showRowStripes || showColumnStripes)) return null;
6961
+ const tables = sheet.tables?.map((candidate)=>candidate.id === tableId ? {
6962
+ ...candidate,
6963
+ name,
6964
+ ...void 0 === patch.name ? {} : {
6965
+ displayName: void 0
6966
+ },
6967
+ style,
6968
+ showFirstColumn,
6969
+ showLastColumn,
6970
+ showRowStripes,
6971
+ showColumnStripes
6972
+ } : candidate);
6973
+ return replaceSpreadsheetTableSheet(content, {
6974
+ ...sheet,
6975
+ tables
6976
+ });
6977
+ }
6978
+ function convertSpreadsheetTableToRange(content, sheetId, tableId) {
6979
+ const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
6980
+ const table = sheet?.tables?.find((candidate)=>candidate.id === tableId);
6981
+ if (!sheet || !table || spreadsheetTableHasStructuredReferences(content, table)) return null;
6982
+ const remaining = sheet.tables?.filter((candidate)=>candidate.id !== tableId);
6983
+ const withoutTable = replaceSpreadsheetTableSheet(content, {
6984
+ ...sheet,
6985
+ tables: remaining?.length ? remaining : void 0
6986
+ });
6987
+ return materializeSpreadsheetTableAppearance(withoutTable, sheetId, table);
6988
+ }
6989
+ function spreadsheetTableAtCell(sheet, row, column) {
6990
+ if (!sheet) return null;
6991
+ return (sheet.tables ?? []).find((table)=>spreadsheetCellRangeContains(table.range, row, column)) ?? null;
6992
+ }
6993
+ function spreadsheetTableFailureMessage(content, request) {
6994
+ const validation = validateSpreadsheetTableRequest(content, request);
6995
+ return validation.ok ? null : validation.message;
6996
+ }
6997
+ function nextSpreadsheetTableName(content) {
6998
+ for(let index = 1; index <= 65536; index += 1){
6999
+ const candidate = `Table${index}`;
7000
+ if (!spreadsheetTableNameExists(content, candidate)) return candidate;
7001
+ }
7002
+ return 'Table65536';
7003
+ }
7004
+ function spreadsheetTableNameExists(content, name, exceptTableId) {
7005
+ const normalized = name.toLocaleLowerCase();
7006
+ return content.sheets.some((sheet)=>(sheet.tables ?? []).some((table)=>table.id !== exceptTableId && [
7007
+ table.name,
7008
+ table.displayName
7009
+ ].some((candidate)=>candidate?.toLocaleLowerCase() === normalized))) || (content.namedRanges ?? []).some((range)=>range.name.trim().toLocaleLowerCase() === normalized);
7010
+ }
7011
+ function validSpreadsheetTableName(name) {
7012
+ return isValidSpreadsheetDefinedName(name) && !name.startsWith('_xlnm.') && ![
7013
+ 'r',
7014
+ 'c'
7015
+ ].includes(name.toLocaleLowerCase());
7016
+ }
7017
+ function validSpreadsheetTableStyle(style) {
7018
+ if ('none' === style.family) return true;
7019
+ if (!Number.isInteger(style.number)) return false;
7020
+ if ('light' === style.family) return style.number >= 1 && style.number <= 21;
7021
+ if ('medium' === style.family) return style.number >= 1 && style.number <= 28;
7022
+ return style.number >= 1 && style.number <= 11;
7023
+ }
7024
+ function spreadsheetTableColumnNames(sheet, range, headerRow) {
7025
+ const observed = new Set();
7026
+ const names = [];
7027
+ for(let column = range.column[0]; column <= range.column[1]; column += 1){
7028
+ const raw = headerRow ? spreadsheet_table_spreadsheetCellText(sheet, range.row[0], column).trim() : '';
7029
+ const base = validTableColumnName(raw) ? raw : `Column${column - range.column[0] + 1}`;
7030
+ let candidate = base;
7031
+ let suffix = 2;
7032
+ while(observed.has(candidate.toLocaleLowerCase())){
7033
+ const suffixText = String(suffix);
7034
+ candidate = `${truncateTableColumnName(base, 255 - suffixText.length)}${suffixText}`;
7035
+ suffix += 1;
7036
+ }
7037
+ observed.add(candidate.toLocaleLowerCase());
7038
+ names.push(candidate);
7039
+ }
7040
+ return names;
7041
+ }
7042
+ function validTableColumnName(name) {
7043
+ const characters = Array.from(name);
7044
+ return name.trim() === name && characters.length >= 1 && characters.length <= 255 && !characters.some((character)=>/\p{Cc}/u.test(character) || '\uFFFE' === character || '\uFFFF' === character);
7045
+ }
7046
+ function truncateTableColumnName(value, maximum) {
7047
+ return Array.from(value).slice(0, maximum).join('');
7048
+ }
7049
+ function spreadsheet_table_spreadsheetCellText(sheet, row, column) {
7050
+ const cell = sheet.data?.[row]?.[column] ?? sheet.celldata?.find((candidate)=>candidate.r === row && candidate.c === column)?.v;
7051
+ const value = cell?.m ?? cell?.v;
7052
+ return null == value ? '' : String(value);
7053
+ }
7054
+ function stampSpreadsheetTableHeaders(sheet, range, names) {
7055
+ if (void 0 !== sheet.data) {
7056
+ const data = cloneSparseMatrixShell(sheet.data);
7057
+ const sourceRow = sheet.data[range.row[0]];
7058
+ const row = cloneSparseRow(sourceRow);
7059
+ for (const [offset, name] of names.entries()){
7060
+ const column = range.column[0] + offset;
7061
+ row[column] = stampHeaderCell(sourceRow?.[column], name);
7062
+ }
7063
+ data[range.row[0]] = row;
7064
+ return {
7065
+ ...sheet,
7066
+ data
7067
+ };
7068
+ }
7069
+ const byCoordinate = new Map((sheet.celldata ?? []).map((entry)=>[
7070
+ `${entry.r}_${entry.c}`,
7071
+ entry
7072
+ ]));
7073
+ for (const [offset, name] of names.entries()){
7074
+ const column = range.column[0] + offset;
7075
+ const key = `${range.row[0]}_${column}`;
7076
+ const source = byCoordinate.get(key);
7077
+ byCoordinate.set(key, {
7078
+ r: range.row[0],
7079
+ c: column,
7080
+ v: stampHeaderCell(source?.v, name)
7081
+ });
7082
+ }
7083
+ return {
7084
+ ...sheet,
7085
+ celldata: [
7086
+ ...byCoordinate.values()
7087
+ ]
7088
+ };
7089
+ }
7090
+ function cloneSparseMatrixShell(source) {
7091
+ const data = [];
7092
+ data.length = source.length;
7093
+ for (const [row, value] of sparseArrayEntries(source))data[row] = value;
7094
+ return data;
7095
+ }
7096
+ function cloneSparseRow(source) {
7097
+ const row = [];
7098
+ if (!source) return row;
7099
+ row.length = source.length;
7100
+ for (const column of sparseArrayIndexes(source))row[column] = source[column];
7101
+ return row;
7102
+ }
7103
+ function stampHeaderCell(source, name) {
7104
+ return {
7105
+ ...source ?? {},
7106
+ m: name,
7107
+ v: name
7108
+ };
7109
+ }
7110
+ function spreadsheetTableIntersectsMerge(sheet, range) {
7111
+ return Object.values(sheet.config?.merge ?? {}).some((merge)=>spreadsheetCellRangesIntersect(range, {
7112
+ row: [
7113
+ merge.r,
7114
+ merge.r + Math.max(1, merge.rs) - 1
7115
+ ],
7116
+ column: [
7117
+ merge.c,
7118
+ merge.c + Math.max(1, merge.cs) - 1
7119
+ ]
7120
+ }));
7121
+ }
7122
+ function replaceSpreadsheetTableSheet(content, nextSheet) {
7123
+ return {
7124
+ ...content,
7125
+ sheets: content.sheets.map((sheet)=>sheet.id === nextSheet.id ? nextSheet : sheet)
7126
+ };
7127
+ }
7128
+ function spreadsheetTableHasStructuredReferences(content, table) {
7129
+ const aliases = [
7130
+ table.name,
7131
+ table.displayName
7132
+ ].filter((value)=>Boolean(value));
7133
+ const patterns = aliases.map((alias)=>new RegExp(`${escapeRegExp(alias)}\\s*\\[`, 'i'));
7134
+ const matches = (value)=>'string' == typeof value && patterns.some((pattern)=>pattern.test(value));
7135
+ for (const sheet of content.sheets){
7136
+ for (const [, row] of sparseArrayEntries(sheet.data))for (const [, cell] of sparseArrayEntries(row))if (matches(cell?.f)) return true;
7137
+ for (const entry of sheet.celldata ?? [])if (matches(entry.v?.f)) return true;
7138
+ 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;
7139
+ }
7140
+ return (content.namedRanges ?? []).some((range)=>matches(range.reference));
7141
+ }
7142
+ function escapeRegExp(value) {
7143
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7144
+ }
7145
+ function cloneRange(range) {
7146
+ return {
7147
+ row: [
7148
+ ...range.row
7149
+ ],
7150
+ column: [
7151
+ ...range.column
7152
+ ]
7153
+ };
7154
+ }
7155
+ function tableError(code) {
7156
+ const messages = {
7157
+ 'auto-filter-overlap': '表格区域不能与工作表自动筛选区域重叠。',
7158
+ 'invalid-column-name': '表格列名必须唯一且不超过 255 个字符。',
7159
+ 'invalid-name': '表格名称必须是有效且不类似单元格引用的标识符。',
7160
+ 'invalid-range': '表格至少需要一行数据。',
7161
+ 'invalid-style': '表格样式身份无效。',
7162
+ 'merged-range': '请先取消表格区域内的合并单元格。',
7163
+ 'name-conflict': '表格名称已被其他表格或名称使用。',
7164
+ 'out-of-bounds': '表格区域超出当前工作表边界。',
7165
+ 'pivot-table': '数据透视表工作表不能创建普通表格。',
7166
+ 'protected-range': '受保护或只读区域不能创建表格。',
7167
+ 'range-too-large': `一次最多可创建 ${100000..toLocaleString()} 个单元格的表格。`,
7168
+ 'sheet-not-found': '找不到目标工作表。',
7169
+ 'table-overlap': '表格区域不能与其他表格重叠。'
7170
+ };
7171
+ return {
7172
+ code,
7173
+ message: messages[code],
7174
+ ok: false
7175
+ };
7176
+ }
7177
+ function createSpreadsheetTableExtension() {
7178
+ return createOfficeEditorExtension({
7179
+ name: 'spreadsheetTables',
7180
+ addCommands: ()=>({
7181
+ openTable: {
7182
+ canExecute: canOpenSpreadsheetTable,
7183
+ execute: openSpreadsheetTable
7184
+ },
7185
+ applyTable: {
7186
+ canExecute: (context, request)=>context.editable && validateSpreadsheetTableRequest(context.content, request).ok,
7187
+ execute: applySpreadsheetTableCommand
7188
+ },
7189
+ updateTable: {
7190
+ canExecute: canUpdateSpreadsheetTable,
7191
+ execute: updateSpreadsheetTableCommand
7192
+ },
7193
+ convertTableToRange: {
7194
+ canExecute: canConvertSpreadsheetTableToRange,
7195
+ execute: convertSpreadsheetTableToRangeCommand
7196
+ }
7197
+ }),
7198
+ addKeyboardShortcuts: ()=>({
7199
+ [spreadsheetCommandCatalog.table.shortcut.editor["0"]]: ({ can, commands }, event)=>runSpreadsheetTableShortcut(event, can.openTable, commands.openTable)
7200
+ })
7201
+ });
7202
+ }
7203
+ function canOpenSpreadsheetTable(context) {
7204
+ const target = liveSpreadsheetTableTarget(context);
7205
+ const sheet = context.content.sheets.find((candidate)=>candidate.id === target?.sheetId);
7206
+ const focus = target?.selection;
7207
+ const row = focus?.row_focus ?? focus?.row[0] ?? 0;
7208
+ const column = focus?.column_focus ?? focus?.column[0] ?? 0;
7209
+ return Boolean(context.editable && context.table.canOpen && context.workbook && target && target.sheetId === context.activeSheetId && sheet && !sheet.isPivotTable && !sheet.pivotTables?.length && !spreadsheetTableAtCell(sheet, row, column));
7210
+ }
7211
+ function openSpreadsheetTable(context) {
7212
+ const target = liveSpreadsheetTableTarget(context);
7213
+ return Boolean(target && canOpenSpreadsheetTable(context) && context.table.open(target));
7214
+ }
7215
+ function applySpreadsheetTableCommand(context, request) {
7216
+ if (!context.editable) return false;
7217
+ const next = applySpreadsheetTable(context.content, request);
7218
+ if (!next) return false;
7219
+ context.onChange(next);
7220
+ return true;
7221
+ }
7222
+ function canUpdateSpreadsheetTable(context, sheetId, tableId, patch) {
7223
+ return Boolean(context.editable && updateSpreadsheetTable(context.content, sheetId, tableId, patch));
7224
+ }
7225
+ function updateSpreadsheetTableCommand(context, sheetId, tableId, patch) {
7226
+ if (!context.editable) return false;
7227
+ const next = updateSpreadsheetTable(context.content, sheetId, tableId, patch);
7228
+ if (!next) return false;
7229
+ context.onChange(next);
7230
+ return true;
7231
+ }
7232
+ function canConvertSpreadsheetTableToRange(context, sheetId, tableId) {
7233
+ return Boolean(context.editable && context.content.sheets.find((sheet)=>sheet.id === sheetId)?.tables?.some((table)=>table.id === tableId && canMaterializeSpreadsheetTableAppearance(table)));
7234
+ }
7235
+ function convertSpreadsheetTableToRangeCommand(context, sheetId, tableId) {
7236
+ if (!context.editable) return false;
7237
+ const next = convertSpreadsheetTableToRange(context.content, sheetId, tableId);
7238
+ if (!next) return false;
7239
+ context.onChange(next);
7240
+ return true;
7241
+ }
7242
+ function liveSpreadsheetTableTarget(context) {
7243
+ if (!context.workbook || !context.targetSheetId) return null;
7244
+ const selection = context.workbook.getSelection()?.at(-1) ?? context.fallbackRange;
7245
+ return {
7246
+ sheetId: context.targetSheetId,
7247
+ selection: finiteSpreadsheetSelection(selection)
7248
+ };
7249
+ }
7250
+ function runSpreadsheetTableShortcut(event, canExecute, execute) {
7251
+ if (event.repeat || event.isComposing || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !isSpreadsheetGridKeyboardTarget(event.target)) return false;
7252
+ if (!event.repeat && canExecute()) execute();
7253
+ return true;
7254
+ }
7255
+ function createSpreadsheetEditorExtensions() {
7256
+ return [
7257
+ createOfficeEditorExtension({
7258
+ name: 'spreadsheetDocument',
7259
+ addCommands: ()=>({
7260
+ setSpreadsheetContent: {
7261
+ canExecute: (context)=>context.editable,
7262
+ execute: (context, content)=>{
7263
+ if (!context.editable) return false;
7264
+ context.onChange(content);
7265
+ return true;
7266
+ }
7267
+ }
7268
+ })
7269
+ }),
7270
+ createSpreadsheetCellFormatExtension(),
7271
+ createSpreadsheetCellBorderExtension(),
7272
+ createSpreadsheetCellStyleExtension(),
7273
+ createSpreadsheetNumberFormatExtension(),
7274
+ createSpreadsheetNavigationExtension(),
7275
+ createSpreadsheetDataValidationExtension(),
7276
+ createSpreadsheetHyperlinkExtension(),
7277
+ createSpreadsheetTableExtension(),
7278
+ createSpreadsheetAutoSumExtension(),
7279
+ createSpreadsheetCellFillExtension(),
7280
+ createOfficeEditorExtension({
7281
+ name: 'spreadsheetFormatPainter',
7282
+ addCommands: ()=>({
7283
+ activateFormatPainter: {
7284
+ canExecute: ({ formatPainter })=>formatPainter.canActivate,
7285
+ execute: ({ formatPainter }, mode)=>formatPainter.canActivate && formatPainter.activate(mode)
7286
+ },
7287
+ applyFormatPainter: {
7288
+ canExecute: ({ formatPainter })=>formatPainter.active,
7289
+ execute: ({ formatPainter }, target)=>formatPainter.active && formatPainter.applySelection(target)
7290
+ },
7291
+ cancelFormatPainter: {
7292
+ canExecute: ({ formatPainter })=>formatPainter.active,
7293
+ execute: ({ formatPainter })=>formatPainter.active && formatPainter.cancel()
7294
+ }
7295
+ })
7296
+ }),
7297
+ createOfficeEditorExtension({
7298
+ name: 'spreadsheetAutoFilter',
7299
+ addCommands: ()=>({
7300
+ openAutoFilterMenu: {
7301
+ canExecute: ({ autoFilter })=>autoFilter.canOpenMenu,
7302
+ execute: ({ autoFilter })=>autoFilter.canOpenMenu && autoFilter.openMenu()
7303
+ },
7304
+ toggleAutoFilter: {
7305
+ canExecute: ({ autoFilter })=>autoFilter.canToggle,
7306
+ execute: ({ autoFilter })=>autoFilter.canToggle && autoFilter.toggle()
7307
+ }
7308
+ })
7309
+ }),
7310
+ createOfficeEditorExtension({
7311
+ name: 'spreadsheetClipboard',
7312
+ addCommands: ()=>({
7313
+ copySelection: {
7314
+ canExecute: ({ clipboard })=>clipboard.canCopySelection,
7315
+ execute: ({ clipboard })=>clipboard.canCopySelection && clipboard.copySelection()
7316
+ },
7317
+ cutSelection: {
7318
+ canExecute: ({ clipboard })=>clipboard.canCutSelection,
7319
+ execute: ({ clipboard })=>clipboard.canCutSelection && clipboard.cutSelection()
7320
+ },
7321
+ pasteSelection: {
7322
+ canExecute: ({ clipboard })=>clipboard.canPasteSelection,
7323
+ execute: ({ clipboard })=>clipboard.canPasteSelection && clipboard.pasteSelection()
7324
+ },
7325
+ pasteSpecial: {
7326
+ canExecute: ({ clipboard }, content)=>clipboard.canPasteSpecial(content),
7327
+ execute: ({ clipboard }, content)=>clipboard.canPasteSpecial(content) && clipboard.pasteSpecial(content)
7328
+ },
7329
+ openPasteSpecial: {
7330
+ canExecute: ({ clipboard })=>clipboard.canOpenPasteSpecial,
7331
+ execute: ({ clipboard })=>clipboard.canOpenPasteSpecial && clipboard.openPasteSpecial()
7332
+ }
7333
+ })
7334
+ }),
7335
+ createOfficeEditorExtension({
7336
+ name: 'spreadsheetHistory',
7337
+ addCommands: ()=>({
7338
+ redo: {
7339
+ canExecute: (context)=>context.editable && (context.history?.canRedo ?? false),
7340
+ execute: (context)=>context.history?.redo() ?? false
7341
+ },
7342
+ undo: {
7343
+ canExecute: (context)=>context.editable && (context.history?.canUndo ?? false),
7344
+ execute: (context)=>context.history?.undo() ?? false
7345
+ }
7346
+ })
7347
+ }),
7348
+ createOfficeEditorExtension({
7349
+ name: 'spreadsheetSheets',
7350
+ addCommands: ()=>({
7351
+ activateSheet: {
7352
+ canExecute: (context, sheetId)=>{
7353
+ const sheet = context.content.sheets.find((candidate)=>candidate.id === sheetId);
7354
+ return Boolean(sheet && (context.editable || context.view && 1 !== sheet.hide));
7355
+ },
7356
+ execute: (context, sheetId)=>{
7357
+ const sheet = context.content.sheets.find((candidate)=>candidate.id === sheetId);
7358
+ if (!sheet) return false;
7359
+ if (context.view && 1 !== sheet.hide) return context.view?.activateSheet(sheetId) ?? false;
7360
+ if (!context.editable) return false;
7361
+ return applySpreadsheetSheetChange(context, activateSpreadsheetSheet(context.content, sheetId));
7362
+ }
7363
+ },
7364
+ addSheet: {
7365
+ canExecute: (context)=>context.editable,
7366
+ execute: (context)=>applySpreadsheetSheetChange(context, addSpreadsheetSheet(context.content))
7367
+ },
7368
+ deleteSheet: {
7369
+ canExecute: (context, sheetId)=>Boolean(context.editable && context.content.sheets.length > 1 && context.content.sheets.some((sheet)=>sheet.id === sheetId)),
7370
+ execute: (context, sheetId)=>applySpreadsheetSheetChange(context, deleteSpreadsheetSheet(context.content, sheetId))
7371
+ },
7372
+ duplicateSheet: {
7373
+ canExecute: (context, sheetId)=>context.editable && context.content.sheets.some((sheet)=>sheet.id === sheetId),
7374
+ execute: (context, sheetId)=>applySpreadsheetSheetChange(context, duplicateSpreadsheetSheet(context.content, sheetId))
7375
+ },
7376
+ hideSheet: {
7377
+ canExecute: (context, sheetId)=>context.editable && context.content.sheets.filter((sheet)=>1 !== sheet.hide).length > 1 && context.content.sheets.some((sheet)=>sheet.id === sheetId && 1 !== sheet.hide),
7378
+ execute: (context, sheetId)=>applySpreadsheetSheetChange(context, hideSpreadsheetSheet(context.content, sheetId, true))
7379
+ },
7380
+ moveSheet: {
7381
+ canExecute: (context, sheetId, direction)=>Boolean(context.editable && moveSpreadsheetSheet(context.content, sheetId, direction)),
7382
+ execute: (context, sheetId, direction)=>applySpreadsheetSheetChange(context, moveSpreadsheetSheet(context.content, sheetId, direction))
7383
+ },
7384
+ renameSheet: {
7385
+ canExecute: (context, sheetId, name)=>Boolean(context.editable && renameSpreadsheetSheet(context.content, sheetId, name)),
7386
+ execute: (context, sheetId, name)=>applySpreadsheetSheetChange(context, renameSpreadsheetSheet(context.content, sheetId, name))
7387
+ },
7388
+ setSheetColor: {
7389
+ canExecute: (context, sheetId, color)=>Boolean(context.editable && setSpreadsheetSheetColor(context.content, sheetId, color)),
7390
+ execute: (context, sheetId, color)=>applySpreadsheetSheetChange(context, setSpreadsheetSheetColor(context.content, sheetId, color))
7391
+ }
7392
+ })
7393
+ }),
7394
+ createOfficeEditorExtension({
7395
+ name: 'spreadsheetKeyboardShortcuts',
7396
+ addKeyboardShortcuts: ()=>({
6135
7397
  'Mod-a': ({ can, commands }, event)=>selectSpreadsheetFormulaBarContents(event) || runSpreadsheetSelectionScopeShortcut(event, can.selectCellRange, commands.selectCellRange, 'all'),
6136
7398
  'Mod-b': ({ can, commands, context }, event)=>runSpreadsheetCellFormatShortcut(event, context, can.setCellFormat, commands.setCellFormat, 'bl'),
6137
7399
  'Mod-c': ({ can, commands }, event)=>runSpreadsheetClipboardShortcut(event, can.copySelection, commands.copySelection),
@@ -6283,9 +7545,16 @@ function canInsertSelectedStructure(context, axis, _position) {
6283
7545
  const [start, end] = spreadsheetStructureRange(range, axis);
6284
7546
  const count = end - start + 1;
6285
7547
  const maximum = 'row' === axis ? 10000 : 1000;
6286
- return spreadsheetStructureExtent(context, axis) + count < maximum;
7548
+ return spreadsheetStructureExtent(context, axis) + count < maximum && canApplySpreadsheetTableStructureChange(context.content.sheets.find((sheet)=>sheet.id === context.targetSheetId), {
7549
+ axis,
7550
+ count,
7551
+ direction: 'before' === _position ? 'lefttop' : 'rightbottom',
7552
+ index: 'before' === _position ? start : end,
7553
+ kind: 'insert'
7554
+ });
6287
7555
  }
6288
7556
  function insertSelectedStructure(context, axis, position) {
7557
+ if (!canInsertSelectedStructure(context, axis, position)) return false;
6289
7558
  if (!context.workbook || !context.targetSheetId) return false;
6290
7559
  const [start, end] = spreadsheetStructureRange(liveRange(context), axis);
6291
7560
  const before = 'before' === position;
@@ -6301,9 +7570,15 @@ function insertSelectedStructure(context, axis, position) {
6301
7570
  function canDeleteSelectedStructure(context, axis) {
6302
7571
  if (!canEditSelectedCells(context)) return false;
6303
7572
  const [start, end] = spreadsheetStructureRange(liveRange(context), axis);
6304
- return end - start + 1 < spreadsheetStructureExtent(context, axis);
7573
+ return end - start + 1 < spreadsheetStructureExtent(context, axis) && canApplySpreadsheetTableStructureChange(context.content.sheets.find((sheet)=>sheet.id === context.targetSheetId), {
7574
+ axis,
7575
+ end,
7576
+ kind: 'delete',
7577
+ start
7578
+ });
6305
7579
  }
6306
7580
  function deleteSelectedStructure(context, axis) {
7581
+ if (!canDeleteSelectedStructure(context, axis)) return false;
6307
7582
  if (!context.workbook || !context.targetSheetId) return false;
6308
7583
  const range = liveRange(context);
6309
7584
  const [start, end] = spreadsheetStructureRange(range, axis);
@@ -8654,89 +9929,350 @@ function SpreadsheetPrintSettingsPanel({ content, onChange }) {
8654
9929
  /*#__PURE__*/ jsxs("div", {
8655
9930
  className: "actions",
8656
9931
  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: "取消更改"
9932
+ error && /*#__PURE__*/ jsx(InlineNotice, {
9933
+ className: "work-office-form-error",
9934
+ tone: "danger",
9935
+ role: "alert",
9936
+ children: error
9937
+ }),
9938
+ /*#__PURE__*/ jsx(button_Button, {
9939
+ tone: "secondary",
9940
+ disabled: dirty || !savedArea && !savedTitles && !savedPageBreaks && !savedPageSetup,
9941
+ onClick: clearSettings,
9942
+ children: "清除"
9943
+ }),
9944
+ /*#__PURE__*/ jsx(button_Button, {
9945
+ tone: "secondary",
9946
+ disabled: !dirty,
9947
+ onClick: cancelDraft,
9948
+ children: "取消更改"
9949
+ }),
9950
+ /*#__PURE__*/ jsx(button_Button, {
9951
+ type: "submit",
9952
+ tone: "primary",
9953
+ disabled: !sheetId || !dirty,
9954
+ children: "保存打印设置"
9955
+ })
9956
+ ]
9957
+ })
9958
+ ]
9959
+ });
9960
+ }
9961
+ function spreadsheetPrintSettingsDraft(content, sheetId, empty = false) {
9962
+ const area = empty ? void 0 : content.printAreas?.find((item)=>item.sheetId === sheetId);
9963
+ const titles = empty ? void 0 : content.printTitles?.find((item)=>item.sheetId === sheetId);
9964
+ const pageBreaks = empty ? void 0 : content.pageBreaks?.find((item)=>item.sheetId === sheetId);
9965
+ const pageSetup = empty ? void 0 : content.pageSetups?.find((item)=>item.sheetId === sheetId);
9966
+ return {
9967
+ reference: area?.reference ?? '',
9968
+ titleRows: titles?.rows ?? '',
9969
+ titleColumns: titles?.columns ?? '',
9970
+ rowPageBreaks: formatSpreadsheetRowPageBreaks(pageBreaks?.rows),
9971
+ columnPageBreaks: formatSpreadsheetColumnPageBreaks(pageBreaks?.columns),
9972
+ pageSetup: effectiveSpreadsheetPageSetup(pageSetup)
9973
+ };
9974
+ }
9975
+ function spreadsheetMaximumRow(sheet) {
9976
+ return Math.max(0, (sheet?.row ?? 1) - 1, (sheet?.data?.length ?? 1) - 1);
9977
+ }
9978
+ function spreadsheetMaximumColumn(sheet) {
9979
+ let maximum = Math.max(0, (sheet?.column ?? 1) - 1);
9980
+ for (const row of sheet?.data ?? [])maximum = Math.max(maximum, row.length - 1);
9981
+ return maximum;
9982
+ }
9983
+ function PageMarginField({ label, value, onChange }) {
9984
+ return /*#__PURE__*/ jsxs("div", {
9985
+ className: "work-office-field",
9986
+ children: [
9987
+ /*#__PURE__*/ jsx("span", {
9988
+ children: label
9989
+ }),
9990
+ /*#__PURE__*/ jsx(CommittedOfficeNumberField, {
9991
+ ariaLabel: label,
9992
+ min: 0,
9993
+ max: 100,
9994
+ step: 0.01,
9995
+ value: value,
9996
+ normalizeValue: (nextValue)=>normalizePrintDecimal(nextValue, 0, 100),
9997
+ onValueCommit: onChange
9998
+ })
9999
+ ]
10000
+ });
10001
+ }
10002
+ function normalizePrintInteger(value, minimum, maximum) {
10003
+ if (!value.trim()) return null;
10004
+ const number = Number(value);
10005
+ return Number.isFinite(number) ? Math.min(maximum, Math.max(minimum, Math.round(number))) : null;
10006
+ }
10007
+ function normalizePrintDecimal(value, minimum, maximum) {
10008
+ if (!value.trim()) return null;
10009
+ const number = Number(value);
10010
+ return Number.isFinite(number) ? Math.min(maximum, Math.max(minimum, Math.round(100 * number) / 100)) : null;
10011
+ }
10012
+ function validPageSetup(pageSetup) {
10013
+ const validInteger = (value, minimum, maximum)=>Number.isInteger(value) && value >= minimum && value <= maximum;
10014
+ 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);
10015
+ }
10016
+ function SpreadsheetTableDesignRibbon({ can, commands, sheetId, table }) {
10017
+ return /*#__PURE__*/ jsxs(Fragment, {
10018
+ children: [
10019
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
10020
+ label: "属性",
10021
+ priority: "high",
10022
+ children: [
10023
+ /*#__PURE__*/ jsx(SpreadsheetTableNameControl, {
10024
+ can: can,
10025
+ commands: commands,
10026
+ sheetId: sheetId,
10027
+ table: table
10028
+ }),
10029
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
10030
+ label: "转换为区域",
10031
+ disabled: !can.convertTableToRange(sheetId, table.id),
10032
+ onClick: ()=>{
10033
+ if (!commands.convertTableToRange(sheetId, table.id)) showToast('无法转换为区域。请先移除引用此表格的结构化引用。', 'error');
10034
+ },
10035
+ children: /*#__PURE__*/ jsx(TableProperties, {
10036
+ size: 19
10037
+ })
10038
+ })
10039
+ ]
10040
+ }),
10041
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
10042
+ label: "表格样式",
10043
+ priority: "high",
10044
+ children: /*#__PURE__*/ jsx(SpreadsheetTableStyleGallery, {
10045
+ commands: commands,
10046
+ sheetId: sheetId,
10047
+ table: table
10048
+ })
10049
+ }),
10050
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
10051
+ label: "表格样式选项",
10052
+ children: [
10053
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10054
+ label: "首列",
10055
+ active: table.showFirstColumn,
10056
+ icon: /*#__PURE__*/ jsx(Columns3, {
10057
+ size: 17
10058
+ }),
10059
+ disabled: !can.updateTable(sheetId, table.id, {
10060
+ showFirstColumn: !table.showFirstColumn
10061
+ }),
10062
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10063
+ showFirstColumn: !table.showFirstColumn
10064
+ })
10065
+ }),
10066
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10067
+ label: "末列",
10068
+ active: table.showLastColumn,
10069
+ icon: /*#__PURE__*/ jsx(Columns3, {
10070
+ size: 17
10071
+ }),
10072
+ disabled: !can.updateTable(sheetId, table.id, {
10073
+ showLastColumn: !table.showLastColumn
10074
+ }),
10075
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10076
+ showLastColumn: !table.showLastColumn
10077
+ })
10078
+ }),
10079
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10080
+ label: "行条纹",
10081
+ active: table.showRowStripes,
10082
+ icon: /*#__PURE__*/ jsx(Rows3, {
10083
+ size: 17
10084
+ }),
10085
+ disabled: !can.updateTable(sheetId, table.id, {
10086
+ showRowStripes: !table.showRowStripes
10087
+ }),
10088
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10089
+ showRowStripes: !table.showRowStripes
10090
+ })
10091
+ }),
10092
+ /*#__PURE__*/ jsx(SpreadsheetTableOption, {
10093
+ label: "列条纹",
10094
+ active: table.showColumnStripes,
10095
+ icon: /*#__PURE__*/ jsx(Columns3, {
10096
+ size: 17
10097
+ }),
10098
+ disabled: !can.updateTable(sheetId, table.id, {
10099
+ showColumnStripes: !table.showColumnStripes
10100
+ }),
10101
+ onToggle: ()=>commands.updateTable(sheetId, table.id, {
10102
+ showColumnStripes: !table.showColumnStripes
10103
+ })
10104
+ })
10105
+ ]
10106
+ })
10107
+ ]
10108
+ });
10109
+ }
10110
+ function SpreadsheetTableNameControl({ can, commands, sheetId, table }) {
10111
+ const [name, setName] = useState(table.name);
10112
+ useEffect(()=>setName(table.name), [
10113
+ table.id,
10114
+ table.name
10115
+ ]);
10116
+ const commit = ()=>{
10117
+ const candidate = name.trim();
10118
+ if (!candidate || candidate === table.name) return void setName(table.name);
10119
+ const patch = {
10120
+ name: candidate
10121
+ };
10122
+ if (!can.updateTable(sheetId, table.id, patch) || !commands.updateTable(sheetId, table.id, patch)) {
10123
+ setName(table.name);
10124
+ showToast('表格名称无效或已被使用。', 'error');
10125
+ }
10126
+ };
10127
+ return /*#__PURE__*/ jsxs("label", {
10128
+ className: "work-spreadsheet-table-name",
10129
+ children: [
10130
+ /*#__PURE__*/ jsx("span", {
10131
+ children: "表格名称"
10132
+ }),
10133
+ /*#__PURE__*/ jsx("input", {
10134
+ type: "text",
10135
+ "aria-label": "表格名称",
10136
+ autoCapitalize: "none",
10137
+ spellCheck: false,
10138
+ value: name,
10139
+ onBlur: commit,
10140
+ onChange: (event)=>setName(event.currentTarget.value),
10141
+ onKeyDown: (event)=>{
10142
+ if ('Enter' === event.key) {
10143
+ event.preventDefault();
10144
+ commit();
10145
+ } else if ('Escape' === event.key) {
10146
+ event.preventDefault();
10147
+ setName(table.name);
10148
+ }
10149
+ }
10150
+ })
10151
+ ]
10152
+ });
10153
+ }
10154
+ function SpreadsheetTableStyleGallery({ commands, sheetId, table }) {
10155
+ const choices = spreadsheetTableStyleChoices();
10156
+ const selected = choices.find((choice)=>spreadsheetTableUsesStyle(table.style, choice.style));
10157
+ const families = [
10158
+ {
10159
+ id: 'light',
10160
+ label: '浅色'
10161
+ },
10162
+ {
10163
+ id: 'medium',
10164
+ label: '中等'
10165
+ },
10166
+ {
10167
+ id: 'dark',
10168
+ label: '深色'
10169
+ }
10170
+ ];
10171
+ return /*#__PURE__*/ jsx(Popover, {
10172
+ label: "表格样式",
10173
+ panelLabel: "表格样式库",
10174
+ panelRole: "menu",
10175
+ portal: true,
10176
+ className: "work-spreadsheet-table-style-root",
10177
+ panelClassName: "work-spreadsheet-table-style-menu",
10178
+ focusFirstOnOpen: true,
10179
+ onPanelKeyDown: moveSpreadsheetTableStyleFocus,
10180
+ trigger: (triggerProps, { open })=>/*#__PURE__*/ jsxs("button", {
10181
+ ...triggerProps,
10182
+ type: "button",
10183
+ className: `with-label work-spreadsheet-table-style-trigger${open ? ' active' : ''}`,
10184
+ title: `表格样式(当前:${selected?.label ?? '无'})`,
10185
+ children: [
10186
+ /*#__PURE__*/ jsx(SpreadsheetTableStylePreview, {
10187
+ choice: selected ?? choices[0]
8674
10188
  }),
8675
- /*#__PURE__*/ jsx(button_Button, {
8676
- type: "submit",
8677
- tone: "primary",
8678
- disabled: !sheetId || !dirty,
8679
- children: "保存打印设置"
10189
+ /*#__PURE__*/ jsx("span", {
10190
+ children: "表格样式"
8680
10191
  })
8681
10192
  ]
10193
+ }),
10194
+ children: (close)=>/*#__PURE__*/ jsx(Fragment, {
10195
+ children: families.map((family)=>/*#__PURE__*/ jsxs("fieldset", {
10196
+ className: "work-spreadsheet-table-style-family",
10197
+ "data-office-menu-grid": true,
10198
+ "aria-label": family.label,
10199
+ children: [
10200
+ /*#__PURE__*/ jsx("span", {
10201
+ children: family.label
10202
+ }),
10203
+ /*#__PURE__*/ jsx("div", {
10204
+ children: choices.filter((choice)=>choice.style.family === family.id).map((choice)=>{
10205
+ const checked = spreadsheetTableUsesStyle(table.style, choice.style);
10206
+ return /*#__PURE__*/ jsx("button", {
10207
+ type: "button",
10208
+ role: "menuitemradio",
10209
+ tabIndex: -1,
10210
+ "aria-label": `应用表格样式:${choice.label}`,
10211
+ "aria-checked": checked,
10212
+ title: choice.label,
10213
+ onClick: ()=>{
10214
+ close();
10215
+ commands.updateTable(sheetId, table.id, {
10216
+ style: choice.style
10217
+ });
10218
+ },
10219
+ children: /*#__PURE__*/ jsx(SpreadsheetTableStylePreview, {
10220
+ choice: choice
10221
+ })
10222
+ }, choice.ooxmlName);
10223
+ })
10224
+ })
10225
+ ]
10226
+ }, family.id))
8682
10227
  })
8683
- ]
8684
10228
  });
8685
10229
  }
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",
10230
+ function moveSpreadsheetTableStyleFocus(event) {
10231
+ const active = document.activeElement;
10232
+ const group = [
10233
+ ...event.currentTarget.querySelectorAll('[data-office-menu-grid]')
10234
+ ].find((candidate)=>candidate.contains(active));
10235
+ const grid = group?.querySelector(':scope > div');
10236
+ const template = grid ? getComputedStyle(grid).gridTemplateColumns : '';
10237
+ const repeated = template.match(/^repeat\(\s*(\d+)/)?.[1];
10238
+ const resolved = template.split(/\s+/).filter((value)=>value && 'none' !== value).length;
10239
+ const columns = repeated ? Number(repeated) : resolved || 7;
10240
+ return moveOfficeGridMenuFocus(event, Math.max(1, columns));
10241
+ }
10242
+ function spreadsheetTableUsesStyle(tableStyle, choice) {
10243
+ return 'none' !== tableStyle.family && tableStyle.family === choice.family && tableStyle.number === choice.number;
10244
+ }
10245
+ function SpreadsheetTableStylePreview({ choice }) {
10246
+ return /*#__PURE__*/ jsxs("span", {
10247
+ className: "work-spreadsheet-table-style-preview",
10248
+ "aria-hidden": "true",
8711
10249
  children: [
8712
- /*#__PURE__*/ jsx("span", {
8713
- children: label
10250
+ /*#__PURE__*/ jsx("i", {
10251
+ style: {
10252
+ backgroundColor: choice.palette.header
10253
+ }
8714
10254
  }),
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
10255
+ /*#__PURE__*/ jsx("i", {
10256
+ style: {
10257
+ backgroundColor: choice.palette.primaryRow
10258
+ }
10259
+ }),
10260
+ /*#__PURE__*/ jsx("i", {
10261
+ style: {
10262
+ backgroundColor: choice.palette.secondaryRow
10263
+ }
8723
10264
  })
8724
10265
  ]
8725
10266
  });
8726
10267
  }
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);
10268
+ function SpreadsheetTableOption({ active, disabled, icon, label, onToggle }) {
10269
+ return /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
10270
+ label: label,
10271
+ active: active,
10272
+ disabled: disabled,
10273
+ onClick: onToggle,
10274
+ children: icon
10275
+ });
8740
10276
  }
8741
10277
  const defaultSpreadsheetFreezePanesSelection = {
8742
10278
  row: [
@@ -8750,7 +10286,11 @@ const defaultSpreadsheetFreezePanesSelection = {
8750
10286
  row_focus: 0,
8751
10287
  column_focus: 0
8752
10288
  };
8753
- function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, commands, content, fileActions, findOpen, formatPainterMode = null, freezePanesActive = false, freezePanesSelection = defaultSpreadsheetFreezePanesSelection, gridLinesVisible, panelId, onTabChange, onTogglePanel, panel, toolbarCell, toolbarCellBorders }) {
10289
+ 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 }) {
10290
+ const ribbonTabs = activeTable ? [
10291
+ ...spreadsheetRibbonTabs,
10292
+ spreadsheetTableDesignRibbonTab
10293
+ ] : spreadsheetRibbonTabs;
8754
10294
  const formulaCount = useMemo(()=>spreadsheetFormulaCount(content), [
8755
10295
  content
8756
10296
  ]);
@@ -8759,7 +10299,7 @@ function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, com
8759
10299
  ]);
8760
10300
  return /*#__PURE__*/ jsx(WorkOfficeRibbon, {
8761
10301
  ariaLabel: "表格功能区",
8762
- tabs: spreadsheetRibbonTabs,
10302
+ tabs: ribbonTabs,
8763
10303
  defaultTab: "home",
8764
10304
  activeTab: activeTab,
8765
10305
  onTabChange: onTabChange,
@@ -8991,6 +10531,20 @@ function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, com
8991
10531
  }),
8992
10532
  insert: /*#__PURE__*/ jsxs(Fragment, {
8993
10533
  children: [
10534
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
10535
+ label: "表格",
10536
+ priority: "high",
10537
+ children: /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
10538
+ label: spreadsheetCommandCatalog.table.label,
10539
+ title: `${spreadsheetCommandCatalog.table.label}(${spreadsheetCommandCatalog.table.shortcut.label})`,
10540
+ "aria-keyshortcuts": spreadsheetCommandCatalog.table.shortcut.aria,
10541
+ disabled: !can.openTable(),
10542
+ onClick: commands.openTable,
10543
+ children: /*#__PURE__*/ jsx(Table2, {
10544
+ size: 19
10545
+ })
10546
+ })
10547
+ }),
8994
10548
  /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
8995
10549
  label: "链接",
8996
10550
  priority: "high",
@@ -9188,7 +10742,13 @@ function SpreadsheetEditorRibbon({ activeTab, autoFilterActive = false, can, com
9188
10742
  })
9189
10743
  })
9190
10744
  ]
9191
- })
10745
+ }),
10746
+ tableDesign: activeTable && activeTableSheetId ? /*#__PURE__*/ jsx(SpreadsheetTableDesignRibbon, {
10747
+ can: can,
10748
+ commands: commands,
10749
+ sheetId: activeTableSheetId,
10750
+ table: activeTable
10751
+ }) : null
9192
10752
  }
9193
10753
  });
9194
10754
  }
@@ -12086,6 +13646,59 @@ function SheetMenuButton({ icon, label, danger = false, disabled = false, onClic
12086
13646
  ]
12087
13647
  });
12088
13648
  }
13649
+ const pendingContextRestores = new WeakMap();
13650
+ function beginSpreadsheetTableCellRender(_cell, tableStyle, conditionalStyle, context) {
13651
+ if (tableStyle) context.fillStyle = tableStyle.background;
13652
+ if (conditionalStyle?.cellColor) context.fillStyle = conditionalStyle.cellColor;
13653
+ if (!tableStyle || pendingContextRestores.has(context) || 'function' != typeof context.fillText) return;
13654
+ const fillText = context.fillText;
13655
+ pendingContextRestores.set(context, {
13656
+ fillText,
13657
+ fillTextOwned: Object.hasOwn(context, 'fillText')
13658
+ });
13659
+ const textColor = conditionalStyle?.textColor ?? tableStyle.textColor;
13660
+ context.fillText = (text, x, y, maxWidth)=>{
13661
+ const previousFillStyle = context.fillStyle;
13662
+ const previousFont = context.font;
13663
+ context.fillStyle = textColor;
13664
+ if (tableStyle.bold) context.font = boldSpreadsheetTableCanvasFont(context.font);
13665
+ try {
13666
+ if (void 0 === maxWidth) fillText.call(context, text, x, y);
13667
+ else fillText.call(context, text, x, y, maxWidth);
13668
+ } finally{
13669
+ context.fillStyle = previousFillStyle;
13670
+ context.font = previousFont;
13671
+ }
13672
+ };
13673
+ }
13674
+ function finishSpreadsheetTableCellRender(_cell, cellInfo, tableStyle, context) {
13675
+ if (tableStyle) drawSpreadsheetTableCellBorder(cellInfo, tableStyle, context);
13676
+ const restore = pendingContextRestores.get(context);
13677
+ if (!restore) return;
13678
+ if (restore.fillTextOwned) context.fillText = restore.fillText;
13679
+ else delete context.fillText;
13680
+ pendingContextRestores.delete(context);
13681
+ }
13682
+ function boldSpreadsheetTableCanvasFont(font) {
13683
+ return /(?:^|\s)(?:bold|[6-9]00)(?:\s|$)/i.test(font) ? font : `bold ${font}`;
13684
+ }
13685
+ function drawSpreadsheetTableCellBorder(cellInfo, tableStyle, context) {
13686
+ const left = Math.round(cellInfo.startX) + 0.5;
13687
+ const top = Math.round(cellInfo.startY) + 0.5;
13688
+ const right = Math.round(cellInfo.endX) - 0.5;
13689
+ const bottom = Math.round(cellInfo.endY) - 0.5;
13690
+ context.save();
13691
+ context.beginPath();
13692
+ context.strokeStyle = tableStyle.borderColor;
13693
+ context.lineWidth = 'header' === tableStyle.role ? 1 : 0.75;
13694
+ context.moveTo(left, top);
13695
+ context.lineTo(right, top);
13696
+ context.lineTo(right, bottom);
13697
+ context.lineTo(left, bottom);
13698
+ context.closePath();
13699
+ context.stroke();
13700
+ context.restore();
13701
+ }
12089
13702
  function validateSpreadsheetChartSeriesTrendlines(series, seriesIndex) {
12090
13703
  for (const [trendlineIndex, trendline] of (series.trendlines ?? []).entries()){
12091
13704
  const prefix = `系列 ${seriesIndex + 1} 的趋势线 ${trendlineIndex + 1}`;
@@ -16195,220 +17808,28 @@ function NamedRangeManager({ content, onChange }) {
16195
17808
  onClick: cancelDraft,
16196
17809
  children: "取消更改"
16197
17810
  }),
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
17811
+ /*#__PURE__*/ jsx(button_Button, {
17812
+ type: "submit",
17813
+ tone: "primary",
17814
+ disabled: Boolean(draft.id) && !dirty,
17815
+ children: "保存名称"
17816
+ })
17817
+ ]
17818
+ })
17819
+ ]
17820
+ })
16332
17821
  ]
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
17822
  });
16352
17823
  }
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);
17824
+ function rangeDraft(range) {
16394
17825
  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
- ]
17826
+ id: range?.id,
17827
+ name: range?.name ?? '',
17828
+ reference: range?.reference ?? '',
17829
+ scopeSheetId: range?.scopeSheetId ?? '',
17830
+ comment: range?.comment ?? ''
16403
17831
  };
16404
17832
  }
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
17833
  function useSpreadsheetAutoFilter({ canvasRef, content, editable, mountRevision, onChange, selection, sheetId, workbook = null }) {
16413
17834
  const contentRef = useRef(content);
16414
17835
  const editableRef = useRef(editable);
@@ -19984,6 +21405,206 @@ function spreadsheetHyperlinkValidationMessage(content, source, value) {
19984
21405
  function spreadsheetHyperlinkFailureMessage(content, source, value, fallback) {
19985
21406
  return spreadsheetHyperlinkValidationMessage(content, source, value) ?? fallback;
19986
21407
  }
21408
+ function SpreadsheetTableDialog({ source, restoreFocusTarget, onApply, onClose, onValidate }) {
21409
+ const [value, setValue] = useState(source.value);
21410
+ const [touched, setTouched] = useState(false);
21411
+ const formId = useId();
21412
+ const validationError = onValidate(value);
21413
+ const visibleError = touched ? validationError : null;
21414
+ const submit = (event)=>{
21415
+ event.preventDefault();
21416
+ setTouched(true);
21417
+ if (validationError) return;
21418
+ if (onApply(value)) onClose();
21419
+ };
21420
+ return /*#__PURE__*/ jsx(Dialog, {
21421
+ title: "创建表格",
21422
+ description: source.sheetName,
21423
+ className: "work-spreadsheet-table-dialog",
21424
+ restoreFocusTarget: restoreFocusTarget,
21425
+ onClose: onClose,
21426
+ footer: /*#__PURE__*/ jsxs(Fragment, {
21427
+ children: [
21428
+ /*#__PURE__*/ jsx(button_Button, {
21429
+ tone: "quiet",
21430
+ onClick: onClose,
21431
+ children: "取消"
21432
+ }),
21433
+ /*#__PURE__*/ jsx(button_Button, {
21434
+ tone: "primary",
21435
+ type: "submit",
21436
+ form: formId,
21437
+ disabled: Boolean(validationError),
21438
+ children: "确定"
21439
+ })
21440
+ ]
21441
+ }),
21442
+ children: /*#__PURE__*/ jsxs("form", {
21443
+ id: formId,
21444
+ onSubmit: submit,
21445
+ children: [
21446
+ /*#__PURE__*/ jsxs("div", {
21447
+ className: "work-spreadsheet-table-scope",
21448
+ children: [
21449
+ /*#__PURE__*/ jsx("span", {
21450
+ "aria-hidden": "true",
21451
+ children: /*#__PURE__*/ jsx(Table2, {
21452
+ size: 18
21453
+ })
21454
+ }),
21455
+ /*#__PURE__*/ jsxs("div", {
21456
+ children: [
21457
+ /*#__PURE__*/ jsx("strong", {
21458
+ children: source.name
21459
+ }),
21460
+ /*#__PURE__*/ jsx("small", {
21461
+ children: "将连续数据区域转换为原生工作表表格"
21462
+ })
21463
+ ]
21464
+ })
21465
+ ]
21466
+ }),
21467
+ /*#__PURE__*/ jsx(field_Field, {
21468
+ label: "表格区域",
21469
+ required: true,
21470
+ error: visibleError ?? void 0,
21471
+ description: "输入一个连续区域,例如 A1:C20。",
21472
+ children: /*#__PURE__*/ jsx("input", {
21473
+ type: "text",
21474
+ autoCapitalize: "none",
21475
+ autoFocus: true,
21476
+ spellCheck: false,
21477
+ value: value.rangeReference,
21478
+ onBlur: ()=>setTouched(true),
21479
+ onChange: (event)=>{
21480
+ const rangeReference = event.currentTarget.value;
21481
+ setValue((current)=>({
21482
+ ...current,
21483
+ rangeReference
21484
+ }));
21485
+ setTouched(true);
21486
+ }
21487
+ })
21488
+ }),
21489
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
21490
+ ariaLabel: "表包含标题",
21491
+ checked: value.headerRow,
21492
+ onCheckedChange: (headerRow)=>setValue((current)=>({
21493
+ ...current,
21494
+ headerRow
21495
+ })),
21496
+ children: "表包含标题"
21497
+ })
21498
+ ]
21499
+ })
21500
+ });
21501
+ }
21502
+ function useSpreadsheetTable({ commandsRef, contentRef, focusGrid, getGridFocusTarget, getLiveSelection, preview }) {
21503
+ const [source, setSource] = useState(null);
21504
+ const invokerRef = useRef(null);
21505
+ const selectionRef = useRef(null);
21506
+ const applyingRef = useRef(false);
21507
+ const appliedRef = useRef(false);
21508
+ useEffect(()=>{
21509
+ if (!preview) return;
21510
+ setSource(null);
21511
+ invokerRef.current = null;
21512
+ selectionRef.current = null;
21513
+ applyingRef.current = false;
21514
+ appliedRef.current = false;
21515
+ }, [
21516
+ preview
21517
+ ]);
21518
+ const open = useCallback((target)=>{
21519
+ if (preview || source) return false;
21520
+ const nextSource = createSpreadsheetTableDialogSource(contentRef.current, target);
21521
+ if (!nextSource) {
21522
+ showToast('请选择包含标题和至少一行数据的连续、未合并区域。', 'error');
21523
+ return false;
21524
+ }
21525
+ const grid = getGridFocusTarget();
21526
+ invokerRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : grid;
21527
+ selectionRef.current = {
21528
+ sheetId: target.sheetId,
21529
+ selection: finiteSpreadsheetSelection(getLiveSelection() ?? target.selection)
21530
+ };
21531
+ appliedRef.current = false;
21532
+ setSource(nextSource);
21533
+ return true;
21534
+ }, [
21535
+ contentRef,
21536
+ getGridFocusTarget,
21537
+ getLiveSelection,
21538
+ preview,
21539
+ source
21540
+ ]);
21541
+ const close = useCallback(()=>{
21542
+ const invoker = invokerRef.current;
21543
+ const grid = getGridFocusTarget();
21544
+ const restoreGrid = appliedRef.current || invoker === grid;
21545
+ setSource(null);
21546
+ requestAnimationFrame(()=>{
21547
+ if (restoreGrid || !invoker?.isConnected) focusGrid(invoker);
21548
+ else invoker.focus({
21549
+ preventScroll: true
21550
+ });
21551
+ if (invokerRef.current === invoker) invokerRef.current = null;
21552
+ selectionRef.current = null;
21553
+ appliedRef.current = false;
21554
+ });
21555
+ }, [
21556
+ focusGrid,
21557
+ getGridFocusTarget
21558
+ ]);
21559
+ const commandPort = useMemo(()=>({
21560
+ canOpen: !preview && null === source,
21561
+ open
21562
+ }), [
21563
+ open,
21564
+ preview,
21565
+ source
21566
+ ]);
21567
+ const selectionForChange = useCallback(()=>applyingRef.current ? selectionRef.current : null, []);
21568
+ return {
21569
+ commandPort,
21570
+ selectionForChange,
21571
+ dialog: source ? /*#__PURE__*/ jsx(SpreadsheetTableDialog, {
21572
+ source: source,
21573
+ restoreFocusTarget: ()=>appliedRef.current ? getGridFocusTarget() : invokerRef.current?.isConnected ? invokerRef.current : getGridFocusTarget(),
21574
+ onApply: (value)=>{
21575
+ const range = spreadsheetTableRangeFromText(value.rangeReference);
21576
+ if (!range) return false;
21577
+ applyingRef.current = true;
21578
+ let handled = false;
21579
+ try {
21580
+ handled = commandsRef.current?.applyTable({
21581
+ headerRow: value.headerRow,
21582
+ name: source.name,
21583
+ range,
21584
+ sheetId: source.sheetId
21585
+ }) ?? false;
21586
+ appliedRef.current = handled;
21587
+ } finally{
21588
+ applyingRef.current = false;
21589
+ }
21590
+ if (!handled) showToast(spreadsheetTableValidationMessage(contentRef.current, source, value) ?? '无法创建表格。', 'error');
21591
+ return handled;
21592
+ },
21593
+ onClose: close,
21594
+ onValidate: (value)=>spreadsheetTableValidationMessage(contentRef.current, source, value)
21595
+ }) : null
21596
+ };
21597
+ }
21598
+ function spreadsheetTableValidationMessage(content, source, value) {
21599
+ const range = spreadsheetTableRangeFromText(value.rangeReference);
21600
+ if (!range) return '请输入一个连续区域。';
21601
+ return spreadsheetTableFailureMessage(content, {
21602
+ headerRow: value.headerRow,
21603
+ name: source.name,
21604
+ range,
21605
+ sheetId: source.sheetId
21606
+ });
21607
+ }
19987
21608
  const MAXIMUM_BUFFERED_SPREADSHEET_OPERATIONS = 10001;
19988
21609
  function useSpreadsheetWorkbookSync(content, synchronizeExternalWorkbook) {
19989
21610
  const mountedContentRef = useRef(content);
@@ -20117,6 +21738,14 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20117
21738
  getLiveSelections: getSpreadsheetDataValidationLiveSelections,
20118
21739
  preview
20119
21740
  });
21741
+ const spreadsheetTable = useSpreadsheetTable({
21742
+ commandsRef: spreadsheetCommandsRef,
21743
+ contentRef,
21744
+ focusGrid: focusSpreadsheetDialogGrid,
21745
+ getGridFocusTarget: getSpreadsheetDialogGridFocusTarget,
21746
+ getLiveSelection: getSpreadsheetHyperlinkLiveSelection,
21747
+ preview
21748
+ });
20120
21749
  const officeDialog = useOfficeDialog();
20121
21750
  const [findOpen, setFindOpen] = useState(false);
20122
21751
  const [findFocusRequest, setFindFocusRequest] = useState(0);
@@ -20149,6 +21778,16 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20149
21778
  ]);
20150
21779
  const conditionalStylesBySheetRef = useRef(conditionalStylesBySheet);
20151
21780
  conditionalStylesBySheetRef.current = conditionalStylesBySheet;
21781
+ const tableStylesBySheet = useMemo(()=>new Map(materializedContent.sheets.flatMap((sheet)=>sheet.id ? [
21782
+ [
21783
+ sheet.id,
21784
+ createSpreadsheetTableRenderResolver(sheet.tables ?? [])
21785
+ ]
21786
+ ] : [])), [
21787
+ materializedContent.sheets
21788
+ ]);
21789
+ const tableStylesBySheetRef = useRef(tableStylesBySheet);
21790
+ tableStylesBySheetRef.current = tableStylesBySheet;
20152
21791
  useEffect(()=>{
20153
21792
  setPreviewActiveSheetId(preview ? contentActiveSheetId : null);
20154
21793
  }, [
@@ -20259,21 +21898,28 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20259
21898
  endColumn: Math.max(selection.column[0], selection.column[1])
20260
21899
  }).length);
20261
21900
  },
20262
- beforeRenderCell: (_cell, cellInfo, context)=>{
21901
+ beforeRenderCell: (cell, cellInfo, context)=>{
21902
+ const tableStyle = tableStylesBySheetRef.current.get(activeSheetIdRef.current)?.(cellInfo.row, cellInfo.column) ?? null;
20263
21903
  const style = conditionalStylesBySheetRef.current.get(activeSheetIdRef.current)?.get(`${cellInfo.row}_${cellInfo.column}`);
20264
- if (style?.cellColor) context.fillStyle = style.cellColor;
21904
+ beginSpreadsheetTableCellRender(cell, tableStyle, style, context);
20265
21905
  return true;
20266
21906
  },
20267
21907
  afterRenderCell: (cell, cellInfo, context)=>{
21908
+ const tableStyle = tableStylesBySheetRef.current.get(activeSheetIdRef.current)?.(cellInfo.row, cellInfo.column) ?? null;
20268
21909
  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);
21910
+ try {
21911
+ if (style?.icon || style?.dataBar) {
21912
+ const background = style.cellColor ?? tableStyle?.background ?? ('string' == typeof cell?.bg ? cell.bg : '#ffffff');
21913
+ if (style.dataBar) drawSpreadsheetConditionalDataBar(context, cellInfo, {
21914
+ ...style.dataBar,
21915
+ showValue: style.dataBar.showValue && (style.icon?.showValue ?? true)
21916
+ }, cell, background, style.textColor);
21917
+ if (style.icon) drawSpreadsheetConditionalIcon(context, cellInfo, style.icon, background, style.dataBar ? false : !style.icon.showValue);
21918
+ if (cell?.ps) drawSpreadsheetCommentMarker(context, cellInfo);
21919
+ }
21920
+ } finally{
21921
+ finishSpreadsheetTableCellRender(cell, cellInfo, tableStyle, context);
21922
+ }
20277
21923
  }
20278
21924
  }), [
20279
21925
  collaborationView
@@ -20393,6 +22039,17 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20393
22039
  const toolbarColumn = toolbarSelection.column_focus ?? toolbarSelection.column[0];
20394
22040
  const toolbarCell = spreadsheetCellAt(toolbarSheet, toolbarRow, toolbarColumn);
20395
22041
  const toolbarCellBorders = spreadsheetCellBordersAt(toolbarSheet, toolbarRow, toolbarColumn);
22042
+ const toolbarContentSheet = materializedContent.sheets.find((sheet)=>sheet.id === toolbarSheetId);
22043
+ const activeTable = spreadsheetTableAtCell(toolbarContentSheet, toolbarRow, toolbarColumn);
22044
+ const activeTableId = activeTable?.id ?? null;
22045
+ useEffect(()=>{
22046
+ setRibbonTab((current)=>{
22047
+ if (activeTableId) return 'tableDesign' === current ? current : 'tableDesign';
22048
+ return 'tableDesign' === current ? 'home' : current;
22049
+ });
22050
+ }, [
22051
+ activeTableId
22052
+ ]);
20396
22053
  const selectedRange = spreadsheetSingleRange(toolbarSelection);
20397
22054
  const multipleCellsSelected = selectedRange.row[0] !== selectedRange.row[1] || selectedRange.column[0] !== selectedRange.column[1];
20398
22055
  const selectionSummary = multipleCellsSelected ? spreadsheetSelectionSummary(toolbarSheet, toolbarSelection) : null;
@@ -20633,12 +22290,18 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20633
22290
  onChange: (next)=>{
20634
22291
  const formatCellsSelection = formatCellsApplyingRef.current ? formatCellsSelectionRef.current : null;
20635
22292
  const dataValidationSelection = spreadsheetDataValidation.selectionForChange();
22293
+ const tableSelection = spreadsheetTable.selectionForChange();
20636
22294
  const hyperlinkSelection = spreadsheetHyperlink.selectionForChange();
20637
22295
  const preservedSelection = formatCellsSelection ? {
20638
22296
  sheetId: formatCellsSelection.sheetId,
20639
22297
  selections: [
20640
22298
  formatCellsSelection.selection
20641
22299
  ]
22300
+ } : tableSelection ? {
22301
+ sheetId: tableSelection.sheetId,
22302
+ selections: [
22303
+ tableSelection.selection
22304
+ ]
20642
22305
  } : dataValidationSelection ?? (hyperlinkSelection ? {
20643
22306
  sheetId: hyperlinkSelection.sheetId,
20644
22307
  selections: [
@@ -20650,6 +22313,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20650
22313
  onChange(controlled);
20651
22314
  },
20652
22315
  selection: selectionState,
22316
+ table: spreadsheetTable.commandPort,
20653
22317
  targetSheetGridSize,
20654
22318
  targetSheetId: toolbarSheetId,
20655
22319
  toolbarCell,
@@ -20853,6 +22517,8 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
20853
22517
  }),
20854
22518
  !preview && /*#__PURE__*/ jsx(SpreadsheetEditorRibbon, {
20855
22519
  activeTab: ribbonTab,
22520
+ activeTable: activeTable,
22521
+ activeTableSheetId: activeTable ? toolbarSheetId : void 0,
20856
22522
  autoFilterActive: autoFilterActive,
20857
22523
  can: spreadsheetCan,
20858
22524
  commands: spreadsheetRibbonCommands,
@@ -21075,6 +22741,7 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
21075
22741
  }),
21076
22742
  spreadsheetDataValidation.dialog,
21077
22743
  spreadsheetHyperlink.dialog,
22744
+ spreadsheetTable.dialog,
21078
22745
  spreadsheetClipboard.dialogSource && /*#__PURE__*/ jsx(SpreadsheetPasteSpecialDialog, {
21079
22746
  source: spreadsheetClipboard.dialogSource,
21080
22747
  restoreFocusTarget: spreadsheetClipboard.restoreDialogFocusTarget,
@@ -21187,6 +22854,7 @@ function spreadsheetCommandsWithGridFocus(commands, restoreFocus) {
21187
22854
  cancelFormatPainter: afterSuccessfulCommand(commands.cancelFormatPainter),
21188
22855
  clearSelectedCells: afterSuccessfulCommand(commands.clearSelectedCells),
21189
22856
  copySelection: afterSuccessfulCommand(commands.copySelection),
22857
+ convertTableToRange: afterSuccessfulCommand(commands.convertTableToRange),
21190
22858
  cutSelection: afterSuccessfulCommand(commands.cutSelection),
21191
22859
  deleteSelectedStructure: afterSuccessfulCommand(commands.deleteSelectedStructure),
21192
22860
  fillSelectedCells: afterSuccessfulCommand(commands.fillSelectedCells),