Skip to content

Commit

Permalink
Editor: Avoid relying on whether the range changes and check the posi…
Browse files Browse the repository at this point in the history
…tion instead
  • Loading branch information
youknowriad committed Aug 24, 2017
1 parent 630f0e8 commit 291a62a
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 39 deletions.
111 changes: 111 additions & 0 deletions editor/utils/dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Check whether the selection touches an edge of the container
*
* @param {Element} container DOM Element
* @param {Boolean} reverse Reverse means check if it touches the start of the container
* @return {Boolean} Is Edge or not
*/
export function isEdge( container, reverse = false ) {
const isInputOrTextarea = [ 'INPUT', 'TEXTAREA' ].indexOf( container.tagName ) !== -1;
const selection = window.getSelection();
const range = selection.rangeCount ? selection.getRangeAt( 0 ) : null;

function isStartOfContainer() {
if ( isInputOrTextarea ) {
return container.selectionStart === 0 && container.selectionStart === container.selectionEnd;
}
if ( ! container.isContentEditable ) {
return true;
}
if ( range.startOffset !== 0 || ! range.collapsed ) {
return false;
}
const start = range.startContainer;
let element = start;
while ( element !== container ) {
const child = element;
element = element.parentNode;
if ( element.firstChild !== child ) {
return false;
}
}
return true;
}

function isEndOfContainer() {
if ( isInputOrTextarea ) {
return container.value.length === container.selectionStart && container.selectionStart === container.selectionEnd;
}
if ( ! container.isContentEditable ) {
return true;
}
if ( range.endOffset !== range.endContainer.textContent.length || ! range.collapsed ) {
return false;
}
const start = range.endContainer;
let element = start;
while ( element !== container ) {
const child = element;
element = element.parentNode;
if ( element.lastChild !== child ) {
return false;
}
}
return true;
}

return reverse ? isStartOfContainer() : isEndOfContainer();
}

/**
* Places the caret at start or end of a given element
*
* @param {Element} container DOM Element
* @param {Boolean} start Position: Start or end of the element
*/
export function placeCaretAtEdge( container, start = false ) {
const isInputOrTextarea = [ 'INPUT', 'TEXTAREA' ].indexOf( container.tagName ) !== -1;
if ( isInputOrTextarea ) {
container.focus();
setTimeout( () => {
if ( start ) {
container.selectionStart = 0;
container.selectionEnd = 0;
} else {
container.selectionStart = container.value.length;
container.selectionEnd = container.value.length;
}
} );
return;
}

function createCaretPlacer( atStart ) {
return ( el ) => {
el.focus();
if (
typeof window.getSelection !== 'undefined' &&
typeof document.createRange !== 'undefined'
) {
const range = document.createRange();
range.selectNodeContents( el );
range.collapse( atStart );
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange( range );
} else if ( typeof document.body.createTextRange !== 'undefined' ) {
const textRange = document.body.createTextRange();
textRange.moveToElementText( el );
textRange.collapse( atStart );
textRange.select();
}
};
}
const placeCaretAtStart = createCaretPlacer( true );
const placeCaretAtEnd = createCaretPlacer( false );

if ( start ) {
placeCaretAtStart( container );
} else {
placeCaretAtEnd( container );
}
}
56 changes: 17 additions & 39 deletions editor/writing-flow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,41 @@
import { Component } from 'element';
import { keycodes } from '@wordpress/utils';

/**
* Internal dependencies
*/
import { isEdge, placeCaretAtEdge } from '../utils/dom';

/**
* Module Constants
*/
const { UP, DOWN, LEFT, RIGHT } = keycodes;

class WritingFlow extends Component {
constructor() {
super( ...arguments );
this.zones = [];
this.onKeyDown = this.onKeyDown.bind( this );
this.onKeyUp = this.onKeyUp.bind( this );
this.bindContainer = this.bindContainer.bind( this );
}

bindContainer( ref ) {
this.container = ref;
}

moveFocusInContainer( target, direction = 'UP' ) {
const selectors = [
getVisibleTabbables() {
const tabblablesSelector = [
'*[contenteditable="true"]',
'*[tabindex]',
'textarea',
'input',
].join( ',' );

].join( ', ' );
const isVisible = ( elem ) => elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
return Array.from( this.container.querySelectorAll( tabblablesSelector ) ).filter( isVisible );
}

const focusableNodes = Array.from( this.container.querySelectorAll( selectors ) ).filter( isVisible );
moveFocusInContainer( target, direction = 'UP' ) {
const focusableNodes = this.getVisibleTabbables();
if ( direction === 'UP' ) {
focusableNodes.reverse();
}
Expand All @@ -41,48 +50,18 @@ class WritingFlow extends Component {
}, null );

if ( targetNode ) {
targetNode.focus();
placeCaretAtEdge( targetNode, direction === 'DOWN' );
}
}

isSameRanges( range1, range2 ) {
return ( ! range1 && ! range2 ) || (
!! range1 &&
!! range2 &&
range1.startContainer === range2.startContainer &&
range1.startOffset === range2.startOffset &&
range1.endContainer === range2.endContainer &&
range1.endOffset === range2.endOffset
);
}

onKeyDown( event ) {
const { keyCode } = event;
if ( [ UP, LEFT, DOWN, RIGHT ].indexOf( keyCode ) !== -1 ) {
const selection = window.getSelection();
this.lastRange = selection.rangeCount ? selection.getRangeAt( 0 ) : null;
}
}

onKeyUp( event ) {
const { keyCode, target } = event;
const moveUp = ( keyCode === UP || keyCode === LEFT );
const moveDown = ( keyCode === DOWN || keyCode === RIGHT );

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 ( ! this.isSameRanges( range, this.lastRange ) ) {
return;
}

if ( ( moveUp || moveDown ) && isEdge( target, moveUp ) ) {
this.moveFocusInContainer( target, moveUp ? 'UP' : 'DOWN' );
}

delete this.lastRange;
}

render() {
Expand All @@ -92,7 +71,6 @@ class WritingFlow extends Component {
<div
ref={ this.bindContainer }
onKeyDown={ this.onKeyDown }
onKeyUp={ this.onKeyUp }
>
{ children }
</div>
Expand Down

0 comments on commit 291a62a

Please sign in to comment.