From 8e962d15d1ac975c19b778265328141ed112320b Mon Sep 17 00:00:00 2001 From: CodingOnStar Date: Wed, 15 Oct 2025 17:20:00 +0800 Subject: [PATCH] feat: improve explore page banner component with enhanced layout and responsive styles --- .../components/explore/banner/banner-item.tsx | 77 ++++++++++++------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/web/app/components/explore/banner/banner-item.tsx b/web/app/components/explore/banner/banner-item.tsx index b956123d9e..c5c95a4d36 100644 --- a/web/app/components/explore/banner/banner-item.tsx +++ b/web/app/components/explore/banner/banner-item.tsx @@ -27,10 +27,15 @@ type BannerItemProps = { const RESPONSIVE_BREAKPOINT = 1280 const MAX_RESPONSIVE_WIDTH = 600 +const INDICATOR_WIDTH = 20 +const INDICATOR_GAP = 8 +const MIN_VIEW_MORE_WIDTH = 480 export const BannerItem: FC = ({ banner, autoplayDelay, isPaused = false }) => { const { t } = useTranslation() const { api, selectedIndex } = useCarousel() + const { category, title, description, 'img-src': imgSrc } = banner.content + const [resetKey, setResetKey] = useState(0) const textAreaRef = useRef(null) const [maxWidth, setMaxWidth] = useState(undefined) @@ -42,6 +47,21 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause return { slides, totalSlides, nextIndex } }, [api, selectedIndex]) + const indicatorsWidth = useMemo(() => { + const count = slideInfo.totalSlides + if (count === 0) return 0 + // Calculate: indicator buttons + gaps + extra spacing (3 * 20px for divider and padding) + return (count + 2) * INDICATOR_WIDTH + (count - 1) * INDICATOR_GAP + }, [slideInfo.totalSlides]) + + const viewMoreStyle = useMemo(() => { + if (!maxWidth) return undefined + return { + maxWidth: `${maxWidth}px`, + minWidth: indicatorsWidth ? `${Math.min(maxWidth - indicatorsWidth, MIN_VIEW_MORE_WIDTH)}px` : undefined, + } + }, [maxWidth, indicatorsWidth]) + const responsiveStyle = useMemo( () => (maxWidth !== undefined ? { maxWidth: `${maxWidth}px` } : undefined), [maxWidth], @@ -49,7 +69,6 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause const incrementResetKey = useCallback(() => setResetKey(prev => prev + 1), []) - // Update max width based on text area width when screen < 1280px useEffect(() => { const updateMaxWidth = () => { if (window.innerWidth < RESPONSIVE_BREAKPOINT && textAreaRef.current) { @@ -75,12 +94,11 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause } }, []) - // Reset progress when slide changes useEffect(() => { incrementResetKey() }, [selectedIndex, incrementResetKey]) - const handleClick = useCallback(() => { + const handleBannerClick = useCallback(() => { incrementResetKey() if (banner.link) window.open(banner.link, '_blank', 'noopener,noreferrer') @@ -93,10 +111,8 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause return (
{/* Left content area */}
@@ -110,10 +126,10 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause style={responsiveStyle} >

- {banner.content.category} + {category}

- {banner.content.title} + {title}

{/* Description area */} @@ -122,17 +138,17 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause style={responsiveStyle} >

- {banner.content.description} + {description}

{/* Actions section */} -
+
{/* View more button */}
@@ -142,23 +158,26 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause
- {/* Slide navigation indicators */}
- {slideInfo.slides.map((_: unknown, index: number) => ( - handleIndicatorClick(index)} - /> - ))} + {/* Slide navigation indicators */} +
+ {slideInfo.slides.map((_: unknown, index: number) => ( + handleIndicatorClick(index)} + /> + ))} +
+
@@ -167,8 +186,8 @@ export const BannerItem: FC = ({ banner, autoplayDelay, isPause {/* Right image area */}
{banner.content.title}