feat: adds arrow keys navigation on powersearch

This commit is contained in:
franciscoemanuel
2021-10-19 02:20:30 -03:00
parent be414d8279
commit 686d8e5be7
4 changed files with 107 additions and 3 deletions

View File

@@ -4,6 +4,7 @@
v-for="(shortcut, shortcutIndex) in searchResults"
:key="`shortcut-${shortcutIndex}`"
:ref="`item-${shortcutIndex}`"
:active="shortcutIndex === selectedEntry"
:shortcut="shortcut.item"
@action="$emit('action', shortcut.item.action)"
/>
@@ -20,8 +21,15 @@
</template>
<script setup lang="ts">
import { computed } from "@nuxtjs/composition-api"
import {
computed,
onUnmounted,
onMounted,
getCurrentInstance,
} from "@nuxtjs/composition-api"
import Fuse from "fuse.js"
import { useArrowKeysNavigation } from "~/helpers/powerSearchNavigation"
import { HoppAction } from "~/helpers/actions"
const props = defineProps<{
input: Record<string, any>[]
@@ -35,4 +43,23 @@ const options = {
const fuse = new Fuse(props.input, options)
const searchResults = computed(() => fuse.search(props.search))
const searchResultsItems = computed(() =>
searchResults.value.map((searchResult: any) => searchResult.item)
)
const currentInstance = getCurrentInstance()
const emitSearchAction = (action: HoppAction) =>
currentInstance.emit("action", action)
const { bindArrowKeysListerners, unbindArrowKeysListerners, selectedEntry } =
useArrowKeysNavigation(searchResultsItems, {
onEnter: emitSearchAction,
stopPropagation: true,
})
onMounted(bindArrowKeysListerners)
onUnmounted(unbindArrowKeysListerners)
</script>

View File

@@ -47,6 +47,7 @@
v-for="(shortcut, shortcutIndex) in map.shortcuts"
:key="`map-${mapIndex}-shortcut-${shortcutIndex}`"
:shortcut="shortcut"
:active="shortcutsItems.indexOf(shortcut) === selectedEntry"
@action="runAction"
/>
</div>
@@ -56,9 +57,10 @@
</template>
<script setup lang="ts">
import { ref } from "@nuxtjs/composition-api"
import { ref, computed, onUnmounted, onMounted } from "@nuxtjs/composition-api"
import { HoppAction, invokeAction } from "~/helpers/actions"
import { spotlight as mappings, fuse } from "~/helpers/shortcuts"
import { useArrowKeysNavigation } from "~/helpers/powerSearchNavigation"
defineProps<{
show: boolean
@@ -79,4 +81,20 @@ const runAction = (command: HoppAction) => {
invokeAction(command)
hideModal()
}
const shortcutsItems = computed(() =>
mappings.reduce(
(shortcuts, section) => [...shortcuts, ...section.shortcuts],
[]
)
)
const { bindArrowKeysListerners, unbindArrowKeysListerners, selectedEntry } =
useArrowKeysNavigation(shortcutsItems, {
onEnter: runAction,
})
onMounted(bindArrowKeysListerners)
onUnmounted(unbindArrowKeysListerners)
</script>

View File

@@ -13,6 +13,7 @@
focus-visible:bg-primaryLight
search-entry
"
:class="{ active, 'outline-none': active, 'focus-visible': active }"
tabindex="0"
@click="$emit('action', shortcut.action)"
@keydown.enter="$emit('action', shortcut.action)"
@@ -26,6 +27,7 @@
group-hover:text-secondaryDark group-hover:opacity-100
group-focus:opacity-100
"
:class="{ 'opacity-100': active, 'text-secondaryDark': active }"
:name="shortcut.icon"
/>
<span
@@ -36,6 +38,7 @@
group-hover:text-secondaryDark
group-focus:text-secondaryDark
"
:class="{ 'text-secondaryDark': active }"
>
{{ $t(shortcut.label) }}
</span>
@@ -52,6 +55,7 @@
<script setup lang="ts">
defineProps<{
shortcut: Object
active: Boolean
}>()
</script>
@@ -73,7 +77,8 @@ defineProps<{
}
&:hover::after,
&:focus::after {
&:focus::after,
&.active::after {
@apply bg-accentLight;
}
}

View File

@@ -0,0 +1,54 @@
import { ref } from "@nuxtjs/composition-api"
const NAVIGATION_KEYS = ["ArrowDown", "ArrowUp", "Enter"]
export function useArrowKeysNavigation(searchItems: any, options: any = {}) {
function handleArrowKeysNavigation(
event: any,
itemIndex: any,
preventPropagation: Boolean
) {
if (!NAVIGATION_KEYS.includes(event.key)) return
if (preventPropagation) event.stopImmediatePropagation()
const itemsLength = searchItems.value.length
const lastItemIndex = itemsLength - 1
const itemIndexValue = itemIndex.value
const action = searchItems.value[itemIndexValue].action
if (action && event.key === "Enter" && options.onEnter) {
options.onEnter(action)
return
}
if (event.key === "ArrowDown") {
itemIndex.value = itemIndexValue < lastItemIndex ? itemIndexValue + 1 : 0
} else if (itemIndexValue === 0) itemIndex.value = lastItemIndex
else if (event.key === "ArrowUp") itemIndex.value = itemIndexValue - 1
}
const preventPropagation = options && options.stopPropagation
const selectedEntry = ref(0)
const onKeyUp = (event: any) => {
handleArrowKeysNavigation(event, selectedEntry, preventPropagation)
}
function bindArrowKeysListerners() {
window.addEventListener("keydown", onKeyUp, { capture: preventPropagation })
}
function unbindArrowKeysListerners() {
window.removeEventListener("keydown", onKeyUp, {
capture: preventPropagation,
})
}
return {
bindArrowKeysListerners,
unbindArrowKeysListerners,
selectedEntry,
}
}