@@ -3,50 +3,52 @@ import FilterInput from "@/components/FilterInput.vue";
3
3
import { ref , watch } from " vue" ;
4
4
5
5
const selected = defineModel <string >({ required: true });
6
- const props = defineProps <{ items: string []; instructions: string ; itemName: string ; defaultEmptyText: string ; showClear: boolean ; showFilter: boolean }>();
6
+ const props = withDefaults (
7
+ defineProps <{
8
+ items: string [];
9
+ instructions? : string ;
10
+ itemName: string ;
11
+ defaultEmptyText? : string ;
12
+ canClear? : boolean ;
13
+ showClear: boolean ;
14
+ showFilter: boolean ;
15
+ }>(),
16
+ { canClear: true }
17
+ );
7
18
const filter = ref (" " );
8
19
const filteredItems = ref (props .items );
9
20
10
21
watch ([filter , () => props .items ], () => {
11
- if (filter .value !== " " && filter .value !== null && filter . value !== undefined ) {
22
+ if (filter .value !== " " && filter .value != null ) {
12
23
filteredItems .value = props .items .filter ((item ) => item .toLowerCase ().includes (filter .value .toLowerCase ()));
13
24
} else {
14
25
filteredItems .value = props .items ;
15
26
}
16
27
});
17
28
18
- function onclick(item : string , isSelected : boolean ) {
19
- if (isSelected ) {
20
- selected .value = " " ;
21
- } else {
22
- selected .value = item ;
23
- }
29
+ function setFilter(item : string , isSelected : boolean ) {
30
+ selected .value = isSelected && props .canClear ? " " : item ;
24
31
}
25
32
</script >
26
33
27
34
<template >
28
35
<div class =" dropdown" >
29
36
<button type =" button" aria-label =" open dropdown menu" class =" btn btn-dropdown dropdown-toggle" data-bs-toggle =" dropdown" aria-haspopup =" true" aria-expanded =" false" >
30
- <span class =" wrap-text" >{{ selected ? selected : defaultEmptyText }}</span >
37
+ <span class =" wrap-text" >{{ selected || defaultEmptyText }}</span >
31
38
</button >
32
39
<div class =" dropdown-menu wrapper" >
33
40
<div class =" instructions" >{{ instructions }}</div >
34
41
<div v-if =" showFilter" class =" filter-input" >
35
42
<FilterInput v-model =" filter" :placeholder =" `Filter ${itemName}s`" />
36
43
</div >
37
44
<div class =" items-container" >
38
- <div class =" item-container" v-if =" showClear && selected" >
39
- <svg class =" fa-cross" xmlns =" http://www.w3.org/2000/svg" viewBox =" 0 0 384 512" >
40
- <!-- !Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
41
- <path
42
- d =" M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"
43
- />
44
- </svg >
45
- <a class =" clear" @click.prevent =" () => onclick('', true)" > Clear selected {{ itemName }}</a >
45
+ <div class =" item-container" v-if =" showClear && selected" @click.prevent =" () => setFilter('', true)" >
46
+ <i class =" fa fa-times" />
47
+ <span class =" clear" > Clear selected {{ itemName }}</span >
46
48
</div >
47
- <div class =" item-container" v-for =" item in filteredItems" :key =" item" >
49
+ <div class =" item-container" v-for =" item in filteredItems" :key =" item" @click.prevent = " () => setFilter(item, item === selected) " >
48
50
<i v-if =" item === selected" class =" fa fa-check" />
49
- <a class =" item" :class =" { selected: item === selected }" @click.prevent = " () => onclick(item, item === selected) " > {{ item }}</a >
51
+ <span class =" item" :class =" { selected: item === selected }" > {{ item }}</span >
50
52
</div >
51
53
</div >
52
54
</div >
@@ -55,61 +57,53 @@ function onclick(item: string, isSelected: boolean) {
55
57
56
58
<style scoped>
57
59
.wrap-text {
58
- max-width : 250 px ;
60
+ max-width : 18 em ;
59
61
word-wrap : break-word ;
60
62
}
61
63
.wrapper {
62
- padding : 0.5 em ;
63
- min-width : 200 px ;
64
+ padding : 0.5 rem ;
65
+ min-width : 12.5 rem ;
64
66
}
65
67
.instructions {
66
68
font-weight : bold ;
67
- margin-bottom : 0.5 em ;
69
+ margin-bottom : 0.5 rem ;
68
70
}
69
71
.items-container {
70
- max-height : 300 px ;
71
- max-width : 400 px ;
72
+ max-height : 18 rem ;
73
+ max-width : 25 rem ;
72
74
overflow-y : auto ;
73
75
}
74
76
.item {
75
- margin-left : 20 px ;
76
- font-size : 14 px ;
77
+ font-size : 0.875 rem ;
78
+ margin-left : 1.25 rem ;
77
79
font-weight : 400 ;
78
- cursor : pointer ;
79
80
color : #262626 ;
80
81
word-wrap : break-word ;
81
82
text-decoration : none ;
82
83
width : 100% ;
83
84
}
84
85
.item-container {
85
- padding : 0.3 em 0 ;
86
+ padding : 0.25 rem 0 ;
86
87
display : flex ;
87
88
place-items : center ;
89
+ cursor : pointer ;
88
90
}
89
91
90
92
.item-container :hover {
91
93
background-color : #f5f5f5 ;
92
94
}
93
95
94
96
.filter-input {
95
- margin-bottom : 0.5 em ;
97
+ margin-bottom : 0.5 rem ;
96
98
}
97
99
98
100
.clear {
99
101
color : #262626 ;
100
- font-size : 14px ;
101
102
font-weight : 400 ;
102
- cursor : pointer ;
103
- text-decoration : none ;
104
- margin-left : 6px ;
103
+ margin-left : 0.375rem ;
105
104
}
106
105
107
106
.selected {
108
- margin-left : 6px ;
109
- }
110
-
111
- .fa-cross {
112
- width : 14px ;
113
- height : 14px ;
107
+ margin-left : 0.375rem ;
114
108
}
115
109
</style >
0 commit comments