diff options
Diffstat (limited to 'app/assets')
63 files changed, 852 insertions, 332 deletions
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 57457ebd0a3..ff2e0768a87 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -81,8 +81,7 @@ { gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" target="_blank" rel="noopener noreferrer"> - ${_.escape(s__('ClusterIntegration|Gitlab Integration'))} - </a>`, + ${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`, }, false, ); diff --git a/app/assets/javascripts/commit_merge_requests.js b/app/assets/javascripts/commit_merge_requests.js new file mode 100644 index 00000000000..f76c9b7e690 --- /dev/null +++ b/app/assets/javascripts/commit_merge_requests.js @@ -0,0 +1,73 @@ +/* global Flash */ + +import axios from './lib/utils/axios_utils'; +import { n__, s__ } from './locale'; + +export function getHeaderText(childElementCount, mergeRequestCount) { + if (childElementCount === 0) { + return `${mergeRequestCount} ${n__('merge request', 'merge requests', mergeRequestCount)}`; + } + return ','; +} + +export function createHeader(childElementCount, mergeRequestCount) { + const headerText = getHeaderText(childElementCount, mergeRequestCount); + + return $('<span />', { + class: 'append-right-5', + text: headerText, + }); +} + +export function createLink(mergeRequest) { + return $('<a />', { + class: 'append-right-5', + href: mergeRequest.path, + text: `!${mergeRequest.iid}`, + }); +} + +export function createTitle(mergeRequest) { + return $('<span />', { + text: mergeRequest.title, + }); +} + +export function createItem(mergeRequest) { + const $item = $('<span />'); + const $link = createLink(mergeRequest); + const $title = createTitle(mergeRequest); + $item.append($link); + $item.append($title); + + return $item; +} + +export function createContent(mergeRequests) { + const $content = $('<span />'); + + if (mergeRequests.length === 0) { + $content.text(s__('Commits|No related merge requests found')); + } else { + mergeRequests.forEach((mergeRequest) => { + const $header = createHeader($content.children().length, mergeRequests.length); + const $item = createItem(mergeRequest); + $content.append($header); + $content.append($item); + }); + } + + return $content; +} + +export function fetchCommitMergeRequests() { + const $container = $('.merge-requests'); + + axios.get($container.data('projectCommitPath')) + .then((response) => { + const $content = createContent(response.data); + + $container.html($content); + }) + .catch(() => Flash(s__('Commits|An error occurred while fetching merge requests data.'))); +} diff --git a/app/assets/javascripts/protected_tags/protected_tag_dropdown.js b/app/assets/javascripts/create_item_dropdown.js index a0224213aa0..c3eceb285f5 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_dropdown.js +++ b/app/assets/javascripts/create_item_dropdown.js @@ -1,6 +1,6 @@ import _ from 'underscore'; -export default class ProtectedTagDropdown { +export default class CreateItemDropdown { /** * @param {Object} options containing * `$dropdown` target element @@ -8,11 +8,14 @@ export default class ProtectedTagDropdown { * $dropdown must be an element created using `dropdown_tag()` rails helper */ constructor(options) { - this.onSelect = options.onSelect; + this.defaultToggleLabel = options.defaultToggleLabel; + this.fieldName = options.fieldName; + this.onSelect = options.onSelect || (() => {}); + this.getDataOption = options.getData; this.$dropdown = options.$dropdown; this.$dropdownContainer = this.$dropdown.parent(); this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer'); - this.$protectedTag = this.$dropdownContainer.find('.js-create-new-protected-tag'); + this.$createButton = this.$dropdownContainer.find('.js-dropdown-create-new-item'); this.buildDropdown(); this.bindEvents(); @@ -23,7 +26,7 @@ export default class ProtectedTagDropdown { buildDropdown() { this.$dropdown.glDropdown({ - data: this.getProtectedTags.bind(this), + data: this.getData.bind(this), filterable: true, remote: false, search: { @@ -31,14 +34,14 @@ export default class ProtectedTagDropdown { }, selectable: true, toggleLabel(selected) { - return (selected && 'id' in selected) ? selected.title : 'Protected Tag'; + return (selected && 'id' in selected) ? selected.title : this.defaultToggleLabel; }, - fieldName: 'protected_tag[name]', - text(protectedTag) { - return _.escape(protectedTag.title); + fieldName: this.fieldName, + text(item) { + return _.escape(item.title); }, - id(protectedTag) { - return _.escape(protectedTag.id); + id(item) { + return _.escape(item.id); }, onFilter: this.toggleCreateNewButton.bind(this), clicked: (options) => { @@ -49,37 +52,37 @@ export default class ProtectedTagDropdown { } bindEvents() { - this.$protectedTag.on('click', this.onClickCreateWildcard.bind(this)); + this.$createButton.on('click', this.onClickCreateWildcard.bind(this)); } onClickCreateWildcard(e) { + e.preventDefault(); + + // Refresh the dropdown's data, which ends up calling `getData` this.$dropdown.data('glDropdown').remote.execute(); this.$dropdown.data('glDropdown').selectRowAtIndex(); - e.preventDefault(); } - getProtectedTags(term, callback) { - if (this.selectedTag) { - callback(gon.open_tags.concat(this.selectedTag)); - } else { - callback(gon.open_tags); - } + getData(term, callback) { + this.getDataOption(term, (data = []) => { + callback(data.concat(this.selectedItem || [])); + }); } - toggleCreateNewButton(tagName) { - if (tagName) { - this.selectedTag = { - title: tagName, - id: tagName, - text: tagName, + toggleCreateNewButton(item) { + if (item) { + this.selectedItem = { + title: item, + id: item, + text: item, }; this.$dropdownContainer - .find('.js-create-new-protected-tag code') - .text(tagName); + .find('.js-dropdown-create-new-item code') + .text(item); } - this.toggleFooter(!tagName); + this.toggleFooter(!item); } toggleFooter(toggleState) { diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f8fedab36a1..1c6336073e9 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -1,50 +1,34 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ -import { s__ } from './locale'; import projectSelect from './project_select'; -import IssuableIndex from './issuable_index'; import Milestone from './milestone'; import IssuableForm from './issuable_form'; import LabelsSelect from './labels_select'; import MilestoneSelect from './milestone_select'; -import NewBranchForm from './new_branch_form'; import NotificationsForm from './notifications_form'; import notificationsDropdown from './notifications_dropdown'; import groupAvatar from './group_avatar'; import GroupLabelSubscription from './group_label_subscription'; import LineHighlighter from './line_highlighter'; -import NewCommitForm from './new_commit_form'; import Project from './project'; import projectAvatar from './project_avatar'; import MergeRequest from './merge_request'; import Compare from './compare'; -import initCompareAutocomplete from './compare_autocomplete'; -import ProjectFindFile from './project_find_file'; import ProjectNew from './project_new'; -import projectImport from './project_import'; import Labels from './labels'; import LabelManager from './label_manager'; import Sidebar from './right_sidebar'; import IssuableTemplateSelectors from './templates/issuable_template_selectors'; import Flash from './flash'; -import CommitsList from './commits'; -import Issue from './issue'; import BindInOut from './behaviors/bind_in_out'; import SecretValues from './behaviors/secret_values'; -import DeleteModal from './branches/branches_delete_modal'; import Group from './group'; import ProjectsList from './projects_list'; -import setupProjectEdit from './project_edit'; -import MiniPipelineGraph from './mini_pipeline_graph_dropdown'; -import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater'; -import BlobForkSuggestion from './blob/blob_fork_suggestion'; import UserCallout from './user_callout'; import ShortcutsWiki from './shortcuts_wiki'; import BlobViewer from './blob/viewer/index'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import UsersSelect from './users_select'; -import RefSelectDropdown from './ref_select_dropdown'; import GfmAutoComplete from './gfm_auto_complete'; -import ShortcutsBlob from './shortcuts_blob'; import Star from './star'; import TreeView from './tree'; import Wikis from './wikis'; @@ -54,21 +38,16 @@ import PerformanceBar from './performance_bar'; import initNotes from './init_notes'; import initIssuableSidebar from './init_issuable_sidebar'; import initProjectVisibilitySelector from './project_visibility'; -import GpgBadges from './gpg_badges'; -import initChangesDropdown from './init_changes_dropdown'; import NewGroupChild from './groups/new_group_child'; import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils'; -import AjaxLoadingSpinner from './ajax_loading_spinner'; import GlFieldErrors from './gl_field_errors'; import GLForm from './gl_form'; import Shortcuts from './shortcuts'; import ShortcutsNavigation from './shortcuts_navigation'; -import ShortcutsFindFile from './shortcuts_find_file'; import ShortcutsIssuable from './shortcuts_issuable'; import U2FAuthenticate from './u2f/authenticate'; import Members from './members'; import memberExpirationDate from './member_expiration_date'; -import DueDateSelectors from './due_date_select'; import Diff from './diff'; import ProjectLabelSubscription from './project_label_subscription'; import SearchAutocomplete from './search_autocomplete'; @@ -85,7 +64,7 @@ import Activities from './activities'; } Dispatcher.prototype.initPageScripts = function() { - var path, shortcut_handler, fileBlobPermalinkUrlElement, fileBlobPermalinkUrl; + var path, shortcut_handler; const page = $('body').attr('data-page'); if (!page) { return false; @@ -110,33 +89,6 @@ import Activities from './activities'; }); }); - function initBlob() { - new LineHighlighter(); - - new BlobLinePermalinkUpdater( - document.querySelector('#blob-content-holder'), - '.diff-line-num[data-line-number]', - document.querySelectorAll('.js-data-file-blob-permalink-url, .js-blob-blame-link'), - ); - - shortcut_handler = new ShortcutsNavigation(); - fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url'); - fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href'); - new ShortcutsBlob({ - skipResetBindings: true, - fileBlobPermalinkUrl, - }); - - new BlobForkSuggestion({ - openButtons: document.querySelectorAll('.js-edit-blob-link-fork-toggler'), - forkButtons: document.querySelectorAll('.js-fork-suggestion-button'), - cancelButtons: document.querySelectorAll('.js-cancel-fork-suggestion-button'), - suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'), - actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'), - }) - .init(); - } - const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); switch (page) { @@ -153,22 +105,22 @@ import Activities from './activities'; shortcut_handler = true; break; case 'projects:merge_requests:index': + import('./pages/projects/merge_requests/index') + .then(callDefault) + .catch(fail); + shortcut_handler = true; + break; case 'projects:issues:index': - if (filteredSearchEnabled) { - const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests'); - filteredSearchManager.setup(); - } - const pagePrefix = page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_'; - new IssuableIndex(pagePrefix); - - shortcut_handler = new ShortcutsNavigation(); - new UsersSelect(); + import('./pages/projects/issues/index') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:issues:show': - new Issue(); - shortcut_handler = new ShortcutsIssuable(); - new ZenMode(); - initIssuableSidebar(); + import('./pages/projects/issues/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'dashboard:milestones:index': import('./pages/dashboard/milestones/index') @@ -225,40 +177,60 @@ import Activities from './activities'; .catch(fail); break; case 'projects:milestones:new': + case 'projects:milestones:create': + import('./pages/projects/milestones/new') + .then(callDefault) + .catch(fail); + break; case 'projects:milestones:edit': case 'projects:milestones:update': - new ZenMode(); - new DueDateSelectors(); - new GLForm($('.milestone-form'), true); + import('./pages/projects/milestones/edit') + .then(callDefault) + .catch(fail); break; case 'groups:milestones:new': + case 'groups:milestones:create': + import('./pages/groups/milestones/new') + .then(callDefault) + .catch(fail); + break; case 'groups:milestones:edit': case 'groups:milestones:update': - new ZenMode(); - new DueDateSelectors(); - new GLForm($('.milestone-form'), false); + import('./pages/groups/milestones/edit') + .then(callDefault) + .catch(fail); break; case 'projects:compare:show': - new Diff(); - const paddingTop = 16; - initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); + import('./pages/projects/compare/show') + .then(callDefault) + .catch(fail); break; case 'projects:branches:new': + import('./pages/projects/branches/new') + .then(callDefault) + .catch(fail); + break; case 'projects:branches:create': - new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); + import('./pages/projects/branches/new') + .then(callDefault) + .catch(fail); break; case 'projects:branches:index': - AjaxLoadingSpinner.init(); - new DeleteModal(); + import('./pages/projects/branches/index') + .then(callDefault) + .catch(fail); break; case 'projects:issues:new': + import('./pages/projects/issues/new') + .then(callDefault) + .catch(fail); + shortcut_handler = true; + break; case 'projects:issues:edit': - shortcut_handler = new ShortcutsNavigation(); - new GLForm($('.issue-form'), true); - new IssuableForm($('.issue-form')); - new LabelsSelect(); - new MilestoneSelect(); - new IssuableTemplateSelectors(); + import('./pages/projects/issues/edit') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:merge_requests:creations:new': const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); @@ -286,9 +258,9 @@ import Activities from './activities'; new AutoWidthDropdownSelect($('.js-target-branch-select')).init(); break; case 'projects:tags:new': - new ZenMode(); - new GLForm($('.tag-form'), true); - new RefSelectDropdown($('.js-branch-select')); + import('./pages/projects/tags/new') + .then(callDefault) + .catch(fail); break; case 'projects:snippets:show': initNotes(); @@ -302,11 +274,24 @@ import Activities from './activities'; new ZenMode(); break; case 'snippets:new': + import('./pages/snippets/new') + .then(callDefault) + .catch(fail); + break; case 'snippets:edit': + import('./pages/snippets/edit') + .then(callDefault) + .catch(fail); + break; case 'snippets:create': + import('./pages/snippets/new') + .then(callDefault) + .catch(fail); + break; case 'snippets:update': - new GLForm($('.snippet-form'), false); - new ZenMode(); + import('./pages/snippets/edit') + .then(callDefault) + .catch(fail); break; case 'projects:releases:edit': new ZenMode(); @@ -332,22 +317,15 @@ import Activities from './activities'; .catch(fail); break; case 'projects:commit:show': - new Diff(); - new ZenMode(); - shortcut_handler = new ShortcutsNavigation(); - new MiniPipelineGraph({ - container: '.js-commit-pipeline-graph', - }).bindEvents(); - initNotes(); - const stickyBarPaddingTop = 16; - initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop); - $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); + import('./pages/projects/commit/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:commit:pipelines': - new MiniPipelineGraph({ - container: '.js-commit-pipeline-graph', - }).bindEvents(); - $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); + import('./pages/projects/commit/pipelines') + .then(callDefault) + .catch(fail); break; case 'projects:activity': import('./pages/projects/activity') @@ -356,9 +334,10 @@ import Activities from './activities'; shortcut_handler = true; break; case 'projects:commits:show': - CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); - shortcut_handler = new ShortcutsNavigation(); - GpgBadges.fetch(); + import('./pages/projects/commits/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:show': shortcut_handler = new ShortcutsNavigation(); @@ -376,12 +355,14 @@ import Activities from './activities'; }); break; case 'projects:edit': - setupProjectEdit(); - // Initialize expandable settings panels - initSettingsPanels(); + import('./pages/projects/edit') + .then(callDefault) + .catch(fail); break; case 'projects:imports:show': - projectImport(); + import('./pages/projects/imports/show') + .then(callDefault) + .catch(fail); break; case 'projects:pipelines:new': case 'projects:pipelines:create': @@ -441,39 +422,49 @@ import Activities from './activities'; groupAvatar(); break; case 'projects:tree:show': - shortcut_handler = new ShortcutsNavigation(); - new TreeView(); - new BlobViewer(); - new NewCommitForm($('.js-create-dir-form')); - $('#tree-slider').waitForImages(function() { - ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); - }); + import('./pages/projects/tree/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:find_file:show': - const findElement = document.querySelector('.js-file-finder'); - const projectFindFile = new ProjectFindFile($(".file-finder-holder"), { - url: findElement.dataset.fileFindUrl, - treeUrl: findElement.dataset.findTreeUrl, - blobUrlTemplate: findElement.dataset.blobUrlTemplate, - }); - new ShortcutsFindFile(projectFindFile); + import('./pages/projects/find_file/show') + .then(callDefault) + .catch(fail); shortcut_handler = true; break; case 'projects:blob:show': - new BlobViewer(); - initBlob(); + import('./pages/projects/blob/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:blame:show': - initBlob(); + import('./pages/projects/blame/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'groups:labels:new': case 'groups:labels:edit': + new Labels(); + break; case 'projects:labels:new': + import('./pages/projects/labels/new') + .then(callDefault) + .catch(fail); + break; case 'projects:labels:edit': - new Labels(); + import('./pages/projects/labels/edit') + .then(callDefault) + .catch(fail); break; - case 'groups:labels:index': case 'projects:labels:index': + import('./pages/projects/labels/index') + .then(callDefault) + .catch(fail); + break; + case 'groups:labels:index': if ($('.prioritized-labels').length) { new LabelManager(); } @@ -493,7 +484,7 @@ import Activities from './activities'; shortcut_handler = true; break; case 'projects:forks:new': - import(/* webpackChunkName: 'project_fork' */ './project_fork') + import('./pages/projects/forks/new') .then(callDefault) .catch(fail); break; @@ -550,7 +541,9 @@ import Activities from './activities'; import('./pages/admin/conversational_development_index/show').then(m => m.default()).catch(fail); break; case 'snippets:show': - import('./pages/snippets/show').then(m => m.default()).catch(fail); + import('./pages/snippets/show') + .then(callDefault) + .catch(fail); break; case 'import:fogbugz:new_user_map': import('./pages/import/fogbugz/new_user_map').then(m => m.default()).catch(fail); @@ -566,20 +559,14 @@ import Activities from './activities'; .catch(fail); break; case 'projects:clusters:show': - import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle') - .then(cluster => new cluster.default()) // eslint-disable-line new-cap - .catch((err) => { - Flash(s__('ClusterIntegration|Problem setting up the cluster')); - throw err; - }); + import('./pages/projects/clusters/show') + .then(callDefault) + .catch(fail); break; case 'projects:clusters:index': - import(/* webpackChunkName: "clusters_index" */ './clusters/clusters_index') - .then(clusterIndex => clusterIndex.default()) - .catch((err) => { - Flash(s__('ClusterIntegration|Problem setting up the clusters list')); - throw err; - }); + import('./pages/projects/clusters/index') + .then(callDefault) + .catch(fail); break; } switch (path[0]) { @@ -659,7 +646,9 @@ import Activities from './activities'; projectAvatar(); switch (path[1]) { case 'compare': - initCompareAutocomplete(); + import('./pages/projects/compare') + .then(callDefault) + .catch(fail); break; case 'edit': shortcut_handler = new ShortcutsNavigation(); diff --git a/app/assets/javascripts/init_labels.js b/app/assets/javascripts/init_labels.js new file mode 100644 index 00000000000..5f20055510f --- /dev/null +++ b/app/assets/javascripts/init_labels.js @@ -0,0 +1,18 @@ +import LabelManager from './label_manager'; +import GroupLabelSubscription from './group_label_subscription'; +import ProjectLabelSubscription from './project_label_subscription'; + +export default () => { + if ($('.prioritized-labels').length) { + new LabelManager(); // eslint-disable-line no-new + } + $('.label-subscription').each((i, el) => { + const $el = $(el); + + if ($el.find('.dropdown-group-label').length) { + new GroupLabelSubscription($el); // eslint-disable-line no-new + } else { + new ProjectLabelSubscription($el); // eslint-disable-line no-new + } + }); +}; diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue index 9e3f659db5f..357bc9aab17 100644 --- a/app/assets/javascripts/jobs/components/header.vue +++ b/app/assets/javascripts/jobs/components/header.vue @@ -30,8 +30,12 @@ shouldRenderContent() { return !this.isLoading && Object.keys(this.job).length; }, + /** + * When job has not started the key will be `false` + * When job started the key will be a string with a date. + */ jobStarted() { - return this.job.started; + return !this.job.started === false; }, }, watch: { @@ -72,6 +76,7 @@ <loading-icon v-if="isLoading" size="2" + class="prepend-top-default append-bottom-default" /> </div> </template> diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index cb3cdea8111..e26bf437efc 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -110,7 +110,7 @@ MergeRequest.prototype.initCommitMessageListeners = function() { }); }; -MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, newStatusText) { +MergeRequest.updateStatusText = function(classToRemove, classToAdd, newStatusText) { $('.detail-page-header .status-box') .removeClass(classToRemove) .addClass(classToAdd) @@ -118,14 +118,14 @@ MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, ne .text(newStatusText); }; -MergeRequest.prototype.decreaseCounter = function(by = 1) { - const $el = $('.nav-links .js-merge-counter'); +MergeRequest.decreaseCounter = function(by = 1) { + const $el = $('.js-merge-counter'); const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0); $el.text(addDelimiter(count)); }; -MergeRequest.prototype.hideCloseButton = function() { +MergeRequest.hideCloseButton = function() { const el = document.querySelector('.merge-request .js-issuable-actions'); const closeDropdownItem = el.querySelector('li.close-item'); if (closeDropdownItem) { diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 1f18c196137..3c8452ac808 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -271,7 +271,7 @@ Please check your network connection and try again.`; <div class="timeline-content timeline-content-form"> <form ref="commentForm" - class="new-note js-quick-submit common-note-form gfm-form js-main-target-form" + class="new-note common-note-form gfm-form js-main-target-form" > <div class="error-alert"></div> @@ -301,7 +301,8 @@ js-gfm-input js-autosize markdown-area js-vue-textarea" :disabled="isSubmitting" placeholder="Write a comment or drag your files here..." @keydown.up="editCurrentUserLastNote()" - @keydown.meta.enter="handleSave()"> + @keydown.meta.enter="handleSave()" + @keydown.ctrl.enter="handleSave()"> </textarea> </markdown-field> <div class="note-form-actions"> diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index aeda3497715..d382a9bb642 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -155,6 +155,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea" slot="textarea" placeholder="Write a comment or drag your files here..." @keydown.meta.enter="handleUpdate()" + @keydown.ctrl.enter="handleUpdate()" @keydown.up="editMyLastNote()" @keydown.esc="cancelHandler(true)"> </textarea> diff --git a/app/assets/javascripts/pages/groups/milestones/edit/index.js b/app/assets/javascripts/pages/groups/milestones/edit/index.js new file mode 100644 index 00000000000..5c99c90e24d --- /dev/null +++ b/app/assets/javascripts/pages/groups/milestones/edit/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(false); diff --git a/app/assets/javascripts/pages/groups/milestones/new/index.js b/app/assets/javascripts/pages/groups/milestones/new/index.js new file mode 100644 index 00000000000..5c99c90e24d --- /dev/null +++ b/app/assets/javascripts/pages/groups/milestones/new/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(false); diff --git a/app/assets/javascripts/pages/projects/blame/show/index.js b/app/assets/javascripts/pages/projects/blame/show/index.js new file mode 100644 index 00000000000..480357a309c --- /dev/null +++ b/app/assets/javascripts/pages/projects/blame/show/index.js @@ -0,0 +1,3 @@ +import initBlob from '~/pages/projects/init_blob'; + +export default initBlob; diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js new file mode 100644 index 00000000000..a3eeb1cefb6 --- /dev/null +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -0,0 +1,7 @@ +import BlobViewer from '~/blob/viewer/index'; +import initBlob from '~/pages/projects/init_blob'; + +export default () => { + new BlobViewer(); // eslint-disable-line no-new + initBlob(); +}; diff --git a/app/assets/javascripts/pages/projects/branches/index/index.js b/app/assets/javascripts/pages/projects/branches/index/index.js new file mode 100644 index 00000000000..cee0f19bf2a --- /dev/null +++ b/app/assets/javascripts/pages/projects/branches/index/index.js @@ -0,0 +1,7 @@ +import AjaxLoadingSpinner from '~/ajax_loading_spinner'; +import DeleteModal from '~/branches/branches_delete_modal'; + +export default () => { + AjaxLoadingSpinner.init(); + new DeleteModal(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js new file mode 100644 index 00000000000..ae5e033e97e --- /dev/null +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -0,0 +1,3 @@ +import NewBranchForm from '~/new_branch_form'; + +export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); diff --git a/app/assets/javascripts/pages/projects/clusters/index/index.js b/app/assets/javascripts/pages/projects/clusters/index/index.js new file mode 100644 index 00000000000..d531ab81dc7 --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/index/index.js @@ -0,0 +1,5 @@ +import ClustersIndex from '~/clusters/clusters_index'; + +export default () => { + new ClustersIndex(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/clusters/show/index.js b/app/assets/javascripts/pages/projects/clusters/show/index.js new file mode 100644 index 00000000000..0458c02a66f --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/show/index.js @@ -0,0 +1,5 @@ +import ClustersBundle from '~/clusters/clusters_bundle'; + +export default () => { + new ClustersBundle(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js new file mode 100644 index 00000000000..523ad567021 --- /dev/null +++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js @@ -0,0 +1,8 @@ +import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; + +export default () => { + new MiniPipelineGraph({ + container: '.js-commit-pipeline-graph', + }).bindEvents(); + $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); +}; diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js new file mode 100644 index 00000000000..5ac38e6f278 --- /dev/null +++ b/app/assets/javascripts/pages/projects/commit/show/index.js @@ -0,0 +1,22 @@ +/* eslint-disable no-new */ +import Diff from '~/diff'; +import ZenMode from '~/zen_mode'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; +import initNotes from '~/init_notes'; +import initChangesDropdown from '~/init_changes_dropdown'; +import { fetchCommitMergeRequests } from '~/commit_merge_requests'; + +export default () => { + new Diff(); + new ZenMode(); + new ShortcutsNavigation(); + new MiniPipelineGraph({ + container: '.js-commit-pipeline-graph', + }).bindEvents(); + initNotes(); + const stickyBarPaddingTop = 16; + initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop); + $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); + fetchCommitMergeRequests(); +}; diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js new file mode 100644 index 00000000000..90b5882a24f --- /dev/null +++ b/app/assets/javascripts/pages/projects/commits/show/index.js @@ -0,0 +1,9 @@ +import CommitsList from '~/commits'; +import GpgBadges from '~/gpg_badges'; +import ShortcutsNavigation from '~/shortcuts_navigation'; + +export default () => { + CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); + new ShortcutsNavigation(); // eslint-disable-line no-new + GpgBadges.fetch(); +}; diff --git a/app/assets/javascripts/pages/projects/compare/index.js b/app/assets/javascripts/pages/projects/compare/index.js new file mode 100644 index 00000000000..890062eeee6 --- /dev/null +++ b/app/assets/javascripts/pages/projects/compare/index.js @@ -0,0 +1,5 @@ +import initCompareAutocomplete from '~/compare_autocomplete'; + +export default () => { + initCompareAutocomplete(); +}; diff --git a/app/assets/javascripts/pages/projects/compare/show/index.js b/app/assets/javascripts/pages/projects/compare/show/index.js new file mode 100644 index 00000000000..6b8d4503568 --- /dev/null +++ b/app/assets/javascripts/pages/projects/compare/show/index.js @@ -0,0 +1,8 @@ +import Diff from '~/diff'; +import initChangesDropdown from '~/init_changes_dropdown'; + +export default () => { + new Diff(); // eslint-disable-line no-new + const paddingTop = 16; + initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); +}; diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js new file mode 100644 index 00000000000..7f662ef6b6a --- /dev/null +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -0,0 +1,8 @@ +import initSettingsPanels from '~/settings_panels'; +import setupProjectEdit from '~/project_edit'; + +export default () => { + setupProjectEdit(); + // Initialize expandable settings panels + initSettingsPanels(); +}; diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js new file mode 100644 index 00000000000..42bde0ff779 --- /dev/null +++ b/app/assets/javascripts/pages/projects/find_file/show/index.js @@ -0,0 +1,12 @@ +import ProjectFindFile from '~/project_find_file'; +import ShortcutsFindFile from '~/shortcuts_find_file'; + +export default () => { + const findElement = document.querySelector('.js-file-finder'); + const projectFindFile = new ProjectFindFile($('.file-finder-holder'), { + url: findElement.dataset.fileFindUrl, + treeUrl: findElement.dataset.findTreeUrl, + blobUrlTemplate: findElement.dataset.blobUrlTemplate, + }); + new ShortcutsFindFile(projectFindFile); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/forks/new/index.js b/app/assets/javascripts/pages/projects/forks/new/index.js new file mode 100644 index 00000000000..7825eb01949 --- /dev/null +++ b/app/assets/javascripts/pages/projects/forks/new/index.js @@ -0,0 +1,5 @@ +import ProjectFork from '~/project_fork'; + +export default () => { + new ProjectFork(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/imports/show/index.js b/app/assets/javascripts/pages/projects/imports/show/index.js new file mode 100644 index 00000000000..378f7b3f38b --- /dev/null +++ b/app/assets/javascripts/pages/projects/imports/show/index.js @@ -0,0 +1,5 @@ +import ProjectImport from '~/project_import'; + +export default () => { + new ProjectImport(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js new file mode 100644 index 00000000000..26f0ad46114 --- /dev/null +++ b/app/assets/javascripts/pages/projects/init_blob.js @@ -0,0 +1,33 @@ +import LineHighlighter from '~/line_highlighter'; +import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import ShortcutsBlob from '~/shortcuts_blob'; +import BlobForkSuggestion from '~/blob/blob_fork_suggestion'; + +export default () => { + new LineHighlighter(); // eslint-disable-line no-new + + new BlobLinePermalinkUpdater( // eslint-disable-line no-new + document.querySelector('#blob-content-holder'), + '.diff-line-num[data-line-number]', + document.querySelectorAll('.js-data-file-blob-permalink-url, .js-blob-blame-link'), + ); + + const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url'); + const fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href'); + + new ShortcutsNavigation(); // eslint-disable-line no-new + + new ShortcutsBlob({ // eslint-disable-line no-new + skipResetBindings: true, + fileBlobPermalinkUrl, + }); + + new BlobForkSuggestion({ // eslint-disable-line no-new + openButtons: document.querySelectorAll('.js-edit-blob-link-fork-toggler'), + forkButtons: document.querySelectorAll('.js-fork-suggestion-button'), + cancelButtons: document.querySelectorAll('.js-cancel-fork-suggestion-button'), + suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'), + actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'), + }).init(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/edit/index.js b/app/assets/javascripts/pages/projects/issues/edit/index.js new file mode 100644 index 00000000000..7f27f379d8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/edit/index.js @@ -0,0 +1,5 @@ +import initForm from '../form'; + +export default () => { + initForm(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js new file mode 100644 index 00000000000..5c7daf84738 --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/form.js @@ -0,0 +1,16 @@ +/* eslint-disable no-new */ +import GLForm from '~/gl_form'; +import IssuableForm from '~/issuable_form'; +import LabelsSelect from '~/labels_select'; +import MilestoneSelect from '~/milestone_select'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import IssuableTemplateSelectors from '~/templates/issuable_template_selectors'; + +export default () => { + new ShortcutsNavigation(); + new GLForm($('.issue-form'), true); + new IssuableForm($('.issue-form')); + new LabelsSelect(); + new MilestoneSelect(); + new IssuableTemplateSelectors(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js new file mode 100644 index 00000000000..fd395a45f00 --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/index/index.js @@ -0,0 +1,18 @@ + +/* eslint-disable no-new */ + +import IssuableIndex from '~/issuable_index'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import UsersSelect from '~/users_select'; + +export default () => { + const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); + if (filteredSearchEnabled) { + const filteredSearchManager = new gl.FilteredSearchManager('issues'); + filteredSearchManager.setup(); + } + new IssuableIndex('issue_'); + + new ShortcutsNavigation(); + new UsersSelect(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/new/index.js b/app/assets/javascripts/pages/projects/issues/new/index.js new file mode 100644 index 00000000000..7f27f379d8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/new/index.js @@ -0,0 +1,5 @@ +import initForm from '../form'; + +export default () => { + initForm(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/show/index.js b/app/assets/javascripts/pages/projects/issues/show/index.js new file mode 100644 index 00000000000..48ed8fb2243 --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/show/index.js @@ -0,0 +1,13 @@ + +/* eslint-disable no-new */ +import initIssuableSidebar from '~/init_issuable_sidebar'; +import Issue from '~/issue'; +import ShortcutsIssuable from '~/shortcuts_issuable'; +import ZenMode from '~/zen_mode'; + +export default () => { + new Issue(); + new ShortcutsIssuable(); + new ZenMode(); + initIssuableSidebar(); +}; diff --git a/app/assets/javascripts/pages/projects/labels/edit/index.js b/app/assets/javascripts/pages/projects/labels/edit/index.js new file mode 100644 index 00000000000..72c5e4744ac --- /dev/null +++ b/app/assets/javascripts/pages/projects/labels/edit/index.js @@ -0,0 +1,3 @@ +import Labels from '~/labels'; + +export default () => new Labels(); diff --git a/app/assets/javascripts/pages/projects/labels/index/index.js b/app/assets/javascripts/pages/projects/labels/index/index.js new file mode 100644 index 00000000000..018345fa112 --- /dev/null +++ b/app/assets/javascripts/pages/projects/labels/index/index.js @@ -0,0 +1,3 @@ +import initLabels from '~/init_labels'; + +export default initLabels; diff --git a/app/assets/javascripts/pages/projects/labels/new/index.js b/app/assets/javascripts/pages/projects/labels/new/index.js new file mode 100644 index 00000000000..72c5e4744ac --- /dev/null +++ b/app/assets/javascripts/pages/projects/labels/new/index.js @@ -0,0 +1,3 @@ +import Labels from '~/labels'; + +export default () => new Labels(); diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js new file mode 100644 index 00000000000..a52bea03aa2 --- /dev/null +++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js @@ -0,0 +1,16 @@ +import IssuableIndex from '~/issuable_index'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import UsersSelect from '~/users_select'; + +export default () => { + const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); + + if (filteredSearchEnabled) { + const filteredSearchManager = new gl.FilteredSearchManager('merge_requests'); + filteredSearchManager.setup(); + } + + new IssuableIndex('merge_request_'); // eslint-disable-line no-new + new ShortcutsNavigation(); // eslint-disable-line no-new + new UsersSelect(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/milestones/edit/index.js b/app/assets/javascripts/pages/projects/milestones/edit/index.js new file mode 100644 index 00000000000..10e3979a36e --- /dev/null +++ b/app/assets/javascripts/pages/projects/milestones/edit/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(); diff --git a/app/assets/javascripts/pages/projects/milestones/new/index.js b/app/assets/javascripts/pages/projects/milestones/new/index.js new file mode 100644 index 00000000000..10e3979a36e --- /dev/null +++ b/app/assets/javascripts/pages/projects/milestones/new/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(); diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js new file mode 100644 index 00000000000..dacc2875c8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/tags/new/index.js @@ -0,0 +1,9 @@ +import RefSelectDropdown from '../../../../ref_select_dropdown'; +import ZenMode from '../../../../zen_mode'; +import GLForm from '../../../../gl_form'; + +export default () => { + new ZenMode(); // eslint-disable-line no-new + new GLForm($('.tag-form'), true); // eslint-disable-line no-new + new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js new file mode 100644 index 00000000000..28a0160f47d --- /dev/null +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -0,0 +1,15 @@ +import TreeView from '../../../../tree'; +import ShortcutsNavigation from '../../../../shortcuts_navigation'; +import BlobViewer from '../../../../blob/viewer'; +import NewCommitForm from '../../../../new_commit_form'; +import { ajaxGet } from '../../../../lib/utils/common_utils'; + +export default () => { + new ShortcutsNavigation(); // eslint-disable-line no-new + new TreeView(); // eslint-disable-line no-new + new BlobViewer(); // eslint-disable-line no-new + new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new + $('#tree-slider').waitForImages(() => + ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); +}; + diff --git a/app/assets/javascripts/pages/snippets/edit/index.js b/app/assets/javascripts/pages/snippets/edit/index.js new file mode 100644 index 00000000000..9c664b5f1ff --- /dev/null +++ b/app/assets/javascripts/pages/snippets/edit/index.js @@ -0,0 +1,3 @@ +import form from '../form'; + +export default form; diff --git a/app/assets/javascripts/pages/snippets/form.js b/app/assets/javascripts/pages/snippets/form.js new file mode 100644 index 00000000000..f996d3cd74e --- /dev/null +++ b/app/assets/javascripts/pages/snippets/form.js @@ -0,0 +1,7 @@ +import GLForm from '~/gl_form'; +import ZenMode from '~/zen_mode'; + +export default () => { + new GLForm($('.snippet-form'), false); // eslint-disable-line no-new + new ZenMode(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/snippets/new/index.js b/app/assets/javascripts/pages/snippets/new/index.js new file mode 100644 index 00000000000..9c664b5f1ff --- /dev/null +++ b/app/assets/javascripts/pages/snippets/new/index.js @@ -0,0 +1,3 @@ +import form from '../form'; + +export default form; diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index 942acc8c412..e08c2092680 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -92,6 +92,7 @@ <loading-icon v-if="isLoading" size="2" + class="prepend-top-default append-bottom-default" /> </div> </template> diff --git a/app/assets/javascripts/projects_dropdown/index.js b/app/assets/javascripts/projects_dropdown/index.js index 2660da3c558..e78ebce2923 100644 --- a/app/assets/javascripts/projects_dropdown/index.js +++ b/app/assets/javascripts/projects_dropdown/index.js @@ -19,11 +19,8 @@ document.addEventListener('DOMContentLoaded', () => { return; } - $(navEl).on('show.bs.dropdown', (e) => { - const dropdownEl = $(e.currentTarget).find('.projects-dropdown-menu'); - dropdownEl.one('transitionend', () => { - eventHub.$emit('dropdownOpen'); - }); + $(navEl).on('shown.bs.dropdown', () => { + eventHub.$emit('dropdownOpen'); }); // eslint-disable-next-line no-new diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js index 0a9fdb074e5..2948baeab11 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_create.js +++ b/app/assets/javascripts/protected_branches/protected_branch_create.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; -import ProtectedBranchDropdown from './protected_branch_dropdown'; +import CreateItemDropdown from '../create_item_dropdown'; import AccessorUtilities from '../lib/utils/accessor'; const PB_LOCAL_STORAGE_KEY = 'protected-branches-defaults'; @@ -35,10 +35,12 @@ export default class ProtectedBranchCreate { onSelect: this.onSelectCallback, }); - // Protected branch dropdown - this.protectedBranchDropdown = new ProtectedBranchDropdown({ + this.createItemDropdown = new CreateItemDropdown({ $dropdown: $protectedBranchDropdown, + defaultToggleLabel: 'Protected Branch', + fieldName: 'protected_branch[name]', onSelect: this.onSelectCallback, + getData: ProtectedBranchCreate.getProtectedBranches, }); this.loadPreviousSelection($allowedToMergeDropdown.data('glDropdown'), $allowedToPushDropdown.data('glDropdown')); @@ -60,6 +62,10 @@ export default class ProtectedBranchCreate { this.$form.find('input[type="submit"]').attr('disabled', completedForm); } + static getProtectedBranches(term, callback) { + callback(gon.open_branches); + } + loadPreviousSelection(mergeDropdown, pushDropdown) { let mergeIndex = 0; let pushIndex = 0; diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js deleted file mode 100644 index 678882a8d2c..00000000000 --- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js +++ /dev/null @@ -1,90 +0,0 @@ -import _ from 'underscore'; - -export default class ProtectedBranchDropdown { - /** - * @param {Object} options containing - * `$dropdown` target element - * `onSelect` event callback - * $dropdown must be an element created using `dropdown_branch()` rails helper - */ - constructor(options) { - this.onSelect = options.onSelect; - this.$dropdown = options.$dropdown; - this.$dropdownContainer = this.$dropdown.parent(); - this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer'); - this.$protectedBranch = this.$dropdownContainer.find('.js-create-new-protected-branch'); - - this.buildDropdown(); - this.bindEvents(); - - // Hide footer - this.toggleFooter(true); - } - - buildDropdown() { - this.$dropdown.glDropdown({ - data: this.getProtectedBranches.bind(this), - filterable: true, - remote: false, - search: { - fields: ['title'], - }, - selectable: true, - toggleLabel(selected) { - return (selected && 'id' in selected) ? selected.title : 'Protected Branch'; - }, - fieldName: 'protected_branch[name]', - text(protectedBranch) { - return _.escape(protectedBranch.title); - }, - id(protectedBranch) { - return _.escape(protectedBranch.id); - }, - onFilter: this.toggleCreateNewButton.bind(this), - clicked: (options) => { - options.e.preventDefault(); - this.onSelect(); - }, - }); - } - - bindEvents() { - this.$protectedBranch.on('click', this.onClickCreateWildcard.bind(this)); - } - - onClickCreateWildcard(e) { - e.preventDefault(); - - // Refresh the dropdown's data, which ends up calling `getProtectedBranches` - this.$dropdown.data('glDropdown').remote.execute(); - this.$dropdown.data('glDropdown').selectRowAtIndex(); - } - - getProtectedBranches(term, callback) { - if (this.selectedBranch) { - callback(gon.open_branches.concat(this.selectedBranch)); - } else { - callback(gon.open_branches); - } - } - - toggleCreateNewButton(branchName) { - if (branchName) { - this.selectedBranch = { - title: branchName, - id: branchName, - text: branchName, - }; - - this.$dropdownContainer - .find('.js-create-new-protected-branch code') - .text(branchName); - } - - this.toggleFooter(!branchName); - } - - toggleFooter(toggleState) { - this.$dropdownFooter.toggleClass('hidden', toggleState); - } -} diff --git a/app/assets/javascripts/protected_tags/protected_tag_create.js b/app/assets/javascripts/protected_tags/protected_tag_create.js index 91bd140bd12..d1e4a75c17b 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_create.js +++ b/app/assets/javascripts/protected_tags/protected_tag_create.js @@ -1,5 +1,5 @@ import ProtectedTagAccessDropdown from './protected_tag_access_dropdown'; -import ProtectedTagDropdown from './protected_tag_dropdown'; +import CreateItemDropdown from '../create_item_dropdown'; export default class ProtectedTagCreate { constructor() { @@ -24,9 +24,12 @@ export default class ProtectedTagCreate { $allowedToCreateDropdown.data('glDropdown').selectRowAtIndex(0); // Protected tag dropdown - this.protectedTagDropdown = new ProtectedTagDropdown({ + this.createItemDropdown = new CreateItemDropdown({ $dropdown: this.$form.find('.js-protected-tag-select'), + defaultToggleLabel: 'Protected Tag', + fieldName: 'protected_tag[name]', onSelect: this.onSelectCallback, + getData: ProtectedTagCreate.getProtectedTags, }); } @@ -38,4 +41,8 @@ export default class ProtectedTagCreate { this.$form.find('input[type="submit"]').attr('disabled', !($tagInput.val() && $allowedToCreateInput.length)); } + + static getProtectedTags(term, callback) { + callback(gon.open_tags); + } } diff --git a/app/assets/javascripts/shared/milestones/form.js b/app/assets/javascripts/shared/milestones/form.js new file mode 100644 index 00000000000..db466f722c4 --- /dev/null +++ b/app/assets/javascripts/shared/milestones/form.js @@ -0,0 +1,9 @@ +import ZenMode from '../../zen_mode'; +import DueDateSelectors from '../../due_date_select'; +import GLForm from '../../gl_form'; + +export default (initGFM = true) => { + new ZenMode(); // eslint-disable-line no-new + new DueDateSelectors(); // eslint-disable-line no-new + new GLForm($('.milestone-form'), initGFM); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue index 7226076a8fc..d69d100a26c 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue @@ -1,12 +1,22 @@ <script> - /* eslint-disable vue/require-default-prop */ - import { __ } from '../../../locale'; + import { __ } from '~/locale'; + import icon from '~/vue_shared/components/icon.vue'; + import toggleButton from '~/vue_shared/components/toggle_button.vue'; + import tooltip from '~/vue_shared/directives/tooltip'; import eventHub from '../../event_hub'; - import loadingButton from '../../../vue_shared/components/loading_button.vue'; + + const ICON_ON = 'notifications'; + const ICON_OFF = 'notifications-off'; + const LABEL_ON = __('Notifications on'); + const LABEL_OFF = __('Notifications off'); export default { + directives: { + tooltip, + }, components: { - loadingButton, + icon, + toggleButton, }, props: { loading: { @@ -17,22 +27,23 @@ subscribed: { type: Boolean, required: false, + default: null, }, id: { type: Number, required: false, + default: null, }, }, computed: { - buttonLabel() { - let label; - if (this.subscribed === false) { - label = __('Subscribe'); - } else if (this.subscribed === true) { - label = __('Unsubscribe'); - } - - return label; + showLoadingState() { + return this.subscribed === null; + }, + notificationIcon() { + return this.subscribed ? ICON_ON : ICON_OFF; + }, + notificationTooltip() { + return this.subscribed ? LABEL_ON : LABEL_OFF; }, }, methods: { @@ -46,21 +57,29 @@ <template> <div> <div class="sidebar-collapsed-icon"> - <i - class="fa fa-rss" - aria-hidden="true" + <span + v-tooltip + :title="notificationTooltip" + data-container="body" + data-placement="left" > - </i> + <icon + :name="notificationIcon" + :size="16" + aria-hidden="true" + class="sidebar-item-icon is-active" + /> + </span> </div> <span class="issuable-header-text hide-collapsed pull-left"> {{ __('Notifications') }} </span> - <loading-button - ref="loadingButton" - class="btn btn-default pull-right hide-collapsed js-issuable-subscribe-button" - :loading="loading" - :label="buttonLabel" - @click="toggleSubscription" + <toggle-button + ref="toggleButton" + class="pull-right hide-collapsed js-issuable-subscribe-button" + :is-loading="showLoadingState" + :value="subscribed" + @change="toggleSubscription" /> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js index e82fb979162..60f42c46ffe 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js @@ -1,6 +1,7 @@ import successSvg from 'icons/_icon_status_success.svg'; import warningSvg from 'icons/_icon_status_warning.svg'; import simplePoll from '~/lib/utils/simple_poll'; +import MergeRequest from '../../../merge_request'; import Flash from '../../../flash'; import statusIcon from '../mr_widget_status_icon'; import eventHub from '../../event_hub'; @@ -165,11 +166,9 @@ export default { // If state is merged we should update the widget and stop the polling eventHub.$emit('MRWidgetUpdateRequested'); eventHub.$emit('FetchActionsContent'); - if (window.mergeRequest) { - window.mergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged'); - window.mergeRequest.hideCloseButton(); - window.mergeRequest.decreaseCounter(); - } + MergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged'); + MergeRequest.hideCloseButton(); + MergeRequest.decreaseCounter(); stopPolling(); // If user checked remove source branch and we didn't remove the branch yet diff --git a/app/assets/javascripts/vue_shared/components/modal.vue b/app/assets/javascripts/vue_shared/components/modal.vue index c103c45c7dd..8227428d8ba 100644 --- a/app/assets/javascripts/vue_shared/components/modal.vue +++ b/app/assets/javascripts/vue_shared/components/modal.vue @@ -122,7 +122,7 @@ > <button type="button" - class="btn pull-left" + class="btn" :class="btnCancelKindClass" @click="emitCancel($event)" data-dismiss="modal" @@ -132,7 +132,7 @@ <button v-if="primaryButtonLabel" type="button" - class="btn pull-right js-primary-button" + class="btn js-primary-button" :disabled="submitDisabled" :class="btnKindClass" @click="emitSubmit($event)" diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue new file mode 100644 index 00000000000..86f06c8d266 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue @@ -0,0 +1,127 @@ +<script> +import tooltip from '~/vue_shared/directives/tooltip'; + +export default { + directives: { + tooltip, + }, + props: { + cssClass: { + type: String, + required: false, + default: '', + }, + successLabel: { + type: String, + required: true, + }, + failureLabel: { + type: String, + required: true, + }, + neutralLabel: { + type: String, + required: true, + }, + successCount: { + type: Number, + required: true, + }, + failureCount: { + type: Number, + required: true, + }, + totalCount: { + type: Number, + required: true, + }, + }, + computed: { + neutralCount() { + return this.totalCount - this.successCount - this.failureCount; + }, + successPercent() { + return this.getPercent(this.successCount); + }, + successBarStyle() { + return this.barStyle(this.successPercent); + }, + successTooltip() { + return this.getTooltip(this.successLabel, this.successCount); + }, + failurePercent() { + return this.getPercent(this.failureCount); + }, + failureBarStyle() { + return this.barStyle(this.failurePercent); + }, + failureTooltip() { + return this.getTooltip(this.failureLabel, this.failureCount); + }, + neutralPercent() { + return this.getPercent(this.neutralCount); + }, + neutralBarStyle() { + return this.barStyle(this.neutralPercent); + }, + neutralTooltip() { + return this.getTooltip(this.neutralLabel, this.neutralCount); + }, + }, + methods: { + getPercent(count) { + return Math.ceil((count / this.totalCount) * 100); + }, + barStyle(percent) { + return `width: ${percent}%;`; + }, + getTooltip(label, count) { + return `${label}: ${count}`; + }, + }, +}; +</script> + +<template> + <div + class="stacked-progress-bar" + :class="cssClass" + > + <span + v-if="!totalCount" + class="status-unavailable" + > + {{ __("Not available") }} + </span> + <span + v-tooltip + v-if="successPercent" + class="status-green" + data-placement="bottom" + :title="successTooltip" + :style="successBarStyle" + > + {{ successPercent }}% + </span> + <span + v-tooltip + v-if="neutralPercent" + class="status-neutral" + data-placement="bottom" + :title="neutralTooltip" + :style="neutralBarStyle" + > + {{ neutralPercent }}% + </span> + <span + v-tooltip + v-if="failurePercent" + class="status-red" + data-placement="bottom" + :title="failureTooltip" + :style="failureBarStyle" + > + {{ failurePercent }}% + </span> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue index 2b12718ae96..09031d3ffa1 100644 --- a/app/assets/javascripts/vue_shared/components/toggle_button.vue +++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue @@ -23,11 +23,12 @@ name: { type: String, required: false, - default: '', + default: null, }, value: { type: Boolean, - required: true, + required: false, + default: null, }, disabledInput: { type: Boolean, @@ -61,6 +62,7 @@ <template> <label class="toggle-wrapper"> <input + v-if="name" type="hidden" :name="name" :value="value" diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 43b16d3cf7d..cff47ea76ec 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -59,3 +59,4 @@ @import "framework/snippets"; @import "framework/memory_graph"; @import "framework/responsive_tables"; +@import "framework/stacked-progress-bar"; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index d1b3754d4ef..1d2303a3a2b 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -666,6 +666,16 @@ } } +.dropdown-create-new-item-button { + @include dropdown-link; + + width: 100%; + background-color: transparent; + border: 0; + text-align: left; + text-overflow: ellipsis; +} + .dropdown-loading { position: absolute; top: 0; diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index ad160f37641..3b7256f3000 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -104,7 +104,10 @@ img { height: 28px; - margin-right: 8px; + + + .logo-text { + margin-left: 8px; + } } &.wrap { diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 1be66d0ab21..924472f2d7e 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -1,4 +1,5 @@ .modal-header { + background-color: $modal-body-bg; padding: #{3 * $grid-size} #{2 * $grid-size}; .page-title { @@ -8,6 +9,7 @@ .modal-body { background-color: $modal-body-bg; + min-height: $modal-body-height; position: relative; padding: #{3 * $grid-size} #{2 * $grid-size}; @@ -20,6 +22,30 @@ } } +.modal-footer { + display: flex; + flex-direction: row; + + .btn + .btn { + margin-left: $grid-size; + } + + @media (max-width: $screen-xs-max) { + flex-direction: column; + + .btn + .btn { + margin-left: 0; + margin-top: $grid-size; + } + } + + @media (min-width: $screen-sm-min) { + .btn:first-of-type { + margin-left: auto; + } + } +} + body.modal-open { overflow: hidden; } @@ -32,12 +58,6 @@ body.modal-open { } } -@media (min-width: $screen-md-min) { - .modal-dialog { - width: 860px; - } -} - @media (min-width: $screen-lg-min) { .modal-full { width: 98%; diff --git a/app/assets/stylesheets/framework/stacked-progress-bar.scss b/app/assets/stylesheets/framework/stacked-progress-bar.scss new file mode 100644 index 00000000000..4869cda73e5 --- /dev/null +++ b/app/assets/stylesheets/framework/stacked-progress-bar.scss @@ -0,0 +1,54 @@ +.stacked-progress-bar { + display: flex; + height: 16px; + border-radius: 10px; + overflow: hidden; + background-color: $theme-gray-100; + + .status-unavailable, + .status-green, + .status-neutral, + .status-red, { + height: 100%; + min-width: 25px; + padding: 0 5px; + font-size: $tooltip-font-size; + font-weight: normal; + color: $white-light; + line-height: 16px; + + &:hover { + cursor: pointer; + } + } + + .status-unavailable { + padding: 0 10px; + color: $theme-gray-700; + } + + .status-green { + background-color: $green-500; + + &:hover { + background-color: $green-600; + } + } + + .status-neutral { + background-color: $theme-gray-200; + color: $gl-gray-dark; + + &:hover { + background-color: $theme-gray-300; + } + } + + .status-red { + background-color: $red-500; + + &:hover { + background-color: $red-600; + } + } +} diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index a23131e0818..d04e555769b 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -194,6 +194,6 @@ $modal-body-bg: $white-light; //** Modal footer border color // $modal-footer-border-color: $modal-header-border-color -// $modal-lg: 900px -// $modal-md: 600px +$modal-lg: 860px; +$modal-md: 540px; // $modal-sm: 300px diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index ef1520f1f63..da18ddf78d3 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -733,3 +733,8 @@ $popup-box-shadow-color: rgba(90, 90, 90, 0.05); Multi file editor */ $border-color-settings: #e1e1e1; + +/* +Modals +*/ +$modal-body-height: 134px; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index ae9a8b0182c..759719a72da 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -162,10 +162,6 @@ border: 0; } - span { - display: inline-block; - } - .select2-container span { margin-top: 0; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 61a76d0387a..bf41005b6d5 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -895,17 +895,6 @@ pre.light-well { } } -.create-new-protected-branch-button, -.create-new-protected-tag-button { - @include dropdown-link; - - width: 100%; - background-color: transparent; - border: 0; - text-align: left; - text-overflow: ellipsis; -} - .protected-branches-list, .protected-tags-list { margin-bottom: 30px; |