diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx index 012206517b48133c965b9514c21a0926821575e7..3674dbbe6fd90ea646882fc79d4065f60745de9e 100644 --- a/src/components/asset-panel/selector.jsx +++ b/src/components/asset-panel/selector.jsx @@ -57,6 +57,7 @@ Selector.propTypes = { url: PropTypes.string, name: PropTypes.string.isRequired })), + raiseSprites: PropTypes.bool, onDeleteClick: PropTypes.func, onItemClick: PropTypes.func.isRequired, selectedItemIndex: PropTypes.number.isRequired diff --git a/src/components/sprite-selector/sprite-selector.css b/src/components/sprite-selector/sprite-selector.css index 67b0532d394fce05d91962a759bbb21ad85a6ba9..f23e36024d589a56c7fc91126ef6ff21f6c64b2a 100644 --- a/src/components/sprite-selector/sprite-selector.css +++ b/src/components/sprite-selector/sprite-selector.css @@ -65,3 +65,44 @@ bottom: 0.75rem; right: 1rem; } + +.raised { + -webkit-box-shadow: 0px 0px 6px 2px rgba(0,222,7,1); + -moz-box-shadow: 0px 0px 6px 2px rgba(0,222,7,1); + box-shadow: 0px 0px 6px 2px rgba(0,222,7,1); +} + +.raised:hover { + -webkit-animation-name: wiggle; + -ms-animation-name: wiggle; + -ms-animation-duration: 1000ms; + -webkit-animation-duration: 1000ms; + -webkit-animation-iteration-count: 1; + -ms-animation-iteration-count: 1; + -webkit-animation-timing-function: ease-in-out; + -ms-animation-timing-function: ease-in-out; +} + +@-webkit-keyframes wiggle { + 0% {-webkit-transform: rotate(10deg);} + 25% {-webkit-transform: rotate(-10deg);} + 50% {-webkit-transform: rotate(20deg);} + 75% {-webkit-transform: rotate(-5deg);} + 100% {-webkit-transform: rotate(0deg);} +} + +@-ms-keyframes wiggle { + 0% {-ms-transform: rotate(1deg);} + 25% {-ms-transform: rotate(-1deg);} + 50% {-ms-transform: rotate(1.5deg);} + 75% {-ms-transform: rotate(-5deg);} + 100% {-ms-transform: rotate(0deg);} +} + +@keyframes wiggle { + 0% {transform: rotate(10deg);} + 25% {transform: rotate(-10deg);} + 50% {transform: rotate(20deg);} + 75% {transform: rotate(-5deg);} + 100% {transform: rotate(0deg);} +} \ No newline at end of file diff --git a/src/components/sprite-selector/sprite-selector.jsx b/src/components/sprite-selector/sprite-selector.jsx index 41e51a31626b26a6bf54a19d2b4db7f20b3ca055..806dd4ab035701c185efccc707c3dc6773fddb42 100644 --- a/src/components/sprite-selector/sprite-selector.jsx +++ b/src/components/sprite-selector/sprite-selector.jsx @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; +import classNames from 'classnames'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; import Box from '../box/box.jsx'; @@ -31,6 +32,7 @@ const SpriteSelectorComponent = function (props) { onDuplicateSprite, onNewSpriteClick, onSelectSprite, + raised, selectedId, sprites, ...componentProps @@ -72,7 +74,10 @@ const SpriteSelectorComponent = function (props) { .map(sprite => ( <SpriteSelectorItem assetId={sprite.costume && sprite.costume.assetId} - className={styles.sprite} + className={ + (raised && sprite.id !== selectedId) ? + classNames(styles.sprite, styles.raised) : styles.sprite + } id={sprite.id} key={sprite.id} name={sprite.name} @@ -107,6 +112,7 @@ SpriteSelectorComponent.propTypes = { onDuplicateSprite: PropTypes.func, onNewSpriteClick: PropTypes.func, onSelectSprite: PropTypes.func, + raised: PropTypes.bool, selectedId: PropTypes.string, sprites: PropTypes.shape({ id: PropTypes.shape({ diff --git a/src/components/target-pane/target-pane.jsx b/src/components/target-pane/target-pane.jsx index 30267688fe968e554928247d8f2aa7674f0478f0..551fb2b5f3d7d04627c0cf91604ce241529d5f88 100644 --- a/src/components/target-pane/target-pane.jsx +++ b/src/components/target-pane/target-pane.jsx @@ -32,6 +32,7 @@ const TargetPane = ({ onRequestCloseSpriteLibrary, onRequestCloseBackdropLibrary, onSelectSprite, + raiseSprites, stage, sprites, vm, @@ -43,6 +44,7 @@ const TargetPane = ({ > <SpriteSelectorComponent + raised={raiseSprites} selectedId={editingTarget} sprites={sprites} onChangeSpriteDirection={onChangeSpriteDirection} @@ -120,6 +122,7 @@ TargetPane.propTypes = { onRequestCloseExtensionLibrary: PropTypes.func, onRequestCloseSpriteLibrary: PropTypes.func, onSelectSprite: PropTypes.func, + raiseSprites: PropTypes.bool, spriteLibraryVisible: PropTypes.bool, sprites: PropTypes.objectOf(spriteShape), stage: spriteShape, diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx index 9879d80038ecf14e7d63d6a99e3e4431a4498e31..bc10479b8eba18443685539b5f68a1f412a019c9 100644 --- a/src/containers/sprite-selector-item.jsx +++ b/src/containers/sprite-selector-item.jsx @@ -60,6 +60,7 @@ SpriteSelectorItem.propTypes = { onClick: PropTypes.func, onDeleteButtonClick: PropTypes.func, onDuplicateButtonClick: PropTypes.func, + onHover: PropTypes.func, selected: PropTypes.bool }; diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx index 32ad448bc7d7b33ed060bc5670720274d4572c3c..acde49a4e81297d1b90a0951b78474a7e5eb5a84 100644 --- a/src/containers/target-pane.jsx +++ b/src/containers/target-pane.jsx @@ -92,6 +92,7 @@ const mapStateToProps = state => ({ return sprites; }, {}), stage: state.targets.stage, + raiseSprites: state.blockdrag, spriteLibraryVisible: state.modals.spriteLibrary, backdropLibraryVisible: state.modals.backdropLibrary }); diff --git a/src/index.ejs b/src/index.ejs index 3f824efb0e55b7e44cb18013dea2346bd6386062..964efa23c425a5f68ece7441ba3d87514b3cabcd 100644 --- a/src/index.ejs +++ b/src/index.ejs @@ -9,7 +9,7 @@ <!-- Sentry error logging to help with finding bugs --> <script src="https://cdn.ravenjs.com/3.22.1/raven.min.js" crossorigin="anonymous"></script> <script> - Raven.config('https://42b7d13da8ad4d68b13e57c5e54f9a23@sentry.io/273218').install(); + // Raven.config('https://42b7d13da8ad4d68b13e57c5e54f9a23@sentry.io/273218').install(); </script> <!-- /Sentry --> diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx index 081817c780e5518dbc0cfe4d3e88f06423809d88..5ccda74550524e899427431b41372bf929554dc6 100644 --- a/src/lib/vm-listener-hoc.jsx +++ b/src/lib/vm-listener-hoc.jsx @@ -6,6 +6,7 @@ import VM from 'scratch-vm'; import {connect} from 'react-redux'; import {updateEditingTarget, updateTargets} from '../reducers/targets'; +import {updateBlockDrag} from '../reducers/blockdrag'; import {updateMonitors} from '../reducers/monitors'; /* @@ -29,6 +30,7 @@ const vmListenerHOC = function (WrappedComponent) { // we need to start listening before mounting the wrapped component. this.props.vm.on('targetsUpdate', this.props.onTargetsUpdate); this.props.vm.on('MONITORS_UPDATE', this.props.onMonitorsUpdate); + this.props.vm.on('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); } componentDidMount () { @@ -86,10 +88,11 @@ const vmListenerHOC = function (WrappedComponent) { } VMListener.propTypes = { attachKeyboardEvents: PropTypes.bool, + onBlockDragUpdate: PropTypes.func.isRequired, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, - onMonitorsUpdate: PropTypes.func, - onTargetsUpdate: PropTypes.func, + onMonitorsUpdate: PropTypes.func.isRequired, + onTargetsUpdate: PropTypes.func.isRequired, vm: PropTypes.instanceOf(VM).isRequired }; VMListener.defaultProps = { @@ -105,6 +108,9 @@ const vmListenerHOC = function (WrappedComponent) { }, onMonitorsUpdate: monitorList => { dispatch(updateMonitors(monitorList)); + }, + onBlockDragUpdate: areBlocksOverGui => { + dispatch(updateBlockDrag(areBlocksOverGui)); } }); return connect( diff --git a/src/reducers/blockdrag.js b/src/reducers/blockdrag.js new file mode 100644 index 0000000000000000000000000000000000000000..cd424ea570b29dd2618ba51b6f6f08c00eafb6ce --- /dev/null +++ b/src/reducers/blockdrag.js @@ -0,0 +1,28 @@ +const BLOCK_DRAG_UPDATE = 'scratch-gui/blockdrag/BLOCK_DRAG_UPDATE'; + +const initialState = false; + +const reducer = function (state, action) { + if (typeof state === 'undefined') state = initialState; + switch (action.type) { + case BLOCK_DRAG_UPDATE: + return action.areBlocksOverGui; + default: + return state; + } +}; + +const updateBlockDrag = function (areBlocksOverGui) { + return { + type: BLOCK_DRAG_UPDATE, + areBlocksOverGui: areBlocksOverGui, + meta: { + throttle: 30 + } + }; +}; + +export { + reducer as default, + updateBlockDrag +}; diff --git a/src/reducers/gui.js b/src/reducers/gui.js index d49144b4f059e66bb16a4b374783c2724ce73d34..92cdab550dc53fac3fbd702cf373fac138762aa6 100644 --- a/src/reducers/gui.js +++ b/src/reducers/gui.js @@ -1,6 +1,7 @@ import {combineReducers} from 'redux'; import colorPickerReducer from './color-picker'; import customProceduresReducer from './custom-procedures'; +import blockDragReducer from './blockdrag'; import intlReducer from './intl'; import modalReducer from './modals'; import monitorReducer from './monitors'; @@ -12,6 +13,7 @@ import stageSizeReducer from './stage-size'; import {ScratchPaintReducer} from 'scratch-paint'; export default combineReducers({ + blockdrag: blockDragReducer, colorPicker: colorPickerReducer, customProcedures: customProceduresReducer, intl: intlReducer,