@wordpress/dom
Advanced tools
Comparing version 1.0.0-alpha.3 to 1.0.0
@@ -0,6 +1,7 @@ | ||
import "core-js/modules/es6.regexp.to-string"; | ||
/** | ||
* External dependencies | ||
*/ | ||
import { includes, first } from 'lodash'; | ||
import tinymce from 'tinymce'; | ||
import { includes } from 'lodash'; | ||
/** | ||
@@ -11,20 +12,61 @@ * Browser dependencies | ||
var _window = window, | ||
getComputedStyle = _window.getComputedStyle, | ||
DOMRect = _window.DOMRect; | ||
getComputedStyle = _window.getComputedStyle; | ||
var _window$Node = window.Node, | ||
TEXT_NODE = _window$Node.TEXT_NODE, | ||
ELEMENT_NODE = _window$Node.ELEMENT_NODE; | ||
ELEMENT_NODE = _window$Node.ELEMENT_NODE, | ||
DOCUMENT_POSITION_PRECEDING = _window$Node.DOCUMENT_POSITION_PRECEDING, | ||
DOCUMENT_POSITION_FOLLOWING = _window$Node.DOCUMENT_POSITION_FOLLOWING; | ||
/** | ||
* Check whether the caret is horizontally at the edge of the container. | ||
* Returns true if the given selection object is in the forward direction, or | ||
* false otherwise. | ||
* | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check left, false for right. | ||
* @param {boolean} collapseRanges Whether or not to collapse the selection range before the check. | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition | ||
* | ||
* @param {Selection} selection Selection object to check. | ||
* | ||
* @return {boolean} Whether the selection is forward. | ||
*/ | ||
function isSelectionForward(selection) { | ||
var anchorNode = selection.anchorNode, | ||
focusNode = selection.focusNode, | ||
anchorOffset = selection.anchorOffset, | ||
focusOffset = selection.focusOffset; | ||
var position = anchorNode.compareDocumentPosition(focusNode); // Disable reason: `Node#compareDocumentPosition` returns a bitmask value, | ||
// so bitwise operators are intended. | ||
/* eslint-disable no-bitwise */ | ||
// Compare whether anchor node precedes focus node. If focus node (where | ||
// end of selection occurs) is after the anchor node, it is forward. | ||
if (position & DOCUMENT_POSITION_PRECEDING) { | ||
return false; | ||
} | ||
if (position & DOCUMENT_POSITION_FOLLOWING) { | ||
return true; | ||
} | ||
/* eslint-enable no-bitwise */ | ||
// `compareDocumentPosition` returns 0 when passed the same node, in which | ||
// case compare offsets. | ||
if (position === 0) { | ||
return anchorOffset <= focusOffset; | ||
} // This should never be reached, but return true as default case. | ||
return true; | ||
} | ||
/** | ||
* 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) { | ||
var collapseRanges = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
if (includes(['INPUT', 'TEXTAREA'], container.tagName)) { | ||
@@ -44,54 +86,27 @@ if (container.selectionStart !== container.selectionEnd) { | ||
return true; | ||
} // If the container is empty, the caret is always at the edge. | ||
if (tinymce.DOM.isEmpty(container)) { | ||
return true; | ||
} | ||
var selection = window.getSelection(); | ||
var range = selection.rangeCount ? selection.getRangeAt(0) : null; | ||
var selection = window.getSelection(); // Create copy of range for setting selection to find effective offset. | ||
if (collapseRanges) { | ||
range = range.cloneRange(); | ||
range.collapse(isReverse); | ||
} | ||
var range = selection.getRangeAt(0).cloneRange(); // Collapse in direction of selection. | ||
if (!range || !range.collapsed) { | ||
return false; | ||
if (!selection.isCollapsed) { | ||
range.collapse(!isSelectionForward(selection)); | ||
} | ||
var position = isReverse ? 'start' : 'end'; | ||
var order = isReverse ? 'first' : 'last'; | ||
var offset = range["".concat(position, "Offset")]; | ||
var node = range.startContainer; | ||
var endContainer = range.endContainer, | ||
endOffset = range.endOffset; | ||
range.selectNodeContents(container); | ||
range.setEnd(endContainer, endOffset); // Check if the caret position is at the expected start/end position based | ||
// on the value of `isReverse`. If so, consider the horizontal edge to be | ||
// reached. | ||
if (isReverse && offset !== 0) { | ||
return false; | ||
} | ||
var maxOffset = node.nodeType === TEXT_NODE ? node.nodeValue.length : node.childNodes.length; | ||
if (!isReverse && offset !== maxOffset) { | ||
return false; | ||
} | ||
while (node !== container) { | ||
var parentNode = node.parentNode; | ||
if (parentNode["".concat(order, "Child")] !== node) { | ||
return false; | ||
} | ||
node = parentNode; | ||
} | ||
return true; | ||
var caretOffset = range.toString().length; | ||
return caretOffset === (isReverse ? 0 : container.textContent.length); | ||
} | ||
/** | ||
* Check whether the caret is vertically at the edge of the container. | ||
* Check whether the selection is vertically at the edge of the container. | ||
* | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check top, false for bottom. | ||
* @param {boolean} collapseRanges Whether or not to collapse the selection range before the check. | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check top, false for bottom. | ||
* | ||
@@ -102,4 +117,2 @@ * @return {boolean} True if at the edge, false if not. | ||
export function isVerticalEdge(container, isReverse) { | ||
var collapseRanges = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
if (includes(['INPUT', 'TEXTAREA'], container.tagName)) { | ||
@@ -116,11 +129,3 @@ return isHorizontalEdge(container, isReverse); | ||
if (collapseRanges && range && !range.collapsed) { | ||
var newRange = document.createRange(); // Get the end point of the selection (see focusNode vs. anchorNode) | ||
newRange.setStart(selection.focusNode, selection.focusOffset); | ||
newRange.collapse(true); | ||
range = newRange; | ||
} | ||
if (!range || !range.collapsed) { | ||
if (!range) { | ||
return false; | ||
@@ -163,22 +168,18 @@ } | ||
return range.getBoundingClientRect(); | ||
} // If the collapsed range starts (and therefore ends) at an element node, | ||
// `getClientRects` will return undefined. To fix this we can get the | ||
// bounding rectangle of the element node to create a DOMRect based on that. | ||
} | ||
var rect = range.getClientRects()[0]; // If the collapsed range starts (and therefore ends) at an element node, | ||
// `getClientRects` can be empty in some browsers. This can be resolved | ||
// by adding a temporary text node with zero-width space to the range. | ||
// | ||
// See: https://stackoverflow.com/a/6847328/995445 | ||
if (range.startContainer.nodeType === ELEMENT_NODE) { | ||
var _range$startContainer = range.startContainer.getBoundingClientRect(), | ||
x = _range$startContainer.x, | ||
y = _range$startContainer.y, | ||
height = _range$startContainer.height; // Create a new DOMRect with zero width. | ||
if (!rect) { | ||
var padNode = document.createTextNode("\u200B"); | ||
range.insertNode(padNode); | ||
rect = range.getClientRects()[0]; | ||
padNode.parentNode.removeChild(padNode); | ||
} | ||
return new DOMRect(x, y, 0, height); | ||
} // For normal collapsed ranges (exception above), the bounding rectangle of | ||
// the range may be inaccurate in some browsers. There will only be one | ||
// rectangle since it is a collapsed range, so it is safe to pass this as | ||
// the union of them. This works consistently in all browsers. | ||
return first(range.getClientRects()); | ||
return rect; | ||
} | ||
@@ -201,3 +202,3 @@ /** | ||
if (!range || !range.collapsed) { | ||
if (!range) { | ||
return; | ||
@@ -237,7 +238,11 @@ } | ||
return; | ||
} | ||
} // Select on extent child of the container, not the container itself. This | ||
// avoids the selection always being `endOffset` of 1 when placed at end, | ||
// where `startContainer`, `endContainer` would always be container itself. | ||
var rangeTarget = container[isReverse ? 'lastChild' : 'firstChild']; | ||
var selection = window.getSelection(); | ||
var range = document.createRange(); | ||
range.selectNodeContents(container); | ||
range.selectNodeContents(rangeTarget); | ||
range.collapse(!isReverse); | ||
@@ -332,3 +337,3 @@ selection.removeAllRanges(); | ||
var editableRect = container.getBoundingClientRect(); | ||
var x = rect.left + rect.width / 2; | ||
var x = rect.left; | ||
var y = isReverse ? editableRect.bottom - buffer : editableRect.top + buffer; | ||
@@ -335,0 +340,0 @@ var selection = window.getSelection(); |
176
build/dom.js
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
require("core-js/modules/es7.array.includes"); | ||
@@ -29,6 +27,6 @@ | ||
require("core-js/modules/es6.regexp.to-string"); | ||
var _lodash = require("lodash"); | ||
var _tinymce = _interopRequireDefault(require("tinymce")); | ||
/** | ||
@@ -42,20 +40,61 @@ * External dependencies | ||
var _window = window, | ||
getComputedStyle = _window.getComputedStyle, | ||
DOMRect = _window.DOMRect; | ||
getComputedStyle = _window.getComputedStyle; | ||
var _window$Node = window.Node, | ||
TEXT_NODE = _window$Node.TEXT_NODE, | ||
ELEMENT_NODE = _window$Node.ELEMENT_NODE; | ||
ELEMENT_NODE = _window$Node.ELEMENT_NODE, | ||
DOCUMENT_POSITION_PRECEDING = _window$Node.DOCUMENT_POSITION_PRECEDING, | ||
DOCUMENT_POSITION_FOLLOWING = _window$Node.DOCUMENT_POSITION_FOLLOWING; | ||
/** | ||
* Check whether the caret is horizontally at the edge of the container. | ||
* Returns true if the given selection object is in the forward direction, or | ||
* false otherwise. | ||
* | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check left, false for right. | ||
* @param {boolean} collapseRanges Whether or not to collapse the selection range before the check. | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition | ||
* | ||
* @param {Selection} selection Selection object to check. | ||
* | ||
* @return {boolean} Whether the selection is forward. | ||
*/ | ||
function isSelectionForward(selection) { | ||
var anchorNode = selection.anchorNode, | ||
focusNode = selection.focusNode, | ||
anchorOffset = selection.anchorOffset, | ||
focusOffset = selection.focusOffset; | ||
var position = anchorNode.compareDocumentPosition(focusNode); // Disable reason: `Node#compareDocumentPosition` returns a bitmask value, | ||
// so bitwise operators are intended. | ||
/* eslint-disable no-bitwise */ | ||
// Compare whether anchor node precedes focus node. If focus node (where | ||
// end of selection occurs) is after the anchor node, it is forward. | ||
if (position & DOCUMENT_POSITION_PRECEDING) { | ||
return false; | ||
} | ||
if (position & DOCUMENT_POSITION_FOLLOWING) { | ||
return true; | ||
} | ||
/* eslint-enable no-bitwise */ | ||
// `compareDocumentPosition` returns 0 when passed the same node, in which | ||
// case compare offsets. | ||
if (position === 0) { | ||
return anchorOffset <= focusOffset; | ||
} // This should never be reached, but return true as default case. | ||
return true; | ||
} | ||
/** | ||
* 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. | ||
*/ | ||
function isHorizontalEdge(container, isReverse) { | ||
var collapseRanges = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
if ((0, _lodash.includes)(['INPUT', 'TEXTAREA'], container.tagName)) { | ||
@@ -75,54 +114,27 @@ if (container.selectionStart !== container.selectionEnd) { | ||
return true; | ||
} // If the container is empty, the caret is always at the edge. | ||
if (_tinymce.default.DOM.isEmpty(container)) { | ||
return true; | ||
} | ||
var selection = window.getSelection(); | ||
var range = selection.rangeCount ? selection.getRangeAt(0) : null; | ||
var selection = window.getSelection(); // Create copy of range for setting selection to find effective offset. | ||
if (collapseRanges) { | ||
range = range.cloneRange(); | ||
range.collapse(isReverse); | ||
} | ||
var range = selection.getRangeAt(0).cloneRange(); // Collapse in direction of selection. | ||
if (!range || !range.collapsed) { | ||
return false; | ||
if (!selection.isCollapsed) { | ||
range.collapse(!isSelectionForward(selection)); | ||
} | ||
var position = isReverse ? 'start' : 'end'; | ||
var order = isReverse ? 'first' : 'last'; | ||
var offset = range["".concat(position, "Offset")]; | ||
var node = range.startContainer; | ||
var endContainer = range.endContainer, | ||
endOffset = range.endOffset; | ||
range.selectNodeContents(container); | ||
range.setEnd(endContainer, endOffset); // Check if the caret position is at the expected start/end position based | ||
// on the value of `isReverse`. If so, consider the horizontal edge to be | ||
// reached. | ||
if (isReverse && offset !== 0) { | ||
return false; | ||
} | ||
var maxOffset = node.nodeType === TEXT_NODE ? node.nodeValue.length : node.childNodes.length; | ||
if (!isReverse && offset !== maxOffset) { | ||
return false; | ||
} | ||
while (node !== container) { | ||
var parentNode = node.parentNode; | ||
if (parentNode["".concat(order, "Child")] !== node) { | ||
return false; | ||
} | ||
node = parentNode; | ||
} | ||
return true; | ||
var caretOffset = range.toString().length; | ||
return caretOffset === (isReverse ? 0 : container.textContent.length); | ||
} | ||
/** | ||
* Check whether the caret is vertically at the edge of the container. | ||
* Check whether the selection is vertically at the edge of the container. | ||
* | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check top, false for bottom. | ||
* @param {boolean} collapseRanges Whether or not to collapse the selection range before the check. | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check top, false for bottom. | ||
* | ||
@@ -134,4 +146,2 @@ * @return {boolean} True if at the edge, false if not. | ||
function isVerticalEdge(container, isReverse) { | ||
var collapseRanges = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
if ((0, _lodash.includes)(['INPUT', 'TEXTAREA'], container.tagName)) { | ||
@@ -148,11 +158,3 @@ return isHorizontalEdge(container, isReverse); | ||
if (collapseRanges && range && !range.collapsed) { | ||
var newRange = document.createRange(); // Get the end point of the selection (see focusNode vs. anchorNode) | ||
newRange.setStart(selection.focusNode, selection.focusOffset); | ||
newRange.collapse(true); | ||
range = newRange; | ||
} | ||
if (!range || !range.collapsed) { | ||
if (!range) { | ||
return false; | ||
@@ -196,22 +198,18 @@ } | ||
return range.getBoundingClientRect(); | ||
} // If the collapsed range starts (and therefore ends) at an element node, | ||
// `getClientRects` will return undefined. To fix this we can get the | ||
// bounding rectangle of the element node to create a DOMRect based on that. | ||
} | ||
var rect = range.getClientRects()[0]; // If the collapsed range starts (and therefore ends) at an element node, | ||
// `getClientRects` can be empty in some browsers. This can be resolved | ||
// by adding a temporary text node with zero-width space to the range. | ||
// | ||
// See: https://stackoverflow.com/a/6847328/995445 | ||
if (range.startContainer.nodeType === ELEMENT_NODE) { | ||
var _range$startContainer = range.startContainer.getBoundingClientRect(), | ||
x = _range$startContainer.x, | ||
y = _range$startContainer.y, | ||
height = _range$startContainer.height; // Create a new DOMRect with zero width. | ||
if (!rect) { | ||
var padNode = document.createTextNode("\u200B"); | ||
range.insertNode(padNode); | ||
rect = range.getClientRects()[0]; | ||
padNode.parentNode.removeChild(padNode); | ||
} | ||
return new DOMRect(x, y, 0, height); | ||
} // For normal collapsed ranges (exception above), the bounding rectangle of | ||
// the range may be inaccurate in some browsers. There will only be one | ||
// rectangle since it is a collapsed range, so it is safe to pass this as | ||
// the union of them. This works consistently in all browsers. | ||
return (0, _lodash.first)(range.getClientRects()); | ||
return rect; | ||
} | ||
@@ -235,3 +233,3 @@ /** | ||
if (!range || !range.collapsed) { | ||
if (!range) { | ||
return; | ||
@@ -272,7 +270,11 @@ } | ||
return; | ||
} | ||
} // Select on extent child of the container, not the container itself. This | ||
// avoids the selection always being `endOffset` of 1 when placed at end, | ||
// where `startContainer`, `endContainer` would always be container itself. | ||
var rangeTarget = container[isReverse ? 'lastChild' : 'firstChild']; | ||
var selection = window.getSelection(); | ||
var range = document.createRange(); | ||
range.selectNodeContents(container); | ||
range.selectNodeContents(rangeTarget); | ||
range.collapse(!isReverse); | ||
@@ -368,3 +370,3 @@ selection.removeAllRanges(); | ||
var editableRect = container.getBoundingClientRect(); | ||
var x = rect.left + rect.width / 2; | ||
var x = rect.left; | ||
var y = isReverse ? editableRect.bottom - buffer : editableRect.top + buffer; | ||
@@ -371,0 +373,0 @@ var selection = window.getSelection(); |
{ | ||
"name": "@wordpress/dom", | ||
"version": "1.0.0-alpha.3", | ||
"version": "1.0.0", | ||
"description": "DOM utils module for WordPress", | ||
@@ -30,3 +30,3 @@ "author": "The WordPress Contributors", | ||
}, | ||
"gitHead": "8827c049ec802471f51a5cb906d9096ffc1b4e48" | ||
"gitHead": "6ed34d66a85436978721a06d845353e0f3109d04" | ||
} |
172
src/dom.js
/** | ||
* External dependencies | ||
*/ | ||
import { includes, first } from 'lodash'; | ||
import tinymce from 'tinymce'; | ||
import { includes } from 'lodash'; | ||
@@ -10,15 +9,64 @@ /** | ||
*/ | ||
const { getComputedStyle, DOMRect } = window; | ||
const { TEXT_NODE, ELEMENT_NODE } = window.Node; | ||
const { getComputedStyle } = window; | ||
const { | ||
TEXT_NODE, | ||
ELEMENT_NODE, | ||
DOCUMENT_POSITION_PRECEDING, | ||
DOCUMENT_POSITION_FOLLOWING, | ||
} = window.Node; | ||
/** | ||
* Check whether the caret is horizontally at the edge of the container. | ||
* Returns true if the given selection object is in the forward direction, or | ||
* false otherwise. | ||
* | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check left, false for right. | ||
* @param {boolean} collapseRanges Whether or not to collapse the selection range before the check. | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition | ||
* | ||
* @param {Selection} selection Selection object to check. | ||
* | ||
* @return {boolean} Whether the selection is forward. | ||
*/ | ||
function isSelectionForward( selection ) { | ||
const { | ||
anchorNode, | ||
focusNode, | ||
anchorOffset, | ||
focusOffset, | ||
} = selection; | ||
const position = anchorNode.compareDocumentPosition( focusNode ); | ||
// Disable reason: `Node#compareDocumentPosition` returns a bitmask value, | ||
// so bitwise operators are intended. | ||
/* eslint-disable no-bitwise */ | ||
// Compare whether anchor node precedes focus node. If focus node (where | ||
// end of selection occurs) is after the anchor node, it is forward. | ||
if ( position & DOCUMENT_POSITION_PRECEDING ) { | ||
return false; | ||
} | ||
if ( position & DOCUMENT_POSITION_FOLLOWING ) { | ||
return true; | ||
} | ||
/* eslint-enable no-bitwise */ | ||
// `compareDocumentPosition` returns 0 when passed the same node, in which | ||
// case compare offsets. | ||
if ( position === 0 ) { | ||
return anchorOffset <= focusOffset; | ||
} | ||
// This should never be reached, but return true as default case. | ||
return true; | ||
} | ||
/** | ||
* 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, collapseRanges = false ) { | ||
export function isHorizontalEdge( container, isReverse ) { | ||
if ( includes( [ 'INPUT', 'TEXTAREA' ], container.tagName ) ) { | ||
@@ -40,57 +88,32 @@ if ( container.selectionStart !== container.selectionEnd ) { | ||
// If the container is empty, the caret is always at the edge. | ||
if ( tinymce.DOM.isEmpty( container ) ) { | ||
return true; | ||
} | ||
const selection = window.getSelection(); | ||
let range = selection.rangeCount ? selection.getRangeAt( 0 ) : null; | ||
if ( collapseRanges ) { | ||
range = range.cloneRange(); | ||
range.collapse( isReverse ); | ||
} | ||
if ( ! range || ! range.collapsed ) { | ||
return false; | ||
} | ||
// Create copy of range for setting selection to find effective offset. | ||
const range = selection.getRangeAt( 0 ).cloneRange(); | ||
const position = isReverse ? 'start' : 'end'; | ||
const order = isReverse ? 'first' : 'last'; | ||
const offset = range[ `${ position }Offset` ]; | ||
let node = range.startContainer; | ||
if ( isReverse && offset !== 0 ) { | ||
return false; | ||
// Collapse in direction of selection. | ||
if ( ! selection.isCollapsed ) { | ||
range.collapse( ! isSelectionForward( selection ) ); | ||
} | ||
const maxOffset = node.nodeType === TEXT_NODE ? node.nodeValue.length : node.childNodes.length; | ||
const { endContainer, endOffset } = range; | ||
range.selectNodeContents( container ); | ||
range.setEnd( endContainer, endOffset ); | ||
if ( ! isReverse && offset !== maxOffset ) { | ||
return false; | ||
} | ||
while ( node !== container ) { | ||
const parentNode = node.parentNode; | ||
if ( parentNode[ `${ order }Child` ] !== node ) { | ||
return false; | ||
} | ||
node = parentNode; | ||
} | ||
return true; | ||
// Check if the caret position is at the expected start/end position based | ||
// on the value of `isReverse`. If so, consider the horizontal edge to be | ||
// reached. | ||
const caretOffset = range.toString().length; | ||
return caretOffset === ( isReverse ? 0 : container.textContent.length ); | ||
} | ||
/** | ||
* Check whether the caret is vertically at the edge of the container. | ||
* Check whether the selection is vertically at the edge of the container. | ||
* | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check top, false for bottom. | ||
* @param {boolean} collapseRanges Whether or not to collapse the selection range before the check. | ||
* @param {Element} container Focusable element. | ||
* @param {boolean} isReverse Set to true to check top, false for bottom. | ||
* | ||
* @return {boolean} True if at the edge, false if not. | ||
*/ | ||
export function isVerticalEdge( container, isReverse, collapseRanges = false ) { | ||
export function isVerticalEdge( container, isReverse ) { | ||
if ( includes( [ 'INPUT', 'TEXTAREA' ], container.tagName ) ) { | ||
@@ -105,12 +128,4 @@ return isHorizontalEdge( container, isReverse ); | ||
const selection = window.getSelection(); | ||
let range = selection.rangeCount ? selection.getRangeAt( 0 ) : null; | ||
if ( collapseRanges && range && ! range.collapsed ) { | ||
const newRange = document.createRange(); | ||
// Get the end point of the selection (see focusNode vs. anchorNode) | ||
newRange.setStart( selection.focusNode, selection.focusOffset ); | ||
newRange.collapse( true ); | ||
range = newRange; | ||
} | ||
if ( ! range || ! range.collapsed ) { | ||
const range = selection.rangeCount ? selection.getRangeAt( 0 ) : null; | ||
if ( ! range ) { | ||
return false; | ||
@@ -156,17 +171,17 @@ } | ||
let rect = range.getClientRects()[ 0 ]; | ||
// If the collapsed range starts (and therefore ends) at an element node, | ||
// `getClientRects` will return undefined. To fix this we can get the | ||
// bounding rectangle of the element node to create a DOMRect based on that. | ||
if ( range.startContainer.nodeType === ELEMENT_NODE ) { | ||
const { x, y, height } = range.startContainer.getBoundingClientRect(); | ||
// Create a new DOMRect with zero width. | ||
return new DOMRect( x, y, 0, height ); | ||
// `getClientRects` can be empty in some browsers. This can be resolved | ||
// by adding a temporary text node with zero-width space to the range. | ||
// | ||
// See: https://stackoverflow.com/a/6847328/995445 | ||
if ( ! rect ) { | ||
const padNode = document.createTextNode( '\u200b' ); | ||
range.insertNode( padNode ); | ||
rect = range.getClientRects()[ 0 ]; | ||
padNode.parentNode.removeChild( padNode ); | ||
} | ||
// For normal collapsed ranges (exception above), the bounding rectangle of | ||
// the range may be inaccurate in some browsers. There will only be one | ||
// rectangle since it is a collapsed range, so it is safe to pass this as | ||
// the union of them. This works consistently in all browsers. | ||
return first( range.getClientRects() ); | ||
return rect; | ||
} | ||
@@ -189,3 +204,3 @@ | ||
if ( ! range || ! range.collapsed ) { | ||
if ( ! range ) { | ||
return; | ||
@@ -225,6 +240,11 @@ } | ||
// Select on extent child of the container, not the container itself. This | ||
// avoids the selection always being `endOffset` of 1 when placed at end, | ||
// where `startContainer`, `endContainer` would always be container itself. | ||
const rangeTarget = container[ isReverse ? 'lastChild' : 'firstChild' ]; | ||
const selection = window.getSelection(); | ||
const range = document.createRange(); | ||
range.selectNodeContents( container ); | ||
range.selectNodeContents( rangeTarget ); | ||
range.collapse( ! isReverse ); | ||
@@ -323,3 +343,3 @@ | ||
const editableRect = container.getBoundingClientRect(); | ||
const x = rect.left + ( rect.width / 2 ); | ||
const x = rect.left; | ||
const y = isReverse ? ( editableRect.bottom - buffer ) : ( editableRect.top + buffer ); | ||
@@ -326,0 +346,0 @@ const selection = window.getSelection(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
79555
2317
0