I have mui v5 dialog that I am not able to set its width using style() component.
import {
Dialog,
DialogContent,
DialogTitle,
Paper,
Typography,
} from "#mui/material";
import { Close } from "#mui/icons-material";
import { styled } from "#mui/material/styles";
import ActionButton from "./ActionButton";
import React from "react";
const StyledDialog = styled(Dialog)(({ theme }) => ({
fullWidth: true, // it's not taking effect
maxWidth: "lg", // it's not taking effect
padding: theme.spacing(2),
position: "absolute",
top: theme.spacing(5),
"& MuiDialog-paper": {
padding: theme.spacing(2),
position: "absolute",
top: theme.spacing(5),
},
"& .MuiTypography-h6": {
paddingRight: "0px",
},
}));
export default function Popup(props) {
const { title, children, openPopup, setOpenPopup } = props;
return (
<StyledDialog open={openPopup} onClose={() => setOpenPopup(false)}>
<DialogTitle>
<div style={{ display: "flex" }}>
<Typography variant="h6" component="div" style={{ flexGrow: 1 }}>
{title}
</Typography>
<ActionButton
color="secondary"
onClick={() => setOpenPopup(false)}
>
<Close />
</ActionButton>
</div>
</DialogTitle>
<DialogContent dividers>{children}</DialogContent>
</StyledDialog>
However, if I listed the props directly inside the it works.
<StyledDialog fullWidth="true" maxWidth="lg">
</StyledDialog>
What is the correct way of setting up the width and maxWidth.
Try this on the styledDialog declaration:
const StyledDialog = styled((props) => (
<Dialog
fullWidth={true}
maxWidth={'lg'}
{...props}
/>
))(({ theme }) => ({
// Your code to style the dialog goes here
}));
The problem on your code is that you arent passing the properties fullWidth and maxWidth to the component.
You are trying to use maxWidth: lg and fullWidth: true like css, but they are only for the Dialog API.
So you could use maxWidth in the component as an API like
<Dialog maxWidth="lg" fullWidth ... >
or you can add it as css style using theme.
const StyledDialog = styled(Dialog)(({ theme }) => ({
maxWidth: theme.breakpoints.values.lg, // there's no styling such fullWidth
...
}));
You could have a look at the default theme.
Related
I have the following component:
import * as React from "react";
import { createTheme, ThemeProvider } from '#mui/material/styles'
import { Box } from '#mui/system';
import { InputBase, TextField, Typography } from "#mui/material";
import ReactQuill from 'react-quill';
import { NoEncryption } from "#mui/icons-material";
type Props = {
issueId: string
}
export default function DialogBox({ issueId }: Props) {
const myTheme = createTheme({
// Set up your custom MUI theme here
})
const [newMsg, setNewMsg] = React.useState("");
const [startNewMsg, setStartNewMsg] = React.useState(false)
const handleNewMsgInput = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
}
const handleKeyPress = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "Escape") {
// setStartNewMsg(false);
setNewMsg((prev) => "");
}
}
return (
<Box flexDirection="column" sx={{ display: "flex", alignItems: "center", backgroundColor: "lightblue", height: "100%", gap: "2rem" }} onKeyPress={(e) => {
handleKeyPress(e)
}}>
<Typography
sx={{
width: "fit-content",
height: "fit-content",
fontFamily: "Amatic SC",
background: "lightblue",
fontSize: "3rem"
}}>
Message board
</Typography>
{startNewMsg ?
<Box sx={{ width: "fit-content", height: "fit-content" }}>
<ReactQuill style={{ backgroundColor: "white", height: "10rem", maxWidth: "30rem", maxHeight: "10rem" }} theme="snow" />
</Box>
:
<TextField id="filled-basic" label="write new message" sx={{ "& fieldset": { border: 'none' }, backgroundColor: "white", borderRadius: "5px" }} variant="filled" fullWidth={true} onClick={(e) => setStartNewMsg((prev) => true)} onChange={(e) => handleNewMsgInput(e)} />}
</Box >
)
}
Which's causing me the following issue of text appearing out of my white textbox:
I notice on inspection the following property which's responsible for the problem:
My question is, what would be the best way to manipulating the values of that element? How should I retrieve them?
Regards!
Use ch units (the size of the text characters) and adjust the size based on the input.
Example:
const Test = () => {
const [text, setText] = useState('');
return (
<div>
<input
value={text}
onChange= {(e) => setText(e.target.value)}
style= {{height: `${text.length}ch`, width: `${text.length}ch`}}
/>
</div>
);
};
^ that is an input box that will grow based on the size of the text.
You don't want:
height: 100%;
as this will only allow the height to be as big as its parent container.
I am having an issue trying to use MUI Styled () on bigger scale: can someone take a look at the code we used to use in prior versions and let me know how to replicate it in MUI V5.
Old way:
const useStyles = makeStyles((theme) => ({
root: {
backgroundColor: "#fdfdff",
},
pageHeader: {
padding: theme.spacing(4),
display: "flex",
marginBottom: theme.spacing,
},
pageIcon: {
display: "inline-block",
padding: theme.spacing(2),
color: "#3c44b1",
},
pageTitle: {
paddingLeft: theme.spacing(4),
"& .MuiTypography-subtitle2": {
opacity: "0.6",
},
},
}));
export default function PageHeader(props) {
const classes = useStyles();
const { title, subTitle, icon } = props;
return (
<Paper elevation={0} square className={classes.root}>
<div className={classes.pageHeader}>
<Card className={classes.pageIcon}>{icon}</Card>
<div className={classes.pageTitle}>
<Typography variant="h6" component="div">
{title}
</Typography>
<Typography variant="subtitle2" component="div">
{subTitle}
</Typography>
</div>
</div>
</Paper>
);
}
My attempt to accomplish the same in MUI V5 is not working properly. it renders but it doesn't look the same and it's all over the place.
const rootStyle = styled("div")({
backgroundColor: "#fdfdff",
});
const headerStyle = styled("div")(({ theme }) => ({
padding: theme.spacing(4),
display: "flex",
marginBottom: theme.spacing,
}));
const iconStyle = styled("div")(({ theme }) => ({
display: "inline-block",
padding: theme.spacing(2),
color: "#3c44b1",
}));
const titleStyle = styled("div")(({ theme }) => ({
paddingLeft: theme.spacing(4),
"& .MuiTypography-subtitle2": {
opacity: "0.6",
},
}));
export default function PageHeader(props) {
const { title, subTitle, icon } = props;
return (
<rootStyle>
<Paper elevation={0} square>
<headerStyle>
<iconStyle>
<Card>{icon}</Card>
</iconStyle>
<titleStyle>
<Typography variant="h6" component="div">
{title}
</Typography>
<Typography variant="subtitle2" component="div">
{subTitle}
</Typography>
</titleStyle>
</headerStyle>
</Paper>
</rootStyle>
);
}
I am new to MUI and there are not a lot of examples out there that cover this. I truly appreciate your help!
Below is a v5 version of your code with the same look as the v4 version. I added default values for the props just for demonstration purposes.
You had two main issues:
You added additional div layers for the styling rather than styling the elements that originally received the styles (e.g. Paper, Card).
You assigned the styled divs to variable names that start with a lowercase letter which caused them to be rendered as DOM tags rather than components (so the styling would have been completely ignored).
From https://reactjs.org/docs/components-and-props.html#rendering-a-component:
React treats components starting with lowercase letters as DOM tags.
import Paper from "#mui/material/Paper";
import Card from "#mui/material/Card";
import Typography from "#mui/material/Typography";
import { styled } from "#mui/material/styles";
import PersonIcon from "#mui/icons-material/Person";
const StyledPaper = styled(Paper)({
backgroundColor: "#fdfdff"
});
const HeaderDiv = styled("div")(({ theme }) => ({
padding: theme.spacing(4),
display: "flex",
marginBottom: theme.spacing
}));
const StyledCard = styled(Card)(({ theme }) => ({
display: "inline-block",
padding: theme.spacing(2),
color: "#3c44b1"
}));
const TitleDiv = styled("div")(({ theme }) => ({
paddingLeft: theme.spacing(4),
"& .MuiTypography-subtitle2": {
opacity: "0.6"
}
}));
export default function PageHeader(props) {
const {
title = "Title",
subTitle = "sub-title",
icon = <PersonIcon />
} = props;
return (
<StyledPaper elevation={0} square>
<HeaderDiv>
<StyledCard>{icon}</StyledCard>
<TitleDiv>
<Typography variant="h6" component="div">
{title}
</Typography>
<Typography variant="subtitle2" component="div">
{subTitle}
</Typography>
</TitleDiv>
</HeaderDiv>
</StyledPaper>
);
}
An alternative (and much more concise) way to convert the v4 code to v5 is to use the sx prop:
import Paper from "#mui/material/Paper";
import Card from "#mui/material/Card";
import Typography from "#mui/material/Typography";
import PersonIcon from "#mui/icons-material/Person";
import Box from "#mui/material/Box";
export default function PageHeader(props) {
const {
title = "Title",
subTitle = "sub-title",
icon = <PersonIcon />
} = props;
return (
<Paper elevation={0} square sx={{ bgcolor: "#fdfdff" }}>
<Box sx={{ p: 4, display: "flex", mb: 1 }}>
<Card sx={{ display: "inline-block", p: 2, color: "#3c44b1" }}>
{icon}
</Card>
<Box sx={{ pl: 4, "& .MuiTypography-subtitle2": { opacity: 0.6 } }}>
<Typography variant="h6" component="div">
{title}
</Typography>
<Typography variant="subtitle2" component="div">
{subTitle}
</Typography>
</Box>
</Box>
</Paper>
);
}
Here is one more option using a single styled call, though in my opinion this would be more brittle to maintain than the other options:
import Paper from "#mui/material/Paper";
import Card from "#mui/material/Card";
import Typography from "#mui/material/Typography";
import { styled } from "#mui/material/styles";
import PersonIcon from "#mui/icons-material/Person";
const StyledPaper = styled(Paper)(({ theme }) => ({
backgroundColor: "#fdfdff",
"& > div": {
padding: theme.spacing(4),
display: "flex",
marginBottom: theme.spacing(1),
"& .MuiCard-root": {
display: "inline-block",
padding: theme.spacing(2),
color: "#3c44b1"
},
"& > div": {
paddingLeft: theme.spacing(4),
"& .MuiTypography-subtitle2": {
opacity: "0.6"
}
}
}
}));
export default function PageHeader(props) {
const {
title = "Title",
subTitle = "sub-title",
icon = <PersonIcon />
} = props;
return (
<StyledPaper elevation={0} square>
<div>
<Card>{icon}</Card>
<div>
<Typography variant="h6" component="div">
{title}
</Typography>
<Typography variant="subtitle2" component="div">
{subTitle}
</Typography>
</div>
</div>
</StyledPaper>
);
}
I am trying to pass function as prop. I did this before but now with the same logic it is giving me error (this.props.functionName is not a function).
I have a child (Navbar) and a parent component(MainComponent). I want to send a input value from Navbar to MainComponet and set it to the state value in parent Component.
Parent Component
import React ,{Component}from 'react'
import Navbar from '../Presentational/Navbar'
class Main extends Component{
constructor(props){
super(props)
this.state = {
searchItem: ''
}
}
GetSearchItem(search){
this.setState({searchItem:search})
}
render(){
return(
<div className = 'container'>
<div className = 'row'>
<div className = 'col-12 mt-1'>
<Navbar onChange = {(search)=>this.GetSearchItem(search)}></Navbar>
</div>
</div>
<div className = 'row'>
<div className = 'col-3'>
<h3>{this.state.searchItem}</h3>
</div>
</div>
</div>
)
}
}
export default Main
Child Component (Navbar)
import React,{Component} from 'react'
import {AppBar,Toolbar,IconButton,Typography,InputBase} from '#material-ui/core'
import MenuIcon from '#material-ui/icons/Menu';
import SearchIcon from '#material-ui/icons/Search';
import {fade , makeStyles} from '#material-ui/core/styles'
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
display: 'none',
[theme.breakpoints.up('sm')]: {
display: 'block',
},
},
search: {
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(1),
width: 'auto',
},
},
searchIcon: {
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
inputRoot: {
color: 'inherit',
},
inputInput: {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('sm')]: {
width: '12ch',
'&:focus': {
width: '20ch',
},
},
},
}));
class Navbar extends Component{
render(){
const classes = this.props.classes;;
return(
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="open drawer"
>
<MenuIcon />
</IconButton>
<Typography className={classes.title} variant="h6" noWrap>
Pizaa Valley
</Typography>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'search' }}
onChange={(event)=>this.props.onChange(event.target.value)}
/>
</div>
</Toolbar>
</AppBar>
</div>
)
}
}
export default () => {
const classes = useStyles();
return (
<Navbar classes={classes} />
)
}
The problem is that you have two Navbar types. You first have the class component created using class Navbar. And second you have the following functional component defined here:
export default () => {
const classes = useStyles();
return (
<Navbar classes={classes} />
)
}
When you do
import Navbar from '../Presentational/Navbar'
<Navbar onChange = {(search)=>this.GetSearchItem(search)}></Navbar>
The onChange prop is correctly given to the functional component, but is never passed along to the class-based component. You can fix this by replacing your functional component with the below code:
export default props => {
const classes = useStyles();
return (
// using the "spread operator", we pass along all the props given
// to the functional component, so the class-based component can
// also access these
<Navbar {...props} classes={classes} />
)
}
you've done everything correctly except change this:
GetSearchItem(search){
this.setState({searchItem:search})
}
to
GetSearchItem = (search) => {
this.setState({searchItem:search})
}
as an arrow function it has access to the scope above
Try with the following:-
In your parent component modified the below line:-
<Navbar onChangeCallBack = {(search)=>this.GetSearchItem(search)}></Navbar>
In your child Navbar component only modified the below line:-
onChange={(event)=>this.props.onChangeCallBack(event.target.value)}
i have tried to center it using flexbox justify-content, but it didn't work
Then I tried by replacing the Typography component with a div tag, still, it didn't work
import {AppBar,Zoom,Toolbar,Typography,CssBaseline,useScrollTrigger,Fab,makeStyles,IconButton,Container } from '#material-ui/core'
import PropTypes from 'prop-types';
import KeyboardArrowUpIcon from '#material-ui/icons/KeyboardArrowUp';
import styles from './menu.module.css'
import MenuIcon from '#material-ui/icons/Menu'
const useStyles = makeStyles(theme => ({
root: {
position: "fixed",
bottom: theme.spacing(2),
right: theme.spacing(2)
}
}));
function ScrollTop(props) {
const { children, window } = props;
const classes = useStyles();
// Note that you normally won't need to set the window ref as useScrollTrigger
// will default to window.
// This is only being set here because the demo is in an iframe.
const trigger = useScrollTrigger({
target: window ? window() : undefined,
disableHysteresis: true,
threshold: 100
});
const handleClick = event => {
const anchor = (event.target.ownerDocument || document).querySelector(
"#back-to-top-anchor"
);
if (anchor) {
anchor.scrollIntoView({ behavior: "smooth", block: "center" });
}
};
return (
<Zoom in={trigger}>
<div onClick={handleClick} role="presentation" className={classes.root}>
{children}
</div>
</Zoom>
);
}
// ScrollTop.propTypes = {
// children: PropTypes.element.isRequired,
// /**
// * Injected by the documentation to work in an iframe.
// * You won't need it on your project.
// */
// window: PropTypes.func
// };
export default function BackToTop(props) {
return (
<React.Fragment>
<CssBaseline />
<AppBar>
<Toolbar>
<IconButton>
<MenuIcon
className={styles.icon}
edge="end"
color="inherit"
aria-label="menu"
/>
</IconButton>
<Typography align='center' variant="h5">
Información
</Typography>
</Toolbar>
</AppBar>
<Toolbar id="back-to-top-anchor" />
<Container>
<Typography></Typography>
</Container>
<ScrollTop {...props}>
<Fab color="secondary" size="small" aria-label="scroll back to top">
<KeyboardArrowUpIcon />
</Fab>
</ScrollTop>
</React.Fragment>
);
}}```
The problem is probably Toolbar does not center its children. You could do something like
const useStyles = makeStyles(theme => ({
root: {
position: "fixed",
bottom: theme.spacing(2),
right: theme.spacing(2)
},
toolBar: {
display: "flex",
justifyContent: "center",
alignItems: "center",
}
}));
and use it
<Toolbar className={classes.toolBar}>
<IconButton>
<MenuIcon
className={styles.icon}
edge="end"
color="inherit"
aria-label="menu"
/>
</IconButton>
<Typography align='center' variant="h5">
Información
</Typography>
</Toolbar>
To make sure the typography is centered all the time, you can wrap it with the component. Because Container always centers it's children, the typography will be centered as a result of that. Like this
<Container>
<Typography variant="h5">Información</Typography>
</Container>
I have a tab component in Material-UI and I want to implement a tooltip on it.
My problem is that when I click the tab component, the tooltip is not disappearing. It must disappear after I click on that tab.
Currently, it continues to be visible even after I click on the tab.
How do I rectify that?
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ToolTip description={description}>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ToolTip>
);
}
)}
</Tabs>
If you look at the document of Material-UI tooltip API
You would find a props named disableHoverListener
bool
default: false
Do not respond to hover events.
Set it as True would turn off the tooltip onMouseOver event trigger.
Update
Or you can simply make it totally under control.
By binding the onClick, onMouseOver, onMouseLeave, open to related component.
import React, { useState } from "react";
import "./styles.css";
import { Tooltip, Tab } from "#material-ui/core";
export default function App() {
const [flg, setFlg] = useState(false);
const [isHover, setIsHover] = useState(false);
return (
<div className="App">
<Tooltip
title={"message"}
aria-label="add"
placement="bottom"
open={!flg && isHover}
>
<Tab
label={`Click: ${!flg ? "enabled" : "disabled"}`}
onClick={() => setFlg(!flg)}
onMouseOver={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
/>
</Tooltip>
</div>
);
}
Try it online:
You can also implement a generic tooltip with a managed state when to open/close the tooltip.
import Tooltip, { TooltipProps } from "#mui/material/Tooltip";
import { useState } from "react";
/**
* MUI Tooltip wrapper with adaption to the move away once focuses left.
*/
export function ManagedTooltip(props: TooltipProps) {
const [open, setOpen] = useState<boolean>(false);
// Wrap Tooltip with div to capture mouse events
return <div style={{ display: 'flex' }}
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
onClick={() => setOpen(false)}
>
{/* Show the original MUI Tooltip with all props. */}
{/* Just override the open attribute to be fully managed, and disable internal listeners */}
<Tooltip {...props} open={open} disableHoverListener disableFocusListener />
</div>;
}
Once it's ready, you can use it anywhere exactly like the original MUI tooltip.
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ManagedTooltip description={description}>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ManagedTooltip>
);
}
)}
</Tabs>
The way I solved this was by rendering the tooltip conditionally. In your case I suppose you want the tooltip not to render for the tab of the current active route:
function ConditionalTooltip({renderTooltip, children, ...props}) {
return renderTooltip ? <Tooltip {...props}>{children}</Tooltip> : children;
}
function Tabs() {
const location = useLocation();
return (
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ConditionalTooltip
renderTooltip={location.pathname.indexOf(title) === -1} /* only render tooltip on not active urls */
title={description}
>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ConditionalTooltip>
);
}
)}
</Tabs>
)
}