From d7962240e1eab9ded6183853f4ef4a85d0223bac Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Tue, 28 Feb 2017 14:51:51 -0800 Subject: [PATCH] fix scrolling to selection --- examples/hovering-menu/index.js | 5 +-- package.json | 2 +- src/components/node.js | 7 ++-- src/utils/scroll-to-selection.js | 37 ++++++++++++++++++++ src/utils/scroll-to.js | 58 -------------------------------- 5 files changed, 46 insertions(+), 63 deletions(-) create mode 100644 src/utils/scroll-to-selection.js delete mode 100644 src/utils/scroll-to.js diff --git a/examples/hovering-menu/index.js b/examples/hovering-menu/index.js index 349544c4a..157f24fec 100644 --- a/examples/hovering-menu/index.js +++ b/examples/hovering-menu/index.js @@ -3,7 +3,6 @@ import { Editor, Raw } from '../..' import Portal from 'react-portal' import React from 'react' import initialState from './state.json' -import position from 'selection-position' /** * Define a schema. @@ -185,7 +184,9 @@ class HoveringMenu extends React.Component { return } - const rect = position() + const selection = window.getSelection() + const range = selection.getRangeAt(0) + const rect = range.getBoundingClientRect() menu.style.opacity = 1 menu.style.top = `${rect.top + window.scrollY - menu.offsetHeight}px` menu.style.left = `${rect.left + window.scrollX - menu.offsetWidth / 2 + rect.width / 2}px` diff --git a/package.json b/package.json index f3cd9c113..1f5053544 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "is-empty": "^1.0.0", "keycode": "^2.1.2", "react-portal": "^3.0.0", + "selection-is-backward": "^1.0.0", "type-of": "^2.0.1" }, "peerDependencies": { @@ -63,7 +64,6 @@ "react-router": "^2.5.1", "read-metadata": "^1.0.0", "read-yaml-promise": "^1.0.2", - "selection-position": "^1.0.0", "slate-auto-replace-text": "^0.3.0", "slate-collapse-on-escape": "^0.2.0", "slate-soft-break": "^0.2.0", diff --git a/src/components/node.js b/src/components/node.js index 47f4d7a6f..8b912f152 100644 --- a/src/components/node.js +++ b/src/components/node.js @@ -6,7 +6,8 @@ import ReactDOM from 'react-dom' import TYPES from '../constants/types' import Leaf from './leaf' import Void from './void' -import scrollTo from '../utils/scroll-to' +import getWindow from 'get-window' +import scrollToSelection from '../utils/scroll-to-selection' /** * Debug. @@ -207,7 +208,9 @@ class Node extends React.Component { if (!selection.hasEndIn(node)) return const el = ReactDOM.findDOMNode(this) - scrollTo(el) + const window = getWindow(el) + const native = window.getSelection() + scrollToSelection(native) this.debug('updateScroll', el) } diff --git a/src/utils/scroll-to-selection.js b/src/utils/scroll-to-selection.js new file mode 100644 index 000000000..47b3cc9e9 --- /dev/null +++ b/src/utils/scroll-to-selection.js @@ -0,0 +1,37 @@ + +import getWindow from 'get-window' +import isBackward from 'selection-is-backward' + +/** + * Scroll the current selection's focus point into view if needed. + * + * @param {Selection} selection + */ + +function scrollToSelection(selection) { + const window = getWindow(selection.anchorNode) + const backward = isBackward(selection) + const range = selection.getRangeAt(0) + const rect = range.getBoundingClientRect() + const { innerWidth, innerHeight, scrollY, scrollX } = window + const top = (backward ? rect.top : rect.bottom) + scrollY + const left = (backward ? rect.left : rect.right) + scrollX + + const x = left < scrollX || innerWidth + scrollX < left + ? left - innerWidth / 2 + : scrollX + + const y = top < scrollY || innerHeight + scrollY < top + ? top - innerHeight / 2 + : scrollY + + window.scrollTo(x, y) +} + +/** + * Export. + * + * @type {Function} + */ + +export default scrollToSelection diff --git a/src/utils/scroll-to.js b/src/utils/scroll-to.js deleted file mode 100644 index f5d488d57..000000000 --- a/src/utils/scroll-to.js +++ /dev/null @@ -1,58 +0,0 @@ - -/** - * Helps scroll the cursor into the middle of view if it isn't in view - */ - -import getWindow from 'get-window' - -function scrollWindow(window, cursorTop, cursorLeft, cursorHeight) { - let scrollX = window.scrollX - let scrollY = window.scrollY - const cursorBottom = cursorTop + cursorHeight - - if (cursorTop < 0 || cursorBottom > window.innerHeight) { - scrollY += cursorTop - window.innerHeight / 2 + cursorHeight / 2 - } - - if (cursorLeft < 0 || cursorLeft > window.innerWidth) { - scrollX += cursorLeft - window.innerWidth / 2 - } - - window.scrollTo(scrollX, scrollY) -} - -function scrollTo(element) { - const window = getWindow(element) - const s = window.getSelection() - if (s.rangeCount > 0) { - const selectionRect = s.getRangeAt(0).getBoundingClientRect() - let innerRect = selectionRect - let wrapper = element - const cursorHeight = innerRect.height - let cursorTop = innerRect.top - let cursorLeft = innerRect.left - - while (wrapper != window.document.body) { - const wrapperRect = wrapper.getBoundingClientRect() - const currentY = cursorTop - const cursorBottom = cursorTop + cursorHeight - if (cursorTop < wrapperRect.top || cursorBottom > wrapperRect.top + wrapper.offsetHeight) { - cursorTop = wrapperRect.top + wrapperRect.height / 2 - cursorHeight / 2 - wrapper.scrollTop += currentY - cursorTop - } - - const currentLeft = cursorLeft - if (cursorLeft < wrapperRect.left || cursorLeft > wrapperRect.right) { - cursorLeft = wrapperRect.left + wrapperRect.width / 2 - wrapper.scrollLeft += currentLeft - cursorLeft - } - - innerRect = wrapperRect - wrapper = wrapper.parentNode - } - - scrollWindow(window, cursorTop, cursorLeft, cursorHeight) - } -} - -export default scrollTo