🔧 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(
const newPinnedModels = pinnedModels.includes(modelId) async (modelId: string) => {
? pinnedModels.filter((id) => id !== modelId) const newPinnedModels = pinnedModels.includes(modelId)
: [...pinnedModels, modelId] ? pinnedModels.filter((id) => id !== 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(
// Check if model is already selected (model: Model) => {
if (mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(model))) { // Check if model is already selected
return if (mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(model))) {
} return
onSelect(model, fromKeyboard) }
setIsOpen(false) onSelect(model, fromKeyboard)
} 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,25 +371,27 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
} }
const DropdownMenuStyle = createGlobalStyle` const DropdownMenuStyle = createGlobalStyle`
/* Apply background to all animation states */ /* 将样式限定在 mention-models-dropdown 类下 */
.ant-dropdown,
.ant-slide-up-enter .ant-dropdown-menu,
.ant-slide-up-appear .ant-dropdown-menu,
.ant-slide-up-leave .ant-dropdown-menu,
.ant-slide-up-enter-active .ant-dropdown-menu,
.ant-slide-up-appear-active .ant-dropdown-menu,
.ant-slide-up-leave-active .ant-dropdown-menu {
background: rgba(var(--color-base-rgb), 0.65) !important;
backdrop-filter: blur(35px) saturate(150%) !important;
}
.mention-models-dropdown { .mention-models-dropdown {
&.ant-dropdown { &.ant-dropdown {
background: rgba(var(--color-base-rgb), 0.65) !important;
backdrop-filter: blur(35px) saturate(150%) !important;
animation-duration: 0.15s !important; animation-duration: 0.15s !important;
margin-top: 4px; }
/* 移动其他样式到 mention-models-dropdown 类下 */
.ant-slide-up-enter .ant-dropdown-menu,
.ant-slide-up-appear .ant-dropdown-menu,
.ant-slide-up-leave .ant-dropdown-menu,
.ant-slide-up-enter-active .ant-dropdown-menu,
.ant-slide-up-appear-active .ant-dropdown-menu,
.ant-slide-up-leave-active .ant-dropdown-menu {
background: rgba(var(--color-base-rgb), 0.65) !important;
backdrop-filter: blur(35px) saturate(150%) !important;
} }
.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;
@ -445,7 +453,7 @@ const DropdownMenuStyle = createGlobalStyle`
.ant-dropdown-menu-item-group { .ant-dropdown-menu-item-group {
margin-bottom: 4px; margin-bottom: 4px;
&:not(:first-child) { &:not(:first-child) {
margin-top: 4px; margin-top: 4px;
} }
@ -469,7 +477,7 @@ const DropdownMenuStyle = createGlobalStyle`
font-size: 13px; font-size: 13px;
opacity: 0.8; opacity: 0.8;
margin-bottom: 40px; margin-bottom: 40px;
&:hover { &:hover {
background: none; background: none;
} }