Skip to content

Commit

Permalink
Allow the editor to be moved to a new window/shadow root with setRoot
Browse files Browse the repository at this point in the history
FEATURE: The new `EditorView.setRoot` method can be used when an editor view is
moved to a new document or shadow root.

See https://discuss.codemirror.net/t/cursor-and-autocomplete-disappearing-when-moving-to-new-window/4800
  • Loading branch information
marijnh committed Aug 5, 2022
1 parent f3a6911 commit 7dfd24b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 10 deletions.
37 changes: 29 additions & 8 deletions src/domobserver.ts
Expand Up @@ -19,6 +19,7 @@ const useCharData = browser.ie && browser.ie_version <= 11

export class DOMObserver {
dom: HTMLElement
win: Window

observer: MutationObserver
active: boolean = false
Expand Down Expand Up @@ -82,19 +83,21 @@ export class DOMObserver {
}

this.onSelectionChange = this.onSelectionChange.bind(this)
this.onResize = this.onResize.bind(this)
this.onPrint = this.onPrint.bind(this)
this.onScroll = this.onScroll.bind(this)

window.addEventListener("resize", this.onResize = this.onResize.bind(this))
if (typeof ResizeObserver == "function") {
this.resize = new ResizeObserver(() => {
if (this.view.docView.lastUpdate < Date.now() - 75) this.onResize()
})
this.resize.observe(view.scrollDOM)
}
window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this))
this.win = view.dom.ownerDocument.defaultView
this.addWindowListeners(this.win)

this.start()

window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this))
if (typeof IntersectionObserver == "function") {
this.intersection = new IntersectionObserver(entries => {
if (this.parentCheck < 0) this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000)
Expand All @@ -112,7 +115,6 @@ export class DOMObserver {
}
this.listenForScroll()
this.readSelectionRange()
this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange)
}

onScroll(e: Event) {
Expand Down Expand Up @@ -350,16 +352,35 @@ export class DOMObserver {
}
}

setWindow(win: Window) {
if (win != this.win) {
this.removeWindowListeners(this.win)
this.win = win
this.addWindowListeners(this.win)
}
}

addWindowListeners(win: Window) {
win.addEventListener("resize", this.onResize)
win.addEventListener("beforeprint", this.onPrint)
win.addEventListener("scroll", this.onScroll)
win.document.addEventListener("selectionchange", this.onSelectionChange)
}

removeWindowListeners(win: Window) {
win.removeEventListener("scroll", this.onScroll)
win.removeEventListener("resize", this.onResize)
win.removeEventListener("beforeprint", this.onPrint)
win.document.removeEventListener("selectionchange", this.onSelectionChange)
}

destroy() {
this.stop()
this.intersection?.disconnect()
this.gapIntersection?.disconnect()
this.resize?.disconnect()
for (let dom of this.scrollTargets) dom.removeEventListener("scroll", this.onScroll)
window.removeEventListener("scroll", this.onScroll)
window.removeEventListener("resize", this.onResize)
window.removeEventListener("beforeprint", this.onPrint)
this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange)
this.removeWindowListeners(this.win)
clearTimeout(this.parentCheck)
clearTimeout(this.resizeTimeout)
}
Expand Down
16 changes: 14 additions & 2 deletions src/editorview.ts
Expand Up @@ -109,8 +109,10 @@ export class EditorView {

private _dispatch: (tr: Transaction) => void

private _root: DocumentOrShadowRoot

/// The document or shadow root that the view lives in.
readonly root: DocumentOrShadowRoot
get root() { return this._root }

/// The DOM element that wraps the entire editor view.
readonly dom: HTMLElement
Expand Down Expand Up @@ -178,7 +180,7 @@ export class EditorView {

this._dispatch = config.dispatch || ((tr: Transaction) => this.update([tr]))
this.dispatch = this.dispatch.bind(this)
this.root = (config.root || getRoot(config.parent) || document) as DocumentOrShadowRoot
this._root = (config.root || getRoot(config.parent) || document) as DocumentOrShadowRoot

this.viewState = new ViewState(config.state || EditorState.create(config))
this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec))
Expand Down Expand Up @@ -702,6 +704,16 @@ export class EditorView {
})
}

/// Update the [root](##view.EditorViewConfig.root) in which the editor lives. This is only
/// necessary when moving the editor's existing DOM to a new window or shadow root.
setRoot(root: Document | ShadowRoot) {
if (this._root != root) {
this._root = root
this.observer.setWindow((root.nodeType == 9 ? root as Document : root.ownerDocument!).defaultView!)
this.mountStyles()
}
}

/// Clean up this editor view, removing its element from the
/// document, unregistering event handlers, and notifying
/// plugins. The view instance can no longer be used after
Expand Down

0 comments on commit 7dfd24b

Please sign in to comment.