diff --git a/lib/components/content.js b/lib/components/content.js index 8c6a21bb8..18e7e1191 100644 --- a/lib/components/content.js +++ b/lib/components/content.js @@ -556,41 +556,7 @@ class Content extends React.Component { if (this.tmp.isCopying) return if (this.tmp.isComposing) return - let { state } = this.props - let { document, selection } = state - const native = window.getSelection() - - if (!native.rangeCount) { - selection = selection.merge({ isFocused: false }) - state = state.merge({ selection }) - this.onChange(state) - return - } - - // Calculate the Slate-specific selection based on the native one. - const { anchorNode, anchorOffset, focusNode, focusOffset } = native - const anchor = OffsetKey.findPoint(anchorNode, anchorOffset, state) - const focus = OffsetKey.findPoint(focusNode, focusOffset, state) - - // If the native selection is inside text nodes, we can trust the native - // state and not need to re-render. - const isNative = ( - anchorNode.nodeType == 3 && - focusNode.nodeType == 3 - ) - - state = state - .transform() - .focus() - .moveTo({ - anchorKey: anchor.key, - anchorOffset: anchor.offset, - focusKey: focus.key, - focusOffset: focus.offset - }) - .apply({ isNative }) - - this.onChange(state) + this.props.onSelect(e) } /** @@ -683,4 +649,3 @@ class Content extends React.Component { */ export default Content - diff --git a/lib/components/editor.js b/lib/components/editor.js index 47603a6bf..286b11d32 100644 --- a/lib/components/editor.js +++ b/lib/components/editor.js @@ -28,6 +28,7 @@ class Editor extends React.Component { onDrop: React.PropTypes.func, onKeyDown: React.PropTypes.func, onPaste: React.PropTypes.func, + onSelect: React.PropTypes.func, onSelectionChange: React.PropTypes.func, placeholder: React.PropTypes.any, placeholderClassName: React.PropTypes.string, @@ -201,6 +202,16 @@ class Editor extends React.Component { this.onEvent('onPaste', ...args) } + /** + * On selection. + * + * @param {Mixed} ...args + */ + + onSelect = (...args) => { + this.onEvent('onSelect', ...args) + } + /** * Render the editor. * @@ -217,6 +228,7 @@ class Editor extends React.Component { onDrop={this.onDrop} onKeyDown={this.onKeyDown} onPaste={this.onPaste} + onSelect={this.onSelect} readOnly={this.props.readOnly} renderMark={this.renderMark} renderNode={this.renderNode} diff --git a/lib/plugins/core.js b/lib/plugins/core.js index a7abe9a87..c961142bd 100644 --- a/lib/plugins/core.js +++ b/lib/plugins/core.js @@ -5,6 +5,7 @@ import React from 'react' import String from '../utils/string' import keycode from 'keycode' import { IS_WINDOWS, IS_MAC } from '../utils/environment' +import OffsetKey from '../utils/offset-key' /** * The default plugin. @@ -328,6 +329,51 @@ function Plugin(options = {}) { } }, + /** + * The core `onSelect` handler. + * + * @param {Event} e + * @param {State} state + * @param {Editor} editor + * @return {State or Null} + */ + + onSelect(e, state, editor) { + let { document, selection } = state + const native = window.getSelection() + + if (!native.rangeCount) { + selection = selection.merge({ isFocused: false }) + state = state.merge({ selection }) + return state + } + + // Calculate the Slate-specific selection based on the native one. + const { anchorNode, anchorOffset, focusNode, focusOffset } = native + const anchor = OffsetKey.findPoint(anchorNode, anchorOffset, state) + const focus = OffsetKey.findPoint(focusNode, focusOffset, state) + + // If the native selection is inside text nodes, we can trust the native + // state and not need to re-render. + const isNative = ( + anchorNode.nodeType == 3 && + focusNode.nodeType == 3 + ) + + state = state + .transform() + .focus() + .moveTo({ + anchorKey: anchor.key, + anchorOffset: anchor.offset, + focusKey: focus.key, + focusOffset: focus.offset + }) + .apply({ isNative }) + + return state + }, + /** * The core `node` renderer, which uses plain `