summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/awards_handler.js79
-rw-r--r--app/assets/javascripts/behaviors/gl_emoji.js17
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js5
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.js3
-rw-r--r--app/assets/javascripts/build.js10
-rw-r--r--app/assets/javascripts/diff.js5
-rw-r--r--app/assets/javascripts/diff_notes/components/diff_note_avatars.js4
-rw-r--r--app/assets/javascripts/files_comment_button.js193
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js25
-rw-r--r--app/assets/javascripts/gl_form.js6
-rw-r--r--app/assets/javascripts/group_name.js23
-rw-r--r--app/assets/javascripts/issuable_bulk_update_sidebar.js26
-rw-r--r--app/assets/javascripts/jobs/components/sidebar_details_block.vue13
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js24
-rw-r--r--app/assets/javascripts/main.js6
-rw-r--r--app/assets/javascripts/merge_request_tabs.js4
-rw-r--r--app/assets/javascripts/notes.js23
-rw-r--r--app/assets/javascripts/right_sidebar.js23
-rw-r--r--app/assets/javascripts/sidebar_height_manager.js33
-rw-r--r--app/assets/javascripts/single_file_diff.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue6
-rw-r--r--app/assets/javascripts/webpack.js9
-rw-r--r--app/assets/stylesheets/framework/files.scss2
-rw-r--r--app/assets/stylesheets/new_nav.scss129
-rw-r--r--app/assets/stylesheets/new_sidebar.scss38
-rw-r--r--app/assets/stylesheets/pages/diff.scss10
-rw-r--r--app/assets/stylesheets/pages/issuable.scss55
-rw-r--r--app/assets/stylesheets/pages/labels.scss6
-rw-r--r--app/assets/stylesheets/pages/notes.scss10
-rw-r--r--app/assets/stylesheets/pages/projects.scss3
-rw-r--r--app/assets/stylesheets/pages/runners.scss17
-rw-r--r--app/assets/stylesheets/pages/tree.scss6
32 files changed, 526 insertions, 291 deletions
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index c34d80f0601..18cd04b176a 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -2,7 +2,6 @@
/* global Flash */
import Cookies from 'js-cookie';
-import * as Emoji from './emoji';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
@@ -24,27 +23,9 @@ const categoryLabelMap = {
flags: 'Flags',
};
-function renderCategory(name, emojiList, opts = {}) {
- return `
- <h5 class="emoji-menu-title">
- ${name}
- </h5>
- <ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
- ${emojiList.map(emojiName => `
- <li class="emoji-menu-list-item">
- <button class="emoji-menu-btn text-center js-emoji-btn" type="button">
- ${Emoji.glEmojiTag(emojiName, {
- sprite: true,
- })}
- </button>
- </li>
- `).join('\n')}
- </ul>
- `;
-}
-
-export default class AwardsHandler {
- constructor() {
+class AwardsHandler {
+ constructor(emoji) {
+ this.emoji = emoji;
this.eventListeners = [];
// If the user shows intent let's pre-build the menu
this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
@@ -78,10 +59,10 @@ export default class AwardsHandler {
const $target = $(e.currentTarget);
const $glEmojiElement = $target.find('gl-emoji');
const $spriteIconElement = $target.find('.icon');
- const emoji = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
+ const emojiName = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
$target.closest('.js-awards-block').addClass('current');
- this.addAward(this.getVotesBlock(), this.getAwardUrl(), emoji);
+ this.addAward(this.getVotesBlock(), this.getAwardUrl(), emojiName);
});
}
@@ -139,16 +120,16 @@ export default class AwardsHandler {
this.isCreatingEmojiMenu = true;
// Render the first category
- const categoryMap = Emoji.getEmojiCategoryMap();
+ const categoryMap = this.emoji.getEmojiCategoryMap();
const categoryNameKey = Object.keys(categoryMap)[0];
const emojisInCategory = categoryMap[categoryNameKey];
- const firstCategory = renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
+ const firstCategory = this.renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
// Render the frequently used
const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
let frequentlyUsedCatgegory = '';
if (frequentlyUsedEmojis.length > 0) {
- frequentlyUsedCatgegory = renderCategory('Frequently used', frequentlyUsedEmojis, {
+ frequentlyUsedCatgegory = this.renderCategory('Frequently used', frequentlyUsedEmojis, {
menuListClass: 'frequent-emojis',
});
}
@@ -179,7 +160,7 @@ export default class AwardsHandler {
}
this.isAddingRemainingEmojiMenuCategories = true;
- const categoryMap = Emoji.getEmojiCategoryMap();
+ const categoryMap = this.emoji.getEmojiCategoryMap();
// Avoid the jank and render the remaining categories separately
// This will take more time, but makes UI more responsive
@@ -191,7 +172,7 @@ export default class AwardsHandler {
promiseChain.then(() =>
new Promise((resolve) => {
const emojisInCategory = categoryMap[categoryNameKey];
- const categoryMarkup = renderCategory(
+ const categoryMarkup = this.renderCategory(
categoryLabelMap[categoryNameKey],
emojisInCategory,
);
@@ -216,6 +197,25 @@ export default class AwardsHandler {
});
}
+ renderCategory(name, emojiList, opts = {}) {
+ return `
+ <h5 class="emoji-menu-title">
+ ${name}
+ </h5>
+ <ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
+ ${emojiList.map(emojiName => `
+ <li class="emoji-menu-list-item">
+ <button class="emoji-menu-btn text-center js-emoji-btn" type="button">
+ ${this.emoji.glEmojiTag(emojiName, {
+ sprite: true,
+ })}
+ </button>
+ </li>
+ `).join('\n')}
+ </ul>
+ `;
+ }
+
positionMenu($menu, $addBtn) {
const position = $addBtn.data('position');
// The menu could potentially be off-screen or in a hidden overflow element
@@ -234,7 +234,7 @@ export default class AwardsHandler {
}
addAward(votesBlock, awardUrl, emoji, checkMutuality, callback) {
- const normalizedEmoji = Emoji.normalizeEmojiName(emoji);
+ const normalizedEmoji = this.emoji.normalizeEmojiName(emoji);
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
this.postEmoji($emojiButton, awardUrl, normalizedEmoji, () => {
this.addAwardToEmojiBar(votesBlock, normalizedEmoji, checkMutuality);
@@ -249,7 +249,7 @@ export default class AwardsHandler {
this.checkMutuality(votesBlock, emoji);
}
this.addEmojiToFrequentlyUsedList(emoji);
- const normalizedEmoji = Emoji.normalizeEmojiName(emoji);
+ const normalizedEmoji = this.emoji.normalizeEmojiName(emoji);
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
if ($emojiButton.length > 0) {
if (this.isActive($emojiButton)) {
@@ -374,7 +374,7 @@ export default class AwardsHandler {
createAwardButtonForVotesBlock(votesBlock, emojiName) {
const buttonHtml = `
<button class="btn award-control js-emoji-btn has-tooltip active" title="You" data-placement="bottom">
- ${Emoji.glEmojiTag(emojiName)}
+ ${this.emoji.glEmojiTag(emojiName)}
<span class="award-control-text js-counter">1</span>
</button>
`;
@@ -440,7 +440,7 @@ export default class AwardsHandler {
}
addEmojiToFrequentlyUsedList(emoji) {
- if (Emoji.isEmojiNameValid(emoji)) {
+ if (this.emoji.isEmojiNameValid(emoji)) {
this.frequentlyUsedEmojis = _.uniq(this.getFrequentlyUsedEmojis().concat(emoji));
Cookies.set('frequently_used_emojis', this.frequentlyUsedEmojis.join(','), { expires: 365 });
}
@@ -450,7 +450,7 @@ export default class AwardsHandler {
return this.frequentlyUsedEmojis || (() => {
const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
- inputName => Emoji.isEmojiNameValid(inputName),
+ inputName => this.emoji.isEmojiNameValid(inputName),
);
return this.frequentlyUsedEmojis;
@@ -493,7 +493,7 @@ export default class AwardsHandler {
}
findMatchingEmojiElements(query) {
- const emojiMatches = Emoji.filterEmojiNamesByAlias(query);
+ const emojiMatches = this.emoji.filterEmojiNamesByAlias(query);
const $emojiElements = $('.emoji-menu-list:not(.frequent-emojis) [data-name]');
const $matchingElements = $emojiElements
.filter((i, elm) => emojiMatches.indexOf(elm.dataset.name) >= 0);
@@ -507,3 +507,12 @@ export default class AwardsHandler {
$('.emoji-menu').remove();
}
}
+
+let awardsHandlerPromise = null;
+export default function loadAwardsHandler(reload = false) {
+ if (!awardsHandlerPromise || reload) {
+ awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji')
+ .then(Emoji => new AwardsHandler(Emoji));
+ }
+ return awardsHandlerPromise;
+}
diff --git a/app/assets/javascripts/behaviors/gl_emoji.js b/app/assets/javascripts/behaviors/gl_emoji.js
index 8156e491a42..7e98e04303a 100644
--- a/app/assets/javascripts/behaviors/gl_emoji.js
+++ b/app/assets/javascripts/behaviors/gl_emoji.js
@@ -1,5 +1,4 @@
import installCustomElements from 'document-register-element';
-import { emojiImageTag, emojiFallbackImageSrc } from '../emoji';
import isEmojiUnicodeSupported from '../emoji/support';
installCustomElements(window);
@@ -32,11 +31,19 @@ export default function installGlEmojiElement() {
// IE 11 doesn't like adding multiple at once :(
this.classList.add('emoji-icon');
this.classList.add(fallbackSpriteClass);
- } else if (hasImageFallback) {
- this.innerHTML = emojiImageTag(name, fallbackSrc);
} else {
- const src = emojiFallbackImageSrc(name);
- this.innerHTML = emojiImageTag(name, src);
+ import(/* webpackChunkName: 'emoji' */ '../emoji')
+ .then(({ emojiImageTag, emojiFallbackImageSrc }) => {
+ if (hasImageFallback) {
+ this.innerHTML = emojiImageTag(name, fallbackSrc);
+ } else {
+ const src = emojiFallbackImageSrc(name);
+ this.innerHTML = emojiImageTag(name, src);
+ }
+ })
+ .catch(() => {
+ // do nothing
+ });
}
}
};
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index c7afd4ead6b..590b7be36e3 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -34,7 +34,10 @@ gl.issueBoards.BoardSidebar = Vue.extend({
},
milestoneTitle() {
return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
- }
+ },
+ canRemove() {
+ return !this.list.preset;
+ },
},
watch: {
detail: {
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
index 5597f128b80..6a900d4abd0 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
@@ -46,8 +46,7 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({
},
template: `
<div
- class="block list"
- v-if="list.type !== 'closed'">
+ class="block list">
<button
class="btn btn-default btn-block"
type="button"
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 9974e135022..60103155ce0 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -85,9 +85,8 @@ window.Build = (function () {
if (!this.hasBeenScrolled) {
this.scrollToBottom();
}
- });
-
- this.verifyTopPosition();
+ })
+ .then(() => this.verifyTopPosition());
}
Build.prototype.canScroll = function () {
@@ -176,7 +175,7 @@ window.Build = (function () {
}
if ($flashError.length) {
- topPostion += $flashError.outerHeight();
+ topPostion += $flashError.outerHeight() + prependTopDefault;
}
this.$buildTrace.css({
@@ -234,7 +233,8 @@ window.Build = (function () {
if (!this.hasBeenScrolled) {
this.scrollToBottom();
}
- });
+ })
+ .then(() => this.verifyTopPosition());
}, 4000);
} else {
this.$buildRefreshAnimation.remove();
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index 725ec7b9c70..1be9df19c81 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -1,6 +1,7 @@
/* eslint-disable class-methods-use-this */
import './lib/utils/url_utility';
+import FilesCommentButton from './files_comment_button';
const UNFOLD_COUNT = 20;
let isBound = false;
@@ -8,8 +9,10 @@ let isBound = false;
class Diff {
constructor() {
const $diffFile = $('.files .diff-file');
+
$diffFile.singleFileDiff();
- $diffFile.filesCommentButton();
+
+ FilesCommentButton.init($diffFile);
$diffFile.each((index, file) => new gl.ImageFile(file));
diff --git a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
index 517bdb6be09..c37249c060a 100644
--- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
+++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
@@ -139,9 +139,9 @@ const DiffNoteAvatars = Vue.extend({
const notesCount = this.notesCount;
$(this.$el).closest('.js-avatar-container')
- .toggleClass('js-no-comment-btn', notesCount > 0)
+ .toggleClass('no-comment-btn', notesCount > 0)
.nextUntil('.js-avatar-container')
- .toggleClass('js-no-comment-btn', notesCount > 0);
+ .toggleClass('no-comment-btn', notesCount > 0);
},
toggleDiscussionsToggleState() {
const $notesHolders = $(this.$el).closest('.code').find('.notes_holder');
diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js
index 534e651b030..d02e4cd5876 100644
--- a/app/assets/javascripts/files_comment_button.js
+++ b/app/assets/javascripts/files_comment_button.js
@@ -1,150 +1,73 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, consistent-return */
-/* global FilesCommentButton */
/* global notes */
-let $commentButtonTemplate;
-
-window.FilesCommentButton = (function() {
- var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
-
- COMMENT_BUTTON_CLASS = '.add-diff-note';
-
- LINE_HOLDER_CLASS = '.line_holder';
-
- LINE_NUMBER_CLASS = 'diff-line-num';
-
- LINE_CONTENT_CLASS = 'line_content';
-
- UNFOLDABLE_LINE_CLASS = 'js-unfold';
-
- EMPTY_CELL_CLASS = 'empty-cell';
-
- OLD_LINE_CLASS = 'old_line';
-
- LINE_COLUMN_CLASSES = "." + LINE_NUMBER_CLASS + ", .line_content";
-
- TEXT_FILE_SELECTOR = '.text-file';
-
- function FilesCommentButton(filesContainerElement) {
- this.render = this.render.bind(this);
- this.hideButton = this.hideButton.bind(this);
- this.isParallelView = notes.isParallelView();
- filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
- .on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton);
- }
-
- FilesCommentButton.prototype.render = function(e) {
- var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button;
- $currentTarget = $(e.currentTarget);
-
- if ($currentTarget.hasClass('js-no-comment-btn')) return;
-
- lineContentElement = this.getLineContent($currentTarget);
- buttonParentElement = this.getButtonParent($currentTarget);
-
- if (!this.validateButtonParent(buttonParentElement) || !this.validateLineContent(lineContentElement)) return;
-
- $button = $(COMMENT_BUTTON_CLASS, buttonParentElement);
- buttonParentElement.addClass('is-over')
- .nextUntil(`.${LINE_CONTENT_CLASS}`).addClass('is-over');
-
- if ($button.length) {
- return;
+/* Developer beware! Do not add logic to showButton or hideButton
+ * that will force a reflow. Doing so will create a signficant performance
+ * bottleneck for pages with large diffs. For a comprehensive list of what
+ * causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a
+ */
+
+const LINE_NUMBER_CLASS = 'diff-line-num';
+const UNFOLDABLE_LINE_CLASS = 'js-unfold';
+const NO_COMMENT_CLASS = 'no-comment-btn';
+const EMPTY_CELL_CLASS = 'empty-cell';
+const OLD_LINE_CLASS = 'old_line';
+const LINE_COLUMN_CLASSES = `.${LINE_NUMBER_CLASS}, .line_content`;
+const DIFF_CONTAINER_SELECTOR = '.files';
+const DIFF_EXPANDED_CLASS = 'diff-expanded';
+
+export default {
+ init($diffFile) {
+ /* Caching is used only when the following members are *true*. This is because there are likely to be
+ * differently configured versions of diffs in the same session. However if these values are true, they
+ * will be true in all cases */
+
+ if (!this.userCanCreateNote) {
+ // data-can-create-note is an empty string when true, otherwise undefined
+ this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === '';
}
- textFileElement = this.getTextFileElement($currentTarget);
- buttonParentElement.append(this.buildButton({
- discussionID: lineContentElement.attr('data-discussion-id'),
- lineType: lineContentElement.attr('data-line-type'),
-
- noteableType: textFileElement.attr('data-noteable-type'),
- noteableID: textFileElement.attr('data-noteable-id'),
- commitID: textFileElement.attr('data-commit-id'),
- noteType: lineContentElement.attr('data-note-type'),
-
- // LegacyDiffNote
- lineCode: lineContentElement.attr('data-line-code'),
-
- // DiffNote
- position: lineContentElement.attr('data-position')
- }));
- };
-
- FilesCommentButton.prototype.hideButton = function(e) {
- var $currentTarget = $(e.currentTarget);
- var buttonParentElement = this.getButtonParent($currentTarget);
-
- buttonParentElement.removeClass('is-over')
- .nextUntil(`.${LINE_CONTENT_CLASS}`).removeClass('is-over');
- };
-
- FilesCommentButton.prototype.buildButton = function(buttonAttributes) {
- return $commentButtonTemplate.clone().attr({
- 'data-discussion-id': buttonAttributes.discussionID,
- 'data-line-type': buttonAttributes.lineType,
-
- 'data-noteable-type': buttonAttributes.noteableType,
- 'data-noteable-id': buttonAttributes.noteableID,
- 'data-commit-id': buttonAttributes.commitID,
- 'data-note-type': buttonAttributes.noteType,
-
- // LegacyDiffNote
- 'data-line-code': buttonAttributes.lineCode,
-
- // DiffNote
- 'data-position': buttonAttributes.position
- });
- };
-
- FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) {
- return hoveredElement.closest(TEXT_FILE_SELECTOR);
- };
-
- FilesCommentButton.prototype.getLineContent = function(hoveredElement) {
- if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) {
- return hoveredElement;
- }
- if (!this.isParallelView) {
- return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS);
- } else {
- return $(hoveredElement).next("." + LINE_CONTENT_CLASS);
+ if (typeof notes !== 'undefined' && !this.isParallelView) {
+ this.isParallelView = notes.isParallelView && notes.isParallelView();
}
- };
- FilesCommentButton.prototype.getButtonParent = function(hoveredElement) {
- if (!this.isParallelView) {
- if (hoveredElement.hasClass(OLD_LINE_CLASS)) {
- return hoveredElement;
- }
- return hoveredElement.parent().find("." + OLD_LINE_CLASS);
- } else {
- if (hoveredElement.hasClass(LINE_NUMBER_CLASS)) {
- return hoveredElement;
- }
- return $(hoveredElement).prev("." + LINE_NUMBER_CLASS);
+ if (this.userCanCreateNote) {
+ $diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e))
+ .on('mouseleave', LINE_COLUMN_CLASSES, e => this.hideButton(this.isParallelView, e));
}
- };
+ },
- FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) {
- return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS);
- };
+ showButton(isParallelView, e) {
+ const buttonParentElement = this.getButtonParent(e.currentTarget, isParallelView);
- FilesCommentButton.prototype.validateLineContent = function(lineContentElement) {
- return lineContentElement.attr('data-note-type') && lineContentElement.attr('data-note-type') !== '';
- };
+ if (!this.validateButtonParent(buttonParentElement)) return;
- return FilesCommentButton;
-})();
+ buttonParentElement.classList.add('is-over');
+ buttonParentElement.nextElementSibling.classList.add('is-over');
+ },
-$.fn.filesCommentButton = function() {
- $commentButtonTemplate = $('<button name="button" type="submit" class="add-diff-note js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>');
+ hideButton(isParallelView, e) {
+ const buttonParentElement = this.getButtonParent(e.currentTarget, isParallelView);
- if (!(this && (this.parent().data('can-create-note') != null))) {
- return;
- }
- return this.each(function() {
- if (!$.data(this, 'filesCommentButton')) {
- return $.data(this, 'filesCommentButton', new FilesCommentButton($(this)));
+ buttonParentElement.classList.remove('is-over');
+ buttonParentElement.nextElementSibling.classList.remove('is-over');
+ },
+
+ getButtonParent(hoveredElement, isParallelView) {
+ if (isParallelView) {
+ if (!hoveredElement.classList.contains(LINE_NUMBER_CLASS)) {
+ return hoveredElement.previousElementSibling;
+ }
+ } else if (!hoveredElement.classList.contains(OLD_LINE_CLASS)) {
+ return hoveredElement.parentNode.querySelector(`.${OLD_LINE_CLASS}`);
}
- });
+ return hoveredElement;
+ },
+
+ validateButtonParent(buttonParentElement) {
+ return !buttonParentElement.classList.contains(EMPTY_CELL_CLASS) &&
+ !buttonParentElement.classList.contains(UNFOLDABLE_LINE_CLASS) &&
+ !buttonParentElement.classList.contains(NO_COMMENT_CLASS) &&
+ !buttonParentElement.parentNode.classList.contains(DIFF_EXPANDED_CLASS);
+ },
};
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index f99bac7da1a..2c56b718212 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -1,4 +1,3 @@
-import { validEmojiNames, glEmojiTag } from './emoji';
import glRegexp from './lib/utils/regexp';
import AjaxCache from './lib/utils/ajax_cache';
@@ -373,7 +372,12 @@ class GfmAutoComplete {
if (this.cachedData[at]) {
this.loadData($input, at, this.cachedData[at]);
} else if (GfmAutoComplete.atTypeMap[at] === 'emojis') {
- this.loadData($input, at, validEmojiNames);
+ import(/* webpackChunkName: 'emoji' */ './emoji')
+ .then(({ validEmojiNames, glEmojiTag }) => {
+ this.loadData($input, at, validEmojiNames);
+ GfmAutoComplete.glEmojiTag = glEmojiTag;
+ })
+ .catch(() => { this.isLoadingData[at] = false; });
} else {
AjaxCache.retrieve(this.dataSources[GfmAutoComplete.atTypeMap[at]], true)
.then((data) => {
@@ -396,6 +400,13 @@ class GfmAutoComplete {
this.cachedData = {};
}
+ destroy() {
+ this.input.each((i, input) => {
+ const $input = $(input);
+ $input.atwho('destroy');
+ });
+ }
+
static isLoading(data) {
let dataToInspect = data;
if (data && data.length > 0) {
@@ -421,12 +432,14 @@ GfmAutoComplete.atTypeMap = {
};
// Emoji
+GfmAutoComplete.glEmojiTag = null;
GfmAutoComplete.Emoji = {
templateFunction(name) {
- return `<li>
- ${name} ${glEmojiTag(name)}
- </li>
- `;
+ // glEmojiTag helper is loaded on-demand in fetchData()
+ if (GfmAutoComplete.glEmojiTag) {
+ return `<li>${name} ${GfmAutoComplete.glEmojiTag(name)}</li>`;
+ }
+ return `<li>${name}</li>`;
},
};
// Team Members
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index dc9f114af99..4e8141b2956 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -21,6 +21,9 @@ function GLForm(form, enableGFM = false) {
GLForm.prototype.destroy = function() {
// Clean form listeners
this.clearEventListeners();
+ if (this.autoComplete) {
+ this.autoComplete.destroy();
+ }
return this.form.data('gl-form', null);
};
@@ -33,7 +36,8 @@ GLForm.prototype.setupForm = function() {
this.form.addClass('gfm-form');
// remove notify commit author checkbox for non-commit notes
gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button, .js-note-new-discussion'));
- new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup(this.form.find('.js-gfm-input'), {
+ this.autoComplete = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
+ this.autoComplete.setup(this.form.find('.js-gfm-input'), {
emojis: true,
members: this.enableGFM,
issues: this.enableGFM,
diff --git a/app/assets/javascripts/group_name.js b/app/assets/javascripts/group_name.js
index 462d792b8d5..37c6765d942 100644
--- a/app/assets/javascripts/group_name.js
+++ b/app/assets/javascripts/group_name.js
@@ -1,13 +1,13 @@
-
+import Cookies from 'js-cookie';
import _ from 'underscore';
export default class GroupName {
constructor() {
- this.titleContainer = document.querySelector('.title-container');
- this.title = document.querySelector('.title');
+ this.titleContainer = document.querySelector('.js-title-container');
+ this.title = this.titleContainer.querySelector('.title');
this.titleWidth = this.title.offsetWidth;
- this.groupTitle = document.querySelector('.group-title');
- this.groups = document.querySelectorAll('.group-path');
+ this.groupTitle = this.titleContainer.querySelector('.group-title');
+ this.groups = this.titleContainer.querySelectorAll('.group-path');
this.toggle = null;
this.isHidden = false;
this.init();
@@ -33,11 +33,20 @@ export default class GroupName {
createToggle() {
this.toggle = document.createElement('button');
+ this.toggle.setAttribute('type', 'button');
this.toggle.className = 'text-expander group-name-toggle';
this.toggle.setAttribute('aria-label', 'Toggle full path');
- this.toggle.innerHTML = '...';
+ if (Cookies.get('new_nav') === 'true') {
+ this.toggle.innerHTML = '<i class="fa fa-ellipsis-h" aria-hidden="true"></i>';
+ } else {
+ this.toggle.innerHTML = '...';
+ }
this.toggle.addEventListener('click', this.toggleGroups.bind(this));
- this.titleContainer.insertBefore(this.toggle, this.title);
+ if (Cookies.get('new_nav') === 'true') {
+ this.title.insertBefore(this.toggle, this.groupTitle);
+ } else {
+ this.titleContainer.insertBefore(this.toggle, this.title);
+ }
this.toggleGroups();
}
diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js
index a8856120c5e..4f376599ba9 100644
--- a/app/assets/javascripts/issuable_bulk_update_sidebar.js
+++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js
@@ -5,6 +5,7 @@
/* global SubscriptionSelect */
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
+import SidebarHeightManager from './sidebar_height_manager';
const HIDDEN_CLASS = 'hidden';
const DISABLED_CONTENT_CLASS = 'disabled-content';
@@ -56,18 +57,6 @@ export default class IssuableBulkUpdateSidebar {
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();
}
@@ -97,7 +86,7 @@ export default class IssuableBulkUpdateSidebar {
this.toggleCheckboxDisplay(enable);
if (enable) {
- this.initSidebar();
+ SidebarHeightManager.init();
}
}
@@ -143,17 +132,6 @@ export default class IssuableBulkUpdateSidebar {
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');
diff --git a/app/assets/javascripts/jobs/components/sidebar_details_block.vue b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
index 4223a8fea49..d0145fed396 100644
--- a/app/assets/javascripts/jobs/components/sidebar_details_block.vue
+++ b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
@@ -39,6 +39,17 @@
runnerId() {
return `#${this.job.runner.id}`;
},
+ renderBlock() {
+ return this.job.merge_request ||
+ this.job.duration ||
+ this.job.finished_data ||
+ this.job.erased_at ||
+ this.job.queued ||
+ this.job.runner ||
+ this.job.coverage ||
+ this.job.tags.length ||
+ this.job.cancel_path;
+ },
},
};
</script>
@@ -63,7 +74,7 @@
Retry
</a>
</div>
- <div class="block">
+ <div :class="{block : renderBlock }">
<p
class="build-detail-row js-job-mr"
v-if="job.merge_request">
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index bfcc50996cc..1d1763c3963 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -112,29 +112,11 @@ window.dateFormat = dateFormat;
return timefor;
};
- w.gl.utils.cachedTimeagoElements = [];
w.gl.utils.renderTimeago = function($els) {
- if (!$els && !w.gl.utils.cachedTimeagoElements.length) {
- w.gl.utils.cachedTimeagoElements = [].slice.call(document.querySelectorAll('.js-timeago-render'));
- } else if ($els) {
- w.gl.utils.cachedTimeagoElements = w.gl.utils.cachedTimeagoElements.concat($els.toArray());
- }
-
- w.gl.utils.cachedTimeagoElements.forEach(gl.utils.updateTimeagoText);
- };
-
- w.gl.utils.updateTimeagoText = function(el) {
- const formattedDate = gl.utils.getTimeago().format(el.getAttribute('datetime'), lang);
-
- if (el.textContent !== formattedDate) {
- el.textContent = formattedDate;
- }
- };
-
- w.gl.utils.initTimeagoTimeout = function() {
- gl.utils.renderTimeago();
+ const timeagoEls = $els || document.querySelectorAll('.js-timeago-render');
- gl.utils.timeagoTimeout = setTimeout(gl.utils.initTimeagoTimeout, 1000);
+ // timeago.js sets timeouts internally for each timeago value to be updated in real time
+ gl.utils.getTimeago().render(timeagoEls, lang);
};
w.gl.utils.getDayDifference = function(a, b) {
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index d27b4ec78c6..fe752d95b90 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -70,7 +70,7 @@ import './ajax_loading_spinner';
import './api';
import './aside';
import './autosave';
-import AwardsHandler from './awards_handler';
+import loadAwardsHandler from './awards_handler';
import './breakpoints';
import './broadcast_message';
import './build';
@@ -355,10 +355,10 @@ $(function () {
$window.off('resize.app').on('resize.app', function () {
return fitSidebarForSize();
});
- gl.awardsHandler = new AwardsHandler();
+ loadAwardsHandler();
new Aside();
- gl.utils.initTimeagoTimeout();
+ gl.utils.renderTimeago();
$(document).trigger('init.scrolling-tabs');
});
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 3cf3233cc65..7840f05a8ae 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -144,7 +144,9 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
this.resetViewContainer();
this.mountPipelinesView();
} else {
- this.expandView();
+ if (Breakpoints.get().getBreakpointSize() !== 'xs') {
+ this.expandView();
+ }
this.resetViewContainer();
this.destroyPipelinesView();
}
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 34476f3303f..555b8c8a65c 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -18,6 +18,7 @@ import 'vendor/jquery.caret'; // required by jquery.atwho
import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache';
import CommentTypeToggle from './comment_type_toggle';
+import loadAwardsHandler from './awards_handler';
import './autosave';
import './dropzone_input';
import './task_list';
@@ -291,8 +292,13 @@ export default class Notes {
if ('emoji_award' in noteEntity.commands_changes) {
votesBlock = $('.js-awards-block').eq(0);
- gl.awardsHandler.addAwardToEmojiBar(votesBlock, noteEntity.commands_changes.emoji_award);
- return gl.awardsHandler.scrollToAwards();
+
+ loadAwardsHandler().then((awardsHandler) => {
+ awardsHandler.addAwardToEmojiBar(votesBlock, noteEntity.commands_changes.emoji_award);
+ awardsHandler.scrollToAwards();
+ }).catch(() => {
+ // ignore
+ });
}
}
}
@@ -337,6 +343,10 @@ export default class Notes {
if (!noteEntity.valid) {
if (noteEntity.errors.commands_only) {
+ if (noteEntity.commands_changes &&
+ Object.keys(noteEntity.commands_changes).length > 0) {
+ $notesList.find('.system-note.being-posted').remove();
+ }
this.addFlash(noteEntity.errors.commands_only, 'notice', this.parentTimeline);
this.refresh();
}
@@ -829,6 +839,8 @@ export default class Notes {
*/
setupDiscussionNoteForm(dataHolder, form) {
// setup note target
+ const diffFileData = dataHolder.closest('.text-file');
+
var discussionID = dataHolder.data('discussionId');
if (discussionID) {
@@ -839,9 +851,10 @@ export default class Notes {
form.attr('data-line-code', dataHolder.data('lineCode'));
form.find('#line_type').val(dataHolder.data('lineType'));
- form.find('#note_noteable_type').val(dataHolder.data('noteableType'));
- form.find('#note_noteable_id').val(dataHolder.data('noteableId'));
- form.find('#note_commit_id').val(dataHolder.data('commitId'));
+ form.find('#note_noteable_type').val(diffFileData.data('noteableType'));
+ form.find('#note_noteable_id').val(diffFileData.data('noteableId'));
+ form.find('#note_commit_id').val(diffFileData.data('commitId'));
+
form.find('#note_type').val(dataHolder.data('noteType'));
// LegacyDiffNote
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 322162afdb8..d8f1fe10b26 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,6 +1,7 @@
/* 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 Cookies from 'js-cookie';
+import SidebarHeightManager from './sidebar_height_manager';
(function() {
this.Sidebar = (function() {
@@ -8,10 +9,6 @@ import Cookies from 'js-cookie';
this.toggleTodo = this.toggleTodo.bind(this);
this.sidebar = $('aside');
- this.$sidebarInner = this.sidebar.find('.issuable-sidebar');
- this.$navGitlab = $('.navbar-gitlab');
- this.$rightSidebar = $('.js-right-sidebar');
-
this.removeListeners();
this.addEventListeners();
}
@@ -25,16 +22,14 @@ import Cookies from 'js-cookie';
};
Sidebar.prototype.addEventListeners = function() {
+ SidebarHeightManager.init();
const $document = $(document);
- const throttledSetSidebarHeight = _.throttle(this.setSidebarHeight.bind(this), 20);
- const debouncedSetSidebarHeight = _.debounce(this.setSidebarHeight.bind(this), 200);
this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
$('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
$('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
- $(window).on('resize', () => throttledSetSidebarHeight());
- $document.on('scroll', () => debouncedSetSidebarHeight());
+
$document.on('click', '.js-sidebar-toggle', function(e, triggered) {
var $allGutterToggleIcons, $this, $thisIcon;
e.preventDefault();
@@ -212,18 +207,6 @@ import Cookies from 'js-cookie';
}
};
- Sidebar.prototype.setSidebarHeight = function() {
- const $navHeight = this.$navGitlab.outerHeight();
- const diff = $navHeight - $(window).scrollTop();
- if (diff > 0) {
- this.$rightSidebar.outerHeight($(window).height() - diff);
- this.$sidebarInner.height('100%');
- } else {
- this.$rightSidebar.outerHeight('100%');
- this.$sidebarInner.height('');
- }
- };
-
Sidebar.prototype.isOpen = function() {
return this.sidebar.is('.right-sidebar-expanded');
};
diff --git a/app/assets/javascripts/sidebar_height_manager.js b/app/assets/javascripts/sidebar_height_manager.js
new file mode 100644
index 00000000000..022415f22b2
--- /dev/null
+++ b/app/assets/javascripts/sidebar_height_manager.js
@@ -0,0 +1,33 @@
+export default {
+ init() {
+ if (!this.initialized) {
+ this.$window = $(window);
+ this.$rightSidebar = $('.js-right-sidebar');
+ this.$navHeight = $('.navbar-gitlab').outerHeight() +
+ $('.layout-nav').outerHeight() +
+ $('.sub-nav-scroll').outerHeight();
+
+ const throttledSetSidebarHeight = _.throttle(() => this.setSidebarHeight(), 20);
+ const debouncedSetSidebarHeight = _.debounce(() => this.setSidebarHeight(), 200);
+
+ this.$window.on('scroll', throttledSetSidebarHeight);
+ this.$window.on('resize', debouncedSetSidebarHeight);
+ this.initialized = true;
+ }
+ },
+
+ setSidebarHeight() {
+ const currentScrollDepth = window.pageYOffset || 0;
+ const diff = this.$navHeight - currentScrollDepth;
+
+ if (diff > 0) {
+ const newSidebarHeight = window.innerHeight - diff;
+ this.$rightSidebar.outerHeight(newSidebarHeight);
+ this.sidebarHeightIsCustom = true;
+ } else if (this.sidebarHeightIsCustom) {
+ this.$rightSidebar.outerHeight('100%');
+ this.sidebarHeightIsCustom = false;
+ }
+ },
+};
+
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index c44892dae3d..9316a2af0b7 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -1,5 +1,7 @@
/* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, max-len */
+import FilesCommentButton from './files_comment_button';
+
(function() {
window.SingleFileDiff = (function() {
var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER;
@@ -78,6 +80,8 @@
gl.diffNotesCompileComponents();
}
+ FilesCommentButton.init($(_this.file));
+
if (cb) cb();
};
})(this));
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index e6977681e96..8303c556f64 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -64,6 +64,12 @@
*/
return new gl.GLForm($(this.$refs['gl-form']), true);
},
+ beforeDestroy() {
+ const glForm = $(this.$refs['gl-form']).data('gl-form');
+ if (glForm) {
+ glForm.destroy();
+ }
+ },
};
</script>
diff --git a/app/assets/javascripts/webpack.js b/app/assets/javascripts/webpack.js
new file mode 100644
index 00000000000..9a9cf395fb8
--- /dev/null
+++ b/app/assets/javascripts/webpack.js
@@ -0,0 +1,9 @@
+/**
+ * This is the first script loaded by webpack's runtime. It is used to manually configure
+ * config.output.publicPath to account for relative_url_root or CDN settings which cannot be
+ * baked-in to our webpack bundles.
+ */
+
+if (gon && gon.webpack_public_path) {
+ __webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line
+}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 245117b2559..c7c2684d548 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -17,6 +17,8 @@
max-width: $limited-layout-width-sm;
margin-left: auto;
margin-right: auto;
+ padding-top: 64px;
+ padding-bottom: 64px;
}
}
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 441bfc479f6..bfb7a0c7e25 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -11,20 +11,19 @@ header.navbar-gitlab-new {
padding-left: 0;
.title-container {
+ align-items: stretch;
padding-top: 0;
overflow: visible;
}
.title {
- display: block;
- height: 100%;
+ display: flex;
padding-right: 0;
color: currentColor;
> a {
display: flex;
align-items: center;
- height: 100%;
padding-top: 3px;
padding-right: $gl-padding;
padding-left: $gl-padding;
@@ -265,3 +264,127 @@ header.navbar-gitlab-new {
}
}
}
+
+.breadcrumbs {
+ display: flex;
+ min-height: 60px;
+ padding-top: $gl-padding-top;
+ padding-bottom: $gl-padding-top;
+ color: $gl-text-color;
+ border-bottom: 1px solid $border-color;
+
+ .dropdown-toggle-caret {
+ position: relative;
+ top: -1px;
+ padding: 0 5px;
+ color: rgba($black, .65);
+ font-size: 10px;
+ line-height: 1;
+ background: none;
+ border: 0;
+
+ &:focus {
+ outline: 0;
+ }
+ }
+}
+
+.breadcrumbs-container {
+ display: flex;
+ width: 100%;
+ position: relative;
+
+ .dropdown-menu-projects {
+ margin-top: -$gl-padding;
+ margin-left: $gl-padding;
+ }
+}
+
+.breadcrumbs-links {
+ flex: 1;
+ align-self: center;
+ color: $black-transparent;
+
+ a {
+ color: rgba($black, .65);
+
+ &:not(:first-child),
+ &.group-path {
+ margin-left: 4px;
+ }
+
+ &:not(:last-of-type),
+ &.group-path {
+ margin-right: 3px;
+ }
+ }
+
+ .title {
+ white-space: nowrap;
+
+ > a {
+ &:last-of-type {
+ font-weight: 600;
+ }
+ }
+ }
+
+ .avatar-tile {
+ margin-right: 5px;
+ border: 1px solid $border-color;
+ border-radius: 50%;
+ vertical-align: sub;
+
+ &.identicon {
+ float: left;
+ width: 16px;
+ height: 16px;
+ margin-top: 2px;
+ font-size: 10px;
+ }
+ }
+
+ .text-expander {
+ margin-left: 4px;
+ margin-right: 4px;
+
+ > i {
+ position: relative;
+ top: 1px;
+ }
+ }
+}
+
+.breadcrumbs-extra {
+ flex: 0 0 auto;
+ margin-left: auto;
+}
+
+.breadcrumbs-sub-title {
+ margin: 2px 0 0;
+ font-size: 16px;
+ font-weight: normal;
+
+ ul {
+ margin: 0;
+ }
+
+ li {
+ display: inline-block;
+
+ &:not(:last-child) {
+ &::after {
+ content: "/";
+ margin: 0 2px 0 5px;
+ }
+ }
+
+ &:last-child a {
+ font-weight: 600;
+ }
+ }
+
+ a {
+ color: $gl-text-color;
+ }
+}
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index 06c6025ed6b..17f23f7fce3 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -5,21 +5,51 @@
$new-sidebar-width: 220px;
.page-with-new-sidebar {
-
@media (min-width: $screen-sm-min) {
padding-left: $new-sidebar-width;
}
+ // Override position: absolute
.right-sidebar {
position: fixed;
height: 100%;
}
}
+.context-header {
+ background-color: $gray-normal;
+ border-bottom: 1px solid $border-color;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ padding: 10px 14px;
+
+ .avatar-container {
+ flex: 0 0 40px;
+ }
+
+ &:hover {
+ background-color: $border-color;
+ }
+}
+
+.settings-avatar {
+ background-color: $white-light;
+
+ i {
+ font-size: 20px;
+ width: 100%;
+ color: $gl-text-color-secondary;
+ text-align: center;
+ align-self: center;
+ }
+}
+
.nav-sidebar {
position: fixed;
z-index: 400;
width: $new-sidebar-width;
+ transition: width $sidebar-transition-duration;
top: 50px;
bottom: 0;
left: 0;
@@ -33,6 +63,8 @@ $new-sidebar-width: 220px;
}
li {
+ white-space: nowrap;
+
a {
display: block;
padding: 12px 14px;
@@ -43,6 +75,10 @@ $new-sidebar-width: 220px;
color: $gl-text-color;
text-decoration: none;
}
+
+ @media (max-width: $screen-xs-max) {
+ width: 0;
+ }
}
.sidebar-sub-level-items {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index b58922626fa..55011e8a21b 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -20,8 +20,6 @@
}
.diff-content {
- overflow: auto;
- overflow-y: hidden;
background: $white-light;
color: $gl-text-color;
border-radius: 0 0 3px 3px;
@@ -476,6 +474,7 @@
height: 19px;
width: 19px;
margin-left: -15px;
+ z-index: 100;
&:hover {
.diff-comment-avatar,
@@ -491,7 +490,7 @@
transform: translateX((($i * $x-pos) - $x-pos));
&:hover {
- transform: translateX((($i * $x-pos) - $x-pos)) scale(1.2);
+ transform: translateX((($i * $x-pos) - $x-pos));
}
}
}
@@ -542,6 +541,7 @@
height: 19px;
padding: 0;
transition: transform .1s ease-out;
+ z-index: 100;
svg {
position: absolute;
@@ -555,10 +555,6 @@
fill: $white-light;
}
- &:hover {
- transform: scale(1.2);
- }
-
&:focus {
outline: 0;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index e3ebcc8af6c..057d457b3a2 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -597,7 +597,38 @@
.issue-info-container {
-webkit-flex: 1;
flex: 1;
+ display: flex;
padding-right: $gl-padding;
+
+ .issue-main-info {
+ flex: 1 auto;
+ margin-right: 10px;
+ }
+
+ .issuable-meta {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ flex: 1 0 auto;
+
+ .controls {
+ margin-bottom: 2px;
+ line-height: 20px;
+ padding: 0;
+ }
+
+ .issue-updated-at {
+ line-height: 20px;
+ }
+ }
+
+ @media(max-width: $screen-xs-max) {
+ .issuable-meta {
+ .controls li {
+ margin-right: 0;
+ }
+ }
+ }
}
.issue-check {
@@ -609,6 +640,30 @@
vertical-align: text-top;
}
}
+
+ .issuable-milestone,
+ .issuable-info,
+ .task-status,
+ .issuable-updated-at {
+ font-weight: normal;
+ color: $gl-text-color-secondary;
+
+ a {
+ color: $gl-text-color;
+
+ .fa {
+ color: $gl-text-color-secondary;
+ }
+ }
+ }
+
+ @media(max-width: $screen-md-max) {
+ .task-status,
+ .issuable-due-date,
+ .project-ref-path {
+ display: none;
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index b158416b940..ee48f7a3626 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -279,5 +279,9 @@
.label-link {
display: inline-block;
- vertical-align: text-top;
+ vertical-align: top;
+
+ .label {
+ vertical-align: inherit;
+ }
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 53d5cf2f7bc..303425041df 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -628,8 +628,14 @@ ul.notes {
* Line note button on the side of diffs
*/
+.line_holder .is-over:not(.no-comment-btn) {
+ .add-diff-note {
+ opacity: 1;
+ }
+}
+
.add-diff-note {
- display: none;
+ opacity: 0;
margin-top: -2px;
border-radius: 50%;
background: $white-light;
@@ -642,13 +648,11 @@ ul.notes {
width: 23px;
height: 23px;
border: 1px solid $blue-500;
- transition: transform .1s ease-in-out;
&:hover {
background: $blue-500;
border-color: $blue-600;
color: $white-light;
- transform: scale(1.15);
}
&:active {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index ba530bf7f9b..7d7c34115f9 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -483,11 +483,12 @@ a.deploy-project-label {
.project-stats {
font-size: 0;
text-align: center;
+ max-width: 100%;
+ border-bottom: 1px solid $border-color;
.nav {
padding-top: 12px;
padding-bottom: 12px;
- border-bottom: 1px solid $border-color;
}
.nav > li {
diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss
index 9b6ff237557..57c73295d1e 100644
--- a/app/assets/stylesheets/pages/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
@@ -33,3 +33,20 @@
font-weight: normal;
}
}
+
+.admin-runner-btn-group-cell {
+ min-width: 150px;
+
+ .btn-sm {
+ padding: 4px 9px;
+ }
+
+ .btn-default {
+ color: $gl-text-color-secondary;
+ }
+
+ .fa-pause,
+ .fa-play {
+ font-size: 11px;
+ }
+}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 9b2ed0d68a1..dc88cf3e699 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,4 +1,5 @@
.tree-holder {
+
.nav-block {
margin: 10px 0;
@@ -15,6 +16,11 @@
.btn-group {
margin-left: 10px;
}
+
+ .control {
+ float: left;
+ margin-left: 10px;
+ }
}
.tree-ref-holder {