@blocknote/core 0.12.4 → 0.13.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.
Files changed (45) hide show
  1. package/dist/blocknote.js +1079 -685
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +6 -6
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/webpack-stats.json +1 -1
  6. package/package.json +2 -2
  7. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +1 -1
  8. package/src/editor/BlockNoteEditor.ts +24 -8
  9. package/src/editor/BlockNoteExtensions.ts +22 -11
  10. package/src/editor/BlockNoteTipTapEditor.ts +9 -6
  11. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +24 -41
  12. package/src/extensions/ImagePanel/ImageToolbarPlugin.ts +27 -30
  13. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +25 -3
  14. package/src/extensions/Placeholder/PlaceholderPlugin.ts +95 -0
  15. package/src/extensions/SideMenu/SideMenuPlugin.ts +3 -2
  16. package/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts +3 -0
  17. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +6 -2
  18. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +18 -44
  19. package/src/extensions/TableHandles/TableHandlesPlugin.ts +1 -1
  20. package/src/i18n/dictionary.ts +17 -0
  21. package/src/i18n/locales/en.ts +196 -0
  22. package/src/i18n/locales/index.ts +2 -0
  23. package/src/i18n/locales/nl.ts +197 -0
  24. package/src/index.ts +4 -1
  25. package/src/pm-nodes/BlockContainer.ts +17 -1
  26. package/src/util/browser.ts +2 -2
  27. package/src/util/typescript.ts +8 -0
  28. package/types/src/editor/BlockNoteEditor.d.ts +11 -1
  29. package/types/src/editor/BlockNoteExtensions.d.ts +3 -3
  30. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +5 -4
  31. package/types/src/extensions/ImagePanel/ImageToolbarPlugin.d.ts +7 -5
  32. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +3 -1
  33. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +3 -0
  34. package/types/src/extensions/SuggestionMenu/DefaultSuggestionItem.d.ts +2 -0
  35. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +2 -1
  36. package/types/src/i18n/dictionary.d.ts +2 -0
  37. package/types/src/i18n/locales/en.d.ts +184 -0
  38. package/types/src/i18n/locales/index.d.ts +2 -0
  39. package/types/src/i18n/locales/nl.d.ts +2 -0
  40. package/types/src/index.d.ts +4 -1
  41. package/types/src/pm-nodes/BlockContainer.d.ts +1 -1
  42. package/types/src/util/browser.d.ts +1 -1
  43. package/types/src/util/typescript.d.ts +1 -0
  44. package/src/extensions/Placeholder/PlaceholderExtension.ts +0 -124
  45. package/types/src/extensions/Placeholder/PlaceholderExtension.d.ts +0 -12
@@ -1 +1 @@
1
- {"builtAt":1712138303295,"assets":[{"name":"blocknote.umd.cjs","size":103205},{"name":"blocknote.umd.cjs.map","size":466872}],"chunks":[{"id":"a1ee98a","entry":true,"initial":true,"files":["blocknote.umd.cjs"],"names":["index"]}],"modules":[{"name":"./src/extensions/UniqueID/UniqueID.ts","size":8518,"chunks":["a1ee98a"]},{"name":"./src/api/getBlockInfoFromPos.ts","size":1691,"chunks":["a1ee98a"]},{"name":"./src/schema/inlineContent/types.ts","size":302,"chunks":["a1ee98a"]},{"name":"./src/util/typescript.ts","size":108,"chunks":["a1ee98a"]},{"name":"./src/api/nodeConversions/nodeConversions.ts","size":11959,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/util/sharedHTMLConversion.ts","size":2293,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts","size":2687,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/externalHTMLExporter.ts","size":1066,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/internalHTMLSerializer.ts","size":704,"chunks":["a1ee98a"]},{"name":"./src/blocks/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts","size":316,"chunks":["a1ee98a"]},{"name":"./src/util/browser.ts","size":515,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultBlockHelpers.ts","size":1731,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultProps.ts","size":269,"chunks":["a1ee98a"]},{"name":"./src/util/string.ts","size":108,"chunks":["a1ee98a"]},{"name":"./src/schema/blocks/internal.ts","size":3701,"chunks":["a1ee98a"]},{"name":"./src/schema/blocks/createSpec.ts","size":2781,"chunks":["a1ee98a"]},{"name":"./src/schema/inlineContent/internal.ts","size":1357,"chunks":["a1ee98a"]},{"name":"./src/schema/inlineContent/createSpec.ts","size":1627,"chunks":["a1ee98a"]},{"name":"./src/schema/styles/internal.ts","size":1162,"chunks":["a1ee98a"]},{"name":"./src/schema/styles/createSpec.ts","size":1263,"chunks":["a1ee98a"]},{"name":"./src/extensions/BackgroundColor/BackgroundColorMark.ts","size":946,"chunks":["a1ee98a"]},{"name":"./src/extensions/TextColor/TextColorMark.ts","size":866,"chunks":["a1ee98a"]},{"name":"./src/api/getCurrentBlockContentType.ts","size":192,"chunks":["a1ee98a"]},{"name":"./src/blocks/HeadingBlockContent/HeadingBlockContent.ts","size":3439,"chunks":["a1ee98a"]},{"name":"./src/blocks/ImageBlockContent/ImageBlockContent.ts","size":9280,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts","size":1151,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts","size":2964,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts","size":1862,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts","size":3421,"chunks":["a1ee98a"]},{"name":"./src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts","size":1172,"chunks":["a1ee98a"]},{"name":"./src/blocks/TableBlockContent/TableExtension.ts","size":1462,"chunks":["a1ee98a"]},{"name":"./src/blocks/TableBlockContent/TableBlockContent.ts","size":1207,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultBlocks.ts","size":1025,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultBlockTypeGuards.ts","size":757,"chunks":["a1ee98a"]},{"name":"./src/api/nodeUtil.ts","size":548,"chunks":["a1ee98a"]},{"name":"./src/api/blockManipulation/blockManipulation.ts","size":5738,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/markdown/removeUnderlinesRehypePlugin.ts","size":752,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/markdown/markdownExporter.ts","size":549,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/html/util/nestedLists.ts","size":2174,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/html/parseHTML.ts","size":503,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/markdown/parseMarkdown.ts","size":1054,"chunks":["a1ee98a"]},{"name":"./src/util/EventEmitter.ts","size":744,"chunks":["a1ee98a"]},{"name":"./src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts","size":4664,"chunks":["a1ee98a"]},{"name":"./src/extensions/LinkToolbar/LinkToolbarPlugin.ts","size":6822,"chunks":["a1ee98a"]},{"name":"./src/extensions/SuggestionMenu/SuggestionPlugin.ts","size":7582,"chunks":["a1ee98a"]},{"name":"./src/extensions/SideMenu/MultipleNodeSelection.ts","size":1616,"chunks":["a1ee98a"]},{"name":"./src/extensions/SideMenu/SideMenuPlugin.ts","size":14923,"chunks":["a1ee98a"]},{"name":"./src/extensions/ImagePanel/ImageToolbarPlugin.ts","size":3482,"chunks":["a1ee98a"]},{"name":"./src/extensions/TableHandles/TableHandlesPlugin.ts","size":14755,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/copyExtension.ts","size":2883,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/pasteExtension.ts","size":1131,"chunks":["a1ee98a"]},{"name":"./src/extensions/BackgroundColor/BackgroundColorExtension.ts","size":688,"chunks":["a1ee98a"]},{"name":"./src/extensions/Placeholder/PlaceholderExtension.ts","size":2880,"chunks":["a1ee98a"]},{"name":"./src/extensions/TextAlignment/TextAlignmentExtension.ts","size":748,"chunks":["a1ee98a"]},{"name":"./src/extensions/TextColor/TextColorExtension.ts","size":622,"chunks":["a1ee98a"]},{"name":"./src/extensions/TrailingNode/TrailingNodeExtension.ts","size":1594,"chunks":["a1ee98a"]},{"name":"./src/extensions/NonEditableBlocks/NonEditableBlockPlugin.ts","size":467,"chunks":["a1ee98a"]},{"name":"./src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts","size":4572,"chunks":["a1ee98a"]},{"name":"./src/pm-nodes/BlockContainer.ts","size":17235,"chunks":["a1ee98a"]},{"name":"./src/pm-nodes/BlockGroup.ts","size":1097,"chunks":["a1ee98a"]},{"name":"./src/pm-nodes/Doc.ts","size":90,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteExtensions.ts","size":3536,"chunks":["a1ee98a"]},{"name":"./src/editor/transformPasted.ts","size":1017,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteSchema.ts","size":827,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteTipTapEditor.ts","size":2514,"chunks":["a1ee98a"]},{"name":"./src/editor/Block.css","size":17,"chunks":["a1ee98a"]},{"name":"./src/editor/editor.css","size":18,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteEditor.ts","size":22844,"chunks":["a1ee98a"]},{"name":"./src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts","size":5300,"chunks":["a1ee98a"]},{"name":"./src/api/testUtil/partialBlockTestUtil.ts","size":2166,"chunks":["a1ee98a"]},{"name":"./src/index.ts","size":0,"chunks":["a1ee98a"]}]}
1
+ {"builtAt":1714926597310,"assets":[{"name":"blocknote.umd.cjs","size":109661},{"name":"blocknote.umd.cjs.map","size":481369}],"chunks":[{"id":"a1ee98a","entry":true,"initial":true,"files":["blocknote.umd.cjs"],"names":["index"]}],"modules":[{"name":"./src/i18n/locales/en.ts","size":4307,"chunks":["a1ee98a"]},{"name":"./src/i18n/locales/nl.ts","size":4477,"chunks":["a1ee98a"]},{"name":"./src/i18n/locales/index.ts","size":0,"chunks":["a1ee98a"]},{"name":"./src/extensions/UniqueID/UniqueID.ts","size":8518,"chunks":["a1ee98a"]},{"name":"./src/api/getBlockInfoFromPos.ts","size":1691,"chunks":["a1ee98a"]},{"name":"./src/schema/inlineContent/types.ts","size":302,"chunks":["a1ee98a"]},{"name":"./src/util/typescript.ts","size":331,"chunks":["a1ee98a"]},{"name":"./src/api/nodeConversions/nodeConversions.ts","size":11959,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/util/sharedHTMLConversion.ts","size":2293,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts","size":2687,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/externalHTMLExporter.ts","size":1066,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/html/internalHTMLSerializer.ts","size":704,"chunks":["a1ee98a"]},{"name":"./src/blocks/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts","size":316,"chunks":["a1ee98a"]},{"name":"./src/util/browser.ts","size":536,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultBlockHelpers.ts","size":1731,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultProps.ts","size":269,"chunks":["a1ee98a"]},{"name":"./src/util/string.ts","size":108,"chunks":["a1ee98a"]},{"name":"./src/schema/blocks/internal.ts","size":3701,"chunks":["a1ee98a"]},{"name":"./src/schema/blocks/createSpec.ts","size":2781,"chunks":["a1ee98a"]},{"name":"./src/schema/inlineContent/internal.ts","size":1357,"chunks":["a1ee98a"]},{"name":"./src/schema/inlineContent/createSpec.ts","size":1627,"chunks":["a1ee98a"]},{"name":"./src/schema/styles/internal.ts","size":1162,"chunks":["a1ee98a"]},{"name":"./src/schema/styles/createSpec.ts","size":1263,"chunks":["a1ee98a"]},{"name":"./src/extensions/BackgroundColor/BackgroundColorMark.ts","size":946,"chunks":["a1ee98a"]},{"name":"./src/extensions/TextColor/TextColorMark.ts","size":866,"chunks":["a1ee98a"]},{"name":"./src/api/getCurrentBlockContentType.ts","size":192,"chunks":["a1ee98a"]},{"name":"./src/blocks/HeadingBlockContent/HeadingBlockContent.ts","size":3439,"chunks":["a1ee98a"]},{"name":"./src/blocks/ImageBlockContent/ImageBlockContent.ts","size":9303,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts","size":1151,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts","size":2964,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts","size":1862,"chunks":["a1ee98a"]},{"name":"./src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts","size":3421,"chunks":["a1ee98a"]},{"name":"./src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts","size":1172,"chunks":["a1ee98a"]},{"name":"./src/blocks/TableBlockContent/TableExtension.ts","size":1462,"chunks":["a1ee98a"]},{"name":"./src/blocks/TableBlockContent/TableBlockContent.ts","size":1207,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultBlocks.ts","size":1025,"chunks":["a1ee98a"]},{"name":"./src/blocks/defaultBlockTypeGuards.ts","size":757,"chunks":["a1ee98a"]},{"name":"./src/api/nodeUtil.ts","size":548,"chunks":["a1ee98a"]},{"name":"./src/api/blockManipulation/blockManipulation.ts","size":5738,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/markdown/removeUnderlinesRehypePlugin.ts","size":752,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/markdown/markdownExporter.ts","size":549,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/html/util/nestedLists.ts","size":2174,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/html/parseHTML.ts","size":503,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/markdown/parseMarkdown.ts","size":1054,"chunks":["a1ee98a"]},{"name":"./src/util/EventEmitter.ts","size":744,"chunks":["a1ee98a"]},{"name":"./src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts","size":4287,"chunks":["a1ee98a"]},{"name":"./src/extensions/ImagePanel/ImageToolbarPlugin.ts","size":3393,"chunks":["a1ee98a"]},{"name":"./src/extensions/LinkToolbar/LinkToolbarPlugin.ts","size":7273,"chunks":["a1ee98a"]},{"name":"./src/extensions/SuggestionMenu/SuggestionPlugin.ts","size":7646,"chunks":["a1ee98a"]},{"name":"./src/extensions/SideMenu/MultipleNodeSelection.ts","size":1616,"chunks":["a1ee98a"]},{"name":"./src/extensions/SideMenu/SideMenuPlugin.ts","size":14952,"chunks":["a1ee98a"]},{"name":"./src/extensions/TableHandles/TableHandlesPlugin.ts","size":14755,"chunks":["a1ee98a"]},{"name":"./src/api/exporters/copyExtension.ts","size":2883,"chunks":["a1ee98a"]},{"name":"./src/api/parsers/pasteExtension.ts","size":1131,"chunks":["a1ee98a"]},{"name":"./src/extensions/BackgroundColor/BackgroundColorExtension.ts","size":688,"chunks":["a1ee98a"]},{"name":"./src/extensions/TextAlignment/TextAlignmentExtension.ts","size":748,"chunks":["a1ee98a"]},{"name":"./src/extensions/TextColor/TextColorExtension.ts","size":622,"chunks":["a1ee98a"]},{"name":"./src/extensions/TrailingNode/TrailingNodeExtension.ts","size":1594,"chunks":["a1ee98a"]},{"name":"./src/extensions/NonEditableBlocks/NonEditableBlockPlugin.ts","size":467,"chunks":["a1ee98a"]},{"name":"./src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts","size":4578,"chunks":["a1ee98a"]},{"name":"./src/pm-nodes/BlockContainer.ts","size":17589,"chunks":["a1ee98a"]},{"name":"./src/pm-nodes/BlockGroup.ts","size":1097,"chunks":["a1ee98a"]},{"name":"./src/pm-nodes/Doc.ts","size":90,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteExtensions.ts","size":3859,"chunks":["a1ee98a"]},{"name":"./src/editor/transformPasted.ts","size":1017,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteSchema.ts","size":827,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteTipTapEditor.ts","size":2655,"chunks":["a1ee98a"]},{"name":"./src/extensions/Placeholder/PlaceholderPlugin.ts","size":2253,"chunks":["a1ee98a"]},{"name":"./src/editor/Block.css","size":17,"chunks":["a1ee98a"]},{"name":"./src/editor/editor.css","size":18,"chunks":["a1ee98a"]},{"name":"./src/editor/BlockNoteEditor.ts","size":22961,"chunks":["a1ee98a"]},{"name":"./src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts","size":4553,"chunks":["a1ee98a"]},{"name":"./src/api/testUtil/partialBlockTestUtil.ts","size":2166,"chunks":["a1ee98a"]},{"name":"./src/index.ts","size":0,"chunks":["a1ee98a"]}]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "homepage": "https://github.com/TypeCellOS/BlockNote",
4
4
  "private": false,
5
5
  "license": "MPL-2.0",
6
- "version": "0.12.4",
6
+ "version": "0.13.0",
7
7
  "files": [
8
8
  "dist",
9
9
  "types",
@@ -116,5 +116,5 @@
116
116
  "access": "public",
117
117
  "registry": "https://registry.npmjs.org/"
118
118
  },
119
- "gitHead": "54716a4b6d83f46e0d318dc39cd313d35fc8909e"
119
+ "gitHead": "501dc35254104a4ee56718ee299d31d534586b55"
120
120
  }
@@ -76,7 +76,7 @@ export const renderImage = (
76
76
  // Text for the add image button.
77
77
  const addImageButtonText = document.createElement("p");
78
78
  addImageButtonText.className = "bn-add-image-button-text";
79
- addImageButtonText.innerText = "Add Image";
79
+ addImageButtonText.innerText = editor.dictionary.image.add_button;
80
80
 
81
81
  // Wrapper element for the image, resize handles and caption.
82
82
  const imageAndCaptionWrapper = document.createElement("div");
@@ -27,10 +27,10 @@ import {
27
27
  PartialBlock,
28
28
  } from "../blocks/defaultBlocks";
29
29
  import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin";
30
+ import { ImagePanelProsemirrorPlugin } from "../extensions/ImagePanel/ImageToolbarPlugin";
30
31
  import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin";
31
32
  import { SideMenuProsemirrorPlugin } from "../extensions/SideMenu/SideMenuPlugin";
32
33
  import { SuggestionMenuProseMirrorPlugin } from "../extensions/SuggestionMenu/SuggestionPlugin";
33
- import { ImagePanelProsemirrorPlugin } from "../extensions/ImagePanel/ImageToolbarPlugin";
34
34
  import { TableHandlesProsemirrorPlugin } from "../extensions/TableHandles/TableHandlesPlugin";
35
35
  import { UniqueID } from "../extensions/UniqueID/UniqueID";
36
36
  import {
@@ -62,6 +62,9 @@ import {
62
62
  } from "./BlockNoteTipTapEditor";
63
63
 
64
64
  // CSS
65
+ import { PlaceholderPlugin } from "../extensions/Placeholder/PlaceholderPlugin";
66
+ import { Dictionary } from "../i18n/dictionary";
67
+ import { en } from "../i18n/locales";
65
68
  import "./Block.css";
66
69
  import "./editor.css";
67
70
 
@@ -73,6 +76,14 @@ export type BlockNoteEditorOptions<
73
76
  // TODO: Figure out if enableBlockNoteExtensions/disableHistoryExtension are needed and document them.
74
77
  enableBlockNoteExtensions: boolean;
75
78
 
79
+ /**
80
+ * A dictionary object containing translations for the editor.
81
+ */
82
+ dictionary?: Dictionary;
83
+
84
+ /**
85
+ * @deprecated, provide placeholders via dictionary instead
86
+ */
76
87
  placeholders: Record<string | "default", string>;
77
88
 
78
89
  /**
@@ -133,6 +144,8 @@ export type BlockNoteEditorOptions<
133
144
 
134
145
  // tiptap options, undocumented
135
146
  _tiptapOptions: Partial<EditorOptions>;
147
+
148
+ trailingBlock?: boolean;
136
149
  };
137
150
 
138
151
  const blockNoteTipTapOptions = {
@@ -150,6 +163,8 @@ export class BlockNoteEditor<
150
163
  contentComponent: any;
151
164
  };
152
165
  public blockCache = new WeakMap<Node, Block<any, any, any>>();
166
+ public readonly dictionary: Dictionary;
167
+
153
168
  public readonly schema: BlockNoteSchema<BSchema, ISchema, SSchema>;
154
169
 
155
170
  public readonly blockImplementations: BlockSpecs;
@@ -216,11 +231,17 @@ export class BlockNoteEditor<
216
231
  );
217
232
  }
218
233
 
234
+ this.dictionary = options.dictionary || en;
235
+
219
236
  // apply defaults
220
237
  const newOptions = {
221
238
  defaultStyles: true,
222
239
  schema: options.schema || BlockNoteSchema.create(),
223
240
  ...options,
241
+ placeholders: {
242
+ ...this.dictionary.placeholders,
243
+ ...options.placeholders,
244
+ },
224
245
  };
225
246
 
226
247
  // @ts-ignore
@@ -243,13 +264,13 @@ export class BlockNoteEditor<
243
264
 
244
265
  const extensions = getBlockNoteExtensions({
245
266
  editor: this,
246
- placeholders: newOptions.placeholders,
247
267
  domAttributes: newOptions.domAttributes || {},
248
268
  blockSchema: this.schema.blockSchema,
249
269
  blockSpecs: this.schema.blockSpecs,
250
270
  styleSpecs: this.schema.styleSpecs,
251
271
  inlineContentSpecs: this.schema.inlineContentSpecs,
252
272
  collaboration: newOptions.collaboration,
273
+ trailingBlock: newOptions.trailingBlock,
253
274
  });
254
275
 
255
276
  const blockNoteUIExtension = Extension.create({
@@ -263,6 +284,7 @@ export class BlockNoteEditor<
263
284
  this.suggestionMenus.plugin,
264
285
  ...(this.imagePanel ? [this.imagePanel.plugin] : []),
265
286
  ...(this.tableHandles ? [this.tableHandles.plugin] : []),
287
+ PlaceholderPlugin(this, newOptions.placeholders),
266
288
  ];
267
289
  },
268
290
  });
@@ -759,8 +781,6 @@ export class BlockNoteEditor<
759
781
  * @param styles The styles to add.
760
782
  */
761
783
  public addStyles(styles: Styles<SSchema>) {
762
- this._tiptapEditor.view.focus();
763
-
764
784
  for (const [style, value] of Object.entries(styles)) {
765
785
  const config = this.schema.styleSchema[style];
766
786
  if (!config) {
@@ -781,8 +801,6 @@ export class BlockNoteEditor<
781
801
  * @param styles The styles to remove.
782
802
  */
783
803
  public removeStyles(styles: Styles<SSchema>) {
784
- this._tiptapEditor.view.focus();
785
-
786
804
  for (const style of Object.keys(styles)) {
787
805
  this._tiptapEditor.commands.unsetMark(style);
788
806
  }
@@ -793,8 +811,6 @@ export class BlockNoteEditor<
793
811
  * @param styles The styles to toggle.
794
812
  */
795
813
  public toggleStyles(styles: Styles<SSchema>) {
796
- this._tiptapEditor.view.focus();
797
-
798
814
  for (const [style, value] of Object.entries(styles)) {
799
815
  const config = this.schema.styleSchema[style];
800
816
  if (!config) {
@@ -1,4 +1,4 @@
1
- import { Extensions, extensions } from "@tiptap/core";
1
+ import { Extension, Extensions, extensions } from "@tiptap/core";
2
2
 
3
3
  import type { BlockNoteEditor } from "./BlockNoteEditor";
4
4
 
@@ -14,7 +14,6 @@ import * as Y from "yjs";
14
14
  import { createCopyToClipboardExtension } from "../api/exporters/copyExtension";
15
15
  import { createPasteFromClipboardExtension } from "../api/parsers/pasteExtension";
16
16
  import { BackgroundColorExtension } from "../extensions/BackgroundColor/BackgroundColorExtension";
17
- import { Placeholder } from "../extensions/Placeholder/PlaceholderExtension";
18
17
  import { TextAlignmentExtension } from "../extensions/TextAlignment/TextAlignmentExtension";
19
18
  import { TextColorExtension } from "../extensions/TextColor/TextColorExtension";
20
19
  import { TrailingNode } from "../extensions/TrailingNode/TrailingNodeExtension";
@@ -39,12 +38,12 @@ export const getBlockNoteExtensions = <
39
38
  S extends StyleSchema
40
39
  >(opts: {
41
40
  editor: BlockNoteEditor<BSchema, I, S>;
42
- placeholders?: Record<string | "default", string>;
43
41
  domAttributes: Partial<BlockNoteDOMAttributes>;
44
42
  blockSchema: BSchema;
45
43
  blockSpecs: BlockSpecs;
46
44
  inlineContentSpecs: InlineContentSpecs;
47
45
  styleSpecs: StyleSpecs;
46
+ trailingBlock: boolean | undefined;
48
47
  collaboration?: {
49
48
  fragment: Y.XmlFragment;
50
49
  user: {
@@ -66,12 +65,6 @@ export const getBlockNoteExtensions = <
66
65
  Gapcursor,
67
66
 
68
67
  // DropCursor,
69
- Placeholder.configure({
70
- // TODO: This shorthand is kind of ugly
71
- ...(opts.placeholders !== undefined
72
- ? { placeholders: opts.placeholders }
73
- : {}),
74
- }),
75
68
  UniqueID.configure({
76
69
  types: ["blockContainer"],
77
70
  }),
@@ -92,10 +85,26 @@ export const getBlockNoteExtensions = <
92
85
  BackgroundColorExtension,
93
86
  TextAlignmentExtension,
94
87
 
88
+ // make sure escape blurs editor, so that we can tab to other elements in the host page (accessibility)
89
+ Extension.create({
90
+ name: "OverrideEscape",
91
+ addKeyboardShortcuts() {
92
+ return {
93
+ Escape: () => {
94
+ if (opts.editor.suggestionMenus.shown) {
95
+ // escape is handled by suggestionmenu
96
+ return false;
97
+ }
98
+ return this.editor.commands.blur();
99
+ },
100
+ };
101
+ },
102
+ }),
103
+
95
104
  // nodes
96
105
  Doc,
97
106
  BlockContainer.configure({
98
- editor: opts.editor as any,
107
+ editor: opts.editor,
99
108
  domAttributes: opts.domAttributes,
100
109
  }),
101
110
  BlockGroup.configure({
@@ -131,7 +140,9 @@ export const getBlockNoteExtensions = <
131
140
  Dropcursor.configure({ width: 5, color: "#ddeeff" }),
132
141
  // This needs to be at the bottom of this list, because Key events (such as enter, when selecting a /command),
133
142
  // should be handled before Enter handlers in other components like splitListItem
134
- TrailingNode,
143
+ ...(opts.trailingBlock === undefined || opts.trailingBlock
144
+ ? [TrailingNode]
145
+ : []),
135
146
  ];
136
147
 
137
148
  if (opts.collaboration) {
@@ -126,12 +126,15 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
126
126
  private createViewAlternative() {
127
127
  // Without queueMicrotask, custom IC / styles will give a React FlushSync error
128
128
  queueMicrotask(() => {
129
- this.view = new EditorView(this.options.element, {
130
- ...this.options.editorProps,
131
- // @ts-ignore
132
- dispatchTransaction: this.dispatchTransaction.bind(this),
133
- state: this.state,
134
- });
129
+ this.view = new EditorView(
130
+ { mount: this.options.element as any }, // use mount option so that we reuse the existing element instead of creating a new one
131
+ {
132
+ ...this.options.editorProps,
133
+ // @ts-ignore
134
+ dispatchTransaction: this.dispatchTransaction.bind(this),
135
+ state: this.state,
136
+ }
137
+ );
135
138
 
136
139
  // `editor.view` is not yet available at this time.
137
140
  // Therefore we will add all plugins and node views directly afterwards.
@@ -1,5 +1,5 @@
1
1
  import { isNodeSelection, posToDOMRect } from "@tiptap/core";
2
- import { EditorState, Plugin, PluginKey } from "prosemirror-state";
2
+ import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
3
3
  import { EditorView } from "prosemirror-view";
4
4
 
5
5
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
@@ -9,7 +9,7 @@ import { EventEmitter } from "../../util/EventEmitter";
9
9
 
10
10
  export type FormattingToolbarState = UiElementPosition;
11
11
 
12
- export class FormattingToolbarView {
12
+ export class FormattingToolbarView implements PluginView {
13
13
  public state?: FormattingToolbarState;
14
14
  public emitUpdate: () => void;
15
15
 
@@ -48,9 +48,6 @@ export class FormattingToolbarView {
48
48
  pmView.dom.addEventListener("dragstart", this.dragHandler);
49
49
  pmView.dom.addEventListener("dragover", this.dragHandler);
50
50
 
51
- pmView.dom.addEventListener("focus", this.focusHandler);
52
- pmView.dom.addEventListener("blur", this.blurHandler);
53
-
54
51
  document.addEventListener("scroll", this.scrollHandler);
55
52
  }
56
53
 
@@ -71,39 +68,6 @@ export class FormattingToolbarView {
71
68
  }
72
69
  };
73
70
 
74
- focusHandler = () => {
75
- // we use `setTimeout` to make sure `selection` is already updated
76
- setTimeout(() => this.update(this.pmView));
77
- };
78
-
79
- blurHandler = (event: FocusEvent) => {
80
- if (this.preventHide) {
81
- this.preventHide = false;
82
-
83
- return;
84
- }
85
-
86
- const editorWrapper = this.pmView.dom.parentElement!;
87
-
88
- // Checks if the focus is moving to an element outside the editor. If it is,
89
- // the toolbar is hidden.
90
- if (
91
- // An element is clicked.
92
- event &&
93
- event.relatedTarget &&
94
- // Element is inside the editor.
95
- (editorWrapper === (event.relatedTarget as Node) ||
96
- editorWrapper.contains(event.relatedTarget as Node))
97
- ) {
98
- return;
99
- }
100
-
101
- if (this.state?.show) {
102
- this.state.show = false;
103
- this.emitUpdate();
104
- }
105
- };
106
-
107
71
  scrollHandler = () => {
108
72
  if (this.state?.show) {
109
73
  this.state.referencePos = this.getSelectionBoundingBox();
@@ -174,12 +138,16 @@ export class FormattingToolbarView {
174
138
  this.pmView.dom.removeEventListener("dragstart", this.dragHandler);
175
139
  this.pmView.dom.removeEventListener("dragover", this.dragHandler);
176
140
 
177
- this.pmView.dom.removeEventListener("focus", this.focusHandler);
178
- this.pmView.dom.removeEventListener("blur", this.blurHandler);
179
-
180
141
  document.removeEventListener("scroll", this.scrollHandler);
181
142
  }
182
143
 
144
+ closeMenu = () => {
145
+ if (this.state?.show) {
146
+ this.state.show = false;
147
+ this.emitUpdate();
148
+ }
149
+ };
150
+
183
151
  getSelectionBoundingBox() {
184
152
  const { state } = this.pmView;
185
153
  const { selection } = state;
@@ -219,10 +187,25 @@ export class FormattingToolbarProsemirrorPlugin extends EventEmitter<any> {
219
187
  });
220
188
  return this.view;
221
189
  },
190
+ props: {
191
+ handleKeyDown: (_view, event: KeyboardEvent) => {
192
+ if (event.key === "Escape" && this.shown) {
193
+ this.view!.closeMenu();
194
+ return true;
195
+ }
196
+ return false;
197
+ },
198
+ },
222
199
  });
223
200
  }
224
201
 
202
+ public get shown() {
203
+ return this.view?.state?.show || false;
204
+ }
205
+
225
206
  public onUpdate(callback: (state: FormattingToolbarState) => void) {
226
207
  return this.on("update", callback);
227
208
  }
209
+
210
+ public closeMenu = () => this.view!.closeMenu();
228
211
  }
@@ -1,15 +1,15 @@
1
- import { EditorState, Plugin, PluginKey } from "prosemirror-state";
1
+ import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
2
2
  import { EditorView } from "prosemirror-view";
3
3
 
4
+ import { DefaultBlockSchema } from "../../blocks/defaultBlocks";
4
5
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
6
+ import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
5
7
  import type {
6
8
  BlockFromConfig,
7
9
  InlineContentSchema,
8
10
  StyleSchema,
9
11
  } from "../../schema";
10
- import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
11
12
  import { EventEmitter } from "../../util/EventEmitter";
12
- import { DefaultBlockSchema } from "../../blocks/defaultBlocks";
13
13
 
14
14
  export type ImagePanelState<
15
15
  I extends InlineContentSchema,
@@ -22,7 +22,8 @@ export type ImagePanelState<
22
22
  export class ImagePanelView<
23
23
  I extends InlineContentSchema,
24
24
  S extends StyleSchema
25
- > {
25
+ > implements PluginView
26
+ {
26
27
  public state?: ImagePanelState<I, S>;
27
28
  public emitUpdate: () => void;
28
29
 
@@ -45,8 +46,6 @@ export class ImagePanelView<
45
46
 
46
47
  pmView.dom.addEventListener("dragstart", this.dragstartHandler);
47
48
 
48
- pmView.dom.addEventListener("blur", this.blurHandler);
49
-
50
49
  document.addEventListener("scroll", this.scrollHandler);
51
50
  }
52
51
 
@@ -65,28 +64,6 @@ export class ImagePanelView<
65
64
  }
66
65
  };
67
66
 
68
- blurHandler = (event: FocusEvent) => {
69
- const editorWrapper = this.pmView.dom.parentElement!;
70
-
71
- // Checks if the focus is moving to an element outside the editor. If it is,
72
- // the panel is hidden.
73
- if (
74
- // An element is clicked.
75
- event &&
76
- event.relatedTarget &&
77
- // Element is inside the editor.
78
- (editorWrapper === (event.relatedTarget as Node) ||
79
- editorWrapper.contains(event.relatedTarget as Node))
80
- ) {
81
- return;
82
- }
83
-
84
- if (this.state?.show) {
85
- this.state.show = false;
86
- this.emitUpdate();
87
- }
88
- };
89
-
90
67
  scrollHandler = () => {
91
68
  if (this.state?.show) {
92
69
  const blockElement = document.querySelector(
@@ -131,13 +108,18 @@ export class ImagePanelView<
131
108
  }
132
109
  }
133
110
 
111
+ closeMenu = () => {
112
+ if (this.state?.show) {
113
+ this.state.show = false;
114
+ this.emitUpdate();
115
+ }
116
+ };
117
+
134
118
  destroy() {
135
119
  this.pmView.dom.removeEventListener("mousedown", this.mouseDownHandler);
136
120
 
137
121
  this.pmView.dom.removeEventListener("dragstart", this.dragstartHandler);
138
122
 
139
- this.pmView.dom.removeEventListener("blur", this.blurHandler);
140
-
141
123
  document.removeEventListener("scroll", this.scrollHandler);
142
124
  }
143
125
  }
@@ -170,6 +152,15 @@ export class ImagePanelProsemirrorPlugin<
170
152
  );
171
153
  return this.view;
172
154
  },
155
+ props: {
156
+ handleKeyDown: (_view, event: KeyboardEvent) => {
157
+ if (event.key === "Escape" && this.shown) {
158
+ this.view!.closeMenu();
159
+ return true;
160
+ }
161
+ return false;
162
+ },
163
+ },
173
164
  state: {
174
165
  init: () => {
175
166
  return {
@@ -189,7 +180,13 @@ export class ImagePanelProsemirrorPlugin<
189
180
  });
190
181
  }
191
182
 
183
+ public get shown() {
184
+ return this.view?.state?.show || false;
185
+ }
186
+
192
187
  public onUpdate(callback: (state: ImagePanelState<I, S>) => void) {
193
188
  return this.on("update", callback);
194
189
  }
190
+
191
+ public closeMenu = () => this.view!.closeMenu();
195
192
  }
@@ -1,11 +1,11 @@
1
1
  import { getMarkRange, posToDOMRect, Range } from "@tiptap/core";
2
2
  import { EditorView } from "@tiptap/pm/view";
3
3
  import { Mark } from "prosemirror-model";
4
- import { Plugin, PluginKey } from "prosemirror-state";
4
+ import { Plugin, PluginKey, PluginView } from "prosemirror-state";
5
5
 
6
6
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
7
- import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
8
7
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
8
+ import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
9
9
  import { EventEmitter } from "../../util/EventEmitter";
10
10
 
11
11
  export type LinkToolbarState = UiElementPosition & {
@@ -15,7 +15,7 @@ export type LinkToolbarState = UiElementPosition & {
15
15
  text: string;
16
16
  };
17
17
 
18
- class LinkToolbarView {
18
+ class LinkToolbarView implements PluginView {
19
19
  public state?: LinkToolbarState;
20
20
  public emitUpdate: () => void;
21
21
 
@@ -258,6 +258,13 @@ class LinkToolbarView {
258
258
  }
259
259
  }
260
260
 
261
+ closeMenu = () => {
262
+ if (this.state?.show) {
263
+ this.state.show = false;
264
+ this.emitUpdate();
265
+ }
266
+ };
267
+
261
268
  destroy() {
262
269
  this.pmView.dom.removeEventListener("mouseover", this.mouseOverHandler);
263
270
  document.removeEventListener("scroll", this.scrollHandler);
@@ -285,6 +292,15 @@ export class LinkToolbarProsemirrorPlugin<
285
292
  });
286
293
  return this.view;
287
294
  },
295
+ props: {
296
+ handleKeyDown: (_view, event: KeyboardEvent) => {
297
+ if (event.key === "Escape" && this.shown) {
298
+ this.view!.closeMenu();
299
+ return true;
300
+ }
301
+ return false;
302
+ },
303
+ },
288
304
  });
289
305
  }
290
306
 
@@ -327,4 +343,10 @@ export class LinkToolbarProsemirrorPlugin<
327
343
  public stopHideTimer = () => {
328
344
  this.view!.stopMenuUpdateTimer();
329
345
  };
346
+
347
+ public get shown() {
348
+ return this.view?.state?.show || false;
349
+ }
350
+
351
+ public closeMenu = () => this.view!.closeMenu();
330
352
  }