feat(ui): Enhance mention models dropdown with improved styling and scrollbar

This commit is contained in:
Carter Cheng 2025-03-06 21:54:26 +08:00 committed by 亢奋猫
parent 0fdb2ed0ef
commit 94e0559dd3
6 changed files with 147 additions and 31 deletions

View File

@ -459,6 +459,7 @@
"embedding_model": "Embedding Model",
"embedding_model_tooltip": "Add in Settings->Model Provider->Manage",
"free": "Free",
"no_matches": "No models available",
"parameter_name": "Parameter Name",
"parameter_type": {
"boolean": "Boolean",

View File

@ -479,7 +479,8 @@
},
"vision": "画像",
"websearch": "ウェブ検索",
"edit": "モデルを編集"
"edit": "モデルを編集",
"no_matches": "利用可能なモデルがありません"
},
"navbar": {
"expand": "ダイアログを展開",

View File

@ -479,7 +479,8 @@
},
"vision": "Визуальные",
"websearch": "Веб-поисковые",
"edit": "Редактировать модель"
"edit": "Редактировать модель",
"no_matches": "Нет доступных моделей"
},
"navbar": {
"expand": "Развернуть диалоговое окно",

View File

@ -479,7 +479,8 @@
},
"vision": "视觉",
"websearch": "联网",
"edit": "编辑模型"
"edit": "编辑模型",
"no_matches": "无可用模型"
},
"navbar": {
"expand": "伸缩对话框",

View File

@ -479,7 +479,8 @@
},
"vision": "視覺",
"websearch": "網路搜尋",
"edit": "編輯模型"
"edit": "編輯模型",
"no_matches": "無可用模型"
},
"navbar": {
"expand": "伸縮對話框",

View File

@ -266,6 +266,44 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
}
}, [isOpen, selectedIndex, flatModelItems, mentionModels, menuDismissed])
useEffect(() => {
const updateScrollbarClass = () => {
requestAnimationFrame(() => {
if (menuRef.current) {
const hasScrollbar = menuRef.current.scrollHeight > menuRef.current.clientHeight
menuRef.current.classList.toggle('has-scrollbar', hasScrollbar)
menuRef.current.classList.toggle('no-scrollbar', !hasScrollbar)
}
})
}
// Update on initial render and whenever content changes
const observer = new MutationObserver(updateScrollbarClass)
const resizeObserver = new ResizeObserver(updateScrollbarClass)
if (menuRef.current) {
// Observe content changes
observer.observe(menuRef.current, {
childList: true,
subtree: true,
attributes: true,
characterData: true
})
// Observe size changes
resizeObserver.observe(menuRef.current)
// Initial check after a short delay to ensure DOM is ready
setTimeout(updateScrollbarClass, 0)
}
// Cleanup
return () => {
observer.disconnect()
resizeObserver.disconnect()
}
}, [isOpen, searchText, flatModelItems.length]) // Add dependencies that affect content
const menu = (
<div ref={menuRef} className="ant-dropdown-menu">
{flatModelItems.length > 0 ? (
@ -328,72 +366,145 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
}
const DropdownMenuStyle = createGlobalStyle`
/* Apply background to all animation states */
.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 {
&.ant-dropdown {
animation-duration: 0.15s !important;
margin-top: 4px;
}
.ant-dropdown-menu {
max-height: 400px;
overflow-y: auto;
overflow-x: hidden;
padding: 4px 0;
margin-bottom: 40px;
padding: 4px 12px;
position: relative;
background: rgba(var(--color-base-rgb), 0.65) !important;
backdrop-filter: blur(35px) saturate(150%) !important;
border: 0.5px solid rgba(var(--color-border-rgb), 0.3);
border-radius: 10px;
box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.15),
0 4px 16px rgba(0, 0, 0, 0.15),
0 2px 8px rgba(0, 0, 0, 0.12),
inset 0 0 0 0.5px rgba(255, 255, 255, 0.1);
transform-origin: top;
will-change: transform, opacity;
transition: all 0.15s cubic-bezier(0.4, 0.0, 0.2, 1);
&.no-scrollbar {
padding-right: 12px;
}
&.has-scrollbar {
padding-right: 2px;
}
// Scrollbar styles
&::-webkit-scrollbar {
width: 6px;
width: 14px;
height: 6px;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
background: var(--color-scrollbar-thumb);
border: 4px solid transparent;
background-clip: padding-box;
border-radius: 7px;
background-color: transparent;
min-height: 50px;
transition: all 0.2s;
}
&:hover {
background: var(--color-scrollbar-thumb-hover);
}
&:hover::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
}
&::-webkit-scrollbar-thumb:hover {
background-color: rgba(0, 0, 0, 0.3);
border: 4px solid transparent;
}
&::-webkit-scrollbar-thumb:active {
background-color: rgba(0, 0, 0, 0.4);
border: 3px solid transparent;
}
&::-webkit-scrollbar-track {
background: transparent;
}
.no-results {
padding: 8px 12px;
color: var(--color-text-3);
cursor: default;
font-size: 14px;
&:hover {
background: none;
}
border-radius: 7px;
}
}
// // Apply margin to the last group
// .ant-dropdown-menu-item-group:last-child {
// margin-bottom: 40px;
// }
.ant-dropdown-menu-item-group {
margin-bottom: 4px;
&:not(:first-child) {
margin-top: 4px;
}
.ant-dropdown-menu-item-group-title {
padding: 5px 12px;
color: var(--color-text-3);
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.03em;
opacity: 0.7;
}
}
// Handle no-results case margin
.no-results {
padding: 8px 12px;
color: var(--color-text-3);
cursor: default;
font-size: 13px;
opacity: 0.8;
margin-bottom: 40px;
&:hover {
background: none;
}
}
.ant-dropdown-menu-item {
padding: 5px 12px;
margin: 0 -12px;
cursor: pointer;
transition: all 0.3s;
transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
display: flex;
align-items: center;
gap: 8px;
border-radius: 6px;
font-size: 13px;
&:hover {
background: var(--color-hover);
background: rgba(var(--color-hover-rgb), 0.5);
}
&.ant-dropdown-menu-item-selected {
background-color: var(--color-primary-bg);
background-color: rgba(var(--color-primary-rgb), 0.12);
color: var(--color-primary);
}
.ant-dropdown-menu-item-icon {
margin-right: 0;
opacity: 0.9;
}
}
}
@ -403,7 +514,7 @@ const ModelItem = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
font-size: 13px;
width: 100%;
min-width: 200px;
gap: 16px;
@ -425,15 +536,15 @@ const ModelNameRow = styled.div`
const PinIcon = styled.span.attrs({ className: 'pin-icon' })<{ $isPinned: boolean }>`
margin-left: auto;
padding: 0 8px;
opacity: ${(props) => (props.$isPinned ? 1 : 'inherit')};
transition: opacity 0.2s;
opacity: ${(props) => (props.$isPinned ? 0.9 : 0)};
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
right: 0;
color: ${(props) => (props.$isPinned ? 'var(--color-primary)' : 'inherit')};
transform: ${(props) => (props.$isPinned ? 'rotate(-45deg)' : 'none')};
opacity: 0;
font-size: 13px;
&:hover {
opacity: 1 !important;
opacity: ${(props) => (props.$isPinned ? 1 : 0.7)} !important;
color: ${(props) => (props.$isPinned ? 'var(--color-primary)' : 'inherit')};
}
`