diff options
author | Bryce Johnson <bryce@gitlab.com> | 2017-05-15 18:12:43 -0400 |
---|---|---|
committer | Bryce Johnson <bryce@gitlab.com> | 2017-06-06 09:16:27 -0400 |
commit | c9a67266d2a79bd55f944516716a59300c1844f6 (patch) | |
tree | 73dd4086376d3674faf9484ff28c6d7edfe9765b /app/assets | |
parent | 34f925fe0bebdc7212c1c960678114a4e44828ef (diff) | |
download | gitlab-ce-c9a67266d2a79bd55f944516716a59300c1844f6.tar.gz |
Move issuable bulk edit form into a new sidebar.28340-mass-edit-issues-and-mrs-from-sidebar
Diffstat (limited to 'app/assets')
-rw-r--r-- | app/assets/javascripts/dispatcher.js | 9 | ||||
-rw-r--r-- | app/assets/javascripts/issuable_bulk_update_actions.js | 159 | ||||
-rw-r--r-- | app/assets/javascripts/issuable_bulk_update_sidebar.js | 165 | ||||
-rw-r--r-- | app/assets/javascripts/issuable_index.js (renamed from app/assets/javascripts/issuable.js) | 81 | ||||
-rw-r--r-- | app/assets/javascripts/issues_bulk_assignment.js | 166 | ||||
-rw-r--r-- | app/assets/javascripts/labels_select.js | 15 | ||||
-rw-r--r-- | app/assets/javascripts/main.js | 3 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/common.scss | 6 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/filters.scss | 12 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/mobile.scss | 4 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/sidebar.scss | 36 |
11 files changed, 407 insertions, 249 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index baa20d0c34a..aa0871eb771 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -3,7 +3,7 @@ /* global ActiveTabMemoizer */ /* global ShortcutsNavigation */ /* global Build */ -/* global Issuable */ +/* global IssuableIndex */ /* global ShortcutsIssuable */ /* global ZenMode */ /* global Milestone */ @@ -127,10 +127,9 @@ import ShortcutsBlob from './shortcuts_blob'; const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests'); filteredSearchManager.setup(); } - Issuable.init(); - new gl.IssuableBulkActions({ - prefixId: page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_', - }); + const pagePrefix = page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_'; + IssuableIndex.init(pagePrefix); + shortcut_handler = new ShortcutsNavigation(); new UsersSelect(); break; diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js new file mode 100644 index 00000000000..e46c0e90255 --- /dev/null +++ b/app/assets/javascripts/issuable_bulk_update_actions.js @@ -0,0 +1,159 @@ +/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */ +/* global IssuableIndex */ +/* global Flash */ + +export default { + init({ container, form, issues, prefixId } = {}) { + this.prefixId = prefixId || 'issue_'; + this.form = form || this.getElement('.bulk-update'); + this.$labelDropdown = this.form.find('.js-label-select'); + this.issues = issues || this.getElement('.issues-list .issue'); + this.willUpdateLabels = false; + this.bindEvents(); + }, + + bindEvents() { + return this.form.off('submit').on('submit', this.onFormSubmit.bind(this)); + }, + + onFormSubmit(e) { + e.preventDefault(); + return this.submit(); + }, + + submit() { + const _this = this; + const xhr = $.ajax({ + url: this.form.attr('action'), + method: this.form.attr('method'), + dataType: 'JSON', + data: this.getFormDataAsObject() + }); + xhr.done(() => window.location.reload()); + xhr.fail(() => this.onFormSubmitFailure()); + }, + + onFormSubmitFailure() { + this.form.find('[type="submit"]').enable(); + return new Flash("Issue update failed"); + }, + + getSelectedIssues() { + return this.issues.has('.selected_issue:checked'); + }, + + getLabelsFromSelection() { + const labels = []; + this.getSelectedIssues().map(function() { + const labelsData = $(this).data('labels'); + if (labelsData) { + return labelsData.map(function(labelId) { + if (labels.indexOf(labelId) === -1) { + return labels.push(labelId); + } + }); + } + }); + return labels; + }, + + /** + * Will return only labels that were marked previously and the user has unmarked + * @return {Array} Label IDs + */ + + getUnmarkedIndeterminedLabels() { + const result = []; + const labelsToKeep = this.$labelDropdown.data('indeterminate'); + + this.getLabelsFromSelection().forEach((id) => { + if (labelsToKeep.indexOf(id) === -1) { + result.push(id); + } + }); + + return result; + }, + + /** + * Simple form serialization, it will return just what we need + * Returns key/value pairs from form data + */ + + getFormDataAsObject() { + const formData = { + update: { + state_event: this.form.find('input[name="update[state_event]"]').val(), + // For Merge Requests + assignee_id: this.form.find('input[name="update[assignee_id]"]').val(), + // For Issues + assignee_ids: [this.form.find('input[name="update[assignee_ids][]"]').val()], + milestone_id: this.form.find('input[name="update[milestone_id]"]').val(), + issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(), + subscription_event: this.form.find('input[name="update[subscription_event]"]').val(), + add_label_ids: [], + remove_label_ids: [] + } + }; + if (this.willUpdateLabels) { + formData.update.add_label_ids = this.$labelDropdown.data('marked'); + formData.update.remove_label_ids = this.$labelDropdown.data('unmarked'); + } + return formData; + }, + + setOriginalDropdownData() { + const $labelSelect = $('.bulk-update .js-label-select'); + $labelSelect.data('common', this.getOriginalCommonIds()); + $labelSelect.data('marked', this.getOriginalMarkedIds()); + $labelSelect.data('indeterminate', this.getOriginalIndeterminateIds()); + }, + + // From issuable's initial bulk selection + getOriginalCommonIds() { + const labelIds = []; + + this.getElement('.selected_issue:checked').each((i, el) => { + labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels')); + }); + return _.intersection.apply(this, labelIds); + }, + + // From issuable's initial bulk selection + getOriginalMarkedIds() { + const labelIds = []; + this.getElement('.selected_issue:checked').each((i, el) => { + labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels')); + }); + return _.intersection.apply(this, labelIds); + }, + + // From issuable's initial bulk selection + getOriginalIndeterminateIds() { + const uniqueIds = []; + const labelIds = []; + let issuableLabels = []; + + // Collect unique label IDs for all checked issues + this.getElement('.selected_issue:checked').each((i, el) => { + issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'); + issuableLabels.forEach((labelId) => { + // Store unique IDs + if (uniqueIds.indexOf(labelId) === -1) { + uniqueIds.push(labelId); + } + }); + // Store array of IDs per issuable + labelIds.push(issuableLabels); + }); + // Add uniqueIds to add it as argument for _.intersection + labelIds.unshift(uniqueIds); + // Return IDs that are present but not in all selected issueables + return _.difference(uniqueIds, _.intersection.apply(this, labelIds)); + }, + + getElement(selector) { + this.scopeEl = this.scopeEl || $('.content'); + return this.scopeEl.find(selector); + }, +}; diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js new file mode 100644 index 00000000000..84bd2e092e6 --- /dev/null +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -0,0 +1,165 @@ +/* eslint-disable class-methods-use-this, no-new */ +/* global LabelsSelect */ +/* global MilestoneSelect */ +/* global IssueStatusSelect */ +/* global SubscriptionSelect */ + +import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; + +const HIDDEN_CLASS = 'hidden'; +const DISABLED_CONTENT_CLASS = 'disabled-content'; +const SIDEBAR_EXPANDED_CLASS = 'right-sidebar-expanded issuable-bulk-update-sidebar'; +const SIDEBAR_COLLAPSED_CLASS = 'right-sidebar-collapsed issuable-bulk-update-sidebar'; + +export default class IssuableBulkUpdateSidebar { + constructor() { + this.initDomElements(); + this.bindEvents(); + this.initDropdowns(); + this.setupBulkUpdateActions(); + } + + initDomElements() { + this.$page = $('.page-with-sidebar'); + this.$sidebar = $('.right-sidebar'); + this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide'); + this.$bulkEditSubmitBtn = $('.update-selected-issues'); + this.$bulkUpdateEnableBtn = $('.js-bulk-update-toggle'); + this.$otherFilters = $('.issues-other-filters'); + this.$checkAllContainer = $('.check-all-holder'); + this.$issueChecks = $('.issue-check'); + this.$issuesList = $('.selected_issue'); + this.$issuableIdsInput = $('#update_issuable_ids'); + } + + bindEvents() { + this.$bulkUpdateEnableBtn.on('click', e => this.toggleBulkEdit(e, true)); + this.$bulkEditCancelBtn.on('click', e => this.toggleBulkEdit(e, false)); + this.$checkAllContainer.on('click', e => this.selectAll(e)); + this.$issuesList.on('change', () => this.updateFormState()); + this.$bulkEditSubmitBtn.on('click', () => this.prepForSubmit()); + this.$checkAllContainer.on('click', () => this.updateFormState()); + } + + initDropdowns() { + new LabelsSelect(); + new MilestoneSelect(); + new IssueStatusSelect(); + new SubscriptionSelect(); + } + + getNavHeight() { + const navbarHeight = $('.navbar-gitlab').outerHeight(); + const layoutNavHeight = $('.layout-nav').outerHeight(); + const subNavScroll = $('.sub-nav-scroll').outerHeight(); + return navbarHeight + layoutNavHeight + subNavScroll; + } + + initSidebar() { + if (!this.navHeight) { + this.navHeight = this.getNavHeight(); + } + + if (!this.sidebarInitialized) { + $(document).off('scroll').on('scroll', _.throttle(this.setSidebarHeight, 10).bind(this)); + $(window).off('resize').on('resize', _.throttle(this.setSidebarHeight, 10).bind(this)); + this.sidebarInitialized = true; + } + } + + setupBulkUpdateActions() { + IssuableBulkUpdateActions.setOriginalDropdownData(); + } + + updateFormState() { + const noCheckedIssues = !$('.selected_issue:checked').length; + + this.toggleSubmitButtonDisabled(noCheckedIssues); + this.updateSelectedIssuableIds(); + + IssuableBulkUpdateActions.setOriginalDropdownData(); + } + + prepForSubmit() { + // if submit button is disabled, submission is blocked. This ensures we disable after + // form submission is carried out + setTimeout(() => this.$bulkEditSubmitBtn.disable()); + this.updateSelectedIssuableIds(); + } + + toggleBulkEdit(e, enable) { + e.preventDefault(); + + this.toggleSidebarDisplay(enable); + this.toggleBulkEditButtonDisabled(enable); + this.toggleOtherFiltersDisabled(enable); + this.toggleCheckboxDisplay(enable); + + if (enable) { + this.initSidebar(); + } + } + + updateSelectedIssuableIds() { + this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds()); + } + + selectAll() { + const checkAllButtonState = this.$checkAllContainer.find('input').prop('checked'); + + this.$issuesList.prop('checked', checkAllButtonState); + } + + toggleSidebarDisplay(show) { + this.$page.toggleClass(SIDEBAR_EXPANDED_CLASS, show); + this.$page.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show); + this.$sidebar.toggleClass(SIDEBAR_EXPANDED_CLASS, show); + this.$sidebar.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show); + } + + toggleBulkEditButtonDisabled(disable) { + if (disable) { + this.$bulkUpdateEnableBtn.disable(); + } else { + this.$bulkUpdateEnableBtn.enable(); + } + } + + toggleCheckboxDisplay(show) { + this.$checkAllContainer.toggleClass(HIDDEN_CLASS, !show); + this.$issueChecks.toggleClass(HIDDEN_CLASS, !show); + } + + toggleOtherFiltersDisabled(disable) { + this.$otherFilters.toggleClass(DISABLED_CONTENT_CLASS, disable); + } + + toggleSubmitButtonDisabled(disable) { + if (disable) { + this.$bulkEditSubmitBtn.disable(); + } else { + this.$bulkEditSubmitBtn.enable(); + } + } + // loosely based on method of the same name in right_sidebar.js + setSidebarHeight() { + const currentScrollDepth = window.pageYOffset || 0; + const diff = this.navHeight - currentScrollDepth; + + if (diff > 0) { + this.$sidebar.outerHeight(window.innerHeight - diff); + } else { + this.$sidebar.outerHeight('100%'); + } + } + + static getCheckedIssueIds() { + const $checkedIssues = $('.selected_issue:checked'); + + if ($checkedIssues.length > 0) { + return $.map($checkedIssues, value => $(value).data('id')); + } + + return []; + } +} diff --git a/app/assets/javascripts/issuable.js b/app/assets/javascripts/issuable_index.js index 3bfce32768a..5c96646def8 100644 --- a/app/assets/javascripts/issuable.js +++ b/app/assets/javascripts/issuable_index.js @@ -1,30 +1,33 @@ /* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */ -/* global Issuable */ +/* global IssuableIndex */ + +import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; +import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; ((global) => { var issuable_created; issuable_created = false; - global.Issuable = { - init: function() { - Issuable.initTemplates(); - Issuable.initSearch(); - Issuable.initChecks(); - Issuable.initResetFilters(); - Issuable.resetIncomingEmailToken(); - return Issuable.initLabelFilterRemove(); + global.IssuableIndex = { + init: function(pagePrefix) { + IssuableIndex.initTemplates(); + IssuableIndex.initSearch(); + IssuableIndex.initBulkUpdate(pagePrefix); + IssuableIndex.initResetFilters(); + IssuableIndex.resetIncomingEmailToken(); + IssuableIndex.initLabelFilterRemove(); }, initTemplates: function() { - return Issuable.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>'); + return IssuableIndex.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>'); }, initSearch: function() { const $searchInput = $('#issuable_search'); - Issuable.initSearchState($searchInput); + IssuableIndex.initSearchState($searchInput); // `immediate` param set to false debounces on the `trailing` edge, lets user finish typing - const debouncedExecSearch = _.debounce(Issuable.executeSearch, 1000, false); + const debouncedExecSearch = _.debounce(IssuableIndex.executeSearch, 1000, false); $searchInput.off('keyup').on('keyup', debouncedExecSearch); @@ -37,16 +40,16 @@ initSearchState: function($searchInput) { const currentSearchVal = $searchInput.val(); - Issuable.searchState = { + IssuableIndex.searchState = { elem: $searchInput, current: currentSearchVal }; - Issuable.maybeFocusOnSearch(); + IssuableIndex.maybeFocusOnSearch(); }, accessSearchPristine: function(set) { // store reference to previous value to prevent search on non-mutating keyup - const state = Issuable.searchState; + const state = IssuableIndex.searchState; const currentSearchVal = state.elem.val(); if (set) { @@ -56,10 +59,10 @@ } }, maybeFocusOnSearch: function() { - const currentSearchVal = Issuable.searchState.current; + const currentSearchVal = IssuableIndex.searchState.current; if (currentSearchVal && currentSearchVal !== '') { const queryLength = currentSearchVal.length; - const $searchInput = Issuable.searchState.elem; + const $searchInput = IssuableIndex.searchState.elem; /* The following ensures that the cursor is initially placed at * the end of search input when focus is applied. It accounts @@ -80,7 +83,7 @@ const $searchValue = $search.val(); const $filtersForm = $('.js-filter-form'); const $input = $(`input[name='${$searchName}']`, $filtersForm); - const isPristine = Issuable.accessSearchPristine(); + const isPristine = IssuableIndex.accessSearchPristine(); if (isPristine) { return; @@ -92,7 +95,7 @@ $input.val($searchValue); } - Issuable.filterResults($filtersForm); + IssuableIndex.filterResults($filtersForm); }, initLabelFilterRemove: function() { return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) { @@ -103,7 +106,7 @@ return this.value === $button.data('label'); }).remove(); // Submit the form to get new data - Issuable.filterResults($('.filter-form')); + IssuableIndex.filterResults($('.filter-form')); }); }, filterResults: (function(_this) { @@ -132,38 +135,18 @@ gl.utils.visitUrl(baseIssuesUrl); }); }, - initChecks: function() { - this.issuableBulkActions = $('.bulk-update').data('bulkActions'); - $('.check_all_issues').off('click').on('click', function() { - $('.selected_issue').prop('checked', this.checked); - return Issuable.checkChanged(); - }); - return $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(this)); - }, - checkChanged: function() { - const $checkedIssues = $('.selected_issue:checked'); - const $updateIssuesIds = $('#update_issuable_ids'); - const $issuesOtherFilters = $('.issues-other-filters'); - const $issuesBulkUpdate = $('.issues_bulk_update'); - - this.issuableBulkActions.willUpdateLabels = false; - this.issuableBulkActions.setOriginalDropdownData(); - - if ($checkedIssues.length > 0) { - const ids = $.map($checkedIssues, function(value) { - return $(value).data('id'); + initBulkUpdate: function(pagePrefix) { + const userCanBulkUpdate = $('.issues-bulk-update').length > 0; + const alreadyInitialized = !!this.bulkUpdateSidebar; + + if (userCanBulkUpdate && !alreadyInitialized) { + IssuableBulkUpdateActions.init({ + prefixId: pagePrefix, }); - $updateIssuesIds.val(ids); - $issuesOtherFilters.hide(); - $issuesBulkUpdate.show(); - } else { - $updateIssuesIds.val([]); - $issuesBulkUpdate.hide(); - $issuesOtherFilters.show(); + + this.bulkUpdateSidebar = new IssuableBulkUpdateSidebar(); } - return true; }, - resetIncomingEmailToken: function() { $('.incoming-email-token-reset').on('click', function(e) { e.preventDefault(); diff --git a/app/assets/javascripts/issues_bulk_assignment.js b/app/assets/javascripts/issues_bulk_assignment.js deleted file mode 100644 index fee3429e2b8..00000000000 --- a/app/assets/javascripts/issues_bulk_assignment.js +++ /dev/null @@ -1,166 +0,0 @@ -/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */ -/* global Issuable */ -/* global Flash */ - -((global) => { - class IssuableBulkActions { - constructor({ container, form, issues, prefixId } = {}) { - this.prefixId = prefixId || 'issue_'; - this.form = form || this.getElement('.bulk-update'); - this.$labelDropdown = this.form.find('.js-label-select'); - this.issues = issues || this.getElement('.issues-list .issue'); - this.form.data('bulkActions', this); - this.willUpdateLabels = false; - this.bindEvents(); - // Fixes bulk-assign not working when navigating through pages - Issuable.initChecks(); - } - - bindEvents() { - return this.form.off('submit').on('submit', this.onFormSubmit.bind(this)); - } - - onFormSubmit(e) { - e.preventDefault(); - return this.submit(); - } - - submit() { - const _this = this; - const xhr = $.ajax({ - url: this.form.attr('action'), - method: this.form.attr('method'), - dataType: 'JSON', - data: this.getFormDataAsObject() - }); - xhr.done(() => window.location.reload()); - xhr.fail(() => new Flash("Issue update failed")); - return xhr.always(this.onFormSubmitAlways.bind(this)); - } - - onFormSubmitAlways() { - return this.form.find('[type="submit"]').enable(); - } - - getSelectedIssues() { - return this.issues.has('.selected_issue:checked'); - } - - getLabelsFromSelection() { - const labels = []; - this.getSelectedIssues().map(function() { - const labelsData = $(this).data('labels'); - if (labelsData) { - return labelsData.map(function(labelId) { - if (labels.indexOf(labelId) === -1) { - return labels.push(labelId); - } - }); - } - }); - return labels; - } - - /** - * Will return only labels that were marked previously and the user has unmarked - * @return {Array} Label IDs - */ - - getUnmarkedIndeterminedLabels() { - const result = []; - const labelsToKeep = this.$labelDropdown.data('indeterminate'); - - this.getLabelsFromSelection().forEach((id) => { - if (labelsToKeep.indexOf(id) === -1) { - result.push(id); - } - }); - - return result; - } - - /** - * Simple form serialization, it will return just what we need - * Returns key/value pairs from form data - */ - - getFormDataAsObject() { - const formData = { - update: { - state_event: this.form.find('input[name="update[state_event]"]').val(), - // For Merge Requests - assignee_id: this.form.find('input[name="update[assignee_id]"]').val(), - // For Issues - assignee_ids: [this.form.find('input[name="update[assignee_ids][]"]').val()], - milestone_id: this.form.find('input[name="update[milestone_id]"]').val(), - issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(), - subscription_event: this.form.find('input[name="update[subscription_event]"]').val(), - add_label_ids: [], - remove_label_ids: [] - } - }; - if (this.willUpdateLabels) { - formData.update.add_label_ids = this.$labelDropdown.data('marked'); - formData.update.remove_label_ids = this.$labelDropdown.data('unmarked'); - } - return formData; - } - - setOriginalDropdownData() { - const $labelSelect = $('.bulk-update .js-label-select'); - $labelSelect.data('common', this.getOriginalCommonIds()); - $labelSelect.data('marked', this.getOriginalMarkedIds()); - $labelSelect.data('indeterminate', this.getOriginalIndeterminateIds()); - } - - // From issuable's initial bulk selection - getOriginalCommonIds() { - const labelIds = []; - - this.getElement('.selected_issue:checked').each((i, el) => { - labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels')); - }); - return _.intersection.apply(this, labelIds); - } - - // From issuable's initial bulk selection - getOriginalMarkedIds() { - const labelIds = []; - this.getElement('.selected_issue:checked').each((i, el) => { - labelIds.push(this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels')); - }); - return _.intersection.apply(this, labelIds); - } - - // From issuable's initial bulk selection - getOriginalIndeterminateIds() { - const uniqueIds = []; - const labelIds = []; - let issuableLabels = []; - - // Collect unique label IDs for all checked issues - this.getElement('.selected_issue:checked').each((i, el) => { - issuableLabels = this.getElement(`#${this.prefixId}${el.dataset.id}`).data('labels'); - issuableLabels.forEach((labelId) => { - // Store unique IDs - if (uniqueIds.indexOf(labelId) === -1) { - uniqueIds.push(labelId); - } - }); - // Store array of IDs per issuable - labelIds.push(issuableLabels); - }); - // Add uniqueIds to add it as argument for _.intersection - labelIds.unshift(uniqueIds); - // Return IDs that are present but not in all selected issueables - return _.difference(uniqueIds, _.intersection.apply(this, labelIds)); - } - - getElement(selector) { - this.scopeEl = this.scopeEl || $('.content'); - return this.scopeEl.find(selector); - } - } - - global.IssuableBulkActions = IssuableBulkActions; -})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index ac5ce84e31b..8d7d3d73571 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -2,6 +2,8 @@ /* global Issuable */ /* global ListLabel */ +import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; + (function() { this.LabelsSelect = (function() { function LabelsSelect(els) { @@ -430,20 +432,15 @@ if ($('.selected_issue:checked').length) { return; } - return $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label'); + return $('.issues-bulk-update .labels-filter .dropdown-toggle-text').text('Label'); }; LabelsSelect.prototype.enableBulkLabelDropdown = function() { - var issuableBulkActions; - if ($('.selected_issue:checked').length) { - issuableBulkActions = $('.bulk-update').data('bulkActions'); - return issuableBulkActions.willUpdateLabels = true; - } + IssuableBulkUpdateActions.willUpdateLabels = true; }; LabelsSelect.prototype.setDropdownData = function($dropdown, isMarking, value) { var i, markedIds, unmarkedIds, indeterminateIds; - var issuableBulkActions = $('.bulk-update').data('bulkActions'); markedIds = $dropdown.data('marked') || []; unmarkedIds = $dropdown.data('unmarked') || []; @@ -469,13 +466,13 @@ } // If an indeterminate item is being unmarked - if (issuableBulkActions.getOriginalIndeterminateIds().indexOf(value) > -1) { + if (IssuableBulkUpdateActions.getOriginalIndeterminateIds().indexOf(value) > -1) { unmarkedIds.push(value); } // If a marked item is being unmarked // (a marked item could also be a label that is present in all selection) - if (issuableBulkActions.getOriginalCommonIds().indexOf(value) > -1) { + if (IssuableBulkUpdateActions.getOriginalCommonIds().indexOf(value) > -1) { unmarkedIds.push(value); } } diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 1ac82b7e291..fe367d0c42a 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -104,12 +104,11 @@ import './group_label_subscription'; import './groups_select'; import './header'; import './importer_status'; -import './issuable'; +import './issuable_index'; import './issuable_context'; import './issuable_form'; import './issue'; import './issue_status_select'; -import './issues_bulk_assignment'; import './label_manager'; import './labels'; import './labels_select'; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 57387b913dc..00c981f64c5 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -445,3 +445,9 @@ table { word-wrap: break-word; } } + +.disabled-content { + pointer-events: none; + opacity: .5; +} + diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 585f4871f5f..acf5de0e3b5 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -22,12 +22,6 @@ } @media (min-width: $screen-sm-min) { - .issues_bulk_update { - .dropdown-menu-toggle { - width: 132px; - } - } - .filter-item:not(:last-child) { margin-right: 6px; } @@ -376,12 +370,6 @@ padding: 0; } -@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - .issue-bulk-update-dropdown-toggle { - width: 100px; - } -} - @media (max-width: $screen-xs-max) { .issues-details-filters { padding: 0 0 10px; diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 0140dcf19c3..600a1f53b58 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -29,10 +29,6 @@ display: none; } - .issues-holder .issue-check { - display: none; - } - .rss-btn { display: none; } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 5b62d7fa3a7..d4421e3af74 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -33,7 +33,7 @@ padding-right: 0; @media (min-width: $screen-sm-min) { - &:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper { + &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper { padding-right: $gutter_collapsed_width; } @@ -56,7 +56,7 @@ z-index: 300; @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - &:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper { + &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper { padding-right: $gutter_collapsed_width; } } @@ -88,3 +88,35 @@ min-height: 100%; } } + +@mixin maintain-sidebar-dimensions { + display: block; + width: $gutter-width; + padding: 10px 20px; +} + +.issues-bulk-update.right-sidebar { + @include maintain-sidebar-dimensions; + transition: right $sidebar-transition-duration; + right: -$gutter-width; + + &.right-sidebar-expanded { + @include maintain-sidebar-dimensions; + right: 0; + } + + &.right-sidebar-collapsed { + @include maintain-sidebar-dimensions; + right: -$gutter-width; + + .block { + padding: 16px 0; + width: 250px; + border-bottom: 1px solid $border-color; + } + } + + .issuable-sidebar { + padding: 0 3px; + } +} |