mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-26 03:31:24 +08:00
fix: quickpanel auto-scroll behaviour (#5950)
* fix: quickpanel scrollto changed to smart * fix: add scrollTrigger as the replacement for scrollBlock * fix: add a 'none' trigger to prevent accidental scrolling
This commit is contained in:
parent
3a1ba4eb20
commit
e9afab7725
@ -64,3 +64,5 @@ export interface QuickPanelContextType {
|
||||
readonly beforeAction?: (Options: QuickPanelCallBackOptions) => void
|
||||
readonly afterAction?: (Options: QuickPanelCallBackOptions) => void
|
||||
}
|
||||
|
||||
export type QuickPanelScrollTrigger = 'initial' | 'keyboard' | 'none'
|
||||
|
||||
@ -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<Props> = ({ setInputText }) => {
|
||||
// 避免上下翻页时,鼠标干扰
|
||||
const [isMouseOver, setIsMouseOver] = useState(false)
|
||||
|
||||
const scrollTriggerRef = useRef<QuickPanelScrollTrigger>('initial')
|
||||
const [_index, setIndex] = useState(ctx.defaultIndex)
|
||||
const index = useDeferredValue(_index)
|
||||
const [historyPanel, setHistoryPanel] = useState<QuickPanelOpenOptions[]>([])
|
||||
@ -140,6 +147,7 @@ export const QuickPanelView: React.FC<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ setInputText }) => {
|
||||
$selectedColor={selectedColor}
|
||||
$selectedColorHover={selectedColorHover}
|
||||
className={ctx.isVisible ? 'visible' : ''}>
|
||||
<QuickPanelBody ref={bodyRef} onMouseMove={() => setIsMouseOver(true)}>
|
||||
<QuickPanelBody
|
||||
ref={bodyRef}
|
||||
onMouseMove={() =>
|
||||
setIsMouseOver((prev) => {
|
||||
scrollTriggerRef.current = 'initial'
|
||||
return prev ? prev : true
|
||||
})
|
||||
}>
|
||||
<FixedSizeList
|
||||
ref={listRef}
|
||||
itemCount={list.length}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user