react javascript css grid masonry responsive issue - css

The responsive part of my code doesn't work well, as the browser width shrink, the height of image-list doesn't adjust properly. Why?
here is my imageList.js
import React from "react";
import "./imageList.css";
import ImageCard from "./imageCard";
const imageList = ({ images }) => {
const renderList = images.map((image) => {
return <ImageCard key={image.id} image={image} />;
});
return <div className="image-list">{renderList}</div>;
};
export default imageList;
here is my imageList.css
.image-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-gap: 0px 10px;
grid-auto-rows: minmax(1px, auto);
}
.image-list img {
width: 250px;
grid-row-end: span 2;
}
here is my imageCard.js
import React, { useRef, useState, useEffect } from "react";
const ImageCard = ({ image }) => {
const imageRef = useRef(null);
const [spans, setSpans] = useState(0);
useEffect(() => {
imageRef.current.addEventListener("load", handleSpansChange);
}, [spans]);
const handleSpansChange = () => {
const height = imageRef.current.clientHeight;
const spans = Math.ceil(height) + 10;
setSpans(spans);
console.log(spans);
};
return (
<div style={{ gridRowEnd: `span ${spans}` }}>
<img src={image.urls.regular} alt={image.description} ref={imageRef} />
</div>
);
};
export default ImageCard;
Notes that ImageList is a component render in app.js file

Related

Cards not staying horizontally aligned after running for loop

I am trying to align the cards horizontally and it worked fine before, but after I pulled the data from the API and used for-loop to show the data in the browser the cards stacked vertically.
Here is the javascript code
import React, { useEffect, useState } from "react";
import "./Home.css";
import axios from "axios";
function Home() {
const [ApiData, setApiData] = useState([]);
useEffect(() => {
async function getData() {
const data = await axios.get("http://127.0.0.1:8000/?format=json");
console.log(data.data);
setApiData(data.data.Product);
return data;
}
getData();
}, []);
return (
<>
{ApiData.map((obj, index) => {
let x;
for (x in obj) {
return (
<div className="container">
<div className="card">
{/* <img className="store-img" src={obj.image} alt="" /> */}
<span>{obj.name}</span>
</div>
</div>
);
}
})}
</>
);
}
export default Home;
Here is the CSS
.container {
display: flex;
flex-direction: row;
flex: 1;
}
.card {
display: flex;
flex-direction: row;
margin: 10px 50px 10px 50px;
justify-content: center;
align-items: baseline;
flex: 1;
height: 16em;
width: 12em;
max-height: 18em;
max-width: 16em;
border-radius: 4px;
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
}
.store-img {
height: 100%;
width: 100%;
}
The JSON File which I am using for this code
You are creating a new div with class container in every loop iteration. Put loop inside container div.
import React, { useEffect, useState } from "react";
import "./Home.css";
import axios from "axios";
function Home() {
const [ApiData, setApiData] = useState([]);
useEffect(() => {
async function getData() {
const data = await axios.get("http://127.0.0.1:8000/?format=json");
console.log(data.data);
setApiData(data.data.Product);
return data;
}
getData();
}, []);
return (
<div className="container">
{ApiData.map((obj, index) => {
let x;
for (x in obj) {
return (
<div className="card">
{/* <img className="store-img" src={obj.image} alt="" /> */}
<span>{obj.name}</span>
</div>
);
}
})}
</div>
);
}
export default Home;
Wrap your divs with class d-flex if you are using bootstrap or create a class with property display: flex. Try below code :
import React, { useEffect, useState } from "react";
import "./Home.css";
import axios from "axios";
function Home() {
const [ApiData, setApiData] = useState([]);
useEffect(() => {
async function getData() {
const data = await axios.get("http://127.0.0.1:8000/?format=json");
console.log(data.data);
setApiData(data.data.Product);
return data;
}
getData();
}, []);
return (
<div className="d-flex">
{ApiData.map((obj, index) => {
let x;
for (x in obj) {
return (
<div className="container" key={index}>
<div className="card">
{/* <img className="store-img" src={obj.image} alt="" /> */}
<span>{obj.name}</span>
</div>
</div>
);
}
})}
</div>
);
}
export default Home;

How to make container expand up instead of down when component added?

I have a React component that asks user to upload profile photo. In the same view, the user can see a greyed out 'next' button. When the photo is uploaded and displayed, the user sees a horizontal scrolling bar to adjust photo size that comes between the photo and next button. My goal is to keep the next button in view at all times but also keep all elements of the component in the same order. Right now, a user has to scroll down to see the 'next' once the image is loaded. I've been experimenting with flex and position and even a ternary in the JSX to add paddingBottom when !isLoading. Can someone give a good direction so that the user doesn't have to scroll down to see the next' button?
Here is my component:
import React, { useContext, useState, useEffect } from 'react';
import styled from 'styled-components';
import Typography from 'components/Typography';
// import { useMediaQuery } from 'beautiful-react-hooks';
import { medium } from 'constants/mediaQueries';
import AppButton from 'components/AppButton';
import useFetcher from 'hooks/useFetcher';
import { getMeApi } from 'utils/apiRoutes';
import Loader from 'components/Loader';
import routePaths from 'containers/Router/routePaths';
import { useHistory, useLocation } from 'react-router-dom';
import { UserContext } from 'containers/context/UserContext';
import checkmarkImage from 'assets/images/checkmark.svg';
import { useDispatch } from 'react-redux';
import BackArrowGrid from 'components/Auth/BackArrowGrid';
import UploadImageInput from 'components/UploadImageInput';
import { setPlainLogo, setInitialLogo } from '../../store/header/actions';
// const MOBILE = 250;
// const DESKTOP = 356;
const Container = styled.div`
width: 100%;
position: relative;
align-items: center;
justify-content: flex-end;
display: flex;
flex-direction: column;
border: pink solid 2px;
`;
const ButtonContainer = styled.div`
margin-top: 32px;
display: flex;
align-items: center;
flex-direction: row;
div {
margin-bottom: 16px;
}
img {
margin-left: 8px;
}
`;
const Title = styled(Typography)`
font-size: 40px;
text-transform: uppercase;
margin-bottom: 64px;
#media (min-width: ${medium}) {
margin-bottom: 64px;
}
`;
const ButtonContent = styled.span`
display: flex;
align-items: center;
`;
const Form = styled.div`
width: 100%;
display: flex;
flex-direction: column;
align-self: flex-end;
border: yellow solid 2px;
.empty {
display: none;
}
#media (min-width: ${medium}) {
flex-direction: column;
align-items: center;
min-height: auto;
.empty {
display: block;
width: 118px;
}
}
`;
const UploadPicture = () => {
const { fetcher, error, isLoading } = useFetcher();
const { setUserFromLogin } = useContext(UserContext);
const history = useHistory();
const location = useLocation();
// const isDesktop = useMediaQuery(`(min-width: ${medium})`);
const dispatch = useDispatch();
const [image, setImage] = useState({});
const uploadImage = async () => {
const result = await fetcher({
url: getMeApi,
method: 'POST',
body: {
photo: image.croppedImage.source,
},
});
if (result.errors) {
return;
}
setUserFromLogin(result.user);
if (location.profile) {
history.push(routePaths.Profile);
} else {
history.push(routePaths.Welcome);
}
};
useEffect(() => {
dispatch(setPlainLogo());
return () => dispatch(setInitialLogo());
}, []);
const onPictureChange = newImage => {
setImage(prevImage => ({
...prevImage,
...newImage,
}));
};
return (
<>
<BackArrowGrid>
<Container>
<Title h1>Profile photo</Title>
<Form>
<div className="empty"> </div>
<UploadImageInput
onChange={onPictureChange}
image={image}
placeholder={
"Upload a profile photo. This is how you'll appear to other Color TV users."
}
height={356}
width={356}
horizontalPadding={60}
aspect={1}
round
/>
<ButtonContainer>
{error && (
<Typography error>
Error while uploading the image, please try again.
</Typography>
)}
<AppButton
primary={!!image.croppedImage}
disabled={isLoading || !image.croppedImage}
onClick={uploadImage}
noBorder
>
<ButtonContent>
{isLoading ? (
<Loader className="center" size={30} />
) : (
'Next'
)}
{!isLoading && !!image.croppedImage && (
<img src={checkmarkImage} alt="checkmark_icon" />
)}
</ButtonContent>
</AppButton>
</ButtonContainer>
</Form>
</Container>
</BackArrowGrid>
</>
);
};
export default UploadPicture;
Since this component is part of a larger code base, I'm not sure how to run a snippet of it but here are 2 photos - before and after. Once the photo is uploaded, the next button drops out of view because the slider component is added in. I want to next button to stay in view no matter how the browser size.

How can you use the #supports css rule in material ui makeStyles?

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

How to make a list in MaterialUI go to a new line?

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%'
}
})
);

Why is the flex-value not updating in the styled-component?

I have a sidebar and some main content. The main content takes up the majority of the screen whilst the sidebar should only take a small portion. I've got a parent container that is a flexbox. The two children (sidebar and main content) are both div elements.
The sidebar is closed by default
The Issue: Toggling the sidebar does not expand the sidebar as expected
Things I've Checked:
Flex values in the sidebar css are being updated correctly
Sidebar events are being fired and the isOpen hook is being updated correctly
// main.ts
import styled from 'styled-components';
export const Content = styled.div`
flex: 4;
margin-top: 1em;
margin-bottom: 2em;
height: 100vh;
`;
export const Container = styled.div`
display: flex;
`;
// sidebar/styles.ts
import styled from 'styled-components';
interface RootProps {
isOpen: boolean;
}
export const Root = styled.div<RootProps>`
padding: 1em;
background-color: ${DARK};
flex: ${({ isOpen }: RootProps) => (isOpen ? 1 : 0)};
`;
// sidebar/index.tsx
export const Sidebar: React.FC = () => {
const dispatch = useDispatch();
const isOpen = useSidebar();
const handleOpen = () => dispatch(toggleSidebar());
return (
<Root isOpen={isOpen}>
<Button onClick={handleOpen}>
CLICK ME
</Button>
</Root>
);
};
// Usage
// Sidebar styles mirror what's in the sidebar styled component file
<Container>
<Sidebar />
<Content />
</Container>
Expected outcome is that the sidebar expands and collapses when toggling the button. No error messages are displayed and the sidebar flex values are being updated correctly.
JSFiddle with just HTML / CSS but essentially the desired effect: https://jsfiddle.net/5dLk9ex3/3/
The problem with your code is somewhere in the reducer scope (your question is incomplete), your Sidebar doesn't rerender after you dispatching an action.
Working example:
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
const Container = styled.div`
display: flex;
`;
const First = styled.div`
flex: ${({ isOpen }) => (isOpen ? 1 : 0)};
background-color: red;
height: 20px;
`;
const Second = styled.div`
flex: 4;
background-color: green;
height: 20px;
`;
const DEFAULT_INITIAL = false;
const App = () => {
const [isOpen, setIsOpen] = useState(DEFAULT_INITIAL);
const onClick = () => {
console.log('Toggle Sidebar');
setIsOpen(p => !p);
};
return (
<>
<Container>
<First isOpen={isOpen}>FIRST</First>
<Second>SECOND</Second>
</Container>
<button onClick={onClick}>OpenSider</button>
</>
);
};
Please refer to the first comment of this answer
I'm unsure as to why but setting the main content div to min-width: 0 resolved the issue. See this for more: Flex items not shrinking when window gets smaller

Resources