mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-26 03:31:24 +08:00
refactor(Select): provide consistent search experience for antd Select (#7363)
- Add CustomSelect for enhanced searching - Replace some Select components with CustomSelect - Fix translation language searching problem
This commit is contained in:
parent
5ca0ce682b
commit
c7a0b05841
17
src/renderer/src/components/CustomSelect.tsx
Normal file
17
src/renderer/src/components/CustomSelect.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { includeKeywords } from '@renderer/utils/search'
|
||||
import { Select, SelectProps } from 'antd'
|
||||
|
||||
/**
|
||||
* 自定义 Select,使用增强的搜索 filter
|
||||
*/
|
||||
const CustomSelect = ({ ref, ...props }: SelectProps & { ref?: React.RefObject<any | null> }) => {
|
||||
return <Select ref={ref} filterOption={enhancedFilterOption} {...props} />
|
||||
}
|
||||
|
||||
CustomSelect.displayName = 'CustomSelect'
|
||||
|
||||
function enhancedFilterOption(input: string, option: any) {
|
||||
return includeKeywords(option.label, input)
|
||||
}
|
||||
|
||||
export default CustomSelect
|
||||
@ -1,5 +1,6 @@
|
||||
import { RedoOutlined } from '@ant-design/icons'
|
||||
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
|
||||
import CustomSelect from '@renderer/components/CustomSelect'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import PromptPopup from '@renderer/components/Popups/PromptPopup'
|
||||
import { isEmbeddingModel } from '@renderer/config/models'
|
||||
@ -96,7 +97,7 @@ const ModelSettings: FC = () => {
|
||||
</HStack>
|
||||
</SettingTitle>
|
||||
<HStack alignItems="center">
|
||||
<Select
|
||||
<CustomSelect
|
||||
value={defaultModelValue}
|
||||
defaultValue={defaultModelValue}
|
||||
style={{ width: 360 }}
|
||||
@ -118,7 +119,7 @@ const ModelSettings: FC = () => {
|
||||
</HStack>
|
||||
</SettingTitle>
|
||||
<HStack alignItems="center">
|
||||
<Select
|
||||
<CustomSelect
|
||||
value={defaultTopicNamingModel}
|
||||
defaultValue={defaultTopicNamingModel}
|
||||
style={{ width: 360 }}
|
||||
@ -140,7 +141,7 @@ const ModelSettings: FC = () => {
|
||||
</HStack>
|
||||
</SettingTitle>
|
||||
<HStack alignItems="center">
|
||||
<Select
|
||||
<CustomSelect
|
||||
value={defaultTranslateModel}
|
||||
defaultValue={defaultTranslateModel}
|
||||
style={{ width: 360 }}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { CheckOutlined, DeleteOutlined, HistoryOutlined, SendOutlined } from '@ant-design/icons'
|
||||
import { NavbarCenter, NavbarMain } from '@renderer/components/app/Navbar'
|
||||
import CustomSelect from '@renderer/components/CustomSelect'
|
||||
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import { isEmbeddingModel } from '@renderer/config/models'
|
||||
import { translateLanguageOptions } from '@renderer/config/translate'
|
||||
import { TranslateLanguageOptions } from '@renderer/config/translate'
|
||||
import db from '@renderer/databases'
|
||||
import { useDefaultModel } from '@renderer/hooks/useAssistant'
|
||||
import { useProviders } from '@renderer/hooks/useProvider'
|
||||
@ -114,7 +115,7 @@ const TranslateSettings: FC<{
|
||||
<div>
|
||||
<div style={{ marginBottom: 8, fontWeight: 500 }}>{t('translate.settings.model')}</div>
|
||||
<HStack alignItems="center" gap={5}>
|
||||
<Select
|
||||
<CustomSelect
|
||||
style={{ width: '100%' }}
|
||||
placeholder={t('translate.settings.model_placeholder')}
|
||||
value={defaultTranslateModel}
|
||||
@ -174,38 +175,38 @@ const TranslateSettings: FC<{
|
||||
{isBidirectional && (
|
||||
<Flex align="center" justify="space-between" gap={10}>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ flex: 1 }}
|
||||
value={localPair[0]}
|
||||
optionFilterProp="label"
|
||||
onChange={(value) => setLocalPair([value, localPair[1]])}
|
||||
options={translateLanguageOptions().map((lang) => ({
|
||||
value: lang.value,
|
||||
label: (
|
||||
<Space.Compact direction="horizontal" block>
|
||||
<span role="img" aria-label={lang.emoji} style={{ marginRight: 8 }}>
|
||||
{lang.emoji}
|
||||
</span>
|
||||
<Space.Compact block>{lang.label}</Space.Compact>
|
||||
</Space.Compact>
|
||||
)
|
||||
}))}
|
||||
options={TranslateLanguageOptions}
|
||||
optionRender={(option) => (
|
||||
<Space>
|
||||
<span role="img" aria-label={(option.data as any).emoji}>
|
||||
{(option.data as any).emoji}
|
||||
</span>
|
||||
{option.label}
|
||||
</Space>
|
||||
)}
|
||||
suffixIcon={<ChevronDown strokeWidth={1.5} size={16} color="var(--color-text-3)" />}
|
||||
/>
|
||||
<span>⇆</span>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ flex: 1 }}
|
||||
value={localPair[1]}
|
||||
optionFilterProp="label"
|
||||
onChange={(value) => setLocalPair([localPair[0], value])}
|
||||
options={translateLanguageOptions().map((lang) => ({
|
||||
value: lang.value,
|
||||
label: (
|
||||
<Space.Compact direction="horizontal" block>
|
||||
<span role="img" aria-label={lang.emoji} style={{ marginRight: 8 }}>
|
||||
{lang.emoji}
|
||||
</span>
|
||||
<div style={{ textAlign: 'left', flex: 1 }}>{lang.label}</div>
|
||||
</Space.Compact>
|
||||
)
|
||||
}))}
|
||||
options={TranslateLanguageOptions}
|
||||
optionRender={(option) => (
|
||||
<Space>
|
||||
<span role="img" aria-label={(option.data as any).emoji}>
|
||||
{(option.data as any).emoji}
|
||||
</span>
|
||||
{option.label}
|
||||
</Space>
|
||||
)}
|
||||
suffixIcon={<ChevronDown strokeWidth={1.5} size={16} color="var(--color-text-3)" />}
|
||||
/>
|
||||
</Flex>
|
||||
@ -436,23 +437,23 @@ const TranslatePage: FC = () => {
|
||||
|
||||
return (
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: 160 }}
|
||||
value={targetLanguage}
|
||||
optionFilterProp="label"
|
||||
onChange={(value) => {
|
||||
setTargetLanguage(value)
|
||||
db.settings.put({ id: 'translate:target:language', value })
|
||||
}}
|
||||
options={translateLanguageOptions().map((lang) => ({
|
||||
value: lang.value,
|
||||
label: (
|
||||
<Space.Compact direction="horizontal" block>
|
||||
<span role="img" aria-label={lang.emoji} style={{ marginRight: 8 }}>
|
||||
{lang.emoji}
|
||||
</span>
|
||||
<Space.Compact block>{lang.label}</Space.Compact>
|
||||
</Space.Compact>
|
||||
)
|
||||
}))}
|
||||
options={TranslateLanguageOptions}
|
||||
optionRender={(option) => (
|
||||
<Space>
|
||||
<span role="img" aria-label={(option.data as any).emoji}>
|
||||
{(option.data as any).emoji}
|
||||
</span>
|
||||
{option.label}
|
||||
</Space>
|
||||
)}
|
||||
suffixIcon={<ChevronDown strokeWidth={1.5} size={16} color="var(--color-text-3)" />}
|
||||
/>
|
||||
)
|
||||
@ -538,18 +539,25 @@ const TranslatePage: FC = () => {
|
||||
? `${t('translate.detected.language')}(${t(`languages.${detectedLanguage.toLowerCase()}`)})`
|
||||
: t('translate.detected.language')
|
||||
},
|
||||
...translateLanguageOptions().map((lang) => ({
|
||||
...TranslateLanguageOptions.map((lang) => ({
|
||||
value: lang.value,
|
||||
label: (
|
||||
<Space.Compact direction="horizontal" block>
|
||||
<span role="img" aria-label={lang.emoji} style={{ marginRight: 8 }}>
|
||||
{lang.emoji}
|
||||
</span>
|
||||
<Space.Compact block>{lang.label}</Space.Compact>
|
||||
</Space.Compact>
|
||||
)
|
||||
label: lang.label,
|
||||
emoji: lang.emoji
|
||||
}))
|
||||
]}
|
||||
optionRender={(option) => {
|
||||
if (option.value === 'auto') {
|
||||
return option.label
|
||||
}
|
||||
return (
|
||||
<Space>
|
||||
<span role="img" aria-label={(option.data as any).emoji}>
|
||||
{(option.data as any).emoji}
|
||||
</span>
|
||||
{option.label}
|
||||
</Space>
|
||||
)
|
||||
}}
|
||||
suffixIcon={<ChevronDown strokeWidth={1.5} size={16} color="var(--color-text-3)" />}
|
||||
/>
|
||||
<Button
|
||||
|
||||
Loading…
Reference in New Issue
Block a user