Skip to content

Commit 2383529

Browse files
aasimkhan30Aasim KhanCopilot
authored
Adding a thin wrapper on SlickgridReact to keep styling consistent. (#21976)
* Move options to common folder. * feat: add VS Code-style grid menu styles for SlickGrid integration * feat: implement Fluent theme styles for SlickGrid integration and update imports * refactor: update SlickGrid CSS variables for improved menu styling and organization * feat: add utility functions for calculating profiler column widths and corresponding tests * style: enhance SlickGrid button styles and alignment for improved UI consistency * feat: implement FluentSlickGrid component and integrate into Profiler and TableDataGrid pages * docs: update AGENTS.md with guidelines for using FluentSlickGrid wrapper in webviews * feat: enhance FluentSlickGrid options and refine Profiler and TableDataGrid configurations * feat: enhance FluentSlickGrid functionality with copy menu and tab navigation support * refactor: simplify FluentSlickGrid component by removing unused props and keyboard navigation logic * fix: update slickgrid-react and related dependencies to version 10.5.1; enhance Profiler grid options and copy functionality Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Aasim Khan <aasimkhan@gmail.com> Co-authored-by: Copilot <copilot@github.com>
1 parent 8164973 commit 2383529

11 files changed

Lines changed: 353 additions & 150 deletions

File tree

extensions/mssql/package-lock.json

Lines changed: 23 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/mssql/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
"sinon": "^21.0.3",
130130
"sinon-chai": "^4.0.1",
131131
"slickgrid": "github:Microsoft/SlickGrid.ADS#v2.3.51",
132-
"slickgrid-react": "10.5.0",
132+
"slickgrid-react": "10.5.1",
133133
"source-map-support": "^0.5.21",
134134
"ts-node": "^10.9.2",
135135
"typescript": "^5.8.3",

extensions/mssql/src/webviews/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
9. Avoid using `setTimeout(..., 0)` to sync stuff. Usually `setTimeout` calls are delayed for a minimum of 1 second during webview initialization and a series of them could stall the webview rendering. Use requestAnimationFrame or queueMicrotask if possible.
1212
10. Avoid using gray colors on dark theme. It doesn't have good enough contrast for visibility (aim for at least WCAG 2.1 AA 4.5:1 contrast ratio for text). Strongly prefer readability and contrast over style.
1313
11. Use targeted imports instead of wildcard (`*`) imports to avoid increasing the bundle size. For example, prefer `import { Button } from "@fluentui/react-components"` over `import * as Fluent from "@fluentui/react-components"`.
14+
12. For React SlickGrid usage in webviews, do not use `SlickgridReact` directly. Use the shared `FluentSlickGrid` wrapper in `src/webviews/common/FluentSlickGrid/FluentSlickGrid.tsx` so base options and shared styling stay centralized.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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 React, { useMemo } from "react";
7+
import { GridOption, SlickgridReact } from "slickgrid-react";
8+
import "@slickgrid-universal/common/dist/styles/css/slickgrid-theme-fluent.css";
9+
import "./fluentSlickGrid.css";
10+
11+
export {
12+
createFluentSlickGridCopyMenu,
13+
FLUENT_SLICK_GRID_COPY_COMMAND,
14+
getFluentSlickGridSelectionText,
15+
} from "./fluentSlickGridCopy";
16+
17+
type FluentAutoResizeOptions = NonNullable<GridOption["autoResize"]>;
18+
type FluentAutoResizeOverrides = Partial<
19+
Omit<FluentAutoResizeOptions, "container" | "calculateAvailableSizeBy" | "resizeDetection">
20+
>;
21+
22+
const baseFluentGridOption: GridOption = {
23+
alwaysShowVerticalScroll: true,
24+
contextMenu: {
25+
iconCollapseAllGroupsCommand: "fi fi-arrow-minimize",
26+
iconExpandAllGroupsCommand: "fi fi-arrow-maximize",
27+
iconClearGroupingCommand: "fi fi-dismiss",
28+
iconCopyCellValueCommand: "fi fi-copy",
29+
iconExportCsvCommand: "fi fi-arrow-download",
30+
iconExportExcelCommand: "fi fi-arrow-download",
31+
iconExportPdfCommand: "fi fi-arrow-download",
32+
iconExportTextDelimitedCommand: "fi fi-arrow-download",
33+
subItemChevronClass: "fi fi-chevron-right",
34+
},
35+
gridMenu: {
36+
iconCssClass: "fi fi-navigation",
37+
iconClearAllFiltersCommand: "fi fi-filter-dismiss",
38+
iconClearAllSortingCommand: "fi fi-arrow-sort",
39+
iconClearFrozenColumnsCommand: "fi fi-pin-off",
40+
iconExportCsvCommand: "fi fi-arrow-download",
41+
iconExportExcelCommand: "fi fi-arrow-download",
42+
iconExportPdfCommand: "fi fi-arrow-download",
43+
iconExportTextDelimitedCommand: "fi fi-arrow-download",
44+
iconRefreshDatasetCommand: "fi fi-arrow-sync",
45+
iconToggleDarkModeCommand: "fi fi-dark-theme",
46+
iconToggleFilterCommand: "fi fi-split-horizontal",
47+
iconTogglePreHeaderCommand: "fi fi-split-horizontal",
48+
subItemChevronClass: "fi fi-chevron-right",
49+
},
50+
headerMenu: {
51+
iconClearFilterCommand: "fi fi-filter-dismiss",
52+
iconClearSortCommand: "fi fi-arrow-sort",
53+
iconFilterShortcutSubMenu: "fi fi-filter",
54+
iconFreezeColumns: "fi fi-pin",
55+
iconUnfreezeColumns: "fi fi-pin-off",
56+
iconSortAscCommand: "fi fi-sort-arrow-up",
57+
iconSortDescCommand: "fi fi-sort-arrow-down",
58+
iconColumnHideCommand: "fi fi-dismiss",
59+
iconColumnResizeByContentCommand: "fi fi-arrow-bidirection",
60+
subItemChevronClass: "fi fi-chevron-right",
61+
},
62+
enableAutoResize: true,
63+
enableCellNavigation: true,
64+
enableColumnReorder: true,
65+
enableExcelCopyBuffer: true,
66+
enableTextSelectionOnCells: false,
67+
forceFitColumns: false,
68+
};
69+
70+
export const baseFluentReadOnlyGridOption: GridOption = {
71+
autoFitColumnsOnFirstLoad: false,
72+
enableSorting: false,
73+
enableFiltering: false,
74+
enablePagination: false,
75+
enableColumnPicker: false,
76+
enableGridMenu: false,
77+
enableHeaderMenu: false,
78+
enableAutoTooltip: true,
79+
showHeaderRow: false,
80+
rowHeight: 25,
81+
};
82+
83+
export function createFluentAutoResizeOptions(
84+
container: string,
85+
overrides: FluentAutoResizeOverrides = {},
86+
): FluentAutoResizeOptions {
87+
return {
88+
container,
89+
calculateAvailableSizeBy: "container",
90+
resizeDetection: "container",
91+
...overrides,
92+
};
93+
}
94+
95+
type SlickgridReactPublicProps = React.JSX.LibraryManagedAttributes<
96+
typeof SlickgridReact,
97+
React.ComponentProps<typeof SlickgridReact>
98+
>;
99+
100+
export interface FluentSlickGridProps extends Omit<SlickgridReactPublicProps, "options"> {
101+
options: GridOption;
102+
}
103+
104+
export const FluentSlickGrid: React.FC<FluentSlickGridProps> = ({ options, ...props }) => {
105+
const mergedOptions = useMemo<GridOption>(
106+
() => ({
107+
...baseFluentGridOption,
108+
...options,
109+
contextMenu: {
110+
...baseFluentGridOption.contextMenu,
111+
...options.contextMenu,
112+
},
113+
gridMenu: {
114+
...baseFluentGridOption.gridMenu,
115+
...options.gridMenu,
116+
},
117+
headerMenu: {
118+
...baseFluentGridOption.headerMenu,
119+
...options.headerMenu,
120+
},
121+
}),
122+
[options],
123+
);
124+
125+
return <SlickgridReact {...props} options={mergedOptions} />;
126+
};

extensions/mssql/src/webviews/common/FluentSlickGrid/fluentGridOptions.ts

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 { Column, ContextMenu, SlickgridReactInstance } from "slickgrid-react";
7+
8+
export const FLUENT_SLICK_GRID_COPY_COMMAND = "copy";
9+
10+
interface FluentSlickGridRange {
11+
fromRow: number;
12+
toRow: number;
13+
fromCell: number;
14+
toCell: number;
15+
}
16+
17+
export function createFluentSlickGridCopyMenu(copyLabel: string): ContextMenu {
18+
return {
19+
hideCommands: [FLUENT_SLICK_GRID_COPY_COMMAND],
20+
hideCloseButton: true,
21+
commandItems: [
22+
{
23+
command: FLUENT_SLICK_GRID_COPY_COMMAND,
24+
title: copyLabel,
25+
iconCssClass: "fi fi-copy",
26+
positionOrder: 1,
27+
},
28+
],
29+
};
30+
}
31+
32+
export function getFluentSlickGridSelectionText(
33+
reactGrid: SlickgridReactInstance | undefined,
34+
): string | undefined {
35+
const grid = reactGrid?.slickGrid;
36+
const dataView = reactGrid?.dataView;
37+
if (!grid || !dataView) {
38+
return undefined;
39+
}
40+
41+
const visibleColumns = grid.getVisibleColumns() as Column[];
42+
const selectionModel = grid.getSelectionModel();
43+
const selectedRanges = (selectionModel?.getSelectedRanges() || []) as FluentSlickGridRange[];
44+
45+
const rangesToProcess =
46+
selectedRanges.length > 0
47+
? selectedRanges.map((range) => ({
48+
fromRow: Math.min(range.fromRow, range.toRow),
49+
toRow: Math.max(range.fromRow, range.toRow),
50+
fromCell: Math.min(range.fromCell, range.toCell),
51+
toCell: Math.max(range.fromCell, range.toCell),
52+
}))
53+
: (() => {
54+
const activeCell = grid.getActiveCell();
55+
return activeCell
56+
? [
57+
{
58+
fromRow: activeCell.row,
59+
toRow: activeCell.row,
60+
fromCell: activeCell.cell,
61+
toCell: activeCell.cell,
62+
},
63+
]
64+
: [];
65+
})();
66+
67+
if (rangesToProcess.length === 0) {
68+
return undefined;
69+
}
70+
71+
const lines: string[] = [];
72+
for (const range of rangesToProcess) {
73+
for (let row = range.fromRow; row <= range.toRow; row++) {
74+
const item = dataView.getItem(row);
75+
if (!item) {
76+
continue;
77+
}
78+
79+
lines.push(
80+
visibleColumns
81+
.slice(range.fromCell, range.toCell + 1)
82+
.map((column) => {
83+
if (!column.field) {
84+
return "";
85+
}
86+
return item[column.field]?.toString() || "";
87+
})
88+
.join("\t"),
89+
);
90+
}
91+
}
92+
93+
return lines.join("\n");
94+
}

0 commit comments

Comments
 (0)