Skip to content

Commit 8ca16ef

Browse files
authored
feat: add tag selection component (#217)
1 parent 6d5ff55 commit 8ca16ef

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

frontend/assets/cross.svg

Lines changed: 3 additions & 0 deletions
Loading

frontend/assets/searchicon.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<template>
2+
<div class="flex flex-col space-y-2 w-96">
3+
<div
4+
class="relative flex justify-between items-start flex-col sm:flex-row sm:h-16 h-20 w-full"
5+
>
6+
<div class="px-5 sm:px-2">
7+
<h1 class="font-normal poppins text-2xl text-[#6C6C6C]">Tags</h1>
8+
</div>
9+
<div class="relative h-[36px] w-[251px] mb-1 sm:mb-0">
10+
<!-- Input field for adding new tags -->
11+
<input
12+
type="text"
13+
v-model="input"
14+
@keydown.enter.prevent="handleEnter"
15+
@focus="isFocused = true"
16+
@blur="isFocused = false"
17+
placeholder="search for tags here"
18+
class="h-[36px] w-[251px] poppins text-sm font-normal px-5 rounded-full searchbar focus:outline-none focus:ring-2 focus:ring-blue-500"
19+
/>
20+
<img
21+
src="../assets/searchicon.svg"
22+
class="absolute top-[25%] right-[10%]"
23+
alt="searchicon"
24+
/>
25+
26+
<!-- Suggestions dropdown -->
27+
<ul
28+
v-if="suggestions.length && isFocused"
29+
class="absolute mt-1 border-2 border-[#808080] rounded-b-3xl bg-white max-h-48 overflow-y-auto w-[251px] z-1 mt-5 py-2 px-4 poppins"
30+
>
31+
<li
32+
v-for="(suggestion, index) in suggestions"
33+
:key="index"
34+
@mousedown.prevent="selectSuggestion(suggestion)"
35+
class="py-1 px-2 cursor-pointer hover:bg-gray-100 text-black rounded-md"
36+
>
37+
{{ suggestion }}
38+
</li>
39+
</ul>
40+
</div>
41+
</div>
42+
43+
<!-- Display selected tags -->
44+
<div class="flex flex-wrap gap-3 mb-2 poppins">
45+
<span
46+
v-for="(tag, index) in selectedTags"
47+
:key="index"
48+
class="text-[#808080] h-[36px] px-3 ml-1 flex items-center bg-transparent border-[2.5px] border-[#808080] rounded-full text-xs shadow"
49+
>
50+
{{ tag }}
51+
<button
52+
@click="removeTag(index)"
53+
class="ml-7 hover:text-red-800 h-4 w-4 flex justify-end items-center"
54+
>
55+
<img src="../assets/cross.svg" alt="removetag" />
56+
</button>
57+
</span>
58+
</div>
59+
</div>
60+
</template>
61+
62+
<script setup>
63+
import { ref, computed } from "vue";
64+
65+
const props = defineProps({
66+
availableTags: {
67+
type: Array,
68+
default: () => [],
69+
},
70+
initialTags: {
71+
type: Array,
72+
default: () => [],
73+
},
74+
});
75+
76+
const emit = defineEmits(["update:tags"]);
77+
78+
const input = ref("");
79+
const isFocused = ref(false);
80+
const selectedTags = ref([...props.initialTags]);
81+
82+
const suggestions = computed(() => {
83+
if (!input.value) return [];
84+
const lowercaseInput = input.value.toLowerCase();
85+
return props.availableTags
86+
.filter(
87+
(tag) =>
88+
tag.toLowerCase().includes(lowercaseInput) &&
89+
!selectedTags.value.includes(tag)
90+
)
91+
.slice(0, 3);
92+
});
93+
94+
const handleEnter = () => {
95+
const tag = input.value.trim();
96+
if (tag) {
97+
addTag(tag);
98+
input.value = "";
99+
}
100+
};
101+
102+
const addTag = (tag) => {
103+
if (!selectedTags.value.includes(tag)) {
104+
selectedTags.value.push(tag);
105+
emit("update:tags", selectedTags.value);
106+
}
107+
};
108+
109+
const removeTag = (index) => {
110+
selectedTags.value.splice(index, 1);
111+
emit("update:tags", selectedTags.value);
112+
};
113+
114+
const selectSuggestion = (suggestion) => {
115+
addTag(suggestion);
116+
input.value = "";
117+
};
118+
</script>
119+
120+
<style scoped>
121+
.searchbar {
122+
box-shadow: 0px 0px 8px 1.5px rgb(0 0 0 / 0.1);
123+
}
124+
.poppins {
125+
font-family: "Poppins", sans-serif;
126+
font-smooth: always;
127+
}
128+
</style>

0 commit comments

Comments
 (0)