Material-UI Set Class Property From State - reactjs

I have a component like below. I want to be able to use a value from the state such as [opacity, setOpacity] = useGlobal('opacity') and have the opacity be the value for tileBody background. What would be the best way to go about this?
import React from 'react';
import clsx from 'clsx';
import { makeStyles } from '#material-ui/core/styles';
import Typography from '#material-ui/core/Typography';
import Grid from '#material-ui/core/Grid';
import {CardContent, fade, Hidden} from "#material-ui/core";
import Card from "#material-ui/core/Card";
import { useGlobal, setGlobal, useDispatch } from 'reactn';
/*
options = {
...other options,
tileTransparencyValue: 75,
*/
const useStyles = makeStyles(theme => ({
tile: {
backgroundColor: 'transparent',
},
tileHeader: {
backgroundColor: fade(theme.palette.grey[800], 0.7),
},
tileBody: {
backgroundColor: fade(theme.palette.grey[800], tileTransparencyValue/100),
},
}));
export default function DashTile(props) {
const [options, setOptions] = useGlobal('options');
const {title, content} = props;
const classes = useStyles();
return (
<Grid item lg={4} xs={12}>
<Card className={classes.tile}>
<CardContent className={classes.tileHeader}>
<Typography variant="h5">{title}</Typography>
</CardContent>
<CardContent className={classes.tileBody}>
<Typography variant="content">{content}</Typography>
</CardContent>
</Card>
</Grid>
);
}

You can pass props to your styles like this:
export default function DashTile(props) {
const [options, setOptions] = useGlobal('options');
const [opacity, setOpacity] = useGlobal('opacity')
const {title, content} = props;
const classes = useStyles({opacity})
return (
<Grid item lg={4} xs={12}>
<Card className={classes.tile}>
<CardContent className={classes.tileHeader}>
<Typography variant="h5">{title}</Typography>
</CardContent>
<CardContent className={classes.tileBody}>
<Typography variant="content">{content}</Typography>
</CardContent>
</Card>
</Grid>
);
}
Then you can retrive the props inside the styles like this:
const useStyles = makeStyles(theme => ({
tile: {
backgroundColor: 'transparent',
},
tileHeader: {
backgroundColor: fade(theme.palette.grey[800], 0.7),
},
tileBody: {
backgroundColor: ({opacity}) => fade(theme.palette.grey[800], opacity)
},
}));
Reference: makeStyles

Related

Mui grid is causing last two elements of an array to show up larger, and will not display side by side card grid

I have implemented an MUI card grid to display contents of my infinite scroll. However, the last two elements of the returned contents are showing up larger for some reason. Also the grid wont display cards side by side without a map function, which I cannot use or my infinite scroll breaks.
All data is mock data
Any solutions to either part would be wonderful!
Infinite Scroll Component
`
import { useRef, useCallback, useState, useEffect } from 'react'
import {
Box,
CardMedia,
CardContent,
Grid,
Paper,
Typography,
} from "#mui/material";
import { styled } from '#mui/material/styles';
import CountryListCard from '../CountryListCard/CountryListCard';
import Card4 from '../CountryListCard/HomeCards/HomeCountryCard'
import { useInfiniteQuery } from 'react-query'
import { getPostsPage } from '../../services/countries.service'
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
gridContainer: {
paddingLeft: "40px",
paddingRight: "40px",
}
});
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
const HomeScroll = () => {
const classes = useStyles();
const {
fetchNextPage, //function
hasNextPage, // boolean
isFetchingNextPage, // boolean
data,
status,
error
} = useInfiniteQuery('/country', ({ pageParam = 1 }) => getPostsPage(pageParam), {
getNextPageParam: (lastPage, allPages) => {
return lastPage.length ? allPages.length + 1 : undefined
}
})
const intObserver = useRef()
const lastPostRef = useCallback(country => {
if (isFetchingNextPage) return
if (intObserver.current) intObserver.current.disconnect()
intObserver.current = new IntersectionObserver(countries => {
if (countries[0].isIntersecting && hasNextPage) {
console.log('We are near the last post!')
fetchNextPage()
}
})
if (country) intObserver.current.observe(country)
}, [isFetchingNextPage, fetchNextPage, hasNextPage])
if (status === 'error') return <p className='center'>Error: {error.message}</p>
const content = data?.pages.map(pg => {
return pg.map((country, i) => {
if (pg.length === i + 1) {
return (
<Grid container spacing={2}>
<Grid item xs>
<CountryListCard ref={lastPostRef} key={country.id} country={country} />
</Grid>
</Grid>
)
}
return (
<Grid container spacing={2}>
<Grid item xs={6}>
<CountryListCard key={country.id} country={country} />
</Grid>
</Grid>
)
})
})
return (
<>
<Grid container spacing={2}>
<Grid item xs={6}>
{content}
</Grid>
</Grid>
{/* <Review ref={lastPostRef} key={country._id} country={country} review={review}/> */}
{isFetchingNextPage && <p className="center">Loading More Posts...</p>}
<p className="center">Back to Top</p>
</>
)
}
export default HomeScroll;
Card Components
`
import {
CardMedia,
CardContent,
CardActions,
Grid,
Box,
Paper,
Typography,
} from "#mui/material";
import { makeStyles } from "#material-ui/core/styles";
import { styled } from '#mui/material/styles';
import React, { useState, useEffect, useContext } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import MenuList from "../menu/menuItem";
import Flash from "../../components/Flash";
import { AuthContext } from "../../services/auth.context";
import Card4 from './HomeCards/HomeCountryCard';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
const useStyles = makeStyles({
gridContainer: {
paddingLeft: "40px",
paddingRight: "40px"
}
});
const CountryListCard = React.forwardRef(({ country }, ref) => {
const location = useLocation();
const navigate = useNavigate();
const { isAuthenticated, user } = useContext(AuthContext);
const classes = useStyles();
const rows =[{
country: country,
}]
const postBody = (
<>
<Card4 country={country} key={country.id}/>
</>
)
const content = ref
? <div ref={ref}>{postBody}</div>
: <div>{postBody}</div>
const handleViewClick = () => {
navigate(`/country/${country._id}`);
};
return (
<>
{location.state?.message && (
<Flash type={location.state.type} message={location.state.message} />
)}
{content}
{user && isAuthenticated && (
<Grid item xl={6} md={6} sm={12} xs={12}>
<MenuList countryId={country._id} />
</Grid>
)}
</>
);
})
export default CountryListCard;
`
`
import {
Rating,
Grid,
} from "#mui/material";
import React, { useState, useEffect, useContext } from "react";
import { useNavigate } from "react-router-dom";
import { Card, Col, Row, Button, Text } from "#nextui-org/react";
const Card4 = ({ country }) => {
const navigate = useNavigate();
const handleViewClick = () => {
navigate(`/country/${country._id}`);
};
return (
<>
<Card isPressable>
<Card.Body css={{ p: 0 }}>
<Card.Image
src={country.images[0].image}
objectFit="cover"
width="100%"
height={140}
alt={country.title}
/>
<Rating name="simple-controlled" value={country.overallRating} readOnly/>
</Card.Body>
<Card.Footer css={{ justifyItems: "flex-start" }}>
<Row wrap="wrap" justify="space-between" align="center">
<Text b>{country.title}</Text>
<Button flat auto rounded color="secondary"
onClick={handleViewClick}
>
<Text
css={{ color: "inherit" }}
size={12}
weight="bold"
transform="uppercase"
>
View Country
</Text>
</Button>
</Row>
</Card.Footer>
</Card>
</>
);
};
export default Card4
Picture of the problem
picture](https://i.stack.imgur.com/QObLp.png)
I tried adding the mui code to all different elements, I tried using a map function instead of infinite scroll but that was unfeasible, I tried other frontend libraries and got the same issues. I also tried changing all kinds of different spacing and sizing of elements.

Material ui change color for select box

This is my code,
import { AppBar, createStyles, MenuItem, Select, Toolbar, Typography } from '#mui/material';
import { makeStyles } from '#mui/styles';
import { useState } from 'react';
const useStyles = makeStyles((theme) =>
createStyles({
selectRoot: {
color: "white",
}
}),
);
const Navbar = () => {
const classes = useStyles();
const [sort, setSort] = useState(1);
return (
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1}}>Tools</Typography>
<Select
variant="standard"
value={sort}
className={classes.selectRoot}
onChange={(event) => setSort(event.target.value)}
>
<MenuItem value={1}>Default sort</MenuItem>
<MenuItem value={2}>Sort by Title ASC</MenuItem>
<MenuItem value={3}>Sort by Title DESC</MenuItem>
</Select>
</Toolbar>
</AppBar>
)
}
export default Navbar;
What I need to change the color of select tag. In inspect seems like overridden by this class.
.css-a88p61-MuiInputBase-root-MuiInput-root{
color: rgba(0, 0, 0, 0.87);
}
You need to use .MuiMenuItem-root inside your select tag , this will color your entire menuItems.
MenuProps={{
sx: {
"&& .MuiMenuItem-root":{
backgroundColor: "red",
color: "orange"
}
}
}}
But if you don't want to apply color to a specific menuItem then you need to use the .MuiMenuItem-gutters class in Select Component and set disableGutters={true} to the menuItem to which you don't want to apply the color
Example using .MuiMenuItem-root class
import { AppBar, createStyles, MenuItem, Select, Toolbar, Typography } from '#mui/material';
import { makeStyles } from '#mui/styles';
import { useState } from 'react';
const useStyles = makeStyles((theme) =>
createStyles({
selectRoot: {
color: "white",
},
menuItem:{
color: "red",
}
}),
);
const Select = () => {
const classes = useStyles();
const [sort, setSort] = useState(1);
return (
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1}}>Tools</Typography>
<Select
variant="standard"
value={sort}
className={classes.selectRoot}
onChange={(event) => setSort(event.target.value)}
MenuProps={{
sx: {
"&& .MuiMenuItem-root":{
backgroundColor: "red",
color: "orange"
}
}
}}
>
<MenuItem value={1}>Default sort</MenuItem>
<MenuItem value={2}>Sort by Title ASC</MenuItem>
<MenuItem value={3}>Sort by Title DESC</MenuItem>
</Select>
</Toolbar>
</AppBar>
)
}
export default Select;
Example using .MuiMenuItem-gutters class
import { AppBar, createStyles, MenuItem, Select, Toolbar, Typography } from '#mui/material';
import { makeStyles } from '#mui/styles';
import { useState } from 'react';
const useStyles = makeStyles((theme) =>
createStyles({
selectRoot: {
color: "white",
},
}),
);
const Select = () => {
const classes = useStyles();
const [sort, setSort] = useState(1);
return (
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1}}>Tools</Typography>
<Select
variant="standard"
value={sort}
className={classes.selectRoot}
onChange={(event) => setSort(event.target.value)}
MenuProps={{
sx: {
"&& .MuiMenuItem-gutters": {
backgroundColor: "pink",
color: "red"
},
}
}}
>
<MenuItem disableGutters = {true} value={1}>Default sort</MenuItem>
<MenuItem value={2}>Sort by Title ASC</MenuItem>
<MenuItem value={3}>Sort by Title DESC</MenuItem>
</Select>
</Toolbar>
</AppBar>
)
}
export default Select;
Refer documentation for more css properties

i should click twice to get my page [react typescript pagination]

I'm using asp.net core and react typescript and I would like to use paginatedEntity from my API and use it to create pagination in react. Any leads, please?
Below is the code:
import { Card, CardActionArea } from "#material-ui/core";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
import { Grid, Item } from "semantic-ui-react";
import LoadingComponent from "../../app/layout/LoadingComponent";
import { PagingParams } from "../../app/stores/pagination";
import { useStore } from "../../app/stores/store";
import { makeStyles, createStyles } from '#material-ui/core/styles';
import Pagination from '#material-ui/lab/Pagination';
import React from "react";
const useStyles = makeStyles((theme) =>
createStyles({
root: {
'& > * + *': {
marginTop: theme.spacing(2),
},
},
}),
);
export default observer(function AllProduct() {
const { productStore } = useStore();
const [, setLoadingPage] = useState(false);
const classes = useStyles();
const [page, setPage] = React.useState(1);
const handleChange = (_event: React.ChangeEvent<unknown>, value: number) => {
setPage(value);
};
function handleGetPage() {
setLoadingPage(true);
productStore.setPagingParams(new PagingParams(page));
productStore.loadProducts().then(() => setLoadingPage(false));
}
useEffect(() => {
productStore.loadProducts();
}, [productStore])
if (productStore.loadingInitial) return <LoadingComponent />
return (
<Grid style={{ marginTop: '7em' }}>
<Grid.Column width='5' id='FilterHeightSide'>
</Grid.Column>
<Grid.Column width='10'>
<div className={classes.root}>
{
productStore.products.map((product: any) =>
<Grid.Column key={product.id} width='10'>
<Card>
<CardActionArea >
<Item.Group>
<Item>
<Item.Image id='MainPhotoItems' style={{ marginLeft: '1em', marginTop: '1em' }} src={product.mainPhotoUrl} />
<Item.Content>
<Item.Header style={{ marginTop: '0.5em' }}>{product.productName}</Item.Header>
<Item.Meta style={{ marginTop: '1.5em' }}>
<span id="TextPrice" className='price'>${product.priceIncTax}</span>
<span className='stay'></span>
</Item.Meta>
<Item.Description><img src={product.brandPhotoUrl} alt="brand"
id="LogoBrandItem" /></Item.Description>
</Item.Content>
</Item>
</Item.Group>
</CardActionArea>
</Card>
</Grid.Column>
)
}
<Pagination count={productStore.totalPages} onChange={handleChange} onClick=
{handleGetPage} page={page} />
</div>
</Grid.Column>
</Grid>
)
})
I'm using asp.net core and react typescript and I would like to use paginatedEntity from my API and use it to create pagination in react. Any leads, please?
Well i finally find the solution :
import { Card, CardActionArea } from "#material-ui/core";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
import { Grid, Item } from "semantic-ui-react";
import LoadingComponent from "../../app/layout/LoadingComponent";
import { PagingParams } from "../../app/stores/pagination";
import { useStore } from "../../app/stores/store";
import { makeStyles, createStyles } from '#material-ui/core/styles';
import Pagination from '#material-ui/lab/Pagination';
import React from "react";
const useStyles = makeStyles((theme) =>
createStyles({
root: {
'& > * + *': {
marginTop: theme.spacing(2),
},
},
}),
);
export default observer( function AllProduct(){
const {productStore }=useStore();
const [, setLoadingPage] = useState(false);
const classes = useStyles();
const [page, setPage] = React.useState(1);
const handleChange = (_event: React.ChangeEvent<unknown>, value: number) => {
setLoadingPage(true);
productStore.setPagingParams(new PagingParams(productStore.pagingParams.pageNumber=value));
productStore.loadProducts().then(()=>setPage(value)).then(() => setLoadingPage(false));
};
useEffect(()=> {
productStore.loadProducts();
},[productStore])
if(productStore.loadingInitial ) return <LoadingComponent/>
return(
<Grid style={{marginTop:'8em'}}>
<Grid.Column width='5' id='FilterHeightSide'>
</Grid.Column>
<Grid.Column width='10'>
<div className={classes.root}>
{
productStore.products.map((product:any)=>
<Grid.Column key={product.id} width='10'>
<Card>
<CardActionArea >
<Item.Group>
<Item>
<Item.Image id='MainPhotoItems' style={{marginLeft:'1em' , marginTop:'1em'}} src={product.mainPhotoUrl} />
<Item.Content>
<Item.Header style={{marginTop:'0.5em'}}>{product.productName}</Item.Header>
<Item.Meta style={{marginTop:'1.5em'}}>
<span id="TextPrice" className='price'>${product.priceIncTax}</span>
<span className='stay'></span>
</Item.Meta>
<Item.Description><img src={product.brandPhotoUrl} alt="brand" id="LogoBrandItem" /></Item.Description>
</Item.Content>
</Item>
</Item.Group>
</CardActionArea>
</Card>
</Grid.Column>
)
}
<Pagination count={productStore.totalPages} page={page} onChange={handleChange} />
</div>
</Grid.Column>
</Grid>
)
})
i hope that will help others :)

Material UI Grid keeps displaying as a column

I have been working with Material UI grid and I am trying to display data in a grid of cards that show in rows as opposed to a single column. My grid currently displays like this stacked in a column(please ignore the size of the cards this is just a zoomed in screenshot):
I would like the grid to display like this in rows as opposed to a single column:
I feel like I'm missing something simple but I'm just not sure why it keeps stacking in a single column. The code is as follows:
import React, { useState, useEffect } from "react"
// components
import Grid from "#material-ui/core/Grid"
import { makeStyles } from "#material-ui/core/styles"
import axios from "axios"
import RepoCard from "../components/RepoCard"
import Container from "#material-ui/core/Container"
// context
const Profile = () => {
const [data, setData] = useState(null)
const fetchGit = () => {
axios
.get(`https://api.github.com/users/rterrell25/repos?`)
.then((res) => {
setData(res.data)
console.log(res.data)
})
.catch((err) => console.log(err))
}
useEffect(() => {
fetchGit()
}, [])
return (
<div style={{ marginTop: 85 }}>
{!data ? (
<h1>Loading...</h1>
) : (
data.map((user) => (
<Container>
<Grid container spacing={4}>
<RepoCard name={user.name} />
</Grid>
</Container>
))
)}
</div>
)
}
export default Profile
the Repo Card Component code:
import React from "react"
import { Link } from "react-router-dom"
// MUI stuff
import Button from "#material-ui/core/Button"
import { makeStyles } from "#material-ui/core/styles"
import Card from "#material-ui/core/Card"
import CardActions from "#material-ui/core/CardActions"
import CardContent from "#material-ui/core/CardContent"
import CardMedia from "#material-ui/core/CardMedia"
import Grid from "#material-ui/core/Grid"
import Typography from "#material-ui/core/Typography"
const useStyles = makeStyles((theme) => ({
icon: {
marginRight: theme.spacing(2),
},
heroContent: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(8, 0, 6),
},
heroButtons: {
marginTop: theme.spacing(4),
},
cardGrid: {
paddingTop: theme.spacing(8),
paddingBottom: theme.spacing(8),
},
card: {
height: "100%",
display: "flex",
flexDirection: "column",
},
cardMedia: {
paddingTop: "56.25%", // 16:9
},
cardContent: {
flexGrow: 1,
},
footer: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(6),
},
}))
const MovieCard = ({ name }) => {
const classes = useStyles()
return (
<Grid item xs={12} sm={6} md={4}>
<Card className={classes.card}>
<CardMedia
className={classes.cardMedia}
image={"#"}
title='Movie nane'
/>
<CardContent className={classes.cardContent}>
<Typography gutterBottom variant='h5' component='h2'>
"Movie name"
</Typography>
<Typography>Genre: Comedy</Typography>
<Typography noWrap>"Movie Summary"</Typography>
</CardContent>
<CardActions>
<Link to={""}>
<Button size='small' color='primary'>
Details
</Button>
</Link>
</CardActions>
</Card>
</Grid>
)
}
export default MovieCard
Change the xs={12} to xs={4}
The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs. Material Design’s responsive UI is based on a 12-column grid layout.
Refer: document of Material-UI Grid

Invoke a function inside a React stateless component

I'm trying to invoke the function ButtonAppBar inside my stateless component but the TS compiler gives me this error: '{' expected. I'm not sure whether I should be passing it to my New Header component or whether I should give it a type.
Here's my component
import * as React from "react";
import { withStyles, createStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
const styles: any = createStyles({
root: {
flexGrow: 1
},
header: {
backgroundColor: "#007"
},
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
}
});
const ButtonAppBar = (styles) => {
const classes = styles;
return (
<div className={classes.root}>
<AppBar position="static" className={classes.header}>
<Toolbar>
<IconButton
className={classes.menuButton}
color="inherit"
aria-label="Menu"
>
<MenuIcon />
</IconButton>
<Button color="inherit">Home</Button>
<Button color="inherit">Help</Button>
</Toolbar>
</AppBar>
</div>
);
}
const NewHeader: React.StatelessComponent<props> = ({}) => {
return (
{ButtonAppBar()}
);
}
export default withStyles(styles, NewHeader);
Your code is not parsed properly.
const NewHeader: React.StatelessComponent<props> = ({}) => ButtonAppBar();
either:
const NewHeader: React.StatelessComponent<props> = ({}) => {
return ButtonAppBar();
}

Resources