Merge pull request #633 from paulkaplan/turbo-mode

Implement turbo mode basic indicator
This commit is contained in:
Paul Kaplan authored and GitHub committed 2017-08-18 12:04:10 -04:00
commit e78ef1cae3
17 files changed
+264 -75

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
.controls-container {
display: flex;
}
+53
View File
@@ -0,0 +1,53 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import GreenFlag from '../green-flag/green-flag.jsx';
import StopAll from '../stop-all/stop-all.jsx';
import TurboMode from '../turbo-mode/turbo-mode.jsx';
import styles from './controls.css';
const Controls = function (props) {
const {
active,
className,
onGreenFlagClick,
onStopAllClick,
turbo,
...componentProps
} = props;
return (
<div
className={classNames(styles.controlsContainer, className)}
{...componentProps}
>
<GreenFlag
active={active}
onClick={onGreenFlagClick}
/>
<StopAll
active={active}
onClick={onStopAllClick}
/>
{turbo ? (
<TurboMode />
) : null}
</div>
);
};
Controls.propTypes = {
active: PropTypes.bool,
className: PropTypes.string,
onGreenFlagClick: PropTypes.func.isRequired,
onStopAllClick: PropTypes.func.isRequired,
turbo: PropTypes.bool
};
Controls.defaultProps = {
active: false,
turbo: false
};
export default Controls;
+14 -6
View File
@@ -1,13 +1,21 @@
.green-flag {
width: 1.1rem;
height: 1.1rem;
opacity: 0.5;
box-sizing: content-box;
width: 1.25rem;
height: 1.25rem;
padding: 0.375rem;
border-radius: 0.25rem;
user-select: none;
cursor: pointer;
transition: opacity 0.2s ease-out; /* @todo: standardize with var */
transition: 0.2s ease-out;
}
.green-flag.is-active,
.green-flag:hover {
opacity: 1;
/* Scale flag image by 1.2, but keep background static */
width: 1.5rem;
height: 1.5rem;
padding: 0.25rem;
}
.green-flag.is-active {
background-color: rgba(0, 0, 0, 0.1);
}
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 13.8 14.5" style="enable-background:new 0 0 13.8 14.5;" xml:space="preserve">
<style type="text/css">
.st0{fill:#55BA58;stroke:#469945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st1{fill:#55BA58;stroke:#469945;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
</style>
<g>
<path class="st0" d="M0.8,1.8c1.7-1.4,4.5-1.4,6.2,0l0,0c1.7,1.4,4.5,1.4,6.2,0v8.4c-1.7,1.4-4.5,1.4-6.2,0l0,0
c-1.7-1.4-4.5-1.4-6.2,0"/>
<line class="st1" x1="0.8" y1="13.8" x2="0.8" y2="0.8"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 816 B

+1 -5
View File
@@ -101,10 +101,6 @@
position: relative;
}
.green-flag {
margin: 0.25rem 0.6rem;
}
.stage-and-target-wrapper {
/*
Makes rows for children:
@@ -130,7 +126,7 @@
display: flex;
flex-grow: 1;
flex-basis: 0;
padding-top: $space;
padding-left: $space;
padding-right: $space;
+2 -7
View File
@@ -8,11 +8,10 @@ import VM from 'scratch-vm';
import Blocks from '../../containers/blocks.jsx';
import CostumeTab from '../../containers/costume-tab.jsx';
import GreenFlag from '../../containers/green-flag.jsx';
import Controls from '../../containers/controls.jsx';
import TargetPane from '../../containers/target-pane.jsx';
import SoundTab from '../../containers/sound-tab.jsx';
import Stage from '../../containers/stage.jsx';
import StopAll from '../../containers/stop-all.jsx';
import Box from '../box/box.jsx';
import MenuBar from '../menu-bar/menu-bar.jsx';
@@ -90,11 +89,7 @@ const GUIComponent = props => {
<Box className={styles.stageAndTargetWrapper}>
<Box className={styles.stageMenuWrapper}>
<GreenFlag
className={styles.greenFlag}
vm={vm}
/>
<StopAll vm={vm} />
<Controls vm={vm} />
</Box>
<Box className={styles.stageWrapper}>
<MediaQuery minWidth={layout.fullSizeMinWidth}>{isFullSize => (
+14 -5
View File
@@ -1,13 +1,22 @@
.stop-all {
width: 1.1rem;
height: 1.1rem;
opacity: 0.5;
box-sizing: content-box;
width: 1.25rem;
height: 1.25rem;
padding: 0.375rem;
border-radius: 0.25rem;
user-select: none;
cursor: pointer;
transition: opacity 0.2s ease-out; /* @todo: standardize with var */
transition: 0.2s ease-out;
}
.stop-all {
opacity: 0.5;
}
.stop-all.is-active,
.stop-all:hover {
transform: scale(1.2);
}
.stop-all.is-active {
opacity: 1;
}
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>turbo-bolt-icon</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="turbo-bolt-icon" stroke="#FF8C1A" fill="#FF8C1A">
<path d="M6.1919265,9.97041267 C6.39817578,10.4827993 6.8451296,10.5515418 7.19456044,10.1185825 L7.4614448,9.78790167 C7.80893496,9.35734694 8.43422564,9.28545516 8.87346661,9.63974521 L11.68656,11.9087754 C12.118908,12.2575055 12.3030858,12.1265903 12.0962705,11.6122341 L9.74921575,5.77503453 C9.54314462,5.26252912 9.09584472,5.19401738 8.7458012,5.62738515 L8.4784489,5.9583779 C8.13034947,6.38933881 7.50456486,6.46155987 7.06532389,6.10726982 L4.25223048,3.83823967 C3.81988252,3.48950949 3.63584851,3.62032891 3.84284261,4.13456591 L6.1919265,9.97041267 Z" id="turbo-bolt" transform="translate(7.969395, 7.873508) rotate(69.000000) translate(-7.969395, -7.873508) "></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+20
View File
@@ -0,0 +1,20 @@
@import "../../css/colors.css";
.turbo-container {
display: flex;
align-items: center;
padding: 0.25rem;
user-select: none;
}
.turbo-icon {
margin: 0.25rem;
}
.turbo-label {
font-size: 0.625rem;
font-weight: bold;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: $control-primary;
white-space: nowrap;
}
+24
View File
@@ -0,0 +1,24 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';
import turboIcon from './icon--turbo.svg';
import styles from './turbo-mode.css';
const TurboMode = () => (
<div className={styles.turboContainer}>
<img
className={styles.turboIcon}
src={turboIcon}
/>
<div className={styles.turboLabel}>
<FormattedMessage
defaultMessage="Turbo Mode"
description="Label indicating turbo mode is active"
id="controls.turboMode"
/>
</div>
</div>
);
export default TurboMode;
+70
View File
@@ -0,0 +1,70 @@
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import VM from 'scratch-vm';
import ControlsComponent from '../components/controls/controls.jsx';
class Controls extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleGreenFlagClick',
'handleStopAllClick',
'onProjectRunStart',
'onProjectRunStop'
]);
this.state = {
projectRunning: false,
turbo: false
};
}
componentDidMount () {
this.props.vm.addListener('PROJECT_RUN_START', this.onProjectRunStart);
this.props.vm.addListener('PROJECT_RUN_STOP', this.onProjectRunStop);
}
componentWillUnmount () {
this.props.vm.removeListener('PROJECT_RUN_START', this.onProjectRunStart);
this.props.vm.removeListener('PROJECT_RUN_STOP', this.onProjectRunStop);
}
onProjectRunStart () {
this.setState({projectRunning: true});
}
onProjectRunStop () {
this.setState({projectRunning: false});
}
handleGreenFlagClick (e) {
e.preventDefault();
if (e.shiftKey) {
this.setState({turbo: !this.state.turbo});
this.props.vm.setTurboMode(!this.state.turbo);
} else {
this.props.vm.greenFlag();
}
}
handleStopAllClick (e) {
e.preventDefault();
this.props.vm.stopAll();
}
render () {
const {
vm, // eslint-disable-line no-unused-vars
...props
} = this.props;
return (
<ControlsComponent
{...props}
active={this.state.projectRunning}
turbo={this.state.turbo}
onGreenFlagClick={this.handleGreenFlagClick}
onStopAllClick={this.handleStopAllClick}
/>
);
}
}
Controls.propTypes = {
vm: PropTypes.instanceOf(VM)
};
export default Controls;
+2 -14
View File
@@ -11,24 +11,18 @@ class GreenFlag extends React.Component {
super(props);
bindAll(this, [
'handleClick',
'handleKeyDown',
'handleKeyUp',
'onProjectRunStart',
'onProjectRunStop'
]);
this.state = {projectRunning: false, shiftKeyDown: false};
this.state = {projectRunning: false};
}
componentDidMount () {
this.props.vm.addListener('PROJECT_RUN_START', this.onProjectRunStart);
this.props.vm.addListener('PROJECT_RUN_STOP', this.onProjectRunStop);
document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp);
}
componentWillUnmount () {
this.props.vm.removeListener('PROJECT_RUN_START', this.onProjectRunStart);
this.props.vm.removeListener('PROJECT_RUN_STOP', this.onProjectRunStop);
document.removeEventListener('keydown', this.handleKeyDown);
document.removeEventListener('keyup', this.handleKeyUp);
}
onProjectRunStart () {
this.setState({projectRunning: true});
@@ -36,15 +30,9 @@ class GreenFlag extends React.Component {
onProjectRunStop () {
this.setState({projectRunning: false});
}
handleKeyDown (e) {
this.setState({shiftKeyDown: e.shiftKey});
}
handleKeyUp (e) {
this.setState({shiftKeyDown: e.shiftKey});
}
handleClick (e) {
e.preventDefault();
if (this.state.shiftKeyDown) {
if (e.shiftKey) {
this.props.vm.setTurboMode(!this.props.vm.runtime.turboMode);
} else {
this.props.vm.greenFlag();
+2
View File
@@ -14,4 +14,6 @@ $red-tertiary: #E64D00;
$sound-primary: #CF63CF;
$sound-tertiary: #A63FA6;
$control-primary: #FFAB19;
$form-border: #E9EEF2;
+1 -8
View File
@@ -1,11 +1,4 @@
.green-flag {
position: absolute;
z-index: 2;
top: 10px;
right: 50px;
}
.stop-all {
.controls {
position: absolute;
z-index: 2;
top: 10px;
+3 -6
View File
@@ -3,8 +3,7 @@ import ReactDOM from 'react-dom';
import {connect} from 'react-redux';
import AppStateHOC from '../lib/app-state-hoc.jsx';
import GreenFlag from '../containers/green-flag.jsx';
import StopAll from '../containers/stop-all.jsx';
import Controls from '../containers/controls.jsx';
import Blocks from '../containers/blocks.jsx';
import GUI from '../containers/gui.jsx';
import ProjectLoaderHOC from '../lib/project-loader-hoc.jsx';
@@ -14,8 +13,7 @@ import styles from './blocks-only.css';
const mapStateToProps = state => ({vm: state.vm});
const VMBlocks = connect(mapStateToProps)(Blocks);
const VMGreenFlag = connect(mapStateToProps)(GreenFlag);
const VMStopAll = connect(mapStateToProps)(StopAll);
const VMControls = connect(mapStateToProps)(Controls);
const BlocksOnly = props => (
<GUI {...props}>
@@ -25,8 +23,7 @@ const BlocksOnly = props => (
media: `static/blocks-media/`
}}
/>
<VMGreenFlag className={styles.greenFlag} />
<VMStopAll className={styles.stopAll} />
<VMControls className={styles.controls} />
</GUI>
);
+3 -10
View File
@@ -3,8 +3,7 @@ import ReactDOM from 'react-dom';
import {connect} from 'react-redux';
import AppStateHOC from '../lib/app-state-hoc.jsx';
import GreenFlag from '../containers/green-flag.jsx';
import StopAll from '../containers/stop-all.jsx';
import Controls from '../containers/controls.jsx';
import Stage from '../containers/stage.jsx';
import Box from '../components/box/box.jsx';
import GUI from '../containers/gui.jsx';
@@ -15,8 +14,7 @@ import './player.css';
const mapStateToProps = state => ({vm: state.vm});
const VMStage = connect(mapStateToProps)(Stage);
const VMGreenFlag = connect(mapStateToProps)(GreenFlag);
const VMStopAll = connect(mapStateToProps)(StopAll);
const VMControls = connect(mapStateToProps)(Controls);
class Player extends React.Component {
constructor (props) {
@@ -55,17 +53,12 @@ class Player extends React.Component {
width={width}
>
<Box height={40}>
<VMGreenFlag
<VMControls
style={{
marginRight: 10,
height: 40
}}
/>
<VMStopAll
style={{
height: 40
}}
/>
</Box>
<VMStage
height={height}
+40
View File
@@ -0,0 +1,40 @@
import React from 'react';
import {shallowWithIntl} from '../../helpers/intl-helpers.jsx';
import Controls from '../../../src/components/controls/controls';
import TurboMode from '../../../src/components/turbo-mode/turbo-mode';
describe('Controls component', () => {
const defaultProps = () => ({
active: false,
greenFlagTitle: 'Go',
onGreenFlagClick: jest.fn(),
onStopAllClick: jest.fn(),
stopAllTitle: 'Stop',
turbo: false
});
test('shows turbo mode when in turbo mode', () => {
const component = shallowWithIntl(
<Controls
{...defaultProps()}
/>
);
expect(component.find(TurboMode).exists()).toEqual(false);
component.setProps({turbo: true});
expect(component.find(TurboMode).exists()).toEqual(true);
});
test('triggers the right callbacks when clicked', () => {
const props = defaultProps();
const component = shallowWithIntl(
<Controls
{...props}
/>
);
component.find('[title="Go"]').simulate('click');
expect(props.onGreenFlagClick).toHaveBeenCalled();
component.find('[title="Stop"]').simulate('click');
expect(props.onStopAllClick).toHaveBeenCalled();
});
});