Skip to content

Commit 900dab0

Browse files
committed
feat(ui): add clear-formatting button to floating toolbar
1 parent e971c54 commit 900dab0

1 file changed

Lines changed: 28 additions & 3 deletions

File tree

src/components/molecules/MarkdownEditor/FloatingFormatToolbarPlugin.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import {
1010
SELECTION_CHANGE_COMMAND,
1111
type TextFormatType,
1212
} from "lexical";
13-
import { Bold, Code, Italic, Strikethrough } from "lucide-react";
13+
import { Bold, Code, Italic, RemoveFormatting, Strikethrough } from "lucide-react";
1414
import { AnimatePresence, m } from "motion/react";
1515
import { type ReactNode, useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
1616
import { createPortal } from "react-dom";
1717

18-
import { $getSelectedFormattableTextNodes } from "./codeBlock";
18+
import { $getSelectedFormattableTextNodes, $isFormattableTextNode } from "./codeBlock";
1919
import { type Anchor, firstLineAnchor, placeCenteredAbove } from "./placement";
2020

2121
// The four inline formats the toolbar toggles, in display order. Each maps to a
@@ -30,6 +30,9 @@ const FORMATS: readonly ToolbarFormat[] = [
3030
{ type: "code", label: "Inline code", icon: Code },
3131
];
3232

33+
// Separator definition between format toggles and the clear-formatting button.
34+
const CLEAR_FORMAT = { label: "Clear formatting", icon: RemoveFormatting } as const;
35+
3336
// Initial estimate of the toolbar size, derived from the fixed markup below, so
3437
// the first frame can be positioned before the node has been measured (the real
3538
// size is cached on mount and takes over — see `measure`). Each button is a
@@ -39,7 +42,7 @@ const BUTTON_SIZE = 24;
3942
const BUTTON_GAP = 2;
4043
const PANEL_INSET = 5;
4144
const TOOLBAR_WIDTH =
42-
FORMATS.length * BUTTON_SIZE + (FORMATS.length - 1) * BUTTON_GAP + 2 * PANEL_INSET;
45+
(FORMATS.length + 1) * BUTTON_SIZE + FORMATS.length * BUTTON_GAP + 2 * PANEL_INSET;
4346
const TOOLBAR_HEIGHT = BUTTON_SIZE + 2 * PANEL_INSET;
4447

4548
// As the selection is dragged wider, its bounding box — and thus the toolbar's
@@ -254,6 +257,28 @@ export function FloatingFormatToolbarPlugin(): ReactNode {
254257
</button>
255258
);
256259
})}
260+
{/* Clear formatting */}
261+
<div className="bg-cork-border/40 mx-0.5 h-4 w-px" />
262+
<button
263+
key="clear-formatting"
264+
type="button"
265+
aria-label={CLEAR_FORMAT.label}
266+
title={CLEAR_FORMAT.label}
267+
onMouseDown={(e) => e.preventDefault()}
268+
onClick={() => {
269+
editor.update(() => {
270+
const selection = $getSelection();
271+
if (!$isRangeSelection(selection) || selection.isCollapsed()) return;
272+
for (const node of selection.extract().filter($isFormattableTextNode)) {
273+
node.setFormat(0);
274+
}
275+
selection.format = 0;
276+
});
277+
}}
278+
className="text-cork-muted hover:bg-cork-border/50 hover:text-cork-text flex cursor-pointer items-center justify-center rounded-md p-1 transition-colors duration-200"
279+
>
280+
<RemoveFormatting className="size-4" />
281+
</button>
257282
</m.div>
258283
)}
259284
</AnimatePresence>,

0 commit comments

Comments
 (0)