I'm trying to implement a context menu using BlueprintJs Popover component; that uses Popper.js to position the popover, under the hood.
The problemis that: I have fixed elements and absolutely positioned elements (with transform css property set to translate3d - I believe these create new stacking contexts, possibly causing issues too) in the dom tree, above the context menu, that can not be changed. I've read somewhere in the Popper.js documentation, that I should use the fixed position strategy in this case.
Unfortunately BlueprintJs Popover does not allow me (as far as I know) to set Popper.js options, only modifiers.
So can the positioning strategy be changed with modifiers?
Here's the code and what I've tried:
import React, { useState } from 'react';
import { Popover, Position, Classes } from '@blueprintjs/core';
const getModifiers = (left, top) => {
return {
preventOverflow: { boundariesElement: 'viewport' },
computeStyle: {
// set to false to avoid using transform property to position element,
// as that clashes with other transform: translate3d styles set earlier
gpuAcceleration: false,
// I could just overwrite the computeStyles fn, and use position fixed;
// but I'd like to avoid that and let Popper.js do the coordinate arithmetics
// fn: (data) => {
// return {
// ...data,
// styles: {
// ...data.styles,
// position: 'fixed',
// left: `${left}px`,
// top: `${top}px`,
// }
// };
// },
},
// here's where I try to change the position strategy using custom modifier
changeStrategyWithModifier: {
order: 0,
enabled: true,
name: 'changeStrategyWithModifier',
phase: 'main',
fn: (data) => {
return {
...data,
instance: {
...data.instance,
options: {
...data.instance.options,
positionFixed: true, // does not seem ot have any effect
strategy: 'fixed', // does not seem ot have any effect
},
},
state: {
// reset set to true to restart process after changing strategy
...data.instance.state,
reset: true,
},
positionFixed: true, // does not seem ot have any effect
};
},
},
};
};
const ContextMenu = (props) => {
const [isOpen, setOpen] = useState(false);
const [offset, setOffset] = useState();
const portalContainer = useGetPortalContainer();
const handleCloseContextMenu = () => setOpen(false);
const handleInteraction = () => setOpen(false);
const handleOpenContextMenu = (mouseEvent) => {
mouseEvent.preventDefault();
setOffset({ left: mouseEvent.clientX, top: mouseEvent.clientY });
setOpen(true);
};
const modifiers = getModifiers(offset.left, offset.top);
return (
<>
<div className={Classes.CONTEXT_MENU_POPOVER_TARGET} style={offset}>
<Popover
isOpen={isOpen}
onInteraction={handleInteraction}
content={props.renderMenu(handleCloseContextMenu)}
target={<div />}
usePortal={true}
portalContainer={portalContainer}
position={Position.TOP_LEFT}
modifiers={modifiers}
/>
</div>
{props.renderComponent(handleOpenContextMenu)}
</>
);
};
strategy: 'fixed'
into thePopper.createPopper
options: popper.js.org/docs/v2/faq/… – Mccauley