-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Expand file tree
/
Copy pathnodeViewPositionRegistry.ts
More file actions
70 lines (59 loc) · 2.05 KB
/
nodeViewPositionRegistry.ts
File metadata and controls
70 lines (59 loc) · 2.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import type { Editor } from '../Editor.js'
/**
* Per-editor registry for centralized NodeView position-change checks.
* A single editor.on('update') listener + rAF is shared across all NodeViews
* for a given editor, keeping overhead bounded regardless of NodeView count.
*
* This is consumed by React, Vue 3, and Vue 2 NodeView renderers.
*/
interface PositionUpdateRegistry {
callbacks: Set<() => void>
rafId: number | null
handler: () => void
}
const positionUpdateRegistries = new WeakMap<Editor, PositionUpdateRegistry>()
/**
* Register a callback to be called (via a shared rAF) after every editor
* update transaction. If this is the first registration for the given editor,
* a new registry entry and a single `editor.on('update')` listener are created.
*/
export function schedulePositionCheck(editor: Editor, callback: () => void): void {
let registry = positionUpdateRegistries.get(editor)
if (!registry) {
const newRegistry: PositionUpdateRegistry = {
callbacks: new Set(),
rafId: null,
handler: () => {
if (newRegistry.rafId !== null) {
cancelAnimationFrame(newRegistry.rafId)
}
newRegistry.rafId = requestAnimationFrame(() => {
newRegistry.rafId = null
newRegistry.callbacks.forEach(cb => cb())
})
},
}
positionUpdateRegistries.set(editor, newRegistry)
editor.on('update', newRegistry.handler)
registry = newRegistry
}
registry.callbacks.add(callback)
}
/**
* Unregister a previously registered callback. When the last callback for an
* editor is removed, the shared listener and any pending rAF are also cleaned up.
*/
export function cancelPositionCheck(editor: Editor, callback: () => void): void {
const registry = positionUpdateRegistries.get(editor)
if (!registry) {
return
}
registry.callbacks.delete(callback)
if (registry.callbacks.size === 0) {
if (registry.rafId !== null) {
cancelAnimationFrame(registry.rafId)
}
editor.off('update', registry.handler)
positionUpdateRegistries.delete(editor)
}
}