Skip to content

Commit 0d927da

Browse files
Scott DoverScott Dover
authored andcommitted
chore: prep for code review
Signed-off-by: Scott Dover <[email protected]>
1 parent 402a220 commit 0d927da

File tree

9 files changed

+211
-210
lines changed

9 files changed

+211
-210
lines changed

client/src/connection/itc/ItcLibraryAdapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class ItcLibraryAdapter implements LibraryAdapter {
127127
}
128128
: {};
129129

130-
const { rows } = await this.getRows(item, start, limit);
130+
const { rows } = await this.getRows(item, start, limit, []);
131131

132132
rows.unshift(columns);
133133

client/src/connection/itc/script.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ class SASRunner{
258258
$this.dataConnection, # Use the active connection
259259
3, # adOpenStatic
260260
1, # adLockReadOnly
261-
1 # adCmdTableDirect
261+
1 # adCmdText
262262
)
263263
264264
$records = [List[List[object]]]::new()

client/src/webview/ColumnHeader.tsx

Lines changed: 17 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0
33
import { useRef } from "react";
44

5-
import { AgColumn, ColumnState, GridApi } from "ag-grid-community";
5+
import { AgColumn, GridApi } from "ag-grid-community";
66

7-
import { ColumnHeaderProps } from "./ColumnHeaderMenu";
7+
import { ColumnMenuProps, useColumnMenu } from "./ColumnMenu";
88

99
const getIconForColumnType = (type: string) => {
1010
switch (type.toLocaleLowerCase()) {
@@ -38,7 +38,7 @@ const ColumnHeader = ({
3838
column: AgColumn;
3939
currentColumn: () => AgColumn | undefined;
4040
columnType: string;
41-
setColumnMenu: (props: ColumnHeaderProps) => void;
41+
setColumnMenu: (props: ColumnMenuProps) => void;
4242
theme: string;
4343
}) => {
4444
const ref = useRef<HTMLButtonElement>(undefined!);
@@ -48,94 +48,37 @@ const ColumnHeader = ({
4848
column.sort && currentSortedColumns.length > 1
4949
? `${column.sortIndex + 1}`
5050
: "";
51+
const dropdownClassname =
52+
currentColumn?.colId === column.colId ? "active dropdown" : "dropdown";
5153

52-
const applyColumnState = (state: ColumnState[]) => {
53-
api.applyColumnState({ state, defaultState: { sort: null } });
54-
api.ensureIndexVisible(0);
55-
};
56-
54+
const getColumnMenu = useColumnMenu({ api, theme });
5755
const displayColumnMenu = () => {
5856
if (currentColumn) {
5957
return setColumnMenu(undefined);
6058
}
61-
const { height, top, left } = ref.current.getBoundingClientRect();
62-
setColumnMenu({
63-
left,
64-
top: top + height,
65-
column,
66-
theme,
67-
hasSort: api.getColumnState().some((c) => c.sort),
68-
sortColumn: (direction: "asc" | "desc" | null) => {
69-
const newColumnState = api.getColumnState().filter((c) => c.sort);
70-
const colIndex = newColumnState.findIndex(
71-
(c) => c.colId === column.colId,
72-
);
73-
if (colIndex === -1) {
74-
newColumnState.push({
75-
colId: column.colId,
76-
sort: direction,
77-
sortIndex: newColumnState.length,
78-
});
79-
} else {
80-
newColumnState[colIndex].sort = direction;
81-
}
82-
applyColumnState(newColumnState);
83-
},
84-
removeAllSorting: () =>
85-
applyColumnState(
86-
api
87-
.getColumnState()
88-
.filter((c) => c.sort)
89-
.map((c) => ({ colId: c.colId, sort: null })),
90-
),
91-
removeFromSort: () =>
92-
applyColumnState(
93-
api
94-
.getColumnState()
95-
.sort((a, b) => a.sortIndex - b.sortIndex)
96-
.filter((c) => c.sort && c.colId !== column.colId)
97-
// After we remove the column, lets reindex what's left
98-
.map((c, sortIndex) => ({ ...c, sortIndex })),
99-
),
100-
101-
dismissMenu: () => setColumnMenu(undefined),
102-
});
59+
setColumnMenu(
60+
getColumnMenu(column, ref.current.getBoundingClientRect(), () =>
61+
setColumnMenu(undefined),
62+
),
63+
);
10364
};
10465

10566
return (
10667
<div className={`ag-cell-label-container ${theme}`} role="presentation">
107-
<div
108-
data-ref="eLabel"
109-
className="ag-header-cell-label"
110-
role="presentation"
111-
>
112-
<span
113-
className={`header-icon ${getIconForColumnType(columnType)}`}
114-
></span>
68+
<div className="ag-header-cell-label" role="presentation">
69+
<span className={`header-icon ${getIconForColumnType(columnType)}`} />
11570
<span className="ag-header-cell-text">{column.colId}</span>
116-
11771
<span className="sort-icon-wrapper">
11872
{!!column.sort && (
11973
<>
120-
<span
121-
className={`sort-icon ${column.sort === "asc" ? "ascending" : "descending"}`}
122-
></span>
123-
{!!columnNumber && (
124-
<span className="sort-number">{columnNumber}</span>
125-
)}
74+
<span className={`icon ${column.sort}`}></span>
75+
{!!columnNumber && <span className="number">{columnNumber}</span>}
12676
</>
12777
)}
12878
</span>
129-
130-
<div
131-
className={
132-
currentColumn?.colId === column.colId
133-
? "active dropdown"
134-
: "dropdown"
135-
}
136-
>
79+
<div className={dropdownClassname}>
13780
<button ref={ref} type="button" onClick={displayColumnMenu}>
138-
<span></span>
81+
<span />
13982
</button>
14083
</div>
14184
</div>

client/src/webview/ColumnMenu.tsx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import { AgColumn, ColumnState, GridApi } from "ag-grid-community";
4+
5+
import GridMenu from "./GridMenu";
6+
7+
export interface ColumnMenuProps {
8+
column: AgColumn;
9+
dismissMenu: () => void;
10+
hasSort: boolean;
11+
left: number;
12+
messages?: Record<string, string>;
13+
removeAllSorting: () => void;
14+
removeFromSort: () => void;
15+
sortColumn: (direction: "asc" | "desc") => void;
16+
theme: string;
17+
top: number;
18+
}
19+
20+
export const useColumnMenu = ({
21+
api,
22+
theme,
23+
}: {
24+
api: GridApi;
25+
theme: ColumnMenuProps["theme"];
26+
}) => {
27+
const applyColumnState = (state: ColumnState[]) => {
28+
api.applyColumnState({ state, defaultState: { sort: null } });
29+
api.ensureIndexVisible(0);
30+
};
31+
32+
return (
33+
column: AgColumn,
34+
{ height, top, left }: DOMRect,
35+
dismissMenu: () => void,
36+
) => ({
37+
column,
38+
dismissMenu,
39+
hasSort: api.getColumnState().some((c) => c.sort),
40+
left,
41+
theme,
42+
top: top + height,
43+
sortColumn: (direction: "asc" | "desc" | null) => {
44+
const newColumnState = api.getColumnState().filter((c) => c.sort);
45+
const colIndex = newColumnState.findIndex(
46+
(c) => c.colId === column.colId,
47+
);
48+
if (colIndex === -1) {
49+
newColumnState.push({
50+
colId: column.colId,
51+
sort: direction,
52+
sortIndex: newColumnState.length,
53+
});
54+
} else {
55+
newColumnState[colIndex].sort = direction;
56+
}
57+
applyColumnState(newColumnState);
58+
},
59+
removeAllSorting: () =>
60+
applyColumnState(
61+
api
62+
.getColumnState()
63+
.filter((c) => c.sort)
64+
.map((c) => ({ colId: c.colId, sort: null })),
65+
),
66+
removeFromSort: () =>
67+
applyColumnState(
68+
api
69+
.getColumnState()
70+
.sort((a, b) => a.sortIndex - b.sortIndex)
71+
.filter((c) => c.sort && c.colId !== column.colId)
72+
// After we remove the column, lets reindex what's left
73+
.map((c, sortIndex) => ({ ...c, sortIndex })),
74+
),
75+
});
76+
};
77+
78+
const ColumnMenu = ({
79+
column,
80+
dismissMenu,
81+
hasSort,
82+
left,
83+
removeAllSorting,
84+
removeFromSort,
85+
sortColumn,
86+
theme,
87+
top,
88+
messages: t,
89+
}: ColumnMenuProps) => {
90+
const menuItems = [
91+
{
92+
name: t.Sort,
93+
children: [
94+
{
95+
name:
96+
hasSort && !column.sort
97+
? t["Ascending (add to sorting)"]
98+
: t.Ascending,
99+
checked: column.sort === "asc",
100+
onPress: () => {
101+
sortColumn("asc");
102+
dismissMenu();
103+
},
104+
},
105+
{
106+
name:
107+
hasSort && !column.sort
108+
? t["Descending (add to sorting)"]
109+
: t.Descending,
110+
checked: column.sort === "desc",
111+
onPress: () => {
112+
sortColumn("desc");
113+
dismissMenu();
114+
},
115+
},
116+
"separator",
117+
{
118+
name: t["Remove sorting"],
119+
onPress: () => {
120+
removeFromSort();
121+
dismissMenu();
122+
},
123+
disabled: !hasSort || !column.sort,
124+
},
125+
{
126+
name: t["Remove all sorting"],
127+
onPress: () => {
128+
removeAllSorting();
129+
dismissMenu();
130+
},
131+
disabled: !hasSort,
132+
},
133+
],
134+
},
135+
];
136+
137+
return <GridMenu menuItems={menuItems} top={top} left={left} theme={theme} />;
138+
};
139+
140+
export default ColumnMenu;

client/src/webview/DataViewer.css

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,30 +126,30 @@ button,
126126
margin-left: auto;
127127
}
128128

129-
.sort-icon-wrapper .sort-icon,
130-
.sort-icon-wrapper .sort-number {
129+
.sort-icon-wrapper .icon,
130+
.sort-icon-wrapper .number {
131131
width: 16px;
132132
height: 16px;
133133
display: inline-block;
134134
vertical-align: middle;
135135
}
136136

137-
.sort-icon-wrapper .sort-icon.ascending {
137+
.sort-icon-wrapper .icon.asc {
138138
background: url(../../../icons/light/arrow-up.svg);
139139
background-size: 16px 16px;
140140
}
141141

142-
.vscode-dark .sort-icon-wrapper .sort-icon.ascending {
142+
.vscode-dark .sort-icon-wrapper .icon.asc {
143143
background: url(../../../icons/dark/arrow-up.svg);
144144
background-size: 16px 16px;
145145
}
146146

147-
.sort-icon-wrapper .sort-icon.descending {
147+
.sort-icon-wrapper .icon.desc {
148148
background: url(../../../icons/light/arrow-down.svg);
149149
background-size: 16px 16px;
150150
}
151151

152-
.vscode-dark .sort-icon-wrapper .sort-icon.descending {
152+
.vscode-dark .sort-icon-wrapper .icon.desc {
153153
background: url(../../../icons/dark/arrow-down.svg);
154154
background-size: 16px 16px;
155155
}

client/src/webview/DataViewer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createRoot } from "react-dom/client";
66
import { AgGridReact } from "ag-grid-react";
77

88
import ".";
9-
import ColumnHeaderMenu from "./ColumnHeaderMenu";
9+
import ColumnMenu from "./ColumnMenu";
1010
import useDataViewer from "./useDataViewer";
1111

1212
import "./DataViewer.css";
@@ -61,7 +61,7 @@ const DataViewer = () => {
6161
return (
6262
<div className="data-viewer">
6363
{columnMenu && (
64-
<ColumnHeaderMenu theme={theme} messages={messages} {...columnMenu} />
64+
<ColumnMenu theme={theme} messages={messages} {...columnMenu} />
6565
)}
6666
<div
6767
className={`ag-grid-wrapper ${theme}`}

0 commit comments

Comments
 (0)