feat: select box setting

pull/22133/head
Joel 10 months ago
parent 38d1c85c57
commit 52b845a5bb

@ -1,8 +1,9 @@
'use client' 'use client'
import { RiCloseLine } from '@remixicon/react' import { RiCloseLine, RiSearchLine } from '@remixicon/react'
import TagsFilter from './tags-filter' import TagsFilter from './tags-filter'
import ActionButton from '@/app/components/base/action-button' import ActionButton from '@/app/components/base/action-button'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { RiAddLine } from '@remixicon/react'
type SearchBoxProps = { type SearchBoxProps = {
search: string search: string
@ -13,6 +14,9 @@ type SearchBoxProps = {
size?: 'small' | 'large' size?: 'small' | 'large'
placeholder?: string placeholder?: string
locale?: string locale?: string
supportAddCustomTool?: boolean
onShowAddCustomCollectionModal?: () => void
onAddedCustomTool?: () => void
} }
const SearchBox = ({ const SearchBox = ({
search, search,
@ -23,46 +27,62 @@ const SearchBox = ({
size = 'small', size = 'small',
placeholder = '', placeholder = '',
locale, locale,
supportAddCustomTool,
onShowAddCustomCollectionModal,
}: SearchBoxProps) => { }: SearchBoxProps) => {
return ( return (
<div <div
className={cn( className='z-[11] flex items-center'
'z-[11] flex items-center',
size === 'large' && 'rounded-xl border border-components-chat-input-border bg-components-panel-bg-blur p-1.5 shadow-md',
size === 'small' && 'rounded-lg bg-components-input-bg-normal p-0.5',
inputClassName,
)}
> >
<TagsFilter <div className={
tags={tags} cn('flex items-center',
onTagsChange={onTagsChange} size === 'large' && 'rounded-xl border border-components-chat-input-border bg-components-panel-bg-blur p-1.5 shadow-md',
size={size} size === 'small' && 'rounded-lg bg-components-input-bg-normal p-0.5',
locale={locale} inputClassName,
/> )
<div className='mx-1 h-3.5 w-[1px] bg-divider-regular'></div> }>
<div className='relative flex grow items-center p-1 pl-2'> <div className='relative flex grow items-center p-1 pl-2'>
<div className='mr-2 flex w-full items-center'> <div className='mr-2 flex w-full items-center'>
<input <RiSearchLine className='mr-1.5 size-4 text-text-placeholder' />
className={cn( <input
'body-md-medium block grow appearance-none bg-transparent text-text-secondary outline-none', className={cn(
)} 'body-md-medium block grow appearance-none bg-transparent text-text-secondary outline-none',
value={search} )}
onChange={(e) => { value={search}
onSearchChange(e.target.value) onChange={(e) => {
}} onSearchChange(e.target.value)
placeholder={placeholder} }}
/> placeholder={placeholder}
{ />
search && ( {
<div className='absolute right-2 top-1/2 -translate-y-1/2'> search && (
<ActionButton onClick={() => onSearchChange('')}> <div className='absolute right-2 top-1/2 -translate-y-1/2'>
<RiCloseLine className='h-4 w-4' /> <ActionButton onClick={() => onSearchChange('')}>
</ActionButton> <RiCloseLine className='h-4 w-4' />
</div> </ActionButton>
) </div>
} )
}
</div>
</div> </div>
<div className='mx-1 h-3.5 w-[1px] bg-divider-regular'></div>
<TagsFilter
tags={tags}
onTagsChange={onTagsChange}
size={size}
locale={locale}
/>
</div> </div>
{supportAddCustomTool && (
<div className='flex shrink-0 items-center'>
<ActionButton
className='ml-2 rounded-full bg-components-button-primary-bg text-components-button-primary-text hover:bg-components-button-primary-bg hover:text-components-button-primary-text'
onClick={onShowAddCustomCollectionModal}
>
<RiAddLine className='h-4 w-4' />
</ActionButton>
</div>
)}
</div> </div>
) )
} }

@ -2,9 +2,7 @@
import { useState } from 'react' import { useState } from 'react'
import { import {
RiArrowDownSLine, RiPriceTag3Line,
RiCloseCircleFill,
RiFilter3Line,
} from '@remixicon/react' } from '@remixicon/react'
import { import {
PortalToFollowElem, PortalToFollowElem,
@ -57,47 +55,15 @@ const TagsFilter = ({
onClick={() => setOpen(v => !v)} onClick={() => setOpen(v => !v)}
> >
<div className={cn( <div className={cn(
'flex cursor-pointer items-center rounded-lg text-text-tertiary hover:bg-state-base-hover', 'ml-0.5 mr-1.5 flex items-center text-text-tertiary ',
size === 'large' && 'h-8 px-2 py-1', size === 'large' && 'h-8 py-1',
size === 'small' && 'h-7 py-0.5 pl-1 pr-1.5 ', size === 'small' && 'h-7 py-0.5 ',
selectedTagsLength && 'text-text-secondary', // selectedTagsLength && 'text-text-secondary',
open && 'bg-state-base-hover', // open && 'bg-state-base-hover',
)}> )}>
<div className='p-0.5'> <div className='cursor-pointer rounded-md p-0.5 hover:bg-state-base-hover'>
<RiFilter3Line className='h-4 w-4' /> <RiPriceTag3Line className='h-4 w-4 text-text-tertiary' />
</div> </div>
<div className={cn(
'system-sm-medium flex items-center p-1',
size === 'large' && 'p-1',
size === 'small' && 'px-0.5 py-1',
)}>
{
!selectedTagsLength && t('pluginTags.allTags')
}
{
!!selectedTagsLength && tags.map(tag => tagsMap[tag].label).slice(0, 2).join(',')
}
{
selectedTagsLength > 2 && (
<div className='system-xs-medium ml-1 text-text-tertiary'>
+{selectedTagsLength - 2}
</div>
)
}
</div>
{
!!selectedTagsLength && (
<RiCloseCircleFill
className='h-4 w-4 cursor-pointer text-text-quaternary'
onClick={() => onTagsChange([])}
/>
)
}
{
!selectedTagsLength && (
<RiArrowDownSLine className='h-4 w-4' />
)
}
</div> </div>
</PortalToFollowElemTrigger> </PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[1000]'> <PortalToFollowElemContent className='z-[1000]'>

@ -8,9 +8,12 @@ import {
} from '@/app/components/base/portal-to-follow-elem' } from '@/app/components/base/portal-to-follow-elem'
import { useFetchPluginListOrBundleList } from '@/service/use-plugins' import { useFetchPluginListOrBundleList } from '@/service/use-plugins'
import { PLUGIN_TYPE_SEARCH_MAP } from '../../marketplace/plugin-type-switch' import { PLUGIN_TYPE_SEARCH_MAP } from '../../marketplace/plugin-type-switch'
import SearchBox from '@/app/components/plugins/marketplace/search-box'
import { useTranslation } from 'react-i18next'
import cn from '@/utils/classnames'
type Props = { type Props = {
trigger: React.ReactNode trigger: React.ReactNode
value: string[] value: string[]
onChange: (value: string[]) => void onChange: (value: string[]) => void
isShow: boolean isShow: boolean
@ -18,8 +21,6 @@ type Props = {
} }
const allPluginTypes = [PLUGIN_TYPE_SEARCH_MAP.all, PLUGIN_TYPE_SEARCH_MAP.model, PLUGIN_TYPE_SEARCH_MAP.tool, PLUGIN_TYPE_SEARCH_MAP.agent, PLUGIN_TYPE_SEARCH_MAP.extension, PLUGIN_TYPE_SEARCH_MAP.bundle]
const ToolPicker: FC<Props> = ({ const ToolPicker: FC<Props> = ({
trigger, trigger,
value, value,
@ -27,24 +28,55 @@ const ToolPicker: FC<Props> = ({
isShow, isShow,
onShowChange, onShowChange,
}) => { }) => {
const { t } = useTranslation()
const toggleShowPopup = useCallback(() => { const toggleShowPopup = useCallback(() => {
onShowChange(!isShow) onShowChange(!isShow)
}, [onShowChange, isShow]) }, [onShowChange, isShow])
const tabs = [
{
key: PLUGIN_TYPE_SEARCH_MAP.all,
name: t('plugin.category.all'),
},
{
key: PLUGIN_TYPE_SEARCH_MAP.model,
name: t('plugin.category.models'),
},
{
key: PLUGIN_TYPE_SEARCH_MAP.tool,
name: t('plugin.category.tools'),
},
{
key: PLUGIN_TYPE_SEARCH_MAP.agent,
name: t('plugin.category.agents'),
},
{
key: PLUGIN_TYPE_SEARCH_MAP.extension,
name: t('plugin.category.extensions'),
},
{
key: PLUGIN_TYPE_SEARCH_MAP.bundle,
name: t('plugin.category.bundles'),
},
]
const [pluginType, setPluginType] = useState(PLUGIN_TYPE_SEARCH_MAP.all) const [pluginType, setPluginType] = useState(PLUGIN_TYPE_SEARCH_MAP.all)
const [query, setQuery] = useState('') const [query, setQuery] = useState('')
const [tags, setTags] = useState<string[]>([])
const { data } = useFetchPluginListOrBundleList({ const { data } = useFetchPluginListOrBundleList({
query, query,
tags,
category: pluginType, category: pluginType,
}) })
const isBundle = pluginType === PLUGIN_TYPE_SEARCH_MAP.bundle const isBundle = pluginType === PLUGIN_TYPE_SEARCH_MAP.bundle
const list = (isBundle ? data?.data?.bundles : data?.data?.plugins) || [] const list = (isBundle ? data?.data?.bundles : data?.data?.plugins) || []
console.log(list) console.log(list)
return ( return (
<PortalToFollowElem <PortalToFollowElem
placement='top-start' placement='top-start'
offset={0} offset={0}
open={isShow} open={true}
onOpenChange={onShowChange} onOpenChange={onShowChange}
> >
<PortalToFollowElemTrigger <PortalToFollowElemTrigger
@ -53,7 +85,38 @@ const ToolPicker: FC<Props> = ({
{trigger} {trigger}
</PortalToFollowElemTrigger> </PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[1000]'> <PortalToFollowElemContent className='z-[1000]'>
<div>aafdf</div> <div className={cn('relative min-h-20 w-[356px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pb-2 shadow-lg backdrop-blur-sm')}>
<div className='p-2 pb-1'>
<SearchBox
search={query}
onSearchChange={setQuery}
tags={tags}
onTagsChange={setTags}
size='small'
placeholder={t('plugin.searchTools')!}
inputClassName='w-full'
/>
</div>
<div className='flex items-center justify-between border-b-[0.5px] border-divider-subtle bg-background-default-hover px-3 shadow-xs'>
<div className='flex h-8 items-center space-x-1'>
{
tabs.map(tab => (
<div
className={cn(
'flex h-6 cursor-pointer items-center rounded-md px-2 hover:bg-state-base-hover',
'text-xs font-medium text-text-secondary',
pluginType === tab.key && 'bg-state-base-hover-alt',
)}
key={tab.key}
onClick={() => setPluginType(tab.key)}
>
{tab.name}
</div>
))
}
</div>
</div>
</div>
</PortalToFollowElemContent> </PortalToFollowElemContent>
</PortalToFollowElem> </PortalToFollowElem>
) )

Loading…
Cancel
Save