floating-ui.core.umd.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FloatingUICore = {}));
  5. })(this, (function (exports) { 'use strict';
  6. /**
  7. * Custom positioning reference element.
  8. * @see https://floating-ui.com/docs/virtual-elements
  9. */
  10. const sides = ['top', 'right', 'bottom', 'left'];
  11. const alignments = ['start', 'end'];
  12. const placements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + "-" + alignments[0], side + "-" + alignments[1]), []);
  13. const min = Math.min;
  14. const max = Math.max;
  15. const oppositeSideMap = {
  16. left: 'right',
  17. right: 'left',
  18. bottom: 'top',
  19. top: 'bottom'
  20. };
  21. function clamp(start, value, end) {
  22. return max(start, min(value, end));
  23. }
  24. function evaluate(value, param) {
  25. return typeof value === 'function' ? value(param) : value;
  26. }
  27. function getSide(placement) {
  28. return placement.split('-')[0];
  29. }
  30. function getAlignment(placement) {
  31. return placement.split('-')[1];
  32. }
  33. function getOppositeAxis(axis) {
  34. return axis === 'x' ? 'y' : 'x';
  35. }
  36. function getAxisLength(axis) {
  37. return axis === 'y' ? 'height' : 'width';
  38. }
  39. function getSideAxis(placement) {
  40. const firstChar = placement[0];
  41. return firstChar === 't' || firstChar === 'b' ? 'y' : 'x';
  42. }
  43. function getAlignmentAxis(placement) {
  44. return getOppositeAxis(getSideAxis(placement));
  45. }
  46. function getAlignmentSides(placement, rects, rtl) {
  47. if (rtl === void 0) {
  48. rtl = false;
  49. }
  50. const alignment = getAlignment(placement);
  51. const alignmentAxis = getAlignmentAxis(placement);
  52. const length = getAxisLength(alignmentAxis);
  53. let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';
  54. if (rects.reference[length] > rects.floating[length]) {
  55. mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
  56. }
  57. return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
  58. }
  59. function getExpandedPlacements(placement) {
  60. const oppositePlacement = getOppositePlacement(placement);
  61. return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
  62. }
  63. function getOppositeAlignmentPlacement(placement) {
  64. return placement.includes('start') ? placement.replace('start', 'end') : placement.replace('end', 'start');
  65. }
  66. const lrPlacement = ['left', 'right'];
  67. const rlPlacement = ['right', 'left'];
  68. const tbPlacement = ['top', 'bottom'];
  69. const btPlacement = ['bottom', 'top'];
  70. function getSideList(side, isStart, rtl) {
  71. switch (side) {
  72. case 'top':
  73. case 'bottom':
  74. if (rtl) return isStart ? rlPlacement : lrPlacement;
  75. return isStart ? lrPlacement : rlPlacement;
  76. case 'left':
  77. case 'right':
  78. return isStart ? tbPlacement : btPlacement;
  79. default:
  80. return [];
  81. }
  82. }
  83. function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
  84. const alignment = getAlignment(placement);
  85. let list = getSideList(getSide(placement), direction === 'start', rtl);
  86. if (alignment) {
  87. list = list.map(side => side + "-" + alignment);
  88. if (flipAlignment) {
  89. list = list.concat(list.map(getOppositeAlignmentPlacement));
  90. }
  91. }
  92. return list;
  93. }
  94. function getOppositePlacement(placement) {
  95. const side = getSide(placement);
  96. return oppositeSideMap[side] + placement.slice(side.length);
  97. }
  98. function expandPaddingObject(padding) {
  99. return {
  100. top: 0,
  101. right: 0,
  102. bottom: 0,
  103. left: 0,
  104. ...padding
  105. };
  106. }
  107. function getPaddingObject(padding) {
  108. return typeof padding !== 'number' ? expandPaddingObject(padding) : {
  109. top: padding,
  110. right: padding,
  111. bottom: padding,
  112. left: padding
  113. };
  114. }
  115. function rectToClientRect(rect) {
  116. const {
  117. x,
  118. y,
  119. width,
  120. height
  121. } = rect;
  122. return {
  123. width,
  124. height,
  125. top: y,
  126. left: x,
  127. right: x + width,
  128. bottom: y + height,
  129. x,
  130. y
  131. };
  132. }
  133. function computeCoordsFromPlacement(_ref, placement, rtl) {
  134. let {
  135. reference,
  136. floating
  137. } = _ref;
  138. const sideAxis = getSideAxis(placement);
  139. const alignmentAxis = getAlignmentAxis(placement);
  140. const alignLength = getAxisLength(alignmentAxis);
  141. const side = getSide(placement);
  142. const isVertical = sideAxis === 'y';
  143. const commonX = reference.x + reference.width / 2 - floating.width / 2;
  144. const commonY = reference.y + reference.height / 2 - floating.height / 2;
  145. const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;
  146. let coords;
  147. switch (side) {
  148. case 'top':
  149. coords = {
  150. x: commonX,
  151. y: reference.y - floating.height
  152. };
  153. break;
  154. case 'bottom':
  155. coords = {
  156. x: commonX,
  157. y: reference.y + reference.height
  158. };
  159. break;
  160. case 'right':
  161. coords = {
  162. x: reference.x + reference.width,
  163. y: commonY
  164. };
  165. break;
  166. case 'left':
  167. coords = {
  168. x: reference.x - floating.width,
  169. y: commonY
  170. };
  171. break;
  172. default:
  173. coords = {
  174. x: reference.x,
  175. y: reference.y
  176. };
  177. }
  178. switch (getAlignment(placement)) {
  179. case 'start':
  180. coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
  181. break;
  182. case 'end':
  183. coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
  184. break;
  185. }
  186. return coords;
  187. }
  188. /**
  189. * Resolves with an object of overflow side offsets that determine how much the
  190. * element is overflowing a given clipping boundary on each side.
  191. * - positive = overflowing the boundary by that number of pixels
  192. * - negative = how many pixels left before it will overflow
  193. * - 0 = lies flush with the boundary
  194. * @see https://floating-ui.com/docs/detectOverflow
  195. */
  196. async function detectOverflow(state, options) {
  197. var _await$platform$isEle;
  198. if (options === void 0) {
  199. options = {};
  200. }
  201. const {
  202. x,
  203. y,
  204. platform,
  205. rects,
  206. elements,
  207. strategy
  208. } = state;
  209. const {
  210. boundary = 'clippingAncestors',
  211. rootBoundary = 'viewport',
  212. elementContext = 'floating',
  213. altBoundary = false,
  214. padding = 0
  215. } = evaluate(options, state);
  216. const paddingObject = getPaddingObject(padding);
  217. const altContext = elementContext === 'floating' ? 'reference' : 'floating';
  218. const element = elements[altBoundary ? altContext : elementContext];
  219. const clippingClientRect = rectToClientRect(await platform.getClippingRect({
  220. 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))),
  221. boundary,
  222. rootBoundary,
  223. strategy
  224. }));
  225. const rect = elementContext === 'floating' ? {
  226. x,
  227. y,
  228. width: rects.floating.width,
  229. height: rects.floating.height
  230. } : rects.reference;
  231. const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));
  232. const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) ? (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {
  233. x: 1,
  234. y: 1
  235. } : {
  236. x: 1,
  237. y: 1
  238. };
  239. const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
  240. elements,
  241. rect,
  242. offsetParent,
  243. strategy
  244. }) : rect);
  245. return {
  246. top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
  247. bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
  248. left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
  249. right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x
  250. };
  251. }
  252. // Maximum number of resets that can occur before bailing to avoid infinite reset loops.
  253. const MAX_RESET_COUNT = 50;
  254. /**
  255. * Computes the `x` and `y` coordinates that will place the floating element
  256. * next to a given reference element.
  257. *
  258. * This export does not have any `platform` interface logic. You will need to
  259. * write one for the platform you are using Floating UI with.
  260. */
  261. const computePosition = async (reference, floating, config) => {
  262. const {
  263. placement = 'bottom',
  264. strategy = 'absolute',
  265. middleware = [],
  266. platform
  267. } = config;
  268. const platformWithDetectOverflow = platform.detectOverflow ? platform : {
  269. ...platform,
  270. detectOverflow
  271. };
  272. const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));
  273. let rects = await platform.getElementRects({
  274. reference,
  275. floating,
  276. strategy
  277. });
  278. let {
  279. x,
  280. y
  281. } = computeCoordsFromPlacement(rects, placement, rtl);
  282. let statefulPlacement = placement;
  283. let resetCount = 0;
  284. const middlewareData = {};
  285. for (let i = 0; i < middleware.length; i++) {
  286. const currentMiddleware = middleware[i];
  287. if (!currentMiddleware) {
  288. continue;
  289. }
  290. const {
  291. name,
  292. fn
  293. } = currentMiddleware;
  294. const {
  295. x: nextX,
  296. y: nextY,
  297. data,
  298. reset
  299. } = await fn({
  300. x,
  301. y,
  302. initialPlacement: placement,
  303. placement: statefulPlacement,
  304. strategy,
  305. middlewareData,
  306. rects,
  307. platform: platformWithDetectOverflow,
  308. elements: {
  309. reference,
  310. floating
  311. }
  312. });
  313. x = nextX != null ? nextX : x;
  314. y = nextY != null ? nextY : y;
  315. middlewareData[name] = {
  316. ...middlewareData[name],
  317. ...data
  318. };
  319. if (reset && resetCount < MAX_RESET_COUNT) {
  320. resetCount++;
  321. if (typeof reset === 'object') {
  322. if (reset.placement) {
  323. statefulPlacement = reset.placement;
  324. }
  325. if (reset.rects) {
  326. rects = reset.rects === true ? await platform.getElementRects({
  327. reference,
  328. floating,
  329. strategy
  330. }) : reset.rects;
  331. }
  332. ({
  333. x,
  334. y
  335. } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
  336. }
  337. i = -1;
  338. }
  339. }
  340. return {
  341. x,
  342. y,
  343. placement: statefulPlacement,
  344. strategy,
  345. middlewareData
  346. };
  347. };
  348. /**
  349. * Provides data to position an inner element of the floating element so that it
  350. * appears centered to the reference element.
  351. * @see https://floating-ui.com/docs/arrow
  352. */
  353. const arrow = options => ({
  354. name: 'arrow',
  355. options,
  356. async fn(state) {
  357. const {
  358. x,
  359. y,
  360. placement,
  361. rects,
  362. platform,
  363. elements,
  364. middlewareData
  365. } = state;
  366. // Since `element` is required, we don't Partial<> the type.
  367. const {
  368. element,
  369. padding = 0
  370. } = evaluate(options, state) || {};
  371. if (element == null) {
  372. return {};
  373. }
  374. const paddingObject = getPaddingObject(padding);
  375. const coords = {
  376. x,
  377. y
  378. };
  379. const axis = getAlignmentAxis(placement);
  380. const length = getAxisLength(axis);
  381. const arrowDimensions = await platform.getDimensions(element);
  382. const isYAxis = axis === 'y';
  383. const minProp = isYAxis ? 'top' : 'left';
  384. const maxProp = isYAxis ? 'bottom' : 'right';
  385. const clientProp = isYAxis ? 'clientHeight' : 'clientWidth';
  386. const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];
  387. const startDiff = coords[axis] - rects.reference[axis];
  388. const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));
  389. let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0;
  390. // DOM platform can return `window` as the `offsetParent`.
  391. if (!clientSize || !(await (platform.isElement == null ? void 0 : platform.isElement(arrowOffsetParent)))) {
  392. clientSize = elements.floating[clientProp] || rects.floating[length];
  393. }
  394. const centerToReference = endDiff / 2 - startDiff / 2;
  395. // If the padding is large enough that it causes the arrow to no longer be
  396. // centered, modify the padding so that it is centered.
  397. const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;
  398. const minPadding = min(paddingObject[minProp], largestPossiblePadding);
  399. const maxPadding = min(paddingObject[maxProp], largestPossiblePadding);
  400. // Make sure the arrow doesn't overflow the floating element if the center
  401. // point is outside the floating element's bounds.
  402. const min$1 = minPadding;
  403. const max = clientSize - arrowDimensions[length] - maxPadding;
  404. const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
  405. const offset = clamp(min$1, center, max);
  406. // If the reference is small enough that the arrow's padding causes it to
  407. // to point to nothing for an aligned placement, adjust the offset of the
  408. // floating element itself. To ensure `shift()` continues to take action,
  409. // a single reset is performed when this is true.
  410. const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
  411. const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max : 0;
  412. return {
  413. [axis]: coords[axis] + alignmentOffset,
  414. data: {
  415. [axis]: offset,
  416. centerOffset: center - offset - alignmentOffset,
  417. ...(shouldAddOffset && {
  418. alignmentOffset
  419. })
  420. },
  421. reset: shouldAddOffset
  422. };
  423. }
  424. });
  425. function getPlacementList(alignment, autoAlignment, allowedPlacements) {
  426. const allowedPlacementsSortedByAlignment = alignment ? [...allowedPlacements.filter(placement => getAlignment(placement) === alignment), ...allowedPlacements.filter(placement => getAlignment(placement) !== alignment)] : allowedPlacements.filter(placement => getSide(placement) === placement);
  427. return allowedPlacementsSortedByAlignment.filter(placement => {
  428. if (alignment) {
  429. return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);
  430. }
  431. return true;
  432. });
  433. }
  434. /**
  435. * Optimizes the visibility of the floating element by choosing the placement
  436. * that has the most space available automatically, without needing to specify a
  437. * preferred placement. Alternative to `flip`.
  438. * @see https://floating-ui.com/docs/autoPlacement
  439. */
  440. const autoPlacement = function (options) {
  441. if (options === void 0) {
  442. options = {};
  443. }
  444. return {
  445. name: 'autoPlacement',
  446. options,
  447. async fn(state) {
  448. var _middlewareData$autoP, _middlewareData$autoP2, _placementsThatFitOnE;
  449. const {
  450. rects,
  451. middlewareData,
  452. placement,
  453. platform,
  454. elements
  455. } = state;
  456. const {
  457. crossAxis = false,
  458. alignment,
  459. allowedPlacements = placements,
  460. autoAlignment = true,
  461. ...detectOverflowOptions
  462. } = evaluate(options, state);
  463. const placements$1 = alignment !== undefined || allowedPlacements === placements ? getPlacementList(alignment || null, autoAlignment, allowedPlacements) : allowedPlacements;
  464. const overflow = await platform.detectOverflow(state, detectOverflowOptions);
  465. const currentIndex = ((_middlewareData$autoP = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP.index) || 0;
  466. const currentPlacement = placements$1[currentIndex];
  467. if (currentPlacement == null) {
  468. return {};
  469. }
  470. const alignmentSides = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));
  471. // Make `computeCoords` start from the right place.
  472. if (placement !== currentPlacement) {
  473. return {
  474. reset: {
  475. placement: placements$1[0]
  476. }
  477. };
  478. }
  479. const currentOverflows = [overflow[getSide(currentPlacement)], overflow[alignmentSides[0]], overflow[alignmentSides[1]]];
  480. const allOverflows = [...(((_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.overflows) || []), {
  481. placement: currentPlacement,
  482. overflows: currentOverflows
  483. }];
  484. const nextPlacement = placements$1[currentIndex + 1];
  485. // There are more placements to check.
  486. if (nextPlacement) {
  487. return {
  488. data: {
  489. index: currentIndex + 1,
  490. overflows: allOverflows
  491. },
  492. reset: {
  493. placement: nextPlacement
  494. }
  495. };
  496. }
  497. const placementsSortedByMostSpace = allOverflows.map(d => {
  498. const alignment = getAlignment(d.placement);
  499. return [d.placement, alignment && crossAxis ?
  500. // Check along the mainAxis and main crossAxis side.
  501. d.overflows.slice(0, 2).reduce((acc, v) => acc + v, 0) :
  502. // Check only the mainAxis.
  503. d.overflows[0], d.overflows];
  504. }).sort((a, b) => a[1] - b[1]);
  505. const placementsThatFitOnEachSide = placementsSortedByMostSpace.filter(d => d[2].slice(0,
  506. // Aligned placements should not check their opposite crossAxis
  507. // side.
  508. getAlignment(d[0]) ? 2 : 3).every(v => v <= 0));
  509. const resetPlacement = ((_placementsThatFitOnE = placementsThatFitOnEachSide[0]) == null ? void 0 : _placementsThatFitOnE[0]) || placementsSortedByMostSpace[0][0];
  510. if (resetPlacement !== placement) {
  511. return {
  512. data: {
  513. index: currentIndex + 1,
  514. overflows: allOverflows
  515. },
  516. reset: {
  517. placement: resetPlacement
  518. }
  519. };
  520. }
  521. return {};
  522. }
  523. };
  524. };
  525. /**
  526. * Optimizes the visibility of the floating element by flipping the `placement`
  527. * in order to keep it in view when the preferred placement(s) will overflow the
  528. * clipping boundary. Alternative to `autoPlacement`.
  529. * @see https://floating-ui.com/docs/flip
  530. */
  531. const flip = function (options) {
  532. if (options === void 0) {
  533. options = {};
  534. }
  535. return {
  536. name: 'flip',
  537. options,
  538. async fn(state) {
  539. var _middlewareData$arrow, _middlewareData$flip;
  540. const {
  541. placement,
  542. middlewareData,
  543. rects,
  544. initialPlacement,
  545. platform,
  546. elements
  547. } = state;
  548. const {
  549. mainAxis: checkMainAxis = true,
  550. crossAxis: checkCrossAxis = true,
  551. fallbackPlacements: specifiedFallbackPlacements,
  552. fallbackStrategy = 'bestFit',
  553. fallbackAxisSideDirection = 'none',
  554. flipAlignment = true,
  555. ...detectOverflowOptions
  556. } = evaluate(options, state);
  557. // If a reset by the arrow was caused due to an alignment offset being
  558. // added, we should skip any logic now since `flip()` has already done its
  559. // work.
  560. // https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643
  561. if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
  562. return {};
  563. }
  564. const side = getSide(placement);
  565. const initialSideAxis = getSideAxis(initialPlacement);
  566. const isBasePlacement = getSide(initialPlacement) === initialPlacement;
  567. const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
  568. const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
  569. const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== 'none';
  570. if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {
  571. fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));
  572. }
  573. const placements = [initialPlacement, ...fallbackPlacements];
  574. const overflow = await platform.detectOverflow(state, detectOverflowOptions);
  575. const overflows = [];
  576. let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
  577. if (checkMainAxis) {
  578. overflows.push(overflow[side]);
  579. }
  580. if (checkCrossAxis) {
  581. const sides = getAlignmentSides(placement, rects, rtl);
  582. overflows.push(overflow[sides[0]], overflow[sides[1]]);
  583. }
  584. overflowsData = [...overflowsData, {
  585. placement,
  586. overflows
  587. }];
  588. // One or more sides is overflowing.
  589. if (!overflows.every(side => side <= 0)) {
  590. var _middlewareData$flip2, _overflowsData$filter;
  591. const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;
  592. const nextPlacement = placements[nextIndex];
  593. if (nextPlacement) {
  594. const ignoreCrossAxisOverflow = checkCrossAxis === 'alignment' ? initialSideAxis !== getSideAxis(nextPlacement) : false;
  595. if (!ignoreCrossAxisOverflow ||
  596. // We leave the current main axis only if every placement on that axis
  597. // overflows the main axis.
  598. overflowsData.every(d => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) {
  599. // Try next placement and re-run the lifecycle.
  600. return {
  601. data: {
  602. index: nextIndex,
  603. overflows: overflowsData
  604. },
  605. reset: {
  606. placement: nextPlacement
  607. }
  608. };
  609. }
  610. }
  611. // First, find the candidates that fit on the mainAxis side of overflow,
  612. // then find the placement that fits the best on the main crossAxis side.
  613. 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;
  614. // Otherwise fallback.
  615. if (!resetPlacement) {
  616. switch (fallbackStrategy) {
  617. case 'bestFit':
  618. {
  619. var _overflowsData$filter2;
  620. const placement = (_overflowsData$filter2 = overflowsData.filter(d => {
  621. if (hasFallbackAxisSideDirection) {
  622. const currentSideAxis = getSideAxis(d.placement);
  623. return currentSideAxis === initialSideAxis ||
  624. // Create a bias to the `y` side axis due to horizontal
  625. // reading directions favoring greater width.
  626. currentSideAxis === 'y';
  627. }
  628. return true;
  629. }).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$filter2[0];
  630. if (placement) {
  631. resetPlacement = placement;
  632. }
  633. break;
  634. }
  635. case 'initialPlacement':
  636. resetPlacement = initialPlacement;
  637. break;
  638. }
  639. }
  640. if (placement !== resetPlacement) {
  641. return {
  642. reset: {
  643. placement: resetPlacement
  644. }
  645. };
  646. }
  647. }
  648. return {};
  649. }
  650. };
  651. };
  652. function getSideOffsets(overflow, rect) {
  653. return {
  654. top: overflow.top - rect.height,
  655. right: overflow.right - rect.width,
  656. bottom: overflow.bottom - rect.height,
  657. left: overflow.left - rect.width
  658. };
  659. }
  660. function isAnySideFullyClipped(overflow) {
  661. return sides.some(side => overflow[side] >= 0);
  662. }
  663. /**
  664. * Provides data to hide the floating element in applicable situations, such as
  665. * when it is not in the same clipping context as the reference element.
  666. * @see https://floating-ui.com/docs/hide
  667. */
  668. const hide = function (options) {
  669. if (options === void 0) {
  670. options = {};
  671. }
  672. return {
  673. name: 'hide',
  674. options,
  675. async fn(state) {
  676. const {
  677. rects,
  678. platform
  679. } = state;
  680. const {
  681. strategy = 'referenceHidden',
  682. ...detectOverflowOptions
  683. } = evaluate(options, state);
  684. switch (strategy) {
  685. case 'referenceHidden':
  686. {
  687. const overflow = await platform.detectOverflow(state, {
  688. ...detectOverflowOptions,
  689. elementContext: 'reference'
  690. });
  691. const offsets = getSideOffsets(overflow, rects.reference);
  692. return {
  693. data: {
  694. referenceHiddenOffsets: offsets,
  695. referenceHidden: isAnySideFullyClipped(offsets)
  696. }
  697. };
  698. }
  699. case 'escaped':
  700. {
  701. const overflow = await platform.detectOverflow(state, {
  702. ...detectOverflowOptions,
  703. altBoundary: true
  704. });
  705. const offsets = getSideOffsets(overflow, rects.floating);
  706. return {
  707. data: {
  708. escapedOffsets: offsets,
  709. escaped: isAnySideFullyClipped(offsets)
  710. }
  711. };
  712. }
  713. default:
  714. {
  715. return {};
  716. }
  717. }
  718. }
  719. };
  720. };
  721. function getBoundingRect(rects) {
  722. const minX = min(...rects.map(rect => rect.left));
  723. const minY = min(...rects.map(rect => rect.top));
  724. const maxX = max(...rects.map(rect => rect.right));
  725. const maxY = max(...rects.map(rect => rect.bottom));
  726. return {
  727. x: minX,
  728. y: minY,
  729. width: maxX - minX,
  730. height: maxY - minY
  731. };
  732. }
  733. function getRectsByLine(rects) {
  734. const sortedRects = rects.slice().sort((a, b) => a.y - b.y);
  735. const groups = [];
  736. let prevRect = null;
  737. for (let i = 0; i < sortedRects.length; i++) {
  738. const rect = sortedRects[i];
  739. if (!prevRect || rect.y - prevRect.y > prevRect.height / 2) {
  740. groups.push([rect]);
  741. } else {
  742. groups[groups.length - 1].push(rect);
  743. }
  744. prevRect = rect;
  745. }
  746. return groups.map(rect => rectToClientRect(getBoundingRect(rect)));
  747. }
  748. /**
  749. * Provides improved positioning for inline reference elements that can span
  750. * over multiple lines, such as hyperlinks or range selections.
  751. * @see https://floating-ui.com/docs/inline
  752. */
  753. const inline = function (options) {
  754. if (options === void 0) {
  755. options = {};
  756. }
  757. return {
  758. name: 'inline',
  759. options,
  760. async fn(state) {
  761. const {
  762. placement,
  763. elements,
  764. rects,
  765. platform,
  766. strategy
  767. } = state;
  768. // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a
  769. // ClientRect's bounds, despite the event listener being triggered. A
  770. // padding of 2 seems to handle this issue.
  771. const {
  772. padding = 2,
  773. x,
  774. y
  775. } = evaluate(options, state);
  776. const nativeClientRects = Array.from((await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) || []);
  777. const clientRects = getRectsByLine(nativeClientRects);
  778. const fallback = rectToClientRect(getBoundingRect(nativeClientRects));
  779. const paddingObject = getPaddingObject(padding);
  780. function getBoundingClientRect() {
  781. // There are two rects and they are disjoined.
  782. if (clientRects.length === 2 && clientRects[0].left > clientRects[1].right && x != null && y != null) {
  783. // Find the first rect in which the point is fully inside.
  784. 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;
  785. }
  786. // There are 2 or more connected rects.
  787. if (clientRects.length >= 2) {
  788. if (getSideAxis(placement) === 'y') {
  789. const firstRect = clientRects[0];
  790. const lastRect = clientRects[clientRects.length - 1];
  791. const isTop = getSide(placement) === 'top';
  792. const top = firstRect.top;
  793. const bottom = lastRect.bottom;
  794. const left = isTop ? firstRect.left : lastRect.left;
  795. const right = isTop ? firstRect.right : lastRect.right;
  796. const width = right - left;
  797. const height = bottom - top;
  798. return {
  799. top,
  800. bottom,
  801. left,
  802. right,
  803. width,
  804. height,
  805. x: left,
  806. y: top
  807. };
  808. }
  809. const isLeftSide = getSide(placement) === 'left';
  810. const maxRight = max(...clientRects.map(rect => rect.right));
  811. const minLeft = min(...clientRects.map(rect => rect.left));
  812. const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);
  813. const top = measureRects[0].top;
  814. const bottom = measureRects[measureRects.length - 1].bottom;
  815. const left = minLeft;
  816. const right = maxRight;
  817. const width = right - left;
  818. const height = bottom - top;
  819. return {
  820. top,
  821. bottom,
  822. left,
  823. right,
  824. width,
  825. height,
  826. x: left,
  827. y: top
  828. };
  829. }
  830. return fallback;
  831. }
  832. const resetRects = await platform.getElementRects({
  833. reference: {
  834. getBoundingClientRect
  835. },
  836. floating: elements.floating,
  837. strategy
  838. });
  839. 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) {
  840. return {
  841. reset: {
  842. rects: resetRects
  843. }
  844. };
  845. }
  846. return {};
  847. }
  848. };
  849. };
  850. const originSides = /*#__PURE__*/new Set(['left', 'top']);
  851. // For type backwards-compatibility, the `OffsetOptions` type was also
  852. // Derivable.
  853. async function convertValueToCoords(state, options) {
  854. const {
  855. placement,
  856. platform,
  857. elements
  858. } = state;
  859. const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
  860. const side = getSide(placement);
  861. const alignment = getAlignment(placement);
  862. const isVertical = getSideAxis(placement) === 'y';
  863. const mainAxisMulti = originSides.has(side) ? -1 : 1;
  864. const crossAxisMulti = rtl && isVertical ? -1 : 1;
  865. const rawValue = evaluate(options, state);
  866. // eslint-disable-next-line prefer-const
  867. let {
  868. mainAxis,
  869. crossAxis,
  870. alignmentAxis
  871. } = typeof rawValue === 'number' ? {
  872. mainAxis: rawValue,
  873. crossAxis: 0,
  874. alignmentAxis: null
  875. } : {
  876. mainAxis: rawValue.mainAxis || 0,
  877. crossAxis: rawValue.crossAxis || 0,
  878. alignmentAxis: rawValue.alignmentAxis
  879. };
  880. if (alignment && typeof alignmentAxis === 'number') {
  881. crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;
  882. }
  883. return isVertical ? {
  884. x: crossAxis * crossAxisMulti,
  885. y: mainAxis * mainAxisMulti
  886. } : {
  887. x: mainAxis * mainAxisMulti,
  888. y: crossAxis * crossAxisMulti
  889. };
  890. }
  891. /**
  892. * Modifies the placement by translating the floating element along the
  893. * specified axes.
  894. * A number (shorthand for `mainAxis` or distance), or an axes configuration
  895. * object may be passed.
  896. * @see https://floating-ui.com/docs/offset
  897. */
  898. const offset = function (options) {
  899. if (options === void 0) {
  900. options = 0;
  901. }
  902. return {
  903. name: 'offset',
  904. options,
  905. async fn(state) {
  906. var _middlewareData$offse, _middlewareData$arrow;
  907. const {
  908. x,
  909. y,
  910. placement,
  911. middlewareData
  912. } = state;
  913. const diffCoords = await convertValueToCoords(state, options);
  914. // If the placement is the same and the arrow caused an alignment offset
  915. // then we don't need to change the positioning coordinates.
  916. if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
  917. return {};
  918. }
  919. return {
  920. x: x + diffCoords.x,
  921. y: y + diffCoords.y,
  922. data: {
  923. ...diffCoords,
  924. placement
  925. }
  926. };
  927. }
  928. };
  929. };
  930. /**
  931. * Optimizes the visibility of the floating element by shifting it in order to
  932. * keep it in view when it will overflow the clipping boundary.
  933. * @see https://floating-ui.com/docs/shift
  934. */
  935. const shift = function (options) {
  936. if (options === void 0) {
  937. options = {};
  938. }
  939. return {
  940. name: 'shift',
  941. options,
  942. async fn(state) {
  943. const {
  944. x,
  945. y,
  946. placement,
  947. platform
  948. } = state;
  949. const {
  950. mainAxis: checkMainAxis = true,
  951. crossAxis: checkCrossAxis = false,
  952. limiter = {
  953. fn: _ref => {
  954. let {
  955. x,
  956. y
  957. } = _ref;
  958. return {
  959. x,
  960. y
  961. };
  962. }
  963. },
  964. ...detectOverflowOptions
  965. } = evaluate(options, state);
  966. const coords = {
  967. x,
  968. y
  969. };
  970. const overflow = await platform.detectOverflow(state, detectOverflowOptions);
  971. const crossAxis = getSideAxis(getSide(placement));
  972. const mainAxis = getOppositeAxis(crossAxis);
  973. let mainAxisCoord = coords[mainAxis];
  974. let crossAxisCoord = coords[crossAxis];
  975. if (checkMainAxis) {
  976. const minSide = mainAxis === 'y' ? 'top' : 'left';
  977. const maxSide = mainAxis === 'y' ? 'bottom' : 'right';
  978. const min = mainAxisCoord + overflow[minSide];
  979. const max = mainAxisCoord - overflow[maxSide];
  980. mainAxisCoord = clamp(min, mainAxisCoord, max);
  981. }
  982. if (checkCrossAxis) {
  983. const minSide = crossAxis === 'y' ? 'top' : 'left';
  984. const maxSide = crossAxis === 'y' ? 'bottom' : 'right';
  985. const min = crossAxisCoord + overflow[minSide];
  986. const max = crossAxisCoord - overflow[maxSide];
  987. crossAxisCoord = clamp(min, crossAxisCoord, max);
  988. }
  989. const limitedCoords = limiter.fn({
  990. ...state,
  991. [mainAxis]: mainAxisCoord,
  992. [crossAxis]: crossAxisCoord
  993. });
  994. return {
  995. ...limitedCoords,
  996. data: {
  997. x: limitedCoords.x - x,
  998. y: limitedCoords.y - y,
  999. enabled: {
  1000. [mainAxis]: checkMainAxis,
  1001. [crossAxis]: checkCrossAxis
  1002. }
  1003. }
  1004. };
  1005. }
  1006. };
  1007. };
  1008. /**
  1009. * Built-in `limiter` that will stop `shift()` at a certain point.
  1010. */
  1011. const limitShift = function (options) {
  1012. if (options === void 0) {
  1013. options = {};
  1014. }
  1015. return {
  1016. options,
  1017. fn(state) {
  1018. const {
  1019. x,
  1020. y,
  1021. placement,
  1022. rects,
  1023. middlewareData
  1024. } = state;
  1025. const {
  1026. offset = 0,
  1027. mainAxis: checkMainAxis = true,
  1028. crossAxis: checkCrossAxis = true
  1029. } = evaluate(options, state);
  1030. const coords = {
  1031. x,
  1032. y
  1033. };
  1034. const crossAxis = getSideAxis(placement);
  1035. const mainAxis = getOppositeAxis(crossAxis);
  1036. let mainAxisCoord = coords[mainAxis];
  1037. let crossAxisCoord = coords[crossAxis];
  1038. const rawOffset = evaluate(offset, state);
  1039. const computedOffset = typeof rawOffset === 'number' ? {
  1040. mainAxis: rawOffset,
  1041. crossAxis: 0
  1042. } : {
  1043. mainAxis: 0,
  1044. crossAxis: 0,
  1045. ...rawOffset
  1046. };
  1047. if (checkMainAxis) {
  1048. const len = mainAxis === 'y' ? 'height' : 'width';
  1049. const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;
  1050. const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;
  1051. if (mainAxisCoord < limitMin) {
  1052. mainAxisCoord = limitMin;
  1053. } else if (mainAxisCoord > limitMax) {
  1054. mainAxisCoord = limitMax;
  1055. }
  1056. }
  1057. if (checkCrossAxis) {
  1058. var _middlewareData$offse, _middlewareData$offse2;
  1059. const len = mainAxis === 'y' ? 'width' : 'height';
  1060. const isOriginSide = originSides.has(getSide(placement));
  1061. 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);
  1062. 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);
  1063. if (crossAxisCoord < limitMin) {
  1064. crossAxisCoord = limitMin;
  1065. } else if (crossAxisCoord > limitMax) {
  1066. crossAxisCoord = limitMax;
  1067. }
  1068. }
  1069. return {
  1070. [mainAxis]: mainAxisCoord,
  1071. [crossAxis]: crossAxisCoord
  1072. };
  1073. }
  1074. };
  1075. };
  1076. /**
  1077. * Provides data that allows you to change the size of the floating element —
  1078. * for instance, prevent it from overflowing the clipping boundary or match the
  1079. * width of the reference element.
  1080. * @see https://floating-ui.com/docs/size
  1081. */
  1082. const size = function (options) {
  1083. if (options === void 0) {
  1084. options = {};
  1085. }
  1086. return {
  1087. name: 'size',
  1088. options,
  1089. async fn(state) {
  1090. var _state$middlewareData, _state$middlewareData2;
  1091. const {
  1092. placement,
  1093. rects,
  1094. platform,
  1095. elements
  1096. } = state;
  1097. const {
  1098. apply = () => {},
  1099. ...detectOverflowOptions
  1100. } = evaluate(options, state);
  1101. const overflow = await platform.detectOverflow(state, detectOverflowOptions);
  1102. const side = getSide(placement);
  1103. const alignment = getAlignment(placement);
  1104. const isYAxis = getSideAxis(placement) === 'y';
  1105. const {
  1106. width,
  1107. height
  1108. } = rects.floating;
  1109. let heightSide;
  1110. let widthSide;
  1111. if (side === 'top' || side === 'bottom') {
  1112. heightSide = side;
  1113. widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';
  1114. } else {
  1115. widthSide = side;
  1116. heightSide = alignment === 'end' ? 'top' : 'bottom';
  1117. }
  1118. const maximumClippingHeight = height - overflow.top - overflow.bottom;
  1119. const maximumClippingWidth = width - overflow.left - overflow.right;
  1120. const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight);
  1121. const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth);
  1122. const noShift = !state.middlewareData.shift;
  1123. let availableHeight = overflowAvailableHeight;
  1124. let availableWidth = overflowAvailableWidth;
  1125. if ((_state$middlewareData = state.middlewareData.shift) != null && _state$middlewareData.enabled.x) {
  1126. availableWidth = maximumClippingWidth;
  1127. }
  1128. if ((_state$middlewareData2 = state.middlewareData.shift) != null && _state$middlewareData2.enabled.y) {
  1129. availableHeight = maximumClippingHeight;
  1130. }
  1131. if (noShift && !alignment) {
  1132. const xMin = max(overflow.left, 0);
  1133. const xMax = max(overflow.right, 0);
  1134. const yMin = max(overflow.top, 0);
  1135. const yMax = max(overflow.bottom, 0);
  1136. if (isYAxis) {
  1137. availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right));
  1138. } else {
  1139. availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom));
  1140. }
  1141. }
  1142. await apply({
  1143. ...state,
  1144. availableWidth,
  1145. availableHeight
  1146. });
  1147. const nextDimensions = await platform.getDimensions(elements.floating);
  1148. if (width !== nextDimensions.width || height !== nextDimensions.height) {
  1149. return {
  1150. reset: {
  1151. rects: true
  1152. }
  1153. };
  1154. }
  1155. return {};
  1156. }
  1157. };
  1158. };
  1159. exports.arrow = arrow;
  1160. exports.autoPlacement = autoPlacement;
  1161. exports.computePosition = computePosition;
  1162. exports.detectOverflow = detectOverflow;
  1163. exports.flip = flip;
  1164. exports.hide = hide;
  1165. exports.inline = inline;
  1166. exports.limitShift = limitShift;
  1167. exports.offset = offset;
  1168. exports.rectToClientRect = rectToClientRect;
  1169. exports.shift = shift;
  1170. exports.size = size;
  1171. }));