@gechiui/dom
Version:
DOM utilities module for GeChiUI.
136 lines (104 loc) • 4.87 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = isEdge;
var _isRtl = _interopRequireDefault(require("./is-rtl"));
var _getRangeHeight = _interopRequireDefault(require("./get-range-height"));
var _getRectangleFromRange = _interopRequireDefault(require("./get-rectangle-from-range"));
var _isSelectionForward = _interopRequireDefault(require("./is-selection-forward"));
var _hiddenCaretRangeFromPoint = _interopRequireDefault(require("./hidden-caret-range-from-point"));
var _assertIsDefined = require("../utils/assert-is-defined");
var _isInputOrTextArea = _interopRequireDefault(require("./is-input-or-text-area"));
/**
* Internal dependencies
*/
/**
* Check whether the selection is at the edge of the container. Checks for
* horizontal position by default. Set `onlyVertical` to true to check only
* vertically.
*
* @param {Element} container Focusable element.
* @param {boolean} isReverse Set to true to check left, false to check right.
* @param {boolean} [onlyVertical=false] Set to true to check only vertical position.
*
* @return {boolean} True if at the edge, false if not.
*/
function isEdge(container, isReverse) {
let onlyVertical = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
if ((0, _isInputOrTextArea.default)(container) && typeof container.selectionStart === 'number') {
if (container.selectionStart !== container.selectionEnd) {
return false;
}
if (isReverse) {
return container.selectionStart === 0;
}
return container.value.length === container.selectionStart;
}
if (!
/** @type {HTMLElement} */
container.isContentEditable) {
return true;
}
const {
ownerDocument
} = container;
const {
defaultView
} = ownerDocument;
(0, _assertIsDefined.assertIsDefined)(defaultView, 'defaultView');
const selection = defaultView.getSelection();
if (!selection || !selection.rangeCount) {
return false;
}
const range = selection.getRangeAt(0);
const collapsedRange = range.cloneRange();
const isForward = (0, _isSelectionForward.default)(selection);
const isCollapsed = selection.isCollapsed; // Collapse in direction of selection.
if (!isCollapsed) {
collapsedRange.collapse(!isForward);
}
const collapsedRangeRect = (0, _getRectangleFromRange.default)(collapsedRange);
const rangeRect = (0, _getRectangleFromRange.default)(range);
if (!collapsedRangeRect || !rangeRect) {
return false;
} // Only consider the multiline selection at the edge if the direction is
// towards the edge. The selection is multiline if it is taller than the
// collapsed selection.
const rangeHeight = (0, _getRangeHeight.default)(range);
if (!isCollapsed && rangeHeight && rangeHeight > collapsedRangeRect.height && isForward === isReverse) {
return false;
} // In the case of RTL scripts, the horizontal edge is at the opposite side.
const isReverseDir = (0, _isRtl.default)(container) ? !isReverse : isReverse;
const containerRect = container.getBoundingClientRect(); // To check if a selection is at the edge, we insert a test selection at the
// edge of the container and check if the selections have the same vertical
// or horizontal position. If they do, the selection is at the edge.
// This method proves to be better than a DOM-based calculation for the
// horizontal edge, since it ignores empty textnodes and a trailing line
// break element. In other words, we need to check visual positioning, not
// DOM positioning.
// It also proves better than using the computed style for the vertical
// edge, because we cannot know the padding and line height reliably in
// pixels. `getComputedStyle` may return a value with different units.
const x = isReverseDir ? containerRect.left + 1 : containerRect.right - 1;
const y = isReverse ? containerRect.top + 1 : containerRect.bottom - 1;
const testRange = (0, _hiddenCaretRangeFromPoint.default)(ownerDocument, x, y,
/** @type {HTMLElement} */
container);
if (!testRange) {
return false;
}
const testRect = (0, _getRectangleFromRange.default)(testRange);
if (!testRect) {
return false;
}
const verticalSide = isReverse ? 'top' : 'bottom';
const horizontalSide = isReverseDir ? 'left' : 'right';
const verticalDiff = testRect[verticalSide] - rangeRect[verticalSide];
const horizontalDiff = testRect[horizontalSide] - collapsedRangeRect[horizontalSide]; // Allow the position to be 1px off.
const hasVerticalDiff = Math.abs(verticalDiff) <= 1;
const hasHorizontalDiff = Math.abs(horizontalDiff) <= 1;
return onlyVertical ? hasVerticalDiff : hasVerticalDiff && hasHorizontalDiff;
}
//# sourceMappingURL=is-edge.js.map