forked from comses/comses.net
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TaggerField.vue
100 lines (90 loc) · 2.85 KB
/
TaggerField.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<template>
<div>
<slot name="label">
<FieldLabel v-if="label" :label="label" :id-for="id" :required="required" />
</slot>
<FormPlaceholder v-if="showPlaceholder" />
<VueMultiSelect
v-else
v-model="value"
:id="id"
v-bind="attrs"
:multiple="true"
track-by="name"
label="name"
:placeholder="placeholder"
:options="matchingTags"
:loading="isLoading"
:searchable="true"
:internal-search="false"
:clear-on-select="false"
:close-on-select="false"
:options-limit="50"
:taggable="true"
:limit="20"
@tag="addTag"
@search-change="fetchMatchingTags"
:class="{ 'is-invalid': error }"
>
<template #clear v-if="value?.length">
<div class="multiselect__clear">
<span @mousedown.prevent.stop="value = []">×</span>
</div>
</template>
<template #caret="{ toggle }">
<div :class="{ 'multiselect__search-toggle': true, 'd-none': value?.length }">
<i class="fas fa-search" @mousedown.prevent.stop="toggle" />
</div>
</template>
<template #noOptions>No matching tags found.</template>
</VueMultiSelect>
<slot name="help">
<FieldHelp v-if="help" :help="help" :id-for="id" />
</slot>
<slot name="error">
<FieldError v-if="error" :error="error" :id-for="id" />
</slot>
</div>
</template>
<script setup lang="ts">
import { ref, onBeforeMount, inject } from "vue";
import VueMultiSelect from "vue-multiselect";
import { useField } from "@/composables/form";
import FieldLabel from "@/components/form/FieldLabel.vue";
import FieldHelp from "@/components/form/FieldHelp.vue";
import FieldError from "@/components/form/FieldError.vue";
import FormPlaceholder from "@/components/form/FormPlaceholder.vue";
import { useTagsAPI } from "@/composables/api/tags";
import type { Tags, TagType } from "@/types";
interface TaggerFieldProps {
// FIXME: extend from types/BaseFieldProps when vuejs/core#8083 makes it into a release
name: string;
label?: string;
help?: string;
placeholder?: string;
required?: boolean;
type?: TagType;
}
const props = withDefaults(defineProps<TaggerFieldProps>(), {
type: "",
placeholder: "Type to add tags",
});
const { id, value, attrs, error } = useField<Tags>(props, "name");
const showPlaceholder = inject("showPlaceholder", false);
const matchingTags = ref<Tags>([]);
const isLoading = ref(false);
const { search } = useTagsAPI();
onBeforeMount(() => {
// force the inital value to be an empty array
value.value = [];
});
async function fetchMatchingTags(query: string) {
isLoading.value = true;
const response = await search({ query, type: props.type });
matchingTags.value = response.data.results;
isLoading.value = false;
}
async function addTag(name: string) {
value.value.push({ name });
}
</script>