@blocknote/core 0.11.2 → 0.12.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.
Files changed (138) hide show
  1. package/README.md +13 -17
  2. package/dist/blocknote.js +1662 -1447
  3. package/dist/blocknote.js.map +1 -1
  4. package/dist/blocknote.umd.cjs +6 -6
  5. package/dist/blocknote.umd.cjs.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/dist/webpack-stats.json +1 -1
  8. package/package.json +7 -3
  9. package/src/api/blockManipulation/blockManipulation.test.ts +19 -15
  10. package/src/api/blockManipulation/blockManipulation.ts +107 -17
  11. package/src/api/exporters/html/externalHTMLExporter.ts +3 -7
  12. package/src/api/exporters/html/htmlConversion.test.ts +6 -3
  13. package/src/api/exporters/html/internalHTMLSerializer.ts +3 -7
  14. package/src/api/exporters/html/util/sharedHTMLConversion.ts +3 -3
  15. package/src/api/exporters/markdown/markdownExporter.test.ts +7 -3
  16. package/src/api/exporters/markdown/markdownExporter.ts +2 -6
  17. package/src/api/getCurrentBlockContentType.ts +14 -0
  18. package/src/api/nodeConversions/nodeConversions.test.ts +14 -7
  19. package/src/api/nodeConversions/nodeConversions.ts +1 -2
  20. package/src/api/parsers/html/parseHTML.test.ts +5 -1
  21. package/src/api/parsers/html/parseHTML.ts +2 -6
  22. package/src/api/parsers/html/util/nestedLists.ts +11 -1
  23. package/src/api/parsers/markdown/parseMarkdown.test.ts +3 -0
  24. package/src/api/parsers/markdown/parseMarkdown.ts +2 -6
  25. package/src/api/testUtil/cases/customBlocks.ts +18 -16
  26. package/src/api/testUtil/cases/customInlineContent.ts +12 -13
  27. package/src/api/testUtil/cases/customStyles.ts +12 -10
  28. package/src/api/testUtil/index.ts +4 -2
  29. package/src/api/testUtil/partialBlockTestUtil.ts +2 -6
  30. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +50 -21
  31. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +1 -2
  32. package/src/blocks/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts +8 -1
  33. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +18 -5
  34. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +7 -1
  35. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +18 -5
  36. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +14 -5
  37. package/src/blocks/defaultBlockHelpers.ts +3 -3
  38. package/src/blocks/defaultBlockTypeGuards.ts +84 -0
  39. package/src/blocks/defaultBlocks.ts +29 -3
  40. package/src/editor/Block.css +2 -31
  41. package/src/editor/BlockNoteEditor.ts +223 -267
  42. package/src/editor/BlockNoteExtensions.ts +5 -2
  43. package/src/editor/BlockNoteSchema.ts +98 -0
  44. package/src/editor/BlockNoteTipTapEditor.ts +162 -0
  45. package/src/editor/cursorPositionTypes.ts +2 -6
  46. package/src/editor/editor.css +0 -1
  47. package/src/editor/selectionTypes.ts +2 -6
  48. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +22 -29
  49. package/src/extensions/{ImageToolbar → ImagePanel}/ImageToolbarPlugin.ts +54 -60
  50. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +330 -0
  51. package/src/extensions/Placeholder/PlaceholderExtension.ts +81 -88
  52. package/src/extensions/SideMenu/SideMenuPlugin.ts +55 -56
  53. package/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts +8 -0
  54. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +353 -0
  55. package/src/extensions/{SlashMenu/defaultSlashMenuItems.ts → SuggestionMenu/getDefaultSlashMenuItems.ts} +119 -89
  56. package/src/extensions/TableHandles/TableHandlesPlugin.ts +62 -45
  57. package/src/extensions-shared/UiElementPosition.ts +4 -0
  58. package/src/index.ts +8 -8
  59. package/src/pm-nodes/BlockContainer.ts +5 -5
  60. package/src/schema/blocks/types.ts +15 -15
  61. package/src/schema/inlineContent/createSpec.ts +2 -2
  62. package/src/schema/inlineContent/types.ts +1 -1
  63. package/src/util/browser.ts +6 -4
  64. package/src/util/typescript.ts +7 -4
  65. package/types/src/api/blockManipulation/blockManipulation.d.ts +6 -1
  66. package/types/src/api/exporters/html/externalHTMLExporter.d.ts +2 -1
  67. package/types/src/api/exporters/html/internalHTMLSerializer.d.ts +2 -1
  68. package/types/src/api/exporters/markdown/markdownExporter.d.ts +2 -1
  69. package/types/src/api/getCurrentBlockContentType.d.ts +2 -0
  70. package/types/src/api/nodeConversions/nodeConversions.d.ts +2 -1
  71. package/types/src/api/parsers/html/parseHTML.d.ts +2 -1
  72. package/types/src/api/parsers/markdown/parseMarkdown.d.ts +2 -1
  73. package/types/src/api/testUtil/cases/customBlocks.d.ts +72 -13
  74. package/types/src/api/testUtil/cases/customInlineContent.d.ts +281 -6
  75. package/types/src/api/testUtil/cases/customStyles.d.ts +247 -13
  76. package/types/src/api/testUtil/index.d.ts +4 -2
  77. package/types/src/api/testUtil/partialBlockTestUtil.d.ts +2 -1
  78. package/types/src/blocks/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +6 -1
  79. package/types/src/blocks/defaultBlockHelpers.d.ts +2 -2
  80. package/types/src/blocks/defaultBlockTypeGuards.d.ts +24 -0
  81. package/types/src/blocks/defaultBlocks.d.ts +21 -15
  82. package/types/src/editor/BlockNoteEditor.d.ts +51 -56
  83. package/types/src/editor/BlockNoteExtensions.d.ts +1 -0
  84. package/types/src/editor/BlockNoteSchema.d.ts +34 -0
  85. package/types/src/editor/BlockNoteTipTapEditor.d.ts +28 -0
  86. package/types/src/editor/cursorPositionTypes.d.ts +2 -1
  87. package/types/src/editor/selectionTypes.d.ts +2 -1
  88. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +5 -6
  89. package/types/src/extensions/ImagePanel/ImageToolbarPlugin.d.ts +32 -0
  90. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +40 -0
  91. package/types/src/extensions/Placeholder/PlaceholderExtension.d.ts +2 -15
  92. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +8 -7
  93. package/types/src/extensions/SuggestionMenu/DefaultSuggestionItem.d.ts +8 -0
  94. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +31 -0
  95. package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +10 -0
  96. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +7 -7
  97. package/types/src/extensions-shared/UiElementPosition.d.ts +4 -0
  98. package/types/src/index.d.ts +8 -8
  99. package/types/src/pm-nodes/BlockContainer.d.ts +3 -2
  100. package/types/src/pm-nodes/BlockGroup.d.ts +1 -1
  101. package/types/src/schema/blocks/types.d.ts +15 -15
  102. package/types/src/schema/inlineContent/types.d.ts +1 -1
  103. package/types/src/util/browser.d.ts +1 -0
  104. package/types/src/util/typescript.d.ts +1 -0
  105. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +0 -335
  106. package/src/extensions/SlashMenu/BaseSlashMenuItem.ts +0 -12
  107. package/src/extensions/SlashMenu/SlashMenuPlugin.ts +0 -53
  108. package/src/extensions-shared/BaseUiElementTypes.ts +0 -8
  109. package/src/extensions-shared/README.md +0 -3
  110. package/src/extensions-shared/suggestion/SuggestionItem.ts +0 -3
  111. package/src/extensions-shared/suggestion/SuggestionPlugin.ts +0 -448
  112. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +0 -38
  113. package/types/src/extensions/ImageToolbar/ImageToolbarPlugin.d.ts +0 -31
  114. package/types/src/extensions/SlashMenu/BaseSlashMenuItem.d.ts +0 -7
  115. package/types/src/extensions/SlashMenu/SlashMenuPlugin.d.ts +0 -13
  116. package/types/src/extensions/SlashMenu/defaultSlashMenuItems.d.ts +0 -3
  117. package/types/src/extensions-shared/BaseUiElementTypes.d.ts +0 -7
  118. package/types/src/extensions-shared/suggestion/SuggestionItem.d.ts +0 -3
  119. package/types/src/extensions-shared/suggestion/SuggestionPlugin.d.ts +0 -36
  120. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-100.woff +0 -0
  121. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-100.woff2 +0 -0
  122. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-200.woff +0 -0
  123. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-200.woff2 +0 -0
  124. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-300.woff +0 -0
  125. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-300.woff2 +0 -0
  126. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-500.woff +0 -0
  127. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-500.woff2 +0 -0
  128. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-600.woff +0 -0
  129. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-600.woff2 +0 -0
  130. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-700.woff +0 -0
  131. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-700.woff2 +0 -0
  132. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-800.woff +0 -0
  133. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-800.woff2 +0 -0
  134. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-900.woff +0 -0
  135. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-900.woff2 +0 -0
  136. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-regular.woff +0 -0
  137. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-regular.woff2 +0 -0
  138. /package/src/{assets/fonts-inter.css → fonts/inter.css} +0 -0
@@ -39,6 +39,7 @@ export const getBlockNoteExtensions = <
39
39
  S extends StyleSchema
40
40
  >(opts: {
41
41
  editor: BlockNoteEditor<BSchema, I, S>;
42
+ placeholders?: Record<string | "default", string>;
42
43
  domAttributes: Partial<BlockNoteDOMAttributes>;
43
44
  blockSchema: BSchema;
44
45
  blockSpecs: BlockSpecs;
@@ -66,8 +67,10 @@ export const getBlockNoteExtensions = <
66
67
 
67
68
  // DropCursor,
68
69
  Placeholder.configure({
69
- includeChildren: true,
70
- showOnlyCurrent: false,
70
+ // TODO: This shorthand is kind of ugly
71
+ ...(opts.placeholders !== undefined
72
+ ? { placeholders: opts.placeholders }
73
+ : {}),
71
74
  }),
72
75
  UniqueID.configure({
73
76
  types: ["blockContainer"],
@@ -0,0 +1,98 @@
1
+ import {
2
+ defaultBlockSpecs,
3
+ defaultInlineContentSpecs,
4
+ defaultStyleSpecs,
5
+ } from "../blocks/defaultBlocks";
6
+ import {
7
+ BlockSchema,
8
+ BlockSchemaFromSpecs,
9
+ BlockSpecs,
10
+ InlineContentSchema,
11
+ InlineContentSchemaFromSpecs,
12
+ InlineContentSpecs,
13
+ StyleSchema,
14
+ StyleSchemaFromSpecs,
15
+ StyleSpecs,
16
+ getBlockSchemaFromSpecs,
17
+ getInlineContentSchemaFromSpecs,
18
+ getStyleSchemaFromSpecs,
19
+ } from "../schema";
20
+ import type {
21
+ BlockNoDefaults,
22
+ PartialBlockNoDefaults,
23
+ } from "../schema/blocks/types";
24
+ import type { BlockNoteEditor } from "./BlockNoteEditor";
25
+
26
+ export class BlockNoteSchema<
27
+ BSchema extends BlockSchema,
28
+ ISchema extends InlineContentSchema,
29
+ SSchema extends StyleSchema
30
+ > {
31
+ public readonly blockSpecs: BlockSpecs;
32
+ public readonly inlineContentSpecs: InlineContentSpecs;
33
+ public readonly styleSpecs: StyleSpecs;
34
+
35
+ public readonly blockSchema: BSchema;
36
+ public readonly inlineContentSchema: ISchema;
37
+ public readonly styleSchema: SSchema;
38
+
39
+ // Helper so that you can use typeof schema.BlockNoteEditor
40
+ public readonly BlockNoteEditor: BlockNoteEditor<BSchema, ISchema, SSchema> =
41
+ "only for types" as any;
42
+
43
+ public readonly Block: BlockNoDefaults<BSchema, ISchema, SSchema> =
44
+ "only for types" as any;
45
+
46
+ public readonly PartialBlock: PartialBlockNoDefaults<
47
+ BSchema,
48
+ ISchema,
49
+ SSchema
50
+ > = "only for types" as any;
51
+
52
+ public static create<
53
+ BSpecs extends BlockSpecs = typeof defaultBlockSpecs,
54
+ ISpecs extends InlineContentSpecs = typeof defaultInlineContentSpecs,
55
+ SSpecs extends StyleSpecs = typeof defaultStyleSpecs
56
+ >(options?: {
57
+ /**
58
+ * A list of custom block types that should be available in the editor.
59
+ */
60
+ blockSpecs?: BSpecs;
61
+ /**
62
+ * A list of custom InlineContent types that should be available in the editor.
63
+ */
64
+ inlineContentSpecs?: ISpecs;
65
+ /**
66
+ * A list of custom Styles that should be available in the editor.
67
+ */
68
+ styleSpecs?: SSpecs;
69
+ }) {
70
+ return new BlockNoteSchema<
71
+ BlockSchemaFromSpecs<BSpecs>,
72
+ InlineContentSchemaFromSpecs<ISpecs>,
73
+ StyleSchemaFromSpecs<SSpecs>
74
+ >(options);
75
+ // as BlockNoteSchema<
76
+ // BlockSchemaFromSpecs<BSpecs>,
77
+ // InlineContentSchemaFromSpecs<ISpecs>,
78
+ // StyleSchemaFromSpecs<SSpecs>
79
+ // >;
80
+ }
81
+
82
+ constructor(opts?: {
83
+ blockSpecs?: BlockSpecs;
84
+ inlineContentSpecs?: InlineContentSpecs;
85
+ styleSpecs?: StyleSpecs;
86
+ }) {
87
+ this.blockSpecs = opts?.blockSpecs || defaultBlockSpecs;
88
+ this.inlineContentSpecs =
89
+ opts?.inlineContentSpecs || defaultInlineContentSpecs;
90
+ this.styleSpecs = opts?.styleSpecs || defaultStyleSpecs;
91
+
92
+ this.blockSchema = getBlockSchemaFromSpecs(this.blockSpecs) as any;
93
+ this.inlineContentSchema = getInlineContentSchemaFromSpecs(
94
+ this.inlineContentSpecs
95
+ ) as any;
96
+ this.styleSchema = getStyleSchemaFromSpecs(this.styleSpecs) as any;
97
+ }
98
+ }
@@ -0,0 +1,162 @@
1
+ import { EditorOptions, createDocument } from "@tiptap/core";
2
+ // import "./blocknote.css";
3
+ import { Editor as TiptapEditor } from "@tiptap/core";
4
+ import { Node } from "@tiptap/pm/model";
5
+ import { EditorView } from "@tiptap/pm/view";
6
+ import { EditorState } from "prosemirror-state";
7
+
8
+ import { blockToNode } from "../api/nodeConversions/nodeConversions";
9
+ import { PartialBlock } from "../blocks/defaultBlocks";
10
+ import { StyleSchema } from "../schema";
11
+
12
+ export type BlockNoteTipTapEditorOptions = Partial<
13
+ Omit<EditorOptions, "content">
14
+ > & {
15
+ content: PartialBlock<any, any, any>[];
16
+ };
17
+
18
+ /**
19
+ * Custom Editor class that extends TiptapEditor and separates
20
+ * the creation of the view from the constructor.
21
+ */
22
+ // @ts-ignore
23
+ export class BlockNoteTipTapEditor extends TiptapEditor {
24
+ private _state: EditorState;
25
+
26
+ constructor(options: BlockNoteTipTapEditorOptions, styleSchema: StyleSchema) {
27
+ // possible fix for next.js server side rendering
28
+ // const d = globalThis.document;
29
+ // const w = globalThis.window;
30
+ // if (!globalThis.document) {
31
+ // globalThis.document = {
32
+ // createElement: () => {},
33
+ // };
34
+ // }
35
+ // if (!globalThis.window) {
36
+ // globalThis.window = {
37
+ // setTimeout: () => {},
38
+ // };
39
+ // }
40
+ // options.injectCSS = false
41
+ super({ ...options, content: undefined });
42
+
43
+ // try {
44
+ // globalThis.window = w;
45
+ // } catch(e) {}
46
+ // try {
47
+ // globalThis.document = d;
48
+ // } catch(e) {}
49
+
50
+ // This is a hack to make "initial content detection" by y-prosemirror (and also tiptap isEmpty)
51
+ // properly detect whether or not the document has changed.
52
+ // We change the doc.createAndFill function to make sure the initial block id is set, instead of null
53
+ const schema = this.schema;
54
+ let cache: any;
55
+ const oldCreateAndFill = schema.nodes.doc.createAndFill;
56
+ (schema.nodes.doc as any).createAndFill = (...args: any) => {
57
+ if (cache) {
58
+ return cache;
59
+ }
60
+ const ret = oldCreateAndFill.apply(schema.nodes.doc, args);
61
+
62
+ // create a copy that we can mutate (otherwise, assigning attrs is not safe and corrupts the pm state)
63
+ const jsonNode = JSON.parse(JSON.stringify(ret!.toJSON()));
64
+ jsonNode.content[0].content[0].attrs.id = "initialBlockId";
65
+
66
+ cache = Node.fromJSON(schema, jsonNode);
67
+ return cache;
68
+ };
69
+
70
+ let doc: Node;
71
+
72
+ try {
73
+ const pmNodes = options?.content.map((b) =>
74
+ blockToNode(b, this.schema, styleSchema).toJSON()
75
+ );
76
+ doc = createDocument(
77
+ {
78
+ type: "doc",
79
+ content: [
80
+ {
81
+ type: "blockGroup",
82
+ content: pmNodes,
83
+ },
84
+ ],
85
+ },
86
+ this.schema,
87
+ this.options.parseOptions
88
+ );
89
+ } catch (e) {
90
+ console.error(
91
+ "Error creating document from blocks passed as `initialContent`. Caused by exception: ",
92
+ e
93
+ );
94
+ throw new Error(
95
+ "Error creating document from blocks passed as `initialContent`:\n" +
96
+ +JSON.stringify(options.content)
97
+ );
98
+ }
99
+
100
+ // Create state immediately, so that it's available independently from the View,
101
+ // the way Prosemirror "intends it to be". This also makes sure that we can access
102
+ // the state before the view is created / mounted.
103
+ this._state = EditorState.create({
104
+ doc,
105
+ schema: this.schema,
106
+ // selection: selection || undefined,
107
+ });
108
+ }
109
+
110
+ get state() {
111
+ if (this.view) {
112
+ this._state = this.view.state;
113
+ }
114
+ return this._state;
115
+ }
116
+
117
+ createView() {
118
+ // no-op
119
+ // Disable default call to `createView` in the Editor constructor.
120
+ // We should call `createView` manually only when a DOM element is available
121
+ }
122
+
123
+ /**
124
+ * Replace the default `createView` method with a custom one - which we call on mount
125
+ */
126
+ private createViewAlternative() {
127
+ // Without queueMicrotask, custom IC / styles will give a React FlushSync error
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
+ });
135
+
136
+ // `editor.view` is not yet available at this time.
137
+ // Therefore we will add all plugins and node views directly afterwards.
138
+ const newState = this.state.reconfigure({
139
+ plugins: this.extensionManager.plugins,
140
+ });
141
+
142
+ this.view.updateState(newState);
143
+
144
+ this.createNodeViews();
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Mounts / unmounts the editor to a dom element
150
+ *
151
+ * @param element DOM element to mount to, ur null / undefined to destroy
152
+ */
153
+ public mount = (element?: HTMLElement | null) => {
154
+ if (!element) {
155
+ this.destroy();
156
+ } else {
157
+ this.options.element = element;
158
+ // @ts-ignore
159
+ this.createViewAlternative();
160
+ }
161
+ };
162
+ }
@@ -1,9 +1,5 @@
1
- import {
2
- Block,
3
- BlockSchema,
4
- InlineContentSchema,
5
- StyleSchema,
6
- } from "../schema";
1
+ import { Block } from "../blocks/defaultBlocks";
2
+ import { BlockSchema, InlineContentSchema, StyleSchema } from "../schema";
7
3
 
8
4
  export type TextCursorPosition<
9
5
  BSchema extends BlockSchema,
@@ -1,4 +1,3 @@
1
- @import url("../assets/fonts-inter.css");
2
1
  @import url("prosemirror-tables/style/tables.css");
3
2
 
4
3
  .bn-editor {
@@ -1,9 +1,5 @@
1
- import {
2
- Block,
3
- BlockSchema,
4
- InlineContentSchema,
5
- StyleSchema,
6
- } from "../schema";
1
+ import { Block } from "../blocks/defaultBlocks";
2
+ import { BlockSchema, InlineContentSchema, StyleSchema } from "../schema";
7
3
 
8
4
  export type Selection<
9
5
  BSchema extends BlockSchema,
@@ -3,20 +3,15 @@ import { EditorState, Plugin, PluginKey } from "prosemirror-state";
3
3
  import { EditorView } from "prosemirror-view";
4
4
 
5
5
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
6
- import {
7
- BaseUiElementCallbacks,
8
- BaseUiElementState,
9
- } from "../../extensions-shared/BaseUiElementTypes";
6
+ import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
10
7
  import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
11
8
  import { EventEmitter } from "../../util/EventEmitter";
12
9
 
13
- export type FormattingToolbarCallbacks = BaseUiElementCallbacks;
14
-
15
- export type FormattingToolbarState = BaseUiElementState;
10
+ export type FormattingToolbarState = UiElementPosition;
16
11
 
17
12
  export class FormattingToolbarView {
18
- private formattingToolbarState?: FormattingToolbarState;
19
- public updateFormattingToolbar: () => void;
13
+ public state?: FormattingToolbarState;
14
+ public emitUpdate: () => void;
20
15
 
21
16
  public preventHide = false;
22
17
  public preventShow = false;
@@ -36,18 +31,16 @@ export class FormattingToolbarView {
36
31
  StyleSchema
37
32
  >,
38
33
  private readonly pmView: EditorView,
39
- updateFormattingToolbar: (
40
- formattingToolbarState: FormattingToolbarState
41
- ) => void
34
+ emitUpdate: (state: FormattingToolbarState) => void
42
35
  ) {
43
- this.updateFormattingToolbar = () => {
44
- if (!this.formattingToolbarState) {
36
+ this.emitUpdate = () => {
37
+ if (!this.state) {
45
38
  throw new Error(
46
39
  "Attempting to update uninitialized formatting toolbar"
47
40
  );
48
41
  }
49
42
 
50
- updateFormattingToolbar(this.formattingToolbarState);
43
+ emitUpdate(this.state);
51
44
  };
52
45
 
53
46
  pmView.dom.addEventListener("mousedown", this.viewMousedownHandler);
@@ -72,9 +65,9 @@ export class FormattingToolbarView {
72
65
 
73
66
  // For dragging the whole editor.
74
67
  dragHandler = () => {
75
- if (this.formattingToolbarState?.show) {
76
- this.formattingToolbarState.show = false;
77
- this.updateFormattingToolbar();
68
+ if (this.state?.show) {
69
+ this.state.show = false;
70
+ this.emitUpdate();
78
71
  }
79
72
  };
80
73
 
@@ -105,16 +98,16 @@ export class FormattingToolbarView {
105
98
  return;
106
99
  }
107
100
 
108
- if (this.formattingToolbarState?.show) {
109
- this.formattingToolbarState.show = false;
110
- this.updateFormattingToolbar();
101
+ if (this.state?.show) {
102
+ this.state.show = false;
103
+ this.emitUpdate();
111
104
  }
112
105
  };
113
106
 
114
107
  scrollHandler = () => {
115
- if (this.formattingToolbarState?.show) {
116
- this.formattingToolbarState.referencePos = this.getSelectionBoundingBox();
117
- this.updateFormattingToolbar();
108
+ if (this.state?.show) {
109
+ this.state.referencePos = this.getSelectionBoundingBox();
110
+ this.emitUpdate();
118
111
  }
119
112
  };
120
113
 
@@ -152,24 +145,24 @@ export class FormattingToolbarView {
152
145
  !this.preventShow &&
153
146
  (shouldShow || this.preventHide)
154
147
  ) {
155
- this.formattingToolbarState = {
148
+ this.state = {
156
149
  show: true,
157
150
  referencePos: this.getSelectionBoundingBox(),
158
151
  };
159
152
 
160
- this.updateFormattingToolbar();
153
+ this.emitUpdate();
161
154
 
162
155
  return;
163
156
  }
164
157
 
165
158
  // Checks if menu should be hidden.
166
159
  if (
167
- this.formattingToolbarState?.show &&
160
+ this.state?.show &&
168
161
  !this.preventHide &&
169
162
  (!shouldShow || this.preventShow || !this.editor.isEditable)
170
163
  ) {
171
- this.formattingToolbarState.show = false;
172
- this.updateFormattingToolbar();
164
+ this.state.show = false;
165
+ this.emitUpdate();
173
166
 
174
167
  return;
175
168
  }
@@ -1,51 +1,44 @@
1
1
  import { EditorState, Plugin, PluginKey } from "prosemirror-state";
2
2
  import { EditorView } from "prosemirror-view";
3
3
 
4
- import { EventEmitter } from "../../util/EventEmitter";
5
4
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
6
- import {
7
- BlockSchema,
5
+ import type {
6
+ BlockFromConfig,
8
7
  InlineContentSchema,
9
- SpecificBlock,
10
8
  StyleSchema,
11
9
  } from "../../schema";
12
- import {
13
- BaseUiElementCallbacks,
14
- BaseUiElementState,
15
- } from "../../extensions-shared/BaseUiElementTypes";
16
- export type ImageToolbarCallbacks = BaseUiElementCallbacks;
17
-
18
- export type ImageToolbarState<
19
- B extends BlockSchema,
10
+ import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
11
+ import { EventEmitter } from "../../util/EventEmitter";
12
+ import { DefaultBlockSchema } from "../../blocks/defaultBlocks";
13
+
14
+ export type ImagePanelState<
20
15
  I extends InlineContentSchema,
21
- S extends StyleSchema = StyleSchema
22
- > = BaseUiElementState & {
23
- block: SpecificBlock<B, "image", I, S>;
16
+ S extends StyleSchema
17
+ > = UiElementPosition & {
18
+ // TODO: This typing is not quite right (children should be from BSchema)
19
+ block: BlockFromConfig<DefaultBlockSchema["image"], I, S>;
24
20
  };
25
21
 
26
- export class ImageToolbarView<
27
- BSchema extends BlockSchema,
22
+ export class ImagePanelView<
28
23
  I extends InlineContentSchema,
29
24
  S extends StyleSchema
30
25
  > {
31
- private imageToolbarState?: ImageToolbarState<BSchema, I, S>;
32
- public updateImageToolbar: () => void;
26
+ public state?: ImagePanelState<I, S>;
27
+ public emitUpdate: () => void;
33
28
 
34
29
  public prevWasEditable: boolean | null = null;
35
30
 
36
31
  constructor(
37
32
  private readonly pluginKey: PluginKey,
38
33
  private readonly pmView: EditorView,
39
- updateImageToolbar: (
40
- imageToolbarState: ImageToolbarState<BSchema, I, S>
41
- ) => void
34
+ emitUpdate: (state: ImagePanelState<I, S>) => void
42
35
  ) {
43
- this.updateImageToolbar = () => {
44
- if (!this.imageToolbarState) {
45
- throw new Error("Attempting to update uninitialized image toolbar");
36
+ this.emitUpdate = () => {
37
+ if (!this.state) {
38
+ throw new Error("Attempting to update uninitialized image panel");
46
39
  }
47
40
 
48
- updateImageToolbar(this.imageToolbarState);
41
+ emitUpdate(this.state);
49
42
  };
50
43
 
51
44
  pmView.dom.addEventListener("mousedown", this.mouseDownHandler);
@@ -58,17 +51,17 @@ export class ImageToolbarView<
58
51
  }
59
52
 
60
53
  mouseDownHandler = () => {
61
- if (this.imageToolbarState?.show) {
62
- this.imageToolbarState.show = false;
63
- this.updateImageToolbar();
54
+ if (this.state?.show) {
55
+ this.state.show = false;
56
+ this.emitUpdate();
64
57
  }
65
58
  };
66
59
 
67
60
  // For dragging the whole editor.
68
61
  dragstartHandler = () => {
69
- if (this.imageToolbarState?.show) {
70
- this.imageToolbarState.show = false;
71
- this.updateImageToolbar();
62
+ if (this.state?.show) {
63
+ this.state.show = false;
64
+ this.emitUpdate();
72
65
  }
73
66
  };
74
67
 
@@ -76,7 +69,7 @@ export class ImageToolbarView<
76
69
  const editorWrapper = this.pmView.dom.parentElement!;
77
70
 
78
71
  // Checks if the focus is moving to an element outside the editor. If it is,
79
- // the toolbar is hidden.
72
+ // the panel is hidden.
80
73
  if (
81
74
  // An element is clicked.
82
75
  event &&
@@ -88,41 +81,40 @@ export class ImageToolbarView<
88
81
  return;
89
82
  }
90
83
 
91
- if (this.imageToolbarState?.show) {
92
- this.imageToolbarState.show = false;
93
- this.updateImageToolbar();
84
+ if (this.state?.show) {
85
+ this.state.show = false;
86
+ this.emitUpdate();
94
87
  }
95
88
  };
96
89
 
97
90
  scrollHandler = () => {
98
- if (this.imageToolbarState?.show) {
91
+ if (this.state?.show) {
99
92
  const blockElement = document.querySelector(
100
- `[data-node-type="blockContainer"][data-id="${this.imageToolbarState.block.id}"]`
93
+ `[data-node-type="blockContainer"][data-id="${this.state.block.id}"]`
101
94
  )!;
102
95
 
103
- this.imageToolbarState.referencePos =
104
- blockElement.getBoundingClientRect();
105
- this.updateImageToolbar();
96
+ this.state.referencePos = blockElement.getBoundingClientRect();
97
+ this.emitUpdate();
106
98
  }
107
99
  };
108
100
 
109
101
  update(view: EditorView, prevState: EditorState) {
110
102
  const pluginState: {
111
- block: SpecificBlock<BSchema, "image", I, S>;
103
+ block: BlockFromConfig<DefaultBlockSchema["image"], I, S>;
112
104
  } = this.pluginKey.getState(view.state);
113
105
 
114
- if (!this.imageToolbarState?.show && pluginState.block) {
106
+ if (!this.state?.show && pluginState.block) {
115
107
  const blockElement = document.querySelector(
116
108
  `[data-node-type="blockContainer"][data-id="${pluginState.block.id}"]`
117
109
  )!;
118
110
 
119
- this.imageToolbarState = {
111
+ this.state = {
120
112
  show: true,
121
113
  referencePos: blockElement.getBoundingClientRect(),
122
114
  block: pluginState.block,
123
115
  };
124
116
 
125
- this.updateImageToolbar();
117
+ this.emitUpdate();
126
118
 
127
119
  return;
128
120
  }
@@ -131,10 +123,10 @@ export class ImageToolbarView<
131
123
  !view.state.selection.eq(prevState.selection) ||
132
124
  !view.state.doc.eq(prevState.doc)
133
125
  ) {
134
- if (this.imageToolbarState?.show) {
135
- this.imageToolbarState.show = false;
126
+ if (this.state?.show) {
127
+ this.state.show = false;
136
128
 
137
- this.updateImageToolbar();
129
+ this.emitUpdate();
138
130
  }
139
131
  }
140
132
  }
@@ -150,26 +142,27 @@ export class ImageToolbarView<
150
142
  }
151
143
  }
152
144
 
153
- export const imageToolbarPluginKey = new PluginKey("ImageToolbarPlugin");
145
+ const imagePanelPluginKey = new PluginKey("ImagePanelPlugin");
154
146
 
155
- export class ImageToolbarProsemirrorPlugin<
156
- BSchema extends BlockSchema,
147
+ export class ImagePanelProsemirrorPlugin<
157
148
  I extends InlineContentSchema,
158
149
  S extends StyleSchema
159
150
  > extends EventEmitter<any> {
160
- private view: ImageToolbarView<BSchema, I, S> | undefined;
151
+ private view: ImagePanelView<I, S> | undefined;
161
152
  public readonly plugin: Plugin;
162
153
 
163
- constructor(_editor: BlockNoteEditor<BSchema, I, S>) {
154
+ constructor(
155
+ _editor: BlockNoteEditor<{ image: DefaultBlockSchema["image"] }, I, S>
156
+ ) {
164
157
  super();
165
158
  this.plugin = new Plugin<{
166
- block: SpecificBlock<BSchema, "image", I, S> | undefined;
159
+ block: BlockFromConfig<DefaultBlockSchema["image"], I, S> | undefined;
167
160
  }>({
168
- key: imageToolbarPluginKey,
161
+ key: imagePanelPluginKey,
169
162
  view: (editorView) => {
170
- this.view = new ImageToolbarView(
163
+ this.view = new ImagePanelView(
171
164
  // editor,
172
- imageToolbarPluginKey,
165
+ imagePanelPluginKey,
173
166
  editorView,
174
167
  (state) => {
175
168
  this.emit("update", state);
@@ -184,8 +177,9 @@ export class ImageToolbarProsemirrorPlugin<
184
177
  };
185
178
  },
186
179
  apply: (transaction) => {
187
- const block: SpecificBlock<BSchema, "image", I, S> | undefined =
188
- transaction.getMeta(imageToolbarPluginKey)?.block;
180
+ const block:
181
+ | BlockFromConfig<DefaultBlockSchema["image"], I, S>
182
+ | undefined = transaction.getMeta(imagePanelPluginKey)?.block;
189
183
 
190
184
  return {
191
185
  block,
@@ -195,7 +189,7 @@ export class ImageToolbarProsemirrorPlugin<
195
189
  });
196
190
  }
197
191
 
198
- public onUpdate(callback: (state: ImageToolbarState<BSchema, I, S>) => void) {
192
+ public onUpdate(callback: (state: ImagePanelState<I, S>) => void) {
199
193
  return this.on("update", callback);
200
194
  }
201
195
  }