UNPKG

@ariakit/core

Version:
226 lines (223 loc) 7.03 kB
"use client"; // src/utils/dom.ts var canUseDOM = checkIsBrowser(); function checkIsBrowser() { var _a; return typeof window !== "undefined" && !!((_a = window.document) == null ? void 0 : _a.createElement); } function getDocument(node) { if (!node) return document; if ("self" in node) return node.document; return node.ownerDocument || document; } function getWindow(node) { if (!node) return self; if ("self" in node) return node.self; return getDocument(node).defaultView || window; } function getActiveElement(node, activeDescendant = false) { const { activeElement } = getDocument(node); if (!(activeElement == null ? void 0 : activeElement.nodeName)) { return null; } if (isFrame(activeElement) && activeElement.contentDocument) { return getActiveElement( activeElement.contentDocument.body, activeDescendant ); } if (activeDescendant) { const id = activeElement.getAttribute("aria-activedescendant"); if (id) { const element = getDocument(activeElement).getElementById(id); if (element) { return element; } } } return activeElement; } function contains(parent, child) { return parent === child || parent.contains(child); } function isFrame(element) { return element.tagName === "IFRAME"; } function isButton(element) { const tagName = element.tagName.toLowerCase(); if (tagName === "button") return true; if (tagName === "input" && element.type) { return buttonInputTypes.indexOf(element.type) !== -1; } return false; } var buttonInputTypes = [ "button", "color", "file", "image", "reset", "submit" ]; function isVisible(element) { if (typeof element.checkVisibility === "function") { return element.checkVisibility(); } const htmlElement = element; return htmlElement.offsetWidth > 0 || htmlElement.offsetHeight > 0 || element.getClientRects().length > 0; } function isTextField(element) { try { const isTextInput = element instanceof HTMLInputElement && element.selectionStart !== null; const isTextArea = element.tagName === "TEXTAREA"; return isTextInput || isTextArea || false; } catch (error) { return false; } } function isTextbox(element) { return element.isContentEditable || isTextField(element); } function getTextboxValue(element) { if (isTextField(element)) { return element.value; } if (element.isContentEditable) { const range = getDocument(element).createRange(); range.selectNodeContents(element); return range.toString(); } return ""; } function getTextboxSelection(element) { let start = 0; let end = 0; if (isTextField(element)) { start = element.selectionStart || 0; end = element.selectionEnd || 0; } else if (element.isContentEditable) { const selection = getDocument(element).getSelection(); if ((selection == null ? void 0 : selection.rangeCount) && selection.anchorNode && contains(element, selection.anchorNode) && selection.focusNode && contains(element, selection.focusNode)) { const range = selection.getRangeAt(0); const nextRange = range.cloneRange(); nextRange.selectNodeContents(element); nextRange.setEnd(range.startContainer, range.startOffset); start = nextRange.toString().length; nextRange.setEnd(range.endContainer, range.endOffset); end = nextRange.toString().length; } } return { start, end }; } function getPopupRole(element, fallback) { const allowedPopupRoles = ["dialog", "menu", "listbox", "tree", "grid"]; const role = element == null ? void 0 : element.getAttribute("role"); if (role && allowedPopupRoles.indexOf(role) !== -1) { return role; } return fallback; } function getPopupItemRole(element, fallback) { var _a; const itemRoleByPopupRole = { menu: "menuitem", listbox: "option", tree: "treeitem" }; const popupRole = getPopupRole(element); if (!popupRole) return fallback; const key = popupRole; return (_a = itemRoleByPopupRole[key]) != null ? _a : fallback; } function scrollIntoViewIfNeeded(element, arg) { if (isPartiallyHidden(element) && "scrollIntoView" in element) { element.scrollIntoView(arg); } } function getScrollingElement(element) { if (!element) return null; const isScrollableOverflow = (overflow) => { if (overflow === "auto") return true; if (overflow === "scroll") return true; return false; }; if (element.clientHeight && element.scrollHeight > element.clientHeight) { const { overflowY } = getComputedStyle(element); if (isScrollableOverflow(overflowY)) return element; } else if (element.clientWidth && element.scrollWidth > element.clientWidth) { const { overflowX } = getComputedStyle(element); if (isScrollableOverflow(overflowX)) return element; } return getScrollingElement(element.parentElement) || document.scrollingElement || document.body; } function isPartiallyHidden(element) { const elementRect = element.getBoundingClientRect(); const scroller = getScrollingElement(element); if (!scroller) return false; const scrollerRect = scroller.getBoundingClientRect(); const isHTML = scroller.tagName === "HTML"; const scrollerTop = isHTML ? scrollerRect.top + scroller.scrollTop : scrollerRect.top; const scrollerBottom = isHTML ? scroller.clientHeight : scrollerRect.bottom; const scrollerLeft = isHTML ? scrollerRect.left + scroller.scrollLeft : scrollerRect.left; const scrollerRight = isHTML ? scroller.clientWidth : scrollerRect.right; const top = elementRect.top < scrollerTop; const left = elementRect.left < scrollerLeft; const bottom = elementRect.bottom > scrollerBottom; const right = elementRect.right > scrollerRight; return top || left || bottom || right; } function setSelectionRange(element, ...args) { if (/text|search|password|tel|url/i.test(element.type)) { element.setSelectionRange(...args); } } function sortBasedOnDOMPosition(items, getElement) { const pairs = items.map((item, index) => [index, item]); let isOrderDifferent = false; pairs.sort(([indexA, a], [indexB, b]) => { const elementA = getElement(a); const elementB = getElement(b); if (elementA === elementB) return 0; if (!elementA || !elementB) return 0; if (isElementPreceding(elementA, elementB)) { if (indexA > indexB) { isOrderDifferent = true; } return -1; } if (indexA < indexB) { isOrderDifferent = true; } return 1; }); if (isOrderDifferent) { return pairs.map(([_, item]) => item); } return items; } function isElementPreceding(a, b) { return Boolean( b.compareDocumentPosition(a) & Node.DOCUMENT_POSITION_PRECEDING ); } export { canUseDOM, getDocument, getWindow, getActiveElement, contains, isFrame, isButton, isVisible, isTextField, isTextbox, getTextboxValue, getTextboxSelection, getPopupRole, getPopupItemRole, scrollIntoViewIfNeeded, getScrollingElement, isPartiallyHidden, setSelectionRange, sortBasedOnDOMPosition };