UNPKG

@ng-doc/ui-kit

Version:

<!-- PROJECT LOGO --> <br /> <div align="center"> <a href="https://github.com/ng-doc/ng-doc"> <img src="https://ng-doc.com/assets/images/ng-doc.svg?raw=true" alt="Logo" height="150px"> </a>

283 lines (277 loc) 10.9 kB
import { asArray } from '@ng-doc/core/helpers/as-array'; class NgDocFocusUtils { static isNativeKeyboardFocusable(element) { if (element.hasAttribute('disabled') || element.getAttribute('tabIndex') === '-1') { return false; } if ((element instanceof HTMLElement && element.isContentEditable) || element.getAttribute('tabIndex') === '0') { return true; } switch (element.tagName) { case 'BUTTON': case 'SELECT': case 'TEXTAREA': return true; case 'VIDEO': case 'AUDIO': return element.hasAttribute('controls'); case 'INPUT': return element.getAttribute('type') !== 'hidden'; case 'A': case 'LINK': return element.hasAttribute('href'); default: return false; } } static getClosestKeyboardFocusable(initial, root, forward = true) { if (!root.ownerDocument) { return null; } // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const svgNodeFilter = ((node) => 'ownerSVGElement' in node ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT); const treeWalker = root.ownerDocument.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, svgNodeFilter); treeWalker.currentNode = initial; while (forward ? treeWalker.nextNode() : treeWalker.previousNode()) { if (treeWalker.currentNode instanceof HTMLElement) { initial = treeWalker.currentNode; } if (NgDocFocusUtils.isNativeKeyboardFocusable(initial)) { return initial; } } return null; } static focusClosestElement(initial, root, forward = true) { const focusable = NgDocFocusUtils.getClosestKeyboardFocusable(initial, root, forward); if (focusable) { focusable.focus(); } } } const NG_DOC_ARROW_MARGIN = 32; const POSITION_DESCRIPTION = { 'top-left': { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', }, 'top-center': { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', }, 'top-right': { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', }, 'bottom-left': { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', }, 'bottom-center': { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', }, 'bottom-right': { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', }, 'left-top': { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top', }, 'left-center': { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', }, 'left-bottom': { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom', }, 'right-top': { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top', }, 'right-center': { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', }, 'right-bottom': { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom', }, }; class NgDocOverlayUtils { static getConnectedPosition(dropdownPositions, origin, offset = 0, withPointer = false) { return asArray(dropdownPositions).map((position) => { const connectedPosition = NgDocOverlayUtils.toConnectedPosition(position); const marginMultiplier = NgDocOverlayUtils.getMarginMultiplier(connectedPosition); const marginX = !NgDocOverlayUtils.isVerticalPosition(connectedPosition) ? offset * marginMultiplier : 0; const marginY = NgDocOverlayUtils.isVerticalPosition(connectedPosition) ? offset * marginMultiplier : 0; connectedPosition.offsetX = connectedPosition.offsetX || 0; connectedPosition.offsetY = connectedPosition.offsetY || 0; connectedPosition.offsetX += (withPointer ? NgDocOverlayUtils.getOffsetX(origin, connectedPosition) : 0) + marginX; connectedPosition.offsetY += (withPointer ? NgDocOverlayUtils.getOffsetY(origin, connectedPosition) : 0) + marginY; return connectedPosition; }); } static toConnectedPosition(position) { return typeof position === 'string' ? { ...POSITION_DESCRIPTION[position] } : { ...position }; } static toConnectedPositions(positions) { return positions.map(NgDocOverlayUtils.toConnectedPosition); } static getOffsetX(origin, position) { const isVertical = NgDocOverlayUtils.isVerticalPosition(position); const offsetMultiplier = NgDocOverlayUtils.getOffsetMultiplier(position); const isCenter = NgDocOverlayUtils.isCenterPosition(position); const width = (position.originX === 'center' && position.overlayX !== 'center') || NgDocOverlayUtils.overlayIsOutByX(position) ? NG_DOC_ARROW_MARGIN - 24 : origin.offsetWidth; return ((isVertical && !isCenter ? Math.max(NG_DOC_ARROW_MARGIN - width, 0) : 0) * offsetMultiplier); } static getOffsetY(origin, position) { const isVertical = NgDocOverlayUtils.isVerticalPosition(position); const offsetMultiplier = NgDocOverlayUtils.getOffsetMultiplier(position); const isCenter = NgDocOverlayUtils.isCenterPosition(position); const height = (position.originY === 'center' && position.overlayY !== 'center') || NgDocOverlayUtils.overlayIsOutByY(position) ? NG_DOC_ARROW_MARGIN - 24 : origin.offsetHeight; return ((!isVertical && !isCenter ? Math.max(NG_DOC_ARROW_MARGIN - height, 0) : 0) * offsetMultiplier); } static overlayIsOutByX(position) { return ((position.originX === 'start' && position.overlayX === 'end') || (position.originX === 'end' && position.overlayX === 'start')); } static overlayIsOutByY(position) { return ((position.originY === 'top' && position.overlayY === 'bottom') || (position.originY === 'bottom' && position.overlayY === 'top')); } static getOffsetMultiplier(position) { return (NgDocOverlayUtils.isVerticalPosition(position) && position.overlayX === 'end') || (!NgDocOverlayUtils.isVerticalPosition(position) && position.overlayY === 'bottom') ? 1 : -1; } static getMarginMultiplier(position) { return ['right', 'bottom'].includes(NgDocOverlayUtils.getRelativePosition(position) || '') ? 1 : -1; } static isVerticalPosition(position) { return ['bottom', 'top'].includes(NgDocOverlayUtils.getRelativePosition(position) || ''); } static isCenterPosition(position) { return position.overlayX === 'center' || position.overlayY === 'center'; } static getPositionAlign(position) { if (NgDocOverlayUtils.isVerticalPosition(position)) { return position.overlayX === 'start' ? 'left' : position.overlayX === 'end' ? 'right' : null; } else { return position.originY === 'top' ? 'top' : position.originY === 'bottom' ? 'bottom' : null; } } static getRelativePosition(pos) { const position = NgDocOverlayUtils.toConnectedPosition(pos); if (position.originY === 'bottom' && position.overlayY === 'top') { return 'bottom'; } if (position.originY === 'top' && position.overlayY === 'bottom') { return 'top'; } if (position.originX === 'start' && position.overlayX === 'end') { return 'left'; } if (position.originX === 'end' && position.overlayX === 'start') { return 'right'; } return null; } static getOverlayPosition(positionPair) { const existsPosition = Object.keys(POSITION_DESCRIPTION).find((key) => { const positionDescription = POSITION_DESCRIPTION[key]; return (positionPair.originX === positionDescription.originX && positionPair.originY === positionDescription.originY && positionPair.overlayX === positionDescription.overlayX && positionPair.overlayY === positionDescription.overlayY); }); return existsPosition ? existsPosition : positionPair; } } class NgDocPositionUtils { /** * Getting the position of the element relative to the viewPort, this function is faster than BoundingClientRect, * it also takes into account the change in the position of the element through transform * @param element */ static getElementPosition(element) { let xPos = 0; let yPos = 0; while (element) { if (element === document.body) { const documentElement = document.documentElement; xPos += documentElement.offsetLeft - documentElement.scrollLeft + documentElement.clientLeft; yPos += documentElement.offsetTop - documentElement.scrollTop + documentElement.clientTop; element = null; } else { const elementMatrix = new DOMMatrix(element.style.transform); xPos += element.offsetLeft - element.scrollLeft + element.clientLeft + elementMatrix.m41; yPos += element.offsetTop - element.scrollTop + element.clientTop + elementMatrix.m42; element = NgDocPositionUtils.getOffsetParent(element); } } return { x: xPos, y: yPos }; } /** * An implementation of the element.offsetParent function, this implementation closes a bug in Firefox when it * returns an offsetParent for elements with position: fixed * @param element */ static getOffsetParent(element) { const computerStyles = getComputedStyle(element); if (computerStyles.position === 'fixed' || computerStyles.display === 'none') { return null; } return element.offsetParent; } } /** * Generated bundle index. Do not edit. */ export { NG_DOC_ARROW_MARGIN, NgDocFocusUtils, NgDocOverlayUtils, NgDocPositionUtils }; //# sourceMappingURL=ng-doc-ui-kit-utils.mjs.map