(
-
-

-
-
-
-

-
{
+ this.setFocusedRef(this.itemRefs[0]);
+ });
+ }
+ }
+
+ static contextType = MenuRefContext;
+
+ handleOnClose () {
+ this.context.removeByRef(this.settingsRef);
+ this.props.onRequestClose();
+ this.setState({focusedIndex: -1});
+ }
+
+ handleOnOpen () {
+ if (this.context.isOpenMenu(this.settingsRef)) return;
+
+ this.setState({focusedIndex: 0}, () => {
+ this.props.onRequestOpen();
+ this.context.addInner(this.settingsRef);
+ this.setFocusedRef(this.itemRefs[0]);
+ });
+ }
+
+ setFocusedRef (component) {
+ this.focusedRef = component;
+ if (this.focusedRef && this.focusedRef.current) {
+ this.focusedRef.current.focus();
+ }
+ }
+
+ handleKeyPress (e) {
+ if (e.key === 'Tab') {
+ this.handleOnClose();
+ }
+
+ if (this.context.isTopMenu(this.settingsRef)) {
+ this.handleKeyPressOpenMenu(e);
+ } else if (!this.context.isOpenMenu(this.settingsRef) && (e.key === ' ' || e.key === 'ArrowRight')) {
+ e.preventDefault();
+ this.handleOnOpen();
+ }
+ }
+
+ handleKeyPressOpenMenu (e) {
+ if (e.key === 'ArrowLeft' || e.key === 'Escape') {
+ e.preventDefault();
+ this.handleOnClose();
+ }
+
+ if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ this.handleMove(-1);
+ }
+
+ if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ this.handleMove(1);
+ }
+ }
+
+ handleMove (direction) {
+ const nextIndex =
+ (this.state.focusedIndex + direction + this.itemRefs.length) % this.itemRefs.length;
+ this.setState({focusedIndex: nextIndex}, () => {
+ this.setFocusedRef(this.itemRefs[nextIndex]);
+ });
+ }
+
+ render () {
+ const {
+ canChangeLanguage,
+ canChangeTheme,
+ isRtl,
+ onRequestClose,
+ settingsMenuOpen
+ } = this.props;
+
+ return (
-
- {canChangeLanguage && }
- {canChangeTheme && }
-
-
-
-);
+
+
+
+
+
+
+
+ {canChangeLanguage && }
+ {canChangeTheme && }
+
+
+ );
+ }
+};
SettingsMenu.propTypes = {
canChangeLanguage: PropTypes.bool,
diff --git a/packages/scratch-gui/src/components/menu-bar/theme-menu.jsx b/packages/scratch-gui/src/components/menu-bar/theme-menu.jsx
index e9ce24f1de..0cddd1d3ad 100644
--- a/packages/scratch-gui/src/components/menu-bar/theme-menu.jsx
+++ b/packages/scratch-gui/src/components/menu-bar/theme-menu.jsx
@@ -1,5 +1,6 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
+import bindAll from 'lodash.bindall';
import React from 'react';
import {FormattedMessage} from 'react-intl';
import {connect} from 'react-redux';
@@ -8,8 +9,9 @@ import check from './check.svg';
import {MenuItem, Submenu} from '../menu/menu.jsx';
import {DEFAULT_THEME, HIGH_CONTRAST_THEME, themeMap} from '../../lib/themes';
import {persistTheme} from '../../lib/themes/themePersistance';
-import {openThemeMenu, themeMenuOpen} from '../../reducers/menus.js';
+import {openThemeMenu, closeThemeMenu} from '../../reducers/menus.js';
import {setTheme} from '../../reducers/theme.js';
+import {MenuRefContext} from '../context-menu/menu-path-context.jsx';
import styles from './settings-menu.css';
@@ -19,7 +21,11 @@ const ThemeMenuItem = props => {
const themeInfo = themeMap[props.theme];
return (
-