fix(web): match standalone marketplace mobile banners and collections

Show recommend as icon-only on mobile, keep event/ad posters uncropped,
and hide collection carousel dots under 879px.
This commit is contained in:
CodingOnStar 2026-08-24 19:02:46 +08:00
parent 0da1c88de7
commit 6e5cd92eec
4 changed files with 105 additions and 25 deletions

View File

@ -12,18 +12,18 @@ import type { MarketplaceBannerPage } from './banners'
import { cn } from '@langgenius/dify-ui/cn'
import { useTranslation } from '#i18n'
import { trackEvent } from '@/app/components/base/amplitude'
import {
rememberMarketplaceSiteReferrer,
trackMarketplaceSiteEvent,
} from '@/utils/marketplace-site-track'
import { buildMarketplaceBannerClickProperties } from './home-trending-track'
import Partner from '@/app/components/plugins/base/badges/partner'
import Verified from '@/app/components/plugins/base/badges/verified'
import { MARKETPLACE_API_PREFIX } from '@/config'
import Link from '@/next/link'
import {
rememberMarketplaceSiteReferrer,
trackMarketplaceSiteEvent,
} from '@/utils/marketplace-site-track'
import { getPluginLinkInMarketplace } from '../utils'
import background from './assets/background.webp'
import difyUpdatesArt from './assets/dify-updates-art.png'
import { buildMarketplaceBannerClickProperties } from './home-trending-track'
import styles from './home-trending.module.css'
const getMarketplaceAssetURL = (path?: string) => {
@ -120,7 +120,12 @@ function TrendingCopy({
<h2 className="shrink-0 text-xl leading-6 font-semibold tracking-[-0.4px] text-text-primary">
{heading}
</h2>
<p className="w-full text-[13px] leading-5 font-normal tracking-[-0.065px] text-text-tertiary">
<p
className={cn(
styles.copyDescription,
'w-full text-[13px] leading-5 font-normal tracking-[-0.065px] text-text-tertiary',
)}
>
{description}
</p>
</div>
@ -175,7 +180,10 @@ function TrendingCard({
)}
>
<div
className="flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-[10px] border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge"
className={cn(
styles.cardIcon,
'flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-[10px] border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge',
)}
style={{
backgroundColor: !iconURL ? card.icon_background : undefined,
}}
@ -196,7 +204,7 @@ function TrendingCard({
)}
</div>
<div className="flex w-full items-end gap-1">
<div className={cn(styles.cardMeta, 'flex w-full items-end gap-1')}>
<div className="flex min-w-0 flex-1 flex-col items-start gap-[3px]">
<div className="flex w-full min-w-0 items-center gap-[3px]">
<h3 className="min-w-0 truncate text-sm leading-[normal] font-medium text-text-primary">
@ -257,9 +265,18 @@ function TrendingRecommendationSlide({
height={900}
alt=""
aria-hidden
className="absolute top-[-173px] left-[-990px] h-[1201px] w-[2135px] max-w-none opacity-80"
className={cn(
styles.recommendBackdrop,
'absolute top-[-173px] left-[-990px] h-[1201px] w-[2135px] max-w-none opacity-80',
)}
/>
<div
aria-hidden
className={cn(
styles.recommendBackdrop,
'absolute inset-0 bg-text-accent mix-blend-color',
)}
/>
<div aria-hidden className="absolute inset-0 bg-text-accent mix-blend-color" />
<div className={cn(styles.recommendCards, 'relative z-10 h-full items-center')}>
{banner.content.cards.map((card) => (
@ -440,20 +457,12 @@ export function HomeBannerSlide({
}) {
if (banner.style_type === 'blog')
return (
<BlogBannerSlide
banner={banner}
isMarketplacePlatform={isMarketplacePlatform}
page={page}
/>
<BlogBannerSlide banner={banner} isMarketplacePlatform={isMarketplacePlatform} page={page} />
)
if (banner.style_type === 'event' || banner.style_type === 'ad')
return (
<ImageBannerSlide
banner={banner}
isMarketplacePlatform={isMarketplacePlatform}
page={page}
/>
<ImageBannerSlide banner={banner} isMarketplacePlatform={isMarketplacePlatform} page={page} />
)
return (

View File

@ -116,8 +116,60 @@
width: 100%;
}
/* Recommend mobile: 96px app icons (Figma 1026:24938), not desktop cards. */
:global([data-marketplace-standalone]) .recommendBackdrop {
display: none;
}
:global([data-marketplace-standalone]) .recommendCards {
padding: 16px 20px;
justify-content: center;
gap: 20px;
overflow: hidden;
padding: 36px 12px;
}
:global([data-marketplace-standalone]) .recommendCards > .card {
display: flex;
flex: none;
align-items: center;
justify-content: center;
width: 96px;
min-width: 96px;
max-width: 96px;
height: 96px;
padding: 0;
overflow: visible;
background: transparent;
border-radius: 20px;
box-shadow: none;
}
:global([data-marketplace-standalone]) .recommendCards > .card:nth-child(-n + 3) {
display: flex;
}
:global([data-marketplace-standalone]) .recommendCards > .card:nth-child(n + 4) {
display: none;
}
:global([data-marketplace-standalone]) .cardIcon {
width: 96px;
height: 96px;
border-color: var(--color-effects-icon-border);
border-radius: 20px;
box-shadow:
0 0.5px 5px 0 var(--color-shadow-shadow-4),
0 0.5px 2px -0.5px var(--color-shadow-shadow-4);
backdrop-filter: blur(5px);
}
:global([data-marketplace-standalone]) .cardMeta {
display: none;
}
:global([data-marketplace-standalone]) .copyDescription {
font-size: 15px;
letter-spacing: -0.075px;
}
:global([data-marketplace-standalone]) .updatesArt {
@ -167,6 +219,11 @@
border-radius: 16px;
}
:global([data-marketplace-standalone]) .recommendVisual {
border-radius: 12px;
background-color: var(--color-text-accent);
}
:global([data-marketplace-standalone]) .stackedCopy {
flex: none;
height: 160px;
@ -178,10 +235,18 @@
gap: 4px;
}
/* Event/ad mobile art is a 393×357 frame (Figma 1026:28881), matching
the stacked blog/recommend card height of 197 + 160. */
/* Event/ad mobile: show the full poster instead of cropping it into a
357px cover box. Height follows the selected image's aspect ratio. */
:global([data-marketplace-standalone]) .imageSlide {
height: 357px;
height: auto;
}
:global([data-marketplace-standalone]) .imageSlide :is(picture, img) {
width: 100%;
height: auto;
aspect-ratio: auto;
object-fit: contain;
object-position: center;
}
:global([data-marketplace-standalone]) .readMoreDesktop {

View File

@ -0,0 +1,5 @@
@media (max-width: 879px) {
:global([data-marketplace-standalone]) .pagination {
display: none;
}
}

View File

@ -8,6 +8,7 @@ import useEmblaCarousel from 'embla-carousel-react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from '#i18n'
import { MARKETPLACE_CONTAINER_ID } from '../constants'
import styles from './carousel.module.css'
import { CAROUSEL_PAGE_CLASS } from './collection-constants'
export type CarouselPage = {
@ -76,7 +77,7 @@ const CarouselControls = ({
return (
<div className="absolute -top-10 right-0 flex items-center gap-3">
{showPagination && (
<div className="flex items-center gap-1">
<div className={cn(styles.pagination, 'flex items-center gap-1')}>
{paginationItems.map((item, index) => (
<button
key={item.id}