3
3
ComponentPortal ,
4
4
DomPortalOutlet ,
5
5
Portal ,
6
- PortalModule
6
+ PortalModule ,
7
7
} from '@angular/cdk/portal' ;
8
8
import { HttpClient , HttpErrorResponse } from '@angular/common/http' ;
9
9
import { DomSanitizer } from '@angular/platform-browser' ;
@@ -21,13 +21,14 @@ import {
21
21
Output ,
22
22
SecurityContext ,
23
23
ViewContainerRef ,
24
- input
24
+ input ,
25
25
} from '@angular/core' ;
26
26
import { Observable , Subscription } from 'rxjs' ;
27
27
import { shareReplay , take , tap } from 'rxjs/operators' ;
28
28
import { ExampleViewer } from '../example-viewer/example-viewer' ;
29
29
import { HeaderLink } from './header-link' ;
30
30
import { DeprecatedFieldComponent } from './deprecated-tooltip' ;
31
+ import { ModuleImportCopyButton } from './module-import-copy-button' ;
31
32
32
33
@Injectable ( { providedIn : 'root' } )
33
34
class DocFetcher {
@@ -41,7 +42,7 @@ class DocFetcher {
41
42
}
42
43
43
44
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 ) ) ) ;
45
46
}
46
47
}
47
48
@@ -55,7 +56,7 @@ class DocFetcher {
55
56
}
56
57
` ,
57
58
standalone : true ,
58
- imports : [ PortalModule ]
59
+ imports : [ PortalModule ] ,
59
60
} )
60
61
export class DocViewer implements OnDestroy {
61
62
private _portalHosts : DomPortalOutlet [ ] = [ ] ;
@@ -86,10 +87,12 @@ export class DocViewer implements OnDestroy {
86
87
/** The document text. It should not be HTML encoded. */
87
88
textContent = '' ;
88
89
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
+ ) {
93
96
exampleViewerComponent . example = example ;
94
97
if ( file ) {
95
98
// 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 {
106
109
// otherwise it is an embedded demo
107
110
exampleViewerComponent . view = 'demo' ;
108
111
}
109
-
110
112
}
111
113
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
+ ) { }
121
124
122
125
/** Fetch a document by URL. */
123
126
private _fetchDocument ( url : string ) {
124
127
this . _documentFetchSubscription ?. unsubscribe ( ) ;
125
128
this . _documentFetchSubscription = this . _docFetcher . fetchDocument ( url ) . subscribe (
126
129
document => this . updateDocument ( document ) ,
127
- error => this . showError ( url , error )
130
+ error => this . showError ( url , error ) ,
128
131
) ;
129
132
}
130
133
@@ -148,6 +151,9 @@ export class DocViewer implements OnDestroy {
148
151
// Create tooltips for the deprecated fields
149
152
this . _createTooltipsForDeprecated ( ) ;
150
153
154
+ // Create icon buttons to copy module import
155
+ this . _createCopyIconForModule ( ) ;
156
+
151
157
// Resolving and creating components dynamically in Angular happens synchronously, but since
152
158
// we want to emit the output if the components are actually rendered completely, we wait
153
159
// until the Angular zone becomes stable.
@@ -159,21 +165,23 @@ export class DocViewer implements OnDestroy {
159
165
/** Show an error that occurred when fetching a document. */
160
166
private showError ( url : string , error : HttpErrorResponse ) {
161
167
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 } ` ;
164
169
}
165
170
166
171
/** Instantiate a ExampleViewer for each example. */
167
172
private _loadComponents ( componentName : string , componentClass : any ) {
168
- const exampleElements =
169
- this . _elementRef . nativeElement . querySelectorAll ( `[${ componentName } ]` ) ;
173
+ const exampleElements = this . _elementRef . nativeElement . querySelectorAll ( `[${ componentName } ]` ) ;
170
174
171
175
[ ...exampleElements ] . forEach ( ( element : Element ) => {
172
176
const example = element . getAttribute ( componentName ) ;
173
177
const region = element . getAttribute ( 'region' ) ;
174
178
const file = element . getAttribute ( 'file' ) ;
175
179
const portalHost = new DomPortalOutlet (
176
- element , this . _componentFactoryResolver , this . _appRef , this . _injector ) ;
180
+ element ,
181
+ this . _componentFactoryResolver ,
182
+ this . _appRef ,
183
+ this . _injector ,
184
+ ) ;
177
185
const examplePortal = new ComponentPortal ( componentClass , this . _viewContainerRef ) ;
178
186
const exampleViewer = portalHost . attach ( examplePortal ) ;
179
187
const exampleViewerComponent = exampleViewer . instance as ExampleViewer ;
@@ -195,36 +203,71 @@ export class DocViewer implements OnDestroy {
195
203
}
196
204
197
205
_createTooltipsForDeprecated ( ) {
198
- // all of the deprecated symbols end with `deprecated-marker`
206
+ // all of the deprecated symbols end with `deprecated-marker`
199
207
// 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>,
202
210
// these can vary for each deprecated symbols such for class, interface,
203
211
// type alias, constants or properties:
204
212
// .docs-api-class-interface-marker, docs-api-type-alias-deprecated-marker
205
213
// .docs-api-constant-deprecated-marker, .some-more
206
214
// so instead of manually writing each deprecated class, we just query
207
215
// 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
+ ) ;
210
219
211
220
[ ...deprecatedElements ] . forEach ( ( element : Element ) => {
212
221
// the deprecation message, it will include alternative to deprecated item
213
222
// and breaking change if there is one included.
214
223
const deprecationTitle = element . getAttribute ( 'deprecated-message' ) ;
215
224
216
225
const elementPortalOutlet = new DomPortalOutlet (
217
- element , this . _componentFactoryResolver , this . _appRef , this . _injector ) ;
226
+ element ,
227
+ this . _componentFactoryResolver ,
228
+ this . _appRef ,
229
+ this . _injector ,
230
+ ) ;
218
231
219
232
const tooltipPortal = new ComponentPortal ( DeprecatedFieldComponent , this . _viewContainerRef ) ;
220
233
const tooltipOutlet = elementPortalOutlet . attach ( tooltipPortal ) ;
221
234
222
-
223
235
if ( deprecationTitle ) {
224
236
tooltipOutlet . instance . message = deprecationTitle ;
225
237
}
226
238
227
239
this . _portalHosts . push ( elementPortalOutlet ) ;
228
240
} ) ;
229
241
}
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
+ }
230
273
}
0 commit comments