From 8f701e9434595c29e64145dbd36f0a83b7a5b068 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 12 May 2024 13:20:48 +0800 Subject: [PATCH] Extract FocusTarget (fix #4732) --- src/components/launch-bar/LaunchBar.vue | 65 +++++++++++++---------- src/components/launch-bar/focus-target.ts | 38 +++++++++++++ 2 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 src/components/launch-bar/focus-target.ts diff --git a/src/components/launch-bar/LaunchBar.vue b/src/components/launch-bar/LaunchBar.vue index 8d0a7d145..bd0c02ff5 100644 --- a/src/components/launch-bar/LaunchBar.vue +++ b/src/components/launch-bar/LaunchBar.vue @@ -28,9 +28,9 @@ v-for="(a, index) of actions" :key="a.key" :action="a" - @previous-item="previousItem($event, index)" - @next-item="nextItem($event, index)" - @delete-item="onDeleteItem($event, index)" + @previous-item="previousItem()" + @next-item="nextItem()" + @delete-item="onDeleteItem()" @action=" index === actions.length - 1 && onClearHistory() onAction(a) @@ -49,12 +49,12 @@ class="suggest-item disabled" > @@ -66,6 +66,7 @@ import Fuse from 'fuse.js' import { VIcon, VLoading, VEmpty } from '@/ui' import { registerAndGetData } from '@/plugins/data' import { select } from '@/core/spin-query' +import { ascendingSort } from '@/core/utils/sort' import { matchUrlPattern } from '@/core/utils' import ActionItem from './ActionItem.vue' import { @@ -75,7 +76,7 @@ import { } from './launch-bar-action' import { searchProvider, search } from './search-provider' import { historyProvider } from './history-provider' -import { ascendingSort } from '@/core/utils/sort' +import { FocusTarget } from './focus-target' const [actionProviders] = registerAndGetData(LaunchBarActionProviders, [ searchProvider, @@ -144,10 +145,12 @@ export default Vue.extend({ ActionItem, }, data() { + const focusTarget = new FocusTarget(0) return { recommended, actions: [], keyword: '', + focusTarget, noActions: false, } }, @@ -160,9 +163,15 @@ export default Vue.extend({ keyword() { this.getActions() }, + actions() { + this.focusTarget.reset(this.actions.length) + }, }, async mounted() { - this.getActions() + await this.getActions() + this.focusTarget.addEventListener('index-change', () => { + this.handleIndexUpdate() + }) if (!matchUrlPattern(/^https?:\/\/search\.bilibili\.com/)) { return } @@ -211,47 +220,47 @@ export default Vue.extend({ if (e.isComposing) { return } - this.$refs.list.querySelector('.suggest-item:last-child').focus() + this.focusTarget.previous() e.preventDefault() }, handleDown(e: KeyboardEvent) { if (e.isComposing) { return } - this.$refs.list.querySelector('.suggest-item').focus() + this.focusTarget.next() e.preventDefault() }, - previousItem(element: HTMLElement, index: number) { - if (index === 0) { - this.focus() - } else { - ;(element.previousElementSibling as HTMLElement).focus() + handleIndexUpdate() { + if (!this.focusTarget.hasFocus) { + this.focusInput() + return } + this.focusSuggestItem(this.focusTarget.index + 1) }, - nextItem(element: HTMLElement, index: number) { - const lastItemIndex = this.actions.length - 1 - if (index !== lastItemIndex) { - ;(element.nextElementSibling as HTMLElement).focus() - } else { - this.focus() - } + previousItem() { + this.focusTarget.previous() + }, + nextItem() { + this.focusTarget.next() }, search, - onDeleteItem(element: HTMLElement, index: number) { - this.previousItem(element, index) + onDeleteItem() { + this.focusTarget.previous() this.getActions() }, onClearHistory() { - this.focus() + this.focusInput() this.getActions() }, onAction() { - // this.focus() this.handleSelect() }, - focus() { + focusInput() { this.$refs.input.focus() }, + focusSuggestItem(nth: number) { + this.$refs.list.querySelector(`.suggest-item:nth-child(${nth})`)?.focus() + }, }, }) diff --git a/src/components/launch-bar/focus-target.ts b/src/components/launch-bar/focus-target.ts new file mode 100644 index 000000000..6207ecffd --- /dev/null +++ b/src/components/launch-bar/focus-target.ts @@ -0,0 +1,38 @@ +export class FocusTarget extends EventTarget { + /** + * -1: Input Focus + * > -1: Item Focus + */ + private itemIndex = -1 + private itemLength = 0 + + constructor(length: number, index = -1) { + super() + this.itemLength = length + this.index = index + } + + get index() { + return this.itemIndex + } + private set index(value: number) { + this.itemIndex = lodash.clamp(value, -1, this.itemLength - 1) + this.dispatchEvent(new CustomEvent('index-change', { detail: this })) + } + get hasFocus() { + return this.itemIndex > -1 + } + + reset(length: number, index = this.index) { + this.itemLength = length + this.index = index + } + next() { + this.index += 1 + console.log(this.index) + } + previous() { + this.index -= 1 + console.log(this.index) + } +}