Skip to content

Commit 45b0e38

Browse files
committed
Convert career select to combo search dropdown
1 parent c1c0833 commit 45b0e38

1 file changed

Lines changed: 106 additions & 20 deletions

File tree

RP1AnalyticsWebApp/src/components/CareerSelect.vue

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,73 @@
11
<template>
2-
<div class="control has-icons-left">
3-
<div class="select is-rounded">
4-
<select @change="careerChanged($event.target.value)" class="browser-default" :value="selectedCareer">
5-
<option value="Select a career" disabled="" selected="">Select a career</option>
6-
<optgroup v-for="[key, value] in items" :label="key">
7-
<option v-for="option in value" :value="option.id">
8-
{{ option.name }}
9-
</option>
10-
</optgroup>
11-
</select>
2+
<div class="combo-box control dropdown is-expanded is-rounded" :class="{ 'is-active': isOpen, 'is-loading': isLoading }">
3+
<div class="dropdown-trigger">
4+
<div class="control has-icons-left has-icons-right">
5+
<input type="text"
6+
v-model="inputText"
7+
@focus="isOpen = true"
8+
placeholder="Select a career"
9+
class="combo-input input is-rounded" />
10+
<div class="icon is-small is-left">
11+
<i class="fas fa-database"></i>
12+
</div>
13+
<span v-if="!isLoading" class="icon is-small is-right">
14+
<i class="fas fa-angle-down" aria-hidden="true"></i>
15+
</span>
16+
</div>
1217
</div>
13-
<div class="icon is-small is-left">
14-
<i class="fas fa-database"></i>
18+
19+
<div v-if="isOpen" class="combo-dropdown dropdown-menu" role="menu">
20+
<div class="dropdown-content">
21+
<template v-for="[key, value] in filteredGroups" :key="key">
22+
<div class="combo-group-label dropdown-item">
23+
{{ key }}
24+
</div>
25+
26+
<button v-for="option in value"
27+
:key="option.id"
28+
@click="selectValue(option)"
29+
class="combo-option dropdown-item">
30+
{{ option.name }}
31+
</button>
32+
</template>
33+
34+
<div v-if="filteredGroups.size === 0" class="dropdown-item">
35+
No results
36+
</div>
37+
</div>
1538
</div>
1639
</div>
1740
</template>
1841

42+
<style scoped>
43+
.combo-box input {
44+
min-width: 20rem;
45+
}
46+
47+
.combo-group-label {
48+
font-weight: bold;
49+
}
50+
51+
.combo-group-label:not(:first-child) {
52+
margin-top: 6px;
53+
}
54+
55+
.combo-option {
56+
padding-left: 1.5rem;
57+
}
58+
</style>
59+
1960
<script lang="ts">
2061
import { defineComponent } from 'vue';
2162
import type { PropType } from 'vue'
2263
import type { CareerListItem, Filters } from 'types';
2364
import { fetchCareerListItems } from '../utils/api';
2465
2566
interface ComponentData {
26-
items: Map<string, CareerListItem[]> | null;
67+
items: CareerListItem[] | null;
2768
isLoading: boolean;
69+
isOpen: boolean;
70+
inputText: string;
2871
}
2972
3073
export default defineComponent({
@@ -40,25 +83,37 @@
4083
data(): ComponentData {
4184
return {
4285
items: null,
43-
isLoading: false
86+
isLoading: false,
87+
isOpen: false,
88+
inputText: ''
4489
};
4590
},
4691
methods: {
4792
async queryData(filters: Filters) {
4893
this.isLoading = true;
4994
try {
5095
const arr = await fetchCareerListItems(filters);
51-
const groupedMap = arr.reduce(
52-
(entryMap, e) => entryMap.set(this.getPlayerName(e), [...entryMap.get(this.getPlayerName(e)) || [], e]),
53-
new Map<string, CareerListItem[]>()
54-
);
55-
56-
this.items = groupedMap;
96+
this.items = arr;
97+
this.updateInputText();
5798
}
5899
finally {
59100
this.isLoading = false;
60101
}
61102
},
103+
selectValue(value) {
104+
this.inputText = value.name;
105+
this.isOpen = false;
106+
this.careerChanged(value.id);
107+
},
108+
updateInputText() {
109+
const selItem = this.items.find(i => i.id === this.selectedCareer);
110+
this.inputText = selItem?.name ?? '';
111+
},
112+
onClickOutside(e) {
113+
if (!e.target.closest(".combo-box")) {
114+
this.isOpen = false;
115+
}
116+
},
62117
careerChanged(careerId: string) {
63118
this.$emit('update:selectedCareer', careerId);
64119
this.$emit('careerChanged', careerId);
@@ -67,12 +122,43 @@
67122
return entry.userPreferredName ? entry.userPreferredName : entry.user;
68123
}
69124
},
125+
computed: {
126+
canEdit(): boolean {
127+
return this.career != null && currentUser != null && this.career.userLogin === currentUser.userName;
128+
},
129+
filteredGroups() {
130+
let items = this.items;
131+
if (!items) return new Map<string, CareerListItem[]>();
132+
133+
const text = this.inputText.toLowerCase();
134+
if (text.length > 0) {
135+
items = this.items.filter(i => this.getPlayerName(i).toLowerCase().includes(text) ||
136+
i.name.toLowerCase().includes(text));
137+
}
138+
139+
const groupedMap = items.reduce(
140+
(entryMap, e) => entryMap.set(this.getPlayerName(e), [...entryMap.get(this.getPlayerName(e)) || [], e]),
141+
new Map<string, CareerListItem[]>()
142+
);
143+
144+
return groupedMap;
145+
}
146+
},
70147
mounted() {
71148
this.$nextTick(function () {
72149
this.queryData(this.filters);
73150
});
151+
document.addEventListener('click', this.onClickOutside);
152+
},
153+
beforeUnmount() {
154+
document.removeEventListener('click', this.onClickOutside);
74155
},
75156
watch: {
157+
selectedCareer() {
158+
if (this.items) {
159+
this.updateInputText();
160+
}
161+
},
76162
filters: {
77163
handler() {
78164
this.queryData(this.filters);

0 commit comments

Comments
 (0)