...
|
...
|
@@ -16,9 +16,9 @@ |
16
|
16
|
|
17
|
17
|
import {
|
18
|
18
|
AfterViewInit, ChangeDetectorRef,
|
19
|
|
- Component, ComponentRef,
|
|
19
|
+ Component, ComponentFactoryResolver, ComponentRef,
|
20
|
20
|
Directive,
|
21
|
|
- ElementRef,
|
|
21
|
+ ElementRef, HostBinding,
|
22
|
22
|
Inject,
|
23
|
23
|
Input,
|
24
|
24
|
NgZone,
|
...
|
...
|
@@ -34,8 +34,7 @@ import { BreakpointObserver } from '@angular/cdk/layout'; |
34
|
34
|
import { MediaBreakpoints } from '@shared/models/constants';
|
35
|
35
|
import { MatButton } from '@angular/material/button';
|
36
|
36
|
import Timeout = NodeJS.Timeout;
|
37
|
|
-import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
|
38
|
|
-import { ComponentPortal, PortalInjector } from '@angular/cdk/portal';
|
|
37
|
+import { PortalInjector } from '@angular/cdk/portal';
|
39
|
38
|
|
40
|
39
|
@Directive({
|
41
|
40
|
selector: '[tb-toast]'
|
...
|
...
|
@@ -49,7 +48,6 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
49
|
48
|
private hideNotificationSubscription: Subscription = null;
|
50
|
49
|
|
51
|
50
|
private snackBarRef: MatSnackBarRef<any> = null;
|
52
|
|
- private overlayRef: OverlayRef;
|
53
|
51
|
private toastComponentRef: ComponentRef<TbSnackBarComponent>;
|
54
|
52
|
private currentMessage: NotificationMessage = null;
|
55
|
53
|
|
...
|
...
|
@@ -58,7 +56,7 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
58
|
56
|
constructor(private elementRef: ElementRef,
|
59
|
57
|
private viewContainerRef: ViewContainerRef,
|
60
|
58
|
private notificationService: NotificationService,
|
61
|
|
- private overlay: Overlay,
|
|
59
|
+ private componentFactoryResolver: ComponentFactoryResolver,
|
62
|
60
|
private snackBar: MatSnackBar,
|
63
|
61
|
private ngZone: NgZone,
|
64
|
62
|
private breakpointObserver: BreakpointObserver,
|
...
|
...
|
@@ -104,9 +102,11 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
104
|
102
|
if (this.snackBarRef) {
|
105
|
103
|
this.snackBarRef.dismiss();
|
106
|
104
|
}
|
107
|
|
-
|
108
|
|
- const position = this.overlay.position();
|
109
|
|
- let panelClass = ['tb-toast-panel'];
|
|
105
|
+ if (this.toastComponentRef) {
|
|
106
|
+ this.viewContainerRef.detach(0);
|
|
107
|
+ this.toastComponentRef.destroy();
|
|
108
|
+ }
|
|
109
|
+ let panelClass = ['tb-toast-panel', 'toast-panel'];
|
110
|
110
|
if (notificationMessage.panelClass) {
|
111
|
111
|
if (typeof notificationMessage.panelClass === 'string') {
|
112
|
112
|
panelClass.push(notificationMessage.panelClass);
|
...
|
...
|
@@ -114,46 +114,35 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
114
|
114
|
panelClass = panelClass.concat(notificationMessage.panelClass);
|
115
|
115
|
}
|
116
|
116
|
}
|
117
|
|
- const overlayConfig = new OverlayConfig({
|
118
|
|
- panelClass,
|
119
|
|
- backdropClass: 'cdk-overlay-transparent-backdrop',
|
120
|
|
- hasBackdrop: false,
|
121
|
|
- disposeOnNavigation: true
|
122
|
|
- });
|
123
|
|
- let originX;
|
124
|
|
- let originY;
|
125
|
117
|
const horizontalPosition = notificationMessage.horizontalPosition || 'left';
|
126
|
118
|
const verticalPosition = notificationMessage.verticalPosition || 'top';
|
127
|
119
|
if (horizontalPosition === 'start' || horizontalPosition === 'left') {
|
128
|
|
- originX = 'start';
|
|
120
|
+ panelClass.push('left');
|
129
|
121
|
} else if (horizontalPosition === 'end' || horizontalPosition === 'right') {
|
130
|
|
- originX = 'end';
|
|
122
|
+ panelClass.push('right');
|
131
|
123
|
} else {
|
132
|
|
- originX = 'center';
|
|
124
|
+ panelClass.push('h-center');
|
133
|
125
|
}
|
134
|
126
|
if (verticalPosition === 'top') {
|
135
|
|
- originY = 'top';
|
|
127
|
+ panelClass.push('top');
|
136
|
128
|
} else {
|
137
|
|
- originY = 'bottom';
|
|
129
|
+ panelClass.push('bottom');
|
138
|
130
|
}
|
139
|
|
- const connectedPosition: ConnectedPosition = {
|
140
|
|
- originX,
|
141
|
|
- originY,
|
142
|
|
- overlayX: originX,
|
143
|
|
- overlayY: originY
|
144
|
|
- };
|
145
|
|
- overlayConfig.positionStrategy = position.flexibleConnectedTo(this.elementRef)
|
146
|
|
- .withPositions([connectedPosition]);
|
147
|
|
- this.overlayRef = this.overlay.create(overlayConfig);
|
|
131
|
+
|
|
132
|
+ const componentFactory = this.componentFactoryResolver.resolveComponentFactory(TbSnackBarComponent);
|
148
|
133
|
const data: ToastPanelData = {
|
149
|
|
- notification: notificationMessage
|
|
134
|
+ notification: notificationMessage,
|
|
135
|
+ panelClass,
|
|
136
|
+ destroyToastComponent: () => {
|
|
137
|
+ this.viewContainerRef.detach(0);
|
|
138
|
+ this.toastComponentRef.destroy();
|
|
139
|
+ }
|
150
|
140
|
};
|
151
|
141
|
const injectionTokens = new WeakMap<any, any>([
|
152
|
|
- [MAT_SNACK_BAR_DATA, data],
|
153
|
|
- [OverlayRef, this.overlayRef]
|
|
142
|
+ [MAT_SNACK_BAR_DATA, data]
|
154
|
143
|
]);
|
155
|
144
|
const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens);
|
156
|
|
- this.toastComponentRef = this.overlayRef.attach(new ComponentPortal(TbSnackBarComponent, this.viewContainerRef, injector));
|
|
145
|
+ this.toastComponentRef = this.viewContainerRef.createComponent(componentFactory, 0, injector);
|
157
|
146
|
this.cd.detectChanges();
|
158
|
147
|
|
159
|
148
|
if (notificationMessage.duration && notificationMessage.duration > 0) {
|
...
|
...
|
@@ -173,7 +162,6 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
173
|
162
|
clearTimeout(this.dismissTimeout);
|
174
|
163
|
this.dismissTimeout = null;
|
175
|
164
|
}
|
176
|
|
- this.overlayRef = null;
|
177
|
165
|
this.toastComponentRef = null;
|
178
|
166
|
this.currentMessage = null;
|
179
|
167
|
});
|
...
|
...
|
@@ -181,43 +169,45 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
181
|
169
|
}
|
182
|
170
|
|
183
|
171
|
private showSnackBar(notificationMessage: NotificationMessage, isGtSm: boolean) {
|
184
|
|
- const data: ToastPanelData = {
|
185
|
|
- notification: notificationMessage,
|
186
|
|
- parent: this.elementRef
|
187
|
|
- };
|
188
|
|
- const config: MatSnackBarConfig = {
|
189
|
|
- horizontalPosition: notificationMessage.horizontalPosition || 'left',
|
190
|
|
- verticalPosition: !isGtSm ? 'bottom' : (notificationMessage.verticalPosition || 'top'),
|
191
|
|
- viewContainerRef: this.viewContainerRef,
|
192
|
|
- duration: notificationMessage.duration,
|
193
|
|
- panelClass: notificationMessage.panelClass,
|
194
|
|
- data
|
195
|
|
- };
|
196
|
172
|
this.ngZone.run(() => {
|
197
|
173
|
if (this.snackBarRef) {
|
198
|
174
|
this.snackBarRef.dismiss();
|
199
|
175
|
}
|
|
176
|
+ const data: ToastPanelData = {
|
|
177
|
+ notification: notificationMessage,
|
|
178
|
+ parent: this.elementRef,
|
|
179
|
+ panelClass: [],
|
|
180
|
+ destroyToastComponent: () => {}
|
|
181
|
+ };
|
|
182
|
+ const config: MatSnackBarConfig = {
|
|
183
|
+ horizontalPosition: notificationMessage.horizontalPosition || 'left',
|
|
184
|
+ verticalPosition: !isGtSm ? 'bottom' : (notificationMessage.verticalPosition || 'top'),
|
|
185
|
+ viewContainerRef: this.viewContainerRef,
|
|
186
|
+ duration: notificationMessage.duration,
|
|
187
|
+ panelClass: notificationMessage.panelClass,
|
|
188
|
+ data
|
|
189
|
+ };
|
200
|
190
|
this.snackBarRef = this.snackBar.openFromComponent(TbSnackBarComponent, config);
|
201
|
|
- });
|
202
|
|
- if (notificationMessage.duration && notificationMessage.duration > 0 && notificationMessage.forceDismiss) {
|
203
|
|
- if (this.dismissTimeout !== null) {
|
204
|
|
- clearTimeout(this.dismissTimeout);
|
205
|
|
- this.dismissTimeout = null;
|
206
|
|
- }
|
207
|
|
- this.dismissTimeout = setTimeout(() => {
|
208
|
|
- if (this.snackBarRef) {
|
209
|
|
- this.snackBarRef.instance.actionButton._elementRef.nativeElement.click();
|
|
191
|
+ if (notificationMessage.duration && notificationMessage.duration > 0 && notificationMessage.forceDismiss) {
|
|
192
|
+ if (this.dismissTimeout !== null) {
|
|
193
|
+ clearTimeout(this.dismissTimeout);
|
|
194
|
+ this.dismissTimeout = null;
|
210
|
195
|
}
|
211
|
|
- this.dismissTimeout = null;
|
212
|
|
- }, notificationMessage.duration);
|
213
|
|
- }
|
214
|
|
- this.snackBarRef.afterDismissed().subscribe(() => {
|
215
|
|
- if (this.dismissTimeout !== null) {
|
216
|
|
- clearTimeout(this.dismissTimeout);
|
217
|
|
- this.dismissTimeout = null;
|
|
196
|
+ this.dismissTimeout = setTimeout(() => {
|
|
197
|
+ if (this.snackBarRef) {
|
|
198
|
+ this.snackBarRef.instance.actionButton._elementRef.nativeElement.click();
|
|
199
|
+ }
|
|
200
|
+ this.dismissTimeout = null;
|
|
201
|
+ }, notificationMessage.duration);
|
218
|
202
|
}
|
219
|
|
- this.snackBarRef = null;
|
220
|
|
- this.currentMessage = null;
|
|
203
|
+ this.snackBarRef.afterDismissed().subscribe(() => {
|
|
204
|
+ if (this.dismissTimeout !== null) {
|
|
205
|
+ clearTimeout(this.dismissTimeout);
|
|
206
|
+ this.dismissTimeout = null;
|
|
207
|
+ }
|
|
208
|
+ this.snackBarRef = null;
|
|
209
|
+ this.currentMessage = null;
|
|
210
|
+ });
|
221
|
211
|
});
|
222
|
212
|
}
|
223
|
213
|
|
...
|
...
|
@@ -235,8 +225,9 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
235
|
225
|
}
|
236
|
226
|
|
237
|
227
|
ngOnDestroy(): void {
|
238
|
|
- if (this.overlayRef) {
|
239
|
|
- this.overlayRef.dispose();
|
|
228
|
+ if (this.toastComponentRef) {
|
|
229
|
+ this.viewContainerRef.detach(0);
|
|
230
|
+ this.toastComponentRef.destroy();
|
240
|
231
|
}
|
241
|
232
|
if (this.notificationSubscription) {
|
242
|
233
|
this.notificationSubscription.unsubscribe();
|
...
|
...
|
@@ -250,6 +241,8 @@ export class ToastDirective implements AfterViewInit, OnDestroy { |
250
|
241
|
interface ToastPanelData {
|
251
|
242
|
notification: NotificationMessage;
|
252
|
243
|
parent?: ElementRef;
|
|
244
|
+ panelClass: string[];
|
|
245
|
+ destroyToastComponent: () => void;
|
253
|
246
|
}
|
254
|
247
|
|
255
|
248
|
import {
|
...
|
...
|
@@ -288,6 +281,11 @@ export class TbSnackBarComponent implements AfterViewInit, OnDestroy { |
288
|
281
|
|
289
|
282
|
@ViewChild('actionButton', {static: true}) actionButton: MatButton;
|
290
|
283
|
|
|
284
|
+ @HostBinding('class')
|
|
285
|
+ get panelClass(): string[] {
|
|
286
|
+ return this.data.panelClass;
|
|
287
|
+ }
|
|
288
|
+
|
291
|
289
|
private parentEl: HTMLElement;
|
292
|
290
|
private snackBarContainerEl: HTMLElement;
|
293
|
291
|
private parentScrollSubscription: Subscription = null;
|
...
|
...
|
@@ -305,9 +303,7 @@ export class TbSnackBarComponent implements AfterViewInit, OnDestroy { |
305
|
303
|
private data: ToastPanelData,
|
306
|
304
|
private elementRef: ElementRef,
|
307
|
305
|
@Optional()
|
308
|
|
- private snackBarRef: MatSnackBarRef<TbSnackBarComponent>,
|
309
|
|
- @Optional()
|
310
|
|
- private overlayRef: OverlayRef) {
|
|
306
|
+ private snackBarRef: MatSnackBarRef<TbSnackBarComponent>) {
|
311
|
307
|
this.animationState = !!this.snackBarRef ? 'default' : 'opened';
|
312
|
308
|
this.notification = data.notification;
|
313
|
309
|
}
|
...
|
...
|
@@ -319,9 +315,8 @@ export class TbSnackBarComponent implements AfterViewInit, OnDestroy { |
319
|
315
|
this.snackBarContainerEl.style.position = 'absolute';
|
320
|
316
|
this.updateContainerRect();
|
321
|
317
|
this.updatePosition(this.snackBarRef.containerInstance.snackBarConfig);
|
322
|
|
- const snackBarComponent = this;
|
323
|
318
|
this.parentScrollSubscription = onParentScrollOrWindowResize(this.parentEl).subscribe(() => {
|
324
|
|
- snackBarComponent.updateContainerRect();
|
|
319
|
+ this.updateContainerRect();
|
325
|
320
|
});
|
326
|
321
|
}
|
327
|
322
|
}
|
...
|
...
|
@@ -373,7 +368,7 @@ export class TbSnackBarComponent implements AfterViewInit, OnDestroy { |
373
|
368
|
const isFadeOut = (toState as ToastAnimationState) === 'closing';
|
374
|
369
|
const itFinished = this.animationState === 'closing';
|
375
|
370
|
if (isFadeOut && itFinished) {
|
376
|
|
- this.overlayRef.dispose();
|
|
371
|
+ this.data.destroyToastComponent();
|
377
|
372
|
}
|
378
|
373
|
}
|
379
|
374
|
} |
...
|
...
|
|