Skip to content

Commit

Permalink
Merge pull request #213 from agiledev-students-fall2023/feat/post-arr…
Browse files Browse the repository at this point in the history
…ange

Improve Masonry layout of browse page
  • Loading branch information
kevin-huang-cc authored Jan 28, 2025
2 parents 56cea66 + 4169740 commit d0f1fc1
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 151 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 84 additions & 0 deletions front-end/src/components/MasonryLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useEffect, useRef, useState } from "react";

const MasonryLayout = ({ images, columnCount = 3 }) => {
const containerRef = useRef(null);
const [columnHeights, setColumnHeights] = useState(Array(columnCount).fill(0));
const [positions, setPositions] = useState([]);

useEffect(() => {
if (images.length) {
positionItems();
}
// Recalculate positions when the window resizes
const handleResize = () => {
setColumnHeights(Array(columnCount).fill(0));
positionItems();
};
window.addEventListener("resize", handleResize);

return () => {
window.removeEventListener("resize", handleResize);
};
}, [images, columnCount]);

const positionItems = () => {
if (!containerRef.current) return;

const containerWidth = containerRef.current.offsetWidth;
const columnWidth = containerWidth / columnCount;
const newColumnHeights = Array(columnCount).fill(0);
const newPositions = [];

images.forEach((image, index) => {
const shortestColumnIndex = newColumnHeights.indexOf(
Math.min(...newColumnHeights)
);
const top = newColumnHeights[shortestColumnIndex];
const left = shortestColumnIndex * columnWidth;

newPositions.push({ top, left, width: columnWidth });
newColumnHeights[shortestColumnIndex] += image.height * (columnWidth / image.width);
});

setPositions(newPositions);
setColumnHeights(newColumnHeights);
};

return (
<div
ref={containerRef}
style={{
position: "relative",
width: "100%",
height: Math.max(...columnHeights),
}}
>
{images.map((image, index) => (
<div
key={index}
style={{
position: "absolute",
top: positions[index]?.top || 0,
left: positions[index]?.left || 0,
width: positions[index]?.width || "auto",
transition: "all 0.3s ease-in-out",
}}
>
<img
src={image.src}
alt={image.alt || ""}
style={{
display: "block",
width: "100%",
height: "auto",
backgroundColor: "#f0f0f0", // Placeholder background
}}
loading="lazy"
/>
</div>
))}
</div>
);
};

export default MasonryLayout;
14 changes: 9 additions & 5 deletions front-end/src/components/PostBlock.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
.post-block {
margin-bottom: 1rem; /* Space between posts */
width: 33%; /* Example for three columns layout */
float: left; /* Helps in positioning with Masonry */
/*background-color: #cc554d60;*/
display: block; /* Prevent inline-block from conflicting */
margin-bottom: 1rem; /* Consistent vertical spacing */
width: 100%; /* Full column width */
background-color: #F5F3EE;
border-radius: 20px;
box-shadow: 2px 2px 5px #d7cbb3;
container-type: inline-size;
cursor: pointer;
break-inside: avoid; /* Prevent breaking */
overflow: hidden; /* Prevent content overflow */
}

.post-block .post-block-image-container {
Expand Down Expand Up @@ -71,4 +71,8 @@
margin-right: 0.5em;
/*border: .05em solid #efe2c0;*/
box-shadow: 0 0 .1em .05em #efe2c0;
}

.post-block:hover {
transform: translateY(-5px);
}
7 changes: 6 additions & 1 deletion front-end/src/components/PostBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ const PostBlock = ({ post }) => {
<span>{post.title}</span>
</div>
<div className='post-author'>
<img src={`${process.env.REACT_APP_BACKEND_HOST}${post.usrImg}`} alt="Post" className="post-author-img" />
<img
src={`${process.env.REACT_APP_BACKEND_HOST}${post.usrImg}`}
alt="Post"
className="post-author-img"
loading="lazy"
/>

<div className='post-author-name'>
{post.author}
Expand Down
111 changes: 31 additions & 80 deletions front-end/src/components/PostFlow.css
Original file line number Diff line number Diff line change
@@ -1,95 +1,46 @@
/* PostFlow.css */


.post-flow {
.post-flow-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: start;
}

@media (max-width: 300px) {
.post-block {
width: 100%;
margin-right: 0;
margin-left: 0;
height: clamp(34vh, 30vw, 45vh);
}
}
@media (min-width: 300px){
.post-block {
width: calc(50% - 10px);
margin-bottom: 10px;
/* min-height: 40vw; */
height: clamp(70vw, 70vw, 70vw);

.post-flow {
width: 70%;
column-count: 2; /* Number of columns (adjust for responsiveness) */
column-gap: 20px; /* Space between columns */
}

@media (max-width: 768px) {
.post-flow {
column-count: 1; /* Single column for smaller screens */
}
}
@media (min-width: 350px){
.post-block {
width: calc(50% - 10px);
margin-bottom: 10px;
/* min-height: 40vw; */
height: clamp(60vw, 60vw, 60vw);
}
}
@media (min-width: 500px){
.post-block {
width: calc(50% - 10px);
margin-bottom: 10px;
/* max-height: 45vw; */
height: clamp(50vw, 50vw, 50vw);

@media (min-width: 1200px) {
.post-flow {
column-count: 3; /* More columns for larger screens */
}
}
@media (min-width: 600px){
.post-block {
width: calc(33% - 10px);
margin-bottom: 10px;
max-height: 40vw;
/* height: clamp(50vw, 50vw, 50vw); */
}
}
@media (min-width: 900px){
.post-block {
width: calc(33% - 10px);
margin-bottom: 10px;
max-height: 30vw;
}
}
@media (min-width: 1100px){
.post-block {
width: calc(25% - 10px);
margin-bottom: 10px;
min-height: 20vw;
/* height: clamp(30vh, 10vw, 40vh); */
max-height: 25vw;
}
}
@media (min-width: 1500px){
.post-block {
width: calc(20% - 20px);
margin-left: 10px;
margin-right: 2px;
margin-bottom: 15px;
height: clamp(30vh, 10vw, 45vh);
/* background-color: aquamarine; */
/* max-height: 10px; */
height: clamp(10vw, 50vh, 20vw);
min-height: 10vw;
max-height: 20vw;

}
.post-block {
display: inline-block; /* Ensures items are treated individually */
width: 100%; /* Block spans full column width */
margin-bottom: 20px; /* Consistent vertical spacing between items */
background-color: #F5F3EE;
border-radius: 20px;
box-shadow: 2px 2px 5px #d7cbb3;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}



/*.post-block:nth-child(odd) {
margin-right: 20px;
.post-block:hover {
transform: translateY(-5px);
box-shadow: 2px 4px 10px #d7cbb3;
}

.post-block:nth-child(even) {
margin-left: 20px;
} */



.post-block .post-block-image {
width: 100%;
height: auto;
object-fit: contain; /* Ensure the image fits within the block */
border-radius: 20px;
}
77 changes: 12 additions & 65 deletions front-end/src/components/PostFlow.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,17 @@
import React, { useEffect, useRef, useState } from 'react';
import Masonry from 'masonry-layout';
import imagesLoaded from 'imagesloaded';
import PostBlock from './PostBlock';
import { debounce } from 'lodash';
import React from "react";
import PostBlock from "./PostBlock";
import "./PostFlow.css";

const PostFlow = ({ posts }) => {
const gridRef = useRef(null);
const[layoutReady, setLayoutReady] = useState(false)

useEffect(() => {
const gridElement = gridRef.current;
if (gridElement) {
// Ensure all images are loaded before layout with Masonry
imagesLoaded(gridElement, () => {
setTimeout(() => {
setLayoutReady(true);
}, 100);
});
}
}, [posts]);

useEffect(() => {
const gridElement = gridRef.current;
if (gridElement && layoutReady) {
const msnry = new Masonry(gridElement, {
itemSelector: '.post-block',
columnWidth: '.post-block',
percentPosition: true,
gutter: 15,
});

const layoutAndCenter = () => {
msnry.layout();
centerMasonryColumns(msnry);
}

const centerMasonryColumns = (msnry) => {
const msnryWidth = msnry.cols * msnry.columnWidth;
const viewportWidth = window.innerWidth;
const extraSpace = (viewportWidth - msnryWidth) / 2;
msnry.element.style.marginLeft = `${extraSpace > 0 ? extraSpace : 0}px`;
console.log("Masonry Grid Width:", msnryWidth, "viewport Width:", viewportWidth);
};

const resizeMasonryGrid = debounce(layoutAndCenter, 150);
window.addEventListener('resize', resizeMasonryGrid);

layoutAndCenter(); //Initial layout
setTimeout(layoutAndCenter, 700);

return () => {
window.removeEventListener('resize', resizeMasonryGrid);
msnry.destroy();
};
}
}, [layoutReady]);

return (
<div className="post-flow-container">
<div ref={gridRef} className="post-flow">
{posts.map((post) => (
<PostBlock key={post.id} post={post} />
))}
</div>
</div>
);
return (
<div className="post-flow-container">
<div className="post-flow">
{posts.map((post) => (
<PostBlock key={post.id} post={post} />
))}
</div>
</div>
);
};

export default PostFlow;

0 comments on commit d0f1fc1

Please sign in to comment.