diff --git a/lib/components/node.js b/lib/components/node.js index 41238564b..a8d6eda9b 100644 --- a/lib/components/node.js +++ b/lib/components/node.js @@ -2,8 +2,10 @@ import Base64 from '../serializers/base-64' import Debug from 'debug' import React from 'react' +import ReactDOM from 'react-dom' import TYPES from '../utils/types' import Text from './text' +import scrollTo from 'element-scroll-to' /** * Debugger. @@ -63,6 +65,50 @@ class Node extends React.Component { ) } + /** + * On mount, update the scroll position. + */ + + componentDidMount = () => { + this.updateScroll() + } + + /** + * After update, update the scroll position if the node's content changed. + * + * @param {Object} prevProps + * @param {Object} prevState + */ + + componentDidUpdate = (prevProps, prevState) => { + if (this.props.node != prevProps.node) this.updateScroll() + } + + /** + * Update the scroll position after a change as occured if this is a leaf + * block and it has the selection's ending edge. This ensures that scrolling + * matches native `contenteditable` behavior even for cases where the edit is + * not applied natively, like when enter is pressed. + */ + + updateScroll = () => { + const { node, state } = this.props + const { selection } = state + + // If this isn't a block, or it's a wrapping block, abort. + if (node.kind != 'block') return + if (node.nodes.first().kind == 'block') return + + // If the selection is blurred, or this block doesn't contain it, abort. + if (selection.isBlurred) return + if (!selection.hasEndIn(node)) return + + const el = ReactDOM.findDOMNode(this) + scrollTo(el) + + this.debug('updateScroll', el) + } + /** * On drag start, add a serialized representation of the node to the data. * diff --git a/package.json b/package.json index 3216cb975..366267f4b 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "debug": "^2.2.0", "detect-browser": "^1.3.3", "direction": "^0.1.5", + "element-scroll-to": "^1.1.0", "esrever": "^0.2.0", "immutable": "^3.8.1", "is-empty": "^1.0.0",