Skip to content

Commit 24e55aa

Browse files
Scott DoverScott Dover
authored andcommitted
chore: experiment with using native ag grid html/css
Signed-off-by: Scott Dover <[email protected]>
1 parent c5e6517 commit 24e55aa

File tree

9 files changed

+228
-22
lines changed

9 files changed

+228
-22
lines changed

client/src/webview/ColumnHeader.tsx

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,20 @@ const ColumnHeader = ({
3030
currentColumn: getCurrentColumn,
3131
columnType,
3232
setColumnMenu,
33+
theme,
3334
}: {
3435
api: GridApi;
3536
column: AgColumn;
3637
currentColumn: () => AgColumn | undefined;
3738
columnType: string;
3839
setColumnMenu: (props: ColumnHeaderProps) => void;
40+
theme: string;
3941
}) => {
4042
const ref = useRef<HTMLButtonElement>(undefined!);
4143
const currentColumn = getCurrentColumn();
4244

43-
console.log({ currentColumn, column });
4445
return (
45-
<div className="ag-cell-label-container" role="presentation">
46+
<div className={`ag-cell-label-container ${theme}`} role="presentation">
4647
<div
4748
data-ref="eLabel"
4849
className="ag-header-cell-label"
@@ -52,16 +53,15 @@ const ColumnHeader = ({
5253
className={`header-icon ${getIconForColumnType(columnType)}`}
5354
></span>
5455
<span className="ag-header-cell-text">{column.colId}</span>
55-
<span
56-
data-ref="eSortOrder"
57-
className="ag-header-icon ag-header-label-icon ag-sort-order"
58-
aria-hidden="true"
59-
></span>
6056
{column.sort === "asc" && (
61-
<span className="ag-header-icon ag-header-label-icon ag-sort-ascending-icon"></span>
57+
<span className="sort-icon-wrapper">
58+
<span className="sort-icon ascending"></span>
59+
</span>
6260
)}
6361
{column.sort === "desc" && (
64-
<span className="ag-header-icon ag-header-label-icon ag-sort-descending-icon"></span>
62+
<span className="sort-icon-wrapper">
63+
<span className="sort-icon descending"></span>
64+
</span>
6565
)}
6666
<div className="dropdown">
6767
<button
@@ -72,24 +72,17 @@ const ColumnHeader = ({
7272
if (currentColumn) {
7373
return setColumnMenu(undefined);
7474
}
75-
const { height, top, right } =
76-
ref.current.getBoundingClientRect();
75+
const { height, top, left } = ref.current.getBoundingClientRect();
7776
setColumnMenu({
78-
left: right,
77+
left,
7978
top: top + height,
8079
column,
80+
theme,
8181
sortColumn: (direction: "asc" | "desc" | null) => {
8282
api.applyColumnState({
8383
state: [{ colId: column.colId, sort: direction }],
8484
defaultState: { sort: null },
8585
});
86-
// console.log(props.api.getColumnState());
87-
// props.api.applyColumnState({
88-
// state: [{}]
89-
// })
90-
// const currentSort = props.api.sort
91-
// console.log(direction);
92-
// console.log(props);
9386
},
9487
dismissMenu: () => setColumnMenu(undefined),
9588
});

client/src/webview/ColumnHeaderMenu.tsx

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Fragment, useRef, useState } from "react";
2+
13
import { AgColumn } from "ag-grid-community";
24

35
export interface ColumnHeaderProps {
@@ -6,15 +8,187 @@ export interface ColumnHeaderProps {
68
column: AgColumn;
79
sortColumn: (direction: "asc" | "desc") => void;
810
dismissMenu: () => void;
11+
theme: string;
12+
}
13+
14+
interface MenuItem {
15+
name: string;
16+
checked?: boolean;
17+
onPress?: () => void;
18+
children?: (MenuItem | string)[];
919
}
1020

21+
const GridMenu = ({
22+
menuItems,
23+
theme,
24+
top,
25+
left,
26+
subMenu,
27+
}: {
28+
menuItems: (MenuItem | string)[];
29+
theme: string;
30+
top: number;
31+
left: number;
32+
subMenu?: boolean;
33+
}) => {
34+
const menuRef = useRef<HTMLDivElement>(undefined);
35+
const [subMenuItems, setSubMenuItems] = useState<(MenuItem | string)[]>([]);
36+
const className = subMenu
37+
? `ag-menu ag-ltr ag-popup-child ${theme}`
38+
: `ag-menu ag-column-menu ag-ltr ag-popup-child ag-popup-positioned-under ${theme}`;
39+
40+
return (
41+
<Fragment>
42+
{subMenuItems.length > 0 && (
43+
<GridMenu
44+
menuItems={subMenuItems}
45+
theme={theme}
46+
top={top}
47+
left={left + menuRef.current.getBoundingClientRect().width}
48+
subMenu
49+
/>
50+
)}
51+
<div className="ag-theme-sas ag-popup">
52+
<div
53+
className={className}
54+
role="presentation"
55+
style={{ top, left }}
56+
ref={menuRef}
57+
>
58+
{menuItems.map((menuItem, index) => {
59+
if (typeof menuItem === "string") {
60+
return (
61+
<div
62+
className="ag-menu-separator"
63+
aria-hidden="true"
64+
key={index}
65+
>
66+
{" "}
67+
<div className="ag-menu-separator-part"></div>{" "}
68+
<div className="ag-menu-separator-part"></div>{" "}
69+
<div className="ag-menu-separator-part"></div>{" "}
70+
<div className="ag-menu-separator-part"></div>{" "}
71+
</div>
72+
);
73+
}
74+
return (
75+
<div
76+
className="ag-menu-list ag-focus-managed"
77+
role="menu"
78+
key={menuItem.name}
79+
>
80+
<div
81+
className="ag-tab-guard ag-tab-guard-top"
82+
role="presentation"
83+
></div>
84+
<div
85+
aria-expanded="false"
86+
className="ag-menu-option"
87+
role="menuitem"
88+
aria-haspopup="menu"
89+
tabIndex={-1}
90+
>
91+
<span
92+
className="ag-menu-option-part ag-menu-option-icon"
93+
data-ref="eIcon"
94+
role="presentation"
95+
>
96+
{menuItem.checked && (
97+
<span
98+
className="ag-icon ag-icon-tick"
99+
role="presentation"
100+
unselectable="on"
101+
></span>
102+
)}
103+
</span>
104+
<span
105+
className="ag-menu-option-part ag-menu-option-text"
106+
data-ref="eName"
107+
onClick={() => {
108+
if (menuItem.onPress) {
109+
return menuItem.onPress();
110+
}
111+
if (menuItem.children) {
112+
setSubMenuItems(menuItem.children);
113+
}
114+
}}
115+
>
116+
{menuItem.name}
117+
</span>
118+
<span
119+
className="ag-menu-option-part ag-menu-option-shortcut"
120+
data-ref="eShortcut"
121+
></span>
122+
{menuItem.children && menuItem.children.length > 0 && (
123+
<span
124+
className="ag-menu-option-part ag-menu-option-popup-pointer"
125+
data-ref="ePopupPointer"
126+
>
127+
<span
128+
className="ag-icon ag-icon-small-right"
129+
role="presentation"
130+
unselectable="on"
131+
></span>
132+
</span>
133+
)}
134+
</div>
135+
<div
136+
className="ag-tab-guard ag-tab-guard-bottom"
137+
role="presentation"
138+
></div>
139+
</div>
140+
);
141+
})}
142+
</div>
143+
</div>
144+
</Fragment>
145+
);
146+
};
147+
11148
const ColumnHeaderMenu = ({
12149
left,
13150
top,
14151
column,
15152
sortColumn,
16153
dismissMenu,
154+
theme,
17155
}: ColumnHeaderProps) => {
156+
const menuItems = [
157+
{
158+
name: "Sort",
159+
children: [
160+
{
161+
name: "Ascending",
162+
checked: column.sort === "asc",
163+
onPress: () => {
164+
sortColumn(column.sort === "asc" ? null : "asc");
165+
dismissMenu();
166+
},
167+
},
168+
{
169+
name: "Descending",
170+
checked: column.sort === "desc",
171+
onPress: () => {
172+
sortColumn(column.sort === "desc" ? null : "desc");
173+
dismissMenu();
174+
},
175+
},
176+
"separator",
177+
{
178+
name: "Remove Sorting",
179+
},
180+
{
181+
name: "Remove all sorting",
182+
},
183+
],
184+
},
185+
];
186+
187+
if (1 === 1) {
188+
return (
189+
<GridMenu menuItems={menuItems} top={top} left={left} theme={theme} />
190+
);
191+
}
18192
return (
19193
<div className="header-menu" style={{ left, top }}>
20194
<ul>

client/src/webview/DataViewer.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,29 @@ button {
179179
.ag-header-cell-label button.active {
180180
display: block;
181181
}
182+
183+
.sort-icon-wrapper .sort-icon {
184+
width: 16px;
185+
height: 16px;
186+
display: inline-block;
187+
}
188+
189+
.sort-icon-wrapper .sort-icon.ascending {
190+
background: url(../../../icons/light/arrow-up.svg);
191+
background-size: 16px 16px;
192+
}
193+
194+
.vscode-dark .sort-icon-wrapper .sort-icon.ascending {
195+
background: url(../../../icons/dark/arrow-up.svg);
196+
background-size: 16px 16px;
197+
}
198+
199+
.sort-icon-wrapper .sort-icon.descending {
200+
background: url(../../../icons/light/arrow-down.svg);
201+
background-size: 16px 16px;
202+
}
203+
204+
.vscode-dark .sort-icon-wrapper .sort-icon.descending {
205+
background: url(../../../icons/dark/arrow-down.svg);
206+
background-size: 16px 16px;
207+
}

client/src/webview/DataViewer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ const gridStyles = {
2121
};
2222

2323
const DataViewer = () => {
24-
const { columns, onGridReady, columnMenu } = useDataViewer();
2524
const theme = useMemo(() => {
2625
const themeKind = document
2726
.querySelector("[data-vscode-theme-kind]")
@@ -36,14 +35,15 @@ const DataViewer = () => {
3635
return "ag-theme-alpine-dark";
3736
}
3837
}, []);
38+
const { columns, onGridReady, columnMenu } = useDataViewer(theme);
3939

4040
if (columns.length === 0) {
4141
return null;
4242
}
4343

4444
return (
4545
<div className="data-viewer">
46-
{columnMenu && <ColumnHeaderMenu {...columnMenu} />}
46+
{columnMenu && <ColumnHeaderMenu theme={theme} {...columnMenu} />}
4747
<div className={`ag-grid-wrapper ${theme}`} style={gridStyles}>
4848
<AgGridReact
4949
cacheBlockSize={100}

client/src/webview/useDataViewer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ const fetchColumns = (): Promise<Column[]> => {
101101
});
102102
};
103103

104-
const useDataViewer = () => {
104+
const useDataViewer = (theme: string) => {
105105
const [columns, setColumns] = useState<ColDef[]>([]);
106106
const [columnMenu, setColumnMenu] = useState<ColumnHeaderProps | undefined>();
107107

@@ -155,6 +155,7 @@ const useDataViewer = () => {
155155
columnType: column.type,
156156
setColumnMenu,
157157
currentColumn: () => columnMenuRef.current?.column,
158+
theme,
158159
},
159160
}));
160161
columns.unshift({

icons/dark/arrow-down.svg

Lines changed: 3 additions & 0 deletions
Loading

icons/dark/arrow-up.svg

Lines changed: 3 additions & 0 deletions
Loading

icons/light/arrow-down.svg

Lines changed: 3 additions & 0 deletions
Loading

icons/light/arrow-up.svg

Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)