@tiptap/core 3.14.0 → 3.15.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/core",
3
3
  "description": "headless rich text editor",
4
- "version": "3.14.0",
4
+ "version": "3.15.0",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -52,10 +52,10 @@
52
52
  "jsx-dev-runtime"
53
53
  ],
54
54
  "devDependencies": {
55
- "@tiptap/pm": "^3.14.0"
55
+ "@tiptap/pm": "^3.15.0"
56
56
  },
57
57
  "peerDependencies": {
58
- "@tiptap/pm": "^3.14.0"
58
+ "@tiptap/pm": "^3.15.0"
59
59
  },
60
60
  "repository": {
61
61
  "type": "git",
package/src/Editor.ts CHANGED
@@ -115,6 +115,7 @@ export class Editor extends EventEmitter<EditorEvents> {
115
115
  onPaste: () => null,
116
116
  onDrop: () => null,
117
117
  onDelete: () => null,
118
+ enableExtensionDispatchTransaction: true,
118
119
  }
119
120
 
120
121
  constructor(options: Partial<EditorOptions> = {}) {
@@ -518,14 +519,23 @@ export class Editor extends EventEmitter<EditorEvents> {
518
519
  * Creates a ProseMirror view.
519
520
  */
520
521
  private createView(element: NonNullable<EditorOptions['element']>): void {
522
+ const { editorProps, enableExtensionDispatchTransaction } = this.options
523
+ // If a user provided a custom `dispatchTransaction` through `editorProps`,
524
+ // we use that as the base dispatch function.
525
+ // Otherwise, we use Tiptap's internal `dispatchTransaction` method.
526
+ const baseDispatch = (editorProps as any).dispatchTransaction || this.dispatchTransaction.bind(this)
527
+ const dispatch = enableExtensionDispatchTransaction
528
+ ? this.extensionManager.dispatchTransaction(baseDispatch)
529
+ : baseDispatch
530
+
521
531
  this.editorView = new EditorView(element, {
522
- ...this.options.editorProps,
532
+ ...editorProps,
523
533
  attributes: {
524
534
  // add `role="textbox"` to the editor element
525
535
  role: 'textbox',
526
- ...this.options.editorProps?.attributes,
536
+ ...editorProps?.attributes,
527
537
  },
528
- dispatchTransaction: this.dispatchTransaction.bind(this),
538
+ dispatchTransaction: dispatch,
529
539
  state: this.editorState,
530
540
  markViews: this.extensionManager.markViews,
531
541
  nodeViews: this.extensionManager.nodeViews,
package/src/Extendable.ts CHANGED
@@ -9,6 +9,7 @@ import type { Node } from './Node.js'
9
9
  import type { PasteRule } from './PasteRule.js'
10
10
  import type {
11
11
  AnyConfig,
12
+ DispatchTransactionProps,
12
13
  EditorEvents,
13
14
  Extensions,
14
15
  GlobalAttributes,
@@ -447,6 +448,33 @@ export interface ExtendableConfig<
447
448
  event: EditorEvents['destroy'],
448
449
  ) => void)
449
450
  | null
451
+
452
+ /**
453
+ * This hook allows you to intercept and modify transactions before they are dispatched.
454
+ *
455
+ * Example
456
+ * ```ts
457
+ * dispatchTransaction({ transaction, next }) {
458
+ * console.log('Dispatching transaction:', transaction)
459
+ * next(transaction)
460
+ * }
461
+ * ```
462
+ *
463
+ * @param props - The dispatch transaction props
464
+ */
465
+ dispatchTransaction?:
466
+ | ((
467
+ this: {
468
+ name: string
469
+ options: Options
470
+ storage: Storage
471
+ editor: Editor
472
+ type: PMType
473
+ parent: ParentConfig<Config>['dispatchTransaction']
474
+ },
475
+ props: DispatchTransactionProps,
476
+ ) => void)
477
+ | null
450
478
  }
451
479
 
452
480
  export class Extendable<
@@ -1,6 +1,6 @@
1
1
  import { keymap } from '@tiptap/pm/keymap'
2
2
  import type { Schema } from '@tiptap/pm/model'
3
- import type { Plugin } from '@tiptap/pm/state'
3
+ import type { Plugin, Transaction } from '@tiptap/pm/state'
4
4
  import type { MarkViewConstructor, NodeViewConstructor } from '@tiptap/pm/view'
5
5
 
6
6
  import type { Editor } from './Editor.js'
@@ -243,6 +243,40 @@ export class ExtensionManager {
243
243
  )
244
244
  }
245
245
 
246
+ /**
247
+ * Get the composed dispatchTransaction function from all extensions.
248
+ * @param baseDispatch The base dispatch function (e.g. from the editor or user props)
249
+ * @returns A composed dispatch function
250
+ */
251
+ dispatchTransaction(baseDispatch: (tr: Transaction) => void): (tr: Transaction) => void {
252
+ const { editor } = this
253
+ const extensions = sortExtensions([...this.extensions].reverse())
254
+
255
+ return extensions.reduceRight((next, extension) => {
256
+ const context = {
257
+ name: extension.name,
258
+ options: extension.options,
259
+ storage: this.editor.extensionStorage[extension.name as keyof Storage],
260
+ editor,
261
+ type: getSchemaTypeByName(extension.name, this.schema),
262
+ }
263
+
264
+ const dispatchTransaction = getExtensionField<AnyConfig['dispatchTransaction']>(
265
+ extension,
266
+ 'dispatchTransaction',
267
+ context,
268
+ )
269
+
270
+ if (!dispatchTransaction) {
271
+ return next
272
+ }
273
+
274
+ return (transaction: Transaction) => {
275
+ dispatchTransaction.call(context, { transaction, next })
276
+ }
277
+ }, baseDispatch)
278
+ }
279
+
246
280
  get markViews(): Record<string, MarkViewConstructor> {
247
281
  const { editor } = this
248
282
  const { markExtensions } = splitExtensions(this.extensions)
package/src/types.ts CHANGED
@@ -264,6 +264,23 @@ export interface EditorEvents {
264
264
  )
265
265
  }
266
266
 
267
+ /**
268
+ * Props passed to the `dispatchTransaction` hook in extensions.
269
+ */
270
+ export type DispatchTransactionProps = {
271
+ /**
272
+ * The transaction that is about to be dispatched.
273
+ */
274
+ transaction: Transaction
275
+ /**
276
+ * A function that should be called to pass the transaction down to the next extension
277
+ * (or eventually to the editor).
278
+ *
279
+ * @param transaction The transaction to dispatch
280
+ */
281
+ next: (transaction: Transaction) => void
282
+ }
283
+
267
284
  export type EnableRules = (AnyExtension | string)[] | boolean
268
285
 
269
286
  export interface EditorOptions {
@@ -450,6 +467,17 @@ export interface EditorOptions {
450
467
  * Called when content is deleted from the editor.
451
468
  */
452
469
  onDelete: (props: EditorEvents['delete']) => void
470
+ /**
471
+ * Whether to enable extension-level dispatching of transactions.
472
+ * If `false`, extensions cannot define their own `dispatchTransaction` hook.
473
+ *
474
+ * @default true
475
+ * @example
476
+ * new Editor({
477
+ * enableExtensionDispatchTransaction: false,
478
+ * })
479
+ */
480
+ enableExtensionDispatchTransaction?: boolean
453
481
  }
454
482
 
455
483
  /**