1<script>
2import Tribute from '@gitlab/tributejs';
3import {
4  GfmAutocompleteType,
5  tributeConfig,
6} from 'ee_else_ce/vue_shared/components/gfm_autocomplete/utils';
7import * as Emoji from '~/emoji';
8import createFlash from '~/flash';
9import axios from '~/lib/utils/axios_utils';
10import { __ } from '~/locale';
11import SidebarMediator from '~/sidebar/sidebar_mediator';
12
13export default {
14  errorMessage: __(
15    'An error occurred while getting autocomplete data. Please refresh the page and try again.',
16  ),
17  props: {
18    autocompleteTypes: {
19      type: Array,
20      required: false,
21      default: () => Object.values(GfmAutocompleteType),
22    },
23    dataSources: {
24      type: Object,
25      required: false,
26      default: () => gl.GfmAutoComplete?.dataSources || {},
27    },
28  },
29  computed: {
30    config() {
31      return this.autocompleteTypes.map((type) => ({
32        ...tributeConfig[type].config,
33        loadingItemTemplate: `<span class="gl-spinner gl-vertical-align-text-bottom gl-ml-3 gl-mr-2"></span>${__(
34          'Loading',
35        )}`,
36        requireLeadingSpace: true,
37        values: this.getValues(type),
38      }));
39    },
40  },
41  mounted() {
42    this.cache = {};
43    this.tribute = new Tribute({ collection: this.config });
44
45    const input = this.$slots.default?.[0]?.elm;
46    this.tribute.attach(input);
47  },
48  beforeDestroy() {
49    const input = this.$slots.default?.[0]?.elm;
50    this.tribute.detach(input);
51  },
52  methods: {
53    cacheAssignees() {
54      const isAssigneesLengthSame =
55        this.assignees?.length === SidebarMediator.singleton?.store?.assignees?.length;
56
57      if (!this.assignees || !isAssigneesLengthSame) {
58        this.assignees =
59          SidebarMediator.singleton?.store?.assignees?.map((assignee) => assignee.username) || [];
60      }
61    },
62    filterValues(type) {
63      // The assignees AJAX response can come after the user first invokes autocomplete
64      // so we need to check more than once if we need to update the assignee cache
65      this.cacheAssignees();
66
67      return tributeConfig[type].filterValues
68        ? tributeConfig[type].filterValues({
69            assignees: this.assignees,
70            collection: this.cache[type],
71            fullText: this.$slots.default?.[0]?.elm?.value,
72            selectionStart: this.$slots.default?.[0]?.elm?.selectionStart,
73          })
74        : this.cache[type];
75    },
76    getValues(type) {
77      return (inputText, processValues) => {
78        if (this.cache[type]) {
79          processValues(this.filterValues(type));
80        } else if (type === GfmAutocompleteType.Emojis) {
81          Emoji.initEmojiMap()
82            .then(() => {
83              const emojis = Emoji.getValidEmojiNames();
84              this.cache[type] = emojis;
85              processValues(emojis);
86            })
87            .catch(() => createFlash({ message: this.$options.errorMessage }));
88        } else if (this.dataSources[type]) {
89          axios
90            .get(this.dataSources[type])
91            .then((response) => {
92              this.cache[type] = response.data;
93              processValues(this.filterValues(type));
94            })
95            .catch(() => createFlash({ message: this.$options.errorMessage }));
96        } else {
97          processValues([]);
98        }
99      };
100    },
101  },
102  render(createElement) {
103    return createElement('div', this.$slots.default);
104  },
105};
106</script>
107