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
5 changes: 3 additions & 2 deletions client/src/webview/ColumnHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useRef } from "react";

import { AgColumn, GridApi } from "ag-grid-community";

import useTheme from "./useTheme";

const getIconForColumnType = (type: string) => {
switch (type.toLocaleLowerCase()) {
case "float":
Expand All @@ -29,16 +31,15 @@ const ColumnHeader = ({
column,
currentColumn: getCurrentColumn,
columnType,
theme,
displayMenuForColumn,
}: {
api: GridApi;
column: AgColumn;
currentColumn: () => AgColumn | undefined;
columnType: string;
theme: string;
displayMenuForColumn: (api: GridApi, column: AgColumn, rect: DOMRect) => void;
}) => {
const theme = useTheme();
const ref = useRef<HTMLButtonElement>(undefined!);
const currentColumn = getCurrentColumn();
const currentSortedColumns = api.getColumnState().filter((c) => c.sort);
Expand Down
6 changes: 2 additions & 4 deletions client/src/webview/ColumnMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AgColumn, ColumnState, GridApi } from "ag-grid-community";
import GridMenu from "./GridMenu";
import localize from "./localize";
import { storeViewProperties } from "./useDataViewer";
import useTheme from "./useTheme";

export interface ColumnMenuProps {
column: AgColumn;
Expand All @@ -15,7 +16,6 @@ export interface ColumnMenuProps {
removeAllSorting: () => void;
removeFromSort: () => void;
sortColumn: (direction: "asc" | "desc") => void;
theme: string;
top: number;
}

Expand All @@ -27,7 +27,6 @@ const applyColumnState = (api: GridApi, state: ColumnState[]) => {

export const getColumnMenu = (
api: GridApi,
theme: ColumnMenuProps["theme"],
column: AgColumn,
{ height, top, left }: DOMRect,
dismissMenu: () => void,
Expand All @@ -37,7 +36,6 @@ export const getColumnMenu = (
dismissMenu,
hasSort: api.getColumnState().some((c) => c.sort),
left,
theme,
top: top + height,
sortColumn: (direction: "asc" | "desc" | null) => {
const newColumnState = api.getColumnState().filter((c) => c.sort);
Expand Down Expand Up @@ -85,9 +83,9 @@ const ColumnMenu = ({
removeAllSorting,
removeFromSort,
sortColumn,
theme,
top,
}: ColumnMenuProps) => {
const theme = useTheme();
const menuItems = [
{
name: localize("Sort"),
Expand Down
21 changes: 4 additions & 17 deletions client/src/webview/DataViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Copyright © 2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { useCallback, useEffect, useMemo } from "react";
import { useCallback, useEffect } from "react";
import { createRoot } from "react-dom/client";

import { AgGridReact } from "ag-grid-react";

import ".";
import ColumnMenu from "./ColumnMenu";
import useDataViewer from "./useDataViewer";
import useTheme from "./useTheme";

import "./DataViewer.css";
import "ag-grid-community/styles/ag-grid.css";
Expand All @@ -21,22 +22,8 @@ const gridStyles = {
};

const DataViewer = () => {
const theme = useMemo(() => {
const themeKind = document
.querySelector("[data-vscode-theme-kind]")
.getAttribute("data-vscode-theme-kind");

switch (themeKind) {
case "vscode-high-contrast-light":
case "vscode-light":
return "ag-theme-alpine";
case "vscode-high-contrast":
case "vscode-dark":
return "ag-theme-alpine-dark";
}
}, []);
const { columns, onGridReady, columnMenu, dismissMenu } =
useDataViewer(theme);
const theme = useTheme();
const { columns, onGridReady, columnMenu, dismissMenu } = useDataViewer();

const handleKeydown = useCallback(
(event) => {
Expand Down
8 changes: 3 additions & 5 deletions client/src/webview/useDataViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const storeViewProperties = (viewProperties: ViewProperties) =>
data: { viewProperties },
});

const useDataViewer = (theme: string) => {
const useDataViewer = () => {
const [columns, setColumns] = useState<ColDef[]>([]);
const [columnMenu, setColumnMenu] = useState<ColumnMenuProps | undefined>();
const columnMenuRef = useRef<ColumnMenuProps | undefined>(columnMenu);
Expand Down Expand Up @@ -168,7 +168,6 @@ const useDataViewer = (theme: string) => {
setColumnMenu(
getColumnMenu(
api,
theme,
column,
rect,
() => setColumnMenu(undefined),
Expand All @@ -181,7 +180,7 @@ const useDataViewer = (theme: string) => {
),
);
},
[theme],
[],
);

useEffect(() => {
Expand All @@ -201,7 +200,6 @@ const useDataViewer = (theme: string) => {
columnType: column.type,
currentColumn: () => columnMenuRef.current?.column,
displayMenuForColumn,
theme,
},
...getColumnState(column.name),
suppressHeaderKeyboardEvent: (
Expand Down Expand Up @@ -247,7 +245,7 @@ const useDataViewer = (theme: string) => {

setColumns(columns);
});
}, [columns.length, theme, displayMenuForColumn]);
}, [columns.length, displayMenuForColumn]);

useEffect(() => {
window.addEventListener("contextmenu", contextMenuHandler, true);
Expand Down
47 changes: 47 additions & 0 deletions client/src/webview/useTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { useEffect, useMemo, useState } from "react";

const THEME_ATTRIBUTE = "data-vscode-theme-kind";
const SELECTOR = `[${THEME_ATTRIBUTE}]`;

/**
* This listens for changes to vscode's theme kind and updates our internal
* theme to match.
* @returns theme:string matching the ag grid theme for the vscode theme kind
*/
const useTheme = () => {
const [themeKind, setThemeKind] = useState(
document.querySelector(SELECTOR).getAttribute(THEME_ATTRIBUTE),
);
useEffect(() => {
const obs = new MutationObserver((record) =>
setThemeKind(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
(record[0].target as HTMLElement).getAttribute(THEME_ATTRIBUTE),
),
);
obs.observe(document.querySelector(SELECTOR), {
attributes: true,
attributeFilter: [THEME_ATTRIBUTE],
});
return () => {
obs.disconnect();
};
}, []);

const theme = useMemo(() => {
switch (themeKind) {
case "vscode-high-contrast-light":
case "vscode-light":
return "ag-theme-alpine";
case "vscode-high-contrast":
case "vscode-dark":
return "ag-theme-alpine-dark";
}
}, [themeKind]);

return theme;
};

export default useTheme;
Loading