I am new to react.
I am working on a project where I have to show a whiteboard and draw on it.
I want to open the whiteboard when a menu is clicked and on that window I will draw.
How can I do that?
Here is my code:
This is my component for showing menu.
Menu.tsx
export default function Menu(props: { buttonClassName?: string }) {
const [showWindowPortal, setWindowPortal] = useState(false);
const anchorRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
window.addEventListener('beforeunload', () => {
setWindowPortal(false);
});
});
return (
<>
<Button onClick={() => setMenuOpen(isOpen => !isOpen)} ref={anchorRef} className=
{props.buttonClassName}>
{isMobile ? (
<MoreIcon />
) : (
<>
<span style={{ color: '#fff' }}>Settings</span>
<span style={{ color: '#fff', paddingTop: '7px' }}>
<ExpandMoreIcon />
</span>
</>
)}
</Button>
<MenuContainer
open={menuOpen}
onClose={() => setMenuOpen(isOpen => !isOpen)}
anchorEl={anchorRef.current}
anchorOrigin={{
vertical: 'top',
horizontal: 'left',
}}
transformOrigin={{
vertical: isMobile ? -55 : 'bottom',
horizontal: 'center',
}}
>
<MenuItem
onClick={() => {
setWindowPortal(true);
}}
>
<Typography variant="body1">Whiteboard</Typography>
</MenuItem>
</MenuContainer>
{showWindowPortal &&(
<Container
open={showWindowPortal}
onClose={() => {
setSettingsOpen(false);
setMenuOpen(false);
}}
></Container>
)}
</>
);
}
I want to show below component in a new window when I click the menu.
Container.tsx
function Container({ open, onClose }: { open: boolean; onClose: () => void},props:any) {
const classes = useStyles();
const [color, setColor] = useState('#000000');
const [size, setSize] = useState(5);
const [domReady, setDomReady] = useState(false);
let containerEl:any ;
let externalWindow:any ;
useEffect(() => {
externalWindow = window.open('', '', 'width=600,height=400,left=200,top=200');
containerEl = externalWindow.document.createElement('div');
externalWindow.document.body.appendChild(containerEl);
externalWindow.document.title = 'A React portal window';
setDomReady(true);
externalWindow.addEventListener('beforeunload', () => {
//props.closeWindowPortal();
});
return ()=>{
externalWindow.close();
}
});
function changeColor(e: any) {
setColor(e.target.value);
}
function changeSize(e: any) {
setSize(e.target.value);
}
if (!containerEl) {
return null;
}
return domReady? createPortal(<div className="container">Hello</div>
,containerEl):null;
--------I want to show the below to the new window ---------------------
return (
<Dialog open={open} onClose={onClose} classes={{ paper: classes.container }}>
<div className="container">
<div className="tools-section">
<div className="color-picker-container">
Select Brush Color :
<input type="color" value={color} onChange={changeColor} />
</div>
<div className="brushsize-container">
Select Brush Size :
<select value={size} onChange={changeSize}>
<option> 5 </option>
<option> 10 </option>
<option> 15 </option>
<option> 20 </option>
<option> 25 </option>
<option> 30 </option>
</select>
</div>
</div>
<div className="board-container">
<Board color={color} size={size}></Board>
</div>
</div>
</Dialog>
);
}
export default Container;
Related
I'm a newbie in React and Typescript and I'm trying to render some dropdowns from a dialog combining the Select(Chip) and Dialog from Material UI.
The dropdowns are displayed well when I open them, but When I click on any of the displayed values, this overwrite the rest of the dropdowns.
Any help with these?
Thanks you all!
[1]: https://i.stack.imgur.com/QkeZQ.png
[2]: https://i.stack.imgur.com/P45sc.png
[3]: https://i.stack.imgur.com/OWp7D.png
export default function DialogSelect(props: { data: Map<string, any[]>; colName: string; }) {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const [attributeName, setattributeName] = React.useState<string[]>([]);
const handleChangeChip = (event: SelectChangeEvent<typeof attributeName>) => {
const {
target: { value },
} = event;
setattributeName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = (event: React.SyntheticEvent<unknown>, reason?: string) => {
if (reason !== 'backdropClick') {
setOpen(false);
}
};
const handleDelete = (e: React.MouseEvent, value: string) => {
e.preventDefault();
console.log("clicked delete");
setattributeName((current) => _without(current, value));
};
return (
<div>
<Button onClick={handleClickOpen}>Open select dialog</Button>
<Dialog disableEscapeKeyDown open={open} onClose={handleClose}>
<DialogTitle>Fill the form</DialogTitle>
<DialogContent>
<Box component="form" sx={{ display: 'flex', flexWrap: 'wrap' }}>
{Array.from(props.data).map((attribute, index) => (
<FormControl sx={{ m: 1, width: 300}}>
<InputLabel id={"demo-multiple-chip-label"+attribute[0]}>{attribute[0]}</InputLabel>
<Select sx={{backgroundColor:'honeydew'}}
labelId={"demo-multiple-chip-label"+attribute[0]}
id={"demo-multiple-chip"+attribute[0]}
className="attributeValues"
multiple
value={attributeName}
onChange={handleChangeChip}
input={<OutlinedInput id="select-multiple-chip" label={props.colName} />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip clickable variant="outlined" deleteIcon={
<CancelIcon
onMouseDown={(event) => event.stopPropagation()}
/>
} onDelete={(e) => handleDelete(e, value)} key={value} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
>
{attribute[1].map((name: any) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, attributeName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
))}
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>Ok</Button>
</DialogActions>
</Dialog>
</div>
);
}
I have an array of data and map it into Popover component. These components have MenuItem which contains some data. But they render only the last data, instead of the different data that exists. Example:
Wrong data, should be to 59 as shown below. The textfield and popover contain the same MenuItem components, only thing different is the popovers.
Code:
const [anchorEl, setAnchorEl] = React.useState(null);
const handlePopoverClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handlePopoverClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
// New section
<Fab
className={classes.fabStyle}
size="small"
variant="outlined"
onClick={handlePopoverClick}
>
<ExpandMoreIcon />
</Fab>
<Popover
id={id}
open={open}
className={classes.popoverContainer}
anchorEl={anchorEl}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
PaperProps={{ elevation: 1 }}
>
{action.children.map((child) => {
if (child.type === "link") {
console.log(child.to);
return (
<MenuItem>
<Link
style={{
textDecoration: "none",
color:
themes.default.palette
.text.primary,
}}
to={child.to}
>
{child.label}
{child.to}
</Link>
</MenuItem>
);
} else if (
child.type === "dialog"
) {
return (
<MenuItem
onClick={() =>
child.handleOpenDialog(row)
}
>
{child.label}
</MenuItem>
);
}
})}
</Popover>
I made a dropdown using Button and a Popper using Material UI components where you can click on the button and get a list of subjects to choose from. To make the popper disappear either we can click on the button again or use a <ClickAwayListener> component which listens to click event and closes the Popper. Now I've to make the list capable of drag and drop feature so I use the react-beautiful-dnd npm package.But the <ClickAwayListener> doesn't seem to work when I include <DragDropContext>, <Droppable> and <Draggable> components.Can anyone help me figure it out?
Here's the code without drag and drop feature. CodeSandbox link https://codesandbox.io/s/gallant-newton-mfmhd?file=/demo.js
const subjectsFromBackend = [
{ name: "Physics", selected: false },
{ name: "Chemistry", selected: false },
{ name: "Biology", selected: false },
{ name: "Mathematics", selected: false },
{ name: "Computer Science", selected: false },
];
const useStyles = makeStyles((theme) => ({
root: {
display: "flex"
},
paper: {
marginRight: theme.spacing(2)
}
}));
export default function MenuListComposition() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const [subjects, setSubjects] = React.useState(subjectsFromBackend);
const handleToggle = () => {
setOpen(!open);
};
const handleClose = () => {
setOpen(false);
};
const ColumnItem = ({ subjectName, selected }) => {
return (
<>
<Grid container>
<Grid item>
<Checkbox checked={selected} />
</Grid>
<Grid item>{subjectName}</Grid>
</Grid>
</>
);
};
return (
<div className={classes.root}>
<div>
<Button
ref={anchorRef}
onClick={handleToggle}
style={{ width: 385, justifyContent: "left", textTransform: "none" }}
>
{`Subjects Selected`}
</Button>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === "bottom" ? "center top" : "center bottom"
}}
>
<Paper style={{ maxHeight: 200, overflow: "auto", width: 385 }}>
<ClickAwayListener onClickAway={handleClose}>
<List>
{subjects.map((col, index) => (
<ListItem>
<ColumnItem
subjectName={col.name}
selected={col.selected}
/>
</ListItem>
))}
</List>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
</div>
);
}
Here's the same code using drag and drop. CodeSandbox Link https://codesandbox.io/s/material-demo-forked-ertti
const subjectsFromBackend = [
{ name: "Physics", selected: false },
{ name: "Chemistry", selected: false },
{ name: "Biology", selected: false },
{ name: "Mathematics", selected: false },
{ name: "Computer Science", selected: false },
];
const useStyles = makeStyles((theme) => ({
root: {
display: "flex"
},
paper: {
marginRight: theme.spacing(2)
}
}));
export default function MenuListComposition() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const [subjects, setSubjects] = React.useState(subjectsFromBackend);
const handleToggle = () => {
setOpen(!open);
};
const handleClose = () => {
setOpen(false);
};
const ColumnItem = ({ subjectName, selected }) => {
return (
<>
<Grid container>
<Grid item>
<Checkbox checked={selected} />
</Grid>
<Grid item>{subjectName}</Grid>
</Grid>
</>
);
};
const onDragEnd = (result, subjects, setSubjects) => {
const { source, destination } = result;
if (!destination) return;
if (source.droppableId !== destination.droppableId) return;
const copiedItems = [...subjects];
const [removed] = copiedItems.splice(source.index, 1);
copiedItems.splice(destination.index, 0, removed);
setSubjects(copiedItems);
};
return (
<div className={classes.root}>
<div>
<Button
ref={anchorRef}
onClick={handleToggle}
style={{ width: 385, justifyContent: "left", textTransform: "none" }}
>
{`Subjects Selected`}
</Button>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === "bottom" ? "center top" : "center bottom"
}}
>
<DragDropContext
onDragEnd={(res) => onDragEnd(res, subjects, setSubjects)}
>
<Paper style={{ maxHeight: 200, overflow: "auto", width: 385 }}>
<ClickAwayListener onClickAway={handleClose}>
<Droppable droppableId={"subjectsColumn"}>
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
<List>
{subjects.map((col, index) => (
<Draggable
key={col.name}
draggableId={col.name}
index={index}
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<ListItem>
<ColumnItem
subjectName={col.name}
selected={col.selected}
/>
</ListItem>
</div>
)}
</Draggable>
))}
</List>
{provided.placeholder}
</div>
)}
</Droppable>
</ClickAwayListener>
</Paper>
</DragDropContext>
</Grow>
)}
</Popper>
</div>
</div>
);
}
I found out that the ClickAwayListener's child component should be wrapped around a div so that the click event could be triggered properly.
You need to have your ClickAwayListener at the top when you are using the Drag and Drop.
return (
<ClickAwayListener
onClickAway={() => {
console.log("i got called");
handleClose();
}}
>
.....
</ClickAwayListener>
Here is the working codesandbox - https://codesandbox.io/s/material-demo-forked-h1o1s?file=/demo.js:4877-4897
I have a NotificationStepper Component which gets inputs from user, I need to update this input value in run time in another component named TabComponent. Both of the components are child components of SendNotification Component. How can i achieve that in reactjs.
EDIT
NotificationStepper.js
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
}
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
function getSteps() {
return ['Create', 'Audience', 'Timing'];
}
function getStepContent(step, $this) {
switch (step) {
case 0:
return (
<div className="row">
<CardBox styleName="col-lg-12"
heading="">
<form className="row" noValidate autoComplete="off" style={{"flex-wrap":"no-wrap", "flex-direction": "column" }}>
<div className="col-md-12 col-12">
<TextField
id="campaign_name"
label="Campaign Name"
value={$this.state.name}
onChange={$this.handleChange('name')}
margin="normal"
fullWidth
/>
</div>
</form>
</CardBox>
</div>
);
default:
return 'Unknown step';
}
}
class NotificationStepper extends React.Component {
state = {
activeStep: 0,
name: '',
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
handleNext = () => {
this.setState({
activeStep: this.state.activeStep + 1,
});
};
handleBack = () => {
this.setState({
activeStep: this.state.activeStep - 1,
});
};
handleReset = () => {
this.setState({
activeStep: 0,
});
};
render() {
const steps = getSteps();
const {activeStep} = this.state;
return (
<div className="col-xl-12 col-lg-12 col-md-7 col-12">
<Stepper className="MuiPaper-root-custom" activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
return (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent className="pb-3">
<Typography>{getStepContent(index, this)}</Typography>
<div className="mt-2">
<div>
<Button
disabled={activeStep === 0}
onClick={this.handleBack}
className="jr-btn"
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={this.handleNext}
className="jr-btn"
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length && (
<Paper square elevation={0} className="p-2">
<Typography>All steps completed - you"re finished</Typography>
<Button onClick={this.handleReset} className="jr-btn">
Reset
</Button>
</Paper>
)}
</div>
);
}
}
export default NotificationStepper;
TabComponent.js
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
dir: PropTypes.string.isRequired,
};
class TabComponent extends Component {
state = {
value: 0,
};
render() {
const {theme} = this.props;
return (
<div className="col-xl-12 col-lg-12 col-md-12 col-12" style={{"margin-top": "15px"}}>
<NotifCard key={0} data={{'name': 'Title of Notification', 'company': 'sayge.ai', 'image': require("assets/images/chrome.png"), 'description': 'Notification Message here'}} styleName="card shadow "/>
</div>
);
}
}
TabComponent.propTypes = {
theme: PropTypes.object.isRequired,
};
export default withStyles(null, {withTheme: true})(TabComponent);
and this is my SendNotification.js
const SendNotification = ({match}) => {
return (
<div className="dashboard animated slideInUpTiny animation-duration-3">
<ContainerHeader match={match} title={<IntlMessages id="sidebar.notification"/>}/>
<div className="row" style={{'flex-wrap': 'no wrap', "flex-direction": 'row'}}>
<div className="col-xl-7 col-lg-7 col-md-7 col-7">
<NotificationStepper/>
<div className='flex-class' style={{'width': '100%'}}>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2">Fetch</Button>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2" color="primary">Discard</Button>
</div>
</div>
<div className="col-xl-5 col-lg-5 col-md-5 col-5" style={{"padding-top": "20px"}}>
<span style={{"margin-left" : "20px", "font-weight": "bold"}}>Preview</span>
<TabComponent />
</div>
</div>
</div>
);
};
export default SendNotification;
i need to get the name value from NotificationStepper and Update it in pass it to in TabComponent.
There are plenty of tutorials about that. For instance, this.
import React, { Component, createRef } from "react";
class CustomTextInput extends Component {
textInput = createRef();
focusTextInput = () => this.textInput.current.focus();
render() {
return (
<>
<input type="text" ref={this.textInput} />
<button onClick={this.focusTextInput}>Focus the text input</button>
</>
);
}
}
import React, { useRef } from "react";
const CustomTextInput = () => {
const textInput = useRef();
focusTextInput = () => textInput.current.focus();
return (
<>
<input type="text" ref={textInput} />
<button onClick={focusTextInput}>Focus the text input</button>
</>
);
}
I have a child component StepperNotification which gets an input from user and returns it as prop to its child component in following way.
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
}
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
function getSteps() {
return ['Create', 'Audience', 'Timing'];
}
function getStepContent(step, $this) {
switch (step) {
case 0:
return (
<div className="row">
<CardBox styleName="col-lg-12"
heading="">
<form className="row" noValidate autoComplete="off" style={{"flex-wrap":"no-wrap", "flex-direction": "column" }}>
<div className="col-md-12 col-12">
<TextField
id="campaign_name"
label="Campaign Name"
value={$this.state.name}
onChange={$this.handleChange('name')}
margin="normal"
fullWidth
/>
</div>
</form>
</CardBox>
</div>
);
default:
return 'Unknown step';
}
}
class NotificationStepper extends React.Component {
state = {
activeStep: 0,
name: '',
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
this.props.titlechange(event.target.value);
};
handleNext = () => {
this.setState({
activeStep: this.state.activeStep + 1,
});
};
handleBack = () => {
this.setState({
activeStep: this.state.activeStep - 1,
});
};
handleReset = () => {
this.setState({
activeStep: 0,
});
};
render() {
const steps = getSteps();
const {activeStep} = this.state;
return (
<div className="col-xl-12 col-lg-12 col-md-7 col-12">
<Stepper className="MuiPaper-root-custom" activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
return (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent className="pb-3">
<Typography>{getStepContent(index, this)}</Typography>
<div className="mt-2">
<div>
<Button
disabled={activeStep === 0}
onClick={this.handleBack}
className="jr-btn"
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={this.handleNext}
className="jr-btn"
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length && (
<Paper square elevation={0} className="p-2">
<Typography>All steps completed - you"re finished</Typography>
<Button onClick={this.handleReset} className="jr-btn">
Reset
</Button>
</Paper>
)}
</div>
);
}
}
export default NotificationStepper;
In my parent component i am getting this prop value and passing it to another child component Tabcomponent in following way
ParentCompoennt.js
const SendNotification = ({match}) => {
let titlename = '';
let message = '';
function handleTitle(title_) {
console.log('in here');
console.log(title_);
titlename = title_;
}
return (
<div className="dashboard animated slideInUpTiny animation-duration-3">
<ContainerHeader match={match} title={<IntlMessages id="sidebar.notification"/>}/>
<div className="row" style={{'flex-wrap': 'no wrap', "flex-direction": 'row'}}>
<div className="col-xl-7 col-lg-7 col-md-7 col-7">
<NotificationStepper titlechange={handleTitle} />
<div className='flex-class' style={{'width': '100%'}}>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2">Fetch</Button>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2" color="primary">Discard</Button>
</div>
</div>
<div className="col-xl-5 col-lg-5 col-md-5 col-5" style={{"padding-top": "20px"}}>
<span style={{"margin-left" : "20px", "font-weight": "bold"}}>Preview</span>
<TabComponent {...{[title]:titlename}} message={message} />
</div>
</div>
</div>
);
};
export default SendNotification;
and in TabComponent i am getting this prop value and using it component in following way
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
dir: PropTypes.string.isRequired,
};
class TabComponent extends Component {
state = {
value: 0,
};
render() {
const {theme} = this.props;
const title = this.props.title;
return (
<div className="col-xl-12 col-lg-12 col-md-12 col-12" style={{"margin-top": "15px"}}>
<NotifCard key={0} data={{'name': title, 'company': 'sayge.ai', 'image': require("assets/images/bell.png"), 'description': this.props.message}} styleName="card shadow "/>
</div>
);
}
}
TabComponent.propTypes = {
theme: PropTypes.object.isRequired,
};
export default withStyles(null, {withTheme: true})(TabComponent);
StepperNotification is working fine and props are being updated in parent. i have checked this by printing the updated values in console, but the props in TabComponent are not being updated. What am i doing wrong here? any help is appreciated.
Probaly this is your issue
<TabComponent {...{[title]:titlename}} message={message} />` this could be just
title is undefined and you are sending props[undefined]=titlename
Just do this
<TabComponent title={titlename} message={message} />`
And if SendNotification is react fuction component useState for keeping track of current titlename. If it still doesn't work after first fix this second will be another source of your problems.