-
-
Notifications
You must be signed in to change notification settings - Fork 123
/
Copy pathMultiselect.vue
120 lines (108 loc) · 3.18 KB
/
Multiselect.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<template>
<div ref="menu" class="form-control w-full">
<label class="label">
<span class="label-text">{{ label }}</span>
</label>
<div class="dropdown dropdown-top sm:dropdown-end">
<div tabindex="0" class="flex min-h-[48px] w-full flex-wrap gap-2 rounded-lg border border-gray-400 p-4">
<span v-for="itm in value" :key="itm.id" class="badge">
{{ itm.name }}
</span>
<button
v-if="value.length > 0"
type="button"
class="absolute inset-y-0 right-6 flex items-center rounded-r-md px-2 focus:outline-none"
@click="clear"
>
<MdiClose class="size-5" />
</button>
</div>
<div
tabindex="0"
style="display: inline"
class="dropdown-content menu bg-base-100 z-[9999] mb-1 w-full rounded border border-gray-400 shadow"
>
<div class="m-2">
<input v-model="search" placeholder="Search…" class="input input-bordered input-sm w-full" />
</div>
<ul class="max-h-60 overflow-y-scroll">
<li
v-for="(obj, idx) in filteredItems"
:key="idx"
:class="{
bordered: selected.includes(obj.id),
}"
>
<button type="button" @click="toggle(obj.id)">
{{ obj.name }}
</button>
</li>
<li v-if="!filteredItems.some(itm => itm.name === search) && search.length > 0">
<button type="button" @click="createAndAdd(search)">{{ $t("global.create") }} {{ search }}</button>
</li>
</ul>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import MdiClose from "~icons/mdi/close";
const emit = defineEmits(["update:modelValue"]);
const props = defineProps({
label: {
type: String,
default: "",
},
modelValue: {
type: Array as () => any[],
default: null,
},
items: {
type: Array as () => any[],
required: true,
},
selectFirst: {
type: Boolean,
default: false,
},
});
const value = useVModel(props, "modelValue", emit);
const search = ref("");
const filteredItems = computed(() => {
if (!search.value) {
return props.items;
}
return props.items.filter(item => {
return item.name.toLowerCase().includes(search.value.toLowerCase());
});
});
function clear() {
value.value = [];
}
const selected = computed<string[]>(() => {
return value.value.map(itm => itm.id);
});
function toggle(uniqueField: string) {
const item = props.items.find(itm => itm.id === uniqueField);
if (selected.value.includes(item.id)) {
value.value = value.value.filter(itm => itm.id !== item.id);
} else {
value.value = [...value.value, item];
}
}
const api = useUserApi();
const toast = useNotifier();
async function createAndAdd(name: string) {
const { error, data } = await api.labels.create({
name,
color: "", // Future!
description: "",
});
if (error) {
console.error(error);
toast.error(`Failed to create label: ${name}`);
} else {
value.value = [...value.value, data];
}
}
</script>