debian-mirror-gitlab/app/assets/javascripts/admin/topics/components/topic_select.vue
2023-04-23 21:23:45 +05:30

129 lines
3 KiB
Vue

<script>
import { GlAvatarLabeled, GlCollapsibleListbox } from '@gitlab/ui';
import { uniqueId } from 'lodash';
import { s__, n__ } from '~/locale';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import searchProjectTopics from '~/graphql_shared/queries/project_topics_search.query.graphql';
export default {
components: {
GlAvatarLabeled,
GlCollapsibleListbox,
},
props: {
selectedTopic: {
type: Object,
required: false,
default: () => ({}),
},
labelText: {
type: String,
required: false,
default: null,
},
},
apollo: {
topics: {
query: searchProjectTopics,
variables() {
return {
search: this.search,
};
},
update(data) {
return data.topics?.nodes || [];
},
debounce: 250,
},
},
data() {
return {
topics: [],
search: '',
selected: null,
};
},
computed: {
loading() {
return this.$apollo.queries.topics.loading;
},
dropdownText() {
if (Object.keys(this.selectedTopic).length) {
return this.selectedTopic.name;
}
return this.$options.i18n.dropdownText;
},
items() {
return this.topics.map(({ id, title, name, avatarUrl }) => ({
value: id,
text: title,
secondaryText: name,
icon: avatarUrl,
}));
},
searchSummary() {
return n__('TopicSelect|%d topic found', 'TopicSelect|%d topics found', this.topics.length);
},
labelId() {
if (!this.labelText) {
return null;
}
return uniqueId('topic-listbox-label-');
},
},
methods: {
onSelect(topicId) {
const topicObj = this.topics.find((topic) => topic.id === topicId);
if (!topicObj) return;
this.$emit('click', topicObj);
},
onSearch(query) {
this.search = query;
},
},
i18n: {
dropdownText: s__('TopicSelect|Select a topic'),
searchPlaceholder: s__('TopicSelect|Search topics'),
emptySearchResult: s__('TopicSelect|No matching results'),
},
AVATAR_SHAPE_OPTION_RECT,
};
</script>
<template>
<div>
<label v-if="labelText" :id="labelId">{{ labelText }}</label>
<gl-collapsible-listbox
v-model="selected"
block
searchable
is-check-centered
:items="items"
:toggle-text="dropdownText"
:searching="loading"
:search-placeholder="$options.i18n.searchPlaceholder"
:no-results-text="$options.i18n.emptySearchResult"
:toggle-aria-labelled-by="labelId"
@select="onSelect"
@search="onSearch"
>
<template #list-item="{ item: { text, secondaryText, icon } }">
<gl-avatar-labeled
:label="text"
:sub-label="secondaryText"
:src="icon"
:entity-name="secondaryText"
:size="32"
:shape="$options.AVATAR_SHAPE_OPTION_RECT"
/>
</template>
<template #search-summary-sr-only>
{{ searchSummary }}
</template>
</gl-collapsible-listbox>
</div>
</template>