Page MenuHomec4science

usePopper.js
No OneTemporary

File Metadata

Created
Wed, Feb 19, 10:20

usePopper.js

const _excluded = ["enabled", "placement", "strategy", "modifiers"];
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { dequal } from 'dequal';
import useSafeState from '@restart/hooks/useSafeState';
import { createPopper } from './popper';
const disabledApplyStylesModifier = {
name: 'applyStyles',
enabled: false,
phase: 'afterWrite',
fn: () => undefined
}; // until docjs supports type exports...
const ariaDescribedByModifier = {
name: 'ariaDescribedBy',
enabled: true,
phase: 'afterWrite',
effect: ({
state
}) => () => {
const {
reference,
popper
} = state.elements;
if ('removeAttribute' in reference) {
const ids = (reference.getAttribute('aria-describedby') || '').split(',').filter(id => id.trim() !== popper.id);
if (!ids.length) reference.removeAttribute('aria-describedby');else reference.setAttribute('aria-describedby', ids.join(','));
}
},
fn: ({
state
}) => {
var _popper$getAttribute;
const {
popper,
reference
} = state.elements;
const role = (_popper$getAttribute = popper.getAttribute('role')) == null ? void 0 : _popper$getAttribute.toLowerCase();
if (popper.id && role === 'tooltip' && 'setAttribute' in reference) {
const ids = reference.getAttribute('aria-describedby');
if (ids && ids.split(',').indexOf(popper.id) !== -1) {
return;
}
reference.setAttribute('aria-describedby', ids ? `${ids},${popper.id}` : popper.id);
}
}
};
const EMPTY_MODIFIERS = [];
/**
* Position an element relative some reference element using Popper.js
*
* @param referenceElement
* @param popperElement
* @param {object} options
* @param {object=} options.modifiers Popper.js modifiers
* @param {boolean=} options.enabled toggle the popper functionality on/off
* @param {string=} options.placement The popper element placement relative to the reference element
* @param {string=} options.strategy the positioning strategy
* @param {function=} options.onCreate called when the popper is created
* @param {function=} options.onUpdate called when the popper is updated
*
* @returns {UsePopperState} The popper state
*/
function usePopper(referenceElement, popperElement, _ref = {}) {
let {
enabled = true,
placement = 'bottom',
strategy = 'absolute',
modifiers = EMPTY_MODIFIERS
} = _ref,
config = _objectWithoutPropertiesLoose(_ref, _excluded);
const prevModifiers = useRef(modifiers);
const popperInstanceRef = useRef();
const update = useCallback(() => {
var _popperInstanceRef$cu;
(_popperInstanceRef$cu = popperInstanceRef.current) == null ? void 0 : _popperInstanceRef$cu.update();
}, []);
const forceUpdate = useCallback(() => {
var _popperInstanceRef$cu2;
(_popperInstanceRef$cu2 = popperInstanceRef.current) == null ? void 0 : _popperInstanceRef$cu2.forceUpdate();
}, []);
const [popperState, setState] = useSafeState(useState({
placement,
update,
forceUpdate,
attributes: {},
styles: {
popper: {},
arrow: {}
}
}));
const updateModifier = useMemo(() => ({
name: 'updateStateModifier',
enabled: true,
phase: 'write',
requires: ['computeStyles'],
fn: ({
state
}) => {
const styles = {};
const attributes = {};
Object.keys(state.elements).forEach(element => {
styles[element] = state.styles[element];
attributes[element] = state.attributes[element];
});
setState({
state,
styles,
attributes,
update,
forceUpdate,
placement: state.placement
});
}
}), [update, forceUpdate, setState]);
const nextModifiers = useMemo(() => {
if (!dequal(prevModifiers.current, modifiers)) {
prevModifiers.current = modifiers;
}
return prevModifiers.current;
}, [modifiers]);
useEffect(() => {
if (!popperInstanceRef.current || !enabled) return;
popperInstanceRef.current.setOptions({
placement,
strategy,
modifiers: [...nextModifiers, updateModifier, disabledApplyStylesModifier]
});
}, [strategy, placement, updateModifier, enabled, nextModifiers]);
useEffect(() => {
if (!enabled || referenceElement == null || popperElement == null) {
return undefined;
}
popperInstanceRef.current = createPopper(referenceElement, popperElement, Object.assign({}, config, {
placement,
strategy,
modifiers: [...nextModifiers, ariaDescribedByModifier, updateModifier]
}));
return () => {
if (popperInstanceRef.current != null) {
popperInstanceRef.current.destroy();
popperInstanceRef.current = undefined;
setState(s => Object.assign({}, s, {
attributes: {},
styles: {
popper: {}
}
}));
}
}; // This is only run once to _create_ the popper
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [enabled, referenceElement, popperElement]);
return popperState;
}
export default usePopper;

Event Timeline