From a5faeb5885055d9b012bd03b8706a38053e77f79 Mon Sep 17 00:00:00 2001 From: maria-ericsson Date: Fri, 23 Oct 2020 14:05:01 -0500 Subject: [PATCH] Implement Tooltip Signed-off-by: maria-ericsson --- .../browser/style/output-components-style.css | 120 +++++++++--------- .../components/timegraph-output-component.tsx | 73 +++++++++-- .../components/utils/tooltip-component.tsx | 43 +++++++ 3 files changed, 164 insertions(+), 72 deletions(-) create mode 100644 viewer-prototype/src/browser/trace-viewer/components/utils/tooltip-component.tsx diff --git a/viewer-prototype/src/browser/style/output-components-style.css b/viewer-prototype/src/browser/style/output-components-style.css index 853b64e2f..0c9934161 100644 --- a/viewer-prototype/src/browser/style/output-components-style.css +++ b/viewer-prototype/src/browser/style/output-components-style.css @@ -1,96 +1,94 @@ /* Output component styling */ /* Main container*/ .output-container { - display: flex; - color: var(--theia-ui-font-color0) + display: flex; + color: var(--theia-ui-font-color0) } .widget-handle { - background-color: var(--theia-layout-color4); - display: grid; - grid-template-rows: 25px 1fr; + background-color: var(--theia-layout-color4); + display: grid; + grid-template-rows: 25px 1fr; } .title-bar-label { - text-align: center; - writing-mode: vertical-rl; - height: 50%; - min-height: 150px; - transform: rotate(180deg); - user-select: none; - align-self: center; - justify-self: center; + text-align: center; + writing-mode: vertical-rl; + height: 50%; + min-height: 150px; + transform: rotate(180deg); + user-select: none; + align-self: center; + justify-self: center; } + .remove-component-button { - background: none; - border: none; - padding: 2px 8px; - align-self: center; - justify-self: center; - color: var(--theia-ui-font-color0) + background: none; + border: none; + padding: 2px 8px; + align-self: center; + justify-self: center; + color: var(--theia-ui-font-color0) } .main-output-container { - display: flex; + display: flex; } .output-component-tree { - overflow-y: scroll; - white-space: pre-wrap; + overflow-y: scroll; + white-space: pre-wrap; } .output-component-chart { - align-self: center; - text-align: center; + align-self: center; + text-align: center; +} + +#tooltip-box { + position:absolute; + overflow:hidden; + z-index :2; + top: 80px; + right: 20px; + background-color: rgb(247, 231, 8); + width: 250px; + height: auto; + opacity: 0.8; + font-size: 13px; + font-family: arial; + text-align: left; + color: black; + padding-inline-start: 10px; } #timegraph-main { - width: 100%; - display: flex; + width:100%; + overflow-y:scroll; + position:relative; + height: 300px; + z-index:1; } #main { - border: 1px solid; - margin: 10px 0; - overflow: hidden; + border: 1px solid; + margin: 10px 0; + overflow: hidden; } canvas { - display: block; + display: block; } .innerContainer { - width: 100%; -} - -.table-tree>tbody>tr:nth-child(1)>th { - background-color: var(--theia-editor-background); - position: sticky; - top: 0; + width: 100%; } -.table-tree th, .table-tree td { - padding: 3px 5px; - text-align: left; - border-bottom: 1px solid #333; - border-right: 1px solid #333; - white-space: nowrap; - min-width: 50px; -} - -.timegraph-tree tr { - /* TODO: Fix row alignment, this number is arbitrary, it works [on my machine], but it should match line height in timeline-chart */ - line-height: 18px; - position: relative; - white-space: nowrap; - top: 50%; - padding: 0 0; +#input-filter-tree { + background-color: var(--theia-input-background); + border: none; + border-bottom: 1px solid var(--theia-input-foreground); + padding: 3px; + width: 180px; + color: var(--theia-input-placeholder-foreground) } -#input-filter-tree { - background-color: var(--theia-input-background); - border: none; - border-bottom: 1px solid var(--theia-input-foreground); - padding: 3px; - width: 180px; - color: var(--theia-input-placeholder-foreground) -} \ No newline at end of file diff --git a/viewer-prototype/src/browser/trace-viewer/components/timegraph-output-component.tsx b/viewer-prototype/src/browser/trace-viewer/components/timegraph-output-component.tsx index 0db560c33..e20c7585d 100644 --- a/viewer-prototype/src/browser/trace-viewer/components/timegraph-output-component.tsx +++ b/viewer-prototype/src/browser/trace-viewer/components/timegraph-output-component.tsx @@ -21,6 +21,7 @@ import { EntryTree } from './utils/filtrer-tree/entry-tree'; import { listToTree, getAllExpandedNodeIds } from './utils/filtrer-tree/utils'; import hash from '../../../common/utils/value-hash'; import ColumnHeader from './utils/filtrer-tree/column-header'; +import { TooltipComponent } from './utils/tooltip-component'; type TimegraphOutputProps = AbstractOutputProps & { addWidgetResizeHandler: (handler: () => void) => void; @@ -30,15 +31,17 @@ type TimegraphOutputState = AbstractOutputState & { timegraphTree: TimeGraphEntry[]; collapsedNodes: number[]; columns: ColumnHeader[]; + isElementSelected: boolean; + isTooltipOpened: boolean; }; - export class TimegraphOutputComponent extends AbstractTreeOutputComponent { private totalHeight = 0; private rowController: TimeGraphRowController; private chartLayer: TimeGraphChart; private vscrollLayer: TimeGraphVerticalScrollbar; private horizontalContainer: React.RefObject; - + private toolTipObject: { [key: string]: string } = {}; + private toolTipPosition: { x: number, y: number } = { x: 0, y: 0 }; private tspDataProvider: TspDataProvider; private styleMap = new Map(); @@ -50,9 +53,13 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { + this.setTooltipPosition(el, ev); + }, + mouseover: (el: TimeGraphRowElement, ev: PIXI.InteractionEvent) => { + this.setTooltipPosition(el, ev); + }, + mouseout: () => { + setTimeout(() => { this.closeTooltip(); }, 6000); + } + }); this.rowController.onVerticalOffsetChangedHandler(() => { if (this.treeRef.current) { this.treeRef.current.scrollTop = this.rowController.verticalOffset; } }); - this.chartLayer.onSelectedRowElementChanged(model => { if (model) { const el = this.chartLayer.getElementById(model.id); @@ -93,6 +110,32 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { + this.setState({ + isTooltipOpened: false + }); + }; + + openTooltip(): void { + this.setState({ + isElementSelected: true, + isTooltipOpened: true + }); + } + + setTooltipPosition(el: TimeGraphRowElement, ev: PIXI.InteractionEvent): void { + const X_MAX = document.documentElement.clientWidth * 0.5; + const Y_MAX = this.totalHeight - 100; + const TOOLTIP_OFFSET = 40; + let posx = ev.data.getLocalPosition(el.displayObject).x; + posx = Math.min(posx, X_MAX - 0.5 * TOOLTIP_OFFSET); + el.position.y = Math.min(el.position.y, Y_MAX - 1.5 * TOOLTIP_OFFSET); + this.toolTipPosition.x = (posx > 0.9 * X_MAX) ? posx - 0.5 * TOOLTIP_OFFSET : posx + 0.5 * TOOLTIP_OFFSET; + this.toolTipPosition.y = (el.position.y > 0.5 * Y_MAX) ? el.position.y - 0.5 * TOOLTIP_OFFSET : el.position.y + 0.5 * TOOLTIP_OFFSET; + this.onElementSelected(this.chartLayer.getElementById(el.id)); + this.closeTooltip(); + this.openTooltip(); + } async componentDidMount(): Promise { this.waitAnalysisCompletion(); @@ -109,10 +152,10 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent 0) { treeResponse.model.headers.forEach(header => { - columns.push({title: header.name, sortable: true, tooltip: header.tooltip}); + columns.push({ title: header.name, sortable: true, tooltip: header.tooltip }); }); } else { - columns.push({title: 'Name', sortable: true}); + columns.push({ title: 'Name', sortable: true }); } // TODO Style should not be retreive in the "initialization" part or at least async const styleResponse = (await this.props.tspClient.fetchStyles(this.props.traceId, this.props.outputDescriptor.id, QueryHelper.query())).getModel(); @@ -157,8 +200,11 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent {this.state.outputStatus === ResponseStatus.COMPLETED ? -
{ ev.preventDefault(); ev.stopPropagation(); }} style={{ height: this.props.style.height }} > +
this.closeTooltip()} + onWheel={ev => { ev.preventDefault(); ev.stopPropagation(); }} style={{ height: this.props.style.height }}> {this.renderTimeGraphContent()} + {this.state.isElementSelected && this.state.isTooltipOpened && + }
: 'Analysis running...'} ; @@ -209,8 +255,7 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent; } - - private async onElementSelected(element: TimeGraphRowElement | undefined) { + async getToolTipObject(element: TimeGraphRowElement | undefined): Promise<{ [key: string]: string }> { if (element && this.props.viewRange) { const elementRange = element.model.range; const offset = this.props.viewRange.getOffset(); @@ -225,11 +270,16 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { + SignalManager.getInstance().fireTooltipSignal(await this.getToolTipObject(element)); } - private async fetchTimegraphData(range: TimelineChart.TimeGraphRange, resolution: number) { const treeNodes = listToTree(this.state.timegraphTree, this.state.columns); const orderedTreeIds = getAllExpandedNodeIds(treeNodes, this.state.collapsedNodes); @@ -359,3 +409,4 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { + constructor(props: TooltipProps) { + super(props); + this.state = { style: { top: props.position.y + 'px', left: props.position.x + 'px' } }; + } + + tooltipRef: React.RefObject = React.createRef(); + + renderTooltip(): React.ReactNode { + const tooltipArray: React.ReactNode[] = []; + if (this.props.tooltip) { + const keys = Object.keys(this.props.tooltip); + keys.forEach(key => { + tooltipArray.push(

{key + ': ' + this.props.tooltip[key]}

); + }); + } + else { + console.log('Tooltip null'); + } + return + {tooltipArray.map(element => element)} + ; + } + + render(): React.ReactNode { + return
+ {this.renderTooltip()} +
; + } +}