Skip to content

Commit

Permalink
feat: search bar keyboard navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
xdeq committed Oct 9, 2023
1 parent f2698fe commit 164ae91
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 13 deletions.
99 changes: 88 additions & 11 deletions components/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
/** API */
import { search } from "@/services/api/search"
/**
* Composable
*/
import { useOutside } from "@/composable/outside"
/**
* Store
*/
Expand All @@ -11,16 +16,18 @@ const notificationsStore = useNotificationsStore()
const router = useRouter()
const searchTerm = ref("")
const searchRef = ref()
const inputRef = ref()
const isActive = ref(false)
let history = []
const showHistory = ref(false)
const handleClick = () => {
inputRef.value.focus()
}
let removeOutside = null
const trap = ref({})
const itemsEl = ref(null)
onMounted(() => {
document.addEventListener("keydown", onKeydown)
Expand All @@ -30,17 +37,69 @@ onMounted(() => {
onBeforeUnmount(() => {
document.removeEventListener("keydown", onKeydown)
removeOutside()
})
const onKeydown = (e) => {
if (e.code === "Escape" && isActive.value) {
inputRef.value.blur()
handleBlur()
}
if (e.code === "Slash" && !isActive.value) {
inputRef.value.focus()
e.preventDefault()
}
if ((e.code === "ArrowUp" && isActive.value) || (e.code === "Tab" && e.shiftKey && isActive.value)) {
e.preventDefault()
const focusableItems = [...itemsEl.value?.wrapper.children]
if (!focusableItems.includes(document.activeElement)) {
focusableItems[focusableItems.length - 1].focus()
} else {
const currentFocusedIdx = focusableItems.indexOf(document.activeElement)
if (focusableItems[currentFocusedIdx - 1]) {
focusableItems[currentFocusedIdx - 1].focus()
} else {
focusableItems[focusableItems.length - 1].focus()
}
}
return
}
if (["Tab", "ArrowDown"].includes(e.code) && isActive.value) {
e.preventDefault()
const focusableItems = [...itemsEl.value?.wrapper.children]
if (!focusableItems.includes(document.activeElement)) {
focusableItems[0].focus()
} else {
const currentFocusedIdx = focusableItems.indexOf(document.activeElement)
if (focusableItems[currentFocusedIdx + 1]) {
focusableItems[currentFocusedIdx + 1].focus()
} else {
focusableItems[0].focus()
}
}
}
if (e.code === "PageUp" && isActive.value) {
e.preventDefault()
const focusableItems = [...itemsEl.value?.wrapper.children]
focusableItems[0].focus()
}
if (e.code === "PageDown" && isActive.value) {
e.preventDefault()
const focusableItems = [...itemsEl.value?.wrapper.children]
focusableItems[focusableItems.length - 1].focus()
}
}
const handleClear = () => {
Expand All @@ -49,14 +108,28 @@ const handleClear = () => {
searchTerm.value = ""
}
const handleFocus = () => {
const handleClick = () => {
inputRef.value.focus()
}
const onFocus = () => {
isActive.value = true
showHistory.value = true
nextTick(() => {
removeOutside = useOutside(searchRef.value.wrapper, () => {
handleBlur()
})
})
}
const handleBlur = () => {
removeOutside()
isActive.value = false
showHistory.value = false
inputRef.value.blur()
}
const handleEnter = (e) => {
Expand Down Expand Up @@ -112,16 +185,15 @@ const saveToLocaleStorage = (type, height) => {
</script>
<template>
<Flex :class="$style.wrapper">
<Flex ref="searchRef" :class="$style.wrapper">
<Flex @click="handleClick" align="center" justify="between" :class="[$style.container, isActive && $style.active]">
<Flex align="center" gap="8" wide>
<Icon name="search" size="16" color="support" :class="$style.search_icon" />
<input
ref="inputRef"
v-model="searchTerm"
@focus="handleFocus"
@blur="handleBlur"
@focus="onFocus"
@keydown.enter="handleEnter"
placeholder="Find transaction or block"
/>
Expand All @@ -144,9 +216,9 @@ const saveToLocaleStorage = (type, height) => {
<Flex v-if="showHistory && history" direction="column" gap="8" :class="$style.history_popup">
<Text size="12" weight="600" color="tertiary">Search History</Text>
<Flex direction="column" gap="2">
<NuxtLink :to="`/block/${item.height}`" v-for="item in history">
<Flex justify="between" :class="$style.item">
<Flex ref="itemsEl" direction="column" gap="2">
<NuxtLink :to="`/block/${item.height}`" v-for="item in history" :class="$style.item">
<Flex justify="between">
<Flex align="center" gap="8" style="max-width: calc(100% - 20px)">
<Icon
:name="(item.type === 'block' && 'block') || (item.type === 'tx' && 'zap') || 'tag'"
Expand Down Expand Up @@ -271,6 +343,7 @@ const saveToLocaleStorage = (type, height) => {
.item {
border-radius: 6px;
cursor: pointer;
outline: none;
padding: 8px;
margin: 0 -8px;
Expand All @@ -285,6 +358,10 @@ const saveToLocaleStorage = (type, height) => {
}
}
&:focus-visible {
background: var(--op-10);
}
.arrow_icon {
opacity: 0;
Expand Down
4 changes: 2 additions & 2 deletions components/widgets/BlockWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ onBeforeUnmount(() => {
<Flex direction="column" gap="8">
<Flex align="center" gap="4">
<Text size="16" weight="600" color="primary"> Block </Text>
<Text size="16" weight="600" color="brand"> {{ comma(lastBlock.height + 1) }}</Text>
<Text size="16" weight="600" color="green"> {{ comma(lastBlock.height + 1) }}</Text>
</Flex>

<Text size="13" weight="500" color="tertiary"> Chain Mocha-4 </Text>
Expand Down Expand Up @@ -104,7 +104,7 @@ onBeforeUnmount(() => {
left: 0;
width: 336px;
background: var(--brand);
background: var(--neutral-green);
z-index: -1;
Expand Down

0 comments on commit 164ae91

Please sign in to comment.