diff --git a/package.json b/package.json index 6239bdcd8..ef161dfa7 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-dom": "^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.7.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 @@ + + + + add-sprite + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + sound icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + record icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + \ 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..d37cca757 100644 --- a/src/components/asset-panel/selector.jsx +++ b/src/components/asset-panel/selector.jsx @@ -1,29 +1,22 @@ -const PropTypes = require('prop-types'); -const React = require('react'); +import PropTypes from 'prop-types'; +import React from 'react'; -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) => ( + + + + {message} + + + ))} + ); }; Selector.propTypes = { + buttons: PropTypes.arrayOf(PropTypes.shape({ + message: PropTypes.node.isRequired, + 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 @@ + + + + handle + Created with Sketch. + + + + + + + + + + + + + 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 ( + + {Array(nBars).fill(0) + .map((value, index) => ( + + ))} + + + ); +}; + +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 @@ + + + + re-record + Created with Sketch. + + + + + + + + + + + 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 @@ + + + + play + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + stop-playback + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + stop-recording + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + \ 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 {
Sprite -
@@ -53,16 +52,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 = ({