Skip to content

Commit fbb85e6

Browse files
Merge pull request #48 from Demonstrandum/cursor/superimposed-plots-interaction-5d68
Superimposed plots interaction
2 parents ee1c845 + 76f1c11 commit fbb85e6

6 files changed

Lines changed: 254 additions & 1 deletion

File tree

tensorbored/webapp/metrics/views/card_renderer/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,8 @@ tf_ng_module(
430430
"superimposed_card_component.ng.html",
431431
],
432432
deps = [
433+
":scalar_card_data_table",
434+
":scalar_card_line_chart",
433435
":scalar_card_types",
434436
":utils",
435437
"//tensorbored/webapp:app_state",
@@ -446,8 +448,11 @@ tf_ng_module(
446448
"//tensorbored/webapp/metrics/data_source",
447449
"//tensorbored/webapp/metrics/store",
448450
"//tensorbored/webapp/metrics/views/main_view:common_selectors",
451+
"//tensorbored/webapp/runs:types",
449452
"//tensorbored/webapp/types",
450453
"//tensorbored/webapp/types:ui",
454+
"//tensorbored/webapp/widgets/card_fob:types",
455+
"//tensorbored/webapp/widgets/data_table:types",
451456
"//tensorbored/webapp/widgets/experiment_alias",
452457
"//tensorbored/webapp/widgets/intersection_observer",
453458
"//tensorbored/webapp/widgets/line_chart_v2",

tensorbored/webapp/metrics/views/card_renderer/superimposed_card_component.ng.html

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
[userViewBox]="userViewBox"
132132
(onViewBoxOverridden)="isViewBoxOverridden = $event"
133133
(viewBoxChanged)="onViewBoxChanged($event)"
134+
[customChartOverlayTemplate]="lineChartCustomXAxisVis"
134135
></line-chart>
135136
<ng-template
136137
#tooltip
@@ -194,3 +195,64 @@
194195
</ng-template>
195196
</div>
196197
</div>
198+
<ng-container *ngIf="showDataTable()">
199+
<div
200+
#dataTableContainer
201+
[ngClass]="{
202+
'data-table-container': true,
203+
'expanded': tableExpanded
204+
}"
205+
>
206+
<scalar-card-data-table
207+
[chartMetadataMap]="chartMetadataMap"
208+
[dataSeries]="dataSeries"
209+
[stepOrLinkedTimeSelection]="stepOrLinkedTimeSelection"
210+
[columnHeaders]="columnHeaders"
211+
[sortingInfo]="sortingInfo"
212+
[columnCustomizationEnabled]="false"
213+
[columnContextMenusEnabled]="false"
214+
[smoothingEnabled]="smoothingEnabled"
215+
[columnFilters]="emptyColumnFilters"
216+
[runToHparamMap]="emptyRunToHparamMap"
217+
[selectableColumns]="emptySelectableColumns"
218+
[numColumnsLoaded]="0"
219+
[numColumnsToLoad]="0"
220+
(sortDataBy)="sortDataBy($event)"
221+
>
222+
</scalar-card-data-table>
223+
</div>
224+
<div class="bottom-area">
225+
<button
226+
*ngIf="canExpandTable()"
227+
mat-icon-button
228+
class="expand-button"
229+
aria-label="Expand Table"
230+
[title]="shouldExpandTable() ? 'Expand Table' : 'Collapse Table'"
231+
(click)="toggleTableExpanded()"
232+
>
233+
<mat-icon
234+
[svgIcon]="shouldExpandTable() ? 'expand_more_24px' : 'expand_less_24px'"
235+
></mat-icon>
236+
</button>
237+
</div>
238+
</ng-container>
239+
<ng-template
240+
#lineChartCustomXAxisVis
241+
let-viewExtent="viewExtent"
242+
let-domDim="domDimension"
243+
let-xScale="xScale"
244+
let-interactionState="interactionState"
245+
>
246+
<ng-container *ngIf="showFobController()">
247+
<scalar-card-fob-controller
248+
[disableInteraction]="interactionState !== 'NONE'"
249+
[timeSelection]="stepOrLinkedTimeSelection"
250+
[scale]="xScale"
251+
[minMaxHorizontalViewExtend]="viewExtent.x"
252+
[minMaxStep]="minMaxStep"
253+
[axisSize]="domDim.width"
254+
(onTimeSelectionChanged)="onTimeSelectionChanged.emit($event)"
255+
(onTimeSelectionToggled)="onFobRemoved()"
256+
></scalar-card-fob-controller>
257+
</ng-container>
258+
</ng-template>

tensorbored/webapp/metrics/views/card_renderer/superimposed_card_component.scss

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ limitations under the License.
2222
.superimposed-card {
2323
display: flex;
2424
flex-direction: column;
25-
height: 100%;
25+
flex-grow: 1;
26+
min-height: 0;
2627
border: 2px solid #673ab7;
2728
border-radius: 4px;
2829
background: var(--tb-card-background, #fff);
@@ -122,6 +123,7 @@ limitations under the License.
122123
overflow: hidden;
123124
position: relative;
124125
padding: 8px;
126+
resize: vertical;
125127

126128
mat-spinner {
127129
position: absolute;
@@ -136,6 +138,30 @@ limitations under the License.
136138
}
137139
}
138140

141+
$_data_table_initial_height: 100px;
142+
143+
.data-table-container {
144+
height: $_data_table_initial_height;
145+
min-height: $_data_table_initial_height;
146+
max-height: 50em;
147+
overflow: auto;
148+
resize: vertical;
149+
150+
&.expanded {
151+
height: auto;
152+
}
153+
}
154+
155+
.bottom-area {
156+
display: flex;
157+
flex-direction: column;
158+
align-items: center;
159+
160+
.expand-button {
161+
color: var(--tb-secondary-text, #666);
162+
}
163+
}
164+
139165
.tooltip {
140166
background: rgba(0, 0, 0, 0.8);
141167
border-radius: 4px;

tensorbored/webapp/metrics/views/card_renderer/superimposed_card_component.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ import {
2222
ViewChild,
2323
} from '@angular/core';
2424
import {DataLoadState} from '../../../types/data';
25+
import {
26+
TimeSelection,
27+
TimeSelectionAffordance,
28+
TimeSelectionToggleAffordance,
29+
} from '../../../widgets/card_fob/card_fob_types';
2530
import {
2631
Formatter,
2732
intlNumberFormatter,
@@ -38,10 +43,20 @@ import {
3843
} from '../../../widgets/line_chart_v2/types';
3944
import {TooltipSort, XAxisType, SuperimposedCardId} from '../../types';
4045
import {
46+
ColumnHeader,
47+
SortingInfo,
48+
SortingOrder,
49+
DiscreteFilter,
50+
IntervalFilter,
51+
} from '../../../widgets/data_table/types';
52+
import {
53+
MinMaxStep,
4154
ScalarCardDataSeries,
4255
ScalarCardSeriesMetadata,
4356
ScalarCardSeriesMetadataMap,
4457
} from './scalar_card_types';
58+
import {isDatumVisible} from './utils';
59+
import {RunToHparamMap} from '../../../runs/types';
4560

4661
type ScalarTooltipDatum = TooltipDatum<
4762
ScalarCardSeriesMetadata & {
@@ -81,6 +96,9 @@ export class SuperimposedCardComponent {
8196
@Input() useDarkMode!: boolean;
8297
@Input() forceSvg!: boolean;
8398
@Input() userViewBox: Extent | null = null;
99+
@Input() stepOrLinkedTimeSelection: TimeSelection | undefined;
100+
@Input() minMaxStep: MinMaxStep | undefined;
101+
@Input() columnHeaders: ColumnHeader[] = [];
84102

85103
@Output() onDeleteCard = new EventEmitter<void>();
86104
@Output() onRemoveTag = new EventEmitter<string>();
@@ -91,6 +109,12 @@ export class SuperimposedCardComponent {
91109
onSymlogLinearThresholdChanged = new EventEmitter<number>();
92110
@Output() onYAxisScaleChanged = new EventEmitter<ScaleType>();
93111
@Output() onXAxisScaleChanged = new EventEmitter<ScaleType>();
112+
@Output() onTimeSelectionChanged = new EventEmitter<{
113+
timeSelection: TimeSelection;
114+
affordance?: TimeSelectionAffordance;
115+
}>();
116+
@Output() onStepSelectorToggled =
117+
new EventEmitter<TimeSelectionToggleAffordance>();
94118

95119
showFullWidth = false;
96120
showFullHeight = false;
@@ -108,10 +132,25 @@ export class SuperimposedCardComponent {
108132
@ViewChild(LineChartComponent)
109133
lineChart?: LineChartComponent;
110134

135+
@ViewChild('dataTableContainer')
136+
dataTableContainer?: ElementRef;
137+
111138
constructor(private readonly ref: ElementRef) {}
112139

113140
isViewBoxOverridden = false;
114141
additionalItemsCount = 0;
142+
tableExpanded = false;
143+
sortingInfo: SortingInfo = {
144+
name: 'run',
145+
order: SortingOrder.ASCENDING,
146+
};
147+
148+
readonly emptyColumnFilters = new Map<
149+
string,
150+
DiscreteFilter | IntervalFilter
151+
>();
152+
readonly emptyRunToHparamMap: RunToHparamMap = {};
153+
readonly emptySelectableColumns: ColumnHeader[] = [];
115154

116155
private static nextScale(current: ScaleType): ScaleType {
117156
switch (current) {
@@ -283,6 +322,44 @@ export class SuperimposedCardComponent {
283322
return scalarTooltipData.slice(0, MAX_TOOLTIP_ITEMS);
284323
}
285324

325+
showFobController(): boolean {
326+
return this.xAxisType === XAxisType.STEP && !!this.minMaxStep;
327+
}
328+
329+
showDataTable(): boolean {
330+
return (
331+
this.xAxisType === XAxisType.STEP && !!this.stepOrLinkedTimeSelection
332+
);
333+
}
334+
335+
onFobRemoved() {
336+
this.onStepSelectorToggled.emit(TimeSelectionToggleAffordance.FOB_DESELECT);
337+
}
338+
339+
sortDataBy(sortingInfo: SortingInfo) {
340+
this.sortingInfo = sortingInfo;
341+
}
342+
343+
canExpandTable(): boolean {
344+
const visibleRuns = this.dataSeries.filter((datum) => {
345+
return isDatumVisible(datum, this.chartMetadataMap);
346+
});
347+
return visibleRuns.length > 3;
348+
}
349+
350+
shouldExpandTable(): boolean {
351+
return Boolean(
352+
this.dataTableContainer?.nativeElement.style.height || !this.tableExpanded
353+
);
354+
}
355+
356+
toggleTableExpanded() {
357+
this.tableExpanded = this.shouldExpandTable();
358+
if (this.dataTableContainer) {
359+
this.dataTableContainer.nativeElement.style.height = '';
360+
}
361+
}
362+
286363
onRemoveTagClick(tag: string, event: Event) {
287364
event.stopPropagation();
288365
this.onRemoveTag.emit(tag);

0 commit comments

Comments
 (0)