|
| 1 | +import { Editor } from "@tiptap/core"; |
1 | 2 | import { TagParseRule } from "@tiptap/pm/model"; |
| 3 | +import { NodeView } from "@tiptap/pm/view"; |
2 | 4 | import type { BlockNoteEditor } from "../../editor/BlockNoteEditor"; |
3 | 5 | import { InlineContentSchema } from "../inlineContent/types"; |
4 | 6 | import { StyleSchema } from "../styles/types"; |
@@ -61,6 +63,27 @@ export type CustomBlockImplementation< |
61 | 63 | ) => PartialBlockFromConfig<T, I, S>["props"] | undefined; |
62 | 64 | }; |
63 | 65 |
|
| 66 | +// Function that enables copying of selected content within non-selectable |
| 67 | +// blocks. |
| 68 | +export function applyNonSelectableBlockFix(nodeView: NodeView, editor: Editor) { |
| 69 | + nodeView.stopEvent = (event) => { |
| 70 | + // Ensures copy events are handled by the browser and not by ProseMirror. |
| 71 | + if (event.type === "copy" || event.type === "cut") { |
| 72 | + return true; |
| 73 | + } |
| 74 | + // Blurs the editor on mouse down as the block is non-selectable. This is |
| 75 | + // mainly done to prevent UI elements like the formatting toolbar from being |
| 76 | + // visible while content within a non-selectable block is selected. |
| 77 | + if (event.type === "mousedown") { |
| 78 | + setTimeout(() => { |
| 79 | + editor.view.dom.blur(); |
| 80 | + }, 10); |
| 81 | + return true; |
| 82 | + } |
| 83 | + return false; |
| 84 | + }; |
| 85 | +} |
| 86 | + |
64 | 87 | // Function that uses the 'parse' function of a blockConfig to create a |
65 | 88 | // TipTap node's `parseHTML` property. This is only used for parsing content |
66 | 89 | // from the clipboard. |
@@ -125,7 +148,7 @@ export function createBlockSpec< |
125 | 148 | ? "inline*" |
126 | 149 | : "") as T["content"] extends "inline" ? "inline*" : "", |
127 | 150 | group: "blockContent", |
128 | | - selectable: true, |
| 151 | + selectable: blockConfig.isSelectable ?? true, |
129 | 152 |
|
130 | 153 | addAttributes() { |
131 | 154 | return propsToAttributes(blockConfig.propSchema); |
@@ -163,13 +186,19 @@ export function createBlockSpec< |
163 | 186 |
|
164 | 187 | const output = blockImplementation.render(block as any, editor); |
165 | 188 |
|
166 | | - return wrapInBlockStructure( |
| 189 | + const nodeView: NodeView = wrapInBlockStructure( |
167 | 190 | output, |
168 | 191 | block.type, |
169 | 192 | block.props, |
170 | 193 | blockConfig.propSchema, |
171 | 194 | blockContentDOMAttributes |
172 | 195 | ); |
| 196 | + |
| 197 | + if (blockConfig.isSelectable === false) { |
| 198 | + applyNonSelectableBlockFix(nodeView, this.editor); |
| 199 | + } |
| 200 | + |
| 201 | + return nodeView; |
173 | 202 | }; |
174 | 203 | }, |
175 | 204 | }); |
|
0 commit comments