Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/content_editor/components/suggestions_dropdown.vue')
-rw-r--r--app/assets/javascripts/content_editor/components/suggestions_dropdown.vue124
1 files changed, 91 insertions, 33 deletions
diff --git a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
index b34ebe85eb4..dcca76b0786 100644
--- a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
@@ -1,12 +1,17 @@
<script>
-import { GlAvatarLabeled, GlLoadingIcon } from '@gitlab/ui';
+import { GlAvatar, GlLoadingIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
components: {
- GlAvatarLabeled,
+ GlAvatar,
GlLoadingIcon,
},
+ directives: {
+ SafeHtml,
+ },
+
props: {
char: {
type: String,
@@ -38,6 +43,12 @@ export default {
required: false,
default: false,
},
+
+ query: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
@@ -90,20 +101,30 @@ export default {
isEmoji() {
return this.nodeType === 'emoji';
},
+
+ shouldSelectFirstItem() {
+ return this.items.length && this.query;
+ },
},
watch: {
items() {
- this.selectedIndex = -1;
+ this.selectedIndex = this.shouldSelectFirstItem ? 0 : -1;
},
selectedIndex() {
this.scrollIntoView();
},
},
+ mounted() {
+ if (this.shouldSelectFirstItem) {
+ this.selectedIndex = 0;
+ }
+ },
+
methods: {
getText(item) {
- if (this.isEmoji) return item.e;
+ if (this.isEmoji) return item.emoji.e;
switch (this.isReference && this.nodeProps.referenceType) {
case 'user':
@@ -133,10 +154,10 @@ export default {
if (this.isEmoji) {
Object.assign(props, {
- name: item.name,
- unicodeVersion: item.u,
- title: item.d,
- moji: item.e,
+ name: item.emoji.name,
+ unicodeVersion: item.emoji.u,
+ title: item.emoji.d,
+ moji: item.emoji.e,
});
}
@@ -173,7 +194,7 @@ export default {
return true;
}
- if (event.key === 'Enter') {
+ if (event.key === 'Enter' || event.key === 'Tab') {
this.enterHandler();
return true;
}
@@ -194,7 +215,7 @@ export default {
},
scrollIntoView() {
- this.$refs.dropdownItems[this.selectedIndex]?.scrollIntoView({ block: 'nearest' });
+ this.$refs.dropdownItems?.[this.selectedIndex]?.scrollIntoView({ block: 'nearest' });
},
selectItem(index) {
@@ -211,7 +232,17 @@ export default {
avatarSubLabel(item) {
return item.count ? `${item.name} (${item.count})` : item.name;
},
+
+ highlight(text) {
+ return this.query
+ ? String(text).replace(
+ new RegExp(this.query, 'i'),
+ (match) => `<strong class="gl-text-body!">${match}</strong>`,
+ )
+ : text;
+ },
},
+ safeHtmlConfig: { ALLOWED_TAGS: ['strong'] },
};
</script>
@@ -238,29 +269,45 @@ export default {
@click="selectItem(index)"
>
<div class="gl-new-dropdown-item-text-wrapper">
- <gl-avatar-labeled
- v-if="isUser"
- :label="item.username"
- :sub-label="avatarSubLabel(item)"
- :src="item.avatar_url"
- :entity-name="item.username"
- :shape="item.type === 'Group' ? 'rect' : 'circle'"
- :size="32"
- />
+ <span v-if="isUser" class="gl-flex">
+ <gl-avatar
+ :src="item.avatar_url"
+ :entity-name="item.username"
+ :size="24"
+ :shape="item.type === 'Group' ? 'rect' : 'circle'"
+ class="gl-vertical-align-middle gl-mx-2"
+ />
+ <span class="gl-vertical-align-middle">
+ <span v-safe-html:safeHtmlConfig="highlight(item.username)"></span>
+ <small
+ v-safe-html:safeHtmlConfig="highlight(avatarSubLabel(item))"
+ class="gl-text-gray-500"
+ ></small>
+ </span>
+ </span>
<span v-if="isIssue || isMergeRequest">
- <small>{{ item.iid }}</small>
- {{ item.title }}
+ <small
+ v-safe-html:safeHtmlConfig="highlight(item.iid)"
+ class="gl-text-gray-500"
+ ></small>
+ <span v-safe-html:safeHtmlConfig="highlight(item.title)"></span>
</span>
<span v-if="isVulnerability || isSnippet">
- <small>{{ item.id }}</small>
- {{ item.title }}
+ <small
+ v-safe-html:safeHtmlConfig="highlight(item.id)"
+ class="gl-text-gray-500"
+ ></small>
+ <span v-safe-html:safeHtmlConfig="highlight(item.title)"></span>
</span>
<span v-if="isEpic">
- <small>{{ item.reference }}</small>
- {{ item.title }}
+ <small
+ v-safe-html:safeHtmlConfig="highlight(item.reference)"
+ class="gl-text-gray-500"
+ ></small>
+ <span v-safe-html:safeHtmlConfig="highlight(item.title)"></span>
</span>
<span v-if="isMilestone">
- {{ item.title }}
+ <span v-safe-html:safeHtmlConfig="highlight(item.title)"></span>
</span>
<span v-if="isLabel" class="gl-display-flex">
<span
@@ -268,20 +315,31 @@ export default {
class="dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3"
:style="{ backgroundColor: item.color }"
></span>
- {{ item.title }}
+ <span v-safe-html:safeHtmlConfig="highlight(item.title)"></span>
</span>
<div v-if="isCommand">
<div class="gl-mb-1">
- <span class="gl-font-weight-bold">/{{ item.name }}</span>
- <em class="gl-text-gray-500 gl-font-sm">{{ item.params[0] }}</em>
+ /<span v-safe-html:safeHtmlConfig="highlight(item.name)"></span>
+ <span class="gl-text-gray-500 gl-font-sm">{{ item.params[0] }}</span>
</div>
- <small class="gl-text-gray-500"> {{ item.description }} </small>
+ <em
+ v-safe-html:safeHtmlConfig="highlight(item.description)"
+ class="gl-text-gray-500 gl-font-sm"
+ ></em>
</div>
<div v-if="isEmoji" class="gl-display-flex gl-align-items-center">
- <div class="gl-pr-4 gl-font-lg">{{ item.e }}</div>
+ <div class="gl-pr-4 gl-font-lg">
+ <gl-emoji
+ :key="item.emoji.e"
+ :data-name="item.emoji.name"
+ :title="item.emoji.d"
+ :data-unicode-version="item.emoji.u"
+ :data-fallback-src="item.emoji.src"
+ >{{ item.emoji.e }}</gl-emoji
+ >
+ </div>
<div class="gl-flex-grow-1">
- {{ item.name }}<br />
- <small>{{ item.d }}</small>
+ <span v-safe-html:safeHtmlConfig="highlight(item.fieldValue)"></span>
</div>
</div>
</div>