refactor: move marketplace footer outside scroll containers

This commit is contained in:
lyzno1 2025-10-24 10:07:02 +08:00
parent c3eec7ea8a
commit aee9a8366f
No known key found for this signature in database
3 changed files with 104 additions and 84 deletions

View File

@ -2,7 +2,6 @@
import { import {
useCallback, useCallback,
useEffect, useEffect,
useRef,
useState, useState,
} from 'react' } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -19,6 +18,8 @@ import Button from '@/app/components/base/button'
import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
import { BlockEnum as BlockEnumValue } from '../types' import { BlockEnum as BlockEnumValue } from '../types'
const marketplaceFooterClassName = 'system-sm-medium z-10 flex h-8 flex-none cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg'
type AllStartBlocksProps = { type AllStartBlocksProps = {
className?: string className?: string
searchText: string searchText: string
@ -35,7 +36,6 @@ const AllStartBlocks = ({
tags = [], tags = [],
}: AllStartBlocksProps) => { }: AllStartBlocksProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const wrapElemRef = useRef<HTMLDivElement>(null)
const [hasStartBlocksContent, setHasStartBlocksContent] = useState(false) const [hasStartBlocksContent, setHasStartBlocksContent] = useState(false)
const [hasPluginContent, setHasPluginContent] = useState(false) const [hasPluginContent, setHasPluginContent] = useState(false)
@ -62,11 +62,8 @@ const AllStartBlocks = ({
return ( return (
<div className={cn('min-w-[400px] max-w-[500px]', className)}> <div className={cn('min-w-[400px] max-w-[500px]', className)}>
<div <div className='flex max-h-[640px] flex-col'>
ref={wrapElemRef} <div className='flex-1 overflow-y-auto'>
className='flex max-h-[640px] flex-col overflow-y-auto'
>
<div className='flex-1'>
<div className={cn(shouldShowEmptyState && 'hidden')}> <div className={cn(shouldShowEmptyState && 'hidden')}>
<StartBlocks <StartBlocks
searchText={searchText} searchText={searchText}
@ -110,7 +107,7 @@ const AllStartBlocks = ({
{!shouldShowEmptyState && ( {!shouldShowEmptyState && (
// Footer - Same as Tools tab marketplace footer // Footer - Same as Tools tab marketplace footer
<Link <Link
className='system-sm-medium sticky bottom-0 z-10 flex h-8 cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg' className={marketplaceFooterClassName}
href={getMarketplaceUrl('')} href={getMarketplaceUrl('')}
target='_blank' target='_blank'
> >

View File

@ -32,6 +32,10 @@ import RAGToolSuggestions from './rag-tool-suggestions'
import FeaturedTools from './featured-tools' import FeaturedTools from './featured-tools'
import Link from 'next/link' import Link from 'next/link'
import Divider from '@/app/components/base/divider' import Divider from '@/app/components/base/divider'
import { RiArrowRightUpLine } from '@remixicon/react'
import { getMarketplaceUrl } from '@/utils/var'
const marketplaceFooterClassName = 'system-sm-medium z-10 flex h-8 flex-none cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg'
type AllToolsProps = { type AllToolsProps = {
className?: string className?: string
@ -173,6 +177,7 @@ const AllTools = ({
&& activeTab === ToolTypeEnum.All && activeTab === ToolTypeEnum.All
&& !hasFilter && !hasFilter
&& (featuredLoading || featuredPlugins.length > 0) && (featuredLoading || featuredPlugins.length > 0)
const shouldShowMarketplaceFooter = enable_marketplace && !hasFilter
return ( return (
<div className={cn('min-w-[400px] max-w-[500px]', className)}> <div className={cn('min-w-[400px] max-w-[500px]', className)}>
@ -198,90 +203,103 @@ const AllTools = ({
<ViewTypeSelect viewType={activeView} onChange={setActiveView} /> <ViewTypeSelect viewType={activeView} onChange={setActiveView} />
)} )}
</div> </div>
<div <div className='flex max-h-[464px] flex-col'>
ref={wrapElemRef} <div
className='flex max-h-[464px] flex-col overflow-y-auto' ref={wrapElemRef}
onScroll={pluginRef.current?.handleScroll} className='flex-1 overflow-y-auto'
> onScroll={pluginRef.current?.handleScroll}
<div className={cn('flex-1', shouldShowEmptyState && 'hidden')}> >
{isShowRAGRecommendations && onTagsChange && ( <div className={cn(shouldShowEmptyState && 'hidden')}>
<RAGToolSuggestions {isShowRAGRecommendations && onTagsChange && (
viewType={isSupportGroupView ? activeView : ViewType.flat} <RAGToolSuggestions
onSelect={onSelect} viewType={isSupportGroupView ? activeView : ViewType.flat}
onTagsChange={onTagsChange}
/>
)}
{shouldShowFeatured && (
<>
<FeaturedTools
plugins={featuredPlugins}
providerMap={providerMap}
onSelect={onSelect} onSelect={onSelect}
selectedTools={selectedTools} onTagsChange={onTagsChange}
canChooseMCPTool={canChooseMCPTool}
isLoading={featuredLoading}
onInstallSuccess={async () => {
await onFeaturedInstallSuccess?.()
}}
/> />
<div className='px-3'> )}
<Divider className='!h-px' /> {shouldShowFeatured && (
</div> <>
</> <FeaturedTools
)} plugins={featuredPlugins}
{(hasToolsListContent || enable_marketplace) && ( providerMap={providerMap}
<>
<div className='px-3 pb-1 pt-2'>
<span className='system-xs-medium text-text-primary'>{t('tools.allTools')}</span>
</div>
{hasToolsListContent && (
<Tools
className={toolContentClassName}
tools={tools}
onSelect={onSelect} onSelect={onSelect}
canNotSelectMultiple={canNotSelectMultiple}
onSelectMultiple={onSelectMultiple}
toolType={activeTab}
viewType={isSupportGroupView ? activeView : ViewType.flat}
hasSearchText={hasSearchText}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool} canChooseMCPTool={canChooseMCPTool}
isShowRAGRecommendations={isShowRAGRecommendations} isLoading={featuredLoading}
onInstallSuccess={async () => {
await onFeaturedInstallSuccess?.()
}}
/> />
)} <div className='px-3'>
{enable_marketplace && ( <Divider className='!h-px' />
<PluginList </div>
ref={pluginRef} </>
wrapElemRef={wrapElemRef as RefObject<HTMLElement>} )}
list={notInstalledPlugins} {(hasToolsListContent || enable_marketplace) && (
searchText={searchText} <>
toolContentClassName={toolContentClassName} <div className='px-3 pb-1 pt-2'>
tags={tags} <span className='system-xs-medium text-text-primary'>{t('tools.allTools')}</span>
/> </div>
)} {hasToolsListContent && (
</> <Tools
className={toolContentClassName}
tools={tools}
onSelect={onSelect}
canNotSelectMultiple={canNotSelectMultiple}
onSelectMultiple={onSelectMultiple}
toolType={activeTab}
viewType={isSupportGroupView ? activeView : ViewType.flat}
hasSearchText={hasSearchText}
selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
isShowRAGRecommendations={isShowRAGRecommendations}
/>
)}
{enable_marketplace && (
<PluginList
ref={pluginRef}
wrapElemRef={wrapElemRef as RefObject<HTMLElement>}
list={notInstalledPlugins}
searchText={searchText}
toolContentClassName={toolContentClassName}
tags={tags}
hideFindMoreFooter
/>
)}
</>
)}
</div>
{shouldShowEmptyState && (
<div className='flex h-full flex-col items-center justify-center gap-3 py-12 text-center'>
<SearchMenu className='h-8 w-8 text-text-quaternary' />
<div className='text-sm font-medium text-text-secondary'>
{t('workflow.tabs.noPluginsFound')}
</div>
<Link
href='https://github.com/langgenius/dify-plugins/issues/new?template=plugin_request.yaml'
target='_blank'
>
<Button
size='small'
variant='secondary-accent'
className='h-6 cursor-pointer px-3 text-xs'
>
{t('workflow.tabs.requestToCommunity')}
</Button>
</Link>
</div>
)} )}
</div> </div>
{shouldShowMarketplaceFooter && (
{shouldShowEmptyState && ( <Link
<div className='flex h-full flex-col items-center justify-center gap-3 py-12 text-center'> className={marketplaceFooterClassName}
<SearchMenu className='h-8 w-8 text-text-quaternary' /> href={getMarketplaceUrl('')}
<div className='text-sm font-medium text-text-secondary'> target='_blank'
{t('workflow.tabs.noPluginsFound')} >
</div> <span>{t('plugin.findMoreInMarketplace')}</span>
<Link <RiArrowRightUpLine className='ml-0.5 h-3 w-3' />
href='https://github.com/langgenius/dify-plugins/issues/new?template=plugin_request.yaml' </Link>
target='_blank'
>
<Button
size='small'
variant='secondary-accent'
className='h-6 cursor-pointer px-3 text-xs'
>
{t('workflow.tabs.requestToCommunity')}
</Button>
</Link>
</div>
)} )}
</div> </div>
</div> </div>

View File

@ -18,6 +18,7 @@ export type ListProps = {
tags: string[] tags: string[]
toolContentClassName?: string toolContentClassName?: string
disableMaxWidth?: boolean disableMaxWidth?: boolean
hideFindMoreFooter?: boolean
ref?: React.Ref<ListRef> ref?: React.Ref<ListRef>
} }
@ -30,6 +31,7 @@ const List = ({
list, list,
toolContentClassName, toolContentClassName,
disableMaxWidth = false, disableMaxWidth = false,
hideFindMoreFooter = false,
ref, ref,
}: ListProps) => { }: ListProps) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -70,6 +72,9 @@ const List = ({
} }
if (noFilter) { if (noFilter) {
if (hideFindMoreFooter)
return null
return ( return (
<Link <Link
className='system-sm-medium sticky bottom-0 z-10 flex h-8 cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg' className='system-sm-medium sticky bottom-0 z-10 flex h-8 cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg'