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
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
}
0 commit comments