1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-30 02:19:52 +02:00

refactor scroll logic (#1428)

This commit is contained in:
Evan Henley
2017-12-04 13:27:54 -06:00
committed by Ian Storm Taylor
parent a4028cac6b
commit 4f1c26961d

View File

@@ -20,13 +20,13 @@ const OVERFLOWS = [
* @param {el} Element
*/
function findScrollContainer(el) {
const window = getWindow(el)
function findScrollContainer(el, window) {
let parent = el.parentNode
let scroller
while (!scroller) {
if (!parent.parentNode) break
const style = window.getComputedStyle(parent)
const { overflowY } = style
@@ -38,7 +38,14 @@ function findScrollContainer(el) {
parent = parent.parentNode
}
if (!scroller) return window
// COMPAT: Because Chrome does not allow doucment.body.scrollTop, we're
// assuming that window.scrollTo() should be used if the scrollable element
// turns out to be document.body or document.documentElement. This will work
// unless body is intentionally set to scrollable by restricting its height
// (e.g. height: 100vh).
if (!scroller) {
return window.document.body
}
return scroller
}
@@ -53,41 +60,88 @@ function scrollToSelection(selection) {
if (!selection.anchorNode) return
const window = getWindow(selection.anchorNode)
const scroller = findScrollContainer(selection.anchorNode)
const isWindow = scroller == window
const scroller = findScrollContainer(selection.anchorNode, window)
const isWindow = scroller == window.document.body || scroller == window.document.documentElement
const backward = isBackward(selection)
const range = selection.getRangeAt(0)
const rect = range.getBoundingClientRect()
let selectionRect = range.getBoundingClientRect()
// COMPAT: range.getBoundingClientRect() returns 0s in Safari when range is
// collapsed. Expanding the range by 1 is a relatively effective workaround
// for vertical scroll, although horizontal may be off by 1 character.
// https://bugs.webkit.org/show_bug.cgi?id=138949
// https://bugs.chromium.org/p/chromium/issues/detail?id=435438
if (range.collapsed && selectionRect.top == 0 && selectionRect.height == 0) {
if (range.startOffset == 0) {
range.setEnd(range.endContainer, 1)
} else {
range.setStart(range.startContainer, range.startOffset - 1)
}
selectionRect = range.getBoundingClientRect()
}
let width
let height
let yOffset
let xOffset
let scrollerTop = 0
let scrollerLeft = 0
let scrollerBordersY = 0
let scrollerBordersX = 0
if (isWindow) {
const { innerWidth, innerHeight, pageYOffset, pageXOffset } = scroller
const { innerWidth, innerHeight, pageYOffset, pageXOffset } = window
width = innerWidth
height = innerHeight
yOffset = pageYOffset
xOffset = pageXOffset
} else {
const { offsetWidth, offsetHeight, scrollTop, scrollLeft } = scroller
const {
borderTopWidth,
borderBottomWidth,
borderLeftWidth,
borderRightWidth,
} = window.getComputedStyle(scroller)
const scrollerRect = scroller.getBoundingClientRect()
width = offsetWidth
height = offsetHeight
yOffset = scrollTop - scrollerRect.top
xOffset = scrollLeft - scrollerRect.left
scrollerTop = scrollerRect.top + parseInt(borderTopWidth, 10)
scrollerLeft = scrollerRect.left + parseInt(borderLeftWidth, 10)
scrollerBordersY = parseInt(borderTopWidth, 10) + parseInt(borderBottomWidth, 10)
scrollerBordersX = parseInt(borderLeftWidth, 10) + parseInt(borderRightWidth, 10)
yOffset = scrollTop
xOffset = scrollLeft
}
const top = (backward ? rect.top : rect.bottom) + yOffset
const left = (backward ? rect.left : rect.right) + xOffset
const selectionFocusTop = backward ? selectionRect.top : selectionRect.bottom
const selectionTop = selectionFocusTop + yOffset - scrollerTop
const x = left < yOffset || (width + xOffset) < left
? left - width / 2
: xOffset
const selectionFocusLeft = backward ? selectionRect.left : selectionRect.right
const selectionLeft = selectionFocusLeft + xOffset - scrollerLeft
let x = xOffset
let y = yOffset
if (selectionLeft < xOffset) {
// selection to the left of viewport
x = selectionLeft
} else if (selectionLeft + selectionRect.width + scrollerBordersX > xOffset + width) {
// selection to the right of viewport
x = selectionLeft + scrollerBordersX - width
}
if (selectionTop < yOffset) {
// selection above viewport
y = selectionTop
} else if (selectionTop + selectionRect.height + scrollerBordersY > yOffset + height) {
// selection below viewport
y = selectionTop + scrollerBordersY + selectionRect.height - height
}
const y = top < yOffset || (height + yOffset) < top
? top - height / 2
: yOffset
if (isWindow) {
window.scrollTo(x, y)