Skip to content

Commit 8c9caa8

Browse files
authored
Merge pull request primefaces#12044 from primefaces/issue-12031
Fixed primefaces#12031 - Component: Responsive Overlay
2 parents d734902 + c990b87 commit 8c9caa8

File tree

7 files changed

+421
-167
lines changed

7 files changed

+421
-167
lines changed

src/app/components/api/primengconfig.ts

+6
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import { Subject } from 'rxjs';
33
import { FilterMatchMode } from './filtermatchmode';
44
import { Translation } from './translation';
55

6+
interface OverlayOptions {
7+
breakpoint: number;
8+
}
9+
610
@Injectable({providedIn: 'root'})
711
export class PrimeNGConfig {
812

913
ripple: boolean = false;
1014

15+
overlayOptions: OverlayOptions;
16+
1117
filterMatchModeOptions = {
1218
text: [
1319
FilterMatchMode.STARTS_WITH,

src/app/components/dropdown/dropdown.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,4 @@ input.p-dropdown-label {
8585

8686
.p-fluid .p-dropdown .p-dropdown-label {
8787
width: 1%;
88-
}
88+
}

src/app/components/dropdown/dropdown.ts

+110-166
Large diffs are not rendered by default.

src/app/components/overlay/ng-package.json

Whitespace-only changes.
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.p-overlay-mask {
2+
position: fixed;
3+
top: 0;
4+
left: 0;
5+
width: 100%;
6+
height: 100%;
7+
display: flex;
8+
justify-content: center;
9+
align-items: center;
10+
}
11+
12+
.p-overlay-responsive {
13+
overflow: hidden;
14+
position: static;
15+
height: fit-content;
16+
width: inherit;
17+
}
18+
19+
.p-overlay-panel-start {
20+
align-items: flex-start;
21+
}
22+
23+
.p-overlay-panel-center {
24+
align-items: center;
25+
}
26+
27+
.p-overlay-panel-end {
28+
align-items: flex-end;
29+
}
30+
31+
.p-overlay {
32+
position: absolute;
33+
}
34+
35+
.p-overlay-panel-static {
36+
position: static;
37+
}

src/app/components/overlay/overlay.ts

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
import {NgModule,Component,Input,OnDestroy,Renderer2,ElementRef,ChangeDetectionStrategy, ViewEncapsulation, ViewChild, Inject, Output, EventEmitter, ChangeDetectorRef} from '@angular/core';
2+
import {CommonModule, DOCUMENT} from '@angular/common';
3+
import {DomHandler} from 'primeng/dom';
4+
import {SharedModule, PrimeNGConfig, OverlayService} from 'primeng/api';
5+
import {RippleModule} from 'primeng/ripple';
6+
import {trigger,style,transition,animate, animation, useAnimation} from '@angular/animations';
7+
import {ZIndexUtils} from 'primeng/utils';
8+
9+
const showAnimation = animation([
10+
style({ transform: '{{transform}}', opacity: 0 }),
11+
animate('{{showTransitionParams}}')
12+
]);
13+
14+
const hideAnimation = animation([
15+
animate('{{hideTransitionParams}}', style({ transform: '{{transform}}', opacity: 0 }))
16+
]);
17+
18+
@Component({
19+
selector: 'p-overlay',
20+
template: `
21+
<div class="p-overlay" #overlay (click)="onOverlayClick($event)">
22+
<div #mask [ngClass]="{'p-mask p-overlay-mask p-component-overlay-enter': responsive}">
23+
<div #content [@overlayAnimation]="{value: 'visible', params: {showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions, transform: transformOptions}}" (@overlayAnimation.start)="onOverlayAnimationStart($event)" (@overlayAnimation.done)="onOverlayAnimationEnd($event)">
24+
<ng-content></ng-content>
25+
</div>
26+
</div>
27+
</div>
28+
`,
29+
animations: [
30+
trigger('overlayAnimation', [
31+
transition(':enter', [
32+
useAnimation(showAnimation)
33+
]),
34+
transition(':leave', [
35+
useAnimation(hideAnimation)
36+
])
37+
])
38+
],
39+
changeDetection: ChangeDetectionStrategy.OnPush,
40+
encapsulation: ViewEncapsulation.None,
41+
styleUrls: ['./overlay.css'],
42+
host: {
43+
'class': 'p-element'
44+
}
45+
})
46+
export class Overlay implements OnDestroy {
47+
48+
@Input() baseZIndex: number = 0;
49+
50+
@Input() showTransitionOptions: string = '.12s cubic-bezier(0, 0, 0.2, 1)';
51+
52+
@Input() hideTransitionOptions: string = '.1s linear';
53+
54+
@Input() container: ElementRef;
55+
56+
@Input() autoZIndex: boolean;
57+
58+
@Output() onAnimationStart: EventEmitter<any> = new EventEmitter();
59+
60+
@Output() onAnimationEnd: EventEmitter<any> = new EventEmitter();
61+
62+
@Output() onOverlayHide: EventEmitter<any> = new EventEmitter();
63+
64+
@ViewChild('overlay') overlayViewChild: ElementRef;
65+
66+
@ViewChild('content') contentViewChild: ElementRef;
67+
68+
@ViewChild('mask') maskViewChild: ElementRef;
69+
70+
@Input() set overlayDirection(value: string) {
71+
if (value && this.viewport.width < this.overlayBreakpoints) {
72+
this._overlayDirection = value;
73+
74+
switch (value) {
75+
case 'start':
76+
this.transformOptions = "translate3d(0px, -100%, 0px)";
77+
break;
78+
case 'end':
79+
this.transformOptions = "translate3d(0px, 100%, 0px)";
80+
break;
81+
case 'center':
82+
this.transformOptions = "scale(0.8)";
83+
break;
84+
}
85+
}
86+
else {
87+
this.transformOptions = "scaleY(0.8)";
88+
}
89+
}
90+
91+
get overlayDirection(): string {
92+
return this._overlayDirection;
93+
}
94+
95+
@Input() set appendTo(val) {
96+
this._appendTo = val;
97+
}
98+
99+
get appendTo(): any {
100+
return this._appendTo;
101+
}
102+
103+
@Input() set visible(value: boolean) {
104+
this._visible = value;
105+
}
106+
107+
get visible(): boolean {
108+
return this._visible;
109+
}
110+
111+
get overlayBreakpoints(): any {
112+
return this.config.overlayOptions ? this.config.overlayOptions.breakpoint : null;
113+
}
114+
115+
get viewport(): any {
116+
this._viewport = DomHandler.getViewport();
117+
return this._viewport;
118+
}
119+
120+
_visible: boolean;
121+
122+
_overlayDirection: string;
123+
124+
_viewport: any;
125+
126+
_overlayBreakpoints: number;
127+
128+
_appendTo: any;
129+
130+
transformOptions: string = "scaleY(0.8)";
131+
132+
documentResizeListener: any;
133+
134+
responsive: boolean;
135+
136+
constructor(@Inject(DOCUMENT) private document: Document, public el: ElementRef, public renderer: Renderer2, private config: PrimeNGConfig, public overlayService: OverlayService, private cd: ChangeDetectorRef) {}
137+
138+
ngOnInit() {
139+
this.bindDocumentResizeListener();
140+
}
141+
142+
onOverlayClick(event: MouseEvent) {
143+
this.overlayService.add({
144+
originalEvent: event,
145+
target: this.el.nativeElement
146+
});
147+
}
148+
149+
onOverlayAnimationStart(event) {
150+
this.onAnimationStart.emit(event);
151+
}
152+
153+
onOverlayAnimationEnd(event) {
154+
if (event.toState === 'void') {
155+
this.destroyOverlay();
156+
}
157+
this.onAnimationEnd.emit(event);
158+
}
159+
160+
appendOverlay() {
161+
if (this.autoZIndex) {
162+
ZIndexUtils.set('modal', this.el.nativeElement, this.baseZIndex + this.config.zIndex.modal);
163+
}
164+
if (this.viewport.width < this.overlayBreakpoints) {
165+
this.responsive = true;
166+
DomHandler.addClass(this.document.body, 'p-overflow-hidden');
167+
DomHandler.addClass(this.overlayViewChild.nativeElement, 'p-overlay-responsive');
168+
DomHandler.addClass(this.contentViewChild.nativeElement.firstChild, 'p-overlay-panel-static');
169+
}
170+
else {
171+
this.responsive = false;
172+
if (this.appendTo) {
173+
if (this.appendTo === 'body') {
174+
this.document.body.appendChild(this.overlayViewChild.nativeElement);
175+
}
176+
else {
177+
DomHandler.appendChild(this.overlayViewChild.nativeElement, this.appendTo);
178+
}
179+
}
180+
}
181+
if (!this.contentViewChild.nativeElement.style.minWidth) {
182+
this.contentViewChild.nativeElement.style.minWidth = DomHandler.getWidth(this.container) + 'px';
183+
}
184+
}
185+
186+
alignOverlay() {
187+
if(this.overlayViewChild) {
188+
if (this.viewport.width < this.overlayBreakpoints) {
189+
switch (this.overlayDirection) {
190+
case 'start':
191+
DomHandler.addClass(this.maskViewChild.nativeElement, 'p-overlay-panel-start')
192+
break;
193+
194+
case 'center':
195+
DomHandler.addClass(this.maskViewChild.nativeElement, 'p-overlay-panel-center')
196+
break;
197+
198+
case 'end':
199+
DomHandler.addClass(this.maskViewChild.nativeElement, 'p-overlay-panel-end')
200+
break;
201+
}
202+
}
203+
else {
204+
if (this.appendTo) {
205+
DomHandler.absolutePosition(this.overlayViewChild.nativeElement, this.container);
206+
}
207+
else {
208+
DomHandler.relativePosition(this.overlayViewChild.nativeElement, this.container);
209+
}
210+
}
211+
}
212+
}
213+
214+
blockScroll() {
215+
DomHandler.addClass(this.document.body, 'p-overflow-hidden');
216+
}
217+
218+
unblockScroll() {
219+
DomHandler.removeClass(this.document.body, 'p-overflow-hidden');
220+
}
221+
222+
bindDocumentResizeListener() {
223+
if (!this.documentResizeListener) {
224+
this.documentResizeListener = this.renderer.listen(window, 'resize', this.onWindowResize.bind(this));
225+
}
226+
}
227+
228+
unbindDocumentResizeListener() {
229+
if (this.documentResizeListener) {
230+
this.documentResizeListener();
231+
this.documentResizeListener = null;
232+
}
233+
}
234+
235+
onWindowResize() {
236+
if (this.visible) {
237+
this.visible = false;
238+
this.onOverlayHide.emit({visible: this.visible});
239+
this.cd.markForCheck();
240+
}
241+
}
242+
243+
destroyOverlay() {
244+
this.unblockScroll();
245+
this.unbindDocumentResizeListener();
246+
247+
if (this.overlayViewChild && this.overlayViewChild.nativeElement) {
248+
ZIndexUtils.clear(this.el.nativeElement);
249+
this.overlayViewChild = null;
250+
}
251+
252+
this.onOverlayHide.emit({visible: this.visible});
253+
}
254+
255+
ngOnDestroy() {
256+
this.destroyOverlay();
257+
}
258+
259+
}
260+
261+
@NgModule({
262+
imports: [CommonModule,RippleModule, SharedModule],
263+
exports: [Overlay, SharedModule],
264+
declarations: [Overlay]
265+
})
266+
export class OverlayModule { }
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './overlay';

0 commit comments

Comments
 (0)