Skip to content

Commit 5facea6

Browse files
committed
feat: improve play-page fav-picker & HotkeyDisplay
1 parent 5fc65a1 commit 5facea6

6 files changed

Lines changed: 81 additions & 41 deletions

File tree

src/components/ModalSettings/tab-panes/pane-other-pages.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { APP_NAME } from '$common'
2-
import { CustomKbd } from '$components/fragments'
2+
import { HotkeyDisplay } from '$components/fragments'
33
import { CheckboxSettingItem } from '../setting-item'
44
import { ResetPartialSettingsButton, SettingsGroup, sharedClassNames } from './shared'
55

@@ -24,10 +24,16 @@ export function TabPaneOtherPages() {
2424
<li>
2525
<span className='flex-v-center'>
2626
支持从收藏夹图标 或 快捷键
27-
<CustomKbd className='mx-1 h-14px py-0 line-height-13px'>e</CustomKbd>
27+
<HotkeyDisplay k='E' className='mx-1 h-14px py-0 line-height-13px' />
2828
触发
2929
</span>
3030
</li>
31+
<li>
32+
<span className='flex-v-center'>
33+
<HotkeyDisplay k='Shift+E' className='mx-1 h-14px flex py-0 line-height-13px' />
34+
无视此开关, 总是生效
35+
</span>
36+
</li>
3137
</ul>
3238
</>
3339
}

src/components/ModalSettings/tab-panes/pane-video-card.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Divider, Select, Slider, Tag } from 'antd'
33
import { isEqual, pick } from 'es-toolkit'
44
import { useMemo } from 'react'
55
import { HelpInfo } from '$components/_base/HelpInfo'
6+
import { HotkeyDisplay } from '$components/fragments'
67
import { EVideoLinkOpenMode, VideoLinkOpenModeConfig } from '$components/VideoCard/index.shared'
78
import { antMessage } from '$modules/antd'
89
import { settings, updateSettings, useSettingsSnapshot, type Settings } from '$modules/settings'
@@ -114,7 +115,7 @@ export function TabPaneVideoCard() {
114115
勾选后, 视频卡片会有边框包裹, 更像是一个卡片~ <br />
115116
整个卡片区域可点击 / 可触发预览 / 可使用右键菜单 <br />
116117
否则只是封面区域可以 <br />
117-
使用快捷键 <Tag color='green'>shift+b</Tag> 切换状态 <br />
118+
使用快捷键 <HotkeyDisplay k='Shift+B' /> 切换状态 <br />
118119
{borderCycleListLabels.map((label) => (
119120
<Tag color='success' key={label} className='mx-1'>
120121
{label}
@@ -165,7 +166,7 @@ export function TabPaneVideoCard() {
165166
tooltip={
166167
<>
167168
{explainForFlag('鼠标悬浮后自动开始预览, 不跟随鼠标位置', '预览进度跟随鼠标位置(百分比)')}
168-
切换设置快捷键: <Tag color='green'>shift+m</Tag>
169+
切换设置快捷键: <HotkeyDisplay k='Shift+M' />
169170
</>
170171
}
171172
/>
@@ -187,8 +188,8 @@ export function TabPaneVideoCard() {
187188
label='键盘选中后自动开始'
188189
tooltip={
189190
<>
190-
手动预览快捷键: <Tag color='green'>.</Tag> or <Tag color='green'>p</Tag> <br />
191-
切换设置快捷键: <Tag color='green'>shift+p</Tag>
191+
手动预览快捷键: <HotkeyDisplay k='.' /> or <HotkeyDisplay k='P' /> <br />
192+
切换设置快捷键: <HotkeyDisplay k='Shift+P' />
192193
</>
193194
}
194195
/>

src/components/RecGrid/display-mode/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ import { InputNumber, Radio, Space, type CheckboxOptionType, type RadioChangeEve
33
import { useMemo } from 'react'
44
import { useUnoMerge } from 'unocss-merge/react'
55
import { useSnapshot } from 'valtio'
6-
import { CustomKbd } from '$components/fragments'
6+
import { HotkeyDisplay } from '$components/fragments'
77
import { EGridDisplayMode } from '$enums'
88
import { AntdTooltip } from '$modules/antd/custom'
99
import { settings } from '$modules/settings'
10-
import { isMac } from '$ua'
1110
import { TwoColumnModeAlignSwitcher } from './two-column-mode'
1211

1312
export function gridDisplayModeChecker(x: EGridDisplayMode) {
@@ -118,8 +117,9 @@ export function GridTemplateColumnsConfig({ className }: { className?: string })
118117
自适应: 指按照「卡片最小宽度」自适应 <br />
119118
如果期望显示更多的列, 可以调小「卡片最小宽度」; <br />
120119
如果期望显示更少的列, 可以调大「卡片最小宽度」; <br />
121-
Tip: 先点击输入框, 再移动鼠标到透视按钮, 然后使用键盘{' '}
122-
<CustomKbd>{isMac ? 'Option' : 'Alt'}</CustomKbd> + <CustomKbd>上下键</CustomKbd>调整
120+
Tip: 先点击输入框, 再移动鼠标到透视按钮, 然后使用键盘
121+
<HotkeyDisplay k='Alt+ArrowUp' className='mx-1' />
122+
<HotkeyDisplay k='Alt+ArrowDown' className='mx-1' /> 调整
123123
</>
124124
}
125125
>

src/components/fragments.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
import { formatForDisplay, type Hotkey } from '@tanstack/react-hotkeys'
12
import { useUnoMerge } from 'unocss-merge/react'
23
import type { ComponentProps } from 'react'
34

45
export const kbdClassName =
5-
'inline-block cursor-pointer rounded bg-gate-primary-lv-3 px-6px text-white line-height-tight font-mono'
6+
'inline-block cursor-pointer rounded bg-gate-primary-lv-3 px-6px text-white line-height-tight tracking-1 font-mono'
67

78
export function CustomKbd({ className, ...restProps }: ComponentProps<'kbd'>) {
8-
return <kbd {...restProps} className={useUnoMerge(kbdClassName, className)}></kbd>
9+
return <kbd {...restProps} className={useUnoMerge(kbdClassName, className)} />
10+
}
11+
12+
export function HotkeyDisplay({ k, ...restProps }: Omit<ComponentProps<'kbd'>, 'children'> & { k: Hotkey }) {
13+
return <CustomKbd {...restProps}>{formatForDisplay(k)}</CustomKbd>
914
}
1015

1116
export const antSpinIndicator = (

src/main/video-play-page.tsx

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { bv2av } from '@mgdn/bvid'
2+
import { matchesKeyboardEvent } from '@tanstack/react-hotkeys'
23
import { delay } from 'es-toolkit'
34
import ms from 'ms'
45
import { baseDebug } from '$common'
@@ -129,35 +130,44 @@ function registerAddToFavCommand() {
129130
GM.registerMenuCommand?.('⭐️ 加入收藏', () => addToFav())
130131
}
131132
async function setupCustomFavPicker() {
132-
if (!settings.fav.useCustomFavPicker.onPlayPage) return
133133
if (!getCurrentPageBvid()) return
134+
const willUseCustomFavPicker = () => settings.fav.useCustomFavPicker.onPlayPage
135+
136+
// setup keyboard shortcut
137+
// Shift+E: always on
138+
// E: only when willUseCustomFavPicker
134139
document.addEventListener(
135140
'keydown',
136141
(e) => {
137-
if (e.key !== 'e') return
138-
if (shouldDisableShortcut()) return
139-
const target = e.target as HTMLElement
140-
if (target.closest('bili-comments')) return // emit from a <bili-comments> element
141-
e.stopImmediatePropagation()
142-
e.preventDefault()
143-
addToFav()
142+
if (matchesKeyboardEvent(e, 'Shift+E') || (willUseCustomFavPicker() && matchesKeyboardEvent(e, 'E'))) {
143+
if (!getCurrentPageBvid()) return
144+
if (shouldDisableShortcut()) return
145+
const target = e.target as HTMLElement
146+
if (target.closest('bili-comments')) return // emit from a <bili-comments> element
147+
e.stopImmediatePropagation()
148+
e.preventDefault()
149+
addToFav()
150+
}
144151
},
145152
{ capture: true },
146153
)
147154

148-
const el = await poll(() => document.querySelector<HTMLDivElement>('.video-fav.video-toolbar-left-item'), {
149-
interval: 100,
150-
timeout: 5_000,
151-
})
152-
el?.addEventListener(
153-
'click',
154-
(e) => {
155-
e.stopImmediatePropagation()
156-
e.preventDefault()
157-
addToFav()
158-
},
159-
{ capture: true },
160-
)
155+
if (willUseCustomFavPicker()) {
156+
const el = await poll(() => document.querySelector<HTMLDivElement>('.video-fav.video-toolbar-left-item'), {
157+
interval: 100,
158+
timeout: 5_000,
159+
})
160+
el?.addEventListener(
161+
'click',
162+
(e) => {
163+
if (!willUseCustomFavPicker() || !getCurrentPageBvid()) return
164+
e.stopImmediatePropagation()
165+
e.preventDefault()
166+
addToFav()
167+
},
168+
{ capture: true },
169+
)
170+
}
161171
}
162172

163173
async function addToFav(sourceFavFolderIds?: number[] | undefined) {

src/utility/dom.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,28 +100,46 @@ export async function tryToRemove(
100100
}
101101

102102
/**
103-
* input 输入中, 用于拦截快捷键处理
103+
* from https://github.com/TanStack/hotkeys/blob/%40tanstack/react-hotkeys%400.4.1/packages/hotkeys/src/manager.utils.ts#L48
104+
* it's not exported
104105
*/
106+
export function isInputElement(element: EventTarget | null): boolean {
107+
if (!element) {
108+
return false
109+
}
105110

106-
export function shouldDisableShortcut() {
107-
// if activeElement is input, disable shortcut
108-
const activeTagName = document.activeElement?.tagName?.toLowerCase() ?? ''
109-
if (['input', 'textarea'].includes(activeTagName)) {
111+
if (element instanceof HTMLInputElement) {
112+
const type = element.type.toLowerCase()
113+
if (type === 'button' || type === 'submit' || type === 'reset') {
114+
return false
115+
}
110116
return true
111117
}
112118

113-
// if search panel is open, disable shortcut
114-
if (document.querySelector('.center-search__bar.is-focus')) {
119+
if (element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
120+
return true
121+
}
122+
123+
// Check for contenteditable elements (includes "true", "", "plaintext-only",
124+
// and inherited contenteditable from ancestor elements)
125+
if (element instanceof HTMLElement && element.isContentEditable) {
115126
return true
116127
}
117128

118129
return false
119130
}
120131

121132
/**
122-
* https://youmightnotneedjquery.com/#offset
133+
* input 输入中, 用于拦截快捷键处理
123134
*/
135+
export function shouldDisableShortcut() {
136+
if (isInputElement(document.activeElement)) return true
137+
return false
138+
}
124139

140+
/**
141+
* https://youmightnotneedjquery.com/#offset
142+
*/
125143
export function getElementOffset(el: HTMLElement, rect?: DOMRect) {
126144
rect ??= el.getBoundingClientRect()
127145
const docElem = document.documentElement

0 commit comments

Comments
 (0)