@a3s-lab/office 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -558,9 +558,13 @@ interaction model.
558
558
  Format Painter with
559
559
  single-use and double-click locked
560
560
  sessions, cross-sheet range-pattern tiling, Escape cancellation, and native
561
- style-only writes, a Home and Font cell-border split control with side, all,
561
+ style-only writes, Home and Font Grow/Shrink Font commands with
562
+ `Cmd/Ctrl+Shift+.` / `Cmd/Ctrl+]` and `Cmd/Ctrl+Shift+,` / `Cmd/Ctrl+[` aliases,
563
+ per-cell mixed-size stepping, native rectangle compaction, a 10,000-cell
564
+ bound, and one-step Undo, plus a cell-border split control with side, all,
562
565
  outside, inside, horizontal, vertical, clear, and diagonal targets, ten
563
566
  native line styles, exact color selection, compact overlapping range writes,
567
+ `Cmd/Ctrl+Shift+&` Outside Borders and `Cmd/Ctrl+Shift+_` Clear Borders,
564
568
  single-update undo, and grid-focus restoration, Conditional Formatting under
565
569
  Home and Styles, executable
566
570
  sorting plus WPS-style AutoFilter under Data, finite current-region discovery,
@@ -1515,6 +1519,7 @@ without hard-coded return URLs.
1515
1519
 
1516
1520
  - [Live Playground](https://a3s-lab.github.io/Office/)
1517
1521
  - [Documentation center](https://a3s-lab.github.io/Office/docs/)
1522
+ - [A3S Office 0.17.0 documentation](https://a3s-lab.github.io/Office/docs/0.17.0/)
1518
1523
  - [A3S Office 0.16.0 documentation](https://a3s-lab.github.io/Office/docs/0.16.0/)
1519
1524
  - [A3S Office 0.15.0 documentation](https://a3s-lab.github.io/Office/docs/0.15.0/)
1520
1525
  - [A3S Office 0.14.0 documentation](https://a3s-lab.github.io/Office/docs/0.14.0/)
@@ -1,7 +1,7 @@
1
1
  import { __webpack_require__ } from "./0~rslib-runtime.js";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Workbook } from "@fortune-sheet/react";
4
- import { AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BadgeJapaneseYen, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Globe2, Grid2X2, Grid3X3, Hash, Italic, KeyRound, Layers2, Link2, Link2Off, ListChecks, ListFilter, LocateFixed, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, Settings2, ShieldCheck, Sigma, SortAsc, SortDesc, Strikethrough, SwatchBook, Table2, TableProperties, Trash2, Underline, Undo2, WrapText, X } from "lucide-react";
4
+ import { AArrowDown, AArrowUp, AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowDownToLine, ArrowLeft, ArrowLeftToLine, ArrowRight, ArrowRightToLine, ArrowUp, ArrowUpToLine, BadgeJapaneseYen, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Globe2, Grid2X2, Grid3X3, Hash, Italic, KeyRound, Layers2, Link2, Link2Off, ListChecks, ListFilter, LocateFixed, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, Settings2, ShieldCheck, Sigma, SortAsc, SortDesc, Strikethrough, SwatchBook, Table2, TableProperties, Trash2, Underline, Undo2, WrapText, X } from "lucide-react";
5
5
  import { Fragment as external_react_Fragment, cloneElement, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
6
  import { genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
7
7
  import { freezeImportedSpreadsheetCell, withoutEditableRange, spreadsheetMatrixProfile, normalizeSpreadsheetConditionalVisualOptions, effectiveSpreadsheetPageSetup, attachSpreadsheetShownCommentCells, withEditableRange, sameSpreadsheetHistoryValue, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, isSpreadsheetConditionalComparisonOperator, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, normalizeSpreadsheetDateValidationBoundary, sheetProtectionAuthority, editableRangeCellCount, unlockedCellCount, registerDerivedSpreadsheetMatrix, editableRangeRequiresCredentials, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSheetProtectionAuthority, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, spreadsheetConditionalComparisonNeedsUpperValue, drawSpreadsheetConditionalIcon, replaceOfficeSpreadsheetCollaboration as replaceWorkOfficeSpreadsheetCollaboration, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, spreadsheetConditionalThresholdsEqual } from "./2180.js";
@@ -1357,6 +1357,28 @@ 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
+ const spreadsheetFontSizes = [
1361
+ 9,
1362
+ 10,
1363
+ 11,
1364
+ 12,
1365
+ 14,
1366
+ 16,
1367
+ 18,
1368
+ 20,
1369
+ 22,
1370
+ 24,
1371
+ 28,
1372
+ 36,
1373
+ 48,
1374
+ 72
1375
+ ];
1376
+ function nextSpreadsheetFontSize(current, direction) {
1377
+ if (!Number.isFinite(current) || current < 1 || current > 409) return null;
1378
+ return 'grow' === direction ? spreadsheetFontSizes.find((size)=>size > current) ?? null : [
1379
+ ...spreadsheetFontSizes
1380
+ ].reverse().find((size)=>size < current) ?? null;
1381
+ }
1360
1382
  function canApplySpreadsheetTableStructureChange(sheet, change) {
1361
1383
  if (!sheet || !validSpreadsheetTableStructureChange(change)) return false;
1362
1384
  for (const table of sheet.tables ?? []){
@@ -1700,22 +1722,6 @@ function sameSpreadsheetTableColumns(left, right) {
1700
1722
  function spreadsheet_table_reconciliation_isRecord(value) {
1701
1723
  return Boolean(value && 'object' == typeof value && !Array.isArray(value));
1702
1724
  }
1703
- const spreadsheetFontSizes = [
1704
- 9,
1705
- 10,
1706
- 11,
1707
- 12,
1708
- 14,
1709
- 16,
1710
- 18,
1711
- 20,
1712
- 22,
1713
- 24,
1714
- 28,
1715
- 36,
1716
- 48,
1717
- 72
1718
- ];
1719
1725
  function spreadsheetSelectionReference(selection) {
1720
1726
  const rowStart = Math.min(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
1721
1727
  const rowEnd = Math.max(selection.row[0] ?? 0, selection.row[1] ?? selection.row[0] ?? 0);
@@ -2805,6 +2811,40 @@ const spreadsheetCommandCatalog = {
2805
2811
  ]
2806
2812
  }
2807
2813
  },
2814
+ growFont: {
2815
+ id: 'font.grow',
2816
+ label: '增大字号',
2817
+ location: {
2818
+ area: 'ribbon',
2819
+ tab: 'home',
2820
+ group: 'font'
2821
+ },
2822
+ shortcut: {
2823
+ label: 'Cmd/Ctrl+Shift+. 或 Cmd/Ctrl+]',
2824
+ aria: 'Control+Shift+. Meta+Shift+. Control+] Meta+]',
2825
+ editor: [
2826
+ 'Mod-Shift-.',
2827
+ 'Mod-]'
2828
+ ]
2829
+ }
2830
+ },
2831
+ shrinkFont: {
2832
+ id: 'font.shrink',
2833
+ label: '减小字号',
2834
+ location: {
2835
+ area: 'ribbon',
2836
+ tab: 'home',
2837
+ group: 'font'
2838
+ },
2839
+ shortcut: {
2840
+ label: 'Cmd/Ctrl+Shift+, 或 Cmd/Ctrl+[',
2841
+ aria: 'Control+Shift+, Meta+Shift+, Control+[ Meta+[',
2842
+ editor: [
2843
+ 'Mod-Shift-,',
2844
+ 'Mod-['
2845
+ ]
2846
+ }
2847
+ },
2808
2848
  numberFormatGeneral: {
2809
2849
  id: 'number.general',
2810
2850
  label: spreadsheetNumberFormatPresetLabels.general,
@@ -3021,6 +3061,14 @@ const spreadsheetCommandCatalog = {
3021
3061
  area: 'ribbon',
3022
3062
  tab: 'home',
3023
3063
  group: 'font'
3064
+ },
3065
+ shortcut: {
3066
+ label: 'Cmd/Ctrl+Shift+_',
3067
+ aria: 'Control+Shift+_ Meta+Shift+_',
3068
+ editor: [
3069
+ 'Mod-Shift-_',
3070
+ 'Mod-Shift-Minus'
3071
+ ]
3024
3072
  }
3025
3073
  },
3026
3074
  borderAll: {
@@ -3039,6 +3087,14 @@ const spreadsheetCommandCatalog = {
3039
3087
  area: 'ribbon',
3040
3088
  tab: 'home',
3041
3089
  group: 'font'
3090
+ },
3091
+ shortcut: {
3092
+ label: 'Cmd/Ctrl+Shift+&',
3093
+ aria: 'Control+Shift+& Meta+Shift+&',
3094
+ editor: [
3095
+ 'Mod-Shift-&',
3096
+ 'Mod-Shift-7'
3097
+ ]
3042
3098
  }
3043
3099
  },
3044
3100
  borderInside: {
@@ -3842,6 +3898,8 @@ function spreadsheet_cell_clear_isRecord(value) {
3842
3898
  return Boolean(value && 'object' == typeof value && !Array.isArray(value));
3843
3899
  }
3844
3900
  function createSpreadsheetCellBorderExtension() {
3901
+ const outsideShortcuts = spreadsheetCommandCatalog.borderOutside.shortcut.editor;
3902
+ const noneShortcuts = spreadsheetCommandCatalog.borderNone.shortcut.editor;
3845
3903
  return createOfficeEditorExtension({
3846
3904
  name: 'spreadsheetCellBorders',
3847
3905
  addCommands: ()=>({
@@ -3849,6 +3907,28 @@ function createSpreadsheetCellBorderExtension() {
3849
3907
  canExecute: canSetSelectedCellBorders,
3850
3908
  execute: setSelectedCellBorders
3851
3909
  }
3910
+ }),
3911
+ addKeyboardShortcuts: ()=>({
3912
+ [outsideShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3913
+ target: 'outside',
3914
+ color: '#000000',
3915
+ style: 'thin'
3916
+ }),
3917
+ [outsideShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3918
+ target: 'outside',
3919
+ color: '#000000',
3920
+ style: 'thin'
3921
+ }),
3922
+ [noneShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3923
+ target: 'none',
3924
+ color: '#000000',
3925
+ style: 'thin'
3926
+ }),
3927
+ [noneShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetBorderShortcut(event, can.setSelectedCellBorders, commands.setSelectedCellBorders, {
3928
+ target: 'none',
3929
+ color: '#000000',
3930
+ style: 'thin'
3931
+ })
3852
3932
  })
3853
3933
  });
3854
3934
  }
@@ -3856,15 +3936,22 @@ function canSetSelectedCellBorders(context, format) {
3856
3936
  return Boolean(context.editable && context.workbook && context.targetSheetId && canSetSpreadsheetCellBorders(context.content, context.targetSheetId, liveSpreadsheetBorderRange(context), format));
3857
3937
  }
3858
3938
  function setSelectedCellBorders(context, format) {
3859
- if (!canSetSelectedCellBorders(context, format)) return false;
3860
- const next = setSpreadsheetCellBorders(context.content, context.targetSheetId, liveSpreadsheetBorderRange(context), format);
3939
+ if (!context.editable || !context.workbook || !context.targetSheetId) return false;
3940
+ const selection = context.workbook.getSelection()?.at(-1) ?? context.fallbackRange;
3941
+ const range = spreadsheetSingleRange(selection);
3942
+ if (!canSetSpreadsheetCellBorders(context.content, context.targetSheetId, range, format)) return false;
3943
+ const next = setSpreadsheetCellBorders(context.content, context.targetSheetId, range, format);
3861
3944
  if (!next) return false;
3862
- context.onChange(next);
3945
+ context.onChange(spreadsheetContentWithSelection(next, context.targetSheetId, selection));
3863
3946
  return true;
3864
3947
  }
3865
3948
  function liveSpreadsheetBorderRange(context) {
3866
3949
  return spreadsheetSingleRange(context.workbook?.getSelection()?.at(-1) ?? context.fallbackRange);
3867
3950
  }
3951
+ function runSpreadsheetBorderShortcut(event, canExecute, execute, format) {
3952
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute(format)) return false;
3953
+ return execute(format);
3954
+ }
3868
3955
  const MAX_SPREADSHEET_PER_CELL_BORDER_CELLS = 10000;
3869
3956
  function setSpreadsheetCellBordersPerCell(content, sheetId, range, formats) {
3870
3957
  const normalizedRange = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
@@ -4719,6 +4806,157 @@ function spreadsheetSelectedCellFillPlan(context, direction) {
4719
4806
  const sheet = context.content.sheets.find((candidate)=>candidate.id === context.targetSheetId);
4720
4807
  return canApplySpreadsheetCellFill(sheet, plan) ? plan : null;
4721
4808
  }
4809
+ const MAX_SPREADSHEET_FONT_SIZE_CELLS = 10000;
4810
+ function createSpreadsheetFontSizeExtension() {
4811
+ const growShortcuts = spreadsheetCommandCatalog.growFont.shortcut.editor;
4812
+ const shrinkShortcuts = spreadsheetCommandCatalog.shrinkFont.shortcut.editor;
4813
+ return createOfficeEditorExtension({
4814
+ name: 'spreadsheetFontSize',
4815
+ addCommands: ()=>({
4816
+ adjustFontSize: {
4817
+ canExecute: canAdjustSelectedFontSize,
4818
+ execute: adjustSelectedFontSize
4819
+ }
4820
+ }),
4821
+ addKeyboardShortcuts: ()=>({
4822
+ [growShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'grow'),
4823
+ [growShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'grow'),
4824
+ [shrinkShortcuts[0]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'shrink'),
4825
+ [shrinkShortcuts[1]]: ({ can, commands }, event)=>runSpreadsheetFontSizeShortcut(event, can.adjustFontSize, commands.adjustFontSize, 'shrink')
4826
+ })
4827
+ });
4828
+ }
4829
+ function spreadsheetFontSizeApiCalls(cells, range, sheetId, direction) {
4830
+ const normalizedRange = spreadsheet_cell_range_normalizeSpreadsheetCellRange(range);
4831
+ if (!normalizedRange || !sheetId || !isSpreadsheetFontSizeDirection(direction) || spreadsheet_cell_range_spreadsheetCellRangeArea(normalizedRange) > MAX_SPREADSHEET_FONT_SIZE_CELLS) return [];
4832
+ const rectangles = [];
4833
+ let active = new Map();
4834
+ const rowCount = normalizedRange.row[1] - normalizedRange.row[0] + 1;
4835
+ const columnCount = normalizedRange.column[1] - normalizedRange.column[0] + 1;
4836
+ for(let rowOffset = 0; rowOffset < rowCount; rowOffset += 1){
4837
+ const row = normalizedRange.row[0] + rowOffset;
4838
+ const runs = spreadsheetFontSizeRowRuns(cells[rowOffset], row, normalizedRange.column[0], columnCount, direction);
4839
+ const nextActive = new Map();
4840
+ for (const run of runs){
4841
+ const signature = `${run.key}:${run.range.column[0]}:${run.range.column[1]}`;
4842
+ const previous = active.get(signature);
4843
+ if (previous && previous.range.row[1] === row - 1) {
4844
+ previous.range.row[1] = row;
4845
+ nextActive.set(signature, previous);
4846
+ } else nextActive.set(signature, run);
4847
+ }
4848
+ for (const [signature, run] of active)if (!nextActive.has(signature)) rectangles.push(run);
4849
+ active = nextActive;
4850
+ }
4851
+ rectangles.push(...active.values());
4852
+ rectangles.sort((left, right)=>left.range.row[0] - right.range.row[0] || left.range.column[0] - right.range.column[0] || left.range.row[1] - right.range.row[1] || left.range.column[1] - right.range.column[1]);
4853
+ return rectangles.map(({ range: targetRange, size })=>({
4854
+ name: 'setCellFormatByRange',
4855
+ args: [
4856
+ 'fs',
4857
+ size,
4858
+ targetRange,
4859
+ {
4860
+ id: sheetId
4861
+ }
4862
+ ]
4863
+ }));
4864
+ }
4865
+ function spreadsheetFontSizeRowRuns(cells, row, firstColumn, columnCount, direction) {
4866
+ const runs = [];
4867
+ let current = null;
4868
+ for(let columnOffset = 0; columnOffset < columnCount; columnOffset += 1){
4869
+ const column = firstColumn + columnOffset;
4870
+ const size = adjustedSpreadsheetFontSize(cells?.[columnOffset], direction);
4871
+ if (null === size) {
4872
+ if (current) runs.push(current);
4873
+ current = null;
4874
+ continue;
4875
+ }
4876
+ const key = String(size);
4877
+ if (current && current.key === key) {
4878
+ current.range.column[1] = column;
4879
+ continue;
4880
+ }
4881
+ if (current) runs.push(current);
4882
+ current = {
4883
+ key,
4884
+ range: {
4885
+ row: [
4886
+ row,
4887
+ row
4888
+ ],
4889
+ column: [
4890
+ column,
4891
+ column
4892
+ ]
4893
+ },
4894
+ size
4895
+ };
4896
+ }
4897
+ if (current) runs.push(current);
4898
+ return runs;
4899
+ }
4900
+ function adjustedSpreadsheetFontSize(cell, direction) {
4901
+ const size = cell?.fs === void 0 ? 10 : Number(cell.fs);
4902
+ return nextSpreadsheetFontSize(size, direction);
4903
+ }
4904
+ function canAdjustSelectedFontSize(context, direction) {
4905
+ const workbook = context.workbook;
4906
+ const sheetId = context.targetSheetId;
4907
+ if (!context.editable || !workbook || !sheetId || sheetId !== context.activeSheetId || !isSpreadsheetFontSizeDirection(direction)) return false;
4908
+ const range = liveSpreadsheetFontSizeRange(context);
4909
+ if (spreadsheet_cell_range_spreadsheetCellRangeArea(range) > MAX_SPREADSHEET_FONT_SIZE_CELLS) return false;
4910
+ try {
4911
+ const cells = workbook.getCellsByRange(range, {
4912
+ id: sheetId
4913
+ });
4914
+ return canAdjustSpreadsheetFontSizeCells(range.row[1] - range.row[0] + 1, range.column[1] - range.column[0] + 1, (rowOffset, columnOffset)=>cells[rowOffset]?.[columnOffset] ?? null, direction);
4915
+ } catch {
4916
+ return false;
4917
+ }
4918
+ }
4919
+ function adjustSelectedFontSize(context, direction) {
4920
+ const workbook = context.workbook;
4921
+ const sheetId = context.targetSheetId;
4922
+ if (!context.editable || !workbook || !sheetId || sheetId !== context.activeSheetId || !isSpreadsheetFontSizeDirection(direction)) return false;
4923
+ const range = liveSpreadsheetFontSizeRange(context);
4924
+ if (spreadsheet_cell_range_spreadsheetCellRangeArea(range) > MAX_SPREADSHEET_FONT_SIZE_CELLS) return false;
4925
+ try {
4926
+ const cells = workbook.getCellsByRange(range, {
4927
+ id: sheetId
4928
+ });
4929
+ const calls = spreadsheetFontSizeApiCalls(cells, range, sheetId, direction);
4930
+ if (!calls.length) return false;
4931
+ workbook.batchCallApis(calls);
4932
+ return true;
4933
+ } catch {
4934
+ return false;
4935
+ }
4936
+ }
4937
+ function canAdjustSpreadsheetFontSizeCells(rowCount, columnCount, cellAt, direction) {
4938
+ for(let rowOffset = 0; rowOffset < rowCount; rowOffset += 1)for(let columnOffset = 0; columnOffset < columnCount; columnOffset += 1)if (null !== adjustedSpreadsheetFontSize(cellAt(rowOffset, columnOffset), direction)) return true;
4939
+ return false;
4940
+ }
4941
+ function liveSpreadsheetFontSizeRange(context) {
4942
+ return spreadsheet_cell_range_normalizeSpreadsheetCellRange(spreadsheetSingleRange(context.workbook?.getSelection()?.at(-1) ?? context.fallbackRange)) ?? {
4943
+ row: [
4944
+ 0,
4945
+ 0
4946
+ ],
4947
+ column: [
4948
+ 0,
4949
+ 0
4950
+ ]
4951
+ };
4952
+ }
4953
+ function isSpreadsheetFontSizeDirection(value) {
4954
+ return 'grow' === value || 'shrink' === value;
4955
+ }
4956
+ function runSpreadsheetFontSizeShortcut(event, canExecute, execute, direction) {
4957
+ if (event.repeat || isOfficeShortcutBlocked(event.target) || isSpreadsheetNativeTextUndoTarget(event.target) || !canExecute(direction)) return false;
4958
+ return execute(direction);
4959
+ }
4722
4960
  const spreadsheet_go_to_spreadsheetMaximumRows = 1048576;
4723
4961
  const spreadsheet_go_to_spreadsheetMaximumColumns = 16384;
4724
4962
  const spreadsheetGoToMaximumInputLength = 512;
@@ -7268,6 +7506,7 @@ function createSpreadsheetEditorExtensions() {
7268
7506
  })
7269
7507
  }),
7270
7508
  createSpreadsheetCellFormatExtension(),
7509
+ createSpreadsheetFontSizeExtension(),
7271
7510
  createSpreadsheetCellBorderExtension(),
7272
7511
  createSpreadsheetCellStyleExtension(),
7273
7512
  createSpreadsheetNumberFormatExtension(),
@@ -8846,6 +9085,7 @@ function SpreadsheetBorderRibbon({ can, commands }) {
8846
9085
  color
8847
9086
  };
8848
9087
  const currentDefinition = spreadsheetBorderTargetOptions.find((option)=>option.target === target)?.definition;
9088
+ const currentShortcut = currentDefinition && 'shortcut' in currentDefinition ? currentDefinition.shortcut : void 0;
8849
9089
  const currentLabel = currentDefinition?.label ?? '所有框线';
8850
9090
  const styleLabel = spreadsheetBorderStyleOptions.find((option)=>option.value === style)?.label;
8851
9091
  const menuDisabled = spreadsheetBorderTargetOptions.every((option)=>!can.setSelectedCellBorders({
@@ -8868,7 +9108,8 @@ function SpreadsheetBorderRibbon({ can, commands }) {
8868
9108
  type: "button",
8869
9109
  className: "work-spreadsheet-border-primary",
8870
9110
  "aria-label": currentLabel,
8871
- title: `${currentLabel}(${styleLabel},${color.toUpperCase()})`,
9111
+ "aria-keyshortcuts": currentShortcut?.aria,
9112
+ title: `${currentLabel}(${styleLabel},${color.toUpperCase()}${currentShortcut ? `;${currentShortcut.label}` : ''})`,
8872
9113
  disabled: !can.setSelectedCellBorders(format),
8873
9114
  onClick: ()=>commands.setSelectedCellBorders(format),
8874
9115
  children: /*#__PURE__*/ jsx(SpreadsheetBorderGlyph, {
@@ -8902,11 +9143,14 @@ function SpreadsheetBorderRibbon({ can, commands }) {
8902
9143
  ...format,
8903
9144
  target: option
8904
9145
  };
9146
+ const shortcut = 'shortcut' in definition ? definition.shortcut : void 0;
8905
9147
  return /*#__PURE__*/ jsxs("button", {
8906
9148
  type: "button",
8907
9149
  role: "menuitemradio",
8908
9150
  tabIndex: 0 === index ? 0 : -1,
8909
9151
  "aria-checked": option === target,
9152
+ "aria-label": definition.label,
9153
+ "aria-keyshortcuts": shortcut?.aria,
8910
9154
  disabled: !can.setSelectedCellBorders(next),
8911
9155
  onClick: ()=>{
8912
9156
  setTarget(option);
@@ -8918,7 +9162,11 @@ function SpreadsheetBorderRibbon({ can, commands }) {
8918
9162
  target: option
8919
9163
  }),
8920
9164
  /*#__PURE__*/ jsx("span", {
9165
+ className: "work-spreadsheet-border-target-label",
8921
9166
  children: definition.label
9167
+ }),
9168
+ shortcut && /*#__PURE__*/ jsx("kbd", {
9169
+ children: shortcut.label
8922
9170
  })
8923
9171
  ]
8924
9172
  }, definition.id);
@@ -9058,6 +9306,8 @@ function SpreadsheetFontRibbonGroup({ can, commands, toolbarCell }) {
9058
9306
  const strike = 1 === Number(toolbarCell?.cl);
9059
9307
  const textColor = 'string' == typeof toolbarCell?.fc ? toolbarCell.fc : '#172033';
9060
9308
  const fillColor = 'string' == typeof toolbarCell?.bg ? toolbarCell.bg : '#ffffff';
9309
+ const growFont = spreadsheetCommandCatalog.growFont;
9310
+ const shrinkFont = spreadsheetCommandCatalog.shrinkFont;
9061
9311
  return /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
9062
9312
  label: "字体",
9063
9313
  priority: "high",
@@ -9077,6 +9327,28 @@ function SpreadsheetFontRibbonGroup({ can, commands, toolbarCell }) {
9077
9327
  options: spreadsheetFontSizeOptions(toolbarCell?.fs),
9078
9328
  onValueChange: (value)=>commands.setCellFormat('fs', Number(value))
9079
9329
  }),
9330
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
9331
+ label: growFont.label,
9332
+ title: `${growFont.label}(${growFont.shortcut.label})`,
9333
+ "aria-keyshortcuts": growFont.shortcut.aria,
9334
+ displayLabel: false,
9335
+ disabled: !can.adjustFontSize('grow'),
9336
+ onClick: ()=>commands.adjustFontSize('grow'),
9337
+ children: /*#__PURE__*/ jsx(AArrowUp, {
9338
+ size: 15
9339
+ })
9340
+ }),
9341
+ /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
9342
+ label: shrinkFont.label,
9343
+ title: `${shrinkFont.label}(${shrinkFont.shortcut.label})`,
9344
+ "aria-keyshortcuts": shrinkFont.shortcut.aria,
9345
+ displayLabel: false,
9346
+ disabled: !can.adjustFontSize('shrink'),
9347
+ onClick: ()=>commands.adjustFontSize('shrink'),
9348
+ children: /*#__PURE__*/ jsx(AArrowDown, {
9349
+ size: 15
9350
+ })
9351
+ }),
9080
9352
  /*#__PURE__*/ jsx(SpreadsheetFontToggle, {
9081
9353
  active: bold,
9082
9354
  attribute: "bl",
@@ -22849,6 +23121,7 @@ function spreadsheetCommandsWithGridFocus(commands, restoreFocus) {
22849
23121
  return {
22850
23122
  ...commands,
22851
23123
  adjustDecimalPlaces: afterSuccessfulCommand(commands.adjustDecimalPlaces),
23124
+ adjustFontSize: afterSuccessfulCommand(commands.adjustFontSize),
22852
23125
  applyAutoSum: afterSuccessfulCommand(commands.applyAutoSum),
22853
23126
  activateFormatPainter: afterSuccessfulCommand(commands.activateFormatPainter),
22854
23127
  cancelFormatPainter: afterSuccessfulCommand(commands.cancelFormatPainter),
@@ -193,6 +193,34 @@ export declare const spreadsheetCommandCatalog: {
193
193
  readonly editor: readonly ["Mod-5"];
194
194
  };
195
195
  };
196
+ readonly growFont: {
197
+ readonly id: "font.grow";
198
+ readonly label: "增大字号";
199
+ readonly location: {
200
+ readonly area: "ribbon";
201
+ readonly tab: "home";
202
+ readonly group: "font";
203
+ };
204
+ readonly shortcut: {
205
+ readonly label: "Cmd/Ctrl+Shift+. 或 Cmd/Ctrl+]";
206
+ readonly aria: "Control+Shift+. Meta+Shift+. Control+] Meta+]";
207
+ readonly editor: readonly ["Mod-Shift-.", "Mod-]"];
208
+ };
209
+ };
210
+ readonly shrinkFont: {
211
+ readonly id: "font.shrink";
212
+ readonly label: "减小字号";
213
+ readonly location: {
214
+ readonly area: "ribbon";
215
+ readonly tab: "home";
216
+ readonly group: "font";
217
+ };
218
+ readonly shortcut: {
219
+ readonly label: "Cmd/Ctrl+Shift+, 或 Cmd/Ctrl+[";
220
+ readonly aria: "Control+Shift+, Meta+Shift+, Control+[ Meta+[";
221
+ readonly editor: readonly ["Mod-Shift-,", "Mod-["];
222
+ };
223
+ };
196
224
  readonly numberFormatGeneral: {
197
225
  readonly id: "number.general";
198
226
  readonly label: "常规";
@@ -394,6 +422,11 @@ export declare const spreadsheetCommandCatalog: {
394
422
  readonly tab: "home";
395
423
  readonly group: "font";
396
424
  };
425
+ readonly shortcut: {
426
+ readonly label: "Cmd/Ctrl+Shift+_";
427
+ readonly aria: "Control+Shift+_ Meta+Shift+_";
428
+ readonly editor: readonly ["Mod-Shift-_", "Mod-Shift-Minus"];
429
+ };
397
430
  };
398
431
  readonly borderAll: {
399
432
  readonly id: "font.borderAll";
@@ -412,6 +445,11 @@ export declare const spreadsheetCommandCatalog: {
412
445
  readonly tab: "home";
413
446
  readonly group: "font";
414
447
  };
448
+ readonly shortcut: {
449
+ readonly label: "Cmd/Ctrl+Shift+&";
450
+ readonly aria: "Control+Shift+& Meta+Shift+&";
451
+ readonly editor: readonly ["Mod-Shift-&", "Mod-Shift-7"];
452
+ };
415
453
  };
416
454
  readonly borderInside: {
417
455
  readonly id: "font.borderInside";
@@ -9,6 +9,7 @@ import type { SpreadsheetCellFormatRequest } from './spreadsheet-cell-format';
9
9
  import type { SpreadsheetCellRange } from './spreadsheet-cell-range';
10
10
  import type { SpreadsheetCellStyleChoice } from './spreadsheet-cell-style';
11
11
  import type { SpreadsheetCellFillDirection } from './spreadsheet-cell-fill';
12
+ import { type SpreadsheetFontSizeDirection } from './spreadsheet-font-size-command';
12
13
  import type { SpreadsheetDataValidationRequest, SpreadsheetDataValidationTarget } from './spreadsheet-data-validation';
13
14
  import { type SpreadsheetCellMergeCommand } from './spreadsheet-cell-merge';
14
15
  import type { SpreadsheetFormatPainterMode } from './spreadsheet-format-painter';
@@ -149,6 +150,7 @@ export interface SpreadsheetEditorCommands {
149
150
  activateFormatPainter: (mode: SpreadsheetFormatPainterMode) => boolean;
150
151
  addSheet: () => boolean;
151
152
  adjustDecimalPlaces: (direction: SpreadsheetDecimalPlacesDirection) => boolean;
153
+ adjustFontSize: (direction: SpreadsheetFontSizeDirection) => boolean;
152
154
  applyCellStyle: (preset: SpreadsheetCellStyleChoice) => boolean;
153
155
  applyCellFormat: (request: SpreadsheetCellFormatRequest) => boolean;
154
156
  applyDataValidation: (request: SpreadsheetDataValidationRequest) => boolean;
@@ -0,0 +1,18 @@
1
+ import type { Cell } from '@fortune-sheet/core';
2
+ import type { WorkSpreadsheetContent } from '../work-types';
3
+ import type { SpreadsheetCommandContext, SpreadsheetEditorCommands } from './spreadsheet-command-controller';
4
+ import { type SpreadsheetCellRange, type SpreadsheetCellRangeInput } from './spreadsheet-cell-range';
5
+ import { type SpreadsheetFontSizeDirection } from './spreadsheet-font-size';
6
+ import { type OfficeEditorExtension } from './office-editor-extension';
7
+ export { nextSpreadsheetFontSize } from './spreadsheet-font-size';
8
+ export type { SpreadsheetFontSizeDirection } from './spreadsheet-font-size';
9
+ export declare const MAX_SPREADSHEET_FONT_SIZE_CELLS = 10000;
10
+ export interface SpreadsheetFontSizeApiCall {
11
+ name: 'setCellFormatByRange';
12
+ args: ['fs', number, SpreadsheetCellRange, {
13
+ id: string;
14
+ }];
15
+ }
16
+ export declare function createSpreadsheetFontSizeExtension(): OfficeEditorExtension<SpreadsheetCommandContext, SpreadsheetEditorCommands>;
17
+ export declare function canAdjustSpreadsheetFontSize(content: WorkSpreadsheetContent, sheetId: string, range: SpreadsheetCellRangeInput, direction: SpreadsheetFontSizeDirection): boolean;
18
+ export declare function spreadsheetFontSizeApiCalls(cells: readonly (readonly (Cell | null)[])[], range: SpreadsheetCellRangeInput, sheetId: string, direction: SpreadsheetFontSizeDirection): SpreadsheetFontSizeApiCall[];
@@ -0,0 +1,4 @@
1
+ export declare const DEFAULT_SPREADSHEET_FONT_SIZE = 10;
2
+ export declare const spreadsheetFontSizes: readonly [9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 28, 36, 48, 72];
3
+ export type SpreadsheetFontSizeDirection = 'grow' | 'shrink';
4
+ export declare function nextSpreadsheetFontSize(current: number, direction: SpreadsheetFontSizeDirection): number | null;
Binary file
package/dist/styles.css CHANGED
@@ -15180,7 +15180,7 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
15180
15180
 
15181
15181
  .work-spreadsheet-border-panel {
15182
15182
  --work-office-menu-accent: var(--work-office-control-accent, #159469);
15183
- width: min(292px, 100vw - 32px);
15183
+ width: min(360px, 100vw - 32px);
15184
15184
  padding: 8px;
15185
15185
  }
15186
15186
 
@@ -15207,7 +15207,7 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
15207
15207
  background: none;
15208
15208
  border: 0;
15209
15209
  border-radius: 6px;
15210
- grid-template-columns: 22px minmax(0, 1fr);
15210
+ grid-template-columns: 22px minmax(0, 1fr) auto;
15211
15211
  align-items: center;
15212
15212
  gap: 7px;
15213
15213
  padding: 4px 7px;
@@ -15231,13 +15231,20 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
15231
15231
  opacity: .4;
15232
15232
  }
15233
15233
 
15234
- .work-spreadsheet-border-targets > button > span:last-child {
15234
+ .work-spreadsheet-border-target-label {
15235
15235
  text-overflow: ellipsis;
15236
15236
  white-space: nowrap;
15237
15237
  font-size: 11px;
15238
15238
  overflow: hidden;
15239
15239
  }
15240
15240
 
15241
+ .work-spreadsheet-border-targets kbd {
15242
+ color: var(--a3s-faint);
15243
+ font: inherit;
15244
+ white-space: nowrap;
15245
+ font-size: 8px;
15246
+ }
15247
+
15241
15248
  .work-spreadsheet-border-glyph {
15242
15249
  border: 1px solid color-mix(in srgb, currentcolor 34%, transparent);
15243
15250
  width: 17px;
@@ -15377,9 +15384,14 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
15377
15384
  }
15378
15385
 
15379
15386
  .work-spreadsheet-border-targets > button {
15387
+ grid-template-columns: 22px minmax(0, 1fr);
15380
15388
  min-height: 44px;
15381
15389
  }
15382
15390
 
15391
+ .work-spreadsheet-border-targets kbd {
15392
+ display: none;
15393
+ }
15394
+
15383
15395
  .work-spreadsheet-border-settings {
15384
15396
  grid-template-columns: 1fr;
15385
15397
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a3s-lab/office",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Open-source collaborative browser editors for documents, Markdown, spreadsheets, presentations, and PDFs.",
5
5
  "keywords": [
6
6
  "office",
@@ -135,6 +135,8 @@
135
135
  "test:e2e:spreadsheet-ribbon:check": "a3s-test check tests/e2e/spreadsheet-ribbon-alignment.acl --json",
136
136
  "test:e2e:spreadsheet-cell-style": "a3s-test run tests/e2e/spreadsheet-cell-style.acl --json",
137
137
  "test:e2e:spreadsheet-cell-style:check": "a3s-test check tests/e2e/spreadsheet-cell-style.acl --json",
138
+ "test:e2e:spreadsheet-font-size-border-shortcuts": "a3s-test run tests/e2e/spreadsheet-font-size-border-shortcuts.acl --json",
139
+ "test:e2e:spreadsheet-font-size-border-shortcuts:check": "a3s-test check tests/e2e/spreadsheet-font-size-border-shortcuts.acl --json",
138
140
  "test:e2e:spreadsheet-table": "a3s-test run tests/e2e/spreadsheet-table.acl --json",
139
141
  "test:e2e:spreadsheet-table:check": "a3s-test check tests/e2e/spreadsheet-table.acl --json",
140
142
  "test:e2e:spreadsheet-cell-fill": "a3s-test run tests/e2e/spreadsheet-cell-fill.acl --json",