Skip to content

Commit 15993a6

Browse files
Merge pull request #7000 from christianbeeznest/GH-6958
Course: Add searchselect component in coursecopy form
2 parents d665b75 + 0aba7a2 commit 15993a6

File tree

2 files changed

+332
-73
lines changed

2 files changed

+332
-73
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<template>
2+
<div class="w-full">
3+
<FloatLabel variant="on">
4+
<Dropdown
5+
v-model="innerValue"
6+
:options="localOptions"
7+
:loading="loading"
8+
:showClear="clearable"
9+
:filter="true"
10+
:filterFields="filterFields"
11+
:virtualScrollerOptions="virtual ? { itemSize: 38, showLoader: true } : null"
12+
:emptyMessage="emptyMessage"
13+
:placeholder="placeholder"
14+
optionLabel="label"
15+
optionValue="id"
16+
class="w-full"
17+
panelClass="cm-searchselect-panel"
18+
@change="emitChange"
19+
>
20+
<!-- Item template: label on one line, optional sublabel on second line -->
21+
<template #option="slotProps">
22+
<div class="flex flex-col leading-tight">
23+
<span class="text-sm text-gray-90">{{ slotProps.option.label }}</span>
24+
<span
25+
v-if="slotProps.option.sublabel"
26+
class="text-xs text-gray-50"
27+
>
28+
{{ slotProps.option.sublabel }}
29+
</span>
30+
</div>
31+
</template>
32+
</Dropdown>
33+
<label :for="inputId">{{ label }}</label>
34+
</FloatLabel>
35+
36+
<small
37+
v-if="hint"
38+
class="mt-1 block text-xs text-gray-50"
39+
>{{ hint }}</small
40+
>
41+
<small
42+
v-if="errorText"
43+
class="mt-1 block text-xs text-danger"
44+
>{{ errorText }}</small
45+
>
46+
</div>
47+
</template>
48+
49+
<script setup>
50+
import { ref, watch } from "vue"
51+
import FloatLabel from "primevue/floatlabel"
52+
import Dropdown from "primevue/dropdown"
53+
54+
const props = defineProps({
55+
modelValue: { type: [String, Number], default: "" },
56+
options: { type: Array, default: () => [] }, // [{ id, label, sublabel?, payload? }]
57+
label: { type: String, default: "Select an option" },
58+
placeholder: { type: String, default: "Search…" },
59+
inputId: { type: String, default: "searchable-select" },
60+
loading: { type: Boolean, default: false },
61+
clearable: { type: Boolean, default: true },
62+
virtual: { type: Boolean, default: true },
63+
filterFields: { type: Array, default: () => ["label", "sublabel"] },
64+
emptyMessage: { type: String, default: "No matches found." },
65+
hint: { type: String, default: "" },
66+
errorText: { type: String, default: "" },
67+
})
68+
69+
const emit = defineEmits(["update:modelValue", "change"])
70+
71+
const innerValue = ref(props.modelValue)
72+
const localOptions = ref([...props.options])
73+
74+
watch(
75+
() => props.modelValue,
76+
(v) => (innerValue.value = v),
77+
)
78+
watch(
79+
() => props.options,
80+
(v) => (localOptions.value = [...v]),
81+
)
82+
83+
function emitChange() {
84+
emit("update:modelValue", innerValue.value)
85+
emit("change", innerValue.value)
86+
}
87+
</script>
88+
89+
<style scoped>
90+
:deep(.p-dropdown) {
91+
@apply w-full rounded border border-gray-25 text-sm;
92+
}
93+
:deep(.p-dropdown .p-inputtext) {
94+
@apply text-sm;
95+
}
96+
:deep(.cm-searchselect-panel) {
97+
@apply max-h-80;
98+
}
99+
</style>

0 commit comments

Comments
 (0)