diff options
Diffstat (limited to 'popperjs/package/lib/dom-utils/getClippingRect.js.flow')
-rw-r--r-- | popperjs/package/lib/dom-utils/getClippingRect.js.flow | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/popperjs/package/lib/dom-utils/getClippingRect.js.flow b/popperjs/package/lib/dom-utils/getClippingRect.js.flow new file mode 100644 index 0000000..7efd8d0 --- /dev/null +++ b/popperjs/package/lib/dom-utils/getClippingRect.js.flow @@ -0,0 +1,104 @@ +// @flow +import type { ClientRectObject } from '../types'; +import type { Boundary, RootBoundary } from '../enums'; +import { viewport } from '../enums'; +import getViewportRect from './getViewportRect'; +import getDocumentRect from './getDocumentRect'; +import listScrollParents from './listScrollParents'; +import getOffsetParent from './getOffsetParent'; +import getDocumentElement from './getDocumentElement'; +import getComputedStyle from './getComputedStyle'; +import { isElement, isHTMLElement } from './instanceOf'; +import getBoundingClientRect from './getBoundingClientRect'; +import getParentNode from './getParentNode'; +import contains from './contains'; +import getNodeName from './getNodeName'; +import rectToClientRect from '../utils/rectToClientRect'; +import { max, min } from '../utils/math'; + +function getInnerBoundingClientRect(element: Element) { + const rect = getBoundingClientRect(element); + + rect.top = rect.top + element.clientTop; + rect.left = rect.left + element.clientLeft; + rect.bottom = rect.top + element.clientHeight; + rect.right = rect.left + element.clientWidth; + rect.width = element.clientWidth; + rect.height = element.clientHeight; + rect.x = rect.left; + rect.y = rect.top; + + return rect; +} + +function getClientRectFromMixedType( + element: Element, + clippingParent: Element | RootBoundary +): ClientRectObject { + return clippingParent === viewport + ? rectToClientRect(getViewportRect(element)) + : isElement(clippingParent) + ? getInnerBoundingClientRect(clippingParent) + : rectToClientRect(getDocumentRect(getDocumentElement(element))); +} + +// A "clipping parent" is an overflowable container with the characteristic of +// clipping (or hiding) overflowing elements with a position different from +// `initial` +function getClippingParents(element: Element): Array<Element> { + const clippingParents = listScrollParents(getParentNode(element)); + const canEscapeClipping = + ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0; + const clipperElement = + canEscapeClipping && isHTMLElement(element) + ? getOffsetParent(element) + : element; + + if (!isElement(clipperElement)) { + return []; + } + + // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414 + return clippingParents.filter( + (clippingParent) => + isElement(clippingParent) && + contains(clippingParent, clipperElement) && + getNodeName(clippingParent) !== 'body' && + (canEscapeClipping + ? getComputedStyle(clippingParent).position !== 'static' + : true) + ); +} + +// Gets the maximum area that the element is visible in due to any number of +// clipping parents +export default function getClippingRect( + element: Element, + boundary: Boundary, + rootBoundary: RootBoundary +): ClientRectObject { + const mainClippingParents = + boundary === 'clippingParents' + ? getClippingParents(element) + : [].concat(boundary); + const clippingParents = [...mainClippingParents, rootBoundary]; + const firstClippingParent = clippingParents[0]; + + const clippingRect = clippingParents.reduce((accRect, clippingParent) => { + const rect = getClientRectFromMixedType(element, clippingParent); + + accRect.top = max(rect.top, accRect.top); + accRect.right = min(rect.right, accRect.right); + accRect.bottom = min(rect.bottom, accRect.bottom); + accRect.left = max(rect.left, accRect.left); + + return accRect; + }, getClientRectFromMixedType(element, firstClippingParent)); + + clippingRect.width = clippingRect.right - clippingRect.left; + clippingRect.height = clippingRect.bottom - clippingRect.top; + clippingRect.x = clippingRect.left; + clippingRect.y = clippingRect.top; + + return clippingRect; +} |