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