diff --git a/package.json b/package.json
index 4628f12a0..de1e45edb 100644
--- a/package.json
+++ b/package.json
@@ -20,11 +20,11 @@
"url": "git+ssh://git@github.com/LLK/scratch-gui.git"
},
"peerDependencies": {
- "react": "^15",
+ "react": "^15.6.0",
"react-dom": "^15.6.0"
},
"devDependencies": {
- "autoprefixer": "7.1.1",
+ "autoprefixer": "7.1.2",
"babel-core": "^6.23.1",
"babel-eslint": "^7.1.1",
"babel-loader": "^7.0.0",
@@ -55,10 +55,11 @@
"react": "15.5.4",
"react-dom": "15.5.4",
"react-draggable": "2.2.6",
- "react-modal": "1.7.7",
+ "react-modal": "2.2.1",
+ "react-intl": "2.3.0",
"react-redux": "5.0.5",
"react-style-proptype": "3.0.0",
- "react-tabs": "1.0.0",
+ "react-tabs": "1.1.0",
"redux": "3.6.0",
"redux-throttle": "0.1.1",
"rimraf": "^2.6.1",
@@ -69,7 +70,8 @@
"scratch-vm": "latest",
"style-loader": "^0.18.0",
"svg-to-image": "1.1.3",
- "svg-url-loader": "2.0.2",
+ "svg-url-loader": "2.1.0",
+ "wav-encoder": "1.1.0",
"webpack": "^2.4.1",
"webpack-dev-server": "^2.4.1",
"xhr": "2.4.0"
diff --git a/src/components/asset-panel/asset-panel.css b/src/components/asset-panel/asset-panel.css
index 8ff5ca4cb..25d13588d 100644
--- a/src/components/asset-panel/asset-panel.css
+++ b/src/components/asset-panel/asset-panel.css
@@ -6,7 +6,7 @@
flex-grow: 1;
border: 1px solid $ui-pane-border;
border-top-right-radius: $space;
- background: $ui-pane-gray;
+ background: white;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 0.85rem;
}
diff --git a/src/components/asset-panel/asset-panel.jsx b/src/components/asset-panel/asset-panel.jsx
index 2d6ea710b..743256294 100644
--- a/src/components/asset-panel/asset-panel.jsx
+++ b/src/components/asset-panel/asset-panel.jsx
@@ -1,8 +1,8 @@
-const React = require('react');
+import React from 'react';
-const Box = require('../box/box.jsx');
-const Selector = require('./selector.jsx');
-const styles = require('./asset-panel.css');
+import Box from '../box/box.jsx';
+import Selector from './selector.jsx';
+import styles from './asset-panel.css';
const AssetPanel = props => (
@@ -11,7 +11,7 @@ const AssetPanel = props => (
{...props}
/>
- {/* @todo editor area */}
+ {props.children}
);
diff --git a/src/components/asset-panel/icon--add-costume-lib.svg b/src/components/asset-panel/icon--add-costume-lib.svg
new file mode 100644
index 000000000..cb364d3b0
--- /dev/null
+++ b/src/components/asset-panel/icon--add-costume-lib.svg
@@ -0,0 +1,30 @@
+
+
\ No newline at end of file
diff --git a/src/components/asset-panel/icon--add-sound-lib.svg b/src/components/asset-panel/icon--add-sound-lib.svg
new file mode 100644
index 000000000..1293940a9
--- /dev/null
+++ b/src/components/asset-panel/icon--add-sound-lib.svg
@@ -0,0 +1,26 @@
+
+
\ No newline at end of file
diff --git a/src/components/asset-panel/icon--add-sound-record.svg b/src/components/asset-panel/icon--add-sound-record.svg
new file mode 100644
index 000000000..b82e7a412
--- /dev/null
+++ b/src/components/asset-panel/icon--add-sound-record.svg
@@ -0,0 +1,25 @@
+
+
\ No newline at end of file
diff --git a/src/components/asset-panel/selector.css b/src/components/asset-panel/selector.css
index 3050409c1..2a4de1a25 100644
--- a/src/components/asset-panel/selector.css
+++ b/src/components/asset-panel/selector.css
@@ -1,27 +1,48 @@
-/* Need to use a fixed height for the new container to make list scrollable */
-$new-height: 60px;
+@import "../../css/colors.css";
.wrapper {
- width: 250px;
+ width: 150px;
position: relative;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ background: $ui-background-blue;
}
-.new-item {
- background: white;
- border-bottom: 1px solid #ddd;
- height: $new-height;
+.new-buttons {
display: flex;
+ flex-direction: column;
align-items: center;
- padding-left: 1.75rem;
+ justify-content: space-around;
+ margin: 1.25rem 0;
+ color: $motion-primary;
+}
+
+.new-button {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ font-size: 0.75rem;
cursor: pointer;
- font-size: 0.85rem;
+ transition: 0.2s;
+}
+
+.new-button:hover {
+ transform: scale(1.2);
+}
+
+.new-button + .new-button {
+ margin-top: 1.25rem;
+}
+
+.new-button-icon + .new-button-label {
+ margin-top: 0.5rem;
}
.list-area {
- position: absolute;
- width: 100%;
- top: 60px;
- height: calc(100% - $new-height);
+ /* Must have some height (recalculated by flex-grow) in order to scroll */
+ height: 0;
+ flex-grow: 1;
overflow-y: scroll;
}
@@ -30,9 +51,3 @@ $new-height: 60px;
min-height: 5rem;
margin: 1rem auto;
}
-
-.delete-button {
- position: absolute;
- top: 2px;
- right: 2px;
-}
diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx
index 0ddc2bb51..41cc41771 100644
--- a/src/components/asset-panel/selector.jsx
+++ b/src/components/asset-panel/selector.jsx
@@ -1,29 +1,23 @@
-const PropTypes = require('prop-types');
-const React = require('react');
+import PropTypes from 'prop-types';
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
-const SpriteSelectorItem = require('../../containers/sprite-selector-item.jsx');
+import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
-const Box = require('../box/box.jsx');
-const styles = require('./selector.css');
+import Box from '../box/box.jsx';
+import styles from './selector.css';
const Selector = props => {
const {
+ buttons,
items,
- newText,
selectedItemIndex,
onDeleteClick,
- onItemClick,
- onNewClick
+ onItemClick
} = props;
return (
-
- {newText}
-
{items.map((item, index) => (
{
/>
))}
+
+ {buttons.map(({message, img, onClick}, index) => (
+
+
+
+
+
+
+ ))}
+
);
};
Selector.propTypes = {
+ buttons: PropTypes.arrayOf(PropTypes.shape({
+ message: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string,
+ description: PropTypes.string
+ }),
+ img: PropTypes.string.isRequired,
+ onClick: PropTypes.func.isRequired
+ })),
items: PropTypes.arrayOf(PropTypes.shape({
url: PropTypes.string,
name: PropTypes.string.isRequired
})),
- newText: PropTypes.string.isRequired,
onDeleteClick: PropTypes.func.isRequired,
onItemClick: PropTypes.func.isRequired,
- onNewClick: PropTypes.func,
selectedItemIndex: PropTypes.number.isRequired
};
diff --git a/src/components/audio-trimmer/audio-trimmer.css b/src/components/audio-trimmer/audio-trimmer.css
new file mode 100644
index 000000000..454887ab3
--- /dev/null
+++ b/src/components/audio-trimmer/audio-trimmer.css
@@ -0,0 +1,103 @@
+@import "../../css/colors.css";
+
+$border-radius: 4px;
+$trim-handle-width: 12px;
+$trim-handle-height: 14px;
+$stripe-size: 10px;
+$hover-scale: 2;
+
+.absolute {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.trim-background {
+ cursor: pointer;
+}
+
+.trim-background-mask {
+ border: 1px solid $red-tertiary;
+ opacity: 0.5;
+
+ background: repeating-linear-gradient(
+ 45deg,
+ $red-primary,
+ $red-primary $stripe-size,
+ $red-tertiary $stripe-size,
+ $red-tertiary calc(2 * $stripe-size)
+ );
+}
+
+.start-trim-background .trim-background-mask {
+ border-top-left-radius: $border-radius;
+ border-bottom-left-radius: $border-radius;
+}
+
+.end-trim-background .trim-background-mask {
+ border-top-right-radius: $border-radius;
+ border-bottom-right-radius: $border-radius;
+}
+
+.trim-line {
+ position: absolute;
+ top: 0;
+ width: 0px;
+ height: 100%;
+ border: 1px solid $red-tertiary;
+}
+
+.playhead {
+ border: 1px solid $motion-primary;
+}
+
+.start-trim-line {
+ right: 0;
+}
+
+.end-trim-line {
+ left: 0;
+}
+
+.trim-handle {
+ position: absolute;
+ left: calc(-$trim-handle-width / 2);
+ width: $trim-handle-width;
+ height: $trim-handle-height;
+}
+
+.trim-handle img {
+ position: absolute;
+ width: $trim-handle-width;
+ height: $trim-handle-height;
+
+ /* Make sure image dragging isn't triggered */
+ user-select: none;
+ user-drag: none;
+ -webkit-user-drag: none; /* Autoprefixer doesn't seem to work for this */
+
+ transition: 0.2s;
+}
+
+.top-trim-handle {
+ top: -$trim-handle-height;
+}
+
+.bottom-trim-handle {
+ bottom: -$trim-handle-height;
+}
+
+.top-trim-handle img {
+ transform: rotate(180deg);
+}
+
+/* Increase handle size when anywhere on draggable area is hovered */
+.trim-background:hover img {
+ transform: scale($hover-scale);
+}
+
+.trim-background:hover .top-trim-handle img {
+ transform: rotate(180deg) scale($hover-scale);
+}
diff --git a/src/components/audio-trimmer/audio-trimmer.jsx b/src/components/audio-trimmer/audio-trimmer.jsx
new file mode 100644
index 000000000..840b240f5
--- /dev/null
+++ b/src/components/audio-trimmer/audio-trimmer.jsx
@@ -0,0 +1,70 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import classNames from 'classnames';
+import Box from '../box/box.jsx';
+import styles from './audio-trimmer.css';
+import handleIcon from './icon--handle.svg';
+
+const AudioTrimmer = props => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {props.playhead ? (
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+AudioTrimmer.propTypes = {
+ containerRef: PropTypes.func,
+ onTrimEndMouseDown: PropTypes.func.isRequired,
+ onTrimStartMouseDown: PropTypes.func.isRequired,
+ playhead: PropTypes.number,
+ trimEnd: PropTypes.number.isRequired,
+ trimStart: PropTypes.number.isRequired
+};
+
+module.exports = AudioTrimmer;
diff --git a/src/components/audio-trimmer/icon--handle.svg b/src/components/audio-trimmer/icon--handle.svg
new file mode 100644
index 000000000..a3f52078f
--- /dev/null
+++ b/src/components/audio-trimmer/icon--handle.svg
@@ -0,0 +1,18 @@
+
+
diff --git a/src/components/blocks/blocks.jsx b/src/components/blocks/blocks.jsx
index a50edba7c..8afcad5e9 100644
--- a/src/components/blocks/blocks.jsx
+++ b/src/components/blocks/blocks.jsx
@@ -1,7 +1,7 @@
-const PropTypes = require('prop-types');
-const React = require('react');
-const Box = require('../box/box.jsx');
-const styles = require('./blocks.css');
+import PropTypes from 'prop-types';
+import React from 'react';
+import Box from '../box/box.jsx';
+import styles from './blocks.css';
const BlocksComponent = props => {
const {
diff --git a/src/components/box/box.jsx b/src/components/box/box.jsx
index cabafd501..cd4a202b0 100644
--- a/src/components/box/box.jsx
+++ b/src/components/box/box.jsx
@@ -1,8 +1,8 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
-const stylePropType = require('react-style-proptype');
-const styles = require('./box.css');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+import stylePropType from 'react-style-proptype';
+import styles from './box.css';
const getRandomColor = (function () {
// In "DEBUG" mode this is used to output a random background color for each
@@ -75,40 +75,59 @@ const Box = props => {
}, children);
};
Box.propTypes = {
+ /** Defines how the browser distributes space between and around content items vertically within this box. */
alignContent: PropTypes.oneOf([
'flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch'
]),
+ /** Defines how the browser distributes space between and around flex items horizontally within this box. */
alignItems: PropTypes.oneOf([
'flex-start', 'flex-end', 'center', 'baseline', 'stretch'
]),
+ /** Specifies how this box should be aligned inside of its container (requires the container to be flexable). */
alignSelf: PropTypes.oneOf([
'auto', 'flex-start', 'flex-end', 'center', 'baseline', 'stretch'
]),
+ /** Specifies the initial length of this box */
basis: PropTypes.oneOfType([
PropTypes.number,
PropTypes.oneOf(['auto'])
]),
+ /** Specifies the the HTML nodes which will be child elements of this box. */
children: PropTypes.node,
+ /** Specifies the class name that will be set on this box */
className: PropTypes.string,
+ /**
+ * A callback function whose first parameter is the underlying dom elements.
+ * This call back will be executed immediately after the component is mounted or unmounted
+ */
componentRef: PropTypes.func,
+ /** https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction */
direction: PropTypes.oneOf([
'row', 'row-reverse', 'column', 'column-reverse'
]),
+ /** Specifies the type of HTML element of this box. Defaults to div. */
element: PropTypes.string,
+ /** Specifies the flex grow factor of a flex item. */
grow: PropTypes.number,
+ /** The height in pixels (if specified as a number) or a string if different units are required. */
height: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string
]),
+ /** https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content */
justifyContent: PropTypes.oneOf([
'flex-start', 'flex-end', 'center', 'space-between', 'space-around'
]),
+ /** Specifies the flex shrink factor of a flex item. */
shrink: PropTypes.number,
+ /** An object whose keys are css property names and whose values correspond the the css property. */
style: stylePropType,
+ /** The width in pixels (if specified as a number) or a string if different units are required. */
width: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string
]),
+ /** How whitespace should wrap within this block. */
wrap: PropTypes.oneOf([
'nowrap', 'wrap', 'wrap-reverse'
])
diff --git a/src/components/buffered-input/buffered-input.jsx b/src/components/buffered-input/buffered-input.jsx
new file mode 100644
index 000000000..b66d80eb9
--- /dev/null
+++ b/src/components/buffered-input/buffered-input.jsx
@@ -0,0 +1,52 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
+
+class BufferedInput extends React.Component {
+ constructor (props) {
+ super(props);
+ bindAll(this, [
+ 'handleChange',
+ 'handleKeyPress',
+ 'handleFlush'
+ ]);
+ this.state = {
+ value: null
+ };
+ }
+ handleKeyPress (e) {
+ if (e.key === 'Enter') {
+ this.handleFlush();
+ e.target.blur();
+ }
+ }
+ handleFlush () {
+ const isNumeric = typeof this.props.value === 'number';
+ const validatesNumeric = isNumeric ? !isNaN(this.state.value) : true;
+ if (this.state.value !== null && validatesNumeric) {
+ this.props.onSubmit(isNumeric ? Number(this.state.value) : this.state.value);
+ }
+ this.setState({value: null});
+ }
+ handleChange (e) {
+ this.setState({value: e.target.value});
+ }
+ render () {
+ const bufferedValue = this.state.value === null ? this.props.value : this.state.value;
+ return (
+
+ );
+ }
+}
+
+BufferedInput.propTypes = {
+ onSubmit: PropTypes.func.isRequired,
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
+};
+module.exports = BufferedInput;
diff --git a/src/components/button/button.jsx b/src/components/button/button.jsx
index dbc852395..59609cf76 100644
--- a/src/components/button/button.jsx
+++ b/src/components/button/button.jsx
@@ -1,8 +1,8 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
-const styles = require('./button.css');
+import styles from './button.css';
const ButtonComponent = ({
className,
diff --git a/src/components/close-button/close-button.css b/src/components/close-button/close-button.css
index 66b79d8a6..b679f8fb2 100644
--- a/src/components/close-button/close-button.css
+++ b/src/components/close-button/close-button.css
@@ -25,7 +25,7 @@
width: 1rem;
height: 1rem;
color: #FFF;
- background-color: $blue;
+ background-color: $motion-primary;
}
.large {
diff --git a/src/components/close-button/close-button.jsx b/src/components/close-button/close-button.jsx
index c2d78c796..688e991b6 100644
--- a/src/components/close-button/close-button.jsx
+++ b/src/components/close-button/close-button.jsx
@@ -1,9 +1,9 @@
-const PropTypes = require('prop-types');
-const React = require('react');
-const classNames = require('classnames');
+import PropTypes from 'prop-types';
+import React from 'react';
+import classNames from 'classnames';
-const styles = require('./close-button.css');
-const closeIcon = require('./icon--close.svg');
+import styles from './close-button.css';
+import closeIcon from './icon--close.svg';
const CloseButton = props => (
{
const {
diff --git a/src/components/green-flag/green-flag.jsx b/src/components/green-flag/green-flag.jsx
index 44998dbcc..0e9267389 100644
--- a/src/components/green-flag/green-flag.jsx
+++ b/src/components/green-flag/green-flag.jsx
@@ -1,9 +1,9 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
-const greenFlagIcon = require('./icon--green-flag.svg');
-const styles = require('./green-flag.css');
+import greenFlagIcon from './icon--green-flag.svg';
+import styles from './green-flag.css';
const GreenFlagComponent = function (props) {
const {
diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css
index 821f55014..de9c7f9ec 100644
--- a/src/components/gui/gui.css
+++ b/src/components/gui/gui.css
@@ -1,4 +1,5 @@
@import "../../css/units.css";
+@import "../../css/colors.css";
.page-wrapper {
height: 100%;
@@ -6,7 +7,7 @@
.body-wrapper {
height: calc(100% - $menu-bar-height);
- background-color: #e8edf1;
+ background-color: $ui-background-blue;
}
.flex-wrapper {
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 6d0bb59dc..3eafbb71c 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -1,22 +1,22 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
-const {Tab, Tabs, TabList, TabPanel} = require('react-tabs');
-const tabStyles = require('react-tabs/style/react-tabs.css');
-const VM = require('scratch-vm');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
+import tabStyles from 'react-tabs/style/react-tabs.css';
+import VM from 'scratch-vm';
-const Blocks = require('../../containers/blocks.jsx');
-const CostumeTab = require('../../containers/costume-tab.jsx');
-const GreenFlag = require('../../containers/green-flag.jsx');
-const TargetPane = require('../../containers/target-pane.jsx');
-const SoundTab = require('../../containers/sound-tab.jsx');
-const Stage = require('../../containers/stage.jsx');
-const StopAll = require('../../containers/stop-all.jsx');
+import Blocks from '../../containers/blocks.jsx';
+import CostumeTab from '../../containers/costume-tab.jsx';
+import GreenFlag from '../../containers/green-flag.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';
-const Box = require('../box/box.jsx');
-const MenuBar = require('../menu-bar/menu-bar.jsx');
+import Box from '../box/box.jsx';
+import MenuBar from '../menu-bar/menu-bar.jsx';
-const styles = require('./gui.css');
+import styles from './gui.css';
const GUIComponent = props => {
@@ -62,7 +62,7 @@ const GUIComponent = props => {
onSelect={onTabSelect}
>
- Scripts
+ Blocks
Costumes
Sounds
@@ -70,7 +70,7 @@ const GUIComponent = props => {
{
+ const {
+ level,
+ width,
+ height
+ } = props;
+
+ const nGreen = 11;
+ const nYellow = 5;
+ const nRed = 3;
+
+ const nBars = nGreen + nYellow + nRed;
+
+ const barSpacing = 2.5;
+ const barRounding = 3;
+ const barHeight = (height - barSpacing * (nBars + 1)) / nBars;
+
+ const nBarsToMask = nBars - Math.floor(level * nBars);
+
+ return (
+
+ );
+};
+
+Meter.propTypes = {
+ height: PropTypes.number,
+ level: PropTypes.number,
+ width: PropTypes.number
+};
+
+module.exports = Meter;
diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css
index a72a4f06d..6b28f8b2e 100644
--- a/src/components/modal/modal.css
+++ b/src/components/modal/modal.css
@@ -11,16 +11,17 @@
background-color: rgba(0, 0, 0, .75);
}
-/* @todo: extract to type: full ? */
-.full-modal-content {
- position: absolute;
+.modal-content {
+ margin: 100px auto;
outline: none;
- overflow-y: auto;
- -webkit-overflow-scrolling: 'touch';
+ border: 3px solid rgb(126, 133, 151);
+ padding: 0;
+ border-radius: $space;
user-select: none;
- height: 100%;
- width: 100%;
- display: flex;
+
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: #5d647a;
+ overflow: hidden;
}
/*
@@ -41,7 +42,7 @@ $sides: 20rem;
box-sizing: border-box;
width: 100%;
- background-color: $blue;
+ background-color: $motion-primary;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1rem;
@@ -76,4 +77,3 @@ $sides: 20rem;
flex-basis: $sides;
justify-content: flex-end;
}
-
diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx
index 2ccd4b593..da4c62027 100644
--- a/src/components/modal/modal.jsx
+++ b/src/components/modal/modal.jsx
@@ -1,19 +1,19 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
-const ReactModal = require('react-modal');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+import ReactModal from 'react-modal';
-const Box = require('../box/box.jsx');
-const CloseButton = require('../close-button/close-button.jsx');
-const Filter = require('../filter/filter.jsx');
+import Box from '../box/box.jsx';
+import CloseButton from '../close-button/close-button.jsx';
+import Filter from '../filter/filter.jsx';
-const styles = require('./modal.css');
+import styles from './modal.css';
class ModalComponent extends React.Component {
render () {
return (
-
+ {this.props.onFilterChange ? (
+
+ ) : null}
(
(
-
-
- {props.title}
-
{props.label}
@@ -44,7 +40,7 @@ const PromptComponent = props => (
-
+
);
PromptComponent.propTypes = {
diff --git a/src/components/record-modal/icon--back.svg b/src/components/record-modal/icon--back.svg
new file mode 100644
index 000000000..47d09bc7f
--- /dev/null
+++ b/src/components/record-modal/icon--back.svg
@@ -0,0 +1,16 @@
+
+
diff --git a/src/components/record-modal/icon--play.svg b/src/components/record-modal/icon--play.svg
new file mode 100644
index 000000000..e1b586917
--- /dev/null
+++ b/src/components/record-modal/icon--play.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/src/components/record-modal/icon--stop-playback.svg b/src/components/record-modal/icon--stop-playback.svg
new file mode 100644
index 000000000..bf7bb64be
--- /dev/null
+++ b/src/components/record-modal/icon--stop-playback.svg
@@ -0,0 +1,28 @@
+
+
\ No newline at end of file
diff --git a/src/components/record-modal/icon--stop-recording.svg b/src/components/record-modal/icon--stop-recording.svg
new file mode 100644
index 000000000..4bf61fb50
--- /dev/null
+++ b/src/components/record-modal/icon--stop-recording.svg
@@ -0,0 +1,28 @@
+
+
\ No newline at end of file
diff --git a/src/components/record-modal/playback-step.jsx b/src/components/record-modal/playback-step.jsx
new file mode 100644
index 000000000..0eddd9142
--- /dev/null
+++ b/src/components/record-modal/playback-step.jsx
@@ -0,0 +1,86 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import Box from '../box/box.jsx';
+import Waveform from '../waveform/waveform.jsx';
+import Meter from '../meter/meter.jsx';
+import AudioTrimmer from '../../containers/audio-trimmer.jsx';
+
+import styles from './record-modal.css';
+import backIcon from './icon--back.svg';
+import stopIcon from './icon--stop-playback.svg';
+import playIcon from './icon--play.svg';
+
+const PlaybackStep = props => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+PlaybackStep.propTypes = {
+ encoding: PropTypes.bool.isRequired,
+ levels: PropTypes.arrayOf(PropTypes.number).isRequired,
+ onBack: PropTypes.func.isRequired,
+ onPlay: PropTypes.func.isRequired,
+ onSetTrimEnd: PropTypes.func.isRequired,
+ onSetTrimStart: PropTypes.func.isRequired,
+ onStopPlaying: PropTypes.func.isRequired,
+ onSubmit: PropTypes.func.isRequired,
+ playhead: PropTypes.number,
+ playing: PropTypes.bool.isRequired,
+ trimEnd: PropTypes.number.isRequired,
+ trimStart: PropTypes.number.isRequired
+};
+
+module.exports = PlaybackStep;
diff --git a/src/components/record-modal/record-modal.css b/src/components/record-modal/record-modal.css
new file mode 100644
index 000000000..9a750fd4b
--- /dev/null
+++ b/src/components/record-modal/record-modal.css
@@ -0,0 +1,120 @@
+@import "../../css/colors.css";
+@import "../../css/units.css";
+
+.modal-content {
+ width: 600px;
+}
+
+.body {
+ background: white;
+ padding: 1.5rem 2.25rem;
+}
+
+.visualization-container {
+ display: flex;
+ justify-content: space-around;
+}
+
+.meter-container, .waveform-container {
+ background: $ui-pane-gray;
+ border: 1px solid $ui-pane-border;
+ border-radius: 5px;
+ padding: 3px;
+
+}
+.meter-container {
+ margin-right: 5px;
+ height: 180px;
+}
+
+.waveform-container {
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ width: 100%;
+ height: 180px;
+ position: relative;
+}
+
+.help-text {
+ margin: 10px auto 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: rgb(167, 170, 181);
+ font-size: 0.95rem;
+ font-weight: 500;
+}
+
+.playing-text {
+ color: $motion-primary;
+}
+
+.recording-text {
+ color: $red-primary;
+}
+
+.main-button-row {
+ display: flex;
+ justify-content: space-around;
+ margin-top: 15px;
+}
+
+.main-button-row button {
+ padding: 0.5rem 0.75rem;
+ border-radius: 0.25rem;
+ background: transparent;
+ border: none;
+}
+
+.main-button-row button:disabled {
+ opacity: 0.25;
+}
+
+.main-button-row button:active, .main-button-row button:focus {
+ outline: none;
+}
+
+.button-row {
+ font-weight: bolder;
+ text-align: right;
+ display: flex;
+ justify-content: space-between;
+}
+
+.button-row button {
+ padding: 0.75rem 1rem;
+ border-radius: 0.25rem;
+ background: white;
+ border: 1px solid $ui-pane-border;
+ font-weight: 600;
+ font-size: 0.85rem;
+ color: $motion-primary;
+}
+
+.button-row button.ok-button {
+ background: $motion-primary;
+ border: $motion-primary;
+ color: white;
+}
+
+.button-row button + button {
+ margin-left: 0.5rem;
+}
+
+.main-button {
+ text-align: center;
+}
+
+.record-button {
+ overflow: visible;
+}
+
+.record-button-circle {
+ fill: $red-primary;
+ stroke: $red-tertiary;
+}
+
+.record-button-circle-outline {
+ fill: $red-primary;
+ opacity: 0.2;
+ transition: 0.1s;
+}
diff --git a/src/components/record-modal/record-modal.jsx b/src/components/record-modal/record-modal.jsx
new file mode 100644
index 000000000..92bc0e38e
--- /dev/null
+++ b/src/components/record-modal/record-modal.jsx
@@ -0,0 +1,66 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import Box from '../box/box.jsx';
+import RecordingStep from '../../containers/recording-step.jsx';
+import PlaybackStep from '../../containers/playback-step.jsx';
+import Modal from '../modal/modal.jsx';
+import styles from './record-modal.css';
+
+const RecordModal = props => (
+
+
+ {props.samples ? (
+
+ ) : (
+
+ )}
+
+
+);
+
+RecordModal.propTypes = {
+ encoding: PropTypes.bool.isRequired,
+ levels: PropTypes.arrayOf(PropTypes.number),
+ onBack: PropTypes.func.isRequired,
+ onCancel: PropTypes.func.isRequired,
+ onPlay: PropTypes.func.isRequired,
+ onRecord: PropTypes.func.isRequired,
+ onSetPlayhead: PropTypes.func.isRequired,
+ onSetTrimEnd: PropTypes.func.isRequired,
+ onSetTrimStart: PropTypes.func.isRequired,
+ onStopPlaying: PropTypes.func.isRequired,
+ onStopRecording: PropTypes.func.isRequired,
+ onSubmit: PropTypes.func.isRequired,
+ playhead: PropTypes.number,
+ playing: PropTypes.bool,
+ recording: PropTypes.bool,
+ samples: PropTypes.instanceOf(Float32Array),
+ trimEnd: PropTypes.number.isRequired,
+ trimStart: PropTypes.number.isRequired
+};
+
+module.exports = RecordModal;
diff --git a/src/components/record-modal/recording-step.jsx b/src/components/record-modal/recording-step.jsx
new file mode 100644
index 000000000..ef78768c4
--- /dev/null
+++ b/src/components/record-modal/recording-step.jsx
@@ -0,0 +1,83 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import Box from '../box/box.jsx';
+import Meter from '../meter/meter.jsx';
+import Waveform from '../waveform/waveform.jsx';
+
+import styles from './record-modal.css';
+import stopIcon from './icon--stop-recording.svg';
+
+const RecordingStep = props => (
+
+
+
+
+
+
+ {props.levels ? (
+
+ ) : (
+
+ {props.listening ? 'Begin recording by clicking the button below' :
+ '↖️ \u00A0We need your permission to use your microphone'}
+
+ )}
+
+
+
+
+
+
+);
+
+RecordingStep.propTypes = {
+ level: PropTypes.number,
+ levels: PropTypes.arrayOf(PropTypes.number),
+ listening: PropTypes.bool,
+ onRecord: PropTypes.func.isRequired,
+ onStopRecording: PropTypes.func.isRequired,
+ recording: PropTypes.bool
+};
+
+module.exports = RecordingStep;
diff --git a/src/components/sprite-info/sprite-info.jsx b/src/components/sprite-info/sprite-info.jsx
index e3fbf53cc..f9e3e73bd 100644
--- a/src/components/sprite-info/sprite-info.jsx
+++ b/src/components/sprite-info/sprite-info.jsx
@@ -1,14 +1,15 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
-const Box = require('../box/box.jsx');
-const styles = require('./sprite-info.css');
+import Box from '../box/box.jsx';
+import BufferedInput from '../buffered-input/buffered-input.jsx';
+import styles from './sprite-info.css';
-const xIcon = require('./icon--x.svg');
-const yIcon = require('./icon--y.svg');
-const showIcon = require('./icon--show.svg');
-const hideIcon = require('./icon--hide.svg');
+import xIcon from './icon--x.svg';
+import yIcon from './icon--y.svg';
+import showIcon from './icon--show.svg';
+import hideIcon from './icon--hide.svg';
const ROTATION_STYLES = ['left-right', 'don\'t rotate', 'all around'];
@@ -32,16 +33,14 @@ class SpriteInfo extends React.Component {
x
-
@@ -74,16 +71,14 @@ class SpriteInfo extends React.Component {
/>
y
-
@@ -134,15 +129,13 @@ class SpriteInfo extends React.Component {
Direction
-
@@ -178,10 +171,6 @@ SpriteInfo.propTypes = {
]),
disabled: PropTypes.bool,
name: PropTypes.string,
- onBlurDirection: PropTypes.func,
- onBlurName: PropTypes.func,
- onBlurX: PropTypes.func,
- onBlurY: PropTypes.func,
onChangeDirection: PropTypes.func,
onChangeName: PropTypes.func,
onChangeRotationStyle: PropTypes.func,
@@ -189,7 +178,6 @@ SpriteInfo.propTypes = {
onChangeY: PropTypes.func,
onClickNotVisible: PropTypes.func,
onClickVisible: PropTypes.func,
- onKeyPress: PropTypes.func,
rotationStyle: PropTypes.oneOf(ROTATION_STYLES),
visible: PropTypes.bool,
x: PropTypes.oneOfType([
diff --git a/src/components/sprite-selector-item/sprite-selector-item.jsx b/src/components/sprite-selector-item/sprite-selector-item.jsx
index f4fe637f1..f9e3b5b1b 100644
--- a/src/components/sprite-selector-item/sprite-selector-item.jsx
+++ b/src/components/sprite-selector-item/sprite-selector-item.jsx
@@ -1,11 +1,11 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
-const Box = require('../box/box.jsx');
-const CostumeCanvas = require('../costume-canvas/costume-canvas.jsx');
-const CloseButton = require('../close-button/close-button.jsx');
-const styles = require('./sprite-selector-item.css');
+import Box from '../box/box.jsx';
+import CostumeCanvas from '../costume-canvas/costume-canvas.jsx';
+import CloseButton from '../close-button/close-button.jsx';
+import styles from './sprite-selector-item.css';
const SpriteSelectorItem = props => (
{
const {
diff --git a/src/components/stage/stage.jsx b/src/components/stage/stage.jsx
index 4e3e9a62d..63a27bf56 100644
--- a/src/components/stage/stage.jsx
+++ b/src/components/stage/stage.jsx
@@ -1,9 +1,9 @@
-const PropTypes = require('prop-types');
-const React = require('react');
+import PropTypes from 'prop-types';
+import React from 'react';
-const Box = require('../box/box.jsx');
-const MonitorList = require('../../containers/monitor-list.jsx');
-const styles = require('./stage.css');
+import Box from '../box/box.jsx';
+import MonitorList from '../../containers/monitor-list.jsx';
+import styles from './stage.css';
const StageComponent = props => {
const {
diff --git a/src/components/stop-all/stop-all.jsx b/src/components/stop-all/stop-all.jsx
index 57907e234..4d85e2c46 100644
--- a/src/components/stop-all/stop-all.jsx
+++ b/src/components/stop-all/stop-all.jsx
@@ -1,9 +1,9 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
-const stopAllIcon = require('./icon--stop-all.svg');
-const styles = require('./stop-all.css');
+import stopAllIcon from './icon--stop-all.svg';
+import styles from './stop-all.css';
const StopAllComponent = function (props) {
const {
diff --git a/src/components/target-pane/target-pane.css b/src/components/target-pane/target-pane.css
index 5f70c5878..448295da1 100644
--- a/src/components/target-pane/target-pane.css
+++ b/src/components/target-pane/target-pane.css
@@ -44,6 +44,7 @@
.add-button {
height: 0.8rem;
+ margin: auto;
}
/* @todo: This is hacky. Better: move buttons in their corresponding panes, and set values relatively */
diff --git a/src/components/target-pane/target-pane.jsx b/src/components/target-pane/target-pane.jsx
index c779f2747..0d48924af 100644
--- a/src/components/target-pane/target-pane.jsx
+++ b/src/components/target-pane/target-pane.jsx
@@ -1,19 +1,19 @@
-const classNames = require('classnames');
-const PropTypes = require('prop-types');
-const React = require('react');
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
-const VM = require('scratch-vm');
+import VM from 'scratch-vm';
-const Box = require('../box/box.jsx');
-const BackdropLibrary = require('../../containers/backdrop-library.jsx');
-const CostumeLibrary = require('../../containers/costume-library.jsx');
-const SoundLibrary = require('../../containers/sound-library.jsx');
-const SpriteLibrary = require('../../containers/sprite-library.jsx');
-const SpriteSelectorComponent = require('../sprite-selector/sprite-selector.jsx');
-const StageSelector = require('../../containers/stage-selector.jsx');
+import Box from '../box/box.jsx';
+import BackdropLibrary from '../../containers/backdrop-library.jsx';
+import CostumeLibrary from '../../containers/costume-library.jsx';
+import SoundLibrary from '../../containers/sound-library.jsx';
+import SpriteLibrary from '../../containers/sprite-library.jsx';
+import SpriteSelectorComponent from '../sprite-selector/sprite-selector.jsx';
+import StageSelector from '../../containers/stage-selector.jsx';
-const styles = require('./target-pane.css');
-const addIcon = require('./icon--add.svg');
+import styles from './target-pane.css';
+import addIcon from './icon--add.svg';
/*
* Pane that contains the sprite selector, sprite info, stage selector,
@@ -78,6 +78,7 @@ const TargetPane = ({