mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-17 17:22:42 -05:00
Change tables to make them work for our scenario (#12193)
* Change tables to make them work for our scenario * Comments & deprecate API * Disable selections by default
This commit is contained in:
@@ -372,6 +372,17 @@ export abstract class ContainerBase<T, TPropertyBag extends azdata.ComponentProp
|
||||
return;
|
||||
}
|
||||
|
||||
public mergeCss(...styles: azdata.CssStyles[]): azdata.CssStyles {
|
||||
const x = styles.reduce((previous, current) => {
|
||||
if (current) {
|
||||
return Object.assign(previous, current);
|
||||
}
|
||||
return previous;
|
||||
}, {});
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
protected onItemsUpdated(): void {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,45 @@
|
||||
<table role=grid #container *ngIf="columns" class="declarative-table" [style.height]="getHeight()" [style.width]="getWidth()" [attr.aria-label]="ariaLabel">
|
||||
<table role=grid #container *ngIf="columns" class="declarative-table" [style.height]="getHeight()"
|
||||
[style.width]="getWidth()" [attr.aria-label]="ariaLabel">
|
||||
<thead>
|
||||
<ng-container *ngFor="let column of columns; let c = index;">
|
||||
<th class="declarative-table-header" aria-sort="none" [style.width]="getColumnWidth(column)" [attr.aria-label]="column.ariaLabel" [ngStyle]="column.headerCssStyles" role="columnheader">
|
||||
{{column.displayName}}
|
||||
<checkbox *ngIf="isCheckBox(c)" [checked]="isHeaderChecked(c)" (onChange)="onHeaderCheckBoxChanged($event,c)" label="" ></checkbox>
|
||||
</th>
|
||||
<th class="declarative-table-header" aria-sort="none" [style.width]="getColumnWidth(column)"
|
||||
[attr.aria-label]="column.ariaLabel" [ngStyle]="column.headerCssStyles" role="columnheader">
|
||||
{{column.displayName}}
|
||||
<checkbox *ngIf="isCheckBox(c)" [checked]="isHeaderChecked(c)"
|
||||
(onChange)="onHeaderCheckBoxChanged($event,c)" label=""></checkbox>
|
||||
</th>
|
||||
</ng-container>
|
||||
</thead>
|
||||
<ng-container *ngIf="data">
|
||||
<ng-container *ngFor="let row of data;let r = index;">
|
||||
<tr class="declarative-table-row">
|
||||
<ng-container *ngFor="let cellData of row;let c = index;trackBy:trackByFnCols">
|
||||
<td class="declarative-table-cell" [style.width]="getColumnWidth(c)" [attr.aria-label]="getAriaLabel(r, c)" [ngStyle]="columns[c].rowCssStyles">
|
||||
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" [enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"></checkbox>
|
||||
<select-box *ngIf="isSelectBox(c)" [options]="getOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="getSelectedOptionDisplayName(r,c)"></select-box>
|
||||
<editable-select-box *ngIf="isEditableSelectBox(c)" [options]="getOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="getSelectedOptionDisplayName(r,c)"></editable-select-box>
|
||||
<input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
|
||||
<ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container>
|
||||
<model-component-wrapper *ngIf="isComponent(c) && getItemDescriptor(cellData)" [descriptor]="getItemDescriptor(cellData)" [modelStore]="modelStore"></model-component-wrapper>
|
||||
</td>
|
||||
</ng-container>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="data.length > 0">
|
||||
<ng-container *ngFor="let row of data;let r = index;">
|
||||
<tr class="declarative-table-row" [class.selected]="isRowSelected(r)">
|
||||
<ng-container *ngFor="let cellData of row;let c = index;trackBy:trackByFnCols">
|
||||
<td class="declarative-table-cell" [style.width]="getColumnWidth(c)"
|
||||
[attr.aria-label]="getAriaLabel(r, c)"
|
||||
[ngStyle]="mergeCss(columns[c].rowCssStyles, cellData.style)">
|
||||
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)"
|
||||
[enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"
|
||||
[ngStyle]="mergeCss(columns[c].rowCssStyles, cellData.style)">
|
||||
</checkbox>
|
||||
<select-box *ngIf="isSelectBox(c)" [options]="getOptions(c)"
|
||||
(onDidSelect)="onSelectBoxChanged($event,r,c)"
|
||||
[selectedOption]="getSelectedOptionDisplayName(r,c)">
|
||||
</select-box>
|
||||
<editable-select-box *ngIf="isEditableSelectBox(c)" [options]="getOptions(c)"
|
||||
(onDidSelect)="onSelectBoxChanged($event,r,c)"
|
||||
[selectedOption]="getSelectedOptionDisplayName(r,c)">
|
||||
</editable-select-box>
|
||||
<input-box *ngIf="isInputBox(c)" [value]="cellData.value"
|
||||
(onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
|
||||
<span *ngIf="isLabel(c)" (click)="onCellClick(r)">
|
||||
{{cellData.value}}
|
||||
</span>
|
||||
<model-component-wrapper *ngIf="isComponent(c) && getItemDescriptor(cellData)"
|
||||
[descriptor]="getItemDescriptor(cellData)" [modelStore]="modelStore">
|
||||
</model-component-wrapper>
|
||||
</td>
|
||||
</ng-container>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</table>
|
||||
</ng-container>
|
||||
</table>
|
||||
|
||||
@@ -30,12 +30,13 @@ export enum DeclarativeDataType {
|
||||
selector: 'modelview-declarativeTable',
|
||||
templateUrl: decodeURI(require.toUrl('./declarativeTable.component.html'))
|
||||
})
|
||||
export default class DeclarativeTableComponent extends ContainerBase<any> implements IComponent, OnDestroy, AfterViewInit {
|
||||
export default class DeclarativeTableComponent extends ContainerBase<any, azdata.DeclarativeTableProperties> implements IComponent, OnDestroy, AfterViewInit {
|
||||
@Input() descriptor: IComponentDescriptor;
|
||||
@Input() modelStore: IModelStore;
|
||||
|
||||
private data: any[][] = [];
|
||||
private _data: azdata.DeclarativeTableCellValue[][] = [];
|
||||
private columns: azdata.DeclarativeTableColumn[] = [];
|
||||
private _selectedRow: number;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||
@@ -77,7 +78,12 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
|
||||
public isChecked(rowIdx: number, colIdx: number): boolean {
|
||||
let cellData = this.data[rowIdx][colIdx];
|
||||
return cellData;
|
||||
if (cellData?.value === false) {
|
||||
return false;
|
||||
}
|
||||
// Disabling it to check for null and undefined.
|
||||
// eslint-disable-next-line eqeqeq
|
||||
return cellData != undefined;
|
||||
}
|
||||
|
||||
public onInputBoxChanged(e: string, rowIdx: number, colIdx: number): void {
|
||||
@@ -86,10 +92,11 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
|
||||
public onCheckBoxChanged(e: boolean, rowIdx: number, colIdx: number): void {
|
||||
this.onCellDataChanged(e, rowIdx, colIdx);
|
||||
// If all of the rows in that column are now checked, let's update the header.
|
||||
if (this.columns[colIdx].showCheckAll) {
|
||||
if (e) {
|
||||
for (let rowIdx = 0; rowIdx < this.data.length; rowIdx++) {
|
||||
if (!this.data[rowIdx][colIdx]) {
|
||||
if (this.data[rowIdx][colIdx].value === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -102,20 +109,20 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
public onHeaderCheckBoxChanged(e: boolean, colIdx: number): void {
|
||||
this.columns[colIdx].isChecked = e;
|
||||
this.data.forEach((row, rowIdx) => {
|
||||
if (row[colIdx] !== e) {
|
||||
if (row[colIdx].value !== e) {
|
||||
this.onCellDataChanged(e, rowIdx, colIdx);
|
||||
}
|
||||
});
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
public trackByFnCols(index: number, item: any): any {
|
||||
public trackByFnCols(index: number, _item: any): number {
|
||||
return index;
|
||||
}
|
||||
|
||||
public onSelectBoxChanged(e: ISelectData | string, rowIdx: number, colIdx: number): void {
|
||||
|
||||
let column: azdata.DeclarativeTableColumn = this.columns[colIdx];
|
||||
|
||||
if (column.categoryValues) {
|
||||
if (typeof e === 'string') {
|
||||
let category = find(column.categoryValues, c => c.displayName === e);
|
||||
@@ -130,8 +137,8 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
}
|
||||
}
|
||||
|
||||
private onCellDataChanged(newValue: any, rowIdx: number, colIdx: number): void {
|
||||
this.data[rowIdx][colIdx] = newValue;
|
||||
private onCellDataChanged(newValue: string | number | boolean | any, rowIdx: number, colIdx: number): void {
|
||||
this.data[rowIdx][colIdx].value = newValue;
|
||||
this.setPropertyFromUI<any[][]>((props, value) => props.data = value, this.data);
|
||||
let newCellData: azdata.TableCell = {
|
||||
row: rowIdx,
|
||||
@@ -177,11 +184,11 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
let column: azdata.DeclarativeTableColumn = this.columns[colIdx];
|
||||
let cellData = this.data[rowIdx][colIdx];
|
||||
if (cellData && column.categoryValues) {
|
||||
let category = find(column.categoryValues, v => v.name === cellData);
|
||||
let category = find(column.categoryValues, v => v.name === cellData.value);
|
||||
if (category) {
|
||||
return category.displayName;
|
||||
} else if (this.isEditableSelectBox(colIdx)) {
|
||||
return cellData;
|
||||
return String(cellData.value);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
@@ -192,7 +199,19 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
|
||||
public getAriaLabel(rowIdx: number, colIdx: number): string {
|
||||
const cellData = this.data[rowIdx][colIdx];
|
||||
return this.isLabel(colIdx) ? (cellData && cellData !== '' ? cellData : localize('blankValue', "blank")) : '';
|
||||
if (this.isLabel(colIdx)) {
|
||||
if (cellData) {
|
||||
if (cellData.ariaLabel) {
|
||||
return cellData.ariaLabel;
|
||||
} else if (cellData.value) {
|
||||
return String(cellData.value);
|
||||
}
|
||||
} else {
|
||||
return localize('blankValue', "blank");
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public getItemDescriptor(componentId: string): IComponentDescriptor {
|
||||
@@ -206,12 +225,34 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
this.layout();
|
||||
}
|
||||
|
||||
public setProperties(properties: { [key: string]: any; }): void {
|
||||
const newData = properties.data ?? [];
|
||||
private static ACCEPTABLE_VALUES = new Set<string>(['number', 'string', 'boolean']);
|
||||
public setProperties(properties: azdata.DeclarativeTableProperties): void {
|
||||
const basicData: any[][] = properties.data ?? [];
|
||||
const complexData: azdata.DeclarativeTableCellValue[][] = properties.dataValues;
|
||||
let finalData: azdata.DeclarativeTableCellValue[][];
|
||||
|
||||
finalData = basicData.map(row => {
|
||||
return row.map((value): azdata.DeclarativeTableCellValue => {
|
||||
if (DeclarativeTableComponent.ACCEPTABLE_VALUES.has(typeof (value))) {
|
||||
return {
|
||||
value: value
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
value: JSON.stringify(value)
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (finalData.length <= 0) {
|
||||
finalData = complexData;
|
||||
}
|
||||
|
||||
this.columns = properties.columns ?? [];
|
||||
|
||||
// check whether the data property is changed before actually setting the properties.
|
||||
const isDataPropertyChanged = !arrayEquals(this.data, newData ?? [], (a, b) => {
|
||||
const isDataPropertyChanged = !arrayEquals(this.data, finalData ?? [], (a, b) => {
|
||||
return arrayEquals(a, b);
|
||||
});
|
||||
|
||||
@@ -221,15 +262,45 @@ export default class DeclarativeTableComponent extends ContainerBase<any> implem
|
||||
// so that the events can be passed upwards through the control hierarchy.
|
||||
if (isDataPropertyChanged) {
|
||||
this.clearContainer();
|
||||
this.data = newData;
|
||||
this._data = finalData;
|
||||
this.data?.forEach(row => {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
if (this.isComponent(i)) {
|
||||
this.addToContainer(this.getItemDescriptor(row[i] as string), undefined);
|
||||
this.addToContainer(this.getItemDescriptor(row[i].value as string), undefined);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
super.setProperties(properties);
|
||||
}
|
||||
|
||||
public get data(): azdata.DeclarativeTableCellValue[][] {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
public isRowSelected(row: number): boolean {
|
||||
// Only react when the user wants you to
|
||||
if (this.getProperties().selectEffect !== true) {
|
||||
return false;
|
||||
}
|
||||
return this._selectedRow === row;
|
||||
}
|
||||
|
||||
public onCellClick(row: number) {
|
||||
// Only react when the user wants you to
|
||||
if (this.getProperties().selectEffect !== true) {
|
||||
return;
|
||||
}
|
||||
if (!this.isRowSelected(row)) {
|
||||
this._selectedRow = row;
|
||||
this._changeRef.detectChanges();
|
||||
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.onDidClick,
|
||||
args: {
|
||||
row
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,3 +26,7 @@
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.declarative-table-row.selected {
|
||||
background-color: rgb(0, 120, 215);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user