mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 22:10:21 +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
e7c0bbb348
commit
8bc8a9fc99
@ -64,3 +64,5 @@ export interface QuickPanelContextType {
|
|||||||
readonly beforeAction?: (Options: QuickPanelCallBackOptions) => void
|
readonly beforeAction?: (Options: QuickPanelCallBackOptions) => void
|
||||||
readonly afterAction?: (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 Color from 'color'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { Check } from 'lucide-react'
|
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 { FixedSizeList } from 'react-window'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import * as tinyPinyin from 'tiny-pinyin'
|
import * as tinyPinyin from 'tiny-pinyin'
|
||||||
|
|
||||||
import { QuickPanelContext } from './provider'
|
import { QuickPanelContext } from './provider'
|
||||||
import { QuickPanelCallBackOptions, QuickPanelCloseAction, QuickPanelListItem, QuickPanelOpenOptions } from './types'
|
import {
|
||||||
|
QuickPanelCallBackOptions,
|
||||||
|
QuickPanelCloseAction,
|
||||||
|
QuickPanelListItem,
|
||||||
|
QuickPanelOpenOptions,
|
||||||
|
QuickPanelScrollTrigger
|
||||||
|
} from './types'
|
||||||
|
|
||||||
const ITEM_HEIGHT = 31
|
const ITEM_HEIGHT = 31
|
||||||
|
|
||||||
@ -45,6 +51,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
// 避免上下翻页时,鼠标干扰
|
// 避免上下翻页时,鼠标干扰
|
||||||
const [isMouseOver, setIsMouseOver] = useState(false)
|
const [isMouseOver, setIsMouseOver] = useState(false)
|
||||||
|
|
||||||
|
const scrollTriggerRef = useRef<QuickPanelScrollTrigger>('initial')
|
||||||
const [_index, setIndex] = useState(ctx.defaultIndex)
|
const [_index, setIndex] = useState(ctx.defaultIndex)
|
||||||
const index = useDeferredValue(_index)
|
const index = useDeferredValue(_index)
|
||||||
const [historyPanel, setHistoryPanel] = useState<QuickPanelOpenOptions[]>([])
|
const [historyPanel, setHistoryPanel] = useState<QuickPanelOpenOptions[]>([])
|
||||||
@ -140,6 +147,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
(action?: QuickPanelCloseAction) => {
|
(action?: QuickPanelCloseAction) => {
|
||||||
ctx.close(action)
|
ctx.close(action)
|
||||||
setHistoryPanel([])
|
setHistoryPanel([])
|
||||||
|
scrollTriggerRef.current = 'initial'
|
||||||
|
|
||||||
if (action === 'delete-symbol') {
|
if (action === 'delete-symbol') {
|
||||||
const textArea = document.querySelector('.inputbar textarea') as HTMLTextAreaElement
|
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
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [ctx.isVisible])
|
}, [ctx.isVisible])
|
||||||
|
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (index >= 0) {
|
if (!listRef.current || index < 0 || scrollTriggerRef.current === 'none') return
|
||||||
listRef.current?.scrollToItem(index, 'auto')
|
|
||||||
}
|
const alignment = scrollTriggerRef.current === 'keyboard' ? 'auto' : 'smart'
|
||||||
|
listRef.current?.scrollToItem(index, alignment)
|
||||||
|
|
||||||
|
scrollTriggerRef.current = 'none'
|
||||||
}, [index])
|
}, [index])
|
||||||
|
|
||||||
// 处理键盘事件
|
// 处理键盘事件
|
||||||
@ -277,6 +288,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
|
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
|
scrollTriggerRef.current = 'keyboard'
|
||||||
if (isAssistiveKeyPressed) {
|
if (isAssistiveKeyPressed) {
|
||||||
setIndex((prev) => {
|
setIndex((prev) => {
|
||||||
const newIndex = prev - ctx.pageSize
|
const newIndex = prev - ctx.pageSize
|
||||||
@ -289,6 +301,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
break
|
break
|
||||||
|
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
|
scrollTriggerRef.current = 'keyboard'
|
||||||
if (isAssistiveKeyPressed) {
|
if (isAssistiveKeyPressed) {
|
||||||
setIndex((prev) => {
|
setIndex((prev) => {
|
||||||
const newIndex = prev + ctx.pageSize
|
const newIndex = prev + ctx.pageSize
|
||||||
@ -301,6 +314,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
break
|
break
|
||||||
|
|
||||||
case 'PageUp':
|
case 'PageUp':
|
||||||
|
scrollTriggerRef.current = 'keyboard'
|
||||||
setIndex((prev) => {
|
setIndex((prev) => {
|
||||||
const newIndex = prev - ctx.pageSize
|
const newIndex = prev - ctx.pageSize
|
||||||
return newIndex < 0 ? 0 : newIndex
|
return newIndex < 0 ? 0 : newIndex
|
||||||
@ -308,6 +322,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
break
|
break
|
||||||
|
|
||||||
case 'PageDown':
|
case 'PageDown':
|
||||||
|
scrollTriggerRef.current = 'keyboard'
|
||||||
setIndex((prev) => {
|
setIndex((prev) => {
|
||||||
const newIndex = prev + ctx.pageSize
|
const newIndex = prev + ctx.pageSize
|
||||||
return newIndex >= list.length ? list.length - 1 : newIndex
|
return newIndex >= list.length ? list.length - 1 : newIndex
|
||||||
@ -317,6 +332,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
case 'ArrowLeft':
|
case 'ArrowLeft':
|
||||||
if (!isAssistiveKeyPressed) return
|
if (!isAssistiveKeyPressed) return
|
||||||
if (!historyPanel.length) return
|
if (!historyPanel.length) return
|
||||||
|
scrollTriggerRef.current = 'initial'
|
||||||
clearSearchText(false)
|
clearSearchText(false)
|
||||||
if (historyPanel.length > 0) {
|
if (historyPanel.length > 0) {
|
||||||
const lastPanel = historyPanel.pop()
|
const lastPanel = historyPanel.pop()
|
||||||
@ -329,6 +345,7 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
case 'ArrowRight':
|
case 'ArrowRight':
|
||||||
if (!isAssistiveKeyPressed) return
|
if (!isAssistiveKeyPressed) return
|
||||||
if (!list?.[index]?.isMenu) return
|
if (!list?.[index]?.isMenu) return
|
||||||
|
scrollTriggerRef.current = 'initial'
|
||||||
clearSearchText(false)
|
clearSearchText(false)
|
||||||
handleItemAction(list[index], 'enter')
|
handleItemAction(list[index], 'enter')
|
||||||
break
|
break
|
||||||
@ -413,7 +430,14 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
|
|||||||
$selectedColor={selectedColor}
|
$selectedColor={selectedColor}
|
||||||
$selectedColorHover={selectedColorHover}
|
$selectedColorHover={selectedColorHover}
|
||||||
className={ctx.isVisible ? 'visible' : ''}>
|
className={ctx.isVisible ? 'visible' : ''}>
|
||||||
<QuickPanelBody ref={bodyRef} onMouseMove={() => setIsMouseOver(true)}>
|
<QuickPanelBody
|
||||||
|
ref={bodyRef}
|
||||||
|
onMouseMove={() =>
|
||||||
|
setIsMouseOver((prev) => {
|
||||||
|
scrollTriggerRef.current = 'initial'
|
||||||
|
return prev ? prev : true
|
||||||
|
})
|
||||||
|
}>
|
||||||
<FixedSizeList
|
<FixedSizeList
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
itemCount={list.length}
|
itemCount={list.length}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user