Merge branch 'develop' into patch-10
This commit is contained in:
commit
840c9cfa3d
22 files changed
+403
-188
No files matched your search
+1
-1
@@ -62,7 +62,7 @@
|
||||
"scratch-audio": "^0.1.0-prerelease.0",
|
||||
"scratch-blocks": "^0.1.0-prerelease.0",
|
||||
"scratch-render": "^0.1.0-prerelease.0",
|
||||
"scratch-storage": "^0.0.1-prerelease.0",
|
||||
"scratch-storage": "^0.1.0",
|
||||
"scratch-vm": "^0.1.0-prerelease.0",
|
||||
"style-loader": "0.16.1",
|
||||
"svg-to-image": "1.1.3",
|
||||
|
||||
@@ -27,6 +27,7 @@ const Selector = props => {
|
||||
<Box className={styles.listArea}>
|
||||
{items.map((item, index) => (
|
||||
<SpriteSelectorItem
|
||||
assetId={item.assetId}
|
||||
className={styles.listItem}
|
||||
costumeURL={item.url}
|
||||
id={index}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.button {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
const classNames = require('classnames');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const styles = require('./button.css');
|
||||
|
||||
const ButtonComponent = ({
|
||||
className,
|
||||
onClick,
|
||||
children,
|
||||
...props
|
||||
}) => (
|
||||
<span
|
||||
className={classNames(
|
||||
styles.button,
|
||||
className
|
||||
)}
|
||||
role="button"
|
||||
onClick={onClick}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
ButtonComponent.propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
onClick: PropTypes.func.isRequired
|
||||
};
|
||||
module.exports = ButtonComponent;
|
||||
@@ -19,6 +19,8 @@ const GUIComponent = props => {
|
||||
basePath,
|
||||
children,
|
||||
vm,
|
||||
onTabSelect,
|
||||
tabIndex,
|
||||
...componentProps
|
||||
} = props;
|
||||
if (children) {
|
||||
@@ -29,13 +31,6 @@ const GUIComponent = props => {
|
||||
);
|
||||
}
|
||||
|
||||
// @todo hack to resize blockly manually in case resize happened while hidden
|
||||
const handleTabSelect = tabIndex => {
|
||||
if (tabIndex === 0) {
|
||||
setTimeout(() => window.dispatchEvent(new Event('resize')));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
className={styles.pageWrapper}
|
||||
@@ -48,7 +43,7 @@ const GUIComponent = props => {
|
||||
<Tabs
|
||||
className={styles.tabs}
|
||||
forceRenderTabPanel={true} // eslint-disable-line react/jsx-boolean-value
|
||||
onSelect={handleTabSelect}
|
||||
onSelect={onTabSelect}
|
||||
>
|
||||
<TabList className={styles.tabList}>
|
||||
<Tab className={styles.tab}>Scripts</Tab>
|
||||
@@ -59,6 +54,7 @@ const GUIComponent = props => {
|
||||
<Box className={styles.blocksWrapper}>
|
||||
<Blocks
|
||||
grow={1}
|
||||
isVisible={tabIndex === 0} // Scripts tab
|
||||
options={{
|
||||
media: `${basePath}static/blocks-media/`
|
||||
}}
|
||||
@@ -102,6 +98,8 @@ const GUIComponent = props => {
|
||||
GUIComponent.propTypes = {
|
||||
basePath: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
onTabSelect: PropTypes.func,
|
||||
tabIndex: PropTypes.number,
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
GUIComponent.defaultProps = {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const ButtonComponent = require('../button/button.jsx');
|
||||
|
||||
const styles = require('./load-button.css');
|
||||
|
||||
const LoadButtonComponent = ({
|
||||
inputRef,
|
||||
onChange,
|
||||
onClick,
|
||||
title,
|
||||
...props
|
||||
}) => (
|
||||
<span {...props}>
|
||||
<ButtonComponent onClick={onClick}>{title}</ButtonComponent>
|
||||
<input
|
||||
className={styles.fileInput}
|
||||
ref={inputRef}
|
||||
type="file"
|
||||
onChange={onChange}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
LoadButtonComponent.propTypes = {
|
||||
className: PropTypes.string,
|
||||
inputRef: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
title: PropTypes.string
|
||||
};
|
||||
LoadButtonComponent.defaultProps = {
|
||||
title: 'Load'
|
||||
};
|
||||
module.exports = LoadButtonComponent;
|
||||
@@ -2,6 +2,9 @@ const classNames = require('classnames');
|
||||
const React = require('react');
|
||||
|
||||
const Box = require('../box/box.jsx');
|
||||
const LoadButton = require('../../containers/load-button.jsx');
|
||||
const SaveButton = require('../../containers/save-button.jsx');
|
||||
|
||||
const styles = require('./menu-bar.css');
|
||||
const scratchLogo = require('./scratch-logo.svg');
|
||||
|
||||
@@ -18,7 +21,8 @@ const MenuBar = function MenuBar () {
|
||||
src={scratchLogo}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.menuItem} >Animation Playtest Prototype</div>
|
||||
<SaveButton className={styles.menuItem} />
|
||||
<LoadButton className={styles.menuItem} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -53,17 +53,15 @@ const SpriteSelectorComponent = function (props) {
|
||||
{Object.keys(sprites)
|
||||
// Re-order by list order
|
||||
.sort((id1, id2) => sprites[id1].order - sprites[id2].order)
|
||||
.map(id => (
|
||||
.map(id => sprites[id])
|
||||
.map(sprite => (
|
||||
<SpriteSelectorItem
|
||||
assetId={sprite.costume && sprite.costume.assetId}
|
||||
className={styles.sprite}
|
||||
costumeURL={
|
||||
sprites[id].costume &&
|
||||
sprites[id].costume.url
|
||||
}
|
||||
id={id}
|
||||
key={id}
|
||||
name={sprites[id].name}
|
||||
selected={id === selectedId}
|
||||
id={sprite.id}
|
||||
key={sprite.id}
|
||||
name={sprite.name}
|
||||
selected={sprite.id === selectedId}
|
||||
onClick={onSelectSprite}
|
||||
onDeleteButtonClick={onDeleteSprite}
|
||||
/>
|
||||
|
||||
@@ -23,4 +23,5 @@
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
const isEqual = require('lodash.isequal');
|
||||
const omit = require('lodash.omit');
|
||||
const classNames = require('classnames');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
@@ -23,119 +21,106 @@ const addIcon = require('./icon--add.svg');
|
||||
* @param {object} props Props for the component
|
||||
* @returns {React.Component} rendered component
|
||||
*/
|
||||
class TargetPane extends React.Component {
|
||||
shouldComponentUpdate (nextProps) {
|
||||
return (
|
||||
// Do a normal shallow compare on all props except sprites
|
||||
Object.keys(omit(nextProps, ['sprites']))
|
||||
.reduce((all, k) => all || nextProps[k] !== this.props[k], false) ||
|
||||
// Deep compare on sprites object
|
||||
!isEqual(this.props.sprites, nextProps.sprites)
|
||||
);
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
editingTarget,
|
||||
backdropLibraryVisible,
|
||||
costumeLibraryVisible,
|
||||
soundLibraryVisible,
|
||||
spriteLibraryVisible,
|
||||
onChangeSpriteDirection,
|
||||
onChangeSpriteName,
|
||||
onChangeSpriteRotationStyle,
|
||||
onChangeSpriteVisibility,
|
||||
onChangeSpriteX,
|
||||
onChangeSpriteY,
|
||||
onDeleteSprite,
|
||||
onNewSpriteClick,
|
||||
onNewBackdropClick,
|
||||
onRequestCloseBackdropLibrary,
|
||||
onRequestCloseCostumeLibrary,
|
||||
onRequestCloseSoundLibrary,
|
||||
onRequestCloseSpriteLibrary,
|
||||
onSelectSprite,
|
||||
stage,
|
||||
sprites,
|
||||
vm,
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<Box
|
||||
className={styles.targetPane}
|
||||
{...componentProps}
|
||||
>
|
||||
const TargetPane = ({
|
||||
editingTarget,
|
||||
backdropLibraryVisible,
|
||||
costumeLibraryVisible,
|
||||
soundLibraryVisible,
|
||||
spriteLibraryVisible,
|
||||
onChangeSpriteDirection,
|
||||
onChangeSpriteName,
|
||||
onChangeSpriteRotationStyle,
|
||||
onChangeSpriteVisibility,
|
||||
onChangeSpriteX,
|
||||
onChangeSpriteY,
|
||||
onDeleteSprite,
|
||||
onNewSpriteClick,
|
||||
onNewBackdropClick,
|
||||
onRequestCloseBackdropLibrary,
|
||||
onRequestCloseCostumeLibrary,
|
||||
onRequestCloseSoundLibrary,
|
||||
onRequestCloseSpriteLibrary,
|
||||
onSelectSprite,
|
||||
stage,
|
||||
sprites,
|
||||
vm,
|
||||
...componentProps
|
||||
}) => (
|
||||
<Box
|
||||
className={styles.targetPane}
|
||||
{...componentProps}
|
||||
>
|
||||
|
||||
<SpriteSelectorComponent
|
||||
selectedId={editingTarget}
|
||||
sprites={sprites}
|
||||
onChangeSpriteDirection={onChangeSpriteDirection}
|
||||
onChangeSpriteName={onChangeSpriteName}
|
||||
onChangeSpriteRotationStyle={onChangeSpriteRotationStyle}
|
||||
onChangeSpriteVisibility={onChangeSpriteVisibility}
|
||||
onChangeSpriteX={onChangeSpriteX}
|
||||
onChangeSpriteY={onChangeSpriteY}
|
||||
onDeleteSprite={onDeleteSprite}
|
||||
onSelectSprite={onSelectSprite}
|
||||
<SpriteSelectorComponent
|
||||
selectedId={editingTarget}
|
||||
sprites={sprites}
|
||||
onChangeSpriteDirection={onChangeSpriteDirection}
|
||||
onChangeSpriteName={onChangeSpriteName}
|
||||
onChangeSpriteRotationStyle={onChangeSpriteRotationStyle}
|
||||
onChangeSpriteVisibility={onChangeSpriteVisibility}
|
||||
onChangeSpriteX={onChangeSpriteX}
|
||||
onChangeSpriteY={onChangeSpriteY}
|
||||
onDeleteSprite={onDeleteSprite}
|
||||
onSelectSprite={onSelectSprite}
|
||||
/>
|
||||
<Box className={styles.stageSelectorWrapper}>
|
||||
{stage.id && <StageSelector
|
||||
assetId={
|
||||
stage.costume &&
|
||||
stage.costume.assetId
|
||||
}
|
||||
backdropCount={stage.costumeCount}
|
||||
id={stage.id}
|
||||
selected={stage.id === editingTarget}
|
||||
onSelect={onSelectSprite}
|
||||
/>}
|
||||
<Box>
|
||||
|
||||
<button
|
||||
className={classNames(styles.addButtonWrapper, styles.addButtonWrapperSprite)}
|
||||
onClick={onNewSpriteClick}
|
||||
>
|
||||
<img
|
||||
className={styles.addButton}
|
||||
src={addIcon}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={classNames(styles.addButtonWrapper, styles.addButtonWrapperStage)}
|
||||
onClick={onNewBackdropClick}
|
||||
>
|
||||
<img
|
||||
className={styles.addButton}
|
||||
src={addIcon}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<SpriteLibrary
|
||||
visible={spriteLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseSpriteLibrary}
|
||||
/>
|
||||
<CostumeLibrary
|
||||
visible={costumeLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseCostumeLibrary}
|
||||
/>
|
||||
<SoundLibrary
|
||||
visible={soundLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseSoundLibrary}
|
||||
/>
|
||||
<BackdropLibrary
|
||||
visible={backdropLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseBackdropLibrary}
|
||||
/>
|
||||
<Box className={styles.stageSelectorWrapper}>
|
||||
{stage.id && <StageSelector
|
||||
backdropCount={stage.costumeCount}
|
||||
id={stage.id}
|
||||
selected={stage.id === editingTarget}
|
||||
url={
|
||||
stage.costume &&
|
||||
stage.costume.url
|
||||
}
|
||||
onSelect={onSelectSprite}
|
||||
/>}
|
||||
<Box>
|
||||
|
||||
<button
|
||||
className={classNames(styles.addButtonWrapper, styles.addButtonWrapperSprite)}
|
||||
onClick={onNewSpriteClick}
|
||||
>
|
||||
<img
|
||||
className={styles.addButton}
|
||||
src={addIcon}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={classNames(styles.addButtonWrapper, styles.addButtonWrapperStage)}
|
||||
onClick={onNewBackdropClick}
|
||||
>
|
||||
<img
|
||||
className={styles.addButton}
|
||||
src={addIcon}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<SpriteLibrary
|
||||
visible={spriteLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseSpriteLibrary}
|
||||
/>
|
||||
<CostumeLibrary
|
||||
visible={costumeLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseCostumeLibrary}
|
||||
/>
|
||||
<SoundLibrary
|
||||
visible={soundLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseSoundLibrary}
|
||||
/>
|
||||
<BackdropLibrary
|
||||
visible={backdropLibraryVisible}
|
||||
vm={vm}
|
||||
onRequestClose={onRequestCloseBackdropLibrary}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const spriteShape = PropTypes.shape({
|
||||
costume: PropTypes.shape({
|
||||
url: PropTypes.string,
|
||||
|
||||
@@ -52,7 +52,21 @@ class Blocks extends React.Component {
|
||||
this.attachVM();
|
||||
}
|
||||
shouldComponentUpdate (nextProps, nextState) {
|
||||
return this.state.prompt !== nextState.prompt;
|
||||
return this.state.prompt !== nextState.prompt || this.props.isVisible !== nextProps.isVisible;
|
||||
}
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.isVisible === prevProps.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo hack to resize blockly manually in case resize happened while hidden
|
||||
if (this.props.isVisible) { // Scripts tab
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
this.workspace.setVisible(true);
|
||||
this.workspace.toolbox_.refreshSelection();
|
||||
} else {
|
||||
this.workspace.setVisible(false);
|
||||
}
|
||||
}
|
||||
componentWillUnmount () {
|
||||
this.detachVM();
|
||||
@@ -146,6 +160,7 @@ class Blocks extends React.Component {
|
||||
const {
|
||||
options, // eslint-disable-line no-unused-vars
|
||||
vm, // eslint-disable-line no-unused-vars
|
||||
isVisible, // eslint-disable-line no-unused-vars
|
||||
...props
|
||||
} = this.props;
|
||||
return (
|
||||
@@ -169,11 +184,12 @@ class Blocks extends React.Component {
|
||||
}
|
||||
|
||||
Blocks.propTypes = {
|
||||
isVisible: PropTypes.bool.isRequired,
|
||||
options: PropTypes.shape({
|
||||
media: PropTypes.string,
|
||||
zoom: PropTypes.shape({
|
||||
controls: PropTypes.boolean,
|
||||
wheel: PropTypes.boolean,
|
||||
controls: PropTypes.bool,
|
||||
wheel: PropTypes.bool,
|
||||
startScale: PropTypes.number
|
||||
}),
|
||||
colours: PropTypes.shape({
|
||||
|
||||
@@ -38,7 +38,7 @@ class CostumeTab extends React.Component {
|
||||
editingTarget.sprite.costumes = editingTarget.sprite.costumes
|
||||
.slice(0, costumeIndex)
|
||||
.concat(editingTarget.sprite.costumes.slice(costumeIndex + 1));
|
||||
this.props.vm.runtime.spriteInfoReport(editingTarget);
|
||||
this.props.vm.runtime.requestTargetsUpdate(editingTarget);
|
||||
// @todo not sure if this is getting redrawn correctly
|
||||
this.props.vm.runtime.requestRedraw();
|
||||
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const VM = require('scratch-vm');
|
||||
const bindAll = require('lodash.bindall');
|
||||
|
||||
const vmListenerHOC = require('../lib/vm-listener-hoc.jsx');
|
||||
|
||||
const GUIComponent = require('../components/gui/gui.jsx');
|
||||
|
||||
class GUI extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleTabSelect'
|
||||
]);
|
||||
this.state = {tabIndex: 0};
|
||||
}
|
||||
componentDidMount () {
|
||||
this.props.vm.loadProject(this.props.projectData);
|
||||
this.props.vm.setCompatibilityMode(true);
|
||||
@@ -20,6 +28,9 @@ class GUI extends React.Component {
|
||||
componentWillUnmount () {
|
||||
this.props.vm.stopAll();
|
||||
}
|
||||
handleTabSelect (tabIndex) {
|
||||
this.setState({tabIndex});
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
projectData, // eslint-disable-line no-unused-vars
|
||||
@@ -28,7 +39,9 @@ class GUI extends React.Component {
|
||||
} = this.props;
|
||||
return (
|
||||
<GUIComponent
|
||||
tabIndex={this.state.tabIndex}
|
||||
vm={vm}
|
||||
onTabSelect={this.handleTabSelect}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
const bindAll = require('lodash.bindall');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const {connect} = require('react-redux');
|
||||
|
||||
const LoadButtonComponent = require('../components/load-button/load-button.jsx');
|
||||
|
||||
class LoadButton extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'setFileInput',
|
||||
'handleChange',
|
||||
'handleClick'
|
||||
]);
|
||||
}
|
||||
handleChange (e) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => this.props.loadProject(reader.result);
|
||||
reader.readAsText(e.target.files[0]);
|
||||
}
|
||||
handleClick () {
|
||||
this.fileInput.click();
|
||||
}
|
||||
setFileInput (input) {
|
||||
this.fileInput = input;
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
loadProject, // eslint-disable-line no-unused-vars
|
||||
...props
|
||||
} = this.props;
|
||||
return (
|
||||
<LoadButtonComponent
|
||||
inputRef={this.setFileInput}
|
||||
onChange={this.handleChange}
|
||||
onClick={this.handleClick}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LoadButton.propTypes = {
|
||||
loadProject: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
loadProject: state.vm.fromJSON.bind(state.vm)
|
||||
});
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps,
|
||||
() => ({}) // omit dispatch prop
|
||||
)(LoadButton);
|
||||
@@ -0,0 +1,62 @@
|
||||
const bindAll = require('lodash.bindall');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const {connect} = require('react-redux');
|
||||
|
||||
const ButtonComponent = require('../components/button/button.jsx');
|
||||
|
||||
class SaveButton extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleClick'
|
||||
]);
|
||||
}
|
||||
handleClick () {
|
||||
const json = this.props.saveProjectSb3();
|
||||
|
||||
// Download project data into a file - create link element,
|
||||
// simulate click on it, and then remove it.
|
||||
const saveLink = document.createElement('a');
|
||||
document.body.appendChild(saveLink);
|
||||
|
||||
const data = new Blob([json], {type: 'text'});
|
||||
const url = window.URL.createObjectURL(data);
|
||||
saveLink.href = url;
|
||||
|
||||
// File name: project-DATE-TIME
|
||||
const date = new Date();
|
||||
const timestamp = `${date.toLocaleDateString()}-${date.toLocaleTimeString()}`;
|
||||
saveLink.download = `project-${timestamp}.json`;
|
||||
saveLink.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(saveLink);
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
saveProjectSb3, // eslint-disable-line no-unused-vars
|
||||
...props
|
||||
} = this.props;
|
||||
return (
|
||||
<ButtonComponent
|
||||
onClick={this.handleClick}
|
||||
{...props}
|
||||
>
|
||||
Save
|
||||
</ButtonComponent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SaveButton.propTypes = {
|
||||
saveProjectSb3: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
saveProjectSb3: state.vm.saveProjectSb3.bind(state.vm)
|
||||
});
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps,
|
||||
() => ({}) // omit dispatch prop
|
||||
)(SaveButton);
|
||||
@@ -2,6 +2,8 @@ const bindAll = require('lodash.bindall');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const {connect} = require('react-redux');
|
||||
|
||||
const SpriteSelectorItemComponent = require('../components/sprite-selector-item/sprite-selector-item.jsx');
|
||||
|
||||
class SpriteSelectorItem extends React.Component {
|
||||
@@ -24,9 +26,12 @@ class SpriteSelectorItem extends React.Component {
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
id, // eslint-disable-line no-unused-vars
|
||||
onClick, // eslint-disable-line no-unused-vars
|
||||
onDeleteButtonClick, // eslint-disable-line no-unused-vars
|
||||
/* eslint-disable no-unused-vars */
|
||||
assetId,
|
||||
id,
|
||||
onClick,
|
||||
onDeleteButtonClick,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...props
|
||||
} = this.props;
|
||||
return (
|
||||
@@ -40,6 +45,7 @@ class SpriteSelectorItem extends React.Component {
|
||||
}
|
||||
|
||||
SpriteSelectorItem.propTypes = {
|
||||
assetId: PropTypes.string,
|
||||
costumeURL: PropTypes.string,
|
||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
name: PropTypes.string,
|
||||
@@ -48,4 +54,10 @@ SpriteSelectorItem.propTypes = {
|
||||
selected: PropTypes.bool
|
||||
};
|
||||
|
||||
module.exports = SpriteSelectorItem;
|
||||
const mapStateToProps = (state, {assetId, costumeURL}) => ({
|
||||
costumeURL: costumeURL || (assetId && state.vm.runtime.storage.get(assetId).encodeDataURI())
|
||||
});
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps
|
||||
)(SpriteSelectorItem);
|
||||
@@ -2,6 +2,8 @@ const bindAll = require('lodash.bindall');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const {connect} = require('react-redux');
|
||||
|
||||
const StageSelectorComponent = require('../components/stage-selector/stage-selector.jsx');
|
||||
|
||||
class StageSelector extends React.Component {
|
||||
@@ -18,6 +20,7 @@ class StageSelector extends React.Component {
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
assetId,
|
||||
id,
|
||||
onSelect,
|
||||
/* eslint-enable no-unused-vars */
|
||||
@@ -36,4 +39,12 @@ StageSelector.propTypes = {
|
||||
id: PropTypes.string,
|
||||
onSelect: PropTypes.func
|
||||
};
|
||||
module.exports = StageSelector;
|
||||
|
||||
const mapStateToProps = (state, {assetId}) => ({
|
||||
url: assetId && state.vm.runtime.storage.get(assetId).encodeDataURI()
|
||||
});
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps,
|
||||
() => ({}) // omit dispatch prop
|
||||
)(StageSelector);
|
||||
@@ -3,8 +3,6 @@ const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const VM = require('scratch-vm');
|
||||
|
||||
const Storage = require('./storage');
|
||||
|
||||
const {connect} = require('react-redux');
|
||||
|
||||
const targets = require('../reducers/targets');
|
||||
@@ -29,7 +27,6 @@ const vmListenerHOC = function (WrappedComponent) {
|
||||
// If the wrapped component uses the vm in componentDidMount, then
|
||||
// we need to start listening before mounting the wrapped component.
|
||||
this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate);
|
||||
this.props.vm.on('SPRITE_INFO_REPORT', this.props.onSpriteInfoReport);
|
||||
}
|
||||
componentDidMount () {
|
||||
if (this.props.attachKeyboardEvents) {
|
||||
@@ -92,20 +89,16 @@ const vmListenerHOC = function (WrappedComponent) {
|
||||
onTargetsUpdate: PropTypes.func,
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
const defaultVM = new VM('vm-listener-hoc');
|
||||
defaultVM.attachStorage(new Storage());
|
||||
VMListener.defaultProps = {
|
||||
attachKeyboardEvents: true,
|
||||
vm: defaultVM
|
||||
attachKeyboardEvents: true
|
||||
};
|
||||
const mapStateToProps = () => ({});
|
||||
const mapStateToProps = state => ({
|
||||
vm: state.vm
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onTargetsUpdate: data => {
|
||||
dispatch(targets.updateEditingTarget(data.editingTarget));
|
||||
dispatch(targets.updateTargets(data.targetList));
|
||||
},
|
||||
onSpriteInfoReport: spriteInfo => {
|
||||
dispatch(targets.updateTarget(spriteInfo));
|
||||
}
|
||||
});
|
||||
return connect(
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ const {combineReducers} = require('redux');
|
||||
|
||||
module.exports = combineReducers({
|
||||
modals: require('./modals'),
|
||||
monitors: require('./monitors'),
|
||||
targets: require('./targets'),
|
||||
monitors: require('./monitors')
|
||||
vm: require('./vm')
|
||||
});
|
||||
+4
-36
@@ -1,8 +1,5 @@
|
||||
const defaultsDeep = require('lodash.defaultsdeep');
|
||||
|
||||
const UPDATE_EDITING_TARGET = 'scratch-gui/targets/UPDATE_EDITING_TARGET';
|
||||
const UPDATE_TARGET_LIST = 'scratch-gui/targets/UPDATE_TARGET_LIST';
|
||||
const UPDATE_TARGET = 'scratch/targets/UPDATE_TARGET';
|
||||
|
||||
const initialState = {
|
||||
sprites: {},
|
||||
@@ -12,39 +9,19 @@ const initialState = {
|
||||
const reducer = function (state, action) {
|
||||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
case UPDATE_TARGET:
|
||||
if (action.target.id === state.stage.id) {
|
||||
return Object.assign({}, state, {
|
||||
stage: Object.assign({}, state.stage, action.target)
|
||||
});
|
||||
}
|
||||
return Object.assign({}, state, {
|
||||
sprites: defaultsDeep(
|
||||
{[action.target.id]: action.target},
|
||||
state.sprites
|
||||
)
|
||||
});
|
||||
case UPDATE_TARGET_LIST:
|
||||
return Object.assign({}, state, {
|
||||
sprites: action.targets
|
||||
.filter(target => !target.isStage)
|
||||
.reduce(
|
||||
(targets, target, listId) => defaultsDeep(
|
||||
{[target.id]: {order: listId, ...target}},
|
||||
{[target.id]: state.sprites[target.id]},
|
||||
targets
|
||||
(targets, target, listId) => Object.assign(
|
||||
targets,
|
||||
{[target.id]: {order: listId, ...target}}
|
||||
),
|
||||
{}
|
||||
),
|
||||
stage: action.targets
|
||||
.filter(target => target.isStage)
|
||||
.reduce(
|
||||
(stage, target) => {
|
||||
if (target.id !== stage.id) return target;
|
||||
return defaultsDeep(target, stage);
|
||||
},
|
||||
state.stage
|
||||
)
|
||||
.filter(target => target.isStage)[0] || {}
|
||||
});
|
||||
case UPDATE_EDITING_TARGET:
|
||||
return Object.assign({}, state, {editingTarget: action.target});
|
||||
@@ -52,15 +29,6 @@ const reducer = function (state, action) {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
reducer.updateTarget = function (target) {
|
||||
return {
|
||||
type: UPDATE_TARGET,
|
||||
target: target,
|
||||
meta: {
|
||||
throttle: 30
|
||||
}
|
||||
};
|
||||
};
|
||||
reducer.updateTargets = function (targetList) {
|
||||
return {
|
||||
type: UPDATE_TARGET_LIST,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
const VM = require('scratch-vm');
|
||||
const Storage = require('../lib/storage');
|
||||
|
||||
const SET_VM = 'scratch-gui/vm/SET_VM';
|
||||
const defaultVM = new VM();
|
||||
defaultVM.attachStorage(new Storage());
|
||||
const initialState = defaultVM;
|
||||
|
||||
const reducer = function (state, action) {
|
||||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
case SET_VM:
|
||||
return action.vm;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
reducer.setVM = function (vm) {
|
||||
return {
|
||||
type: SET_VM,
|
||||
vm: vm
|
||||
};
|
||||
};
|
||||
module.exports = reducer;
|
||||
Reference in new issue
Block a user