Material UI: dialog with Groupbutton - reactjs

I have this file and I'm trying to have a "group batten", and when I click on it, I have a list with ["Confirm Review", "Reject Invoice", "Assign to User"] and I want when I press "Confirm Review" to show me a Dialog, how can I solve this problem?
And this is a file that contains the part of groupButton
import React from "react";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import ButtonGroup from "#material-ui/core/ButtonGroup";
import ArrowDropDownIcon from "#material-ui/icons/ArrowDropDown";
import ClickAwayListener from "#material-ui/core/ClickAwayListener";
import Grow from "#material-ui/core/Grow";
import Paper from "#material-ui/core/Paper";
import Popper from "#material-ui/core/Popper";
import MenuItem from "#material-ui/core/MenuItem";
import MenuList from "#material-ui/core/MenuList";
const options = ["Confirm Review", "Reject Invoice", "Assign to User"];
export default function SplitButton() {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleClick = () => {
console.info(`You clicked ${options[selectedIndex]}`);
};
const handleMenuItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(false);
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
return (
<Grid container direction="column" alignItems="center">
<Grid item xs={12}>
<ButtonGroup
variant="contained"
color="primary"
ref={anchorRef}
aria-label="split button"
>
<Button
style={{
paddingLeft: "2.4rem",
paddinRight: "2.4rem",
paddingTop: "1.5rem",
paddingBottom: "1.5rem",
// backgroundColor: "#d82c2c",
// color: "#FFFFFF",
borderRadius: 4,
}}
onClick={handleClick}
>
{options[selectedIndex] || "Action"}{" "}
</Button>
<Button
color="primary"
size="small"
aria-controls={open ? "split-button-menu" : undefined}
aria-expanded={open ? "true" : undefined}
aria-label="select merge strategy"
aria-haspopup="menu"
onClick={handleToggle}
style={{
paddingTop: "1.5rem",
paddingBottom: "1.5rem",
// backgroundColor: "#d82c2c",
// color: "#FFFFFF",
borderRadius: 4,
}}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === "bottom" ? "center top" : "center bottom",
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu">
{options.map((option, index) => (
<MenuItem
key={option}
disabled={index === 2}
selected={index === selectedIndex}
onClick={(event) => handleMenuItemClick(event, index)}
>
{option}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</Grid>
</Grid>
);
}

First, is that MUI v4?
Also, why use style prop when sx (v5) and makeStyle (v4) are a thing.
A high-level overview of what you want, minus all those decorative styles and props.
import { Fragment, useState } from "react";
import { ButtonGroup, Button, Dialog, DialogContent } from "#mui/material";
const ButtonGroupWithDialog = () => {
const [dialogOpen, setDialogOpen] = useState(false);
const handleDialogOpen = () => setDialogOpen(true);
const handleDialogClose = () => setDialogOpen(false);
return (
<Fragment>
<ButtonGroup>
<Button onClick={handleDialogOpen}>Confirm Review</Button>
<Button>Reject Invoice</Button>
<Button>Assign to User</Button>
</ButtonGroup>
<Dialog open={dialogOpen} onClose={handleDialogClose}>
<DialogContent>
{/* content here */}
</DialogContent>
</Dialog>
</Fragment>
);
};

Related

Why after switching views, React-Leaflet map grayed out? It happens after interaction with the map and then switching between views

I was making a dialog which has two tabs named Address and Map, which allows the user to enter city name and street name in two different textfields in address tab and be able to pinpoint or auto-locate the location in the map. In map tab I was using react-leaflet map to show the map itself to the user, so far so good but after switching between tabs the map changes to a monotonic gray image. zoom in and out won't help it!
Code:
import * as React from 'react';
import { useEffect, useState } from 'react';
import Button from '#mui/material/Button';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogTitle from '#mui/material/DialogTitle';
import useMediaQuery from '#mui/material/useMediaQuery';
import CloseIcon from '#mui/icons-material/CloseOutlined';
import { Divider, IconButton, InputLabel, } from '#mui/material';
import { Box, Grid, Tab, TextField, useTheme } from '#material-ui/core';
import { TabContext, TabList, TabPanel } from '#mui/lab';
import "leaflet/dist/leaflet.css";
import icon from "../../../../Account/components/constants";
import { MapContainer, TileLayer, Marker, useMapEvents, } from 'react-leaflet'
const useGeoLocation = () => {
// this function will allow the user to get the current location of the device!
const [location, setLocation] = useState({
loaded: false,
coordinates: { lat: "", lng: "" }
});
const onSuccess = (location) => {
setLocation({
loaded: true,
coordinates: {
lat: location.coords.latitude,
lng: location.coords.longitude,
}
});
};
const onError = (error) => {
setLocation({
loaded: true,
error,
});
};
useEffect(() => {
if (!("geolocation" in navigator)) {
onError({
code: 0,
message: "Your device GPS is OFF!",
});
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);
}, []);
return location;
}
export default function AddressDialog() {
// Genral Properties!
const [open, setOpen] = useState(false);
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const [value, setValue] = useState(0);
// Address Tab Properties!
const [city, setCity] = useState("");
const [street, setStreet] = useState();
// Map Properties!
const [initializerPos,] = useState([40.689247, -74.044502]);
const [position, setPosition] = useState(initializerPos);
const [mapState, setMapState] = useState({
position: position,
map: null
});
const zoom_level = 18;
const location = useGeoLocation();
// Arrow funcitons!
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
setValue(0);
};
const handleSubmit = () => {
// api update in here
}
const showMyLocation = () => {
if (location.loaded && !location.error) {
let pos = [location.coordinates.lat, location.coordinates.lng];
setPosition([location.coordinates.lat, location.coordinates.lng]);
setMapState((state) => ({
...state,
position: pos,
}));
const { map } = mapState;
if (map) {
map.flyTo(pos, zoom_level);
}
} else {
if (location.error) {
alert(location.error.message)
}
else {
alert("Problem in loading curent location!")
}
}
};
function AddMarkerToClick() {
const [markers, setMarkers] = useState([]);
useMapEvents({
click(e) {
const newMarker = e.latlng
setMarkers([...markers, newMarker]);
setPosition([e.latlng.lat, e.latlng.lng]);
setMapState((state) => ({
...state,
position: newMarker,
}));
const { map } = mapState;
if (map)
{
map.flyTo(newMarker, zoom_level);
}
},
});
return null
};
return (
<div dir="ltr">
<Button onClick={handleClickOpen} variant="contained" type="button" aria-label="Edit Info" fullWidth size="small">
Edit Address Info
</Button>
<Dialog fullScreen={fullScreen} open={open} aria-labelledby="responsive-dialog-title">
<DialogTitle>
<IconButton onClick={handleClose} aria-label="Close Dialog">
<CloseIcon fontSize="medium" />
</IconButton>
</DialogTitle>
<Divider />
<DialogTitle>Edit Address</DialogTitle>
<DialogContent id ="dialogContent" >
<DialogContentText>
In this section you are able to edit your address info
</DialogContentText>
<TabContext value={value.toString()} >
<Box >
<TabList
onChange={(event, newValue) => { setValue(parseInt(newValue, 10));}}
aria-label="address-map-tab">
<Tab label="Address" value="0" />
<Tab label="Map" value="1" />
</TabList>
</Box>
<TabPanel value="0">
<Grid container spacing={theme.spacing(0)}
>
<Grid item xs={12} sm={6}>
<TextField value={city} onChange={(e) => setCity(e.target.value)} margin="normal" variant="outlined"
required
fullWidth
type="text"
name="area"
id="area"
label={"city"}
placeholder={"ex: New York"}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
value={street}
onChange={(e) => setStreet(e.target.value)}
margin="normal"
variant="outlined"
required
fullWidth
type="text"
name="street"
id="street"
label={"Streen Name"}
placeholder={"ex: wall street"}
/>
</Grid>
</Grid>
</TabPanel>
<TabPanel value="1">
<Grid container>
<div style={{
marginLeft: "auto",
marginRight: "auto",
width: "100%"
}}>
<InputLabel>
Your location in map:
</InputLabel>
<MapContainer
center={mapState.position}
zoom ={15}
scrollWheelZoom
style={{
height: fullScreen ? 200 : 350,
width: fullScreen ? "100%" : "100%",
textAlign: "center",
marginLeft: "auto",
marginRight: "auto",
marginTop: theme.spacing(1)
}}
whenCreated={map => setMapState({ map })}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<AddMarkerToClick />
{
position && (
<Marker position={position} icon={icon}></Marker>
)
}
</MapContainer>
<Button variant="outlined" color="primary" size="large" onClick={showMyLocation}>
Here!
</Button>
</div>
</Grid>
</TabPanel>
</TabContext>
</DialogContent>
<DialogActions style={{ marginLeft: theme.spacing(2), marginRight: theme.spacing(2) }}>
<Grid container direction="row" spacing={1}>
<Grid item container xs={4} dir="left" justifyContent="flex-end">
<Button variant="contained" type="button" color="error" fullWidth
name="cancel-btn" onClick={handleClose}
>
Cancel
</Button>
</Grid>
<Grid item container xs={8} >
<Button variant="contained" type="button" color="primary" fullWidth
name="submit-btn" onClick={handleSubmit} >
Save
</Button>
</Grid>
</Grid>
</DialogActions>
</Dialog>
</div>
);
After switching between tabs, the map grayed out, and shows nothing!
Then interacting with map, e.g, clicking on it, zoom in or out results the error!
Uncaught Error: Set map center and zoom first.
My Solution:
What I did is to save the current (after first interaction with map) state of the map and then try to restart it from saved state for future use (switching to other tabs/views).
To save the state of the map, It should happen right exactly before switching out from the map view! In this case before switching back to address tab or closing The Dialog.
Fixed Code:
import * as React from 'react';
import { useEffect, useState } from 'react';
import Button from '#mui/material/Button';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogTitle from '#mui/material/DialogTitle';
import useMediaQuery from '#mui/material/useMediaQuery';
import CloseIcon from '#mui/icons-material/CloseOutlined';
import { Divider, IconButton, InputLabel, } from '#mui/material';
import { Box, Grid, Tab, TextField, useTheme } from '#material-ui/core';
import { TabContext, TabList, TabPanel } from '#mui/lab';
import "leaflet/dist/leaflet.css";
import icon from "../../../../Account/components/constants";
import { MapContainer, TileLayer, Marker, useMapEvents, } from 'react-leaflet'
const useGeoLocation = () => {
// this function will allow the user to get the current location of the device!
const [location, setLocation] = useState({
loaded: false,
coordinates: { lat: "", lng: "" }
});
const onSuccess = (location) => {
setLocation({
loaded: true,
coordinates: {
lat: location.coords.latitude,
lng: location.coords.longitude,
}
});
};
const onError = (error) => {
setLocation({
loaded: true,
error,
});
};
useEffect(() => {
if (!("geolocation" in navigator)) {
onError({
code: 0,
message: "Your device GPS is OFF!",
});
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);
}, []);
return location;
}
export default function AddressDialog() {
// Genral Properties!
const [open, setOpen] = useState(false);
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const [value, setValue] = useState(0);
// Address Tab Properties!
const [city, setCity] = useState("");
const [street, setStreet] = useState();
// Map Properties!
const [initializerPos,] = useState([40.689247, -74.044502]);
const [position, setPosition] = useState(initializerPos);
const [mapState, setMapState] = useState({
position: position,
map: null
});
const zoom_level = 18;
const location = useGeoLocation();
// Two new properties to overcome the gray out problem.
const initializeZoom_level = 15;
const [mapZoom, setMapZoom] = useState(initializeZoom_level);
// Arrow funcitons!
const stabilizeMapData = (isItADialogClose ) => {
setMapZoom(isItADialogClose ? initializeZoom_level : mapZoom);
setPosition(isItADialogClose ? initializerPos : position);
setMapState({
position: isItADialogClose ? initializerPos : position,
map: null
});
}
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
stabilizeMapData(true); // The user is about to close The Dialog!
setOpen(false);
setValue(0);
};
const handleSubmit = () => {
// api update in here
}
const showMyLocation = () => {
if (location.loaded && !location.error) {
let pos = [location.coordinates.lat, location.coordinates.lng];
setPosition([location.coordinates.lat, location.coordinates.lng]);
setMapState((state) => ({
...state,
position: pos,
}));
const { map } = mapState;
if (map) {
map.flyTo(pos, zoom_level);
setMapZoom(zoom_level);
}
} else {
if (location.error) {
alert(location.error.message)
}
else {
alert("Problem in loading curent location!")
}
}
};
function AddMarkerToClick() {
const [markers, setMarkers] = useState([]);
useMapEvents({
click(e) {
const newMarker = e.latlng
setMarkers([...markers, newMarker]);
setPosition([e.latlng.lat, e.latlng.lng]);
setMapState((state) => ({
...state,
position: newMarker,
}));
const { map } = mapState;
if (map)
{
map.flyTo(newMarker, zoom_level);
setMapZoom(zoom_level);
}
},
});
return null
};
return (
<div dir="ltr">
<Button onClick={handleClickOpen} variant="contained" type="button" aria-label="Edit Info" fullWidth size="small">
Edit Address Info
</Button>
<Dialog fullScreen={fullScreen} open={open} aria-labelledby="responsive-dialog-title">
<DialogTitle>
<IconButton onClick={handleClose} aria-label="Close Dialog">
<CloseIcon fontSize="medium" />
</IconButton>
</DialogTitle>
<Divider />
<DialogTitle>Edit Address</DialogTitle>
<DialogContent id ="dialogContent" >
<DialogContentText>
In this section you are able to edit your address info
</DialogContentText>
<TabContext value={value.toString()} >
<Box >
<TabList
onChange={(event, newValue) => {
if (value === 1) {
// if it is in The Map Tab and It's about to switch the tab!
stabilizeMapData(false);
}
setValue(parseInt(newValue, 10));
}}
aria-label="address-map-tab">
<Tab label="Address" value="0" />
<Tab label="Map" value="1" />
</TabList>
</Box>
<TabPanel value="0">
<Grid container spacing={theme.spacing(0)}
>
<Grid item xs={12} sm={6}>
<TextField value={city} onChange={(e) => setCity(e.target.value)} margin="normal" variant="outlined"
required
fullWidth
type="text"
name="area"
id="area"
label={"city"}
placeholder={"ex: New York"}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
value={street}
onChange={(e) => setStreet(e.target.value)}
margin="normal"
variant="outlined"
required
fullWidth
type="text"
name="street"
id="street"
label={"Streen Name"}
placeholder={"ex: wall street"}
/>
</Grid>
</Grid>
</TabPanel>
<TabPanel value="1">
<Grid container>
<div style={{
marginLeft: "auto",
marginRight: "auto",
width: "100%"
}}>
<InputLabel>
Your location in map:
</InputLabel>
<MapContainer
center={mapState.position}
zoom={mapZoom}
scrollWheelZoom
style={{
height: fullScreen ? 200 : 350,
width: fullScreen ? "100%" : "100%",
textAlign: "center",
marginLeft: "auto",
marginRight: "auto",
marginTop: theme.spacing(1)
}}
whenCreated={map => setMapState({ map })}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<AddMarkerToClick />
{
position && (
<Marker position={position} icon={icon}></Marker>
)
}
</MapContainer>
<Button variant="outlined" color="primary" size="large" onClick={showMyLocation}>
Here!
</Button>
</div>
</Grid>
</TabPanel>
</TabContext>
</DialogContent>
<DialogActions style={{ marginLeft: theme.spacing(2), marginRight: theme.spacing(2) }}>
<Grid container direction="row" spacing={1}>
<Grid item container xs={4} dir="left" justifyContent="flex-end">
<Button variant="contained" type="button" color="error" fullWidth
name="cancel-btn" onClick={handleClose}
>
Cancel
</Button>
</Grid>
<Grid item container xs={8} >
<Button variant="contained" type="button" color="primary" fullWidth
name="submit-btn" onClick={handleSubmit} >
Save
</Button>
</Grid>
</Grid>
</DialogActions>
</Dialog>
</div>
);
}
I did some comments in the code to demonstrate the solution. If you know a better solution to this problem I looking forward to see it.

How to make Material UI Temporary Drawer button as icon instead of text?

I'm using Material UI to create a navigation bar with Temporary Drawer. When the user clicks on the hamburger menu icon, I want the menu to fade-in to the screen and slide from the right.
Basically, all the functionality works, except only the button named 'RIGHT' works instead of the hamburger icon I have created beside it..
I have tried removing 'right' and replace it with the icon, but when I do that the menu comes out from left-top...
import React, { useEffect, useState } from 'react'
import { makeStyles } from '#material-ui/core/styles';
import { AppBar, IconButton, Toolbar, Typography, Collapse } from '#material-ui/core';
import SortIcon from '#material-ui/icons/Sort';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import { Link as Scroll } from 'react-scroll'
import clsx from 'clsx';
import Drawer from '#material-ui/core/Drawer';
import Button from '#material-ui/core/Button';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import InboxIcon from '#material-ui/icons/MoveToInbox';
import MailIcon from '#material-ui/icons/Mail';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: "100vh",
fontFamily: "Roboto",
},
appbar: {
background: "none",
},
appbarWrapper: {
width: "80%",
margin: "0 auto"
},
appbarTitle: {
fontSize: "2rem",
flexGrow: '1',
},
icon: {
color: '#fff',
fontSize: "2rem",
},
colorText: {
color: "#5AFF3D",
},
container: {
textAlign: "center",
},
title: {
color: "#fff",
fontSize: "4.5rem",
},
goDown: {
color: '#5AFF3D',
fontSize: '4rem',
},
list: { //NEW
width: 250,
},
fullList: {
width: 'auto',
},
}));
export default function Header() {
const classes = useStyles();
const [checked, setChecked] = useState(false);
useEffect(() => {
setChecked(true);
}, [])
// NEW
const [state, setState] = React.useState({
right: false,
});
const toggleDrawer = (anchor, open) => (event) => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return;
}
setState({ ...state, [anchor]: open });
};
const list = (anchor) => (
<div
className={clsx(classes.list, {
[classes.fullList]: anchor === 'top' || anchor === 'bottom',
})}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
/*
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
*/
return (
<div className={classes.root} id="header">
<AppBar className={classes.appbar} elevation={0}>
<Toolbar className={classes.appbarWrapper}>
<h1 className={classes.appbarTitle}>
We<span className={classes.colorText}>cycle</span>
</h1>
<IconButton>
<SortIcon className={classes.icon} /*onClick={handleClick} aria-control="fade-menu" aria-haspopup="true"*/ />
{['right'].map((anchor) => (
<React.Fragment key={anchor}>
<Button onClick={toggleDrawer(anchor, true)}>{anchor}</Button>
<Drawer anchor={anchor} open={state[anchor]} onClose={toggleDrawer(anchor, false)}>
{list(anchor)}
</Drawer>
</React.Fragment>
))}
</IconButton>
{/* <Menu
id="fade-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
TransitionComponent={Fade}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu> */}
</Toolbar>
</AppBar>
<Collapse
in={checked} {...(checked ? { timeout: 1000 } : {})}
collapsedHeight={50}
>
<div className={classes.container}>
<h1 className={classes.title}>
Meet the <br /> <span className={classes.colorText}>Team </span>
</h1>
<Scroll to="place-to-visit" smooth={true}>
<IconButton>
<ExpandMoreIcon className={classes.goDown} />
</IconButton>
</Scroll>
</div>
</Collapse>
</div>
);
}
I've made your code work for what you need and left some comments inside. I don't know what you ideally want to make, but don't copy all the code from the MUI example which you might not fully understand.
const [anchorEl, setAnchorEl] = useState(false);
// const open = Boolean(anchorEl);
const handleDrawerOpen = () => {
setAnchorEl(true);
};
const handleDrawerClose = () => {
setAnchorEl(false);
};
Above is used to control the state of Drawer.
Following is the code that you can just copy.
import React, { useEffect, useState } from 'react'
import { makeStyles } from '#material-ui/core/styles';
import { AppBar, IconButton, Toolbar, Typography, Collapse } from '#material-ui/core';
import SortIcon from '#material-ui/icons/Sort';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import { Link as Scroll } from 'react-scroll'
import clsx from 'clsx';
import Drawer from '#material-ui/core/Drawer';
import Button from '#material-ui/core/Button';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import InboxIcon from '#material-ui/icons/MoveToInbox';
import MailIcon from '#material-ui/icons/Mail';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: "100vh",
fontFamily: "Roboto",
},
appbar: {
background: "none",
},
appbarWrapper: {
width: "80%",
margin: "0 auto"
},
appbarTitle: {
fontSize: "2rem",
flexGrow: '1',
},
icon: {
color: '#fff',
fontSize: "2rem",
},
colorText: {
color: "#5AFF3D",
},
container: {
textAlign: "center",
},
title: {
color: "#fff",
fontSize: "4.5rem",
},
goDown: {
color: '#5AFF3D',
fontSize: '4rem',
},
list: { //NEW
width: 250,
},
fullList: {
width: 'auto',
},
}));
export default function Header() {
const classes = useStyles();
const [checked, setChecked] = useState(false);
useEffect(() => {
setChecked(true);
}, [])
// NEW
const [state, setState] = React.useState({
right: false,
});
const toggleDrawer = (anchor, open) => (event) => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return;
}
setState({ ...state, [anchor]: open });
};
const list = (anchor) => (
<div
className={clsx(classes.list, {
[classes.fullList]: anchor === 'top' || anchor === 'bottom',
})}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
const [anchorEl, setAnchorEl] = useState(false);
// const open = Boolean(anchorEl);
const handleDrawerOpen = () => {
setAnchorEl(true);
};
const handleDrawerClose = () => {
setAnchorEl(false);
};
return (
<div className={classes.root} id="header">
<AppBar className={classes.appbar} elevation={0}>
<Toolbar className={classes.appbarWrapper}>
<h1 className={classes.appbarTitle}>
We<span className={classes.colorText}>cycle</span>
</h1>
{/* You Don't need the map here unless you want many button to toggle one Drawer,
like the example on https://material-ui.com/components/drawers/.
and the point here for your question is to move the onClick to IconButton, and you may
not want the button inside IconButton, since it is a button already,
the inside button would make it a little ugly */}
<IconButton onClick={handleDrawerOpen} onClose={handleDrawerClose}>
<SortIcon className={classes.icon} /*onClick={handleClick} aria-control="fade-menu" aria-haspopup="true"*/ />
RIGHT{/* delete the text here if you don't need */}
</IconButton>
<Drawer anchor='right' open={anchorEl} onClose={handleDrawerClose}>
{list('right')}
</Drawer>
{/* <Menu
id="fade-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
TransitionComponent={Fade}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu> */}
</Toolbar>
</AppBar>
<Collapse
in={checked} {...(checked ? { timeout: 1000 } : {})}
collapsedHeight={50}
>
<div className={classes.container}>
<h1 className={classes.title}>
Meet the <br /> <span className={classes.colorText}>Team </span>
</h1>
<Scroll to="place-to-visit" smooth={true}>
<IconButton>
<ExpandMoreIcon className={classes.goDown} />
</IconButton>
</Scroll>
</div>
</Collapse>
</div>
);
}
Recomandation: since you have import React, { useEffect, useState } from 'react', you may not use React.useState() but use useState() directly.

Material UI fixed MenuItem

I'm wondering is there possibility to add fixed MenuItem to Menu? I'd like to use one of MenuItems to show header. Also if there's another solutions I'm open to try them too.
Here's the structure:
<Menu>
<MenuItem className={classes.header}>This is header</MenuItem>
<MenuItem>Item</MenuItem>
<MenuItem>Item</MenuItem>
<MenuItem>Item</MenuItem>
</Menu>
I tried to set header MenuItem's position to fixed but it throws whole Menu top of the page.
header: {
position: 'fixed',
},
EDIT: To clear a bit what I'm looking for. GitHub has same kind of menu:
You can use position sticky but you will need to "adjust" z-index too because of this issue:
so you can do something like this (based on material ui example):
import React from "react";
import IconButton from "#material-ui/core/IconButton";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
import { makeStyles } from "#material-ui/core/styles";
import MoreVertIcon from "#material-ui/icons/MoreVert";
import { Paper, TextField } from "#material-ui/core";
const useStyles = makeStyles({
header: {
position: "sticky",
top: 0,
backgroundColor: "white",
zIndex: 2
},
search: {
marginBottom: "5px",
},
card: {
width: "100%"
}
});
const options = [
"None",
"Atria",
"Callisto",
"Dione",
"Ganymede",
"Hangouts Call",
"Luna",
"Oberon",
"Phobos",
"Pyxis",
"Sedna",
"Titania",
"Triton",
"Umbriel"
];
const ITEM_HEIGHT = 48;
export default function LongMenu() {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const classes = useStyles();
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu
elevation={1}
className={classes.menu}
id="long-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: "800px",
backgroundColor: "white"
}
}}
>
<div className={classes.header}>
<Paper className={classes.card} elevation={3}>
<TextField
className={classes.search}
label={"search filter"}
variant={"outlined"}
></TextField>
</Paper>
</div>
{options.map((option) => (
<MenuItem
key={option}
selected={option === "Pyxis"}
onClick={handleClose}
>
{option}
</MenuItem>
))}
</Menu>
</div>
);
}
here a sandbox link
this is the result after adding z-index:

How to implement edit photo like LinkedIn edit background feature in React.js?

Anybody has experience or idea how to implement an edit photo feature like in LinkedIn edit photo or background in React.js?
Here's the functionalities I wanted.
Can upload image
Show image
Edit uploaded image
zoom-in/zoom-out
rotate
delete
Use react-cropper package which is a react version of cropper.js. You can perform all the functionalities you need using that. There are some changes required in this code also add rotate functionality from cropper.js.
import React, { useState, useRef } from "react";
import Cropper from "react-cropper";
import Button from "#material-ui/core/Button";
import "cropperjs/dist/cropper.css";
import Grid from '#material-ui/core/Grid';
import IconButton from '#material-ui/core/IconButton';
import Tooltip from '#material-ui/core/Tooltip';
import CheckCircleIcon from '#material-ui/icons/CheckCircle';
import ArrowForwardIcon from '#material-ui/icons/ArrowForward';
import ArrowBackIcon from '#material-ui/icons/ArrowBack';
import ArrowDownwardIcon from '#material-ui/icons/ArrowDownward';
import ArrowUpwardIcon from '#material-ui/icons/ArrowUpward';
import CancelIcon from '#material-ui/icons/Cancel';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
button: {
backgroundColor: "#2774b8",
'&:hover': {
backgroundColor: "#2774b8",
}
},
toolsCtr: {
display: 'flex',
padding: '0.5rem'
},
tooltip: {
marginRight: '0.5rem'
},
icnBtn: {
padding: '8px',
borderRadius: '3px',
backgroundColor: '#f0f8ff'
},
icn: {
fontSize: '1.5rem',
color: '#2774b8'
},
cropperCtr: {
border: '2px solid #d9e5f0'
}
}))
export default function Crop(props) {
const cropperRef = useRef();
const classes = useStyles();
const [uploadedImg, setUploadedImg] = useState(props && props.image)
const [croppedLiveUrl, setCoppedLiveUrl] = useState("");
const [croppedData, setCoppedData] = useState({ url: "", data: {} });
const selForProcessing = () => {
props.useImage(croppedLiveUrl);
}
const _onMoveCropBox = () => {
if (null !== cropperRef.current) {
setCoppedLiveUrl(cropperRef.current.cropper.getCroppedCanvas().toDataURL());
setCoppedData({ url: "", data: {} });
}
};
const _onMoveImageLeft = () => {
if (null !== cropperRef.current) {
cropperRef.current.cropper.move(-10, 0);
}
}
const _onMoveImageRight = () => {
if (null !== cropperRef.current) {
cropperRef.current.cropper.move(10, 0);
}
}
const _onMoveImageTop = () => {
if (null !== cropperRef.current) {
cropperRef.current.cropper.move(0, -10);
}
}
const _onMoveImageBottom = () => {
if (null !== cropperRef.current) {
cropperRef.current.cropper.move(0, 10);
}
}
return (
<React.Fragment>
<div className={classes.cropperCtr}>
<Grid container spacing={2}>
<Grid item xs={12} sm={12} md={12} lg={12}>
<Cropper
ref={cropperRef}
src={uploadedImg}
style={{ height: 400, width: "100%", overflow:
'scroll' }}
guides={false}
crop={() => { _onMoveCropBox() }}
crossOrigin={"true"}
autoCrop={false}
movable={true}
move={() => { _onMoveImageTop() }}
/>
</Grid>
</Grid>
<div className={classes.toolsCtr}>
<Tooltip title="Use Selection" aria-label="use" className=.
{classes.tooltip}>
<IconButton className={classes.icnBtn} onClick={() => {
selForProcessing()
}}>
<CheckCircleIcon className={classes.icn} />
</IconButton>
</Tooltip>
<Tooltip title="Move Left" aria-label="left" className=
{classes.tooltip}>
<IconButton className={classes.icnBtn} onClick={() => {
_onMoveImageLeft()
}}>
<ArrowBackIcon className={classes.icn} />
</IconButton>
</Tooltip>
<Tooltip title="Move Right" aria-label="right" className=
{classes.tooltip}>
<IconButton className={classes.icnBtn} onClick={() => {
_onMoveImageRight()
}}>
<ArrowForwardIcon className={classes.icn} />
</IconButton>
</Tooltip>
<Tooltip title="Move Top" aria-label="top" className=.
{classes.tooltip}>
<IconButton className={classes.icnBtn} onClick={() => {
_onMoveImageTop()
}}>
<ArrowUpwardIcon className={classes.icn} />
</IconButton>
</Tooltip>
<Tooltip title="Move Down" aria-label="down" className={classes.tooltip}>
<IconButton className={classes.icnBtn} onClick={() => {
_onMoveImageBottom()
}}>
<ArrowDownwardIcon className={classes.icn} />
</IconButton>
</Tooltip>
</div>
</div>
</React.Fragment >
);
}

Transfer data to dialog box when clicked

I would like when clicking on my accordion, the data in this line will go into the form of my dialog box. But I am not receiving any data in the form.
Why is the form not receiving the data?
import React, { useEffect, useState } from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import moment from 'moment';
import PerfectScrollbar from 'react-perfect-scrollbar';
import {
Avatar,
Box,
Card,
Accordion,
AccordionSummary,
AccordionDetails,
Grid,
SvgIcon,
InputAdornment,
CardContent,
TextField,
ListItemText,
ListItem,
List,
Checkbox,
Table,
TableBody,
TableCell,
TableHead,
TablePagination,
IconButton,
Typography,
makeStyles,
Button
} from '#material-ui/core';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import getInitials from 'src/utils/getInitials';
import EditProjAdminE from './editProjAdminE';
import AjoutEven from './ajoutEven';
import dataEven from './data';
import SpeedDialTooltipOpen from './speedDialE';
import EditIcon from '#material-ui/icons/Edit';
import AddIcon from '#material-ui/icons/Add';
import { Search as SearchIcon } from 'react-feather';
import { Slide } from 'react-slideshow-image';
import axios from "axios";
import DeleteIcon from '#material-ui/icons/Delete';
const useStyles = makeStyles((theme) => ({
root: {},
absolute: {
position: 'absolute',
bottom: theme.spacing(2),
right: theme.spacing(3),
},
avatar: {
marginRight: theme.spacing(2)
}
}));
const ProjetAdminE = ({ className, dataEleve, ...rest }) => {
const classes = useStyles();
// const [data, setData] = useState(dataEven);
const [filteredEven, setFilteredEven] = useState([]);
const [dataSearch, setDataSearch] = useState([]);
const [loading, setLoading] = useState(false);
const [limit, setLimit] = useState(10);
const [page, setPage] = useState(0);
const [selectedeleveIds, setSelectedeleveIds] = useState([]);
// const dateR = new Date()
// const dateReel = dateR.setDate(dateR.getDate()+1);
const handleLimitChange = (event) => {
setLimit(event.target.value);
};
const handlePageChange = (event, newPage) => {
setPage(newPage);
};
const [search, setSearch] = useState('');
useEffect(() => {
setLoading(true);
axios
.get("http://localhost:8080/employees")
.then((res) => {
setDataSearch(res.data);
setLoading(false);
})
.catch((err) => {
console.log(err);
});
}, []);
const suppression = (id) => {
fetch('http://localhost:8080/evenement/' + id, {
method: 'DELETE',
})
.then(res => res.text())
.then(res => {
// setDataSearch(res.data);
console.log(res)
})
alert(JSON.stringify("événement Numéro " + id + " supprimé"))
}
// const modification =(id) =>{
// fetch('http://localhost:8080/update/' + id, {
// method: 'PUT',
// })
// .then(res => res.text())
// .then(res => console.log(res))
// alert(JSON.stringify("événement Numéro " +id+ " modifié"))
// }
// alert(JSON.stringify(index))
useEffect(() => {
setFilteredEven(
dataSearch.filter((Evenen) =>
Evenen.descrip_evene.toLowerCase().includes(search.toLowerCase())
)
);
}, [search, dataSearch]);
return (
<Card
className={clsx(classes.root, className)}
{...rest}
>
<>
<Box
display="flex"
justifyContent="left"
style={{ height: 30 }}
>
<Typography variant="h3" style={{ margin: 10, color: '#1565C0' }}>
LISTE DES EVENEMENTS
</Typography>
</Box>
<Box mt={3}>
<Card>
<CardContent>
<Box maxWidth={500}>
<TextField
value={search}
onChange={e => {
setSearch(e.target.value);
}}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SvgIcon
fontSize="small"
color="action"
>
<SearchIcon />
</SvgIcon>
</InputAdornment>
)
}}
placeholder="Recherchez un évenement"
variant="outlined"
/>
</Box>
{/* <Button
color="primary"
variant="contained"
onClick = {alert(JSON.stringify(dateReel))}
>
Rechercher
</Button> */}
</CardContent>
</Card>
</Box>
<Grid>
<Grid spacing={1} md={8} xs={12} style={{ margin: 2 }}>
{filteredEven.map((recherche, index) => (
<Accordion style={{ marginTop: 30 }} >
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
style={{ backgroundColor: '#DADFD9' }}
>
<Grid container spacing={1} md={12} xs={12} style={{ margin: 0 }}>
<Grid item md={2} xs={4}>
{recherche.date_even.slice(0, 10)}
</Grid>
<Grid item md={2} xs={2}>
{recherche.prix_even}
</Grid>
<Grid item md={2} xs={3}>
{recherche.statut}
</Grid>
<Grid item md={3} xs={3}>
{recherche.descrip_evene}
</Grid>
<Grid item md={3} xs={8} style={{ marginTop: -10 }}>
<IconButton>
<EditProjAdminE
dataSearch={dataSearch}
setDataSearch={setDataSearch}
id={recherche.id}
/>
</IconButton>
<IconButton
onClick={async () => {
suppression(recherche.id)
window.location.reload(false)
}}
>
<DeleteIcon fontSize="small" style={{ color: 'black' }} />
</IconButton>
{/* <SpeedDialTooltipOpen/> */}
</Grid>
</Grid>
</AccordionSummary>
<AccordionDetails>
<List>
<ListItem>
<ListItemText primary={
<Typography variant="caption" style={{ fontWeight: 'bold', fontSize: 16 }}>
{recherche.id}
</Typography>
}
secondary="Membre concerné"
/>
</ListItem>
<ListItem>
<ListItemText primary={
<Typography variant="caption" style={{ fontWeight: 'bold', fontSize: 16 }}>
{recherche.lieu}
</Typography>
}
secondary="Lieu"
/>
</ListItem>
<ListItem>
<ListItemText primary={
<Typography variant="caption" style={{ fontWeight: 'bold', fontSize: 16 }}>
{recherche.heure}
</Typography>
}
secondary="Heure"
/>
</ListItem>
</List>
</AccordionDetails>
</Accordion>
))}
</Grid>
</Grid>
<AjoutEven />
</>
</Card>
);
};
// Results.propTypes = {
// className: PropTypes.string,
// dataEleve: PropTypes.array.isRequired
// };
export default ProjetAdminE;
editProjetEven.js
import React, { useState } from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import moment from 'moment';
import PerfectScrollbar from 'react-perfect-scrollbar';
import { makeStyles, useTheme, withStyles } from '#material-ui/core/styles';
import {
Button,
Grid,
MenuItem,
DialogContent,
DialogActions,
Dialog,
IconButton,
Avatar,
TextField,
DialogTitle,
} from '#material-ui/core';
import getInitials from 'src/utils/getInitials';
import CreateIcon from '#material-ui/icons/Create';
import EditIcon from '#material-ui/icons/Edit';
import DeleteIcon from '#material-ui/icons/Delete';
const useStyles = makeStyles((theme) => ({
root: {
width: 645,
},
item: {
height: 50
},
buttonDial: {
color: "#fff",
},
buttonAnnuler: {
color: "red"
},
buttonSave: {
background: "#558b2f",
color: "#558b2f",
}
}));
export default function EditProjAdminE(setDataSearch,dataSearch,data,setData,id,index) {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [opens, setOpens] = React.useState(false);
const handleChange = event => {
const { name, value } = event.target
setDataSearch({ ...dataSearch, [name]: value })
}
// const fullScreen = useMediaQuery(theme.breakpoints.down(645));
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
// const handleInputChange = event => {
// const { name, value } = event.target
// setData({ ...data, [name]: value })
// }
// const item = dataSearch.find(id);
return (
<div>
<div display="flex">
<IconButton onClick={handleClickOpen}>
<EditIcon fontSize="small" alt="modifier" style={{ color: '#205723' }} />
</IconButton>
</div>
<Dialog
fullWidth
// fullScreen
open={open }
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
maxWidth = 'md'
>
{/* { index ==="" ? "aucune information":
<> */}
<DialogTitle id="responsive-dialog-title">Virement</DialogTitle>
<DialogContent >
<Grid container spacing={1}>
<Grid item md={4} xs={12}>
<TextField
fullWidth
margin="dense"
type="text"
name="prix_even"
value={dataSearch.prix_even}
// defaultValue={dataSearch.prix_even}
label="Prix"
variant="outlined"
onChange={(event) => handleChange(event)}
/>
</Grid>
<Grid item md={4} xs={12}>
<TextField
fullWidth
margin="dense"
type="text"
name="heure"
value={dataSearch.heure}
label="Heure"
variant="outlined"
onChange={(event) => handleChange(event)}
/>
</Grid>
<Grid item md={4} xs={12}>
<TextField
fullWidth
margin="dense"
type="text"
name="lieu"
value={dataSearch.lieu}
label="Lieu"
variant="outlined"
onChange={(event) => handleChange(event)}
/>
</Grid>
<Grid item md={4} xs={12}>
<TextField
fullWidth
margin="dense"
type="date"
name="date_even"
value={dataSearch.date_even}
helperText="Date de l'événement"
variant="outlined"
onChange={(event) => handleChange(event)}
/>
</Grid>
<Grid item md={4} xs={12}>
<TextField
fullWidth
margin="dense"
type="text"
name="descrip_even"
value={dataSearch.descrip_even}
label="Descr de l'événement"
variant="outlined"
onChange={(event) => handleChange(event)}
/>
</Grid>
<Grid item md={4} xs={12}>
<TextField
fullWidth
margin="dense"
type="text"
name="statut"
value={dataSearch.statut}
label="Statut"
variant="outlined"
onChange={(event) => handleChange(event)}
/>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button
// onClick={alert(JSON.stringify(dataSearch))}
>
Modifier
</Button>
<Button onClick={handleClose} className={classes.buttonAnnuler}>
Fermer
</Button>
</DialogActions>
{/* </>
} */}
</Dialog>
</div>
);
}
on click I pass the index of the object to the array and send it to a new array that I created
<IconButton onClick={async () => {
setData({ ...dataSearch[index] })
handleClickOpen()
}}
>
<EditIcon fontSize="small" alt="modifier" style={{ color: '#205723' }} />
</IconButton>
The handleClickOpen seems to be only opening the dialog box and not invoking setDataSearch({ ...dataSearch, [name]: value }) which seems to be responsible to add values to the dataSearch object that is used in your form fields.
I would personally double check if the EditProjAdminE component is receiving the proper dataSearch and then later call a similar function to handleChange when the open event is triggered.

Resources