How do I configure a material cdk overlay position strategy that works great, on big and small screens?
My objective is to create an overlay with the Angular overlay CDK, that follow these rules:
I have accomplished some of the above requirements, as you can see here the positioning works great on devices with enough vertical space (in this case 512px):
However as you can tell from the following gif, this does not work on small devices with insufficient vertical space (in this case 255px). Actually, as you can tell from the GIF, in 2 of the cases it was very close. The position was correct, only the height was off.
Here the goal is to use the available space, as illustrated in red:
I have a stack blitz, where you can experiment here.
component
openPopup(element: any) {
const overlayRef = this.overlay.create({
hasBackdrop: true,
positionStrategy: this.overlay.position()
.flexibleConnectedTo(element)
.withLockedPosition()
.withPositions([
{
panelClass: 'custom-panel1',
originX: 'center',
originY: 'center',
overlayX: 'start',
overlayY: 'top',
},
{
panelClass: 'custom-panel2',
originX: 'center',
originY: 'center',
overlayX: 'start',
overlayY: 'center',
},
{
panelClass: 'custom-panel3',
originX: 'center',
originY: 'center',
overlayX: 'start',
overlayY: 'bottom',
},
])
,
width: '200px',
maxHeight: 300,
});
const popupComponentPortal = new ComponentPortal(PopupComponent);
overlayRef.attach(popupComponentPortal);
overlayRef.backdropClick().subscribe(() => {
overlayRef.dispose();
});
}
global styles
.cdk-overlay-pane {
max-height: 300px;
}
I have been thinking about using the global positioning strategy when the height of the viewport is getting small. However, I would prefer to avoid this scenario, as I would love a solution that could tackle any height of the overlay (respecting the max height off-course).
I recommend using the "Open in New Window LIVE" feature of stackbllitz, when testing the stackblitz. Link is here again.
I would be SO grateful, if you could help solve this issue or point me in the right direction 🤷🏾♂️
I solved the above problem, by implementing a check - after the dialog is opened. This is done with the resize-observer. The code looks like this:
/*
Read more about the resize observer here: https://developers.google.com/web/updates/2016/10/resizeobserver
Basicaly, what we do is that we subscribe to size events on the overlay.
Currently we only get one event, (then we disconnet the resize observer).
But then we simply calculate if we need to improve the layout.
*/
const ro = new ResizeObserver(entries => {
for (const entry of entries) {
// We get the hight of the element from the the contnetRect, provided by the resize observer
const height = entry.contentRect.height;
const { left: x, top: y } = entry.target.getBoundingClientRect();
const offsetPlusHeight = y + height;
const pixelsOverflow = offsetPlusHeight - this.viewPortHeight;
// If y is negative, we are off-screen to the top.
// If pixelsOverflow is positive, we are off-screen on the bottom
// In either case, we adopt a new strategy.
if (y < 0 || pixelsOverflow > 1) {
ro.disconnect();
overlayRef.updateSize({
height: height,
maxHeight: config.maxHeight,
width: config.width,
});
// Trust the initial positioning strategy, in terms of the x coordinate
// Now determine if we need to throw the overlap, all the way to the top.
const delta = this.viewPortHeight - height;
const yOffset = delta > 0 ? delta : 0;
// Finnaly, we can apply canculate and apply the new position
const newPositionStrategy = this.getGlobalPosition(yOffset, x);
overlayRef.updatePositionStrategy(newPositionStrategy);
}
}
});
// Element for which to observe height and width
ro.observe(overlayRef.overlayElement);
I think the reason I need this extra check, is because what I am actually looking for is a n cdk overlay strategy for items with variable height.
If you are interested in a working solution, I have a working stackblitz here: