@tiptap/core 3.22.0 → 3.22.1

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.22.0",
4
+ "version": "3.22.1",
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.22.0"
55
+ "@tiptap/pm": "^3.22.1"
56
56
  },
57
57
  "peerDependencies": {
58
- "@tiptap/pm": "^3.22.0"
58
+ "@tiptap/pm": "^3.22.1"
59
59
  },
60
60
  "repository": {
61
61
  "type": "git",
package/src/NodeView.ts CHANGED
@@ -173,6 +173,7 @@ export class NodeView<
173
173
  }
174
174
 
175
175
  const isDragEvent = event.type.startsWith('drag')
176
+ const isDragOverEnterEvent = event.type === 'dragover' || event.type === 'dragenter'
176
177
  const isDropEvent = event.type === 'drop'
177
178
  const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable
178
179
 
@@ -237,7 +238,15 @@ export class NodeView<
237
238
  }
238
239
 
239
240
  // these events are handled by prosemirror
240
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || (isClickEvent && isSelectable)) {
241
+ if (
242
+ isDragging ||
243
+ isDragOverEnterEvent ||
244
+ isDropEvent ||
245
+ isCopyEvent ||
246
+ isPasteEvent ||
247
+ isCutEvent ||
248
+ (isClickEvent && isSelectable)
249
+ ) {
241
250
  return false
242
251
  }
243
252
 
@@ -23,5 +23,6 @@ export * as markdown from './markdown/index.js'
23
23
  export * from './mergeAttributes.js'
24
24
  export * from './mergeDeep.js'
25
25
  export * from './minMax.js'
26
+ export * from './nodeViewPositionRegistry.js'
26
27
  export * from './objectIncludes.js'
27
28
  export * from './removeDuplicates.js'
@@ -0,0 +1,70 @@
1
+ import type { Editor } from '../Editor.js'
2
+
3
+ /**
4
+ * Per-editor registry for centralized NodeView position-change checks.
5
+ * A single editor.on('update') listener + rAF is shared across all NodeViews
6
+ * for a given editor, keeping overhead bounded regardless of NodeView count.
7
+ *
8
+ * This is consumed by React, Vue 3, and Vue 2 NodeView renderers.
9
+ */
10
+ interface PositionUpdateRegistry {
11
+ callbacks: Set<() => void>
12
+ rafId: number | null
13
+ handler: () => void
14
+ }
15
+
16
+ const positionUpdateRegistries = new WeakMap<Editor, PositionUpdateRegistry>()
17
+
18
+ /**
19
+ * Register a callback to be called (via a shared rAF) after every editor
20
+ * update transaction. If this is the first registration for the given editor,
21
+ * a new registry entry and a single `editor.on('update')` listener are created.
22
+ */
23
+ export function schedulePositionCheck(editor: Editor, callback: () => void): void {
24
+ let registry = positionUpdateRegistries.get(editor)
25
+
26
+ if (!registry) {
27
+ const newRegistry: PositionUpdateRegistry = {
28
+ callbacks: new Set(),
29
+ rafId: null,
30
+ handler: () => {
31
+ if (newRegistry.rafId !== null) {
32
+ cancelAnimationFrame(newRegistry.rafId)
33
+ }
34
+ newRegistry.rafId = requestAnimationFrame(() => {
35
+ newRegistry.rafId = null
36
+ newRegistry.callbacks.forEach(cb => cb())
37
+ })
38
+ },
39
+ }
40
+
41
+ positionUpdateRegistries.set(editor, newRegistry)
42
+ editor.on('update', newRegistry.handler)
43
+ registry = newRegistry
44
+ }
45
+
46
+ registry.callbacks.add(callback)
47
+ }
48
+
49
+ /**
50
+ * Unregister a previously registered callback. When the last callback for an
51
+ * editor is removed, the shared listener and any pending rAF are also cleaned up.
52
+ */
53
+ export function cancelPositionCheck(editor: Editor, callback: () => void): void {
54
+ const registry = positionUpdateRegistries.get(editor)
55
+
56
+ if (!registry) {
57
+ return
58
+ }
59
+
60
+ registry.callbacks.delete(callback)
61
+
62
+ if (registry.callbacks.size === 0) {
63
+ if (registry.rafId !== null) {
64
+ cancelAnimationFrame(registry.rafId)
65
+ }
66
+
67
+ editor.off('update', registry.handler)
68
+ positionUpdateRegistries.delete(editor)
69
+ }
70
+ }