@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/Extension.ts CHANGED
@@ -1,502 +1,17 @@
1
- import { Plugin, Transaction } from '@tiptap/pm/state'
1
+ import { type ExtendableConfig, Extendable } from './Extendable.js'
2
2
 
3
- import { Editor } from './Editor.js'
4
- import { getExtensionField } from './helpers/getExtensionField.js'
5
- import { ExtensionConfig } from './index.js'
6
- import { InputRule } from './InputRule.js'
7
- import { Mark } from './Mark.js'
8
- import { Node } from './Node.js'
9
- import { PasteRule } from './PasteRule.js'
10
- import { AnyConfig, Extensions, GlobalAttributes, KeyboardShortcutCommand, ParentConfig, RawCommands } from './types.js'
11
- import { callOrReturn } from './utilities/callOrReturn.js'
12
- import { mergeDeep } from './utilities/mergeDeep.js'
13
-
14
- declare module '@tiptap/core' {
15
- interface ExtensionConfig<Options = any, Storage = any> {
16
- // @ts-ignore - this is a dynamic key
17
- [key: string]: any
18
-
19
- /**
20
- * The extension name - this must be unique.
21
- * It will be used to identify the extension.
22
- *
23
- * @example 'myExtension'
24
- */
25
- name: string
26
-
27
- /**
28
- * The priority of your extension. The higher, the earlier it will be called
29
- * and will take precedence over other extensions with a lower priority.
30
- * @default 100
31
- * @example 101
32
- */
33
- priority?: number
34
-
35
- /**
36
- * The default options for this extension.
37
- * @example
38
- * defaultOptions: {
39
- * myOption: 'foo',
40
- * myOtherOption: 10,
41
- * }
42
- */
43
- defaultOptions?: Options
44
-
45
- /**
46
- * This method will add options to this extension
47
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#settings
48
- * @example
49
- * addOptions() {
50
- * return {
51
- * myOption: 'foo',
52
- * myOtherOption: 10,
53
- * }
54
- */
55
- addOptions?: (this: {
56
- name: string
57
- parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addOptions'], undefined>
58
- }) => Options
59
-
60
- /**
61
- * The default storage this extension can save data to.
62
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#storage
63
- * @example
64
- * defaultStorage: {
65
- * prefetchedUsers: [],
66
- * loading: false,
67
- * }
68
- */
69
- addStorage?: (this: {
70
- name: string
71
- options: Options
72
- parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addStorage'], undefined>
73
- }) => Storage
74
-
75
- /**
76
- * This function adds globalAttributes to specific nodes.
77
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#global-attributes
78
- * @example
79
- * addGlobalAttributes() {
80
- * return [
81
- * {
82
- // Extend the following extensions
83
- * types: [
84
- * 'heading',
85
- * 'paragraph',
86
- * ],
87
- * // … with those attributes
88
- * attributes: {
89
- * textAlign: {
90
- * default: 'left',
91
- * renderHTML: attributes => ({
92
- * style: `text-align: ${attributes.textAlign}`,
93
- * }),
94
- * parseHTML: element => element.style.textAlign || 'left',
95
- * },
96
- * },
97
- * },
98
- * ]
99
- * }
100
- */
101
- addGlobalAttributes?: (this: {
102
- name: string
103
- options: Options
104
- storage: Storage
105
- extensions: (Node | Mark)[]
106
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes']
107
- }) => GlobalAttributes
108
-
109
- /**
110
- * This function adds commands to the editor
111
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#commands
112
- * @example
113
- * addCommands() {
114
- * return {
115
- * myCommand: () => ({ chain }) => chain().setMark('type', 'foo').run(),
116
- * }
117
- * }
118
- */
119
- addCommands?: (this: {
120
- name: string
121
- options: Options
122
- storage: Storage
123
- editor: Editor
124
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['addCommands']
125
- }) => Partial<RawCommands>
126
-
127
- /**
128
- * This function registers keyboard shortcuts.
129
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#keyboard-shortcuts
130
- * @example
131
- * addKeyboardShortcuts() {
132
- * return {
133
- * 'Mod-l': () => this.editor.commands.toggleBulletList(),
134
- * }
135
- * },
136
- */
137
- addKeyboardShortcuts?: (this: {
138
- name: string
139
- options: Options
140
- storage: Storage
141
- editor: Editor
142
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['addKeyboardShortcuts']
143
- }) => {
144
- [key: string]: KeyboardShortcutCommand
145
- }
146
-
147
- /**
148
- * This function adds input rules to the editor.
149
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#input-rules
150
- * @example
151
- * addInputRules() {
152
- * return [
153
- * markInputRule({
154
- * find: inputRegex,
155
- * type: this.type,
156
- * }),
157
- * ]
158
- * },
159
- */
160
- addInputRules?: (this: {
161
- name: string
162
- options: Options
163
- storage: Storage
164
- editor: Editor
165
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['addInputRules']
166
- }) => InputRule[]
167
-
168
- /**
169
- * This function adds paste rules to the editor.
170
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#paste-rules
171
- * @example
172
- * addPasteRules() {
173
- * return [
174
- * markPasteRule({
175
- * find: pasteRegex,
176
- * type: this.type,
177
- * }),
178
- * ]
179
- * },
180
- */
181
- addPasteRules?: (this: {
182
- name: string
183
- options: Options
184
- storage: Storage
185
- editor: Editor
186
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['addPasteRules']
187
- }) => PasteRule[]
188
-
189
- /**
190
- * This function adds Prosemirror plugins to the editor
191
- * @see https://tiptap.dev/docs/editor/guide/custom-extensions#prosemirror-plugins
192
- * @example
193
- * addProseMirrorPlugins() {
194
- * return [
195
- * customPlugin(),
196
- * ]
197
- * }
198
- */
199
- addProseMirrorPlugins?: (this: {
200
- name: string
201
- options: Options
202
- storage: Storage
203
- editor: Editor
204
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['addProseMirrorPlugins']
205
- }) => Plugin[]
206
-
207
- /**
208
- * This function adds additional extensions to the editor. This is useful for
209
- * building extension kits.
210
- * @example
211
- * addExtensions() {
212
- * return [
213
- * BulletList,
214
- * OrderedList,
215
- * ListItem
216
- * ]
217
- * }
218
- */
219
- addExtensions?: (this: {
220
- name: string
221
- options: Options
222
- storage: Storage
223
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['addExtensions']
224
- }) => Extensions
225
-
226
- /**
227
- * This function extends the schema of the node.
228
- * @example
229
- * extendNodeSchema() {
230
- * return {
231
- * group: 'inline',
232
- * selectable: false,
233
- * }
234
- * }
235
- */
236
- extendNodeSchema?:
237
- | ((
238
- this: {
239
- name: string
240
- options: Options
241
- storage: Storage
242
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendNodeSchema']
243
- },
244
- extension: Node,
245
- ) => Record<string, any>)
246
- | null
247
-
248
- /**
249
- * This function extends the schema of the mark.
250
- * @example
251
- * extendMarkSchema() {
252
- * return {
253
- * group: 'inline',
254
- * selectable: false,
255
- * }
256
- * }
257
- */
258
- extendMarkSchema?:
259
- | ((
260
- this: {
261
- name: string
262
- options: Options
263
- storage: Storage
264
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendMarkSchema']
265
- },
266
- extension: Mark,
267
- ) => Record<string, any>)
268
- | null
269
-
270
- /**
271
- * The editor is not ready yet.
272
- */
273
- onBeforeCreate?:
274
- | ((this: {
275
- name: string
276
- options: Options
277
- storage: Storage
278
- editor: Editor
279
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBeforeCreate']
280
- }) => void)
281
- | null
282
-
283
- /**
284
- * The editor is ready.
285
- */
286
- onCreate?:
287
- | ((this: {
288
- name: string
289
- options: Options
290
- storage: Storage
291
- editor: Editor
292
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onCreate']
293
- }) => void)
294
- | null
295
-
296
- /**
297
- * The content has changed.
298
- */
299
- onUpdate?:
300
- | ((this: {
301
- name: string
302
- options: Options
303
- storage: Storage
304
- editor: Editor
305
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onUpdate']
306
- }) => void)
307
- | null
308
-
309
- /**
310
- * The selection has changed.
311
- */
312
- onSelectionUpdate?:
313
- | ((this: {
314
- name: string
315
- options: Options
316
- storage: Storage
317
- editor: Editor
318
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onSelectionUpdate']
319
- }) => void)
320
- | null
321
-
322
- /**
323
- * The editor state has changed.
324
- */
325
- onTransaction?:
326
- | ((
327
- this: {
328
- name: string
329
- options: Options
330
- storage: Storage
331
- editor: Editor
332
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onTransaction']
333
- },
334
- props: {
335
- editor: Editor
336
- transaction: Transaction
337
- },
338
- ) => void)
339
- | null
340
-
341
- /**
342
- * The editor is focused.
343
- */
344
- onFocus?:
345
- | ((
346
- this: {
347
- name: string
348
- options: Options
349
- storage: Storage
350
- editor: Editor
351
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onFocus']
352
- },
353
- props: {
354
- event: FocusEvent
355
- },
356
- ) => void)
357
- | null
358
-
359
- /**
360
- * The editor isn’t focused anymore.
361
- */
362
- onBlur?:
363
- | ((
364
- this: {
365
- name: string
366
- options: Options
367
- storage: Storage
368
- editor: Editor
369
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBlur']
370
- },
371
- props: {
372
- event: FocusEvent
373
- },
374
- ) => void)
375
- | null
376
-
377
- /**
378
- * The editor is destroyed.
379
- */
380
- onDestroy?:
381
- | ((this: {
382
- name: string
383
- options: Options
384
- storage: Storage
385
- editor: Editor
386
- parent: ParentConfig<ExtensionConfig<Options, Storage>>['onDestroy']
387
- }) => void)
388
- | null
389
- }
390
- }
3
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
4
+ export interface ExtensionConfig<Options = any, Storage = any>
5
+ extends ExtendableConfig<Options, Storage, ExtensionConfig<Options, Storage>, null> {}
391
6
 
392
7
  /**
393
8
  * The Extension class is the base class for all extensions.
394
9
  * @see https://tiptap.dev/api/extensions#create-a-new-extension
395
10
  */
396
- export class Extension<Options = any, Storage = any> {
11
+ export class Extension<Options = any, Storage = any> extends Extendable<Options, Storage> {
397
12
  type = 'extension'
398
13
 
399
- name = 'extension'
400
-
401
- parent: Extension | null = null
402
-
403
- child: Extension | null = null
404
-
405
- options: Options
406
-
407
- storage: Storage
408
-
409
- config: ExtensionConfig = {
410
- name: this.name,
411
- defaultOptions: {},
412
- }
413
-
414
- constructor(config: Partial<ExtensionConfig<Options, Storage>> = {}) {
415
- this.config = {
416
- ...this.config,
417
- ...config,
418
- }
419
-
420
- this.name = this.config.name
421
-
422
- if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
423
- console.warn(
424
- `[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
425
- )
426
- }
427
-
428
- // TODO: remove `addOptions` fallback
429
- this.options = this.config.defaultOptions
430
-
431
- if (this.config.addOptions) {
432
- this.options = callOrReturn(
433
- getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
434
- name: this.name,
435
- }),
436
- )
437
- }
438
-
439
- this.storage =
440
- callOrReturn(
441
- getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
442
- name: this.name,
443
- options: this.options,
444
- }),
445
- ) || {}
446
- }
447
-
448
14
  static create<O = any, S = any>(config: Partial<ExtensionConfig<O, S>> = {}) {
449
15
  return new Extension<O, S>(config)
450
16
  }
451
-
452
- configure(options: Partial<Options> = {}) {
453
- // return a new instance so we can use the same extension
454
- // with different calls of `configure`
455
- const extension = this.extend<Options, Storage>({
456
- ...this.config,
457
- addOptions: () => {
458
- return mergeDeep(this.options as Record<string, any>, options) as Options
459
- },
460
- })
461
-
462
- // Always preserve the current name
463
- extension.name = this.name
464
- // Set the parent to be our parent
465
- extension.parent = this.parent
466
-
467
- return extension
468
- }
469
-
470
- extend<ExtendedOptions = Options, ExtendedStorage = Storage>(
471
- extendedConfig: Partial<ExtensionConfig<ExtendedOptions, ExtendedStorage>> = {},
472
- ) {
473
- const extension = new Extension<ExtendedOptions, ExtendedStorage>({ ...this.config, ...extendedConfig })
474
-
475
- extension.parent = this
476
-
477
- this.child = extension
478
-
479
- extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
480
-
481
- if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
482
- console.warn(
483
- `[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`,
484
- )
485
- }
486
-
487
- extension.options = callOrReturn(
488
- getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
489
- name: extension.name,
490
- }),
491
- )
492
-
493
- extension.storage = callOrReturn(
494
- getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
495
- name: extension.name,
496
- options: extension.options,
497
- }),
498
- )
499
-
500
- return extension
501
- }
502
17
  }
@@ -1,7 +1,7 @@
1
1
  import { keymap } from '@tiptap/pm/keymap'
2
2
  import { Schema } from '@tiptap/pm/model'
3
3
  import { Plugin } from '@tiptap/pm/state'
4
- import { NodeViewConstructor } from '@tiptap/pm/view'
4
+ import { MarkViewConstructor, NodeViewConstructor } from '@tiptap/pm/view'
5
5
 
6
6
  import type { Editor } from './Editor.js'
7
7
  import {
@@ -17,7 +17,7 @@ import {
17
17
  sortExtensions,
18
18
  splitExtensions,
19
19
  } from './helpers/index.js'
20
- import type { NodeConfig } from './index.js'
20
+ import { type MarkConfig, type NodeConfig, type Storage, getMarkType } from './index.js'
21
21
  import { InputRule, inputRulesPlugin } from './InputRule.js'
22
22
  import { Mark } from './Mark.js'
23
23
  import { PasteRule, pasteRulesPlugin } from './PasteRule.js'
@@ -55,7 +55,7 @@ export class ExtensionManager {
55
55
  const context = {
56
56
  name: extension.name,
57
57
  options: extension.options,
58
- storage: extension.storage,
58
+ storage: this.editor.extensionStorage[extension.name as keyof Storage],
59
59
  editor: this.editor,
60
60
  type: getSchemaTypeByName(extension.name, this.schema),
61
61
  }
@@ -95,7 +95,7 @@ export class ExtensionManager {
95
95
  const context = {
96
96
  name: extension.name,
97
97
  options: extension.options,
98
- storage: extension.storage,
98
+ storage: this.editor.extensionStorage[extension.name as keyof Storage],
99
99
  editor,
100
100
  type: getSchemaTypeByName(extension.name, this.schema),
101
101
  }
@@ -111,7 +111,7 @@ export class ExtensionManager {
111
111
  let defaultBindings: Record<string, () => boolean> = {}
112
112
 
113
113
  // bind exit handling
114
- if (extension.type === 'mark' && getExtensionField<AnyConfig['exitable']>(extension, 'exitable', context)) {
114
+ if (extension.type === 'mark' && getExtensionField<MarkConfig['exitable']>(extension, 'exitable', context)) {
115
115
  defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: extension as Mark })
116
116
  }
117
117
 
@@ -194,7 +194,7 @@ export class ExtensionManager {
194
194
  const context = {
195
195
  name: extension.name,
196
196
  options: extension.options,
197
- storage: extension.storage,
197
+ storage: this.editor.extensionStorage[extension.name as keyof Storage],
198
198
  editor,
199
199
  type: getNodeType(extension.name, this.schema),
200
200
  }
@@ -226,19 +226,64 @@ export class ExtensionManager {
226
226
  )
227
227
  }
228
228
 
229
+ get markViews(): Record<string, MarkViewConstructor> {
230
+ const { editor } = this
231
+ const { markExtensions } = splitExtensions(this.extensions)
232
+
233
+ return Object.fromEntries(
234
+ markExtensions
235
+ .filter(extension => !!getExtensionField(extension, 'addMarkView'))
236
+ .map(extension => {
237
+ const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)
238
+ const context = {
239
+ name: extension.name,
240
+ options: extension.options,
241
+ storage: this.editor.extensionStorage[extension.name as keyof Storage],
242
+ editor,
243
+ type: getMarkType(extension.name, this.schema),
244
+ }
245
+ const addMarkView = getExtensionField<MarkConfig['addMarkView']>(extension, 'addMarkView', context)
246
+
247
+ if (!addMarkView) {
248
+ return []
249
+ }
250
+
251
+ const markView: MarkViewConstructor = (mark, view, inline) => {
252
+ const HTMLAttributes = getRenderedAttributes(mark, extensionAttributes)
253
+
254
+ return addMarkView()({
255
+ // pass-through
256
+ mark,
257
+ view,
258
+ inline,
259
+ // tiptap-specific
260
+ editor,
261
+ extension,
262
+ HTMLAttributes,
263
+ })
264
+ }
265
+
266
+ return [extension.name, markView]
267
+ }),
268
+ )
269
+ }
270
+
229
271
  /**
230
272
  * Go through all extensions, create extension storages & setup marks
231
273
  * & bind editor event listener.
232
274
  */
233
275
  private setupExtensions() {
234
- this.extensions.forEach(extension => {
235
- // store extension storage in editor
236
- this.editor.extensionStorage[extension.name] = extension.storage
276
+ const extensions = this.extensions
277
+ // re-initialize the extension storage object instance
278
+ this.editor.extensionStorage = Object.fromEntries(
279
+ extensions.map(extension => [extension.name, extension.storage]),
280
+ ) as unknown as Storage
237
281
 
282
+ extensions.forEach(extension => {
238
283
  const context = {
239
284
  name: extension.name,
240
285
  options: extension.options,
241
- storage: extension.storage,
286
+ storage: this.editor.extensionStorage[extension.name as keyof Storage],
242
287
  editor: this.editor,
243
288
  type: getSchemaTypeByName(extension.name, this.schema),
244
289
  }