UNPKG

@base-ui-components/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

102 lines (96 loc) 3.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.baseArrow = exports.arrow = void 0; var _utils = require("@floating-ui/utils"); /** * Fork of the original `arrow` middleware from Floating UI that allows * configuring the offset parent. */ const baseArrow = options => ({ name: 'arrow', options, async fn(state) { const { x, y, placement, rects, platform, elements, middlewareData } = state; // Since `element` is required, we don't Partial<> the type. const { element, padding = 0, offsetParent = 'real' } = (0, _utils.evaluate)(options, state) || {}; if (element == null) { return {}; } const paddingObject = (0, _utils.getPaddingObject)(padding); const coords = { x, y }; const axis = (0, _utils.getAlignmentAxis)(placement); const length = (0, _utils.getAxisLength)(axis); const arrowDimensions = await platform.getDimensions(element); const isYAxis = axis === 'y'; const minProp = isYAxis ? 'top' : 'left'; const maxProp = isYAxis ? 'bottom' : 'right'; const clientProp = isYAxis ? 'clientHeight' : 'clientWidth'; const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length]; const startDiff = coords[axis] - rects.reference[axis]; const arrowOffsetParent = offsetParent === 'real' ? await platform.getOffsetParent?.(element) : elements.floating; let clientSize = elements.floating[clientProp] || rects.floating[length]; // DOM platform can return `window` as the `offsetParent`. if (!clientSize || !(await platform.isElement?.(arrowOffsetParent))) { clientSize = elements.floating[clientProp] || rects.floating[length]; } const centerToReference = endDiff / 2 - startDiff / 2; // If the padding is large enough that it causes the arrow to no longer be // centered, modify the padding so that it is centered. const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1; const minPadding = Math.min(paddingObject[minProp], largestPossiblePadding); const maxPadding = Math.min(paddingObject[maxProp], largestPossiblePadding); // Make sure the arrow doesn't overflow the floating element if the center // point is outside the floating element's bounds. const min = minPadding; const max = clientSize - arrowDimensions[length] - maxPadding; const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference; const offset = (0, _utils.clamp)(min, center, max); // If the reference is small enough that the arrow's padding causes it to // to point to nothing for an aligned placement, adjust the offset of the // floating element itself. To ensure `shift()` continues to take action, // a single reset is performed when this is true. const shouldAddOffset = !middlewareData.arrow && (0, _utils.getAlignment)(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0; // eslint-disable-next-line no-nested-ternary const alignmentOffset = shouldAddOffset ? center < min ? center - min : center - max : 0; return { [axis]: coords[axis] + alignmentOffset, data: { [axis]: offset, centerOffset: center - offset - alignmentOffset, ...(shouldAddOffset && { alignmentOffset }) }, reset: shouldAddOffset }; } }); /** * Provides data to position an inner element of the floating element so that it * appears centered to the reference element. * This wraps the core `arrow` middleware to allow React refs as the element. * @see https://floating-ui.com/docs/arrow */ exports.baseArrow = baseArrow; const arrow = (options, deps) => ({ ...baseArrow(options), options: [options, deps] }); exports.arrow = arrow;