support menu column (#15754)

* support menu column

* comments
This commit is contained in:
Alan Ren
2021-06-21 10:36:55 -07:00
committed by GitHub
parent 22dcf7777c
commit 0e1a8ab8ea
5 changed files with 80 additions and 11 deletions

View File

@@ -260,7 +260,8 @@ declare module 'azdata' {
export enum DeclarativeDataType { export enum DeclarativeDataType {
component = 'component' component = 'component',
menu = 'menu'
} }
export type DeclarativeTableRowSelectedEvent = { export type DeclarativeTableRowSelectedEvent = {
@@ -394,11 +395,23 @@ declare module 'azdata' {
selectedRow?: number; selectedRow?: number;
} }
export interface DeclarativeTableMenuCellValue {
/**
* commands for the menu. Use an array for a group and menu separators will be added.
*/
commands: (string | string[])[];
/**
* context that will be passed to the commands.
*/
context: { [key: string]: string | boolean | number } | string | boolean | number | undefined
}
export interface DeclarativeTableCellValue { export interface DeclarativeTableCellValue {
/** /**
* The cell value * The cell value
*/ */
value: string | number | boolean | Component; value: string | number | boolean | Component | DeclarativeTableMenuCellValue;
/** /**
* The aria-label of the cell * The aria-label of the cell
*/ */

View File

@@ -387,7 +387,8 @@ export enum DeclarativeDataType {
category = 'category', category = 'category',
boolean = 'boolean', boolean = 'boolean',
editableCategory = 'editableCategory', editableCategory = 'editableCategory',
component = 'component' component = 'component',
menu = 'menu'
} }
export enum CardType { export enum CardType {

View File

@@ -1,10 +1,10 @@
<table role="grid" #container *ngIf="columns" class="declarative-table" [attr.aria-label]="ariaLabel" [ngStyle]="CSSStyles" (focusin)="onFocusIn()" (focusout)="onFocusOut()"> <table role="grid" #container *ngIf="columns" class="declarative-table" [attr.aria-label]="ariaLabel"
[ngStyle]="CSSStyles" (focusin)="onFocusIn()" (focusout)="onFocusOut()">
<thead role="rowgroup"> <thead role="rowgroup">
<tr role="row"> <tr role="row">
<ng-container *ngFor="let column of columns; let c = index;"> <ng-container *ngFor="let column of columns; let c = index;">
<th class="declarative-table-header" aria-sort="none" [style.width]="getColumnWidth(column)" <th class="declarative-table-header" aria-sort="none" [style.width]="getColumnWidth(column)"
[ngStyle]="column.headerCssStyles" [ngStyle]="column.headerCssStyles" [attr.aria-label]="getHeaderAriaLabel(c)">
[attr.aria-label]="getHeaderAriaLabel(c)">
{{column.displayName}} {{column.displayName}}
<checkbox *ngIf="headerCheckboxVisible(c)" [checked]="isHeaderChecked(c)" <checkbox *ngIf="headerCheckboxVisible(c)" [checked]="isHeaderChecked(c)"
[aria-label]="getCheckAllColumnAriaLabel(c)" (onChange)="onHeaderCheckBoxChanged($event,c)" [aria-label]="getCheckAllColumnAriaLabel(c)" (onChange)="onHeaderCheckBoxChanged($event,c)"
@@ -20,8 +20,7 @@
<ng-container *ngFor="let cellData of row;let c = index;trackBy:trackByFnCols"> <ng-container *ngFor="let cellData of row;let c = index;trackBy:trackByFnCols">
<td class="declarative-table-cell" [style.width]="getColumnWidth(c)" <td class="declarative-table-cell" [style.width]="getColumnWidth(c)"
[attr.aria-label]="getAriaLabel(r, c)" [attr.aria-label]="getAriaLabel(r, c)"
[ngStyle]="mergeCss(columns[c].rowCssStyles, cellData.style)" [ngStyle]="mergeCss(columns[c].rowCssStyles, cellData.style)" role="gridcell">
role="gridcell">
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" <checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)"
[enabled]="isControlEnabled(r, c)" [checked]="isChecked(r,c)" [enabled]="isControlEnabled(r, c)" [checked]="isChecked(r,c)"
[ngStyle]="mergeCss(columns[c].rowCssStyles, cellData.style)"> [ngStyle]="mergeCss(columns[c].rowCssStyles, cellData.style)">
@@ -42,6 +41,8 @@
<model-component-wrapper *ngIf="isComponent(c) && getItemDescriptor(cellData.value)" <model-component-wrapper *ngIf="isComponent(c) && getItemDescriptor(cellData.value)"
[descriptor]="getItemDescriptor(cellData.value)" [modelStore]="modelStore"> [descriptor]="getItemDescriptor(cellData.value)" [modelStore]="modelStore">
</model-component-wrapper> </model-component-wrapper>
<button *ngIf="isContextMenuColumn(c)" [title]="contextMenuButtonTitle" [attr.aria-label]="contextMenuButtonTitle" class="codicon toggle-more" (click)="onContextMenuRequested($event,r,c)">
</button>
</td> </td>
</ng-container> </ng-container>
</tr> </tr>

View File

@@ -19,13 +19,18 @@ import { ILogService } from 'vs/platform/log/common/log';
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { equals } from 'vs/base/common/objects'; import { equals } from 'vs/base/common/objects';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions';
import { IAction, Separator } from 'vs/base/common/actions';
export enum DeclarativeDataType { export enum DeclarativeDataType {
string = 'string', string = 'string',
category = 'category', category = 'category',
boolean = 'boolean', boolean = 'boolean',
editableCategory = 'editableCategory', editableCategory = 'editableCategory',
component = 'component' component = 'component',
menu = 'menu'
} }
@Component({ @Component({
@@ -53,7 +58,9 @@ export default class DeclarativeTableComponent extends ContainerBase<any, azdata
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef, @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef, @Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(ILogService) logService: ILogService, @Inject(ILogService) logService: ILogService,
@Inject(IThemeService) themeService: IThemeService @Inject(IThemeService) themeService: IThemeService,
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
@Inject(IInstantiationService) private instantiationService: IInstantiationService
) { ) {
super(changeRef, el, logService); super(changeRef, el, logService);
this._colorTheme = themeService.getColorTheme(); this._colorTheme = themeService.getColorTheme();
@@ -89,6 +96,10 @@ export default class DeclarativeTableComponent extends ContainerBase<any, azdata
return column.valueType === DeclarativeDataType.boolean; return column.valueType === DeclarativeDataType.boolean;
} }
public isContextMenuColumn(colIdx: number): boolean {
return this.columns[colIdx].valueType === DeclarativeDataType.menu;
}
public isControlEnabled(rowIdx: number, colIdx: number): boolean { public isControlEnabled(rowIdx: number, colIdx: number): boolean {
const cellData = this.data[rowIdx][colIdx]; const cellData = this.data[rowIdx][colIdx];
const column: azdata.DeclarativeTableColumn = this.columns[colIdx]; const column: azdata.DeclarativeTableColumn = this.columns[colIdx];
@@ -346,6 +357,41 @@ export default class DeclarativeTableComponent extends ContainerBase<any, azdata
} }
} }
public get contextMenuButtonTitle(): string {
return localize('declarativeTable.showActions', "Show Actions");
}
public onContextMenuRequested(event: MouseEvent, row: number, column: number) {
const cellValue = this.data[row][column].value as azdata.DeclarativeTableMenuCellValue;
const actions: IAction[] = [];
let addSeparator = false;
for (const [index, command] of cellValue.commands.entries()) {
const isCommand = typeof command === 'string';
if (addSeparator || (!isCommand && index !== 0)) {
actions.push(new Separator());
}
if (typeof command === 'string') {
addSeparator = false;
actions.push(this.createMenuItem(command));
} else {
addSeparator = true;
actions.push(...command.map(cmd => {
return this.createMenuItem(cmd);
}));
}
}
this.contextMenuService.showContextMenu({
getAnchor: () => event.currentTarget as HTMLElement,
getActions: () => actions,
getActionsContext: () => cellValue.context
});
}
private createMenuItem(commandId: string): MenuItemAction {
const command = MenuRegistry.getCommand(commandId);
return this.instantiationService.createInstance(MenuItemAction, command, undefined, { shouldForwardArgs: true });
}
public onKey(e: KeyboardEvent, row: number) { public onKey(e: KeyboardEvent, row: number) {
// Ignore the bubble up events // Ignore the bubble up events
if (e.target !== e.currentTarget) { if (e.target !== e.currentTarget) {

View File

@@ -26,3 +26,11 @@
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
.declarative-table .declarative-table-cell .codicon.toggle-more {
border-width: 0px;
height: 16px;
width: 26px;
vertical-align: middle;
cursor: pointer;
}