Skip to content

Commit 421a7ad

Browse files
aasimkhan30Aasim Khan
andauthored
Adding ability to resize grid columns in results (#20335)
* initi commit * fix stuff * fixing command bar accessibility * commom utils * fixing all grid accessibility * loc * fixing width * fix comments * undoing extra change * Fixed stuff * Make it consistent * undoing comment * Adding comments * adding resize code. * loc * Fixing stuff * code cleanup * cleaning up keyboard shortcuts. * Fixing validation * Fixing validation loc * Fixing minimum width * Fixing fluent react package * removing package resolutions * code cleanup * keyboard shortcut. * fix formatting --------- Co-authored-by: Aasim Khan <[email protected]>
1 parent e23a3e1 commit 421a7ad

File tree

16 files changed

+738
-479
lines changed

16 files changed

+738
-479
lines changed

localization/l10n/bundle.l10n.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,16 @@
411411
"comment": ["{0} is the number of selected rows"]
412412
},
413413
"Sort": "Sort",
414+
"Resize": "Resize",
415+
"Resize column '{0}'/{0} is the name of the column": {
416+
"message": "Resize column '{0}'",
417+
"comment": ["{0} is the name of the column"]
418+
},
419+
"Enter desired column width in pixels": "Enter desired column width in pixels",
420+
"Column width must be at least {0} pixels./{0} is the minimum column width in pixels": {
421+
"message": "Column width must be at least {0} pixels.",
422+
"comment": ["{0} is the minimum column width in pixels"]
423+
},
414424
"Add new column": "Add new column",
415425
"Table": "Table",
416426
"Save": "Save",

localization/xliff/vscode-mssql.xlf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,10 @@
631631
<trans-unit id="++CODE++bb7bef87d3a1f764c39c6df670038039c136d360153a1ced5c633afa9590fd36">
632632
<source xml:lang="en">Column name cannot be empty</source>
633633
</trans-unit>
634+
<trans-unit id="++CODE++79304c2a3f7dd799711c04e0513ca9fce1762fbdbf0e8dbd378987d42e35fee5">
635+
<source xml:lang="en">Column width must be at least {0} pixels.</source>
636+
<note>{0} is the minimum column width in pixels</note>
637+
</trans-unit>
634638
<trans-unit id="++CODE++53aade77cd69a77bbb51bf8ca4e7ea8b282db75edce8883775e47042478d35b5">
635639
<source xml:lang="en">Columns</source>
636640
</trans-unit>
@@ -1180,6 +1184,9 @@
11801184
<trans-unit id="++CODE++91cf329b31a4b1a6c0bdec6de797b6a90ab9309490bd62ec38f98f2d0f6db423">
11811185
<source xml:lang="en">Enter description (optional)</source>
11821186
</trans-unit>
1187+
<trans-unit id="++CODE++657e6ba252bef59f38c0dbaab7e8e25d55503ba3a41d6ce527f92c88baea3576">
1188+
<source xml:lang="en">Enter desired column width in pixels</source>
1189+
</trans-unit>
11831190
<trans-unit id="++CODE++caaac8aa0d0641aea8406e7f1011aaeab33423fdd4ee9b3354ee88cc1487b0bf">
11841191
<source xml:lang="en">Enter hostname</source>
11851192
</trans-unit>
@@ -2623,6 +2630,13 @@
26232630
<trans-unit id="++CODE++daee7606b339f3c339076fe2c9f372a3ff40c8ee896005d829c7481b64ca5303">
26242631
<source xml:lang="en">Reset</source>
26252632
</trans-unit>
2633+
<trans-unit id="++CODE++2956e06ac0651084bbd5558dfe469615e9a5fc3072f60a09a9cb3c597a19324c">
2634+
<source xml:lang="en">Resize</source>
2635+
</trans-unit>
2636+
<trans-unit id="++CODE++fc71b6e1d435d817984f9c7394f47e21505efa2a9f81b5f3e17082ced1432cc7">
2637+
<source xml:lang="en">Resize column &apos;{0}&apos;</source>
2638+
<note>{0} is the name of the column</note>
2639+
</trans-unit>
26262640
<trans-unit id="++CODE++ae6d9c313ef115d3be932d158acd74f5ab4db5c068cdc2c10ade081168b185d5">
26272641
<source xml:lang="en">Resource Group</source>
26282642
</trans-unit>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"@dagrejs/dagre": "^1.1.4",
8989
"@eslint/compat": "^1.1.0",
9090
"@fluentui-contrib/react-data-grid-react-window": "^1.2.0",
91-
"@fluentui/react-components": "^9.72.2",
91+
"@fluentui/react-components": "^9.72.3",
9292
"@istanbuljs/nyc-config-typescript": "^1.0.2",
9393
"@monaco-editor/react": "^4.6.0",
9494
"@playwright/test": "^1.45.0",

src/reactviews/common/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export const cmdAKeyboardShortcut = "⌘A";
99
export const ctrlAKeyboardShortcut = "Ctrl+A";
1010
export const cmdCKeyboardShortcut = "⌘C";
1111
export const ctrlCKeyboardShortcut = "Ctrl+C";
12-
export const altShiftOKeyboardShortcut = "Shift+Alt+O";
12+
export const altShiftOKeyboardShortcut = "Alt+Shift+O";
13+
export const altShiftSKeyboardShortcut = "Alt+Shift+S";

src/reactviews/common/locConstants.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,22 @@ export class LocConstants {
520520
}),
521521
sort: l10n.t("Sort"),
522522
filter: l10n.t("Filter"),
523+
resize: l10n.t("Resize"),
524+
resizeColumn: (columnName: string) => {
525+
return l10n.t({
526+
message: "Resize column '{0}'",
527+
args: [columnName],
528+
comment: ["{0} is the name of the column"],
529+
});
530+
},
531+
enterDesiredColumnWidth: l10n.t("Enter desired column width in pixels"),
532+
resizeValidationError: (minWidth: number) => {
533+
return l10n.t({
534+
message: "Column width must be at least {0} pixels.",
535+
args: [minWidth],
536+
comment: ["{0} is the minimum column width in pixels"],
537+
});
538+
},
523539
};
524540
}
525541

src/reactviews/common/utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,13 @@ export function isMac(): boolean {
129129
}
130130

131131
/**
132-
* Checks if the meta key is pressed based on the user's OS.
132+
* Checks if the meta key on Mac or Ctrl key on Windows/Linux is pressed.
133133
* @param e The keyboard or mouse event to check.
134134
* @returns True if the meta key is pressed, false otherwise.
135135
*/
136-
export function isMetaKeyPressed(e: KeyboardEvent | MouseEvent | React.KeyboardEvent): boolean {
136+
export function isMetaOrCtrlKeyPressed(
137+
e: KeyboardEvent | MouseEvent | React.KeyboardEvent,
138+
): boolean {
137139
return isMac() ? e.metaKey : e.ctrlKey;
138140
}
139141

src/reactviews/pages/QueryResult/queryResultPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { makeStyles, shorthands } from "@fluentui/react-components";
77
import { useEffect } from "react";
88
import { QueryResultPane } from "./queryResultPane";
99
import { KeyCode } from "../../common/keys";
10-
import { isMetaKeyPressed } from "../../common/utils";
10+
import { isMetaOrCtrlKeyPressed } from "../../common/utils";
1111

1212
const useStyles = makeStyles({
1313
root: {
@@ -95,7 +95,7 @@ export const QueryResult = () => {
9595
// This is needed to stop the browser from selecting all the raw text in the webview when ctrl+a is pressed
9696
useEffect(() => {
9797
const handleKeyDown = async (e: KeyboardEvent): Promise<void> => {
98-
if (isMetaKeyPressed(e) && e.code === KeyCode.KeyA) {
98+
if (isMetaOrCtrlKeyPressed(e) && e.code === KeyCode.KeyA) {
9999
e.preventDefault();
100100
e.stopPropagation();
101101
}

src/reactviews/pages/QueryResult/queryResultStateProvider.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import ColumnMenuPopup, {
2323
FilterListItem,
2424
FilterValue,
2525
} from "./table/plugins/ColumnMenuPopup";
26+
import { TableColumnResizeDialog } from "./table/TableColumnResizeDialog";
2627

2728
export interface ColumnFilterPopupOptions {
2829
columnId: string;
@@ -36,8 +37,22 @@ export interface ColumnFilterPopupOptions {
3637
onSortAscending: () => Promise<void>;
3738
onSortDescending: () => Promise<void>;
3839
currentSort: SortProperties;
40+
onResize: () => void;
3941
}
4042

43+
/**
44+
* Options for opening the resize column dialog
45+
*/
46+
type ResizeColumnDialogState = {
47+
open: boolean;
48+
columnId: string;
49+
columnName: string;
50+
initialWidth: number;
51+
gridId: string;
52+
onSubmit: (width: number) => Promise<void> | void;
53+
onDismiss: () => void;
54+
};
55+
4156
export interface QueryResultReactProvider
4257
extends Omit<ExecutionPlanProvider, "getExecutionPlan">,
4358
CoreRPCs {
@@ -65,6 +80,12 @@ export interface QueryResultReactProvider
6580
* @param type the type of file to open
6681
*/
6782
openFileThroughLink(content: string, type: string): void;
83+
/**
84+
* Opens the resize column dialog
85+
* @param options options for the resize dialog
86+
* @returns void
87+
*/
88+
openResizeDialog: (options: Partial<ResizeColumnDialogState>) => void;
6889
}
6990

7091
export const QueryResultCommandsContext = createContext<QueryResultReactProvider | undefined>(
@@ -89,6 +110,16 @@ const QueryResultStateProvider: React.FC<QueryResultProviderProps> = ({ children
89110
undefined,
90111
);
91112

113+
const [resizeDialogState, setResizeDialogState] = useState<ResizeColumnDialogState>({
114+
open: false,
115+
columnId: "",
116+
columnName: "",
117+
initialWidth: 0,
118+
gridId: "",
119+
onDismiss: () => {},
120+
onSubmit: () => {},
121+
});
122+
92123
const hideFilterPopup = useCallback(() => {
93124
setFilterPopupState((state) => {
94125
if (state?.onDismiss) {
@@ -171,6 +202,13 @@ const QueryResultStateProvider: React.FC<QueryResultProviderProps> = ({ children
171202
updateTotalCost: (addedCost: number) => {
172203
extensionRpc.action("updateTotalCost", { addedCost });
173204
},
205+
openResizeDialog: (options: Partial<ResizeColumnDialogState>) => {
206+
setResizeDialogState((state) => ({
207+
...state,
208+
...options,
209+
open: true,
210+
}));
211+
},
174212
}),
175213
[extensionRpc, hideFilterPopup],
176214
);
@@ -227,9 +265,28 @@ const QueryResultStateProvider: React.FC<QueryResultProviderProps> = ({ children
227265
onClearSort={filterPopupState.onClearSort}
228266
onSortAscending={filterPopupState.onSortAscending}
229267
onSortDescending={filterPopupState.onSortDescending}
268+
onResize={() => {
269+
hideFilterPopup();
270+
filterPopupState.onResize();
271+
}}
230272
currentSort={filterPopupState.currentSort}
231273
/>
232274
)}
275+
{resizeDialogState.open && (
276+
<TableColumnResizeDialog
277+
open={resizeDialogState.open}
278+
columnName={resizeDialogState.columnName}
279+
initialWidth={resizeDialogState.initialWidth}
280+
onSubmit={async (newWidth: number) => {
281+
await resizeDialogState.onSubmit(newWidth);
282+
setResizeDialogState((state) => ({ ...state, open: false }));
283+
}}
284+
onDismiss={() => {
285+
resizeDialogState.onDismiss();
286+
setResizeDialogState((state) => ({ ...state, open: false }));
287+
}}
288+
/>
289+
)}
233290
</QueryResultCommandsContext.Provider>
234291
);
235292
};
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import {
7+
Button,
8+
Dialog,
9+
DialogActions,
10+
DialogBody,
11+
DialogContent,
12+
DialogSurface,
13+
DialogTitle,
14+
Field,
15+
Input,
16+
} from "@fluentui/react-components";
17+
import { useEffect, useState } from "react";
18+
import { locConstants } from "../../../common/locConstants";
19+
import { MAX_COLUMN_WIDTH_PX as maxWidth, MIN_COLUMN_WIDTH_PX as minWidth } from "../table/table";
20+
21+
interface TableColumnResizeDialogProps {
22+
open: boolean;
23+
columnName: string;
24+
initialWidth: number;
25+
onSubmit: (width: number) => Promise<void> | void;
26+
onDismiss: () => void;
27+
}
28+
29+
export const TableColumnResizeDialog: React.FC<TableColumnResizeDialogProps> = ({
30+
open,
31+
columnName,
32+
initialWidth,
33+
onSubmit,
34+
onDismiss,
35+
}) => {
36+
const [inputValue, setInputValue] = useState<string>(Math.round(initialWidth).toString());
37+
38+
useEffect(() => {
39+
if (open) {
40+
setInputValue(Math.round(initialWidth).toString());
41+
}
42+
}, [initialWidth, open]);
43+
44+
const isValid = (inputValue: string) => {
45+
const parsedWidth = parseInt(inputValue);
46+
const isValid = Number.isFinite(parsedWidth) && parsedWidth >= minWidth;
47+
return isValid;
48+
};
49+
50+
if (!open) {
51+
return null;
52+
}
53+
54+
const handleSubmit = () => {
55+
if (!isValid(inputValue)) {
56+
return;
57+
}
58+
void onSubmit(parseInt(inputValue));
59+
};
60+
61+
return (
62+
<Dialog
63+
open={open}
64+
onOpenChange={(_, data) => {
65+
if (!data.open) {
66+
onDismiss();
67+
}
68+
}}>
69+
<DialogSurface>
70+
<DialogBody>
71+
<DialogTitle>{locConstants.queryResult.resizeColumn(columnName)}</DialogTitle>
72+
<DialogContent>
73+
<Field
74+
label={locConstants.queryResult.enterDesiredColumnWidth}
75+
validationMessage={
76+
isValid(inputValue)
77+
? undefined
78+
: locConstants.queryResult.resizeValidationError(minWidth)
79+
}>
80+
<Input
81+
type="number"
82+
value={inputValue}
83+
min={minWidth}
84+
max={maxWidth}
85+
onChange={(_, data) => setInputValue(data.value)}
86+
onKeyDown={(event) => {
87+
if (event.key === "Enter") {
88+
event.preventDefault();
89+
handleSubmit();
90+
}
91+
}}
92+
/>
93+
</Field>
94+
</DialogContent>
95+
<DialogActions>
96+
<Button
97+
appearance="primary"
98+
onClick={handleSubmit}
99+
disabled={!isValid(inputValue)}>
100+
{locConstants.queryResult.resize}
101+
</Button>
102+
<Button appearance="secondary" onClick={onDismiss}>
103+
{locConstants.common.cancel}
104+
</Button>
105+
</DialogActions>
106+
</DialogBody>
107+
</DialogSurface>
108+
</Dialog>
109+
);
110+
};

0 commit comments

Comments
 (0)