No text shown in dialog box (react js dialog box)? - reactjs

I use https://www.npmjs.com/package/react-bootstrap-dialog#dialogprompt-generators for my notification generating module. but only display buttons and no any text message shown in dialogbox..
enter image description here
onClick () {
this.dialog.show({
title: 'Greedings',
body: 'How are you?',
actions: [
Dialog.CancelAction(),
Dialog.OKAction()
],
bsSize: 'small',
onHide: (dialog) => {
dialog.hide()
console.log('closed by clicking background.')
}
})
}
This is the code part i used for that..Help me..
I have used material dashboard react free template for my project development, So in this dialog box doesn't work, but when i use codesandbox, it works properly, doesn't it work with material dashboard? .I can't understand why,If you know, Help me..
codesandbox.io/s/amazing-glade-lh5tl
import React from "react";
import firebase from "../../config/firebase.js";
import PropTypes from "prop-types";
import carfix from "./s.jpg";
// react plugin for creating charts
import {Link, withRouter} from 'react-router-dom';
import ChartistGraph from "react-chartist"; // #material-ui/core
import withStyles from "#material-ui/core/styles/withStyles";
import Icon from "#material-ui/core/Icon"; // #material-ui/icons
import Store from "#material-ui/icons/Store";
import Warning from "#material-ui/icons/Warning";
import DateRange from "#material-ui/icons/DateRange";
import LocalOffer from "#material-ui/icons/LocalOffer";
import Update from "#material-ui/icons/Update";
import ArrowUpward from "#material-ui/icons/ArrowUpward";
import AccessTime from "#material-ui/icons/AccessTime";
import Accessibility from "#material-ui/icons/Accessibility";
import BugReport from "#material-ui/icons/BugReport";
import Code from "#material-ui/icons/Code";
import Cloud from "#material-ui/icons/Cloud"; // core components
import GridItem from "components/Grid/GridItem.jsx";
import GridContainer from "components/Grid/GridContainer.jsx";
import Table from "components/Table/Table.jsx";
import Tasks from "components/Tasks/Tasks.jsx";
import CustomTabs from "components/CustomTabs/CustomTabs.jsx";
import Danger from "components/Typography/Danger.jsx";
import Card from "components/Card/Card.jsx";
import CardHeader from "components/Card/CardHeader.jsx";
import CardIcon from "components/Card/CardIcon.jsx";
import CardBody from "components/Card/CardBody.jsx";
import CardFooter from "components/Card/CardFooter.jsx";
import WorkAssign from "pages/work_assignmt.js";
import WorkDone from "pages/work_done.js";
import Modal from 'react-bootstrap/Modal';
import {Button} from 'react-bootstrap'
import Dialog from 'react-bootstrap-dialog'
import { bugs, website, server } from "variables/general.jsx";
import { dailySalesChart, emailsSubscriptionChart, completedTasksChart } from "variables/charts.jsx";
import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.jsx";
class Dashboard extends React.Component { constructor(props) {
super(props);
this.onClick = this.onClick.bind(this)
this.state = {
field1:0,
field2:0,
field3:0,
isDialogOpen: false
}; }
onClick () {
this.dialog.show({
title: 'Greedings',
body: 'How are you?',
actions: [
Dialog.CancelAction(),
Dialog.OKAction()
],
bsSize: 'small',
onHide: (dialog) => {
dialog.hide()
console.log('closed by clicking background.')
}
}) }
componentWillMount = async() =>{
this.setFieldData(); };
handleChange = (event, value) => {
this.setState({ value }); };
handleChangeIndex = index => {
this.setState({ value: index }); };
setFieldData = async() => {
var ref = firebase.database().ref("Daily Work Data").child("02-10-2019").child("Field 1");
var sum=0;
var ref = firebase.database().ref("Daily Work Data").child("02-10-2019").child("Field 2");
sum=0;
this.setState({field2: sum});
var ref = firebase.database().ref("Daily Work Data").child("02-10-2019").child("Field 3");
sum=0;
await ref.once('value',function (snapshot) {
snapshot.forEach(element => {
sum = sum + element.val().amount;
});
});
this.setState({field3: sum});
};
render() { const { classes } = this.props;
return (
<div
className="App"
style={{
backgroundImage: `linear-gradient(0deg,rgba(20,100,20,0.5), rgba(9, 93, 225, 0.0)),url(${carfix})`
}}>
<div className="wrappe">
<div>
<Dialog ref={(component) => { this.dialog = component }} />
</div>
<div>
<GridContainer>
<GridItem xs={12} sm={6} md={4}>
<Card>
<Button variant="underlined"
onClick={this.onClick}>
<CardHeader color="warning" stats icon>
<CardIcon color="warning">
<i class="material-icons">notifications_active</i>
</CardIcon>
<p className={classes.cardCategory}></p>
<h4 className={classes.cardTitle}>New Notifications</h4>
</CardHeader>
<CardFooter stats>
<div className={classes.stats}>
<hr/>
</div>
</CardFooter>
</Button>
</Card>
</GridItem>
<GridItem xs={12} sm={6} md={4}>
<Card>
<Button variant="underlined"
onClick={this.onClick}>
<CardHeader color="primary" stats icon>
<CardIcon color="primary">
<i class="material-icons">pan_tool</i>
</CardIcon>
<p className={classes.cardCategory}></p>
<h4 className={classes.cardTitle}>Pending Notifications</h4>
</CardHeader>
<CardFooter stats>
<div className={classes.stats}>
<hr/>
</div>
</CardFooter>
</Button>
</Card>
</GridItem>
<GridItem xs={12} sm={6} md={4}>
<Card>
<Button variant="underlined"
onClick={this.onClick}>
<CardHeader color="danger" stats icon>
<CardIcon color="danger">
<i class="material-icons">thumb_up</i>
</CardIcon>
<p className={classes.cardCategory}></p>
<h4 className={classes.cardTitle}>Responded Notifications</h4>
</CardHeader>
<CardFooter stats>
<div className={classes.stats}>
<hr/>
</div>
</CardFooter>
</Button>
</Card>
</GridItem>
</GridContainer>
<div>
</div>
</div> </div> </div>
); } }
Dashboard.propTypes = { classes: PropTypes.object.isRequired };
export default withStyles(dashboardStyle)(Dashboard);

Related

setState (In react functional component) is not updating the existing state in react todo app

I have to do app using react and material ui which is combined of these components:
1- App.js
`
import './App.css';
import Header from './components/Header'
import ToDoApp from './components/ToDoApp';
function App() {
return (
<div className="App">
<Header />
<ToDoApp />
</div>
);
}
export default App;
`
2- ToDoApp.js
`
import React from 'react'
import ToDoList from './ToDoList';
import Task from './Task';
import { Container, Grid } from '#mui/material';
import {useState, } from 'react';
export default function ToDoApp() {
const [todos, setTodos] = useState([
{
id: 1,
Title: "Task 1",
done: true
},
{
id: 2,
Title: "Task 2",
done: true
},
])
const deleteTask = (id) => {
// console.log(id);
const newtodos = todos.filter(function(todo) {
return todo.id !== id;
})
setTodos(newtodos);
}
const addTodo = (todoTitle) => {
const lastTodo = todos[todos.length-1];
const newtodosId = lastTodo.id + 1;
const newtodos = todos;
newtodos.push({
id: newtodosId,
title: todoTitle,
done: false,
})
setTodos(newtodos);
console.log(newtodos);
}
return (
<Container maxWidth="sm">
<Grid item xs={12} md={6}>
<ToDoList newTodo={addTodo}/>
<Task ToDoAppList={todos} DeleteTodoTask={deleteTask}/>
</Grid>
</Container>
)
}
`
3- ToDoList.js
`
import * as React from 'react';
import FormControl from '#mui/material/FormControl';
import Typography from '#mui/material/Typography';
import TextField from '#mui/material/TextField';
import IconButton from '#mui/material/IconButton';
import { Add, AddToDriveOutlined } from '#mui/icons-material/';
import { useState } from 'react';
import { useEffect } from 'react';
export default function ToDoList(props) {
const addTodo = props.newTodo
const [todo, setTodo] = useState('')
function writeTodo(e) {
// console.log(e.target.value);
setTodo(e.target.value);
}
const addText = (e) => {
e.preventDefault();
addTodo(todo);
// console.log(todo);
}
return (
<>
<Typography sx={{ mt: 4, mb: 2 }} variant="h6" component="div">
ToDo List
</Typography>
<form onSubmit={(e) => addText(e)}>
<FormControl>
<div
style={{display: 'flex', marginBottom: 20}}>
<TextField
id="standard-helperText"
label="New ToDo"
style={{ width: 450 }}
variant="standard"
value={todo}
onChange={(e) => writeTodo(e)}
/>
<IconButton edge="end" aria-label="create" type="submit">
<Add />
</IconButton>
</div>
</FormControl>
</form>
</>
)
}
`
4-Task.js
`
import React from 'react'
import Paper from '#mui/material/Paper';
import IconButton from '#mui/material/IconButton';
import { Tag, Check, Delete } from '#mui/icons-material';
export default function Task(props) {
const tasks = props.ToDoAppList;
const deleteTask = props.DeleteTodoTask;
const List = tasks.map(task => { return(
<Paper elevation={3} style={{padding: 10, marginTop: 10}} key={task.id}>
<IconButton aria-label="create">
<Tag />
</IconButton>
<span style={{textDecoration: 'line-through'}}>{task.Title}</span>
<IconButton aria-label="delete" style={{float: 'right', color: 'red'}} onClick={()=>deleteTask(task.id)}>
<Delete />
</IconButton>
<IconButton aria-label="check" style={{float: 'right'}}>
<Check />
</IconButton>
</Paper>
)})
return (
<>
{List}
</>
)
}
`
When Submitting the new Todo, the result is not shown on the DOM.
In the ToDoApp.js -> function "addTodo" I used setState to push the new array with the new item, and is sent to ToDoList.js -> form "onSubmit" event in "addText" function. I put the console.log in the function and I am getting the new array but it's not showing on the page.
The tasks listing is handled in the task.js by sending the array through props and using map function to loop over the array.
Note: I handled the delete function in the ToDoApp.js and it's woking with no problem. I don't understand why it's not running with "addTodo" funciton.
First you need to change title to Title (based on your state Schema)
then you dont need to update whole state just add your new task to your current state (function in setTodos)
const addTodo = (todoTitle) => {
const lastTodo = todos[todos.length - 1];
const newtodosId = lastTodo.id + 1;
let newTodo = {
id: newtodosId,
Title: todoTitle,
done: false
};
setTodos((prev)=>[...prev,newTodo])
};

Rendering different youtube videos on button click

I created a react app where I display different video games and the app decides which game to play. I also have a file where I stored the data of video games. The goal I'm trying to achieve is to render the youtube video trailer of the corresponding video game when clicking on a button while using React Hooks. I've been using the npm package react-player. If someone could help, I'd appreciate it.
This is the code for the Video Game component:
import React from 'react';
import { Button, message } from 'antd';
import { Row, Col } from 'antd';
import GameEntry from '../GameEntry';
import Games from '../Games';
function VideoGameSection() {
const chooseGame = () => {
var randomGameTitle = [
'Gears 5',
'Halo',
'Hellblade',
'It takes two',
'A Plague Tale',
'Psychonauts',
'Twelve Minutes',
'Ori',
'Streets of Rage',
'Last of Us',
'Boodborne',
'Geenshin Impact',
'Dragon Ball Z:KAKAROT',
'Ghost Tsushima',
'Naruto',
'Overcooked',
'Horizon',
'Tomb Raider',
'Uncharted',
'Person 5 Royal',
'Ratchet',
'Spider-Man',
];
var randomIndex = Math.floor(Math.random() * randomGameTitle.length);
return message.info(
'The game you will play is: ' + randomGameTitle[randomIndex] + '.',
);
};
return (
<div id="video-game" className="block bgGray">
<div className="container-fluid">
<div className="titleHolder">
<h2>Video Games</h2>
<p>A list of current games</p>
<div className="site-button-ghost-wrapper">
<Button
className="gameButton"
type="primary"
danger
ghost
onClick={chooseGame}
>
Pick for me
</Button>
</div>
</div>
<Row gutter={[16, 24]}>
{Games.map((videogame, i) => (
<Col span={8}>
<GameEntry
id={i}
key={i}
title={videogame.title}
imgURL={videogame.imgURL}
description={videogame.console}
/>
</Col>
))}
</Row>
</div>
</div>
);
}
export default VideoGameSection;
This is the code for my game entry component:
import React, { useState } from 'react';
import { Card, Button, Modal } from 'antd';
import YoutubeSection from './Home/YoutubeSection';
const { Meta } = Card;
function GameEntry(props) {
const [isModalVisible, setIsModalVisible] = useState(false);
const showModal = () => {
setIsModalVisible(true);
};
const handleClose = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
return (
<div>
<Card
className="gameCard"
hoverable
cover={<img className="cardImg" alt={props.title} src={props.imgURL} />}
>
<div className="cardTitle">
<Meta title={props.title} description={props.description} />
</div>
<>
<Button
className="trailerButton"
type="primary"
block
style={{
color: '#fff',
borderColor: '#fff',
backgroundColor: '#e6544f',
}}
onClick={showModal}
>
Click for trailer
</Button>
<Modal
title={props.title}
width={'725px'}
visible={isModalVisible}
onOk={handleClose}
onCancel={handleCancel}
>
<YoutubeSection />
</Modal>
</>
</Card>
</div>
);
}
export default GameEntry;
This is the code for the youtube component:
import React, { useState } from 'react';
import ReactPlayer from 'react-player';
function YoutubeSection(props) {
return (
<div className="container-fluid">
<ReactPlayer
// url={videoTrailer}
muted={false}
playing={true}
controls={true}
/>
</div>
);
}
export default YoutubeSection;
example of data file:
const Games = [
{
id: 1,
title: 'Gears 5',
imgURL: '../Images/gears-5.jpeg',
console: 'Xbox',
videoID: 'SEpWlFfpEkU&t=7s',
},
You can keep a single Modal component and use it for that.
ModalView.js
import React, { useState } from "react";
import YoutubeSection from "./YoutubeSection";
import { Modal } from "antd";
const ModalView = ({
title,
isModalVisible,
handleClose,
handleCancel,
videoID
}) => {
return (
<Modal
title={title}
width={"725px"}
visible={isModalVisible}
onOk={handleClose}
onCancel={handleCancel}
>
<YoutubeSection videoID={videoID} />
</Modal>
);
};
export default ModalView;
Move the ModalView and its state, control functions to the VideoGameSection.
VideoGameSection.js
import React, { useState } from "react";
import { Button, message } from "antd";
import { Row, Col } from "antd";
import GameEntry from "./GameEntry";
import Games from "./Games";
import ModalView from "./ModalView";
function VideoGameSection() {
const [isModalVisible, setIsModalVisible] = useState(false);
const [currentVideoID, setCurrentVideoID] = useState("");
const showModal = () => {
setIsModalVisible(true);
};
const handleClose = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
const chooseGame = () => {
var randomGameTitle = [
"Gears 5",
"Halo",
"Hellblade",
"It takes two",
"A Plague Tale",
"Psychonauts",
"Twelve Minutes",
"Ori",
"Streets of Rage",
"Last of Us",
"Boodborne",
"Geenshin Impact",
"Dragon Ball Z:KAKAROT",
"Ghost Tsushima",
"Naruto",
"Overcooked",
"Horizon",
"Tomb Raider",
"Uncharted",
"Person 5 Royal",
"Ratchet",
"Spider-Man"
];
var randomIndex = Math.floor(Math.random() * randomGameTitle.length);
return message.info(
"The game you will play is: " + randomGameTitle[randomIndex] + "."
);
};
return (
<div id="video-game" className="block bgGray">
<div className="container-fluid">
<div className="titleHolder">
<h2>Video Games</h2>
<p>A list of current games</p>
<div className="site-button-ghost-wrapper">
<Button
className="gameButton"
type="primary"
danger
ghost
onClick={chooseGame}
>
Pick for me
</Button>
</div>
</div>
<Row gutter={[16, 24]}>
{Games.map((videogame, i) => (
<Col span={8}>
<GameEntry
id={i}
key={i}
title={videogame.title}
imgURL={videogame.imgURL}
description={videogame.console}
videoID={videogame.videoID}
setCurrentVideoID={setCurrentVideoID}
showModal={showModal}
/>
</Col>
))}
<ModalView
videoID={currentVideoID}
handleClose={handleClose}
isModalVisible={isModalVisible}
/>
</Row>
</div>
</div>
);
}
export default VideoGameSection;
Access the videoID passed via ModalView. You can save gameId instead of videoID to get any other info of the game Ex:title.
YoutubeSection.js
import React, { useState } from "react";
import ReactPlayer from "react-player";
function YoutubeSection(props) {
return (
<div className="container-fluid">
<ReactPlayer
url={`https://www.youtube.com/watch?v=${props.videoID}`}
muted={false}
playing={true}
controls={true}
/>
</div>
);
}
export default YoutubeSection;
GameEntry.js
import React, { useState } from "react";
import { Card, Button, Modal } from "antd";
import YoutubeSection from "./YoutubeSection";
const { Meta } = Card;
function GameEntry(props) {
return (
<div>
<Card
className="gameCard"
hoverable
cover={<img className="cardImg" alt={props.title} src={props.imgURL} />}
>
<div className="cardTitle">
<Meta title={props.title} description={props.description} />
</div>
<>
<Button
className="trailerButton"
type="primary"
block
style={{
color: "#fff",
borderColor: "#fff",
backgroundColor: "#e6544f"
}}
onClick={() => {
props.setCurrentVideoID(props.videoID);
props.showModal();
}}
>
Click for trailer
</Button>
</>
</Card>
</div>
);
}
export default GameEntry;
Code sandbox => https://codesandbox.io/s/flamboyant-dan-z0kc0?file=/src/ModalView.js

onClick Handler in React Picking Up Child Nodes

I'm trying to add interactivity to a four quadrant chart whereby the user can click a box to highlight it, and the others will deactivate, similar to how radio boxes work in a form.
The idea was to add an onClick event to each card and have a handler function that will check which box was clicked on, activate it, and then deactivate the rest.
The problem I'm having is that e.target seems to be picking up the child nodes of each card instead of the card itself, so I'm having trouble figuring out which card was clicked.
e.g. console log = '>> event.target <li>A</li>'
I was hoping to determine which card was picked by doing something like event.target.id
I've tried a bunch of things and nothing has worked... How do people normally set this type of interaction up?
import React from "react";
import Card from "#material-ui/core/Card";
import CardContent from "#material-ui/core/CardContent";
import CardActionArea from '#material-ui/core/CardActionArea';
import Typography from "#material-ui/core/Typography";
import Paper from "#material-ui/core/Paper";
function MyCard({ title }) {
return (
<CardActionArea onClick={quadrantClickHandler}>
<Card>
<CardContent>
<Typography>{title}</Typography>
</CardContent>
</Card>
</CardActionArea>
);
}
function quadrantClickHandler(e) {
e.stopPropagation();
console.log('>> event.target ',e.target);
//the idea here is that I will click a "card"
//and then determine which card was clicked so that
//I can highlight it similar to a radio box set in a form.
}
function Quadrants() {
return (
<React.Fragment>
<MyCard
title={
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
<li>F</li>
<li>G</li>
</ul>
} />
<MyCard title="Fast but expensive" />
<MyCard title="Slow but Cheap" />
<MyCard title="Slow but Fast" />
</React.Fragment>
);
}
function FourQuadrants() {
const classes = useStyles();
return (
<div>
<h2>Make a choice:</h2>
<Paper className={classes.paper}>
<Typography className={classes.top}>Big</Typography>
<Typography className={classes.bottom}>Small</Typography>
<Typography align="center" className={classes.left}>Expensive</Typography>
<Typography align="center" className={classes.right}>Cheap</Typography>
<Quadrants />
</Paper>
</div>
);
}
export default FourQuadrants;
Rather than trying to pull information out of the event target, you can have the click handler know everything of importance.
The key aspects in my example below are:
State at the top level (selectedId) to track which card is selected
A click-handler that knows the id of its card and sets the selectedId accordingly
I'm providing each card with its own id and the selectedId, so that it can style itself differently based on whether or not it is selected
import ReactDOM from "react-dom";
import React from "react";
import Card from "#material-ui/core/Card";
import CardContent from "#material-ui/core/CardContent";
import CardActionArea from "#material-ui/core/CardActionArea";
import Typography from "#material-ui/core/Typography";
import { makeStyles } from "#material-ui/core";
const useStyles = makeStyles({
selected: {
border: "1px solid green"
}
});
function MyCard({ title, id, selectedId, handleClick }) {
const classes = useStyles();
return (
<Card className={id === selectedId ? classes.selected : null}>
<CardActionArea onClick={handleClick(id)}>
<CardContent>
<Typography>{title}</Typography>
</CardContent>
</CardActionArea>
</Card>
);
}
function Quadrants() {
const [selectedId, setSelectedId] = React.useState();
const handleClick = id => e => {
setSelectedId(id);
};
const cardProps = { selectedId, handleClick };
return (
<React.Fragment>
<MyCard
{...cardProps}
id={1}
title={
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
<li>F</li>
<li>G</li>
</ul>
}
/>
<MyCard {...cardProps} id={2} title="Fast but expensive" />
<MyCard {...cardProps} id={3} title="Slow but Cheap" />
<MyCard {...cardProps} id={4} title="Slow but Fast" />
</React.Fragment>
);
}
function FourQuadrants() {
return (
<div>
<h2>Make a choice:</h2>
<Quadrants />
</div>
);
}
function App() {
return <FourQuadrants />;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

How to implement add method with user selection?

I have an add method in my application. When a widget is clicked you are given the ability to add another widget. Currently the default component being added is but I want the user to be able to select from a list of components. I am new to react any help would be great.
addEvent=(index)=>{
this.setState({isModalOpen: true})
const copyWidgets=Object.assign([],this.state.widgets);
let widget=this.state.widgets[index];
widget.content=<DataTable/>; //I would like to allow the user to choose from a list of components instead of just adding <DataTable/>
copyWidgets[index]=widget;
this.setState({
widgets:copyWidgets
})
}
This is my app.js which contains the widgets and add event:
import React, { Component } from 'react';
import Swappable from './components/SwappableComponent'
import './App.css';
import DataTable from './components/tableWidget';
import CheckboxList from './components/CheckboxList';
import AddWidgetDialog from './components/AddWidgetDialog';
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";
import { Table } from '#material-ui/core';
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={
widgetOptions:[{name:"Data Table", comp:<DataTable/>},{name:"List", comp:<CheckboxList/>}],
widgets:[ //array for layout
{id:1, content: <DataTable/>},
{id:2, content: <CheckboxList/>},
{id:3, content: ""},
{id:4, content: ""}
],
isModalOpen: false
}
}
deleteEvent=(index)=>{
const copyWidgets=Object.assign([],this.state.widgets);
let widget=this.state.widgets[index];
widget.content="";
copyWidgets[index]=widget;
this.setState({
widgets:copyWidgets
})
}
addEvent=(index)=>{
this.setState({isModalOpen: true})
const copyWidgets=Object.assign([],this.state.widgets);
let widget=this.state.widgets[index];
widget.content=<DataTable/>;
copyWidgets[index]=widget;
this.setState({
widgets:copyWidgets
})
}
onRequestClose = () => {
this.setState({
isModalOpen: false,
});
}
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<AddWidgetDialog widgets={this.state.widgetOptions} isModalOpen={this.state.isModalOpen} onRequestClose={this.onRequestClose} />
<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)} add={this.addEvent.bind(this,index)}/></Paper>
</Grid>
)
})
}
</Grid>
</div>
);
}
}
App.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(App);
This is my modal:
import React, { PropTypes } from 'react';
import Modal from 'react-modal';
const AddWidgetDialog = ({ widgets, isModalOpen, onRequestClose}) => {
const widgetItems = Object.keys(widgets).map((widget, key) => {
return (
<div key={key} className="list-group">
<a href="#" className="list-group-item">
<h6 className="list-group-item-heading">{widgets[widget].name}</h6>
</a>
</div>
);
});
return (
<Modal
className="Modal__Bootstrap modal-dialog"
isOpen={isModalOpen}>
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" onClick={onRequestClose}>
<span aria-hidden="true">×</span>
<span className="sr-only">Close</span>
</button>
<h4 className="modal-title">Add a widget</h4>
</div>
<div className="modal-body">
<h5>Pick a widget to add</h5>
{widgetItems}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" onClick={onRequestClose}>Close</button>
</div>
</div>
</Modal>
);
};
export default AddWidgetDialog;
I am not sure did I understand well, but in general I would do something like this:
function compGallery(picked) {
switch(picked) {
case "ListWidget": return <ListWidget />
case "Table": return <Table />
...
default: return <DataTable />
}
}
Then I would create a list component where the user can pick , which handler would call this.setState({ pickedComponent })
and use it inside your method:
addEvent=(index)=>{
this.setState({isModalOpen: true})
const copyWidgets=Object.assign([],this.state.widgets);
let widget=this.state.widgets[index];
widget.content=compGalery(this.state.pickedComponent);
copyWidgets[index]=widget;
this.setState({
widgets:copyWidgets
})
}
I hope it helps.
After update of the question here are my suggestions:
we pass the handler as a prop handleWidgetSelection
const AddWidgetDialog = ({ handleWidgetSelection, widgets, isModalOpen, onRequestClose}) => {
const widgetItems = Object.keys(widgets).map((widget, key) => {
return (
<div key={key} className="list-group">
<a href="#" onClick={() => handleWidgetSelection(widget.name)} className="list-group-item">
<h6 className="list-group-item-heading">{widgets[widget].name}</h6>
</a>
</div>
);
});
return (
<Modal
className="Modal__Bootstrap modal-dialog"
isOpen={isModalOpen}>
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" onClick={onRequestClose}>
<span aria-hidden="true">×</span>
<span className="sr-only">Close</span>
</button>
<h4 className="modal-title">Add a widget</h4>
</div>
<div className="modal-body">
<h5>Pick a widget to add</h5>
{widgetItems}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" onClick={onRequestClose}>Close</button>
</div>
</div>
</Modal>
);
};
Here we can define the handler which updates selectedWidgetId which is basically the item you selected in the modal, once we have that we just pass find which component want to show
import React, { Component } from 'react';
import Swappable from './components/SwappableComponent'
import './App.css';
import DataTable from './components/tableWidget';
import CheckboxList from './components/CheckboxList';
import AddWidgetDialog from './components/AddWidgetDialog';
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";
import { Table } from '#material-ui/core';
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={
selectedWidgetId: "Data Table",
widgetOptions:[{name:"Data Table", comp:<DataTable/>},{name:"List", comp:<CheckboxList/>}],
widgets:[ //array for layout
{id:1, content: <DataTable/>},
{id:2, content: <CheckboxList/>},
{id:3, content: ""},
{id:4, content: ""}
],
isModalOpen: false
}
}
handleWidgetSelection=(id) => {
this.setState({selectedWidgetId: id})
}
deleteEvent=(index)=>{
const copyWidgets=Object.assign([],this.state.widgets);
let widget=this.state.widgets[index];
widget.content="";
copyWidgets[index]=widget;
this.setState({
widgets:copyWidgets
})
}
addEvent=(index)=>{
this.setState({isModalOpen: true})
const copyWidgets=Object.assign([],this.state.widgets);
let widget=this.state.widgets[index];
widget.content=this.state.widgetOptions.find(w => w.name === this.state.selectedWidgetId).comp;
copyWidgets[index]=widget;
this.setState({
widgets:copyWidgets
})
}
onRequestClose = () => {
this.setState({
isModalOpen: false,
});
}
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<AddWidgetDialog handleWidgetSelection={this.handleWidgetSelection} widgets={this.state.widgetOptions} isModalOpen={this.state.isModalOpen} onRequestClose={this.onRequestClose} />
<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)} add={this.addEvent.bind(this,index)}/></Paper>
</Grid>
)
})
}
</Grid>
</div>
);
}
}
App.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(App);

react-konva How to change image after uploading?

I'm working on a small project that allows user to upload image and then the image will be displayed on a canvas.
I'm using react-konva for this.
I have a container component called DesignPage, which manages the state and pass event handlers to its children.
Inside this DesignPage component, I have 2 other components: Tools - Canvas
When I upload an image using Tools component, the image should be displayed on Canvas component.
I'm using react-dropzone inside Tools component to handle file upload
Inside this Canvas component, there is a child component called DesignImage, which is just for displaying the image.
But the thing is, it just doesn't change the image on canvas when I upload.
How can I fix that?
Here is my code:
DesignPage component:
import React, {Component} from 'react';
import {
Row,
Col
} from 'reactstrap';
import Tools from "../components/DesignPage/Tools";
import Canvas from "../components/DesignPage/Canvas";
import Styles from "../components/DesignPage/Styles";
class DesignPage extends Component {
state = {
text: '',
image: '',
files: []
};
static propTypes = {};
handleTextChange = e => {
this.setState({text: e.target.value});
};
handleFileDrop = files => {
this.setState({
files,
image: files[0].preview
});
};
render() {
return <Row>
<Col xs={12} md={4}>
<Tools
files={this.state.files}
onTextChange={this.handleTextChange}
onFileDrop={this.handleFileDrop}/>
</Col>
<Col xs={12} md={5}>
<Canvas
text={this.state.text}
image={this.state.image}/>
</Col>
<Col xs={12} md={3}>
<Styles/>
</Col>
</Row>;
}
}
export default DesignPage;
Tools component:
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
TabContent,
TabPane,
Nav,
NavItem,
NavLink,
Row,
Col,
FormGroup,
Label
} from 'reactstrap';
import classnames from 'classnames';
import Dropzone from 'react-dropzone';
class Tools extends Component {
state = {
activeTab: '1'
};
toggle = (tab) => {
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
});
}
};
render() {
return <Row>
<Col xs={12}>
<div>
<Nav tabs justified>
<NavItem>
<NavLink
className={classnames({active: this.state.activeTab === '1'})}
onClick={() => {
this.toggle('1');
}}
>
Text
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({active: this.state.activeTab === '2'})}
onClick={() => {
this.toggle('2');
}}
>
Art
</NavLink>
</NavItem>
</Nav>
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1">
<Row>
<Col sm="12">
<FormGroup>
<Label for={"custom-text"}>Enter text below</Label>
<textarea
className={"form-control"}
id={"custom-text"}
onChange={this.props.onTextChange}/>
</FormGroup>
<FormGroup>
<Label for={"font-select"}>Choose a font</Label>
</FormGroup>
</Col>
</Row>
</TabPane>
<TabPane tabId="2">
<Row>
<Col sm="12">
<FormGroup>
<div className="dropzone-container">
<Dropzone onDrop={this.props.onFileDrop}>
<p>Drop a design here, or click to select design to upload.</p>
</Dropzone>
</div>
</FormGroup>
</Col>
</Row>
</TabPane>
</TabContent>
</div>
</Col>
</Row>;
}
}
Tools.propTypes = {
files: PropTypes.array.isRequired,
onTextChange: PropTypes.func.isRequired,
onFileDrop: PropTypes.func.isRequired
};
export default Tools;
Canvas component:
import React from 'react';
import PropTypes from 'prop-types';
import {
Row,
Col
} from 'reactstrap';
import {Stage, Layer} from 'react-konva';
import UserText from "./Canvas/UserText";
import DesignImage from "./Canvas/DesignImage";
const Canvas = props => {
return <Row>
<Col xs={12} className={"canvas-container"}>
<div className={"object-container"}>
<img className={"object-img"} src={"images/iPhone5A.png"} alt={"iPhone5A"}/>
<div className="drawing-area">
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<UserText text={props.text}/>
<DesignImage image={props.image}/>
</Layer>
</Stage>
</div>
</div>
</Col>
</Row>;
};
Canvas.propTypes = {
text: PropTypes.string.isRequired,
image: PropTypes.string.isRequired
};
export default Canvas;
DesignImage component:
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Image} from 'react-konva';
class DesignImage extends Component {
state = {
image: null
};
static propTypes = {
image: PropTypes.string.isRequired
};
componentDidMount() {
const image = new window.Image();
image.src = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRYTULZCGUVEQJEXt9iB8PU4Kb2FMS9Z6ufR1FnQTdrEl5uBOl52Q';
image.onload = () => {
// setState will redraw layer
// because "image" property is changed
this.setState({
image: image
});
};
}
render() {
return <Image image={this.props.image} draggable={true}/>;
}
}
export default DesignImage;
You need write a code to update the image when the component has a new image from props.
class DesignImage extends Component {
state = {
image: null
};
static propTypes = {
image: PropTypes.string.isRequired
};
componentDidMount() {
this.updateImage();
}
componentDidUpdate() {
this.updateImage();
}
updateImage() {
const image = new window.Image();
image.src = this.props.image;
image.onload = () => {
this.setState({
image: image
});
};
}
render() {
return <Image image={this.state.image} draggable={true}/>;
}
}
Update:
You can use use-image hook for simpler image loading:
import useImage from 'use-image';
const DesignImage = ({ image }) => {
const imgElement = useImage(image);
return <Image image={imgElement} draggable={true}/>;
}

Resources