Skip to content

Commit 3980e24

Browse files
committed
feat: integrate react-responsive-masonry for improved gallery layout
- Added the react-responsive-masonry package to enhance the layout of the GalleryShowcase component, allowing for a responsive grid display of galleries. - Updated the GalleryShowcase component to utilize Masonry and ResponsiveMasonry for better visual organization of gallery items. - Included new dependencies in package.json and pnpm-lock.yaml for react-responsive-masonry and its types. Signed-off-by: Innei <[email protected]>
1 parent 471c1b1 commit 3980e24

File tree

3 files changed

+116
-96
lines changed

3 files changed

+116
-96
lines changed

apps/landing/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"react-error-boundary": "6.0.0",
3939
"react-intersection-observer": "10.0.0",
4040
"react-markdown": "^9.0.1",
41+
"react-responsive-masonry": "2.7.1",
4142
"sonner": "2.0.7",
4243
"tailwind-merge": "3.4.0",
4344
"usehooks-ts": "3.1.1",
@@ -58,6 +59,7 @@
5859
"@types/node": "24.10.1",
5960
"@types/react": "19.2.3",
6061
"@types/react-dom": "19.2.3",
62+
"@types/react-responsive-masonry": "2.6.0",
6163
"autoprefixer": "10.4.22",
6264
"code-inspector-plugin": "1.2.10",
6365
"cross-env": "10.1.0",

apps/landing/src/components/landing/GalleryShowcase.tsx

Lines changed: 99 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useQuery } from '@tanstack/react-query'
44
import { useLocale, useTranslations } from 'next-intl'
55
import { useEffect, useState } from 'react'
6+
import Masonry, { ResponsiveMasonry } from 'react-responsive-masonry'
67

78
import { API_URL } from '~/constants/env'
89

@@ -152,109 +153,113 @@ export const GalleryShowcase = () => {
152153
)}
153154

154155
{!isLoading && !error && galleries.length > 0 && (
155-
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
156-
{galleries.map((gallery) => (
157-
<a
158-
key={gallery.id}
159-
href={buildGalleryUrl(gallery)}
160-
target="_blank"
161-
rel="noopener noreferrer"
162-
className="group relative overflow-hidden rounded-3xl border border-white/10 bg-linear-to-br from-white/8 to-transparent p-6 transition hover:border-white/30 hover:bg-white/10"
163-
>
164-
{/* Author Avatar & Info */}
165-
{gallery.author && (
166-
<div className="mb-4 flex items-center gap-3">
167-
<div className="relative size-10 shrink-0 overflow-hidden rounded-full border border-white/10 bg-white/5">
168-
{gallery.author.avatar ? (
169-
<img
170-
src={gallery.author.avatar}
171-
alt={gallery.author.name}
172-
className="h-full w-full object-cover"
173-
onError={(e) => {
174-
const target = e.target as HTMLImageElement
175-
target.style.display = 'none'
176-
}}
177-
/>
178-
) : null}
179-
{(!gallery.author.avatar ||
180-
gallery.author.avatar === '') && (
181-
<div className="bg-accent-20 text-accent flex h-full w-full items-center justify-center">
182-
<span className="text-sm font-medium">
183-
{gallery.author.name.charAt(0).toUpperCase()}
184-
</span>
185-
</div>
186-
)}
187-
</div>
188-
<div className="min-w-0 flex-1">
189-
<p className="truncate text-sm font-medium text-white">
190-
{gallery.author.name}
191-
</p>
192-
<p className="truncate text-xs text-white/50">
193-
{getDisplayUrl(gallery)}
194-
</p>
156+
<ResponsiveMasonry
157+
columnsCountBreakPoints={{ 350: 1, 640: 2, 1024: 3 }}
158+
>
159+
<Masonry gutter="1rem">
160+
{galleries.map((gallery) => (
161+
<a
162+
key={gallery.id}
163+
href={buildGalleryUrl(gallery)}
164+
target="_blank"
165+
rel="noopener noreferrer"
166+
className="group relative block w-full overflow-hidden rounded-3xl border border-white/10 bg-linear-to-br from-white/8 to-transparent p-6 transition hover:border-white/30 hover:bg-white/10"
167+
>
168+
{/* Author Avatar & Info */}
169+
{gallery.author && (
170+
<div className="mb-4 flex items-center gap-3">
171+
<div className="relative size-10 shrink-0 overflow-hidden rounded-full border border-white/10 bg-white/5">
172+
{gallery.author.avatar ? (
173+
<img
174+
src={gallery.author.avatar}
175+
alt={gallery.author.name}
176+
className="h-full w-full object-cover"
177+
onError={(e) => {
178+
const target = e.target as HTMLImageElement
179+
target.style.display = 'none'
180+
}}
181+
/>
182+
) : null}
183+
{(!gallery.author.avatar ||
184+
gallery.author.avatar === '') && (
185+
<div className="bg-accent-20 text-accent flex h-full w-full items-center justify-center">
186+
<span className="text-sm font-medium">
187+
{gallery.author.name.charAt(0).toUpperCase()}
188+
</span>
189+
</div>
190+
)}
191+
</div>
192+
<div className="min-w-0 flex-1">
193+
<p className="truncate text-sm font-medium text-white">
194+
{gallery.author.name}
195+
</p>
196+
<p className="truncate text-xs text-white/50">
197+
{getDisplayUrl(gallery)}
198+
</p>
199+
</div>
195200
</div>
196-
</div>
197-
)}
201+
)}
198202

199-
{/* Site Name */}
200-
<h3 className="group-hover:text-accent mb-2 font-serif text-xl text-white transition">
201-
{gallery.name}
202-
</h3>
203+
{/* Site Name */}
204+
<h3 className="group-hover:text-accent mb-2 font-serif text-xl text-white transition">
205+
{gallery.name}
206+
</h3>
203207

204-
{/* Description */}
205-
{gallery.description && (
206-
<p className="mb-4 line-clamp-2 text-sm leading-relaxed text-white/70">
207-
{gallery.description}
208-
</p>
209-
)}
208+
{/* Description */}
209+
{gallery.description && (
210+
<p className="mb-4 line-clamp-2 text-sm leading-relaxed text-white/70">
211+
{gallery.description}
212+
</p>
213+
)}
210214

211-
{/* Photo Count & Tags */}
212-
<div className="mb-4 space-y-2">
213-
{gallery.photoCount > 0 && (
214-
<div className="flex items-center gap-2 text-xs text-white/60">
215-
<i className="i-lucide-image size-3.5" />
216-
<span>
217-
{gallery.photoCount}{' '}
215+
{/* Photo Count & Tags */}
216+
<div className="mb-4 space-y-2">
217+
{gallery.photoCount > 0 && (
218+
<div className="flex items-center gap-2 text-xs text-white/60">
219+
<i className="i-lucide-image size-3.5" />
218220
<span>
219-
{gallery.photoCount === 1 ? 'photo' : 'photos'}
220-
</span>
221-
</span>
222-
</div>
223-
)}
224-
{gallery.tags.length > 0 && (
225-
<div className="flex flex-wrap gap-1.5">
226-
{gallery.tags.slice(0, 4).map((tag) => (
227-
<span
228-
key={tag}
229-
className="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 text-xs text-white/70"
230-
>
231-
{tag}
232-
</span>
233-
))}
234-
{gallery.tags.length > 4 && (
235-
<span className="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 text-xs text-white/50">
236-
+{gallery.tags.length - 4}
221+
{gallery.photoCount}{' '}
222+
<span>
223+
{gallery.photoCount === 1 ? 'photo' : 'photos'}
224+
</span>
237225
</span>
238-
)}
239-
</div>
240-
)}
241-
</div>
226+
</div>
227+
)}
228+
{gallery.tags.length > 0 && (
229+
<div className="flex flex-wrap gap-1.5">
230+
{gallery.tags.slice(0, 4).map((tag) => (
231+
<span
232+
key={tag}
233+
className="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 text-xs text-white/70"
234+
>
235+
{tag}
236+
</span>
237+
))}
238+
{gallery.tags.length > 4 && (
239+
<span className="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 text-xs text-white/50">
240+
+{gallery.tags.length - 4}
241+
</span>
242+
)}
243+
</div>
244+
)}
245+
</div>
242246

243-
{/* Divider */}
244-
<div className="mb-4 h-px w-full bg-linear-to-r from-transparent via-white/30 to-transparent opacity-50" />
247+
{/* Divider */}
248+
<div className="mb-4 h-px w-full bg-linear-to-r from-transparent via-white/30 to-transparent opacity-50" />
245249

246-
{/* Footer */}
247-
<div className="flex items-center justify-between">
248-
<div className="text-xs text-white/40">
249-
{formatDate(gallery.createdAt)}
250-
</div>
251-
<div className="text-white/30 transition group-hover:text-white/60">
252-
<i className="i-lucide-external-link size-4" />
250+
{/* Footer */}
251+
<div className="flex items-center justify-between">
252+
<div className="text-xs text-white/40">
253+
{formatDate(gallery.createdAt)}
254+
</div>
255+
<div className="text-white/30 transition group-hover:text-white/60">
256+
<i className="i-lucide-external-link size-4" />
257+
</div>
253258
</div>
254-
</div>
255-
</a>
256-
))}
257-
</div>
259+
</a>
260+
))}
261+
</Masonry>
262+
</ResponsiveMasonry>
258263
)}
259264
</section>
260265
)

pnpm-lock.yaml

Lines changed: 15 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)