11import { Attribute , Node } from "@tiptap/core" ;
2- import { BlockNoteEditor } from "../../.." ;
2+ import { BlockNoteDOMAttributes , BlockNoteEditor } from "../../.." ;
33import styles from "../nodes/Block.module.css" ;
44import {
55 BlockConfig ,
99 TipTapNode ,
1010 TipTapNodeConfig ,
1111} from "./blockTypes" ;
12+ import { mergeCSSClasses } from "../../../shared/utils" ;
1213
1314export function camelToDataKebab ( str : string ) : string {
1415 return "data-" + str . replace ( / ( [ a - z ] ) ( [ A - Z ] ) / g, "$1-$2" ) . toLowerCase ( ) ;
@@ -124,17 +125,17 @@ export function createBlockSpec<
124125> (
125126 blockConfig : BlockConfig < BType , PSchema , ContainsInlineContent , BSchema >
126127) : BlockSpec < BType , PSchema > {
127- const node = createTipTapBlock < BType > ( {
128+ const node = createTipTapBlock <
129+ BType ,
130+ {
131+ editor : BlockNoteEditor < BSchema > ;
132+ domAttributes ?: BlockNoteDOMAttributes ;
133+ }
134+ > ( {
128135 name : blockConfig . type ,
129136 content : blockConfig . containsInlineContent ? "inline*" : "" ,
130137 selectable : blockConfig . containsInlineContent ,
131138
132- addOptions ( ) {
133- return {
134- editor : undefined ,
135- } ;
136- } ,
137-
138139 addAttributes ( ) {
139140 return propsToAttributes ( blockConfig ) ;
140141 } ,
@@ -151,8 +152,21 @@ export function createBlockSpec<
151152 return ( { HTMLAttributes, getPos } ) => {
152153 // Create blockContent element
153154 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+ ) ;
156170 // Add blockContent HTML attribute
157171 blockContent . setAttribute ( "data-content-type" , blockConfig . type ) ;
158172 // Add props as HTML attributes in kebab-case with "data-" prefix
@@ -186,13 +200,24 @@ export function createBlockSpec<
186200
187201 // Render elements
188202 const rendered = blockConfig . render ( block as any , editor ) ;
189- // Add inlineContent class to inline content
203+ // Add HTML attributes to contentDOM
190204 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+ ) ;
196221 }
197222 // Add elements to blockContent
198223 blockContent . appendChild ( rendered . dom ) ;
@@ -210,20 +235,28 @@ export function createBlockSpec<
210235 } ) ;
211236
212237 return {
213- node : node ,
238+ node : node as TipTapNode < BType > ,
214239 propSchema : blockConfig . propSchema ,
215240 } ;
216241}
217242
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 > {
221254 // Type cast is needed as Node.name is mutable, though there is basically no
222255 // reason to change it after creation. Alternative is to wrap Node in a new
223256 // class, which I don't think is worth it since we'd only be changing 1
224257 // attribute to be read only.
225- return Node . create ( {
258+ return Node . create < Options , Storage > ( {
226259 ...config ,
227260 group : "blockContent" ,
228- } ) as TipTapNode < Type > ;
261+ } ) as TipTapNode < Type , Options , Storage > ;
229262}
0 commit comments