Skip to content

Commit

Permalink
Make explorer's available views widget a pure React component
Browse files Browse the repository at this point in the history
This new react component ReactAvailableViewsWidget will be part of the
packages/react-components. It can be used in other web applications
independently from Theia.

It listens to the AVAILABLE_OUTPUTS_CHANGED Signal to update the widget.

Signed-off-by: Bernd Hufmann <Bernd.Hufmann@ericsson.com>
  • Loading branch information
bhufmann committed Mar 29, 2021
1 parent 3b876ca commit cc4ec7b
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 111 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';

export class AvailableAnalysesChangedSignalPayload {
export class AvailableViewsChangedSignalPayload {
private _availableOutputDescriptors: OutputDescriptor[];
private _experiment: Experiment;

Expand Down
6 changes: 3 additions & 3 deletions packages/base/src/signals/signal-manager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EventEmitter } from 'events';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import { Trace } from 'tsp-typescript-client/lib/models/trace';
import { AvailableAnalysesChangedSignalPayload } from './available-analyses-changed-signal-payload';
import { AvailableViewsChangedSignalPayload } from './available-views-changed-signal-payload';
import { OpenedTracesUpdatedSignalPayload } from './opened-traces-updated-signal-payload';
import { OutputAddedSignalPayload } from './output-added-signal-payload';

Expand All @@ -12,7 +12,7 @@ export declare interface SignalManager {
fireExperimentClosedSignal(experiment: Experiment): void;
fireExperimentSelectedSignal(experiment: Experiment): void;
fireOpenedTracesChangedSignal(payload: OpenedTracesUpdatedSignalPayload): void;
fireAvailableOutputsChangedSignal(payload: AvailableAnalysesChangedSignalPayload): void;
fireAvailableOutputsChangedSignal(payload: AvailableViewsChangedSignalPayload): void;
fireOutputAddedSignal(payload: OutputAddedSignalPayload): void;
fireTooltipSignal(tooltip?: { [key: string]: string }): void;
fireThemeChangedSignal(theme: string): void;
Expand Down Expand Up @@ -56,7 +56,7 @@ export class SignalManager extends EventEmitter implements SignalManager {
fireOpenedTracesChangedSignal(payload: OpenedTracesUpdatedSignalPayload): void {
this.emit(Signals.OPENED_TRACES_UPDATED, payload);
}
fireAvailableOutputsChangedSignal(payload: AvailableAnalysesChangedSignalPayload): void {
fireAvailableOutputsChangedSignal(payload: AvailableViewsChangedSignalPayload): void {
this.emit(Signals.AVAILABLE_OUTPUTS_CHANGED, payload);
}
fireOutputAddedSignal(payload: OutputAddedSignalPayload): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { signalManager, Signals } from '@trace-viewer/base/lib/signals/signal-ma
import ReactModal from 'react-modal';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCopy } from '@fortawesome/free-solid-svg-icons';
import { AvailableAnalysesChangedSignalPayload } from '@trace-viewer/base/lib/signals/available-analyses-changed-signal-payload';
import { AvailableViewsChangedSignalPayload } from '@trace-viewer/base/lib/signals/available-views-changed-signal-payload';
import { OpenedTracesUpdatedSignalPayload } from '@trace-viewer/base/lib/signals/opened-traces-updated-signal-payload';
import { ITspClientProvider } from '@trace-viewer/base/lib/tsp-client-provider';

Expand Down Expand Up @@ -295,7 +295,7 @@ export class ReactOpenTracesWidget extends React.Component<ReactOpenTracesWidget
}
}
if (outputs !== undefined && signalExperiment !== undefined) {
signalManager().fireAvailableOutputsChangedSignal(new AvailableAnalysesChangedSignalPayload(outputs, signalExperiment));
signalManager().fireAvailableOutputsChangedSignal(new AvailableViewsChangedSignalPayload(outputs, signalExperiment));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import * as React from 'react';
import { List, ListRowProps, AutoSizer } from 'react-virtualized';
import { OutputAddedSignalPayload } from '@trace-viewer/base/lib/signals/output-added-signal-payload';
import { signalManager, Signals } from '@trace-viewer/base/lib/signals/signal-manager';
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import { AvailableViewsChangedSignalPayload } from '@trace-viewer/base/lib/signals/available-views-changed-signal-payload';
// import { ExperimentManager } from '@trace-viewer/base/src/experiment-manager';

export interface ReactAvailableViewsProps {
id: string,
title: string,
// experimentManager: ExperimentManager,
contextMenuRenderer?: (anchor: {x: number, y: number}, output: OutputDescriptor) => void,
}

export interface ReactAvailableViewsState {
availableOutputDescriptors: OutputDescriptor[];
}

export class ReactAvailableViewsWidget extends React.Component<ReactAvailableViewsProps, ReactAvailableViewsState> {
static LIST_MARGIN = 2;
static LINE_HEIGHT = 16;
static ROW_HEIGHT = (2 * ReactAvailableViewsWidget.LINE_HEIGHT) + ReactAvailableViewsWidget.LIST_MARGIN;

protected forceUpdateKey = false;
protected lastSelectedOutputIndex = -1;

protected selectedExperiment: Experiment | undefined;

protected onHandleAvailableViewsChanged = (payload: AvailableViewsChangedSignalPayload): void => this.doHandleAvailableViewsChanged(payload);

constructor(props: ReactAvailableViewsProps) {
super(props);
signalManager().on(Signals.AVAILABLE_OUTPUTS_CHANGED, this.onHandleAvailableViewsChanged);
this.state = { availableOutputDescriptors: [] };
}

componentWillUnmount(): void {
signalManager().off(Signals.AVAILABLE_OUTPUTS_CHANGED, this.onHandleAvailableViewsChanged);
}

render(): React.ReactNode {
this.forceUpdateKey = !this.forceUpdateKey;
const key = Number(this.forceUpdateKey);
let outputsRowCount = 0;
const outputs = this.state.availableOutputDescriptors;
if (outputs) {
outputsRowCount = outputs.length;
}
const totalHeight = this.getTotalHeight();
return (
<div className='trace-explorer-views'>
<div className='trace-explorer-panel-content'>
<AutoSizer>
{({ width }) =>
<List
key={key}
height={totalHeight}
width={width}
rowCount={outputsRowCount}
rowHeight={ReactAvailableViewsWidget.ROW_HEIGHT}
rowRenderer={this.renderRowOutputs}
/>
}
</AutoSizer>
</div>
</div>
);
}

protected renderRowOutputs = (props: ListRowProps): React.ReactNode => this.doRenderRowOutputs(props);

private doRenderRowOutputs(props: ListRowProps): React.ReactNode {
let outputName = '';
let outputDescription = '';
let output: OutputDescriptor | undefined;
const outputDescriptors = this.state.availableOutputDescriptors;
if (outputDescriptors && outputDescriptors.length && props.index < outputDescriptors.length) {
output = outputDescriptors[props.index];
outputName = output.name;
outputDescription = output.description;
}
let traceContainerClassName = 'outputs-list-container';
if (props.index === this.lastSelectedOutputIndex) {
traceContainerClassName = traceContainerClassName + ' theia-mod-selected';
}
return <div className={traceContainerClassName}
id={`${traceContainerClassName}-${props.index}`}
key={props.key}
style={props.style}
onClick={this.handleOutputClicked}
onContextMenu={event => { this.handleContextMenuEvent(event, output); }}
data-id={`${props.index}`}
>
<h4 className='outputs-element-name'>
{outputName}
</h4>
<div className='outputs-element-description child-element'>
{outputDescription}
</div>
</div>;
}

protected getTotalHeight(): number {
let totalHeight = 0;
const outputDescriptors = this.state.availableOutputDescriptors;
outputDescriptors?.forEach(() => totalHeight += ReactAvailableViewsWidget.ROW_HEIGHT);
return totalHeight;
}

protected handleOutputClicked = (e: React.MouseEvent<HTMLDivElement>): void => this.doHandleOutputClicked(e);
protected handleContextMenuEvent = (e: React.MouseEvent<HTMLDivElement>, output: OutputDescriptor | undefined): void => this.doHandleContextMenuEvent(e, output);

private doHandleOutputClicked(e: React.MouseEvent<HTMLDivElement>) {
const index = Number(e.currentTarget.getAttribute('data-id'));
this.lastSelectedOutputIndex = index;
const outputs = this.state.availableOutputDescriptors;
if (outputs && this.selectedExperiment) {
signalManager().fireExperimentSelectedSignal(this.selectedExperiment);
signalManager().fireOutputAddedSignal(new OutputAddedSignalPayload(outputs[index], this.selectedExperiment));
}
}

protected doHandleContextMenuEvent(event: React.MouseEvent<HTMLDivElement>, output: OutputDescriptor | undefined): void {
if (this.props.contextMenuRenderer && output) {
this.props.contextMenuRenderer({ x: event.clientX, y: event.clientY }, output);
}
event.preventDefault();
event.stopPropagation();
}

protected doHandleAvailableViewsChanged(payload: AvailableViewsChangedSignalPayload): void {
this.setState({ availableOutputDescriptors: payload.getAvailableOutputDescriptors()} );
this.selectedExperiment = payload.getExperiment();
}
}
Original file line number Diff line number Diff line change
@@ -1,121 +1,31 @@
import { inject, injectable, postConstruct } from 'inversify';
import { injectable, postConstruct } from 'inversify';
import { ReactWidget, Widget, Message } from '@theia/core/lib/browser';
import * as React from 'react';
import { List, ListRowProps, AutoSizer } from 'react-virtualized';
import { OutputAddedSignalPayload } from '@trace-viewer/base/lib/signals/output-added-signal-payload';
import { signalManager, Signals } from '@trace-viewer/base/lib/signals/signal-manager';
import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor';
import { TspClientProvider } from '../../tsp-client-provider-impl';
import { Experiment } from 'tsp-typescript-client/lib/models/experiment';
import { AvailableAnalysesChangedSignalPayload } from '@trace-viewer/base/lib/signals/available-analyses-changed-signal-payload';
import { ReactAvailableViewsWidget} from '@trace-viewer/react-components/lib/trace-explorer/trace-explorer-views-widget';

@injectable()
export class TraceExplorerViewsWidget extends ReactWidget {
static ID = 'trace-explorer-views-widget';
static LABEL = 'Available Views';
static LIST_MARGIN = 2;
static LINE_HEIGHT = 16;
static ROW_HEIGHT = (2 * TraceExplorerViewsWidget.LINE_HEIGHT) + TraceExplorerViewsWidget.LIST_MARGIN;

@inject(TspClientProvider) protected readonly tspClientProvider!: TspClientProvider;
protected forceUpdateKey = false;
protected lastSelectedOutputIndex = -1;

protected selectedExperiment: Experiment | undefined;
protected availableOutputDescriptors: OutputDescriptor[] | undefined;

private onAvailableAnalysesChanged = (payload: AvailableAnalysesChangedSignalPayload): void => this.doHandleAvailableAnalysesChangedSignal(payload);

@postConstruct()
init(): void {
this.id = TraceExplorerViewsWidget.ID;
this.title.label = TraceExplorerViewsWidget.LABEL;
signalManager().on(Signals.AVAILABLE_OUTPUTS_CHANGED, this.onAvailableAnalysesChanged);
this.update();
}

dispose(): void {
super.dispose();
signalManager().off(Signals.AVAILABLE_OUTPUTS_CHANGED, this.onAvailableAnalysesChanged);
}

render(): React.ReactNode {
this.forceUpdateKey = !this.forceUpdateKey;
const key = Number(this.forceUpdateKey);
let outputsRowCount = 0;
const outputs = this.availableOutputDescriptors;
if (outputs) {
outputsRowCount = outputs.length;
}
const totalHeight = this.getTotalHeight();
return (
<div className='trace-explorer-views'>
<div className='trace-explorer-panel-content'>
<AutoSizer>
{({ width }) =>
<List
key={key}
height={totalHeight}
width={width}
rowCount={outputsRowCount}
rowHeight={TraceExplorerViewsWidget.ROW_HEIGHT}
rowRenderer={this.renderRowOutputs}
/>
}
</AutoSizer>
</div>
</div>
);
}

protected renderRowOutputs = (props: ListRowProps): React.ReactNode => this.doRenderRowOutputs(props);

private doRenderRowOutputs(props: ListRowProps): React.ReactNode {
let outputName = '';
let outputDescription = '';
const outputDescriptors = this.availableOutputDescriptors;
if (outputDescriptors && outputDescriptors.length && props.index < outputDescriptors.length) {
outputName = outputDescriptors[props.index].name;
outputDescription = outputDescriptors[props.index].description;
}
let traceContainerClassName = 'outputs-list-container';
if (props.index === this.lastSelectedOutputIndex) {
traceContainerClassName = traceContainerClassName + ' theia-mod-selected';
}
return <div className={traceContainerClassName}
id={`${traceContainerClassName}-${props.index}`}
key={props.key}
style={props.style}
onClick={this.handleOutputClicked}
data-id={`${props.index}`}
>
<h4 className='outputs-element-name'>
{outputName}
</h4>
<div className='outputs-element-description child-element'>
{outputDescription}
</div>
</div>;
}

protected getTotalHeight(): number {
let totalHeight = 0;
const outputDescriptors = this.availableOutputDescriptors;
outputDescriptors?.forEach(() => totalHeight += TraceExplorerViewsWidget.ROW_HEIGHT);
return totalHeight;
}

protected handleOutputClicked = (e: React.MouseEvent<HTMLDivElement>): void => this.doHandleOutputClicked(e);

private doHandleOutputClicked(e: React.MouseEvent<HTMLDivElement>) {
const index = Number(e.currentTarget.getAttribute('data-id'));
this.lastSelectedOutputIndex = index;
const outputs = this.availableOutputDescriptors;
if (outputs && this.selectedExperiment) {
signalManager().fireExperimentSelectedSignal(this.selectedExperiment);
signalManager().fireOutputAddedSignal(new OutputAddedSignalPayload(outputs[index], this.selectedExperiment));
}
this.update();
return (<div>
{ <ReactAvailableViewsWidget
id={this.id}
title={this.title.label}
></ReactAvailableViewsWidget>
}
</div>);
}

protected onResize(msg: Widget.ResizeMessage): void {
Expand All @@ -127,10 +37,4 @@ export class TraceExplorerViewsWidget extends ReactWidget {
super.onAfterShow(msg);
this.update();
}

protected doHandleAvailableAnalysesChangedSignal(payload: AvailableAnalysesChangedSignalPayload): void {
this.availableOutputDescriptors = payload.getAvailableOutputDescriptors();
this.selectedExperiment = payload.getExperiment();
this.update();
}
}

0 comments on commit cc4ec7b

Please sign in to comment.