diff options
Diffstat (limited to 'popperjs/package/lib/utils/detectOverflow.js.flow')
-rw-r--r-- | popperjs/package/lib/utils/detectOverflow.js.flow | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/popperjs/package/lib/utils/detectOverflow.js.flow b/popperjs/package/lib/utils/detectOverflow.js.flow new file mode 100644 index 0000000..29cbabb --- /dev/null +++ b/popperjs/package/lib/utils/detectOverflow.js.flow @@ -0,0 +1,109 @@ +// @flow +import type { State, SideObject, Padding } from '../types'; +import type { Placement, Boundary, RootBoundary, Context } from '../enums'; +import getClippingRect from '../dom-utils/getClippingRect'; +import getDocumentElement from '../dom-utils/getDocumentElement'; +import getBoundingClientRect from '../dom-utils/getBoundingClientRect'; +import computeOffsets from './computeOffsets'; +import rectToClientRect from './rectToClientRect'; +import { + clippingParents, + reference, + popper, + bottom, + top, + right, + basePlacements, + viewport, +} from '../enums'; +import { isElement } from '../dom-utils/instanceOf'; +import mergePaddingObject from './mergePaddingObject'; +import expandToHashMap from './expandToHashMap'; + +// eslint-disable-next-line import/no-unused-modules +export type Options = { + placement: Placement, + boundary: Boundary, + rootBoundary: RootBoundary, + elementContext: Context, + altBoundary: boolean, + padding: Padding, +}; + +export default function detectOverflow( + state: State, + options: $Shape<Options> = {} +): SideObject { + const { + placement = state.placement, + boundary = clippingParents, + rootBoundary = viewport, + elementContext = popper, + altBoundary = false, + padding = 0, + } = options; + + const paddingObject = mergePaddingObject( + typeof padding !== 'number' + ? padding + : expandToHashMap(padding, basePlacements) + ); + + const altContext = elementContext === popper ? reference : popper; + + const popperRect = state.rects.popper; + const element = state.elements[altBoundary ? altContext : elementContext]; + + const clippingClientRect = getClippingRect( + isElement(element) + ? element + : element.contextElement || getDocumentElement(state.elements.popper), + boundary, + rootBoundary + ); + + const referenceClientRect = getBoundingClientRect(state.elements.reference); + + const popperOffsets = computeOffsets({ + reference: referenceClientRect, + element: popperRect, + strategy: 'absolute', + placement, + }); + + const popperClientRect = rectToClientRect({ + ...popperRect, + ...popperOffsets, + }); + + const elementClientRect = + elementContext === popper ? popperClientRect : referenceClientRect; + + // positive = overflowing the clipping rect + // 0 or negative = within the clipping rect + const overflowOffsets = { + top: clippingClientRect.top - elementClientRect.top + paddingObject.top, + bottom: + elementClientRect.bottom - + clippingClientRect.bottom + + paddingObject.bottom, + left: clippingClientRect.left - elementClientRect.left + paddingObject.left, + right: + elementClientRect.right - clippingClientRect.right + paddingObject.right, + }; + + const offsetData = state.modifiersData.offset; + + // Offsets can be applied only to the popper element + if (elementContext === popper && offsetData) { + const offset = offsetData[placement]; + + Object.keys(overflowOffsets).forEach((key) => { + const multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1; + const axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x'; + overflowOffsets[key] += offset[axis] * multiply; + }); + } + + return overflowOffsets; +} |