UNPKG

5.97 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4import useSafeState from '@restart/hooks/useSafeState';
5import { createPopper } from './popper';
6var initialPopperStyles = {
7 position: 'absolute',
8 top: '0',
9 left: '0',
10 opacity: '0',
11 pointerEvents: 'none'
12};
13var initialArrowStyles = {}; // until docjs supports type exports...
14
15export function toModifierMap(modifiers) {
16 var result = {};
17
18 if (!Array.isArray(modifiers)) {
19 return modifiers || result;
20 } // eslint-disable-next-line no-unused-expressions
21
22
23 modifiers == null ? void 0 : modifiers.forEach(function (m) {
24 result[m.name] = m;
25 });
26 return result;
27}
28export function toModifierArray(map) {
29 if (map === void 0) {
30 map = {};
31 }
32
33 if (Array.isArray(map)) return map;
34 return Object.keys(map).map(function (k) {
35 map[k].name = k;
36 return map[k];
37 });
38}
39
40/**
41 * Position an element relative some reference element using Popper.js
42 *
43 * @param referenceElement
44 * @param popperElement
45 * @param {object} options
46 * @param {object=} options.modifiers Popper.js modifiers
47 * @param {boolean=} options.enabled toggle the popper functionality on/off
48 * @param {string=} options.placement The popper element placement relative to the reference element
49 * @param {string=} options.strategy the positioning strategy
50 * @param {boolean=} options.eventsEnabled have Popper listen on window resize events to reposition the element
51 * @param {function=} options.onCreate called when the popper is created
52 * @param {function=} options.onUpdate called when the popper is updated
53 *
54 * @returns {UsePopperState} The popper state
55 */
56function usePopper(referenceElement, popperElement, _temp) {
57 var _ref = _temp === void 0 ? {} : _temp,
58 _ref$enabled = _ref.enabled,
59 enabled = _ref$enabled === void 0 ? true : _ref$enabled,
60 _ref$placement = _ref.placement,
61 placement = _ref$placement === void 0 ? 'bottom' : _ref$placement,
62 _ref$strategy = _ref.strategy,
63 strategy = _ref$strategy === void 0 ? 'absolute' : _ref$strategy,
64 _ref$eventsEnabled = _ref.eventsEnabled,
65 eventsEnabled = _ref$eventsEnabled === void 0 ? true : _ref$eventsEnabled,
66 userModifiers = _ref.modifiers,
67 popperOptions = _objectWithoutPropertiesLoose(_ref, ["enabled", "placement", "strategy", "eventsEnabled", "modifiers"]);
68
69 var popperInstanceRef = useRef();
70 var scheduleUpdate = useCallback(function () {
71 if (popperInstanceRef.current) {
72 popperInstanceRef.current.update();
73 }
74 }, []);
75
76 var _useSafeState = useSafeState(useState({
77 placement: placement,
78 scheduleUpdate: scheduleUpdate,
79 outOfBoundaries: false,
80 styles: initialPopperStyles,
81 arrowStyles: initialArrowStyles
82 })),
83 state = _useSafeState[0],
84 setState = _useSafeState[1];
85
86 var updateModifier = useMemo(function () {
87 return {
88 name: 'updateStateModifier',
89 enabled: true,
90 phase: 'afterWrite',
91 requires: ['computeStyles'],
92 fn: function fn(data) {
93 var _data$state$modifiers, _data$state$styles, _data$state$styles2;
94
95 setState({
96 scheduleUpdate: scheduleUpdate,
97 outOfBoundaries: !!((_data$state$modifiers = data.state.modifiersData.hide) == null ? void 0 : _data$state$modifiers.isReferenceHidden),
98 placement: data.state.placement,
99 styles: _extends({}, (_data$state$styles = data.state.styles) == null ? void 0 : _data$state$styles.popper),
100 arrowStyles: _extends({}, (_data$state$styles2 = data.state.styles) == null ? void 0 : _data$state$styles2.arrow),
101 state: data.state
102 });
103 }
104 };
105 }, [scheduleUpdate, setState]);
106 var modifiers = toModifierArray(userModifiers);
107 var eventsModifier = modifiers.find(function (m) {
108 return m.name === 'eventListeners';
109 });
110
111 if (!eventsModifier && eventsEnabled) {
112 eventsModifier = {
113 name: 'eventListeners',
114 enabled: true
115 };
116 modifiers = [].concat(modifiers, [eventsModifier]);
117 } // A placement difference in state means popper determined a new placement
118 // apart from the props value. By the time the popper element is rendered with
119 // the new position Popper has already measured it, if the place change triggers
120 // a size change it will result in a misaligned popper. So we schedule an update to be sure.
121
122
123 useEffect(function () {
124 scheduleUpdate();
125 }, [state.placement, scheduleUpdate]);
126 useEffect(function () {
127 if (!popperInstanceRef.current || !enabled) return;
128 popperInstanceRef.current.setOptions({
129 placement: placement,
130 strategy: strategy,
131 modifiers: [].concat(modifiers, [updateModifier])
132 }); // intentionally NOT re-running on new modifiers
133 // eslint-disable-next-line react-hooks/exhaustive-deps
134 }, [strategy, placement, eventsModifier.enabled, updateModifier, enabled]);
135 useEffect(function () {
136 if (!enabled || referenceElement == null || popperElement == null) {
137 return undefined;
138 }
139
140 popperInstanceRef.current = createPopper(referenceElement, popperElement, _extends({}, popperOptions, {
141 placement: placement,
142 strategy: strategy,
143 modifiers: [].concat(modifiers, [updateModifier])
144 }));
145 return function () {
146 if (popperInstanceRef.current != null) {
147 popperInstanceRef.current.destroy();
148 popperInstanceRef.current = undefined;
149 setState(function (s) {
150 return _extends({}, s, {
151 styles: initialPopperStyles,
152 arrowStyles: initialArrowStyles
153 });
154 });
155 }
156 }; // This is only run once to _create_ the popper
157 // eslint-disable-next-line react-hooks/exhaustive-deps
158 }, [enabled, referenceElement, popperElement]);
159 return state;
160}
161
162export default usePopper;
\No newline at end of file