From 33c1401f344074d49d9c3251418e555cdaa2634f Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:43:13 +0100 Subject: [PATCH 1/4] fix: search highlights clearing incorrectly --- .../components/analysis-view/AnalysisView.ts | 5 ++--- .../components/calltree-view/CalltreeView.ts | 6 ++---- .../modules/components/database-view/DMLView.ts | 16 +++++++++++----- .../modules/components/database-view/SOQLView.ts | 16 +++++++++++----- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/log-viewer/modules/components/analysis-view/AnalysisView.ts b/log-viewer/modules/components/analysis-view/AnalysisView.ts index a942feea..1b02a1ac 100644 --- a/log-viewer/modules/components/analysis-view/AnalysisView.ts +++ b/log-viewer/modules/components/analysis-view/AnalysisView.ts @@ -219,8 +219,7 @@ export class AnalysisView extends LitElement { newFindArgs.options.matchCase !== this.findArgs.options?.matchCase; this.findArgs = newFindArgs; - const clearHighlights = - e.type === 'lv-find-close' || (!isTableVisible && newFindArgs.count === 0); + const clearHighlights = e.type === 'lv-find-close'; if (clearHighlights) { newFindArgs.text = ''; } @@ -423,7 +422,7 @@ export class AnalysisView extends LitElement { _clearSearchHighlights() { this._find( - new CustomEvent('lv-find', { + new CustomEvent('lv-find-close', { detail: { text: '', count: 0, options: { matchCase: false } }, }), ); diff --git a/log-viewer/modules/components/calltree-view/CalltreeView.ts b/log-viewer/modules/components/calltree-view/CalltreeView.ts index 619bd7cc..e552e062 100644 --- a/log-viewer/modules/components/calltree-view/CalltreeView.ts +++ b/log-viewer/modules/components/calltree-view/CalltreeView.ts @@ -358,9 +358,7 @@ export class CalltreeView extends LitElement { newFindArgs.options.matchCase !== this.findArgs.options?.matchCase; this.findArgs = newFindArgs; - const clearHighlights = - e.type === 'lv-find-close' || - (this.canClearSearchHighlights && !isTableVisible && newFindArgs.count === 0); + const clearHighlights = e.type === 'lv-find-close'; if (clearHighlights) { newFindArgs.text = ''; } @@ -805,7 +803,7 @@ export class CalltreeView extends LitElement { private _clearSearchHighlights() { this._find( - new CustomEvent('lv-find', { + new CustomEvent('lv-find-close', { detail: { text: '', count: 0, options: { matchCase: false } }, }), ); diff --git a/log-viewer/modules/components/database-view/DMLView.ts b/log-viewer/modules/components/database-view/DMLView.ts index e6b9f8e3..2426c569 100644 --- a/log-viewer/modules/components/database-view/DMLView.ts +++ b/log-viewer/modules/components/database-view/DMLView.ts @@ -62,6 +62,7 @@ export class DMLView extends LitElement { }; findMap: { [key: number]: RowComponent } = {}; totalMatches = 0; + canClearHighlights = false; constructor() { super(); @@ -219,6 +220,8 @@ export class DMLView extends LitElement { return; } + this.canClearHighlights = true; + const newFindArgs = JSON.parse(JSON.stringify(e.detail)); if (!isTableVisible) { newFindArgs.text = ''; @@ -229,8 +232,7 @@ export class DMLView extends LitElement { newFindArgs.options.matchCase !== this.findArgs.options?.matchCase; this.findArgs = newFindArgs; - const clearHighlights = - e.type === 'lv-find-close' || (!isTableVisible && newFindArgs.count === 0); + const clearHighlights = e.type === 'lv-find-close'; if (clearHighlights) { newFindArgs.text = ''; } @@ -248,6 +250,7 @@ export class DMLView extends LitElement { ); } } + this.canClearHighlights = false; } _renderDMLTable(dmlTableContainer: HTMLElement, dmlLines: DMLBeginLine[]) { @@ -298,6 +301,7 @@ export class DMLView extends LitElement { selectableRows: 'highlight', dataTree: true, dataTreeBranchElement: false, + dataTreeStartExpanded: true, columnDefaults: { title: 'default', resizable: true, @@ -377,8 +381,10 @@ export class DMLView extends LitElement { }); this.dmlTable.on('dataFiltering', () => { - this._resetFindWidget(); - this._clearSearchHighlights(); + if (this.canClearHighlights) { + this._resetFindWidget(); + this._clearSearchHighlights(); + } }); this.dmlTable.on('groupClick', (e: UIEvent, group: GroupComponent) => { @@ -438,7 +444,7 @@ export class DMLView extends LitElement { private _clearSearchHighlights() { this._find( - new CustomEvent('lv-find', { + new CustomEvent('lv-find-close', { detail: { text: '', count: 0, options: { matchCase: false } }, }), ); diff --git a/log-viewer/modules/components/database-view/SOQLView.ts b/log-viewer/modules/components/database-view/SOQLView.ts index db59b0bb..d11d54dc 100644 --- a/log-viewer/modules/components/database-view/SOQLView.ts +++ b/log-viewer/modules/components/database-view/SOQLView.ts @@ -73,6 +73,7 @@ export class SOQLView extends LitElement { }; findMap: { [key: number]: RowComponent } = {}; totalMatches = 0; + canClearHighlights = false; get _soqlTableWrapper(): HTMLDivElement | null { return this.renderRoot?.querySelector('#db-soql-table') ?? null; @@ -253,14 +254,15 @@ export class SOQLView extends LitElement { return; } + this.canClearHighlights = true; + const newFindArgs = JSON.parse(JSON.stringify(e.detail)); const newSearch = newFindArgs.text !== this.findArgs.text || newFindArgs.options.matchCase !== this.findArgs.options?.matchCase; this.findArgs = newFindArgs; - const clearHighlights = - e.type === 'lv-find-close' || (!isTableVisible && newFindArgs.count === 0); + const clearHighlights = e.type === 'lv-find-close'; if (clearHighlights) { newFindArgs.text = ''; } @@ -278,6 +280,8 @@ export class SOQLView extends LitElement { ); } } + + this.canClearHighlights = false; } _renderSOQLTable(soqlTableContainer: HTMLElement, soqlLines: SOQLExecuteBeginLine[]) { @@ -540,8 +544,10 @@ export class SOQLView extends LitElement { }); this.soqlTable.on('dataFiltering', () => { - this._resetFindWidget(); - this._clearSearchHighlights(); + if (this.canClearHighlights) { + this._resetFindWidget(); + this._clearSearchHighlights(); + } }); this.soqlTable.on('renderStarted', () => { @@ -567,7 +573,7 @@ export class SOQLView extends LitElement { _clearSearchHighlights() { this._find( - new CustomEvent('lv-find', { + new CustomEvent('lv-find-close', { detail: { text: '', count: 0, options: { matchCase: false } }, }), ); From 3c587603747db4ebf4d6fb23cb729535ad2a1d9c Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:45:04 +0100 Subject: [PATCH 2/4] fix: group rows not auto expanding children on DB grids --- log-viewer/modules/components/database-view/DMLView.ts | 7 ------- log-viewer/modules/components/database-view/SOQLView.ts | 8 +------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/log-viewer/modules/components/database-view/DMLView.ts b/log-viewer/modules/components/database-view/DMLView.ts index 2426c569..7ea6d345 100644 --- a/log-viewer/modules/components/database-view/DMLView.ts +++ b/log-viewer/modules/components/database-view/DMLView.ts @@ -395,13 +395,6 @@ export class DMLView extends LitElement { this.dmlTable?.blockRedraw(); group.toggle(); - if (!group.isVisible()) { - this.dmlTable?.getRows().forEach((row) => { - if (!row.isTreeExpanded()) { - row.treeExpand(); - } - }); - } this.dmlTable?.restoreRedraw(); }); diff --git a/log-viewer/modules/components/database-view/SOQLView.ts b/log-viewer/modules/components/database-view/SOQLView.ts index d11d54dc..2915fe9b 100644 --- a/log-viewer/modules/components/database-view/SOQLView.ts +++ b/log-viewer/modules/components/database-view/SOQLView.ts @@ -344,6 +344,7 @@ export class SOQLView extends LitElement { }, dataTree: true, dataTreeBranchElement: false, + dataTreeStartExpanded: true, columnDefaults: { title: 'default', resizable: true, @@ -517,13 +518,6 @@ export class SOQLView extends LitElement { this.soqlTable?.blockRedraw(); group.toggle(); - if (!group.isVisible()) { - this.soqlTable?.getRows().forEach((row) => { - if (!row.isTreeExpanded()) { - row.treeExpand(); - } - }); - } this.soqlTable?.restoreRedraw(); }); From a2cdcc628ce4e965de2267f220944340754901fc Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:47:21 +0100 Subject: [PATCH 3/4] fix: sorting of grouped rows --- .../modules/datagrid/groups/GroupCalcs.ts | 2 +- .../modules/datagrid/groups/GroupSort.ts | 61 ++++++++++++------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/log-viewer/modules/datagrid/groups/GroupCalcs.ts b/log-viewer/modules/datagrid/groups/GroupCalcs.ts index e0a91aa9..c1d8f179 100644 --- a/log-viewer/modules/datagrid/groups/GroupCalcs.ts +++ b/log-viewer/modules/datagrid/groups/GroupCalcs.ts @@ -27,10 +27,10 @@ export class GroupCalcs extends Module { const arrowClone = rawGroup.arrowElement.cloneNode(true); rawGroup.arrowElement = document.createElement('span'); - const rowFrag = document.createDocumentFragment(); const firstCell = row.cells[0].getElement(); firstCell.insertBefore(arrowClone, firstCell.firstChild); + const rowFrag = document.createDocumentFragment(); row.cells.forEach((cell: { getElement(): HTMLElement }) => { rowFrag.appendChild(cell.getElement()); }); diff --git a/log-viewer/modules/datagrid/groups/GroupSort.ts b/log-viewer/modules/datagrid/groups/GroupSort.ts index a026065e..5e6459e0 100644 --- a/log-viewer/modules/datagrid/groups/GroupSort.ts +++ b/log-viewer/modules/datagrid/groups/GroupSort.ts @@ -12,11 +12,7 @@ export class GroupSort extends Module { 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(); - }); + this.subscribe('sort-changed', this._sortGroups.bind(this)); } } @@ -47,41 +43,55 @@ export class GroupSort extends Module { ? 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) { + if (this.table && options.sortMode !== 'remote' && validGrps) { + const originalGroupVals = options.groupValues ?? [[]]; const { modules } = this.table; - const groupRows = modules.groupRows; + const groupsByKey: { [key: string]: unknown[] } = {}; const rows = this.table.rowManager.rows; - groupRows.configureGroupSetup(); - groupRows.generateGroups(rows); + + let groupFunc = grpArray[0]; + const grpField = groupFunc as string; + if (typeof groupFunc === 'string') { + groupFunc = function (data) { + return data[grpField]; + }; + } + + if (groupFunc) { + rows.forEach((row: InternalColumnTotal) => { + const grpVal = groupFunc(row.data); + let groupRows = groupsByKey[grpVal]; + if (!groupRows) { + groupRows = []; + groupsByKey[grpVal] = groupRows; + } + groupRows.push(row); + }); + } 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; + + for (const [key, rows] of Object.entries(groupsByKey)) { + const row = columnCalcs.generateBottomRow(rows); + row.data[field] = key; + row.key = key; + row.rows = 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; - + const sortObj = item.column.modules.sort; if (sortObj) { //if no sorter has been defined, take a guess if (!sortObj.sorter) { @@ -115,7 +125,12 @@ export class GroupSort extends Module { groupValues.push(colTotals.data[field] as string); }); - this.table?.setGroupValues([groupValues]); + const ogGroups = originalGroupVals[0] ?? []; + if (!this._areGroupsEqual(groupValues, ogGroups)) { + this.table?.setGroupValues([groupValues]); + } + } else { + this.table?.setGroupValues([]); } } } From 572d9b31e623573786a5321201258a48f66112c5 Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:18:37 +0100 Subject: [PATCH 4/4] refactor: move sort to function --- .../modules/datagrid/groups/GroupSort.ts | 111 +++++++++--------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/log-viewer/modules/datagrid/groups/GroupSort.ts b/log-viewer/modules/datagrid/groups/GroupSort.ts index 5e6459e0..3307a122 100644 --- a/log-viewer/modules/datagrid/groups/GroupSort.ts +++ b/log-viewer/modules/datagrid/groups/GroupSort.ts @@ -30,14 +30,6 @@ export class GroupSort extends Module { } } - _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 @@ -46,12 +38,6 @@ export class GroupSort extends Module { const validGrps = grpArray.filter(Boolean).length > 0; if (this.table && options.sortMode !== 'remote' && validGrps) { - const originalGroupVals = options.groupValues ?? [[]]; - const { modules } = this.table; - - const groupsByKey: { [key: string]: unknown[] } = {}; - const rows = this.table.rowManager.rows; - let groupFunc = grpArray[0]; const grpField = groupFunc as string; if (typeof groupFunc === 'string') { @@ -60,7 +46,9 @@ export class GroupSort extends Module { }; } + const groupsByKey: { [key: string]: unknown[] } = {}; if (groupFunc) { + const rows = this.table.rowManager.rows; rows.forEach((row: InternalColumnTotal) => { const grpVal = groupFunc(row.data); let groupRows = groupsByKey[grpVal]; @@ -72,10 +60,9 @@ export class GroupSort extends Module { }); } - const groupTotalsRows: InternalColumnTotal[] = []; - const columnCalcs = modules.columnCalcs; + let groupTotalsRows: InternalColumnTotal[] = []; + const columnCalcs = this.table.modules.columnCalcs; const field = columnCalcs.botCalcs[0].field; - for (const [key, rows] of Object.entries(groupsByKey)) { const row = columnCalcs.generateBottomRow(rows); row.data[field] = key; @@ -84,55 +71,71 @@ export class GroupSort extends Module { row.generateCells(); groupTotalsRows.push(row); } - const sortListActual: unknown[] = []; - const sorter = modules.sort; - const sortList: InternalSortItem[] = options.sortOrderReverse - ? sorter.sortList.slice().reverse() - : sorter.sortList; - sortList.forEach((item) => { - if (item.column) { - const 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; - }); - } + groupTotalsRows = this._sortGroupTotals(groupTotalsRows); const groupValues: string[] = []; groupTotalsRows.forEach((colTotals) => { groupValues.push(colTotals.data[field] as string); }); - const ogGroups = originalGroupVals[0] ?? []; - if (!this._areGroupsEqual(groupValues, ogGroups)) { + const originalGroupVals = (options.groupValues ?? [[]])[0] ?? []; + if (!this._areGroupsEqual(groupValues, originalGroupVals)) { this.table?.setGroupValues([groupValues]); } } else { this.table?.setGroupValues([]); } } + + _areGroupsEqual(oldGroups: unknown[], newGroups: unknown[]) { + return ( + oldGroups && + newGroups.length === oldGroups.length && + newGroups.every((value, index) => value === oldGroups[index]) + ); + } + + _sortGroupTotals(groupTotalsRows: InternalColumnTotal[]) { + const sortListActual: unknown[] = []; + const { modules, options } = this.table; + + const sorter = modules.sort; + const sortList: InternalSortItem[] = options.sortOrderReverse + ? sorter.sortList.slice().reverse() + : sorter.sortList; + sortList.forEach((item) => { + if (item.column) { + const 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; + }); + } + return groupTotalsRows; + } } // 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.