11import {
2+ Body ,
23 Button ,
34 css ,
45 Icon ,
89 Select ,
910 spacing ,
1011 Toggle ,
12+ Tooltip ,
1113} from '@mongodb-js/compass-components' ;
12- import React , { useState } from 'react' ;
14+ import React , { useEffect , useState } from 'react' ;
1315import { connect } from 'react-redux' ;
1416import type { RootState } from '../../modules' ;
1517import {
@@ -47,12 +49,27 @@ const fakeToggleLabelStyles = css({
4749 ) } ch`,
4850} ) ;
4951
52+ const menuStyles = css ( {
53+ width : '240px' ,
54+ } ) ;
55+
5056const menuItemStyles = css ( {
5157 '&:after' : {
5258 content : 'attr(data-hotkey)' ,
59+ whiteSpace : 'nowrap' ,
5360 } ,
5461} ) ;
5562
63+ const tooltipContentStyles = css ( {
64+ display : 'flex' ,
65+ alignItems : 'center' ,
66+ gap : spacing [ 3 ] ,
67+ } ) ;
68+
69+ const tooltipContentItemStyles = css ( {
70+ flexShrink : 0 ,
71+ } ) ;
72+
5673export const FocusModeModalHeader : React . FunctionComponent <
5774 FocusModeModalHeaderProps
5875> = ( {
@@ -64,8 +81,62 @@ export const FocusModeModalHeader: React.FunctionComponent<
6481 onStageDisabledToggleClick,
6582} ) => {
6683 const [ menuOpen , setMenuOpen ] = useState ( false ) ;
84+
85+ const keyEventListener = ( e : KeyboardEvent ) => {
86+ const isShiftKey = e . shiftKey ;
87+ const isCtrlOrMetaKey = e . ctrlKey || e . metaKey ;
88+ if ( ! isShiftKey || ! isCtrlOrMetaKey ) {
89+ return ;
90+ }
91+
92+ switch ( e . key ) {
93+ case '9' :
94+ return onPreviousStage ( ) ;
95+ case '0' :
96+ return onNextStage ( ) ;
97+ case 'a' :
98+ return onAddStageAfter ( ) ;
99+ case 'b' :
100+ return onAddStageBefore ( ) ;
101+ default :
102+ return ;
103+ }
104+ } ;
105+
106+ useEffect ( ( ) => {
107+ window . addEventListener ( 'keydown' , keyEventListener ) ;
108+ return ( ) => {
109+ window . removeEventListener ( 'keydown' , keyEventListener ) ;
110+ } ;
111+ } , [ keyEventListener ] ) ;
112+
67113 const isFirst = stageIndex === 0 ;
68114 const isLast = stages . length - 1 === stageIndex ;
115+
116+ const onPreviousStage = ( ) => {
117+ if ( isFirst ) {
118+ return ;
119+ }
120+ onStageSelect ( stageIndex - 1 ) ;
121+ } ;
122+
123+ const onNextStage = ( ) => {
124+ if ( isLast ) {
125+ return ;
126+ }
127+ onStageSelect ( stageIndex + 1 ) ;
128+ } ;
129+
130+ const onAddStageBefore = ( ) => {
131+ onAddStageClick ( stageIndex - 1 ) ;
132+ setMenuOpen ( false ) ;
133+ } ;
134+
135+ const onAddStageAfter = ( ) => {
136+ onAddStageClick ( stageIndex ) ;
137+ setMenuOpen ( false ) ;
138+ } ;
139+
69140 const stageSelectLabels = stages . map ( ( stageName , index ) => {
70141 return `Stage ${ index + 1 } : ${ stageName ?? 'select' } ` ;
71142 } ) ;
@@ -82,22 +153,34 @@ export const FocusModeModalHeader: React.FunctionComponent<
82153 return (
83154 < div className = { controlsContainerStyles } >
84155 < div className = { controlContainerStyles } >
85- < Button
86- size = "xsmall"
87- disabled = { isFirst }
88- onClick = { ( ) => {
89- onStageSelect ( stageIndex - 1 ) ;
90- } }
91- aria-label = "Edit previous stage"
156+ < Tooltip
157+ isDisabled = { isFirst }
158+ trigger = { ( { children, ...props } ) => (
159+ < span { ...props } >
160+ { children }
161+ < Button
162+ size = "xsmall"
163+ disabled = { isFirst }
164+ onClick = { onPreviousStage }
165+ aria-label = "Edit previous stage"
166+ >
167+ < Icon
168+ size = "xsmall"
169+ title = { null }
170+ role = "presentation"
171+ glyph = "ChevronLeft"
172+ > </ Icon >
173+ </ Button >
174+ </ span >
175+ ) }
92176 >
93- < Icon
94- size = "xsmall"
95- title = { null }
96- role = "presentation"
97- glyph = "ChevronLeft"
98- > </ Icon >
99- </ Button >
100-
177+ < Body className = { tooltipContentStyles } >
178+ < span className = { tooltipContentItemStyles } >
179+ Go to previous stage
180+ </ span >
181+ < span className = { tooltipContentItemStyles } > Ctrl + Shift + 9</ span >
182+ </ Body >
183+ </ Tooltip >
101184 { /* @ts -expect-error leafygreen unresonably expects a labelledby here */ }
102185 < Select
103186 allowDeselect = { false }
@@ -118,21 +201,32 @@ export const FocusModeModalHeader: React.FunctionComponent<
118201 } ) }
119202 </ Select >
120203
121- < Button
122- size = "xsmall"
123- disabled = { isLast }
124- onClick = { ( ) => {
125- onStageSelect ( stageIndex + 1 ) ;
126- } }
127- aria-label = "Edit next stage"
204+ < Tooltip
205+ isDisabled = { isLast }
206+ trigger = { ( { children, ...props } ) => (
207+ < span { ...props } >
208+ { children }
209+ < Button
210+ size = "xsmall"
211+ disabled = { isLast }
212+ onClick = { onNextStage }
213+ aria-label = "Edit next stage"
214+ >
215+ < Icon
216+ size = "xsmall"
217+ title = { null }
218+ role = "presentation"
219+ glyph = "ChevronRight"
220+ > </ Icon >
221+ </ Button >
222+ </ span >
223+ ) }
128224 >
129- < Icon
130- size = "xsmall"
131- title = { null }
132- role = "presentation"
133- glyph = "ChevronRight"
134- > </ Icon >
135- </ Button >
225+ < Body className = { tooltipContentStyles } >
226+ < span > Go to next stage</ span >
227+ < span > Ctrl + Shift + 0</ span >
228+ </ Body >
229+ </ Tooltip >
136230 </ div >
137231
138232 < div className = { controlContainerStyles } >
@@ -150,6 +244,7 @@ export const FocusModeModalHeader: React.FunctionComponent<
150244 </ div >
151245
152246 < Menu
247+ className = { menuStyles }
153248 open = { menuOpen }
154249 setOpen = { setMenuOpen }
155250 trigger = { ( { onClick, children } : any ) => {
@@ -182,21 +277,15 @@ export const FocusModeModalHeader: React.FunctionComponent<
182277 >
183278 < MenuItem
184279 className = { menuItemStyles }
185- data-hotkey = "A+"
186- onClick = { ( ) => {
187- onAddStageClick ( stageIndex ) ;
188- setMenuOpen ( false ) ;
189- } }
280+ onClick = { onAddStageAfter }
281+ data-hotkey = "Ctrl + Shift + A"
190282 >
191283 Add stage after
192284 </ MenuItem >
193285 < MenuItem
194286 className = { menuItemStyles }
195- data-hotkey = "B+"
196- onClick = { ( ) => {
197- onAddStageClick ( stageIndex - 1 ) ;
198- setMenuOpen ( false ) ;
199- } }
287+ onClick = { onAddStageBefore }
288+ data-hotkey = "Ctrl + Shift + B"
200289 >
201290 Add stage before
202291 </ MenuItem >
0 commit comments