summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/ajax_loading_spinner.js2
-rw-r--r--app/assets/javascripts/awards_handler.js2
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js9
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js1
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js1
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.js1
-rw-r--r--app/assets/javascripts/boards/components/modal/index.js4
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js1
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js2
-rw-r--r--app/assets/javascripts/commons/bootstrap.js1
-rw-r--r--app/assets/javascripts/commons/index.js1
-rw-r--r--app/assets/javascripts/copy_as_gfm.js2
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js2
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js10
-rw-r--r--app/assets/javascripts/dispatcher.js22
-rw-r--r--app/assets/javascripts/droplab/plugins/ajax.js13
-rw-r--r--app/assets/javascripts/dropzone_input.js5
-rw-r--r--app/assets/javascripts/emoji/index.js1
-rw-r--r--app/assets/javascripts/extensions/array.js11
-rw-r--r--app/assets/javascripts/filterable_list.js2
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js3
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js65
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js3
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js8
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js55
-rw-r--r--app/assets/javascripts/fly_out_nav.js51
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js1
-rw-r--r--app/assets/javascripts/gl_dropdown.js19
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors.js1
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js2
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_util.js1
-rw-r--r--app/assets/javascripts/groups/components/group_identicon.vue45
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue13
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js1
-rw-r--r--app/assets/javascripts/issuable_index.js2
-rw-r--r--app/assets/javascripts/labels_select.js16
-rw-r--r--app/assets/javascripts/layout_nav.js3
-rw-r--r--app/assets/javascripts/lib/utils/ajax_cache.js4
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js10
-rw-r--r--app/assets/javascripts/lib/utils/pretty_time.js2
-rw-r--r--app/assets/javascripts/lib/utils/sticky.js23
-rw-r--r--app/assets/javascripts/main.js14
-rw-r--r--app/assets/javascripts/merge_request_tabs.js12
-rw-r--r--app/assets/javascripts/milestone_select.js1
-rw-r--r--app/assets/javascripts/notes.js1
-rw-r--r--app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue4
-rw-r--r--app/assets/javascripts/profile/gl_crop.js3
-rw-r--r--app/assets/javascripts/project.js9
-rw-r--r--app/assets/javascripts/project_edit.js2
-rw-r--r--app/assets/javascripts/projects/project_new.js64
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_dropdown.js2
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_dropdown.js2
-rw-r--r--app/assets/javascripts/right_sidebar.js1
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js3
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js2
-rw-r--r--app/assets/javascripts/sidebar_height_manager.js3
-rw-r--r--app/assets/javascripts/todos.js3
-rw-r--r--app/assets/javascripts/u2f/authenticate.js2
-rw-r--r--app/assets/javascripts/u2f/register.js2
-rw-r--r--app/assets/javascripts/username_validator.js2
-rw-r--r--app/assets/javascripts/users/activity_calendar.js1
-rw-r--r--app/assets/javascripts/users_select.js1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss55
-rw-r--r--app/assets/stylesheets/framework/header.scss21
-rw-r--r--app/assets/stylesheets/framework/lists.scss4
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss25
-rw-r--r--app/assets/stylesheets/new_nav.scss4
-rw-r--r--app/assets/stylesheets/new_sidebar.scss120
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss24
-rw-r--r--app/assets/stylesheets/pages/diff.scss68
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss2
-rw-r--r--app/assets/stylesheets/pages/tree.scss23
75 files changed, 721 insertions, 186 deletions
diff --git a/app/assets/javascripts/ajax_loading_spinner.js b/app/assets/javascripts/ajax_loading_spinner.js
index 38a8317dbd7..8f5e2e545ec 100644
--- a/app/assets/javascripts/ajax_loading_spinner.js
+++ b/app/assets/javascripts/ajax_loading_spinner.js
@@ -10,7 +10,7 @@ class AjaxLoadingSpinner {
e.target.setAttribute('disabled', '');
const iconElement = e.target.querySelector('i');
// get first fa- icon
- const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first();
+ const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g)[0];
iconElement.dataset.icon = originalIcon;
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
$(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index ae0a742340d..023f3527309 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,6 +1,6 @@
/* eslint-disable class-methods-use-this */
/* global Flash */
-
+import _ from 'underscore';
import Cookies from 'js-cookie';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js
index b20d108aa25..035a7e5c431 100644
--- a/app/assets/javascripts/behaviors/requires_input.js
+++ b/app/assets/javascripts/behaviors/requires_input.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
import '../commons/bootstrap';
// Requires Input behavior
@@ -48,7 +49,9 @@ function hideOrShowHelpBlock(form) {
$(() => {
const $form = $('form.js-requires-input');
- $form.requiresInput();
- hideOrShowHelpBlock($form);
- $('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form));
+ if ($form) {
+ $form.requiresInput();
+ hideOrShowHelpBlock($form);
+ $('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form));
+ }
});
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index 77e92ff8caf..b70b0a9bbf8 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -1,4 +1,3 @@
-
// Toggle button. Show/hide content inside parent container.
// Button does not change visibility. If button has icon - it changes chevron style.
//
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js
index 88b054b76e6..89c14180149 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/boards_bundle.js
@@ -2,6 +2,7 @@
/* global BoardService */
/* global Flash */
+import _ from 'underscore';
import Vue from 'vue';
import VueResource from 'vue-resource';
import FilteredSearchBoards from './filtered_search_boards';
diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js
index e7f16899362..edfe7c326db 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js
+++ b/app/assets/javascripts/boards/components/board_blank_state.js
@@ -1,5 +1,6 @@
/* global ListLabel */
+import _ from 'underscore';
import Cookies from 'js-cookie';
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index 1d36519c75c..96af69e7a36 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -1,8 +1,8 @@
/* global ListIssue */
import Vue from 'vue';
-import queryData from '../../utils/query_data';
-import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
+import queryData from '~/boards/utils/query_data';
+import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import './header';
import './list';
import './footer';
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index f29b6caa1ac..72bb9e10fbc 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -1,5 +1,6 @@
/* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var,
promise/catch-or-return */
+import _ from 'underscore';
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 1e12d4ca415..43928e602d6 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -1,6 +1,6 @@
/* eslint-disable comma-dangle, space-before-function-paren, one-var, no-shadow, dot-notation, max-len */
/* global List */
-
+import _ from 'underscore';
import Cookies from 'js-cookie';
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js
index 510bedbf641..389587a2596 100644
--- a/app/assets/javascripts/commons/bootstrap.js
+++ b/app/assets/javascripts/commons/bootstrap.js
@@ -9,6 +9,7 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/tab';
import 'bootstrap-sass/assets/javascripts/bootstrap/transition';
import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip';
import 'bootstrap-sass/assets/javascripts/bootstrap/popover';
+import 'bootstrap-sass/assets/javascripts/bootstrap/button';
// custom jQuery functions
$.fn.extend({
diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js
index 7063f59d446..6db8b3afbef 100644
--- a/app/assets/javascripts/commons/index.js
+++ b/app/assets/javascripts/commons/index.js
@@ -1,3 +1,4 @@
+import 'underscore';
import './polyfills';
import './jquery';
import './bootstrap';
diff --git a/app/assets/javascripts/copy_as_gfm.js b/app/assets/javascripts/copy_as_gfm.js
index 54257531284..13ba4a57293 100644
--- a/app/assets/javascripts/copy_as_gfm.js
+++ b/app/assets/javascripts/copy_as_gfm.js
@@ -1,5 +1,5 @@
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
-
+import _ from 'underscore';
import './lib/utils/common_utils';
import { placeholderImage } from './lazy_loader';
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index 44791a93936..6583e471a48 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -92,7 +92,7 @@ $(() => {
});
},
selectDefaultStage() {
- const stage = this.state.stages.first();
+ const stage = this.state.stages[0];
this.selectStage(stage);
},
selectStage(stage) {
diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
index 37ddca29e71..298f737a2bc 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
@@ -94,7 +94,7 @@ const JumpToDiscussion = Vue.extend({
hasDiscussionsToJumpTo = false;
}
}
- } else if (activeTab !== 'notes') {
+ } else if (activeTab !== 'show') {
// If we are on the commits or builds tabs,
// there are no discussions to jump to.
hasDiscussionsToJumpTo = false;
@@ -103,12 +103,12 @@ const JumpToDiscussion = Vue.extend({
if (!hasDiscussionsToJumpTo) {
// If there are no discussions to jump to on the current page,
// switch to the notes tab and jump to the first disucssion there.
- window.mrTabs.activateTab('notes');
- activeTab = 'notes';
+ window.mrTabs.activateTab('show');
+ activeTab = 'show';
jumpToFirstDiscussion = true;
}
- if (activeTab === 'notes') {
+ if (activeTab === 'show') {
discussionsSelector = '.discussion[data-discussion-id]';
discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
}
@@ -156,7 +156,7 @@ const JumpToDiscussion = Vue.extend({
let $target = $(`${discussionsSelector}[data-discussion-id="${nextUnresolvedDiscussionId}"]`);
- if (activeTab === 'notes') {
+ if (activeTab === 'show') {
$target = $target.closest('.note-discussion');
// If the next discussion is closed, toggle it open.
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 3a328a62558..e769d495bed 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -79,10 +79,6 @@ import GpgBadges from './gpg_badges';
(function() {
var Dispatcher;
- $(function() {
- return new Dispatcher();
- });
-
Dispatcher = (function() {
function Dispatcher() {
this.initSearch();
@@ -139,6 +135,8 @@ import GpgBadges from './gpg_badges';
.init();
}
+ const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
+
switch (page) {
case 'profiles:preferences:show':
initExperimentalFlags();
@@ -155,7 +153,7 @@ import GpgBadges from './gpg_badges';
break;
case 'projects:merge_requests:index':
case 'projects:issues:index':
- if (gl.FilteredSearchManager && document.querySelector('.filtered-search')) {
+ if (filteredSearchEnabled) {
const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests');
filteredSearchManager.setup();
}
@@ -182,11 +180,17 @@ import GpgBadges from './gpg_badges';
break;
case 'dashboard:issues':
case 'dashboard:merge_requests':
- case 'groups:issues':
case 'groups:merge_requests':
new ProjectSelect();
initLegacyFilters();
break;
+ case 'groups:issues':
+ if (filteredSearchEnabled) {
+ const filteredSearchManager = new gl.FilteredSearchManager('issues');
+ filteredSearchManager.setup();
+ }
+ new ProjectSelect();
+ break;
case 'dashboard:todos:index':
new Todos();
break;
@@ -499,7 +503,7 @@ import GpgBadges from './gpg_badges';
new gl.DueDateSelectors();
break;
}
- switch (path.first()) {
+ switch (path[0]) {
case 'sessions':
case 'omniauth_callbacks':
if (!gon.u2f) break;
@@ -628,4 +632,8 @@ import GpgBadges from './gpg_badges';
return Dispatcher;
})();
+
+ $(function() {
+ new Dispatcher();
+ });
}).call(window);
diff --git a/app/assets/javascripts/droplab/plugins/ajax.js b/app/assets/javascripts/droplab/plugins/ajax.js
index c0da5866139..267b53fa4f2 100644
--- a/app/assets/javascripts/droplab/plugins/ajax.js
+++ b/app/assets/javascripts/droplab/plugins/ajax.js
@@ -11,6 +11,16 @@ const Ajax = {
if (!self.destroyed) self.hook.list[config.method].call(self.hook.list, data);
},
+ preprocessing: function preprocessing(config, data) {
+ let results = data;
+
+ if (config.preprocessing && !data.preprocessed) {
+ results = config.preprocessing(data);
+ AjaxCache.override(config.endpoint, results);
+ }
+
+ return results;
+ },
init: function init(hook) {
var self = this;
self.destroyed = false;
@@ -31,7 +41,8 @@ const Ajax = {
dynamicList.outerHTML = loadingTemplate.outerHTML;
}
- AjaxCache.retrieve(config.endpoint)
+ return AjaxCache.retrieve(config.endpoint)
+ .then(self.preprocessing.bind(null, config))
.then((data) => self._loadData(data, config, self))
.catch(config.onError);
},
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 3ec4d9ba318..975903159be 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -1,11 +1,10 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, prefer-arrow-callback */
/* global Dropzone */
-
+import _ from 'underscore';
import './preview_markdown';
window.DropzoneInput = (function() {
function DropzoneInput(form) {
- Dropzone.autoDiscover = false;
const divHover = '<div class="div-dropzone-hover"></div>';
const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
const $attachButton = form.find('.button-attach-file');
@@ -218,7 +217,7 @@ window.DropzoneInput = (function() {
value = e.clipboardData.getData('text/plain');
}
value = value.split("\r");
- return value.first();
+ return value[0];
};
const showSpinner = function(e) {
diff --git a/app/assets/javascripts/emoji/index.js b/app/assets/javascripts/emoji/index.js
index cac35d6eed5..dc7672560ea 100644
--- a/app/assets/javascripts/emoji/index.js
+++ b/app/assets/javascripts/emoji/index.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
import emojiMap from 'emojis/digests.json';
import emojiAliases from 'emojis/aliases.json';
diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js
deleted file mode 100644
index 027222f804d..00000000000
--- a/app/assets/javascripts/extensions/array.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// TODO: remove this
-
-// eslint-disable-next-line no-extend-native
-Array.prototype.first = function first() {
- return this[0];
-};
-
-// eslint-disable-next-line no-extend-native
-Array.prototype.last = function last() {
- return this[this.length - 1];
-};
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index 139206cc185..6d516a253bb 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -1,3 +1,5 @@
+import _ from 'underscore';
+
/**
* Makes search request for content when user types a value in the search input.
* Updates the html content of the page with the received one.
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js
index 2615d626c4c..0bc4b6f22a9 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js
@@ -6,7 +6,7 @@ import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown {
constructor(options = {}) {
- const { input, endpoint, symbol } = options;
+ const { input, endpoint, symbol, preprocessing } = options;
super(options);
this.symbol = symbol;
this.config = {
@@ -14,6 +14,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
endpoint,
method: 'setData',
loadingTemplate: this.loadingTemplate,
+ preprocessing,
onError() {
/* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.');
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index ef8fe071012..8d711e3213c 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
import FilteredSearchContainer from './container';
class DropdownUtils {
@@ -50,6 +51,66 @@ class DropdownUtils {
return updatedItem;
}
+ static mergeDuplicateLabels(dataMap, newLabel) {
+ const updatedMap = dataMap;
+ const key = newLabel.title;
+
+ const hasKeyProperty = Object.prototype.hasOwnProperty.call(updatedMap, key);
+
+ if (!hasKeyProperty) {
+ updatedMap[key] = newLabel;
+ } else {
+ const existing = updatedMap[key];
+
+ if (!existing.multipleColors) {
+ existing.multipleColors = [existing.color];
+ }
+
+ existing.multipleColors.push(newLabel.color);
+ }
+
+ return updatedMap;
+ }
+
+ static duplicateLabelColor(labelColors) {
+ const colors = labelColors;
+ const spacing = 100 / colors.length;
+
+ // Reduce the colors to 4
+ colors.length = Math.min(colors.length, 4);
+
+ const color = colors.map((c, i) => {
+ const percentFirst = Math.floor(spacing * i);
+ const percentSecond = Math.floor(spacing * (i + 1));
+ return `${c} ${percentFirst}%, ${c} ${percentSecond}%`;
+ }).join(', ');
+
+ return `linear-gradient(${color})`;
+ }
+
+ static duplicateLabelPreprocessing(data) {
+ const results = [];
+ const dataMap = {};
+
+ data.forEach(DropdownUtils.mergeDuplicateLabels.bind(null, dataMap));
+
+ Object.keys(dataMap)
+ .forEach((key) => {
+ const label = dataMap[key];
+
+ if (label.multipleColors) {
+ label.color = DropdownUtils.duplicateLabelColor(label.multipleColors);
+ label.text_color = '#000000';
+ }
+
+ results.push(label);
+ });
+
+ results.preprocessed = true;
+
+ return results;
+ }
+
static filterHint(config, item) {
const { input, allowedKeys } = config;
const updatedItem = item;
@@ -62,11 +123,11 @@ class DropdownUtils {
if (!allowMultiple && itemInExistingTokens) {
updatedItem.droplab_hidden = true;
- } else if (!lastKey || searchInput.split('').last() === ' ') {
+ } else if (!lastKey || _.last(searchInput.split('')) === ' ') {
updatedItem.droplab_hidden = false;
} else if (lastKey) {
const split = lastKey.split(':');
- const tokenName = split[0].split(' ').last();
+ const tokenName = _.last(split[0].split(' '));
const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1;
updatedItem.droplab_hidden = tokenName ? match : false;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index 61cef435209..dd1c067df87 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -54,6 +54,7 @@ class FilteredSearchDropdownManager {
extraArguments: {
endpoint: `${this.baseEndpoint}/labels.json`,
symbol: '~',
+ preprocessing: gl.DropdownUtils.duplicateLabelPreprocessing,
},
element: this.container.querySelector('#js-dropdown-label'),
},
@@ -166,7 +167,7 @@ class FilteredSearchDropdownManager {
// Eg. token = 'label:'
const split = lastToken.split(':');
- const dropdownName = split[0].split(' ').last();
+ const dropdownName = _.last(split[0].split(' '));
this.loadDropdown(split.length > 1 ? dropdownName : '');
} else if (lastToken) {
// Token has been initialized into an object because it has a value
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 7872e9e68ad..a31be2b0bc7 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -20,13 +20,13 @@ class FilteredSearchManager {
allowedKeys: this.filteredSearchTokenKeys.getKeys(),
});
this.searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
- const projectPath = this.searchHistoryDropdownElement ?
- this.searchHistoryDropdownElement.dataset.projectFullPath : 'project';
+ const fullPath = this.searchHistoryDropdownElement ?
+ this.searchHistoryDropdownElement.dataset.fullPath : 'project';
let recentSearchesPagePrefix = 'issue-recent-searches';
if (this.page === 'merge_requests') {
recentSearchesPagePrefix = 'merge-request-recent-searches';
}
- const recentSearchesKey = `${projectPath}-${recentSearchesPagePrefix}`;
+ const recentSearchesKey = `${fullPath}-${recentSearchesPagePrefix}`;
this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
}
@@ -367,7 +367,7 @@ class FilteredSearchManager {
const fragments = searchToken.split(':');
if (fragments.length > 1) {
const inputValues = fragments[0].split(' ');
- const tokenKey = inputValues.last();
+ const tokenKey = _.last(inputValues);
if (inputValues.length > 1) {
inputValues.pop();
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
index e9278140af0..243ee4d723a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -58,29 +58,54 @@ class FilteredSearchVisualTokens {
`;
}
+ static setTokenStyle(tokenContainer, backgroundColor, textColor) {
+ const token = tokenContainer;
+
+ // Labels with linear gradient should not override default background color
+ if (backgroundColor.indexOf('linear-gradient') === -1) {
+ token.style.backgroundColor = backgroundColor;
+ }
+
+ token.style.color = textColor;
+
+ if (textColor === '#FFFFFF') {
+ const removeToken = token.querySelector('.remove-token');
+ removeToken.classList.add('inverted');
+ }
+
+ return token;
+ }
+
+ static preprocessLabel(labelsEndpoint, labels) {
+ let processed = labels;
+
+ if (!labels.preprocessed) {
+ processed = gl.DropdownUtils.duplicateLabelPreprocessing(labels);
+ AjaxCache.override(labelsEndpoint, processed);
+ processed.preprocessed = true;
+ }
+
+ return processed;
+ }
+
static updateLabelTokenColor(tokenValueContainer, tokenValue) {
const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search');
const baseEndpoint = filteredSearchInput.dataset.baseEndpoint;
const labelsEndpoint = `${baseEndpoint}/labels.json`;
return AjaxCache.retrieve(labelsEndpoint)
- .then((labels) => {
- const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue);
-
- if (!matchingLabel) {
- return;
- }
+ .then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint))
+ .then((labels) => {
+ const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue);
- const tokenValueStyle = tokenValueContainer.style;
- tokenValueStyle.backgroundColor = matchingLabel.color;
- tokenValueStyle.color = matchingLabel.text_color;
+ if (!matchingLabel) {
+ return;
+ }
- if (matchingLabel.text_color === '#FFFFFF') {
- const removeToken = tokenValueContainer.querySelector('.remove-token');
- removeToken.classList.add('inverted');
- }
- })
- .catch(() => new Flash('An error occurred while fetching label colors.'));
+ FilteredSearchVisualTokens
+ .setTokenStyle(tokenValueContainer, matchingLabel.color, matchingLabel.text_color);
+ })
+ .catch(() => new Flash('An error occurred while fetching label colors.'));
}
static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js
new file mode 100644
index 00000000000..8e9a97fe207
--- /dev/null
+++ b/app/assets/javascripts/fly_out_nav.js
@@ -0,0 +1,51 @@
+/* global bp */
+import './breakpoints';
+
+export const canShowSubItems = () => bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg';
+
+export const calculateTop = (boundingRect, outerHeight) => {
+ const windowHeight = window.innerHeight;
+ const bottomOverflow = windowHeight - (boundingRect.top + outerHeight);
+
+ return bottomOverflow < 0 ? (boundingRect.top - outerHeight) + boundingRect.height :
+ boundingRect.top;
+};
+
+export const showSubLevelItems = (el) => {
+ const subItems = el.querySelector('.sidebar-sub-level-items');
+
+ if (!subItems || !canShowSubItems()) return;
+
+ subItems.style.display = 'block';
+ el.classList.add('is-over');
+
+ const boundingRect = el.getBoundingClientRect();
+ const top = calculateTop(boundingRect, subItems.offsetHeight);
+ const isAbove = top < boundingRect.top;
+
+ subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`;
+
+ if (isAbove) {
+ subItems.classList.add('is-above');
+ }
+};
+
+export const hideSubLevelItems = (el) => {
+ const subItems = el.querySelector('.sidebar-sub-level-items');
+
+ if (!subItems || !canShowSubItems()) return;
+
+ el.classList.remove('is-over');
+ subItems.style.display = 'none';
+ subItems.classList.remove('is-above');
+};
+
+export default () => {
+ const items = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active)')]
+ .filter(el => el.querySelector('.sidebar-sub-level-items'));
+
+ items.forEach((el) => {
+ el.addEventListener('mouseenter', e => showSubLevelItems(e.currentTarget));
+ el.addEventListener('mouseleave', e => hideSubLevelItems(e.currentTarget));
+ });
+};
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 6cb9cfe1382..5c624b79d45 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
import glRegexp from './lib/utils/regexp';
import AjaxCache from './lib/utils/ajax_cache';
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 3babe273100..7d11cd0b6b2 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, prefer-rest-params, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func, no-mixed-operators */
/* global fuzzaldrinPlus */
+import _ from 'underscore';
import { isObject } from './lib/utils/type_utility';
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote;
@@ -114,7 +115,7 @@ GitLabDropdownFilter = (function() {
} else {
elements = this.options.elements();
if (search_text) {
- return elements.each(function() {
+ elements.each(function() {
var $el, matches;
$el = $(this);
matches = fuzzaldrinPlus.match($el.text().trim(), search_text);
@@ -127,8 +128,10 @@ GitLabDropdownFilter = (function() {
}
});
} else {
- return elements.show().removeClass('option-hidden');
+ elements.show().removeClass('option-hidden');
}
+
+ elements.parent().find('.dropdown-menu-empty-link').toggleClass('hidden', elements.is(':visible'));
}
};
@@ -730,10 +733,16 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) {
if (this.options.filterable) {
- $(':focus').blur();
-
this.dropdown.one('transitionend', () => {
- this.filterInput.focus();
+ const initialScrollTop = $(window).scrollTop();
+
+ if (this.dropdown.is('.open')) {
+ this.filterInput.focus();
+ }
+
+ if ($(window).scrollTop() < initialScrollTop) {
+ $(window).scrollTop(initialScrollTop);
+ }
});
if (triggerFocus) {
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js
index c6be4c9e8fe..cdc4fcf6573 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors.js
@@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
+import _ from 'underscore';
import d3 from 'd3';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
index 0deb27e522b..f64b4638485 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
@@ -1,5 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
-
+import _ from 'underscore';
import d3 from 'd3';
const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
index c583757f3f2..77135ad1f0e 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
@@ -1,4 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */
+import _ from 'underscore';
export default {
parse_log: function(log) {
diff --git a/app/assets/javascripts/groups/components/group_identicon.vue b/app/assets/javascripts/groups/components/group_identicon.vue
new file mode 100644
index 00000000000..0edd820743f
--- /dev/null
+++ b/app/assets/javascripts/groups/components/group_identicon.vue
@@ -0,0 +1,45 @@
+<script>
+export default {
+ props: {
+ entityId: {
+ type: Number,
+ required: true,
+ },
+ entityName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ /**
+ * This method is based on app/helpers/application_helper.rb#project_identicon
+ */
+ identiconStyles() {
+ const allowedColors = [
+ '#FFEBEE',
+ '#F3E5F5',
+ '#E8EAF6',
+ '#E3F2FD',
+ '#E0F2F1',
+ '#FBE9E7',
+ '#EEEEEE',
+ ];
+
+ const backgroundColor = allowedColors[this.entityId % 7];
+
+ return `background-color: ${backgroundColor}; color: #555;`;
+ },
+ identiconTitle() {
+ return this.entityName.charAt(0).toUpperCase();
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="avatar s40 identicon"
+ :style="identiconStyles">
+ {{identiconTitle}}
+ </div>
+</template>
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index b1db34b9c50..cb133cf7535 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -1,7 +1,11 @@
<script>
import eventHub from '../event_hub';
+import groupIdenticon from './group_identicon.vue';
export default {
+ components: {
+ groupIdenticon,
+ },
props: {
group: {
type: Object,
@@ -92,6 +96,9 @@ export default {
hasGroups() {
return Object.keys(this.group.subGroups).length > 0;
},
+ hasAvatar() {
+ return this.group.avatarUrl && this.group.avatarUrl.indexOf('/assets/no_group_avatar') === -1;
+ },
},
};
</script>
@@ -194,9 +201,15 @@ export default {
<a
:href="group.groupPath">
<img
+ v-if="hasAvatar"
class="avatar s40"
:src="group.avatarUrl"
/>
+ <group-identicon
+ v-else
+ :entity-id=group.id
+ :entity-name="group.name"
+ />
</a>
</div>
<div
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index e46c0e90255..c39ffdb2e0f 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -1,6 +1,7 @@
/* 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 */
+import _ from 'underscore';
export default {
init({ container, form, issues, prefixId } = {}) {
diff --git a/app/assets/javascripts/issuable_index.js b/app/assets/javascripts/issuable_index.js
index 5c96646def8..ece0220c927 100644
--- a/app/assets/javascripts/issuable_index.js
+++ b/app/assets/javascripts/issuable_index.js
@@ -1,6 +1,6 @@
/* 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 IssuableIndex */
-
+import _ from 'underscore';
import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 8d7d3d73571..7d7f91227f9 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,8 +1,9 @@
/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread */
/* global Issuable */
/* global ListLabel */
-
+import _ from 'underscore';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
+import DropdownUtils from './filtered_search/dropdown_utils';
(function() {
this.LabelsSelect = (function() {
@@ -218,18 +219,7 @@ import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
}
}
if (label.duplicate) {
- spacing = 100 / label.color.length;
- // Reduce the colors to 4
- label.color = label.color.filter(function(color, i) {
- return i < 4;
- });
- color = _.map(label.color, function(color, i) {
- var percentFirst, percentSecond;
- percentFirst = Math.floor(spacing * i);
- percentSecond = Math.floor(spacing * (i + 1));
- return color + " " + percentFirst + "%," + color + " " + percentSecond + "% ";
- }).join(',');
- color = "linear-gradient(" + color + ")";
+ color = gl.DropdownUtils.duplicateLabelColor(label.color);
}
else {
if (label.color != null) {
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index 6186ffe20b3..5c1ba416a03 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -2,6 +2,7 @@
import _ from 'underscore';
import Cookies from 'js-cookie';
import NewNavSidebar from './new_sidebar';
+import initFlyOutNav from './fly_out_nav';
(function() {
var hideEndFade;
@@ -58,6 +59,8 @@ import NewNavSidebar from './new_sidebar';
if (Cookies.get('new_nav') === 'true') {
const newNavSidebar = new NewNavSidebar();
newNavSidebar.bindEvents();
+
+ initFlyOutNav();
}
$(window).on('scroll', _.throttle(applyScrollNavClass, 100));
diff --git a/app/assets/javascripts/lib/utils/ajax_cache.js b/app/assets/javascripts/lib/utils/ajax_cache.js
index 7477b5a5214..629d8f44e18 100644
--- a/app/assets/javascripts/lib/utils/ajax_cache.js
+++ b/app/assets/javascripts/lib/utils/ajax_cache.js
@@ -6,6 +6,10 @@ class AjaxCache extends Cache {
this.pendingRequests = { };
}
+ override(endpoint, data) {
+ this.internalStorage[endpoint] = data;
+ }
+
retrieve(endpoint, forceRetrieve) {
if (this.hasData(endpoint) && !forceRetrieve) {
return Promise.resolve(this.get(endpoint));
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 92ff9a1ed8c..52465f182c8 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -86,8 +86,9 @@
// This is required to handle non-unicode characters in hash
hash = decodeURIComponent(hash);
- var fixedTabs = document.querySelector('.js-tabs-affix');
- var fixedNav = document.querySelector('.navbar-gitlab');
+ const fixedTabs = document.querySelector('.js-tabs-affix');
+ const fixedDiffStats = document.querySelector('.js-diff-files-changed.is-stuck');
+ const fixedNav = document.querySelector('.navbar-gitlab');
var adjustment = 0;
if (fixedNav) adjustment -= fixedNav.offsetHeight;
@@ -104,6 +105,11 @@
if (fixedTabs) {
adjustment -= fixedTabs.offsetHeight;
}
+
+ if (fixedDiffStats) {
+ adjustment -= fixedDiffStats.offsetHeight;
+ }
+
window.scrollBy(0, adjustment);
}
};
diff --git a/app/assets/javascripts/lib/utils/pretty_time.js b/app/assets/javascripts/lib/utils/pretty_time.js
index ae397212e55..716aefbfcb7 100644
--- a/app/assets/javascripts/lib/utils/pretty_time.js
+++ b/app/assets/javascripts/lib/utils/pretty_time.js
@@ -1,3 +1,5 @@
+import _ from 'underscore';
+
(() => {
/*
* TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints,
diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js
new file mode 100644
index 00000000000..43a808b6ab3
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/sticky.js
@@ -0,0 +1,23 @@
+export const isSticky = (el, scrollY, stickyTop) => {
+ const top = el.offsetTop - scrollY;
+
+ if (top === stickyTop) {
+ el.classList.add('is-stuck');
+ } else {
+ el.classList.remove('is-stuck');
+ }
+};
+
+export default (el) => {
+ if (!el) return;
+
+ const computedStyle = window.getComputedStyle(el);
+
+ if (!/sticky/.test(computedStyle.position)) return;
+
+ const stickyTop = parseInt(computedStyle.top, 10);
+
+ document.addEventListener('scroll', () => isSticky(el, window.scrollY, stickyTop), {
+ passive: true,
+ });
+};
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index cd45091c211..42092a34c2f 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -16,9 +16,6 @@ import 'mousetrap';
import 'mousetrap/plugins/pause/mousetrap-pause';
import 'vendor/fuzzaldrin-plus';
-// extensions
-import './extensions/array';
-
// expose common libraries as globals (TODO: remove these)
window.jQuery = jQuery;
window.$ = jQuery;
@@ -36,9 +33,6 @@ import './shortcuts_find_file';
import './shortcuts_issuable';
import './shortcuts_network';
-// behaviors
-import './behaviors/';
-
// templates
import './templates/issuable_template_selector';
import './templates/issuable_template_selectors';
@@ -56,6 +50,9 @@ import './lib/utils/pretty_time';
import './lib/utils/text_utility';
import './lib/utils/url_utility';
+// behaviors
+import './behaviors/';
+
// u2f
import './u2f/authenticate';
import './u2f/error';
@@ -86,7 +83,6 @@ import './copy_as_gfm';
import './copy_to_clipboard';
import './create_label';
import './diff';
-import './dispatcher';
import './dropzone_input';
import './due_date_select';
import './files_comment_button';
@@ -150,9 +146,13 @@ import './subscription';
import './subscription_select';
import './syntax_highlight';
+import './dispatcher';
+
// eslint-disable-next-line global-require, import/no-commonjs
if (process.env.NODE_ENV !== 'production') require('./test_utils/');
+Dropzone.autoDiscover = false;
+
document.addEventListener('beforeunload', function () {
// Unbind scroll events
$(document).off('scroll');
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 7840f05a8ae..4ffd71d9de5 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -7,6 +7,7 @@ import Cookies from 'js-cookie';
import './breakpoints';
import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
+import stickyMonitor from './lib/utils/sticky';
/* eslint-disable max-len */
// MergeRequestTabs
@@ -266,6 +267,10 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
const $container = $('#diffs');
$container.html(data.html);
+ this.initChangesDropdown();
+
+ stickyMonitor(document.querySelector('.js-diff-files-changed'));
+
if (typeof gl.diffNotesCompileComponents !== 'undefined') {
gl.diffNotesCompileComponents();
}
@@ -314,6 +319,13 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
});
}
+ initChangesDropdown() {
+ $('.js-diff-stats-dropdown').glDropdown({
+ filterable: true,
+ remoteFilter: false,
+ });
+ }
+
// Show or hide the loading spinner
//
// status - Boolean, true to show, false to hide
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 6756ab0b3aa..04579058688 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
/* global Issuable */
/* global ListMilestone */
+import _ from 'underscore';
(function() {
this.MilestoneSelect = (function() {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index dfa07a2def4..b38a6abc8d1 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -11,6 +11,7 @@ newline-per-chained-call, no-useless-escape, class-methods-use-this */
/* global mrRefreshWidgetUrl */
import $ from 'jquery';
+import _ from 'underscore';
import Cookies from 'js-cookie';
import autosize from 'vendor/autosize';
import Dropzone from 'dropzone';
diff --git a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue
index ce46b3fa3fa..b5d85299cf8 100644
--- a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue
@@ -1,4 +1,6 @@
<script>
+ import _ from 'underscore';
+
export default {
props: {
initialCronInterval: {
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 77cbaeb43ef..66bc1d1979c 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -1,7 +1,7 @@
<script>
+ import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+ import '~/flash';
import stageColumnComponent from './stage_column_component.vue';
- import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
- import '../../../flash';
export default {
props: {
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index cf1566eeb87..291ae24aa68 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -1,6 +1,7 @@
/* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */
-import 'vendor/cropper';
+import 'cropper';
+import _ from 'underscore';
((global) => {
// Matches everything but the file name
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index a3f7d69b98d..6e1744e8e72 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -10,14 +10,19 @@ import Cookies from 'js-cookie';
const $projectCloneField = $('#project_clone');
const $cloneBtnText = $('a.clone-dropdown-btn span');
+ const selectedCloneOption = $cloneBtnText.text().trim();
+ if (selectedCloneOption.length > 0) {
+ $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
+ }
+
$('a', $cloneOptions).on('click', (e) => {
const $this = $(e.currentTarget);
const url = $this.attr('href');
e.preventDefault();
- $('.active', $cloneOptions).not($this).removeClass('active');
- $this.toggleClass('active');
+ $('.is-active', $cloneOptions).not($this).removeClass('is-active');
+ $this.toggleClass('is-active');
$projectCloneField.val(url);
$cloneBtnText.text($this.text());
diff --git a/app/assets/javascripts/project_edit.js b/app/assets/javascripts/project_edit.js
index d7d284b6c86..7572fec15e0 100644
--- a/app/assets/javascripts/project_edit.js
+++ b/app/assets/javascripts/project_edit.js
@@ -1,6 +1,6 @@
export default function setupProjectEdit() {
const $transferForm = $('.js-project-transfer-form');
- const $selectNamespace = $transferForm.find('.select2');
+ const $selectNamespace = $transferForm.find('select.select2');
$selectNamespace.on('change', () => {
$transferForm.find(':submit').prop('disabled', !$selectNamespace.val());
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 2091b275c3d..1dc1dbf356d 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -1,6 +1,40 @@
-document.addEventListener('DOMContentLoaded', () => {
+let hasUserDefinedProjectPath = false;
+
+const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => {
+ if ($projectImportUrl.attr('disabled') || hasUserDefinedProjectPath) {
+ return;
+ }
+
+ let importUrl = $projectImportUrl.val().trim();
+ if (importUrl.length === 0) {
+ return;
+ }
+
+ /*
+ \/?: remove trailing slash
+ (\.git\/?)?: remove trailing .git (with optional trailing slash)
+ (\?.*)?: remove query string
+ (#.*)?: remove fragment identifier
+ */
+ importUrl = importUrl.replace(/\/?(\.git\/?)?(\?.*)?(#.*)?$/, '');
+
+ // extract everything after the last slash
+ const pathMatch = /\/([^/]+)$/.exec(importUrl);
+ if (pathMatch) {
+ $projectPath.val(pathMatch[1]);
+ }
+};
+
+const bindEvents = () => {
+ const $newProjectForm = $('#new_project');
const importBtnTooltip = 'Please enter a valid project name.';
const $importBtnWrapper = $('.import_gitlab_project');
+ const $projectImportUrl = $('#project_import_url');
+ const $projectPath = $('#project_path');
+
+ if ($newProjectForm.length !== 1) {
+ return;
+ }
$('.how_to_import_link').on('click', (e) => {
e.preventDefault();
@@ -13,19 +47,19 @@ document.addEventListener('DOMContentLoaded', () => {
$('.btn_import_gitlab_project').on('click', () => {
const importHref = $('a.btn_import_gitlab_project').attr('href');
- $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$('#project_path').val()}`);
+ $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
});
- $('.btn_import_gitlab_project').attr('disabled', !$('#project_path').val().trim().length);
+ $('.btn_import_gitlab_project').attr('disabled', !$projectPath.val().trim().length);
$importBtnWrapper.attr('title', importBtnTooltip);
- $('#new_project').on('submit', () => {
- const $path = $('#project_path');
- $path.val($path.val().trim());
+ $newProjectForm.on('submit', () => {
+ $projectPath.val($projectPath.val().trim());
});
- $('#project_path').on('keyup', () => {
- if ($('#project_path').val().trim().length) {
+ $projectPath.on('keyup', () => {
+ hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
+ if (hasUserDefinedProjectPath) {
$('.btn_import_gitlab_project').attr('disabled', false);
$importBtnWrapper.attr('title', '');
$importBtnWrapper.removeClass('has-tooltip');
@@ -35,9 +69,17 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
- $('#project_import_url').disable();
+ $projectImportUrl.disable();
+ $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl, $projectPath));
+
$('.import_git').on('click', () => {
- const $projectImportUrl = $('#project_import_url');
$projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
});
-});
+};
+
+document.addEventListener('DOMContentLoaded', bindEvents);
+
+export default {
+ bindEvents,
+ deriveProjectPathFromUrl,
+};
diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js
index cc0b2ebe071..678882a8d2c 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js
@@ -1,3 +1,5 @@
+import _ from 'underscore';
+
export default class ProtectedBranchDropdown {
/**
* @param {Object} options containing
diff --git a/app/assets/javascripts/protected_tags/protected_tag_dropdown.js b/app/assets/javascripts/protected_tags/protected_tag_dropdown.js
index 9d045886262..a0224213aa0 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_dropdown.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_dropdown.js
@@ -1,3 +1,5 @@
+import _ from 'underscore';
+
export default class ProtectedTagDropdown {
/**
* @param {Object} options containing
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index d8f1fe10b26..fa958d75fa4 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, max-len */
+import _ from 'underscore';
import Cookies from 'js-cookie';
import SidebarHeightManager from './sidebar_height_manager';
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index 51448252c0f..0be141eb5f9 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -3,6 +3,7 @@
/* global ShortcutsNavigation */
/* global sidebar */
+import _ from 'underscore';
import 'mousetrap';
import './shortcuts_navigation';
@@ -58,7 +59,7 @@ import './shortcuts_navigation';
});
// If replyField already has some content, add a newline before our quote
separator = replyField.val().trim() !== "" && "\n\n" || '';
- replyField.val(function(_, current) {
+ replyField.val(function(a, current) {
return current + separator + quote.join('') + "\n";
});
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
index efedea32d1e..d32fe4abc7d 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
+++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
@@ -1,3 +1,5 @@
+import _ from 'underscore';
+
import '~/smart_interval';
import timeTracker from './time_tracker';
diff --git a/app/assets/javascripts/sidebar_height_manager.js b/app/assets/javascripts/sidebar_height_manager.js
index 022415f22b2..df19d7305f8 100644
--- a/app/assets/javascripts/sidebar_height_manager.js
+++ b/app/assets/javascripts/sidebar_height_manager.js
@@ -1,3 +1,5 @@
+import _ from 'underscore';
+
export default {
init() {
if (!this.initialized) {
@@ -30,4 +32,3 @@ export default {
}
},
};
-
diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js
index bba8b5abbb4..a606852c22c 100644
--- a/app/assets/javascripts/todos.js
+++ b/app/assets/javascripts/todos.js
@@ -52,6 +52,7 @@ export default class Todos {
}
updateRowStateClicked(e) {
+ e.stopPropagation();
e.preventDefault();
const target = e.target;
@@ -92,6 +93,7 @@ export default class Todos {
}
updateAllStateClicked(e) {
+ e.stopPropagation();
e.preventDefault();
const target = e.currentTarget;
@@ -142,6 +144,7 @@ export default class Todos {
if (gl.utils.isMetaClick(e)) {
const windowTarget = '_blank';
const selected = e.target;
+ e.stopPropagation();
e.preventDefault();
if (selected.tagName === 'IMG') {
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index cd5280948fd..8821b22477f 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -3,6 +3,8 @@
/* global U2FError */
/* global U2FUtil */
+import _ from 'underscore';
+
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index 1234d17b8fd..3a2534d553b 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -3,6 +3,8 @@
/* global U2FError */
/* global U2FUtil */
+import _ from 'underscore';
+
// Register U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> registered -> POST to server
diff --git a/app/assets/javascripts/username_validator.js b/app/assets/javascripts/username_validator.js
index a348d69153c..bb34d5d2008 100644
--- a/app/assets/javascripts/username_validator.js
+++ b/app/assets/javascripts/username_validator.js
@@ -1,5 +1,7 @@
/* eslint-disable comma-dangle, consistent-return, class-methods-use-this, arrow-parens, no-param-reassign, max-len */
+import _ from 'underscore';
+
const debounceTimeoutDuration = 1000;
const invalidInputClass = 'gl-field-error-outline';
const successInputClass = 'gl-field-success-outline';
diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js
index f091e319f44..3dac31c2121 100644
--- a/app/assets/javascripts/users/activity_calendar.js
+++ b/app/assets/javascripts/users/activity_calendar.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
import d3 from 'd3';
const LOADING_HTML = `
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 5728afb4c59..16ebf5916dc 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
/* global Issuable */
/* global emitSidebarEvent */
+import _ from 'underscore';
// TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index fe5e1bbb55c..546a3f625c7 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -1,7 +1,7 @@
/**
* This file is the centerpiece of an attempt to reduce potential conflicts
* between the CE and EE versions of the MR widget. EE additions to the MR widget should
- * be contained in the ./vue_merge_request_widget/ee directory, and should **extend**
+ * be contained in the ee/vue_merge_request_widget directory, and should **extend**
* rather than mutate CE MR Widget code.
*
* This file should be the only source of conflicts between EE and CE. EE-only components should
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index ebef92e9f00..c63703c5791 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -578,6 +578,7 @@
.dropdown-input-field,
.default-dropdown-input {
+ display: block;
width: 100%;
min-height: 30px;
padding: 0 7px;
@@ -726,3 +727,57 @@
@include set-invisible;
overflow: hidden;
}
+
+// TODO: change global style and remove mixin
+@mixin new-style-dropdown {
+ .dropdown-menu,
+ .dropdown-menu-nav {
+ .divider {
+ margin: 6px 0;
+ }
+
+ li {
+ padding: 0 1px;
+
+ &.dropdown-header {
+ padding: 8px 16px;
+ }
+
+ a {
+ border-radius: 0;
+ padding: 8px 16px;
+
+ &.is-focused,
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: $gray-darker;
+ }
+
+ &.is-active {
+ font-weight: inherit;
+
+ &::before {
+ top: 16px;
+ }
+ }
+ }
+ }
+
+ &.dropdown-menu-selectable {
+ li {
+ a {
+ padding: 8px 40px;
+
+ &.is-active::before {
+ left: 16px;
+ }
+ }
+ }
+ }
+ }
+
+ .dropdown-menu-align-right {
+ margin-top: 2px;
+ }
+}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 1c4238bc564..555e444a062 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -4,6 +4,8 @@
*/
header {
+ @include new-style-dropdown;
+
transition: padding $sidebar-transition-duration;
&.navbar-empty {
@@ -313,25 +315,6 @@ header {
.impersonation i {
color: $red-500;
}
-
- // TODO: fallback to global style
- .dropdown-menu,
- .dropdown-menu-nav {
- li {
- padding: 0 1px;
-
- a {
- border-radius: 0;
- padding: 8px 16px;
-
- &:hover,
- &:active,
- &:focus {
- background-color: $gray-darker;
- }
- }
- }
- }
}
.with-performance-bar header.navbar-gitlab {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 868e65a8f46..ab754f4a492 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -369,6 +369,10 @@ ul.indent-list {
background-color: $row-hover;
cursor: pointer;
}
+
+ .avatar-container > a {
+ width: 100%;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index a2de4598167..fcd4c72b430 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -185,3 +185,28 @@
text-overflow: ellipsis;
}
}
+
+// TODO: fallback to global style
+.atwho-view {
+ .atwho-view-ul {
+ padding: 8px 1px;
+
+ li {
+ padding: 8px 16px;
+ border: 0;
+
+ &.cur {
+ background-color: $gray-darker;
+ color: $gl-text-color;
+
+ small {
+ color: inherit;
+ }
+ }
+
+ strong {
+ color: $gl-text-color;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 1c4a84de7ec..795ee91af8b 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -312,6 +312,10 @@ header.navbar-gitlab-new {
// TODO: fallback to global style
.dropdown-menu {
+ .divider {
+ margin: 6px 0;
+ }
+
li {
padding: 0 1px;
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index 54f3e8d882c..3d202183c82 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -143,10 +143,19 @@ $new-sidebar-width: 220px;
white-space: nowrap;
a {
- display: block;
+ display: flex;
+ align-items: center;
padding: 12px 16px;
color: $inactive-color;
}
+
+ svg {
+ fill: $inactive-color;
+ }
+ }
+
+ .nav-item-name {
+ flex: 1;
}
li.active {
@@ -156,11 +165,25 @@ $new-sidebar-width: 220px;
color: $active-color;
font-weight: 700;
}
+
+ svg {
+ fill: $active-color;
+ }
}
@media (max-width: $screen-xs-max) {
left: (-$new-sidebar-width);
}
+
+ .nav-icon-container {
+ display: flex;
+ margin-right: 8px;
+
+ svg {
+ height: 16px;
+ width: 16px;
+ }
+ }
}
.with-performance-bar .nav-sidebar {
@@ -173,7 +196,7 @@ $new-sidebar-width: 220px;
> li {
a {
- padding: 8px 16px 8px 24px;
+ padding: 8px 16px 8px 50px;
&:hover,
&:focus {
@@ -197,8 +220,83 @@ $new-sidebar-width: 220px;
.sidebar-top-level-items {
> li {
+ > a {
+ @media (min-width: $screen-sm-min) {
+ margin-right: 2px;
+ }
+
+ &:hover {
+ color: $gl-text-color;
+ }
+ }
+
+ &:not(.active) {
+ > a {
+ margin-left: 1px;
+ margin-right: 3px;
+ }
+
+ .sidebar-sub-level-items {
+ @media (min-width: $screen-sm-min) {
+ position: fixed;
+ top: 0;
+ left: 220px;
+ width: 150px;
+ margin-top: -1px;
+ padding: 8px 1px;
+ background-color: $white-light;
+ box-shadow: 2px 1px 3px $dropdown-shadow-color;
+ border: 1px solid $gray-darker;
+ border-left: 0;
+ border-radius: 0 3px 3px 0;
+
+ &::before {
+ content: "";
+ position: absolute;
+ top: -30px;
+ bottom: -30px;
+ left: 0;
+ right: -30px;
+ z-index: -1;
+ }
+
+ &::after {
+ content: "";
+ position: absolute;
+ top: 44px;
+ left: -30px;
+ right: 35px;
+ bottom: 0;
+ height: 100%;
+ max-height: 150px;
+ z-index: -1;
+ transform: skew(33deg);
+ }
+
+ &.is-above {
+ margin-top: 1px;
+
+ &::after {
+ top: auto;
+ bottom: 44px;
+ transform: skew(-30deg);
+ }
+ }
+
+ a {
+ padding: 8px 16px;
+ color: $gl-text-color;
+
+ &:hover,
+ &:focus {
+ background-color: $gray-darker;
+ }
+ }
+ }
+ }
+ }
+
.badge {
- float: right;
background-color: $inactive-badge-background;
color: $inactive-color;
}
@@ -206,6 +304,10 @@ $new-sidebar-width: 220px;
&.active {
background: $active-background;
+ > a {
+ margin-left: 4px;
+ }
+
.badge {
color: $active-color;
font-weight: 600;
@@ -216,14 +318,10 @@ $new-sidebar-width: 220px;
}
}
- > a:hover {
- background-color: $hover-background;
- color: $hover-color;
-
- .badge {
- background-color: $indigo-500;
- color: $hover-color;
- }
+ &:not(.active):hover > a,
+ > a:hover,
+ &.is-over > a {
+ background-color: $white-light;
}
}
}
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 87b50c7687e..6753eb08285 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -1,4 +1,6 @@
#cycle-analytics {
+ @include new-style-dropdown;
+
max-width: 1000px;
margin: 24px auto 0;
position: relative;
@@ -110,10 +112,6 @@
.js-ca-dropdown {
top: $gl-padding-top;
-
- .dropdown-menu-align-right {
- margin-top: 2px;
- }
}
.content-list {
@@ -446,24 +444,6 @@
margin-bottom: 20px;
}
}
-
- // TODO: fallback to global style
- .dropdown-menu {
- li {
- padding: 0 1px;
-
- a {
- border-radius: 0;
- padding: 8px 16px;
-
- &:hover,
- &:active,
- &:focus {
- background-color: $gray-darker;
- }
- }
- }
- }
}
.cycle-analytics-overview {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 398fd4444ea..da77346d8b2 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -395,12 +395,11 @@
background-color: transparent;
border: 0;
color: $gl-link-color;
- transition: color 0.1s linear;
+ font-weight: 600;
&:hover,
&:focus {
outline: none;
- text-decoration: underline;
color: $gl-link-hover-color;
}
}
@@ -559,3 +558,68 @@
outline: 0;
}
}
+
+.diff-files-changed {
+ .commit-stat-summary {
+ @include new-style-dropdown;
+ z-index: -1;
+
+ @media (min-width: $screen-sm-min) {
+ margin-left: -$gl-padding;
+ padding-left: $gl-padding;
+ background-color: $white-light;
+ }
+ }
+
+ @media (min-width: $screen-sm-min) {
+ position: -webkit-sticky;
+ position: sticky;
+ top: 84px;
+ background-color: $white-light;
+ z-index: 190;
+
+ + .files,
+ + .alert {
+ margin-top: 1px;
+ }
+
+ &:not(.is-stuck) .diff-stats-additions-deletions-collapsed {
+ display: none;
+ }
+
+ &.is-stuck {
+ padding-top: 0;
+ padding-bottom: 0;
+ border-bottom: 1px solid $white-dark;
+ transform: translateY(16px);
+
+ .diff-stats-additions-deletions-expanded,
+ .inline-parallel-buttons {
+ display: none;
+ }
+
+ + .files,
+ + .alert {
+ margin-top: 30px;
+ }
+ }
+ }
+}
+
+.diff-file-changes {
+ width: 450px;
+ z-index: 150;
+
+ @media (min-width: $screen-sm-min) {
+ left: $gl-padding;
+ }
+
+ a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ }
+}
+
+.diff-file-changes-path {
+ @include str-truncated(78%);
+}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 4693b2434c7..a4e19094508 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -691,8 +691,10 @@
}
.mr-version-controls {
+ position: relative;
background: $gray-light;
color: $gl-text-color;
+ z-index: 199;
.mr-version-menus-container {
display: -webkit-flex;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index b3a90dff89a..d29421aa1b3 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -282,6 +282,8 @@
}
.project-repo-buttons {
+ @include new-style-dropdown;
+
.project-action-button .dropdown-menu {
max-height: 250px;
overflow-y: auto;
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index e0f46172769..44ab07a4367 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,4 +1,5 @@
.tree-holder {
+ @include new-style-dropdown;
.nav-block {
margin: 10px 0;
@@ -202,28 +203,6 @@
}
}
}
-
- // TODO: fallback to global style
- .dropdown-menu:not(.dropdown-menu-selectable) {
- li {
- padding: 0 1px;
-
- &.dropdown-header {
- padding: 8px 16px;
- }
-
- a {
- border-radius: 0;
- padding: 8px 16px;
-
- &:hover,
- &:active,
- &:focus {
- background-color: $gray-darker;
- }
- }
- }
- }
}
.blob-commit-info {