From 4e021f58b5186e61d7ace2a89a3052412d8ff5e7 Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Sat, 2 Dec 2017 13:52:23 -0800 Subject: [PATCH] handle native `selectionchange` event, closes #1135 (#1441) --- packages/slate-react/package.json | 1 + .../slate-react/src/components/content.js | 31 +++++++++++++++++-- yarn.lock | 4 +++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/slate-react/package.json b/packages/slate-react/package.json index fd213636d..984859d82 100644 --- a/packages/slate-react/package.json +++ b/packages/slate-react/package.json @@ -12,6 +12,7 @@ "is-in-browser": "^1.1.3", "is-window": "^1.0.2", "keycode": "^2.1.2", + "lodash.throttle": "^4.1.1", "prop-types": "^15.5.8", "react-immutable-proptypes": "^2.1.0", "react-portal": "^3.1.0", diff --git a/packages/slate-react/src/components/content.js b/packages/slate-react/src/components/content.js index c8daab456..9184fa4e7 100644 --- a/packages/slate-react/src/components/content.js +++ b/packages/slate-react/src/components/content.js @@ -4,6 +4,7 @@ import React from 'react' import Types from 'prop-types' import getWindow from 'get-window' import logger from 'slate-dev-logger' +import throttle from 'lodash.throttle' import EVENT_HANDLERS from '../constants/event-handlers' import Node from './node' @@ -93,8 +94,11 @@ class Content extends React.Component { componentDidMount = () => { const { editor } = this.props + const window = getWindow(this.element) - // Restrict scoped of `beforeinput` to mobile. + window.document.addEventListener('selectionchange', this.onNativeSelectionChange) + + // COMPAT: Restrict scope of `beforeinput` to mobile. if ((IS_IOS || IS_ANDROID) && SUPPORTED_EVENTS.beforeinput) { this.element.addEventListener('beforeinput', this.onNativeBeforeInput) } @@ -111,7 +115,11 @@ class Content extends React.Component { */ componentWillUnmount() { - // Restrict scoped of `beforeinput` to mobile. + const window = getWindow(this.element) + + window.document.removeEventListener('selectionchange', this.onNativeSelectionChange) + + // COMPAT: Restrict scope of `beforeinput` to mobile. if ((IS_IOS || IS_ANDROID) && SUPPORTED_EVENTS.beforeinput) { this.element.removeEventListener('beforeinput', this.onNativeBeforeInput) } @@ -386,6 +394,25 @@ class Content extends React.Component { }) } + /** + * On native `selectionchange` event, trigger the `onSelect` handler. This is + * needed to account for React's `onSelect` being non-standard and not firing + * until after a selection has been released. This causes issues in situations + * where another change happens while a selection is being made. + * + * @param {Event} event + */ + + onNativeSelectionChange = throttle((event) => { + if (this.props.readOnly) return + + const window = getWindow(event.target) + const { activeElement } = window.document + if (activeElement !== this.element) return + + this.props.onSelect(event) + }, 100) + /** * Render the editor content. * diff --git a/yarn.lock b/yarn.lock index b8f45d67f..1f62b23ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4154,6 +4154,10 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~3.0.0" +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + lodash@^4.0.0, lodash@^4.1.0, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"