From 84d9dd20952d2d33b18086a4df5942e403fed445 Mon Sep 17 00:00:00 2001 From: Scott Dover Date: Fri, 29 Aug 2025 14:19:26 -0400 Subject: [PATCH 1/3] chore: pass sortModel to backend Signed-off-by: Scott Dover --- .../LibraryNavigator/LibraryModel.ts | 10 ++++- .../LibraryNavigator/PaginatedResultSet.ts | 17 ++++--- .../src/components/LibraryNavigator/types.ts | 9 +++- .../src/connection/itc/ItcLibraryAdapter.ts | 2 + .../src/connection/rest/RestLibraryAdapter.ts | 2 + client/src/panels/DataViewer.ts | 5 ++- client/src/webview/useDataViewer.ts | 45 +++++++++++-------- .../PaginatedResultSet.test.ts | 2 +- 8 files changed, 61 insertions(+), 31 deletions(-) diff --git a/client/src/components/LibraryNavigator/LibraryModel.ts b/client/src/components/LibraryNavigator/LibraryModel.ts index 05a690389..f43c0904e 100644 --- a/client/src/components/LibraryNavigator/LibraryModel.ts +++ b/client/src/components/LibraryNavigator/LibraryModel.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { ProgressLocation, l10n, window } from "vscode"; +import { SortModelItem } from "ag-grid-community"; import { Writable } from "stream"; import PaginatedResultSet from "./PaginatedResultSet"; @@ -27,12 +28,17 @@ class LibraryModel { item: LibraryItem, ): PaginatedResultSet<{ data: TableData; error?: Error }> { return new PaginatedResultSet<{ data: TableData; error?: Error }>( - async (start: number, end: number) => { + async (start: number, end: number, sortModel: SortModelItem[]) => { await this.libraryAdapter.setup(); const limit = end - start + 1; try { return { - data: await this.libraryAdapter.getRows(item, start, limit), + data: await this.libraryAdapter.getRows( + item, + start, + limit, + sortModel, + ), }; } catch (e) { return { error: e, data: { rows: [], count: 0 } }; diff --git a/client/src/components/LibraryNavigator/PaginatedResultSet.ts b/client/src/components/LibraryNavigator/PaginatedResultSet.ts index c8d19cafa..1a4a26c52 100644 --- a/client/src/components/LibraryNavigator/PaginatedResultSet.ts +++ b/client/src/components/LibraryNavigator/PaginatedResultSet.ts @@ -1,15 +1,18 @@ // Copyright © 2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { SortModelItem } from "ag-grid-community"; class PaginatedResultSet { - private queryForData: (start: number, end: number) => Promise; + constructor( + protected readonly queryForData: PaginatedResultSet["getData"], + ) {} - constructor(queryForData: (start: number, end: number) => Promise) { - this.queryForData = queryForData; - } - - public async getData(start: number, end: number): Promise { - return await this.queryForData(start, end); + public async getData( + start: number, + end: number, + sortModel: SortModelItem[], + ): Promise { + return await this.queryForData(start, end, sortModel); } } diff --git a/client/src/components/LibraryNavigator/types.ts b/client/src/components/LibraryNavigator/types.ts index b67d567e1..201336c9a 100644 --- a/client/src/components/LibraryNavigator/types.ts +++ b/client/src/components/LibraryNavigator/types.ts @@ -1,5 +1,7 @@ // Copyright © 2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { SortModelItem } from "ag-grid-community"; + import { ColumnCollection } from "../../connection/rest/api/compute"; export const LibraryType = "library"; @@ -39,7 +41,12 @@ export interface LibraryAdapter { items: LibraryItem[]; count: number; }>; - getRows(item: LibraryItem, start: number, limit: number): Promise; + getRows( + item: LibraryItem, + start: number, + limit: number, + sortModel: SortModelItem[], + ): Promise; getRowsAsCSV( item: LibraryItem, start: number, diff --git a/client/src/connection/itc/ItcLibraryAdapter.ts b/client/src/connection/itc/ItcLibraryAdapter.ts index 6739672fa..622aa647b 100644 --- a/client/src/connection/itc/ItcLibraryAdapter.ts +++ b/client/src/connection/itc/ItcLibraryAdapter.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { l10n } from "vscode"; +import { SortModelItem } from "ag-grid-community"; import { ChildProcessWithoutNullStreams } from "child_process"; import { onRunError } from "../../commands/run"; @@ -91,6 +92,7 @@ class ItcLibraryAdapter implements LibraryAdapter { item: LibraryItem, start: number, limit: number, + sortModel: SortModelItem[], ): Promise { const { rows: rawRowValues, count } = await this.getDatasetInformation( item, diff --git a/client/src/connection/rest/RestLibraryAdapter.ts b/client/src/connection/rest/RestLibraryAdapter.ts index b29b065a9..984cfcae6 100644 --- a/client/src/connection/rest/RestLibraryAdapter.ts +++ b/client/src/connection/rest/RestLibraryAdapter.ts @@ -1,5 +1,6 @@ // Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { SortModelItem } from "ag-grid-community"; import { AxiosResponse } from "axios"; import { getSession } from ".."; @@ -44,6 +45,7 @@ class RestLibraryAdapter implements LibraryAdapter { item: LibraryItem, start: number, limit: number, + sortModel: SortModelItem[], ): Promise { const { data } = await this.retryOnFail( async () => diff --git a/client/src/panels/DataViewer.ts b/client/src/panels/DataViewer.ts index fd8d3dcd3..2f31facc7 100644 --- a/client/src/panels/DataViewer.ts +++ b/client/src/panels/DataViewer.ts @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { Uri, window } from "vscode"; +import { SortModelItem } from "ag-grid-community"; + import PaginatedResultSet from "../components/LibraryNavigator/PaginatedResultSet"; import { TableData } from "../components/LibraryNavigator/types"; import { Column } from "../connection/rest/api/compute"; @@ -66,7 +68,7 @@ class DataViewer extends WebView { event: Event & { key: string; command: string; - data?: { start?: number; end?: number }; + data?: { start?: number; end?: number; sortModel?: SortModelItem[] }; }, ): Promise { switch (event.command) { @@ -74,6 +76,7 @@ class DataViewer extends WebView { const { data, error } = await this._paginator.getData( event.data!.start!, event.data!.end!, + event.data!.sortModel!, ); if (error) { await window.showErrorMessage(error.message); diff --git a/client/src/webview/useDataViewer.ts b/client/src/webview/useDataViewer.ts index 7169f12a1..2d08d4c16 100644 --- a/client/src/webview/useDataViewer.ts +++ b/client/src/webview/useDataViewer.ts @@ -8,6 +8,7 @@ import { GridReadyEvent, IGetRowsParams, ModuleRegistry, + SortModelItem, } from "ag-grid-community"; import { v4 } from "uuid"; @@ -34,12 +35,16 @@ const clearQueryTimeout = (): void => { clearTimeout(queryTableDataTimeoutId); queryTableDataTimeoutId = null; }; -const queryTableData = (start: number, end: number): Promise => { +const queryTableData = ( + start: number, + end: number, + sortModel: SortModelItem[], +): Promise => { const requestKey = v4(); vscode.postMessage({ command: "request:loadData", key: requestKey, - data: { start, end }, + data: { start, end, sortModel }, }); return new Promise((resolve, reject) => { @@ -103,23 +108,25 @@ const useDataViewer = () => { const dataSource = { rowCount: undefined, getRows: async (params: IGetRowsParams) => { - await queryTableData(params.startRow, params.endRow).then( - ({ rows, count }: TableData) => { - const rowData = rows.map(({ cells }) => { - const row = cells.reduce( - (carry, cell, index) => ({ - ...carry, - [columns[index].field]: cell, - }), - {}, - ); - - return row; - }); - - params.successCallback(rowData, count); - }, - ); + await queryTableData( + params.startRow, + params.endRow, + params.sortModel, + ).then(({ rows, count }: TableData) => { + const rowData = rows.map(({ cells }) => { + const row = cells.reduce( + (carry, cell, index) => ({ + ...carry, + [columns[index].field]: cell, + }), + {}, + ); + + return row; + }); + + params.successCallback(rowData, count); + }); }, }; diff --git a/client/test/components/LibraryNavigator/PaginatedResultSet.test.ts b/client/test/components/LibraryNavigator/PaginatedResultSet.test.ts index e5bd522b5..8c7de0c63 100644 --- a/client/test/components/LibraryNavigator/PaginatedResultSet.test.ts +++ b/client/test/components/LibraryNavigator/PaginatedResultSet.test.ts @@ -27,7 +27,7 @@ describe("PaginatedResultSet", async function () { async () => mockAxiosResponse, ); - expect(await paginatedResultSet.getData(0, 100)).to.deep.equal( + expect(await paginatedResultSet.getData(0, 100, [])).to.deep.equal( mockAxiosResponse, ); }); From c5e651733dd56cf1db60f347ee6cf9c07376611f Mon Sep 17 00:00:00 2001 From: Scott Dover Date: Mon, 15 Sep 2025 10:38:55 -0400 Subject: [PATCH 2/3] chore: implement sort for rest Signed-off-by: Scott Dover --- .../src/connection/rest/RestLibraryAdapter.ts | 52 ++++++++- client/src/webview/ColumnHeader.tsx | 106 ++++++++++++++++++ client/src/webview/ColumnHeaderMenu.tsx | 55 +++++++++ client/src/webview/DataViewer.css | 105 +++++++++++++++++ client/src/webview/DataViewer.tsx | 32 +++--- client/src/webview/columnHeaderTemplate.ts | 40 ------- .../{useDataViewer.ts => useDataViewer.tsx} | 20 +++- icons/dark/check.svg | 3 + icons/dark/chevron-right.svg | 3 + icons/dark/more.svg | 5 + icons/light/check.svg | 3 + icons/light/chevron-right.svg | 3 + icons/light/more.svg | 5 + 13 files changed, 369 insertions(+), 63 deletions(-) create mode 100644 client/src/webview/ColumnHeader.tsx create mode 100644 client/src/webview/ColumnHeaderMenu.tsx delete mode 100644 client/src/webview/columnHeaderTemplate.ts rename client/src/webview/{useDataViewer.ts => useDataViewer.tsx} (87%) create mode 100644 icons/dark/check.svg create mode 100644 icons/dark/chevron-right.svg create mode 100644 icons/dark/more.svg create mode 100644 icons/light/check.svg create mode 100644 icons/light/chevron-right.svg create mode 100644 icons/light/more.svg diff --git a/client/src/connection/rest/RestLibraryAdapter.ts b/client/src/connection/rest/RestLibraryAdapter.ts index 984cfcae6..e31f62fba 100644 --- a/client/src/connection/rest/RestLibraryAdapter.ts +++ b/client/src/connection/rest/RestLibraryAdapter.ts @@ -42,11 +42,15 @@ class RestLibraryAdapter implements LibraryAdapter { } public async getRows( - item: LibraryItem, + item: Pick, start: number, limit: number, sortModel: SortModelItem[], ): Promise { + if (sortModel.length > 0) { + return await this.getSortedRows(item, start, limit, sortModel); + } + const { data } = await this.retryOnFail( async () => await this.dataAccessApi.getRows( @@ -68,6 +72,43 @@ class RestLibraryAdapter implements LibraryAdapter { }; } + private async getSortedRows( + item: Pick, + start: number, + limit: number, + sortModel: SortModelItem[], + ): Promise { + const { data: viewData } = await this.retryOnFail( + async () => + await this.dataAccessApi.createView( + { + sessionId: this.sessionId, + libref: item.library || "", + tableName: item.name, + viewRequest: { + sortBy: sortModel.map((sortModelItem) => ({ + key: sortModelItem.colId, + direction: + sortModelItem.sort === "asc" ? "ascending" : "descending", + })), + }, + }, + requestOptions, + ), + ); + + const results = await this.getRows( + { library: viewData.libref, name: viewData.name }, + start, + limit, + [], + ); + + await this.deleteTable({ library: viewData.libref, name: viewData.name }); + + return results; + } + public async getRowsAsCSV( item: LibraryItem, start: number, @@ -157,15 +198,18 @@ class RestLibraryAdapter implements LibraryAdapter { } } - public async deleteTable(item: LibraryItem): Promise { + public async deleteTable({ + library, + name, + }: Pick): Promise { await this.setup(); try { await this.retryOnFail( async () => await this.dataAccessApi.deleteTable({ sessionId: this.sessionId, - libref: item.library, - tableName: item.name, + libref: library, + tableName: name, }), ); // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/client/src/webview/ColumnHeader.tsx b/client/src/webview/ColumnHeader.tsx new file mode 100644 index 000000000..992272d73 --- /dev/null +++ b/client/src/webview/ColumnHeader.tsx @@ -0,0 +1,106 @@ +import { useRef } from "react"; + +import { AgColumn, GridApi } from "ag-grid-community"; + +import { ColumnHeaderProps } from "./ColumnHeaderMenu"; + +const getIconForColumnType = (type: string) => { + switch (type.toLocaleLowerCase()) { + case "float": + case "num": + return "float"; + case "date": + return "date"; + case "time": + return "time"; + case "datetime": + return "date-time"; + case "currency": + return "currency"; + case "char": + return "char"; + default: + return ""; + } +}; + +const ColumnHeader = ({ + api, + column, + currentColumn: getCurrentColumn, + columnType, + setColumnMenu, +}: { + api: GridApi; + column: AgColumn; + currentColumn: () => AgColumn | undefined; + columnType: string; + setColumnMenu: (props: ColumnHeaderProps) => void; +}) => { + const ref = useRef(undefined!); + const currentColumn = getCurrentColumn(); + + console.log({ currentColumn, column }); + return ( +
+
+ + {column.colId} + + {column.sort === "asc" && ( + + )} + {column.sort === "desc" && ( + + )} +
+ +
+
+
+ ); +}; + +export default ColumnHeader; diff --git a/client/src/webview/ColumnHeaderMenu.tsx b/client/src/webview/ColumnHeaderMenu.tsx new file mode 100644 index 000000000..94062f353 --- /dev/null +++ b/client/src/webview/ColumnHeaderMenu.tsx @@ -0,0 +1,55 @@ +import { AgColumn } from "ag-grid-community"; + +export interface ColumnHeaderProps { + left: number; + top: number; + column: AgColumn; + sortColumn: (direction: "asc" | "desc") => void; + dismissMenu: () => void; +} + +const ColumnHeaderMenu = ({ + left, + top, + column, + sortColumn, + dismissMenu, +}: ColumnHeaderProps) => { + return ( +
+
    +
  • + Sort +
      +
    • + {column.sort === "asc" && } + +
    • +
    • + {column.sort === "desc" && } + +
    • +
    +
  • +
+
+ ); +}; + +export default ColumnHeaderMenu; diff --git a/client/src/webview/DataViewer.css b/client/src/webview/DataViewer.css index 2c58b8fea..178cca301 100644 --- a/client/src/webview/DataViewer.css +++ b/client/src/webview/DataViewer.css @@ -74,3 +74,108 @@ body, background: url(../../../icons/dark/tableHeaderCharacterTypeDark.svg) center no-repeat; } + +.header-menu { + position: absolute; + z-index: 999; +} +.header-menu > ul { + position: relative; + bottom: 0; + right: 100%; +} +.header-menu ul { + background: green; + background: var(--vscode-editorHoverWidget-background); + border: 1px solid var(--vscode-editorHoverWidget-border); + padding: 0; + margin: 0; + list-style-type: none; + min-width: 100px; + color: var(--vscode-editorWidget-foreground); + box-shadow: var(--vscode-widget-shadow); +} + +.ag-header-cell-label button, +.header-menu button { + background: none; + border: none; + margin: 0; + padding: 0; +} + +.header-menu button { + color: var(--vscode-editorWidget-foreground); +} + +.header-menu li { + padding: 0.5rem; + white-space: nowrap; +} + +.header-menu li:hover { + background: var(--vscode-toolbar-hoverBackground); +} + +.header-menu > ul > li { + position: relative; +} + +.header-menu > ul > li > ul { + display: none; +} + +.header-menu > ul > li:hover > ul { + display: block; + position: absolute; + left: 100%; + top: 0; +} + +.header-menu li > span:has(+ ul) { + display: block; + background: url(../../../icons/light/chevron-right.svg) right center no-repeat; +} + +.vscode-dark .header-menu li > span:has(+ ul) { + background: url(../../../icons/dark/chevron-right.svg) right center no-repeat; +} + +.ag-header-cell-label { + position: relative; +} + +.ag-header-cell-label .dropdown { + position: absolute; + right: 0; +} + +button { + cursor: pointer; +} + +.ag-header-cell-label button { + display: none; + width: 16px; + height: 16px; +} + +.ag-header-cell-label button span { + background: url(../../../icons/light/more.svg); + background-size: 16px 16px; +} + +.ag-header-cell-label button:hover, +.ag-header-cell-label button.active { + background: var(--vscode-toolbar-hoverBackground); +} + +.vscode-dark .ag-header-cell-label button { + background: url(../../../icons/dark/more.svg); + background-size: 16px 16px; +} + +.ag-header-cell-label:hover button, +.ag-header-cell-label button.active { + display: block; +} diff --git a/client/src/webview/DataViewer.tsx b/client/src/webview/DataViewer.tsx index 53fd86725..1d53767e5 100644 --- a/client/src/webview/DataViewer.tsx +++ b/client/src/webview/DataViewer.tsx @@ -6,6 +6,7 @@ import { createRoot } from "react-dom/client"; import { AgGridReact } from "ag-grid-react"; import "."; +import ColumnHeaderMenu from "./ColumnHeaderMenu"; import useDataViewer from "./useDataViewer"; import "./DataViewer.css"; @@ -20,7 +21,7 @@ const gridStyles = { }; const DataViewer = () => { - const { columns, onGridReady } = useDataViewer(); + const { columns, onGridReady, columnMenu } = useDataViewer(); const theme = useMemo(() => { const themeKind = document .querySelector("[data-vscode-theme-kind]") @@ -41,19 +42,22 @@ const DataViewer = () => { } return ( -
- +
+ {columnMenu && } +
+ +
); }; diff --git a/client/src/webview/columnHeaderTemplate.ts b/client/src/webview/columnHeaderTemplate.ts deleted file mode 100644 index 33c499f9a..000000000 --- a/client/src/webview/columnHeaderTemplate.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright © 2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -const getIconForColumnType = (type: string) => { - switch (type.toLocaleLowerCase()) { - case "float": - case "num": - return "float"; - case "date": - return "date"; - case "time": - return "time"; - case "datetime": - return "date-time"; - case "currency": - return "currency"; - case "char": - return "char"; - default: - return ""; - } -}; - -// Taken from https://www.ag-grid.com/react-data-grid/column-headers/#provided-component -const columnHeaderTemplate = (columnType: string) => ` - -`; - -export default columnHeaderTemplate; diff --git a/client/src/webview/useDataViewer.ts b/client/src/webview/useDataViewer.tsx similarity index 87% rename from client/src/webview/useDataViewer.ts rename to client/src/webview/useDataViewer.tsx index 2d08d4c16..e869b121c 100644 --- a/client/src/webview/useDataViewer.ts +++ b/client/src/webview/useDataViewer.tsx @@ -1,6 +1,6 @@ // Copyright © 2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { AllCommunityModule, @@ -14,7 +14,8 @@ import { v4 } from "uuid"; import { TableData } from "../components/LibraryNavigator/types"; import { Column } from "../connection/rest/api/compute"; -import columnHeaderTemplate from "./columnHeaderTemplate"; +import ColumnHeader from "./ColumnHeader"; +import { ColumnHeaderProps } from "./ColumnHeaderMenu"; declare const acquireVsCodeApi; const vscode = acquireVsCodeApi(); @@ -102,6 +103,12 @@ const fetchColumns = (): Promise => { const useDataViewer = () => { const [columns, setColumns] = useState([]); + const [columnMenu, setColumnMenu] = useState(); + + const columnMenuRef = useRef(columnMenu); + useEffect(() => { + columnMenuRef.current = columnMenu; + }, [columnMenu]); const onGridReady = useCallback( (event: GridReadyEvent) => { @@ -143,14 +150,17 @@ const useDataViewer = () => { fetchColumns().then((columnsData) => { const columns: ColDef[] = columnsData.map((column) => ({ field: column.name, - headerName: column.name, + headerComponent: ColumnHeader, headerComponentParams: { - template: columnHeaderTemplate(column.type), + columnType: column.type, + setColumnMenu, + currentColumn: () => columnMenuRef.current?.column, }, })); columns.unshift({ field: "#", suppressMovable: true, + sortable: false, }); setColumns(columns); @@ -165,7 +175,7 @@ const useDataViewer = () => { }; }, []); - return { columns, onGridReady }; + return { columns, onGridReady, columnMenu }; }; export default useDataViewer; diff --git a/icons/dark/check.svg b/icons/dark/check.svg new file mode 100644 index 000000000..cea818ef5 --- /dev/null +++ b/icons/dark/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/dark/chevron-right.svg b/icons/dark/chevron-right.svg new file mode 100644 index 000000000..87e8a8df4 --- /dev/null +++ b/icons/dark/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/dark/more.svg b/icons/dark/more.svg new file mode 100644 index 000000000..105cd7e93 --- /dev/null +++ b/icons/dark/more.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/light/check.svg b/icons/light/check.svg new file mode 100644 index 000000000..3f25ee082 --- /dev/null +++ b/icons/light/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/light/chevron-right.svg b/icons/light/chevron-right.svg new file mode 100644 index 000000000..72cc78181 --- /dev/null +++ b/icons/light/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/light/more.svg b/icons/light/more.svg new file mode 100644 index 000000000..0b41b680c --- /dev/null +++ b/icons/light/more.svg @@ -0,0 +1,5 @@ + + + + + From 24e55aafda3a90a4d8612ba32f4a034843f6a46e Mon Sep 17 00:00:00 2001 From: Scott Dover Date: Mon, 15 Sep 2025 15:33:18 -0400 Subject: [PATCH 3/3] chore: experiment with using native ag grid html/css Signed-off-by: Scott Dover --- client/src/webview/ColumnHeader.tsx | 31 ++--- client/src/webview/ColumnHeaderMenu.tsx | 174 ++++++++++++++++++++++++ client/src/webview/DataViewer.css | 26 ++++ client/src/webview/DataViewer.tsx | 4 +- client/src/webview/useDataViewer.tsx | 3 +- icons/dark/arrow-down.svg | 3 + icons/dark/arrow-up.svg | 3 + icons/light/arrow-down.svg | 3 + icons/light/arrow-up.svg | 3 + 9 files changed, 228 insertions(+), 22 deletions(-) create mode 100644 icons/dark/arrow-down.svg create mode 100644 icons/dark/arrow-up.svg create mode 100644 icons/light/arrow-down.svg create mode 100644 icons/light/arrow-up.svg diff --git a/client/src/webview/ColumnHeader.tsx b/client/src/webview/ColumnHeader.tsx index 992272d73..a83b13dc3 100644 --- a/client/src/webview/ColumnHeader.tsx +++ b/client/src/webview/ColumnHeader.tsx @@ -30,19 +30,20 @@ const ColumnHeader = ({ currentColumn: getCurrentColumn, columnType, setColumnMenu, + theme, }: { api: GridApi; column: AgColumn; currentColumn: () => AgColumn | undefined; columnType: string; setColumnMenu: (props: ColumnHeaderProps) => void; + theme: string; }) => { const ref = useRef(undefined!); const currentColumn = getCurrentColumn(); - console.log({ currentColumn, column }); return ( -
+
{column.colId} - {column.sort === "asc" && ( - + + + )} {column.sort === "desc" && ( - + + + )}