** LATEST Fiddle -- http://jsfiddle.net/cfrapLma/28/
adding chart types -- is there a better way to move this forward -- would the config json be handled via redux -- what is the next step forward. Has anyone tried making a dashboard application using reactjs and d3 before?
I am working on a reactjs project and I am keen to output a set of div holders that will contain future chart parameters, like width, height, url service.
++ How do I push and pull about multiple parameters to create different instances of a chart, placeholder..?
++ Is this a good start for creating a dashboard set of components do I need to create a configuration json for what charts, sizes, services I want to absorb.
//config json?
[{
"width": 200,
"height": 200,
"type": "piechart",
"serviceApi": "api.php?mode=GetCars"
}, {
"width": 100,
"height": 100,
"type": "barchart",
"serviceApi": "api.php?mode=GetBoats"
}]
do I need to create a config json that will control the parameters - an array of charts that are needing to be rendered?
var MultipleCharts = React.createClass({
render: function() {
return (
<div>
<div className="holder1"><PieChart width={this.props.width} height={this.props.height} service={this.props.service}/></div>
<div className="holder2"><PieChart width={this.props.width} height={this.props.height} service={this.props.service}/></div>
</div>
);
}
});
^ this is a hard coded approach and I would need to loop and push through a configuration json so each chart has different properties.
<div data-role="piechart" data-width=240 data-height=240 data-service="api.php?mode=GetCars">
//Latest Fiddle
http://jsfiddle.net/cfrapLma/24/
here is the first prototype build - whether I would have reactjs handle a stack of charts -- as if this information is coming from a configuration json -- like a dashboard setup.
Or the dashboard configuration is hardcoded on the template -- and reactjs invokes the charting tool.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Charts</title>
<script src="https://npmcdn.com/react#15.3.0/dist/react.js"></script>
<script src="https://npmcdn.com/react-dom#15.3.0/dist/react-dom.js"></script>
<script src="https://npmcdn.com/babel-core#5.8.38/browser.min.js"></script>
<script src="https://npmcdn.com/jquery#3.1.0/dist/jquery.min.js"></script>
<script src="https://npmcdn.com/remarkable#1.6.2/dist/remarkable.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
var config = [{
"width": 200,
"height": 200,
"type": "piechart",
"serviceApi": "api.php?mode=GetCars"
}, {
"width": 100,
"height": 100,
"type": "barchart",
"serviceApi": "api.php?mode=GetBoats"
}];
var MultipleCharts = React.createClass({
render: function() {
return (
<div>
<div className="holder1"><PieChart width={this.props.width} height={this.props.height} service={this.props.service}/></div>
<div className="holder2"><BarChart width={this.props.width} height={this.props.height} service={this.props.service}/></div>
</div>
);
}
});
var PieChart = React.createClass({
componentDidMount: function() {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3");
// set el height and width etc.
},
render: function() {
return (
<div className="piechart" data-role="piechart" data-width={this.props.width} data-height={this.props.height} data-service={this.props.service}>pie.
</div>
);
}
});
var BarChart = React.createClass({
componentDidMount: function() {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3");
// set el height and width etc.
},
render: function() {
return (
<div className="barchart" data-role="barchart" data-width={this.props.width} data-height={this.props.height} data-service={this.props.service}>pie.
</div>
);
}
});
ReactDOM.render(
<MultipleCharts width="200" height="200" service="api.php?mode=GetCars"/>,
document.getElementById('example')
);
</script>
</body>
</html>
So, what you can do is to create fabric method that will return corresponding component based on config.type.
Then, you can iterate through all your configs in render method.
And also pass config as props to your component MultipleCharts.
var config = [{
"width": 200,
"height": 200,
"type": "piechart",
"serviceApi": "api.php?mode=GetCars"
}, {
"width": 100,
"height": 100,
"type": "barchart",
"serviceApi": "api.php?mode=GetBoats"
}];
var MultipleCharts = React.createClass({
getChart: function(config) {
switch (config.type) {
case 'piechart':
return <PieChart width={config.width} height={config.height} service={config.service} />
case 'barchart':
return <BarChart width={config.width} height={config.height} service={config.service} />
}
},
render: function () {
var config = this.props.config;
return (
<div>
{config.map((chartConfig, index) => {
return (
<div key={index} className={'holder' + index}>
{this.getChart(chartConfig)}
</div>
)
})}
</div>
);
}
});
var PieChart = React.createClass({
componentDidMount: function () {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3");
// set el height and width etc.
},
render: function () {
return (
<div className="piechart" data-role="piechart" data-width={this.props.width} data-height={this.props.height}
data-service={this.props.service}>pie.
</div>
);
}
});
var BarChart = React.createClass({
componentDidMount: function () {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3");
// set el height and width etc.
},
render: function () {
return (
<div className="barchart" data-role="barchart" data-width={this.props.width} data-height={this.props.height}
data-service={this.props.service}>pie.
</div>
);
}
});
ReactDOM.render(
<MultipleCharts config={config} />,
document.getElementById('example')
);
And please, try to avoid using data-* attributes in React components.
Based in #Sergey's answer I have added a couple of modifications to make it really dynamic. Now there is a typeMapping object that maps from strings to components. If you want to add a new component, you just need to add it there. For convenience I am using ES6, I hope that you can translate it to ES5 if you need to.
var config = [{
"type": "PieChart",
"width": 200,
"height": 200,
"serviceApi": "api.php?mode=GetCars"
}, {
"type": "BarChart",
"width": 100,
"height": 100,
"serviceApi": "api.php?mode=GetBoats"
}];
var MultipleCharts = React.createClass({
getChart: function(config) {
const { type, ...props } = config;
return React.createElement(typeMapping[type], props);
},
render: function () {
var config = this.props.config;
return (
<div>
{config.map((chartConfig, index) => {
return (
<div key={index} className={'holder' + index}>
{this.getChart(chartConfig)}
</div>
)
})}
</div>
);
}
});
var PieChart = React.createClass({
componentDidMount: function () {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3");
// set el height and width etc.
},
render: function () {
return (
<div className="piechart" data-role="piechart" data-width={this.props.width} data-height={this.props.height}
data-service={this.props.serviceApi}>pie.
</div>
);
}
});
var BarChart = React.createClass({
componentDidMount: function () {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3");
// set el height and width etc.
},
render: function () {
return (
<div className="barchart" data-role="barchart" data-width={this.props.width} data-height={this.props.height}
data-service={this.props.serviceApi}>bar.
</div>
);
}
});
// Allowed types
const typeMapping = {
PieChart, // In ES6, it is the same as "PieChart": PieChart,
BarChart,
};
ReactDOM.render(
<MultipleCharts config={config} />,
document.getElementById('example')
);
.piechart{
background: pink;
width: 100px;
height: 50px;
border: 1px solid black;
}
.barchart{
background: green;
width: 100px;
height: 50px;
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://npmcdn.com/react#15.3.1/dist/react.js"></script>
<script src="https://npmcdn.com/react-dom#15.3.1/dist/react-dom.js"></script>
<div id="example"></div>
Alright so using the create-react-app as a base..
I've tried to start cutting up the files. I get an error when I try and put the PieChart/BarChart parts into their respective files -- do I need to modify them as a class?
/src/App.css
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-intro {
font-size: large;
}
#keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/src/App.js
import React, { Component } from 'react';
import './App.css';
import BarChart from './BarChart';
import PieChart from './PieChart';
import LineChart from './LineChart';
// Allowed types
const typeMapping = {
PieChart, // In ES6, it is the same as "PieChart": PieChart,
BarChart,
LineChart
};
class App extends Component {
getChart(config) {
const { type, ...props } = config;
return React.createElement(typeMapping[type], props);
}
render() {
var config = this.props.config;
return (
<div>
{config.map((chartConfig, index) => {
return (
<div key={index} className={'holder' + index}>
{this.getChart(chartConfig)}
</div>
)
})}
</div>
);
}
}
export default App;
/src/BarChart.js
//barchart
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
var $ = require("jquery");
class BarChart extends Component {
componentDidMount() {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3", $this);
// set el height and width etc.
}
render() {
return (
<div className="barchart" data-role="barchart" data-width={this.props.width} data-height={this.props.height}
data-service={this.props.serviceApi}>bar.
</div>
);
}
};
export default BarChart;
/src/Index.css
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.piechart{
background: pink;
width: 100px;
height: 50px;
border: 1px solid black;
}
.barchart{
background: green;
width: 100px;
height: 50px;
border: 1px solid black;
}
.linechart{
background: purple;
width: 100px;
height: 50px;
border: 1px solid black;
}
/src/Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
var config = [{
"type": "PieChart",
"width": 200,
"height": 200,
"serviceApi": "api.php?mode=GetCars"
}, {
"type": "BarChart",
"width": 100,
"height": 100,
"serviceApi": "api.php?mode=GetBoats"
}, {
"type": "LineChart",
"width": 100,
"height": 100,
"serviceApi": "api.php?mode=GetBoats"
}];
ReactDOM.render(
<App config={config} />,
document.getElementById('root')
);
/src/LineChart.js
//linechart
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
var $ = require("jquery");
class LineChart extends Component {
componentDidMount() {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3", $this);
// set el height and width etc.
}
render() {
return (
<div className="linechart" data-role="linechart" data-width={this.props.width} data-height={this.props.height}
data-service={this.props.serviceApi}>line.
</div>
);
}
};
export default LineChart;
/src/PieChart.js
//piechart
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
var $ = require("jquery");
class PieChart extends Component {
componentDidMount() {
var $this = $(ReactDOM.findDOMNode(this));
console.log("rendered div now engage d3", $this);
// set el height and width etc.
var dataset = {
apples: [53245, 28479, 19697, 24037, 40245],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select($this[0]).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(dataset.apples))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc);
}
render() {
return (
<div className="piechart" data-role="piechart" data-width={this.props.width} data-height={this.props.height}
data-service={this.props.serviceApi}>pie.
</div>
);
}
};
export default PieChart;
Related
import { fabric } from 'fabric';
const Canvas = () => {
const [canvas, setCanvas] = useState('');
useEffect(() => {
setCanvas(initCanvas());
}, []);
const initCanvas = () => (
new fabric.Canvas('canvas', {
height: 800,
width: 800,
})
);
const addRect = canvi => {
const rect = new fabric.Rect({
height: 280,
width: 200,
fill: 'yellow'
});
canvi.add(rect);
canvi.renderAll();
}
return(
<div>
<button onClick={() => addRect(canvas)}></button>
<br/><br/>
<canvas id="canvas" />
</div>
);
}
export default Canvas;
I want to display the rectangle without using the button and also to display its state on the browser
the onload event is also not working in the react I also tried to use the other alternative of onload but it's of no help
I'm also working on this if you want to add rect without button click you can add it into the useEffect.
const fuctionName = ()=>{
useEffect(() => {
return () => {
const canvas = new fabric.Canvas('canvas-main');
const rect = new fabric.Rect({
height: 280,
width: 200,
fill: 'yellow',
});
canvas.add(rect);
}
}, [])
return (
<>
<canvas
style={{ border: 'solid 1px #555' }}
id="canvas-main"
width="600px"
height="600px"
/>
</>
);
};
so basically I'm using ref to get component dimensions in componentDidMount() and I can read and console.log that and it gives me the width I want(look into code), but when I want to read and console.log that in the render() method and to use that informations, it gives me undefined. And I don't know what is wrong
var Tooltip = React.createClass({
componentDidMount() {
this.tooltipSize = this.refs.tooltip.getBoundingClientRect();
this.tooltipWidth = this.tooltipSize.width;
// console.log(this.tooltipWidth); here it gives me the width
},
render(){
var tooltipSize,
tooltipWidth,
tooltipStyle = {
top: 0,
left: 0,
};
// console.log(tooltipWidth); here it gives me undefined
return(
<div ref="tooltip" className="tooltip" style={tooltipStyle}>{this.props.tooltip}</div>
);
}
});
var Button = React.createClass({
getInitialState() {
return {
iconStyle: this.props.iconStyle,
style: this.props.style,
cursorPos: {},
};
},
componentDidMount() {
this.size = this.refs.button.getBoundingClientRect();
this.width = this.size.width;
this.height = this.size.height;
this.top = this.size.top;
this.left = this.size.left;
},
...
render() {
var _props = this.props,
top,
left,
width,
height,
size,
//other variables
...
return(
<Style>
{`
.IconButton{
position: relative;
}
.IconButton:disabled{
color: ${_props.disabledColor};
}
.btnhref{
text-decoration: none;
background-color: blue;
}
`}
<a {...opts} className="btnhref" id="tak">
<button ref="button" className={"IconButton" + _props.className} disabled={disabled} style={buttonStyle}
onMouseEnter={this.showTooltip} onMouseLeave={this.removeTooltip} >
<Ink background={true} style={rippleStyle} opacity={rippleOpacity} />
<FontIcon className={_props.iconClassName}/>
</button>
</a>
</Style>
);
}
});
class IconButton extends React.Component {
render(){
return(
<div>
<Tooltip tooltip={this.props.tooltip} />
<Button href={this.props.href} className={this.props.className} iconStyle={this.props.iconStyle} style={this.props.style} iconClassName={this.props.iconClassName} disabled={this.props.disabled} disableTouchRipple={this.props.disableTouchRipple} />
</div>
);
}
}
And one thing else. How can I send variables with informations about dimensions of another component(Button component) to the Tooltip component? Because I need to use them inside of this component to place it. Thanks
Updated code:
var Tooltip = React.createClass({
getInitialState() {
return {
tooltipWidth: null,
tooltipHeight: null
};
},
componentDidMount() {
this.tooltipSize = this.refs.tooltip.getBoundingClientRect();
this.setState({
tooltipWidth: this.tooltipSize.width,
tooltipHeight: this.tooltipSize.height
});
},
...
render(){
var _props = this.props,
fontSize,
fontStyle,
tooltipSize,
tooltipWidth = this.state.tooltipWidth,
tooltipHeight = this.state.tooltipHeight,
w = this.props.buttonWidth,
h = this.props.buttonHeight,
y = this.props.buttonTop,
x = this.props.buttonLeft,
tooltipStyle = {
top: y - tooltipHeight - 20 + "px",
left: x - tooltipWidth/2 + w/2 + "px",
};;
...
return(
<div ref="tooltip" className="tooltip" style={fontStyle}>{this.props.tooltip}</div>
);
}
});
var Button = React.createClass({
getInitialState() {
return {
iconStyle: this.props.iconStyle,
style: this.props.style,
cursorPos: {},
width: null,
height: null,
top: null,
left: null,
};
},
componentDidMount() {
this.size = this.refs.button.getBoundingClientRect();
this.width = this.size.width;
this.height = this.size.height;
this.top = this.size.top;
this.left = this.size.left;
},
transferring1(){
var width = this.width;
return width;
},
transferring2(){
var height = this.height;
return height;
},
transferring3(){
var top = this.top;
return top;
},
transferring4(){
var left = this.left;
return left;
},
...
render() {
var _props = this.props,
opts,
top,
left,
width,
height,
size;
...
return(
<Style>
{`
.IconButton{
position: relative;
}
.IconButton:disabled{
color: ${_props.disabledColor};
}
.btnhref{
text-decoration: none;
background-color: blue;
}
`}
<a {...opts} className="btnhref" id="tak">
<button ref="button" className={"IconButton" + _props.className} disabled={disabled} style={buttonStyle}
onMouseEnter={this.showTooltip} onMouseLeave={this.removeTooltip} >
<Ink background={true} style={rippleStyle} opacity={rippleOpacity} />
<FontIcon className={_props.iconClassName}/>
</button>
</a>
</Style>
);
}
});
class IconButton extends React.Component {
constructor(props) {
super(props);
this.state = {
buttonWidth: null,
buttonHeight: null,
buttonTop: null,
buttonLeft: null,
};
}
componentDidMount() {
this.setState({
buttonWidth: this.refs.btn.transferring1(),
buttonHeight: this.refs.btn.transferring2(),
buttonTop: this.refs.btn.transferring3(),
buttonLeft: this.refs.btn.transferring4(),
});
}
render(){
return(
<div>
<Tooltip tooltipPosition={this.props.tooltipPosition} tooltip={this.props.tooltip} touch={this.props.touch} buttonWidth={this.state.buttonWidth} buttonHeight={this.state.buttonHeight} buttonTop={this.state.buttonTop} buttonLeft={this.state.buttonLeft}/>
<Button ref="btn" href={this.props.href} className={this.props.className} iconStyle={this.props.iconStyle} style={this.props.style} iconClassName={this.props.iconClassName}
disabled={this.props.disabled} disableTouchRipple={this.props.disableTouchRipple} />
</div>
);
}
}
ReactDOM.render(
<IconButton href="" className="" iconStyle="" style="" iconClassName="face" disabled="" disableTouchRipple="" tooltip="! ! ! Guzik ! to ! kozak ! ! !" tooltipPosition="" touch="true" />,
document.getElementById('app')
);
I think you should use state for setting variables in react
example
var Tooltip = React.createClass({
constructor(){
super();
this.state = {tooltipWidth: 0}
}
componentDidMount() {
this.tooltipSize = this.refs.tooltip.getBoundingClientRect();
this.setState({tooltipWidth: this.tooltipSize.width}); //Update the state of this component
},
render(){
console.log(this.state.tooltipWidth) //your tooltip width
return(
<div ref="tooltip" className="tooltip" style={tooltipStyle}>{this.props.tooltip}</div>
);
}
});
and for passing another's component dimension, you should calculate the size of Button component on the parent component (IconButton).
Then pass it to Tooltip like this (just example)
<Tooltip buttonHeight={this.state.buttonHeight} tooltip={this.props.tooltip} />
I am super stuck on something. While I understand how a parent can pass props down to a child, I can't figure out how to use a child to communicate back up to a parent, grandparent, etc.
Basically, I have a child that is a nested component, and I want to make it so that, upon a click of this child component, another child is rendered at the same level as parent.
Here is my code...
var Grandparent = React.createClass({
getInitialState: function() {
return {closed: true};
},
checkMenuState: function() {
if (this.state.closed == true) {
return;
}
else {
return <Child/>;
}
},
handleState: function() {
this.setState({closed: false});
{this.checkMenuState}
},
render: function() {
return <div><Parent/>{this.checkMenuState()}<OtherChild onClick={this.handleState}/></div>
}
})
var Parent = React.createClass({
render: function() {
var parentSquare={backgroundColor: 'blue', height: 400, width: 400};
return <div style={parentSquare}></div>;
}
});
var Child = React.createClass({
render: function() {
var childSquare={backgroundColor: 'orange', height: 100, width: 100, top: 0, position: 'absolute'};
return <div style={childSquare} closed={this.props.closed}></div>
}
});
var OtherChild = React.createClass({
render: function() {
var childSquare={backgroundColor: 'yellow', height: 100, width: 100, top: 100, position: 'absolute'};
return <div style={childSquare}></div>
}
});
ReactDOM.render(
<Grandparent/>,
document.getElementById('container')
);
So upon initial rendering, the page should look like this:
And then, once the yellow div is clicked, it should look like this:
As of right now, nothing is happening when I click. Here is a link to my JSFiddle:
JSFiddle
In order to allow your children to modify its parent's state you should pass a closure (a method with access to another scope) from the parent to the children. Notice that your so called "parent" is not a real parent ( :'( ) but a sibling of your child components. Only the Grandparent has inner components.
var Grandparent = React.createClass({
getInitialState: function() {
return { showChild: false }
},
displayChild: function() {
this.setState({ showChild: true })
},
render: function() {
return <div>
<Parent />
{ this.state.showChild ? <Child /> : undefined }
<OtherChild onClick={ this.displayChild } />
</div>
}
})
var Parent = React.createClass({
render: function() {
var parentSquare = {
backgroundColor: 'blue',
height: 400,
width: 400
}
return <div style={ parentSquare }></div>
}
})
var Child = React.createClass({
render: function() {
var childSquare = {
backgroundColor: 'orange',
height: 100,
width: 100,
top: 0,
position: 'absolute'
}
return <div style={ childSquare }></div>
}
})
var OtherChild = React.createClass({
render: function() {
var childSquare = {
backgroundColor: 'yellow',
height: 100,
width: 100,
top: 100,
position: 'absolute'
}
return <div style={ childSquare } onClick={ this.props.onClick }></div>
}
})
ReactDOM.render(
<Grandparent/>,
document.getElementById('container')
)
Give it a try!
I have something similar to a notes app, and want to be able to drag and drop cards from one group to another (by using react-dnd). Naturally, after a card is dropped, I want to remove it from the source group and add it to the target group. Removing works fine, but the card is not being rendered in the target group. Here is the relevant code:
App = React.createClass({
getInitialState: function() {
...
return {
appState: appState
}
}
removeCard: function(card) {
var content = this.state.appState[card.groupId].content;
content.splice(content.indexOf(card), 1);
this.setState({ appState: this.state.appState });
},
addCard: function(card, target) {
var content = this.state.appState[target.groupId].content;
content.splice(content.indexOf(target) + 1, 0, card);
this.setState({ appState: this.state.appState });
},
onCardDrop: function(source, target) {
this.addCard(source, target); // didn't work
this.removeCard(source); // worked
},
render: function() {
var that = this;
var appState = this.state.appState;
return (
<div>
{_.map(appState, function(group) {
return (
<Group
key={group.id}
id={group.id}
group={group}
onCardDrop={that.onCardDrop} />
)
})}
</div>
)
}
});
So, the card is removed from the source group, but it never appears in the target group even though the console.log of the target group shows the card is there. Is it possible that for some reason the component is not rerendering.
The Group and Card components are rendering ul and li respectively.
I took some time to make a working example based on the code you provided... but it did work. No problems in the code you provided. This indicates that the problem lies elsewhere in your code.
I cannot give you a complete answer because the snippet you provided does not follow the Minimal, Complete, and Verifiable example rule. Though it is minimal, it's incomplete, and also not verifiable.
What I can do is paste the whole code that I made here and hope that it will be useful to you.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script src="https://npmcdn.com/react-dnd-html5-backend#2.1.2/dist/ReactDnDHTML5Backend.min.js"></script>
<script src="https://npmcdn.com/react-dnd#2.1.0/dist/ReactDnD.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
<style>
ul {
display: inline-block;
padding: 10px;
width: 100px;
border: 1px solid gray;
vertical-align: top;
}
li {
display: block;
padding: 0;
width: 100px;
text-align: center;
box-sizing: border-box;
position: relative;
}
li.group {
}
li.card {
height: 100px;
border: 1px solid black;
line-height: 100px;
margin-top: 5px;
font-size: 25px;
font-weight: bold;
cursor: move;
}
li > span {
vertical-align: middle;
display: inline-block;
}
</style>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
window.ItemTypes = {
CARD: "card",
GROUP_TITLE: "group-title"
};
</script>
<script type="text/babel">
var cardSource = {
beginDrag: function (props) {
return { cardId: props.id, groupId: props.groupId, card: props.card };
}
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
}
}
var cardTarget = {
drop: function (props, monitor) {
var item = monitor.getItem();
console.log(item.card)
console.log(props.card)
props.onCardDrop(item.card, props.card);
},
canDrop: function (props, monitor) {
var item = monitor.getItem();
return item.cardId != props.id;
}
};
function collectTgt(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop()
};
}
window.Card = React.createClass({
propTypes: {
connectDragSource: React.PropTypes.func.isRequired,
isDragging: React.PropTypes.bool.isRequired,
isOver: React.PropTypes.bool.isRequired,
canDrop: React.PropTypes.bool.isRequired
},
renderOverlay: function (color) {
return (
<div style={{
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: '100%',
zIndex: 1,
opacity: 0.5,
backgroundColor: color,
}} />
);
},
render: function() {
var connectDragSource = this.props.connectDragSource;
var isDragging = this.props.isDragging;
var connectDropTarget = this.props.connectDropTarget;
var isOver = this.props.isOver;
var canDrop = this.props.canDrop;
return connectDropTarget(connectDragSource(
<li className="card" style={{opacity: isDragging ? 0.5 : 1}}
><span>{this.props.card.name}-{this.props.card.groupId}</span>
{isOver && !canDrop && this.renderOverlay('red')}
{!isOver && canDrop && this.renderOverlay('yellow')}
{isOver && canDrop && this.renderOverlay('green')}
</li>
));
}
});
window.Card = ReactDnD.DragSource(ItemTypes.CARD, cardSource, collect)(window.Card);
window.Card = ReactDnD.DropTarget(ItemTypes.CARD, cardTarget, collectTgt)(window.Card);
</script>
<script type="text/babel">
window.Group = React.createClass({
render: function() {
console.log(this.props.group)
var that = this;
return (
<ul>
<li className="group">Group #{this.props.group.id}</li>
{_.map(this.props.group.content, function(card) {
return (
<Card
key={card.name}
id={card.name}
groupId={card.groupId}
card={card}
onCardDrop={that.props.onCardDrop}
/>
)
})}
</ul>
);
}
});
</script>
<script type="text/babel">
window.App = React.createClass({
getInitialState: function() {
return {
appState: [
{
id: 0,
content: [
{
groupId: 0,
name: "C1"
},
{
groupId: 0,
name: "C2"
},
{
groupId: 0,
name: "C3"
},
{
groupId: 0,
name: "C4"
}
]
},
{
id: 1,
content: [
{
groupId: 1,
name: "C5"
},
{
groupId: 1,
name: "C6"
},
{
groupId: 1,
name: "C7"
},
{
groupId: 1,
name: "C8"
}
]
}
]
};
},
removeCard: function(card) {
var content = this.state.appState[card.groupId].content;
content.splice(content.indexOf(card), 1);
this.setState({ appState: this.state.appState });
},
addCard: function(card, target) {
var content = this.state.appState[target.groupId].content;
content.splice(content.indexOf(target) + 1, 0, card);
card.groupId = target.groupId;
this.setState({ appState: this.state.appState });
},
onCardDrop: function(source, target) {
this.removeCard(source); // worked
this.addCard(source, target); // worked
},
render: function() {
var that = this;
var appState = this.state.appState;
return (
<div>
{_.map(appState, function(group) {
return (
<Group
key={group.id}
id={group.id}
group={group}
onCardDrop={that.onCardDrop}
/>
)
})}
</div>
)
}
});
window.App = ReactDnD.DragDropContext(ReactDnDHTML5Backend)(window.App);
</script>
<script type="text/babel">
ReactDOM.render(
<App />,
document.getElementById('example')
);
</script>
</body>
</html>
First time using React in this way - trying to draw a basic mock-up canvas to start a new game but returning with 'Uncaught TypeError: Cannot read property 'setupCanvas' of undefined'. Can anyone spot why?
game.jsx
// initial game parameters and setup
'use strict';
import React from 'react';
export default React.createClass({
propTypes: {
rectHeight: React.PropTypes.number,
rectWidth: React.PropTypes.number,
width: React.PropTypes.number,
height: React.PropTypes.number
},
getDefaultProps() {
return {
rectHeight: 300,
rectWidth: 25,
width: 800,
height: 600
}
},
getInitialState() {
return {
}
},
componentDidMount: () => {
this.setupCanvas();
this.draw()
},
canvas: undefined,
context: undefined,
canvasStyle: {
margin: 'auto'
},
setupCanvas: () => {
this.canvas = this.getDOMNode();
this.context = this.canvas.getContext('2d');
},
draw() {
this.context.fillRect(50, 50, this.rectWidth, this.rectHeight);
this.context.save();
this.context.fillStyle = 'blue';
},
render() {
return (
<canvas
style={this.canvasStyle}
width={this.props.width}
height={this.props.height} />
)
}
});
app.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import Game from './components/game';
ReactDOM.render(
<Game />, document.getElementById('main')
);
index.html
<html>
<head>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Intelligent Mouse</title>
</head>
<body>
<div id='main'></div>
<script src='bundle.js'></script>
</body>
</html>
By using the arrow function expression, you are lexically binding the this keyword to your function properties. However, because you are defining your functions as properties of your main class, you do not want to do this. Change property values to anonymous functions and it should work.
export default React.createClass({
propTypes: {
rectHeight: React.PropTypes.number,
rectWidth: React.PropTypes.number,
width: React.PropTypes.number,
height: React.PropTypes.number
},
getDefaultProps: function() {
return {
rectHeight: 300,
rectWidth: 25,
width: 800,
height: 600
}
},
getInitialState: function() {
return {
}
},
componentDidMount: function() {
this.setupCanvas();
this.draw()
},
canvas: undefined,
context: undefined,
canvasStyle: {
margin: 'auto'
},
setupCanvas: function() {
this.canvas = this.getDOMNode();
this.context = this.canvas.getContext('2d');
},
draw: function() {
this.context.fillRect(50, 50, this.rectWidth, this.rectHeight);
this.context.save();
this.context.fillStyle = 'blue';
},
render: function() {
return (
<canvas
style={this.canvasStyle}
width={this.props.width}
height={this.props.height} />
)
}
});