react-elegant-ui
Version:
Elegant UI components, made by BEM best practices for react
209 lines (208 loc) • 6.88 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.usePopper = usePopper;
var _react = require("react");
var _isEqual = require("../../../lib/isEqual");
var _useIsomorphicLayoutEffect = require("../../useIsomorphicLayoutEffect");
var _useForceUpdate = require("../../useForceUpdate");
var _createPopper = require("./createPopper");
var _utils = require("./utils");
var __assign = void 0 && (void 0).__assign || function () {
__assign = Object.assign || function (t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __read = void 0 && (void 0).__read || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o),
r,
ar = [],
e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
} catch (error) {
e = {
error: error
};
} finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
} finally {
if (e) throw e.error;
}
}
return ar;
};
var __spreadArray = void 0 && (void 0).__spreadArray || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
/**
* Use popperjs for positioning popup elements
*/
function usePopper(props) {
var _a;
var customPopperConstructor = props.createPopper,
userOptions = props.options,
anchorRef = props.anchorRef,
popupRef = props.popupRef,
arrowRef = props.arrowRef,
_b = props.enabled,
enabled = _b === void 0 ? true : _b,
_c = props.placement,
placement = _c === void 0 ? 'bottom' : _c,
_d = props.arrowMarginThreshold,
arrowMarginThreshold = _d === void 0 ? 4 : _d,
_e = props.marginThreshold,
marginThreshold = _e === void 0 ? 16 : _e,
_f = props.modifiers,
modifiers = _f === void 0 ? [] : _f,
motionless = props.motionless,
offset = props.offset,
UNSAFE_tailOffset = props.UNSAFE_tailOffset,
children = props.children,
boundary = props.boundary;
var _g = __read((0, _react.useState)({}), 2),
state = _g[0],
setState = _g[1];
// Convert to array
var placements = (0, _react.useMemo)(function () {
return Array.isArray(placement) ? placement : [placement];
}, [placement]);
var arrowNode = (_a = arrowRef === null || arrowRef === void 0 ? void 0 : arrowRef.current) !== null && _a !== void 0 ? _a : null;
var prevPopperOptions = (0, _react.useRef)(null);
var popperOptions = (0, _react.useMemo)(function () {
var options;
if (userOptions !== undefined) {
// Set user options
options = __assign(__assign({}, userOptions), {
modifiers: __spreadArray(__spreadArray([], __read(userOptions.modifiers), false), [
// Add hook to update state
{
name: 'updateStyles',
options: {
setState: setState
}
}], false)
});
} else {
var _a = __read(placements),
placement_1 = _a[0],
fallbackPlacements = _a.slice(1);
var popperBoundary = (0, _utils.getElementsFromRefs)(boundary);
// Build options
options = {
// Default placemet, other specified in flip modifier
placement: placement_1,
modifiers: __spreadArray(__spreadArray([{
name: 'eventListeners',
enabled: !motionless
}, {
name: 'extendedEventListeners',
enabled: !motionless
}, {
name: 'autoCorrection'
}, {
name: 'offset',
options: {
offset: offset,
tailOffset: UNSAFE_tailOffset
}
}, {
name: 'computeStyles',
options: {
gpuAcceleration: false
}
}, {
name: 'preventOverflow',
options: {
altBoundary: true,
boundary: popperBoundary
}
}, {
name: 'arrow',
enabled: Boolean(arrowNode),
options: {
element: arrowNode,
padding: arrowMarginThreshold
}
}, {
name: 'flip',
options: {
padding: marginThreshold,
fallbackPlacements: fallbackPlacements,
altBoundary: true,
boundary: popperBoundary
}
}, {
name: 'hide',
options: {
boundary: popperBoundary
}
}], __read(modifiers), false), [{
name: 'updateStyles',
options: {
setState: setState
}
}], false)
};
}
// Return previous object, to prevent re-render when not have difference
if ((0, _isEqual.isEqual)(prevPopperOptions.current, options)) {
return prevPopperOptions.current;
}
prevPopperOptions.current = options;
return options;
}, [setState, userOptions, placements, offset, arrowNode, arrowMarginThreshold, motionless, marginThreshold, UNSAFE_tailOffset, modifiers, boundary]);
var forceUpdate = (0, _useForceUpdate.useForceUpdate)();
var popperRef = (0, _react.useRef)(null);
var anchorNode = anchorRef.current;
var popupNode = popupRef.current;
// Create popper
(0, _useIsomorphicLayoutEffect.useIsomorphicLayoutEffect)(function () {
forceUpdate();
if (anchorNode === null || popupNode === null || !enabled) {
return;
}
var popperConstructor = customPopperConstructor || _createPopper.createPopper;
var popperInstance = popperConstructor(anchorNode, popupNode, popperOptions);
popperRef.current = popperInstance;
return function () {
popperInstance.destroy();
popperRef.current = null;
};
// Separately update a options instead create new instance while update
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [anchorNode, popupNode, enabled, customPopperConstructor]);
// Apply new options
(0, _useIsomorphicLayoutEffect.useIsomorphicLayoutEffect)(function () {
if (popperRef.current !== null) {
popperRef.current.setOptions(popperOptions);
}
}, [popperOptions]);
// Update when content changes
// NOTE: maybe we should use `useEffect` instead isomorphic, and use `update` method instead `forceUpdate`
(0, _useIsomorphicLayoutEffect.useIsomorphicLayoutEffect)(function () {
if (popperRef.current !== null) {
popperRef.current.forceUpdate();
}
}, [children]);
return {
popper: popperRef.current,
attributes: state.attributes,
styles: state.styles
};
}