@blocknote/core 0.23.6 → 0.24.1
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/dist/blocknote.js +690 -550
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +6 -6
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +2 -2
- package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +492 -0
- package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.test.ts +6 -0
- package/src/api/nodeConversions/blockToNode.ts +5 -3
- package/src/api/nodeConversions/nodeToBlock.ts +11 -5
- package/src/editor/BlockNoteEditor.ts +21 -6
- package/src/editor/BlockNoteExtensions.ts +9 -4
- package/src/editor/BlockNoteTipTapEditor.ts +37 -36
- package/src/extensions/Collaboration/createCollaborationExtensions.ts +4 -2
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +69 -54
- package/src/extensions/SideMenu/SideMenuPlugin.ts +49 -39
- package/src/i18n/locales/ar.ts +3 -0
- package/src/i18n/locales/de.ts +3 -0
- package/src/i18n/locales/en.ts +4 -1
- package/src/i18n/locales/es.ts +3 -0
- package/src/i18n/locales/fr.ts +3 -0
- package/src/i18n/locales/hr.ts +3 -0
- package/src/i18n/locales/is.ts +3 -0
- package/src/i18n/locales/it.ts +316 -314
- package/src/i18n/locales/ja.ts +3 -0
- package/src/i18n/locales/ko.ts +3 -0
- package/src/i18n/locales/nl.ts +3 -0
- package/src/i18n/locales/pl.ts +3 -0
- package/src/i18n/locales/pt.ts +3 -0
- package/src/i18n/locales/ru.ts +3 -0
- package/src/i18n/locales/uk.ts +337 -278
- package/src/i18n/locales/vi.ts +3 -0
- package/src/i18n/locales/zh.ts +3 -0
- package/types/src/editor/BlockNoteEditor.d.ts +2 -2
- package/types/src/editor/BlockNoteExtensions.d.ts +2 -2
- package/types/src/editor/BlockNoteTipTapEditor.d.ts +1 -2
- package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +1 -1
- package/types/src/i18n/locales/de.d.ts +3 -0
- package/types/src/i18n/locales/en.d.ts +4 -7
- package/types/src/i18n/locales/es.d.ts +3 -0
- package/types/src/i18n/locales/hr.d.ts +3 -0
- package/types/src/i18n/locales/it.d.ts +3 -0
|
@@ -130,7 +130,10 @@ export type BlockNoteEditorOptions<
|
|
|
130
130
|
/**
|
|
131
131
|
* @deprecated, provide placeholders via dictionary instead
|
|
132
132
|
*/
|
|
133
|
-
placeholders: Record<
|
|
133
|
+
placeholders: Record<
|
|
134
|
+
string | "default" | "emptyDocument",
|
|
135
|
+
string | undefined
|
|
136
|
+
>;
|
|
134
137
|
|
|
135
138
|
/**
|
|
136
139
|
* An object containing attributes that should be added to HTML elements of the editor.
|
|
@@ -486,7 +489,11 @@ export class BlockNoteEditor<
|
|
|
486
489
|
this.resolveFileUrl = newOptions.resolveFileUrl;
|
|
487
490
|
this.headless = newOptions._headless;
|
|
488
491
|
|
|
489
|
-
|
|
492
|
+
const collaborationEnabled =
|
|
493
|
+
"collaboration" in this.extensions ||
|
|
494
|
+
"liveblocksExtension" in this.extensions;
|
|
495
|
+
|
|
496
|
+
if (collaborationEnabled && newOptions.initialContent) {
|
|
490
497
|
// eslint-disable-next-line no-console
|
|
491
498
|
console.warn(
|
|
492
499
|
"When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider"
|
|
@@ -495,7 +502,7 @@ export class BlockNoteEditor<
|
|
|
495
502
|
|
|
496
503
|
const initialContent =
|
|
497
504
|
newOptions.initialContent ||
|
|
498
|
-
(
|
|
505
|
+
(collaborationEnabled
|
|
499
506
|
? [
|
|
500
507
|
{
|
|
501
508
|
type: "paragraph",
|
|
@@ -589,8 +596,11 @@ export class BlockNoteEditor<
|
|
|
589
596
|
*
|
|
590
597
|
* @warning Not needed to call manually when using React, use BlockNoteView to take care of mounting
|
|
591
598
|
*/
|
|
592
|
-
public mount = (
|
|
593
|
-
|
|
599
|
+
public mount = (
|
|
600
|
+
parentElement?: HTMLElement | null,
|
|
601
|
+
contentComponent?: any
|
|
602
|
+
) => {
|
|
603
|
+
this._tiptapEditor.mount(parentElement, contentComponent);
|
|
594
604
|
};
|
|
595
605
|
|
|
596
606
|
public get prosemirrorView() {
|
|
@@ -924,7 +934,12 @@ export class BlockNoteEditor<
|
|
|
924
934
|
for (const mark of marks) {
|
|
925
935
|
const config = this.schema.styleSchema[mark.type.name];
|
|
926
936
|
if (!config) {
|
|
927
|
-
if (
|
|
937
|
+
if (
|
|
938
|
+
// Links are not considered styles in blocknote
|
|
939
|
+
mark.type.name !== "link" &&
|
|
940
|
+
// "blocknoteIgnore" tagged marks (such as comments) are also not considered BlockNote "styles"
|
|
941
|
+
!mark.type.spec.blocknoteIgnore
|
|
942
|
+
) {
|
|
928
943
|
// eslint-disable-next-line no-console
|
|
929
944
|
console.warn("mark not found in styleschema", mark.type.name);
|
|
930
945
|
}
|
|
@@ -7,11 +7,11 @@ import { Text } from "@tiptap/extension-text";
|
|
|
7
7
|
import { Plugin } from "prosemirror-state";
|
|
8
8
|
import * as Y from "yjs";
|
|
9
9
|
|
|
10
|
-
import type { BlockNoteEditor, BlockNoteExtension } from "./BlockNoteEditor.js";
|
|
11
10
|
import { createDropFileExtension } from "../api/clipboard/fromClipboard/fileDropExtension.js";
|
|
12
11
|
import { createPasteFromClipboardExtension } from "../api/clipboard/fromClipboard/pasteExtension.js";
|
|
13
12
|
import { createCopyToClipboardExtension } from "../api/clipboard/toClipboard/copyExtension.js";
|
|
14
13
|
import { BackgroundColorExtension } from "../extensions/BackgroundColor/BackgroundColorExtension.js";
|
|
14
|
+
import { createCollaborationExtensions } from "../extensions/Collaboration/createCollaborationExtensions.js";
|
|
15
15
|
import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlugin.js";
|
|
16
16
|
import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin.js";
|
|
17
17
|
import { KeyboardShortcutsExtension } from "../extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js";
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
StyleSchema,
|
|
41
41
|
StyleSpecs,
|
|
42
42
|
} from "../schema/index.js";
|
|
43
|
-
import {
|
|
43
|
+
import type { BlockNoteEditor, BlockNoteExtension } from "./BlockNoteEditor.js";
|
|
44
44
|
|
|
45
45
|
type ExtensionOptions<
|
|
46
46
|
BSchema extends BlockSchema,
|
|
@@ -69,7 +69,10 @@ type ExtensionOptions<
|
|
|
69
69
|
animations: boolean;
|
|
70
70
|
tableHandles: boolean;
|
|
71
71
|
dropCursor: (opts: any) => Plugin;
|
|
72
|
-
placeholders: Record<
|
|
72
|
+
placeholders: Record<
|
|
73
|
+
string | "default" | "emptyDocument",
|
|
74
|
+
string | undefined
|
|
75
|
+
>;
|
|
73
76
|
tabBehavior?: "prefer-navigate-ui" | "prefer-indent";
|
|
74
77
|
sideMenuDetection: "viewport" | "editor";
|
|
75
78
|
};
|
|
@@ -171,7 +174,9 @@ const getTipTapExtensions = <
|
|
|
171
174
|
protocols: VALID_LINK_PROTOCOLS,
|
|
172
175
|
}),
|
|
173
176
|
...Object.values(opts.styleSpecs).map((styleSpec) => {
|
|
174
|
-
return styleSpec.implementation.mark
|
|
177
|
+
return styleSpec.implementation.mark.configure({
|
|
178
|
+
editor: opts.editor as any,
|
|
179
|
+
});
|
|
175
180
|
}),
|
|
176
181
|
|
|
177
182
|
TextColorExtension,
|
|
@@ -21,10 +21,9 @@ export type BlockNoteTipTapEditorOptions = Partial<
|
|
|
21
21
|
* Custom Editor class that extends TiptapEditor and separates
|
|
22
22
|
* the creation of the view from the constructor.
|
|
23
23
|
*/
|
|
24
|
-
// @ts-ignore
|
|
25
24
|
export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
26
25
|
private _state: EditorState;
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
public static create = (
|
|
29
28
|
options: BlockNoteTipTapEditorOptions,
|
|
30
29
|
styleSchema: StyleSchema
|
|
@@ -150,40 +149,44 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
|
150
149
|
/**
|
|
151
150
|
* Replace the default `createView` method with a custom one - which we call on mount
|
|
152
151
|
*/
|
|
153
|
-
private createViewAlternative() {
|
|
154
|
-
this.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
152
|
+
private createViewAlternative(contentComponent?: any) {
|
|
153
|
+
(this as any).contentComponent = contentComponent;
|
|
154
|
+
|
|
155
|
+
const markViews: any = {};
|
|
156
|
+
this.extensionManager.extensions.forEach((extension) => {
|
|
157
|
+
if (extension.type === "mark" && extension.config.addMarkView) {
|
|
158
|
+
// Note: migrate to using `addMarkView` from tiptap as soon as this lands
|
|
159
|
+
// (currently tiptap doesn't support markviews)
|
|
160
|
+
markViews[extension.name] = extension.config.addMarkView;
|
|
159
161
|
}
|
|
160
|
-
|
|
161
|
-
{ mount: this.options.element as any }, // use mount option so that we reuse the existing element instead of creating a new one
|
|
162
|
-
{
|
|
163
|
-
...this.options.editorProps,
|
|
164
|
-
// @ts-ignore
|
|
165
|
-
dispatchTransaction: this.dispatchTransaction.bind(this),
|
|
166
|
-
state: this.state,
|
|
167
|
-
}
|
|
168
|
-
);
|
|
162
|
+
});
|
|
169
163
|
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
164
|
+
this.view = new EditorView(
|
|
165
|
+
{ mount: this.options.element as any }, // use mount option so that we reuse the existing element instead of creating a new one
|
|
166
|
+
{
|
|
167
|
+
...this.options.editorProps,
|
|
168
|
+
// @ts-ignore
|
|
169
|
+
dispatchTransaction: this.dispatchTransaction.bind(this),
|
|
170
|
+
state: this.state,
|
|
171
|
+
markViews,
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// `editor.view` is not yet available at this time.
|
|
176
|
+
// Therefore we will add all plugins and node views directly afterwards.
|
|
177
|
+
const newState = this.state.reconfigure({
|
|
178
|
+
plugins: this.extensionManager.plugins,
|
|
179
|
+
});
|
|
175
180
|
|
|
176
|
-
|
|
181
|
+
this.view.updateState(newState);
|
|
177
182
|
|
|
178
|
-
|
|
183
|
+
this.createNodeViews();
|
|
179
184
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
this._creating = false;
|
|
186
|
-
});
|
|
185
|
+
// emit the created event, call here manually because we blocked the default call in the constructor
|
|
186
|
+
// (https://github.com/ueberdosis/tiptap/blob/45bac803283446795ad1b03f43d3746fa54a68ff/packages/core/src/Editor.ts#L117)
|
|
187
|
+
this.commands.focus(this.options.autofocus);
|
|
188
|
+
this.emit("create", { editor: this });
|
|
189
|
+
this.isInitialized = true;
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
/**
|
|
@@ -191,15 +194,12 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
|
191
194
|
*
|
|
192
195
|
* @param element DOM element to mount to, ur null / undefined to destroy
|
|
193
196
|
*/
|
|
194
|
-
public mount = (element?: HTMLElement | null) => {
|
|
197
|
+
public mount = (element?: HTMLElement | null, contentComponent?: any) => {
|
|
195
198
|
if (!element) {
|
|
196
199
|
this.destroy();
|
|
197
|
-
// cancel pending microtask
|
|
198
|
-
this._creating = false;
|
|
199
200
|
} else {
|
|
200
201
|
this.options.element = element;
|
|
201
|
-
|
|
202
|
-
this.createViewAlternative();
|
|
202
|
+
this.createViewAlternative(contentComponent);
|
|
203
203
|
}
|
|
204
204
|
};
|
|
205
205
|
}
|
|
@@ -210,5 +210,6 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
|
210
210
|
// We should call `createView` manually only when a DOM element is available
|
|
211
211
|
|
|
212
212
|
// additional fix because onPaste and onDrop depend on installing plugins in constructor which we don't support
|
|
213
|
+
// (note: can probably be removed after tiptap upgrade fixed in 2.8.0)
|
|
213
214
|
this.options.onPaste = this.options.onDrop = undefined;
|
|
214
215
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Collaboration from "@tiptap/extension-collaboration";
|
|
2
|
-
import { Awareness } from "y-protocols/awareness";
|
|
3
2
|
import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
|
|
3
|
+
import { Awareness } from "y-protocols/awareness";
|
|
4
4
|
import * as Y from "yjs";
|
|
5
5
|
|
|
6
6
|
export const createCollaborationExtensions = (collaboration: {
|
|
@@ -93,7 +93,9 @@ export const createCollaborationExtensions = (collaboration: {
|
|
|
93
93
|
);
|
|
94
94
|
|
|
95
95
|
if (!clientState) {
|
|
96
|
-
throw new Error(
|
|
96
|
+
throw new Error(
|
|
97
|
+
"Could not find client state for user, " + JSON.stringify(user)
|
|
98
|
+
);
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
const clientID = clientState[0];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
2
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
3
|
+
import { v4 } from "uuid";
|
|
3
4
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
4
5
|
|
|
5
6
|
const PLUGIN_KEY = new PluginKey(`blocknote-placeholder`);
|
|
@@ -8,16 +9,23 @@ export class PlaceholderPlugin {
|
|
|
8
9
|
public readonly plugin: Plugin;
|
|
9
10
|
constructor(
|
|
10
11
|
editor: BlockNoteEditor<any, any, any>,
|
|
11
|
-
placeholders: Record<
|
|
12
|
+
placeholders: Record<
|
|
13
|
+
string | "default" | "emptyDocument",
|
|
14
|
+
string | undefined
|
|
15
|
+
>
|
|
12
16
|
) {
|
|
13
17
|
this.plugin = new Plugin({
|
|
14
18
|
key: PLUGIN_KEY,
|
|
15
|
-
view: () => {
|
|
19
|
+
view: (view) => {
|
|
20
|
+
const uniqueEditorSelector = `placeholder-selector-${v4()}`;
|
|
21
|
+
view.dom.classList.add(uniqueEditorSelector);
|
|
16
22
|
const styleEl = document.createElement("style");
|
|
23
|
+
|
|
17
24
|
const nonce = editor._tiptapEditor.options.injectNonce;
|
|
18
25
|
if (nonce) {
|
|
19
26
|
styleEl.setAttribute("nonce", nonce);
|
|
20
27
|
}
|
|
28
|
+
|
|
21
29
|
if (editor.prosemirrorView?.root instanceof ShadowRoot) {
|
|
22
30
|
editor.prosemirrorView.root.append(styleEl);
|
|
23
31
|
} else {
|
|
@@ -26,54 +34,50 @@ export class PlaceholderPlugin {
|
|
|
26
34
|
|
|
27
35
|
const styleSheet = styleEl.sheet!;
|
|
28
36
|
|
|
29
|
-
const
|
|
30
|
-
|
|
37
|
+
const getSelector = (additionalSelectors = "") =>
|
|
38
|
+
`.${uniqueEditorSelector} .bn-block-content${additionalSelectors} .bn-inline-content:has(> .ProseMirror-trailingBreak:only-child):before`;
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
try {
|
|
41
|
+
// FIXME: the names "default" and "emptyDocument" are hardcoded
|
|
42
|
+
const {
|
|
43
|
+
default: defaultPlaceholder,
|
|
44
|
+
emptyDocument: emptyPlaceholder,
|
|
45
|
+
...rest
|
|
46
|
+
} = placeholders;
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const blockTypeSelector = `[data-content-type="${blockType}"]`;
|
|
45
|
-
return getBaseSelector(mustBeFocusedSelector + blockTypeSelector);
|
|
46
|
-
};
|
|
48
|
+
// add block specific placeholders
|
|
49
|
+
for (const [blockType, placeholder] of Object.entries(rest)) {
|
|
50
|
+
const blockTypeSelector = `[data-content-type="${blockType}"]`;
|
|
47
51
|
|
|
48
|
-
for (const [blockType, placeholder] of Object.entries(placeholders)) {
|
|
49
|
-
const mustBeFocused = blockType === "default";
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
52
|
styleSheet.insertRule(
|
|
53
|
-
`${getSelector(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)} { content: ${JSON.stringify(placeholder)}; }`
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// For some reason, the placeholders which show when the block is focused
|
|
60
|
-
// take priority over ones which show depending on block type, so we need
|
|
61
|
-
// to make sure the block specific ones are also used when the block is
|
|
62
|
-
// focused.
|
|
63
|
-
if (!mustBeFocused) {
|
|
64
|
-
styleSheet.insertRule(
|
|
65
|
-
`${getSelector(blockType, true)} { content: ${JSON.stringify(
|
|
66
|
-
placeholder
|
|
67
|
-
)}; }`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
} catch (e) {
|
|
71
|
-
// eslint-disable-next-line no-console
|
|
72
|
-
console.warn(
|
|
73
|
-
`Failed to insert placeholder CSS rule - this is likely due to the browser not supporting certain CSS pseudo-element selectors (:has, :only-child:, or :before)`,
|
|
74
|
-
e
|
|
53
|
+
`${getSelector(blockTypeSelector)} { content: ${JSON.stringify(
|
|
54
|
+
placeholder
|
|
55
|
+
)}; }`
|
|
75
56
|
);
|
|
76
57
|
}
|
|
58
|
+
|
|
59
|
+
const onlyBlockSelector = `[data-is-only-empty-block]`;
|
|
60
|
+
const mustBeFocusedSelector = `[data-is-empty-and-focused]`;
|
|
61
|
+
|
|
62
|
+
// placeholder for when there's only one empty block
|
|
63
|
+
styleSheet.insertRule(
|
|
64
|
+
`${getSelector(onlyBlockSelector)} { content: ${JSON.stringify(
|
|
65
|
+
emptyPlaceholder
|
|
66
|
+
)}; }`
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// placeholder for default blocks, only when the cursor is in the block (mustBeFocused)
|
|
70
|
+
styleSheet.insertRule(
|
|
71
|
+
`${getSelector(mustBeFocusedSelector)} { content: ${JSON.stringify(
|
|
72
|
+
defaultPlaceholder
|
|
73
|
+
)}; }`
|
|
74
|
+
);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// eslint-disable-next-line no-console
|
|
77
|
+
console.warn(
|
|
78
|
+
`Failed to insert placeholder CSS rule - this is likely due to the browser not supporting certain CSS pseudo-element selectors (:has, :only-child:, or :before)`,
|
|
79
|
+
e
|
|
80
|
+
);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
return {
|
|
@@ -87,7 +91,6 @@ export class PlaceholderPlugin {
|
|
|
87
91
|
};
|
|
88
92
|
},
|
|
89
93
|
props: {
|
|
90
|
-
// TODO: maybe also add placeholder for empty document ("e.g.: start writing..")
|
|
91
94
|
decorations: (state) => {
|
|
92
95
|
const { doc, selection } = state;
|
|
93
96
|
|
|
@@ -104,20 +107,32 @@ export class PlaceholderPlugin {
|
|
|
104
107
|
return;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
const
|
|
108
|
-
const node = $pos.parent;
|
|
110
|
+
const decs = [];
|
|
109
111
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
// decoration for when there's only one empty block
|
|
113
|
+
// positions are hardcoded for now
|
|
114
|
+
if (state.doc.content.size === 6) {
|
|
115
|
+
decs.push(
|
|
116
|
+
Decoration.node(2, 4, {
|
|
117
|
+
"data-is-only-empty-block": "true",
|
|
118
|
+
})
|
|
119
|
+
);
|
|
112
120
|
}
|
|
113
121
|
|
|
114
|
-
const
|
|
122
|
+
const $pos = selection.$anchor;
|
|
123
|
+
const node = $pos.parent;
|
|
124
|
+
|
|
125
|
+
if (node.content.size === 0) {
|
|
126
|
+
const before = $pos.before();
|
|
115
127
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
decs.push(
|
|
129
|
+
Decoration.node(before, before + node.nodeSize, {
|
|
130
|
+
"data-is-empty-and-focused": "true",
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
}
|
|
119
134
|
|
|
120
|
-
return DecorationSet.create(doc,
|
|
135
|
+
return DecorationSet.create(doc, decs);
|
|
121
136
|
},
|
|
122
137
|
},
|
|
123
138
|
});
|
|
@@ -283,47 +283,57 @@ export class SideMenuView<
|
|
|
283
283
|
|
|
284
284
|
this.editor._tiptapEditor.commands.blur();
|
|
285
285
|
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
286
|
+
// Finds the BlockNote editor element that the drop event occurred in (if
|
|
287
|
+
// any).
|
|
288
|
+
const parentEditorElement =
|
|
289
|
+
event.target instanceof Node
|
|
290
|
+
? (event.target instanceof HTMLElement
|
|
291
|
+
? event.target
|
|
292
|
+
: event.target.parentElement
|
|
293
|
+
)?.closest(".bn-editor") || null
|
|
294
|
+
: null;
|
|
295
|
+
|
|
296
|
+
// Drop event occurred within an editor.
|
|
297
|
+
if (parentEditorElement) {
|
|
298
|
+
// When ProseMirror handles a drop event on the editor while
|
|
299
|
+
// `view.dragging` is set, it deletes the selected content. However, if
|
|
300
|
+
// a block from a different editor is being dropped, this causes some
|
|
301
|
+
// issues that the code below fixes:
|
|
302
|
+
if (!this.isDragOrigin && this.pmView.dom === parentEditorElement) {
|
|
303
|
+
// 1. Because the editor selection is unrelated to the dragged content,
|
|
304
|
+
// we don't want PM to delete its content. Therefore, we collapse the
|
|
305
|
+
// selection.
|
|
306
|
+
this.pmView.dispatch(
|
|
307
|
+
this.pmView.state.tr.setSelection(
|
|
308
|
+
TextSelection.create(
|
|
309
|
+
this.pmView.state.tr.doc,
|
|
310
|
+
this.pmView.state.tr.selection.to
|
|
311
|
+
)
|
|
299
312
|
)
|
|
300
|
-
)
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
313
|
+
);
|
|
314
|
+
} else if (this.isDragOrigin && this.pmView.dom !== parentEditorElement) {
|
|
315
|
+
// 2. Because the editor from which the block originates doesn't get a
|
|
316
|
+
// drop event on it, PM doesn't delete its selected content. Therefore, we
|
|
317
|
+
// need to do so manually.
|
|
318
|
+
//
|
|
319
|
+
// Note: Deleting the selected content from the editor from which the
|
|
320
|
+
// block originates, may change its height. This can cause the position of
|
|
321
|
+
// the editor in which the block is being dropping to shift, before it
|
|
322
|
+
// can handle the drop event. That in turn can cause the drop to happen
|
|
323
|
+
// somewhere other than the user intended. To get around this, we delay
|
|
324
|
+
// deleting the selected content until all editors have had the chance to
|
|
325
|
+
// handle the event.
|
|
326
|
+
setTimeout(
|
|
327
|
+
() => this.pmView.dispatch(this.pmView.state.tr.deleteSelection()),
|
|
328
|
+
0
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
// 3. PM only clears `view.dragging` on the editor that the block was
|
|
332
|
+
// dropped, so we manually have to clear it on all the others. However,
|
|
333
|
+
// PM also needs to read `view.dragging` while handling the event, so we
|
|
334
|
+
// use a `setTimeout` to ensure it's only cleared after that.
|
|
335
|
+
setTimeout(() => (this.pmView.dragging = null), 0);
|
|
321
336
|
}
|
|
322
|
-
// 3. PM only clears `view.dragging` on the editor that the block was
|
|
323
|
-
// dropped, so we manually have to clear it on all the others. However,
|
|
324
|
-
// PM also needs to read `view.dragging` while handling the event, so we
|
|
325
|
-
// use a `setTimeout` to ensure it's only cleared after that.
|
|
326
|
-
setTimeout(() => (this.pmView.dragging = null), 0);
|
|
327
337
|
|
|
328
338
|
if (
|
|
329
339
|
this.sideMenuDetection === "editor" ||
|
package/src/i18n/locales/ar.ts
CHANGED
package/src/i18n/locales/de.ts
CHANGED
package/src/i18n/locales/en.ts
CHANGED
|
@@ -129,7 +129,7 @@ export const en = {
|
|
|
129
129
|
bulletListItem: "List",
|
|
130
130
|
numberedListItem: "List",
|
|
131
131
|
checkListItem: "List",
|
|
132
|
-
},
|
|
132
|
+
} as Record<"default" | "emptyDocument" | string, string | undefined>,
|
|
133
133
|
file_blocks: {
|
|
134
134
|
image: {
|
|
135
135
|
add_button_text: "Add image",
|
|
@@ -275,6 +275,9 @@ export const en = {
|
|
|
275
275
|
align_justify: {
|
|
276
276
|
tooltip: "Justify text",
|
|
277
277
|
},
|
|
278
|
+
comment: {
|
|
279
|
+
tooltip: "Add comment",
|
|
280
|
+
},
|
|
278
281
|
},
|
|
279
282
|
file_panel: {
|
|
280
283
|
upload: {
|
package/src/i18n/locales/es.ts
CHANGED
package/src/i18n/locales/fr.ts
CHANGED
package/src/i18n/locales/hr.ts
CHANGED