Skip to content

Commit

Permalink
Merge isEdge logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix committed Mar 26, 2019
1 parent 87c68af commit dc9694c
Showing 1 changed file with 40 additions and 75 deletions.
115 changes: 40 additions & 75 deletions packages/dom/src/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,17 @@ function isSelectionForward( selection ) {
}

/**
* Check whether the selection is horizontally at the edge of the container.
* Check whether the selection is at the edge of the container. Checks for
* horizontal position by default. Set `isVertical` to true to check only
* vertically.
*
* @param {Element} container Focusable element.
* @param {boolean} isReverse Set to true to check left, false for right.
* @param {Element} container Focusable element.
* @param {boolean} isReverse Set to true to check left, false to check right.
* @param {boolean} isVertical Set to true to check only vertical position.
*
* @return {boolean} True if at the horizontal edge, false if not.
*/
export function isHorizontalEdge( container, isReverse ) {
function isEdge( container, { isReverse, isVertical } ) {
if ( includes( [ 'INPUT', 'TEXTAREA' ], container.tagName ) ) {
if ( container.selectionStart !== container.selectionEnd ) {
return false;
Expand All @@ -86,36 +89,35 @@ export function isHorizontalEdge( container, isReverse ) {

const selection = window.getSelection();

// Only consider the selection at the edge if the direction is towards the
// edge.
if (
! selection.isCollapsed &&
isSelectionForward( selection ) === isReverse
) {
return false;
if ( ! selection.rangeCount ) {
return;
}

const range = selection.rangeCount ? selection.getRangeAt( 0 ) : null;
const rangeRect = getRectangleFromRange( selection.getRangeAt( 0 ) );

if ( ! range ) {
if ( ! rangeRect ) {
return false;
}

const rangeRect = getRectangleFromRange( range );
const computedStyle = window.getComputedStyle( container );
const lineHeight = parseInt( computedStyle.lineHeight, 10 );

if ( ! rangeRect ) {
// Only consider the multiline selection at the edge if the direction is
// towards the edge.
if (
! selection.isCollapsed &&
rangeRect.height > lineHeight &&
isSelectionForward( selection ) === isReverse
) {
return false;
}

const containerRect = container.getBoundingClientRect();

// Calculate a buffer that is half the line height. In some browsers, the
// selection rectangle may not fill the entire height of the line, so we add
// half the line height to the selection rectangle to ensure that it is well
// 3/4 the line height to the selection rectangle to ensure that it is well
// over its line boundary.
const { lineHeight } = window.getComputedStyle( container );
const buffer = 3 * parseInt( lineHeight, 10 ) / 4;

const containerRect = container.getBoundingClientRect();
const verticalEdge = isReverse ?
containerRect.top > rangeRect.top - buffer :
containerRect.bottom < rangeRect.bottom + buffer;
Expand All @@ -124,10 +126,12 @@ export function isHorizontalEdge( container, isReverse ) {
return false;
}

if ( isVertical ) {
return true;
}

const x = isReverse ? containerRect.left + 1 : containerRect.right - 1;
const y = isReverse ?
containerRect.top + buffer :
containerRect.bottom - buffer;
const y = isReverse ? containerRect.top + buffer : containerRect.bottom - buffer;
const testRange = hiddenCaretRangeFromPoint( document, x, y, container );

if ( ! testRange ) {
Expand All @@ -140,6 +144,18 @@ export function isHorizontalEdge( container, isReverse ) {
return Math.round( testRect[ side ] ) === Math.round( rangeRect[ side ] );
}

/**
* Check whether the selection is horizontally at the edge of the container.
*
* @param {Element} container Focusable element.
* @param {boolean} isReverse Set to true to check left, false for right.
*
* @return {boolean} True if at the horizontal edge, false if not.
*/
export function isHorizontalEdge( container, isReverse ) {
return isEdge( container, { isReverse } );
}

/**
* Check whether the selection is vertically at the edge of the container.
*
Expand All @@ -149,58 +165,7 @@ export function isHorizontalEdge( container, isReverse ) {
* @return {boolean} True if at the edge, false if not.
*/
export function isVerticalEdge( container, isReverse ) {
if ( includes( [ 'INPUT', 'TEXTAREA' ], container.tagName ) ) {
return isHorizontalEdge( container, isReverse );
}

if ( ! container.isContentEditable ) {
return true;
}

const selection = window.getSelection();
const range = selection.rangeCount ? selection.getRangeAt( 0 ) : null;

if ( ! range ) {
return false;
}

const rangeRect = getRectangleFromRange( range );

if ( ! rangeRect ) {
return false;
}

// Calculate a buffer that is half the line height. In some browsers, the
// selection rectangle may not fill the entire height of the line, so we add
// half the line height to the selection rectangle to ensure that it is well
// over its line boundary.
const computedStyle = window.getComputedStyle( container );
const lineHeight = parseInt( computedStyle.lineHeight, 10 );

// Only consider the multiline selection at the edge if the direction is
// towards the edge.
if (
! selection.isCollapsed &&
rangeRect.height > lineHeight &&
isSelectionForward( selection ) === isReverse
) {
return false;
}

const editableRect = container.getBoundingClientRect();
const buffer = lineHeight / 2;

// Too low.
if ( isReverse && rangeRect.top - buffer > editableRect.top ) {
return false;
}

// Too high.
if ( ! isReverse && rangeRect.bottom + buffer < editableRect.bottom ) {
return false;
}

return true;
return isEdge( container, { isReverse, isVertical: true } );
}

/**
Expand Down

0 comments on commit dc9694c

Please sign in to comment.