mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-31 02:49:56 +02:00
refactor scroll logic (#1428)
This commit is contained in:
committed by
Ian Storm Taylor
parent
a4028cac6b
commit
4f1c26961d
@@ -20,13 +20,13 @@ const OVERFLOWS = [
|
|||||||
* @param {el} Element
|
* @param {el} Element
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function findScrollContainer(el) {
|
function findScrollContainer(el, window) {
|
||||||
const window = getWindow(el)
|
|
||||||
let parent = el.parentNode
|
let parent = el.parentNode
|
||||||
let scroller
|
let scroller
|
||||||
|
|
||||||
while (!scroller) {
|
while (!scroller) {
|
||||||
if (!parent.parentNode) break
|
if (!parent.parentNode) break
|
||||||
|
|
||||||
const style = window.getComputedStyle(parent)
|
const style = window.getComputedStyle(parent)
|
||||||
const { overflowY } = style
|
const { overflowY } = style
|
||||||
|
|
||||||
@@ -38,7 +38,14 @@ function findScrollContainer(el) {
|
|||||||
parent = parent.parentNode
|
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
|
return scroller
|
||||||
}
|
}
|
||||||
@@ -53,41 +60,88 @@ function scrollToSelection(selection) {
|
|||||||
if (!selection.anchorNode) return
|
if (!selection.anchorNode) return
|
||||||
|
|
||||||
const window = getWindow(selection.anchorNode)
|
const window = getWindow(selection.anchorNode)
|
||||||
const scroller = findScrollContainer(selection.anchorNode)
|
const scroller = findScrollContainer(selection.anchorNode, window)
|
||||||
const isWindow = scroller == window
|
const isWindow = scroller == window.document.body || scroller == window.document.documentElement
|
||||||
const backward = isBackward(selection)
|
const backward = isBackward(selection)
|
||||||
|
|
||||||
const range = selection.getRangeAt(0)
|
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 width
|
||||||
let height
|
let height
|
||||||
let yOffset
|
let yOffset
|
||||||
let xOffset
|
let xOffset
|
||||||
|
let scrollerTop = 0
|
||||||
|
let scrollerLeft = 0
|
||||||
|
let scrollerBordersY = 0
|
||||||
|
let scrollerBordersX = 0
|
||||||
|
|
||||||
if (isWindow) {
|
if (isWindow) {
|
||||||
const { innerWidth, innerHeight, pageYOffset, pageXOffset } = scroller
|
const { innerWidth, innerHeight, pageYOffset, pageXOffset } = window
|
||||||
width = innerWidth
|
width = innerWidth
|
||||||
height = innerHeight
|
height = innerHeight
|
||||||
yOffset = pageYOffset
|
yOffset = pageYOffset
|
||||||
xOffset = pageXOffset
|
xOffset = pageXOffset
|
||||||
} else {
|
} else {
|
||||||
const { offsetWidth, offsetHeight, scrollTop, scrollLeft } = scroller
|
const { offsetWidth, offsetHeight, scrollTop, scrollLeft } = scroller
|
||||||
|
const {
|
||||||
|
borderTopWidth,
|
||||||
|
borderBottomWidth,
|
||||||
|
borderLeftWidth,
|
||||||
|
borderRightWidth,
|
||||||
|
} = window.getComputedStyle(scroller)
|
||||||
|
|
||||||
const scrollerRect = scroller.getBoundingClientRect()
|
const scrollerRect = scroller.getBoundingClientRect()
|
||||||
width = offsetWidth
|
width = offsetWidth
|
||||||
height = offsetHeight
|
height = offsetHeight
|
||||||
yOffset = scrollTop - scrollerRect.top
|
scrollerTop = scrollerRect.top + parseInt(borderTopWidth, 10)
|
||||||
xOffset = scrollLeft - scrollerRect.left
|
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 selectionFocusTop = backward ? selectionRect.top : selectionRect.bottom
|
||||||
const left = (backward ? rect.left : rect.right) + xOffset
|
const selectionTop = selectionFocusTop + yOffset - scrollerTop
|
||||||
|
|
||||||
const x = left < yOffset || (width + xOffset) < left
|
const selectionFocusLeft = backward ? selectionRect.left : selectionRect.right
|
||||||
? left - width / 2
|
const selectionLeft = selectionFocusLeft + xOffset - scrollerLeft
|
||||||
: xOffset
|
|
||||||
|
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) {
|
if (isWindow) {
|
||||||
window.scrollTo(x, y)
|
window.scrollTo(x, y)
|
||||||
|
Reference in New Issue
Block a user