Skip to content

Commit

Permalink
Determine drag-scrolling parent elements separately for x and y axis
Browse files Browse the repository at this point in the history
FIX: Fix a bug that broke drag scrolling along one axis when the innermost
scrollable element around the editor was only scrollable along the other axis.

Closes codemirror/dev#1407
  • Loading branch information
marijnh committed Jul 15, 2024
1 parent 2e98dae commit ed78a0c
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 23 deletions.
11 changes: 6 additions & 5 deletions src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,21 +208,22 @@ export function scrollRectIntoView(dom: HTMLElement, rect: Rect, side: -1 | 1,
}
}

export function scrollableParent(dom: HTMLElement) {
let doc = dom.ownerDocument
export function scrollableParents(dom: HTMLElement) {
let doc = dom.ownerDocument, x: HTMLElement | undefined, y: HTMLElement | undefined
for (let cur = dom.parentNode as HTMLElement | null; cur;) {
if (cur == doc.body) {
if (cur == doc.body || (x && y)) {
break
} else if (cur.nodeType == 1) {
if (cur.scrollHeight > cur.clientHeight || cur.scrollWidth > cur.clientWidth) return cur
if (!y && cur.scrollHeight > cur.clientHeight) y = cur
if (!x && cur.scrollWidth > cur.clientWidth) x = cur
cur = cur.assignedSlot || cur.parentNode as HTMLElement | null
} else if (cur.nodeType == 11) {
cur = (cur as any).host
} else {
break
}
}
return null
return {x, y}
}

export interface SelectionRange {
Expand Down
41 changes: 23 additions & 18 deletions src/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {ViewUpdate, PluginValue, clickAddsSelectionRange, dragMovesSelection as
logException, mouseSelectionStyle, PluginInstance, focusChangeEffect, getScrollMargins} from "./extension"
import browser from "./browser"
import {groupAt, skipAtomicRanges} from "./cursor"
import {getSelection, focusPreventScroll, Rect, dispatchKey, scrollableParent} from "./dom"
import {getSelection, focusPreventScroll, Rect, dispatchKey, scrollableParents} from "./dom"

// This will also be where dragging info and such goes
export class InputState {
Expand Down Expand Up @@ -287,7 +287,7 @@ class MouseSelection {
extend: boolean
multiple: boolean
lastEvent: MouseEvent
scrollParent: HTMLElement | null
scrollParents: {x?: HTMLElement, y?: HTMLElement}
scrollSpeed = {x: 0, y: 0}
scrolling = -1
atoms: readonly RangeSet<any>[]
Expand All @@ -297,7 +297,7 @@ class MouseSelection {
private style: MouseSelectionStyle,
private mustSelect: boolean) {
this.lastEvent = startEvent
this.scrollParent = scrollableParent(view.contentDOM)
this.scrollParents = scrollableParents(view.contentDOM)
this.atoms = view.state.facet(atomicRanges).map(f => f(view))
let doc = view.contentDOM.ownerDocument!
doc.addEventListener("mousemove", this.move = this.move.bind(this))
Expand All @@ -320,18 +320,19 @@ class MouseSelection {
this.select(this.lastEvent = event)

let sx = 0, sy = 0
let rect = this.scrollParent?.getBoundingClientRect()
|| {left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight}
let left = 0, top = 0, right = this.view.win.innerWidth, bottom = this.view.win.innerHeight
if (this.scrollParents.x) ({left, right} = this.scrollParents.x.getBoundingClientRect())
if (this.scrollParents.y) ({top, bottom} = this.scrollParents.y.getBoundingClientRect())
let margins = getScrollMargins(this.view)

if (event.clientX - margins.left <= rect.left + dragScrollMargin)
sx = -dragScrollSpeed(rect.left - event.clientX)
else if (event.clientX + margins.right >= rect.right - dragScrollMargin)
sx = dragScrollSpeed(event.clientX - rect.right)
if (event.clientY - margins.top <= rect.top + dragScrollMargin)
sy = -dragScrollSpeed(rect.top - event.clientY)
else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin)
sy = dragScrollSpeed(event.clientY - rect.bottom)
if (event.clientX - margins.left <= left + dragScrollMargin)
sx = -dragScrollSpeed(left - event.clientX)
else if (event.clientX + margins.right >= right - dragScrollMargin)
sx = dragScrollSpeed(event.clientX - right)
if (event.clientY - margins.top <= top + dragScrollMargin)
sy = -dragScrollSpeed(top - event.clientY)
else if (event.clientY + margins.bottom >= bottom - dragScrollMargin)
sy = dragScrollSpeed(event.clientY - bottom)
this.setScrollSpeed(sx, sy)
}

Expand Down Expand Up @@ -360,12 +361,16 @@ class MouseSelection {
}

scroll() {
if (this.scrollParent) {
this.scrollParent.scrollLeft += this.scrollSpeed.x
this.scrollParent.scrollTop += this.scrollSpeed.y
} else {
this.view.win.scrollBy(this.scrollSpeed.x, this.scrollSpeed.y)
let {x, y} = this.scrollSpeed
if (x && this.scrollParents.x) {
this.scrollParents.x.scrollLeft += x
x = 0
}
if (y && this.scrollParents.y) {
this.scrollParents.y.scrollTop += y
y = 0
}
if (x || y) this.view.win.scrollBy(x, y)
if (this.dragging === false) this.select(this.lastEvent)
}

Expand Down

0 comments on commit ed78a0c

Please sign in to comment.