floating-ui.core.browser.mjs 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  1. function getSide(placement) {
  2. return placement.split('-')[0];
  3. }
  4. function getAlignment(placement) {
  5. return placement.split('-')[1];
  6. }
  7. function getMainAxisFromPlacement(placement) {
  8. return ['top', 'bottom'].includes(getSide(placement)) ? 'x' : 'y';
  9. }
  10. function getLengthFromAxis(axis) {
  11. return axis === 'y' ? 'height' : 'width';
  12. }
  13. function computeCoordsFromPlacement(_ref, placement, rtl) {
  14. let {
  15. reference,
  16. floating
  17. } = _ref;
  18. const commonX = reference.x + reference.width / 2 - floating.width / 2;
  19. const commonY = reference.y + reference.height / 2 - floating.height / 2;
  20. const mainAxis = getMainAxisFromPlacement(placement);
  21. const length = getLengthFromAxis(mainAxis);
  22. const commonAlign = reference[length] / 2 - floating[length] / 2;
  23. const side = getSide(placement);
  24. const isVertical = mainAxis === 'x';
  25. let coords;
  26. switch (side) {
  27. case 'top':
  28. coords = {
  29. x: commonX,
  30. y: reference.y - floating.height
  31. };
  32. break;
  33. case 'bottom':
  34. coords = {
  35. x: commonX,
  36. y: reference.y + reference.height
  37. };
  38. break;
  39. case 'right':
  40. coords = {
  41. x: reference.x + reference.width,
  42. y: commonY
  43. };
  44. break;
  45. case 'left':
  46. coords = {
  47. x: reference.x - floating.width,
  48. y: commonY
  49. };
  50. break;
  51. default:
  52. coords = {
  53. x: reference.x,
  54. y: reference.y
  55. };
  56. }
  57. switch (getAlignment(placement)) {
  58. case 'start':
  59. coords[mainAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
  60. break;
  61. case 'end':
  62. coords[mainAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
  63. break;
  64. }
  65. return coords;
  66. }
  67. /**
  68. * Computes the `x` and `y` coordinates that will place the floating element
  69. * next to a reference element when it is given a certain positioning strategy.
  70. *
  71. * This export does not have any `platform` interface logic. You will need to
  72. * write one for the platform you are using Floating UI with.
  73. */
  74. const computePosition = async (reference, floating, config) => {
  75. const {
  76. placement = 'bottom',
  77. strategy = 'absolute',
  78. middleware = [],
  79. platform
  80. } = config;
  81. const validMiddleware = middleware.filter(Boolean);
  82. const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));
  83. {
  84. if (platform == null) {
  85. console.error(['Floating UI: `platform` property was not passed to config. If you', 'want to use Floating UI on the web, install @floating-ui/dom', 'instead of the /core package. Otherwise, you can create your own', '`platform`: https://floating-ui.com/docs/platform'].join(' '));
  86. }
  87. if (validMiddleware.filter(_ref => {
  88. let {
  89. name
  90. } = _ref;
  91. return name === 'autoPlacement' || name === 'flip';
  92. }).length > 1) {
  93. throw new Error(['Floating UI: duplicate `flip` and/or `autoPlacement` middleware', 'detected. This will lead to an infinite loop. Ensure only one of', 'either has been passed to the `middleware` array.'].join(' '));
  94. }
  95. if (!reference || !floating) {
  96. console.error(['Floating UI: The reference and/or floating element was not defined', 'when `computePosition()` was called. Ensure that both elements have', 'been created and can be measured.'].join(' '));
  97. }
  98. }
  99. let rects = await platform.getElementRects({
  100. reference,
  101. floating,
  102. strategy
  103. });
  104. let {
  105. x,
  106. y
  107. } = computeCoordsFromPlacement(rects, placement, rtl);
  108. let statefulPlacement = placement;
  109. let middlewareData = {};
  110. let resetCount = 0;
  111. for (let i = 0; i < validMiddleware.length; i++) {
  112. const {
  113. name,
  114. fn
  115. } = validMiddleware[i];
  116. const {
  117. x: nextX,
  118. y: nextY,
  119. data,
  120. reset
  121. } = await fn({
  122. x,
  123. y,
  124. initialPlacement: placement,
  125. placement: statefulPlacement,
  126. strategy,
  127. middlewareData,
  128. rects,
  129. platform,
  130. elements: {
  131. reference,
  132. floating
  133. }
  134. });
  135. x = nextX != null ? nextX : x;
  136. y = nextY != null ? nextY : y;
  137. middlewareData = { ...middlewareData,
  138. [name]: { ...middlewareData[name],
  139. ...data
  140. }
  141. };
  142. {
  143. if (resetCount > 50) {
  144. console.warn(['Floating UI: The middleware lifecycle appears to be running in an', 'infinite loop. This is usually caused by a `reset` continually', 'being returned without a break condition.'].join(' '));
  145. }
  146. }
  147. if (reset && resetCount <= 50) {
  148. resetCount++;
  149. if (typeof reset === 'object') {
  150. if (reset.placement) {
  151. statefulPlacement = reset.placement;
  152. }
  153. if (reset.rects) {
  154. rects = reset.rects === true ? await platform.getElementRects({
  155. reference,
  156. floating,
  157. strategy
  158. }) : reset.rects;
  159. }
  160. ({
  161. x,
  162. y
  163. } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
  164. }
  165. i = -1;
  166. continue;
  167. }
  168. }
  169. return {
  170. x,
  171. y,
  172. placement: statefulPlacement,
  173. strategy,
  174. middlewareData
  175. };
  176. };
  177. function expandPaddingObject(padding) {
  178. return {
  179. top: 0,
  180. right: 0,
  181. bottom: 0,
  182. left: 0,
  183. ...padding
  184. };
  185. }
  186. function getSideObjectFromPadding(padding) {
  187. return typeof padding !== 'number' ? expandPaddingObject(padding) : {
  188. top: padding,
  189. right: padding,
  190. bottom: padding,
  191. left: padding
  192. };
  193. }
  194. function rectToClientRect(rect) {
  195. return { ...rect,
  196. top: rect.y,
  197. left: rect.x,
  198. right: rect.x + rect.width,
  199. bottom: rect.y + rect.height
  200. };
  201. }
  202. /**
  203. * Resolves with an object of overflow side offsets that determine how much the
  204. * element is overflowing a given clipping boundary.
  205. * - positive = overflowing the boundary by that number of pixels
  206. * - negative = how many pixels left before it will overflow
  207. * - 0 = lies flush with the boundary
  208. * @see https://floating-ui.com/docs/detectOverflow
  209. */
  210. async function detectOverflow(middlewareArguments, options) {
  211. var _await$platform$isEle;
  212. if (options === void 0) {
  213. options = {};
  214. }
  215. const {
  216. x,
  217. y,
  218. platform,
  219. rects,
  220. elements,
  221. strategy
  222. } = middlewareArguments;
  223. const {
  224. boundary = 'clippingAncestors',
  225. rootBoundary = 'viewport',
  226. elementContext = 'floating',
  227. altBoundary = false,
  228. padding = 0
  229. } = options;
  230. const paddingObject = getSideObjectFromPadding(padding);
  231. const altContext = elementContext === 'floating' ? 'reference' : 'floating';
  232. const element = elements[altBoundary ? altContext : elementContext];
  233. const clippingClientRect = rectToClientRect(await platform.getClippingRect({
  234. 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))),
  235. boundary,
  236. rootBoundary,
  237. strategy
  238. }));
  239. const rect = elementContext === 'floating' ? { ...rects.floating,
  240. x,
  241. y
  242. } : rects.reference;
  243. const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));
  244. const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) ? (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {
  245. x: 1,
  246. y: 1
  247. } : {
  248. x: 1,
  249. y: 1
  250. };
  251. const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
  252. rect,
  253. offsetParent,
  254. strategy
  255. }) : rect);
  256. return {
  257. top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
  258. bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
  259. left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
  260. right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x
  261. };
  262. }
  263. const min = Math.min;
  264. const max = Math.max;
  265. function within(min$1, value, max$1) {
  266. return max(min$1, min(value, max$1));
  267. }
  268. /**
  269. * Positions an inner element of the floating element such that it is centered
  270. * to the reference element.
  271. * @see https://floating-ui.com/docs/arrow
  272. */
  273. const arrow = options => ({
  274. name: 'arrow',
  275. options,
  276. async fn(middlewareArguments) {
  277. // Since `element` is required, we don't Partial<> the type
  278. const {
  279. element,
  280. padding = 0
  281. } = options != null ? options : {};
  282. const {
  283. x,
  284. y,
  285. placement,
  286. rects,
  287. platform
  288. } = middlewareArguments;
  289. if (element == null) {
  290. {
  291. console.warn('Floating UI: No `element` was passed to the `arrow` middleware.');
  292. }
  293. return {};
  294. }
  295. const paddingObject = getSideObjectFromPadding(padding);
  296. const coords = {
  297. x,
  298. y
  299. };
  300. const axis = getMainAxisFromPlacement(placement);
  301. const alignment = getAlignment(placement);
  302. const length = getLengthFromAxis(axis);
  303. const arrowDimensions = await platform.getDimensions(element);
  304. const minProp = axis === 'y' ? 'top' : 'left';
  305. const maxProp = axis === 'y' ? 'bottom' : 'right';
  306. const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];
  307. const startDiff = coords[axis] - rects.reference[axis];
  308. const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));
  309. let clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;
  310. if (clientSize === 0) {
  311. clientSize = rects.floating[length];
  312. }
  313. const centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the floating element if the center
  314. // point is outside the floating element's bounds
  315. const min = paddingObject[minProp];
  316. const max = clientSize - arrowDimensions[length] - paddingObject[maxProp];
  317. const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
  318. const offset = within(min, center, max); // Make sure that arrow points at the reference
  319. const alignmentPadding = alignment === 'start' ? paddingObject[minProp] : paddingObject[maxProp];
  320. const shouldAddOffset = alignmentPadding > 0 && center !== offset && rects.reference[length] <= rects.floating[length];
  321. const alignmentOffset = shouldAddOffset ? center < min ? min - center : max - center : 0;
  322. return {
  323. [axis]: coords[axis] - alignmentOffset,
  324. data: {
  325. [axis]: offset,
  326. centerOffset: center - offset
  327. }
  328. };
  329. }
  330. });
  331. const hash$1 = {
  332. left: 'right',
  333. right: 'left',
  334. bottom: 'top',
  335. top: 'bottom'
  336. };
  337. function getOppositePlacement(placement) {
  338. return placement.replace(/left|right|bottom|top/g, matched => hash$1[matched]);
  339. }
  340. function getAlignmentSides(placement, rects, rtl) {
  341. if (rtl === void 0) {
  342. rtl = false;
  343. }
  344. const alignment = getAlignment(placement);
  345. const mainAxis = getMainAxisFromPlacement(placement);
  346. const length = getLengthFromAxis(mainAxis);
  347. let mainAlignmentSide = mainAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';
  348. if (rects.reference[length] > rects.floating[length]) {
  349. mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
  350. }
  351. return {
  352. main: mainAlignmentSide,
  353. cross: getOppositePlacement(mainAlignmentSide)
  354. };
  355. }
  356. const hash = {
  357. start: 'end',
  358. end: 'start'
  359. };
  360. function getOppositeAlignmentPlacement(placement) {
  361. return placement.replace(/start|end/g, matched => hash[matched]);
  362. }
  363. const sides = ['top', 'right', 'bottom', 'left'];
  364. const allPlacements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + "-start", side + "-end"), []);
  365. function getPlacementList(alignment, autoAlignment, allowedPlacements) {
  366. const allowedPlacementsSortedByAlignment = alignment ? [...allowedPlacements.filter(placement => getAlignment(placement) === alignment), ...allowedPlacements.filter(placement => getAlignment(placement) !== alignment)] : allowedPlacements.filter(placement => getSide(placement) === placement);
  367. return allowedPlacementsSortedByAlignment.filter(placement => {
  368. if (alignment) {
  369. return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);
  370. }
  371. return true;
  372. });
  373. }
  374. /**
  375. * Automatically chooses the `placement` which has the most space available.
  376. * @see https://floating-ui.com/docs/autoPlacement
  377. */
  378. const autoPlacement = function (options) {
  379. if (options === void 0) {
  380. options = {};
  381. }
  382. return {
  383. name: 'autoPlacement',
  384. options,
  385. async fn(middlewareArguments) {
  386. var _middlewareData$autoP, _middlewareData$autoP2, _middlewareData$autoP3, _middlewareData$autoP4, _placementsSortedByLe;
  387. const {
  388. x,
  389. y,
  390. rects,
  391. middlewareData,
  392. placement,
  393. platform,
  394. elements
  395. } = middlewareArguments;
  396. const {
  397. alignment = null,
  398. allowedPlacements = allPlacements,
  399. autoAlignment = true,
  400. ...detectOverflowOptions
  401. } = options;
  402. const placements = getPlacementList(alignment, autoAlignment, allowedPlacements);
  403. const overflow = await detectOverflow(middlewareArguments, detectOverflowOptions);
  404. const currentIndex = (_middlewareData$autoP = (_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.index) != null ? _middlewareData$autoP : 0;
  405. const currentPlacement = placements[currentIndex];
  406. if (currentPlacement == null) {
  407. return {};
  408. }
  409. const {
  410. main,
  411. cross
  412. } = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))); // Make `computeCoords` start from the right place
  413. if (placement !== currentPlacement) {
  414. return {
  415. x,
  416. y,
  417. reset: {
  418. placement: placements[0]
  419. }
  420. };
  421. }
  422. const currentOverflows = [overflow[getSide(currentPlacement)], overflow[main], overflow[cross]];
  423. const allOverflows = [...((_middlewareData$autoP3 = (_middlewareData$autoP4 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP4.overflows) != null ? _middlewareData$autoP3 : []), {
  424. placement: currentPlacement,
  425. overflows: currentOverflows
  426. }];
  427. const nextPlacement = placements[currentIndex + 1]; // There are more placements to check
  428. if (nextPlacement) {
  429. return {
  430. data: {
  431. index: currentIndex + 1,
  432. overflows: allOverflows
  433. },
  434. reset: {
  435. placement: nextPlacement
  436. }
  437. };
  438. }
  439. const placementsSortedByLeastOverflow = allOverflows.slice().sort((a, b) => a.overflows[0] - b.overflows[0]);
  440. const placementThatFitsOnAllSides = (_placementsSortedByLe = placementsSortedByLeastOverflow.find(_ref => {
  441. let {
  442. overflows
  443. } = _ref;
  444. return overflows.every(overflow => overflow <= 0);
  445. })) == null ? void 0 : _placementsSortedByLe.placement;
  446. const resetPlacement = placementThatFitsOnAllSides != null ? placementThatFitsOnAllSides : placementsSortedByLeastOverflow[0].placement;
  447. if (resetPlacement !== placement) {
  448. return {
  449. data: {
  450. index: currentIndex + 1,
  451. overflows: allOverflows
  452. },
  453. reset: {
  454. placement: resetPlacement
  455. }
  456. };
  457. }
  458. return {};
  459. }
  460. };
  461. };
  462. function getExpandedPlacements(placement) {
  463. const oppositePlacement = getOppositePlacement(placement);
  464. return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
  465. }
  466. /**
  467. * Changes the placement of the floating element to one that will fit if the
  468. * initially specified `placement` does not.
  469. * @see https://floating-ui.com/docs/flip
  470. */
  471. const flip = function (options) {
  472. if (options === void 0) {
  473. options = {};
  474. }
  475. return {
  476. name: 'flip',
  477. options,
  478. async fn(middlewareArguments) {
  479. var _middlewareData$flip;
  480. const {
  481. placement,
  482. middlewareData,
  483. rects,
  484. initialPlacement,
  485. platform,
  486. elements
  487. } = middlewareArguments;
  488. const {
  489. mainAxis: checkMainAxis = true,
  490. crossAxis: checkCrossAxis = true,
  491. fallbackPlacements: specifiedFallbackPlacements,
  492. fallbackStrategy = 'bestFit',
  493. flipAlignment = true,
  494. ...detectOverflowOptions
  495. } = options;
  496. const side = getSide(placement);
  497. const isBasePlacement = side === initialPlacement;
  498. const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
  499. const placements = [initialPlacement, ...fallbackPlacements];
  500. const overflow = await detectOverflow(middlewareArguments, detectOverflowOptions);
  501. const overflows = [];
  502. let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
  503. if (checkMainAxis) {
  504. overflows.push(overflow[side]);
  505. }
  506. if (checkCrossAxis) {
  507. const {
  508. main,
  509. cross
  510. } = getAlignmentSides(placement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));
  511. overflows.push(overflow[main], overflow[cross]);
  512. }
  513. overflowsData = [...overflowsData, {
  514. placement,
  515. overflows
  516. }]; // One or more sides is overflowing
  517. if (!overflows.every(side => side <= 0)) {
  518. var _middlewareData$flip$, _middlewareData$flip2;
  519. const nextIndex = ((_middlewareData$flip$ = (_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) != null ? _middlewareData$flip$ : 0) + 1;
  520. const nextPlacement = placements[nextIndex];
  521. if (nextPlacement) {
  522. // Try next placement and re-run the lifecycle
  523. return {
  524. data: {
  525. index: nextIndex,
  526. overflows: overflowsData
  527. },
  528. reset: {
  529. placement: nextPlacement
  530. }
  531. };
  532. }
  533. let resetPlacement = 'bottom';
  534. switch (fallbackStrategy) {
  535. case 'bestFit':
  536. {
  537. var _overflowsData$map$so;
  538. const placement = (_overflowsData$map$so = overflowsData.map(d => [d, 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].placement;
  539. if (placement) {
  540. resetPlacement = placement;
  541. }
  542. break;
  543. }
  544. case 'initialPlacement':
  545. resetPlacement = initialPlacement;
  546. break;
  547. }
  548. if (placement !== resetPlacement) {
  549. return {
  550. reset: {
  551. placement: resetPlacement
  552. }
  553. };
  554. }
  555. }
  556. return {};
  557. }
  558. };
  559. };
  560. function getSideOffsets(overflow, rect) {
  561. return {
  562. top: overflow.top - rect.height,
  563. right: overflow.right - rect.width,
  564. bottom: overflow.bottom - rect.height,
  565. left: overflow.left - rect.width
  566. };
  567. }
  568. function isAnySideFullyClipped(overflow) {
  569. return sides.some(side => overflow[side] >= 0);
  570. }
  571. /**
  572. * Provides data to hide the floating element in applicable situations, such as
  573. * when it is not in the same clipping context as the reference element.
  574. * @see https://floating-ui.com/docs/hide
  575. */
  576. const hide = function (_temp) {
  577. let {
  578. strategy = 'referenceHidden',
  579. ...detectOverflowOptions
  580. } = _temp === void 0 ? {} : _temp;
  581. return {
  582. name: 'hide',
  583. async fn(middlewareArguments) {
  584. const {
  585. rects
  586. } = middlewareArguments;
  587. switch (strategy) {
  588. case 'referenceHidden':
  589. {
  590. const overflow = await detectOverflow(middlewareArguments, { ...detectOverflowOptions,
  591. elementContext: 'reference'
  592. });
  593. const offsets = getSideOffsets(overflow, rects.reference);
  594. return {
  595. data: {
  596. referenceHiddenOffsets: offsets,
  597. referenceHidden: isAnySideFullyClipped(offsets)
  598. }
  599. };
  600. }
  601. case 'escaped':
  602. {
  603. const overflow = await detectOverflow(middlewareArguments, { ...detectOverflowOptions,
  604. altBoundary: true
  605. });
  606. const offsets = getSideOffsets(overflow, rects.floating);
  607. return {
  608. data: {
  609. escapedOffsets: offsets,
  610. escaped: isAnySideFullyClipped(offsets)
  611. }
  612. };
  613. }
  614. default:
  615. {
  616. return {};
  617. }
  618. }
  619. }
  620. };
  621. };
  622. async function convertValueToCoords(middlewareArguments, value) {
  623. const {
  624. placement,
  625. platform,
  626. elements
  627. } = middlewareArguments;
  628. const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
  629. const side = getSide(placement);
  630. const alignment = getAlignment(placement);
  631. const isVertical = getMainAxisFromPlacement(placement) === 'x';
  632. const mainAxisMulti = ['left', 'top'].includes(side) ? -1 : 1;
  633. const crossAxisMulti = rtl && isVertical ? -1 : 1;
  634. const rawValue = typeof value === 'function' ? value(middlewareArguments) : value; // eslint-disable-next-line prefer-const
  635. let {
  636. mainAxis,
  637. crossAxis,
  638. alignmentAxis
  639. } = typeof rawValue === 'number' ? {
  640. mainAxis: rawValue,
  641. crossAxis: 0,
  642. alignmentAxis: null
  643. } : {
  644. mainAxis: 0,
  645. crossAxis: 0,
  646. alignmentAxis: null,
  647. ...rawValue
  648. };
  649. if (alignment && typeof alignmentAxis === 'number') {
  650. crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;
  651. }
  652. return isVertical ? {
  653. x: crossAxis * crossAxisMulti,
  654. y: mainAxis * mainAxisMulti
  655. } : {
  656. x: mainAxis * mainAxisMulti,
  657. y: crossAxis * crossAxisMulti
  658. };
  659. }
  660. /**
  661. * Displaces the floating element from its reference element.
  662. * @see https://floating-ui.com/docs/offset
  663. */
  664. const offset = function (value) {
  665. if (value === void 0) {
  666. value = 0;
  667. }
  668. return {
  669. name: 'offset',
  670. options: value,
  671. async fn(middlewareArguments) {
  672. const {
  673. x,
  674. y
  675. } = middlewareArguments;
  676. const diffCoords = await convertValueToCoords(middlewareArguments, value);
  677. return {
  678. x: x + diffCoords.x,
  679. y: y + diffCoords.y,
  680. data: diffCoords
  681. };
  682. }
  683. };
  684. };
  685. function getCrossAxis(axis) {
  686. return axis === 'x' ? 'y' : 'x';
  687. }
  688. /**
  689. * Shifts the floating element in order to keep it in view when it will overflow
  690. * a clipping boundary.
  691. * @see https://floating-ui.com/docs/shift
  692. */
  693. const shift = function (options) {
  694. if (options === void 0) {
  695. options = {};
  696. }
  697. return {
  698. name: 'shift',
  699. options,
  700. async fn(middlewareArguments) {
  701. const {
  702. x,
  703. y,
  704. placement
  705. } = middlewareArguments;
  706. const {
  707. mainAxis: checkMainAxis = true,
  708. crossAxis: checkCrossAxis = false,
  709. limiter = {
  710. fn: _ref => {
  711. let {
  712. x,
  713. y
  714. } = _ref;
  715. return {
  716. x,
  717. y
  718. };
  719. }
  720. },
  721. ...detectOverflowOptions
  722. } = options;
  723. const coords = {
  724. x,
  725. y
  726. };
  727. const overflow = await detectOverflow(middlewareArguments, detectOverflowOptions);
  728. const mainAxis = getMainAxisFromPlacement(getSide(placement));
  729. const crossAxis = getCrossAxis(mainAxis);
  730. let mainAxisCoord = coords[mainAxis];
  731. let crossAxisCoord = coords[crossAxis];
  732. if (checkMainAxis) {
  733. const minSide = mainAxis === 'y' ? 'top' : 'left';
  734. const maxSide = mainAxis === 'y' ? 'bottom' : 'right';
  735. const min = mainAxisCoord + overflow[minSide];
  736. const max = mainAxisCoord - overflow[maxSide];
  737. mainAxisCoord = within(min, mainAxisCoord, max);
  738. }
  739. if (checkCrossAxis) {
  740. const minSide = crossAxis === 'y' ? 'top' : 'left';
  741. const maxSide = crossAxis === 'y' ? 'bottom' : 'right';
  742. const min = crossAxisCoord + overflow[minSide];
  743. const max = crossAxisCoord - overflow[maxSide];
  744. crossAxisCoord = within(min, crossAxisCoord, max);
  745. }
  746. const limitedCoords = limiter.fn({ ...middlewareArguments,
  747. [mainAxis]: mainAxisCoord,
  748. [crossAxis]: crossAxisCoord
  749. });
  750. return { ...limitedCoords,
  751. data: {
  752. x: limitedCoords.x - x,
  753. y: limitedCoords.y - y
  754. }
  755. };
  756. }
  757. };
  758. };
  759. /**
  760. * Built-in `limiter` that will stop `shift()` at a certain point.
  761. */
  762. const limitShift = function (options) {
  763. if (options === void 0) {
  764. options = {};
  765. }
  766. return {
  767. options,
  768. fn(middlewareArguments) {
  769. const {
  770. x,
  771. y,
  772. placement,
  773. rects,
  774. middlewareData
  775. } = middlewareArguments;
  776. const {
  777. offset = 0,
  778. mainAxis: checkMainAxis = true,
  779. crossAxis: checkCrossAxis = true
  780. } = options;
  781. const coords = {
  782. x,
  783. y
  784. };
  785. const mainAxis = getMainAxisFromPlacement(placement);
  786. const crossAxis = getCrossAxis(mainAxis);
  787. let mainAxisCoord = coords[mainAxis];
  788. let crossAxisCoord = coords[crossAxis];
  789. const rawOffset = typeof offset === 'function' ? offset(middlewareArguments) : offset;
  790. const computedOffset = typeof rawOffset === 'number' ? {
  791. mainAxis: rawOffset,
  792. crossAxis: 0
  793. } : {
  794. mainAxis: 0,
  795. crossAxis: 0,
  796. ...rawOffset
  797. };
  798. if (checkMainAxis) {
  799. const len = mainAxis === 'y' ? 'height' : 'width';
  800. const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;
  801. const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;
  802. if (mainAxisCoord < limitMin) {
  803. mainAxisCoord = limitMin;
  804. } else if (mainAxisCoord > limitMax) {
  805. mainAxisCoord = limitMax;
  806. }
  807. }
  808. if (checkCrossAxis) {
  809. var _middlewareData$offse, _middlewareData$offse2, _middlewareData$offse3, _middlewareData$offse4;
  810. const len = mainAxis === 'y' ? 'width' : 'height';
  811. const isOriginSide = ['top', 'left'].includes(getSide(placement));
  812. const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? (_middlewareData$offse = (_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) != null ? _middlewareData$offse : 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis);
  813. const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : (_middlewareData$offse3 = (_middlewareData$offse4 = middlewareData.offset) == null ? void 0 : _middlewareData$offse4[crossAxis]) != null ? _middlewareData$offse3 : 0) - (isOriginSide ? computedOffset.crossAxis : 0);
  814. if (crossAxisCoord < limitMin) {
  815. crossAxisCoord = limitMin;
  816. } else if (crossAxisCoord > limitMax) {
  817. crossAxisCoord = limitMax;
  818. }
  819. }
  820. return {
  821. [mainAxis]: mainAxisCoord,
  822. [crossAxis]: crossAxisCoord
  823. };
  824. }
  825. };
  826. };
  827. /**
  828. * Provides data to change the size of the floating element. For instance,
  829. * prevent it from overflowing its clipping boundary or match the width of the
  830. * reference element.
  831. * @see https://floating-ui.com/docs/size
  832. */
  833. const size = function (options) {
  834. if (options === void 0) {
  835. options = {};
  836. }
  837. return {
  838. name: 'size',
  839. options,
  840. async fn(middlewareArguments) {
  841. const {
  842. placement,
  843. rects,
  844. platform,
  845. elements
  846. } = middlewareArguments;
  847. const {
  848. apply = () => {},
  849. ...detectOverflowOptions
  850. } = options;
  851. const overflow = await detectOverflow(middlewareArguments, detectOverflowOptions);
  852. const side = getSide(placement);
  853. const alignment = getAlignment(placement);
  854. let heightSide;
  855. let widthSide;
  856. if (side === 'top' || side === 'bottom') {
  857. heightSide = side;
  858. widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';
  859. } else {
  860. widthSide = side;
  861. heightSide = alignment === 'end' ? 'top' : 'bottom';
  862. }
  863. const xMin = max(overflow.left, 0);
  864. const xMax = max(overflow.right, 0);
  865. const yMin = max(overflow.top, 0);
  866. const yMax = max(overflow.bottom, 0);
  867. const dimensions = {
  868. availableHeight: rects.floating.height - (['left', 'right'].includes(placement) ? 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom)) : overflow[heightSide]),
  869. availableWidth: rects.floating.width - (['top', 'bottom'].includes(placement) ? 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right)) : overflow[widthSide])
  870. };
  871. await apply({ ...middlewareArguments,
  872. ...dimensions
  873. });
  874. const nextDimensions = await platform.getDimensions(elements.floating);
  875. if (rects.floating.width !== nextDimensions.width || rects.floating.height !== nextDimensions.height) {
  876. return {
  877. reset: {
  878. rects: true
  879. }
  880. };
  881. }
  882. return {};
  883. }
  884. };
  885. };
  886. /**
  887. * Provides improved positioning for inline reference elements that can span
  888. * over multiple lines, such as hyperlinks or range selections.
  889. * @see https://floating-ui.com/docs/inline
  890. */
  891. const inline = function (options) {
  892. if (options === void 0) {
  893. options = {};
  894. }
  895. return {
  896. name: 'inline',
  897. options,
  898. async fn(middlewareArguments) {
  899. var _await$platform$getCl;
  900. const {
  901. placement,
  902. elements,
  903. rects,
  904. platform,
  905. strategy
  906. } = middlewareArguments; // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a
  907. // ClientRect's bounds, despite the event listener being triggered. A
  908. // padding of 2 seems to handle this issue.
  909. const {
  910. padding = 2,
  911. x,
  912. y
  913. } = options;
  914. const fallback = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
  915. rect: rects.reference,
  916. offsetParent: await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating)),
  917. strategy
  918. }) : rects.reference);
  919. const clientRects = (_await$platform$getCl = await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) != null ? _await$platform$getCl : [];
  920. const paddingObject = getSideObjectFromPadding(padding);
  921. function getBoundingClientRect() {
  922. // There are two rects and they are disjoined
  923. if (clientRects.length === 2 && clientRects[0].left > clientRects[1].right && x != null && y != null) {
  924. var _clientRects$find;
  925. // Find the first rect in which the point is fully inside
  926. return (_clientRects$find = clientRects.find(rect => x > rect.left - paddingObject.left && x < rect.right + paddingObject.right && y > rect.top - paddingObject.top && y < rect.bottom + paddingObject.bottom)) != null ? _clientRects$find : fallback;
  927. } // There are 2 or more connected rects
  928. if (clientRects.length >= 2) {
  929. if (getMainAxisFromPlacement(placement) === 'x') {
  930. const firstRect = clientRects[0];
  931. const lastRect = clientRects[clientRects.length - 1];
  932. const isTop = getSide(placement) === 'top';
  933. const top = firstRect.top;
  934. const bottom = lastRect.bottom;
  935. const left = isTop ? firstRect.left : lastRect.left;
  936. const right = isTop ? firstRect.right : lastRect.right;
  937. const width = right - left;
  938. const height = bottom - top;
  939. return {
  940. top,
  941. bottom,
  942. left,
  943. right,
  944. width,
  945. height,
  946. x: left,
  947. y: top
  948. };
  949. }
  950. const isLeftSide = getSide(placement) === 'left';
  951. const maxRight = max(...clientRects.map(rect => rect.right));
  952. const minLeft = min(...clientRects.map(rect => rect.left));
  953. const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);
  954. const top = measureRects[0].top;
  955. const bottom = measureRects[measureRects.length - 1].bottom;
  956. const left = minLeft;
  957. const right = maxRight;
  958. const width = right - left;
  959. const height = bottom - top;
  960. return {
  961. top,
  962. bottom,
  963. left,
  964. right,
  965. width,
  966. height,
  967. x: left,
  968. y: top
  969. };
  970. }
  971. return fallback;
  972. }
  973. const resetRects = await platform.getElementRects({
  974. reference: {
  975. getBoundingClientRect
  976. },
  977. floating: elements.floating,
  978. strategy
  979. });
  980. 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) {
  981. return {
  982. reset: {
  983. rects: resetRects
  984. }
  985. };
  986. }
  987. return {};
  988. }
  989. };
  990. };
  991. export { arrow, autoPlacement, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, rectToClientRect, shift, size };