Skip to content
Snippets Groups Projects
Commit c4f5820b authored by Paul Kaplan's avatar Paul Kaplan
Browse files

Generalize drop area code into HOC to get sharing assets to stage

parent f9b58453
No related branches found
No related tags found
No related merge requests found
......@@ -17,7 +17,7 @@ const dragTypeMap = {
sprite: DragConstants.BACKPACK_SPRITE
};
const Backpack = ({contents, dragOver, dropAreaRef, error, expanded, loading, onToggle, onDelete}) => (
const Backpack = ({containerRef, contents, dragOver, error, expanded, loading, onToggle, onDelete}) => (
<div className={styles.backpackContainer}>
<div
className={styles.backpackHeader}
......@@ -45,7 +45,7 @@ const Backpack = ({contents, dragOver, dropAreaRef, error, expanded, loading, on
{expanded ? (
<div
className={styles.backpackList}
ref={dropAreaRef}
ref={containerRef}
>
{error ? (
<div className={styles.statusMessage}>
......@@ -104,6 +104,7 @@ const Backpack = ({contents, dragOver, dropAreaRef, error, expanded, loading, on
);
Backpack.propTypes = {
containerRef: PropTypes.func,
contents: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
thumbnailUrl: PropTypes.string,
......@@ -111,7 +112,6 @@ Backpack.propTypes = {
name: PropTypes.string
})),
dragOver: PropTypes.bool,
dropAreaRef: PropTypes.func,
error: PropTypes.bool,
expanded: PropTypes.bool,
loading: PropTypes.bool,
......
......@@ -40,6 +40,8 @@ const messages = defineMessages({
const StageSelector = props => {
const {
backdropCount,
containerRef,
dragOver,
fileInputRef,
intl,
selected,
......@@ -60,9 +62,10 @@ const StageSelector = props => {
<Box
className={classNames(styles.stageSelector, {
[styles.isSelected]: selected,
[styles.raised]: raised,
[styles.raised]: raised || dragOver,
[styles.receivedBlocks]: receivedBlocks
})}
componentRef={containerRef}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
......@@ -128,6 +131,8 @@ const StageSelector = props => {
StageSelector.propTypes = {
backdropCount: PropTypes.number.isRequired,
containerRef: PropTypes.func,
dragOver: PropTypes.bool,
fileInputRef: PropTypes.func,
intl: intlShape.isRequired,
onBackdropFileUpload: PropTypes.func,
......
......@@ -11,11 +11,15 @@ import {
spritePayload
} from '../lib/backpack-api';
import DragConstants from '../lib/drag-constants';
import DropAreaHOC from '../lib/drop-area-hoc.jsx';
import {connect} from 'react-redux';
import storage from '../lib/storage';
import VM from 'scratch-vm';
const dragTypes = [DragConstants.COSTUME, DragConstants.SOUND, DragConstants.SPRITE];
const DroppableBackpack = DropAreaHOC(dragTypes)(BackpackComponent);
class Backpack extends React.Component {
constructor (props) {
super(props);
......@@ -23,8 +27,7 @@ class Backpack extends React.Component {
'handleDrop',
'handleToggle',
'handleDelete',
'refreshContents',
'setRef'
'refreshContents'
]);
this.state = {
dragOver: false,
......@@ -46,29 +49,6 @@ class Backpack extends React.Component {
storage._hasAddedBackpackSource = true;
}
}
componentWillReceiveProps (newProps) {
const dragTypes = [DragConstants.COSTUME, DragConstants.SOUND, DragConstants.SPRITE];
// If `dragging` becomes true, record the drop area rectangle
if (newProps.dragInfo.dragging && !this.props.dragInfo.dragging) {
this.dropAreaRect = this.ref && this.ref.getBoundingClientRect();
// If `dragging` becomes false, call the drop handler
} else if (!newProps.dragInfo.dragging && this.props.dragInfo.dragging && this.state.dragOver) {
this.handleDrop(this.props.dragInfo);
this.setState({dragOver: false});
}
// If a drag is in progress (currentOffset) and it matches the relevant drag types,
// test if the drag is within the drop area rect and set the state accordingly.
if (this.dropAreaRect && newProps.dragInfo.currentOffset && dragTypes.includes(newProps.dragInfo.dragType)) {
const {x, y} = newProps.dragInfo.currentOffset;
const {top, right, bottom, left} = this.dropAreaRect;
if (x > left && x < right && y > top && y < bottom) {
this.setState({dragOver: true});
} else {
this.setState({dragOver: false});
}
}
}
handleToggle () {
const newState = !this.state.expanded;
this.setState({expanded: newState, offset: 0});
......@@ -126,19 +106,15 @@ class Backpack extends React.Component {
});
}
}
setRef (ref) {
this.ref = ref;
}
render () {
return (
<BackpackComponent
<DroppableBackpack
contents={this.state.contents}
dragOver={this.state.dragOver}
dropAreaRef={this.setRef}
error={this.state.error}
expanded={this.state.expanded}
loading={this.state.loading}
onDelete={this.handleDelete}
onDrop={this.handleDrop}
onToggle={this.props.host ? this.handleToggle : null}
/>
);
......@@ -146,15 +122,6 @@ class Backpack extends React.Component {
}
Backpack.propTypes = {
dragInfo: PropTypes.shape({
currentOffset: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number
}),
dragType: PropTypes.string,
dragging: PropTypes.bool,
index: PropTypes.number
}),
host: PropTypes.string,
token: PropTypes.string,
username: PropTypes.string,
......@@ -181,7 +148,6 @@ const getTokenAndUsername = state => {
const mapStateToProps = state => Object.assign(
{
dragInfo: state.scratchGui.assetDrag,
vm: state.scratchGui.vm
},
getTokenAndUsername(state)
......
......@@ -7,6 +7,8 @@ import {connect} from 'react-redux';
import {openBackdropLibrary} from '../reducers/modals';
import {activateTab, COSTUMES_TAB_INDEX} from '../reducers/editor-tab';
import {setHoveredSprite} from '../reducers/hovered-target';
import DragConstants from '../lib/drag-constants';
import DropAreaHOC from '../lib/drop-area-hoc.jsx';
import StageSelectorComponent from '../components/stage-selector/stage-selector.jsx';
......@@ -14,6 +16,9 @@ import backdropLibraryContent from '../lib/libraries/backdrops.json';
import costumeLibraryContent from '../lib/libraries/costumes.json';
import {handleFileUpload, costumeUpload} from '../lib/file-uploader.js';
const dragTypes = [DragConstants.COSTUME, DragConstants.SOUND];
const DroppableStage = DropAreaHOC(dragTypes)(StageSelectorComponent);
class StageSelector extends React.Component {
constructor (props) {
super(props);
......@@ -27,6 +32,7 @@ class StageSelector extends React.Component {
'handleBackdropUpload',
'handleMouseEnter',
'handleMouseLeave',
'handleDrop',
'setFileInput'
]);
}
......@@ -75,6 +81,13 @@ class StageSelector extends React.Component {
handleMouseLeave () {
this.props.dispatchSetHoveredSprite(null);
}
handleDrop (dragInfo) {
if (dragInfo.dragType === DragConstants.COSTUME) {
this.props.vm.shareCostumeToTarget(dragInfo.index, this.props.id);
} else if (dragInfo.dragType === DragConstants.SOUND) {
this.props.vm.shareSoundToTarget(dragInfo.index, this.props.id);
}
}
setFileInput (input) {
this.fileInput = input;
}
......@@ -82,16 +95,16 @@ class StageSelector extends React.Component {
const componentProps = omit(this.props, [
'assetId', 'dispatchSetHoveredSprite', 'id', 'onActivateTab', 'onSelect']);
return (
<StageSelectorComponent
<DroppableStage
fileInputRef={this.setFileInput}
onBackdropFileUpload={this.handleBackdropUpload}
onBackdropFileUploadClick={this.handleFileUploadClick}
onClick={this.handleClick}
onDrop={this.handleDrop}
onEmptyBackdropClick={this.handleEmptyBackdrop}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onSurpriseBackdropClick={this.handleSurpriseBackdrop}
{...componentProps}
/>
);
......
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import omit from 'lodash.omit';
import {connect} from 'react-redux';
const DropAreaHOC = function (dragTypes) {
return function (WrappedComponent) {
class DropAreaWrapper extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'setRef'
]);
this.state = {
dragOver: false
};
this.ref = null;
this.containerBox = null;
}
componentWillReceiveProps (newProps) {
// If `dragging` becomes true, record the drop area rectangle
if (newProps.dragInfo.dragging && !this.props.dragInfo.dragging) {
this.dropAreaRect = this.ref && this.ref.getBoundingClientRect();
// If `dragging` becomes false, call the drop handler
} else if (!newProps.dragInfo.dragging && this.props.dragInfo.dragging && this.state.dragOver) {
this.props.onDrop(this.props.dragInfo);
this.setState({dragOver: false});
}
// If a drag is in progress (currentOffset) and it matches the relevant drag types,
// test if the drag is within the drop area rect and set the state accordingly.
if (this.dropAreaRect && newProps.dragInfo.currentOffset &&
dragTypes.includes(newProps.dragInfo.dragType)) {
const {x, y} = newProps.dragInfo.currentOffset;
const {top, right, bottom, left} = this.dropAreaRect;
if (x > left && x < right && y > top && y < bottom) {
this.setState({dragOver: true});
} else {
this.setState({dragOver: false});
}
}
}
setRef (el) {
this.ref = el;
}
render () {
const componentProps = omit(this.props, ['onDrop', 'dragInfo']);
return (
<WrappedComponent
containerRef={this.setRef}
dragOver={this.state.dragOver}
{...componentProps}
/>
);
}
}
DropAreaWrapper.propTypes = {
dragInfo: PropTypes.shape({
currentOffset: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number
}),
dragType: PropTypes.string,
dragging: PropTypes.bool,
index: PropTypes.number
}),
onDrop: PropTypes.func
};
const mapStateToProps = state => ({
dragInfo: state.scratchGui.assetDrag
});
const mapDispatchToProps = () => ({});
return connect(
mapStateToProps,
mapDispatchToProps
)(DropAreaWrapper);
};
};
export default DropAreaHOC;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment