UNPKG

@popperjs/core

Version:

Tooltip and Popover Positioning Engine

100 lines (84 loc) 3 kB
// @flow import type { Modifier, ModifierArguments, Padding } from '../types'; import getBasePlacement from '../utils/getBasePlacement'; import getLayoutRect from '../dom-utils/getLayoutRect'; import contains from '../dom-utils/contains'; import getMainAxisFromPlacement from '../utils/getMainAxisFromPlacement'; import within from '../utils/within'; import mergePaddingObject from '../utils/mergePaddingObject'; import expandToHashMap from '../utils/expandToHashMap'; import { left, right, basePlacements, top, bottom } from '../enums'; type Options = { element: HTMLElement | string, padding: Padding, }; function arrow({ state, name }: ModifierArguments<Options>) { const arrowElement = state.elements.arrow; const popperOffsets = state.modifiersData.popperOffsets; const basePlacement = getBasePlacement(state.placement); const axis = getMainAxisFromPlacement(basePlacement); const isVertical = [left, right].indexOf(basePlacement) >= 0; const len = isVertical ? 'height' : 'width'; if (!arrowElement) { return; } const paddingObject = state.modifiersData[`${name}#persistent`].padding; const arrowRect = getLayoutRect(arrowElement); const minProp = axis === 'y' ? top : left; const maxProp = axis === 'y' ? bottom : right; const endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len]; const startDiff = popperOffsets[axis] - state.rects.reference[axis]; const centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is // outside of the popper bounds const center = within( paddingObject[minProp], state.rects.popper[len] / 2 - arrowRect[len] / 2 + centerToReference, state.rects.popper[len] - arrowRect[len] - paddingObject[maxProp] ); // Prevents breaking syntax highlighting... const axisProp: string = axis; state.modifiersData[name] = { [axisProp]: center }; } function effect({ state, options, name }: ModifierArguments<Options>) { let { element: arrowElement = '[data-popper-arrow]', padding = 0 } = options; // CSS selector if (typeof arrowElement === 'string') { arrowElement = state.elements.popper.querySelector(arrowElement); if (!arrowElement) { return; } } if (!contains(state.elements.popper, arrowElement)) { if (__DEV__) { console.error( [ 'Popper: "arrow" modifier\'s `element` must be a child of the popper', 'element.', ].join(' ') ); } return; } state.elements.arrow = arrowElement; state.modifiersData[`${name}#persistent`] = { padding: mergePaddingObject( typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements) ), }; } export default ({ name: 'arrow', enabled: true, phase: 'main', fn: arrow, effect, requires: ['popperOffsets'], requiresIfExists: ['preventOverflow'], }: Modifier<Options>);