Showing
15 changed files
with
348 additions
and
10 deletions
... | ... | @@ -36,6 +36,9 @@ import { DatasourceService } from '@core/api/datasource.service'; |
36 | 36 | import { RafService } from '@core/services/raf.service'; |
37 | 37 | import { EntityAliases } from '@shared/models/alias.models'; |
38 | 38 | import { EntityInfo } from '@app/shared/models/entity.models'; |
39 | +import { Type } from '@angular/core'; | |
40 | +import { AssetService } from '@core/http/asset.service'; | |
41 | +import { DialogService } from '@core/services/dialog.service'; | |
39 | 42 | |
40 | 43 | export interface TimewindowFunctions { |
41 | 44 | onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; | ... | ... |
... | ... | @@ -30,6 +30,7 @@ import { |
30 | 30 | MaterialIconsDialogComponent, |
31 | 31 | MaterialIconsDialogData |
32 | 32 | } from '@shared/components/dialog/material-icons-dialog.component'; |
33 | +import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service'; | |
33 | 34 | |
34 | 35 | @Injectable( |
35 | 36 | { |
... | ... | @@ -41,6 +42,7 @@ export class DialogService { |
41 | 42 | constructor( |
42 | 43 | private translate: TranslateService, |
43 | 44 | private authService: AuthService, |
45 | + private dynamicComponentFactoryService: DynamicComponentFactoryService, | |
44 | 46 | public dialog: MatDialog |
45 | 47 | ) { |
46 | 48 | } | ... | ... |
... | ... | @@ -56,6 +56,8 @@ import { ManageWidgetActionsComponent } from './widget/action/manage-widget-acti |
56 | 56 | import { WidgetActionDialogComponent } from './widget/action/widget-action-dialog.component'; |
57 | 57 | import { CustomActionPrettyResourcesTabsComponent } from './widget/action/custom-action-pretty-resources-tabs.component'; |
58 | 58 | import { CustomActionPrettyEditorComponent } from './widget/action/custom-action-pretty-editor.component'; |
59 | +import { CustomDialogService } from './widget/dialog/custom-dialog.service'; | |
60 | +import { CustomDialogContainerComponent } from './widget/dialog/custom-dialog-container.component'; | |
59 | 61 | |
60 | 62 | @NgModule({ |
61 | 63 | entryComponents: [ |
... | ... | @@ -72,7 +74,8 @@ import { CustomActionPrettyEditorComponent } from './widget/action/custom-action |
72 | 74 | EntityAliasDialogComponent, |
73 | 75 | DataKeyConfigDialogComponent, |
74 | 76 | LegendConfigPanelComponent, |
75 | - WidgetActionDialogComponent | |
77 | + WidgetActionDialogComponent, | |
78 | + CustomDialogContainerComponent | |
76 | 79 | ], |
77 | 80 | declarations: |
78 | 81 | [ |
... | ... | @@ -113,7 +116,8 @@ import { CustomActionPrettyEditorComponent } from './widget/action/custom-action |
113 | 116 | ManageWidgetActionsComponent, |
114 | 117 | WidgetActionDialogComponent, |
115 | 118 | CustomActionPrettyResourcesTabsComponent, |
116 | - CustomActionPrettyEditorComponent | |
119 | + CustomActionPrettyEditorComponent, | |
120 | + CustomDialogContainerComponent | |
117 | 121 | ], |
118 | 122 | imports: [ |
119 | 123 | CommonModule, |
... | ... | @@ -149,10 +153,12 @@ import { CustomActionPrettyEditorComponent } from './widget/action/custom-action |
149 | 153 | ManageWidgetActionsComponent, |
150 | 154 | WidgetActionDialogComponent, |
151 | 155 | CustomActionPrettyResourcesTabsComponent, |
152 | - CustomActionPrettyEditorComponent | |
156 | + CustomActionPrettyEditorComponent, | |
157 | + CustomDialogContainerComponent | |
153 | 158 | ], |
154 | 159 | providers: [ |
155 | - WidgetComponentService | |
160 | + WidgetComponentService, | |
161 | + CustomDialogService | |
156 | 162 | ] |
157 | 163 | }) |
158 | 164 | export class HomeComponentsModule { } | ... | ... |
1 | +/*================================================================================*/ | |
2 | +/*======================= New TB 3.0 / Angular 8 Example =======================*/ | |
3 | +/*================================================================================*/ | |
4 | +/* | |
5 | +.edit-entity-form mat-form-field { | |
6 | + padding-right: 10px; | |
7 | +} | |
8 | +*/ | |
1 | 9 | /*=======================================================================*/ |
2 | 10 | /*========== There are two examples: for edit and add entity ==========*/ |
3 | 11 | /*=======================================================================*/ | ... | ... |
1 | 1 | <!--=======================================================================--> |
2 | +<!--=================== New TB 3.0 / Angular 8 Example =================--> | |
3 | +<!--=======================================================================--> | |
4 | +<!--<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"--> | |
5 | +<!-- class="edit-entity-form"--> | |
6 | +<!-- (ngSubmit)="save()" style="width: 600px;">--> | |
7 | +<!-- <mat-toolbar fxLayout="row" color="primary">--> | |
8 | +<!-- <h2>Edit {{entityType.toLowerCase()}} {{entityName}}</h2>--> | |
9 | +<!-- <span fxFlex></span>--> | |
10 | +<!-- <button mat-button mat-icon-button--> | |
11 | +<!-- (click)="cancel()"--> | |
12 | +<!-- type="button">--> | |
13 | +<!-- <mat-icon class="material-icons">close</mat-icon>--> | |
14 | +<!-- </button>--> | |
15 | +<!-- </mat-toolbar>--> | |
16 | +<!-- <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">--> | |
17 | +<!-- </mat-progress-bar>--> | |
18 | +<!-- <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>--> | |
19 | +<!-- <div mat-dialog-content>--> | |
20 | +<!-- <div class="mat-padding" fxLayout="column">--> | |
21 | +<!-- <mat-form-field class="mat-block">--> | |
22 | +<!-- <mat-label>Entity name</mat-label>--> | |
23 | +<!-- <input matInput formControlName="entityName" required>--> | |
24 | +<!-- <mat-error *ngIf="editEntityFormGroup.get('entityName').hasError('required')">--> | |
25 | +<!-- Entity name required--> | |
26 | +<!-- </mat-error>--> | |
27 | +<!-- </mat-form-field>--> | |
28 | +<!-- </div> --> | |
29 | +<!-- </div>--> | |
30 | +<!-- <div mat-dialog-actions fxLayout="row">--> | |
31 | +<!-- <span fxFlex></span>--> | |
32 | +<!-- <button mat-button mat-raised-button color="primary"--> | |
33 | +<!-- type="submit"--> | |
34 | +<!-- [disabled]="(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty">--> | |
35 | +<!-- {{ 'action.save' | translate }}--> | |
36 | +<!-- </button>--> | |
37 | +<!-- <button mat-button color="primary"--> | |
38 | +<!-- style="margin-right: 20px;"--> | |
39 | +<!-- type="button"--> | |
40 | +<!-- [disabled]="(isLoading$ | async)"--> | |
41 | +<!-- (click)="cancel()" cdkFocusInitial>--> | |
42 | +<!-- {{ 'action.cancel' | translate }}--> | |
43 | +<!-- </button>--> | |
44 | +<!-- </div>--> | |
45 | +<!--</form>--> | |
46 | +<!--=======================================================================--> | |
2 | 47 | <!--===== There are two example templates: for edit and add entity =====--> |
3 | 48 | <!--=======================================================================--> |
4 | 49 | <!--======================== Edit entity example ========================--> | ... | ... |
1 | +/*================================================================================*/ | |
2 | +/*======================= New TB 3.0 / Angular 8 Example =======================*/ | |
3 | +/*================================================================================*/ | |
4 | +// | |
5 | +//let $injector = widgetContext.$scope.$injector; | |
6 | +//let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService')); | |
7 | +// | |
8 | +//deviceService.getDevice(entityId.id).subscribe(function(device) { | |
9 | +// console.log(device); | |
10 | +//}); | |
11 | +// | |
12 | +// | |
13 | +//let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog')); | |
14 | +// | |
15 | +//customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe( | |
16 | +// function(res) { | |
17 | +// console.log(res); | |
18 | +// } | |
19 | +//); | |
20 | +// | |
21 | +//function EditEntityDialogController(instance) { | |
22 | +// let vm = instance; | |
23 | +// vm.entityId = entityId; | |
24 | +// vm.entityName = entityName; | |
25 | +// vm.entityType = entityId.entityType; | |
26 | +// | |
27 | +// vm.editEntityFormGroup = vm.fb.group({ | |
28 | +// entityName: [vm.entityName, [vm.Validators.required]] | |
29 | +// }); | |
30 | +// | |
31 | +// vm.cancel = function() { | |
32 | +// vm.dialogRef.close(null); | |
33 | +// }; | |
34 | +// | |
35 | +// vm.save = function() { | |
36 | +// const newVal = vm.editEntityFormGroup.value; | |
37 | +// vm.dialogRef.close(newVal); | |
38 | +// }; | |
39 | +//} | |
40 | +// | |
1 | 41 | /*=======================================================================*/ |
2 | 42 | /*===== There are three examples: for delete, edit and add entity =====*/ |
3 | 43 | /*=======================================================================*/ | ... | ... |
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts
0 → 100644
1 | +/// | |
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | |
18 | +import { | |
19 | + Inject, | |
20 | + ComponentRef, | |
21 | + ElementRef, | |
22 | + ViewContainerRef, | |
23 | + ComponentFactory, | |
24 | + Injector, | |
25 | + ReflectiveInjector, | |
26 | + Component, | |
27 | + OnDestroy | |
28 | +} from '@angular/core'; | |
29 | +import { DialogComponent } from '@shared/components/dialog.component'; | |
30 | +import { Store } from '@ngrx/store'; | |
31 | +import { AppState } from '@core/core.state'; | |
32 | +import { Router } from '@angular/router'; | |
33 | +import { | |
34 | + CustomDialogComponent, | |
35 | + CUSTOM_DIALOG_DATA, | |
36 | + CustomDialogData | |
37 | +} from '@home/components/widget/dialog/custom-dialog.component'; | |
38 | + | |
39 | +export interface CustomDialogContainerData { | |
40 | + controller: (instance: CustomDialogComponent) => void; | |
41 | + data?: any; | |
42 | + customComponentFactory: ComponentFactory<CustomDialogComponent>; | |
43 | +} | |
44 | + | |
45 | +@Component({ | |
46 | + selector: 'tb-custom-dialog-container-component', | |
47 | + template: '' | |
48 | +}) | |
49 | +export class CustomDialogContainerComponent extends DialogComponent<CustomDialogContainerComponent, any> implements OnDestroy { | |
50 | + | |
51 | + private customComponentRef: ComponentRef<CustomDialogComponent>; | |
52 | + | |
53 | + constructor(protected store: Store<AppState>, | |
54 | + protected router: Router, | |
55 | + public viewContainerRef: ViewContainerRef, | |
56 | + public dialogRef: MatDialogRef<CustomDialogContainerComponent, any>, | |
57 | + @Inject(MAT_DIALOG_DATA) public data: CustomDialogContainerData) { | |
58 | + super(store, router, dialogRef); | |
59 | + let customDialogData: CustomDialogData = { | |
60 | + controller: this.data.controller | |
61 | + }; | |
62 | + if (this.data.data) { | |
63 | + customDialogData = {...customDialogData, ...this.data.data}; | |
64 | + } | |
65 | + const injector: Injector = ReflectiveInjector.resolveAndCreate([ | |
66 | + { | |
67 | + provide: CUSTOM_DIALOG_DATA, | |
68 | + useValue: customDialogData | |
69 | + }, | |
70 | + { | |
71 | + provide: MatDialogRef, | |
72 | + useValue: dialogRef | |
73 | + } | |
74 | + ]); | |
75 | + this.customComponentRef = this.viewContainerRef.createComponent(this.data.customComponentFactory, 0, injector); | |
76 | + } | |
77 | + | |
78 | + ngOnDestroy(): void { | |
79 | + super.ngOnDestroy(); | |
80 | + if (this.customComponentRef) { | |
81 | + this.customComponentRef.destroy(); | |
82 | + } | |
83 | + } | |
84 | + | |
85 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | |
18 | +import { Inject, InjectionToken } from '@angular/core'; | |
19 | +import { DialogComponent } from '@shared/components/dialog.component'; | |
20 | +import { Store } from '@ngrx/store'; | |
21 | +import { AppState } from '@core/core.state'; | |
22 | +import { Router } from '@angular/router'; | |
23 | +import { PageComponent } from '@shared/components/page.component'; | |
24 | +import { CustomDialogContainerComponent } from './custom-dialog-container.component'; | |
25 | +import { FormBuilder, Validators } from '@angular/forms'; | |
26 | + | |
27 | +export const CUSTOM_DIALOG_DATA = new InjectionToken<any>('ConfigDialogData'); | |
28 | + | |
29 | +export interface CustomDialogData { | |
30 | + controller: (instance: CustomDialogComponent) => void; | |
31 | + [key: string]: any; | |
32 | +} | |
33 | + | |
34 | +export class CustomDialogComponent extends PageComponent { | |
35 | + | |
36 | + [key: string]: any; | |
37 | + | |
38 | + constructor(protected store: Store<AppState>, | |
39 | + protected router: Router, | |
40 | + public dialogRef: MatDialogRef<CustomDialogContainerComponent>, | |
41 | + public fb: FormBuilder, | |
42 | + @Inject(CUSTOM_DIALOG_DATA) public data: CustomDialogData) { | |
43 | + super(store); | |
44 | + // @ts-ignore | |
45 | + this.Validators = Validators; | |
46 | + this.data.controller(this); | |
47 | + } | |
48 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Injectable, Injector, NgModule } from '@angular/core'; | |
18 | +import { Observable } from 'rxjs'; | |
19 | +import { MatDialog } from '@angular/material'; | |
20 | +import { TranslateService } from '@ngx-translate/core'; | |
21 | +import { AuthService } from '@core/auth/auth.service'; | |
22 | +import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service'; | |
23 | +import { CommonModule } from '@angular/common'; | |
24 | +import { SharedModule } from '@shared/shared.module'; | |
25 | +import { mergeMap, tap } from 'rxjs/operators'; | |
26 | +import { CustomDialogComponent } from './custom-dialog.component'; | |
27 | +import { | |
28 | + CustomDialogContainerComponent, | |
29 | + CustomDialogContainerData | |
30 | +} from '@home/components/widget/dialog/custom-dialog-container.component'; | |
31 | + | |
32 | +@Injectable() | |
33 | +export class CustomDialogService { | |
34 | + | |
35 | + constructor( | |
36 | + private translate: TranslateService, | |
37 | + private authService: AuthService, | |
38 | + private dynamicComponentFactoryService: DynamicComponentFactoryService, | |
39 | + private injector: Injector, | |
40 | + public dialog: MatDialog | |
41 | + ) { | |
42 | + } | |
43 | + | |
44 | + customDialog(template: string, controller: (instance: CustomDialogComponent) => void, data?: any): Observable<any> { | |
45 | + return this.dynamicComponentFactoryService.createDynamicComponentFactory( | |
46 | + class CustomDialogComponentInstance extends CustomDialogComponent {}, | |
47 | + template, | |
48 | + [SharedModule, CustomDialogModule]).pipe( | |
49 | + mergeMap((factory) => { | |
50 | + const dialogData: CustomDialogContainerData = { | |
51 | + controller, | |
52 | + customComponentFactory: factory, | |
53 | + data | |
54 | + }; | |
55 | + return this.dialog.open<CustomDialogContainerComponent, CustomDialogContainerData, any>( | |
56 | + CustomDialogContainerComponent, | |
57 | + { | |
58 | + disableClose: true, | |
59 | + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], | |
60 | + data: dialogData | |
61 | + }).afterClosed().pipe( | |
62 | + tap(() => { | |
63 | + this.dynamicComponentFactoryService.destroyDynamicComponentFactory(factory); | |
64 | + }) | |
65 | + ); | |
66 | + } | |
67 | + )); | |
68 | + } | |
69 | + | |
70 | +} | |
71 | + | |
72 | +@NgModule({ | |
73 | + entryComponents: [ | |
74 | + ], | |
75 | + declarations: | |
76 | + [ | |
77 | + ], | |
78 | + imports: [ | |
79 | + CommonModule, | |
80 | + SharedModule | |
81 | + ], | |
82 | + exports: [ | |
83 | + ] | |
84 | +}) | |
85 | +class CustomDialogModule { } | ... | ... |
... | ... | @@ -28,7 +28,7 @@ import { |
28 | 28 | OnChanges, |
29 | 29 | OnDestroy, |
30 | 30 | OnInit, |
31 | - SimpleChanges, | |
31 | + SimpleChanges, Type, | |
32 | 32 | ViewChild, |
33 | 33 | ViewContainerRef, |
34 | 34 | ViewEncapsulation |
... | ... | @@ -90,6 +90,15 @@ import { DashboardService } from '@core/http/dashboard.service'; |
90 | 90 | import { DatasourceService } from '@core/api/datasource.service'; |
91 | 91 | import { WidgetSubscription } from '@core/api/widget-subscription'; |
92 | 92 | import { EntityService } from '@core/http/entity.service'; |
93 | +import { AssetService } from '@core/http/asset.service'; | |
94 | +import { DialogService } from '@core/services/dialog.service'; | |
95 | +import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; | |
96 | + | |
97 | +const ServicesMap = new Map<string, Type<any>>(); | |
98 | +ServicesMap.set('deviceService', DeviceService); | |
99 | +ServicesMap.set('assetService', AssetService); | |
100 | +ServicesMap.set('dialogs', DialogService); | |
101 | +ServicesMap.set('customDialog', CustomDialogService); | |
93 | 102 | |
94 | 103 | @Component({ |
95 | 104 | selector: 'tb-widget', |
... | ... | @@ -242,6 +251,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI |
242 | 251 | } |
243 | 252 | |
244 | 253 | this.widgetContext = this.dashboardWidget.widgetContext; |
254 | + this.widgetContext.servicesMap = ServicesMap; | |
245 | 255 | this.widgetContext.inited = false; |
246 | 256 | this.widgetContext.hideTitlePanel = false; |
247 | 257 | this.widgetContext.isEdit = this.isEdit; |
... | ... | @@ -647,6 +657,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI |
647 | 657 | this.dynamicWidgetComponent.errorMessages = this.errorMessages; |
648 | 658 | |
649 | 659 | this.widgetContext.$scope = this.dynamicWidgetComponent; |
660 | + this.widgetContext.$scope.$injector = this.injector; | |
650 | 661 | |
651 | 662 | const containerElement = $(this.elementRef.nativeElement.querySelector('#widget-container')); |
652 | 663 | ... | ... |
... | ... | @@ -39,12 +39,16 @@ import { |
39 | 39 | WidgetActionsApi, |
40 | 40 | WidgetSubscriptionApi |
41 | 41 | } from '@core/api/widget-api.models'; |
42 | -import { ComponentFactory } from '@angular/core'; | |
42 | +import { ComponentFactory, Type } from '@angular/core'; | |
43 | 43 | import { HttpErrorResponse } from '@angular/common/http'; |
44 | 44 | import { RafService } from '@core/services/raf.service'; |
45 | 45 | import { WidgetTypeId } from '@shared/models/id/widget-type-id'; |
46 | 46 | import { TenantId } from '@shared/models/id/tenant-id'; |
47 | 47 | import { WidgetLayout } from '@shared/models/dashboard.models'; |
48 | +import { DeviceService } from '@core/http/device.service'; | |
49 | +import { AssetService } from '@app/core/http/asset.service'; | |
50 | +import { DialogService } from '@core/services/dialog.service'; | |
51 | +import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; | |
48 | 52 | |
49 | 53 | export interface IWidgetAction { |
50 | 54 | name: string; |
... | ... | @@ -98,6 +102,7 @@ export interface WidgetContext { |
98 | 102 | customHeaderActions?: Array<WidgetHeaderAction>; |
99 | 103 | widgetActions?: Array<WidgetAction>; |
100 | 104 | |
105 | + servicesMap?: Map<string, Type<any>>; | |
101 | 106 | } |
102 | 107 | |
103 | 108 | export interface IDynamicWidgetComponent { | ... | ... |
... | ... | @@ -92,7 +92,7 @@ export class FullscreenDirective implements OnChanges, OnDestroy { |
92 | 92 | |
93 | 93 | this.overlayRef = this.overlay.create(config); |
94 | 94 | this.overlayRef.attach(new EmptyPortal()); |
95 | - this.overlayRef.overlayElement.append( targetElement ); | |
95 | + this.overlayRef.overlayElement.appendChild( targetElement ); | |
96 | 96 | this.fullscreenChanged.emit(true); |
97 | 97 | } |
98 | 98 | |
... | ... | @@ -100,7 +100,7 @@ export class FullscreenDirective implements OnChanges, OnDestroy { |
100 | 100 | const targetElement: HTMLElement = this.fullscreenElement || this.elementRef.nativeElement; |
101 | 101 | if (this.parentElement) { |
102 | 102 | this.overlayRef.overlayElement.removeChild( targetElement ); |
103 | - this.parentElement.append(targetElement); | |
103 | + this.parentElement.appendChild(targetElement); | |
104 | 104 | this.parentElement = null; |
105 | 105 | } |
106 | 106 | targetElement.classList.remove('tb-fullscreen'); | ... | ... |