How can I use bootstrap grid mapping an array of images? - wordpress

I'm building a portfolio website with gatsby.js. All photos are posted in wordpress, fetched by graphQL and rendered to the website.
I'm trying to use bootstrap grid to organize the photos and make it responsive, but because graphQL return an array with all images fetched from wordpress posts, I can't set a div with class='row' as I'm using array.map. And I don't know how to solve it.
From graphQL i'm setting resolution to width=300px and height=300px.
That's the only way I found to organize sizes, as long as I can't use class row and all images are considered in one row. The problem is that the photo size is not responsive, so it will always be 300X300...
I'd like a way to make it a grid system as it's suppose to work... So when I resize the window, all photos are organized and resized.
const IndexPage = () => {
const data = useStaticQuery(graphql`
query {
allWordpressPost {
edges {
node {
title
featured_media {
localFile {
childImageSharp {
resolutions(width: 300, height: 300) {
src
width
height
}
}
}
}
}
}
}
}
`);
const imagesResolutions = data.allWordpressPost.edges.map(
(edge) => edge.node.featured_media.localFile.childImageSharp.resolutions
);
return (
<Layout>
<Jumbotron />
<div className="container">
<h1 className="my-5 text-center">Portfolio</h1>
{imagesResolutions.map((imageRes) => (
<Img className="col-sm-6 col-lg-4 img-rounded img" resolutions={imageRes} key={imageRes.src} />
))}
</div>
</Layout>
);
};

If you split your data.allWordpressPost.edges array into a chunked array, you can loop through the outer array to render rows, and each of the inner arrays to render cols.
For a 3 column bootstrap grid, you want to pass in a size value of 3 (it's the 2nd param of lodash.chunk in this example). This ensures the length of each chunk is 3.
Here is a simple example ignoring the use of graphql, childImageSharp, and gatsby-image.
import ReactDOM from 'react-dom';
import React, { Component } from 'react';
import arrayChunk from 'lodash.chunk';
import 'bootstrap/dist/css/bootstrap.min.css';
const IndexPage = () => {
const rawData = [1, 2, 3, 4, 5, 6];
const chunkedData = arrayChunk(rawData, 3)
return (
<div>
<div className="container">
{chunkedData.map((row, rowIndex) => {
return (<div key={rowIndex} className="row">{
row.map((col, colIndex) => {return (<div key={colIndex} className="col-sm">{col}</div>)})
}</div>)
}
)}
</div>
</div>
);
};
ReactDOM.render(<IndexPage />, document.getElementById('root'));
stackblitz

Related

React component not rendering inline CSS

I am trying to write a react application where the App component renders an array of components to the screen. But the inline CSS are not showing up.
//App component
import data from "./data.js"
import Item from "./Item"
export default function App(){
const cardElements = data.map(item => <Item/>)
return (<div className='app'>
{cardElements}
</div>);
}
//Item component
export default function Item(){
const customStyle = {border: "2px solid black"};
return (<div style={customStyle} >Item component</div>);
}
The inline CSS in the Item component does not reflect on the webpage.
As you can see in the snippet below the inline style does indeed work. What is likely happening here is your style is bering overridden but we'd need more information to know for sure.
Sidenote: don't forget to add key prop when using .map in React.
const data = [1, 2, 3, 4];
function App() {
const cardElements = data.map(item => <Item key={item} />)
return (
<div className='app'>
{cardElements}
</div>
);
}
function Item() {
const customStyle = { border: "2px solid black" };
return <div style={customStyle}>Item component</div>;
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

Create responsive image display in React

I'm new to frontend, react and have a question on display responsive images.
Inside a React component, I have fetched some image info from an API endpoint and stored them inside a React.useRef in order to display them in a carousel. Like:
const carouselItem = React.useRef<Array<ImageTypeA>>([]);
apiResponse.forEach((image) => {
carouselItem.current.push({
images: (
<img
id={`${image.title}`}
src={image.imageUrl}
),
});
});
But there are two different sizes of carousels I need to display in the same component with the same set of images (In this case carouselItem).
For the two carousels, I want to display a smaller set of images and a larger set of images. And they should display according to the size of the screen (responsive).
const CarouselDiv1 = styled.div`
img: [];
`;
const CarouselDiv2 = styled.div`
img: [];
`;
... return method
return (
<div>
<CarouselDiv1>
// display smaller image carousel
</CarouselDiv1>
<CarouselDiv2>
// display larger image carousel
</CarouselDiv2>
</div>
)
I'm thinking of creating two styled components for each carousel and displaying them inside the return function. But I'm not sure how to do it or is there a better way to do it? Thanks
You can do something like this.
export default function App() {
const [isDesktop, setDesktop] = useState(window.innerWidth > 500);
const updateMedia = () => {
console.log(window.innerWidth);
setDesktop(window.innerWidth > 500);
};
useEffect(() => {
window.addEventListener("resize", updateMedia);
return () => window.removeEventListener("resize", updateMedia);
});
return (
<div>
{isDesktop ? (
<div>higher then 500px</div>
) : (
<div>lower then 500px</div>
)}
</div>
);
}
Or you can use the React-responsive package.
https://www.npmjs.com/package/react-responsive

Creating a custom slider block from InnerBlocks using React Slick

I'm trying to create a somewhat basic custom block that creates a slider based off of the element's inner/nested blocks. Using React Slick, I've ran into an issue where ALL of the inner blocks are being wrapped in a single tag inside of the initialized slick slider element. This means, no matter how many inner blocks I add, there's only a single slide inside of the slick slider element.
Here's a screenshot of what's happening:
I've highlighted the to show you how the inner blocks elements (two basic paragraph blocks) are being combined as one singular slide.
Here's my edit.js:
import { __ } from '#wordpress/i18n';
import React from "react";
import Slider from "react-slick";
import {
InnerBlocks,
useBlockProps,
useInnerBlocksProps,
InspectorControls
} from '#wordpress/block-editor';
import {
PanelBody,
PanelRow,
} from '#wordpress/components';
import './editor.scss';
import classnames from 'classnames';
export default function Edit(props) {
const blockProps = useBlockProps( {
className: classnames( {
'slider': true
} )
} );
var settings = {
dots: true,
infinite: true,
speed: 500,
slidesToShow: 1,
slidesToScroll: 1
};
const { children, ...innerBlocksProps } = useInnerBlocksProps( blockProps, {
templateInsertUpdatesSelection: true
} );
return (
<>
<InspectorControls key="1">
<PanelBody title={ __( 'Slides' ) }>
<PanelRow>
<fieldset>
Test
</fieldset>
</PanelRow>
</PanelBody>
</InspectorControls>
<div { ...innerBlocksProps }>
<Slider { ...settings }>
{ children }
</Slider>
</div>
</>
);
}
My gut is telling me this could have something to do with the timing of how the inner blocks get rendered, but I am a bit of a newbie when it comes to building custom blocks. Can anyone steer me in the right direction? I'd really really appreciate it. Thanks!

the React page is not scrollable

I am not able to make the page scrollable the code is as given below and i have not used any css i am still a new bee i have used map function to render the components i have sent the data using props thats it i am writing this because to fill space and nothing else
import { SimpleGrid, Flex } from "#chakra-ui/react";
import React, { useEffect, useState } from "react";
import Doccard from "../../components/doccard";
import { docdata } from "./docdat";
import data from "./docdat.json";
import Navbar from "../../components/Navbar";
import axios from "axios";
const url1 = "http://localhost:7000/doctors";
function Doctors() {
const [docdatai, setdocdatai] = useState([]);
useEffect(() => {
axios
.get(url1)
.then((Response) => {
console.log(Response.data);
setdocdatai(Response.data);
})
.catch((err) => {
console.log("error fetching data");
});
console.log("fetched");
}, []);
return (
<div className="scrollable-div" style={{ overflow: "scroll" }}>
<br />
<br />
<SimpleGrid columns={[2, 3, 5]} spacing={"20"} mt={4} mx={10}>
{console.log(docdatai)}
{docdatai.map((cardinfo, index) => {
return (
<Doccard
name={cardinfo.name}
username={cardinfo.username}
specialization={cardinfo.specialization}
/>
);
})}
</SimpleGrid>
</div>
);
}
export default Doctors;
Use overflow-y:auto for displaying scroll automatically when the content exceeds the divs set height.
Answer from ZWord, here
Keep in mind, the div will only be scrollable when the content inside is taller than the set height (you may want to set the height to 100vh and the width to 100vw to set the height / width to 100% of the screen). If you want to make the div x-axis scrollable, change it to overflow-x:auto, and if you want it to do both, use overflow:auto

Masonry layout with Next.js

I am developing a multi-page website using Next.js for the frontend and Strapi for the backend. On the blog page, the data is dynamically fetched from the database. I am trying to display this data in an adaptive layout using Masonry.
The problem I am having is that on first load, the page displays every grid-item in a single vertical column. On reload, the Masonry layout then takes effect, but with some overlapping items. On a second reload, everything then displays properly and is even responsive. However, if I navigate away from the page and come back, it is back to square one.
Here is the code for the blog page :
import React from 'react';
import Layout from '../components/StaticLayout';
import Link from 'next/link';
import fetch from 'isomorphic-unfetch';
import '../../static/styles.min.css';
let Masonry = '';
export default class Blog extends React.Component {
constructor(props) {
super(props);
this.state = {
appIsMounted: false
}
}
static async getInitialProps() {
const res = await fetch('http://localhost:1337/blogposts');
const data = await res.json();
return {
posts: data
};
}
async componentDidMount() {
let masonry = await import('masonry-layout');
this.setState({
appIsMounted: true
})
}
render() {
return (
<Layout>
<h1 className="blogHeader">Blog</h1>
{this.state.appIsMounted ? (
<div className="grid js-masonry" data-masonry-options='{ "itemSelector": ".grid-item", "columnWidth": 400 }'>
{this.props.posts.map(
post => (
<div key={post.id} className="grid-item">
<Link href="/p/[id]" as={`/p/${post.id}`}>
<img src={`http://localhost:1337${post.Image.url}`} alt={post.Image.name} className="postImage" />
</Link>
<div className="text">
<Link href="/p/[id]" as={`/p/${post.id}`}>
<a className="postLink">{post.Title}</a>
</Link>
<p className="blurb">{post.Content.substring(0, 300) + '...'}</p>
<Link href="/p/[id]" as={`/p/${post.id}`}>
<button className="viewPost">Read More!</button>
</Link>
</div>
</div>
)
)}
</div>
) : null}
</Layout>
)
}
}
To be noted, the StaticLayout script only ensure the header is at the top of each page.
EDIT
So I've solved the overlapping part of the problem. Instead of initializing Masonry in the html, I've added a js file to './static'. On my blog page, I've added the following :
import Head from 'next/head'
...
return(
<Head>
<script type="text/javascript" src="/static/runMasonry.js"></script>
</Head>
...
)
And the runMasonry.js file looks like this :
window.onload = function () {
var elem = document.querySelector('.grid');
var msnry = new Masonry(elem, {
// options
itemSelector: '.grid-item',
columnWidth: 160,
gutter: 20
});
}
But when I first arrive on the page or navigate away and come back, the elements are still displayed in a column and require a reload.
Came up with a non-ideal fix, but it works.
Added the following code to the runMasonry.js file :
window.addEventListener("DOMSubtreeModified", function () {
var elem = document.querySelector('.grid');
if (elem) {
var msnry = new Masonry(elem, {
// options
itemSelector: '.grid-item',
columnWidth: 160,
gutter: 20
});
}
});
So it detects when new page content is loaded and, if it finds my .grid element, re-runs masonry.
Leaving this open in case someone can provide a better, long-term solution.

Resources