mirror of
https://github.com/langgenius/dify.git
synced 2026-03-19 13:47:37 +08:00
fix(skill): use SearchInput with debounce and align card to Figma
Replace custom search input with SearchInput component (built-in clear button) and add 300ms debounce. Fix template card: use Tailwind token for icon background, fix Badge to use children with badge-s class and uppercase, match empty-tag fallback height to badge size.
This commit is contained in:
@ -17,7 +17,7 @@ import { buildUploadDataFromTemplate } from './templates/template-to-upload'
|
||||
const SkillTemplatesSection = () => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const [activeCategory, setActiveCategory] = useState('all')
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [loadingId, setLoadingId] = useState<string | null>(null)
|
||||
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
@ -65,14 +65,14 @@ const SkillTemplatesSection = () => {
|
||||
}, [appId, storeApi])
|
||||
|
||||
const filtered = useMemo(() => SKILL_TEMPLATES.filter((entry) => {
|
||||
if (searchValue) {
|
||||
const q = searchValue.toLowerCase()
|
||||
if (searchQuery) {
|
||||
const q = searchQuery.toLowerCase()
|
||||
return entry.name.toLowerCase().includes(q) || entry.description.toLowerCase().includes(q)
|
||||
}
|
||||
if (activeCategory !== 'all')
|
||||
return entry.tags?.some(tag => tag.toLowerCase() === activeCategory.toLowerCase())
|
||||
return true
|
||||
}), [searchValue, activeCategory])
|
||||
}), [searchQuery, activeCategory])
|
||||
|
||||
return (
|
||||
<section className="flex flex-col gap-3 px-6 py-2">
|
||||
@ -86,8 +86,7 @@ const SkillTemplatesSection = () => {
|
||||
onCategoryChange={setActiveCategory}
|
||||
/>
|
||||
<TemplateSearch
|
||||
value={searchValue}
|
||||
onChange={setSearchValue}
|
||||
onChange={setSearchQuery}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
|
||||
@ -22,7 +22,7 @@ const TemplateCard = ({ template, onUse }: TemplateCardProps) => {
|
||||
<AppIcon
|
||||
size="large"
|
||||
icon={template.icon || '📁'}
|
||||
background="#f5f3ff"
|
||||
className="!bg-components-icon-bg-violet-soft"
|
||||
/>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-0.5 py-px">
|
||||
<span className="system-md-semibold truncate text-text-secondary">
|
||||
@ -43,11 +43,11 @@ const TemplateCard = ({ template, onUse }: TemplateCardProps) => {
|
||||
? (
|
||||
<div className="flex flex-wrap gap-1 transition-opacity group-hover:opacity-0">
|
||||
{template.tags.map(tag => (
|
||||
<Badge key={tag} text={tag} />
|
||||
<Badge key={tag} className="badge-s" uppercase>{tag}</Badge>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
: <div className="h-5" />}
|
||||
: <div className="h-[18px]" />}
|
||||
<div className="pointer-events-none absolute inset-0 flex items-end px-4 pb-4 opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100">
|
||||
<Button
|
||||
variant="primary"
|
||||
|
||||
@ -1,33 +1,33 @@
|
||||
'use client'
|
||||
|
||||
import { RiSearchLine } from '@remixicon/react'
|
||||
import { memo } from 'react'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import { memo, useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import SearchInput from '@/app/components/base/search-input'
|
||||
|
||||
type TemplateSearchProps = {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
const TemplateSearch = ({
|
||||
value,
|
||||
onChange,
|
||||
}: TemplateSearchProps) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const [localValue, setLocalValue] = useState('')
|
||||
const { run: debouncedOnChange } = useDebounceFn(onChange, { wait: 300 })
|
||||
|
||||
const handleChange = useCallback((v: string) => {
|
||||
setLocalValue(v)
|
||||
debouncedOnChange(v)
|
||||
}, [debouncedOnChange])
|
||||
|
||||
return (
|
||||
<div className="flex shrink-0 items-center gap-0.5 rounded-md bg-components-input-bg-normal p-2">
|
||||
<RiSearchLine className="size-4 shrink-0 text-text-placeholder" aria-hidden="true" />
|
||||
<input
|
||||
type="text"
|
||||
name="template-search"
|
||||
aria-label={t('skill.startTab.searchPlaceholder')}
|
||||
className="system-sm-regular min-w-0 flex-1 bg-transparent px-1 text-text-secondary placeholder:text-components-input-text-placeholder focus:outline-none"
|
||||
placeholder={t('skill.startTab.searchPlaceholder')}
|
||||
value={value}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<SearchInput
|
||||
className="!h-7"
|
||||
placeholder={t('skill.startTab.searchPlaceholder')}
|
||||
value={localValue}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user