I have a separate App.css file that has global css attributes and have classes for responsiveness. The issue is I want to render elements differently for separate devices but can't seem to figure out how to do that as using conditionals isn't applying as such.
import UserItem from "./UserItem";
import Spinner from "../layout/Spinner";
import PropTypes from "prop-types";
const Users = ({ users, loading }) => {
if (loading) {
return <Spinner />;
} else {
return (
<div style={userStyle} className='body'>
{users.map((user) => {
return <UserItem key={user.id} user={user} />;
})}
</div>
);
}
};
const windowWidth = window.innerWidth;
Users.propTypes = {
users: PropTypes.array.isRequired,
loading: PropTypes.bool.isRequired,
};
const userStyle = {
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gridGap: "1rem",
};
export default Users;
My css #media query which I am trying to apply to effect change on a small device.
/* Mobile Styles */
#media (max-width: 700px) {
.hide-sm {
display: none;
}
}
How do I implement this #media css style so that it can render the page differents through jsx?
You can use material ui. that will fulfil your requirement. Please check this example:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Typography from '#material-ui/core/Typography';
import { green } from '#material-ui/core/colors';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
backgroundColor: theme.palette.secondary.main,
},
[theme.breakpoints.up('md')]: {
backgroundColor: theme.palette.primary.main,
},
[theme.breakpoints.up('lg')]: {
backgroundColor: green[500],
},
},
}));
export default function MediaQuery() {
const classes = useStyles();
return (
<div className={classes.root}>
<Typography variant="subtitle1">{'down(sm): red'}</Typography>
<Typography variant="subtitle1">{'up(md): blue'}</Typography>
<Typography variant="subtitle1">{'up(lg): green'}</Typography>
</div>
);
}
Material UI
You can use following example too.
class Card extends Component {
constructor() {
super();
this.mediaQuery = {
desktop: 1200,
tablet: 768,
phone: 576,
};
this.state = {
windowWidth: null
};
}
componentDidMount() {
window.addEventListener('resize', () => {
this.setState({windowWidth: document.body.clientWidth})
});
}
render() {
return (
<div style={{
width: this.state.windowWidth > this.mediaQuery.phone
? '50%'
: '100%',
//more styling :)
}}>
<!-- <Card> contents -->
</div>
);
}
}
Source
I suggest that use CSS #media query to make responsive layouts.
But if you insist on implement with JS and React you should get windowWidth after component mounted. You can use useEffect hook to do so and save value in a state:
const [windowWidth, setWindowWidth] = useState('');
useEffect(() => {
setWindowWidth(window.innerWidth) // or better one -> window.clientWidth
});
Related
I'm trying to make a very simple react component that would crop images with react-easy-crop. Apparently it is possible to customize the style of react-easy-crop module with style prop that takes 3 objects: containerStyle, mediaStyle and cropAreaStyle.
This is the default layout:
I want to expand cropArea to full width of its container and to fit media in it by height (so that we don't see the part of the original image outside of cropArea) but can't figure out how to do it. The cropAreaStyle object doesn't seem to affect width or height since it is calculated and injected in the module file (even after setting disableAutomaticStylesInjection to true).
import React from 'react'
import ReactDOM from 'react-dom'
import Cropper from 'react-easy-crop'
import './styles.css'
class App extends React.Component {
state = {
imageSrc:
'https://img.huffingtonpost.com/asset/5ab4d4ac2000007d06eb2c56.jpeg?cache=sih0jwle4e&ops=1910_1000',
crop: { x: 0, y: 0 },
zoom: 1,
aspect: 1 / 1,
style: { containerStyle: { position: "absolute", top: "0", width: "calc(100% - 2px)", height: window.innerWidth, overflow: "hidden", border: "1px solid black" },
mediaStyle: { height: "100%", display: "block" },
cropAreaStyle: {position: "absolute", top: "0", border: "1px solid black", width: "100%", height: "100%" }}
}
onCropChange = (crop) => {
this.setState({ crop })
}
onCropComplete = (croppedArea, croppedAreaPixels) => {
console.log(croppedArea, croppedAreaPixels)
}
onZoomChange = (zoom) => {
this.setState({ zoom })
}
render() {
return (
<div className="App">
<div className="crop-container">
<Cropper
image={this.state.imageSrc}
crop={this.state.crop}
zoom={this.state.zoom}
aspect={this.state.aspect}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
style={this.state.style}
disableAutomaticStylesInjection={true}
/>
</div>
</div>
)
}
}
const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
This is what I'm trying to achieve:
The black square is cropArea that I can't resize...
I want cropArea to remain square.
Is there an easy way to do this, without changing the module file?
The solution with another module is acceptable also
Thanks in advance
I tried to use the object cropAreaStyle but it's not working, instead use the prop cropSize and don't pass the prop aspect.
In order to get the height of the media pass the prop onMediaLoaded:
onMediaLoad = (mediaSize) => {
this.setState({
cropHeight: mediaSize.height,
});
};
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import Cropper from 'react-easy-crop';
import './style.css';
class App extends React.Component {
state = {
imageSrc:
'https://img.huffingtonpost.com/asset/5ab4d4ac2000007d06eb2c56.jpeg?cache=sih0jwle4e&ops=1910_1000',
crop: { x: 0, y: 0 },
zoom: 1,
cropHeight: 0,
};
onCropChange = (crop) => {
this.setState({ crop });
};
onCropComplete = (croppedArea, croppedAreaPixels) => {
console.log(croppedArea, croppedAreaPixels);
};
onZoomChange = (zoom) => {
this.setState({ zoom });
};
onMediaLoad = (mediaSize) => {
this.setState({
cropHeight: mediaSize.height,
});
};
render() {
const cropSize = {
height: `${this.state.cropHeight}px`,
width: '100%',
};
return (
<div className="App">
<div className="crop-container">
<Cropper
image={this.state.imageSrc}
crop={this.state.crop}
zoom={this.state.zoom}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
onMediaLoaded={this.onMediaLoad}
cropSize={cropSize}
/>
</div>
</div>
);
}
}
export default App;
Demo: https://stackblitz.com/edit/react-4zmgud
It seems that what you need is the objectFit property set to vertical-cover.
See this demo: https://codesandbox.io/s/crazy-liskov-04u7m0
How can you use the #supports css rule in material ui makeStyles?
I tried to search that but didn't find anything describing how to include css rules like supports
Here is the styles I want to have:
#supports (display: grid) {
div {
display: grid;
}
}
I tried this but it didn't work:
const useStyles = makeStyles(() => ({
paper: {
'#supports': {
'(display: grid)': {
display: 'grid';
},
},
}
}))
The syntax for this is similar to the syntax for media queries. In your case, you would want the following:
const useStyles = makeStyles(() => ({
paper: {
'#supports (display: grid)': {
display: 'grid'
}
}
}))
Here's a working example:
import React from "react";
import Button from "#material-ui/core/Button";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
button: {
"#supports (background-color: red)": {
marginTop: theme.spacing(5),
backgroundColor: "red"
},
"#supports not (display: unsupportedvalue)": {
color: "white"
},
"#supports not (display: grid)": {
backgroundColor: "purple"
}
}
}));
export default function App() {
const classes = useStyles();
return (
<Button className={classes.button} variant="contained">
Hello World!
</Button>
);
}
Related answer:
How can I use CSS #media for responsive with makeStyles on Reactjs Material UI?
Related documentation:
https://cssinjs.org/jss-plugin-nested?v=v10.5.0#nest-at-rules
Just like you use media queries in the Mui, the same way you can make use of #support in it!
For example:
const useStyles = makeStyles((theme) => ({
grid: {
"#supports (display: grid)": {
display: "grid",
gridTemplateColumns: "1fr 1fr"
}
}
}));
Whole component will look like this:
import React from "react";
import { makeStyles } from "#material-ui/core";
function Grid() {
const useStyles = makeStyles((theme) => ({
grid: {
"#supports (display: grid)": {
display: "grid",
gridTemplateColumns: "1fr 1fr"
}
}
}));
const styles = useStyles();
return (
<div className={styles.grid}>
<div>Grid Item</div>
<div>Grid Item</div>
<div>Grid Item</div>
<div>Grid Item</div>
</div>
);
}
export default Grid;
And here's the working codesandbox example:
https://codesandbox.io/s/priceless-lamarr-olciu
import React from 'react';
import {
List, ListItem,
} from '#material-ui/core';
import {
makeStyles, createStyles,
} from '#material-ui/core/styles';
import clsx from 'clsx';
import VideoCard from './VideoCard';
const useStyles = makeStyles(() => createStyles({
root: {
display: 'inline-flex',
},
item: {
padding: '80px 40px',
},
}));
export default function VideoList(props: any) {
const { videos } = props;
const classes = useStyles();
return (
<div>
<List className={clsx(classes.root)}>
{videos.map((video: any) => (
<ListItem className={classes.item} button key={video}>
<VideoCard videoTitle={video.title} thumbnailImage={video.imageSrc} key={video} />
</ListItem>
))}
</List>
</div>
);
}
import React from 'react';
import Typography from '#material-ui/core/Typography';
import clsx from 'clsx';
import Thumbnail from './Thumbnail';
export default function VideoCard(props: any) {
const { thumbnailImage, videoTitle } = props;
return (
<div>
<Thumbnail imageSrc={thumbnailImage} />
<Typography>{videoTitle}</Typography>
</div>
);
}
I am trying to display a series of video titles and thumbnails (like how video cards are displayed on the frontpage of youtube). How do I get the cards to go to a new line say every 4 cards? Currently, they line up and go off screen.
Edit: added my VideoCard code aswell
Make it float: 'left' and then set 100% - 25% to make a new line every 4 cards
const useStyles = makeStyles(() =>
createStyles({
root: {
width: "100%",
display: "inline-block"
},
item: {
padding: "80px 40px",
float: 'left',
width: '25%'
}
})
);
I have my own theme, I can theming well.
Right now I have three different styles with material UI tabs. That's why I need to change styles using makeStyles.
This is example of tab I need to change
...
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
width: "100%",
backgroundColor: theme.pallete.primary
},
tabs: {
/// some styles
}
...
}
));
...
<Tabs
...someProps
className={classes.tabs}
>
element inside tab have such classes:
<button class="MuiButtonBase-root MuiTab-root MuiTab-textColorSecondary Mui-selected MuiTab-labelIcon">
I have tried to edit styles the same way as
... = createMuiTHeme ({
overrides: {
...some overrides
}
in my case:
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
width: "100%",
backgroundColor: "#121D42",
MuiButtonBase: {
root: {
///some styles
},
}
},
...
but it doesn't work with makeStyles
So how can I edit buttons inside tabs using makeStyles(), is it possible? Or help me with solution please
I have found a solution for now.
Using Styled Components and with creating a styled element - we can change styles easier. We should StylesProvider
const NewButton = styled(({styledComponentProp, ...rest}) => (
<Button classes={{label: 'label'}} {...rest}/>
))`
.label {
color: blue;
font-size: ${props => props.styledComponentProp};
}
`
export const BlueButton = styled(props => {
return (
<StylesProvider injectFirst>
<NewButton variant="contained" styledComponentProp="20px"> Red Labeled Button </NewButton>
</StylesProvider>
);
})`
`;
But have we any better solutions?
I'm using inline style to style the HTML DOM element. I want to display converted plain CSS. I'm changing the inline style using component state.
I do the following. It prints the style objects. e.g.,
{"display":"flex","flexDirection":"column"}
import React, {Component} from 'react';
class Sample extends Component {
constructor(props) {
super(props);
this.state = {
style: {
display: "flex",
flexDirection: "column"
},
}
}
render() {
const {style} = this.state;
return (
<div>
<div style={style}>
<div id={1}>1</div>
<div id={2}>2</div>
<div id={3}>3</div>
<div id={4}>4</div>
</div>
<div>{JSON.stringify(style)}</div>
</div>
);
}
}
export default Sample;
I expect the output as plain CSS instead of inline style object. e.g., "display: flex; flex-direction: column;"
This is some hack, but it will fulfil your requirement.
import React, {Component} from 'react';
class Sample extends Component {
constructor(props) {
super(props);
this.state = {
style: {
display: "flex",
flexDirection: "column"
},
}
}
getStyle(){
let styled = '{';
Object.keys(this.state.style).forEach(e => {
let key = e.split(/(?=[A-Z])/).join('-').toLowerCase()
styled += `${key}:${this.state.style[e]};`
});
styled += '}'
return styled;
}
render() {
const {style} = this.state;
return (
<div>
<div style={style}>
<div id={1}>1</div>
<div id={2}>2</div>
<div id={3}>3</div>
<div id={4}>4</div>
</div>
<div>{this.getStyle()}</div>
</div>
);
}
}
export default Sample;
Demo
Best way would be configure webpack to extract css to a new file.
npm install extract-text-webpack-plugin --save-dev
npm install style-loader css-loader --save-dev
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}
I come across ReactJS ref document. And I tried the below way. It works as I was expecting. Demo
import React, { Component } from "react";
class Sample extends Component {
constructor(props) {
super(props);
this.itemContainerRef = React.createRef();
this.state = {
style: {
display: "flex",
flexDirection: "column"
},
itemContainerCSS: {}
};
}
componentDidMount() {
this.setState({
itemContainerCSS: this.itemContainerRef.current.style.cssText || {}
});
}
render() {
const { style, itemContainerCSS } = this.state;
return (
<div>
<div style={style} ref={this.itemContainerRef}>
<div id={1}>1</div>
<div id={2}>2</div>
<div id={3}>3</div>
<div id={4}>4</div>
</div>
<div>{JSON.stringify(itemContainerCSS)}</div>
</div>
);
}
}
export default Sample;