@blocknote/core 0.8.5 → 0.9.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 (36) hide show
  1. package/dist/blocknote.js +614 -520
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +4 -4
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +2 -2
  7. package/src/BlockNoteEditor.ts +11 -15
  8. package/src/BlockNoteExtensions.ts +18 -5
  9. package/src/editor.module.css +0 -11
  10. package/src/extensions/Blocks/api/block.ts +55 -22
  11. package/src/extensions/Blocks/api/blockTypes.ts +22 -3
  12. package/src/extensions/Blocks/index.ts +7 -12
  13. package/src/extensions/Blocks/nodes/Block.module.css +1 -1
  14. package/src/extensions/Blocks/nodes/BlockContainer.ts +19 -18
  15. package/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.ts +20 -2
  16. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +20 -2
  17. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +20 -2
  18. package/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts +29 -6
  19. package/src/extensions/Blocks/nodes/BlockGroup.ts +19 -11
  20. package/src/index.ts +1 -0
  21. package/src/shared/utils.ts +4 -0
  22. package/types/src/BlockNoteEditor.d.ts +4 -10
  23. package/types/src/BlockNoteExtensions.d.ts +2 -0
  24. package/types/src/extensions/Blocks/api/block.d.ts +6 -1
  25. package/types/src/extensions/Blocks/api/blockTypes.d.ts +15 -3
  26. package/types/src/extensions/Blocks/api/defaultBlocks.d.ts +36 -4
  27. package/types/src/extensions/Blocks/index.d.ts +4 -1
  28. package/types/src/extensions/Blocks/nodes/BlockContainer.d.ts +9 -4
  29. package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +9 -1
  30. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +9 -1
  31. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +9 -1
  32. package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +9 -1
  33. package/types/src/extensions/Blocks/nodes/BlockGroup.d.ts +9 -1
  34. package/types/src/index.d.ts +1 -0
  35. package/types/src/shared/utils.d.ts +1 -0
  36. package/src/node_modules/.vitest/results.json +0 -1
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.8.5",
6
+ "version": "0.9.0",
7
7
  "files": [
8
8
  "dist",
9
9
  "types",
@@ -109,5 +109,5 @@
109
109
  "access": "public",
110
110
  "registry": "https://registry.npmjs.org/"
111
111
  },
112
- "gitHead": "5bdbc6a60d4142a6a6a5d85cc8f8a74fdbecf78f"
112
+ "gitHead": "75d9591eef6f06f78bdd8dadc32d0ee66c57c4d2"
113
113
  }
@@ -11,9 +11,9 @@ import {
11
11
  updateBlock,
12
12
  } from "./api/blockManipulation/blockManipulation";
13
13
  import {
14
- HTMLToBlocks,
15
14
  blocksToHTML,
16
15
  blocksToMarkdown,
16
+ HTMLToBlocks,
17
17
  markdownToBlocks,
18
18
  } from "./api/formatConversions/formatConversions";
19
19
  import {
@@ -25,6 +25,7 @@ import styles from "./editor.module.css";
25
25
  import {
26
26
  Block,
27
27
  BlockIdentifier,
28
+ BlockNoteDOMAttributes,
28
29
  BlockSchema,
29
30
  PartialBlock,
30
31
  } from "./extensions/Blocks/api/blockTypes";
@@ -48,6 +49,7 @@ import { BaseSlashMenuItem } from "./extensions/SlashMenu/BaseSlashMenuItem";
48
49
  import { SlashMenuProsemirrorPlugin } from "./extensions/SlashMenu/SlashMenuPlugin";
49
50
  import { getDefaultSlashMenuItems } from "./extensions/SlashMenu/defaultSlashMenuItems";
50
51
  import { UniqueID } from "./extensions/UniqueID/UniqueID";
52
+ import { mergeCSSClasses } from "./shared/utils";
51
53
 
52
54
  export type BlockNoteEditorOptions<BSchema extends BlockSchema> = {
53
55
  // TODO: Figure out if enableBlockNoteExtensions/disableHistoryExtension are needed and document them.
@@ -67,11 +69,11 @@ export type BlockNoteEditorOptions<BSchema extends BlockSchema> = {
67
69
  */
68
70
  parentElement: HTMLElement;
69
71
  /**
70
- * An object containing attributes that should be added to the editor's HTML element.
72
+ * An object containing attributes that should be added to HTML elements of the editor.
71
73
  *
72
- * @example { class: "my-editor-class" }
74
+ * @example { editor: { class: "my-editor-class" } }
73
75
  */
74
- editorDOMAttributes: Record<string, string>;
76
+ domAttributes: Partial<BlockNoteDOMAttributes>;
75
77
  /**
76
78
  * A callback function that runs when the editor is ready to be used.
77
79
  */
@@ -98,12 +100,6 @@ export type BlockNoteEditorOptions<BSchema extends BlockSchema> = {
98
100
  * @default true
99
101
  */
100
102
  defaultStyles: boolean;
101
- /**
102
- * Whether to use the light or dark theme.
103
- *
104
- * @default "light"
105
- */
106
- theme: "light" | "dark";
107
103
 
108
104
  /**
109
105
  * A list of block types that should be available in the editor.
@@ -185,6 +181,7 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
185
181
 
186
182
  const extensions = getBlockNoteExtensions<BSchema>({
187
183
  editor: this,
184
+ domAttributes: newOptions.domAttributes || {},
188
185
  blockSchema: newOptions.blockSchema,
189
186
  collaboration: newOptions.collaboration,
190
187
  });
@@ -266,14 +263,13 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
266
263
  : [...(newOptions._tiptapOptions?.extensions || []), ...extensions],
267
264
  editorProps: {
268
265
  attributes: {
269
- "data-theme": options.theme || "light",
270
- ...(newOptions.editorDOMAttributes || {}),
271
- class: [
266
+ ...newOptions.domAttributes?.editor,
267
+ class: mergeCSSClasses(
272
268
  styles.bnEditor,
273
269
  styles.bnRoot,
274
270
  newOptions.defaultStyles ? styles.defaultStyles : "",
275
- newOptions.editorDOMAttributes?.class || "",
276
- ].join(" "),
271
+ newOptions.domAttributes?.editor?.class || ""
272
+ ),
277
273
  },
278
274
  },
279
275
  };
@@ -19,8 +19,11 @@ import * as Y from "yjs";
19
19
  import styles from "./editor.module.css";
20
20
  import { BackgroundColorExtension } from "./extensions/BackgroundColor/BackgroundColorExtension";
21
21
  import { BackgroundColorMark } from "./extensions/BackgroundColor/BackgroundColorMark";
22
- import { blocks } from "./extensions/Blocks";
23
- import { BlockSchema } from "./extensions/Blocks/api/blockTypes";
22
+ import { BlockContainer, BlockGroup, Doc } from "./extensions/Blocks";
23
+ import {
24
+ BlockNoteDOMAttributes,
25
+ BlockSchema,
26
+ } from "./extensions/Blocks/api/blockTypes";
24
27
  import { CustomBlockSerializerExtension } from "./extensions/Blocks/api/serialization";
25
28
  import blockStyles from "./extensions/Blocks/nodes/Block.module.css";
26
29
  import { Placeholder } from "./extensions/Placeholder/PlaceholderExtension";
@@ -35,6 +38,7 @@ import UniqueID from "./extensions/UniqueID/UniqueID";
35
38
  */
36
39
  export const getBlockNoteExtensions = <BSchema extends BlockSchema>(opts: {
37
40
  editor: BlockNoteEditor<BSchema>;
41
+ domAttributes: Partial<BlockNoteDOMAttributes>;
38
42
  blockSchema: BSchema;
39
43
  collaboration?: {
40
44
  fragment: Y.XmlFragment;
@@ -86,10 +90,19 @@ export const getBlockNoteExtensions = <BSchema extends BlockSchema>(opts: {
86
90
  BackgroundColorExtension,
87
91
  TextAlignmentExtension,
88
92
 
89
- // custom blocks:
90
- ...blocks,
93
+ // nodes
94
+ Doc,
95
+ BlockContainer.configure({
96
+ domAttributes: opts.domAttributes,
97
+ }),
98
+ BlockGroup.configure({
99
+ domAttributes: opts.domAttributes,
100
+ }),
91
101
  ...Object.values(opts.blockSchema).map((blockSpec) =>
92
- blockSpec.node.configure({ editor: opts.editor })
102
+ blockSpec.node.configure({
103
+ editor: opts.editor,
104
+ domAttributes: opts.domAttributes,
105
+ })
93
106
  ),
94
107
  CustomBlockSerializerExtension,
95
108
 
@@ -3,7 +3,6 @@
3
3
  .bnEditor {
4
4
  outline: none;
5
5
  padding-inline: 54px;
6
- border-radius: 8px;
7
6
 
8
7
  /* Define a set of colors to be used throughout the app for consistency
9
8
  see https://atlassian.design/foundations/color for more info */
@@ -55,16 +54,6 @@ Tippy popups that are appended to document.body directly
55
54
  -moz-osx-font-smoothing: grayscale;
56
55
  }
57
56
 
58
- [data-theme="light"] {
59
- background-color: #FFFFFF;
60
- color: #3F3F3F;
61
- }
62
-
63
- [data-theme="dark"] {
64
- background-color: #1F1F1F;
65
- color: #CFCFCF;
66
- }
67
-
68
57
  .dragPreview {
69
58
  position: absolute;
70
59
  top: -1000px;
@@ -1,5 +1,5 @@
1
1
  import { Attribute, Node } from "@tiptap/core";
2
- import { BlockNoteEditor } from "../../..";
2
+ import { BlockNoteDOMAttributes, BlockNoteEditor } from "../../..";
3
3
  import styles from "../nodes/Block.module.css";
4
4
  import {
5
5
  BlockConfig,
@@ -9,6 +9,7 @@ import {
9
9
  TipTapNode,
10
10
  TipTapNodeConfig,
11
11
  } from "./blockTypes";
12
+ import { mergeCSSClasses } from "../../../shared/utils";
12
13
 
13
14
  export function camelToDataKebab(str: string): string {
14
15
  return "data-" + str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
@@ -124,17 +125,17 @@ export function createBlockSpec<
124
125
  >(
125
126
  blockConfig: BlockConfig<BType, PSchema, ContainsInlineContent, BSchema>
126
127
  ): BlockSpec<BType, PSchema> {
127
- const node = createTipTapBlock<BType>({
128
+ const node = createTipTapBlock<
129
+ BType,
130
+ {
131
+ editor: BlockNoteEditor<BSchema>;
132
+ domAttributes?: BlockNoteDOMAttributes;
133
+ }
134
+ >({
128
135
  name: blockConfig.type,
129
136
  content: blockConfig.containsInlineContent ? "inline*" : "",
130
137
  selectable: blockConfig.containsInlineContent,
131
138
 
132
- addOptions() {
133
- return {
134
- editor: undefined,
135
- };
136
- },
137
-
138
139
  addAttributes() {
139
140
  return propsToAttributes(blockConfig);
140
141
  },
@@ -151,8 +152,21 @@ export function createBlockSpec<
151
152
  return ({ HTMLAttributes, getPos }) => {
152
153
  // Create blockContent element
153
154
  const blockContent = document.createElement("div");
154
- // Sets blockContent class
155
- blockContent.className = styles.blockContent;
155
+ // Add custom HTML attributes
156
+ const blockContentDOMAttributes =
157
+ this.options.domAttributes?.blockContent || {};
158
+ for (const [attribute, value] of Object.entries(
159
+ blockContentDOMAttributes
160
+ )) {
161
+ if (attribute !== "class") {
162
+ blockContent.setAttribute(attribute, value);
163
+ }
164
+ }
165
+ // Set blockContent & custom classes
166
+ blockContent.className = mergeCSSClasses(
167
+ styles.blockContent,
168
+ blockContentDOMAttributes.class
169
+ );
156
170
  // Add blockContent HTML attribute
157
171
  blockContent.setAttribute("data-content-type", blockConfig.type);
158
172
  // Add props as HTML attributes in kebab-case with "data-" prefix
@@ -186,13 +200,24 @@ export function createBlockSpec<
186
200
 
187
201
  // Render elements
188
202
  const rendered = blockConfig.render(block as any, editor);
189
- // Add inlineContent class to inline content
203
+ // Add HTML attributes to contentDOM
190
204
  if ("contentDOM" in rendered) {
191
- rendered.contentDOM.className = `${
192
- rendered.contentDOM.className
193
- ? rendered.contentDOM.className + " "
194
- : ""
195
- }${styles.inlineContent}`;
205
+ const inlineContentDOMAttributes =
206
+ this.options.domAttributes?.inlineContent || {};
207
+ // Add custom HTML attributes
208
+ for (const [attribute, value] of Object.entries(
209
+ inlineContentDOMAttributes
210
+ )) {
211
+ if (attribute !== "class") {
212
+ rendered.contentDOM.setAttribute(attribute, value);
213
+ }
214
+ }
215
+ // Merge existing classes with inlineContent & custom classes
216
+ rendered.contentDOM.className = mergeCSSClasses(
217
+ rendered.contentDOM.className,
218
+ styles.inlineContent,
219
+ inlineContentDOMAttributes.class
220
+ );
196
221
  }
197
222
  // Add elements to blockContent
198
223
  blockContent.appendChild(rendered.dom);
@@ -210,20 +235,28 @@ export function createBlockSpec<
210
235
  });
211
236
 
212
237
  return {
213
- node: node,
238
+ node: node as TipTapNode<BType>,
214
239
  propSchema: blockConfig.propSchema,
215
240
  };
216
241
  }
217
242
 
218
- export function createTipTapBlock<Type extends string>(
219
- config: TipTapNodeConfig<Type>
220
- ): TipTapNode<Type> {
243
+ export function createTipTapBlock<
244
+ Type extends string,
245
+ Options extends {
246
+ domAttributes?: BlockNoteDOMAttributes;
247
+ } = {
248
+ domAttributes?: BlockNoteDOMAttributes;
249
+ },
250
+ Storage = any
251
+ >(
252
+ config: TipTapNodeConfig<Type, Options, Storage>
253
+ ): TipTapNode<Type, Options, Storage> {
221
254
  // Type cast is needed as Node.name is mutable, though there is basically no
222
255
  // reason to change it after creation. Alternative is to wrap Node in a new
223
256
  // class, which I don't think is worth it since we'd only be changing 1
224
257
  // attribute to be read only.
225
- return Node.create({
258
+ return Node.create<Options, Storage>({
226
259
  ...config,
227
260
  group: "blockContent",
228
- }) as TipTapNode<Type>;
261
+ }) as TipTapNode<Type, Options, Storage>;
229
262
  }
@@ -4,13 +4,28 @@ import { BlockNoteEditor } from "../../../BlockNoteEditor";
4
4
  import { InlineContent, PartialInlineContent } from "./inlineContentTypes";
5
5
  import { DefaultBlockSchema } from "./defaultBlocks";
6
6
 
7
+ export type BlockNoteDOMElement =
8
+ | "editor"
9
+ | "blockContainer"
10
+ | "blockGroup"
11
+ | "blockContent"
12
+ | "inlineContent";
13
+
14
+ export type BlockNoteDOMAttributes = Partial<{
15
+ [DOMElement in BlockNoteDOMElement]: Record<string, string>;
16
+ }>;
17
+
7
18
  // A configuration for a TipTap node, but with stricter type constraints on the
8
19
  // "name" and "group" properties. The "name" property is now always a string
9
20
  // literal type, and the "blockGroup" property cannot be configured as it should
10
21
  // always be "blockContent". Used as the parameter in `createTipTapNode`.
11
22
  export type TipTapNodeConfig<
12
23
  Name extends string,
13
- Options = any,
24
+ Options extends {
25
+ domAttributes?: BlockNoteDOMAttributes;
26
+ } = {
27
+ domAttributes?: BlockNoteDOMAttributes;
28
+ },
14
29
  Storage = any
15
30
  > = {
16
31
  [K in keyof NodeConfig<Options, Storage>]: K extends "name"
@@ -25,7 +40,11 @@ export type TipTapNodeConfig<
25
40
  // "blockGroup" property is now "blockContent". Returned by `createTipTapNode`.
26
41
  export type TipTapNode<
27
42
  Name extends string,
28
- Options = any,
43
+ Options extends {
44
+ domAttributes?: BlockNoteDOMAttributes;
45
+ } = {
46
+ domAttributes?: BlockNoteDOMAttributes;
47
+ },
29
48
  Storage = any
30
49
  > = Node<Options, Storage> & {
31
50
  name: Name;
@@ -104,7 +123,7 @@ export type BlockConfig<
104
123
  // allowing for more advanced custom blocks.
105
124
  export type BlockSpec<Type extends string, PSchema extends PropSchema> = {
106
125
  readonly propSchema: PSchema;
107
- node: TipTapNode<Type>;
126
+ node: TipTapNode<Type, any>;
108
127
  };
109
128
 
110
129
  // Utility type. For a given object block schema, ensures that the key of each
@@ -1,13 +1,8 @@
1
1
  import { Node } from "@tiptap/core";
2
- import { BlockContainer } from "./nodes/BlockContainer";
3
- import { BlockGroup } from "./nodes/BlockGroup";
4
-
5
- export const blocks: any[] = [
6
- BlockContainer,
7
- BlockGroup,
8
- Node.create({
9
- name: "doc",
10
- topNode: true,
11
- content: "blockGroup",
12
- }),
13
- ];
2
+ export { BlockContainer } from "./nodes/BlockContainer";
3
+ export { BlockGroup } from "./nodes/BlockGroup";
4
+ export const Doc = Node.create({
5
+ name: "doc",
6
+ topNode: true,
7
+ content: "blockGroup",
8
+ });
@@ -260,7 +260,7 @@ NESTED BLOCKS
260
260
 
261
261
  /* TODO: would be nicer if defined from code */
262
262
 
263
- .isEmpty.hasAnchor .inlineContent:before {
263
+ .blockContent.isEmpty.hasAnchor .inlineContent:before {
264
264
  content: "Enter text or type '/' for commands";
265
265
  }
266
266
 
@@ -10,12 +10,12 @@ import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos";
10
10
  import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
11
11
  import styles from "./Block.module.css";
12
12
  import BlockAttributes from "./BlockAttributes";
13
- import { BlockSchema, PartialBlock } from "../api/blockTypes";
14
-
15
- // TODO
16
- export interface IBlock {
17
- HTMLAttributes: Record<string, any>;
18
- }
13
+ import {
14
+ BlockNoteDOMAttributes,
15
+ BlockSchema,
16
+ PartialBlock,
17
+ } from "../api/blockTypes";
18
+ import { mergeCSSClasses } from "../../../shared/utils";
19
19
 
20
20
  declare module "@tiptap/core" {
21
21
  interface Commands<ReturnType> {
@@ -39,7 +39,9 @@ declare module "@tiptap/core" {
39
39
  /**
40
40
  * The main "Block node" documents consist of
41
41
  */
42
- export const BlockContainer = Node.create<IBlock>({
42
+ export const BlockContainer = Node.create<{
43
+ domAttributes?: BlockNoteDOMAttributes;
44
+ }>({
43
45
  name: "blockContainer",
44
46
  group: "blockContainer",
45
47
  // A block always contains content, and optionally a blockGroup which contains nested blocks
@@ -48,12 +50,6 @@ export const BlockContainer = Node.create<IBlock>({
48
50
  priority: 50,
49
51
  defining: true,
50
52
 
51
- addOptions() {
52
- return {
53
- HTMLAttributes: {},
54
- };
55
- },
56
-
57
53
  parseHTML() {
58
54
  return [
59
55
  {
@@ -81,6 +77,8 @@ export const BlockContainer = Node.create<IBlock>({
81
77
  },
82
78
 
83
79
  renderHTML({ HTMLAttributes }) {
80
+ const domAttributes = this.options.domAttributes?.blockContainer || {};
81
+
84
82
  return [
85
83
  "div",
86
84
  mergeAttributes(HTMLAttributes, {
@@ -89,11 +87,14 @@ export const BlockContainer = Node.create<IBlock>({
89
87
  }),
90
88
  [
91
89
  "div",
92
- mergeAttributes(HTMLAttributes, {
93
- // TODO: maybe remove html attributes from inner block
94
- class: styles.block,
95
- "data-node-type": this.name,
96
- }),
90
+ mergeAttributes(
91
+ {
92
+ ...domAttributes,
93
+ class: mergeCSSClasses(styles.block, domAttributes.class),
94
+ "data-node-type": this.name,
95
+ },
96
+ HTMLAttributes
97
+ ),
97
98
  0,
98
99
  ],
99
100
  ];
@@ -1,6 +1,7 @@
1
1
  import { InputRule, mergeAttributes } from "@tiptap/core";
2
2
  import { createTipTapBlock } from "../../../api/block";
3
3
  import styles from "../../Block.module.css";
4
+ import { mergeCSSClasses } from "../../../../../shared/utils";
4
5
 
5
6
  export const HeadingBlockContent = createTipTapBlock<"heading">({
6
7
  name: "heading",
@@ -64,13 +65,30 @@ export const HeadingBlockContent = createTipTapBlock<"heading">({
64
65
  },
65
66
 
66
67
  renderHTML({ node, HTMLAttributes }) {
68
+ const blockContentDOMAttributes =
69
+ this.options.domAttributes?.blockContent || {};
70
+ const inlineContentDOMAttributes =
71
+ this.options.domAttributes?.inlineContent || {};
72
+
67
73
  return [
68
74
  "div",
69
75
  mergeAttributes(HTMLAttributes, {
70
- class: styles.blockContent,
76
+ class: mergeCSSClasses(
77
+ styles.blockContent,
78
+ blockContentDOMAttributes.class
79
+ ),
71
80
  "data-content-type": this.name,
72
81
  }),
73
- ["h" + node.attrs.level, { class: styles.inlineContent }, 0],
82
+ [
83
+ "h" + node.attrs.level,
84
+ {
85
+ class: mergeCSSClasses(
86
+ styles.inlineContent,
87
+ inlineContentDOMAttributes.class
88
+ ),
89
+ },
90
+ 0,
91
+ ],
74
92
  ];
75
93
  },
76
94
  });
@@ -2,6 +2,7 @@ import { InputRule, mergeAttributes } from "@tiptap/core";
2
2
  import { createTipTapBlock } from "../../../../api/block";
3
3
  import { handleEnter } from "../ListItemKeyboardShortcuts";
4
4
  import styles from "../../../Block.module.css";
5
+ import { mergeCSSClasses } from "../../../../../../shared/utils";
5
6
 
6
7
  export const BulletListItemBlockContent = createTipTapBlock<"bulletListItem">({
7
8
  name: "bulletListItem",
@@ -82,13 +83,30 @@ export const BulletListItemBlockContent = createTipTapBlock<"bulletListItem">({
82
83
  },
83
84
 
84
85
  renderHTML({ HTMLAttributes }) {
86
+ const blockContentDOMAttributes =
87
+ this.options.domAttributes?.blockContent || {};
88
+ const inlineContentDOMAttributes =
89
+ this.options.domAttributes?.inlineContent || {};
90
+
85
91
  return [
86
92
  "div",
87
93
  mergeAttributes(HTMLAttributes, {
88
- class: styles.blockContent,
94
+ class: mergeCSSClasses(
95
+ styles.blockContent,
96
+ blockContentDOMAttributes.class
97
+ ),
89
98
  "data-content-type": this.name,
90
99
  }),
91
- ["p", { class: styles.inlineContent }, 0],
100
+ [
101
+ "p",
102
+ {
103
+ class: mergeCSSClasses(
104
+ styles.inlineContent,
105
+ inlineContentDOMAttributes.class
106
+ ),
107
+ },
108
+ 0,
109
+ ],
92
110
  ];
93
111
  },
94
112
  });
@@ -3,6 +3,7 @@ import { createTipTapBlock } from "../../../../api/block";
3
3
  import { handleEnter } from "../ListItemKeyboardShortcuts";
4
4
  import { NumberedListIndexingPlugin } from "./NumberedListIndexingPlugin";
5
5
  import styles from "../../../Block.module.css";
6
+ import { mergeCSSClasses } from "../../../../../../shared/utils";
6
7
 
7
8
  export const NumberedListItemBlockContent =
8
9
  createTipTapBlock<"numberedListItem">({
@@ -106,15 +107,32 @@ export const NumberedListItemBlockContent =
106
107
  },
107
108
 
108
109
  renderHTML({ HTMLAttributes }) {
110
+ const blockContentDOMAttributes =
111
+ this.options.domAttributes?.blockContent || {};
112
+ const inlineContentDOMAttributes =
113
+ this.options.domAttributes?.inlineContent || {};
114
+
109
115
  return [
110
116
  "div",
111
117
  mergeAttributes(HTMLAttributes, {
112
- class: styles.blockContent,
118
+ class: mergeCSSClasses(
119
+ styles.blockContent,
120
+ blockContentDOMAttributes.class
121
+ ),
113
122
  "data-content-type": this.name,
114
123
  }),
115
124
  // we use a <p> tag, because for <li> tags we'd need to add a <ul> parent for around siblings to be semantically correct,
116
125
  // which would be quite cumbersome
117
- ["p", { class: styles.inlineContent }, 0],
126
+ [
127
+ "p",
128
+ {
129
+ class: mergeCSSClasses(
130
+ styles.inlineContent,
131
+ inlineContentDOMAttributes.class
132
+ ),
133
+ },
134
+ 0,
135
+ ],
118
136
  ];
119
137
  },
120
138
  });
@@ -1,8 +1,9 @@
1
1
  import { mergeAttributes } from "@tiptap/core";
2
2
  import { createTipTapBlock } from "../../../api/block";
3
3
  import styles from "../../Block.module.css";
4
+ import { mergeCSSClasses } from "../../../../../shared/utils";
4
5
 
5
- export const ParagraphBlockContent = createTipTapBlock<"paragraph">({
6
+ export const ParagraphBlockContent = createTipTapBlock({
6
7
  name: "paragraph",
7
8
  content: "inline*",
8
9
 
@@ -17,13 +18,35 @@ export const ParagraphBlockContent = createTipTapBlock<"paragraph">({
17
18
  },
18
19
 
19
20
  renderHTML({ HTMLAttributes }) {
21
+ const blockContentDOMAttributes =
22
+ this.options.domAttributes?.blockContent || {};
23
+ const inlineContentDOMAttributes =
24
+ this.options.domAttributes?.inlineContent || {};
25
+
20
26
  return [
21
27
  "div",
22
- mergeAttributes(HTMLAttributes, {
23
- class: styles.blockContent,
24
- "data-content-type": this.name,
25
- }),
26
- ["p", { class: styles.inlineContent }, 0],
28
+ mergeAttributes(
29
+ {
30
+ ...blockContentDOMAttributes,
31
+ class: mergeCSSClasses(
32
+ styles.blockContent,
33
+ blockContentDOMAttributes.class
34
+ ),
35
+ "data-content-type": this.name,
36
+ },
37
+ HTMLAttributes
38
+ ),
39
+ [
40
+ "p",
41
+ {
42
+ ...inlineContentDOMAttributes,
43
+ class: mergeCSSClasses(
44
+ styles.inlineContent,
45
+ inlineContentDOMAttributes.class
46
+ ),
47
+ },
48
+ 0,
49
+ ],
27
50
  ];
28
51
  },
29
52
  });
@@ -1,17 +1,15 @@
1
1
  import { mergeAttributes, Node } from "@tiptap/core";
2
2
  import styles from "./Block.module.css";
3
+ import { BlockNoteDOMAttributes } from "../api/blockTypes";
4
+ import { mergeCSSClasses } from "../../../shared/utils";
3
5
 
4
- export const BlockGroup = Node.create({
6
+ export const BlockGroup = Node.create<{
7
+ domAttributes?: BlockNoteDOMAttributes;
8
+ }>({
5
9
  name: "blockGroup",
6
10
  group: "blockGroup",
7
11
  content: "blockContainer+",
8
12
 
9
- addOptions() {
10
- return {
11
- HTMLAttributes: {},
12
- };
13
- },
14
-
15
13
  parseHTML() {
16
14
  return [
17
15
  {
@@ -33,12 +31,22 @@ export const BlockGroup = Node.create({
33
31
  },
34
32
 
35
33
  renderHTML({ HTMLAttributes }) {
34
+ const blockGroupDOMAttributes =
35
+ this.options.domAttributes?.blockGroup || {};
36
+
36
37
  return [
37
38
  "div",
38
- mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
39
- class: styles.blockGroup,
40
- "data-node-type": "blockGroup",
41
- }),
39
+ mergeAttributes(
40
+ {
41
+ ...blockGroupDOMAttributes,
42
+ class: mergeCSSClasses(
43
+ styles.blockGroup,
44
+ blockGroupDOMAttributes.class
45
+ ),
46
+ "data-node-type": "blockGroup",
47
+ },
48
+ HTMLAttributes
49
+ ),
42
50
  0,
43
51
  ];
44
52
  },