mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-13 19:20:04 +00:00
💄 style: center active model on open in model switch panel (#12215)
* 🐛 fix: correct eslint command syntax in lint-ts.sh script * ✨ feat: add scroll functionality for active model in List component * ✨ feat: improve scroll behavior for active model in List component
This commit is contained in:
+1
-1
@@ -2,4 +2,4 @@
|
||||
set -o pipefail
|
||||
|
||||
eslint src/ tests/ --fix --concurrency=auto --prune-suppressions
|
||||
eslint src/ tests/ --concurrency=auto
|
||||
eslint src/ tests/ --concurrency=auto
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Flexbox } from '@lobehub/ui';
|
||||
import { type FC, type ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useEnabledChatModels } from '@/hooks/useEnabledChatModels';
|
||||
@@ -54,28 +54,69 @@ export const List: FC<ListProps> = ({
|
||||
|
||||
const activeKey = menuKey(provider, model);
|
||||
|
||||
// Set initial scroll position to keep active model centered
|
||||
const listRef = useRef<HTMLDivElement | null>(null);
|
||||
const activeNodeRef = useRef<HTMLDivElement | null>(null);
|
||||
const hasInitializedPositionRef = useRef(false);
|
||||
|
||||
const activeItemRef = useCallback((node: HTMLDivElement | null) => {
|
||||
activeNodeRef.current = node;
|
||||
}, []);
|
||||
|
||||
const listHeight = panelHeight - TOOLBAR_HEIGHT - FOOTER_HEIGHT;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (hasInitializedPositionRef.current) return;
|
||||
|
||||
const container = listRef.current;
|
||||
const activeNode = activeNodeRef.current;
|
||||
if (!container || !activeNode) return;
|
||||
|
||||
const targetScrollTop =
|
||||
activeNode.offsetTop - (container.clientHeight - activeNode.offsetHeight) / 2;
|
||||
container.scrollTop = Math.max(0, targetScrollTop);
|
||||
hasInitializedPositionRef.current = true;
|
||||
}, [listHeight, activeKey]);
|
||||
|
||||
return (
|
||||
<Flexbox className={styles.list} flex={1} style={{ height: listHeight }}>
|
||||
{listItems.map((item, index) => (
|
||||
<ListItemRenderer
|
||||
activeKey={activeKey}
|
||||
extraControls={extraControls}
|
||||
item={item}
|
||||
newLabel={newLabel}
|
||||
key={menuKey(
|
||||
'provider' in item && item.provider ? item.provider.id : '',
|
||||
'model' in item && item.model
|
||||
? item.model.id
|
||||
: 'data' in item && item.data
|
||||
? item.data.displayName
|
||||
: `${item.type}-${index}`,
|
||||
)}
|
||||
onClose={handleClose}
|
||||
onModelChange={handleModelChange}
|
||||
/>
|
||||
))}
|
||||
<Flexbox className={styles.list} flex={1} ref={listRef} style={{ height: listHeight }}>
|
||||
{listItems.map((item, index) => {
|
||||
const itemKey = menuKey(
|
||||
'provider' in item && item.provider ? item.provider.id : '',
|
||||
'model' in item && item.model
|
||||
? item.model.id
|
||||
: 'data' in item && item.data
|
||||
? item.data.displayName
|
||||
: `${item.type}-${index}`,
|
||||
);
|
||||
const isActive =
|
||||
(item.type === 'provider-model-item' &&
|
||||
menuKey(item.provider.id, item.model.id) === activeKey) ||
|
||||
(item.type === 'model-item-single' &&
|
||||
menuKey(item.data.providers[0].id, item.data.model.id) === activeKey) ||
|
||||
(item.type === 'model-item-multiple' &&
|
||||
item.data.providers.some((p) => menuKey(p.id, item.data.model.id) === activeKey));
|
||||
|
||||
const renderItem = (key?: string) => (
|
||||
<ListItemRenderer
|
||||
activeKey={activeKey}
|
||||
extraControls={extraControls}
|
||||
item={item}
|
||||
key={key}
|
||||
newLabel={newLabel}
|
||||
onClose={handleClose}
|
||||
onModelChange={handleModelChange}
|
||||
/>
|
||||
);
|
||||
|
||||
return isActive ? (
|
||||
<div key={itemKey} ref={activeItemRef}>
|
||||
{renderItem()}
|
||||
</div>
|
||||
) : (
|
||||
renderItem(itemKey)
|
||||
);
|
||||
})}
|
||||
</Flexbox>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user