Remove legacy frontend assets and update gallery routes
This commit is contained in:
@@ -124,6 +124,10 @@ function Rail({ title, emoji, items, seeAllHref }) {
|
||||
const scrollRef = useRef(null)
|
||||
const isResettingRef = useRef(false)
|
||||
const scrollEndTimer = useRef(null)
|
||||
const suppressClickTimerRef = useRef(null)
|
||||
const touchStartRef = useRef({ x: 0, y: 0 })
|
||||
const draggedRef = useRef(false)
|
||||
const suppressClickRef = useRef(false)
|
||||
const itemCount = items.length
|
||||
|
||||
/* Triple items so we can loop seamlessly: [clone|original|clone] */
|
||||
@@ -139,6 +143,13 @@ function Rail({ title, emoji, items, seeAllHref }) {
|
||||
return el.children[itemCount].offsetLeft - el.children[0].offsetLeft
|
||||
}, [itemCount])
|
||||
|
||||
/* Scroll step based on rendered card width + gap for predictable smooth motion */
|
||||
const getStepWidth = useCallback(() => {
|
||||
const el = scrollRef.current
|
||||
if (!el || el.children.length < 2) return el ? el.clientWidth * 0.75 : 0
|
||||
return el.children[1].offsetLeft - el.children[0].offsetLeft
|
||||
}, [])
|
||||
|
||||
/* Centre on the middle (real) set after mount / data change */
|
||||
useEffect(() => {
|
||||
const el = scrollRef.current
|
||||
@@ -176,6 +187,19 @@ function Rail({ title, emoji, items, seeAllHref }) {
|
||||
}
|
||||
}, [getSetWidth, itemCount])
|
||||
|
||||
/* Keep user in the centre segment before scripted smooth scroll starts */
|
||||
const normalizeToMiddle = useCallback(() => {
|
||||
const el = scrollRef.current
|
||||
if (!el || !itemCount) return
|
||||
const setW = getSetWidth()
|
||||
if (setW === 0) return
|
||||
if (el.scrollLeft < setW || el.scrollLeft >= setW * 2) {
|
||||
el.style.scrollBehavior = 'auto'
|
||||
el.scrollLeft = ((el.scrollLeft % setW) + setW) % setW + setW
|
||||
el.style.scrollBehavior = ''
|
||||
}
|
||||
}, [getSetWidth, itemCount])
|
||||
|
||||
/* Scroll listener: debounced boundary check + resize re-centre */
|
||||
useEffect(() => {
|
||||
const el = scrollRef.current
|
||||
@@ -199,6 +223,7 @@ function Rail({ title, emoji, items, seeAllHref }) {
|
||||
el.removeEventListener('scroll', onScroll)
|
||||
window.removeEventListener('resize', onResize)
|
||||
clearTimeout(scrollEndTimer.current)
|
||||
clearTimeout(suppressClickTimerRef.current)
|
||||
}
|
||||
}, [loopItems, resetIfNeeded, getSetWidth])
|
||||
|
||||
@@ -219,8 +244,48 @@ function Rail({ title, emoji, items, seeAllHref }) {
|
||||
const scroll = useCallback((dir) => {
|
||||
const el = scrollRef.current
|
||||
if (!el) return
|
||||
const amount = el.clientWidth * 0.75
|
||||
normalizeToMiddle()
|
||||
const step = getStepWidth()
|
||||
const amount = step > 0 ? step * 2 : el.clientWidth * 0.75
|
||||
el.scrollBy({ left: dir === 'left' ? -amount : amount, behavior: 'smooth' })
|
||||
clearTimeout(scrollEndTimer.current)
|
||||
scrollEndTimer.current = setTimeout(resetIfNeeded, 260)
|
||||
}, [getStepWidth, normalizeToMiddle, resetIfNeeded])
|
||||
|
||||
/* Prevent accidental link activation after horizontal swipe on touch devices */
|
||||
const onTouchStart = useCallback((e) => {
|
||||
if (!e.touches?.length) return
|
||||
const t = e.touches[0]
|
||||
touchStartRef.current = { x: t.clientX, y: t.clientY }
|
||||
draggedRef.current = false
|
||||
}, [])
|
||||
|
||||
const onTouchMove = useCallback((e) => {
|
||||
if (!e.touches?.length) return
|
||||
const t = e.touches[0]
|
||||
const dx = Math.abs(t.clientX - touchStartRef.current.x)
|
||||
const dy = Math.abs(t.clientY - touchStartRef.current.y)
|
||||
if (dx > 10 && dx > dy) {
|
||||
draggedRef.current = true
|
||||
}
|
||||
}, [])
|
||||
|
||||
const onTouchEnd = useCallback(() => {
|
||||
if (!draggedRef.current) return
|
||||
suppressClickRef.current = true
|
||||
clearTimeout(suppressClickTimerRef.current)
|
||||
suppressClickTimerRef.current = setTimeout(() => {
|
||||
suppressClickRef.current = false
|
||||
}, 260)
|
||||
}, [])
|
||||
|
||||
const onClickCapture = useCallback((e) => {
|
||||
if (!suppressClickRef.current) return
|
||||
const link = e.target?.closest?.('a')
|
||||
if (link) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!items.length) return null
|
||||
@@ -238,7 +303,7 @@ function Rail({ title, emoji, items, seeAllHref }) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="relative" data-nav-swipe-ignore="1">
|
||||
{/* Permanent edge fades for infinite illusion */}
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 z-20 w-24 bg-gradient-to-r from-[#0F1724] to-transparent" />
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 z-20 w-24 bg-gradient-to-l from-[#0F1724] to-transparent" />
|
||||
@@ -248,7 +313,11 @@ function Rail({ title, emoji, items, seeAllHref }) {
|
||||
|
||||
<div
|
||||
ref={scrollRef}
|
||||
className="flex gap-4 overflow-x-auto px-4 pb-3 sm:px-6 lg:px-8 scrollbar-hide [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
|
||||
onTouchStart={onTouchStart}
|
||||
onTouchMove={onTouchMove}
|
||||
onTouchEnd={onTouchEnd}
|
||||
onClickCapture={onClickCapture}
|
||||
className="flex gap-4 overflow-x-auto px-4 pb-3 sm:px-6 lg:px-8 snap-x snap-mandatory scroll-smooth scrollbar-hide [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
|
||||
>
|
||||
{loopItems.map((item, idx) => (
|
||||
<RailCard key={`${item.id || item.url}-${idx}`} item={item} />
|
||||
@@ -356,9 +425,13 @@ export default function ArtworkRecommendationsRails({ artwork, related = [] }) {
|
||||
? `/discover/trending`
|
||||
: '/discover/trending'
|
||||
|
||||
const similarHref = artwork?.name
|
||||
? `/search?q=${encodeURIComponent(artwork.name)}`
|
||||
: '/search'
|
||||
|
||||
return (
|
||||
<div className="space-y-14">
|
||||
<Rail title="Similar Artworks" emoji="✨" items={similarItems} />
|
||||
<Rail title="Similar Artworks" emoji="✨" items={similarItems} seeAllHref={similarHref} />
|
||||
<Rail title={trendingLabel} emoji="🔥" items={trendingRailItems} seeAllHref={trendingHref} />
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user