33 ComponentPortal ,
44 DomPortalOutlet ,
55 Portal ,
6- PortalModule
6+ PortalModule ,
77} from '@angular/cdk/portal' ;
88import { HttpClient , HttpErrorResponse } from '@angular/common/http' ;
99import { DomSanitizer } from '@angular/platform-browser' ;
@@ -21,13 +21,14 @@ import {
2121 Output ,
2222 SecurityContext ,
2323 ViewContainerRef ,
24- input
24+ input ,
2525} from '@angular/core' ;
2626import { Observable , Subscription } from 'rxjs' ;
2727import { shareReplay , take , tap } from 'rxjs/operators' ;
2828import { ExampleViewer } from '../example-viewer/example-viewer' ;
2929import { HeaderLink } from './header-link' ;
3030import { DeprecatedFieldComponent } from './deprecated-tooltip' ;
31+ import { ModuleImportCopyButton } from './module-import-copy-button' ;
3132
3233@Injectable ( { providedIn : 'root' } )
3334class DocFetcher {
@@ -41,7 +42,7 @@ class DocFetcher {
4142 }
4243
4344 const stream = this . _http . get ( url , { responseType : 'text' } ) . pipe ( shareReplay ( 1 ) ) ;
44- return stream . pipe ( tap ( ( ) => this . _cache [ url ] = stream ) ) ;
45+ return stream . pipe ( tap ( ( ) => ( this . _cache [ url ] = stream ) ) ) ;
4546 }
4647}
4748
@@ -55,7 +56,7 @@ class DocFetcher {
5556 }
5657 ` ,
5758 standalone : true ,
58- imports : [ PortalModule ]
59+ imports : [ PortalModule ] ,
5960} )
6061export class DocViewer implements OnDestroy {
6162 private _portalHosts : DomPortalOutlet [ ] = [ ] ;
@@ -86,10 +87,12 @@ export class DocViewer implements OnDestroy {
8687 /** The document text. It should not be HTML encoded. */
8788 textContent = '' ;
8889
89- private static initExampleViewer ( exampleViewerComponent : ExampleViewer ,
90- example : string ,
91- file : string | null ,
92- region : string | null ) {
90+ private static initExampleViewer (
91+ exampleViewerComponent : ExampleViewer ,
92+ example : string ,
93+ file : string | null ,
94+ region : string | null ,
95+ ) {
9396 exampleViewerComponent . example = example ;
9497 if ( file ) {
9598 // if the html div has field `file` then it should be in compact view to show the code
@@ -106,25 +109,25 @@ export class DocViewer implements OnDestroy {
106109 // otherwise it is an embedded demo
107110 exampleViewerComponent . view = 'demo' ;
108111 }
109-
110112 }
111113
112- constructor ( private _appRef : ApplicationRef ,
113- private _componentFactoryResolver : ComponentFactoryResolver ,
114- public _elementRef : ElementRef ,
115- private _injector : Injector ,
116- private _viewContainerRef : ViewContainerRef ,
117- private _ngZone : NgZone ,
118- private _domSanitizer : DomSanitizer ,
119- private _docFetcher : DocFetcher ) {
120- }
114+ constructor (
115+ private _appRef : ApplicationRef ,
116+ private _componentFactoryResolver : ComponentFactoryResolver ,
117+ public _elementRef : ElementRef ,
118+ private _injector : Injector ,
119+ private _viewContainerRef : ViewContainerRef ,
120+ private _ngZone : NgZone ,
121+ private _domSanitizer : DomSanitizer ,
122+ private _docFetcher : DocFetcher ,
123+ ) { }
121124
122125 /** Fetch a document by URL. */
123126 private _fetchDocument ( url : string ) {
124127 this . _documentFetchSubscription ?. unsubscribe ( ) ;
125128 this . _documentFetchSubscription = this . _docFetcher . fetchDocument ( url ) . subscribe (
126129 document => this . updateDocument ( document ) ,
127- error => this . showError ( url , error )
130+ error => this . showError ( url , error ) ,
128131 ) ;
129132 }
130133
@@ -148,6 +151,9 @@ export class DocViewer implements OnDestroy {
148151 // Create tooltips for the deprecated fields
149152 this . _createTooltipsForDeprecated ( ) ;
150153
154+ // Create icon buttons to copy module import
155+ this . _createCopyIconForModule ( ) ;
156+
151157 // Resolving and creating components dynamically in Angular happens synchronously, but since
152158 // we want to emit the output if the components are actually rendered completely, we wait
153159 // until the Angular zone becomes stable.
@@ -159,21 +165,23 @@ export class DocViewer implements OnDestroy {
159165 /** Show an error that occurred when fetching a document. */
160166 private showError ( url : string , error : HttpErrorResponse ) {
161167 console . error ( error ) ;
162- this . _elementRef . nativeElement . innerText =
163- `Failed to load document: ${ url } . Error: ${ error . statusText } ` ;
168+ this . _elementRef . nativeElement . innerText = `Failed to load document: ${ url } . Error: ${ error . statusText } ` ;
164169 }
165170
166171 /** Instantiate a ExampleViewer for each example. */
167172 private _loadComponents ( componentName : string , componentClass : any ) {
168- const exampleElements =
169- this . _elementRef . nativeElement . querySelectorAll ( `[${ componentName } ]` ) ;
173+ const exampleElements = this . _elementRef . nativeElement . querySelectorAll ( `[${ componentName } ]` ) ;
170174
171175 [ ...exampleElements ] . forEach ( ( element : Element ) => {
172176 const example = element . getAttribute ( componentName ) ;
173177 const region = element . getAttribute ( 'region' ) ;
174178 const file = element . getAttribute ( 'file' ) ;
175179 const portalHost = new DomPortalOutlet (
176- element , this . _componentFactoryResolver , this . _appRef , this . _injector ) ;
180+ element ,
181+ this . _componentFactoryResolver ,
182+ this . _appRef ,
183+ this . _injector ,
184+ ) ;
177185 const examplePortal = new ComponentPortal ( componentClass , this . _viewContainerRef ) ;
178186 const exampleViewer = portalHost . attach ( examplePortal ) ;
179187 const exampleViewerComponent = exampleViewer . instance as ExampleViewer ;
@@ -195,36 +203,71 @@ export class DocViewer implements OnDestroy {
195203 }
196204
197205 _createTooltipsForDeprecated ( ) {
198- // all of the deprecated symbols end with `deprecated-marker`
206+ // all of the deprecated symbols end with `deprecated-marker`
199207 // class name on their element.
200- // for example:
201- // <div class="docs-api-deprecated-marker">Deprecated</div>,
208+ // for example:
209+ // <div class="docs-api-deprecated-marker">Deprecated</div>,
202210 // these can vary for each deprecated symbols such for class, interface,
203211 // type alias, constants or properties:
204212 // .docs-api-class-interface-marker, docs-api-type-alias-deprecated-marker
205213 // .docs-api-constant-deprecated-marker, .some-more
206214 // so instead of manually writing each deprecated class, we just query
207215 // elements that ends with `deprecated-marker` in their class name.
208- const deprecatedElements =
209- this . _elementRef . nativeElement . querySelectorAll ( `[class$=deprecated-marker]` ) ;
216+ const deprecatedElements = this . _elementRef . nativeElement . querySelectorAll (
217+ `[class$=deprecated-marker]` ,
218+ ) ;
210219
211220 [ ...deprecatedElements ] . forEach ( ( element : Element ) => {
212221 // the deprecation message, it will include alternative to deprecated item
213222 // and breaking change if there is one included.
214223 const deprecationTitle = element . getAttribute ( 'deprecated-message' ) ;
215224
216225 const elementPortalOutlet = new DomPortalOutlet (
217- element , this . _componentFactoryResolver , this . _appRef , this . _injector ) ;
226+ element ,
227+ this . _componentFactoryResolver ,
228+ this . _appRef ,
229+ this . _injector ,
230+ ) ;
218231
219232 const tooltipPortal = new ComponentPortal ( DeprecatedFieldComponent , this . _viewContainerRef ) ;
220233 const tooltipOutlet = elementPortalOutlet . attach ( tooltipPortal ) ;
221234
222-
223235 if ( deprecationTitle ) {
224236 tooltipOutlet . instance . message = deprecationTitle ;
225237 }
226238
227239 this . _portalHosts . push ( elementPortalOutlet ) ;
228240 } ) ;
229241 }
242+
243+ _createCopyIconForModule ( ) {
244+ // every module import element will be marked with docs-api-module-import-button attribute
245+ const moduleImportElements = this . _elementRef . nativeElement . querySelectorAll (
246+ '[data-docs-api-module-import-button]' ,
247+ ) ;
248+
249+ [ ...moduleImportElements ] . forEach ( ( element : HTMLElement ) => {
250+ // get the module import path stored in the attribute
251+ const moduleImport = element . getAttribute ( 'data-docs-api-module-import-button' ) ;
252+
253+ const elementPortalOutlet = new DomPortalOutlet (
254+ element ,
255+ this . _componentFactoryResolver ,
256+ this . _appRef ,
257+ this . _injector ,
258+ ) ;
259+
260+ const moduleImportPortal = new ComponentPortal (
261+ ModuleImportCopyButton ,
262+ this . _viewContainerRef ,
263+ ) ;
264+ const moduleImportOutlet = elementPortalOutlet . attach ( moduleImportPortal ) ;
265+
266+ if ( moduleImport ) {
267+ moduleImportOutlet . instance . import = moduleImport ;
268+ }
269+
270+ this . _portalHosts . push ( elementPortalOutlet ) ;
271+ } ) ;
272+ }
230273}
0 commit comments