|
1 | 1 | <script setup> |
| 2 | +import { ref, watch } from "vue"; |
2 | 3 | import { Tag } from "primevue"; |
3 | 4 | import { Icon } from "@iconify/vue"; |
4 | | -import { ref } from "vue"; |
5 | 5 | import AutoComplete from "primevue/autocomplete"; |
6 | | -import { defineModel, defineProps } from "vue"; |
| 6 | +import { defineModel } from "vue"; |
7 | 7 | import axios from "axios"; |
8 | 8 |
|
9 | | -const props = defineProps({ |
10 | | - initial: { |
11 | | - type: Array, |
12 | | - required: true, |
13 | | - }, |
14 | | -}); |
15 | | -
|
16 | | -const model = defineModel() |
| 9 | +const model = defineModel(); // v-model from parent |
17 | 10 |
|
18 | | -const selectedTags = ref(new Set(props.initial)); |
| 11 | +const selectedTags = ref([]); |
19 | 12 | const searchValue = ref(""); |
20 | 13 | const tagResult = ref([]); |
21 | 14 | const tagCount = ref({}); |
22 | 15 | const emptySearchMessage = ref(""); |
23 | 16 |
|
| 17 | +// Sync initial model value to internal state |
| 18 | +watch( |
| 19 | + () => model.value, |
| 20 | + (newVal) => { |
| 21 | + if (Array.isArray(newVal)) { |
| 22 | + selectedTags.value = [...newVal]; |
| 23 | + } |
| 24 | + }, |
| 25 | + { immediate: true } |
| 26 | +); |
| 27 | +
|
24 | 28 | const addTag = (tag) => { |
25 | | - if (tag && !selectedTags.value.has(tag)) { |
26 | | - selectedTags.value.add(tag); |
| 29 | + if (tag && !selectedTags.value.includes(tag)) { |
| 30 | + selectedTags.value.push(tag); |
27 | 31 | searchValue.value = ""; |
28 | | - model.value = Array.from(selectedTags.value); |
| 32 | + model.value = [...selectedTags.value]; |
29 | 33 | } |
30 | 34 | }; |
31 | 35 |
|
32 | 36 | const removeTag = (tag) => { |
33 | 37 | if (tag) { |
34 | | - selectedTags.value.delete(tag); |
35 | | - model.value = Array.from(selectedTags.value); |
| 38 | + selectedTags.value = selectedTags.value.filter((t) => t !== tag); |
| 39 | + model.value = [...selectedTags.value]; |
36 | 40 | } |
37 | 41 | }; |
38 | 42 |
|
@@ -70,38 +74,36 @@ const filterSuggestions = () => { |
70 | 74 | </script> |
71 | 75 |
|
72 | 76 | <template> |
73 | | - <div class="flex col-auto gap-3 flex-col justify-center items-center"> |
74 | | - <!-- Search bar to add tags --> |
75 | | - <AutoComplete |
76 | | - v-model="searchValue" |
77 | | - :suggestions="tagResult" |
78 | | - :empty-search-message="emptySearchMessage" |
79 | | - @complete="filterSuggestions" |
80 | | - @item-select="handleSelect" |
81 | | - @keydown="handleKeydown" |
82 | | - placeholder="Type to add tags" |
83 | | - completeOnFocus |
84 | | - > |
85 | | - <template #option="slotProps"> |
86 | | - <div class="flex items-center justify-between w-full"> |
87 | | - <span>{{ slotProps.option }}</span> |
88 | | - <span |
89 | | - v-if="tagCount[slotProps.option] !== undefined" |
90 | | - class="py-0.5 px-1 rounded-lg bg-gray-100 text-sm text-gray-700" |
91 | | - > |
92 | | - {{ tagCount[slotProps.option] }} |
93 | | - </span> |
94 | | - </div> |
95 | | - </template> |
96 | | - </AutoComplete> |
97 | | - <!-- List of tags --> |
98 | | - <div class="mt-2"> |
99 | | - <Tag v-for="tag in selectedTags" :key="tag" class="mr-2 mb-2"> |
100 | | - <button @click="() => removeTag(tag)" class="mr-1"> |
101 | | - <Icon icon="mdi:remove-bold" /> |
102 | | - </button> |
103 | | - <span class="text-base">{{ tag }}</span> |
104 | | - </Tag> |
105 | | - </div> |
| 77 | + <!-- Search bar to add tags --> |
| 78 | + <AutoComplete |
| 79 | + v-model="searchValue" |
| 80 | + :suggestions="tagResult" |
| 81 | + :empty-search-message="emptySearchMessage" |
| 82 | + @complete="filterSuggestions" |
| 83 | + @item-select="handleSelect" |
| 84 | + @keydown="handleKeydown" |
| 85 | + placeholder="Type to add tags" |
| 86 | + completeOnFocus |
| 87 | + > |
| 88 | + <template #option="slotProps"> |
| 89 | + <div class="flex items-center justify-between w-full"> |
| 90 | + <span>{{ slotProps.option }}</span> |
| 91 | + <span |
| 92 | + v-if="tagCount[slotProps.option] !== undefined" |
| 93 | + class="py-0.5 px-1 rounded-lg bg-gray-100 text-sm text-gray-700" |
| 94 | + > |
| 95 | + {{ tagCount[slotProps.option] }} |
| 96 | + </span> |
| 97 | + </div> |
| 98 | + </template> |
| 99 | + </AutoComplete> |
| 100 | + <!-- List of tags --> |
| 101 | + <div class="mt-2"> |
| 102 | + <Tag v-for="tag in selectedTags" :key="tag" class="mr-2 mb-2"> |
| 103 | + <button @click="() => removeTag(tag)" class="mr-1"> |
| 104 | + <Icon icon="mdi:remove-bold" /> |
| 105 | + </button> |
| 106 | + <span class="text-base">{{ tag }}</span> |
| 107 | + </Tag> |
106 | 108 | </div> |
107 | 109 | </template> |
0 commit comments