Skip to content

Commit 0772c15

Browse files
SamTV12345claude
andauthored
feat(sheet): Ctrl+B/I/U toggle bold, italic, underline (#365)
Keyboard shortcuts for the ribbon's style toggles, applied to the current selection. Skips real form fields so the formula bar keeps these keys, and does NOT require grid focus (applying a style blurs the cell, so requiring focus would break chaining B then I). preventDefault stops the browser's contenteditable rich-text default. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d3a3e30 commit 0772c15

2 files changed

Lines changed: 36 additions & 0 deletions

File tree

playwright/specs/sheet_excel_chrome.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,24 @@ test.describe('Sheet Excel chrome', () => {
5151
await expect(cell(page, 0, 0)).toHaveCSS('font-size', /^26\.6/);
5252
});
5353

54+
test('Ctrl+B/I/U toggle bold, italic, underline on the selection', async ({ page }) => {
55+
const padId = `xl-fmtkeys-${Date.now()}`;
56+
await openSheet(page, padId);
57+
await commitCell(page, 0, 0, 'x'); // A1
58+
await cell(page, 0, 0).click();
59+
60+
await page.keyboard.press('Control+b');
61+
await expect(cell(page, 0, 0)).toHaveCSS('font-weight', /700|bold/);
62+
await page.keyboard.press('Control+i');
63+
await expect(cell(page, 0, 0)).toHaveCSS('font-style', 'italic');
64+
await page.keyboard.press('Control+u');
65+
await expect(cell(page, 0, 0)).toHaveCSS('text-decoration', /underline/);
66+
67+
// Ctrl+B again toggles bold back off.
68+
await page.keyboard.press('Control+b');
69+
await expect(cell(page, 0, 0)).toHaveCSS('font-weight', /400|normal/);
70+
});
71+
5472
test('wrap text switches the cell to normal white-space', async ({ page }) => {
5573
const padId = `xl-wrap-${Date.now()}`;
5674
await openSheet(page, padId);

ui/src/js/sheet/sheetEditor.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,24 @@ export function startSheetEditor(root: HTMLElement): void {
573573
doPaste();
574574
return;
575575
}
576+
// Ctrl/Cmd+B/I/U toggle the style on the selection, mirroring the ribbon's
577+
// toggle buttons. Applying a style blurs the active cell, so the next
578+
// shortcut arrives with focus on <body> — we must NOT require grid focus
579+
// (that would break chaining B then I). Instead just skip real form fields
580+
// so the formula bar keeps these keys. preventDefault stops the browser's
581+
// contenteditable rich-text default on the focused cell.
582+
const tag = (e.target as HTMLElement | null)?.tagName;
583+
const inField = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
584+
if (mod && !editingNow() && !readOnly && !inField) {
585+
const k = e.key.toLowerCase();
586+
const styleKey = k === 'b' ? 'bold' : k === 'i' ? 'italic' : k === 'u' ? 'underline' : null;
587+
if (styleKey) {
588+
e.preventDefault();
589+
const on = propsOf(selection.focus.row, selection.focus.col)[styleKey] === '1';
590+
applyStyleToSelection({ [styleKey]: on ? '' : '1' });
591+
return;
592+
}
593+
}
576594
// Clear the selection (single cell or range), like Excel. The grid-focus
577595
// guard replaces the old single-cell exclusion: it lets Delete clear one
578596
// cell while still keeping Backspace working in the formula bar and any

0 commit comments

Comments
 (0)