Skip to content

Commit

Permalink
Fixed #12031 - Component: Responsive Overlay
Browse files Browse the repository at this point in the history
  • Loading branch information
cetincakiroglu committed Oct 13, 2022
1 parent cc3d0e6 commit 37f9629
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 103 deletions.
6 changes: 6 additions & 0 deletions src/app/components/api/primengconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { Subject } from 'rxjs';
import { FilterMatchMode } from './filtermatchmode';
import { Translation } from './translation';

interface OverlayOptions {
breakpoint: number;
}

@Injectable({providedIn: 'root'})
export class PrimeNGConfig {

ripple: boolean = false;

overlayOptions: OverlayOptions;

filterMatchModeOptions = {
text: [
FilterMatchMode.STARTS_WITH,
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/dropdown/dropdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,4 @@ input.p-dropdown-label {

.p-fluid .p-dropdown .p-dropdown-label {
width: 1%;
}
}
153 changes: 51 additions & 102 deletions src/app/components/dropdown/dropdown.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import {NgModule,Component,ElementRef,OnInit,AfterViewInit,AfterContentInit,AfterViewChecked,OnDestroy,Input,Output,Renderer2,EventEmitter,ContentChildren,
QueryList,ViewChild,TemplateRef,forwardRef,ChangeDetectorRef,NgZone,ViewRef,ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {trigger,style,transition,animate,AnimationEvent} from '@angular/animations';
import {trigger,transition,AnimationEvent, query, animateChild} from '@angular/animations';
import {CommonModule} from '@angular/common';
import {OverlayService, PrimeNGConfig, SelectItem, TranslationKeys} from 'primeng/api';
import {PrimeNGConfig, SelectItem, TranslationKeys} from 'primeng/api';
import {SharedModule,PrimeTemplate, FilterService} from 'primeng/api';
import {DomHandler, ConnectedOverlayScrollHandler} from 'primeng/dom';
import {ObjectUtils,UniqueComponentId,ZIndexUtils} from 'primeng/utils';
import {ObjectUtils,UniqueComponentId} from 'primeng/utils';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
import {TooltipModule} from 'primeng/tooltip';
import {Scroller, ScrollerModule, ScrollerOptions} from 'primeng/scroller';
import {RippleModule} from 'primeng/ripple';
import {AutoFocusModule} from 'primeng/autofocus';
import {Overlay, OverlayModule} from '../overlay/overlay';

export const DROPDOWN_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -86,7 +87,7 @@ export class DropdownItem {
<div class="p-dropdown-trigger" role="button" aria-label="dropdown trigger" aria-haspopup="listbox" [attr.aria-expanded]="overlayVisible">
<span class="p-dropdown-trigger-icon" [ngClass]="dropdownIcon"></span>
</div>
<div *ngIf="overlayVisible" [ngClass]="'p-dropdown-panel p-component'" (click)="onOverlayClick($event)" [@overlayAnimation]="{value: 'visible', params: {showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions}}" (@overlayAnimation.start)="onOverlayAnimationStart($event)" (@overlayAnimation.done)="onOverlayAnimationEnd($event)" [ngStyle]="panelStyle" [class]="panelStyleClass">
<p-overlay #overlay *ngIf="overlayVisible" [autoZIndex]="autoZIndex" [container]="container" @overlayAnimation (@overlayAnimation.start)="onOverlayAnimationStart($event)" [showTransitionOptions]="showTransitionOptions" [hideTransitionOptions]="hideTransitionOptions" [panelStyle]="panelStyle" [panelStyleClass]="panelStyleClass" [panelClass]="'p-dropdown-panel p-component'" [appendTo]="appendTo" [overlayDirection]="overlayDirection" [baseZIndex]="baseZIndex">
<ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
<div class="p-dropdown-header" *ngIf="filter" (click)="$event.stopPropagation()">
<ng-container *ngIf="filterTemplate; else builtInFilterElement">
Expand Down Expand Up @@ -153,17 +154,13 @@ export class DropdownItem {
</ng-template>
</div>
<ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
</div>
</p-overlay>
</div>
`,
animations: [
trigger('overlayAnimation', [
transition(':enter', [
style({opacity: 0, transform: 'scaleY(0.8)'}),
animate('{{showTransitionParams}}')
]),
transition(':leave', [
animate('{{hideTransitionParams}}', style({ opacity: 0 }))
transition(':enter, :leave', [
query('@*', animateChild(), {optional: true})
])
])
],
Expand Down Expand Up @@ -279,6 +276,8 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView

@Input() autofocusFilter: boolean = true;

@Input() overlayDirection: string = 'end';

@Output() onChange: EventEmitter<any> = new EventEmitter();

@Output() onFilter: EventEmitter<any> = new EventEmitter();
Expand Down Expand Up @@ -309,6 +308,8 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView

@ViewChild('scroller') scroller: Scroller;

@ViewChild('overlay') overlayViewChild: Overlay;

@ContentChildren(PrimeTemplate) templates: QueryList<any>;

private _disabled: boolean;
Expand Down Expand Up @@ -341,8 +342,6 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView
console.warn("The itemSize property is deprecated, use virtualScrollItemSize property instead.");
}

overlay: HTMLDivElement;

itemsWrapper: HTMLDivElement;

itemTemplate: TemplateRef<any>;
Expand Down Expand Up @@ -421,7 +420,7 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView

listId: string;

constructor(public el: ElementRef, public renderer: Renderer2, public cd: ChangeDetectorRef, public zone: NgZone, public filterService: FilterService, public config: PrimeNGConfig, public overlayService: OverlayService) {}
constructor(public el: ElementRef, public renderer: Renderer2, public cd: ChangeDetectorRef, public zone: NgZone, public filterService: FilterService, public config: PrimeNGConfig) {}

ngAfterContentInit() {
this.templates.forEach((item) => {
Expand Down Expand Up @@ -601,15 +600,15 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView

this.zone.runOutsideAngular(() => {
setTimeout(() => {
this.alignOverlay();
this.overlayViewChild.alignOverlay();
}, 1);
});
}

if (this.selectedOptionUpdated && this.itemsWrapper) {
let selectedItem = DomHandler.findSingle(this.overlay, 'li.p-highlight');
let selectedItem = DomHandler.findSingle(this.overlayViewChild.el.nativeElement, 'li.p-highlight');
if (selectedItem) {
DomHandler.scrollInView(this.itemsWrapper, DomHandler.findSingle(this.overlay, 'li.p-highlight'));
DomHandler.scrollInView(this.itemsWrapper, DomHandler.findSingle(this.overlayViewChild.el.nativeElement, 'li.p-highlight'));
}
this.selectedOptionUpdated = false;
}
Expand Down Expand Up @@ -682,21 +681,14 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView
this.cd.detectChanges();
}

onOverlayClick(event) {
this.overlayService.add({
originalEvent: event,
target: this.el.nativeElement
});
}

isInputClick(event): boolean {
return DomHandler.hasClass(event.target, 'p-dropdown-clear-icon') ||
event.target.isSameNode(this.accessibleViewChild.nativeElement) ||
(this.editableInputViewChild && event.target.isSameNode(this.editableInputViewChild.nativeElement));
}

isOutsideClicked(event: Event): boolean {
return !(this.el.nativeElement.isSameNode(event.target) || this.el.nativeElement.contains(event.target) || (this.overlay && this.overlay.contains(<Node> event.target)));
return !(this.el.nativeElement.isSameNode(event.target) || this.el.nativeElement.contains(event.target) || (this.overlayViewChild && this.overlayViewChild.el.nativeElement.contains(<Node> event.target)));
}

isEmpty() {
Expand Down Expand Up @@ -730,78 +722,50 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView
}

onOverlayAnimationStart(event: AnimationEvent) {
switch (event.toState) {
case 'visible':
this.overlay = event.element;
this.itemsWrapper = DomHandler.findSingle(this.overlay, this.virtualScroll ? '.p-scroller' : '.p-dropdown-items-wrapper');
this.virtualScroll && this.scroller.setContentEl(this.itemsViewChild.nativeElement);
this.appendOverlay();
if (this.autoZIndex) {
ZIndexUtils.set('overlay', this.overlay, this.baseZIndex + this.config.zIndex.overlay);
}
this.alignOverlay();
this.bindDocumentClickListener();
this.bindDocumentResizeListener();
this.bindScrollListener();

if (this.options && this.options.length) {
if (this.virtualScroll) {
const selectedIndex = this.selectedOption ? this.findOptionIndex(this.getOptionValue(this.selectedOption), this.optionsToDisplay) : -1;
if (selectedIndex !== -1) {
this.scroller.scrollToIndex(selectedIndex);
}
}
else {
let selectedListItem = DomHandler.findSingle(this.itemsWrapper, '.p-dropdown-item.p-highlight');

if (selectedListItem) {
selectedListItem.scrollIntoView({ block: 'nearest', inline: 'center' });
}
if (event.toState === null && event.fromState === 'void') {
this.itemsWrapper = DomHandler.findSingle(this.overlayViewChild.el.nativeElement, this.virtualScroll ? '.p-scroller' : '.p-dropdown-items-wrapper');
this.virtualScroll && this.scroller.setContentEl(this.itemsViewChild.nativeElement);
this.overlayViewChild.appendOverlay();
this.overlayViewChild.alignOverlay();
this.bindDocumentClickListener();
this.bindDocumentResizeListener();
this.bindScrollListener();

if (this.options && this.options.length) {
if (this.virtualScroll) {
const selectedIndex = this.selectedOption ? this.findOptionIndex(this.getOptionValue(this.selectedOption), this.optionsToDisplay) : -1;
if (selectedIndex !== -1) {
this.scroller.scrollToIndex(selectedIndex);
}
}
else {
let selectedListItem = DomHandler.findSingle(this.itemsWrapper, '.p-dropdown-item.p-highlight');

if (this.filterViewChild && this.filterViewChild.nativeElement) {
this.preventModelTouched = true;

if (this.autofocusFilter) {
this.filterViewChild.nativeElement.focus();
if (selectedListItem) {
selectedListItem.scrollIntoView({ block: 'nearest', inline: 'center' });
}
}
}

this.onShow.emit(event);
break;
if (this.filterViewChild && this.filterViewChild.nativeElement) {
this.preventModelTouched = true;

case 'void':
this.onOverlayHide();
this.onHide.emit(event);
break;
}
}
if (this.autofocusFilter) {
this.filterViewChild.nativeElement.focus();
}
}

onOverlayAnimationEnd(event: AnimationEvent) {
switch (event.toState) {
case 'void':
ZIndexUtils.clear(event.element);
break;
this.onShow.emit(event);
}
}

appendOverlay() {
if (this.appendTo) {
if (this.appendTo === 'body')
document.body.appendChild(this.overlay);
else
DomHandler.appendChild(this.overlay, this.appendTo);

if (!this.overlay.style.minWidth) {
this.overlay.style.minWidth = DomHandler.getWidth(this.containerViewChild.nativeElement) + 'px';
}
if (event.toState === 'void' && event.fromState === null) {
this.onOverlayHide();
this.onHide.emit(event);
}
}

restoreOverlayAppend() {
if (this.overlay && this.appendTo) {
this.el.nativeElement.appendChild(this.overlay);
if (this.overlayViewChild && this.appendTo) {
this.el.nativeElement.appendChild(this.overlayViewChild.el.nativeElement);
}
}

Expand All @@ -811,19 +775,9 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView
if (this.filter && this.resetFilterOnHide) {
this.resetFilter();
}

this.cd.markForCheck();
}

alignOverlay() {
if (this.overlay) {
if (this.appendTo)
DomHandler.absolutePosition(this.overlay, this.containerViewChild.nativeElement);
else
DomHandler.relativePosition(this.overlay, this.containerViewChild.nativeElement);
}
}

onInputFocus(event) {
this.focused = true;
this.onFocus.emit(event);
Expand Down Expand Up @@ -1288,7 +1242,6 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView
this.unbindDocumentClickListener();
this.unbindDocumentResizeListener();
this.unbindScrollListener();
this.overlay = null;
this.itemsWrapper = null;
this.onModelTouched();
}
Expand All @@ -1298,19 +1251,15 @@ export class Dropdown implements OnInit,AfterViewInit,AfterContentInit,AfterView
this.scrollHandler.destroy();
this.scrollHandler = null;
}

if (this.overlay) {
ZIndexUtils.clear(this.overlay);
}

this.restoreOverlayAppend();
this.onOverlayHide();
}
}

@NgModule({
imports: [CommonModule,SharedModule,TooltipModule,RippleModule,ScrollerModule, AutoFocusModule],
imports: [CommonModule,OverlayModule,SharedModule,TooltipModule,RippleModule,ScrollerModule, AutoFocusModule],
exports: [Dropdown,SharedModule,ScrollerModule],
declarations: [Dropdown,DropdownItem]
declarations: [Dropdown,DropdownItem],
entryComponents: [Overlay]
})
export class DropdownModule { }
Empty file.
28 changes: 28 additions & 0 deletions src/app/components/overlay/overlay.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.p-overlay-mask {
display: flex;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

.p-overlay-responsive {
overflow: hidden;
position: static;
height: fit-content;
width: inherit;
}

.p-overlay-panel-start {
align-self: flex-start;
}

.p-overlay-panel-center {
align-self: center;
}

.p-overlay-panel-end {
align-self: flex-end;
}
Loading

0 comments on commit 37f9629

Please sign in to comment.