Skip to content

Commit e758b95

Browse files
Working stuff saved. Ammend this later
1 parent db365be commit e758b95

File tree

9 files changed

+150
-33
lines changed

9 files changed

+150
-33
lines changed

packages/react-components/src/components/utils/filter-tree/table-cell.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class TableCell extends React.Component<TableCellProps> {
1818
if (node.elementIndex && node.elementIndex === index && node.getElement) {
1919
content = node.getElement();
2020
} else {
21-
content = node.labels[index];
21+
content = node.getEnrichedContent ? node.getEnrichedContent() : node.labels[index];
2222
}
2323

2424
let title = undefined;
@@ -30,6 +30,7 @@ export class TableCell extends React.Component<TableCellProps> {
3030
title = node.labels[index];
3131
}
3232
}
33+
3334
return (
3435
<td key={this.props.index + '-td-' + this.props.node.id}>
3536
<span title={title}>

packages/react-components/src/components/utils/filter-tree/table-row.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export class TableRow extends React.Component<TableRowProps> {
2929

3030
isCollapsed = (): boolean => this.props.collapsedNodes.includes(this.props.node.id);
3131

32-
private handleCollapse = (): void => {
32+
private handleCollapse = (e: React.MouseEvent<HTMLDivElement>): void => {
33+
e.stopPropagation();
3334
this.props.onToggleCollapse(this.props.node.id);
3435
};
3536

@@ -40,10 +41,10 @@ export class TableRow extends React.Component<TableRowProps> {
4041
renderToggleCollapse = (): React.ReactNode => {
4142
const width = (this.props.level + 1) * 12;
4243
return this.props.node.children.length === 0 ? (
43-
<div style={{ width, paddingRight: 5, display: 'inline-block' }} />
44+
<div style={{ width, paddingRight: 5, display: 'inline-block', flexShrink: 0 }} />
4445
) : (
4546
<div
46-
style={{ width, paddingRight: 5, textAlign: 'right', display: 'inline-block' }}
47+
style={{ width, paddingRight: 5, textAlign: 'right', display: 'inline-block', flexShrink: 0 }}
4748
onClick={this.handleCollapse}
4849
>
4950
{this.isCollapsed() ? icons.expand : icons.collapse}

packages/react-components/src/components/utils/filter-tree/tree-node.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,15 @@ export interface TreeNode {
88
showTooltip?: boolean;
99
elementIndex?: number;
1010
getElement?: () => JSX.Element;
11+
/**
12+
* TODO - Remove or fix this comment.
13+
*
14+
* This lets you add dynamic HTML where the content goes in a Tree Node
15+
* Instead of just a string.
16+
* I'm not sure what the use case of getElement()? is based on the logic in TableCell.jsx
17+
* So I didn't use it. But we may be able to just use that instead.
18+
*
19+
* -WY
20+
*/
21+
getEnrichedContent?: () => JSX.Element;
1122
}

packages/react-components/src/trace-explorer/trace-explorer-views-widget.tsx

+90-25
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@ import { ExperimentManager } from 'traceviewer-base/lib/experiment-manager';
99
import { FilterTree } from '../components/utils/filter-tree/tree';
1010
import { TreeNode } from '../components/utils/filter-tree/tree-node';
1111
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';
1214

1315
export interface ReactAvailableViewsProps {
1416
id: string;
1517
title: string;
1618
tspClientProvider: ITspClientProvider;
1719
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;
1826
}
1927

2028
export interface ReactAvailableViewsState {
@@ -179,8 +187,9 @@ export class ReactAvailableViewsWidget extends React.Component<ReactAvailableVie
179187
const idStringToNodeId: { [key: string]: number } = {};
180188

181189
// Fill-in the lookup table
182-
list.forEach(output => {
190+
list.forEach((output, index) => {
183191
const node: TreeNode = this.entryToTreeNode(output, idStringToNodeId);
192+
node.elementIndex = index;
184193
lookup[output.id] = node;
185194
this._nodeIdToOutput[node.id] = output;
186195
});
@@ -207,34 +216,90 @@ export class ReactAvailableViewsWidget extends React.Component<ReactAvailableVie
207216
}
208217

209218
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+
220221
let parentId = -1;
221222
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++);
229224
}
230-
return {
231-
labels: labels,
232-
tooltips: tooltips,
225+
226+
const treeNode: TreeNode = {
227+
labels: [entry.name],
228+
tooltips: entry.description ? [entry.description] : undefined,
233229
showTooltip: true,
234230
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;
239238
}
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+
};
240305
}

packages/react-components/style/output-components-style.css

+39-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ canvas {
246246
text-overflow: ellipsis;
247247
overflow: hidden;
248248
white-space: nowrap;
249-
display: block;
249+
display: flex;
250+
align-items: center;
250251
}
251252

252253
.item-properties-table .table-tree td span {
@@ -557,3 +558,40 @@ canvas {
557558
margin-left: 4px;
558559
cursor: pointer;
559560
}
561+
562+
input[type="button"].input-button,
563+
input[type="submit"].input-button,
564+
input[type="reset"].input-button {
565+
border: none;
566+
color: var(--trace-viewer-button-foreground);
567+
background-color: var(--trace-viewer-button-background);
568+
min-width: 65px;
569+
outline: none;
570+
cursor: pointer;
571+
padding: 4px 9px;
572+
font-size-adjust: 0.45;
573+
margin-left: auto;
574+
margin-right: 10px;
575+
flex-shrink: 0;
576+
}
577+
578+
.remove-output-button-container {
579+
margin-right: 2px;
580+
display: flex;
581+
justify-content: center;
582+
align-items: flex-start;
583+
margin-left: auto;
584+
}
585+
586+
.remove-output-button {
587+
background: none;
588+
border: none;
589+
visibility: visible;
590+
margin-top: 2px;
591+
color: var(--trace-viewer-ui-font-color0);
592+
cursor: pointer;
593+
}
594+
595+
.remove-output-button :hover {
596+
background: none;
597+
}

theia-extensions/viewer-prototype/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@theia/editor": "1.55.0",
2525
"@theia/filesystem": "1.55.0",
2626
"@theia/messages": "1.55.0",
27+
"ajv": "^8.17.1",
2728
"animate.css": "^4.1.1",
2829
"traceviewer-base": "0.7.2",
2930
"traceviewer-react-components": "0.7.2",

theia-extensions/viewer-prototype/src/browser/trace-explorer/trace-explorer-sub-widgets/theia-trace-explorer-views-widget.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class TraceExplorerViewsWidget extends ReactWidget {
2626
id={this.id}
2727
title={this.title.label}
2828
tspClientProvider={this.tspClientProvider}
29+
onCustomizationClick={console.log}
2930
></ReactAvailableViewsWidget>
3031
</div>
3132
);

theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts

-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export default new ContainerModule(bind => {
4646
bind(TabBarToolbarContribution).toService(TraceViewerToolbarContribution);
4747
bind(CommandContribution).toService(TraceViewerToolbarContribution);
4848
bind(TraceServerConnectionStatusClient).to(TraceServerConnectionStatusClientImpl).inSingletonScope();
49-
5049
bind(TraceViewerWidget).toSelf();
5150
bind<WidgetFactory>(WidgetFactory)
5251
.toDynamicValue(context => ({

yarn.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -4452,7 +4452,7 @@ ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.3:
44524452
json-schema-traverse "^0.4.1"
44534453
uri-js "^4.2.2"
44544454

4455-
ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.3, ajv@^8.9.0:
4455+
ajv@^8.0.0, ajv@^8.0.1, ajv@^8.17.1, ajv@^8.6.3, ajv@^8.9.0:
44564456
version "8.17.1"
44574457
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
44584458
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
@@ -14099,7 +14099,7 @@ tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6
1409914099

1410014100
tsp-typescript-client@^0.6.0:
1410114101
version "0.6.0"
14102-
resolved "https://registry.npmjs.org/tsp-typescript-client/-/tsp-typescript-client-0.6.0.tgz#59d53a76dcb7759f8f16eb9e798320a9a790b7b1"
14102+
resolved "https://registry.yarnpkg.com/tsp-typescript-client/-/tsp-typescript-client-0.6.0.tgz#59d53a76dcb7759f8f16eb9e798320a9a790b7b1"
1410314103
integrity sha512-K6tl773Nq7lo2XAexHBtVDKiFGUlrwFbzKL6aZkf33iHRyAM80xBc0cAoXXTgsSLb3pBodLQRVzw8sBTGWGwOA==
1410414104
dependencies:
1410514105
json-bigint sidorares/json-bigint#2c0a5f896d7888e68e5f4ae3c7ea5cd42fd54473

0 commit comments

Comments
 (0)