Upgrade blocknote dependencies from 0.31.1 to 0.47.0. (#18207)
This PR pgrades all BlockNote packages (@blocknote/core, @blocknote/react, @blocknote/mantine, @blocknote/server-util, @blocknote/xl-docx-exporter, @blocknote/xl-pdf-exporter) to 0.47.0 and adapts the codebase to the new API. ### Changes - Dependency upgrades: Bumped all BlockNote packages to 0.47.0, added required Mantine v8 peer dependencies, removed unnecessary prosemirror resolutions - Formatting toolbar: Replaced the manual reimplementation of FormattingToolbarController (which handled visibility, positioning, portal rendering, text-alignment-based placement, and a dangerouslySetInnerHTML transition trick) with BlockNote's built-in FormattingToolbarController. The toolbar buttons themselves are unchanged. - Side menu: Replaced manual drag handle menu positioning and rendering (DashboardBlockDragHandleMenu, DashboardBlockColorPicker, and their floating configs) with BlockNote's built-in SideMenuController, DragHandleButton, and DragHandleMenu components. Deleted 4 files that became dead code. - Extension API migration: Replaced deprecated editor.suggestionMenus and editor.formattingToolbar APIs with the new extension system (SuggestionMenu, useExtensionState, editor.getExtension()) - Slash menu fixes: Filtered out BlockNote's new default "File" item (added in 0.47) to avoid duplicates with our custom one; added icon mappings for new block types (Toggle List, Divider, Toggle Headings, Headings 4-6) - Server-side: Switched @blocknote/server-util to dynamic import() to handle ESM-only transitive dependencies in CJS context
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { filterSuggestionItems } from '@blocknote/core';
|
||||
import { filterSuggestionItems } from '@blocknote/core/extensions';
|
||||
import { BlockNoteView } from '@blocknote/mantine';
|
||||
import { SuggestionMenuController } from '@blocknote/react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
+19
-27
@@ -1,18 +1,16 @@
|
||||
import { type BLOCK_SCHEMA } from '@/blocknote-editor/blocks/Schema';
|
||||
import { SuggestionMenu } from '@blocknote/core/extensions';
|
||||
import { useExtensionState } from '@blocknote/react';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { isSlashMenuOpenComponentState } from '@/blocknote-editor/states/isSlashMenuOpenComponentState';
|
||||
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export type BlockEditorDropdownFocusEffectProps = {
|
||||
editor: typeof BLOCK_SCHEMA.BlockNoteEditor;
|
||||
};
|
||||
export const BlockEditorDropdownFocusEffect = () => {
|
||||
const [prevShowing, setPrevShowing] = useState<boolean | null>(null);
|
||||
|
||||
export const BlockEditorDropdownFocusEffect = ({
|
||||
editor,
|
||||
}: BlockEditorDropdownFocusEffectProps) => {
|
||||
const isSlashMenuOpenState = useAtomComponentStateCallbackState(
|
||||
isSlashMenuOpenComponentState,
|
||||
);
|
||||
@@ -25,30 +23,21 @@ export const BlockEditorDropdownFocusEffect = ({
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const updateCallBack = useCallback(
|
||||
(event: any) => {
|
||||
const eventWantsToOpen = event.show === true;
|
||||
const suggestionState = useExtensionState(SuggestionMenu);
|
||||
|
||||
const isSlashMenuShowing =
|
||||
suggestionState?.show === true && suggestionState?.triggerCharacter === '/';
|
||||
|
||||
const syncSlashMenuState = useCallback(
|
||||
(showing: boolean) => {
|
||||
const isAlreadyOpen = store.get(isSlashMenuOpenState);
|
||||
|
||||
const shouldOpen = eventWantsToOpen && !isAlreadyOpen;
|
||||
|
||||
if (shouldOpen) {
|
||||
if (showing && isAlreadyOpen !== true) {
|
||||
setActiveDropdownFocusIdAndMemorizePrevious('custom-slash-menu');
|
||||
store.set(isSlashMenuOpenState, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const eventWantsToClose = event.show === false;
|
||||
|
||||
const isAlreadyClosed = !isAlreadyOpen;
|
||||
|
||||
const shouldClose = eventWantsToClose && !isAlreadyClosed;
|
||||
|
||||
if (shouldClose) {
|
||||
} else if (!showing && isAlreadyOpen === true) {
|
||||
goBackToPreviousDropdownFocusId();
|
||||
store.set(isSlashMenuOpenState, false);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[
|
||||
@@ -59,7 +48,10 @@ export const BlockEditorDropdownFocusEffect = ({
|
||||
],
|
||||
);
|
||||
|
||||
editor.suggestionMenus.on('update /', updateCallBack);
|
||||
if (prevShowing !== isSlashMenuShowing) {
|
||||
setPrevShowing(isSlashMenuShowing);
|
||||
syncSlashMenuState(isSlashMenuShowing);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
+12
-18
@@ -1,42 +1,36 @@
|
||||
import { type BLOCK_SCHEMA } from '@/blocknote-editor/blocks/Schema';
|
||||
|
||||
import { useComponentsContext } from '@blocknote/react';
|
||||
import { SuggestionMenu } from '@blocknote/core/extensions';
|
||||
import { useBlockNoteEditor, useComponentsContext } from '@blocknote/react';
|
||||
|
||||
type CustomAddBlockItemProps = {
|
||||
editor: typeof BLOCK_SCHEMA.BlockNoteEditor;
|
||||
children: React.ReactNode; // Adding the children prop
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
type ContentItem = {
|
||||
type: string;
|
||||
text: string;
|
||||
styles: any;
|
||||
};
|
||||
export const CustomAddBlockItem = ({
|
||||
editor,
|
||||
children,
|
||||
}: CustomAddBlockItemProps) => {
|
||||
const Components = useComponentsContext();
|
||||
const blockNoteEditor = useBlockNoteEditor();
|
||||
|
||||
if (!Components) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
const blockIdentifier = editor.getTextCursorPosition().block;
|
||||
const currentBlockContent = blockIdentifier?.content as
|
||||
| Array<ContentItem>
|
||||
| undefined;
|
||||
const currentBlock = blockNoteEditor.getTextCursorPosition().block;
|
||||
const blockContent = currentBlock?.content;
|
||||
|
||||
const [firstElement] = currentBlockContent || [];
|
||||
const isEmpty = Array.isArray(blockContent) && blockContent.length === 0;
|
||||
|
||||
if (firstElement === undefined) {
|
||||
editor.openSuggestionMenu('/');
|
||||
} else {
|
||||
editor.openSuggestionMenu('/');
|
||||
editor.sideMenu.unfreezeMenu();
|
||||
if (isEmpty) {
|
||||
editor.setTextCursorPosition(currentBlock);
|
||||
}
|
||||
|
||||
blockNoteEditor.getExtension(SuggestionMenu)?.openSuggestionMenu('/');
|
||||
};
|
||||
|
||||
return (
|
||||
<Components.Generic.Menu.Item onClick={handleClick}>
|
||||
{children}
|
||||
|
||||
@@ -25,15 +25,11 @@ export const CustomSideMenu = ({ editor }: CustomSideMenuProps) => {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<SideMenuController
|
||||
sideMenu={(props) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<SideMenu {...props}>
|
||||
sideMenu={() => (
|
||||
<SideMenu>
|
||||
<DragHandleButton
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
dragHandleMenu={(props) => (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<DragHandleMenu {...props}>
|
||||
dragHandleMenu={() => (
|
||||
<DragHandleMenu>
|
||||
<CustomAddBlockItem editor={editor}>
|
||||
<CustomSideMenuOptions
|
||||
LeftIcon={IconPlus}
|
||||
@@ -41,17 +37,14 @@ export const CustomSideMenu = ({ editor }: CustomSideMenuProps) => {
|
||||
Variant="normal"
|
||||
/>
|
||||
</CustomAddBlockItem>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<BlockColorsItem {...props}>
|
||||
<BlockColorsItem>
|
||||
<CustomSideMenuOptions
|
||||
LeftIcon={IconColorSwatch}
|
||||
text={t`Change Color`}
|
||||
Variant="normal"
|
||||
/>
|
||||
</BlockColorsItem>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<RemoveBlockItem {...props}>
|
||||
{' '}
|
||||
<RemoveBlockItem>
|
||||
<CustomSideMenuOptions
|
||||
LeftIcon={IconTrash}
|
||||
text={t`Delete`}
|
||||
|
||||
@@ -33,7 +33,7 @@ export const CustomSlashMenu = ({
|
||||
const currentBlock = editor?.getTextCursorPosition()?.block;
|
||||
const blockType = currentBlock?.type;
|
||||
const headingLevel =
|
||||
blockType === 'heading' ? (currentBlock?.props?.level as number) : null;
|
||||
blockType === 'heading' ? Number(currentBlock?.props?.level) : null;
|
||||
|
||||
const getOffsetValue = (placement: string) => {
|
||||
if (!placement.startsWith('top')) return 0;
|
||||
|
||||
Reference in New Issue
Block a user