import { IComponentController } from 'angular' import { Component, Inject, Input } from '../decorators' import DropdownMenuController from './dropdown-menu' @Component({ selector: 'mflyDropdownMenuContents', require: { mflyDropdownMenu: '^mflyDropdownMenu', }, template: require('./dropdown-menu-contents.html'), transclude: true, }) export default class DropdownMenuContentsController implements IComponentController { @Input() containerHeight?: number private deregisterCloseHandler: () => void private deregisterOpenHandler: () => void private menuContentsElement: JQuery private mflyDropdownMenu: DropdownMenuController constructor( @Inject('$document') private $document: ng.IDocumentService, @Inject('$element') private $element: ng.IAugmentedJQuery, @Inject('$rootScope') private $rootScope: JSUIRootScope, @Inject('$scope') private $scope: ng.IScope, ) {} $onDestroy() { this.deregisterCloseHandler() this.deregisterOpenHandler() } $onInit() { this.menuContentsElement = this.$element.find('.o-dropdown-menu__contents') this.deregisterCloseHandler = this.$rootScope.$on('dropdown-menu:close', () => { if (!this.mflyDropdownMenu.isOpen()) { return } this.menuContentsElement.removeClass('is-visible') this.mflyDropdownMenu.isOpen(false) this.clearPositionalAdjustments(this.menuContentsElement) this.$scope.$apply() }) this.deregisterOpenHandler = this.$scope.$on('dropdown-menu:open', () => { if (this.mflyDropdownMenu.isOpen()) { return } this.menuContentsElement.addClass('is-visible') this.mflyDropdownMenu.isOpen(true) this.$scope.$apply() // Adjust dropdown if it is appearing offscreen. const menuRect = this.menuContentsElement[0].getBoundingClientRect() const rightEdgeX = menuRect.left + menuRect.width const bottomEdgeY = menuRect.top + menuRect.height let screenHeight = (this.$document[0] as HTMLDocument).body.clientHeight if ( this.containerHeight ) { screenHeight = this.containerHeight } const screenWidth = (this.$document[0] as HTMLDocument).body.clientWidth // excludes scrollbar const offscreenLeft = (menuRect.left < 0) const offscreenRight = (rightEdgeX > screenWidth) const offscreenTop = (menuRect.top < 0) const offscreenBottom = (bottomEdgeY > screenHeight) const adjustPosition = () => { let translateXValue if ( (offscreenLeft && offscreenRight) || (offscreenTop && offscreenBottom) ) { return // ¯\_(ツ)_/¯ } if (offscreenLeft) { translateXValue = -menuRect.left } if (offscreenRight) { translateXValue = screenWidth - rightEdgeX - 1 } if (offscreenLeft || offscreenRight) { this.menuContentsElement.css('transform', `translateX(${translateXValue}px)`) } if (offscreenTop) { if (translateXValue) { this.menuContentsElement.css('transform', `translateX(${translateXValue}px) translateY(${-menuRect.top}px)`) } else { this.menuContentsElement.css('transform', `translateY(${-menuRect.top}px)`) } } if (offscreenBottom) { if (translateXValue) { this.menuContentsElement.css('transform', `translateX(${translateXValue}px) translateY(${screenHeight - bottomEdgeY}px)`) } else { this.menuContentsElement.css('transform', `translateY(${screenHeight - bottomEdgeY}px)`) } } } adjustPosition() }) } private clearPositionalAdjustments(element: JQuery) { element[0].style.removeProperty('transform') } }