Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions ui/core/components/detailed_results/metrics_table/metrics_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { ActionId } from '../../../proto_utils/action_id';
import { ActionMetrics, AuraMetrics, ResourceMetrics, UnitMetrics } from '../../../proto_utils/sim_result';
import { TypedEvent } from '../../../typed_event';
import { ResultComponent, ResultComponentConfig, SimResultData } from '../result_component';

declare let $: any;
import { TableSorter } from './table_sorter';

export enum ColumnSortType {
None,
Expand All @@ -33,7 +32,8 @@ export abstract class MetricsTable<T extends ActionMetrics | AuraMetrics | UnitM
private readonly columnConfigs: Array<MetricsColumnConfig<T>>;

protected readonly tableElem: HTMLElement;
protected readonly bodyElem: HTMLElement;
protected readonly bodyElem: HTMLTableSectionElement;
private readonly sorter: TableSorter;

readonly onUpdate = new TypedEvent<void>('MetricsTableUpdate');

Expand All @@ -50,10 +50,10 @@ export abstract class MetricsTable<T extends ActionMetrics | AuraMetrics | UnitM
</table>,
);

this.tableElem = this.rootElem.getElementsByClassName('metrics-table')[0] as HTMLTableSectionElement;
this.bodyElem = this.rootElem.getElementsByClassName('metrics-table-body')[0] as HTMLElement;
this.tableElem = this.rootElem.getElementsByClassName('metrics-table')[0] as HTMLTableElement;
this.bodyElem = this.rootElem.getElementsByClassName('metrics-table-body')[0] as HTMLTableSectionElement;

const headerRowElem = this.rootElem.getElementsByClassName('metrics-table-header-row')[0] as HTMLElement;
const headerRowElem = this.rootElem.getElementsByClassName('metrics-table-header-row')[0] as HTMLTableRowElement;
this.columnConfigs.forEach(columnConfig => {
const headerCell = document.createElement('th');
const tooltip = columnConfig.tooltip || TOOLTIP_METRIC_LABELS[columnConfig.name as keyof typeof TOOLTIP_METRIC_LABELS];
Expand All @@ -74,13 +74,15 @@ export abstract class MetricsTable<T extends ActionMetrics | AuraMetrics | UnitM
headerRowElem.appendChild(headerCell);
});

const sortList = this.columnConfigs
.map((config, i) => [i, config.sort == ColumnSortType.Ascending ? 0 : 1])
.filter(sortData => this.columnConfigs[sortData[0]].sort);
const sortCol = this.columnConfigs.findIndex(v => !!v.sort);

$(this.tableElem).tablesorter({
sortList: sortList,
cssChildRow: 'child-metric',
this.sorter = new TableSorter({
tableHead: headerRowElem,
tableBody: this.bodyElem,
dataSetKey: 'text',
childRowClass: 'child-metric',
defaultSortCol: sortCol !== -1 ? sortCol : 0,
defaultSortDesc: sortCol !== -1 && this.columnConfigs[sortCol].sort == ColumnSortType.Descending,
});
}

Expand Down Expand Up @@ -168,7 +170,7 @@ export abstract class MetricsTable<T extends ActionMetrics | AuraMetrics | UnitM
this.rootElem.classList.remove('hide');
}
groupedMetrics.forEach(group => this.addGroup(group));
$(this.tableElem).trigger('update');
this.sorter.update();
this.onUpdate.emit(resultData.eventID);
}

Expand Down
106 changes: 106 additions & 0 deletions ui/core/components/detailed_results/metrics_table/table_sorter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
type TableSorterRowData = {
readonly values: ReadonlyArray<string | number>;
readonly rowElement: HTMLTableRowElement;
};

type TableSorterConfig = {
tableHead: HTMLTableRowElement;
tableBody: HTMLTableSectionElement;
dataSetKey: string;
childRowClass: string;
defaultSortCol: number;
defaultSortDesc: boolean;
};

export class TableSorter {
private readonly cfg: Readonly<TableSorterConfig>;
private readonly rowData: Array<TableSorterRowData & { children?: Array<TableSorterRowData> }> = [];
private sortCol = -1;
private sortDesc: Array<boolean>;

constructor(config: TableSorterConfig) {
this.cfg = config;

this.sortCol = this.cfg.defaultSortCol;
this.sortDesc = Array(config.tableHead.cells.length).fill(true);
this.sortDesc[config.defaultSortCol] = config.defaultSortDesc;

for (let i = 0; i < config.tableHead.cells.length; i++) {
config.tableHead.cells[i].addEventListener('click', () => this.setSort(i));
}
}

private sortFunc = (a: TableSorterRowData, b: TableSorterRowData) => {
const aValue = a.values[this.sortCol];
const bValue = b.values[this.sortCol];
const asc = !this.sortDesc[this.sortCol];
if (typeof aValue === 'number' && typeof bValue === 'number') {
return asc ? aValue - bValue : bValue - aValue;
} else {
return asc ? aValue.toString().localeCompare(bValue.toString()) : bValue.toString().localeCompare(aValue.toString());
}
};

private sort() {
if (this.rowData.length === 0) return;
if (typeof this.rowData[0].values[this.sortCol] === 'undefined') return;

this.rowData.sort(this.sortFunc);
for (const row of this.rowData) {
if (row.children) row.children.sort(this.sortFunc);
}

const body = this.cfg.tableBody;
body.innerHTML = '';
for (const row of this.rowData) {
body.appendChild(row.rowElement);
if (row.children) {
for (const childRow of row.children) {
body.appendChild(childRow.rowElement);
}
}
}
}

/**
* Set column to sort by. If set to the current sort column the order will be reversed.
* @param column If omitted use default column.
*/
setSort(column?: number) {
if (typeof column !== 'number') column = this.cfg.defaultSortCol;
this.sortDesc[column] = !this.sortDesc[column];
this.sortCol = column;
this.sort();
}

private parseRowValues(rowElement: HTMLTableRowElement): Array<number | string> {
const values: Array<string | number> = [];
for (const cell of rowElement.cells) {
const val = cell.dataset[this.cfg.dataSetKey] ?? cell.innerText;
const numVal = parseFloat(val);
values.push(!isNaN(numVal) ? numVal : val);
}
return values;
}

/**
* Update internal data structure for changed table data.
*/
update() {
this.rowData.length = 0;

for (const rowElement of this.cfg.tableBody.rows) {
const values = this.parseRowValues(rowElement);
if (!rowElement.classList.contains(this.cfg.childRowClass)) {
this.rowData.push({ values, rowElement });
} else {
const parentData = this.rowData[this.rowData.length - 1];
if (!parentData) throw new Error("Child row has no parent!");
if (!parentData.children) parentData.children = [];
parentData.children.push({ values, rowElement });
}
}

this.sort();
}
}
7 changes: 0 additions & 7 deletions ui/index_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@
crossorigin="anonymous"
referrerpolicy="no-referrer" />

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.3/js/jquery.tablesorter.min.js"
integrity="sha512-qzgd5cYSZcosqpzpn7zF2ZId8f/8CHmFKZ8j7mU4OUXTNRd5g+ZHBPsgKEwoqxCtdQvExE5LprwwPAgoicguNg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>

<!-- Load the top-level ui/index.ts -->
<script src="../index.ts" type="module"></script>
<script src="./index.ts" type="module"></script>
Expand Down
Loading