@@ -8,6 +8,7 @@ import { cn } from '../../../lib/cn'
88import { isCardExportable } from '../../../lib/widgets/widgetRegistry'
99import { copyToClipboard } from '../../../lib/clipboard'
1010import { useDashboardContextOptional } from '../../../hooks/useDashboardContext'
11+ import { useModalState } from '../../../lib/modals'
1112
1213// Card width options (in grid columns out of 12)
1314const WIDTH_OPTIONS = [
@@ -86,9 +87,9 @@ export const CardActionMenu = memo(function CardActionMenu({
8687 const { t } = useTranslation ( [ 'cards' , 'common' ] )
8788 const studioContext = useDashboardContextOptional ( )
8889
89- const [ showMenu , setShowMenu ] = useState ( false )
90- const [ showResizeMenu , setShowResizeMenu ] = useState ( false )
91- const [ showHeightMenu , setShowHeightMenu ] = useState ( false )
90+ const { isOpen : showMenu , open : openMenu , close : closeMenu } = useModalState ( )
91+ const { isOpen : showResizeMenu , open : openResizeMenu , close : closeResizeMenu } = useModalState ( )
92+ const { isOpen : showHeightMenu , open : openHeightMenu , close : closeHeightMenu } = useModalState ( )
9293 const [ resizeMenuOnLeft , setResizeMenuOnLeft ] = useState ( false )
9394 const [ heightMenuOnLeft , setHeightMenuOnLeft ] = useState ( false )
9495 const [ menuPosition , setMenuPosition ] = useState < { top : number ; right : number } | null > ( null )
@@ -107,15 +108,15 @@ export const CardActionMenu = memo(function CardActionMenu({
107108 // Close resize/height submenus when main menu closes (#7869)
108109 useEffect ( ( ) => {
109110 if ( ! showMenu ) {
110- setShowResizeMenu ( false )
111- setShowHeightMenu ( false )
111+ closeResizeMenu ( )
112+ closeHeightMenu ( )
112113 setMenuPosition ( null )
113114 if ( restoreFocusRef . current ) {
114115 menuButtonRef . current ?. focus ( )
115116 restoreFocusRef . current = false
116117 }
117118 }
118- } , [ showMenu ] )
119+ } , [ showMenu , closeResizeMenu , closeHeightMenu ] )
119120
120121 useEffect ( ( ) => {
121122 if ( ! showMenu ) return
@@ -140,12 +141,12 @@ export const CardActionMenu = memo(function CardActionMenu({
140141 function handleOtherMenuOpen ( e : Event ) {
141142 const detail = ( e as CustomEvent ) . detail
142143 if ( detail !== cardId && showMenu ) {
143- setShowMenu ( false )
144+ closeMenu ( )
144145 }
145146 }
146147 window . addEventListener ( 'card-menu-open' , handleOtherMenuOpen )
147148 return ( ) => window . removeEventListener ( 'card-menu-open' , handleOtherMenuOpen )
148- } , [ showMenu , cardId ] )
149+ } , [ showMenu , cardId , closeMenu ] )
149150
150151 // Keep menu anchored to button on scroll/resize (#5253).
151152 useEffect ( ( ) => {
@@ -184,13 +185,13 @@ export const CardActionMenu = memo(function CardActionMenu({
184185 const handleClickOutside = ( e : MouseEvent ) => {
185186 const target = e . target as HTMLElement
186187 if ( ! target . closest ( '[data-tour="card-menu"]' ) && ! target . closest ( '[data-card-action-menu]' ) ) {
187- setShowMenu ( false )
188+ closeMenu ( )
188189 }
189190 }
190191
191192 document . addEventListener ( 'mousedown' , handleClickOutside )
192193 return ( ) => document . removeEventListener ( 'mousedown' , handleClickOutside )
193- } , [ showMenu ] )
194+ } , [ showMenu , closeMenu ] )
194195
195196 // Flip resize submenu to left when near viewport edge
196197 useEffect ( ( ) => {
@@ -212,15 +213,15 @@ export const CardActionMenu = memo(function CardActionMenu({
212213 if ( e . key === 'Escape' ) {
213214 e . preventDefault ( )
214215 restoreFocusRef . current = true
215- setShowResizeMenu ( false )
216- setShowHeightMenu ( false )
217- setShowMenu ( false )
216+ closeResizeMenu ( )
217+ closeHeightMenu ( )
218+ closeMenu ( )
218219 return
219220 }
220221 if ( e . key === 'ArrowLeft' && ( showResizeMenu || showHeightMenu ) ) {
221222 e . preventDefault ( )
222- setShowResizeMenu ( false )
223- setShowHeightMenu ( false )
223+ closeResizeMenu ( )
224+ closeHeightMenu ( )
224225 menuRef . current ?. querySelector < HTMLElement > ( MENU_ITEM_SELECTOR ) ?. focus ( )
225226 return
226227 }
@@ -251,8 +252,10 @@ export const CardActionMenu = memo(function CardActionMenu({
251252 const opening = ! showMenu
252253 if ( opening ) {
253254 window . dispatchEvent ( new CustomEvent ( 'card-menu-open' , { detail : cardId } ) )
255+ openMenu ( )
256+ } else {
257+ closeMenu ( )
254258 }
255- setShowMenu ( opening )
256259 } }
257260 className = "p-1.5 rounded-lg hover:bg-secondary/50 text-muted-foreground hover:text-foreground transition-colors"
258261 aria-label = { t ( 'cardWrapper.cardMenuTooltip' ) }
@@ -275,7 +278,7 @@ export const CardActionMenu = memo(function CardActionMenu({
275278 onKeyDown = { handleMenuKeyDown }
276279 >
277280 < button
278- onClick = { ( ) => { setShowMenu ( false ) ; onConfigure ?.( ) } }
281+ onClick = { ( ) => { closeMenu ( ) ; onConfigure ?.( ) } }
279282 className = "w-full px-4 py-2 text-left text-sm text-muted-foreground hover:text-foreground hover:bg-secondary/50 flex items-center gap-2"
280283 role = "menuitem"
281284 title = { t ( 'cardWrapper.configureTooltip' ) }
@@ -285,7 +288,7 @@ export const CardActionMenu = memo(function CardActionMenu({
285288 </ button >
286289 < button
287290 onClick = { ( ) => {
288- setShowMenu ( false )
291+ closeMenu ( )
289292 const url = `${ window . location . origin } ${ window . location . pathname } ?card=${ cardType } `
290293 copyToClipboard ( url )
291294 } }
@@ -301,7 +304,7 @@ export const CardActionMenu = memo(function CardActionMenu({
301304 { onWidthChange && (
302305 < div className = "relative" ref = { menuContainerRef } >
303306 < button
304- onClick = { ( ) => { setShowResizeMenu ( ! showResizeMenu ) ; setShowHeightMenu ( false ) } }
307+ onClick = { ( ) => { if ( showResizeMenu ) { closeResizeMenu ( ) } else { openResizeMenu ( ) } closeHeightMenu ( ) } }
305308 className = "w-full px-4 py-2 text-left text-sm text-muted-foreground hover:text-foreground hover:bg-secondary/50 flex flex-wrap items-center justify-between gap-y-2"
306309 role = "menuitem"
307310 aria-haspopup = "menu"
@@ -327,7 +330,7 @@ export const CardActionMenu = memo(function CardActionMenu({
327330 { WIDTH_OPTIONS . map ( ( option ) => (
328331 < button
329332 key = { option . value }
330- onClick = { ( ) => { onWidthChange ( option . value ) ; setShowResizeMenu ( false ) ; setShowMenu ( false ) } }
333+ onClick = { ( ) => { onWidthChange ( option . value ) ; closeResizeMenu ( ) ; closeMenu ( ) } }
331334 className = { cn (
332335 'w-full px-3 py-2 text-left text-sm flex flex-wrap items-center justify-between gap-y-2' ,
333336 cardWidth === option . value
@@ -349,7 +352,7 @@ export const CardActionMenu = memo(function CardActionMenu({
349352 { onHeightChange && (
350353 < div className = "relative" ref = { heightMenuContainerRef } >
351354 < button
352- onClick = { ( ) => { setShowHeightMenu ( ! showHeightMenu ) ; setShowResizeMenu ( false ) } }
355+ onClick = { ( ) => { if ( showHeightMenu ) { closeHeightMenu ( ) } else { openHeightMenu ( ) } closeResizeMenu ( ) } }
353356 className = "w-full px-4 py-2 text-left text-sm text-muted-foreground hover:text-foreground hover:bg-secondary/50 flex flex-wrap items-center justify-between gap-y-2"
354357 role = "menuitem"
355358 aria-haspopup = "menu"
@@ -375,7 +378,7 @@ export const CardActionMenu = memo(function CardActionMenu({
375378 { HEIGHT_OPTIONS . map ( ( option ) => (
376379 < button
377380 key = { option . value }
378- onClick = { ( ) => { onHeightChange ( option . value ) ; setShowHeightMenu ( false ) ; setShowMenu ( false ) } }
381+ onClick = { ( ) => { onHeightChange ( option . value ) ; closeHeightMenu ( ) ; closeMenu ( ) } }
379382 className = { cn (
380383 'w-full px-3 py-2 text-left text-sm flex flex-wrap items-center justify-between gap-y-2' ,
381384 cardHeight === option . value
@@ -396,7 +399,7 @@ export const CardActionMenu = memo(function CardActionMenu({
396399 { isCardExportable ( cardType ) && (
397400 < button
398401 onClick = { ( ) => {
399- setShowMenu ( false )
402+ closeMenu ( )
400403 if ( studioContext ?. openAddCardModal ) {
401404 studioContext . openAddCardModal ( 'widgets' , cardType )
402405 } else {
@@ -413,7 +416,7 @@ export const CardActionMenu = memo(function CardActionMenu({
413416 ) }
414417
415418 < button
416- onClick = { ( ) => { setShowMenu ( false ) ; onRemove ?.( ) } }
419+ onClick = { ( ) => { closeMenu ( ) ; onRemove ?.( ) } }
417420 className = "w-full px-4 py-2 text-left text-sm text-red-400 hover:bg-red-500/10 flex items-center gap-2"
418421 role = "menuitem"
419422 title = { t ( 'cardWrapper.removeTooltip' ) }
0 commit comments