You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
7.2 KiB

2 months ago
  1. import getCompositeRect from "./dom-utils/getCompositeRect.js";
  2. import getLayoutRect from "./dom-utils/getLayoutRect.js";
  3. import listScrollParents from "./dom-utils/listScrollParents.js";
  4. import getOffsetParent from "./dom-utils/getOffsetParent.js";
  5. import orderModifiers from "./utils/orderModifiers.js";
  6. import debounce from "./utils/debounce.js";
  7. import mergeByName from "./utils/mergeByName.js";
  8. import detectOverflow from "./utils/detectOverflow.js";
  9. import { isElement } from "./dom-utils/instanceOf.js";
  10. var DEFAULT_OPTIONS = {
  11. placement: 'bottom',
  12. modifiers: [],
  13. strategy: 'absolute'
  14. };
  15. function areValidElements() {
  16. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  17. args[_key] = arguments[_key];
  18. }
  19. return !args.some(function (element) {
  20. return !(element && typeof element.getBoundingClientRect === 'function');
  21. });
  22. }
  23. export function popperGenerator(generatorOptions) {
  24. if (generatorOptions === void 0) {
  25. generatorOptions = {};
  26. }
  27. var _generatorOptions = generatorOptions,
  28. _generatorOptions$def = _generatorOptions.defaultModifiers,
  29. defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,
  30. _generatorOptions$def2 = _generatorOptions.defaultOptions,
  31. defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;
  32. return function createPopper(reference, popper, options) {
  33. if (options === void 0) {
  34. options = defaultOptions;
  35. }
  36. var state = {
  37. placement: 'bottom',
  38. orderedModifiers: [],
  39. options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),
  40. modifiersData: {},
  41. elements: {
  42. reference: reference,
  43. popper: popper
  44. },
  45. attributes: {},
  46. styles: {}
  47. };
  48. var effectCleanupFns = [];
  49. var isDestroyed = false;
  50. var instance = {
  51. state: state,
  52. setOptions: function setOptions(setOptionsAction) {
  53. var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;
  54. cleanupModifierEffects();
  55. state.options = Object.assign({}, defaultOptions, state.options, options);
  56. state.scrollParents = {
  57. reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],
  58. popper: listScrollParents(popper)
  59. }; // Orders the modifiers based on their dependencies and `phase`
  60. // properties
  61. var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers
  62. state.orderedModifiers = orderedModifiers.filter(function (m) {
  63. return m.enabled;
  64. });
  65. runModifierEffects();
  66. return instance.update();
  67. },
  68. // Sync update – it will always be executed, even if not necessary. This
  69. // is useful for low frequency updates where sync behavior simplifies the
  70. // logic.
  71. // For high frequency updates (e.g. `resize` and `scroll` events), always
  72. // prefer the async Popper#update method
  73. forceUpdate: function forceUpdate() {
  74. if (isDestroyed) {
  75. return;
  76. }
  77. var _state$elements = state.elements,
  78. reference = _state$elements.reference,
  79. popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements
  80. // anymore
  81. if (!areValidElements(reference, popper)) {
  82. return;
  83. } // Store the reference and popper rects to be read by modifiers
  84. state.rects = {
  85. reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),
  86. popper: getLayoutRect(popper)
  87. }; // Modifiers have the ability to reset the current update cycle. The
  88. // most common use case for this is the `flip` modifier changing the
  89. // placement, which then needs to re-run all the modifiers, because the
  90. // logic was previously ran for the previous placement and is therefore
  91. // stale/incorrect
  92. state.reset = false;
  93. state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier
  94. // is filled with the initial data specified by the modifier. This means
  95. // it doesn't persist and is fresh on each update.
  96. // To ensure persistent data, use `${name}#persistent`
  97. state.orderedModifiers.forEach(function (modifier) {
  98. return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);
  99. });
  100. for (var index = 0; index < state.orderedModifiers.length; index++) {
  101. if (state.reset === true) {
  102. state.reset = false;
  103. index = -1;
  104. continue;
  105. }
  106. var _state$orderedModifie = state.orderedModifiers[index],
  107. fn = _state$orderedModifie.fn,
  108. _state$orderedModifie2 = _state$orderedModifie.options,
  109. _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,
  110. name = _state$orderedModifie.name;
  111. if (typeof fn === 'function') {
  112. state = fn({
  113. state: state,
  114. options: _options,
  115. name: name,
  116. instance: instance
  117. }) || state;
  118. }
  119. }
  120. },
  121. // Async and optimistically optimized update – it will not be executed if
  122. // not necessary (debounced to run at most once-per-tick)
  123. update: debounce(function () {
  124. return new Promise(function (resolve) {
  125. instance.forceUpdate();
  126. resolve(state);
  127. });
  128. }),
  129. destroy: function destroy() {
  130. cleanupModifierEffects();
  131. isDestroyed = true;
  132. }
  133. };
  134. if (!areValidElements(reference, popper)) {
  135. return instance;
  136. }
  137. instance.setOptions(options).then(function (state) {
  138. if (!isDestroyed && options.onFirstUpdate) {
  139. options.onFirstUpdate(state);
  140. }
  141. }); // Modifiers have the ability to execute arbitrary code before the first
  142. // update cycle runs. They will be executed in the same order as the update
  143. // cycle. This is useful when a modifier adds some persistent data that
  144. // other modifiers need to use, but the modifier is run after the dependent
  145. // one.
  146. function runModifierEffects() {
  147. state.orderedModifiers.forEach(function (_ref) {
  148. var name = _ref.name,
  149. _ref$options = _ref.options,
  150. options = _ref$options === void 0 ? {} : _ref$options,
  151. effect = _ref.effect;
  152. if (typeof effect === 'function') {
  153. var cleanupFn = effect({
  154. state: state,
  155. name: name,
  156. instance: instance,
  157. options: options
  158. });
  159. var noopFn = function noopFn() {};
  160. effectCleanupFns.push(cleanupFn || noopFn);
  161. }
  162. });
  163. }
  164. function cleanupModifierEffects() {
  165. effectCleanupFns.forEach(function (fn) {
  166. return fn();
  167. });
  168. effectCleanupFns = [];
  169. }
  170. return instance;
  171. };
  172. }
  173. export var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules
  174. export { detectOverflow };