Dropping Over a Component inside nested Drop Targets giving error - reactjs

Here is my Container Class
Code on Sandbox
`import React, { Component } from "react";
import { DropTarget } from "react-dnd";
import Box from "./Box";
class Container extends Component {
state = {
Boxes: [
{ left: 60, top: 30, text: "ITEM_1" },
{ left: 100, top: 70, text: "ITEM_2" }
]
};
render() {
const { connectDropTarget } = this.props;
return connectDropTarget(
<div
className="container"
style={{ width: "100%", height: "100vh", background: "yellow" }}
>
{this.state.Boxes.map((box, index) => {
return (
<Box
id={index}
key={index}
left={box.left}
top={box.top}
text={box.text}
moveBox={this.moveBox}
/>
);
})}
</div>
);
}
moveBox(id, left, top) {
const allBoxes = this.state.Boxes;
const singleBox = this.state.Boxes[id];
singleBox.left = left;
singleBox.top = top;
const newBox = allBoxes.filter((box, index) => index !== id);
newBox.push(singleBox);
this.setState({ Boxes: newBox });
}
}
export default DropTarget(
"items",
{
// Spec Object Started
drop(props, monitor, component) {
const item = monitor.getItem();
const delta = monitor.getDifferenceFromInitialOffset();
const left = Math.round(item.left + delta.x);
const top = Math.round(item.top + delta.y);
component.moveBox(item.id, left, top);
}
}, //Spec Oject Ended Here
(connect, monitor) => ({
connectDropTarget: connect.dropTarget()
})
)(Container);
`
And Here is my Box Class
import React, { Component } from "react";
import { DragSource, DropTarget } from "react-dnd";
import flow from "lodash/flow";
let whichDragging = "items";
class Box extends Component {
state = {};
render() {
const { left, top, text, connectDragSouce, connectDropTarget } = this.props;
return connectDragSouce(
connectDropTarget(
<div
style={{
width: "20%",
border: "2px dotted black",
margin: "auto",
position: "relative",
top: top,
left: left
}}
>
{text}
</div>
)
);
}
}
export default flow(
DragSource(
whichDragging,
{
beginDrag(props, monitor, component) {
console.log(component);
const { left, top, text, id } = props;
return {
left,
top,
text,
id
};
}
},
(connect, monitor) => ({
connectDragSouce: connect.dragSource()
})
),
DropTarget(
whichDragging,
{
drop(props, monitor, component) {
whichDragging = "nested";
const item = monitor.getItem();
const delta = monitor.getDifferenceFromInitialOffset();
const left = Math.round(item.left + delta.x);
const top = Math.round(item.top + delta.y);
console.log("Logging");
console.log(component);
// whichDragging = "items";
}
},
(connect, monitor) => ({
connectDropTarget: connect.dropTarget()
})
)
)(Box);
Simple Dragging Dropping Working fine but when i drop item_1 over item_2 or vice versa i got error and my Component in drop shows DragDropContainer in console.log i want to get the id|key of component over which one component is dropped and not able to find any solution since 2 days any help will be appriciated.

Related

How to Convert a Class Component to a Functional Component in React?

I had never used React Class Components And Want to shoot confetti when some event happened. I'm confused with Class Component. Hope You can help with this issue. tried by creating arrow functions and removing this keyword .But I can't understand how to transform getInstance() function even why it is there?
import React, { Component } from 'react';
import ReactCanvasConfetti from 'react-canvas-confetti';
export default class Confetti extends Component {
getInstance = (instance) => {
// saving the instance to an internal property
this.confetti = instance;
}
onClickDefault = () => {
// starting the animation
this.confetti();
}
onClickCustom = () => {
// starting the animation with custom settings
this.confetti({ particleCount: Math.ceil(Math.random() * 1000), spread: 180 });
}
onClickCallback = () => {
// calling console.log after the animation ends
this.confetti().then(() => {
console.log('do something after animation');
});
}
onClickReset = () => {
// cleaning the canvas
this.confetti.reset();
}
render() {
const style = {
position: 'fixed',
width: '100%',
height: '100%',
zIndex: -1
};
const stylediv = {
position: 'fixed',
width: '100%',
height: '100%',
zIndex: 300000
};
return (
<>
<div style={stylediv}>
<ReactCanvasConfetti
// set the styles as for a usual react component
style={style}
// set the class name as for a usual react component
className={'yourClassName'}
// set the callback for getting instance. The callback will be called after initialization ReactCanvasConfetti component
refConfetti={this.getInstance}
/>
<button onClick={this.onClickDefault}>Fire with default</button>
<button onClick={this.onClickCustom}>Fire with custom</button>
<button onClick={this.onClickCallback}>Fire with callback</button>
<button onClick={this.onClickReset}>Reset</button>
</div>
</>
);
}
}
I'm trying to create Functional Component of the above Class Component
import React, { useRef } from 'react';
import ReactCanvasConfetti from 'react-canvas-confetti';
const Confetti = () => {
const Ref = useRef()
const getInstance = (instance) => {
if (Ref.current) {
Ref.current.confetti = instance
}
}
const onClickDefault = () => {
Ref.current.confetti();
}
const onClickCustom = () => {
Ref.current.confetti({ particleCount: Math.ceil(Math.random() * 1000),
spread: 180 });
}
const onClickCallback = () => {
Ref.current.confetti().then(() => {
console.log('do something after animation');
});
}
const onClickReset = () => {
Ref.current.confetti.reset();
}
const style = {
position: 'fixed',
width: '100%',
height: '100%',
zIndex: -1
};
const stylediv = {
position: 'fixed',
width: '100%',
height: '100%',
zIndex: 300000
};
return (
<>
<div ref={Ref} style={stylediv}>
<ReactCanvasConfetti
style={style}
className={'yourClassName'}
refConfetti={getInstance}
/>
<button onClick={onClickDefault}>Fire with default</button>
<button onClick={onClickCustom}>Fire with custom</button>
<button onClick={onClickCallback}>Fire with callback</button>
<button onClick={onClickReset}>Reset</button>
</div>
</>
);
}
export default Confetti

How to rerender value of child from HOC

I want to have a button submiting to stripe a specific quantity, based on an input that can be changed by typing or clicking an increment/decrease button. I have the increment/decrease functions on an higher order component but i am unable to submit it to the button of stripe (child from my understanding)
Relevant code below:
withCheckout (HOC):
import React from "react"
const UpdatedComponent = (OriginalComponent) => {
class NewComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 1
}
this.handleChange = this.handleChange.bind(this)
}
increment = () => {
this.setState(prevState => {
return { count: prevState.count + 1}
})
}
decrease = () => {
this.setState(prevState => {
return { count: prevState.count - 1 }
})
}
handleChange(event) {
this.setState({
count: parseInt(event.target.value)
});
}
render() {
return <OriginalComponent count={this.state.count} increment={this.increment} decrease={this.decrease} handleChange={this.handleChange} />
}
}
return NewComponent
}
export default UpdatedComponent
Checkout:
import React, { useState } from "react"
import { loadStripe } from "#stripe/stripe-js"
import UpdatedComponent from "./withCheckout"
const buttonStyles = {
fontSize: "13px",
textAlign: "center",
color: "#000",
padding: "12px 60px",
boxShadow: "2px 5px 10px rgba(0,0,0,.1)",
backgroundColor: "rgb(255, 178, 56)",
borderRadius: "6px",
letterSpacing: "0.2ch",
display: "block",
margin: "1.5em auto 1.5em auto",
}
const buttonDisabledStyles = {
opacity: "0.5",
cursor: "not-allowed",
}
let stripePromise
const getStripe = () => {
if (!stripePromise) {
stripePromise = loadStripe("KEY_HERE")
}
return stripePromise
}
const Checkout = props => {
const [loading, setLoading] = useState(false)
const redirectToCheckout = async event => {
event.preventDefault()
setLoading(true)
const stripe = await getStripe()
const { error } = await stripe.redirectToCheckout({
mode: "payment",
lineItems: [{ price: "PRICE_ID_HERE", quantity: props.count }],
successUrl: `http://localhost:8000/page-2/`,
cancelUrl: `http://localhost:8000/`,
shippingAddressCollection: {
allowedCountries: ['PT'],
}
})
if (error) {
console.warn("Error:", error)
setLoading(false)
}
}
return (
<button
disabled={loading}
style={
loading ? { ...buttonStyles, ...buttonDisabledStyles } : buttonStyles
}
onClick={redirectToCheckout}
>
Comprar {props.count}
</button>
)
}
export default UpdatedComponent(Checkout)
Example:
See this image
When I change the input to 7, I expect the button text to be "Comprar 7" and I expect to submit quantity: 7 in the striperedirect function. I think the problem has to do with the way props are set, as my Counter is working well the props were passed as const {count} = this.props. Should I add the Counter.js code as well?

Move a square using the coordinate system in reactjs

I need to move this square using _dragTask() function, and i also need to do to it by using the coordinate system. Anyone can do that?
import React from 'react';
import './App.css';
class App extends React.Component {
componentDidMount() {
}
constructor(props) {
super(props);
this.state = {
MainContainer: {backgroundColor: "#282c34", display: "flex", minHeight: "100vh"},
ObjectMap: [],
}
this._createObject = this._createObject.bind(this);
this._createBox = this._createBox.bind(this);
this._buttonCreateBox1 = this._buttonCreateBox1.bind(this);
this._dragTask = this._dragTask.bind(this);
}
_createObject = (object) => {
var ObjectMap = this.state.ObjectMap;
ObjectMap.push(object);
this.setState({ObjectMap: ObjectMap});
}
_createBox = (style) => {
var object = {position: "absolute", top: this.state.positionX, left: this.state.positionY};
var styleObject = Object.assign({},
object, style
)
return (
<div style={styleObject} draggable="true" onDragEnd={(event) => {this._dragTask(event)}}>
</div>
);
}
_dragTask(event) {
event.persist();
this.setState({positionX: event.screenX, positionY: event.screenY}, () => { console.log("Page X:"+ this.state.positionX + " Page Y:" + this.state.positionY); });
}
_buttonCreateBox1 = () => {
this._createObject(this._createBox({backgroundColor: "white", width: "300px", height: "300px"}));
}
render() {
return (
<div style={this.state.MainContainer}>
<button type="button" onClick={ this._buttonCreateBox1 }>Click Me!</button>
{
this.state.ObjectMap.map((item, index) => {
return item
})
}
</div>
);
}
}
export default App;
Right now, the object it created by printed in the screen by the mainloop, and added to it by _createObject() function.
just change your _dragTask method to this
_dragTask(event) {
event.target.style.top = event.clientY + "px";
event.target.style.left = event.clientX + "px";
}
and change the style of the wrapper to have position: relative
constructor(props) {
super(props);
this.state = {
MainContainer: {
backgroundColor: "#282c34",
position: "relative",
display: "flex",
minHeight: "100vh"
},
ObjectMap: []
};
...
checkout this sandbox to see full example

I am trying to implement a way to delete swappable items, but my delete method removes all the items instead of just the one that is clicked

I have two components, App.js which holds my "widget" layout and SwappableComponent.js which creates each swappable widget. I am trying to implement a delete function but when I click on the delete button what happens is it deletes all the swappable components instead of just the one that is clicked on. Any help would be appreciated.
import React, { Component } from 'react';
import Swappable from './components/SwappableComponent'
import './App.css';
import DataTable from './components/tableWidget';
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import Paper from "#material-ui/core/Paper";
import Grid from "#material-ui/core/Grid";
const styles = theme => ({
root: {
flexGrow: 1
},
paper: {
padding: theme.spacing.unit * 2,
textAlign: "center",
color: theme.palette.text.secondary
}
});
class App extends Component {
constructor(props) {
super(props);
this.state={
widgets:[
{id:1, content: <DataTable/>},
{id:2, content: "#2"},
{id:3, content: "#3"},
{id:4, content: "#4"}
]
}
}
deleteEvent=(index)=>{
const copyWidgets=Object.assign([],this.state.widgets);
copyWidgets.splice(index);
this.setState({
widgets:copyWidgets
})
}
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<Grid container spacing={24}>
{
this.state.widgets.map((widget,index)=>{
return(
<Grid item xs={12} sm={6}>
<Paper className={classes.paper}><Swappable id={widget.id} content={widget.content} delete={this.deleteEvent.bind(this,index)}/></Paper>
</Grid>
)
})
}
</Grid>
</div>
);
}
}
App.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(App);
import React, { Component } from 'react'
class Swappable extends Component {
constructor() {
super()
this.state = {
customFunc: null
}
}
allowDrop(ev) {
ev.preventDefault();
}
drag(ev, customFunc = null) {
ev.dataTransfer.setData("src", ev.target.id);
console.log(ev.target.parentNode, 'TARGET DRAGSTART')
this.setState({
initialParentNode: ev.target.parentNode
})
}
dragEnd(ev, customFunc = null) {
console.log(ev.target.parentNode, 'TARGET DRAGEND')
if (customFunc && (ev.target.parentNode != this.state.initialParentNode)) {
console.log('custom func')
this.props.customFunc()
}
}
drop(ev, dragableId, dropzoneId, customFunc = null, swappable = true) {
ev.preventDefault();
let src = document.getElementById(ev.dataTransfer.getData("src"));
let srcParent = src.parentNode;
let target = document.getElementById(dragableId);
console.log(src, 'dragged element');
console.log(srcParent, 'parent of dragged');
console.log(target, 'element to be swapped')
swappable ? this.swapElements(src, target, srcParent) : this.transferElement(src, dropzoneId)
}
swapElements(src, target, srcParent) {
target.replaceWith(src);
srcParent.appendChild(target);
}
transferElement(src, dropzoneId) {
let dropzone = document.getElementById(dropzoneId)
dropzone.appendChild(src);
}
render() {
const dropZoneStyle = {
width: '450px',
minHeight: '300px',
padding: '10px',
border: ''
};
const draggableStyle = {
width: '400px',
height: '300px',
padding: '10px',
border: ''
};
const { id, content, swappable, customFunc } = this.props
const dropzoneId = 'drop' + id
const dragableId = 'drag' + id
console.log(customFunc, 'customFunc')
return (
<div
id = {dropzoneId}
onDrop={(event) => this.drop(event, dragableId, dropzoneId, customFunc, swappable)}
onDragOver={(event) => this.allowDrop(event)}
style={dropZoneStyle}>
<div id={ dragableId }
draggable="true"
onDragStart={(event) => this.drag(event)}
onDragEnd = {(event) => this.dragEnd(event, customFunc)}
style={draggableStyle}>
{ content }
<button onClick={this.props.delete}>Delete</button>
</div>
</div>
)
}
}
export default Swappable;
The reason why all of the items are removed is that you didn't specify a second argument to the .splice function which is the number of items to delete from the array or the deleteCount.
From MDN docs:
If deleteCount is omitted, or if its value is equal to or larger than
array.length - start (that is, if it is equal to or greater than the
number of elements left in the array, starting at start), then all of
the elements from start through the end of the array will be deleted.
To fix it, modify your deleteEvent function to the following:
deleteEvent = (index) => {
const copyWidgets = Object.assign([], this.state.widgets);
copyWidgets.splice(index, 1); // delete one item only
this.setState({
widgets: copyWidgets
});
};
A simple example to show the different behaviour:
console.log('without using second argument with splice');
const letters = ['a', 'b', 'c'];
console.log('before', letters);
letters.splice(0);
console.log('after', letters);
console.log('using second argument with splice');
const numbers = [1, 2, 3];
console.log('before', numbers);
numbers.splice(1, 1);
console.log('after splice', numbers);

react-dnd uncaught typerrors. Trying to follow the simple sortable example

I've been trying to work off of the simple sortable example in the react-dnd examples but I am having trouble trying to convert the es7 code to es6. I've tried using babel but I don't really understand the code that it spits out.
Here is my code that I've tried to translate from es7 to es6:
import React, {PropTypes} from 'react';
import Router from 'react-router';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragSource, DropTarget } from 'react-dnd';
const style= {
border: '1px dashed gray',
padding: '0.5rem 1rem',
marginBottom: '.5rem',
backgroundColor: 'white',
cursor: 'move'
}
const ItemTypes = {
Coursepage: 'coursepage'
};
const coursePageSource = {
beginDrag(props) {
return {
id: props.id,
index: props.index
}
}
}
const coursePageTarget = {
hover(props, monitor, component){
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
//don't replace items with themselves
if(dragIndex === hoverIndex){
return;
}
//Determine rectangle on screen
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
//get vertical middle
const hoverMiddleY = (hoverBoundingRect.Bottom - hoverBoundingRect.Top) /2;
//get top pixels
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
//only perform the move when the mouse has crossed half of the items height
//when dragging downwards, only move when the cursor is below 50%
//when dragging upwards, only move when the cursor is above 50%
//dragging downwards
if(dragIndex < hoverIndex && hoverClientY < hoverMiddleY){
return;
}
//dragging upwards
if(dragIndex > hoverIndex && hoverClientY > hoverMiddleY){
return;
}
//time to actually perform the action
props.moveObject(dragIndex, hoverIndex);
}
}
// const propTypes = {
// connectDragSource: PropTypes.func.isRequired,
// connectDropTarget: PropTypes.func.isRequired,
// index: PropTypes.number.isRequired,
// isDragging: PropTypes.bool.isRequired,
// id: PropTypes.any.isRequired,
// text: PropTypes.string.isRequired,
// moveCard: PropTypes.func.isRequired
// };
function collectDropTarget(connect) {
return {
connectDropTarget: connect.dropTarget(),
};
}
/**
* Specifies which props to inject into your component.
*/
function collectDragSource(connect, monitor) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDragSource: connect.dragSource(),
// You can ask the monitor about the current drag state:
isDragging: monitor.isDragging()
};
}
class Coursepage extends React.Component{
render(){
console.log(this.props);
const {text, isDragging, connectDragSource, connectDropTarget} = this.props;
const opacity = isDragging ? 0 : 1;
return connectDragSource(connectDropTarget(
<div style={{opacity}}>
{text}
</div>
));
}
}
// Coursepage.propTypes = propTypes;
export default DragSource(ItemTypes.Coursepage, coursePageSource, collectDragSource)(Coursepage);
export default DropTarget(ItemTypes.Coursepage, coursePageTarget, collectDropTarget)(Coursepage);
Now the error I'm getting from this is
"Uncaught TypeError: connectDropTarget is not a function."
I console logged this.props in render and I see that connectDragSource is showing up in the this.props object but not connectDropTarget.
Can anyone tell me what I'm missing?
By the way, this is the example code I was using:
https://github.com/gaearon/react-dnd/blob/master/examples/04%20Sortable/Simple/Card.js
I know this is a little old but I landed up here through google so I figured I would give it a go. First of all, you can't have two default exports as referenced here in section 3.2 http://www.2ality.com/2014/09/es6-modules-final.html
Instead you need to pass the result of one of your current default exports into the second function call - you'll see below.
This took me a couple of hours to get working as I'm also an Es6/7 newbie - so I invite any criticism!
// Container.js;
import React, { Component } from 'react';
import update from 'react/lib/update';
import Card from './Card';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
const style = {
width: 400
};
class Container extends Component {
constructor(props) {
super(props);
this.moveCard = this.moveCard.bind(this);
this.findCard = this.findCard.bind(this);
this.state = {
cards: [{
id: 1,
text: 'Write a cool JS library'
}, {
id: 2,
text: 'Make it generic enough'
}, {
id: 3,
text: 'Write README'
}, {
id: 4,
text: 'Create some examples'
}, {
id: 5,
text: 'Spam in Twitter and IRC to promote it (note that this element is taller than the others)'
}, {
id: 6,
text: '???'
}, {
id: 7,
text: 'PROFIT'
}]
};
}
findCard(id) {
const { cards } = this.state;
const card = cards.filter(c => c.id === id)[0];
return {
card,
index: cards.indexOf(card)
};
}
moveCard(id, atIndex) {
const { card, index } = this.findCard(id);
this.setState(update(this.state, {
cards: {
$splice: [
[index, 1],
[atIndex, 0, card]
]
}
}));
}
render() {
const { cards } = this.state;
return (
<div style={style}>
{cards.map((card, i) => {
return (
<Card key={card.id}
index={i}
id={card.id}
text={card.text}
moveCard={this.moveCard}
findCard={this.findCard} />
);
})}
</div>
);
}
}
export default DragDropContext(HTML5Backend)(Container)
Then Card.js
// Card.js
import React, { Component, PropTypes } from 'react';
import ItemTypes from './ItemTypes';
import { DragSource, DropTarget } from 'react-dnd';
const style = {
border: '1px dashed gray',
padding: '0.5rem 1rem',
marginBottom: '.5rem',
backgroundColor: 'white',
cursor: 'move'
};
const cardSource = {
beginDrag(props) {
return {
id: props.id,
originalIndex: props.findCard(props.id).index
};
},
endDrag(props, monitor) {
const { id: droppedId, originalIndex } = monitor.getItem();
const didDrop = monitor.didDrop();
if (!didDrop) {
props.moveCard(droppedId, originalIndex);
}
}
};
const cardTarget = {
canDrop() {
return false;
},
hover(props, monitor) {
const { id: draggedId } = monitor.getItem();
const { id: overId } = props;
if (draggedId !== overId) {
const { index: overIndex } = props.findCard(overId);
props.moveCard(draggedId, overIndex);
}
}
};
function collect(connect, monitor) {
console.log( "HERE2", connect );
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop()
};
}
function collect2(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging()
};
}
class Card extends Component {
render() {
const { text, isDragging, connectDragSource, connectDropTarget } = this.props;
const opacity = isDragging ? 0 : 1;
return connectDragSource(connectDropTarget(
<div >
{text}
</div>
));
}
}
Card.propTypes = {
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
isDragging: PropTypes.bool.isRequired,
id: PropTypes.any.isRequired,
text: PropTypes.string.isRequired,
moveCard: PropTypes.func.isRequired,
findCard: PropTypes.func.isRequired
};
const x = DropTarget(ItemTypes.CARD, cardTarget, collect )(Card)
export default DragSource(ItemTypes.CARD, cardSource, collect2 )( x )
And then the types include
// ItemTypes.js
export default {
CARD: 'card'
};

Resources