refactor: polishing of admin dashboard teams module (#64)
Co-authored-by: Anwarul Islam <anwaarulislaam@gmail.com>
This commit is contained in:
committed by
GitHub
parent
ea847d7d32
commit
e27dc1f7a2
@@ -1,13 +1,9 @@
|
||||
<template>
|
||||
<div class="autocomplete-wrapper">
|
||||
<input ref="acInput" :value="text" type="text" autocomplete="off" :placeholder="placeholder" :spellcheck="spellcheck"
|
||||
:autocapitalize="autocapitalize" :class="styles" @input.stop="
|
||||
(e) => {
|
||||
$emit('input', e.target.value)
|
||||
updateSuggestions(e)
|
||||
}
|
||||
" @keyup="updateSuggestions" @click="updateSuggestions" @keydown="handleKeystroke"
|
||||
@change="$emit('change', $event)" />
|
||||
<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}`"
|
||||
@@ -18,169 +14,162 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue"
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, computed } from "vue"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
spellcheck: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
const acInput = ref<HTMLInputElement>()
|
||||
|
||||
autocapitalize: {
|
||||
type: String,
|
||||
default: "off",
|
||||
required: false,
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false,
|
||||
},
|
||||
|
||||
source: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false,
|
||||
},
|
||||
|
||||
styles: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ["input", "change"],
|
||||
data() {
|
||||
return {
|
||||
text: this.value,
|
||||
selectionStart: 0,
|
||||
suggestionsOffsetLeft: 0,
|
||||
currentSuggestionIndex: -1,
|
||||
suggestionsVisible: false,
|
||||
}
|
||||
const props = defineProps({
|
||||
spellcheck: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Gets the suggestions list to be displayed under the input box.
|
||||
*
|
||||
* @returns {default.props.source|{type, required}}
|
||||
*/
|
||||
suggestions() {
|
||||
const input = this.text.substring(0, this.selectionStart)
|
||||
autocapitalize: {
|
||||
type: String,
|
||||
default: "off",
|
||||
required: false,
|
||||
},
|
||||
|
||||
return (
|
||||
this.source
|
||||
.filter(
|
||||
(entry) =>
|
||||
entry.toLowerCase().startsWith(input.toLowerCase()) &&
|
||||
input.toLowerCase() !== entry.toLowerCase()
|
||||
)
|
||||
// Cut off the part that's already been typed.
|
||||
.map((entry) => entry.substring(this.selectionStart))
|
||||
// We only want the top 10 suggestions.
|
||||
.slice(0, 10)
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false,
|
||||
},
|
||||
|
||||
source: {
|
||||
type: Array<string>,
|
||||
required: true,
|
||||
},
|
||||
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false,
|
||||
},
|
||||
|
||||
styles: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", v: string): void
|
||||
(e: "change", v: Event): void
|
||||
}>()
|
||||
|
||||
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,
|
||||
})
|
||||
})
|
||||
|
||||
const suggestions = computed(() => {
|
||||
const input = text.value.substring(0, selectionStart.value)
|
||||
return (
|
||||
props.source
|
||||
.filter(
|
||||
(entry) =>
|
||||
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)
|
||||
)
|
||||
})
|
||||
|
||||
watch: {
|
||||
value(newValue) {
|
||||
this.text = newValue
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.updateSuggestions({
|
||||
target: this.$refs.acInput,
|
||||
})
|
||||
},
|
||||
function updateSuggestions(event: any) {
|
||||
// Hide suggestions if ESC pressed.
|
||||
if (event.code && event.code === "Escape") {
|
||||
event.preventDefault()
|
||||
suggestionsVisible.value = false
|
||||
currentSuggestionIndex.value = -1
|
||||
return
|
||||
}
|
||||
|
||||
methods: {
|
||||
updateSuggestions(event) {
|
||||
// Hide suggestions if ESC pressed.
|
||||
if (event.code && event.code === "Escape") {
|
||||
event.preventDefault()
|
||||
this.suggestionsVisible = false
|
||||
this.currentSuggestionIndex = -1
|
||||
// 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)
|
||||
updateSuggestions(e)
|
||||
}
|
||||
|
||||
function forceSuggestion(str: string) {
|
||||
const input = text.value.substring(0, selectionStart.value)
|
||||
text.value = input + str
|
||||
|
||||
selectionStart.value = text.value.length
|
||||
suggestionsVisible.value = true
|
||||
currentSuggestionIndex.value = -1
|
||||
|
||||
emit("input", text.value)
|
||||
}
|
||||
|
||||
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
|
||||
: 0
|
||||
break
|
||||
|
||||
case "ArrowDown":
|
||||
event.preventDefault()
|
||||
currentSuggestionIndex.value =
|
||||
currentSuggestionIndex.value < suggestions.value.length - 1
|
||||
? currentSuggestionIndex.value + 1
|
||||
: suggestions.value.length - 1
|
||||
break
|
||||
|
||||
case "Tab": {
|
||||
const activeSuggestion =
|
||||
suggestions.value[
|
||||
currentSuggestionIndex.value >= 0 ? currentSuggestionIndex.value : 0
|
||||
]
|
||||
|
||||
if (!activeSuggestion) {
|
||||
return
|
||||
}
|
||||
|
||||
// As suggestions is a reactive property, this implicitly
|
||||
// causes suggestions to update.
|
||||
this.selectionStart = this.$refs.acInput.selectionStart
|
||||
this.suggestionsOffsetLeft = 12 * this.selectionStart
|
||||
this.suggestionsVisible = true
|
||||
},
|
||||
|
||||
forceSuggestion(text) {
|
||||
const input = this.text.substring(0, this.selectionStart)
|
||||
this.text = input + text
|
||||
|
||||
this.selectionStart = this.text.length
|
||||
this.suggestionsVisible = true
|
||||
this.currentSuggestionIndex = -1
|
||||
|
||||
this.$emit("input", this.text)
|
||||
},
|
||||
|
||||
handleKeystroke(event) {
|
||||
switch (event.code) {
|
||||
case "Enter":
|
||||
event.preventDefault()
|
||||
if (this.currentSuggestionIndex > -1)
|
||||
this.forceSuggestion(
|
||||
this.suggestions.find(
|
||||
(_item, index) => index === this.currentSuggestionIndex
|
||||
)
|
||||
)
|
||||
break
|
||||
|
||||
case "ArrowUp":
|
||||
event.preventDefault()
|
||||
this.currentSuggestionIndex =
|
||||
this.currentSuggestionIndex - 1 >= 0
|
||||
? this.currentSuggestionIndex - 1
|
||||
: 0
|
||||
break
|
||||
|
||||
case "ArrowDown":
|
||||
event.preventDefault()
|
||||
this.currentSuggestionIndex =
|
||||
this.currentSuggestionIndex < this.suggestions.length - 1
|
||||
? this.currentSuggestionIndex + 1
|
||||
: this.suggestions.length - 1
|
||||
break
|
||||
|
||||
case "Tab": {
|
||||
const activeSuggestion =
|
||||
this.suggestions[
|
||||
this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0
|
||||
]
|
||||
|
||||
if (!activeSuggestion) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
const input = this.text.substring(0, this.selectionStart)
|
||||
this.text = input + activeSuggestion
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
event.preventDefault()
|
||||
const input = text.value.substring(0, selectionStart.value)
|
||||
text.value = input + activeSuggestion
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.autocomplete-wrapper {
|
||||
@apply relative;
|
||||
|
||||
Reference in New Issue
Block a user