diff --git a/blocks/editable/index.js b/blocks/editable/index.js index fd78d1c7b017d..3aad67fe1f40d 100644 --- a/blocks/editable/index.js +++ b/blocks/editable/index.js @@ -13,7 +13,7 @@ import 'element-closest'; */ import { createElement, Component, renderToString } from 'element'; import { parse, pasteHandler } from '../api'; -import { BACKSPACE, DELETE, ENTER, UP, DOWN, LEFT, RIGHT } from 'utils/keycodes'; +import { BACKSPACE, DELETE, ENTER } from 'utils/keycodes'; /** * Internal dependencies @@ -230,41 +230,10 @@ export default class Editable extends Component { } onKeyDown( event ) { - const { keyCode } = event; - const moveUp = ( keyCode === UP || keyCode === LEFT ) && this.isStartOfEditor(); - const moveDown = ( keyCode === DOWN || keyCode === RIGHT ) && this.isEndOfEditor(); - const selectors = [ - '*[contenteditable="true"]', - '*[tabindex]', - 'textarea', - 'input', - ].join( ',' ); - - if ( moveUp || moveDown ) { - const rootNode = this.editor.getBody(); - const focusableNodes = Array.from( document.querySelectorAll( selectors ) ); - - if ( moveUp ) { - focusableNodes.reverse(); - } - - const targetNode = focusableNodes - .slice( focusableNodes.indexOf( rootNode ) ) - .reduce( ( result, node ) => { - return result || ( node.contains( rootNode ) ? null : node ); - }, null ); - - if ( targetNode ) { - targetNode.focus(); - event.preventDefault(); - event.stopImmediatePropagation(); - } - } - if ( this.props.onMerge && ( - ( keyCode === BACKSPACE && this.isStartOfEditor() ) || - ( keyCode === DELETE && this.isEndOfEditor() ) + ( event.keyCode === BACKSPACE && this.isStartOfEditor() ) || + ( event.keyCode === DELETE && this.isEndOfEditor() ) ) ) { const forward = event.keyCode === DELETE; diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index 696710a407551..a7ab22ed8791c 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -11,7 +11,7 @@ import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'; * WordPress dependencies */ import { Children, Component } from 'element'; -import { BACKSPACE, ESCAPE, DELETE } from 'utils/keycodes'; +import { BACKSPACE, ESCAPE, DELETE, UP, DOWN, LEFT, RIGHT } from 'utils/keycodes'; import { getBlockType, getBlockDefaultClassname } from 'blocks'; import { __, sprintf } from 'i18n'; @@ -59,6 +59,9 @@ class VisualEditorBlock extends Component { this.mergeBlocks = this.mergeBlocks.bind( this ); this.onFocus = this.onFocus.bind( this ); this.onPointerDown = this.onPointerDown.bind( this ); + this.onKeyDown = this.onKeyDown.bind( this ); + this.onKeyUp = this.onKeyUp.bind( this ); + this.handleArrowKey = this.handleArrowKey.bind( this ); this.previousOffset = null; } @@ -234,6 +237,63 @@ class VisualEditorBlock extends Component { this.props.onSelect(); } + onKeyDown( event ) { + const { keyCode } = event; + + this.handleArrowKey( event ); + + if ( keyCode === UP || keyCode === LEFT || keyCode === DOWN || keyCode === RIGHT ) { + const selection = window.getSelection(); + this.lastRange = selection.rangeCount ? selection.getRangeAt( 0 ) : null; + } + } + + onKeyUp( event ) { + this.removeOrDeselect( event ); + this.handleArrowKey( event ); + } + + handleArrowKey( event ) { + const { keyCode, target } = event; + const moveUp = ( keyCode === UP || keyCode === LEFT ); + const moveDown = ( keyCode === DOWN || keyCode === RIGHT ); + const selectors = [ + '*[contenteditable="true"]', + '*[tabindex]', + 'textarea', + 'input', + ].join( ',' ); + + if ( moveUp || moveDown ) { + const selection = window.getSelection(); + const range = selection.rangeCount ? selection.getRangeAt( 0 ) : null; + + // If there's no movement, so we're either at the end of start, or + // no text input at all. + if ( range !== this.lastRange ) { + return; + } + + const focusableNodes = Array.from( document.querySelectorAll( selectors ) ); + + if ( moveUp ) { + focusableNodes.reverse(); + } + + const targetNode = focusableNodes + .slice( focusableNodes.indexOf( target ) ) + .reduce( ( result, node ) => { + return result || ( node.contains( target ) ? null : node ); + }, null ); + + if ( targetNode ) { + targetNode.focus(); + } + } + + delete this.lastRange; + } + render() { const { block, multiSelectedBlockUids } = this.props; const blockType = getBlockType( block.name ); @@ -278,7 +338,8 @@ class VisualEditorBlock extends Component { return (