diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-16 09:12:24 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-16 09:12:24 +0300 |
commit | 885897989971931fcb139969b49d8b06f907d7d0 (patch) | |
tree | 7c2873a6690b73d0036b56bf379634c235dfb438 /app/assets/javascripts | |
parent | f5c9eb81b000010cf8abc91cbcfd1d6537f66c64 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
20 files changed, 146 insertions, 13 deletions
diff --git a/app/assets/javascripts/design_management/index.js b/app/assets/javascripts/design_management/index.js index 4ae76050aa5..b856ac6c627 100644 --- a/app/assets/javascripts/design_management/index.js +++ b/app/assets/javascripts/design_management/index.js @@ -24,6 +24,7 @@ export default () => { return new Vue({ el, + name: 'DesignRoot', router, apolloProvider, provide: { diff --git a/app/assets/javascripts/emoji/awards_app/index.js b/app/assets/javascripts/emoji/awards_app/index.js index 1a084d37762..0986533dcd1 100644 --- a/app/assets/javascripts/emoji/awards_app/index.js +++ b/app/assets/javascripts/emoji/awards_app/index.js @@ -12,6 +12,7 @@ export default (el) => { return new Vue({ el, + name: 'AwardsListRoot', store: createstore(), computed: { ...mapState(['currentUserId', 'canAwardEmoji', 'awards']), diff --git a/app/assets/javascripts/invite_members/init_invite_members_modal.js b/app/assets/javascripts/invite_members/init_invite_members_modal.js index 588b1c9ef52..e9d620cedf0 100644 --- a/app/assets/javascripts/invite_members/init_invite_members_modal.js +++ b/app/assets/javascripts/invite_members/init_invite_members_modal.js @@ -28,6 +28,7 @@ export default function initInviteMembersModal() { return new Vue({ el, + name: 'InviteMembersModalRoot', provide: { newProjectPath: el.dataset.newProjectPath, }, diff --git a/app/assets/javascripts/invite_members/init_invite_members_trigger.js b/app/assets/javascripts/invite_members/init_invite_members_trigger.js index 935edb35349..54a5eab2e4b 100644 --- a/app/assets/javascripts/invite_members/init_invite_members_trigger.js +++ b/app/assets/javascripts/invite_members/init_invite_members_trigger.js @@ -11,6 +11,7 @@ export default function initInviteMembersTrigger() { return triggers.forEach((el) => { return new Vue({ el, + name: 'InviteMembersTriggerRoot', render: (createElement) => createElement(InviteMembersTrigger, { props: { diff --git a/app/assets/javascripts/issuable/index.js b/app/assets/javascripts/issuable/index.js index 57bad5182e7..00b027523e2 100644 --- a/app/assets/javascripts/issuable/index.js +++ b/app/assets/javascripts/issuable/index.js @@ -97,6 +97,7 @@ export function initIssuableHeaderWarnings(store) { return new Vue({ el, + name: 'IssuableHeaderWarningsRoot', store, provide: { hidden: parseBoolean(hidden) }, render: (createElement) => createElement(IssuableHeaderWarnings), diff --git a/app/assets/javascripts/issues/related_merge_requests/index.js b/app/assets/javascripts/issues/related_merge_requests/index.js index 5045f7e1a2a..196084093c8 100644 --- a/app/assets/javascripts/issues/related_merge_requests/index.js +++ b/app/assets/javascripts/issues/related_merge_requests/index.js @@ -13,6 +13,7 @@ export function initRelatedMergeRequests() { return new Vue({ el, + name: 'RelatedMergeRequestsRoot', store: createStore(), render: (createElement) => createElement(RelatedMergeRequests, { diff --git a/app/assets/javascripts/issues/show/index.js b/app/assets/javascripts/issues/show/index.js index bf4debcdfa9..732bdc09aea 100644 --- a/app/assets/javascripts/issues/show/index.js +++ b/app/assets/javascripts/issues/show/index.js @@ -44,6 +44,7 @@ export function initIncidentApp(issueData = {}) { return new Vue({ el, + name: 'DescriptionRoot', apolloProvider, provide: { issueType: INCIDENT_TYPE, @@ -86,6 +87,7 @@ export function initIssueApp(issueData, store) { return new Vue({ el, + name: 'DescriptionRoot', apolloProvider, store, provide: { @@ -123,6 +125,7 @@ export function initHeaderActions(store, type = '') { return new Vue({ el, + name: 'HeaderActionsRoot', apolloProvider, store, provide: { diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue index 733d0f69f5d..f3380b7b4ba 100644 --- a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue +++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue @@ -1,13 +1,21 @@ <script> -import { GlModal } from '@gitlab/ui'; +import { GlModal, GlSafeHtmlDirective } from '@gitlab/ui'; import { __ } from '~/locale'; export default { cancelAction: { text: __('Cancel') }, + directives: { + SafeHtml: GlSafeHtmlDirective, + }, components: { GlModal, }, props: { + title: { + type: String, + required: false, + default: '', + }, primaryText: { type: String, required: false, @@ -18,11 +26,27 @@ export default { required: false, default: 'confirm', }, + modalHtmlMessage: { + type: String, + required: false, + default: '', + }, + hideCancel: { + type: Boolean, + required: false, + default: false, + }, }, computed: { primaryAction() { return { text: this.primaryText, attributes: { variant: this.primaryVariant } }; }, + cancelAction() { + return this.hideCancel ? null : this.$options.cancelAction; + }, + shouldShowHeader() { + return Boolean(this.title?.length); + }, }, mounted() { this.$refs.modal.show(); @@ -36,12 +60,14 @@ export default { size="sm" modal-id="confirmationModal" body-class="gl-display-flex" + :title="title" :action-primary="primaryAction" - :action-cancel="$options.cancelAction" - hide-header + :action-cancel="cancelAction" + :hide-header="!shouldShowHeader" @primary="$emit('confirmed')" @hidden="$emit('closed')" > - <div class="gl-align-self-center"><slot></slot></div> + <div v-if="!modalHtmlMessage" class="gl-align-self-center"><slot></slot></div> + <div v-else v-safe-html="modalHtmlMessage" class="gl-align-self-center"></div> </gl-modal> </template> diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js index fdd0e045d07..a8a89d0644a 100644 --- a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js +++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js @@ -1,6 +1,9 @@ import Vue from 'vue'; -export function confirmAction(message, { primaryBtnVariant, primaryBtnText } = {}) { +export function confirmAction( + message, + { primaryBtnVariant, primaryBtnText, modalHtmlMessage, title, hideCancel } = {}, +) { return new Promise((resolve) => { let confirmed = false; @@ -15,6 +18,9 @@ export function confirmAction(message, { primaryBtnVariant, primaryBtnText } = { props: { primaryVariant: primaryBtnVariant, primaryText: primaryBtnText, + title, + modalHtmlMessage, + hideCancel, }, on: { confirmed() { diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index 40dd29bea76..ec6789d81ec 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -5,6 +5,12 @@ import { insertText } from '~/lib/utils/common_utils'; const LINK_TAG_PATTERN = '[{text}](url)'; +// at the start of a line, find any amount of whitespace followed by +// a bullet point character (*+-) and an optional checkbox ([ ] [x]) +// OR a number with a . after it and an optional checkbox ([ ] [x]) +// followed by one or more whitespace characters +const LIST_LINE_HEAD_PATTERN = /^(?<indent>\s*)(?<leader>((?<isOl>[*+-])|(?<isUl>\d+\.))( \[([x ])\])?\s)(?<content>.)?/; + function selectedText(text, textarea) { return text.substring(textarea.selectionStart, textarea.selectionEnd); } @@ -13,8 +19,15 @@ function addBlockTags(blockTag, selected) { return `${blockTag}\n${selected}\n${blockTag}`; } -function lineBefore(text, textarea) { - const split = text.substring(0, textarea.selectionStart).trim().split('\n'); +function lineBefore(text, textarea, trimNewlines = true) { + let split = text.substring(0, textarea.selectionStart); + + if (trimNewlines) { + split = split.trim(); + } + + split = split.split('\n'); + return split[split.length - 1]; } @@ -284,9 +297,9 @@ function updateText({ textArea, tag, cursorOffset, blockTag, wrap, select, tagCo } /* eslint-disable @gitlab/require-i18n-strings */ -export function keypressNoteText(e) { +function handleSurroundSelectedText(e, textArea) { if (!gon.markdown_surround_selection) return; - if (this.selectionStart === this.selectionEnd) return; + if (textArea.selectionStart === textArea.selectionEnd) return; const keys = { '*': '**{text}**', // wraps with bold character @@ -306,7 +319,7 @@ export function keypressNoteText(e) { updateText({ tag, - textArea: this, + textArea, blockTag: '', wrap: true, select: '', @@ -316,6 +329,48 @@ export function keypressNoteText(e) { } /* eslint-enable @gitlab/require-i18n-strings */ +function handleContinueList(e, textArea) { + if (!gon.features?.markdownContinueLists) return; + if (!(e.key === 'Enter')) return; + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return; + if (textArea.selectionStart !== textArea.selectionEnd) return; + + const currentLine = lineBefore(textArea.value, textArea, false); + const result = currentLine.match(LIST_LINE_HEAD_PATTERN); + + if (result) { + const { indent, content, leader } = result.groups; + const prevLineEmpty = !content; + + if (prevLineEmpty) { + // erase previous empty list item - select the text and allow the + // natural line feed erase the text + textArea.selectionStart = textArea.selectionStart - result[0].length; + return; + } + + const itemInsert = `${indent}${leader}`; + + e.preventDefault(); + + updateText({ + tag: itemInsert, + textArea, + blockTag: '', + wrap: false, + select: '', + tagContent: '', + }); + } +} + +export function keypressNoteText(e) { + const textArea = this; + + handleContinueList(e, textArea); + handleSurroundSelectedText(e, textArea); +} + export function updateTextForToolbarBtn($toolbarBtn) { return updateText({ textArea: $toolbarBtn.closest('.md-area').find('textarea'), diff --git a/app/assets/javascripts/nav/mount.js b/app/assets/javascripts/nav/mount.js index 51b6a31b8cb..7b0cc977107 100644 --- a/app/assets/javascripts/nav/mount.js +++ b/app/assets/javascripts/nav/mount.js @@ -12,6 +12,7 @@ const mount = (el, Component) => { return new Vue({ el, + name: 'TopNavRoot', store, render(h) { return h(Component, { diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js index 7c9e7703d59..104e9d4183a 100644 --- a/app/assets/javascripts/notes/discussion_filters.js +++ b/app/assets/javascripts/notes/discussion_filters.js @@ -19,7 +19,7 @@ export default (store) => { return new Vue({ el: discussionFilterEl, - name: 'DiscussionFilter', + name: 'DiscussionFilterRoot', components: { DiscussionFilter, }, diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index 2ce60976adb..19fa484d659 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -14,6 +14,7 @@ export default () => { // eslint-disable-next-line no-new new Vue({ el, + name: 'NotesRoot', components: { notesApp, }, diff --git a/app/assets/javascripts/notes/sort_discussions.js b/app/assets/javascripts/notes/sort_discussions.js index ecfa3223039..ca8df880fe4 100644 --- a/app/assets/javascripts/notes/sort_discussions.js +++ b/app/assets/javascripts/notes/sort_discussions.js @@ -8,6 +8,7 @@ export default (store) => { return new Vue({ el, + name: 'SortDiscussionRoot', store, render(createElement) { return createElement(SortDiscussion); diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js index 66e999ca43b..eb5b50dd1ec 100644 --- a/app/assets/javascripts/performance_bar/index.js +++ b/app/assets/javascripts/performance_bar/index.js @@ -20,6 +20,7 @@ const initPerformanceBar = (el) => { return new Vue({ el, + name: 'PerformanceBarRoot', components: { PerformanceBarApp: () => import('./components/performance_bar_app.vue'), }, diff --git a/app/assets/javascripts/popovers/index.js b/app/assets/javascripts/popovers/index.js index 7db669b8c52..94340ae16a0 100644 --- a/app/assets/javascripts/popovers/index.js +++ b/app/assets/javascripts/popovers/index.js @@ -13,7 +13,7 @@ const getPopoversApp = () => { document.body.appendChild(container); const Popovers = Vue.extend(PopoversComponent); - app = new Popovers(); + app = new Popovers({ name: 'PopoversRoot' }); app.$mount(`#${APP_ELEMENT_ID}`); } diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 55913394ca3..62e2cec874a 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -1,6 +1,7 @@ import $ from 'jquery'; import { debounce } from 'lodash'; import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates'; +import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '../lib/utils/constants'; import axios from '../lib/utils/axios_utils'; import { @@ -105,6 +106,21 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => { }; const bindHowToImport = () => { + const importLinks = document.querySelectorAll('.js-how-to-import-link'); + + importLinks.forEach((link) => { + const { modalTitle: title, modalMessage: modalHtmlMessage } = link.dataset; + + link.addEventListener('click', (e) => { + e.preventDefault(); + confirmAction('', { + modalHtmlMessage, + title, + hideCancel: true, + }); + }); + }); + $('.how_to_import_link').on('click', (e) => { e.preventDefault(); $(e.currentTarget).next('.modal').show(); diff --git a/app/assets/javascripts/related_issues/index.js b/app/assets/javascripts/related_issues/index.js index bd9845e616b..35858be90b2 100644 --- a/app/assets/javascripts/related_issues/index.js +++ b/app/assets/javascripts/related_issues/index.js @@ -8,7 +8,7 @@ export default function initRelatedIssues() { // eslint-disable-next-line no-new new Vue({ el: relatedIssuesRootElement, - name: 'RelatedIssuesApp', + name: 'RelatedIssuesRoot', components: { relatedIssuesRoot: RelatedIssuesRoot, }, diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 6363422259e..c29784aa328 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -57,6 +57,7 @@ function mountSidebarToDoWidget() { return new Vue({ el, + name: 'SidebarTodoRoot', apolloProvider, components: { SidebarTodoWidget, @@ -103,6 +104,7 @@ function mountAssigneesComponentDeprecated(mediator) { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarAssigneesRoot', apolloProvider, components: { SidebarAssignees, @@ -135,6 +137,7 @@ function mountAssigneesComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarAssigneesRoot', apolloProvider, components: { SidebarAssigneesWidget, @@ -185,6 +188,7 @@ function mountReviewersComponent(mediator) { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarReviewersRoot', apolloProvider, components: { SidebarReviewers, @@ -218,6 +222,7 @@ function mountCrmContactsComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarCrmContactsRoot', apolloProvider, components: { CrmContacts, @@ -242,6 +247,7 @@ function mountMilestoneSelect() { return new Vue({ el, + name: 'SidebarMilestoneRoot', apolloProvider, components: { SidebarDropdownWidget, @@ -274,6 +280,7 @@ export function mountSidebarLabels() { return new Vue({ el, + name: 'SidebarLabelsRoot', apolloProvider, components: { @@ -328,6 +335,7 @@ function mountConfidentialComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarConfidentialRoot', apolloProvider, components: { SidebarConfidentialityWidget, @@ -362,6 +370,7 @@ function mountDueDateComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarDueDateRoot', apolloProvider, components: { SidebarDueDateWidget, @@ -392,6 +401,7 @@ function mountReferenceComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarReferenceRoot', apolloProvider, components: { SidebarReferenceWidget, @@ -428,6 +438,7 @@ function mountLockComponent(store) { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarLockRoot', store, provide: { fullPath, @@ -451,6 +462,7 @@ function mountParticipantsComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarParticipantsRoot', apolloProvider, components: { SidebarParticipantsWidget, @@ -479,6 +491,7 @@ function mountSubscriptionsComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarSubscriptionsRoot', apolloProvider, components: { SidebarSubscriptionsWidget, @@ -509,6 +522,7 @@ function mountTimeTrackingComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarTimeTrackingRoot', apolloProvider, provide: { issuableType }, render: (createElement) => @@ -534,6 +548,7 @@ function mountSeverityComponent() { return new Vue({ el: severityContainerEl, + name: 'SidebarSeverityRoot', apolloProvider, components: { SidebarSeverity, @@ -562,6 +577,7 @@ function mountCopyEmailComponent() { // eslint-disable-next-line no-new new Vue({ el, + name: 'SidebarCopyEmailRoot', render: (createElement) => createElement(CopyEmailToClipboard, { props: { issueEmailAddress: createNoteEmail } }), }); diff --git a/app/assets/javascripts/tooltips/index.js b/app/assets/javascripts/tooltips/index.js index 49a43b120e0..4639671984a 100644 --- a/app/assets/javascripts/tooltips/index.js +++ b/app/assets/javascripts/tooltips/index.js @@ -21,6 +21,7 @@ const tooltipsApp = () => { document.body.appendChild(container); app = new Vue({ + name: 'TooltipsRoot', render(h) { return h(Tooltips, { props: { |