export class DropdownComponent {
@Input() public isButton: boolean = false;
@Input() public isIconOnly: boolean = false;
@Input() public icon: string = 'icon-dots-vertical';
@Input() public appendTo: string | ElementRef | null = null;
@ViewChild('dropdownMenu') public dropdownMenu!: ElementRef;
@ViewChild(IconComponent, { read: ElementRef }) public iconElement!: ElementRef;
public ButtonGroup = ButtonGroup;
public ButtonIcon = ButtonIcon;
public ButtonType = ButtonType;
public isDropdownOpen = false;
public constructor(
private readonly _renderer: Renderer2,
private readonly _hostElement: ElementRef
) {}
public toggleDropdown(): void {
this.isDropdownOpen = !this.isDropdownOpen;
if (this.isDropdownOpen && this.appendTo) {
setTimeout(() => {
this.appendDropdownToTarget();
});
} else if (!this.isDropdownOpen) {
this.resetDropdownPosition();
}
}
public appendDropdownToTarget(): void {
const dropdownMenuElement = this.dropdownMenu.nativeElement;
let targetElement: HTMLElement | null = null;
if (this.appendTo === 'body') {
targetElement = document.body;
} else if (this.appendTo instanceof ElementRef) {
targetElement = this.appendTo.nativeElement;
} else if (typeof this.appendTo === 'string') {
targetElement = document.querySelector(this.appendTo);
}
if (targetElement) {
this._renderer.appendChild(targetElement, dropdownMenuElement);
const iconRect = this.iconElement.nativeElement.getBoundingClientRect();
const top = iconRect.bottom + window.scrollY;
const left = iconRect.left + window.scrollX;
this._renderer.setStyle(dropdownMenuElement, 'position', 'absolute');
this._renderer.setStyle(dropdownMenuElement, 'top', `${top}px`);
this._renderer.setStyle(dropdownMenuElement, 'left', `${left}px`);
this._renderer.setStyle(dropdownMenuElement, 'min-width', `${iconRect.width}px`);
}
}
public resetDropdownPosition(): void {
const dropdownMenuElement = this.dropdownMenu.nativeElement;
this._renderer.appendChild(this._hostElement.nativeElement, dropdownMenuElement);
}
@HostListener('document:click', ['$event.target'])
public onClickOutside(targetElement: HTMLElement): void {
const clickedInsideHost = this._hostElement.nativeElement.contains(targetElement);
const clickedInsideDropdown = this.dropdownMenu?.nativeElement.contains(targetElement);
if (!clickedInsideHost && !clickedInsideDropdown) {
this.isDropdownOpen = false;
}
}
}
<div class="dropdown-container">
<app-button
[label]="'SHARED.RUNNING_ADS' | transloco"
[buttonType]="ButtonType.SECONDARY"
[buttonGroup]="ButtonGroup.TEXT"
[buttonIcon]="ButtonIcon.ICON_RIGHT"
[icon]="'icon-chevron-down-2'"
[isWide]="true"
[isDisabled]="false"
[isDarkmode]="true"
(click)="toggleDropdown()"
/>
@if (isDropdownOpen) {
<div class="dropdown-menu">
<ng-content />
</div>
}
</div>
@use 'shared' as *;
.dropdown-container {
position: relative;
display: inline-block;
cursor: pointer;
}
.dropdown-menu {
border-radius: 8px;
box-shadow: 0px 16px 16px 0px rgba(217, 218, 243, 0.3);
background: var(--color-white);
border: 1px solid var(--color-grey-300);
position: absolute;
right: 0;
width: 360px;
z-index: 1000;
overflow: auto;
}
.dropdown-menu.show {
display: block;
}