@mantine/core
Version:
React components library focused on usability, accessibility and developer experience
155 lines (154 loc) • 5.53 kB
JavaScript
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { useDidUpdate, useIsomorphicEffect, useUncontrolled } from "@mantine/hooks";
import { arrow, autoUpdate, flip, hide, inline, limitShift, offset, shift, size, useFloating } from "@floating-ui/react";
//#region packages/@mantine/core/src/components/Popover/use-popover.ts
function getDefaultMiddlewares(middlewares) {
if (middlewares === void 0) return {
shift: true,
flip: true
};
const result = { ...middlewares };
if (middlewares.shift === void 0) result.shift = true;
if (middlewares.flip === void 0) result.flip = true;
return result;
}
function getPopoverMiddlewares(options, getFloating, disableFlip, lockEnabled) {
const middlewaresOptions = getDefaultMiddlewares(options.middlewares);
const middlewares = [offset(options.offset), hide()];
if (middlewaresOptions.flip && !disableFlip) {
const userFlip = typeof middlewaresOptions.flip === "boolean" ? {} : middlewaresOptions.flip;
const flipOptions = lockEnabled ? {
fallbackStrategy: "initialPlacement",
...userFlip
} : userFlip;
middlewares.push(flip(flipOptions));
}
if (middlewaresOptions.shift) {
const shiftOptions = typeof middlewaresOptions.shift === "boolean" ? {} : middlewaresOptions.shift;
middlewares.push(shift((state) => {
const isVerticalPlacement = state.placement.startsWith("top") || state.placement.startsWith("bottom");
return {
limiter: limitShift(),
padding: 5,
...options.width === "target" && isVerticalPlacement ? { mainAxis: false } : null,
...shiftOptions
};
}));
}
if (middlewaresOptions.inline) middlewares.push(typeof middlewaresOptions.inline === "boolean" ? inline() : inline(middlewaresOptions.inline));
middlewares.push(arrow({
element: options.arrowRef,
padding: options.arrowOffset
}));
if (middlewaresOptions.size || options.width === "target") middlewares.push(size({
...typeof middlewaresOptions.size === "boolean" ? {} : middlewaresOptions.size,
apply({ rects, availableWidth, availableHeight, ...rest }) {
const styles = getFloating().refs.floating.current?.style ?? {};
if (middlewaresOptions.size) if (typeof middlewaresOptions.size === "object" && !!middlewaresOptions.size.apply) middlewaresOptions.size.apply({
rects,
availableWidth,
availableHeight,
...rest
});
else Object.assign(styles, {
maxWidth: `${availableWidth}px`,
maxHeight: `${availableHeight}px`
});
if (options.width === "target") Object.assign(styles, { width: `${rects.reference.width}px` });
}
}));
return middlewares;
}
function usePopover(options) {
const [_opened, setOpened] = useUncontrolled({
value: options.opened,
defaultValue: options.defaultOpened,
finalValue: false,
onChange: options.onChange
});
const previouslyOpened = useRef(_opened);
const [lockedPlacement, setLockedPlacement] = useState(null);
const lockEnabled = options.preventPositionChangeWhenVisible !== false;
const wasOpenedRef = useRef(_opened);
if (_opened !== wasOpenedRef.current) {
wasOpenedRef.current = _opened;
if (_opened && lockedPlacement !== null) setLockedPlacement(null);
}
const resetLockedPlacement = useCallback(() => setLockedPlacement(null), []);
const onClose = () => {
if (_opened && !options.disabled) setOpened(false);
};
const onToggle = () => {
if (!options.disabled) setOpened(!_opened);
};
const floating = useFloating({
open: _opened,
strategy: options.strategy,
placement: lockEnabled ? lockedPlacement ?? options.position : options.position,
middleware: getPopoverMiddlewares(options, () => floating, lockEnabled && lockedPlacement !== null, lockEnabled),
whileElementsMounted: !options.keepMounted ? autoUpdate : void 0
});
useEffect(() => {
if (!options.keepMounted) return;
const referenceElement = floating.refs.reference.current;
const floatingElement = floating.refs.floating.current;
if (_opened && referenceElement && floatingElement) return autoUpdate(referenceElement, floatingElement, floating.update);
}, [
options.keepMounted,
_opened,
floating.update,
floating.elements.reference,
floating.elements.floating
]);
const measuredAfterShowRef = useRef(false);
useIsomorphicEffect(() => {
if (!_opened) {
measuredAfterShowRef.current = false;
return;
}
if (!lockEnabled || lockedPlacement !== null) return;
const flEl = floating.refs.floating.current;
if (!flEl || flEl.offsetHeight === 0 || flEl.offsetWidth === 0) return;
if (!measuredAfterShowRef.current) {
measuredAfterShowRef.current = true;
floating.update();
return;
}
if (floating.isPositioned) setLockedPlacement(floating.placement);
}, [
lockEnabled,
_opened,
floating.isPositioned,
floating.placement,
lockedPlacement,
floating.update
]);
const previousPlacementRef = useRef(floating.placement);
useIsomorphicEffect(() => {
if (previousPlacementRef.current !== floating.placement) {
previousPlacementRef.current = floating.placement;
options.onPositionChange?.(floating.placement);
}
}, [floating.placement]);
useDidUpdate(() => {
if (_opened !== previouslyOpened.current) if (!_opened) options.onClose?.();
else options.onOpen?.();
previouslyOpened.current = _opened;
}, [
_opened,
options.onClose,
options.onOpen
]);
return {
floating,
controlled: typeof options.opened === "boolean",
opened: _opened,
onClose,
onToggle,
resetLockedPlacement
};
}
//#endregion
export { usePopover };
//# sourceMappingURL=use-popover.mjs.map