Skip to content

Commit aaad427

Browse files
simplify ui elements (#288)
* simplify ui elements * remove unused * fix view * Fixed build * add onUpdate * fix vanilla * fix build * allow props for blocknoteview --------- Co-authored-by: Matthew Lipski <[email protected]>
1 parent 0af18e6 commit aaad427

File tree

24 files changed

+471
-717
lines changed

24 files changed

+471
-717
lines changed

examples/editor/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import "@blocknote/core/style.css";
33
import {
44
BlockNoteView,
5-
useBlockNote,
65
FormattingToolbarPositioner,
76
HyperlinkToolbarPositioner,
8-
SlashMenuPositioner,
97
SideMenuPositioner,
8+
SlashMenuPositioner,
9+
useBlockNote,
1010
} from "@blocknote/react";
1111
import styles from "./App.module.css";
1212

examples/vanilla/src/ui/addFormattingToolbar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const addFormattingToolbar = (editor: BlockNoteEditor) => {
55
let element: HTMLElement;
66
let boldBtn: HTMLAnchorElement;
77

8-
editor.createFormattingToolbar((formattingToolbarState) => {
8+
editor.formattingToolbar.onUpdate((formattingToolbarState) => {
99
if (!element) {
1010
element = document.createElement("div");
1111
element.style.background = "gray";

examples/vanilla/src/ui/addHyperlinkToolbar.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createButton } from "./util";
44
export const addHyperlinkToolbar = (editor: BlockNoteEditor) => {
55
let element: HTMLElement;
66

7-
const callbacks = editor.createHyperlinkToolbar((hyperlinkToolbarState) => {
7+
editor.hyperlinkToolbar.onUpdate((hyperlinkToolbarState) => {
88
if (!element) {
99
element = document.createElement("div");
1010
element.style.background = "gray";
@@ -17,13 +17,13 @@ export const addHyperlinkToolbar = (editor: BlockNoteEditor) => {
1717

1818
const editBtn = createButton("edit", () => {
1919
const newUrl = prompt("new url") || url;
20-
callbacks.editHyperlink(newUrl, text);
20+
editor.hyperlinkToolbar.editHyperlink(newUrl, text);
2121
});
2222

2323
element.appendChild(editBtn);
2424

2525
const removeBtn = createButton("remove", () => {
26-
callbacks.deleteHyperlink();
26+
editor.hyperlinkToolbar.deleteHyperlink();
2727
});
2828

2929
element.appendChild(editBtn);

examples/vanilla/src/ui/addSideMenu.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ import { createButton } from "./util";
44
export const addSideMenu = (editor: BlockNoteEditor) => {
55
let element: HTMLElement;
66

7-
const callbacks = editor.createSideMenu((sideMenuState) => {
7+
editor.sideMenu.onUpdate((sideMenuState) => {
88
if (!element) {
99
element = document.createElement("div");
1010
element.style.background = "gray";
1111
element.style.position = "absolute";
1212
element.style.padding = "10px";
1313
element.style.opacity = "0.8";
1414
const addBtn = createButton("+", () => {
15-
callbacks.addBlock();
15+
editor.sideMenu.addBlock();
1616
});
1717
element.appendChild(addBtn);
1818

1919
const dragBtn = createButton("::", () => {
2020
// TODO: render a submenu with a delete option that calls "props.deleteBlock"
2121
});
2222

23-
dragBtn.addEventListener("dragstart", callbacks.blockDragStart);
24-
dragBtn.addEventListener("dragend", callbacks.blockDragEnd);
23+
dragBtn.addEventListener("dragstart", editor.sideMenu.blockDragStart);
24+
dragBtn.addEventListener("dragend", editor.sideMenu.blockDragEnd);
2525
dragBtn.draggable = true;
2626
element.style.display = "none";
2727
element.appendChild(dragBtn);

examples/vanilla/src/ui/addSlashMenu.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const addSlashMenu = (editor: BlockNoteEditor) => {
2828
return domItems;
2929
}
3030

31-
const callbacks = editor.createSlashMenu((slashMenuState) => {
31+
editor.slashMenu.onUpdate((slashMenuState) => {
3232
if (!element) {
3333
element = document.createElement("div");
3434
element.style.background = "gray";
@@ -43,7 +43,7 @@ export const addSlashMenu = (editor: BlockNoteEditor) => {
4343
if (slashMenuState.show) {
4444
updateItems(
4545
slashMenuState.filteredItems,
46-
callbacks.itemCallback,
46+
editor.slashMenu.itemCallback,
4747
slashMenuState.keyboardHoveredItemIndex
4848
);
4949

examples/vanilla/src/ui/blockSideMenuFactory.ts

Whitespace-only changes.

examples/vanilla/src/ui/formattingToolbarFactory.ts

Whitespace-only changes.

packages/core/src/BlockNoteEditor.ts

Lines changed: 31 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,12 @@ import {
3737
} from "./extensions/Blocks/api/inlineContentTypes";
3838
import { Selection } from "./extensions/Blocks/api/selectionTypes";
3939
import { getBlockInfoFromPos } from "./extensions/Blocks/helpers/getBlockInfoFromPos";
40-
import {
41-
setupFormattingToolbar,
42-
FormattingToolbarState,
43-
FormattingToolbarCallbacks,
44-
} from "./extensions/FormattingToolbar/FormattingToolbarPlugin";
45-
import {
46-
HyperlinkToolbarCallbacks,
47-
HyperlinkToolbarState,
48-
setupHyperlinkToolbar,
49-
} from "./extensions/HyperlinkToolbar/HyperlinkToolbarPlugin";
50-
import {
51-
SuggestionsMenuCallbacks,
52-
SuggestionsMenuState,
53-
} from "./shared/plugins/suggestion/SuggestionPlugin";
54-
import { setupSlashMenu } from "./extensions/SlashMenu/SlashMenuPlugin";
55-
import {
56-
setupSideMenu,
57-
SideMenuCallbacks,
58-
SideMenuState,
59-
} from "./extensions/DraggableBlocks/DraggableBlocksPlugin";
40+
41+
import { FormattingToolbarProsemirrorPlugin } from "./extensions/FormattingToolbar/FormattingToolbarPlugin";
42+
import { HyperlinkToolbarProsemirrorPlugin } from "./extensions/HyperlinkToolbar/HyperlinkToolbarPlugin";
43+
import { SideMenuProsemirrorPlugin } from "./extensions/SideMenu/SideMenuPlugin";
6044
import { BaseSlashMenuItem } from "./extensions/SlashMenu/BaseSlashMenuItem";
45+
import { SlashMenuProsemirrorPlugin } from "./extensions/SlashMenu/SlashMenuPlugin";
6146
import { getDefaultSlashMenuItems } from "./extensions/SlashMenu/defaultSlashMenuItems";
6247

6348
export type BlockNoteEditorOptions<BSchema extends BlockSchema> = {
@@ -163,81 +148,10 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
163148
public readonly schema: BSchema;
164149
public ready = false;
165150

166-
private uiElementCallbacks: {
167-
formattingToolbar: FormattingToolbarCallbacks;
168-
hyperlinkToolbar: HyperlinkToolbarCallbacks;
169-
slashMenu: SuggestionsMenuCallbacks<BaseSlashMenuItem<BSchema>>;
170-
sideMenu: SideMenuCallbacks;
171-
};
172-
173-
private uiElementListeners: Partial<{
174-
formattingToolbar: (formattingToolbarState: FormattingToolbarState) => void;
175-
hyperlinkToolbar: (hyperlinkToolbarState: HyperlinkToolbarState) => void;
176-
slashMenu: (
177-
slashMenuState: SuggestionsMenuState<BaseSlashMenuItem<BSchema>>
178-
) => void;
179-
sideMenu: (sideMenuState: SideMenuState<BSchema>) => void;
180-
}> = {};
181-
182-
public createFormattingToolbar = (
183-
updateFormattingToolbar: (
184-
formattingToolbarState: FormattingToolbarState
185-
) => void
186-
) => {
187-
if ("formattingToolbar" in this.uiElementListeners) {
188-
throw Error(
189-
"You have already created a Formatting Toolbar. You can only create a single Formatting Toolbar per BlockNote editor instance."
190-
);
191-
}
192-
193-
this.uiElementListeners.formattingToolbar = updateFormattingToolbar;
194-
195-
return this.uiElementCallbacks.formattingToolbar;
196-
};
197-
198-
public createHyperlinkToolbar = (
199-
updateHyperlinkToolbar: (
200-
hyperlinkToolbarState: HyperlinkToolbarState
201-
) => void
202-
) => {
203-
if ("hyperlinkToolbar" in this.uiElementListeners) {
204-
throw Error(
205-
"You have already created a Hyperlink Toolbar. You can only create a single Hyperlink Toolbar per BlockNote editor instance."
206-
);
207-
}
208-
209-
this.uiElementListeners.hyperlinkToolbar = updateHyperlinkToolbar;
210-
211-
return this.uiElementCallbacks.hyperlinkToolbar;
212-
};
213-
214-
public createSlashMenu = <T extends BaseSlashMenuItem<BSchema>>(
215-
updateSlashMenu: (slashMenuState: SuggestionsMenuState<T>) => void
216-
) => {
217-
if ("slashMenu" in this.uiElementListeners) {
218-
throw Error(
219-
"You have already created a Slash Menu. You can only create a single Slash Menu per BlockNote editor instance."
220-
);
221-
}
222-
223-
this.uiElementListeners.slashMenu = updateSlashMenu as any;
224-
225-
return this.uiElementCallbacks.slashMenu;
226-
};
227-
228-
public createSideMenu = (
229-
updateSideMenu: (sideMenuState: SideMenuState<BSchema>) => void
230-
) => {
231-
if ("sideMenu" in this.uiElementListeners) {
232-
throw Error(
233-
"You have already created a Side Menu. You can only create a single Side Menu per BlockNote editor instance."
234-
);
235-
}
236-
237-
this.uiElementListeners.sideMenu = updateSideMenu;
238-
239-
return this.uiElementCallbacks.sideMenu;
240-
};
151+
public readonly sideMenu: SideMenuProsemirrorPlugin<BSchema>;
152+
public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin<BSchema>;
153+
public readonly slashMenu: SlashMenuProsemirrorPlugin<BSchema, any>;
154+
public readonly hyperlinkToolbar: HyperlinkToolbarProsemirrorPlugin<BSchema>;
241155

242156
constructor(
243157
private readonly options: Partial<BlockNoteEditorOptions<BSchema>> = {}
@@ -257,140 +171,36 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
257171
...options,
258172
};
259173

260-
const editor = this;
174+
this.sideMenu = new SideMenuProsemirrorPlugin(this);
175+
this.formattingToolbar = new FormattingToolbarProsemirrorPlugin(this);
176+
this.slashMenu = new SlashMenuProsemirrorPlugin(
177+
this,
178+
newOptions.slashCommands ||
179+
getDefaultSlashMenuItems(newOptions.blockSchema)
180+
);
181+
this.hyperlinkToolbar = new HyperlinkToolbarProsemirrorPlugin(this);
261182

262183
const extensions = getBlockNoteExtensions<BSchema>({
263184
editor: this,
264-
slashCommands:
265-
newOptions.slashCommands ||
266-
getDefaultSlashMenuItems(newOptions.blockSchema),
267185
blockSchema: newOptions.blockSchema,
268186
collaboration: newOptions.collaboration,
269187
});
270188

271-
this.schema = newOptions.blockSchema;
272-
273-
let formattingToolbarCallbacks: FormattingToolbarCallbacks;
274-
const formattingToolbarExtension = Extension.create({
275-
name: "FormattingToolbarExtension",
189+
const blockNoteUIExtension = Extension.create({
190+
name: "BlockNoteUIExtension",
276191

277-
addProseMirrorPlugins() {
278-
const { plugin, callbacks } = setupFormattingToolbar(
279-
editor,
280-
this.editor,
281-
(formattingToolbarState) => {
282-
const formattingToolbarListener =
283-
editor.uiElementListeners.formattingToolbar;
284-
285-
if (!formattingToolbarListener) {
286-
return;
287-
}
288-
289-
formattingToolbarListener(formattingToolbarState);
290-
}
291-
);
292-
293-
formattingToolbarCallbacks = {
294-
destroy: () => delete editor.uiElementListeners.formattingToolbar,
295-
...callbacks,
296-
};
297-
298-
return [plugin];
299-
},
300-
});
301-
extensions.push(formattingToolbarExtension);
302-
303-
let hyperlinkToolbarCallbacks: HyperlinkToolbarCallbacks;
304-
const hyperlinkToolbarExtension = Extension.create({
305-
name: "HyperlinkToolbarExtension",
306-
307-
addProseMirrorPlugins() {
308-
const { plugin, callbacks } = setupHyperlinkToolbar(
309-
editor,
310-
this.editor,
311-
(hyperlinkToolbarState) => {
312-
const hyperlinkToolbarListener =
313-
editor.uiElementListeners.hyperlinkToolbar;
314-
315-
if (!hyperlinkToolbarListener) {
316-
return;
317-
}
318-
319-
hyperlinkToolbarListener(hyperlinkToolbarState);
320-
}
321-
);
322-
323-
hyperlinkToolbarCallbacks = {
324-
destroy: () => delete editor.uiElementListeners.hyperlinkToolbar,
325-
...callbacks,
326-
};
327-
328-
return [plugin];
192+
addProseMirrorPlugins: () => {
193+
return [
194+
this.sideMenu.plugin,
195+
this.formattingToolbar.plugin,
196+
this.slashMenu.plugin,
197+
this.hyperlinkToolbar.plugin,
198+
];
329199
},
330200
});
331-
extensions.push(hyperlinkToolbarExtension);
332-
333-
let slashMenuCallbacks: SuggestionsMenuCallbacks<
334-
BaseSlashMenuItem<BSchema>
335-
>;
336-
const slashMenuExtension = Extension.create({
337-
name: "SlashMenuExtension",
338-
339-
addProseMirrorPlugins() {
340-
const { plugin, callbacks } = setupSlashMenu(
341-
editor,
342-
this.editor,
343-
(slashMenuState) => {
344-
const slashMenuListener = editor.uiElementListeners.slashMenu;
345-
346-
if (!slashMenuListener) {
347-
return;
348-
}
349-
350-
slashMenuListener(slashMenuState as any);
351-
},
352-
newOptions.slashCommands ||
353-
getDefaultSlashMenuItems(newOptions.blockSchema)
354-
);
355-
356-
slashMenuCallbacks = {
357-
destroy: () => delete editor.uiElementListeners.slashMenu,
358-
...callbacks,
359-
};
360-
361-
return [plugin];
362-
},
363-
});
364-
extensions.push(slashMenuExtension);
365-
366-
let sideMenuCallbacks: SideMenuCallbacks;
367-
const sideMenuExtension = Extension.create({
368-
name: "SideMenuExtension",
369-
370-
addProseMirrorPlugins() {
371-
const { plugin, callbacks } = setupSideMenu(
372-
editor,
373-
this.editor,
374-
(sideMenuState) => {
375-
const sideMenuListener = editor.uiElementListeners.sideMenu;
376-
377-
if (!sideMenuListener) {
378-
return;
379-
}
380-
381-
sideMenuListener(sideMenuState as any);
382-
}
383-
);
201+
extensions.push(blockNoteUIExtension);
384202

385-
sideMenuCallbacks = {
386-
destroy: () => delete editor.uiElementListeners.sideMenu,
387-
...callbacks,
388-
};
389-
390-
return [plugin];
391-
},
392-
});
393-
extensions.push(sideMenuExtension);
203+
this.schema = newOptions.blockSchema;
394204

395205
const tiptapOptions: EditorOptions = {
396206
// TODO: This approach to setting initial content is "cleaner" but requires the PM editor schema, which is only
@@ -452,15 +262,10 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
452262
this._tiptapEditor = new Editor(tiptapOptions) as Editor & {
453263
contentComponent: any;
454264
};
265+
}
455266

456-
// These need to be assigned after the TipTap editor is created as they are
457-
// initialized in a plugin, which is only loaded on editor creation.
458-
this.uiElementCallbacks = {
459-
formattingToolbar: formattingToolbarCallbacks!,
460-
hyperlinkToolbar: hyperlinkToolbarCallbacks!,
461-
slashMenu: slashMenuCallbacks!,
462-
sideMenu: sideMenuCallbacks!,
463-
};
267+
public get prosemirrorView() {
268+
return this._tiptapEditor.view;
464269
}
465270

466271
public get domElement() {

0 commit comments

Comments
 (0)