feat: enhance carousel layout and search functionality in marketplace components

This commit is contained in:
yessenia
2026-02-11 04:31:55 +08:00
parent f348258a45
commit 4961242a8a
4 changed files with 24 additions and 11 deletions

View File

@ -4,6 +4,9 @@ export const GRID_DISPLAY_LIMIT = 8
export const CAROUSEL_COLUMN_CLASS = 'flex w-[calc((100%-0px)/1)] shrink-0 flex-col gap-3 sm:w-[calc((100%-12px)/2)] lg:w-[calc((100%-24px)/3)] xl:w-[calc((100%-36px)/4)]'
/** Max visible columns at the widest (xl) breakpoint; used to decide 1-row vs 2-row carousel layout. */
export const CAROUSEL_MAX_VISIBLE_COLUMNS = 4
/** Collection name key that triggers carousel display (plugins: partners, templates: featured) */
export const CAROUSEL_COLLECTION_NAMES = {
partners: 'partners',

View File

@ -12,7 +12,7 @@ import { useMarketplaceMoreClick } from '../atoms'
import Empty from '../empty'
import { getItemKeyByField } from '../utils'
import Carousel from './carousel'
import { CAROUSEL_COLUMN_CLASS, GRID_CLASS, GRID_DISPLAY_LIMIT } from './collection-constants'
import { CAROUSEL_COLUMN_CLASS, CAROUSEL_MAX_VISIBLE_COLUMNS, GRID_CLASS, GRID_DISPLAY_LIMIT } from './collection-constants'
type ViewMoreButtonProps = {
searchParams?: SearchParamsFromCollection
@ -80,9 +80,15 @@ export function CarouselCollection<TItem>({
renderCard,
cardContainerClassName,
}: CarouselCollectionProps<TItem>) {
const rows: TItem[][] = []
for (let i = 0; i < items.length; i += 2)
rows.push(items.slice(i, i + 2))
const useDoubleRow = items.length > CAROUSEL_MAX_VISIBLE_COLUMNS
const numColumns = useDoubleRow ? Math.ceil(items.length / 2) : items.length
const columns: TItem[][] = []
for (let i = 0; i < numColumns; i++) {
const column: TItem[] = [items[i]]
if (useDoubleRow && i + numColumns < items.length)
column.push(items[i + numColumns])
columns.push(column)
}
return (
<Carousel
@ -92,7 +98,7 @@ export function CarouselCollection<TItem>({
autoPlay={items.length > 8}
autoPlayInterval={5000}
>
{rows.map((columnItems, idx) => (
{columns.map((columnItems, idx) => (
<div
key={columnItems[0] ? getItemKey(columnItems[0]) : idx}
className={CAROUSEL_COLUMN_CLASS}

View File

@ -14,6 +14,7 @@ import {
import { cn } from '@/utils/classnames'
import {
searchModeAtom,
useSearchTab,
useSearchText,
} from '../atoms'
import { useMarketplaceUnifiedSearch } from '../query'
@ -30,6 +31,7 @@ const SearchBoxWrapper = ({
}: SearchBoxWrapperProps) => {
const { t } = useTranslation()
const [searchText, handleSearchTextChange] = useSearchText()
const [, setSearchTab] = useSearchTab()
const setSearchMode = useSetAtom(searchModeAtom)
const committedSearch = searchText || ''
const [draftSearch, setDraftSearch] = useState(committedSearch)
@ -67,6 +69,7 @@ const SearchBoxWrapper = ({
if (!trimmed)
return
handleSearchTextChange(trimmed)
setSearchTab('all')
setSearchMode(true)
setIsFocused(false)
}

View File

@ -20,6 +20,7 @@ import { getPluginFilterType, mapTemplateDetailToTemplate } from '../utils'
import CreatorCard from './creator-card'
const PAGE_SIZE = 40
const ALL_TAB_PREVIEW_SIZE = 8
const ZERO_WIDTH_SPACE = '\u200B'
type SortValue = { sortBy: string, sortOrder: string }
@ -50,7 +51,7 @@ const SearchPage = () => {
return undefined
return {
query,
page_size: searchTab === 'all' ? 6 : PAGE_SIZE,
page_size: searchTab === 'all' ? ALL_TAB_PREVIEW_SIZE : PAGE_SIZE,
sort_by: sort.sortBy,
sort_order: sort.sortOrder,
type: getPluginFilterType(PLUGIN_TYPE_SEARCH_MAP.all),
@ -63,7 +64,7 @@ const SearchPage = () => {
const { sort_by, sort_order } = mapSortForTemplates(sort)
return {
query,
page_size: searchTab === 'all' ? 6 : PAGE_SIZE,
page_size: searchTab === 'all' ? ALL_TAB_PREVIEW_SIZE : PAGE_SIZE,
sort_by,
sort_order,
}
@ -75,7 +76,7 @@ const SearchPage = () => {
const { sort_by, sort_order } = mapSortForCreators(sort)
return {
query,
page_size: searchTab === 'all' ? 6 : PAGE_SIZE,
page_size: searchTab === 'all' ? ALL_TAB_PREVIEW_SIZE : PAGE_SIZE,
sort_by,
sort_order,
}
@ -172,7 +173,7 @@ const SearchPage = () => {
<h3 className="title-xl-semi-bold mb-3 text-text-primary">
{t('templates', { ns: 'plugin' })}
</h3>
{renderTemplatesSection(templates, 6)}
{renderTemplatesSection(templates, ALL_TAB_PREVIEW_SIZE)}
</section>
)}
{plugins.length > 0 && (
@ -180,7 +181,7 @@ const SearchPage = () => {
<h3 className="title-xl-semi-bold mb-3 text-text-primary">
{t('plugins', { ns: 'plugin' })}
</h3>
{renderPluginsSection(plugins, 6)}
{renderPluginsSection(plugins, ALL_TAB_PREVIEW_SIZE)}
</section>
)}
{creators.length > 0 && (
@ -188,7 +189,7 @@ const SearchPage = () => {
<h3 className="title-xl-semi-bold mb-3 text-text-primary">
{t('marketplace.searchFilterCreators', { ns: 'plugin' })}
</h3>
{renderCreatorsSection(creators, 6)}
{renderCreatorsSection(creators, ALL_TAB_PREVIEW_SIZE)}
</section>
)}
{!isLoading && plugins.length === 0 && templates.length === 0 && creators.length === 0 && (