mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-08 06:19:05 +08:00
feat: goto provider settings from models popup (#9573)
* feat: goto provider settings from models popup * refactor: improve paddings * refactor: update types * refactor: update types * doc: update comments * refactor: more comments * refactor: scroll to the selected provider on navigation * test: update mocks
This commit is contained in:
parent
7a0da13676
commit
ddc5f46e9b
@ -32,7 +32,7 @@ vi.mock('@hello-pangea/dnd', () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@tanstack/react-virtual', () => ({
|
vi.mock('@tanstack/react-virtual', () => ({
|
||||||
useVirtualizer: ({ count }) => ({
|
useVirtualizer: ({ count, getScrollElement }) => ({
|
||||||
getVirtualItems: () =>
|
getVirtualItems: () =>
|
||||||
Array.from({ length: count }, (_, index) => ({
|
Array.from({ length: count }, (_, index) => ({
|
||||||
index,
|
index,
|
||||||
@ -41,7 +41,13 @@ vi.mock('@tanstack/react-virtual', () => ({
|
|||||||
size: 50
|
size: 50
|
||||||
})),
|
})),
|
||||||
getTotalSize: () => count * 50,
|
getTotalSize: () => count * 50,
|
||||||
measureElement: vi.fn()
|
measureElement: vi.fn(),
|
||||||
|
scrollToIndex: vi.fn(),
|
||||||
|
scrollToOffset: vi.fn(),
|
||||||
|
scrollElement: getScrollElement(),
|
||||||
|
measure: vi.fn(),
|
||||||
|
resizeItem: vi.fn(),
|
||||||
|
getVirtualIndexes: () => Array.from({ length: count }, (_, i) => i)
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
export { default as DraggableList } from './list'
|
export { default as DraggableList } from './list'
|
||||||
export { useDraggableReorder } from './useDraggableReorder'
|
export { useDraggableReorder } from './useDraggableReorder'
|
||||||
export { default as DraggableVirtualList } from './virtual-list'
|
export {
|
||||||
|
default as DraggableVirtualList,
|
||||||
|
type DraggableVirtualListProps,
|
||||||
|
type DraggableVirtualListRef
|
||||||
|
} from './virtual-list'
|
||||||
|
|||||||
@ -10,8 +10,19 @@ import {
|
|||||||
} from '@hello-pangea/dnd'
|
} from '@hello-pangea/dnd'
|
||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import Scrollbar from '@renderer/components/Scrollbar'
|
||||||
import { droppableReorder } from '@renderer/utils'
|
import { droppableReorder } from '@renderer/utils'
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual'
|
import { type ScrollToOptions, useVirtualizer, type VirtualItem } from '@tanstack/react-virtual'
|
||||||
import { type Key, memo, useCallback, useRef } from 'react'
|
import { type Key, memo, useCallback, useImperativeHandle, useRef } from 'react'
|
||||||
|
|
||||||
|
export interface DraggableVirtualListRef {
|
||||||
|
measure: () => void
|
||||||
|
scrollElement: () => HTMLDivElement | null
|
||||||
|
scrollToOffset: (offset: number, options?: ScrollToOptions) => void
|
||||||
|
scrollToIndex: (index: number, options?: ScrollToOptions) => void
|
||||||
|
resizeItem: (index: number, size: number) => void
|
||||||
|
getTotalSize: () => number
|
||||||
|
getVirtualItems: () => VirtualItem[]
|
||||||
|
getVirtualIndexes: () => number[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 泛型 Props,用于配置 DraggableVirtualList。
|
* 泛型 Props,用于配置 DraggableVirtualList。
|
||||||
@ -31,8 +42,8 @@ import { type Key, memo, useCallback, useRef } from 'react'
|
|||||||
* @property {React.ReactNode} [header] 列表头部内容
|
* @property {React.ReactNode} [header] 列表头部内容
|
||||||
* @property {(item: T, index: number) => React.ReactNode} children 列表项渲染函数
|
* @property {(item: T, index: number) => React.ReactNode} children 列表项渲染函数
|
||||||
*/
|
*/
|
||||||
interface DraggableVirtualListProps<T> {
|
export interface DraggableVirtualListProps<T> {
|
||||||
ref?: React.Ref<HTMLDivElement>
|
ref?: React.Ref<DraggableVirtualListRef>
|
||||||
className?: string
|
className?: string
|
||||||
style?: React.CSSProperties
|
style?: React.CSSProperties
|
||||||
scrollerStyle?: React.CSSProperties
|
scrollerStyle?: React.CSSProperties
|
||||||
@ -100,9 +111,23 @@ function DraggableVirtualList<T>({
|
|||||||
overscan
|
overscan
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
measure: () => virtualizer.measure(),
|
||||||
|
scrollElement: () => virtualizer.scrollElement,
|
||||||
|
scrollToOffset: (offset, options) => virtualizer.scrollToOffset(offset, options),
|
||||||
|
scrollToIndex: (index, options) => virtualizer.scrollToIndex(index, options),
|
||||||
|
resizeItem: (index, size) => virtualizer.resizeItem(index, size),
|
||||||
|
getTotalSize: () => virtualizer.getTotalSize(),
|
||||||
|
getVirtualItems: () => virtualizer.getVirtualItems(),
|
||||||
|
getVirtualIndexes: () => virtualizer.getVirtualIndexes()
|
||||||
|
}),
|
||||||
|
[virtualizer]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
|
||||||
className={`${className} draggable-virtual-list`}
|
className={`${className} draggable-virtual-list`}
|
||||||
style={{ height: '100%', display: 'flex', flexDirection: 'column', ...style }}>
|
style={{ height: '100%', display: 'flex', flexDirection: 'column', ...style }}>
|
||||||
<DragDropContext onDragStart={onDragStart} onDragEnd={_onDragEnd}>
|
<DragDropContext onDragStart={onDragStart} onDragEnd={_onDragEnd}>
|
||||||
|
|||||||
@ -8,8 +8,9 @@ import { useProviders } from '@renderer/hooks/useProvider'
|
|||||||
import { getModelUniqId } from '@renderer/services/ModelService'
|
import { getModelUniqId } from '@renderer/services/ModelService'
|
||||||
import { Model, Provider } from '@renderer/types'
|
import { Model, Provider } from '@renderer/types'
|
||||||
import { classNames, filterModelsByKeywords, getFancyProviderName } from '@renderer/utils'
|
import { classNames, filterModelsByKeywords, getFancyProviderName } from '@renderer/utils'
|
||||||
import { Avatar, Divider, Empty, Modal } from 'antd'
|
import { Avatar, Button, Divider, Empty, Modal, Tooltip } from 'antd'
|
||||||
import { first, sortBy } from 'lodash'
|
import { first, sortBy } from 'lodash'
|
||||||
|
import { SettingsIcon } from 'lucide-react'
|
||||||
import React, {
|
import React, {
|
||||||
startTransition,
|
startTransition,
|
||||||
useCallback,
|
useCallback,
|
||||||
@ -150,6 +151,22 @@ const PopupContainer: React.FC<Props> = ({ model, resolve, modelFilter }) => {
|
|||||||
key: `provider-${p.id}`,
|
key: `provider-${p.id}`,
|
||||||
type: 'group',
|
type: 'group',
|
||||||
name: getFancyProviderName(p),
|
name: getFancyProviderName(p),
|
||||||
|
actions: (
|
||||||
|
<Tooltip title={t('navigate.provider_settings')} mouseEnterDelay={0.5} mouseLeaveDelay={0}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
shape="circle"
|
||||||
|
icon={<SettingsIcon size={14} color="var(--color-text-3)" style={{ pointerEvents: 'none' }} />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setOpen(false)
|
||||||
|
resolve(undefined)
|
||||||
|
window.navigate(`/settings/provider?id=${p.id}`)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
),
|
||||||
isSelected: false
|
isSelected: false
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -159,7 +176,7 @@ const PopupContainer: React.FC<Props> = ({ model, resolve, modelFilter }) => {
|
|||||||
// 获取可选择的模型项(过滤掉分组标题)
|
// 获取可选择的模型项(过滤掉分组标题)
|
||||||
const modelItems = items.filter((item) => item.type === 'model') as FlatListItem[]
|
const modelItems = items.filter((item) => item.type === 'model') as FlatListItem[]
|
||||||
return { listItems: items, modelItems }
|
return { listItems: items, modelItems }
|
||||||
}, [searchText.length, pinnedModels, providers, modelFilter, createModelItem, t, getFilteredModels])
|
}, [pinnedModels, modelFilter, searchText.length, providers, createModelItem, t, getFilteredModels, resolve])
|
||||||
|
|
||||||
const listHeight = useMemo(() => {
|
const listHeight = useMemo(() => {
|
||||||
return Math.min(PAGE_SIZE, listItems.length) * ITEM_HEIGHT
|
return Math.min(PAGE_SIZE, listItems.length) * ITEM_HEIGHT
|
||||||
@ -307,7 +324,12 @@ const PopupContainer: React.FC<Props> = ({ model, resolve, modelFilter }) => {
|
|||||||
(item: FlatListItem) => {
|
(item: FlatListItem) => {
|
||||||
const isFocused = item.key === focusedItemKey
|
const isFocused = item.key === focusedItemKey
|
||||||
if (item.type === 'group') {
|
if (item.type === 'group') {
|
||||||
return <GroupItem>{item.name}</GroupItem>
|
return (
|
||||||
|
<GroupItem>
|
||||||
|
{item.name}
|
||||||
|
{item.actions}
|
||||||
|
</GroupItem>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ModelItem
|
<ModelItem
|
||||||
@ -397,11 +419,12 @@ const ListContainer = styled.div`
|
|||||||
const GroupItem = styled.div`
|
const GroupItem = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: normal;
|
||||||
height: ${ITEM_HEIGHT}px;
|
height: ${ITEM_HEIGHT}px;
|
||||||
padding: 5px 10px 5px 18px;
|
padding: 5px 12px 5px 18px;
|
||||||
color: var(--color-text-3);
|
color: var(--color-text-3);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background: var(--modal-background);
|
background: var(--modal-background);
|
||||||
|
|||||||
@ -1,20 +1,46 @@
|
|||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
// 列表项类型,组名也作为列表项
|
/**
|
||||||
export type ListItemType = 'group' | 'model'
|
* 滚动触发来源类型
|
||||||
|
*/
|
||||||
// 滚动触发来源类型
|
|
||||||
export type ScrollTrigger = 'initial' | 'search' | 'keyboard' | 'none'
|
export type ScrollTrigger = 'initial' | 'search' | 'keyboard' | 'none'
|
||||||
|
|
||||||
// 扁平化列表项接口
|
/**
|
||||||
export interface FlatListItem {
|
* 列表项分类,组名也作为列表项
|
||||||
|
*/
|
||||||
|
export type ListItemType = 'group' | 'model'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扁平化列表项基础类型
|
||||||
|
*/
|
||||||
|
export type FlatListBaseItem = {
|
||||||
key: string
|
key: string
|
||||||
type: ListItemType
|
type: ListItemType
|
||||||
icon?: ReactNode
|
|
||||||
name: ReactNode
|
name: ReactNode
|
||||||
tags?: ReactNode
|
icon?: ReactNode
|
||||||
model?: Model
|
|
||||||
isPinned?: boolean
|
|
||||||
isSelected?: boolean
|
isSelected?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型分组列表项
|
||||||
|
*/
|
||||||
|
export type FlatListGroup = FlatListBaseItem & {
|
||||||
|
type: 'group'
|
||||||
|
actions?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型列表项
|
||||||
|
*/
|
||||||
|
export type FlatListModel = FlatListBaseItem & {
|
||||||
|
type: 'model'
|
||||||
|
model: Model
|
||||||
|
tags?: ReactNode
|
||||||
|
isPinned?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扁平化列表项
|
||||||
|
*/
|
||||||
|
export type FlatListItem = FlatListGroup | FlatListModel
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "Hide Sidebar",
|
"hide_sidebar": "Hide Sidebar",
|
||||||
"show_sidebar": "Show Sidebar"
|
"show_sidebar": "Show Sidebar"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "Go to provider settings"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "Assistant Response",
|
"assistant": "Assistant Response",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "サイドバーを非表示",
|
"hide_sidebar": "サイドバーを非表示",
|
||||||
"show_sidebar": "サイドバーを表示"
|
"show_sidebar": "サイドバーを表示"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "プロバイダー設定に移動"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "助手回應",
|
"assistant": "助手回應",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "Скрыть боковую панель",
|
"hide_sidebar": "Скрыть боковую панель",
|
||||||
"show_sidebar": "Показать боковую панель"
|
"show_sidebar": "Показать боковую панель"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "Перейти к настройкам поставщика"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "Ответ ассистента",
|
"assistant": "Ответ ассистента",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "隐藏侧边栏",
|
"hide_sidebar": "隐藏侧边栏",
|
||||||
"show_sidebar": "显示侧边栏"
|
"show_sidebar": "显示侧边栏"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "跳转到服务商设置界面"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "助手响应",
|
"assistant": "助手响应",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "隱藏側邊欄",
|
"hide_sidebar": "隱藏側邊欄",
|
||||||
"show_sidebar": "顯示側邊欄"
|
"show_sidebar": "顯示側邊欄"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "跳轉到服務商設置界面"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "助手回應",
|
"assistant": "助手回應",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "Απόκρυψη πλάγιας μπάρας",
|
"hide_sidebar": "Απόκρυψη πλάγιας μπάρας",
|
||||||
"show_sidebar": "Εμφάνιση πλάγιας μπάρας"
|
"show_sidebar": "Εμφάνιση πλάγιας μπάρας"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "Μετάβαση στις ρυθμίσεις παρόχου"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "Απάντηση Βοηθού",
|
"assistant": "Απάντηση Βοηθού",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "Ocultar barra lateral",
|
"hide_sidebar": "Ocultar barra lateral",
|
||||||
"show_sidebar": "Mostrar barra lateral"
|
"show_sidebar": "Mostrar barra lateral"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "Ir a la configuración del proveedor"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "Respuesta del asistente",
|
"assistant": "Respuesta del asistente",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "Cacher la barre latérale",
|
"hide_sidebar": "Cacher la barre latérale",
|
||||||
"show_sidebar": "Afficher la barre latérale"
|
"show_sidebar": "Afficher la barre latérale"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "Aller aux paramètres du fournisseur"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "Réponse de l'assistant",
|
"assistant": "Réponse de l'assistant",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1567,6 +1567,9 @@
|
|||||||
"hide_sidebar": "Ocultar barra lateral",
|
"hide_sidebar": "Ocultar barra lateral",
|
||||||
"show_sidebar": "Mostrar barra lateral"
|
"show_sidebar": "Mostrar barra lateral"
|
||||||
},
|
},
|
||||||
|
"navigate": {
|
||||||
|
"provider_settings": "Ir para as configurações do provedor"
|
||||||
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"assistant": "Resposta do assistente",
|
"assistant": "Resposta do assistente",
|
||||||
"knowledge": {
|
"knowledge": {
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
import { DropResult } from '@hello-pangea/dnd'
|
import { DropResult } from '@hello-pangea/dnd'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import { DraggableVirtualList, useDraggableReorder } from '@renderer/components/DraggableList'
|
import {
|
||||||
|
DraggableVirtualList,
|
||||||
|
type DraggableVirtualListRef,
|
||||||
|
useDraggableReorder
|
||||||
|
} from '@renderer/components/DraggableList'
|
||||||
import { DeleteIcon, EditIcon, PoeLogo } from '@renderer/components/Icons'
|
import { DeleteIcon, EditIcon, PoeLogo } from '@renderer/components/Icons'
|
||||||
import { getProviderLogo } from '@renderer/config/providers'
|
import { getProviderLogo } from '@renderer/config/providers'
|
||||||
import { useAllProviders, useProviders } from '@renderer/hooks/useProvider'
|
import { useAllProviders, useProviders } from '@renderer/hooks/useProvider'
|
||||||
|
import { useTimer } from '@renderer/hooks/useTimer'
|
||||||
import { getProviderLabel } from '@renderer/i18n/label'
|
import { getProviderLabel } from '@renderer/i18n/label'
|
||||||
import ImageStorage from '@renderer/services/ImageStorage'
|
import ImageStorage from '@renderer/services/ImageStorage'
|
||||||
import { isSystemProvider, Provider, ProviderType } from '@renderer/types'
|
import { isSystemProvider, Provider, ProviderType } from '@renderer/types'
|
||||||
@ -18,7 +23,7 @@ import {
|
|||||||
} from '@renderer/utils'
|
} from '@renderer/utils'
|
||||||
import { Avatar, Button, Card, Dropdown, Input, MenuProps, Tag } from 'antd'
|
import { Avatar, Button, Card, Dropdown, Input, MenuProps, Tag } from 'antd'
|
||||||
import { Eye, EyeOff, GripVertical, PlusIcon, Search, UserPen } from 'lucide-react'
|
import { Eye, EyeOff, GripVertical, PlusIcon, Search, UserPen } from 'lucide-react'
|
||||||
import { FC, startTransition, useCallback, useEffect, useState } from 'react'
|
import { FC, startTransition, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSearchParams } from 'react-router-dom'
|
import { useSearchParams } from 'react-router-dom'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -35,11 +40,13 @@ const ProvidersList: FC = () => {
|
|||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
const providers = useAllProviders()
|
const providers = useAllProviders()
|
||||||
const { updateProviders, addProvider, removeProvider, updateProvider } = useProviders()
|
const { updateProviders, addProvider, removeProvider, updateProvider } = useProviders()
|
||||||
|
const { setTimeoutTimer } = useTimer()
|
||||||
const [selectedProvider, _setSelectedProvider] = useState<Provider>(providers[0])
|
const [selectedProvider, _setSelectedProvider] = useState<Provider>(providers[0])
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [searchText, setSearchText] = useState<string>('')
|
const [searchText, setSearchText] = useState<string>('')
|
||||||
const [dragging, setDragging] = useState(false)
|
const [dragging, setDragging] = useState(false)
|
||||||
const [providerLogos, setProviderLogos] = useState<Record<string, string>>({})
|
const [providerLogos, setProviderLogos] = useState<Record<string, string>>({})
|
||||||
|
const listRef = useRef<DraggableVirtualListRef>(null)
|
||||||
|
|
||||||
const setSelectedProvider = useCallback(
|
const setSelectedProvider = useCallback(
|
||||||
(provider: Provider) => {
|
(provider: Provider) => {
|
||||||
@ -75,11 +82,20 @@ const ProvidersList: FC = () => {
|
|||||||
const provider = providers.find((p) => p.id === providerId)
|
const provider = providers.find((p) => p.id === providerId)
|
||||||
if (provider) {
|
if (provider) {
|
||||||
setSelectedProvider(provider)
|
setSelectedProvider(provider)
|
||||||
|
// 滚动到选中的 provider
|
||||||
|
const index = providers.findIndex((p) => p.id === providerId)
|
||||||
|
if (index >= 0) {
|
||||||
|
setTimeoutTimer(
|
||||||
|
'scroll-to-selected-provider',
|
||||||
|
() => listRef.current?.scrollToIndex(index, { align: 'center' }),
|
||||||
|
100
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setSelectedProvider(providers[0])
|
setSelectedProvider(providers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [providers, searchParams, setSelectedProvider])
|
}, [providers, searchParams, setSelectedProvider, setTimeoutTimer])
|
||||||
|
|
||||||
// Handle provider add key from URL schema
|
// Handle provider add key from URL schema
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -485,6 +501,7 @@ const ProvidersList: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</AddButtonWrapper>
|
</AddButtonWrapper>
|
||||||
<DraggableVirtualList
|
<DraggableVirtualList
|
||||||
|
ref={listRef}
|
||||||
list={filteredProviders}
|
list={filteredProviders}
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user