Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Column totals for Grouped rows will now be shown inside the groups instead of on a seperate row ([#583][#583]).
- Table actions: Copy to clipboard and Export to CSV directly from the button above the Analysis and Database table ([#589]).
- Sort grouped rows by clicking column headers ([#592]).
- Tri state on Group name column will sort by number of items in the group

### Changed

Expand Down Expand Up @@ -388,6 +390,7 @@ Skipped due to adopting odd numbering for pre releases and even number for relea
[#583]: https://github.com/certinia/debug-log-analyzer/issues/583
[#589]: https://github.com/certinia/debug-log-analyzer/issues/589
[#590]: https://github.com/certinia/debug-log-analyzer/issues/590
[#592]: https://github.com/certinia/debug-log-analyzer/issues/592

<!-- 1.16.1 -->

Expand Down
13 changes: 9 additions & 4 deletions log-viewer/modules/components/analysis-view/AnalysisView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import { Tabulator, type RowComponent } from 'tabulator-tables';
import { isVisible } from '../../Util.js';
import NumberAccessor from '../../datagrid/dataaccessor/Number.js';
import { progressFormatter } from '../../datagrid/format/Progress.js';
import { GroupCalcs } from '../../datagrid/group-calcs/GroupCalcs.js';
import { GroupCalcs } from '../../datagrid/groups/GroupCalcs.js';
import { GroupSort } from '../../datagrid/groups/GroupSort.js';
import * as CommonModules from '../../datagrid/module/CommonModules.js';
import { RowKeyboardNavigation } from '../../datagrid/module/RowKeyboardNavigation.js';
import { RowNavigation } from '../../datagrid/module/RowNavigation.js';
Expand Down Expand Up @@ -188,8 +189,10 @@ export class AnalysisView extends LitElement {
_groupBy(event: Event) {
const target = event.target as HTMLInputElement;
const fieldName = target.value.toLowerCase();

this.analysisTable?.setGroupBy(fieldName !== 'none' ? fieldName : '');
if (this.analysisTable) {
//@ts-expect-error This is a custom function added in the GroupSort custom module
this.analysisTable?.setSortedGroupBy(fieldName !== 'none' ? fieldName : '');
}
}

_appendTableWhenVisible() {
Expand Down Expand Up @@ -254,7 +257,7 @@ export class AnalysisView extends LitElement {
const metricList = groupMetrics(rootMethod);

Tabulator.registerModule(Object.values(CommonModules));
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs]);
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs, GroupSort]);
this.analysisTable = new Tabulator(this._tableWrapper, {
rowKeyboardNavigation: true,
selectableRows: 'highlight',
Expand Down Expand Up @@ -292,6 +295,7 @@ export class AnalysisView extends LitElement {
height: '100%',
maxHeight: '100%',
groupCalcs: true,
groupSort: true,
groupClosedShowCalcs: true,
groupStartOpen: false,
groupToggleElement: 'header',
Expand Down Expand Up @@ -325,6 +329,7 @@ export class AnalysisView extends LitElement {
formatter: 'textarea',
headerSortStartingDir: 'asc',
sorter: 'string',
headerSortTristate: true,
cssClass: 'datagrid-code-text',
bottomCalc: () => {
return 'Total';
Expand Down
22 changes: 14 additions & 8 deletions log-viewer/modules/components/database-view/DMLView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import { customElement, property, state } from 'lit/decorators.js';
import { Tabulator, type GroupComponent, type RowComponent } from 'tabulator-tables';

// tabulator custom modules
import { GroupCalcs } from '../../datagrid/groups/GroupCalcs.js';
import { GroupSort } from '../../datagrid/groups/GroupSort.js';
import * as CommonModules from '../../datagrid/module/CommonModules.js';
import { RowKeyboardNavigation } from '../../datagrid/module/RowKeyboardNavigation.js';
import { RowNavigation } from '../../datagrid/module/RowNavigation.js';
import { GroupCalcs } from '.././../datagrid/group-calcs/GroupCalcs.js';
import { Find, formatter } from '../calltree-view/module/Find.js';

// tabulator others
Expand Down Expand Up @@ -161,7 +162,8 @@ export class DMLView extends LitElement {

_dmlGroupBy(event: Event) {
const target = event.target as HTMLInputElement;
this.dmlTable?.setGroupBy(target.checked ? 'dml' : '');
//@ts-expect-error This is a custom function added in the GroupSort custom module
this.dmlTable?.setSortedGroupBy(target.checked ? 'dml' : '');
}

get _dmlTableWrapper(): HTMLDivElement | null {
Expand All @@ -181,7 +183,13 @@ export class DMLView extends LitElement {
this.dmlLines = dbAccess.getDMLLines() || [];

Tabulator.registerModule(Object.values(CommonModules));
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs]);
Tabulator.registerModule([
RowKeyboardNavigation,
RowNavigation,
Find,
GroupCalcs,
GroupSort,
]);
this._renderDMLTable(tableWrapper, this.dmlLines);
}
});
Expand Down Expand Up @@ -278,9 +286,11 @@ export class DMLView extends LitElement {
placeholder: 'No DML statements found',
columnCalcs: 'table',
groupCalcs: true,
groupSort: true,
groupClosedShowCalcs: true,
groupStartOpen: false,
groupValues: [dmlText],
groupBy: ['dml'],
groupToggleElement: false,
selectableRowsCheck: function (row: RowComponent) {
return !row.getData().isDetail;
Expand All @@ -295,7 +305,6 @@ export class DMLView extends LitElement {
headerTooltip: true,
headerWordWrap: true,
},
initialSort: [{ column: 'rowCount', dir: 'desc' }],
headerSortElement: function (column, dir) {
switch (dir) {
case 'asc':
Expand All @@ -316,6 +325,7 @@ export class DMLView extends LitElement {
bottomCalc: () => {
return 'Total';
},
headerSortTristate: true,
cssClass: 'datagrid-textarea datagrid-code-text',
variableHeight: true,
formatter: (cell, _formatterParams, _onRendered) => {
Expand Down Expand Up @@ -371,10 +381,6 @@ export class DMLView extends LitElement {
this._clearSearchHighlights();
});

this.dmlTable.on('tableBuilt', () => {
this.dmlTable?.setGroupBy('dml');
});

this.dmlTable.on('groupClick', (e: UIEvent, group: GroupComponent) => {
const { type } = window.getSelection() ?? {};
if (type === 'Range') {
Expand Down
34 changes: 17 additions & 17 deletions log-viewer/modules/components/database-view/SOQLView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
} from 'tabulator-tables';

//tabulator custom modules
import { GroupCalcs } from '../../datagrid/group-calcs/GroupCalcs.js';
import { GroupCalcs } from '../../datagrid/groups/GroupCalcs.js';
import { GroupSort } from '../../datagrid/groups/GroupSort.js';
import * as CommonModules from '../../datagrid/module/CommonModules.js';
import { RowKeyboardNavigation } from '../../datagrid/module/RowKeyboardNavigation.js';
import { RowNavigation } from '../../datagrid/module/RowNavigation.js';
Expand Down Expand Up @@ -192,19 +193,14 @@ export class SOQLView extends LitElement {
_findEvt = ((event: FindEvt) => this._find(event)) as EventListener;

_soqlGroupBy(event: Event) {
const target = event.target as HTMLInputElement;
const fieldName = target.value.toLowerCase();
const groupValue = fieldName !== 'none' ? fieldName : '';
if (!this.soqlTable) {
return;
}

this.soqlTable.setGroupValues([
groupValue
? this.sortByFrequency(this.soqlTable.getData(), groupValue as keyof GridSOQLData)
: [''],
]);
this.soqlTable.setGroupBy(groupValue);
const target = event.target as HTMLInputElement;
const fieldName = target.value.toLowerCase();
const groupValue = fieldName !== 'none' ? fieldName : '';
//@ts-expect-error This is a custom function added in the GroupSort custom module
this.soqlTable.setSortedGroupBy(groupValue);
}

_appendTableWhenVisible() {
Expand All @@ -219,7 +215,13 @@ export class SOQLView extends LitElement {
this.soqlLines = (await DatabaseAccess.create(treeRoot)).getSOQLLines() || [];

Tabulator.registerModule(Object.values(CommonModules));
Tabulator.registerModule([RowKeyboardNavigation, RowNavigation, Find, GroupCalcs]);
Tabulator.registerModule([
RowKeyboardNavigation,
RowNavigation,
Find,
GroupCalcs,
GroupSort,
]);
this._renderSOQLTable(tableWrapper, this.soqlLines);
}
});
Expand Down Expand Up @@ -326,8 +328,10 @@ export class SOQLView extends LitElement {
keybindings: { copyToClipboard: ['ctrl + 67', 'meta + 67'] },
clipboardCopyRowRange: 'all',
groupCalcs: true,
groupSort: true,
groupClosedShowCalcs: true,
groupStartOpen: false,
groupBy: 'soql',
groupValues: [soqlText],
groupToggleElement: false,
selectableRows: 'highlight',
Expand All @@ -343,7 +347,6 @@ export class SOQLView extends LitElement {
headerTooltip: true,
headerWordWrap: true,
},
initialSort: [{ column: 'rowCount', dir: 'desc' }],
headerSortElement: function (column, dir) {
switch (dir) {
case 'asc':
Expand All @@ -366,6 +369,7 @@ export class SOQLView extends LitElement {
bottomCalc: () => {
return 'Total';
},
headerSortTristate: true,
cssClass: 'datagrid-textarea datagrid-code-text',
variableHeight: true,
formatter: (cell, _formatterParams, _onRendered) => {
Expand Down Expand Up @@ -501,10 +505,6 @@ export class SOQLView extends LitElement {
},
});

this.soqlTable.on('tableBuilt', () => {
this.soqlTable?.setGroupBy('soql');
});

this.soqlTable.on('groupClick', (e: UIEvent, group: GroupComponent) => {
const { type } = window.getSelection() ?? {};
if (type === 'Range') {
Expand Down
145 changes: 145 additions & 0 deletions log-viewer/modules/datagrid/groups/GroupSort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { Module, type ColumnComponent, type GroupArg, type Tabulator } from 'tabulator-tables';

export class GroupSort extends Module {
static moduleName = 'groupSort';

constructor(table: Tabulator) {
super(table);
this.registerTableOption('groupSort', false);
this.registerTableFunction('setSortedGroupBy', this._setSortedGroupBy.bind(this));
}

initialize() {
// @ts-expect-error groupSort is a custom propoerty see registerTableOption above
if (this.table.options.groupSort) {
this.table.on('dataSorting', () => {
this.table.blockRedraw();
this._sortGroups();
this.table.restoreRedraw();
});
}
}

_setSortedGroupBy(...args: unknown[]) {
const grpArg = args[0] as GroupArg;
const grpArray = Array.isArray(grpArg) ? grpArg : [grpArg];
const oldGrpArg = this.table.options.groupBy as GroupArg;
const oldGrpArray = Array.isArray(oldGrpArg) ? oldGrpArg : [oldGrpArg];
if (!this._areGroupsEqual(oldGrpArray, grpArray)) {
this.table.options.groupBy = grpArg;
this.table.blockRedraw();
this._sortGroups();
this.table.setGroupBy(grpArg);
this.table.restoreRedraw();
}
}

_areGroupsEqual(oldGroups: unknown[], newGroups: unknown[]) {
return (
oldGroups &&
newGroups.length === oldGroups.length &&
newGroups.every((value, index) => value === oldGroups[index])
);
}

_sortGroups() {
const grpArray = Array.isArray(this.table.options.groupBy)
? this.table.options.groupBy
: [this.table.options.groupBy];
const { options } = this.table;
options.groupValues = [];

const validGrps = grpArray.filter(Boolean).length > 0;
if (this.table && this.table.options.sortMode !== 'remote' && validGrps) {
const { modules } = this.table;

const groupRows = modules.groupRows;
const rows = this.table.rowManager.rows;
groupRows.configureGroupSetup();
groupRows.generateGroups(rows);

const groupTotalsRows: InternalColumnTotal[] = [];
const columnCalcs = modules.columnCalcs;
const field = columnCalcs.botCalcs[0].field;
groupRows.groupList.forEach((group: { key: string; rows: { data: unknown }[] }) => {
const row = columnCalcs.generateBottomRow(group.rows);
row.data[field] = group.key;
row.key = group.key;
row.rows = group.rows;
row.generateCells();
groupTotalsRows.push(row);
});

const sortListActual: unknown[] = [];
//build list of valid sorters and trigger column specific callbacks before sort begins
const sorter = modules.sort;
const sortList: InternalSortItem[] = options.sortOrderReverse
? sorter.sortList.slice().reverse()
: sorter.sortList;
sortList.forEach((item) => {
let sortObj;

if (item.column) {
sortObj = item.column.modules.sort;

if (sortObj) {
//if no sorter has been defined, take a guess
if (!sortObj.sorter) {
sortObj.sorter = sorter.findSorter(item.column);
}

item.params =
typeof sortObj.params === 'function'
? sortObj.params(item.column.getComponent(), item.dir)
: sortObj.params;

sortListActual.push(item);
}
}
});

//sort data
if (sortListActual.length) {
sorter._sortItems(groupTotalsRows, sortListActual);
} else {
groupTotalsRows.sort((a, b) => {
const index = b.rows.length - a.rows.length;
if (index === 0) {
return a.key.localeCompare(b.key);
}
return index;
});
}
const groupValues: string[] = [];
groupTotalsRows.forEach((colTotals) => {
groupValues.push(colTotals.data[field] as string);
});

this.table?.setGroupValues([groupValues]);
}
}
}

// Representations of the internal Tabulator structures, that are entirely private to Tabulator. Subject to change and likely to b a bit flaky. May not cover all cases yet.
type InternalColumnTotal = {
data: { [key: string]: unknown };
key: string;
rows: { [key: string]: unknown }[];
};

type InternalColumn = {
getField(): string;
getComponent(): ColumnComponent;
modules: {
sort: {
params: (column: ColumnComponent, dir: string) => object;
sorter: (...args: unknown[]) => number | boolean;
};
};
};

type InternalSortItem = {
column: InternalColumn;
dir: string;
params: object;
};
Loading