* add query results toolbar buttons

* wire up rpc calls

* cleanup

* fix theming

* add loc, other pr comments

* add locfile

* remove null checks

* pr comments, rename file

* address more pr comments
This commit is contained in:
Christopher Suh 2024-09-27 14:00:56 -07:00 коммит произвёл GitHub
Родитель 1421218db8
Коммит c5cafa027b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 148 добавлений и 18 удалений

Просмотреть файл

@ -94,6 +94,9 @@
"Move Up": "Move Up",
"Move Down": "Move Down",
"Delete": "Delete",
"Save as CSV": "Save as CSV",
"Save as JSON": "Save as JSON",
"Save as Excel": "Save as Excel",
"Results": "Results",
"Messages": "Messages",
"Timestamp": "Timestamp",
@ -571,9 +574,6 @@
"A SQL editor must have focus before executing this command": "A SQL editor must have focus before executing this command",
"Maximize": "Maximize",
"Restore": "Restore",
"Save as CSV": "Save as CSV",
"Save as JSON": "Save as JSON",
"Save as Excel": "Save as Excel",
"CSV": "CSV",
"JSON": "JSON",
"Excel": "Excel",

Просмотреть файл

@ -26,13 +26,6 @@ export enum ContentType {
LocalizedTexts = 12,
}
export interface ISlickRange {
fromCell: number;
fromRow: number;
toCell: number;
toRow: number;
}
export enum AuthenticationTypes {
Integrated = 1,
SqlLogin = 2,
@ -290,13 +283,6 @@ export interface ISelectionData {
endColumn: number;
}
export interface ISlickRange {
fromCell: number;
fromRow: number;
toCell: number;
toRow: number;
}
export enum FieldType {
String = 0,
Boolean = 1,

Просмотреть файл

@ -47,6 +47,15 @@ export class QueryResultWebviewController extends ReactWebviewViewController<
message.selectionData,
);
});
this.registerRequestHandler("saveResults", async (message) => {
return await this._sqlOutputContentProvider.saveResultsRequestHandler(
message.uri,
message.batchId,
message.resultId,
message.format,
message.selection,
);
});
this.registerReducer("setResultTab", async (state, payload) => {
state.tabStates.resultPaneTab = payload.tabId;
return state;

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14 4.552V13c-.028.825-.593 2-2.035 2h-8C3.012 15 2 14.299 2 13V8H.586l2-2H0V2h2.586l-2-2h4.828l1 1h3.608L14 4.552z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M9 3.586L8.414 3H9v.586zM12 6h-2v7h2V6zm-6 7V7.414L5.414 8H4v5h2zm1 0h2V4.414l-1 1V6h-.586L7 6.414V13z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M8 5.414V6h-.586L8 5.414zM13 5v8s-.035 1-1.035 1h-8S3 14 3 13V8h1v5h2V7.414l1-1V13h2V4.414L9.414 4 9 3.586V3h-.586l-1-1h2.227L13 5zm-1 1h-2v7h2V6z" id="iconBg"/><path class="icon-vs-action-blue" d="M8 4L5 7H3l2-2H1V3h4L3 1h2l3 3z" id="colorAction"/></svg>

После

Ширина:  |  Высота:  |  Размер: 940 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14 4.552V13c-.028.825-.593 2-2.035 2h-8C3.012 15 2 14.299 2 13V8H.586l2-2H0V2h2.586l-2-2h4.828l1 1h3.608L14 4.552z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M9 3.586L8.414 3H9v.586zM12 6h-2v7h2V6zm-6 7V7.414L5.414 8H4v5h2zm1 0h2V4.414l-1 1V6h-.586L7 6.414V13z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M8 5.414V6h-.586L8 5.414zM13 5v8s-.035 1-1.035 1h-8S3 14 3 13V8h1v5h2V7.414l1-1V13h2V4.414L9.414 4 9 3.586V3h-.586l-1-1h2.227L13 5zm-1 1h-2v7h2V6z" id="iconBg"/><path class="icon-vs-action-blue" d="M8 4L5 7H3l2-2H1V3h4L3 1h2l3 3z" id="colorAction"/></svg>

После

Ширина:  |  Высота:  |  Размер: 940 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 0v16H0V0h16z" id="canvas"/><path class="icon-vs-out" d="M16 7v8H3.964C3.012 15 2 14.299 2 13V8H.586l2-2H0V2h2.586l-2-2h4.828l1 1h3.608L14 4.552V7h2z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M9 6h3v1H7v6H4V8h1.414L9 4.414V6zm0-3h-.586L9 3.586V3zm0 10h1v-1H9v1zm0-2h1v-1H9v1zm2 2h1v-1h-1v1zm2-3v1h1v-1h-1zm-2 1h1v-1h-1v1zm2 2h1v-1h-1v1z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M8.414 3l-1-1h2.227L13 5v2h-1V6H9V4.414L9.414 4 9 3.586V3h-.586zM4 8H3v5c0 1 .964 1 .964 1H7v-1H4V8zm11 0v6H8V8h7zm-5 4H9v1h1v-1zm0-2H9v1h1v-1zm2 2h-1v1h1v-1zm0-2h-1v1h1v-1zm2 2h-1v1h1v-1zm0-2h-1v1h1v-1z" id="iconBg"/><g id="colorAction"><path class="icon-vs-action-blue" d="M8 4L5 7H3l2-2H1V3h4L3 1h2l3 3z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 1022 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 0v16H0V0h16z" id="canvas"/><path class="icon-vs-out" d="M16 7v8H3.964C3.012 15 2 14.299 2 13V8H.586l2-2H0V2h2.586l-2-2h4.828l1 1h3.608L14 4.552V7h2z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M9 6h3v1H7v6H4V8h1.414L9 4.414V6zm0-3h-.586L9 3.586V3zm0 10h1v-1H9v1zm0-2h1v-1H9v1zm2 2h1v-1h-1v1zm2-3v1h1v-1h-1zm-2 1h1v-1h-1v1zm2 2h1v-1h-1v1z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M8.414 3l-1-1h2.227L13 5v2h-1V6H9V4.414L9.414 4 9 3.586V3h-.586zM4 8H3v5c0 1 .964 1 .964 1H7v-1H4V8zm11 0v6H8V8h7zm-5 4H9v1h1v-1zm0-2H9v1h1v-1zm2 2h-1v1h1v-1zm0-2h-1v1h1v-1zm2 2h-1v1h1v-1zm0-2h-1v1h1v-1z" id="iconBg"/><g id="colorAction"><path class="icon-vs-action-blue" d="M8 4L5 7H3l2-2H1V3h4L3 1h2l3 3z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 1022 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 8.38v2.258s-.992-.001-.997-.003c-.012.052-.003.14-.003.281v1.579c0 1.271-.37 2.185-1.054 2.746-.638.522-1.576.759-2.822.759H10v-3.247s1.014-.001 1.037-.003c.004-.046-.037-.117-.037-.214V11.08c0-.943.222-1.606.539-2.072C11.223 8.527 11 7.843 11 6.869V5.468c0-.087.102-.286.094-.325-.02-.002.063-.143.03-.143H10V2h1.124c1.251 0 2.193.265 2.832.81C14.633 3.387 15 4.3 15 5.522V7h.919L16 8.38zM9.414 4l-4-4H.586l2 2H0v4h2v.586L1.586 7H1v.586L.586 8H1v1.638L1.329 11H2v1.536c0 1.247.495 2.149 1.19 2.711.641.517 1.697.753 2.937.753H7v-3.247s-1.011-.001-1.033-.003c-.008-.053.033-.127.033-.228v-1.401c0-.962-.224-1.637-.542-2.111.256-.378.444-.89.511-1.564L9.414 4z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M15 8.38v1.258c-.697 0-1.046.426-1.046 1.278v1.579c0 .961-.223 1.625-.666 1.989-.445.364-1.166.547-2.164.547v-1.278c.383 0 .661-.092.834-.277s.26-.498.26-.94V11.08c0-1.089.349-1.771 1.046-2.044v-.027c-.697-.287-1.046-1-1.046-2.14V5.468c0-.793-.364-1.189-1.094-1.189V3c.993 0 1.714.19 2.16.571s.67 1.031.67 1.952v1.565c0 .861.349 1.292 1.046 1.292zm-9.967 4.142v-1.401c0-1.117-.351-1.816-1.053-2.099v-.027c.429-.175.71-.519.877-.995H3.049c-.173.247-.436.38-.805.38v1.258c.692 0 1.039.419 1.039 1.258v1.641c0 .934.226 1.584.677 1.948s1.174.547 2.167.547v-1.278c-.388 0-.666-.093-.838-.28-.17-.188-.256-.505-.256-.952z" id="iconBg"/><path class="icon-vs-action-blue" d="M8 4L5 7H3l2-2H1V3h4L3 1h2l3 3z" id="colorAction"/></svg>

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 8.38v2.258s-.992-.001-.997-.003c-.012.052-.003.14-.003.281v1.579c0 1.271-.37 2.185-1.054 2.746-.638.522-1.576.759-2.822.759H10v-3.247s1.014-.001 1.037-.003c.004-.046-.037-.117-.037-.214V11.08c0-.943.222-1.606.539-2.072C11.223 8.527 11 7.843 11 6.869V5.468c0-.087.102-.286.094-.325-.02-.002.063-.143.03-.143H10V2h1.124c1.251 0 2.193.265 2.832.81C14.633 3.387 15 4.3 15 5.522V7h.919L16 8.38zM9.414 4l-4-4H.586l2 2H0v4h2v.586L1.586 7H1v.586L.586 8H1v1.638L1.329 11H2v1.536c0 1.247.495 2.149 1.19 2.711.641.517 1.697.753 2.937.753H7v-3.247s-1.011-.001-1.033-.003c-.008-.053.033-.127.033-.228v-1.401c0-.962-.224-1.637-.542-2.111.256-.378.444-.89.511-1.564L9.414 4z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M15 8.38v1.258c-.697 0-1.046.426-1.046 1.278v1.579c0 .961-.223 1.625-.666 1.989-.445.364-1.166.547-2.164.547v-1.278c.383 0 .661-.092.834-.277s.26-.498.26-.94V11.08c0-1.089.349-1.771 1.046-2.044v-.027c-.697-.287-1.046-1-1.046-2.14V5.468c0-.793-.364-1.189-1.094-1.189V3c.993 0 1.714.19 2.16.571s.67 1.031.67 1.952v1.565c0 .861.349 1.292 1.046 1.292zm-9.967 4.142v-1.401c0-1.117-.351-1.816-1.053-2.099v-.027c.429-.175.71-.519.877-.995H3.049c-.173.247-.436.38-.805.38v1.258c.692 0 1.039.419 1.039 1.258v1.641c0 .934.226 1.584.677 1.948s1.174.547 2.167.547v-1.278c-.388 0-.666-.093-.838-.28-.17-.188-.256-.505-.256-.952z" id="iconBg"/><path class="icon-vs-action-blue" d="M8 4L5 7H3l2-2H1V3h4L3 1h2l3 3z" id="colorAction"/></svg>

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Просмотреть файл

@ -0,0 +1,97 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Button, makeStyles } from "@fluentui/react-components";
import { useContext } from "react";
import { QueryResultContext } from "./queryResultStateProvider";
import { useVscodeWebview } from "../../common/vscodeWebviewProvider";
import * as qr from "../../../sharedInterfaces/queryResult";
import * as l10n from "@vscode/l10n";
import {
saveAsCsvIcon,
saveAsExcelIcon,
saveAsJsonIcon,
} from "./queryResultUtils";
const useStyles = makeStyles({
commandBar: {
display: "flex",
flexDirection: "column" /* Align buttons vertically */,
},
buttonImg: {
display: "block",
height: "16px",
width: "16px",
},
});
const CommandBar = () => {
const context = useContext(QueryResultContext);
if (context === undefined) {
return undefined;
}
const queryResultState = context.state;
const webViewState = useVscodeWebview<
qr.QueryResultWebviewState,
qr.QueryResultReducers
>();
const classes = useStyles();
const saveResults = (buttonLabel: string) => {
webViewState.extensionRpc.call("saveResults", {
uri: queryResultState?.uri,
batchId: queryResultState?.resultSetSummary?.batchId,
resultId: queryResultState?.resultSetSummary?.id,
format: buttonLabel,
selection: queryResultState?.resultSetSummary?.rowCount, //TODO: do for only user selection
});
};
return (
<div className={classes.commandBar}>
<Button
onClick={(_event) => {
saveResults("csv");
}}
icon={
<img
className={classes.buttonImg}
src={saveAsCsvIcon(context.theme)}
/>
}
className="codicon saveCsv"
title={l10n.t("Save as CSV")}
/>
<Button
onClick={(_event) => {
saveResults("json");
}}
icon={
<img
className={classes.buttonImg}
src={saveAsJsonIcon(context.theme)}
/>
}
className="codicon saveJson"
title={l10n.t("Save as JSON")}
/>
<Button
onClick={(_event) => {
saveResults("excel");
}}
icon={
<img
className={classes.buttonImg}
src={saveAsExcelIcon(context.theme)}
/>
}
className="codicon saveExcel"
title={l10n.t("Save as Excel")}
/>
</div>
);
};
export default CommandBar;

Просмотреть файл

@ -28,6 +28,7 @@ import * as qr from "../../../sharedInterfaces/queryResult";
import { useVscodeWebview } from "../../common/vscodeWebviewProvider";
import SlickGrid, { SlickGridHandle } from "./slickgrid";
import * as l10n from "@vscode/l10n";
import CommandBar from "./commandBar";
const useStyles = makeStyles({
root: {
@ -58,6 +59,7 @@ const useStyles = makeStyles({
width: "100%",
height: "100%",
position: "relative",
display: "flex",
},
queryResultPaneOpenButton: {
position: "absolute",
@ -288,6 +290,7 @@ export const QueryResultPane = () => {
ref={gridRef}
resultSetSummary={metadata.resultSetSummary}
/>
<CommandBar />
</div>
)}
{metadata.tabStates!.resultPaneTab ===

Просмотреть файл

@ -6,10 +6,12 @@
import { ReactNode, createContext } from "react";
import * as qr from "../../../sharedInterfaces/queryResult";
import { useVscodeWebview } from "../../common/vscodeWebviewProvider";
import { Theme } from "@fluentui/react-components";
export interface QueryResultState {
provider: qr.QueryResultReactProvider;
state: qr.QueryResultWebviewState;
theme: Theme;
}
const QueryResultContext = createContext<QueryResultState | undefined>(
@ -41,6 +43,7 @@ const QueryResultStateProvider: React.FC<QueryResultContextProps> = ({
},
},
state: webViewState?.state as qr.QueryResultWebviewState,
theme: webViewState?.theme,
}}
>
{children}

Просмотреть файл

@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Theme, webLightTheme } from "@fluentui/react-components";
export const saveAsCsvIcon = (theme: Theme) => {
return theme === webLightTheme
? require("../../media/saveCsv.svg")
: require("../../media/saveCsv_inverse.svg");
};
export const saveAsJsonIcon = (theme: Theme) => {
return theme === webLightTheme
? require("../../media/saveJson.svg")
: require("../../media/saveJson_inverse.svg");
};
export const saveAsExcelIcon = (theme: Theme) => {
return theme === webLightTheme
? require("../../media/saveExcel.svg")
: require("../../media/saveExcel_inverse.svg");
};

Просмотреть файл

@ -26,6 +26,8 @@ function getDefaultOptions<T extends Slick.SlickData>(): Slick.GridOptions<T> {
} as Slick.GridOptions<T>;
}
const ACTIONBAR_WIDTH = 36; //px
export class Table<T extends Slick.SlickData> implements IThemable {
protected styleElement: HTMLStyleElement;
protected idPrefix: string;
@ -71,7 +73,7 @@ export class Table<T extends Slick.SlickData> implements IThemable {
// this._tableContainer.className = //TODO: class name for styles
let gridParent = document.getElementById("grid-parent");
if (gridParent) {
this._tableContainer.style.width = `${gridParent?.clientWidth.toString()}px`;
this._tableContainer.style.width = `${(gridParent?.clientWidth - ACTIONBAR_WIDTH).toString()}px`;
const height = gridParent?.clientHeight - 5;
this._tableContainer.style.height = `${height.toString()}px`;
}