diff --git a/examples/02-ui-components/02-formatting-toolbar-buttons/App.tsx b/examples/02-ui-components/02-formatting-toolbar-buttons/App.tsx index e34a0d9cdf..e2f093fcf9 100644 --- a/examples/02-ui-components/02-formatting-toolbar-buttons/App.tsx +++ b/examples/02-ui-components/02-formatting-toolbar-buttons/App.tsx @@ -70,57 +70,80 @@ export default function App() { return ( ( - - + formattingToolbar={(props) => ( + + {/* Extra button to toggle blue text & background */} - - + + {/* Extra button to toggle code styles */} - + - + )} /> diff --git a/examples/02-ui-components/03-formatting-toolbar-block-type-items/App.tsx b/examples/02-ui-components/03-formatting-toolbar-block-type-items/App.tsx index 315e4afc5b..d57f0f1ad6 100644 --- a/examples/02-ui-components/03-formatting-toolbar-block-type-items/App.tsx +++ b/examples/02-ui-components/03-formatting-toolbar-block-type-items/App.tsx @@ -53,8 +53,9 @@ export default function App() { return ( ( + formattingToolbar={(props) => ( {/* Replaces the default Formatting Toolbar. */} ( - - + formattingToolbar={(props) => ( + + - - + + @@ -124,24 +137,33 @@ export default function App() { - + - + )} /> diff --git a/examples/05-custom-schema/react-custom-blocks/App.tsx b/examples/05-custom-schema/react-custom-blocks/App.tsx index 49a541a21c..8f743abe95 100644 --- a/examples/05-custom-schema/react-custom-blocks/App.tsx +++ b/examples/05-custom-schema/react-custom-blocks/App.tsx @@ -110,11 +110,11 @@ export const bracketsParagraphBlock = createReactBlockSpec( { render: (props) => (
-
{"["}
- {"{"} +
{"["}
+ {"{"}
- {"}"} -
{"]"}
+ {"}"} +
{"]"}
), } diff --git a/examples/05-custom-schema/react-custom-styles/App.tsx b/examples/05-custom-schema/react-custom-styles/App.tsx index aba31bdeda..027468cdab 100644 --- a/examples/05-custom-schema/react-custom-styles/App.tsx +++ b/examples/05-custom-schema/react-custom-styles/App.tsx @@ -52,7 +52,7 @@ const CustomFormattingToolbar = (props: FormattingToolbarProps) => { const activeStyles = useActiveStyles(editor); return ( - + { diff --git a/examples/vanilla-js/react-vanilla-custom-styles/App.tsx b/examples/vanilla-js/react-vanilla-custom-styles/App.tsx index ee30b7b8ef..9753a0c48c 100644 --- a/examples/vanilla-js/react-vanilla-custom-styles/App.tsx +++ b/examples/vanilla-js/react-vanilla-custom-styles/App.tsx @@ -64,7 +64,7 @@ const CustomFormattingToolbar = (props: FormattingToolbarProps) => { const activeStyles = useActiveStyles(editor); return ( - + { diff --git a/packages/core/src/editor/BlockNoteEditor.ts b/packages/core/src/editor/BlockNoteEditor.ts index 65596426c7..2f1836540f 100644 --- a/packages/core/src/editor/BlockNoteEditor.ts +++ b/packages/core/src/editor/BlockNoteEditor.ts @@ -155,7 +155,11 @@ export class BlockNoteEditor< public readonly inlineContentImplementations: InlineContentSpecs; public readonly styleImplementations: StyleSpecs; - public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin; + public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin< + BSchema, + ISchema, + SSchema + >; public readonly hyperlinkToolbar: HyperlinkToolbarProsemirrorPlugin< BSchema, ISchema, @@ -361,7 +365,7 @@ export class BlockNoteEditor< * @deprecated, use `editor.document` instead */ public get topLevelBlocks(): Block[] { - return this.topLevelBlocks; + return this.document; } /** diff --git a/packages/core/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts b/packages/core/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts index a60f1cb398..afc1f9a711 100644 --- a/packages/core/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +++ b/packages/core/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts @@ -6,11 +6,22 @@ import type { BlockNoteEditor } from "../../editor/BlockNoteEditor"; import { UiElementPosition } from "../../extensions-shared/UiElementPosition"; import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema"; import { EventEmitter } from "../../util/EventEmitter"; - -export type FormattingToolbarState = UiElementPosition; - -export class FormattingToolbarView { - public state?: FormattingToolbarState; +import { Block } from "../../blocks/defaultBlocks"; + +export type FormattingToolbarState< + BSchema extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema +> = UiElementPosition & { + selectedBlocks: Block[]; +}; + +export class FormattingToolbarView< + BSchema extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema +> { + public state?: FormattingToolbarState; public emitUpdate: () => void; public preventHide = false; @@ -25,13 +36,9 @@ export class FormattingToolbarView { }) => boolean = ({ state }) => !state.selection.empty; constructor( - private readonly editor: BlockNoteEditor< - BlockSchema, - InlineContentSchema, - StyleSchema - >, + private readonly editor: BlockNoteEditor, private readonly pmView: EditorView, - emitUpdate: (state: FormattingToolbarState) => void + emitUpdate: (state: FormattingToolbarState) => void ) { this.emitUpdate = () => { if (!this.state) { @@ -145,9 +152,14 @@ export class FormattingToolbarView { !this.preventShow && (shouldShow || this.preventHide) ) { + const blockWithTextCursor = this.editor.getTextCursorPosition().block; + const selection = this.editor.getSelection(); + this.state = { show: true, referencePos: this.getSelectionBoundingBox(), + selectedBlocks: + selection !== undefined ? selection.blocks : [blockWithTextCursor], }; this.emitUpdate(); @@ -205,11 +217,15 @@ export const formattingToolbarPluginKey = new PluginKey( "FormattingToolbarPlugin" ); -export class FormattingToolbarProsemirrorPlugin extends EventEmitter { - private view: FormattingToolbarView | undefined; +export class FormattingToolbarProsemirrorPlugin< + BSchema extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema +> extends EventEmitter { + private view: FormattingToolbarView | undefined; public readonly plugin: Plugin; - constructor(editor: BlockNoteEditor) { + constructor(editor: BlockNoteEditor) { super(); this.plugin = new Plugin({ key: formattingToolbarPluginKey, @@ -222,7 +238,9 @@ export class FormattingToolbarProsemirrorPlugin extends EventEmitter { }); } - public onUpdate(callback: (state: FormattingToolbarState) => void) { + public onUpdate( + callback: (state: FormattingToolbarState) => void + ) { return this.on("update", callback); } } diff --git a/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx b/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx index 839903c172..10be98ac25 100644 --- a/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx +++ b/packages/react/src/components/FormattingToolbar/FormattingToolbarController.tsx @@ -1,6 +1,9 @@ import { BlockSchema, + DefaultBlockSchema, + DefaultInlineContentSchema, DefaultProps, + DefaultStyleSchema, InlineContentSchema, StyleSchema, } from "@blocknote/core"; @@ -29,14 +32,14 @@ const textAlignmentToPlacement = ( } }; -export const FormattingToolbarController = (props: { - formattingToolbar?: FC; +export const FormattingToolbarController = < + BSchema extends BlockSchema = DefaultBlockSchema, + I extends InlineContentSchema = DefaultInlineContentSchema, + S extends StyleSchema = DefaultStyleSchema +>(props: { + formattingToolbar?: FC>; }) => { - const editor = useBlockNoteEditor< - BlockSchema, - InlineContentSchema, - StyleSchema - >(); + const editor = useBlockNoteEditor(); const [placement, setPlacement] = useState<"top-start" | "top" | "top-end">( () => { @@ -83,11 +86,13 @@ export const FormattingToolbarController = (props: { return null; } + const { show, referencePos, ...data } = state; + const Component = props.formattingToolbar || FormattingToolbar; return (
- +
); }; diff --git a/packages/react/src/components/FormattingToolbar/FormattingToolbarProps.ts b/packages/react/src/components/FormattingToolbar/FormattingToolbarProps.ts index 158ac4ec32..032e5dc19c 100644 --- a/packages/react/src/components/FormattingToolbar/FormattingToolbarProps.ts +++ b/packages/react/src/components/FormattingToolbar/FormattingToolbarProps.ts @@ -1,5 +1,19 @@ import { BlockTypeDropdownItem } from "./mantine/DefaultDropdowns/BlockTypeDropdown"; +import { + BlockSchema, + DefaultBlockSchema, + DefaultInlineContentSchema, + DefaultStyleSchema, + FormattingToolbarState, + InlineContentSchema, + StyleSchema, + UiElementPosition, +} from "@blocknote/core"; -export type FormattingToolbarProps = { +export type FormattingToolbarProps< + BSchema extends BlockSchema = DefaultBlockSchema, + I extends InlineContentSchema = DefaultInlineContentSchema, + S extends StyleSchema = DefaultStyleSchema +> = Omit, keyof UiElementPosition> & { blockTypeDropdownItems?: BlockTypeDropdownItem[]; }; diff --git a/packages/react/src/components/FormattingToolbar/mantine/DefaultButtons/BasicTextStyleButton.tsx b/packages/react/src/components/FormattingToolbar/mantine/DefaultButtons/BasicTextStyleButton.tsx index 1f5d58bd93..def4cba433 100644 --- a/packages/react/src/components/FormattingToolbar/mantine/DefaultButtons/BasicTextStyleButton.tsx +++ b/packages/react/src/components/FormattingToolbar/mantine/DefaultButtons/BasicTextStyleButton.tsx @@ -1,6 +1,10 @@ import { + Block, BlockNoteEditor, BlockSchema, + DefaultBlockSchema, + DefaultInlineContentSchema, + DefaultStyleSchema, formatKeyboardShortcut, InlineContentSchema, StyleSchema, @@ -17,7 +21,6 @@ import { import { useBlockNoteEditor } from "../../../../hooks/useBlockNoteEditor"; import { useEditorContentOrSelectionChange } from "../../../../hooks/useEditorContentOrSelectionChange"; -import { useSelectedBlocks } from "../../../../hooks/useSelectedBlocks"; import { ToolbarButton } from "../../../mantine-shared/Toolbar/ToolbarButton"; type BasicTextStyle = "bold" | "italic" | "underline" | "strike" | "code"; @@ -38,12 +41,16 @@ const shortcuts = { code: "", } satisfies Record; -function checkBasicTextStyleInSchema