@@ -43,33 +43,29 @@ export const BlockStylesControl = props => {
4343 const blockAttributesFilter = getBlockStyleAttributesFilter ( blockName )
4444 const defaultBlockAttributes = useMemo ( ( ) => getFilteredAttributes ( blockType . attributes , blockAttributesFilter ) , [ ] )
4545
46+ const [ userCanManageOptions , setUserCanManageOptions ] = useState ( false )
4647 const [ openProNotice , setOpenProNotice ] = useState ( false )
4748 const [ openSaveModal , setOpenSaveModal ] = useState ( false )
4849 const [ openPopover , setOpenPopover ] = useState ( false )
50+ const [ focusedIndex , setFocusedIndex ] = useState ( 0 )
4951
5052 const prevBlockStyleRef = useRef ( null )
5153 const buttonRef = useRef ( null )
52- const panelBodyRef = useRef ( null )
54+ const blockStylesListRef = useRef ( null )
55+ const blockStyleButtonsRef = useRef ( [ ] )
5356
54- const [ userCanManageOptions , setUserCanManageOptions ] = useState ( false )
5557 const id = useSelect ( select => select ( 'core' ) . getCurrentUser ( ) ?. id , [ ] )
5658
57- // Check if the user has "manage options" capabilities and can manage block styles.
58- useEffect ( ( ) => {
59- const checkCapabilities = async ( ) => {
60- const capabilities = await currentUserHasCapability ( 'manage_options' )
61- setUserCanManageOptions ( capabilities )
62- }
63-
64- checkCapabilities ( )
65- } , [ id ] )
59+ const attributes = useBlockAttributesContext ( )
60+ const setAttributes = useBlockSetAttributesContext ( )
6661
67- // Reset openProNotice when the popover is closed
68- useEffect ( ( ) => {
69- if ( ! openPopover ) {
70- setOpenProNotice ( false )
71- }
72- } , [ openPopover ] )
62+ const {
63+ blockStyle,
64+ modifiedBlockStyle : isModified ,
65+ uniqueId : _uniqueId ,
66+ generatedCss : _generatedCss ,
67+ ...otherAttributes
68+ } = attributes
7369
7470 const mainClasses = classnames ( [
7571 'components-panel__body' ,
@@ -82,17 +78,75 @@ export const BlockStylesControl = props => {
8278 'ugb-pro-control-button--hidden' : ! openProNotice ,
8379 } )
8480
85- const attributes = useBlockAttributesContext ( )
81+ // Handle keyboard navigation for block style buttons
82+ const handleKeyDown = event => {
83+ // eslint-disable-next-line @wordpress/no-global-active-element
84+ if ( ! blockStyleButtonsRef . current . includes ( document . activeElement ) ) {
85+ return
86+ }
8687
87- const {
88- blockStyle,
89- modifiedBlockStyle : isModified ,
90- uniqueId : _uniqueId ,
91- generatedCss : _generatedCss ,
92- ...otherAttributes
93- } = attributes
88+ if ( event . key === 'ArrowDown' ) {
89+ event . preventDefault ( )
90+ setFocusedIndex ( prevIndex => ( prevIndex + 1 ) % blockStyleButtonsRef . current . length )
91+ } else if ( event . key === 'ArrowUp' ) {
92+ event . preventDefault ( )
93+ setFocusedIndex ( prevIndex => ( prevIndex - 1 + blockStyleButtonsRef . current . length ) % blockStyleButtonsRef . current . length )
94+ }
95+ }
9496
95- const setAttributes = useBlockSetAttributesContext ( )
97+ // Update focused block style
98+ useEffect ( ( ) => {
99+ if ( blockStyleButtonsRef . current [ focusedIndex ] ) {
100+ blockStyleButtonsRef . current [ focusedIndex ] . focus ( )
101+ }
102+ } , [ focusedIndex ] )
103+
104+ useEffect ( ( ) => {
105+ // Reset openProNotice, focusedIndex, and blockStyleButtonsRef when the popover is closed
106+ if ( ! openPopover ) {
107+ setOpenProNotice ( false )
108+ setFocusedIndex ( - 1 )
109+ blockStyleButtonsRef . current = [ ]
110+
111+ return
112+ }
113+
114+ const list = blockStylesListRef . current
115+
116+ if ( ! list ) {
117+ return
118+ }
119+
120+ if ( ! blockStyleButtonsRef . current . length ) {
121+ blockStyleButtonsRef . current = Array . from ( list . querySelectorAll ( 'button' ) )
122+ }
123+
124+ // Focus on the selected block style button when the popover is opened.
125+ const selected = list . querySelector ( '.ugb-block-styles-controls__selected' )
126+
127+ if ( selected && blockStyleButtonsRef . current . length ) {
128+ const index = blockStyleButtonsRef . current . indexOf ( selected )
129+ setFocusedIndex ( index )
130+ }
131+
132+ list . addEventListener ( 'keydown' , handleKeyDown )
133+
134+ return ( ) => {
135+ if ( list ) {
136+ list . removeEventListener ( 'keydown' , handleKeyDown )
137+ }
138+ }
139+ } , [ openPopover ] )
140+
141+ // Check if the user has "manage options" capabilities and can manage block styles.
142+ useEffect ( ( ) => {
143+ const checkCapabilities = async ( ) => {
144+ const capabilities = await currentUserHasCapability ( 'manage_options' )
145+ setUserCanManageOptions ( capabilities )
146+ }
147+
148+ checkCapabilities ( )
149+ } , [ id ] )
96150
97151 useEffect ( ( ) => {
98152 if ( prevBlockStyleRef . current === null ) {
@@ -137,6 +191,8 @@ export const BlockStylesControl = props => {
137191 } , [ blockStyle ] )
138192
139193 const onSelectDefaultBlockStyle = ( ) => {
194+ setFocusedIndex ( 0 )
195+
140196 // Do nothing if block style is already "Default"
141197 if ( ! blockStyle ) {
142198 return
@@ -145,7 +201,9 @@ export const BlockStylesControl = props => {
145201 setAttributes ( { ...defaultBlockAttributes , modifiedBlockStyle : false } )
146202 }
147203
148- const onSelectBlockStyle = option => {
204+ const onSelectBlockStyle = ( option , index ) => {
205+ setFocusedIndex ( index )
206+
149207 if ( isPro ) {
150208 doAction ( 'stackable.global-settings.global-block-styles.select-block-style' ,
151209 option , blockStyle , globalBlockStyles , defaultBlockAttributes , setAttributes )
@@ -176,13 +234,7 @@ export const BlockStylesControl = props => {
176234 size = "small"
177235 icon = "edit"
178236 iconSize = { 12 }
179- onMouseDown = { ( ) => {
180- setOpenPopover ( isOpen => ! isOpen )
181- // Focus on the selected block style button when the popover is opened.
182- setTimeout ( ( ) => {
183- panelBodyRef . current ?. querySelector ( '.ugb-block-styles-controls__selected' ) ?. focus ( )
184- } , 50 )
185- } }
237+ onMouseDown = { ( ) => setOpenPopover ( isOpen => ! isOpen ) }
186238 ref = { buttonRef }
187239 >
188240 { `${ __ ( 'Block Style' , i18n ) } :` } < wbr /> { blockStyleLabel } { isModified && inBlockStyleOptions ? < span className = "stk-panel-modified-indicator stk--visible" > </ span > : '' }
@@ -210,22 +262,22 @@ export const BlockStylesControl = props => {
210262 { openPopover && (
211263 < Popover
212264 className = "ugb-button-icon-control__popover ugb-block-styles-controls__popover"
213- focusOnMount = { false }
265+ anchor = { buttonRef . current }
214266 onEscape = { ( ) => setOpenPopover ( false ) }
215267 onClose = { ( ) => setOpenPopover ( false ) }
216- anchor = { buttonRef . current }
217- offset = { 8 }
268+ focusOnMount = { false }
218269 placement = "left-start"
219270 resize = { false }
271+ offset = { 8 }
220272 >
221- < PanelBody ref = { panelBodyRef } >
273+ < PanelBody >
222274 < h2 className = "components-panel__body-title" > { __ ( 'Block Styles' , i18n ) } </ h2 >
223275 < p className = "components-panel__body-description" >
224276 { __ ( 'Save the styles of this block to reuse on others. You can also update a saved style, and the changes will apply wherever it\'s used.' , i18n ) }
225277
226278 < a href = "https://docs.wpstackable.com/article/737-how-to-use-block-styles" target = "_docs" rel = "noreferrer" > { __ ( 'Learn more' , i18n ) } </ a >
227279 </ p >
228- < ul className = "ugb-block-styles-controls__list" >
280+ < ul className = "ugb-block-styles-controls__list" ref = { blockStylesListRef } >
229281 < li >
230282 < Button
231283 onClick = { ( ) => onSelectDefaultBlockStyle ( ) }
@@ -239,7 +291,7 @@ export const BlockStylesControl = props => {
239291 { globalBlockStyles . map ( ( option , index ) => {
240292 return < li key = { index } >
241293 < Button
242- onClick = { ( ) => onSelectBlockStyle ( option . slug ) }
294+ onClick = { ( ) => onSelectBlockStyle ( option . slug , index + 1 ) }
243295 className = { blockStyle === option . slug ? 'ugb-block-styles-controls__selected' : '' }
244296 tabIndex = { 0 }
245297 >
@@ -254,6 +306,7 @@ export const BlockStylesControl = props => {
254306 </ ul >
255307 { userCanManageOptions && (
256308 < SaveUpdateButtons
309+ blockName = { blockName }
257310 blockStyle = { blockStyle }
258311 inOptions = { inBlockStyleOptions }
259312 isModified = { isModified }
@@ -280,26 +333,17 @@ export const BlockStylesControl = props => {
280333}
281334
282335const SaveUpdateButtons = props => {
283- const {
284- blockStyle, inOptions, isModified, setOpenSaveModal, onAddBlockStyle,
285- } = props
286-
287- const UpdateButton = applyFilters ( 'stackable.global-settings.global-block-styles.update-button' , Fragment )
336+ const { onAddBlockStyle, ...propsToPass } = props
337+ const ActionButtons = applyFilters ( 'stackable.global-settings.global-block-styles.action-buttons' , Fragment )
288338
289339 return ( < >
290340 < Flex style = { { marginTop : '24px' } } >
291- < FlexItem >
292- < UpdateButton
293- blockStyle = { blockStyle }
294- inOptions = { inOptions }
295- isModified = { isModified }
296- setOpenSaveModal = { setOpenSaveModal }
297- />
298- </ FlexItem >
299- < FlexItem >
341+ < ActionButtons { ...propsToPass } />
342+ < FlexItem style = { ! props . blockStyle || ! props . inOptions ? { marginLeft : 'auto' } : { } } >
300343 < Button
301344 variant = "primary"
302345 onClick = { ( ) => onAddBlockStyle ( ) }
346+ size = "small"
303347 >
304348 { __ ( 'Save as New Style' , i18n ) }
305349 { ! isPro && < span className = "stk-pulsating-circle" role = "presentation" /> }
0 commit comments