Skip to content

Commit db29bcc

Browse files
authored
Splits context menu into separate component (#6788)
## Motivation for features / changes Splits DataTableComponent's context menu functionality into its own component to be compatible with future updates that will enable dynamic custom modal creation (for alleviating [stacking context issues](#6737 (comment))) ## Technical description of changes - No new functionality - simply moves the context menu related template and logic to a new ContextMenuComponent - Parameterizes some context menu tests for legibility. ## Detailed steps to verify changes work correctly (as executed by you) - Tested manually that all context menu features work as before
1 parent 955d85a commit db29bcc

12 files changed

+653
-319
lines changed

tensorboard/webapp/widgets/data_table/BUILD

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ tf_sass_binary(
4848
],
4949
)
5050

51+
tf_sass_binary(
52+
name = "context_menu_component_styles",
53+
src = "context_menu_component.scss",
54+
deps = [
55+
"//tensorboard/webapp:angular_material_sass_deps",
56+
"//tensorboard/webapp/theme",
57+
],
58+
)
59+
5160
tf_sass_binary(
5261
name = "filter_dialog_styles",
5362
src = "filter_dialog_component.scss",
@@ -76,6 +85,7 @@ tf_ng_module(
7685
],
7786
deps = [
7887
":column_selector",
88+
":context_menu",
7989
":data_table_header",
8090
":filter_dialog",
8191
":types",
@@ -135,6 +145,27 @@ tf_ng_module(
135145
],
136146
)
137147

148+
tf_ng_module(
149+
name = "context_menu",
150+
srcs = [
151+
"context_menu_component.ts",
152+
"context_menu_module.ts",
153+
],
154+
assets = [
155+
"context_menu_component.ng.html",
156+
":context_menu_component_styles",
157+
],
158+
deps = [
159+
":types",
160+
"//tensorboard/webapp/angular:expect_angular_material_button",
161+
"//tensorboard/webapp/angular:expect_angular_material_icon",
162+
"@npm//@angular/common",
163+
"@npm//@angular/core",
164+
"@npm//@angular/forms",
165+
"@npm//rxjs",
166+
],
167+
)
168+
138169
tf_ng_module(
139170
name = "filter_dialog",
140171
srcs = [
@@ -192,12 +223,14 @@ tf_ts_library(
192223
srcs = [
193224
"column_selector_test.ts",
194225
"content_cell_component_test.ts",
226+
"context_menu_test.ts",
195227
"data_table_test.ts",
196228
"filter_dialog_test.ts",
197229
"header_cell_component_test.ts",
198230
],
199231
deps = [
200232
":column_selector",
233+
":context_menu",
201234
":data_table",
202235
":filter_dialog",
203236
":types",
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<!--
2+
@license
3+
Copyright 2024 The TensorFlow Authors. All Rights Reserved.
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
Unless required by applicable law or agreed to in writing, software
9+
distributed under the License is distributed on an "AS IS" BASIS,
10+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the License for the specific language governing permissions and
12+
limitations under the License.
13+
-->
14+
15+
<div class="context-menu">
16+
<div *ngIf="isContextMenuEmpty()" class="no-actions-message">
17+
No Actions Available
18+
</div>
19+
<button
20+
*ngIf="contextMenuHeader?.removable"
21+
class="context-menu-button"
22+
mat-button
23+
(click)="contextMenuRemoveColumn()"
24+
>
25+
<mat-icon svgIcon="close_24px"></mat-icon>Remove
26+
</button>
27+
<button
28+
*ngIf="contextMenuHeader?.sortable &&
29+
sortingInfo.order === SortingOrder.ASCENDING &&
30+
sortingInfo.name === contextMenuHeader?.name"
31+
class="context-menu-button sort-button"
32+
mat-button
33+
(click)="sortByHeader.emit(contextMenuHeader?.name)"
34+
>
35+
<mat-icon svgIcon="arrow_downward_24px"></mat-icon>Sort Descending
36+
</button>
37+
<button
38+
*ngIf="contextMenuHeader?.sortable &&
39+
(sortingInfo.order !== SortingOrder.ASCENDING ||
40+
sortingInfo.name !== contextMenuHeader?.name)"
41+
class="context-menu-button sort-button"
42+
mat-button
43+
(click)="sortByHeader.emit(contextMenuHeader?.name)"
44+
>
45+
<mat-icon svgIcon="arrow_upward_24px"></mat-icon>Sort Ascending
46+
</button>
47+
<button
48+
*ngIf="contextMenuHeader?.filterable"
49+
class="context-menu-button"
50+
mat-button
51+
(click)="openFilterMenu.emit($event)"
52+
>
53+
<mat-icon svgIcon="filter_alt_24px"></mat-icon> Filter
54+
</button>
55+
<button
56+
mat-button
57+
*ngIf="canContextMenuInsert()"
58+
class="context-menu-button"
59+
(click)="openColumnSelector.emit({event: $event, insertTo: Side.LEFT, isSubMenu: true})"
60+
>
61+
<mat-icon svgIcon="add_24px"></mat-icon>Insert Column Left
62+
</button>
63+
<button
64+
mat-button
65+
*ngIf="canContextMenuInsert()"
66+
class="context-menu-button"
67+
(click)="openColumnSelector.emit({event: $event, insertTo: Side.RIGHT, isSubMenu: true})"
68+
>
69+
<mat-icon svgIcon="add_24px"></mat-icon>Insert Column Right
70+
</button>
71+
</div>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* Copyright 2024 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
16+
@use '@angular/material' as mat;
17+
@import 'tensorboard/webapp/theme/tb_theme';
18+
19+
.context-menu {
20+
display: flex;
21+
flex-direction: column;
22+
border-radius: 4px;
23+
border: 1px solid;
24+
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
25+
border-color: mat.get-color-from-palette($tb-foreground, border);
26+
background-color: mat.get-color-from-palette($tb-background, background);
27+
28+
@include tb-dark-theme {
29+
border-color: mat.get-color-from-palette($tb-dark-foreground, border);
30+
background-color: mat.get-color-from-palette(
31+
$tb-dark-background,
32+
'background'
33+
);
34+
}
35+
36+
.context-menu-button {
37+
justify-content: left;
38+
width: 100%;
39+
text-wrap: nowrap;
40+
}
41+
42+
.no-actions-message {
43+
padding: 8px;
44+
text-wrap: nowrap;
45+
}
46+
47+
.sort-button {
48+
::ng-deep path {
49+
fill: unset;
50+
}
51+
}
52+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/* Copyright 2024 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
16+
import {
17+
ChangeDetectionStrategy,
18+
Component,
19+
EventEmitter,
20+
Input,
21+
Output,
22+
} from '@angular/core';
23+
import {ColumnHeader, Side, SortingInfo, SortingOrder} from './types';
24+
25+
@Component({
26+
selector: 'tb-data-table-context-menu',
27+
templateUrl: 'context_menu_component.ng.html',
28+
styleUrls: ['context_menu_component.css'],
29+
changeDetection: ChangeDetectionStrategy.OnPush,
30+
})
31+
export class ContextMenuComponent {
32+
@Input() contextMenuHeader: ColumnHeader | undefined = undefined;
33+
@Input() selectableColumns?: ColumnHeader[];
34+
@Input() sortingInfo!: SortingInfo;
35+
36+
@Output() removeColumn = new EventEmitter<ColumnHeader>();
37+
@Output() sortByHeader = new EventEmitter<string>();
38+
@Output() openFilterMenu = new EventEmitter<MouseEvent>();
39+
@Output() openColumnSelector = new EventEmitter<{
40+
event: MouseEvent;
41+
insertTo: Side;
42+
isSubmenu: boolean;
43+
}>();
44+
45+
readonly Side = Side;
46+
readonly SortingOrder = SortingOrder;
47+
48+
isContextMenuEmpty() {
49+
return (
50+
!this.contextMenuHeader?.removable &&
51+
!this.contextMenuHeader?.sortable &&
52+
!this.canContextMenuInsert() &&
53+
!this.contextMenuHeader?.filterable
54+
);
55+
}
56+
57+
canContextMenuInsert() {
58+
return (
59+
this.selectableColumns?.length &&
60+
this.contextMenuHeader?.movable &&
61+
this.contextMenuHeader?.type === 'HPARAM'
62+
);
63+
}
64+
65+
contextMenuRemoveColumn() {
66+
if (this.contextMenuHeader === undefined) {
67+
return;
68+
}
69+
this.removeColumn.emit(this.contextMenuHeader);
70+
}
71+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* Copyright 2024 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
16+
import {NgModule} from '@angular/core';
17+
import {CommonModule} from '@angular/common';
18+
import {MatIconModule} from '@angular/material/icon';
19+
import {MatButtonModule} from '@angular/material/button';
20+
21+
import {ContextMenuComponent} from './context_menu_component';
22+
23+
@NgModule({
24+
declarations: [ContextMenuComponent],
25+
imports: [CommonModule, MatIconModule, MatButtonModule],
26+
exports: [ContextMenuComponent],
27+
})
28+
export class ContextMenuModule {}

0 commit comments

Comments
 (0)