@@ -8,7 +8,6 @@ import type { FocusableElement } from 'tabbable';
88import { tabbable } from 'tabbable' ;
99import type { IConfirmationDialogData } from './confirmation-dialog.component' ;
1010import { ConfirmationDialogComponent } from './confirmation-dialog.component' ;
11- import { TemplateDialogComponent } from './template-dialog.component' ;
1211import { TAB_DATA_REF } from './data.ref' ;
1312import { IconComponent } from 'tableau-ui-angular/icon' ;
1413import type { StackOptions } from './stack-options' ;
@@ -24,6 +23,22 @@ export class DialogService {
2423 environmentInjector = inject ( EnvironmentInjector ) ;
2524
2625 openModal < TComponent , TData , TResult > ( component : Type < TComponent > , data : TData , args ?: IModalArgs ) : DialogRef < TResult > {
26+ return this . _openModal < TData , TResult > (
27+ ( injector : Injector ) => this . createView ( component , injector ) ,
28+ data ,
29+ args ,
30+ ) ;
31+ }
32+
33+ openTemplateModal < TContext , TResult > ( contentTemplate : TemplateRef < TContext > , contentTemplateContext : TContext , args ?: IModalArgs ) : DialogRef < TResult > {
34+ return this . _openModal < TContext , TResult > (
35+ ( injector : Injector ) => contentTemplate . createEmbeddedView ( contentTemplateContext , injector ) ,
36+ contentTemplateContext ,
37+ args ,
38+ ) ;
39+ }
40+
41+ _openModal < TData , TResult > ( getViewRef : ( injector : Injector ) => ViewRef , data : TData , args ?: IModalArgs ) : DialogRef < TResult > {
2742 const a = {
2843 width : args ?. width ?? '300px' ,
2944 height : args ?. height ?? 'fit-content' ,
@@ -56,14 +71,10 @@ export class DialogService {
5671 trapFocus : true ,
5772 } as IDialogArgs ;
5873
59- const ref = this . openDialog < TComponent , TData , TResult > ( component , data , a ) ;
74+ const ref = this . _openDialog < TData , TResult > ( getViewRef , data , a ) ;
6075 return ref ;
6176 }
6277
63- openTemplateModal < TContext , TResult > ( contentTemplate : TemplateRef < TContext > , contentTemplateContext ?: TContext , args ?: IModalArgs ) : DialogRef < TResult > {
64- return this . openModal ( TemplateDialogComponent , { contentTemplate, contentTemplateContext } , args ) ;
65- }
66-
6778 async openConfirmationMessageDialog (
6879 title : string ,
6980 message : string ,
@@ -136,14 +147,25 @@ export class DialogService {
136147 args : IDialogArgs ;
137148 } [ ] = [ ] ;
138149
139- openTemplateDialog < TContext , TResult > ( contentTemplate : TemplateRef < TContext > , args : IDialogArgs , contentTemplateContext ?: TContext , stackOptions : StackOptions = new GlobalStackOptions ( ) ) {
140- return this . openDialog <
141- TemplateDialogComponent < { contentTemplate : TemplateRef < TContext | undefined > ; contentTemplateContext : TContext | undefined } > ,
142- { contentTemplate : TemplateRef < TContext > ; contentTemplateContext ?: TContext } ,
143- TResult
144- > ( TemplateDialogComponent , { contentTemplate, contentTemplateContext } , args , stackOptions ) ;
150+ openTemplateDialog < TContext , TResult > ( contentTemplate : TemplateRef < TContext > , args : IDialogArgs , contentTemplateContext : TContext , stackOptions : StackOptions = new GlobalStackOptions ( ) ) {
151+
152+ return this . _openDialog < TContext , TResult > (
153+ ( injector : Injector ) => contentTemplate . createEmbeddedView ( contentTemplateContext , injector ) ,
154+ contentTemplateContext ,
155+ args ,
156+ stackOptions
157+ ) ;
145158 }
159+
146160 openDialog < TComponent , TData , TResult > ( component : Type < TComponent > , data : TData , args : IDialogArgs = { } , stackOptions : StackOptions = new GlobalStackOptions ( ) ) : DialogRef < TResult > {
161+ return this . _openDialog < TData , TResult > (
162+ ( injector : Injector ) => this . createView ( component , injector ) ,
163+ data ,
164+ args ,
165+ stackOptions
166+ ) ;
167+ }
168+ private _openDialog < TData , TResult > ( getViewRef : ( injector : Injector ) => ViewRef , data : TData , args : IDialogArgs = { } , stackOptions : StackOptions = new GlobalStackOptions ( ) ) : DialogRef < TResult > {
147169 let trappedFocus :
148170 | {
149171 elements : {
@@ -181,7 +203,7 @@ export class DialogService {
181203 } ) ;
182204
183205 // Create the component view
184- const componentView = this . createView ( component , injector ) ;
206+ const componentView = getViewRef ( injector ) ;
185207 // Attach component to the application
186208 this . appRef . attachView ( componentView ) ;
187209
@@ -290,9 +312,15 @@ export class DialogService {
290312 }
291313
292314 private createDialogElement ( viewRef : ViewRef , args : IDialogArgs , injector : Injector , dialogRef : IDialogRef , zIndex : number ) : HTMLElement {
293- const dialogElement = ( viewRef as EmbeddedViewRef < unknown > ) . rootNodes [ 0 ] as HTMLElement ;
315+ const embeddedViewRef = viewRef as EmbeddedViewRef < unknown > ;
316+
317+ const dialogElement = document . createElement ( 'div' ) ;
294318 dialogElement . classList . add ( 'dialog-container' ) ;
295319 dialogElement . style . zIndex = zIndex . toString ( ) ;
320+ for ( const rootNode of embeddedViewRef . rootNodes ) {
321+ dialogElement . appendChild ( rootNode as Node ) ;
322+ }
323+
296324
297325 if ( args . containerCss ) {
298326 Object . keys ( args . containerCss ) . forEach ( ( key ) => {
@@ -380,55 +408,44 @@ export class DialogService {
380408 return undefined ;
381409 }
382410 private static calculateAndSetPosition ( dialogElement : HTMLElement , args : IDialogPositionAndSizeArgs , stackOptions : StackOptions ) {
383- if ( args . maxWidth !== undefined ) {
384- dialogElement . style . maxWidth = args . maxWidth ;
385- }
386- if ( args . maxHeight !== undefined ) {
387- dialogElement . style . maxHeight = args . maxHeight ;
388- }
389- dialogElement . style . overflowX = 'hidden' ;
390- dialogElement . style . overflowY = 'hidden' ;
391-
392411 const referenceElement = this . getReferenceElement ( stackOptions ) ;
393412 const referenceElementRect = referenceElement ?. getBoundingClientRect ( ) ;
394- let widthCss : string ;
395- let heightCss : string ;
396- if ( args . width === undefined ) {
397- widthCss = 'fit-content' ;
398- } else if ( typeof args . width === 'function' ) {
399- if ( ! referenceElementRect ) {
400- throw new Error ( 'When using a function for width, insertAfterElement must be provided.' ) ;
413+ const getWidthHeight = ( name : string , defaultValue : string , value : string | ( ( referenceElementRect : DOMRect ) => string | undefined ) | undefined ) => {
414+ if ( value === undefined ) {
415+ return defaultValue ;
416+ } else if ( typeof value === 'function' ) {
417+ if ( ! referenceElementRect ) {
418+ throw new Error ( `When using a function for ${ name } , insertAfterElement must be provided.` ) ;
419+ }
420+ return value ( referenceElementRect ) ?? defaultValue ;
421+ } else {
422+ return value ;
401423 }
402- widthCss = args . width ( referenceElementRect ) ;
403- } else {
404- widthCss = args . width ;
405- }
406- if ( args . height === undefined ) {
407- heightCss = 'fit-content' ;
408- } else if ( typeof args . height === 'function' ) {
409- if ( ! referenceElementRect ) {
410- throw new Error ( 'When using a function for height, insertAfterElement must be provided.' ) ;
411- }
412- heightCss = args . height ( referenceElementRect ) ;
413- } else {
414- heightCss = args . height ;
415- }
416- dialogElement . style . height = heightCss ;
417- dialogElement . style . width = widthCss ;
424+ } ;
425+
426+ dialogElement . style . minWidth = getWidthHeight ( 'minWidth' , 'auto' , args . minWidth ) ;
427+ dialogElement . style . minHeight = getWidthHeight ( 'minHeight' , '0' , args . minHeight ) ;
428+ dialogElement . style . width = getWidthHeight ( 'width' , 'fit-content' , args . width ) ;
429+ dialogElement . style . height = getWidthHeight ( 'height' , 'fit-content' , args . height ) ;
430+ dialogElement . style . maxWidth = getWidthHeight ( 'maxWidth' , 'none' , args . maxWidth ) ;
431+ dialogElement . style . maxHeight = getWidthHeight ( 'maxHeight' , 'none' , args . maxHeight ) ;
432+
433+ dialogElement . style . overflowX = 'hidden' ;
434+ dialogElement . style . overflowY = 'hidden' ;
418435 const actualWidth = dialogElement . offsetWidth ;
419436 const actualHeight = dialogElement . offsetHeight ;
420437
421- if ( typeof args . top === 'function' ) {
422- dialogElement . style . top = args . top ( actualWidth , actualHeight , referenceElementRect ) ;
423- } else if ( typeof args . top === 'string' ) {
424- dialogElement . style . top = args . top ;
425- }
426-
427- if ( typeof args . left === 'function' ) {
428- dialogElement . style . left = args . left ( actualWidth , actualHeight , referenceElementRect ) ;
429- } else if ( typeof args . left === 'string' ) {
430- dialogElement . style . left = args . left ;
431- }
438+ const getTopLeft = ( name : string , value : string | ( ( actualWidth : number , actualHeight : number , referenceElementRect ?: DOMRect ) => string | undefined ) | undefined ) => {
439+ if ( value === undefined ) {
440+ return 'auto' ;
441+ } else if ( typeof value === 'function' ) {
442+ return value ( actualWidth , actualHeight , referenceElementRect ) ?? 'auto' ;
443+ } else {
444+ return value ;
445+ }
446+ } ;
447+ dialogElement . style . top = getTopLeft ( 'top' , args . top ) ;
448+ dialogElement . style . left = getTopLeft ( 'left' , args . left ) ;
432449
433450 // if dialog is higher than the available page height, set it to scroll
434451 if ( actualHeight + dialogElement . offsetTop > window . innerHeight ) {
0 commit comments