Skip to content

Instantly share code, notes, and snippets.

@jpzwarte
Created March 5, 2019 09:47
Show Gist options
  • Save jpzwarte/5b6f848a8cbc1488779181894382f306 to your computer and use it in GitHub Desktop.
Save jpzwarte/5b6f848a8cbc1488779181894382f306 to your computer and use it in GitHub Desktop.
import { convertMenuPositionToConnectedPositions, MenuPositionValue } from './menu-position';
import { ConnectedPosition } from '@angular/cdk/overlay';
interface TestPosition {
side: MenuPositionValue,
align: MenuPositionValue,
positions: ConnectedPosition[]
}
const POSITIONS: TestPosition[] = [
{
side: 'top',
align: 'left',
positions: [
{
originX: 'start',
originY: 'top',
overlayX: 'start',
overlayY: 'bottom',
offsetY: 0
}
]
},
{
side: 'right',
align: 'top',
positions: [
{
originX: 'end',
originY: 'top',
overlayX: 'start',
overlayY: 'top',
offsetY: 0
}
]
},
{
side: 'left',
align: 'bottom',
positions: [
{
originX: 'start',
originY: 'bottom',
overlayX: 'end',
overlayY: 'bottom',
offsetY: 0
}
]
},
{
side: 'bottom',
align: 'left',
positions: [
{
originX: 'start',
originY: 'bottom',
overlayX: 'start',
overlayY: 'top',
offsetY: 0
}
]
},
{
side: 'bottom',
align: 'right',
positions: [
{
originX: 'end',
originY: 'bottom',
overlayX: 'end',
overlayY: 'top',
offsetY: 0
}
]
}
];
fdescribe('MenuPosition', () => {
for (const pos of POSITIONS) {
it(`should convert menu position [${pos.side}, ${pos.align}] to connected positions`, () => {
const positions = convertMenuPositionToConnectedPositions([pos.side as MenuPositionValue, pos.align as MenuPositionValue]);
// expect(positions.length).toBe(pos.positions.length);
expect(positions[0]).toEqual(pos.positions[0]);
});
}
});
import { ConnectedPosition, HorizontalConnectionPos, VerticalConnectionPos } from '@angular/cdk/overlay';
export type MenuPositionValue = 'top' | 'right' | 'bottom' | 'left';
export type MenuPosition = [MenuPositionValue, MenuPositionValue];
interface ConvertedPosition extends ConnectedPosition {
originFallbackX: HorizontalConnectionPos;
originFallbackY: VerticalConnectionPos;
overlayFallbackX: HorizontalConnectionPos;
overlayFallbackY: VerticalConnectionPos;
}
function convertSideMenuPosition(side: MenuPositionValue): ConvertedPosition {
if (side === 'top' || side === 'bottom') {
return {
originX: 'start',
originY: side,
overlayX: 'start',
overlayY: side === 'top' ? 'bottom' : 'top',
originFallbackX: 'end',
originFallbackY: side === 'top' ? 'bottom' : 'top',
overlayFallbackX: 'end',
overlayFallbackY: side
};
} else {
return {
originX: side === 'left' ? 'start' : 'end',
originY: 'top',
overlayX: side === 'left' ? 'end' : 'start',
overlayY: 'top',
originFallbackX: side === 'left' ? 'end' : 'start',
originFallbackY: 'bottom',
overlayFallbackX: side === 'left' ? 'start' : 'end',
overlayFallbackY: 'bottom'
};
}
}
export function convertMenuPositionToConnectedPositions([side, align]: MenuPosition, offsetY = 0): ConnectedPosition[] {
let {
originX,
originY,
overlayX,
overlayY,
originFallbackX,
originFallbackY,
overlayFallbackX,
overlayFallbackY
} = convertSideMenuPosition(side);
if (align === 'top' || align === 'bottom') {
originY = align;
overlayY = originY;
} else {
originX = align === 'left' ? 'start' : 'end';
overlayX = originX;
}
return [
{ originX, originY, overlayX, overlayY, offsetY },
{ originX: originFallbackX, originY, overlayX: overlayFallbackX, overlayY, offsetY },
{
originX,
originY: originFallbackY,
overlayX,
overlayY: overlayFallbackY,
offsetY: -offsetY
},
{
originX: originFallbackX,
originY: originFallbackY,
overlayX: overlayFallbackX,
overlayY: overlayFallbackY,
offsetY: -offsetY
}
];
}
/**
* Sets the appropriate positions on a position strategy
* so the overlay connects with the trigger correctly.
* @param positionStrategy Strategy whose position to update.
*/
private setPosition(positionStrategy: FlexibleConnectedPositionStrategy) {
let positions: ConnectedPosition[];
if (this.triggersSubmenu()) {
// When the menu is a sub-menu, it should always align itself
// to the edges of the trigger, instead of overlapping it.
positions = convertMenuPositionToConnectedPositions(['right', 'top'], -MENU_PANEL_TOP_PADDING);
} else {
positions = convertMenuPositionToConnectedPositions(this.menu.position);
}
positionStrategy.withPositions(positions);
}
@jpzwarte
Copy link
Author

jpzwarte commented Mar 5, 2019

RTL is no requirement for me, so the code doesn't take that into account.

@chriszrc
Copy link

Hi, this looks great, can you tell us how to use this? The trigger directive doesn't look like a regular angular directive, it's not clear how we should be getting the reference to the FlexibleConnectedPositionStrategy?

@jefmenegazzo
Copy link

@chriszrc I don't know if you still need the answer, but below is the way I used it if other people need it:

 
@ViewChildren(MatMenuTrigger)
menuTriggers: QueryList<MatMenuTrigger>;

constructor(
    private ngZone: NgZone
) { }

ngAfterViewInit(): void {

    this.menuTriggers.forEach(item => {

        item.menuOpened.pipe(
            // delay(1000)
        ).subscribe(() => {

            this.menuOpened = item;

            this.ngZone.onStable.pipe(first()).subscribe(() => {
                this.recalculateMenu(item)
            })
        });
    });
}

recalculateMenu(menuTrigger: MatMenuTrigger) {
    const overlayRef: OverlayRef = (menuTrigger as any)._overlayRef;
    const overlayConfig = overlayRef.getConfig();
    (overlayConfig.positionStrategy as any)._isInitialRender = true;
    this.setPosition(menuTrigger, overlayConfig.positionStrategy as FlexibleConnectedPositionStrategy);
    overlayConfig.positionStrategy.apply();
}

setPosition(menuTrigger: MatMenuTrigger, positionStrategy: FlexibleConnectedPositionStrategy) {

    let positions: ConnectedPosition[];

    if (menuTrigger.triggersSubmenu()) {
        // When the menu is a sub-menu, it should always align itself
        // to the edges of the trigger, instead of overlapping it.
        positions = convertMenuPositionToConnectedPositions(["right", "top"], -8);
    } else {
        positions = convertMenuPositionToConnectedPositions(["right", "top"]);
    }

    positionStrategy.withPositions(positions);
}

@BogdanCR7
Copy link

what is this.menuOpened?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment