12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160 |
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FloatingUICore = {}));
- })(this, (function (exports) { 'use strict';
- /**
- * Custom positioning reference element.
- * @see https://floating-ui.com/docs/virtual-elements
- */
- const sides = ['top', 'right', 'bottom', 'left'];
- const alignments = ['start', 'end'];
- const placements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + "-" + alignments[0], side + "-" + alignments[1]), []);
- const min = Math.min;
- const max = Math.max;
- const oppositeSideMap = {
- left: 'right',
- right: 'left',
- bottom: 'top',
- top: 'bottom'
- };
- const oppositeAlignmentMap = {
- start: 'end',
- end: 'start'
- };
- function clamp(start, value, end) {
- return max(start, min(value, end));
- }
- function evaluate(value, param) {
- return typeof value === 'function' ? value(param) : value;
- }
- function getSide(placement) {
- return placement.split('-')[0];
- }
- function getAlignment(placement) {
- return placement.split('-')[1];
- }
- function getOppositeAxis(axis) {
- return axis === 'x' ? 'y' : 'x';
- }
- function getAxisLength(axis) {
- return axis === 'y' ? 'height' : 'width';
- }
- function getSideAxis(placement) {
- return ['top', 'bottom'].includes(getSide(placement)) ? 'y' : 'x';
- }
- function getAlignmentAxis(placement) {
- return getOppositeAxis(getSideAxis(placement));
- }
- function getAlignmentSides(placement, rects, rtl) {
- if (rtl === void 0) {
- rtl = false;
- }
- const alignment = getAlignment(placement);
- const alignmentAxis = getAlignmentAxis(placement);
- const length = getAxisLength(alignmentAxis);
- let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';
- if (rects.reference[length] > rects.floating[length]) {
- mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
- }
- return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
- }
- function getExpandedPlacements(placement) {
- const oppositePlacement = getOppositePlacement(placement);
- return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
- }
- function getOppositeAlignmentPlacement(placement) {
- return placement.replace(/start|end/g, alignment => oppositeAlignmentMap[alignment]);
- }
- function getSideList(side, isStart, rtl) {
- const lr = ['left', 'right'];
- const rl = ['right', 'left'];
- const tb = ['top', 'bottom'];
- const bt = ['bottom', 'top'];
- switch (side) {
- case 'top':
- case 'bottom':
- if (rtl) return isStart ? rl : lr;
- return isStart ? lr : rl;
- case 'left':
- case 'right':
- return isStart ? tb : bt;
- default:
- return [];
- }
- }
- function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
- const alignment = getAlignment(placement);
- let list = getSideList(getSide(placement), direction === 'start', rtl);
- if (alignment) {
- list = list.map(side => side + "-" + alignment);
- if (flipAlignment) {
- list = list.concat(list.map(getOppositeAlignmentPlacement));
- }
- }
- return list;
- }
- function getOppositePlacement(placement) {
- return placement.replace(/left|right|bottom|top/g, side => oppositeSideMap[side]);
- }
- function expandPaddingObject(padding) {
- return {
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- ...padding
- };
- }
- function getPaddingObject(padding) {
- return typeof padding !== 'number' ? expandPaddingObject(padding) : {
- top: padding,
- right: padding,
- bottom: padding,
- left: padding
- };
- }
- function rectToClientRect(rect) {
- return {
- ...rect,
- top: rect.y,
- left: rect.x,
- right: rect.x + rect.width,
- bottom: rect.y + rect.height
- };
- }
- function computeCoordsFromPlacement(_ref, placement, rtl) {
- let {
- reference,
- floating
- } = _ref;
- const sideAxis = getSideAxis(placement);
- const alignmentAxis = getAlignmentAxis(placement);
- const alignLength = getAxisLength(alignmentAxis);
- const side = getSide(placement);
- const isVertical = sideAxis === 'y';
- const commonX = reference.x + reference.width / 2 - floating.width / 2;
- const commonY = reference.y + reference.height / 2 - floating.height / 2;
- const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;
- let coords;
- switch (side) {
- case 'top':
- coords = {
- x: commonX,
- y: reference.y - floating.height
- };
- break;
- case 'bottom':
- coords = {
- x: commonX,
- y: reference.y + reference.height
- };
- break;
- case 'right':
- coords = {
- x: reference.x + reference.width,
- y: commonY
- };
- break;
- case 'left':
- coords = {
- x: reference.x - floating.width,
- y: commonY
- };
- break;
- default:
- coords = {
- x: reference.x,
- y: reference.y
- };
- }
- switch (getAlignment(placement)) {
- case 'start':
- coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
- break;
- case 'end':
- coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
- break;
- }
- return coords;
- }
- /**
- * Computes the `x` and `y` coordinates that will place the floating element
- * next to a given reference element.
- *
- * This export does not have any `platform` interface logic. You will need to
- * write one for the platform you are using Floating UI with.
- */
- const computePosition = async (reference, floating, config) => {
- const {
- placement = 'bottom',
- strategy = 'absolute',
- middleware = [],
- platform
- } = config;
- const validMiddleware = middleware.filter(Boolean);
- const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));
- let rects = await platform.getElementRects({
- reference,
- floating,
- strategy
- });
- let {
- x,
- y
- } = computeCoordsFromPlacement(rects, placement, rtl);
- let statefulPlacement = placement;
- let middlewareData = {};
- let resetCount = 0;
- for (let i = 0; i < validMiddleware.length; i++) {
- const {
- name,
- fn
- } = validMiddleware[i];
- const {
- x: nextX,
- y: nextY,
- data,
- reset
- } = await fn({
- x,
- y,
- initialPlacement: placement,
- placement: statefulPlacement,
- strategy,
- middlewareData,
- rects,
- platform,
- elements: {
- reference,
- floating
- }
- });
- x = nextX != null ? nextX : x;
- y = nextY != null ? nextY : y;
- middlewareData = {
- ...middlewareData,
- [name]: {
- ...middlewareData[name],
- ...data
- }
- };
- if (reset && resetCount <= 50) {
- resetCount++;
- if (typeof reset === 'object') {
- if (reset.placement) {
- statefulPlacement = reset.placement;
- }
- if (reset.rects) {
- rects = reset.rects === true ? await platform.getElementRects({
- reference,
- floating,
- strategy
- }) : reset.rects;
- }
- ({
- x,
- y
- } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
- }
- i = -1;
- }
- }
- return {
- x,
- y,
- placement: statefulPlacement,
- strategy,
- middlewareData
- };
- };
- /**
- * Resolves with an object of overflow side offsets that determine how much the
- * element is overflowing a given clipping boundary on each side.
- * - positive = overflowing the boundary by that number of pixels
- * - negative = how many pixels left before it will overflow
- * - 0 = lies flush with the boundary
- * @see https://floating-ui.com/docs/detectOverflow
- */
- async function detectOverflow(state, options) {
- var _await$platform$isEle;
- if (options === void 0) {
- options = {};
- }
- const {
- x,
- y,
- platform,
- rects,
- elements,
- strategy
- } = state;
- const {
- boundary = 'clippingAncestors',
- rootBoundary = 'viewport',
- elementContext = 'floating',
- altBoundary = false,
- padding = 0
- } = evaluate(options, state);
- const paddingObject = getPaddingObject(padding);
- const altContext = elementContext === 'floating' ? 'reference' : 'floating';
- const element = elements[altBoundary ? altContext : elementContext];
- const clippingClientRect = rectToClientRect(await platform.getClippingRect({
- element: ((_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),
- boundary,
- rootBoundary,
- strategy
- }));
- const rect = elementContext === 'floating' ? {
- ...rects.floating,
- x,
- y
- } : rects.reference;
- const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));
- const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) ? (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {
- x: 1,
- y: 1
- } : {
- x: 1,
- y: 1
- };
- const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
- elements,
- rect,
- offsetParent,
- strategy
- }) : rect);
- return {
- top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
- bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
- left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
- right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x
- };
- }
- /**
- * Provides data to position an inner element of the floating element so that it
- * appears centered to the reference element.
- * @see https://floating-ui.com/docs/arrow
- */
- const arrow = 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
- } = evaluate(options, state) || {};
- if (element == null) {
- return {};
- }
- const paddingObject = getPaddingObject(padding);
- const coords = {
- x,
- y
- };
- const axis = getAlignmentAxis(placement);
- const length = 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 = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));
- let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0;
- // DOM platform can return `window` as the `offsetParent`.
- if (!clientSize || !(await (platform.isElement == null ? void 0 : 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 = min(paddingObject[minProp], largestPossiblePadding);
- const maxPadding = 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$1 = minPadding;
- const max = clientSize - arrowDimensions[length] - maxPadding;
- const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
- const offset = clamp(min$1, 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 && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
- const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max : 0;
- return {
- [axis]: coords[axis] + alignmentOffset,
- data: {
- [axis]: offset,
- centerOffset: center - offset - alignmentOffset,
- ...(shouldAddOffset && {
- alignmentOffset
- })
- },
- reset: shouldAddOffset
- };
- }
- });
- function getPlacementList(alignment, autoAlignment, allowedPlacements) {
- const allowedPlacementsSortedByAlignment = alignment ? [...allowedPlacements.filter(placement => getAlignment(placement) === alignment), ...allowedPlacements.filter(placement => getAlignment(placement) !== alignment)] : allowedPlacements.filter(placement => getSide(placement) === placement);
- return allowedPlacementsSortedByAlignment.filter(placement => {
- if (alignment) {
- return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);
- }
- return true;
- });
- }
- /**
- * Optimizes the visibility of the floating element by choosing the placement
- * that has the most space available automatically, without needing to specify a
- * preferred placement. Alternative to `flip`.
- * @see https://floating-ui.com/docs/autoPlacement
- */
- const autoPlacement = function (options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: 'autoPlacement',
- options,
- async fn(state) {
- var _middlewareData$autoP, _middlewareData$autoP2, _placementsThatFitOnE;
- const {
- rects,
- middlewareData,
- placement,
- platform,
- elements
- } = state;
- const {
- crossAxis = false,
- alignment,
- allowedPlacements = placements,
- autoAlignment = true,
- ...detectOverflowOptions
- } = evaluate(options, state);
- const placements$1 = alignment !== undefined || allowedPlacements === placements ? getPlacementList(alignment || null, autoAlignment, allowedPlacements) : allowedPlacements;
- const overflow = await detectOverflow(state, detectOverflowOptions);
- const currentIndex = ((_middlewareData$autoP = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP.index) || 0;
- const currentPlacement = placements$1[currentIndex];
- if (currentPlacement == null) {
- return {};
- }
- const alignmentSides = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));
- // Make `computeCoords` start from the right place.
- if (placement !== currentPlacement) {
- return {
- reset: {
- placement: placements$1[0]
- }
- };
- }
- const currentOverflows = [overflow[getSide(currentPlacement)], overflow[alignmentSides[0]], overflow[alignmentSides[1]]];
- const allOverflows = [...(((_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.overflows) || []), {
- placement: currentPlacement,
- overflows: currentOverflows
- }];
- const nextPlacement = placements$1[currentIndex + 1];
- // There are more placements to check.
- if (nextPlacement) {
- return {
- data: {
- index: currentIndex + 1,
- overflows: allOverflows
- },
- reset: {
- placement: nextPlacement
- }
- };
- }
- const placementsSortedByMostSpace = allOverflows.map(d => {
- const alignment = getAlignment(d.placement);
- return [d.placement, alignment && crossAxis ?
- // Check along the mainAxis and main crossAxis side.
- d.overflows.slice(0, 2).reduce((acc, v) => acc + v, 0) :
- // Check only the mainAxis.
- d.overflows[0], d.overflows];
- }).sort((a, b) => a[1] - b[1]);
- const placementsThatFitOnEachSide = placementsSortedByMostSpace.filter(d => d[2].slice(0,
- // Aligned placements should not check their opposite crossAxis
- // side.
- getAlignment(d[0]) ? 2 : 3).every(v => v <= 0));
- const resetPlacement = ((_placementsThatFitOnE = placementsThatFitOnEachSide[0]) == null ? void 0 : _placementsThatFitOnE[0]) || placementsSortedByMostSpace[0][0];
- if (resetPlacement !== placement) {
- return {
- data: {
- index: currentIndex + 1,
- overflows: allOverflows
- },
- reset: {
- placement: resetPlacement
- }
- };
- }
- return {};
- }
- };
- };
- /**
- * Optimizes the visibility of the floating element by flipping the `placement`
- * in order to keep it in view when the preferred placement(s) will overflow the
- * clipping boundary. Alternative to `autoPlacement`.
- * @see https://floating-ui.com/docs/flip
- */
- const flip = function (options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: 'flip',
- options,
- async fn(state) {
- var _middlewareData$arrow, _middlewareData$flip;
- const {
- placement,
- middlewareData,
- rects,
- initialPlacement,
- platform,
- elements
- } = state;
- const {
- mainAxis: checkMainAxis = true,
- crossAxis: checkCrossAxis = true,
- fallbackPlacements: specifiedFallbackPlacements,
- fallbackStrategy = 'bestFit',
- fallbackAxisSideDirection = 'none',
- flipAlignment = true,
- ...detectOverflowOptions
- } = evaluate(options, state);
- // If a reset by the arrow was caused due to an alignment offset being
- // added, we should skip any logic now since `flip()` has already done its
- // work.
- // https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643
- if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
- return {};
- }
- const side = getSide(placement);
- const isBasePlacement = getSide(initialPlacement) === initialPlacement;
- const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
- const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
- if (!specifiedFallbackPlacements && fallbackAxisSideDirection !== 'none') {
- fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));
- }
- const placements = [initialPlacement, ...fallbackPlacements];
- const overflow = await detectOverflow(state, detectOverflowOptions);
- const overflows = [];
- let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
- if (checkMainAxis) {
- overflows.push(overflow[side]);
- }
- if (checkCrossAxis) {
- const sides = getAlignmentSides(placement, rects, rtl);
- overflows.push(overflow[sides[0]], overflow[sides[1]]);
- }
- overflowsData = [...overflowsData, {
- placement,
- overflows
- }];
- // One or more sides is overflowing.
- if (!overflows.every(side => side <= 0)) {
- var _middlewareData$flip2, _overflowsData$filter;
- const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;
- const nextPlacement = placements[nextIndex];
- if (nextPlacement) {
- // Try next placement and re-run the lifecycle.
- return {
- data: {
- index: nextIndex,
- overflows: overflowsData
- },
- reset: {
- placement: nextPlacement
- }
- };
- }
- // First, find the candidates that fit on the mainAxis side of overflow,
- // then find the placement that fits the best on the main crossAxis side.
- let resetPlacement = (_overflowsData$filter = overflowsData.filter(d => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;
- // Otherwise fallback.
- if (!resetPlacement) {
- switch (fallbackStrategy) {
- case 'bestFit':
- {
- var _overflowsData$map$so;
- const placement = (_overflowsData$map$so = overflowsData.map(d => [d.placement, d.overflows.filter(overflow => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$map$so[0];
- if (placement) {
- resetPlacement = placement;
- }
- break;
- }
- case 'initialPlacement':
- resetPlacement = initialPlacement;
- break;
- }
- }
- if (placement !== resetPlacement) {
- return {
- reset: {
- placement: resetPlacement
- }
- };
- }
- }
- return {};
- }
- };
- };
- function getSideOffsets(overflow, rect) {
- return {
- top: overflow.top - rect.height,
- right: overflow.right - rect.width,
- bottom: overflow.bottom - rect.height,
- left: overflow.left - rect.width
- };
- }
- function isAnySideFullyClipped(overflow) {
- return sides.some(side => overflow[side] >= 0);
- }
- /**
- * Provides data to hide the floating element in applicable situations, such as
- * when it is not in the same clipping context as the reference element.
- * @see https://floating-ui.com/docs/hide
- */
- const hide = function (options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: 'hide',
- options,
- async fn(state) {
- const {
- rects
- } = state;
- const {
- strategy = 'referenceHidden',
- ...detectOverflowOptions
- } = evaluate(options, state);
- switch (strategy) {
- case 'referenceHidden':
- {
- const overflow = await detectOverflow(state, {
- ...detectOverflowOptions,
- elementContext: 'reference'
- });
- const offsets = getSideOffsets(overflow, rects.reference);
- return {
- data: {
- referenceHiddenOffsets: offsets,
- referenceHidden: isAnySideFullyClipped(offsets)
- }
- };
- }
- case 'escaped':
- {
- const overflow = await detectOverflow(state, {
- ...detectOverflowOptions,
- altBoundary: true
- });
- const offsets = getSideOffsets(overflow, rects.floating);
- return {
- data: {
- escapedOffsets: offsets,
- escaped: isAnySideFullyClipped(offsets)
- }
- };
- }
- default:
- {
- return {};
- }
- }
- }
- };
- };
- function getBoundingRect(rects) {
- const minX = min(...rects.map(rect => rect.left));
- const minY = min(...rects.map(rect => rect.top));
- const maxX = max(...rects.map(rect => rect.right));
- const maxY = max(...rects.map(rect => rect.bottom));
- return {
- x: minX,
- y: minY,
- width: maxX - minX,
- height: maxY - minY
- };
- }
- function getRectsByLine(rects) {
- const sortedRects = rects.slice().sort((a, b) => a.y - b.y);
- const groups = [];
- let prevRect = null;
- for (let i = 0; i < sortedRects.length; i++) {
- const rect = sortedRects[i];
- if (!prevRect || rect.y - prevRect.y > prevRect.height / 2) {
- groups.push([rect]);
- } else {
- groups[groups.length - 1].push(rect);
- }
- prevRect = rect;
- }
- return groups.map(rect => rectToClientRect(getBoundingRect(rect)));
- }
- /**
- * Provides improved positioning for inline reference elements that can span
- * over multiple lines, such as hyperlinks or range selections.
- * @see https://floating-ui.com/docs/inline
- */
- const inline = function (options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: 'inline',
- options,
- async fn(state) {
- const {
- placement,
- elements,
- rects,
- platform,
- strategy
- } = state;
- // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a
- // ClientRect's bounds, despite the event listener being triggered. A
- // padding of 2 seems to handle this issue.
- const {
- padding = 2,
- x,
- y
- } = evaluate(options, state);
- const nativeClientRects = Array.from((await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) || []);
- const clientRects = getRectsByLine(nativeClientRects);
- const fallback = rectToClientRect(getBoundingRect(nativeClientRects));
- const paddingObject = getPaddingObject(padding);
- function getBoundingClientRect() {
- // There are two rects and they are disjoined.
- if (clientRects.length === 2 && clientRects[0].left > clientRects[1].right && x != null && y != null) {
- // Find the first rect in which the point is fully inside.
- return clientRects.find(rect => x > rect.left - paddingObject.left && x < rect.right + paddingObject.right && y > rect.top - paddingObject.top && y < rect.bottom + paddingObject.bottom) || fallback;
- }
- // There are 2 or more connected rects.
- if (clientRects.length >= 2) {
- if (getSideAxis(placement) === 'y') {
- const firstRect = clientRects[0];
- const lastRect = clientRects[clientRects.length - 1];
- const isTop = getSide(placement) === 'top';
- const top = firstRect.top;
- const bottom = lastRect.bottom;
- const left = isTop ? firstRect.left : lastRect.left;
- const right = isTop ? firstRect.right : lastRect.right;
- const width = right - left;
- const height = bottom - top;
- return {
- top,
- bottom,
- left,
- right,
- width,
- height,
- x: left,
- y: top
- };
- }
- const isLeftSide = getSide(placement) === 'left';
- const maxRight = max(...clientRects.map(rect => rect.right));
- const minLeft = min(...clientRects.map(rect => rect.left));
- const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);
- const top = measureRects[0].top;
- const bottom = measureRects[measureRects.length - 1].bottom;
- const left = minLeft;
- const right = maxRight;
- const width = right - left;
- const height = bottom - top;
- return {
- top,
- bottom,
- left,
- right,
- width,
- height,
- x: left,
- y: top
- };
- }
- return fallback;
- }
- const resetRects = await platform.getElementRects({
- reference: {
- getBoundingClientRect
- },
- floating: elements.floating,
- strategy
- });
- if (rects.reference.x !== resetRects.reference.x || rects.reference.y !== resetRects.reference.y || rects.reference.width !== resetRects.reference.width || rects.reference.height !== resetRects.reference.height) {
- return {
- reset: {
- rects: resetRects
- }
- };
- }
- return {};
- }
- };
- };
- // For type backwards-compatibility, the `OffsetOptions` type was also
- // Derivable.
- async function convertValueToCoords(state, options) {
- const {
- placement,
- platform,
- elements
- } = state;
- const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
- const side = getSide(placement);
- const alignment = getAlignment(placement);
- const isVertical = getSideAxis(placement) === 'y';
- const mainAxisMulti = ['left', 'top'].includes(side) ? -1 : 1;
- const crossAxisMulti = rtl && isVertical ? -1 : 1;
- const rawValue = evaluate(options, state);
- let {
- mainAxis,
- crossAxis,
- alignmentAxis
- } = typeof rawValue === 'number' ? {
- mainAxis: rawValue,
- crossAxis: 0,
- alignmentAxis: null
- } : {
- mainAxis: 0,
- crossAxis: 0,
- alignmentAxis: null,
- ...rawValue
- };
- if (alignment && typeof alignmentAxis === 'number') {
- crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;
- }
- return isVertical ? {
- x: crossAxis * crossAxisMulti,
- y: mainAxis * mainAxisMulti
- } : {
- x: mainAxis * mainAxisMulti,
- y: crossAxis * crossAxisMulti
- };
- }
- /**
- * Modifies the placement by translating the floating element along the
- * specified axes.
- * A number (shorthand for `mainAxis` or distance), or an axes configuration
- * object may be passed.
- * @see https://floating-ui.com/docs/offset
- */
- const offset = function (options) {
- if (options === void 0) {
- options = 0;
- }
- return {
- name: 'offset',
- options,
- async fn(state) {
- var _middlewareData$offse, _middlewareData$arrow;
- const {
- x,
- y,
- placement,
- middlewareData
- } = state;
- const diffCoords = await convertValueToCoords(state, options);
- // If the placement is the same and the arrow caused an alignment offset
- // then we don't need to change the positioning coordinates.
- if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
- return {};
- }
- return {
- x: x + diffCoords.x,
- y: y + diffCoords.y,
- data: {
- ...diffCoords,
- placement
- }
- };
- }
- };
- };
- /**
- * Optimizes the visibility of the floating element by shifting it in order to
- * keep it in view when it will overflow the clipping boundary.
- * @see https://floating-ui.com/docs/shift
- */
- const shift = function (options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: 'shift',
- options,
- async fn(state) {
- const {
- x,
- y,
- placement
- } = state;
- const {
- mainAxis: checkMainAxis = true,
- crossAxis: checkCrossAxis = false,
- limiter = {
- fn: _ref => {
- let {
- x,
- y
- } = _ref;
- return {
- x,
- y
- };
- }
- },
- ...detectOverflowOptions
- } = evaluate(options, state);
- const coords = {
- x,
- y
- };
- const overflow = await detectOverflow(state, detectOverflowOptions);
- const crossAxis = getSideAxis(getSide(placement));
- const mainAxis = getOppositeAxis(crossAxis);
- let mainAxisCoord = coords[mainAxis];
- let crossAxisCoord = coords[crossAxis];
- if (checkMainAxis) {
- const minSide = mainAxis === 'y' ? 'top' : 'left';
- const maxSide = mainAxis === 'y' ? 'bottom' : 'right';
- const min = mainAxisCoord + overflow[minSide];
- const max = mainAxisCoord - overflow[maxSide];
- mainAxisCoord = clamp(min, mainAxisCoord, max);
- }
- if (checkCrossAxis) {
- const minSide = crossAxis === 'y' ? 'top' : 'left';
- const maxSide = crossAxis === 'y' ? 'bottom' : 'right';
- const min = crossAxisCoord + overflow[minSide];
- const max = crossAxisCoord - overflow[maxSide];
- crossAxisCoord = clamp(min, crossAxisCoord, max);
- }
- const limitedCoords = limiter.fn({
- ...state,
- [mainAxis]: mainAxisCoord,
- [crossAxis]: crossAxisCoord
- });
- return {
- ...limitedCoords,
- data: {
- x: limitedCoords.x - x,
- y: limitedCoords.y - y
- }
- };
- }
- };
- };
- /**
- * Built-in `limiter` that will stop `shift()` at a certain point.
- */
- const limitShift = function (options) {
- if (options === void 0) {
- options = {};
- }
- return {
- options,
- fn(state) {
- const {
- x,
- y,
- placement,
- rects,
- middlewareData
- } = state;
- const {
- offset = 0,
- mainAxis: checkMainAxis = true,
- crossAxis: checkCrossAxis = true
- } = evaluate(options, state);
- const coords = {
- x,
- y
- };
- const crossAxis = getSideAxis(placement);
- const mainAxis = getOppositeAxis(crossAxis);
- let mainAxisCoord = coords[mainAxis];
- let crossAxisCoord = coords[crossAxis];
- const rawOffset = evaluate(offset, state);
- const computedOffset = typeof rawOffset === 'number' ? {
- mainAxis: rawOffset,
- crossAxis: 0
- } : {
- mainAxis: 0,
- crossAxis: 0,
- ...rawOffset
- };
- if (checkMainAxis) {
- const len = mainAxis === 'y' ? 'height' : 'width';
- const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;
- const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;
- if (mainAxisCoord < limitMin) {
- mainAxisCoord = limitMin;
- } else if (mainAxisCoord > limitMax) {
- mainAxisCoord = limitMax;
- }
- }
- if (checkCrossAxis) {
- var _middlewareData$offse, _middlewareData$offse2;
- const len = mainAxis === 'y' ? 'width' : 'height';
- const isOriginSide = ['top', 'left'].includes(getSide(placement));
- const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis);
- const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0);
- if (crossAxisCoord < limitMin) {
- crossAxisCoord = limitMin;
- } else if (crossAxisCoord > limitMax) {
- crossAxisCoord = limitMax;
- }
- }
- return {
- [mainAxis]: mainAxisCoord,
- [crossAxis]: crossAxisCoord
- };
- }
- };
- };
- /**
- * Provides data that allows you to change the size of the floating element —
- * for instance, prevent it from overflowing the clipping boundary or match the
- * width of the reference element.
- * @see https://floating-ui.com/docs/size
- */
- const size = function (options) {
- if (options === void 0) {
- options = {};
- }
- return {
- name: 'size',
- options,
- async fn(state) {
- const {
- placement,
- rects,
- platform,
- elements
- } = state;
- const {
- apply = () => {},
- ...detectOverflowOptions
- } = evaluate(options, state);
- const overflow = await detectOverflow(state, detectOverflowOptions);
- const side = getSide(placement);
- const alignment = getAlignment(placement);
- const isYAxis = getSideAxis(placement) === 'y';
- const {
- width,
- height
- } = rects.floating;
- let heightSide;
- let widthSide;
- if (side === 'top' || side === 'bottom') {
- heightSide = side;
- widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';
- } else {
- widthSide = side;
- heightSide = alignment === 'end' ? 'top' : 'bottom';
- }
- const overflowAvailableHeight = height - overflow[heightSide];
- const overflowAvailableWidth = width - overflow[widthSide];
- const noShift = !state.middlewareData.shift;
- let availableHeight = overflowAvailableHeight;
- let availableWidth = overflowAvailableWidth;
- if (isYAxis) {
- const maximumClippingWidth = width - overflow.left - overflow.right;
- availableWidth = alignment || noShift ? min(overflowAvailableWidth, maximumClippingWidth) : maximumClippingWidth;
- } else {
- const maximumClippingHeight = height - overflow.top - overflow.bottom;
- availableHeight = alignment || noShift ? min(overflowAvailableHeight, maximumClippingHeight) : maximumClippingHeight;
- }
- if (noShift && !alignment) {
- const xMin = max(overflow.left, 0);
- const xMax = max(overflow.right, 0);
- const yMin = max(overflow.top, 0);
- const yMax = max(overflow.bottom, 0);
- if (isYAxis) {
- availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right));
- } else {
- availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom));
- }
- }
- await apply({
- ...state,
- availableWidth,
- availableHeight
- });
- const nextDimensions = await platform.getDimensions(elements.floating);
- if (width !== nextDimensions.width || height !== nextDimensions.height) {
- return {
- reset: {
- rects: true
- }
- };
- }
- return {};
- }
- };
- };
- exports.arrow = arrow;
- exports.autoPlacement = autoPlacement;
- exports.computePosition = computePosition;
- exports.detectOverflow = detectOverflow;
- exports.flip = flip;
- exports.hide = hide;
- exports.inline = inline;
- exports.limitShift = limitShift;
- exports.offset = offset;
- exports.rectToClientRect = rectToClientRect;
- exports.shift = shift;
- exports.size = size;
- }));
|