Skip to content

Commit 4ed09a3

Browse files
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
1 parent def5ea5 commit 4ed09a3

File tree

26 files changed

+848
-1217
lines changed

26 files changed

+848
-1217
lines changed

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,6 @@
201201
"type-fest": "4.10.1",
202202
"typescript": "5.9.2",
203203
"graphql-redis-subscriptions/ioredis": "^5.6.0",
204-
"prosemirror-view": "1.40.0",
205-
"prosemirror-transform": "1.10.4",
206204
"@lingui/core": "5.1.2",
207205
"@types/qs": "6.9.16"
208206
},

packages/twenty-front/package.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
"dependencies": {
3232
"@ai-sdk/react": "3.0.99",
3333
"@apollo/client": "^3.7.17",
34-
"@blocknote/mantine": "^0.31.1",
35-
"@blocknote/react": "^0.31.1",
36-
"@blocknote/xl-docx-exporter": "^0.31.1",
37-
"@blocknote/xl-pdf-exporter": "^0.31.1",
34+
"@blocknote/mantine": "0.47.0",
35+
"@blocknote/react": "0.47.0",
36+
"@blocknote/xl-docx-exporter": "0.47.0",
37+
"@blocknote/xl-pdf-exporter": "0.47.0",
3838
"@calcom/embed-react": "^1.5.3",
3939
"@cyntler/react-doc-viewer": "^1.17.0",
4040
"@dagrejs/dagre": "^1.1.2",
@@ -46,6 +46,9 @@
4646
"@lingui/core": "^5.1.2",
4747
"@lingui/detect-locale": "^5.2.0",
4848
"@lingui/react": "^5.1.2",
49+
"@mantine/core": "^8.3.11",
50+
"@mantine/hooks": "^8.3.11",
51+
"@mantine/utils": "^6.0.22",
4952
"@monaco-editor/react": "^4.7.0",
5053
"@nivo/core": "^0.99.0",
5154
"@nivo/line": "^0.99.0",
@@ -78,7 +81,6 @@
7881
"buffer": "^6.0.3",
7982
"cron-parser": "5.1.1",
8083
"date-fns": "^2.30.0",
81-
"docx": "^9.1.0",
8284
"file-saver": "^2.0.5",
8385
"graphiql": "^3.1.1",
8486
"graphql": "16.8.1",
Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
import { type BlockNoteEditor } from '@blocknote/core';
2-
32
import {
43
docxDefaultSchemaMappings,
54
DOCXExporter,
65
} from '@blocknote/xl-docx-exporter';
7-
import { Buffer } from 'buffer';
8-
import { Packer } from 'docx';
96
import { saveAs } from 'file-saver';
107

118
export const exportBlockNoteEditorToDocx = async (
129
editor: BlockNoteEditor,
1310
filename: string,
1411
) => {
15-
// Polyfill needed for exportBlockNoteEditorToDocX
16-
if (typeof window !== 'undefined') {
17-
window.Buffer = Buffer;
18-
}
19-
2012
const exporter = new DOCXExporter(editor.schema, docxDefaultSchemaMappings);
2113

22-
const docxDocument = await exporter.toDocxJsDocument(editor.document);
23-
24-
const blob = await Packer.toBlob(docxDocument);
14+
const blob = await exporter.toBlob(editor.document);
2515
saveAs(blob, `${filename}.docx`);
2616
};

packages/twenty-front/src/modules/blocknote-editor/blocks/FileBlock.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ export const FileBlock = createReactBlockSpec(
4242
type: 'file',
4343
propSchema: {
4444
url: {
45-
default: '' as string,
45+
default: '',
4646
},
4747
name: {
48-
default: '' as string,
48+
default: '',
4949
},
5050
fileCategory: {
5151
default: 'OTHER' as AttachmentFileCategory,

packages/twenty-front/src/modules/blocknote-editor/blocks/Schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { MentionInlineContent } from '@/blocknote-editor/blocks/MentionInlineCon
1010
export const BLOCK_SCHEMA = BlockNoteSchema.create({
1111
blockSpecs: {
1212
...defaultBlockSpecs,
13-
file: FileBlock,
13+
file: FileBlock(),
1414
},
1515
inlineContentSpecs: {
1616
...defaultInlineContentSpecs,

packages/twenty-front/src/modules/blocknote-editor/components/BlockEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { filterSuggestionItems } from '@blocknote/core';
1+
import { filterSuggestionItems } from '@blocknote/core/extensions';
22
import { BlockNoteView } from '@blocknote/mantine';
33
import { SuggestionMenuController } from '@blocknote/react';
44
import { useTheme } from '@emotion/react';
Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
import { type BLOCK_SCHEMA } from '@/blocknote-editor/blocks/Schema';
1+
import { SuggestionMenu } from '@blocknote/core/extensions';
2+
import { useExtensionState } from '@blocknote/react';
3+
import { useStore } from 'jotai';
4+
import { useCallback, useState } from 'react';
5+
26
import { isSlashMenuOpenComponentState } from '@/blocknote-editor/states/isSlashMenuOpenComponentState';
37
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
48
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
59
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
6-
import { useStore } from 'jotai';
7-
import { useCallback } from 'react';
810

9-
export type BlockEditorDropdownFocusEffectProps = {
10-
editor: typeof BLOCK_SCHEMA.BlockNoteEditor;
11-
};
11+
export const BlockEditorDropdownFocusEffect = () => {
12+
const [prevShowing, setPrevShowing] = useState<boolean | null>(null);
1213

13-
export const BlockEditorDropdownFocusEffect = ({
14-
editor,
15-
}: BlockEditorDropdownFocusEffectProps) => {
1614
const isSlashMenuOpenState = useAtomComponentStateCallbackState(
1715
isSlashMenuOpenComponentState,
1816
);
@@ -25,30 +23,21 @@ export const BlockEditorDropdownFocusEffect = ({
2523

2624
const store = useStore();
2725

28-
const updateCallBack = useCallback(
29-
(event: any) => {
30-
const eventWantsToOpen = event.show === true;
26+
const suggestionState = useExtensionState(SuggestionMenu);
3127

32-
const isAlreadyOpen = store.get(isSlashMenuOpenState);
28+
const isSlashMenuShowing =
29+
suggestionState?.show === true && suggestionState?.triggerCharacter === '/';
3330

34-
const shouldOpen = eventWantsToOpen && !isAlreadyOpen;
31+
const syncSlashMenuState = useCallback(
32+
(showing: boolean) => {
33+
const isAlreadyOpen = store.get(isSlashMenuOpenState);
3534

36-
if (shouldOpen) {
35+
if (showing && isAlreadyOpen !== true) {
3736
setActiveDropdownFocusIdAndMemorizePrevious('custom-slash-menu');
3837
store.set(isSlashMenuOpenState, true);
39-
return;
40-
}
41-
42-
const eventWantsToClose = event.show === false;
43-
44-
const isAlreadyClosed = !isAlreadyOpen;
45-
46-
const shouldClose = eventWantsToClose && !isAlreadyClosed;
47-
48-
if (shouldClose) {
38+
} else if (!showing && isAlreadyOpen === true) {
4939
goBackToPreviousDropdownFocusId();
5040
store.set(isSlashMenuOpenState, false);
51-
return;
5241
}
5342
},
5443
[
@@ -59,7 +48,10 @@ export const BlockEditorDropdownFocusEffect = ({
5948
],
6049
);
6150

62-
editor.suggestionMenus.on('update /', updateCallBack);
51+
if (prevShowing !== isSlashMenuShowing) {
52+
setPrevShowing(isSlashMenuShowing);
53+
syncSlashMenuState(isSlashMenuShowing);
54+
}
6355

6456
return <></>;
6557
};

packages/twenty-front/src/modules/blocknote-editor/components/CustomAddBlockItem.tsx

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,36 @@
11
import { type BLOCK_SCHEMA } from '@/blocknote-editor/blocks/Schema';
2-
3-
import { useComponentsContext } from '@blocknote/react';
2+
import { SuggestionMenu } from '@blocknote/core/extensions';
3+
import { useBlockNoteEditor, useComponentsContext } from '@blocknote/react';
44

55
type CustomAddBlockItemProps = {
66
editor: typeof BLOCK_SCHEMA.BlockNoteEditor;
7-
children: React.ReactNode; // Adding the children prop
7+
children: React.ReactNode;
88
};
99

10-
type ContentItem = {
11-
type: string;
12-
text: string;
13-
styles: any;
14-
};
1510
export const CustomAddBlockItem = ({
1611
editor,
1712
children,
1813
}: CustomAddBlockItemProps) => {
1914
const Components = useComponentsContext();
15+
const blockNoteEditor = useBlockNoteEditor();
2016

2117
if (!Components) {
2218
return null;
2319
}
2420

2521
const handleClick = () => {
26-
const blockIdentifier = editor.getTextCursorPosition().block;
27-
const currentBlockContent = blockIdentifier?.content as
28-
| Array<ContentItem>
29-
| undefined;
30-
31-
const [firstElement] = currentBlockContent || [];
32-
33-
if (firstElement === undefined) {
34-
editor.openSuggestionMenu('/');
35-
} else {
36-
editor.openSuggestionMenu('/');
37-
editor.sideMenu.unfreezeMenu();
22+
const currentBlock = blockNoteEditor.getTextCursorPosition().block;
23+
const blockContent = currentBlock?.content;
24+
25+
const isEmpty = Array.isArray(blockContent) && blockContent.length === 0;
26+
27+
if (isEmpty) {
28+
editor.setTextCursorPosition(currentBlock);
3829
}
30+
31+
blockNoteEditor.getExtension(SuggestionMenu)?.openSuggestionMenu('/');
3932
};
33+
4034
return (
4135
<Components.Generic.Menu.Item onClick={handleClick}>
4236
{children}

packages/twenty-front/src/modules/blocknote-editor/components/CustomSideMenu.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,26 @@ export const CustomSideMenu = ({ editor }: CustomSideMenuProps) => {
2525
const { t } = useLingui();
2626
return (
2727
<SideMenuController
28-
sideMenu={(props) => (
29-
// eslint-disable-next-line react/jsx-props-no-spreading
30-
<SideMenu {...props}>
28+
sideMenu={() => (
29+
<SideMenu>
3130
<DragHandleButton
32-
// eslint-disable-next-line react/jsx-props-no-spreading
33-
{...props}
34-
dragHandleMenu={(props) => (
35-
// eslint-disable-next-line react/jsx-props-no-spreading
36-
<DragHandleMenu {...props}>
31+
dragHandleMenu={() => (
32+
<DragHandleMenu>
3733
<CustomAddBlockItem editor={editor}>
3834
<CustomSideMenuOptions
3935
LeftIcon={IconPlus}
4036
text={t`Add Block`}
4137
Variant="normal"
4238
/>
4339
</CustomAddBlockItem>
44-
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
45-
<BlockColorsItem {...props}>
40+
<BlockColorsItem>
4641
<CustomSideMenuOptions
4742
LeftIcon={IconColorSwatch}
4843
text={t`Change Color`}
4944
Variant="normal"
5045
/>
5146
</BlockColorsItem>
52-
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
53-
<RemoveBlockItem {...props}>
54-
{' '}
47+
<RemoveBlockItem>
5548
<CustomSideMenuOptions
5649
LeftIcon={IconTrash}
5750
text={t`Delete`}

packages/twenty-front/src/modules/blocknote-editor/components/CustomSlashMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const CustomSlashMenu = ({
3333
const currentBlock = editor?.getTextCursorPosition()?.block;
3434
const blockType = currentBlock?.type;
3535
const headingLevel =
36-
blockType === 'heading' ? (currentBlock?.props?.level as number) : null;
36+
blockType === 'heading' ? Number(currentBlock?.props?.level) : null;
3737

3838
const getOffsetValue = (placement: string) => {
3939
if (!placement.startsWith('top')) return 0;

0 commit comments

Comments
 (0)