🔧 refactor(UI): Optimize MentionModelsButton with performance improvements and styling updates

- Add useCallback for togglePin and handleModelSelect to prevent unnecessary re-renders
- Refactor dropdown menu styling with more specific CSS scoping
- Simplify dropdown open/close logic
- Improve performance by memoizing function dependencies
- Adjust dropdown overlay styling and animation
This commit is contained in:
kangfenmao 2025-03-07 23:01:34 +08:00
parent a8451b7c3d
commit d714a53dc6

View File

@ -8,7 +8,7 @@ import { getModelUniqId } from '@renderer/services/ModelService'
import { Model, Provider } from '@renderer/types' import { Model, Provider } from '@renderer/types'
import { Avatar, Dropdown, Tooltip } from 'antd' import { Avatar, Dropdown, Tooltip } from 'antd'
import { first, sortBy } from 'lodash' import { first, sortBy } from 'lodash'
import { FC, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import { FC, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled, { createGlobalStyle } from 'styled-components' import styled, { createGlobalStyle } from 'styled-components'
@ -37,23 +37,29 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
itemRefs.current[index] = el itemRefs.current[index] = el
} }
const togglePin = async (modelId: string) => { const togglePin = useCallback(
async (modelId: string) => {
const newPinnedModels = pinnedModels.includes(modelId) const newPinnedModels = pinnedModels.includes(modelId)
? pinnedModels.filter((id) => id !== modelId) ? pinnedModels.filter((id) => id !== modelId)
: [...pinnedModels, modelId] : [...pinnedModels, modelId]
await db.settings.put({ id: 'pinned:models', value: newPinnedModels }) await db.settings.put({ id: 'pinned:models', value: newPinnedModels })
setPinnedModels(newPinnedModels) setPinnedModels(newPinnedModels)
} },
[pinnedModels]
)
const handleModelSelect = (model: Model) => { const handleModelSelect = useCallback(
(model: Model) => {
// Check if model is already selected // Check if model is already selected
if (mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(model))) { if (mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(model))) {
return return
} }
onSelect(model, fromKeyboard) onSelect(model, fromKeyboard)
setIsOpen(false) setIsOpen(false)
} },
[fromKeyboard, mentionModels, onSelect]
)
const modelMenuItems = useMemo(() => { const modelMenuItems = useMemo(() => {
const items = providers const items = providers
@ -161,7 +167,7 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
// Remove empty groups // Remove empty groups
return items.filter((group) => group.children.length > 0) return items.filter((group) => group.children.length > 0)
}, [providers, pinnedModels, t, onSelect, mentionModels, searchText]) }, [providers, pinnedModels, t, searchText, togglePin, handleModelSelect])
// Get flattened list of all model items // Get flattened list of all model items
const flatModelItems = useMemo(() => { const flatModelItems = useMemo(() => {
@ -345,14 +351,13 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
<> <>
<DropdownMenuStyle /> <DropdownMenuStyle />
<Dropdown <Dropdown
overlayStyle={{ marginBottom: 20 }}
dropdownRender={() => menu} dropdownRender={() => menu}
trigger={['click']} trigger={['click']}
open={isOpen} open={isOpen}
onOpenChange={(open) => { onOpenChange={(open) => {
setIsOpen(open) setIsOpen(open)
if (open) { open && setFromKeyboard(false) // Set fromKeyboard to false when opened by button click
setFromKeyboard(false) // Set fromKeyboard to false when opened by button click
}
}} }}
overlayClassName="mention-models-dropdown"> overlayClassName="mention-models-dropdown">
<Tooltip placement="top" title={t('agents.edit.model.select.title')} arrow> <Tooltip placement="top" title={t('agents.edit.model.select.title')} arrow>
@ -366,8 +371,15 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
} }
const DropdownMenuStyle = createGlobalStyle` const DropdownMenuStyle = createGlobalStyle`
/* Apply background to all animation states */ /* 将样式限定在 mention-models-dropdown 类下 */
.ant-dropdown, .mention-models-dropdown {
&.ant-dropdown {
background: rgba(var(--color-base-rgb), 0.65) !important;
backdrop-filter: blur(35px) saturate(150%) !important;
animation-duration: 0.15s !important;
}
/* 移动其他样式到 mention-models-dropdown 类下 */
.ant-slide-up-enter .ant-dropdown-menu, .ant-slide-up-enter .ant-dropdown-menu,
.ant-slide-up-appear .ant-dropdown-menu, .ant-slide-up-appear .ant-dropdown-menu,
.ant-slide-up-leave .ant-dropdown-menu, .ant-slide-up-leave .ant-dropdown-menu,
@ -378,13 +390,8 @@ const DropdownMenuStyle = createGlobalStyle`
backdrop-filter: blur(35px) saturate(150%) !important; backdrop-filter: blur(35px) saturate(150%) !important;
} }
.mention-models-dropdown {
&.ant-dropdown {
animation-duration: 0.15s !important;
margin-top: 4px;
}
.ant-dropdown-menu { .ant-dropdown-menu {
/* 保持原有的下拉菜单样式,但限定在 mention-models-dropdown 类下 */
max-height: 400px; max-height: 400px;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
@ -401,6 +408,7 @@ const DropdownMenuStyle = createGlobalStyle`
transform-origin: top; transform-origin: top;
will-change: transform, opacity; will-change: transform, opacity;
transition: all 0.15s cubic-bezier(0.4, 0.0, 0.2, 1); transition: all 0.15s cubic-bezier(0.4, 0.0, 0.2, 1);
margin-bottom: 0;
&.no-scrollbar { &.no-scrollbar {
padding-right: 12px; padding-right: 12px;