@tiptap/core 3.0.0-next.4 → 3.0.0-next.6

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 (39) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/index.cjs +352 -238
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +1084 -1431
  5. package/dist/index.d.ts +1084 -1431
  6. package/dist/index.js +344 -235
  7. package/dist/index.js.map +1 -1
  8. package/dist/jsx-runtime/jsx-runtime.cjs +56 -0
  9. package/dist/jsx-runtime/jsx-runtime.cjs.map +1 -0
  10. package/dist/jsx-runtime/jsx-runtime.d.cts +22 -0
  11. package/dist/jsx-runtime/jsx-runtime.d.ts +22 -0
  12. package/dist/jsx-runtime/jsx-runtime.js +26 -0
  13. package/dist/jsx-runtime/jsx-runtime.js.map +1 -0
  14. package/jsx-runtime/index.cjs +1 -0
  15. package/jsx-runtime/index.d.cts +1 -0
  16. package/jsx-runtime/index.d.ts +1 -0
  17. package/jsx-runtime/index.js +1 -0
  18. package/package.json +20 -3
  19. package/src/Editor.ts +104 -22
  20. package/src/Extendable.ts +483 -0
  21. package/src/Extension.ts +5 -490
  22. package/src/ExtensionManager.ts +55 -10
  23. package/src/Mark.ts +135 -623
  24. package/src/MarkView.ts +66 -0
  25. package/src/Node.ts +325 -829
  26. package/src/commands/clearContent.ts +9 -4
  27. package/src/commands/focus.ts +7 -1
  28. package/src/commands/insertContentAt.ts +6 -2
  29. package/src/commands/setContent.ts +15 -14
  30. package/src/extensions/delete.ts +89 -0
  31. package/src/extensions/index.ts +1 -0
  32. package/src/extensions/keymap.ts +4 -0
  33. package/src/helpers/getExtensionField.ts +10 -7
  34. package/src/index.ts +3 -7
  35. package/src/jsx-runtime.ts +64 -0
  36. package/src/types.ts +334 -19
  37. package/src/utilities/elementFromString.ts +3 -0
  38. package/src/utilities/index.ts +1 -0
  39. package/src/utilities/mergeAttributes.ts +1 -1
package/src/Node.ts CHANGED
@@ -1,850 +1,346 @@
1
- import { DOMOutputSpec, Node as ProseMirrorNode, NodeSpec, NodeType } from '@tiptap/pm/model'
2
- import { Plugin, Transaction } from '@tiptap/pm/state'
3
-
4
- import { Editor } from './Editor.js'
5
- import { getExtensionField } from './helpers/getExtensionField.js'
6
- import { NodeConfig } from './index.js'
7
- import { InputRule } from './InputRule.js'
8
- import { Mark } from './Mark.js'
9
- import { PasteRule } from './PasteRule.js'
10
- import {
11
- AnyConfig,
12
- Attributes,
13
- Extensions,
14
- GlobalAttributes,
15
- KeyboardShortcutCommand,
16
- NodeViewRenderer,
17
- ParentConfig,
18
- RawCommands,
19
- } from './types.js'
20
- import { callOrReturn } from './utilities/callOrReturn.js'
21
- import { mergeDeep } from './utilities/mergeDeep.js'
22
-
23
- declare module '@tiptap/core' {
24
- interface NodeConfig<Options = any, Storage = any> {
25
- // @ts-ignore - this is a dynamic key
26
- [key: string]: any
27
-
28
- /**
29
- * The extension name - this must be unique.
30
- * It will be used to identify the extension.
31
- *
32
- * @example 'myExtension'
33
- */
1
+ import type { DOMOutputSpec, Node as ProseMirrorNode, NodeSpec, NodeType } from '@tiptap/pm/model'
2
+
3
+ import type { Editor } from './Editor.js'
4
+ import type { ExtendableConfig } from './Extendable.js'
5
+ import { Extendable } from './Extendable.js'
6
+ import type { Attributes, NodeViewRenderer, ParentConfig } from './types.js'
7
+
8
+ export interface NodeConfig<Options = any, Storage = any>
9
+ extends ExtendableConfig<Options, Storage, NodeConfig<Options, Storage>, NodeType> {
10
+ /**
11
+ * Node View
12
+ */
13
+ addNodeView?:
14
+ | ((this: {
15
+ name: string
16
+ options: Options
17
+ storage: Storage
18
+ editor: Editor
19
+ type: NodeType
20
+ parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView']
21
+ }) => NodeViewRenderer)
22
+ | null
23
+
24
+ /**
25
+ * Defines if this node should be a top level node (doc)
26
+ * @default false
27
+ * @example true
28
+ */
29
+ topNode?: boolean
30
+
31
+ /**
32
+ * The content expression for this node, as described in the [schema
33
+ * guide](/docs/guide/#schema.content_expressions). When not given,
34
+ * the node does not allow any content.
35
+ *
36
+ * You can read more about it on the Prosemirror documentation here
37
+ * @see https://prosemirror.net/docs/guide/#schema.content_expressions
38
+ * @default undefined
39
+ * @example content: 'block+'
40
+ * @example content: 'headline paragraph block*'
41
+ */
42
+ content?:
43
+ | NodeSpec['content']
44
+ | ((this: {
45
+ name: string
46
+ options: Options
47
+ storage: Storage
48
+ parent: ParentConfig<NodeConfig<Options, Storage>>['content']
49
+ editor?: Editor
50
+ }) => NodeSpec['content'])
51
+
52
+ /**
53
+ * The marks that are allowed inside of this node. May be a
54
+ * space-separated string referring to mark names or groups, `"_"`
55
+ * to explicitly allow all marks, or `""` to disallow marks. When
56
+ * not given, nodes with inline content default to allowing all
57
+ * marks, other nodes default to not allowing marks.
58
+ *
59
+ * @example marks: 'strong em'
60
+ */
61
+ marks?:
62
+ | NodeSpec['marks']
63
+ | ((this: {
64
+ name: string
65
+ options: Options
66
+ storage: Storage
67
+ parent: ParentConfig<NodeConfig<Options, Storage>>['marks']
68
+ editor?: Editor
69
+ }) => NodeSpec['marks'])
70
+
71
+ /**
72
+ * The group or space-separated groups to which this node belongs,
73
+ * which can be referred to in the content expressions for the
74
+ * schema.
75
+ *
76
+ * By default Tiptap uses the groups 'block' and 'inline' for nodes. You
77
+ * can also use custom groups if you want to group specific nodes together
78
+ * and handle them in your schema.
79
+ * @example group: 'block'
80
+ * @example group: 'inline'
81
+ * @example group: 'customBlock' // this uses a custom group
82
+ */
83
+ group?:
84
+ | NodeSpec['group']
85
+ | ((this: {
86
+ name: string
87
+ options: Options
88
+ storage: Storage
89
+ parent: ParentConfig<NodeConfig<Options, Storage>>['group']
90
+ editor?: Editor
91
+ }) => NodeSpec['group'])
92
+
93
+ /**
94
+ * Should be set to true for inline nodes. (Implied for text nodes.)
95
+ */
96
+ inline?:
97
+ | NodeSpec['inline']
98
+ | ((this: {
99
+ name: string
100
+ options: Options
101
+ storage: Storage
102
+ parent: ParentConfig<NodeConfig<Options, Storage>>['inline']
103
+ editor?: Editor
104
+ }) => NodeSpec['inline'])
105
+
106
+ /**
107
+ * Can be set to true to indicate that, though this isn't a [leaf
108
+ * node](https://prosemirror.net/docs/ref/#model.NodeType.isLeaf), it doesn't have directly editable
109
+ * content and should be treated as a single unit in the view.
110
+ *
111
+ * @example atom: true
112
+ */
113
+ atom?:
114
+ | NodeSpec['atom']
115
+ | ((this: {
116
+ name: string
117
+ options: Options
118
+ storage: Storage
119
+ parent: ParentConfig<NodeConfig<Options, Storage>>['atom']
120
+ editor?: Editor
121
+ }) => NodeSpec['atom'])
122
+
123
+ /**
124
+ * Controls whether nodes of this type can be selected as a [node
125
+ * selection](https://prosemirror.net/docs/ref/#state.NodeSelection). Defaults to true for non-text
126
+ * nodes.
127
+ *
128
+ * @default true
129
+ * @example selectable: false
130
+ */
131
+ selectable?:
132
+ | NodeSpec['selectable']
133
+ | ((this: {
134
+ name: string
135
+ options: Options
136
+ storage: Storage
137
+ parent: ParentConfig<NodeConfig<Options, Storage>>['selectable']
138
+ editor?: Editor
139
+ }) => NodeSpec['selectable'])
140
+
141
+ /**
142
+ * Determines whether nodes of this type can be dragged without
143
+ * being selected. Defaults to false.
144
+ *
145
+ * @default: false
146
+ * @example: draggable: true
147
+ */
148
+ draggable?:
149
+ | NodeSpec['draggable']
150
+ | ((this: {
151
+ name: string
152
+ options: Options
153
+ storage: Storage
154
+ parent: ParentConfig<NodeConfig<Options, Storage>>['draggable']
155
+ editor?: Editor
156
+ }) => NodeSpec['draggable'])
157
+
158
+ /**
159
+ * Can be used to indicate that this node contains code, which
160
+ * causes some commands to behave differently.
161
+ */
162
+ code?:
163
+ | NodeSpec['code']
164
+ | ((this: {
165
+ name: string
166
+ options: Options
167
+ storage: Storage
168
+ parent: ParentConfig<NodeConfig<Options, Storage>>['code']
169
+ editor?: Editor
170
+ }) => NodeSpec['code'])
171
+
172
+ /**
173
+ * Controls way whitespace in this a node is parsed. The default is
174
+ * `"normal"`, which causes the [DOM parser](https://prosemirror.net/docs/ref/#model.DOMParser) to
175
+ * collapse whitespace in normal mode, and normalize it (replacing
176
+ * newlines and such with spaces) otherwise. `"pre"` causes the
177
+ * parser to preserve spaces inside the node. When this option isn't
178
+ * given, but [`code`](https://prosemirror.net/docs/ref/#model.NodeSpec.code) is true, `whitespace`
179
+ * will default to `"pre"`. Note that this option doesn't influence
180
+ * the way the node is rendered—that should be handled by `toDOM`
181
+ * and/or styling.
182
+ */
183
+ whitespace?:
184
+ | NodeSpec['whitespace']
185
+ | ((this: {
186
+ name: string
187
+ options: Options
188
+ storage: Storage
189
+ parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace']
190
+ editor?: Editor
191
+ }) => NodeSpec['whitespace'])
192
+
193
+ /**
194
+ * Allows a **single** node to be set as linebreak equivalent (e.g. hardBreak).
195
+ * When converting between block types that have whitespace set to "pre"
196
+ * and don't support the linebreak node (e.g. codeBlock) and other block types
197
+ * that do support the linebreak node (e.g. paragraphs) - this node will be used
198
+ * as the linebreak instead of stripping the newline.
199
+ *
200
+ * See [linebreakReplacement](https://prosemirror.net/docs/ref/#model.NodeSpec.linebreakReplacement).
201
+ */
202
+ linebreakReplacement?:
203
+ | NodeSpec['linebreakReplacement']
204
+ | ((this: {
205
+ name: string
206
+ options: Options
207
+ storage: Storage
208
+ parent: ParentConfig<NodeConfig<Options, Storage>>['linebreakReplacement']
209
+ editor?: Editor
210
+ }) => NodeSpec['linebreakReplacement'])
211
+
212
+ /**
213
+ * When enabled, enables both
214
+ * [`definingAsContext`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingAsContext) and
215
+ * [`definingForContent`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingForContent).
216
+ *
217
+ * @default false
218
+ * @example isolating: true
219
+ */
220
+ defining?:
221
+ | NodeSpec['defining']
222
+ | ((this: {
223
+ name: string
224
+ options: Options
225
+ storage: Storage
226
+ parent: ParentConfig<NodeConfig<Options, Storage>>['defining']
227
+ editor?: Editor
228
+ }) => NodeSpec['defining'])
229
+
230
+ /**
231
+ * When enabled (default is false), the sides of nodes of this type
232
+ * count as boundaries that regular editing operations, like
233
+ * backspacing or lifting, won't cross. An example of a node that
234
+ * should probably have this enabled is a table cell.
235
+ */
236
+ isolating?:
237
+ | NodeSpec['isolating']
238
+ | ((this: {
239
+ name: string
240
+ options: Options
241
+ storage: Storage
242
+ parent: ParentConfig<NodeConfig<Options, Storage>>['isolating']
243
+ editor?: Editor
244
+ }) => NodeSpec['isolating'])
245
+
246
+ /**
247
+ * Associates DOM parser information with this node, which can be
248
+ * used by [`DOMParser.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMParser^fromSchema) to
249
+ * automatically derive a parser. The `node` field in the rules is
250
+ * implied (the name of this node will be filled in automatically).
251
+ * If you supply your own parser, you do not need to also specify
252
+ * parsing rules in your schema.
253
+ *
254
+ * @example parseHTML: [{ tag: 'div', attrs: { 'data-id': 'my-block' } }]
255
+ */
256
+ parseHTML?: (this: {
34
257
  name: string
35
-
36
- /**
37
- * The priority of your extension. The higher, the earlier it will be called
38
- * and will take precedence over other extensions with a lower priority.
39
- * @default 100
40
- * @example 101
41
- */
42
- priority?: number
43
-
44
- /**
45
- * The default options for this extension.
46
- * @example
47
- * defaultOptions: {
48
- * myOption: 'foo',
49
- * myOtherOption: 10,
50
- * }
51
- */
52
- defaultOptions?: Options
53
-
54
- /**
55
- * This method will add options to this extension
56
- * @see https://tiptap.dev/guide/custom-extensions#settings
57
- * @example
58
- * addOptions() {
59
- * return {
60
- * myOption: 'foo',
61
- * myOtherOption: 10,
62
- * }
63
- */
64
- addOptions?: (this: {
65
- name: string
66
- parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addOptions'], undefined>
67
- }) => Options
68
-
69
- /**
70
- * The default storage this extension can save data to.
71
- * @see https://tiptap.dev/guide/custom-extensions#storage
72
- * @example
73
- * defaultStorage: {
74
- * prefetchedUsers: [],
75
- * loading: false,
76
- * }
77
- */
78
- addStorage?: (this: {
79
- name: string
80
- options: Options
81
- parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addStorage'], undefined>
82
- }) => Storage
83
-
84
- /**
85
- * This function adds globalAttributes to specific nodes.
86
- * @see https://tiptap.dev/guide/custom-extensions#global-attributes
87
- * @example
88
- * addGlobalAttributes() {
89
- * return [
90
- * {
91
- // Extend the following extensions
92
- * types: [
93
- * 'heading',
94
- * 'paragraph',
95
- * ],
96
- * // … with those attributes
97
- * attributes: {
98
- * textAlign: {
99
- * default: 'left',
100
- * renderHTML: attributes => ({
101
- * style: `text-align: ${attributes.textAlign}`,
102
- * }),
103
- * parseHTML: element => element.style.textAlign || 'left',
104
- * },
105
- * },
106
- * },
107
- * ]
108
- * }
109
- */
110
- addGlobalAttributes?: (this: {
111
- name: string
112
- options: Options
113
- storage: Storage
114
- extensions: (Node | Mark)[]
115
- parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes']
116
- }) => GlobalAttributes
117
-
118
- /**
119
- * This function adds commands to the editor
120
- * @see https://tiptap.dev/guide/custom-extensions#keyboard-shortcuts
121
- * @example
122
- * addCommands() {
123
- * return {
124
- * myCommand: () => ({ chain }) => chain().setMark('type', 'foo').run(),
125
- * }
126
- * }
127
- */
128
- addCommands?: (this: {
129
- name: string
130
- options: Options
131
- storage: Storage
132
- editor: Editor
133
- type: NodeType
134
- parent: ParentConfig<NodeConfig<Options, Storage>>['addCommands']
135
- }) => Partial<RawCommands>
136
-
137
- /**
138
- * This function registers keyboard shortcuts.
139
- * @see https://tiptap.dev/guide/custom-extensions#keyboard-shortcuts
140
- * @example
141
- * addKeyboardShortcuts() {
142
- * return {
143
- * 'Mod-l': () => this.editor.commands.toggleBulletList(),
144
- * }
145
- * },
146
- */
147
- addKeyboardShortcuts?: (this: {
148
- name: string
149
- options: Options
150
- storage: Storage
151
- editor: Editor
152
- type: NodeType
153
- parent: ParentConfig<NodeConfig<Options, Storage>>['addKeyboardShortcuts']
154
- }) => {
155
- [key: string]: KeyboardShortcutCommand
156
- }
157
-
158
- /**
159
- * This function adds input rules to the editor.
160
- * @see https://tiptap.dev/guide/custom-extensions#input-rules
161
- * @example
162
- * addInputRules() {
163
- * return [
164
- * markInputRule({
165
- * find: inputRegex,
166
- * type: this.type,
167
- * }),
168
- * ]
169
- * },
170
- */
171
- addInputRules?: (this: {
172
- name: string
173
- options: Options
174
- storage: Storage
175
- editor: Editor
176
- type: NodeType
177
- parent: ParentConfig<NodeConfig<Options, Storage>>['addInputRules']
178
- }) => InputRule[]
179
-
180
- /**
181
- * This function adds paste rules to the editor.
182
- * @see https://tiptap.dev/guide/custom-extensions#paste-rules
183
- * @example
184
- * addPasteRules() {
185
- * return [
186
- * markPasteRule({
187
- * find: pasteRegex,
188
- * type: this.type,
189
- * }),
190
- * ]
191
- * },
192
- */
193
- addPasteRules?: (this: {
194
- name: string
195
- options: Options
196
- storage: Storage
197
- editor: Editor
198
- type: NodeType
199
- parent: ParentConfig<NodeConfig<Options, Storage>>['addPasteRules']
200
- }) => PasteRule[]
201
-
202
- /**
203
- * This function adds Prosemirror plugins to the editor
204
- * @see https://tiptap.dev/guide/custom-extensions#prosemirror-plugins
205
- * @example
206
- * addProseMirrorPlugins() {
207
- * return [
208
- * customPlugin(),
209
- * ]
210
- * }
211
- */
212
- addProseMirrorPlugins?: (this: {
213
- name: string
214
- options: Options
215
- storage: Storage
216
- editor: Editor
217
- type: NodeType
218
- parent: ParentConfig<NodeConfig<Options, Storage>>['addProseMirrorPlugins']
219
- }) => Plugin[]
220
-
221
- /**
222
- * This function adds additional extensions to the editor. This is useful for
223
- * building extension kits.
224
- * @example
225
- * addExtensions() {
226
- * return [
227
- * BulletList,
228
- * OrderedList,
229
- * ListItem
230
- * ]
231
- * }
232
- */
233
- addExtensions?: (this: {
234
- name: string
235
- options: Options
236
- storage: Storage
237
- parent: ParentConfig<NodeConfig<Options, Storage>>['addExtensions']
238
- }) => Extensions
239
-
240
- /**
241
- * This function extends the schema of the node.
242
- * @example
243
- * extendNodeSchema() {
244
- * return {
245
- * group: 'inline',
246
- * selectable: false,
247
- * }
248
- * }
249
- */
250
- extendNodeSchema?:
251
- | ((
252
- this: {
253
- name: string
254
- options: Options
255
- storage: Storage
256
- parent: ParentConfig<NodeConfig<Options, Storage>>['extendNodeSchema']
257
- },
258
- extension: Node,
259
- ) => Record<string, any>)
260
- | null
261
-
262
- /**
263
- * This function extends the schema of the mark.
264
- * @example
265
- * extendMarkSchema() {
266
- * return {
267
- * group: 'inline',
268
- * selectable: false,
269
- * }
270
- * }
271
- */
272
- extendMarkSchema?:
273
- | ((
274
- this: {
275
- name: string
276
- options: Options
277
- storage: Storage
278
- parent: ParentConfig<NodeConfig<Options, Storage>>['extendMarkSchema']
279
- editor?: Editor
280
- },
281
- extension: Node,
282
- ) => Record<string, any>)
283
- | null
284
-
285
- /**
286
- * The editor is not ready yet.
287
- */
288
- onBeforeCreate?:
289
- | ((this: {
258
+ options: Options
259
+ storage: Storage
260
+ parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML']
261
+ editor?: Editor
262
+ }) => NodeSpec['parseDOM']
263
+
264
+ /**
265
+ * A description of a DOM structure. Can be either a string, which is
266
+ * interpreted as a text node, a DOM node, which is interpreted as
267
+ * itself, a `{dom, contentDOM}` object, or an array.
268
+ *
269
+ * An array describes a DOM element. The first value in the array
270
+ * should be a string—the name of the DOM element, optionally prefixed
271
+ * by a namespace URL and a space. If the second element is plain
272
+ * object, it is interpreted as a set of attributes for the element.
273
+ * Any elements after that (including the 2nd if it's not an attribute
274
+ * object) are interpreted as children of the DOM elements, and must
275
+ * either be valid `DOMOutputSpec` values, or the number zero.
276
+ *
277
+ * The number zero (pronounced “hole”) is used to indicate the place
278
+ * where a node's child nodes should be inserted. If it occurs in an
279
+ * output spec, it should be the only child element in its parent
280
+ * node.
281
+ *
282
+ * @example toDOM: ['div[data-id="my-block"]', { class: 'my-block' }, 0]
283
+ */
284
+ renderHTML?:
285
+ | ((
286
+ this: {
290
287
  name: string
291
288
  options: Options
292
289
  storage: Storage
293
- editor: Editor
294
- type: NodeType
295
- parent: ParentConfig<NodeConfig<Options, Storage>>['onBeforeCreate']
296
- }) => void)
297
- | null
298
-
299
- /**
300
- * The editor is ready.
301
- */
302
- onCreate?:
303
- | ((this: {
304
- name: string
305
- options: Options
306
- storage: Storage
307
- editor: Editor
308
- type: NodeType
309
- parent: ParentConfig<NodeConfig<Options, Storage>>['onCreate']
310
- }) => void)
311
- | null
312
-
313
- /**
314
- * The content has changed.
315
- */
316
- onUpdate?:
317
- | ((this: {
318
- name: string
319
- options: Options
320
- storage: Storage
321
- editor: Editor
322
- type: NodeType
323
- parent: ParentConfig<NodeConfig<Options, Storage>>['onUpdate']
324
- }) => void)
325
- | null
326
-
327
- /**
328
- * The selection has changed.
329
- */
330
- onSelectionUpdate?:
331
- | ((this: {
332
- name: string
333
- options: Options
334
- storage: Storage
335
- editor: Editor
336
- type: NodeType
337
- parent: ParentConfig<NodeConfig<Options, Storage>>['onSelectionUpdate']
338
- }) => void)
339
- | null
340
-
341
- /**
342
- * The editor state has changed.
343
- */
344
- onTransaction?:
345
- | ((
346
- this: {
347
- name: string
348
- options: Options
349
- storage: Storage
350
- editor: Editor
351
- type: NodeType
352
- parent: ParentConfig<NodeConfig<Options, Storage>>['onTransaction']
353
- },
354
- props: {
355
- editor: Editor
356
- transaction: Transaction
357
- },
358
- ) => void)
359
- | null
360
-
361
- /**
362
- * The editor is focused.
363
- */
364
- onFocus?:
365
- | ((
366
- this: {
367
- name: string
368
- options: Options
369
- storage: Storage
370
- editor: Editor
371
- type: NodeType
372
- parent: ParentConfig<NodeConfig<Options, Storage>>['onFocus']
373
- },
374
- props: {
375
- event: FocusEvent
376
- },
377
- ) => void)
378
- | null
379
-
380
- /**
381
- * The editor isn’t focused anymore.
382
- */
383
- onBlur?:
384
- | ((
385
- this: {
386
- name: string
387
- options: Options
388
- storage: Storage
389
- editor: Editor
390
- type: NodeType
391
- parent: ParentConfig<NodeConfig<Options, Storage>>['onBlur']
392
- },
393
- props: {
394
- event: FocusEvent
395
- },
396
- ) => void)
397
- | null
398
-
399
- /**
400
- * The editor is destroyed.
401
- */
402
- onDestroy?:
403
- | ((this: {
404
- name: string
405
- options: Options
406
- storage: Storage
407
- editor: Editor
408
- type: NodeType
409
- parent: ParentConfig<NodeConfig<Options, Storage>>['onDestroy']
410
- }) => void)
411
- | null
412
-
413
- /**
414
- * Node View
415
- */
416
- addNodeView?:
417
- | ((this: {
418
- name: string
419
- options: Options
420
- storage: Storage
421
- editor: Editor
422
- type: NodeType
423
- parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView']
424
- }) => NodeViewRenderer)
425
- | null
426
-
427
- /**
428
- * Defines if this node should be a top level node (doc)
429
- * @default false
430
- * @example true
431
- */
432
- topNode?: boolean
433
-
434
- /**
435
- * The content expression for this node, as described in the [schema
436
- * guide](/docs/guide/#schema.content_expressions). When not given,
437
- * the node does not allow any content.
438
- *
439
- * You can read more about it on the Prosemirror documentation here
440
- * @see https://prosemirror.net/docs/guide/#schema.content_expressions
441
- * @default undefined
442
- * @example content: 'block+'
443
- * @example content: 'headline paragraph block*'
444
- */
445
- content?:
446
- | NodeSpec['content']
447
- | ((this: {
448
- name: string
449
- options: Options
450
- storage: Storage
451
- parent: ParentConfig<NodeConfig<Options, Storage>>['content']
452
- editor?: Editor
453
- }) => NodeSpec['content'])
454
-
455
- /**
456
- * The marks that are allowed inside of this node. May be a
457
- * space-separated string referring to mark names or groups, `"_"`
458
- * to explicitly allow all marks, or `""` to disallow marks. When
459
- * not given, nodes with inline content default to allowing all
460
- * marks, other nodes default to not allowing marks.
461
- *
462
- * @example marks: 'strong em'
463
- */
464
- marks?:
465
- | NodeSpec['marks']
466
- | ((this: {
467
- name: string
468
- options: Options
469
- storage: Storage
470
- parent: ParentConfig<NodeConfig<Options, Storage>>['marks']
471
- editor?: Editor
472
- }) => NodeSpec['marks'])
473
-
474
- /**
475
- * The group or space-separated groups to which this node belongs,
476
- * which can be referred to in the content expressions for the
477
- * schema.
478
- *
479
- * By default Tiptap uses the groups 'block' and 'inline' for nodes. You
480
- * can also use custom groups if you want to group specific nodes together
481
- * and handle them in your schema.
482
- * @example group: 'block'
483
- * @example group: 'inline'
484
- * @example group: 'customBlock' // this uses a custom group
485
- */
486
- group?:
487
- | NodeSpec['group']
488
- | ((this: {
489
- name: string
490
- options: Options
491
- storage: Storage
492
- parent: ParentConfig<NodeConfig<Options, Storage>>['group']
493
- editor?: Editor
494
- }) => NodeSpec['group'])
495
-
496
- /**
497
- * Should be set to true for inline nodes. (Implied for text nodes.)
498
- */
499
- inline?:
500
- | NodeSpec['inline']
501
- | ((this: {
502
- name: string
503
- options: Options
504
- storage: Storage
505
- parent: ParentConfig<NodeConfig<Options, Storage>>['inline']
290
+ parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML']
506
291
  editor?: Editor
507
- }) => NodeSpec['inline'])
508
-
509
- /**
510
- * Can be set to true to indicate that, though this isn't a [leaf
511
- * node](https://prosemirror.net/docs/ref/#model.NodeType.isLeaf), it doesn't have directly editable
512
- * content and should be treated as a single unit in the view.
513
- *
514
- * @example atom: true
515
- */
516
- atom?:
517
- | NodeSpec['atom']
518
- | ((this: {
292
+ },
293
+ props: {
294
+ node: ProseMirrorNode
295
+ HTMLAttributes: Record<string, any>
296
+ },
297
+ ) => DOMOutputSpec)
298
+ | null
299
+
300
+ /**
301
+ * renders the node as text
302
+ * @example renderText: () => 'foo
303
+ */
304
+ renderText?:
305
+ | ((
306
+ this: {
519
307
  name: string
520
308
  options: Options
521
309
  storage: Storage
522
- parent: ParentConfig<NodeConfig<Options, Storage>>['atom']
310
+ parent: ParentConfig<NodeConfig<Options, Storage>>['renderText']
523
311
  editor?: Editor
524
- }) => NodeSpec['atom'])
525
-
526
- /**
527
- * Controls whether nodes of this type can be selected as a [node
528
- * selection](https://prosemirror.net/docs/ref/#state.NodeSelection). Defaults to true for non-text
529
- * nodes.
530
- *
531
- * @default true
532
- * @example selectable: false
533
- */
534
- selectable?:
535
- | NodeSpec['selectable']
536
- | ((this: {
537
- name: string
538
- options: Options
539
- storage: Storage
540
- parent: ParentConfig<NodeConfig<Options, Storage>>['selectable']
541
- editor?: Editor
542
- }) => NodeSpec['selectable'])
543
-
544
- /**
545
- * Determines whether nodes of this type can be dragged without
546
- * being selected. Defaults to false.
547
- *
548
- * @default: false
549
- * @example: draggable: true
550
- */
551
- draggable?:
552
- | NodeSpec['draggable']
553
- | ((this: {
554
- name: string
555
- options: Options
556
- storage: Storage
557
- parent: ParentConfig<NodeConfig<Options, Storage>>['draggable']
558
- editor?: Editor
559
- }) => NodeSpec['draggable'])
560
-
561
- /**
562
- * Can be used to indicate that this node contains code, which
563
- * causes some commands to behave differently.
564
- */
565
- code?:
566
- | NodeSpec['code']
567
- | ((this: {
568
- name: string
569
- options: Options
570
- storage: Storage
571
- parent: ParentConfig<NodeConfig<Options, Storage>>['code']
572
- editor?: Editor
573
- }) => NodeSpec['code'])
574
-
575
- /**
576
- * Controls way whitespace in this a node is parsed. The default is
577
- * `"normal"`, which causes the [DOM parser](https://prosemirror.net/docs/ref/#model.DOMParser) to
578
- * collapse whitespace in normal mode, and normalize it (replacing
579
- * newlines and such with spaces) otherwise. `"pre"` causes the
580
- * parser to preserve spaces inside the node. When this option isn't
581
- * given, but [`code`](https://prosemirror.net/docs/ref/#model.NodeSpec.code) is true, `whitespace`
582
- * will default to `"pre"`. Note that this option doesn't influence
583
- * the way the node is rendered—that should be handled by `toDOM`
584
- * and/or styling.
585
- */
586
- whitespace?:
587
- | NodeSpec['whitespace']
588
- | ((this: {
589
- name: string
590
- options: Options
591
- storage: Storage
592
- parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace']
593
- editor?: Editor
594
- }) => NodeSpec['whitespace'])
595
-
596
- /**
597
- * Allows a **single** node to be set as linebreak equivalent (e.g. hardBreak).
598
- * When converting between block types that have whitespace set to "pre"
599
- * and don't support the linebreak node (e.g. codeBlock) and other block types
600
- * that do support the linebreak node (e.g. paragraphs) - this node will be used
601
- * as the linebreak instead of stripping the newline.
602
- *
603
- * See [linebreakReplacement](https://prosemirror.net/docs/ref/#model.NodeSpec.linebreakReplacement).
604
- */
605
- linebreakReplacement?:
606
- | NodeSpec['linebreakReplacement']
607
- | ((this: {
608
- name: string
609
- options: Options
610
- storage: Storage
611
- parent: ParentConfig<NodeConfig<Options, Storage>>['linebreakReplacement']
612
- editor?: Editor
613
- }) => NodeSpec['linebreakReplacement'])
614
-
615
- /**
616
- * When enabled, enables both
617
- * [`definingAsContext`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingAsContext) and
618
- * [`definingForContent`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingForContent).
619
- *
620
- * @default false
621
- * @example isolating: true
622
- */
623
- defining?:
624
- | NodeSpec['defining']
625
- | ((this: {
626
- name: string
627
- options: Options
628
- storage: Storage
629
- parent: ParentConfig<NodeConfig<Options, Storage>>['defining']
630
- editor?: Editor
631
- }) => NodeSpec['defining'])
632
-
633
- /**
634
- * When enabled (default is false), the sides of nodes of this type
635
- * count as boundaries that regular editing operations, like
636
- * backspacing or lifting, won't cross. An example of a node that
637
- * should probably have this enabled is a table cell.
638
- */
639
- isolating?:
640
- | NodeSpec['isolating']
641
- | ((this: {
642
- name: string
643
- options: Options
644
- storage: Storage
645
- parent: ParentConfig<NodeConfig<Options, Storage>>['isolating']
646
- editor?: Editor
647
- }) => NodeSpec['isolating'])
648
-
649
- /**
650
- * Associates DOM parser information with this node, which can be
651
- * used by [`DOMParser.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMParser^fromSchema) to
652
- * automatically derive a parser. The `node` field in the rules is
653
- * implied (the name of this node will be filled in automatically).
654
- * If you supply your own parser, you do not need to also specify
655
- * parsing rules in your schema.
656
- *
657
- * @example parseHTML: [{ tag: 'div', attrs: { 'data-id': 'my-block' } }]
658
- */
659
- parseHTML?: (this: {
660
- name: string
661
- options: Options
662
- storage: Storage
663
- parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML']
664
- editor?: Editor
665
- }) => NodeSpec['parseDOM']
666
-
667
- /**
668
- * A description of a DOM structure. Can be either a string, which is
669
- * interpreted as a text node, a DOM node, which is interpreted as
670
- * itself, a `{dom, contentDOM}` object, or an array.
671
- *
672
- * An array describes a DOM element. The first value in the array
673
- * should be a string—the name of the DOM element, optionally prefixed
674
- * by a namespace URL and a space. If the second element is plain
675
- * object, it is interpreted as a set of attributes for the element.
676
- * Any elements after that (including the 2nd if it's not an attribute
677
- * object) are interpreted as children of the DOM elements, and must
678
- * either be valid `DOMOutputSpec` values, or the number zero.
679
- *
680
- * The number zero (pronounced “hole”) is used to indicate the place
681
- * where a node's child nodes should be inserted. If it occurs in an
682
- * output spec, it should be the only child element in its parent
683
- * node.
684
- *
685
- * @example toDOM: ['div[data-id="my-block"]', { class: 'my-block' }, 0]
686
- */
687
- renderHTML?:
688
- | ((
689
- this: {
690
- name: string
691
- options: Options
692
- storage: Storage
693
- parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML']
694
- editor?: Editor
695
- },
696
- props: {
697
- node: ProseMirrorNode
698
- HTMLAttributes: Record<string, any>
699
- },
700
- ) => DOMOutputSpec)
701
- | null
702
-
703
- /**
704
- * renders the node as text
705
- * @example renderText: () => 'foo
706
- */
707
- renderText?:
708
- | ((
709
- this: {
710
- name: string
711
- options: Options
712
- storage: Storage
713
- parent: ParentConfig<NodeConfig<Options, Storage>>['renderText']
714
- editor?: Editor
715
- },
716
- props: {
717
- node: ProseMirrorNode
718
- pos: number
719
- parent: ProseMirrorNode
720
- index: number
721
- },
722
- ) => string)
723
- | null
724
-
725
- /**
726
- * Add attributes to the node
727
- * @example addAttributes: () => ({ class: 'foo' })
728
- */
729
- addAttributes?: (this: {
730
- name: string
731
- options: Options
732
- storage: Storage
733
- parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes']
734
- editor?: Editor
735
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
736
- }) => Attributes | {}
737
- }
312
+ },
313
+ props: {
314
+ node: ProseMirrorNode
315
+ pos: number
316
+ parent: ProseMirrorNode
317
+ index: number
318
+ },
319
+ ) => string)
320
+ | null
321
+
322
+ /**
323
+ * Add attributes to the node
324
+ * @example addAttributes: () => ({ class: 'foo' })
325
+ */
326
+ addAttributes?: (this: {
327
+ name: string
328
+ options: Options
329
+ storage: Storage
330
+ parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes']
331
+ editor?: Editor
332
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
333
+ }) => Attributes | {}
738
334
  }
739
335
 
740
336
  /**
741
337
  * The Node class is used to create custom node extensions.
742
338
  * @see https://tiptap.dev/api/extensions#create-a-new-extension
743
339
  */
744
- export class Node<Options = any, Storage = any> {
340
+ export class Node<Options = any, Storage = any> extends Extendable<Options, Storage> {
745
341
  type = 'node'
746
342
 
747
- name = 'node'
748
-
749
- parent: Node | null = null
750
-
751
- child: Node | null = null
752
-
753
- options: Options
754
-
755
- storage: Storage
756
-
757
- config: NodeConfig = {
758
- name: this.name,
759
- defaultOptions: {},
760
- }
761
-
762
- constructor(config: Partial<NodeConfig<Options, Storage>> = {}) {
763
- this.config = {
764
- ...this.config,
765
- ...config,
766
- }
767
-
768
- this.name = this.config.name
769
-
770
- if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
771
- console.warn(
772
- `[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
773
- )
774
- }
775
-
776
- // TODO: remove `addOptions` fallback
777
- this.options = this.config.defaultOptions
778
-
779
- if (this.config.addOptions) {
780
- this.options = callOrReturn(
781
- getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
782
- name: this.name,
783
- }),
784
- )
785
- }
786
-
787
- this.storage =
788
- callOrReturn(
789
- getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
790
- name: this.name,
791
- options: this.options,
792
- }),
793
- ) || {}
794
- }
795
-
796
343
  static create<O = any, S = any>(config: Partial<NodeConfig<O, S>> = {}) {
797
344
  return new Node<O, S>(config)
798
345
  }
799
-
800
- configure(options: Partial<Options> = {}) {
801
- // return a new instance so we can use the same extension
802
- // with different calls of `configure`
803
- const extension = this.extend<Options, Storage>({
804
- ...this.config,
805
- addOptions: () => {
806
- return mergeDeep(this.options as Record<string, any>, options) as Options
807
- },
808
- })
809
-
810
- // Always preserve the current name
811
- extension.name = this.name
812
- // Set the parent to be our parent
813
- extension.parent = this.parent
814
-
815
- return extension
816
- }
817
-
818
- extend<ExtendedOptions = Options, ExtendedStorage = Storage>(
819
- extendedConfig: Partial<NodeConfig<ExtendedOptions, ExtendedStorage>> = {},
820
- ) {
821
- const extension = new Node<ExtendedOptions, ExtendedStorage>(extendedConfig)
822
-
823
- extension.parent = this
824
-
825
- this.child = extension
826
-
827
- extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
828
-
829
- if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
830
- console.warn(
831
- `[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`,
832
- )
833
- }
834
-
835
- extension.options = callOrReturn(
836
- getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
837
- name: extension.name,
838
- }),
839
- )
840
-
841
- extension.storage = callOrReturn(
842
- getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
843
- name: extension.name,
844
- options: extension.options,
845
- }),
846
- )
847
-
848
- return extension
849
- }
850
346
  }