UNPKG

react-elegant-ui

Version:

Elegant UI components, made by BEM best practices for react

203 lines 6.56 kB
var __assign = this && this.__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 = this && this.__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 = this && this.__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)); }; import { useMemo, useRef, useState } from 'react'; import { isEqual } from '../../../lib/isEqual'; import { useIsomorphicLayoutEffect as useLayoutEffect } from '../../useIsomorphicLayoutEffect'; import { useForceUpdate } from '../../useForceUpdate'; import { createPopper } from './createPopper'; import { getElementsFromRefs } from './utils'; /** * Use popperjs for positioning popup elements */ export 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(useState({}), 2), state = _g[0], setState = _g[1]; // Convert to array var placements = 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 = useRef(null); var popperOptions = 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 = 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 (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 = useForceUpdate(); var popperRef = useRef(null); var anchorNode = anchorRef.current; var popupNode = popupRef.current; // Create popper useLayoutEffect(function () { forceUpdate(); if (anchorNode === null || popupNode === null || !enabled) { return; } var popperConstructor = customPopperConstructor || 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 useLayoutEffect(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` useLayoutEffect(function () { if (popperRef.current !== null) { popperRef.current.forceUpdate(); } }, [children]); return { popper: popperRef.current, attributes: state.attributes, styles: state.styles }; }