feat: implement select mode menu autoscroll for long mode lists

This commit is contained in:
FischLu 2025-02-14 11:52:36 +01:00 committed by 亢奋猫
parent b340b40bcf
commit 23686d4926

View File

@ -8,7 +8,7 @@ import { getModelUniqId } from '@renderer/services/ModelService'
import { Model, Provider } from '@renderer/types'
import { Avatar, Dropdown, Tooltip } from 'antd'
import { first, sortBy } from 'lodash'
import { FC, useEffect, useMemo, useRef, useState } from 'react'
import { FC, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled, { createGlobalStyle } from 'styled-components'
@ -27,6 +27,11 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
const [isOpen, setIsOpen] = useState(false)
const menuRef = useRef<HTMLDivElement>(null)
const [searchText, setSearchText] = useState('')
const itemRefs = useRef<Array<HTMLDivElement | null>>([])
const setItemRef = (index: number, el: HTMLDivElement | null) => {
itemRefs.current[index] = el
}
const togglePin = async (modelId: string) => {
const newPinnedModels = pinnedModels.includes(modelId)
@ -167,9 +172,17 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
loadPinnedModels()
}, [])
// Scroll to the first menu item when the mode selection menu opens
useLayoutEffect(() => {
if (isOpen && flatModelItems.length > 0 && itemRefs.current[0]) {
itemRefs.current[0].scrollIntoView({ block: 'nearest' })
}
}, [isOpen, flatModelItems])
useEffect(() => {
const showModelSelector = () => {
dropdownRef.current?.click()
itemRefs.current = []
setIsOpen(true)
setSelectedIndex(0)
setSearchText('')
@ -180,10 +193,18 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
if (e.key === 'ArrowDown') {
e.preventDefault()
setSelectedIndex((prev) => (prev < flatModelItems.length - 1 ? prev + 1 : 0))
setSelectedIndex((prev) => {
const newIndex = prev < flatModelItems.length - 1 ? prev + 1 : 0
itemRefs.current[newIndex]?.scrollIntoView({ block: 'nearest' })
return newIndex
})
} else if (e.key === 'ArrowUp') {
e.preventDefault()
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : flatModelItems.length - 1))
setSelectedIndex((prev) => {
const newIndex = prev > 0 ? prev - 1 : flatModelItems.length - 1
itemRefs.current[newIndex]?.scrollIntoView({ block: 'nearest' })
return newIndex
})
} else if (e.key === 'Enter') {
e.preventDefault()
if (selectedIndex >= 0 && selectedIndex < flatModelItems.length) {
@ -250,15 +271,20 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
<div key={group.key} className="ant-dropdown-menu-item-group">
<div className="ant-dropdown-menu-item-group-title">{group.label}</div>
<div>
{group.children.map((item, idx) => (
<div
key={item.key}
className={`ant-dropdown-menu-item ${selectedIndex === startIndex + idx ? 'ant-dropdown-menu-item-selected' : ''}`}
onClick={item.onClick}>
<span className="ant-dropdown-menu-item-icon">{item.icon}</span>
{item.label}
</div>
))}
{group.children.map((item, idx) => {
// calculate item global idx
const index = startIndex + idx
return (
<div
key={item.key}
ref={(el) => setItemRef(index, el)}
className={`ant-dropdown-menu-item ${selectedIndex === index ? 'ant-dropdown-menu-item-selected' : ''}`}
onClick={item.onClick}>
<span className="ant-dropdown-menu-item-icon">{item.icon}</span>
{item.label}
</div>
)
})}
</div>
</div>
)