fix: don't cut off the part that's already been typed (#3054)

This commit is contained in:
Liyas Thomas
2023-05-24 02:06:02 +05:30
committed by GitHub
parent aeb9172144
commit 1fe0b8861d

View File

@@ -1,13 +1,27 @@
<template>
<div class="autocomplete-wrapper">
<input ref="acInput" v-model="text" type="text" autocomplete="off" :placeholder="placeholder" :spellcheck="spellcheck"
:autocapitalize="autocapitalize" :class="styles" @input.stop="onInput" @keyup="updateSuggestions"
@click="updateSuggestions" @keydown="handleKeystroke" @change="emit('change', $event)" />
<ul v-if="suggestions.length > 0 && suggestionsVisible" class="suggestions"
:style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }">
<li v-for="(suggestion, index) in suggestions" :key="`suggestion-${index}`"
:class="{ active: currentSuggestionIndex === index }" @click.prevent="forceSuggestion(suggestion)">
<input
ref="acInput"
v-model="text"
type="text"
autocomplete="off"
:placeholder="placeholder"
:spellcheck="spellcheck"
:autocapitalize="autocapitalize"
:class="styles"
@input.stop="onInput"
@keyup="updateSuggestions"
@click="updateSuggestions"
@keydown="handleKeystroke"
@change="emit('change', $event)"
/>
<ul v-if="suggestions.length > 0 && suggestionsVisible" class="suggestions">
<li
v-for="(suggestion, index) in suggestions"
:key="`suggestion-${index}`"
:class="{ active: currentSuggestionIndex === index }"
@click.prevent="forceSuggestion(suggestion)"
>
{{ suggestion }}
</li>
</ul>
@@ -62,11 +76,9 @@ const emit = defineEmits<{
const text = ref(props.value)
const selectionStart = ref(0)
const suggestionsOffsetLeft = ref(0)
const currentSuggestionIndex = ref(-1)
const suggestionsVisible = ref(false)
onMounted(() => {
updateSuggestions({
target: acInput,
@@ -82,14 +94,11 @@ const suggestions = computed(() => {
entry.toLowerCase().startsWith(input.toLowerCase()) &&
input.toLowerCase() !== entry.toLowerCase()
)
// Cut off the part that's already been typed.
.map((entry) => entry.substring(selectionStart.value))
// We only want the top 10 suggestions.
.slice(0, 10)
)
})
function updateSuggestions(event: any) {
// Hide suggestions if ESC pressed.
if (event.code && event.code === "Escape") {
@@ -102,18 +111,16 @@ function updateSuggestions(event: any) {
// As suggestions is a reactive property, this implicitly
// causes suggestions to update.
selectionStart.value = acInput.value?.selectionStart ?? -1
suggestionsOffsetLeft.value = 12 * selectionStart.value
suggestionsVisible.value = true
}
const onInput = (e: Event) => {
emit('input', (e.target as HTMLInputElement).value)
emit("input", (e.target as HTMLInputElement).value)
updateSuggestions(e)
}
function forceSuggestion(str: string) {
const input = text.value.substring(0, selectionStart.value)
text.value = input + str
function forceSuggestion(suggestion: string) {
text.value = suggestion
selectionStart.value = text.value.length
suggestionsVisible.value = true
@@ -124,18 +131,9 @@ function forceSuggestion(str: string) {
function handleKeystroke(event: any) {
switch (event.code) {
case "Enter":
event.preventDefault()
if (currentSuggestionIndex.value > -1)
forceSuggestion(
suggestions.value.find(
(_item, index) => index === currentSuggestionIndex.value
)!
)
break
case "ArrowUp":
event.preventDefault()
currentSuggestionIndex.value =
currentSuggestionIndex.value - 1 >= 0
? currentSuggestionIndex.value - 1
@@ -144,53 +142,63 @@ function handleKeystroke(event: any) {
case "ArrowDown":
event.preventDefault()
currentSuggestionIndex.value =
currentSuggestionIndex.value < suggestions.value.length - 1
? currentSuggestionIndex.value + 1
: suggestions.value.length - 1
break
case "Enter":
event.preventDefault()
if (currentSuggestionIndex.value > -1)
forceSuggestion(
suggestions.value.find(
(_item, index) => index === currentSuggestionIndex.value
)!
)
break
case "Tab": {
event.preventDefault()
const activeSuggestion =
suggestions.value[
currentSuggestionIndex.value >= 0 ? currentSuggestionIndex.value : 0
currentSuggestionIndex.value >= 0 ? currentSuggestionIndex.value : 0
]
if (!activeSuggestion) {
return
}
if (!activeSuggestion) return
event.preventDefault()
const input = text.value.substring(0, selectionStart.value)
text.value = input + activeSuggestion
forceSuggestion(activeSuggestion)
break
}
}
}
</script>
<style lang="scss" scoped>
.autocomplete-wrapper {
@apply relative;
@apply contents;
input:focus+ul.suggestions,
input:focus + ul.suggestions,
ul.suggestions:hover {
@apply block;
}
ul.suggestions {
@apply absolute;
@apply hidden;
@apply bg-popover;
@apply absolute;
@apply mx-2;
@apply left-0;
@apply -left-px;
@apply z-50;
@apply shadow-lg;
@apply max-h-46;
@apply overflow-y-auto;
top: calc(100% - 4px);
@apply border-b border-x border-divider;
top: calc(100% + 1px);
border-radius: 0 0 8px 8px;
li {
@@ -198,15 +206,16 @@ function handleKeystroke(event: any) {
@apply block;
@apply py-2 px-4;
@apply text-secondary;
@apply font-semibold;
&:last-child {
border-radius: 0 0 8px 8px;
border-radius: 0 0 0 8px;
}
&:hover,
&.active {
@apply bg-accentDark;
@apply text-accentContrast;
@apply bg-primaryDark;
@apply text-secondaryDark;
@apply cursor-pointer;
}
}