Merge pull request #152 from rschamp/wire-sprite-info

Wire sprite info panel to VM
This commit is contained in:
Ray Schamp authored and GitHub committed 2017-03-02 10:48:50 -05:00
commit c0ea247958
9 files changed
+376 -120

No files matched your search

+1
View File
@@ -53,6 +53,7 @@
"react-redux": "5.0.2",
"react-style-proptype": "2.0.1",
"redux": "3.6.0",
"redux-throttle": "0.1.1",
"scratch-audio": "latest",
"scratch-blocks": "latest",
"scratch-render": "latest",
+23 -21
View File
@@ -36,29 +36,27 @@
}
/* TODO: refactor from descendant selector, to a regular class */
.group img {
.group img {
display: inline-block;
width: 1rem;
width: 1rem;
height: 1rem;
margin: 0 0.1rem;
}
.icon {
opacity: 0.4;
}
.icon.is-active {
opacity: 1;
}
.draggable-icon {
width: 0.55rem !important;
opacity: 1 !important;
width: 0.55rem;
}
.not-draggable-icon {
width: 0.75rem !important;
opacity: 0.4 !important;
}
.show-icon {
opacity: 1 !important;
}
.hide-icon {
opacity: 0.4 !important;
width: 0.75rem;
}
.input-label {
@@ -87,19 +85,19 @@
font-size: 0.625rem;
font-weight: bold;
border-width: 1px;
border-style: solid;
border-color: #e9eef2;
border-radius: 2rem;
border-style: solid;
border-color: #e9eef2;
border-radius: 2rem;
cursor: text;
transition: 0.25s ease-out; /* TODO: standardize with var */
transition: 0.25s ease-out; /* TODO: standardize with var */
box-shadow: none;
/*
/*
For truncating overflowing text gracefully
Min-width is for a bug: https://css-tricks.com/flexbox-truncated-text
TODO: move this out into a mixin or a helper component
*/
overflow: hidden;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
@@ -122,5 +120,9 @@
border: 1px solid #e9eef2;
border-radius: 0.25rem;
cursor: pointer;
transition: 0.25s ease-out; /* TODO: standardize with var */
transition: 0.25s ease-out; /* TODO: standardize with var */
}
.radio-box.is-disabled {
cursor: default;
}
+161 -87
View File
@@ -11,101 +11,175 @@ const hideIcon = require('./icon--hide.svg');
const draggableIcon = require('./icon--draggable.svg');
const notDraggableIcon = require('./icon--not-draggable.svg');
const SpriteInfo = props => (
<Box
className={styles.spriteInfo}
>
<div className={styles.row}>
<div className={styles.group}>
<span className={styles.inputLabel}>
{props.name}
</span>
<input
className={classNames(styles.inputForm, styles.inputFormSpriteName)}
placeholder="Name"
type="text"
/>
</div>
const ROTATION_STYLES = ['left-right', 'don\'t rotate', 'all around'];
<div className={styles.group}>
<img
className={classNames(styles.xIcon, styles.icon)}
src={xIcon}
/>
<span className={styles.inputLabel}>x</span>
<input
className={classNames(styles.inputForm, styles.inputFormX)}
placeholder="x"
type="text"
/>
</div>
<div className={styles.group}>
<img
className={classNames(styles.yIcon, styles.icon)}
src={yIcon}
/>
<span className={styles.inputLabel}>y</span>
<input
className={classNames(styles.inputForm, styles.inputFormY)}
placeholder="y"
type="text"
/>
</div>
</div>
class SpriteInfo extends React.Component {
shouldComponentUpdate (nextProps) {
return (
this.props.disabled !== nextProps.disabled ||
this.props.draggable !== nextProps.draggable ||
this.props.name !== nextProps.name ||
this.props.rotationStyle !== nextProps.rotationStyle ||
this.props.visible !== nextProps.visible ||
this.props.x !== nextProps.x ||
this.props.y !== nextProps.y
);
}
render () {
return (
<Box
className={styles.spriteInfo}
>
<div className={styles.row}>
<div className={styles.group}>
<span className={styles.inputLabel}>Sprite</span>
<input
className={classNames(styles.inputForm, styles.inputFormSpriteName)}
disabled={this.props.disabled}
placeholder="Name"
type="text"
value={this.props.disabled ? '' : this.props.name}
onChange={this.props.onChangeName}
/>
</div>
<div className={styles.group}>
<img
className={classNames(styles.xIcon, styles.icon)}
src={xIcon}
/>
<span className={styles.inputLabel}>x</span>
<input
className={classNames(styles.inputForm, styles.inputFormX)}
disabled={this.props.disabled}
placeholder="x"
type="text"
value={this.props.disabled ? '' : this.props.x}
onChange={this.props.onChangeX}
/>
</div>
<div className={styles.row}>
<div className={styles.group}>
<span className={styles.inputLabelSmall}>
Show
</span>
<div className={styles.radioBox}>
<img
className={classNames(styles.showIcon, styles.icon)}
src={showIcon}
/>
<img
className={classNames(styles.hideIcon, styles.icon)}
src={hideIcon}
/>
<div className={styles.group}>
<img
className={classNames(styles.yIcon, styles.icon)}
src={yIcon}
/>
<span className={styles.inputLabel}>y</span>
<input
className={classNames(styles.inputForm, styles.inputFormY)}
disabled={this.props.disabled}
placeholder="y"
type="text"
value={this.props.disabled ? '' : this.props.y}
onChange={this.props.onChangeY}
/>
</div>
</div>
</div>
<div className={styles.group}>
<span className={styles.inputLabelSmall}>
Draggable
</span>
<div className={styles.radioBox}>
<img
className={classNames(styles.draggableIcon, styles.icon)}
src={draggableIcon}
/>
<img
className={classNames(styles.notDraggableIcon, styles.icon)}
src={notDraggableIcon}
/>
<div className={styles.row}>
<div className={styles.group}>
<span className={styles.inputLabelSmall}>
Show
</span>
<div className={classNames(styles.radioBox, {[styles.isDisabled]: this.props.disabled})}>
<img
className={classNames(
styles.icon,
styles.showIcon,
{
[styles.isActive]: this.props.visible && !this.props.disabled
}
)}
src={showIcon}
onClick={this.props.onClickVisible}
/>
<img
className={classNames(
styles.icon,
styles.hideIcon,
{
[styles.isActive]: !this.props.visible && !this.props.disabled
}
)}
src={hideIcon}
onClick={this.props.onClickNotVisible}
/>
</div>
</div>
<div className={styles.group}>
<span className={styles.inputLabelSmall}>
Draggable
</span>
<div className={classNames(styles.radioBox, {[styles.isDisabled]: this.props.disabled})}>
<img
className={classNames(
styles.icon,
styles.draggableIcon,
{
[styles.isActive]: this.props.draggable && !this.props.disabled
}
)}
src={draggableIcon}
onClick={this.props.onClickDraggable}
/>
<img
className={classNames(
styles.icon,
styles.notDraggableIcon,
{
[styles.isActive]: !this.props.draggable && !this.props.disabled
}
)}
src={notDraggableIcon}
onClick={this.props.onClickNotDraggable}
/>
</div>
</div>
<div className={styles.group}>
<span className={styles.inputLabelSmall}>
Rotation
</span>
<select
className={classNames(styles.selectForm, styles.inputFormRotationSelect)}
disabled={this.props.disabled}
value={this.props.rotationStyle}
onChange={this.props.onChangeRotationStyle}
>
{ROTATION_STYLES.map(style => (
<option
key={style}
value={style}
>
{style}
</option>
))}
</select>
</div>
</div>
</div>
<div className={styles.group}>
<span className={styles.inputLabelSmall}>
Rotation
</span>
<select
className={classNames(styles.selectForm, styles.inputFormRotationSelect)}
>
<option value="left-right">left-right</option>
<option value="clockwise">don&#39;t rotate</option>
<option value="anchored">all around</option>
</select>
</div>
</div>
</Box>
);
</Box>
);
}
}
SpriteInfo.propTypes = {
name: React.PropTypes.string
disabled: React.PropTypes.bool,
draggable: React.PropTypes.bool,
name: React.PropTypes.string,
onChangeName: React.PropTypes.func,
onChangeRotationStyle: React.PropTypes.func,
onChangeX: React.PropTypes.func,
onChangeY: React.PropTypes.func,
onClickDraggable: React.PropTypes.func,
onClickNotDraggable: React.PropTypes.func,
onClickNotVisible: React.PropTypes.func,
onClickVisible: React.PropTypes.func,
rotationStyle: React.PropTypes.oneOf(ROTATION_STYLES),
visible: React.PropTypes.bool,
x: React.PropTypes.number,
y: React.PropTypes.number
};
module.exports = SpriteInfo;
@@ -1,23 +1,49 @@
const React = require('react');
const Box = require('../box/box.jsx');
const SpriteInfo = require('../../containers/sprite-info.jsx');
const SpriteSelectorItem = require('../../containers/sprite-selector-item.jsx');
const SpriteInfo = require('../sprite-info/sprite-info.jsx');
const styles = require('./sprite-selector.css');
const SpriteSelectorComponent = function (props) {
const {
onChangeSpriteDraggability,
onChangeSpriteName,
onChangeSpriteRotationStyle,
onChangeSpriteVisibility,
onChangeSpriteX,
onChangeSpriteY,
onSelectSprite,
selectedId,
sprites,
...componentProps
} = props;
let selectedSprite = sprites[selectedId];
let spriteInfoDisabled = false;
if (typeof selectedSprite === 'undefined') {
selectedSprite = {};
spriteInfoDisabled = true;
}
return (
<Box
className={styles.spriteSelector}
{...componentProps}
>
<SpriteInfo name="Sprite" />
<SpriteInfo
disabled={spriteInfoDisabled}
draggable={selectedSprite.draggable}
name={selectedSprite.name}
rotationStyle={selectedSprite.rotationStyle}
visible={selectedSprite.visible}
x={selectedSprite.x}
y={selectedSprite.y}
onChangeDraggability={onChangeSpriteDraggability}
onChangeName={onChangeSpriteName}
onChangeRotationStyle={onChangeSpriteRotationStyle}
onChangeVisibility={onChangeSpriteVisibility}
onChangeX={onChangeSpriteX}
onChangeY={onChangeSpriteY}
/>
<Box className={styles.scrollWrapper}>
<Box className={styles.itemsWrapper}>
@@ -43,6 +69,12 @@ const SpriteSelectorComponent = function (props) {
};
SpriteSelectorComponent.propTypes = {
onChangeSpriteDraggability: React.PropTypes.func,
onChangeSpriteName: React.PropTypes.func,
onChangeSpriteRotationStyle: React.PropTypes.func,
onChangeSpriteVisibility: React.PropTypes.func,
onChangeSpriteX: React.PropTypes.func,
onChangeSpriteY: React.PropTypes.func,
onSelectSprite: React.PropTypes.func,
selectedId: React.PropTypes.string,
sprites: React.PropTypes.shape({
+25 -2
View File
@@ -37,6 +37,12 @@ class TargetPane extends React.Component {
backdropLibraryVisible,
costumeLibraryVisible,
spriteLibraryVisible,
onChangeSpriteDraggability,
onChangeSpriteName,
onChangeSpriteRotationStyle,
onChangeSpriteVisibility,
onChangeSpriteX,
onChangeSpriteY,
onNewSpriteClick,
onNewBackdropClick,
onRequestCloseBackdropLibrary,
@@ -53,10 +59,16 @@ class TargetPane extends React.Component {
className={styles.targetPane}
{...componentProps}
>
<SpriteSelectorComponent
selectedId={editingTarget}
sprites={sprites}
onChangeSpriteDraggability={onChangeSpriteDraggability}
onChangeSpriteName={onChangeSpriteName}
onChangeSpriteRotationStyle={onChangeSpriteRotationStyle}
onChangeSpriteVisibility={onChangeSpriteVisibility}
onChangeSpriteX={onChangeSpriteX}
onChangeSpriteY={onChangeSpriteY}
onSelectSprite={onSelectSprite}
/>
<Box className={styles.stageSelectorWrapper}>
@@ -118,15 +130,26 @@ const spriteShape = React.PropTypes.shape({
rotationCenterX: React.PropTypes.number,
rotationCenterY: React.PropTypes.number
}),
draggable: React.PropTypes.bool,
id: React.PropTypes.string,
name: React.PropTypes.string,
order: React.PropTypes.number
order: React.PropTypes.number,
rotationStyle: React.PropTypes.string,
visibility: React.PropTypes.bool,
x: React.PropTypes.number,
y: React.PropTypes.number
});
TargetPane.propTypes = {
backdropLibraryVisible: React.PropTypes.bool,
costumeLibraryVisible: React.PropTypes.bool,
editingTarget: React.PropTypes.string,
onChangeSpriteDraggability: React.PropTypes.func,
onChangeSpriteName: React.PropTypes.func,
onChangeSpriteRotationStyle: React.PropTypes.func,
onChangeSpriteVisibility: React.PropTypes.func,
onChangeSpriteX: React.PropTypes.func,
onChangeSpriteY: React.PropTypes.func,
onNewBackdropClick: React.PropTypes.func,
onNewSpriteClick: React.PropTypes.func,
onRequestCloseBackdropLibrary: React.PropTypes.func,
+80
View File
@@ -0,0 +1,80 @@
const bindAll = require('lodash.bindall');
const React = require('react');
const SpriteInfoComponent = require('../components/sprite-info/sprite-info.jsx');
class SpriteInfo extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleChangeName',
'handleChangeRotationStyle',
'handleChangeX',
'handleChangeY',
'handleClickVisible',
'handleClickNotVisible',
'handleClickDraggable',
'handleClickNotDraggable'
]);
}
handleChangeName (e) {
this.props.onChangeName(e.target.value);
}
handleChangeRotationStyle (e) {
this.props.onChangeRotationStyle(e.target.value);
}
handleChangeX (e) {
let x = e.target.value;
if (x === '-') x = -1;
if (isNaN(x)) return;
this.props.onChangeX(x);
}
handleChangeY (e) {
let y = e.target.value;
if (y === '-') y = -1;
if (isNaN(y)) return;
this.props.onChangeY(y);
}
handleClickVisible (e) {
e.preventDefault();
this.props.onChangeVisibility(true);
}
handleClickNotVisible (e) {
e.preventDefault();
this.props.onChangeVisibility(false);
}
handleClickDraggable (e) {
e.preventDefault();
this.props.onChangeDraggability(true);
}
handleClickNotDraggable (e) {
e.preventDefault();
this.props.onChangeDraggability(false);
}
render () {
return (
<SpriteInfoComponent
{...this.props}
onChangeName={this.handleChangeName}
onChangeRotationStyle={this.handleChangeRotationStyle}
onChangeX={this.handleChangeX}
onChangeY={this.handleChangeY}
onClickDraggable={this.handleClickDraggable}
onClickNotDraggable={this.handleClickNotDraggable}
onClickNotVisible={this.handleClickNotVisible}
onClickVisible={this.handleClickVisible}
/>
);
}
}
SpriteInfo.propTypes = {
onChangeDraggability: React.PropTypes.func,
onChangeName: React.PropTypes.func,
onChangeRotationStyle: React.PropTypes.func,
onChangeVisibility: React.PropTypes.func,
onChangeX: React.PropTypes.func,
onChangeY: React.PropTypes.func
};
module.exports = SpriteInfo;
+34 -2
View File
@@ -1,5 +1,4 @@
const bindAll = require('lodash.bindall');
const pick = require('lodash.pick');
const React = require('react');
const {connect} = require('react-redux');
@@ -18,9 +17,33 @@ class TargetPane extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleChangeSpriteDraggability',
'handleChangeSpriteName',
'handleChangeSpriteRotationStyle',
'handleChangeSpriteVisibility',
'handleChangeSpriteX',
'handleChangeSpriteY',
'handleSelectSprite'
]);
}
handleChangeSpriteDraggability (draggable) {
this.props.vm.postSpriteInfo({draggable});
}
handleChangeSpriteName (name) {
this.props.vm.renameSprite(this.props.editingTarget, name);
}
handleChangeSpriteRotationStyle (rotationStyle) {
this.props.vm.postSpriteInfo({rotationStyle});
}
handleChangeSpriteVisibility (visible) {
this.props.vm.postSpriteInfo({visible});
}
handleChangeSpriteX (x) {
this.props.vm.postSpriteInfo({x});
}
handleChangeSpriteY (y) {
this.props.vm.postSpriteInfo({y});
}
handleSelectSprite (id) {
this.props.vm.setEditingTarget(id);
}
@@ -28,6 +51,12 @@ class TargetPane extends React.Component {
return (
<TargetPaneComponent
{...this.props}
onChangeSpriteDraggability={this.handleChangeSpriteDraggability}
onChangeSpriteName={this.handleChangeSpriteName}
onChangeSpriteRotationStyle={this.handleChangeSpriteRotationStyle}
onChangeSpriteVisibility={this.handleChangeSpriteVisibility}
onChangeSpriteX={this.handleChangeSpriteX}
onChangeSpriteY={this.handleChangeSpriteY}
onSelectSprite={this.handleSelectSprite}
/>
);
@@ -46,7 +75,10 @@ TargetPane.propTypes = {
const mapStateToProps = state => ({
editingTarget: state.targets.editingTarget,
sprites: Object.keys(state.targets.sprites).reduce((sprites, k) => {
sprites[k] = pick(state.targets.sprites[k], ['costume', 'name', 'order']);
let {x, y, ...sprite} = state.targets.sprites[k];
if (typeof x !== 'undefined') x = Math.round(x);
if (typeof y !== 'undefined') y = Math.round(y);
sprites[k] = {...sprite, x, y};
return sprites;
}, {}),
stage: state.targets.stage,
+5 -2
View File
@@ -1,7 +1,8 @@
const React = require('react');
const ReactDOM = require('react-dom');
const {Provider} = require('react-redux');
const {createStore} = require('redux');
const {createStore, applyMiddleware} = require('redux');
const throttle = require('redux-throttle').default;
const GUI = require('./containers/gui.jsx');
const log = require('./lib/log');
@@ -63,7 +64,9 @@ App.propTypes = {
const appTarget = document.createElement('div');
appTarget.className = styles.app;
document.body.appendChild(appTarget);
const store = createStore(
const store = applyMiddleware(
throttle(300, {leading: true, trailing: true})
)(createStore)(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
+13 -4
View File
@@ -30,7 +30,7 @@ const reducer = function (state, action) {
.filter(target => !target.isStage)
.reduce(
(targets, target, listId) => defaultsDeep(
{[target.id]: {order: listId}},
{[target.id]: {order: listId, ...target}},
{[target.id]: state.sprites[target.id]},
targets
),
@@ -55,19 +55,28 @@ const reducer = function (state, action) {
reducer.updateTarget = function (target) {
return {
type: UPDATE_TARGET,
target: target
target: target,
meta: {
throttle: 30
}
};
};
reducer.updateTargets = function (targetList) {
return {
type: UPDATE_TARGET_LIST,
targets: targetList
targets: targetList,
meta: {
throttle: 30
}
};
};
reducer.updateEditingTarget = function (editingTarget) {
return {
type: UPDATE_EDITING_TARGET,
target: editingTarget
target: editingTarget,
meta: {
throttle: 30
}
};
};
module.exports = reducer;