From c973bf0e1a30b39915fa1e48c6cbef1e84566f89 Mon Sep 17 00:00:00 2001 From: Paul Kaplan <pkaplan@media.mit.edu> Date: Wed, 31 Oct 2018 10:17:10 -0400 Subject: [PATCH] Make the blocks workspace a drop area for BACKPACK_CODE drag types --- src/components/backpack/backpack.jsx | 4 ++-- src/components/blocks/blocks.css | 16 ++++++++++++++ src/components/blocks/blocks.jsx | 13 ++++++++---- src/containers/blocks.jsx | 22 +++++++++++++++++--- src/lib/drag-constants.js | 1 + src/lib/drop-area-hoc.jsx | 31 ++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/components/backpack/backpack.jsx b/src/components/backpack/backpack.jsx index 04cdc2937..9e9a4f75c 100644 --- a/src/components/backpack/backpack.jsx +++ b/src/components/backpack/backpack.jsx @@ -10,10 +10,10 @@ import styles from './backpack.css'; // TODO make sprite selector item not require onClick const noop = () => {}; -const dragTypeMap = { +const dragTypeMap = { // Keys correspond with the backpack-server item types costume: DragConstants.BACKPACK_COSTUME, sound: DragConstants.BACKPACK_SOUND, - code: DragConstants.BACKPACK_CODE, + script: DragConstants.BACKPACK_CODE, sprite: DragConstants.BACKPACK_SPRITE }; diff --git a/src/components/blocks/blocks.css b/src/components/blocks/blocks.css index 3d8740589..e0b07ce26 100644 --- a/src/components/blocks/blocks.css +++ b/src/components/blocks/blocks.css @@ -1,6 +1,22 @@ @import "../../css/units.css"; @import "../../css/colors.css"; +.blocks { + height: 100%; +} + +.drag-over:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.75; + background-color: #8cbcff; + transition: all 0.25s ease; +} + .blocks :global(.injectionDiv){ position: absolute; top: 0; diff --git a/src/components/blocks/blocks.jsx b/src/components/blocks/blocks.jsx index 683da23c1..a812b039f 100644 --- a/src/components/blocks/blocks.jsx +++ b/src/components/blocks/blocks.jsx @@ -1,22 +1,27 @@ import PropTypes from 'prop-types'; +import classNames from 'classnames'; import React from 'react'; import Box from '../box/box.jsx'; import styles from './blocks.css'; const BlocksComponent = props => { const { - componentRef, + containerRef, + dragOver, ...componentProps } = props; return ( <Box - className={styles.blocks} - componentRef={componentRef} + className={classNames(styles.blocks, { + [styles.dragOver]: dragOver + })} {...componentProps} + componentRef={containerRef} /> ); }; BlocksComponent.propTypes = { - componentRef: PropTypes.func + containerRef: PropTypes.func, + dragOver: PropTypes.bool }; export default BlocksComponent; diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 73a265be9..89e926d3d 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -16,6 +16,8 @@ import extensionData from '../lib/libraries/extensions/index.jsx'; import CustomProcedures from './custom-procedures.jsx'; import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants'; +import DropAreaHOC from '../lib/drop-area-hoc.jsx'; +import DragConstants from '../lib/drag-constants'; import {connect} from 'react-redux'; import {updateToolbox} from '../reducers/toolbox'; @@ -38,6 +40,10 @@ const addFunctionListener = (object, property, callback) => { }; }; +const DroppableBlocks = DropAreaHOC([ + DragConstants.BACKPACK_CODE +])(BlocksComponent); + class Blocks extends React.Component { constructor (props) { super(props); @@ -47,6 +53,7 @@ class Blocks extends React.Component { 'detachVM', 'handleCategorySelected', 'handleConnectionModalStart', + 'handleDrop', 'handleStatusButtonUpdate', 'handleOpenSoundRecorder', 'handlePromptStart', @@ -404,6 +411,14 @@ class Blocks extends React.Component { ws.refreshToolboxSelection_(); ws.toolbox_.scrollToCategoryById('myBlocks'); } + handleDrop (dragInfo) { + fetch(dragInfo.payload.bodyUrl) + .then(response => response.json()) + .then(blocks => { + this.props.vm.shareBlocksToTarget(blocks, this.props.vm.editingTarget.id); + this.props.vm.refreshWorkspace(); + }); + } render () { /* eslint-disable no-unused-vars */ const { @@ -427,9 +442,10 @@ class Blocks extends React.Component { } = this.props; /* eslint-enable no-unused-vars */ return ( - <div> - <BlocksComponent + <React.Fragment> + <DroppableBlocks componentRef={this.setBlocks} + onDrop={this.handleDrop} {...props} /> {this.state.prompt ? ( @@ -458,7 +474,7 @@ class Blocks extends React.Component { onRequestClose={this.handleCustomProceduresClose} /> ) : null} - </div> + </React.Fragment> ); } } diff --git a/src/lib/drag-constants.js b/src/lib/drag-constants.js index ce4da5af2..2fe13b0b0 100644 --- a/src/lib/drag-constants.js +++ b/src/lib/drag-constants.js @@ -2,6 +2,7 @@ export default { SOUND: 'SOUND', COSTUME: 'COSTUME', SPRITE: 'SPRITE', + CODE: 'CODE', BACKPACK_SOUND: 'BACKPACK_SOUND', BACKPACK_COSTUME: 'BACKPACK_COSTUME', diff --git a/src/lib/drop-area-hoc.jsx b/src/lib/drop-area-hoc.jsx index 9f44293e8..a5bfc6499 100644 --- a/src/lib/drop-area-hoc.jsx +++ b/src/lib/drop-area-hoc.jsx @@ -4,7 +4,34 @@ import React from 'react'; import omit from 'lodash.omit'; import {connect} from 'react-redux'; +/** + * Higher Order Component to give components the ability to react to drag overs + * and drops of objects stored in the assetDrag redux state. + * + * Example: You want to enable MyComponent to receive drops from a drag type + * Wrapped = DropAreaHOC([...dragTypes])( + * <MyComponent /> + * ) + * + * MyComponent now receives 2 new props + * containerRef: a ref that must be set on the container element + * dragOver: boolean if an asset is being dragged above the component + * + * Use the wrapped component: + * <Wrapped onDrop={yourDropHandler} /> + * + * NB: This HOC _only_ works with objects that drag using the assetDrag reducer. + * This _does not_ handle drags for blocks coming from the workspace. + * + * @param {Array.<string>} dragTypes Types to respond to, from DragConstants + * @returns {function} The HOC, specialized for those drag types + */ const DropAreaHOC = function (dragTypes) { + /** + * Return the HOC, specialized for the dragTypes + * @param {React.Component} WrappedComponent component to receive drop behaviors + * @returns {React.Component} component with drag over/drop behavior + */ return function (WrappedComponent) { class DropAreaWrapper extends React.Component { constructor (props) { @@ -46,6 +73,9 @@ const DropAreaHOC = function (dragTypes) { } setRef (el) { this.ref = el; + if (this.props.componentRef) { + this.props.componentRef(this.ref); + } } render () { const componentProps = omit(this.props, ['onDrop', 'dragInfo']); @@ -60,6 +90,7 @@ const DropAreaHOC = function (dragTypes) { } DropAreaWrapper.propTypes = { + componentRef: PropTypes.func, dragInfo: PropTypes.shape({ currentOffset: PropTypes.shape({ x: PropTypes.number, -- GitLab