Get image height in React grid - css

I am making masonry grid gallery in React and I have trouble getting image height after load and before resizing it with grid. I have onLoad handler on img but there I get only already resized image height where it should be original, to use it to calculate number of row spans. I am aware it could be pure css problem, but I don't know how to solve it.
Here is my useEffect:
useEffect(() => {
if (imageRef.current && !loading) {
const height = imageRef.current.clientHeight;
console.log('height', height);
const spansCount = Math.ceil(height / 10 + 1);
setSpansCount(spansCount);
}
}, [imageRef.current, loading]);
And here is my render:
return (
<div
{...rest}
className={styles.gallery__item}
style={{ gridRowEnd: `span ${spansCount}` }}
>
<img
src={image.previewURL}
ref={imageRef}
className={styles.gallery__img}
alt={image.tags}
onLoad={handleLoad}
/>
<Placeholder
ref={loaderRef}
style={loading ? { display: 'block' } : { display: 'none' }}
/>
</div>
Here is my non working Codesandbox.
And here is working Codesandbox with react-load-image package, but I am not sure why this is working and the first one isn't.

I just needed to change this css on image:
.gallery__img {
width: 100%;
height: auto; // not 100%
}

Related

How do I make material ui container have full width and height?

I am trying to wrap a page in a React project in a material UI container but it squeezes in all my content with these weird margins. Here is what it looks like:
But I want it to look like this with full width:
Haven't been able to find any other resources explaining how to change the width of the container. Does anyone have any workarounds? I tried adjusting the width of the container to be 100vw but it was unresponsive to my CSS. Here is my code:
////BUY PAGE
import React from 'react';
import Container from '#mui/material/Container';
import AppBar from '../../components/AppBar/AppBar';
import Footer from '../../components/Footer/Footer';
import './Buy.css';
const Buy = () => {
return (
<Container>
<AppBar />
<Footer />
</Container>
);
};
export default Buy;
////CSS
.buy-container {
overflow-y: hidden;
width: 100vw;
}
You should be able to get the results you're looking for by setting the maxWidth property of Container to false e.g:
<Container maxWidth={false}>
<AppBar />
<Footer />
</Container>
Edit:
The maxWidth property determines the max-width of the container. The container width grows with the size of the screen. By setting it to false you can disable the maxWidth property.
https://mui.com/api/container/
You will need to add maxWidth={false} and disableGutters properties to the <Container/> component. Additionally, you should include the <CssBaseline/> component to reset the browser's CSS.
Example:
<>
<CssBaseline />
<Container maxWidth={false} disableGutters>
{children}
</Container>
</>
Container API
Name
Type
Default
Description
maxWidth
'xs', 'sm', 'md', 'lg', 'xl', false, string
Determine the max-width of the container. The container width grows with the size of the screen. Set to false to disable maxWidth.
disableGutters
bool
false
If true, the left and right padding is removed.
You should avoid to set the custom container width until changing the breakpoints.
Otherwise, you can use a custom div element or Box component.
// makeStyles
const useStyles = makeStyles(() => ({
root: {
height: '100%',
overflow: 'hidden',
width: '100%'
},
}));
// styled
const LayoutContainer = styled('div')(() => ({
height: '100%',
overflow: 'hidden',
width: '100%'
}));
I'd take a look at global css variables to overwrite the standard (see here):
The material docs suggest this way or using styling overrides which may be another option for you.
.MuiContainer-root {
width: 100vw;
padding-left: 0;
padding-right: 0;
}
FYI - I got the global css name from the Container portion of the docs under "root", in case you've not seen it.
I would use the <Container> within <Box>/<Appbar> that has the background color e.g:
<Box sx={{ bgcolor: 'black'}}>
<Container>
My content
</Container>
</Box>
minHeight: '100%' worked for me in that kind of situation

Using Prismjs for syntax highlighted code blocks is breaking layout on mobile - <pre> element wont take dynamic width

I'm using Prismjs alongside Mdx for a code-related blog. I'm using it to show code blocks in a manner consistent with other blogs.
I'm running into an issue where the rendered code blocks (inside a <pre> element are too wide on my mobile layout. For now I am content to have things scroll on the horizontal axis. I'm 99% certain that the <pre> elements are what's breaking the layout because when I comment them out of the blog post, the layout works as expected.
Specifically, I'm using a package called prism-react-renderer (alongside Gatsby), and the code I have for the CodeBlock element (that handles the syntax highlighting) is more or less verbatim from the documentation for prism-react-renderer, but is included here for convenience:
import React from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
import theme from 'prism-react-renderer/themes/nightOwl'
const CodeBlock = (props) => {
const className = props.children.props.className || ''
const matches = className.match(/language-(?<lang>.*)/)
return (
<Highlight {...defaultProps} code={props.children.props.children.trim()} language={
matches && matches.groups && matches.groups.lang
? matches.groups.lang
: ''
}
theme={theme}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style }}>
<code>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
))}
</code>
</pre>
)}
</Highlight>
)
}
export default CodeBlock
This is the component used in the blog post template that handles rendering the .mdx files into HTML:
import React from 'react'
import { Link, graphql } from 'gatsby'
import { MDXRenderer } from 'gatsby-plugin-mdx'
import { MDXProvider } from '#mdx-js/react'
import Layout from '../components/layout'
import CodeBlock from '../components/code-block'
const components = {
pre: CodeBlock
}
const BlogPostTemplate = ({ data, pageContext, location }) => {
const post = data.mdx
const { previous, next } = pageContext
return (
<Layout>
*** Removed irrelevant component ***
<MDXProvider components={components}>
<div className='blog-post-wrapper'>
<article className='blog-post-content'>
<header>
<h1>
{post.frontmatter.title}
</h1>
<time dateTime={post.frontmatter.date}>
{post.frontmatter.date}
</time>
</header>
<MDXRenderer>{post.body}</MDXRenderer>
</article>
<footer className='blog-post-footer'>
*** Removed irrelevant components ***
</footer>
</div>
</MDXProvider>
</Layout>
)
}
export default BlogPostTemplate
I have tried a few different things: flex shrink, applying overflow-x: scroll and overflow-x: auto to both the <pre> element and its parents. When I apply a fixed width to the <pre> element and overflow-x: scroll I can get the behavior I want but I'd like to not have to use a fixed width on this if possible. The .css I have looks like this, including some obviously ineffectual styles:
.blog-post-wrapper {
display: flex;
flex-direction: column;
overflow-y: scroll;
overflow-x: scroll;
}
.blog-post-content {
flex-grow: 1;
margin-bottom: 2rem;
width: 100%;
display: flex;
flex-direction: column;
overflow-y: scroll;
overflow-x: scroll;
}
.blog-post-content .prism-code {
padding: 20px;
border: 3px solid red;
flex-shrink: 1;
overflow-y: scroll;
overflow-x: scroll;
}
I'll attach images of the way the <pre> element is rendering presently, in inspector:
And this is how it looks if I set a fixed width (in inspector):
It's probably too late, but I had the same issue and I was able to fix it by
Adding max-width css property to the main layout. The value should be equal to window.screen.width. I had to use the following hack to be able to get the screen size:
const [windowWidth, setWindowWidth] = useState(width)
useEffect(() => {
setWindowWidth(window.screen.width)
}, [])
Adding overflow: scroll to the pre in the CodeBlock
Not ideal, but I found this combination of CSS properties working together:
pre code {
display: inline-block;
width: 80vw;
overflow-x: auto;
}

Have a "sticky" button right underneath another "sticky" navbar

I have an app file which contains my own custom appbar and different page components:
const styles = theme => ({
appBar: {
width:'100%',
},
});
class App extends Component {
render() {
const {classes} = this.props;
return (
<React.Fragment>
<CssBaseline />
<AppBar position="sticky" className={classes.appBar} />
<Page1 show={someCondition} />
<Page2 show={someCondition} />
.
.
<Page99 show={someCondition} />
</React.Fragment>
);
}
}
The Appbar is sticky so it always shows on the top.
Each page component has a button which is always on the top of that page:
const styles = theme => ({
button: {
width:'100%',
},
});
class Page99 extends Component {
render() {
const {classes} = this.props;
return (
<React.Fragment>
<div>
<Button variant="contained" className= {classes.button}>
Action Button
</Button>
</div>
{/* Some other stuff */>
</React.Fragment>
);
}
}
I know want this button to always be right under the appbar. So when the user scrolls down this button should remain sticky just like the appbar does. I tried to set the positioning to sticky hoping it would stack underneath it but it wouldn't. The appbar is dynamic so I don't know the exact height it will be since on different resolutions it will look different so I couldn't use something like fixed positioning.
You can set position of page container as relative and set button as absolute.
the you can align it to top right of the page or wherever you want.
Check this fiddle is this is what you need
.componentparent {
position: relative;
height:100px;
max-height: 50px;
overflow: auto;
}
.button {
position: fixed;
top: 30px;
}
.otherelements{
top: 70px;
position: relative;
}
<div id="parent-container">
<div> your app bar </div>
<div class='componentparent'>
<button class='button'>my button</button>
<div class='otherelements'>your component</div>
</div>
</div>
Place your button inside your appbar and set your button to position to absolute and add top: 100% to move it exactly at the bottom of appbar.

React style width based on length of props

I have a react component:
const TeamRow = props => {
const items = props.team.map((item, index) => {
const con_width = 100 / props.team.length;
const Style = {
team_img: {
width: "50px",
height: "auto"
},
member_container: {
float: "left",
width: `{con_width}%`
}
};
return (
<div style={Style.member_container}>
<img style={Style.team_img} src={logo} />
<p>{item.name}</p>
<p>{item.bio}</p>
</div>
);
});
return <div>{items}</div>;
};
The idea is to have a set of divs next to one another horizontally. The width of each div should equal 100% divided by the number of divs.
{con_width} is calculated correctly, but in the web inspector, there is no 'width' style at all. It just gets ignored by react. What am I doing wrong?
Change
width: `{con_width}%`
To
width: `${con_width}%`
You need to use ${} in template literals to print the value

How to make ReactPlayer scale with height and width

I am using reactplayer for a youtube video which uses iframe. I am trying to scale the video to my div and I want it to be responsive.
I put a width and height at 100% on the ReactPlayer, and I have a wrapper div that I put height and width on, but the reactplayer does not fit the div. It is stuck at a height of 150px no matter how I resize the screen.
<div className="video-wrapper>
<ReactPlayer
width="100%"
height="100%"
url="https://youtu.be/BGhqlJnFIXU"
controls
muted
config={{
youtube: {
playerVars: { showinfo: 1 }
}
}}
/>
</div>
.css
.video-wrapper {
height: 100%;
width: 100%;
min-height: 225px;
}
This can be easily achieved by further-extending your CSS. Since most videos are shot in 16:9, following this guide by Chris Coyier will make the process easily achievable.
Since you're utilizing React-Player, I am working with the content located on their demo page.
.player-wrapper {
width: auto; // Reset width
height: auto; // Reset height
}
.react-player {
padding-top: 56.25%; // Percentage ratio for 16:9
position: relative; // Set to relative
}
.react-player > div {
position: absolute; // Scaling will occur since parent is relative now
}
Why it works?
TL;DR - Padding in percentages is based on width. By setting an element's height to 0, we can utilize a percentage for 'padding-top' to scale content perfectly.
Generate 16:9's Percentage
(9 / 16) * 100 = 56.25
To force react-player be fully responsive I did the following:
CSS
.player-wrapper {
position: relative;
padding-top: 56.25%; /* 720 / 1280 = 0.5625 */
}
.react-player {
position: absolute;
top: 0;
left: 0;
}
JSX
import React from "react";
import ReactPlayer from "react-player";
import "./Player.css";
const Player = () => (
<div className="player-wrapper">
<ReactPlayer
url="https://youtu.be/3eLrINg3O2Q"
className="react-player"
playing
width="100%"
height="100%"
controls={false}
/>
</div>
);
export default Player;
The Easiest way to make the it responsive is adding the widht as 100%
<ReactPlayer
controls
pip
width="100%"
url={_post.videolink}
/>
you can fill with:
.react-player > video {
position: absolute;
object-fit: fill;
}
For a more modern approach, just add a class to the react player component and set it to:
height: auto !important;
aspect-ratio: 16/9;
https://caniuse.com/mdn-css_properties_aspect-ratio
you can keep the width fixed and then allow the height to adjust according to the video height as different video has its own size like 100*200.
.video-wrapper > video { width: 55vw; height: min-content; }
Here's how I did it.
video { object-fit: cover; }
Now, the size of the video can be adjusted by sizing the wrapper.
If you are using Tailwindcss and Swiper you can use this code
This code implements the last aspect-video class to ensure the aspect ratio always be correct
And also ensure that if the user scroll to the next page, the previous player gets paused
You can also set Max width of videos , here its "max-w-6xl"
import React, { useEffect, useState } from 'react'
import ReactPlayer from 'react-player'
import { Swiper, SwiperSlide } from 'swiper/react';
import { Pagination } from "swiper";
const videos = [
{
id: 1,
url: "https://www.youtube.com/watch?v=1234"
},
{
id: 2,
url: "https://www.youtube.com/watch?v=1234"
},
{
id: 3,
url: "https://www.youtube.com/watch?v=1234"
}
];
const IndexHero = () => {
const [domLoaded, setDomLoaded] = useState(false);
const [isPlaying, setIsPlaying] = useState(null);
useEffect(() => {
setDomLoaded(true);
}, []);
return (
<>
<div className='h-auto'>
{!domLoaded && (
<div className="flex items-start justify-center">
<div className="flex-1 max-w-6xl">
<div className="aspect-video">
{/** For CLS */}
</div>
</div>
</div>
)}
{domLoaded && (
<Swiper
modules={[Pagination]}
pagination={true}
className="h-full "
onSlideChange={() => {
setIsPlaying(null);
}}
autoplay={false}
watchSlidesProgress={true}
>
{videos.map((data) => (
<SwiperSlide key={data.id}>
<div className="flex items-start justify-center">
<div className="flex-1 max-w-6xl">
<div className="aspect-video">
<ReactPlayer
key={data.id}
url={data.url}
width="100%"
height="100%"
controls={true}
onPlay={() => {
setIsPlaying(data.id);
}}
playing={isPlaying === data.id}
/>
</div>
</div>
</div>
</SwiperSlide>
))}
</Swiper> )}
</div>
</>
);
};
export default IndexHero;

Resources