33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { Button , makeStyles , Toolbar , Tooltip } from "@fluentui/react-components" ;
7- import { useContext } from "react" ;
6+ import {
7+ makeStyles ,
8+ Menu ,
9+ MenuItem ,
10+ MenuList ,
11+ MenuPopover ,
12+ MenuTrigger ,
13+ Overflow ,
14+ OverflowItem ,
15+ Toolbar ,
16+ ToolbarButton ,
17+ ToolbarButtonProps ,
18+ useIsOverflowItemVisible ,
19+ useOverflowMenu ,
20+ } from "@fluentui/react-components" ;
21+ import { ReactElement , useContext } from "react" ;
822import { QueryResultCommandsContext } from "./queryResultStateProvider" ;
923import { useVscodeWebview2 } from "../../common/vscodeWebviewProvider2" ;
1024import { useQueryResultSelector } from "./queryResultSelector" ;
@@ -21,19 +35,39 @@ import {
2135 ArrowMaximize16Filled ,
2236 ArrowMinimize16Filled ,
2337 DocumentTextRegular ,
38+ MoreVertical20Filled ,
2439 TableRegular ,
2540} from "@fluentui/react-icons" ;
2641import { WebviewAction } from "../../../sharedInterfaces/webview" ;
42+ import { ACTIONBAR_WIDTH_PX } from "./table/table" ;
2743
2844const useStyles = makeStyles ( {
45+ commandBarContainer : {
46+ width : `${ ACTIONBAR_WIDTH_PX } px` ,
47+ flexShrink : 0 ,
48+ overflow : "hidden" ,
49+ display : "flex" ,
50+ paddingRight : "10px" ,
51+ } ,
2952 commandBar : {
30- width : "16px " ,
53+ width : "100% " ,
3154 } ,
3255 buttonImg : {
3356 display : "block" ,
3457 height : "16px" ,
3558 width : "16px" ,
3659 } ,
60+ toolbarButton : {
61+ width : "32px" ,
62+ height : "32px" ,
63+ minWidth : "32px" ,
64+ minHeight : "32px" ,
65+ padding : "4px" ,
66+ display : "inline-flex" ,
67+ justifyContent : "center" ,
68+ alignItems : "center" ,
69+ flexShrink : 0 ,
70+ } ,
3771} ) ;
3872
3973export interface CommandBarProps {
@@ -44,6 +78,75 @@ export interface CommandBarProps {
4478 isMaximized ?: boolean ;
4579}
4680
81+ type ToolbarOverflowButtonProps = {
82+ overflowId : string ;
83+ overflowGroupId ?: string ;
84+ } & ToolbarButtonProps ;
85+
86+ type CommandBarAction = {
87+ id : string ;
88+ groupId ?: string ;
89+ icon : ReactElement ;
90+ menuIcon ?: ReactElement ;
91+ ariaLabel : string ;
92+ title : string ;
93+ menuLabel : string ;
94+ onClick : ( ) => void ;
95+ disabled ?: boolean ;
96+ className ?: string ;
97+ } ;
98+
99+ const ToolbarOverflowButton = ( {
100+ overflowId,
101+ overflowGroupId,
102+ className,
103+ ...props
104+ } : ToolbarOverflowButtonProps & { className ?: string } ) => {
105+ const classes = useStyles ( ) ;
106+ const mergedClassName = [ classes . toolbarButton , className ] . filter ( Boolean ) . join ( " " ) ;
107+ return (
108+ < OverflowItem id = { overflowId } groupId = { overflowGroupId } >
109+ < ToolbarButton appearance = "subtle" { ...props } className = { mergedClassName } />
110+ </ OverflowItem >
111+ ) ;
112+ } ;
113+
114+ const CommandBarOverflowMenuItem = ( { action } : { action : CommandBarAction } ) => {
115+ const isVisible = useIsOverflowItemVisible ( action . id ) ;
116+ if ( isVisible ) {
117+ return null ;
118+ }
119+ return < MenuItem onClick = { action . onClick } > { action . menuLabel } </ MenuItem > ;
120+ } ;
121+
122+ const CommandBarOverflowMenu = ( { actions } : { actions : CommandBarAction [ ] } ) => {
123+ const { ref, isOverflowing } = useOverflowMenu < HTMLButtonElement > ( ) ;
124+ if ( ! isOverflowing ) {
125+ return null ;
126+ }
127+
128+ return (
129+ < Menu >
130+ < MenuTrigger disableButtonEnhancement >
131+ < ToolbarButton
132+ ref = { ref }
133+ appearance = "subtle"
134+ icon = { < MoreVertical20Filled /> }
135+ aria-label = { locConstants . queryResult . moreQueryActions }
136+ title = { locConstants . queryResult . moreQueryActions }
137+ />
138+ </ MenuTrigger >
139+ < MenuPopover >
140+ < MenuList >
141+ { actions . map ( ( action ) => (
142+ < CommandBarOverflowMenuItem key = { action . id } action = { action } />
143+ ) ) }
144+ </ MenuList >
145+ </ MenuPopover >
146+ </ Menu >
147+ ) ;
148+ } ;
149+
47150const CommandBar = ( props : CommandBarProps ) => {
48151 const classes = useStyles ( ) ;
49152 const { themeKind } = useVscodeWebview2 < qr . QueryResultWebviewState , qr . QueryResultReducers > ( ) ;
@@ -115,114 +218,104 @@ const CommandBar = (props: CommandBarProps) => {
115218 const saveAsExcelTooltip = locConstants . queryResult . saveAsExcel ( saveAsExcelShortcut ?. label ) ;
116219 const saveAsInsertTooltip = locConstants . queryResult . saveAsInsert ( saveAsInsertShortcut ?. label ) ;
117220
118- if ( props . viewMode === qr . QueryResultViewMode . Text ) {
119- return (
120- < div className = { classes . commandBar } >
121- < Tooltip content = { toggleToGridViewTooltip } relationship = "label" >
122- < Button
123- appearance = "subtle"
124- onClick = { toggleViewMode }
125- icon = { < TableRegular /> }
126- title = { toggleToGridViewTooltip }
127- />
128- </ Tooltip >
129- </ div >
221+ const isGridView = props . viewMode === qr . QueryResultViewMode . Grid ;
222+ const hasAdditionalResults = hasMultipleResults ( ) ;
223+ const toggleToGrid = props . viewMode === qr . QueryResultViewMode . Text ;
224+
225+ const actions : CommandBarAction [ ] = [
226+ {
227+ id : "toggleViewMode" ,
228+ groupId : "viewMode" ,
229+ icon : toggleToGrid ? < TableRegular /> : < DocumentTextRegular /> ,
230+ ariaLabel : toggleToGrid ? toggleToGridViewTooltip : toggleToTextViewTooltip ,
231+ title : toggleToGrid ? toggleToGridViewTooltip : toggleToTextViewTooltip ,
232+ menuLabel : toggleToGrid ? toggleToGridViewTooltip : toggleToTextViewTooltip ,
233+ onClick : toggleViewMode ,
234+ } ,
235+ ] ;
236+
237+ if ( isGridView && hasAdditionalResults ) {
238+ actions . push ( {
239+ id : "toggleMaximize" ,
240+ groupId : "viewMode" ,
241+ icon : props . isMaximized ? (
242+ < ArrowMinimize16Filled className = { classes . buttonImg } />
243+ ) : (
244+ < ArrowMaximize16Filled className = { classes . buttonImg } />
245+ ) ,
246+ ariaLabel : isMaximized ? restoreTooltip : maximizeTooltip ,
247+ title : isMaximized ? restoreTooltip : maximizeTooltip ,
248+ menuLabel : isMaximized ? restoreTooltip : maximizeTooltip ,
249+ onClick : ( ) => props . onToggleMaximize ?.( ) ,
250+ } ) ;
251+ }
252+
253+ if ( isGridView ) {
254+ actions . push (
255+ {
256+ id : "saveAsCsv" ,
257+ groupId : "export" ,
258+ icon : < img className = { classes . buttonImg } src = { saveAsCsvIcon ( themeKind ) } /> ,
259+ menuLabel : saveAsCsvTooltip ,
260+ ariaLabel : saveAsCsvTooltip ,
261+ title : saveAsCsvTooltip ,
262+ onClick : ( ) => saveResults ( "csv" ) ,
263+ className : "codicon saveCsv" ,
264+ } ,
265+ {
266+ id : "saveAsJson" ,
267+ groupId : "export" ,
268+ icon : < img className = { classes . buttonImg } src = { saveAsJsonIcon ( themeKind ) } /> ,
269+ menuLabel : saveAsJsonTooltip ,
270+ ariaLabel : saveAsJsonTooltip ,
271+ title : saveAsJsonTooltip ,
272+ onClick : ( ) => saveResults ( "json" ) ,
273+ className : "codicon saveJson" ,
274+ } ,
275+ {
276+ id : "saveAsExcel" ,
277+ groupId : "export" ,
278+ icon : < img className = { classes . buttonImg } src = { saveAsExcelIcon ( themeKind ) } /> ,
279+ menuLabel : saveAsExcelTooltip ,
280+ ariaLabel : saveAsExcelTooltip ,
281+ title : saveAsExcelTooltip ,
282+ onClick : ( ) => saveResults ( "excel" ) ,
283+ className : "codicon saveExcel" ,
284+ } ,
285+ {
286+ id : "saveAsInsert" ,
287+ groupId : "export" ,
288+ icon : < img className = { classes . buttonImg } src = { saveAsInsertIcon ( themeKind ) } /> ,
289+ menuLabel : saveAsInsertTooltip ,
290+ ariaLabel : saveAsInsertTooltip ,
291+ title : saveAsInsertTooltip ,
292+ onClick : ( ) => saveResults ( "insert" ) ,
293+ className : "codicon saveInsert" ,
294+ } ,
130295 ) ;
131296 }
132297
133298 return (
134- < Toolbar vertical className = { classes . commandBar } >
135- { /* View Mode Toggle */ }
136- < Tooltip
137- content = {
138- props . viewMode === qr . QueryResultViewMode . Grid
139- ? toggleToTextViewTooltip
140- : toggleToGridViewTooltip
141- }
142- relationship = "label" >
143- < Button
144- appearance = "subtle"
145- onClick = { toggleViewMode }
146- icon = {
147- props . viewMode === qr . QueryResultViewMode . Grid ? (
148- < DocumentTextRegular />
149- ) : (
150- < TableRegular />
151- )
152- }
153- title = {
154- props . viewMode === qr . QueryResultViewMode . Grid
155- ? toggleToTextViewTooltip
156- : toggleToGridViewTooltip
157- }
158- />
159- </ Tooltip >
160-
161- { hasMultipleResults ( ) && props . viewMode === qr . QueryResultViewMode . Grid && (
162- < Tooltip
163- content = { isMaximized ? restoreTooltip : maximizeTooltip }
164- relationship = "label" >
165- < Button
166- appearance = "subtle"
167- onClick = { ( ) => {
168- props . onToggleMaximize ?.( ) ;
169- } }
170- icon = {
171- isMaximized ? (
172- < ArrowMinimize16Filled className = { classes . buttonImg } />
173- ) : (
174- < ArrowMaximize16Filled className = { classes . buttonImg } />
175- )
176- }
177- title = { isMaximized ? restoreTooltip : maximizeTooltip } > </ Button >
178- </ Tooltip >
179- ) }
180-
181- < Tooltip content = { saveAsCsvTooltip } relationship = "label" >
182- < Button
183- appearance = "subtle"
184- onClick = { ( _event ) => {
185- saveResults ( "csv" ) ;
186- } }
187- icon = { < img className = { classes . buttonImg } src = { saveAsCsvIcon ( themeKind ) } /> }
188- className = "codicon saveCsv"
189- title = { saveAsCsvTooltip }
190- />
191- </ Tooltip >
192- < Tooltip content = { saveAsJsonTooltip } relationship = "label" >
193- < Button
194- appearance = "subtle"
195- onClick = { ( _event ) => {
196- saveResults ( "json" ) ;
197- } }
198- icon = { < img className = { classes . buttonImg } src = { saveAsJsonIcon ( themeKind ) } /> }
199- className = "codicon saveJson"
200- title = { saveAsJsonTooltip }
201- />
202- </ Tooltip >
203- < Tooltip content = { saveAsExcelTooltip } relationship = "label" >
204- < Button
205- appearance = "subtle"
206- onClick = { ( _event ) => {
207- saveResults ( "excel" ) ;
208- } }
209- icon = { < img className = { classes . buttonImg } src = { saveAsExcelIcon ( themeKind ) } /> }
210- className = "codicon saveExcel"
211- title = { saveAsExcelTooltip }
212- />
213- </ Tooltip >
214- < Tooltip content = { saveAsInsertTooltip } relationship = "label" >
215- < Button
216- appearance = "subtle"
217- onClick = { ( _event ) => {
218- saveResults ( "insert" ) ;
219- } }
220- icon = { < img className = { classes . buttonImg } src = { saveAsInsertIcon ( themeKind ) } /> }
221- className = "codicon saveInsert"
222- title = { saveAsInsertTooltip }
223- />
224- </ Tooltip >
225- </ Toolbar >
299+ < div className = { classes . commandBarContainer } >
300+ < Overflow overflowAxis = "vertical" overflowDirection = "end" >
301+ < Toolbar vertical className = { classes . commandBar } aria-label = "Query result commands" >
302+ { actions . map ( ( action ) => (
303+ < ToolbarOverflowButton
304+ key = { action . id }
305+ overflowId = { action . id }
306+ overflowGroupId = { action . groupId }
307+ icon = { action . icon }
308+ aria-label = { action . ariaLabel }
309+ title = { action . title }
310+ onClick = { action . onClick }
311+ disabled = { action . disabled }
312+ className = { action . className }
313+ />
314+ ) ) }
315+ < CommandBarOverflowMenu actions = { actions } />
316+ </ Toolbar >
317+ </ Overflow >
318+ </ div >
226319 ) ;
227320} ;
228321
0 commit comments