Extract FocusTarget (fix #4732)

This commit is contained in:
the1812 2024-05-12 13:20:48 +08:00
parent 1899c12f25
commit 8f701e9434
2 changed files with 75 additions and 28 deletions

View File

@ -28,9 +28,9 @@
v-for="(a, index) of actions" v-for="(a, index) of actions"
:key="a.key" :key="a.key"
:action="a" :action="a"
@previous-item="previousItem($event, index)" @previous-item="previousItem()"
@next-item="nextItem($event, index)" @next-item="nextItem()"
@delete-item="onDeleteItem($event, index)" @delete-item="onDeleteItem()"
@action=" @action="
index === actions.length - 1 && onClearHistory() index === actions.length - 1 && onClearHistory()
onAction(a) onAction(a)
@ -49,12 +49,12 @@
class="suggest-item disabled" class="suggest-item disabled"
></VLoading> ></VLoading>
<ActionItem <ActionItem
v-for="(a, index) of actions" v-for="a of actions"
:key="a.key" :key="a.key"
:action="a" :action="a"
@previous-item="previousItem($event, index)" @previous-item="previousItem()"
@next-item="nextItem($event, index)" @next-item="nextItem()"
@delete-item="onDeleteItem($event, index)" @delete-item="onDeleteItem()"
@action="onAction(a)" @action="onAction(a)"
/> />
</div> </div>
@ -66,6 +66,7 @@ import Fuse from 'fuse.js'
import { VIcon, VLoading, VEmpty } from '@/ui' import { VIcon, VLoading, VEmpty } from '@/ui'
import { registerAndGetData } from '@/plugins/data' import { registerAndGetData } from '@/plugins/data'
import { select } from '@/core/spin-query' import { select } from '@/core/spin-query'
import { ascendingSort } from '@/core/utils/sort'
import { matchUrlPattern } from '@/core/utils' import { matchUrlPattern } from '@/core/utils'
import ActionItem from './ActionItem.vue' import ActionItem from './ActionItem.vue'
import { import {
@ -75,7 +76,7 @@ import {
} from './launch-bar-action' } from './launch-bar-action'
import { searchProvider, search } from './search-provider' import { searchProvider, search } from './search-provider'
import { historyProvider } from './history-provider' import { historyProvider } from './history-provider'
import { ascendingSort } from '@/core/utils/sort' import { FocusTarget } from './focus-target'
const [actionProviders] = registerAndGetData(LaunchBarActionProviders, [ const [actionProviders] = registerAndGetData(LaunchBarActionProviders, [
searchProvider, searchProvider,
@ -144,10 +145,12 @@ export default Vue.extend({
ActionItem, ActionItem,
}, },
data() { data() {
const focusTarget = new FocusTarget(0)
return { return {
recommended, recommended,
actions: [], actions: [],
keyword: '', keyword: '',
focusTarget,
noActions: false, noActions: false,
} }
}, },
@ -160,9 +163,15 @@ export default Vue.extend({
keyword() { keyword() {
this.getActions() this.getActions()
}, },
actions() {
this.focusTarget.reset(this.actions.length)
},
}, },
async mounted() { async mounted() {
this.getActions() await this.getActions()
this.focusTarget.addEventListener('index-change', () => {
this.handleIndexUpdate()
})
if (!matchUrlPattern(/^https?:\/\/search\.bilibili\.com/)) { if (!matchUrlPattern(/^https?:\/\/search\.bilibili\.com/)) {
return return
} }
@ -211,47 +220,47 @@ export default Vue.extend({
if (e.isComposing) { if (e.isComposing) {
return return
} }
this.$refs.list.querySelector('.suggest-item:last-child').focus() this.focusTarget.previous()
e.preventDefault() e.preventDefault()
}, },
handleDown(e: KeyboardEvent) { handleDown(e: KeyboardEvent) {
if (e.isComposing) { if (e.isComposing) {
return return
} }
this.$refs.list.querySelector('.suggest-item').focus() this.focusTarget.next()
e.preventDefault() e.preventDefault()
}, },
previousItem(element: HTMLElement, index: number) { handleIndexUpdate() {
if (index === 0) { if (!this.focusTarget.hasFocus) {
this.focus() this.focusInput()
} else { return
;(element.previousElementSibling as HTMLElement).focus()
} }
this.focusSuggestItem(this.focusTarget.index + 1)
}, },
nextItem(element: HTMLElement, index: number) { previousItem() {
const lastItemIndex = this.actions.length - 1 this.focusTarget.previous()
if (index !== lastItemIndex) { },
;(element.nextElementSibling as HTMLElement).focus() nextItem() {
} else { this.focusTarget.next()
this.focus()
}
}, },
search, search,
onDeleteItem(element: HTMLElement, index: number) { onDeleteItem() {
this.previousItem(element, index) this.focusTarget.previous()
this.getActions() this.getActions()
}, },
onClearHistory() { onClearHistory() {
this.focus() this.focusInput()
this.getActions() this.getActions()
}, },
onAction() { onAction() {
// this.focus()
this.handleSelect() this.handleSelect()
}, },
focus() { focusInput() {
this.$refs.input.focus() this.$refs.input.focus()
}, },
focusSuggestItem(nth: number) {
this.$refs.list.querySelector(`.suggest-item:nth-child(${nth})`)?.focus()
},
}, },
}) })
</script> </script>

View File

@ -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)
}
}