@@ -9,12 +9,20 @@ import { ExperimentManager } from 'traceviewer-base/lib/experiment-manager';
9
9
import { FilterTree } from '../components/utils/filter-tree/tree' ;
10
10
import { TreeNode } from '../components/utils/filter-tree/tree-node' ;
11
11
import { getAllExpandedNodeIds } from '../components/utils/filter-tree/utils' ;
12
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
13
+ import { faPlus , faTimes } from '@fortawesome/free-solid-svg-icons' ;
12
14
13
15
export interface ReactAvailableViewsProps {
14
16
id : string ;
15
17
title : string ;
16
18
tspClientProvider : ITspClientProvider ;
17
19
contextMenuRenderer ?: ( event : React . MouseEvent < HTMLDivElement > , output : OutputDescriptor ) => void ;
20
+ /**
21
+ * This is a placeholder for the customization implementation.
22
+ * TODO - Make sure this comment an accurate reflection before PR.
23
+ * @returns
24
+ */
25
+ onCustomizationClick ?: ( entry : OutputDescriptor , experiment : Experiment ) => void ;
18
26
}
19
27
20
28
export interface ReactAvailableViewsState {
@@ -179,8 +187,9 @@ export class ReactAvailableViewsWidget extends React.Component<ReactAvailableVie
179
187
const idStringToNodeId : { [ key : string ] : number } = { } ;
180
188
181
189
// Fill-in the lookup table
182
- list . forEach ( output => {
190
+ list . forEach ( ( output , index ) => {
183
191
const node : TreeNode = this . entryToTreeNode ( output , idStringToNodeId ) ;
192
+ node . elementIndex = index ;
184
193
lookup [ output . id ] = node ;
185
194
this . _nodeIdToOutput [ node . id ] = output ;
186
195
} ) ;
@@ -207,34 +216,90 @@ export class ReactAvailableViewsWidget extends React.Component<ReactAvailableVie
207
216
}
208
217
209
218
private entryToTreeNode ( entry : OutputDescriptor , idStringToNodeId : { [ key : string ] : number } ) : TreeNode {
210
- const labels = [ entry . name ] ;
211
- let tooltips = undefined ;
212
- if ( entry . description ) {
213
- tooltips = [ entry . description ] ;
214
- }
215
- let id = idStringToNodeId [ entry . id ] ;
216
- if ( id === undefined ) {
217
- id = this . _idGenerator ++ ;
218
- idStringToNodeId [ entry . id ] = id ;
219
- }
219
+ let id = idStringToNodeId [ entry . id ] ?? ( idStringToNodeId [ entry . id ] = this . _idGenerator ++ ) ;
220
+
220
221
let parentId = - 1 ;
221
222
if ( entry . parentId ) {
222
- const existingId = idStringToNodeId [ entry . parentId ] ;
223
- if ( existingId === undefined ) {
224
- parentId = this . _idGenerator ++ ;
225
- idStringToNodeId [ entry . parentId ] = parentId ;
226
- } else {
227
- parentId = existingId ;
228
- }
223
+ parentId = idStringToNodeId [ entry . parentId ] ?? ( idStringToNodeId [ entry . parentId ] = this . _idGenerator ++ ) ;
229
224
}
230
- return {
231
- labels : labels ,
232
- tooltips : tooltips ,
225
+
226
+ const treeNode : TreeNode = {
227
+ labels : [ entry . name ] ,
228
+ tooltips : entry . description ? [ entry . description ] : undefined ,
233
229
showTooltip : true ,
234
230
isRoot : false ,
235
- id : id ,
236
- parentId : parentId ,
237
- children : [ ]
238
- } as TreeNode ;
231
+ id,
232
+ parentId,
233
+ children : [ ] ,
234
+ getEnrichedContent : this . createEnrichedContent ( entry )
235
+ } ;
236
+
237
+ return treeNode ;
239
238
}
239
+
240
+ private createEnrichedContent ( entry : OutputDescriptor ) : ( ( ) => JSX . Element ) | undefined {
241
+ const isCustomizable = entry . capabilities ?. canCreate === true ;
242
+ const isDeletable = entry . capabilities ?. canDelete === true ;
243
+
244
+ if ( ! isCustomizable && ! isDeletable ) {
245
+ return undefined ;
246
+ }
247
+
248
+ const nameSpanStyle = {
249
+ overflow : 'hidden' ,
250
+ textOverflow : 'ellipsis' ,
251
+ whiteSpace : 'nowrap' ,
252
+ minWidth : 0 ,
253
+ flexShrink : 1
254
+ } ;
255
+
256
+ if ( isCustomizable ) {
257
+ return ( ) : JSX . Element => (
258
+ < >
259
+ < span style = { nameSpanStyle } > { entry . name } </ span >
260
+ < div className = "remove-output-button-container" title = { `Add custom analysis to ${ entry . name } ` } >
261
+ < button className = "remove-output-button" onClick = { e => this . handleCustomizeClick ( entry , e ) } >
262
+ < FontAwesomeIcon icon = { faPlus } />
263
+ </ button >
264
+ </ div >
265
+ </ >
266
+ ) ;
267
+ } else {
268
+ // Must be deletable based on our conditions
269
+ return ( ) : JSX . Element => (
270
+ < >
271
+ < span style = { nameSpanStyle } > { entry . configuration ?. name } </ span >
272
+ < div className = "remove-output-button-container" title = { `Remove "${ entry . configuration ?. name } "` } >
273
+ < button className = "remove-output-button" onClick = { e => this . handleDeleteClick ( entry , e ) } >
274
+ < FontAwesomeIcon icon = { faTimes } />
275
+ </ button >
276
+ </ div >
277
+ </ >
278
+ ) ;
279
+ }
280
+ }
281
+
282
+ private handleCustomizeClick = async ( entry : OutputDescriptor , e : React . MouseEvent ) => {
283
+ e . stopPropagation ( ) ;
284
+ if ( this . props . onCustomizationClick && this . _selectedExperiment ) {
285
+ await this . props . onCustomizationClick ( entry , this . _selectedExperiment ) ;
286
+ this . updateAvailableViews ( ) ;
287
+ }
288
+ } ;
289
+
290
+ private handleDeleteClick = async ( entry : OutputDescriptor , e : React . MouseEvent ) => {
291
+ e . stopPropagation ( ) ;
292
+ if ( this . _selectedExperiment ?. UUID ) {
293
+ console . dir ( entry ) ;
294
+ const res = await this . props . tspClientProvider
295
+ . getTspClient ( )
296
+ . deleteDerivedOutput ( this . _selectedExperiment . UUID , entry . parentId as string , entry . id ) ;
297
+ if ( ! res . isOk ( ) ) {
298
+ // request is failing for some reason...
299
+ // But the output is removed when we update available views regardless
300
+ console . error ( `${ res . getStatusCode ( ) } - ${ res . getStatusMessage ( ) } ` ) ;
301
+ }
302
+ this . updateAvailableViews ( ) ;
303
+ }
304
+ } ;
240
305
}
0 commit comments