diff --git a/src/components/language-selector/language-selector.css b/src/components/language-selector/language-selector.css
index 5da79cf25..dd09a7c7e 100644
--- a/src/components/language-selector/language-selector.css
+++ b/src/components/language-selector/language-selector.css
@@ -2,10 +2,20 @@
@import "../../css/units.css";
.group {
- display: inline-flex;
- flex-direction: row; /* makes columns, for each label/form group */
- align-items: center;
- vertical-align: middle;
+ z-index: 50;
+ top: $menu-bar-height;
+ left: 0;
+ position: absolute;
+ border: 1px solid $ui-black-transparent;
+ border-radius: 0 0 8px 8px;
+ background-color: $motion-primary;
+ padding: 0;
+ margin: 0;
+ min-width: 186px;
+ max-width: 260px;
+ overflow: visible;
+ color: $ui-white;
+ box-shadow: 0 8px 8px 0 $ui-black-transparent;
}
.language-icon {
@@ -17,7 +27,8 @@
}
.language-select {
- width: 100%;
+ margin: .5rem;
+ width: calc(100% - 1rem);
height: 1.85rem;
border: 1px solid $motion-primary;
user-select: none;
diff --git a/src/components/language-selector/language-selector.jsx b/src/components/language-selector/language-selector.jsx
index befe47976..07249277f 100644
--- a/src/components/language-selector/language-selector.jsx
+++ b/src/components/language-selector/language-selector.jsx
@@ -1,56 +1,49 @@
-import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import Box from '../box/box.jsx';
import locales from 'scratch-l10n';
-import languageIcon from './language-icon.svg';
-import dropdownCaret from './dropdown-caret.svg';
import styles from './language-selector.css';
-const LanguageSelector = ({
- currentLocale,
- onChange,
- open,
- ...props
-}) => (
-
-
- {open ? (
-
+
+ );
+ }
+}
LanguageSelector.propTypes = {
+ componentRef: PropTypes.func,
currentLocale: PropTypes.string,
onChange: PropTypes.func,
open: PropTypes.bool
diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css
index 8063361d9..eba5f885e 100644
--- a/src/components/menu-bar/menu-bar.css
+++ b/src/components/menu-bar/menu-bar.css
@@ -44,6 +44,14 @@
vertical-align: middle;
}
+.language-icon {
+ height: 1.5rem;
+}
+
+.language-menu {
+ display: inline-flex;
+}
+
.menu {
z-index: $z-index-menu-bar;
top: $menu-bar-height;
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index 7742e210e..aa718b1b0 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -22,7 +22,10 @@ import {
fileMenuOpen,
openEditMenu,
closeEditMenu,
- editMenuOpen
+ editMenuOpen,
+ openLanguageMenu,
+ closeLanguageMenu,
+ languageMenuOpen
} from '../../reducers/menus';
import styles from './menu-bar.css';
@@ -32,6 +35,8 @@ import feedbackIcon from './icon--feedback.svg';
import profileIcon from './icon--profile.png';
import communityIcon from './icon--see-community.svg';
import dropdownCaret from '../language-selector/dropdown-caret.svg';
+import languageIcon from '../language-selector/language-icon.svg';
+
import scratchLogo from './scratch-logo.svg';
import helpIcon from './icon--help.svg';
@@ -39,22 +44,34 @@ import helpIcon from './icon--help.svg';
const MenuBarItemTooltip = ({
children,
className,
+ enable,
id,
place = 'bottom'
-}) => (
-
- {children}
-
-);
+}) => {
+ if (enable) {
+ return (
+
+ {children}
+
+ );
+ }
+ return (
+
+ {children}
+
+ );
+};
+
MenuBarItemTooltip.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
+ enable: PropTypes.bool,
id: PropTypes.string,
place: PropTypes.oneOf(['top', 'bottom', 'left', 'right'])
};
@@ -111,12 +128,31 @@ const MenuBar = props => (
src={scratchLogo}
/>
-
+
({
fileMenuOpen: fileMenuOpen(state),
- editMenuOpen: editMenuOpen(state)
+ editMenuOpen: editMenuOpen(state),
+ languageMenuOpen: languageMenuOpen(state)
});
const mapDispatchToProps = dispatch => ({
@@ -388,6 +428,8 @@ const mapDispatchToProps = dispatch => ({
onRequestCloseFile: () => dispatch(closeFileMenu()),
onClickEdit: () => dispatch(openEditMenu()),
onRequestCloseEdit: () => dispatch(closeEditMenu()),
+ onClickLanguage: () => dispatch(openLanguageMenu()),
+ onRequestCloseLanguage: () => dispatch(closeLanguageMenu()),
onSeeCommunity: () => dispatch(setPlayer(true))
});
diff --git a/src/containers/language-selector.jsx b/src/containers/language-selector.jsx
index 316b1577a..1a1d7bf0d 100644
--- a/src/containers/language-selector.jsx
+++ b/src/containers/language-selector.jsx
@@ -1,18 +1,85 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
import {connect} from 'react-redux';
+import {updateIntl} from 'react-intl-redux';
+import {closeLanguageMenu} from '../reducers/menus';
import LanguageSelectorComponent from '../components/language-selector/language-selector.jsx';
+class LanguageSelector extends React.Component {
+ constructor (props) {
+ super(props);
+ bindAll(this, [
+ 'addListeners',
+ 'removeListeners',
+ 'handleChange',
+ 'handleClick',
+ 'ref'
+ ]);
+ if (props.open) this.addListeners();
+ }
+ componentDidUpdate (prevProps) {
+ if (this.props.open && !prevProps.open) this.addListeners();
+ if (!this.props.open && prevProps.open) this.removeListeners();
+ }
+ addListeners () {
+ document.addEventListener('mouseup', this.handleClick);
+ }
+ removeListeners () {
+ document.removeEventListener('mouseup', this.handleClick);
+ }
+ handleChange (e) {
+ this.props.onChangeLanguage(e.target.value);
+ }
+ handleClick (e) {
+ if (this.props.open && this.selector && !this.selector.contains(e.target)) {
+ this.props.onRequestClose();
+ }
+ }
+ ref (c) {
+ this.selector = c;
+ }
+ render () {
+ const {
+ open,
+ onChangeLanguage, // eslint-disable-line no-unused-vars
+ onRequestClose, // eslint-disable-line no-unused-vars
+ children,
+ ...props
+ } = this.props;
+ if (!open) return null;
+ return (
+
+ {children}
+
+ );
+ }
+}
+
+LanguageSelector.propTypes = {
+ children: PropTypes.node,
+ onChangeLanguage: PropTypes.func.isRequired,
+ onRequestClose: PropTypes.func.isRequired,
+ open: PropTypes.bool.isRequired
+};
+
const mapStateToProps = state => ({
currentLocale: state.intl.locale
});
-const mapDispatchToProps = () => ({
- onChange: e => {
- e.preventDefault();
+const mapDispatchToProps = dispatch => ({
+ onChangeLanguage: locale => {
+ dispatch(updateIntl({locale: locale, messages: {}}));
+ dispatch(closeLanguageMenu());
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
-)(LanguageSelectorComponent);
+)(LanguageSelector);
diff --git a/src/reducers/menus.js b/src/reducers/menus.js
index 9d9ab0be9..6693160c1 100644
--- a/src/reducers/menus.js
+++ b/src/reducers/menus.js
@@ -3,11 +3,13 @@ const CLOSE_MENU = 'scratch-gui/menus/CLOSE_MENU';
const MENU_FILE = 'fileMenu';
const MENU_EDIT = 'editMenu';
+const MENU_LANGUAGE = 'languageMenu';
const initialState = {
[MENU_FILE]: false,
- [MENU_EDIT]: false
+ [MENU_EDIT]: false,
+ [MENU_LANGUAGE]: false
};
const reducer = function (state, action) {
@@ -39,6 +41,9 @@ const fileMenuOpen = state => state.scratchGui.menus[MENU_FILE];
const openEditMenu = () => openMenu(MENU_EDIT);
const closeEditMenu = () => closeMenu(MENU_EDIT);
const editMenuOpen = state => state.scratchGui.menus[MENU_EDIT];
+const openLanguageMenu = () => openMenu(MENU_LANGUAGE);
+const closeLanguageMenu = () => closeMenu(MENU_LANGUAGE);
+const languageMenuOpen = state => state.scratchGui.menus[MENU_LANGUAGE];
export {
reducer as default,
@@ -47,6 +52,9 @@ export {
closeFileMenu,
openEditMenu,
closeEditMenu,
+ openLanguageMenu,
+ closeLanguageMenu,
fileMenuOpen,
- editMenuOpen
+ editMenuOpen,
+ languageMenuOpen
};