diff --git a/src/renderer/src/components/QuickPanel/types.ts b/src/renderer/src/components/QuickPanel/types.ts index e122aa1d29..7cef05be23 100644 --- a/src/renderer/src/components/QuickPanel/types.ts +++ b/src/renderer/src/components/QuickPanel/types.ts @@ -64,3 +64,5 @@ export interface QuickPanelContextType { readonly beforeAction?: (Options: QuickPanelCallBackOptions) => void readonly afterAction?: (Options: QuickPanelCallBackOptions) => void } + +export type QuickPanelScrollTrigger = 'initial' | 'keyboard' | 'none' diff --git a/src/renderer/src/components/QuickPanel/view.tsx b/src/renderer/src/components/QuickPanel/view.tsx index 2bd1b14349..1602b6a4ac 100644 --- a/src/renderer/src/components/QuickPanel/view.tsx +++ b/src/renderer/src/components/QuickPanel/view.tsx @@ -6,13 +6,19 @@ import { theme } from 'antd' import Color from 'color' import { t } from 'i18next' import { Check } from 'lucide-react' -import React, { use, useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react' +import React, { use, useCallback, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import { FixedSizeList } from 'react-window' import styled from 'styled-components' import * as tinyPinyin from 'tiny-pinyin' import { QuickPanelContext } from './provider' -import { QuickPanelCallBackOptions, QuickPanelCloseAction, QuickPanelListItem, QuickPanelOpenOptions } from './types' +import { + QuickPanelCallBackOptions, + QuickPanelCloseAction, + QuickPanelListItem, + QuickPanelOpenOptions, + QuickPanelScrollTrigger +} from './types' const ITEM_HEIGHT = 31 @@ -45,6 +51,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { // 避免上下翻页时,鼠标干扰 const [isMouseOver, setIsMouseOver] = useState(false) + const scrollTriggerRef = useRef('initial') const [_index, setIndex] = useState(ctx.defaultIndex) const index = useDeferredValue(_index) const [historyPanel, setHistoryPanel] = useState([]) @@ -140,6 +147,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { (action?: QuickPanelCloseAction) => { ctx.close(action) setHistoryPanel([]) + scrollTriggerRef.current = 'initial' if (action === 'delete-symbol') { const textArea = document.querySelector('.inputbar textarea') as HTMLTextAreaElement @@ -249,10 +257,13 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [ctx.isVisible]) - useEffect(() => { - if (index >= 0) { - listRef.current?.scrollToItem(index, 'auto') - } + useLayoutEffect(() => { + if (!listRef.current || index < 0 || scrollTriggerRef.current === 'none') return + + const alignment = scrollTriggerRef.current === 'keyboard' ? 'auto' : 'smart' + listRef.current?.scrollToItem(index, alignment) + + scrollTriggerRef.current = 'none' }, [index]) // 处理键盘事件 @@ -277,6 +288,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { switch (e.key) { case 'ArrowUp': + scrollTriggerRef.current = 'keyboard' if (isAssistiveKeyPressed) { setIndex((prev) => { const newIndex = prev - ctx.pageSize @@ -289,6 +301,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { break case 'ArrowDown': + scrollTriggerRef.current = 'keyboard' if (isAssistiveKeyPressed) { setIndex((prev) => { const newIndex = prev + ctx.pageSize @@ -301,6 +314,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { break case 'PageUp': + scrollTriggerRef.current = 'keyboard' setIndex((prev) => { const newIndex = prev - ctx.pageSize return newIndex < 0 ? 0 : newIndex @@ -308,6 +322,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { break case 'PageDown': + scrollTriggerRef.current = 'keyboard' setIndex((prev) => { const newIndex = prev + ctx.pageSize return newIndex >= list.length ? list.length - 1 : newIndex @@ -317,6 +332,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { case 'ArrowLeft': if (!isAssistiveKeyPressed) return if (!historyPanel.length) return + scrollTriggerRef.current = 'initial' clearSearchText(false) if (historyPanel.length > 0) { const lastPanel = historyPanel.pop() @@ -329,6 +345,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { case 'ArrowRight': if (!isAssistiveKeyPressed) return if (!list?.[index]?.isMenu) return + scrollTriggerRef.current = 'initial' clearSearchText(false) handleItemAction(list[index], 'enter') break @@ -413,7 +430,14 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { $selectedColor={selectedColor} $selectedColorHover={selectedColorHover} className={ctx.isVisible ? 'visible' : ''}> - setIsMouseOver(true)}> + + setIsMouseOver((prev) => { + scrollTriggerRef.current = 'initial' + return prev ? prev : true + }) + }>