diff --git a/src/docview.ts b/src/docview.ts index 8b48107..4a553a5 100644 --- a/src/docview.ts +++ b/src/docview.ts @@ -9,7 +9,7 @@ import {getAttrs} from "./attributes" import {clientRectsFor, isEquivalentPosition, maxOffset, Rect, scrollRectIntoView, getSelection, hasSelection, textRange, DOMSelectionState} from "./dom" import {ViewUpdate, decorations as decorationsFacet, outerDecorations, - ChangedRange, ScrollTarget, getScrollMargins} from "./extension" + ChangedRange, ScrollTarget, scrollHandler, getScrollMargins, logException} from "./extension" import {EditorView} from "./editorview" import {Direction} from "./bidi" @@ -558,6 +558,11 @@ export class DocView extends ContentView { return } + for (let handler of this.view.state.facet(scrollHandler)) { + try { if (handler(this.view, target.range, target)) return true } + catch(e) { logException(this.view.state, e, "scroll handler") } + } + let {range} = target let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other if (!rect) return diff --git a/src/editorview.ts b/src/editorview.ts index 5ed21d8..403d1b8 100644 --- a/src/editorview.ts +++ b/src/editorview.ts @@ -16,7 +16,7 @@ import {ViewUpdate, styleModule, exceptionSink, updateListener, logException, viewPlugin, ViewPlugin, PluginValue, PluginInstance, decorations, outerDecorations, atomicRanges, scrollMargins, MeasureRequest, editable, inputHandler, focusChangeEffect, perLineTextDirection, - scrollIntoView, UpdateFlag, ScrollTarget, bidiIsolatedRanges, getIsolatedRanges} from "./extension" + scrollIntoView, UpdateFlag, ScrollTarget, bidiIsolatedRanges, getIsolatedRanges, scrollHandler} from "./extension" import {theme, darkTheme, buildTheme, baseThemeID, baseLightID, baseDarkID, lightDarkIDs, baseTheme} from "./theme" import {DOMObserver} from "./domobserver" import {Attrs, updateAttrs, combineAttrs} from "./attributes" @@ -945,6 +945,12 @@ export class EditorView { /// dispatching the custom behavior as a separate transaction. static inputHandler = inputHandler + /// Scroll handlers can override how things are scrolled into view. + /// If they return `true`, no further handling happens for the + /// scrolling. If they return false, the default scroll behavior is + /// applied. Scroll handlers should never initiate editor updates. + static scrollHandler = scrollHandler + /// This facet can be used to provide functions that create effects /// to be dispatched when the editor's focus state changes. static focusChangeEffect = focusChangeEffect diff --git a/src/extension.ts b/src/extension.ts index 8384410..6a573b0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -38,6 +38,12 @@ export const nativeSelectionHidden = Facet.define({ combine: values => values.some(x => x) }) +export const scrollHandler = Facet.define<( + view: EditorView, + range: SelectionRange, + options: {x: ScrollStrategy, y: ScrollStrategy, xMargin: number, yMargin: number} +) => boolean>() + export class ScrollTarget { constructor( readonly range: SelectionRange,