diff options
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/gfm_autocomplete')
-rw-r--r-- | app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue | 15 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js | 69 |
2 files changed, 73 insertions, 11 deletions
diff --git a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue index 1ad0ca36bf8..9ab91e567e6 100644 --- a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue +++ b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue @@ -4,6 +4,7 @@ import { GfmAutocompleteType, tributeConfig, } from 'ee_else_ce/vue_shared/components/gfm_autocomplete/utils'; +import * as Emoji from '~/emoji'; import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; @@ -27,7 +28,7 @@ export default { }, computed: { config() { - return this.autocompleteTypes.map(type => ({ + return this.autocompleteTypes.map((type) => ({ ...tributeConfig[type].config, loadingItemTemplate: `<span class="gl-spinner gl-vertical-align-text-bottom gl-ml-3 gl-mr-2"></span>${__( 'Loading', @@ -55,7 +56,7 @@ export default { if (!this.assignees || !isAssigneesLengthSame) { this.assignees = - SidebarMediator.singleton?.store?.assignees?.map(assignee => assignee.username) || []; + SidebarMediator.singleton?.store?.assignees?.map((assignee) => assignee.username) || []; } }, filterValues(type) { @@ -76,10 +77,18 @@ export default { return (inputText, processValues) => { if (this.cache[type]) { processValues(this.filterValues(type)); + } else if (type === GfmAutocompleteType.Emojis) { + Emoji.initEmojiMap() + .then(() => { + const emojis = Emoji.getValidEmojiNames(); + this.cache[type] = emojis; + processValues(emojis); + }) + .catch(() => createFlash({ message: this.$options.errorMessage })); } else if (this.dataSources[type]) { axios .get(this.dataSources[type]) - .then(response => { + .then((response) => { this.cache[type] = response.data; processValues(this.filterValues(type)); }) diff --git a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js index 2581888b504..809932b0f29 100644 --- a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js +++ b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js @@ -1,16 +1,24 @@ import { escape, last } from 'lodash'; +import * as Emoji from '~/emoji'; import { spriteIcon } from '~/lib/utils/common_utils'; const groupType = 'Group'; // eslint-disable-line @gitlab/require-i18n-strings +// Number of users to show in the autocomplete menu to avoid doing a mass fetch of 100+ avatars +const memberLimit = 10; + const nonWordOrInteger = /\W|^\d+$/; +export const menuItemLimit = 100; + export const GfmAutocompleteType = { + Emojis: 'emojis', Issues: 'issues', Labels: 'labels', Members: 'members', MergeRequests: 'mergeRequests', Milestones: 'milestones', + QuickActions: 'commands', Snippets: 'snippets', }; @@ -21,10 +29,21 @@ function doesCurrentLineStartWith(searchString, fullText, selectionStart) { } export const tributeConfig = { + [GfmAutocompleteType.Emojis]: { + config: { + trigger: ':', + lookup: (value) => value, + menuItemLimit, + menuItemTemplate: ({ original }) => `${original} ${Emoji.glEmojiTag(original)}`, + selectTemplate: ({ original }) => `:${original}:`, + }, + }, + [GfmAutocompleteType.Issues]: { config: { trigger: '#', - lookup: value => `${value.iid}${value.title}`, + lookup: (value) => `${value.iid}${value.title}`, + menuItemLimit, menuItemTemplate: ({ original }) => `<small>${original.reference || original.iid}</small> ${escape(original.title)}`, selectTemplate: ({ original }) => original.reference || `#${original.iid}`, @@ -35,6 +54,7 @@ export const tributeConfig = { config: { trigger: '~', lookup: 'title', + menuItemLimit, menuItemTemplate: ({ original }) => ` <span class="dropdown-label-box" style="background: ${escape(original.color)};"></span> ${escape(original.title)}`, @@ -45,11 +65,11 @@ export const tributeConfig = { }, filterValues({ collection, fullText, selectionStart }) { if (doesCurrentLineStartWith('/label', fullText, selectionStart)) { - return collection.filter(label => !label.set); + return collection.filter((label) => !label.set); } if (doesCurrentLineStartWith('/unlabel', fullText, selectionStart)) { - return collection.filter(label => label.set); + return collection.filter((label) => label.set); } return collection; @@ -60,8 +80,9 @@ export const tributeConfig = { config: { trigger: '@', fillAttr: 'username', - lookup: value => + lookup: (value) => value.type === groupType ? last(value.name.split(' / ')) : `${value.name}${value.username}`, + menuItemLimit: memberLimit, menuItemTemplate: ({ original }) => { const commonClasses = 'gl-avatar gl-avatar-s24 gl-flex-shrink-0'; const noAvatarClasses = `${commonClasses} gl-rounded-small @@ -101,11 +122,11 @@ export const tributeConfig = { }, filterValues({ assignees, collection, fullText, selectionStart }) { if (doesCurrentLineStartWith('/assign', fullText, selectionStart)) { - return collection.filter(member => !assignees.includes(member.username)); + return collection.filter((member) => !assignees.includes(member.username)); } if (doesCurrentLineStartWith('/unassign', fullText, selectionStart)) { - return collection.filter(member => assignees.includes(member.username)); + return collection.filter((member) => assignees.includes(member.username)); } return collection; @@ -115,7 +136,8 @@ export const tributeConfig = { [GfmAutocompleteType.MergeRequests]: { config: { trigger: '!', - lookup: value => `${value.iid}${value.title}`, + lookup: (value) => `${value.iid}${value.title}`, + menuItemLimit, menuItemTemplate: ({ original }) => `<small>${original.reference || original.iid}</small> ${escape(original.title)}`, selectTemplate: ({ original }) => original.reference || `!${original.iid}`, @@ -126,16 +148,47 @@ export const tributeConfig = { config: { trigger: '%', lookup: 'title', + menuItemLimit, menuItemTemplate: ({ original }) => escape(original.title), selectTemplate: ({ original }) => `%"${escape(original.title)}"`, }, }, + [GfmAutocompleteType.QuickActions]: { + config: { + trigger: '/', + fillAttr: 'name', + lookup: (value) => `${value.name}${value.aliases.join()}`, + menuItemLimit, + menuItemTemplate: ({ original }) => { + const aliases = original.aliases.length + ? `<small>(or /${original.aliases.join(', /')})</small>` + : ''; + + const params = original.params.length ? `<small>${original.params.join(' ')}</small>` : ''; + + let description = ''; + + if (original.warning) { + const confidentialIcon = + original.icon === 'confidential' ? spriteIcon('eye-slash', 's16 gl-mr-2') : ''; + description = `<small>${confidentialIcon}<em>${original.warning}</em></small>`; + } else if (original.description) { + description = `<small><em>${original.description}</em></small>`; + } + + return `<div>/${original.name} ${aliases} ${params}</div> + <div>${description}</div>`; + }, + }, + }, + [GfmAutocompleteType.Snippets]: { config: { trigger: '$', fillAttr: 'id', - lookup: value => `${value.id}${value.title}`, + lookup: (value) => `${value.id}${value.title}`, + menuItemLimit, menuItemTemplate: ({ original }) => `<small>${original.id}</small> ${escape(original.title)}`, }, }, |