generated from agiledev-students-fall2023/generic-project-repository
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #213 from agiledev-students-fall2023/feat/post-arr…
…ange Improve Masonry layout of browse page
- Loading branch information
Showing
6 changed files
with
142 additions
and
151 deletions.
There are no files selected for viewing
Binary file added
BIN
+34 KB
back-end/public/images/Onions-image0-65789e8b0d75b95506130b52706d5eec.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |