@blocknote/core 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/dist/blocknote.js +1424 -5109
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +1 -53
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -16
- package/src/BlockNoteEditor.ts +54 -0
- package/src/BlockNoteExtensions.ts +52 -7
- package/src/assets/fonts-inter.css +92 -0
- package/src/editor.module.css +37 -0
- package/src/extensions/Blocks/BlockAttributes.ts +1 -3
- package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +71 -18
- package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +66 -0
- package/src/extensions/Blocks/index.ts +7 -3
- package/src/extensions/Blocks/nodes/Block.module.css +116 -74
- package/src/extensions/Blocks/nodes/Block.ts +413 -292
- package/src/extensions/Blocks/nodes/BlockGroup.ts +6 -6
- package/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.ts +84 -0
- package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts +177 -0
- package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts +77 -0
- package/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.ts +34 -0
- package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +20 -0
- package/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts +27 -9
- package/src/extensions/DraggableBlocks/{DraggableBlocksPlugin.tsx → DraggableBlocksPlugin.ts} +227 -147
- package/src/extensions/FormattingToolbar/FormattingToolbarExtension.ts +29 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +35 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +308 -0
- package/src/extensions/HyperlinkToolbar/HyperlinkMark.ts +28 -0
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +19 -0
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +251 -0
- package/src/extensions/Placeholder/PlaceholderExtension.ts +2 -2
- package/src/extensions/SlashMenu/SlashMenuExtension.ts +9 -1
- package/src/extensions/SlashMenu/SlashMenuItem.ts +1 -3
- package/src/extensions/SlashMenu/defaultCommands.tsx +33 -22
- package/src/extensions/TrailingNode/TrailingNodeExtension.ts +4 -4
- package/src/extensions/UniqueID/UniqueID.ts +14 -1
- package/src/index.ts +8 -4
- package/src/shared/EditorElement.ts +10 -0
- package/src/shared/plugins/suggestion/SuggestionItem.ts +1 -8
- package/src/shared/plugins/suggestion/SuggestionPlugin.ts +222 -101
- package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +21 -0
- package/src/{utils.ts → shared/utils.ts} +0 -0
- package/types/src/BlockNoteEditor.d.ts +13 -0
- package/types/src/BlockNoteExtensions.d.ts +12 -1
- package/types/src/EditorElement.d.ts +7 -0
- package/types/src/extensions/Blocks/PreviousBlockTypePlugin.d.ts +1 -1
- package/types/src/extensions/Blocks/helpers/getBlockInfoFromPos.d.ts +19 -0
- package/types/src/extensions/Blocks/nodes/Block.d.ts +11 -19
- package/types/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.d.ts +8 -0
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.d.ts +8 -0
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.d.ts +6 -0
- package/types/src/extensions/BubbleMenu/BubbleMenuExtension.d.ts +4 -1
- package/types/src/extensions/BubbleMenu/BubbleMenuFactoryTypes.d.ts +27 -0
- package/types/src/extensions/BubbleMenu/BubbleMenuPlugin.d.ts +10 -12
- package/types/src/extensions/DraggableBlocks/BlockMenuFactoryTypes.d.ts +12 -0
- package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +14 -0
- package/types/src/extensions/DraggableBlocks/DragMenuFactoryTypes.d.ts +18 -0
- package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +9 -3
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +23 -1
- package/types/src/extensions/FormattingToolbar/FormattingToolbarExtension.d.ts +8 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +23 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +43 -0
- package/types/src/extensions/HyperlinkToolbar/HyperlinkMark.d.ts +8 -0
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +12 -0
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +11 -0
- package/types/src/extensions/Hyperlinks/HyperlinkMark.d.ts +2 -1
- package/types/src/extensions/Hyperlinks/HyperlinkMenuFactoryTypes.d.ts +11 -0
- package/types/src/extensions/Hyperlinks/HyperlinkMenuPlugin.d.ts +10 -1
- package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +3 -1
- package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +2 -4
- package/types/src/index.d.ts +8 -3
- package/types/src/shared/EditorElement.d.ts +6 -0
- package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +1 -6
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +15 -10
- package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +12 -0
- package/types/src/shared/utils.d.ts +2 -0
- package/types/src/utils.d.ts +2 -2
- package/src/BlockNoteTheme.ts +0 -150
- package/src/EditorContent.tsx +0 -2
- package/src/extensions/Blocks/OrderedListPlugin.ts +0 -46
- package/src/extensions/Blocks/commands/joinBackward.ts +0 -274
- package/src/extensions/Blocks/helpers/setBlockHeading.ts +0 -30
- package/src/extensions/Blocks/nodes/Content.ts +0 -63
- package/src/extensions/Blocks/rule.ts +0 -48
- package/src/extensions/BubbleMenu/BubbleMenuExtension.tsx +0 -36
- package/src/extensions/BubbleMenu/BubbleMenuPlugin.ts +0 -245
- package/src/extensions/BubbleMenu/component/BubbleMenu.tsx +0 -240
- package/src/extensions/BubbleMenu/component/LinkToolbarButton.tsx +0 -67
- package/src/extensions/DraggableBlocks/components/DragHandle.tsx +0 -102
- package/src/extensions/DraggableBlocks/components/DragHandleMenu.tsx +0 -19
- package/src/extensions/Hyperlinks/HyperlinkMark.tsx +0 -16
- package/src/extensions/Hyperlinks/HyperlinkMenuPlugin.tsx +0 -165
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.tsx +0 -44
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.tsx +0 -34
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.tsx +0 -31
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.tsx +0 -40
- package/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.tsx +0 -37
- package/src/extensions/Hyperlinks/menus/HyperlinkMenu.tsx +0 -63
- package/src/fonts-inter.css +0 -94
- package/src/globals.css +0 -28
- package/src/root.module.css +0 -19
- package/src/shared/components/toolbar/Toolbar.tsx +0 -10
- package/src/shared/components/toolbar/ToolbarButton.tsx +0 -57
- package/src/shared/components/toolbar/ToolbarDropdown.tsx +0 -35
- package/src/shared/components/toolbar/ToolbarDropdownItem.tsx +0 -35
- package/src/shared/components/toolbar/ToolbarDropdownTarget.tsx +0 -31
- package/src/shared/components/tooltip/TooltipContent.module.css +0 -15
- package/src/shared/components/tooltip/TooltipContent.tsx +0 -23
- package/src/shared/hooks/useEditorForceUpdate.tsx +0 -30
- package/src/shared/plugins/suggestion/SuggestionListReactRenderer.tsx +0 -236
- package/src/shared/plugins/suggestion/components/SuggestionGroup.tsx +0 -47
- package/src/shared/plugins/suggestion/components/SuggestionGroupItem.tsx +0 -82
- package/src/shared/plugins/suggestion/components/SuggestionList.tsx +0 -92
- package/src/useEditor.ts +0 -51
- package/types/src/BlockNoteTheme.d.ts +0 -2
- package/types/src/EditorContent.d.ts +0 -1
- package/types/src/commands/indentation.d.ts +0 -2
- package/types/src/extensions/Blocks/OrderedListPlugin.d.ts +0 -2
- package/types/src/extensions/Blocks/commands/joinBackward.d.ts +0 -14
- package/types/src/extensions/Blocks/helpers/setBlockHeading.d.ts +0 -5
- package/types/src/extensions/Blocks/nodes/Content.d.ts +0 -5
- package/types/src/extensions/Blocks/rule.d.ts +0 -16
- package/types/src/extensions/BubbleMenu/component/BubbleMenu.d.ts +0 -5
- package/types/src/extensions/BubbleMenu/component/DropdownBlockItem.d.ts +0 -10
- package/types/src/extensions/BubbleMenu/component/LinkToolbarButton.d.ts +0 -11
- package/types/src/extensions/DraggableBlocks/components/DragHandle.d.ts +0 -12
- package/types/src/extensions/DraggableBlocks/components/DragHandleMenu.d.ts +0 -6
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.d.ts +0 -11
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.d.ts +0 -13
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.d.ts +0 -8
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.d.ts +0 -9
- package/types/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.d.ts +0 -12
- package/types/src/extensions/Hyperlinks/menus/HyperlinkBasicMenu.d.ts +0 -12
- package/types/src/extensions/Hyperlinks/menus/HyperlinkEditMenu.d.ts +0 -10
- package/types/src/extensions/Hyperlinks/menus/HyperlinkMenu.d.ts +0 -21
- package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInput.d.ts +0 -39
- package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInputStyles.d.ts +0 -1
- package/types/src/extensions/Hyperlinks/menus/atlaskit/ToolbarComponent.d.ts +0 -11
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInput.d.ts +0 -39
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInputStyles.d.ts +0 -3
- package/types/src/extensions/Hyperlinks/menus/helpers/ToolbarComponent.d.ts +0 -13
- package/types/src/extensions/helpers/formatKeyboardShortcut.d.ts +0 -1
- package/types/src/lib/atlaskit/browser.d.ts +0 -12
- package/types/src/nodes/ChildgroupNode.d.ts +0 -28
- package/types/src/nodes/patchNodes.d.ts +0 -1
- package/types/src/plugins/TreeViewPlugin/index.d.ts +0 -2
- package/types/src/plugins/animation.d.ts +0 -2
- package/types/src/react/BlockNoteComposer.d.ts +0 -17
- package/types/src/react/BlockNotePlugin.d.ts +0 -1
- package/types/src/react/index.d.ts +0 -3
- package/types/src/react/useBlockNoteSetup.d.ts +0 -2
- package/types/src/registerBlockNote.d.ts +0 -2
- package/types/src/shared/components/toolbar/SimpleToolbarButton.d.ts +0 -15
- package/types/src/shared/components/toolbar/SimpleToolbarDropdown.d.ts +0 -11
- package/types/src/shared/components/toolbar/SimpleToolbarDropdownItem.d.ts +0 -11
- package/types/src/shared/components/toolbar/Toolbar.d.ts +0 -4
- package/types/src/shared/components/toolbar/ToolbarButton.d.ts +0 -15
- package/types/src/shared/components/toolbar/ToolbarDropdown.d.ts +0 -17
- package/types/src/shared/components/toolbar/ToolbarDropdownItem.d.ts +0 -11
- package/types/src/shared/components/toolbar/ToolbarDropdownTarget.d.ts +0 -8
- package/types/src/shared/components/toolbar/ToolbarSeparator.d.ts +0 -2
- package/types/src/shared/components/tooltip/TooltipContent.d.ts +0 -15
- package/types/src/shared/hooks/useEditorForceUpdate.d.ts +0 -2
- package/types/src/shared/plugins/suggestion/SuggestionListReactRenderer.d.ts +0 -71
- package/types/src/shared/plugins/suggestion/components/SuggestionGroup.d.ts +0 -23
- package/types/src/shared/plugins/suggestion/components/SuggestionGroupItem.d.ts +0 -9
- package/types/src/shared/plugins/suggestion/components/SuggestionList.d.ts +0 -11
- package/types/src/themes/BlockNoteEditorTheme.d.ts +0 -11
- package/types/src/useEditor.d.ts +0 -11
package/src/extensions/DraggableBlocks/{DraggableBlocksPlugin.tsx → DraggableBlocksPlugin.ts}
RENAMED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Editor } from "@tiptap/core";
|
|
2
2
|
import { Node } from "prosemirror-model";
|
|
3
|
+
import { NodeSelection, Plugin, PluginKey, Selection } from "prosemirror-state";
|
|
3
4
|
import * as pv from "prosemirror-view";
|
|
4
5
|
import { EditorView } from "prosemirror-view";
|
|
5
|
-
import ReactDOM from "react-dom";
|
|
6
|
-
import { DragHandle } from "./components/DragHandle";
|
|
7
|
-
import { MantineProvider } from "@mantine/core";
|
|
8
|
-
import { BlockNoteTheme } from "../../BlockNoteTheme";
|
|
9
6
|
import { MultipleNodeSelection } from "../Blocks/MultipleNodeSelection";
|
|
7
|
+
import { DraggableBlocksOptions } from "./DraggableBlocksExtension";
|
|
8
|
+
import {
|
|
9
|
+
BlockSideMenu,
|
|
10
|
+
BlockSideMenuDynamicParams,
|
|
11
|
+
BlockSideMenuFactory,
|
|
12
|
+
BlockSideMenuStaticParams,
|
|
13
|
+
} from "./BlockSideMenuFactoryTypes";
|
|
14
|
+
import { getBlockInfoFromPos } from "../Blocks/helpers/getBlockInfoFromPos";
|
|
15
|
+
import { SlashMenuPluginKey } from "../SlashMenu/SlashMenuExtension";
|
|
16
|
+
import styles from "../../editor.module.css";
|
|
10
17
|
|
|
11
18
|
const serializeForClipboard = (pv as any).__serializeForClipboard;
|
|
12
19
|
// code based on https://github.com/ueberdosis/tiptap/issues/323#issuecomment-506637799
|
|
@@ -91,10 +98,7 @@ function blockPositionFromCoords(
|
|
|
91
98
|
return null;
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
function blockPositionsFromSelection(
|
|
95
|
-
selection: Selection,
|
|
96
|
-
doc: Node
|
|
97
|
-
) {
|
|
101
|
+
function blockPositionsFromSelection(selection: Selection, doc: Node) {
|
|
98
102
|
// Absolute positions just before the first block spanned by the selection, and just after the last block. Having the
|
|
99
103
|
// selection start and end just before and just after the target blocks ensures no whitespace/line breaks are left
|
|
100
104
|
// behind after dragging & dropping them.
|
|
@@ -107,9 +111,9 @@ function blockPositionsFromSelection(
|
|
|
107
111
|
// in. If the anchor should update but the head shouldn't and vice versa, it means the user selection is outside a
|
|
108
112
|
// block content node, which should never happen.
|
|
109
113
|
const selectionStartInBlockContent =
|
|
110
|
-
doc.resolve(selection.from).node().type.
|
|
114
|
+
doc.resolve(selection.from).node().type.spec.group === "blockContent";
|
|
111
115
|
const selectionEndInBlockContent =
|
|
112
|
-
doc.resolve(selection.to).node().type.
|
|
116
|
+
doc.resolve(selection.to).node().type.spec.group === "blockContent";
|
|
113
117
|
|
|
114
118
|
// Ensures that entire outermost nodes are selected if the selection spans multiple nesting levels.
|
|
115
119
|
const minDepth = Math.min(selection.$anchor.depth, selection.$head.depth);
|
|
@@ -164,6 +168,7 @@ function setDragImage(view: EditorView, from: number, to = from) {
|
|
|
164
168
|
|
|
165
169
|
// dataTransfer.setDragImage(element) only works if element is attached to the DOM.
|
|
166
170
|
dragImageElement = parentClone;
|
|
171
|
+
dragImageElement.className = styles.dragPreview;
|
|
167
172
|
document.body.appendChild(dragImageElement);
|
|
168
173
|
}
|
|
169
174
|
|
|
@@ -220,157 +225,232 @@ function dragStart(e: DragEvent, view: EditorView) {
|
|
|
220
225
|
}
|
|
221
226
|
}
|
|
222
227
|
|
|
223
|
-
export
|
|
224
|
-
|
|
228
|
+
export type BlockMenuViewProps = {
|
|
229
|
+
editor: Editor;
|
|
230
|
+
blockMenuFactory: BlockSideMenuFactory;
|
|
231
|
+
horizontalPosAnchoredAtRoot: boolean;
|
|
232
|
+
};
|
|
225
233
|
|
|
226
|
-
|
|
234
|
+
export class BlockMenuView {
|
|
235
|
+
editor: Editor;
|
|
227
236
|
|
|
228
237
|
// When true, the drag handle with be anchored at the same level as root elements
|
|
229
238
|
// When false, the drag handle with be just to the left of the element
|
|
230
|
-
|
|
239
|
+
horizontalPosAnchoredAtRoot: boolean;
|
|
231
240
|
|
|
232
|
-
|
|
233
|
-
let addClicked = false;
|
|
241
|
+
blockMenu: BlockSideMenu;
|
|
234
242
|
|
|
235
|
-
|
|
236
|
-
menuShown = true;
|
|
237
|
-
};
|
|
238
|
-
const onHide = () => {
|
|
239
|
-
menuShown = false;
|
|
240
|
-
};
|
|
241
|
-
const onAddClicked = () => {
|
|
242
|
-
addClicked = true;
|
|
243
|
-
};
|
|
243
|
+
hoveredBlock: HTMLElement | undefined;
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
dropElement.addEventListener("dragend", () => unsetDragImage());
|
|
245
|
+
menuOpen = false;
|
|
246
|
+
menuFrozen = false;
|
|
247
|
+
|
|
248
|
+
constructor({
|
|
249
|
+
editor,
|
|
250
|
+
blockMenuFactory,
|
|
251
|
+
horizontalPosAnchoredAtRoot,
|
|
252
|
+
}: BlockMenuViewProps) {
|
|
253
|
+
this.editor = editor;
|
|
254
|
+
this.horizontalPosAnchoredAtRoot = horizontalPosAnchoredAtRoot;
|
|
255
|
+
|
|
256
|
+
this.blockMenu = blockMenuFactory(this.getStaticParams());
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
258
|
+
// Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
|
|
259
|
+
document.body.addEventListener(
|
|
260
|
+
"mousemove",
|
|
261
|
+
(event) => {
|
|
262
|
+
if (this.menuFrozen) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Gets block at mouse cursor's vertical position.
|
|
267
|
+
const coords = {
|
|
268
|
+
left: this.editor.view.dom.clientWidth / 2, // take middle of editor
|
|
269
|
+
top: event.clientY,
|
|
270
|
+
};
|
|
271
|
+
const block = getDraggableBlockFromCoords(coords, this.editor.view);
|
|
272
|
+
|
|
273
|
+
// Closes the menu if the mouse cursor is beyond the editor vertically.
|
|
274
|
+
if (!block) {
|
|
275
|
+
if (this.menuOpen) {
|
|
276
|
+
this.menuOpen = false;
|
|
277
|
+
this.blockMenu.hide();
|
|
264
278
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
279
|
+
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
|
|
284
|
+
if (
|
|
285
|
+
this.menuOpen &&
|
|
286
|
+
this.hoveredBlock?.hasAttribute("data-id") &&
|
|
287
|
+
this.hoveredBlock?.getAttribute("data-id") === block.id
|
|
288
|
+
) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
|
|
293
|
+
const blockContent = block.node.firstChild as HTMLElement;
|
|
294
|
+
this.hoveredBlock = blockContent;
|
|
295
|
+
|
|
296
|
+
if (!blockContent) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Shows or updates elements.
|
|
301
|
+
if (!this.menuOpen) {
|
|
302
|
+
this.menuOpen = true;
|
|
303
|
+
this.blockMenu.render(this.getDynamicParams(), true);
|
|
304
|
+
} else {
|
|
305
|
+
this.blockMenu.render(this.getDynamicParams(), false);
|
|
287
306
|
}
|
|
288
|
-
menuShown = false;
|
|
289
|
-
addClicked = false;
|
|
290
|
-
ReactDOM.render(<></>, dropElement);
|
|
291
|
-
return false;
|
|
292
307
|
},
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return true;
|
|
305
|
-
},
|
|
306
|
-
mousedown(_view, _event: any) {
|
|
307
|
-
if (!dropElement) {
|
|
308
|
-
throw new Error("unexpected");
|
|
309
|
-
}
|
|
310
|
-
menuShown = false;
|
|
311
|
-
addClicked = false;
|
|
312
|
-
ReactDOM.render(<></>, dropElement);
|
|
313
|
-
return false;
|
|
314
|
-
},
|
|
315
|
-
mousemove(view, event: any) {
|
|
316
|
-
if (!dropElement) {
|
|
317
|
-
throw new Error("unexpected");
|
|
318
|
-
}
|
|
308
|
+
true
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Hides and unfreezes the menu whenever the user selects the editor with the mouse or presses a key.
|
|
312
|
+
// TODO: Better integration with suggestions menu and only editor scope?
|
|
313
|
+
document.body.addEventListener(
|
|
314
|
+
"mousedown",
|
|
315
|
+
(event) => {
|
|
316
|
+
if (this.blockMenu.element?.contains(event.target as HTMLElement)) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
const coords = {
|
|
326
|
-
left: view.dom.clientWidth / 2, // take middle of editor
|
|
327
|
-
top: event.clientY,
|
|
328
|
-
};
|
|
329
|
-
const block = getDraggableBlockFromCoords(coords, view);
|
|
330
|
-
|
|
331
|
-
if (!block) {
|
|
332
|
-
console.warn("Perhaps we should hide element?");
|
|
333
|
-
return true;
|
|
334
|
-
}
|
|
320
|
+
if (this.menuOpen) {
|
|
321
|
+
this.menuOpen = false;
|
|
322
|
+
this.blockMenu.hide();
|
|
323
|
+
}
|
|
335
324
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
325
|
+
this.menuFrozen = false;
|
|
326
|
+
},
|
|
327
|
+
true
|
|
328
|
+
);
|
|
329
|
+
document.body.addEventListener(
|
|
330
|
+
"keydown",
|
|
331
|
+
() => {
|
|
332
|
+
if (this.menuOpen) {
|
|
333
|
+
this.menuOpen = false;
|
|
334
|
+
this.blockMenu.hide();
|
|
335
|
+
}
|
|
340
336
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
337
|
+
this.menuFrozen = false;
|
|
338
|
+
},
|
|
339
|
+
true
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
destroy() {
|
|
344
|
+
if (this.menuOpen) {
|
|
345
|
+
this.menuOpen = false;
|
|
346
|
+
this.blockMenu.hide();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
addBlock() {
|
|
351
|
+
this.menuOpen = false;
|
|
352
|
+
this.menuFrozen = true;
|
|
353
|
+
this.blockMenu.hide();
|
|
354
|
+
|
|
355
|
+
const blockBoundingBox = this.hoveredBlock!.getBoundingClientRect();
|
|
356
|
+
|
|
357
|
+
const pos = this.editor.view.posAtCoords({
|
|
358
|
+
left: blockBoundingBox.left,
|
|
359
|
+
top: blockBoundingBox.top,
|
|
360
|
+
});
|
|
361
|
+
if (!pos) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const blockInfo = getBlockInfoFromPos(this.editor.state.doc, pos.pos);
|
|
366
|
+
if (blockInfo === undefined) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const { contentNode, endPos } = blockInfo;
|
|
344
371
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
372
|
+
// Creates a new block if current one is not empty for the suggestion menu to open in.
|
|
373
|
+
if (contentNode.textContent.length !== 0) {
|
|
374
|
+
const newBlockInsertionPos = endPos + 1;
|
|
375
|
+
const newBlockContentPos = newBlockInsertionPos + 2;
|
|
376
|
+
|
|
377
|
+
this.editor
|
|
378
|
+
.chain()
|
|
379
|
+
.BNCreateBlock(newBlockInsertionPos)
|
|
380
|
+
.BNSetContentType(newBlockContentPos, { name: "textContent" })
|
|
381
|
+
.setTextSelection(newBlockContentPos)
|
|
382
|
+
.run();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Focuses and activates the suggestion menu.
|
|
386
|
+
this.editor.view.focus();
|
|
387
|
+
this.editor.view.dispatch(
|
|
388
|
+
this.editor.view.state.tr.scrollIntoView().setMeta(SlashMenuPluginKey, {
|
|
389
|
+
// TODO import suggestion plugin key
|
|
390
|
+
activate: true,
|
|
391
|
+
type: "drag",
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
deleteBlock() {
|
|
397
|
+
this.menuOpen = false;
|
|
398
|
+
this.blockMenu.hide();
|
|
399
|
+
|
|
400
|
+
const blockBoundingBox = this.hoveredBlock!.getBoundingClientRect();
|
|
401
|
+
|
|
402
|
+
const pos = this.editor.view.posAtCoords({
|
|
403
|
+
left: blockBoundingBox.left,
|
|
404
|
+
top: blockBoundingBox.top,
|
|
405
|
+
});
|
|
406
|
+
if (!pos) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this.editor.commands.BNDeleteBlock(pos.pos);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
getStaticParams(): BlockSideMenuStaticParams {
|
|
414
|
+
return {
|
|
415
|
+
addBlock: () => this.addBlock(),
|
|
416
|
+
deleteBlock: () => this.deleteBlock(),
|
|
417
|
+
blockDragStart: (event: DragEvent) => dragStart(event, this.editor.view),
|
|
418
|
+
blockDragEnd: () => unsetDragImage(),
|
|
419
|
+
freezeMenu: () => {
|
|
420
|
+
this.menuFrozen = true;
|
|
421
|
+
},
|
|
422
|
+
unfreezeMenu: () => {
|
|
423
|
+
this.menuFrozen = false;
|
|
373
424
|
},
|
|
374
|
-
}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
getDynamicParams(): BlockSideMenuDynamicParams {
|
|
429
|
+
const blockBoundingBox = this.hoveredBlock!.getBoundingClientRect();
|
|
430
|
+
|
|
431
|
+
return {
|
|
432
|
+
blockBoundingBox: new DOMRect(
|
|
433
|
+
this.horizontalPosAnchoredAtRoot
|
|
434
|
+
? getHorizontalAnchor()
|
|
435
|
+
: blockBoundingBox.x,
|
|
436
|
+
blockBoundingBox.y,
|
|
437
|
+
blockBoundingBox.width,
|
|
438
|
+
blockBoundingBox.height
|
|
439
|
+
),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export const createDraggableBlocksPlugin = (
|
|
445
|
+
options: DraggableBlocksOptions
|
|
446
|
+
) => {
|
|
447
|
+
return new Plugin({
|
|
448
|
+
key: new PluginKey("DraggableBlocksPlugin"),
|
|
449
|
+
view: () =>
|
|
450
|
+
new BlockMenuView({
|
|
451
|
+
editor: options.editor,
|
|
452
|
+
blockMenuFactory: options.blockSideMenuFactory,
|
|
453
|
+
horizontalPosAnchoredAtRoot: true,
|
|
454
|
+
}),
|
|
375
455
|
});
|
|
376
456
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Extension } from "@tiptap/core";
|
|
2
|
+
import { PluginKey } from "prosemirror-state";
|
|
3
|
+
import { FormattingToolbarFactory } from "./FormattingToolbarFactoryTypes";
|
|
4
|
+
import { createFormattingToolbarPlugin } from "./FormattingToolbarPlugin";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The menu that is displayed when selecting a piece of text.
|
|
8
|
+
*/
|
|
9
|
+
export const FormattingToolbarExtension = Extension.create<{
|
|
10
|
+
formattingToolbarFactory: FormattingToolbarFactory;
|
|
11
|
+
}>({
|
|
12
|
+
name: "FormattingToolbarExtension",
|
|
13
|
+
|
|
14
|
+
addProseMirrorPlugins() {
|
|
15
|
+
if (!this.options.formattingToolbarFactory) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"UI Element factory not defined for FormattingToolbarExtension"
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return [
|
|
22
|
+
createFormattingToolbarPlugin({
|
|
23
|
+
editor: this.editor,
|
|
24
|
+
formattingToolbarFactory: this.options.formattingToolbarFactory,
|
|
25
|
+
pluginKey: new PluginKey("FormattingToolbarPlugin"),
|
|
26
|
+
}),
|
|
27
|
+
];
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EditorElement, ElementFactory } from "../../shared/EditorElement";
|
|
2
|
+
import { BlockContentType } from "../Blocks/nodes/Block";
|
|
3
|
+
|
|
4
|
+
export type FormattingToolbarStaticParams = {
|
|
5
|
+
toggleBold: () => void;
|
|
6
|
+
toggleItalic: () => void;
|
|
7
|
+
toggleUnderline: () => void;
|
|
8
|
+
toggleStrike: () => void;
|
|
9
|
+
setHyperlink: (url: string, text?: string) => void;
|
|
10
|
+
|
|
11
|
+
setBlockType: (type: BlockContentType) => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type FormattingToolbarDynamicParams = {
|
|
15
|
+
boldIsActive: boolean;
|
|
16
|
+
italicIsActive: boolean;
|
|
17
|
+
underlineIsActive: boolean;
|
|
18
|
+
strikeIsActive: boolean;
|
|
19
|
+
hyperlinkIsActive: boolean;
|
|
20
|
+
activeHyperlinkUrl: string;
|
|
21
|
+
activeHyperlinkText: string;
|
|
22
|
+
|
|
23
|
+
// BlockContentType is mostly used to set a block's type, so the attr field is optional as block content types have
|
|
24
|
+
// default values for attributes. However, it means that a block type's attributes field will never be undefined due to
|
|
25
|
+
// these default values, which the Required type enforces.
|
|
26
|
+
activeBlockType: Required<BlockContentType>;
|
|
27
|
+
|
|
28
|
+
selectionBoundingBox: DOMRect;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type FormattingToolbar = EditorElement<FormattingToolbarDynamicParams>;
|
|
32
|
+
export type FormattingToolbarFactory = ElementFactory<
|
|
33
|
+
FormattingToolbarStaticParams,
|
|
34
|
+
FormattingToolbarDynamicParams
|
|
35
|
+
>;
|