From c1266a7bc3833f9602560ab58926bc7f3c79af6a Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Mon, 27 Jan 2020 13:10:42 -0500 Subject: [PATCH] Block Editor: Refactor ObserveTyping as function component (#19881) * Block Editor: Refactor ObserveTyping as function component * Block Editor: ObserveTyping: Avoid persisting event --- .../src/components/observe-typing/index.js | 140 ++++++------------ 1 file changed, 48 insertions(+), 92 deletions(-) diff --git a/packages/block-editor/src/components/observe-typing/index.js b/packages/block-editor/src/components/observe-typing/index.js index c3ad69b3d834f..0e9c176e251f2 100644 --- a/packages/block-editor/src/components/observe-typing/index.js +++ b/packages/block-editor/src/components/observe-typing/index.js @@ -6,8 +6,8 @@ import { over, includes } from 'lodash'; /** * WordPress dependencies */ -import { Component } from '@wordpress/element'; -import { withSelect, withDispatch } from '@wordpress/data'; +import { useRef, useEffect } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; import { isTextField } from '@wordpress/dom'; import { UP, @@ -18,7 +18,7 @@ import { BACKSPACE, ESCAPE, } from '@wordpress/keycodes'; -import { withSafeTimeout, compose } from '@wordpress/compose'; +import { withSafeTimeout } from '@wordpress/compose'; /** * Set of key codes upon which typing is to be initiated on a keydown event. @@ -41,37 +41,17 @@ function isKeyDownEligibleForStartTyping( event ) { return ! shiftKey && includes( KEY_DOWN_ELIGIBLE_KEY_CODES, keyCode ); } -class ObserveTyping extends Component { - constructor() { - super( ...arguments ); - - this.stopTypingOnSelectionUncollapse = this.stopTypingOnSelectionUncollapse.bind( this ); - this.stopTypingOnMouseMove = this.stopTypingOnMouseMove.bind( this ); - this.startTypingInTextField = this.startTypingInTextField.bind( this ); - this.stopTypingOnNonTextField = this.stopTypingOnNonTextField.bind( this ); - this.stopTypingOnEscapeKey = this.stopTypingOnEscapeKey.bind( this ); - - this.onKeyDown = over( [ - this.startTypingInTextField, - this.stopTypingOnEscapeKey, - ] ); - - this.lastMouseMove = null; - } - - componentDidMount() { - this.toggleEventBindings( this.props.isTyping ); - } - - componentDidUpdate( prevProps ) { - if ( this.props.isTyping !== prevProps.isTyping ) { - this.toggleEventBindings( this.props.isTyping ); - } - } - - componentWillUnmount() { - this.toggleEventBindings( false ); - } +function ObserveTyping( { + children, + setTimeout: setSafeTimeout, +} ) { + const lastMouseMove = useRef(); + const isTyping = useSelect( ( select ) => select( 'core/block-editor' ).isTyping() ); + const { startTyping, stopTyping } = useDispatch( 'core/block-editor' ); + useEffect( () => { + toggleEventBindings( isTyping ); + return () => toggleEventBindings( false ); + }, [ isTyping ] ); /** * Bind or unbind events to the document when typing has started or stopped @@ -79,10 +59,10 @@ class ObserveTyping extends Component { * * @param {boolean} isBound Whether event bindings should be applied. */ - toggleEventBindings( isBound ) { + function toggleEventBindings( isBound ) { const bindFn = isBound ? 'addEventListener' : 'removeEventListener'; - document[ bindFn ]( 'selectionchange', this.stopTypingOnSelectionUncollapse ); - document[ bindFn ]( 'mousemove', this.stopTypingOnMouseMove ); + document[ bindFn ]( 'selectionchange', stopTypingOnSelectionUncollapse ); + document[ bindFn ]( 'mousemove', stopTypingOnMouseMove ); } /** @@ -90,35 +70,35 @@ class ObserveTyping extends Component { * * @param {MouseEvent} event Mousemove event. */ - stopTypingOnMouseMove( event ) { + function stopTypingOnMouseMove( event ) { const { clientX, clientY } = event; // We need to check that the mouse really moved because Safari triggers // mousemove events when shift or ctrl are pressed. - if ( this.lastMouseMove ) { + if ( lastMouseMove.current ) { const { clientX: lastClientX, clientY: lastClientY, - } = this.lastMouseMove; + } = lastMouseMove.current; if ( lastClientX !== clientX || lastClientY !== clientY ) { - this.props.onStopTyping(); + stopTyping(); } } - this.lastMouseMove = { clientX, clientY }; + lastMouseMove.current = { clientX, clientY }; } /** * On selection change, unset typing flag if user has made an uncollapsed * (shift) selection. */ - stopTypingOnSelectionUncollapse() { + function stopTypingOnSelectionUncollapse() { const selection = window.getSelection(); const isCollapsed = selection.rangeCount > 0 && selection.getRangeAt( 0 ).collapsed; if ( ! isCollapsed ) { - this.props.onStopTyping(); + stopTyping(); } } @@ -127,9 +107,9 @@ class ObserveTyping extends Component { * * @param {KeyboardEvent} event Keypress or keydown event to interpret. */ - stopTypingOnEscapeKey( event ) { - if ( this.props.isTyping && event.keyCode === ESCAPE ) { - this.props.onStopTyping(); + function stopTypingOnEscapeKey( event ) { + if ( isTyping && event.keyCode === ESCAPE ) { + stopTyping(); } } @@ -138,8 +118,7 @@ class ObserveTyping extends Component { * * @param {KeyboardEvent} event Keypress or keydown event to interpret. */ - startTypingInTextField( event ) { - const { isTyping, onStartTyping } = this.props; + function startTypingInTextField( event ) { const { type, target } = event; // Abort early if already typing, or key press is incurred outside a @@ -156,7 +135,7 @@ class ObserveTyping extends Component { return; } - onStartTyping(); + startTyping(); } /** @@ -164,59 +143,36 @@ class ObserveTyping extends Component { * * @param {FocusEvent} event Focus event. */ - stopTypingOnNonTextField( event ) { - event.persist(); + function stopTypingOnNonTextField( event ) { + const { target } = event; // Since focus to a non-text field via arrow key will trigger before // the keydown event, wait until after current stack before evaluating // whether typing is to be stopped. Otherwise, typing will re-start. - this.props.setTimeout( () => { - const { isTyping, onStopTyping } = this.props; - const { target } = event; + setSafeTimeout( () => { if ( isTyping && ! isTextField( target ) ) { - onStopTyping(); + stopTyping(); } } ); } - render() { - const { children } = this.props; - - // Disable reason: This component is responsible for capturing bubbled - // keyboard events which are interpreted as typing intent. - - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( -
- { children } -
- ); - /* eslint-enable jsx-a11y/no-static-element-interactions */ - } + // Disable reason: This component is responsible for capturing bubbled + // keyboard events which are interpreted as typing intent. + + /* eslint-disable jsx-a11y/no-static-element-interactions */ + return ( +
+ { children } +
+ ); + /* eslint-enable jsx-a11y/no-static-element-interactions */ } /** * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/observe-typing/README.md */ -export default compose( [ - withSelect( ( select ) => { - const { isTyping } = select( 'core/block-editor' ); - - return { - isTyping: isTyping(), - }; - } ), - withDispatch( ( dispatch ) => { - const { startTyping, stopTyping } = dispatch( 'core/block-editor' ); - - return { - onStartTyping: startTyping, - onStopTyping: stopTyping, - }; - } ), - withSafeTimeout, -] )( ObserveTyping ); +export default withSafeTimeout( ObserveTyping );