@@ -18,6 +18,7 @@ import Utility from "global/components/utility";
1818import DisclosureNavigationMenu from "global/components/atomic/DisclosureNavigationMenu" ;
1919import { useTranslation } from "react-i18next" ;
2020import Authorize from "hoc/Authorize" ;
21+ import { FocusTrap } from "focus-trap-react" ;
2122
2223export default function Header ( props ) {
2324 const {
@@ -88,8 +89,9 @@ export default function Header(props) {
8889 return (
8990 < button
9091 onClick = { handleOptionsToggleClick }
91- aria-hidden
92- tabIndex = { - 1 }
92+ id = "options-menu-button"
93+ aria-controls = "options-menu"
94+ aria-expanded = { mobileOptionsExpanded }
9395 className = "reader-header__button reader-header__options-button"
9496 >
9597 < span >
@@ -151,6 +153,61 @@ export default function Header(props) {
151153 ) ;
152154 } ;
153155
156+ const renderOptionsNav = ( ) => {
157+ return (
158+ < ul
159+ aria-label = { t ( "reader.header.reader_settings_search" ) }
160+ className = "reader-header__nav-list"
161+ >
162+ < Authorize kind = { "any" } >
163+ < li className = "reader-header__nav-item" >
164+ < ControlMenu . Button
165+ onClick = { panelToggleHandler ( "notes" ) }
166+ icon = "notes24"
167+ label = { t ( "glossary.note_title_case_other" ) }
168+ active = { visibility . uiPanels . notes }
169+ />
170+ </ li >
171+ </ Authorize >
172+ < li className = "reader-header__nav-item" >
173+ < ControlMenu . Button
174+ onClick = { panelToggleHandler ( "visibility" ) }
175+ icon = "eyeball24"
176+ label = { t ( "common.visibility_title_case" ) }
177+ active = { visibility . uiPanels . visibility }
178+ />
179+ </ li >
180+ < li className = "reader-header__nav-item" >
181+ < ControlMenu . Button
182+ onClick = { panelToggleHandler ( "appearance" ) }
183+ icon = "text24"
184+ label = { t ( "reader.header.reader_appearance" ) }
185+ active = { visibility . uiPanels . appearance }
186+ />
187+ </ li >
188+ < li className = "reader-header__nav-item" >
189+ < SearchMenu . Button
190+ toggleSearchMenu = { panelToggleHandler ( "search" ) }
191+ active = { visibility . uiPanels . search }
192+ className = "reader-header__button reader-header__button--pad-narrow"
193+ iconSize = { 32 }
194+ />
195+ </ li >
196+ < li className = "reader-header__nav-item" >
197+ < DisclosureNavigationMenu
198+ visible = { visibility . uiPanels . user }
199+ disclosure = { < UserMenuButton /> }
200+ callbacks = { commonActions }
201+ onBlur = { commonActions . hideUserPanel }
202+ context = "reader"
203+ >
204+ < UserMenuBody />
205+ </ DisclosureNavigationMenu >
206+ </ li >
207+ </ ul >
208+ ) ;
209+ } ;
210+
154211 const innerClassName = classNames ( {
155212 "reader-header__inner" : true ,
156213 "reader-header__inner--shifted" : mobileOptionsExpanded
@@ -173,58 +230,27 @@ export default function Header(props) {
173230 showSection = { ! scrollAware . top }
174231 />
175232 ) }
233+ { /* Options menu */ }
176234 < div className = "reader-header__menu-group reader-header__menu-group--right" >
177- < ul
178- aria-label = { t ( "reader.header.reader_settings_search" ) }
179- className = "reader-header__nav-list"
180- >
181- < Authorize kind = { "any" } >
182- < li className = "reader-header__nav-item" >
183- < ControlMenu . Button
184- onClick = { panelToggleHandler ( "notes" ) }
185- icon = "notes24"
186- label = { t ( "glossary.note_title_case_other" ) }
187- active = { visibility . uiPanels . notes }
188- />
189- </ li >
190- </ Authorize >
191- < li className = "reader-header__nav-item" >
192- < ControlMenu . Button
193- onClick = { panelToggleHandler ( "visibility" ) }
194- icon = "eyeball24"
195- label = { t ( "common.visibility_title_case" ) }
196- active = { visibility . uiPanels . visibility }
197- />
198- </ li >
199- < li className = "reader-header__nav-item" >
200- < ControlMenu . Button
201- onClick = { panelToggleHandler ( "appearance" ) }
202- icon = "text24"
203- label = { t ( "reader.header.reader_appearance" ) }
204- active = { visibility . uiPanels . appearance }
205- />
206- </ li >
207- < li className = "reader-header__nav-item" >
208- < SearchMenu . Button
209- toggleSearchMenu = { panelToggleHandler ( "search" ) }
210- active = { visibility . uiPanels . search }
211- className = "reader-header__button reader-header__button--pad-narrow"
212- iconSize = { 32 }
213- />
214- </ li >
215- < li className = "reader-header__nav-item" >
216- < DisclosureNavigationMenu
217- visible = { visibility . uiPanels . user }
218- disclosure = { < UserMenuButton /> }
219- callbacks = { commonActions }
220- onBlur = { commonActions . hideUserPanel }
221- context = "reader"
222- >
223- < UserMenuBody />
224- </ DisclosureNavigationMenu >
225- </ li >
226- </ ul >
235+ { renderOptionsNav ( ) }
227236 </ div >
237+ { /* Options menu, mobile */ }
238+ < FocusTrap
239+ active = { mobileOptionsExpanded }
240+ focusTrapOptions = { {
241+ allowOutsideClick : true ,
242+ escapeDeactivates : handleOptionsToggleClick
243+ } }
244+ >
245+ < div
246+ className = "reader-header__menu-group reader-header__menu-group--dialog"
247+ id = "options-menu"
248+ aria-labelledby = "options-menu-button"
249+ { ...( mobileOptionsExpanded ? { } : { inert : "" } ) }
250+ >
251+ { renderOptionsNav ( ) }
252+ </ div >
253+ </ FocusTrap >
228254 </ nav >
229255 { ! ! text && (
230256 < >
0 commit comments