diff options
author | Zeger-Jan van de Weg <zegerjan@gitlab.com> | 2016-05-11 08:47:04 +0200 |
---|---|---|
committer | Zeger-Jan van de Weg <zegerjan@gitlab.com> | 2016-05-11 08:47:04 +0200 |
commit | 7e6dcf9cd0626c6d0cbbe96ae5327048d2c6849f (patch) | |
tree | 33f22befc61e52178098bf77e57bba7a96aaae86 /app/assets | |
parent | dccf8a9fc8d4dde91942944f6b47387bfb13c380 (diff) | |
parent | 98d8e3fe9ff4d120469378490c41381ae751597e (diff) | |
download | gitlab-ce-7e6dcf9cd0626c6d0cbbe96ae5327048d2c6849f.tar.gz |
Merge branch 'master' into awardables
Diffstat (limited to 'app/assets')
83 files changed, 1670 insertions, 598 deletions
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index f3ed9a66715..dd1bbb37551 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -5,6 +5,7 @@ group_projects_path: "/api/:version/groups/:id/projects.json" projects_path: "/api/:version/projects.json" labels_path: "/api/:version/projects/:id/labels" + license_path: "/api/:version/licenses/:key" group: (group_id, callback) -> url = Api.buildUrl(Api.group_path) @@ -92,6 +93,16 @@ ).done (projects) -> callback(projects) + # Return text for a specific license + licenseText: (key, data, callback) -> + url = Api.buildUrl(Api.license_path).replace(':key', key) + + $.ajax( + url: url + data: data + ).done (license) -> + callback(license) + buildUrl: (url) -> url = gon.relative_url_root + url if gon.relative_url_root? return url.replace(':version', gon.api_version) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 6f435e4c542..bffce5a0c0f 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -174,7 +174,7 @@ $ -> $('.trigger-submit').on 'change', -> $(@).parents('form').submit() - gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), false) + gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true) # Flash if (flash = $(".flash-container")).length > 0 @@ -204,6 +204,7 @@ $ -> $('.header-content .title').toggle() $('.header-content .navbar-collapse').toggle() $('.navbar-toggle').toggleClass('active') + $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left") # Show/hide comments on diff $("body").on "click", ".js-toggle-diff-comments", (e) -> diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 589caf011ed..c18c9984c1f 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -105,7 +105,7 @@ class @AwardsHandler @postEmoji awardUrl, emoji, => @addAwardToEmojiBar(emoji) - $(".emoji-menu").removeClass "is-visible" + $('.emoji-menu').removeClass 'is-visible' addAwardToEmojiBar: (emoji) -> @addEmojiToFrequentlyUsedList(emoji) @@ -168,7 +168,7 @@ class @AwardsHandler @resetTooltip(award_block) resetTooltip: (award) -> - award.tooltip("destroy") + award.tooltip('destroy') # "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout. setTimeout (-> @@ -194,13 +194,13 @@ class @AwardsHandler $currentBlock.removeClass 'hidden' resolveNameToCssClass: (emoji) -> - emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") + emojiIcon = $(".emoji-menu-content [data-emoji='#{emoji}']") - if emoji_icon.length > 0 - unicodeName = emoji_icon.data("unicode-name") + if emojiIcon.length > 0 + unicodeName = emojiIcon.data('unicode-name') else # Find by alias - unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data("unicode-name") + unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name') "emoji-#{unicodeName}" @@ -217,45 +217,42 @@ class @AwardsHandler scrollTop: $('.awards').offset().top - 80 }, 200) - normilizeEmojiName: (emoji) -> - @aliases[emoji] || emoji - addEmojiToFrequentlyUsedList: (emoji) -> - frequently_used_emojis = @getFrequentlyUsedEmojis() - frequently_used_emojis.push(emoji) - $.cookie('frequently_used_emojis', frequently_used_emojis.join(","), { expires: 365 }) + frequentlyUsedEmojis = @getFrequentlyUsedEmojis() + frequentlyUsedEmojis.push(emoji) + $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }) getFrequentlyUsedEmojis: -> - frequently_used_emojis = ($.cookie('frequently_used_emojis') || "").split(",") - _.compact(_.uniq(frequently_used_emojis)) + frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',') + _.compact(_.uniq(frequentlyUsedEmojis)) renderFrequentlyUsedBlock: -> if $.cookie('frequently_used_emojis') - frequently_used_emojis = @getFrequentlyUsedEmojis() + frequentlyUsedEmojis = @getFrequentlyUsedEmojis() ul = $("<ul class='clearfix emoji-menu-list'>") for emoji in frequently_used_emojis $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul) - $("input.emoji-search").after(ul).after($("<h5>").text("Frequently used")) + $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) setupSearch: -> - $("input.emoji-search").on 'keyup', (ev) => + $('input.emoji-search').keyup (ev) => term = $(ev.target).val() # Clean previous search results - $("ul.emoji-menu-search, h5.emoji-search").remove() + $('ul.emoji-menu-search, h5.emoji-search').remove() if term # Generate a search result block - h5 = $("<h5>").text("Search results").addClass("emoji-search") - found_emojis = @searchEmojis(term).show() - ul = $("<ul>").addClass("emoji-menu-list emoji-menu-search").append(found_emojis) - $(".emoji-menu-content ul, .emoji-menu-content h5").hide() - $(".emoji-menu-content").append(h5).append(ul) + h5 = $('<h5>').text('Search results').addClass('emoji-search') + foundEmojis = @searchEmojis(term).show() + ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis) + $('.emoji-menu-content ul, .emoji-menu-content h5').hide() + $('.emoji-menu-content').append(h5).append(ul) else - $(".emoji-menu-content").children().show() + $('.emoji-menu-content').children().show() searchEmojis: (term)-> $(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone() diff --git a/app/assets/javascripts/blob/blob_license_selector.js.coffee b/app/assets/javascripts/blob/blob_license_selector.js.coffee new file mode 100644 index 00000000000..e17eaa75dc1 --- /dev/null +++ b/app/assets/javascripts/blob/blob_license_selector.js.coffee @@ -0,0 +1,30 @@ +class @BlobLicenseSelector + licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i + + constructor: (editor) -> + @$licenseSelector = $('.js-license-selector') + $fileNameInput = $('#file_name') + + initialFileNameValue = if $fileNameInput.length + $fileNameInput.val() + else if $('.editor-file-name').length + $('.editor-file-name').text().trim() + + @toggleLicenseSelector(initialFileNameValue) + + if $fileNameInput + $fileNameInput.on 'keyup blur', (e) => + @toggleLicenseSelector($(e.target).val()) + + $('select.license-select').on 'change', (e) -> + data = + project: $(this).data('project') + fullname: $(this).data('fullname') + Api.licenseText $(this).val(), data, (license) -> + editor.setValue(license.content, -1) + + toggleLicenseSelector: (fileName) => + if @licenseRegex.test(fileName) + @$licenseSelector.show() + else + @$licenseSelector.hide() diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index 390e41ed8d4..eea9aa972ee 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -1,44 +1,39 @@ class @EditBlob - constructor: (assets_path, mode)-> - ace.config.set "modePath", assets_path + '/ace' + constructor: (assets_path, ace_mode = null) -> + ace.config.set "modePath", "#{assets_path}/ace" ace.config.loadModule "ace/ext/searchbox" - if mode - ace_mode = mode - editor = ace.edit("editor") - editor.focus() - @editor = editor - - if ace_mode - editor.getSession().setMode "ace/mode/" + ace_mode + @editor = ace.edit("editor") + @editor.focus() + @editor.getSession().setMode "ace/mode/#{ace_mode}" if ace_mode # Before a form submission, move the content from the Ace editor into the # submitted textarea - $('form').submit -> - $("#file-content").val(editor.getValue()) + $('form').submit => + $("#file-content").val(@editor.getValue()) + + @initModePanesAndLinks() + new BlobLicenseSelector(@editor) - editModePanes = $(".js-edit-mode-pane") - editModeLinks = $(".js-edit-mode a") - editModeLinks.click (event) -> - event.preventDefault() - currentLink = $(this) - paneId = currentLink.attr("href") - currentPane = editModePanes.filter(paneId) - editModeLinks.parent().removeClass "active hover" - currentLink.parent().addClass "active hover" - editModePanes.hide() - if paneId is "#preview" - currentPane.fadeIn 200 - $.post currentLink.data("preview-url"), - content: editor.getValue() - , (response) -> - currentPane.empty().append response - currentPane.syntaxHighlight() - return + initModePanesAndLinks: -> + @$editModePanes = $(".js-edit-mode-pane") + @$editModeLinks = $(".js-edit-mode a") + @$editModeLinks.click @editModeLinkClickHandler - else - currentPane.fadeIn 200 - editor.focus() - return + editModeLinkClickHandler: (event) => + event.preventDefault() + currentLink = $(event.target) + paneId = currentLink.attr("href") + currentPane = @$editModePanes.filter(paneId) + @$editModeLinks.parent().removeClass "active hover" + currentLink.parent().addClass "active hover" + @$editModePanes.hide() + currentPane.fadeIn 200 + if paneId is "#preview" + $.post currentLink.data("preview-url"), + content: @editor.getValue() + , (response) -> + currentPane.empty().append response + currentPane.syntaxHighlight() - editor: -> - return @editor + else + @editor.focus() diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee deleted file mode 100644 index 68c5e5195e3..00000000000 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ /dev/null @@ -1,20 +0,0 @@ -class @NewBlob - constructor: (assets_path, mode)-> - ace.config.set "modePath", assets_path + '/ace' - ace.config.loadModule "ace/ext/searchbox" - if mode - ace_mode = mode - editor = ace.edit("editor") - editor.focus() - @editor = editor - - if ace_mode - editor.getSession().setMode "ace/mode/" + ace_mode - - # Before a form submission, move the content from the Ace editor into the - # submitted textarea - $('form').submit -> - $("#file-content").val(editor.getValue()) - - editor: -> - return @editor diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee index ffd3627b1b0..0acb4c1955e 100644 --- a/app/assets/javascripts/commits.js.coffee +++ b/app/assets/javascripts/commits.js.coffee @@ -1,7 +1,7 @@ class @CommitsList @timer = null - @init: (ref, limit) -> + @init: (limit) -> $("body").on "click", ".day-commits-table li.commit", (event) -> if event.target.nodeName != "A" location.href = $(this).attr("url") diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 610fa990fc7..e00ca2984b9 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -17,6 +17,7 @@ class Dispatcher switch page when 'projects:issues:index' Issues.init() + Issuable.init() shortcut_handler = new ShortcutsNavigation() when 'projects:issues:show' new Issue() @@ -59,7 +60,7 @@ class Dispatcher new ZenMode() when 'projects:merge_requests:index' shortcut_handler = new ShortcutsNavigation() - MergeRequests.init() + Issuable.init() when 'dashboard:activity' new Activities() when 'dashboard:projects:starred' @@ -109,6 +110,8 @@ class Dispatcher new BuildArtifacts() when 'projects:group_links:index' new GroupsSelect() + when 'search:show' + new Search() switch path.first() when 'admin' diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index 6eb8d27ee2b..e2194589b38 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -61,6 +61,7 @@ class @DropzoneInput return drop: -> + $mdArea.removeClass 'is-dropzone-hover' form.find(".div-dropzone-hover").css "opacity", 0 form_textarea.focus() return diff --git a/app/assets/javascripts/due_date_select.js.coffee b/app/assets/javascripts/due_date_select.js.coffee new file mode 100644 index 00000000000..a4304786cbb --- /dev/null +++ b/app/assets/javascripts/due_date_select.js.coffee @@ -0,0 +1,64 @@ +class @DueDateSelect + constructor: -> + $loading = $('.js-issuable-update .due_date') + .find('.block-loading') + .hide() + + $('.js-due-date-select').each (i, dropdown) -> + $dropdown = $(dropdown) + $dropdownParent = $dropdown.closest('.dropdown') + $datePicker = $dropdownParent.find('.js-due-date-calendar') + $block = $dropdown.closest('.block') + $selectbox = $dropdown.closest('.selectbox') + $value = $block.find('.value') + $sidebarValue = $('.js-due-date-sidebar-value', $block) + + fieldName = $dropdown.data('field-name') + abilityName = $dropdown.data('ability-name') + issueUpdateURL = $dropdown.data('issue-update') + + $dropdown.glDropdown( + hidden: -> + $selectbox.hide() + $value.removeAttr('style') + ) + + addDueDate = -> + # Create the post date + value = $("input[name='#{fieldName}']").val() + date = new Date value.replace(new RegExp('-', 'g'), ',') + mediumDate = $.datepicker.formatDate 'M d, yy', date + + data = {} + data[abilityName] = {} + data[abilityName].due_date = value + + $.ajax( + type: 'PUT' + url: issueUpdateURL + data: data + beforeSend: -> + $loading.fadeIn() + $dropdown.trigger('loading.gl.dropdown') + $selectbox.hide() + $value.removeAttr('style') + + $value.html(mediumDate) + $sidebarValue.html(mediumDate) + ).done (data) -> + $dropdown.trigger('loaded.gl.dropdown') + $dropdown.dropdown('toggle') + $loading.fadeOut() + + $datePicker.datepicker( + dateFormat: 'yy-mm-dd', + defaultDate: $("input[name='#{fieldName}']").val() + altField: "input[name='#{fieldName}']" + onSelect: -> + addDueDate() + ) + + $(document) + .off 'click', '.ui-datepicker-header a' + .on 'click', '.ui-datepicker-header a', (e) -> + e.stopImmediatePropagation() diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 2dc37257e22..1d1bfeb2e77 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -32,10 +32,8 @@ class GitLabDropdownFilter else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS $inputContainer.removeClass HAS_VALUE_CLASS - if keyCode is 13 and @input.val() isnt "" - if @options.enterCallback - @options.enterCallback() - return + if keyCode is 13 + return false clearTimeout timeout timeout = setTimeout => @@ -132,7 +130,6 @@ class GitLabDropdown @filterInput = @getElement(FILTER_INPUT) @highlight = false @filterInputBlur = true - @enterCallback = true } = @options self = @ @@ -157,6 +154,9 @@ class GitLabDropdown @fullData = data @parseData @fullData + + if @options.filterable + @filterInput.trigger 'keyup' } # Init filterable @@ -178,15 +178,15 @@ class GitLabDropdown callback: (data) => currentIndex = -1 @parseData data - enterCallback: => - if @enterCallback - @selectRowAtIndex 0 # Event listeners @dropdown.on "shown.bs.dropdown", @opened @dropdown.on "hidden.bs.dropdown", @hidden @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate + @dropdown.on 'keyup', (e) => + if e.which is 27 # Escape key + $('.dropdown-menu-close', @dropdown).trigger 'click' if @dropdown.find(".dropdown-toggle-page").length @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => @@ -224,6 +224,9 @@ class GitLabDropdown menu.toggleClass PAGE_TWO_CLASS + # Focus first visible input on active page + @dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus() + parseData: (data) -> @renderedData = data @@ -243,7 +246,8 @@ class GitLabDropdown shouldPropagate: (e) => if @options.multiSelect $target = $(e.target) - if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon') + + if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon') and not $target.data('is-link') e.stopPropagation() return false else @@ -378,7 +382,6 @@ class GitLabDropdown selectedObject = @renderedData[selectedIndex] value = if @options.id then @options.id(selectedObject, el) else selectedObject.id field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']") - if el.hasClass(ACTIVE_CLASS) el.removeClass(ACTIVE_CLASS) field.remove() @@ -389,13 +392,13 @@ class GitLabDropdown else selectedObject else - if !value? - field.remove() - - if not @options.multiSelect + if not @options.multiSelect or el.hasClass('dropdown-clear-active') @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS @dropdown.parent().find("input[name='#{fieldName}']").remove() + if !value? + field.remove() + # Toggle active class for the tick mark el.addClass ACTIVE_CLASS diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee index be8d225e73b..b0edc895649 100644 --- a/app/assets/javascripts/importer_status.js.coffee +++ b/app/assets/javascripts/importer_status.js.coffee @@ -4,18 +4,33 @@ class @ImporterStatus this.setAutoUpdate() initStatusPage: -> - $(".js-add-to-import").click (event) => - new_namespace = null - tr = $(event.currentTarget).closest("tr") - id = tr.attr("id").replace("repo_", "") - if tr.find(".import-target input").length > 0 - new_namespace = tr.find(".import-target input").prop("value") - tr.find(".import-target").empty().append(new_namespace + "/" + tr.find(".import-target").data("project_name")) - $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script' - - $(".js-import-all").click (event) => - $(".js-add-to-import").each -> - $(this).click() + $('.js-add-to-import') + .off 'click' + .on 'click', (e) => + new_namespace = null + $btn = $(e.currentTarget) + $tr = $btn.closest('tr') + id = $tr.attr('id').replace('repo_', '') + if $tr.find('.import-target input').length > 0 + new_namespace = $tr.find('.import-target input').prop('value') + $tr.find('.import-target').empty().append("#{new_namespace} / #{$tr.find('.import-target').data('project_name')}") + + $btn + .disable() + .addClass 'is-loading' + + $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script' + + $('.js-import-all') + .off 'click' + .on 'click', (e) -> + $btn = $(@) + $btn + .disable() + .addClass 'is-loading' + + $('.js-add-to-import').each -> + $(this).trigger('click') setAutoUpdate: -> setInterval (=> diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee new file mode 100644 index 00000000000..afffed63ac5 --- /dev/null +++ b/app/assets/javascripts/issuable.js.coffee @@ -0,0 +1,84 @@ +@Issuable = + init: -> + Issuable.initTemplates() + Issuable.initSearch() + + initTemplates: -> + Issuable.labelRow = _.template( + '<% _.each(labels, function(label){ %> + <span class="label-row"> + <a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a> + </span> + <% }); %>' + ) + + initSearch: -> + @timer = null + $('#issue_search') + .off 'keyup' + .on 'keyup', -> + clearTimeout(@timer) + @timer = setTimeout( -> + Issuable.filterResults $('#issue_search_form') + , 500) + + toggleLabelFilters: -> + $filteredLabels = $('.filtered-labels') + if $filteredLabels.find('.label-row').length > 0 + $filteredLabels.removeClass('hidden') + else + $filteredLabels.addClass('hidden') + + filterResults: (form) => + formData = form.serialize() + + $('.issues-holder, .merge-requests-holder').css('opacity', '0.5') + formAction = form.attr('action') + issuesUrl = formAction + issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}") + issuesUrl += formData + $.ajax + type: 'GET' + url: formAction + data: formData + complete: -> + $('.issues-holder, .merge-requests-holder').css('opacity', '1.0') + success: (data) -> + $('.issues-holder, .merge-requests-holder').html(data.html) + # Change url so if user reload a page - search results are saved + history.replaceState {page: issuesUrl}, document.title, issuesUrl + Issuable.reload() + Issuable.updateStateFilters() + $filteredLabels = $('.filtered-labels') + + if typeof Issuable.labelRow is 'function' + $filteredLabels.html(Issuable.labelRow(data)) + + Issuable.toggleLabelFilters() + + dataType: "json" + + reload: -> + if Issues.created + Issues.initChecks() + + $('#filter_issue_search').val($('#issue_search').val()) + + updateStateFilters: -> + stateFilters = $('.issues-state-filters') + newParams = {} + paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search'] + + for paramKey in paramKeys + newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or '' + + if stateFilters.length + stateFilters.find('a').each -> + initialUrl = gl.utils.removeParamQueryString($(this).attr('href'), 'label_name[]') + labelNameValues = gl.utils.getParameterValues('label_name[]') + if labelNameValues + labelNameQueryString = ("label_name[]=#{value}" for value in labelNameValues).join('&') + newUrl = "#{gl.utils.mergeUrlParams(newParams, initialUrl)}&#{labelNameQueryString}" + else + newUrl = gl.utils.mergeUrlParams(newParams, initialUrl) + $(this).attr 'href', newUrl diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee index 2f19513a831..3c491ebfc4c 100644 --- a/app/assets/javascripts/issuable_context.js.coffee +++ b/app/assets/javascripts/issuable_context.js.coffee @@ -9,21 +9,29 @@ class @IssuableContext $(".issuable-sidebar .inline-update").on "change", ".js-assignee", -> $(this).submit() - $(document).off("click", ".edit-link").on "click",".edit-link", (e) -> - $block = $(@).parents('.block') - $selectbox = $block.find('.selectbox') - if $selectbox.is(':visible') - $selectbox.hide() - $block.find('.value').show() - else - $selectbox.show() - $block.find('.value').hide() - - if $selectbox.is(':visible') - setTimeout (-> - $block.find('.dropdown-menu-toggle').trigger 'click' - ), 0 - + $(document) + .off 'click', '.issuable-sidebar .dropdown-content a' + .on 'click', '.issuable-sidebar .dropdown-content a', (e) -> + e.preventDefault() + + $(document) + .off 'click', '.edit-link' + .on 'click', '.edit-link', (e) -> + e.preventDefault() + + $block = $(@).parents('.block') + $selectbox = $block.find('.selectbox') + if $selectbox.is(':visible') + $selectbox.hide() + $block.find('.value').show() + else + $selectbox.show() + $block.find('.value').hide() + + if $selectbox.is(':visible') + setTimeout -> + $block.find('.dropdown-menu-toggle').trigger 'click' + , 0 $(".right-sidebar").niceScroll() diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index c7d74a12f99..157361404e0 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -12,6 +12,7 @@ class @Issue @initMergeRequests() @initRelatedBranches() + @initCanCreateBranch() initTaskList: -> $('.detail-page-description .js-task-list-container').taskList('enable') @@ -92,3 +93,25 @@ class @Issue .success (data) -> if 'html' of data $container.html(data.html) + + initCanCreateBranch: -> + $container = $('div#new-branch') + + # If the user doesn't have the required permissions the container isn't + # rendered at all. + return unless $container + + $.getJSON($container.data('path')) + .error -> + $container.find('.checking').hide() + $container.find('.unavailable').show() + + new Flash('Failed to check if a new branch can be created.', 'alert') + .success (data) -> + if data.can_create_branch + $container.find('.checking').hide() + $container.find('.available').show() + $container.find('a').attr('disabled', false) + else + $container.find('.checking').hide() + $container.find('.unavailable').show() diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index 0d9f2094c2a..3330e6c68ad 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -1,6 +1,6 @@ @Issues = init: -> - Issues.initSearch() + Issues.created = true Issues.initChecks() $("body").on "ajax:success", ".close_issue, .reopen_issue", -> @@ -15,10 +15,6 @@ else $(this).html totalIssues - 1 - reload: -> - Issues.initChecks() - $('#filter_issue_search').val($('#issue_search').val()) - initChecks: -> $(".check_all_issues").click -> $(".selected_issue").prop("checked", @checked) @@ -26,51 +22,6 @@ $(".selected_issue").bind "change", Issues.checkChanged - # Update state filters if present in page - updateStateFilters: -> - stateFilters = $('.issues-state-filters') - newParams = {} - paramKeys = ['author_id', 'label_name', 'milestone_title', 'assignee_id', 'issue_search'] - - for paramKey in paramKeys - newParams[paramKey] = gl.utils.getUrlParameter(paramKey) or '' - - if stateFilters.length - stateFilters.find('a').each -> - initialUrl = $(this).attr 'href' - $(this).attr 'href', gl.utils.mergeUrlParams(newParams, initialUrl) - - # Make sure we trigger ajax request only after user stop typing - initSearch: -> - @timer = null - $("#issue_search").keyup -> - clearTimeout(@timer) - @timer = setTimeout( -> - Issues.filterResults $("#issue_search_form") - , 500) - - filterResults: (form) => - $('.issues-holder, .merge-requests-holder').css("opacity", '0.5') - formAction = form.attr('action') - formData = form.serialize() - issuesUrl = formAction - issuesUrl += ("#{if formAction.indexOf("?") < 0 then '?' else '&'}") - issuesUrl += formData - - $.ajax - type: "GET" - url: formAction - data: formData - complete: -> - $('.issues-holder, .merge-requests-holder').css("opacity", '1.0') - success: (data) -> - $('.issues-holder, .merge-requests-holder').html(data.html) - # Change url so if user reload a page - search results are saved - history.replaceState {page: issuesUrl}, document.title, issuesUrl - Issues.reload() - Issues.updateStateFilters() - dataType: "json" - checkChanged: -> checked_issues = $(".selected_issue:checked") if checked_issues.length > 0 diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index bc80980acb7..995fd768603 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -6,7 +6,7 @@ class @LabelsSelect labelUrl = $dropdown.data('labels') issueUpdateURL = $dropdown.data('issueUpdate') selectedLabel = $dropdown.data('selected') - if selectedLabel? + if selectedLabel? and not $dropdown.hasClass 'js-multiselect' selectedLabel = selectedLabel.split(',') newLabelField = $('#new_label_name') newColorField = $('#new_label_color') @@ -16,33 +16,32 @@ class @LabelsSelect abilityName = $dropdown.data('ability-name') $selectbox = $dropdown.closest('.selectbox') $block = $selectbox.closest('.block') + $form = $dropdown.closest('form') $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span') $value = $block.find('.value') - $loading = $block.find('.block-loading').fadeOut() - - if newLabelField.length - $newLabelCreateButton = $('.js-new-label-btn') - $colorPreview = $('.js-dropdown-label-color-preview') - $newLabelError = $dropdown.parent().find('.js-label-error') - $newLabelError.hide() + $newLabelError = $('.js-label-error') + $colorPreview = $('.js-dropdown-label-color-preview') + $newLabelCreateButton = $('.js-new-label-btn') - # Suggested colors in the dropdown to chose from pre-chosen colors - $('.suggest-colors-dropdown a').on 'click', (e) -> + $newLabelError.hide() + $loading = $block.find('.block-loading').fadeOut() issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL? if issueUpdateURL labelHTMLTemplate = _.template( '<% _.each(labels, function(label){ %> - <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>"> - <span class="label has-tooltip color-label" title="<%= label.description %>" style="background-color: <%= label.color %>;"> - <%= label.title %> + <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>"> + <span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;"> + <%= _.escape(label.title) %> </span> </a> <% }); %>' - ); + ) labelNoneHTMLTemplate = _.template('<div class="light">None</div>') - if newLabelField.length and $dropdown.hasClass 'js-extra-options' + if newLabelField.length + + # Suggested colors in the dropdown to chose from pre-chosen colors $('.suggest-colors-dropdown a').on "click", (e) -> e.preventDefault() e.stopPropagation() @@ -81,26 +80,25 @@ class @LabelsSelect enableLabelCreateButton = -> if newLabelField.val() isnt '' and newColorField.val() isnt '' $newLabelError.hide() - $('.js-new-label-btn').disable() - - # Create new label with API - Api.newLabel projectId, { - name: newLabelField.val() - color: newColorField.val() - }, (label) -> - $('.js-new-label-btn').enable() - - if label.message? - $newLabelError - .text label.message - .show() - else - $('.dropdown-menu-back', $dropdown.parent()).trigger 'click' - $newLabelCreateButton.enable() else $newLabelCreateButton.disable() + saveLabel = -> + # Create new label with API + Api.newLabel projectId, { + name: newLabelField.val() + color: newColorField.val() + }, (label) -> + $newLabelCreateButton.enable() + + if label.message? + $newLabelError + .text label.message + .show() + else + $('.dropdown-menu-back', $dropdown.parent()).trigger 'click' + newLabelField.on 'keyup change', enableLabelCreateButton newColorField.on 'keyup change', enableLabelCreateButton @@ -111,24 +109,7 @@ class @LabelsSelect .on 'click', (e) -> e.preventDefault() e.stopPropagation() - - if newLabelField.val() isnt '' and newColorField.val() isnt '' - $newLabelError.hide() - $('.js-new-label-btn').disable() - - # Create new label with API - Api.newLabel projectId, { - name: newLabelField.val() - color: newColorField.val() - }, (label) -> - $('.js-new-label-btn').enable() - - if label.message? - $newLabelError - .text label.message - .show() - else - $('.dropdown-menu-back', $dropdown.parent()).trigger 'click' + saveLabel() saveLabelData = -> selected = $dropdown @@ -171,7 +152,7 @@ class @LabelsSelect .find('a') .each((i) -> setTimeout(=> - glAnimate($(@), 'pulse') + gl.animate.animate($(@), 'pulse') ,200 * i ) ) @@ -182,6 +163,21 @@ class @LabelsSelect $.ajax( url: labelUrl ).done (data) -> + data = _.chain data + .groupBy (label) -> + label.title + .map (label) -> + color = _.map label, (dup) -> + dup.color + + return { + id: label[0].id + title: label[0].title + color: color + duplicate: color.length > 1 + } + .value() + if $dropdown.hasClass 'js-extra-options' if showNo data.unshift( @@ -197,21 +193,47 @@ class @LabelsSelect if data.length > 2 data.splice 2, 0, 'divider' + callback data renderRow: (label) -> - selectedClass = '' - if $selectbox.find("input[type='hidden']\ - [name='#{$dropdown.data('field-name')}']\ - [value='#{label.id}']").length - selectedClass = 'is-active' + removesAll = label.id is 0 or not label.id? + + selectedClass = [] + if $form.find("input[type='hidden']\ + [name='#{$dropdown.data('fieldName')}']\ + [value='#{this.id(label)}']").length + selectedClass.push 'is-active' + + if $dropdown.hasClass('js-multiselect') and removesAll + selectedClass.push 'dropdown-clear-active' + + if label.duplicate + spacing = 100 / label.color.length + + # Reduce the colors to 4 + label.color = label.color.filter (color, i) -> + i < 4 + + color = _.map(label.color, (color, i) -> + percentFirst = Math.floor(spacing * i) + percentSecond = Math.floor(spacing * (i + 1)) + "#{color} #{percentFirst}%,#{color} #{percentSecond}% " + ).join(',') + color = "linear-gradient(#{color})" + else + if label.color? + color = label.color[0] - color = if label.color? then "<span class='dropdown-label-box' style='background-color: #{label.color}'></span>" else "" + if color + colorEl = "<span class='dropdown-label-box' style='background: #{color}'></span>" + else + colorEl = '' "<li> - <a href='#' class='#{selectedClass}'> - #{color} - #{label.title} + <a href='#' class='#{selectedClass.join(' ')}'> + #{colorEl} + #{_.escape(label.title)} </a> </li>" filterable: true @@ -219,37 +241,56 @@ class @LabelsSelect fields: ['title'] selectable: true - toggleLabel: (selected) -> + toggleLabel: (selected, el) -> + selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active') + if selected and selected.title? - selected.title + if selected_labels.length > 1 + "#{selected.title} +#{selected_labels.length - 1} more" + else + selected.title + else if not selected and selected_labels.length isnt 0 + if selected_labels.length > 1 + "#{$(selected_labels[0]).text()} +#{selected_labels.length - 1} more" + else if selected_labels.length is 1 + $(selected_labels).text() else defaultLabel fieldName: $dropdown.data('field-name') id: (label) -> - if label.isAny? - '' - else if $dropdown.hasClass "js-filter-submit" - label.title + if $dropdown.hasClass("js-filter-submit") and not label.isAny? + _.escape label.title else label.id hidden: -> + page = $('body').data 'page' + isIssueIndex = page is 'projects:issues:index' + isMRIndex = page is 'projects:merge_requests:index' + $selectbox.hide() # display:block overrides the hide-collapse rule $value.removeAttr('style') if $dropdown.hasClass 'js-multiselect' - saveLabelData() + if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) + selectedLabels = $dropdown + .closest('form') + .find("input:hidden[name='#{$dropdown.data('fieldName')}']") + Issuable.filterResults $dropdown.closest('form') + else if $dropdown.hasClass('js-filter-submit') + $dropdown.closest('form').submit() + else + saveLabelData() multiSelect: $dropdown.hasClass 'js-multiselect' clicked: (label) -> page = $('body').data 'page' isIssueIndex = page is 'projects:issues:index' - isMRIndex = page is page is 'projects:merge_requests:index' - + isMRIndex = page is 'projects:merge_requests:index' if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) - selectedLabel = label.title - - Issues.filterResults $dropdown.closest('form') + if not $dropdown.hasClass 'js-multiselect' + selectedLabel = label.title + Issuable.filterResults $dropdown.closest('form') else if $dropdown.hasClass 'js-filter-submit' $dropdown.closest('form').submit() else diff --git a/app/assets/javascripts/lib/animate.js.coffee b/app/assets/javascripts/lib/animate.js.coffee index 8f892b5a2b9..ec3b44d6126 100644 --- a/app/assets/javascripts/lib/animate.js.coffee +++ b/app/assets/javascripts/lib/animate.js.coffee @@ -1,13 +1,39 @@ ((w) -> + if not w.gl? then w.gl = {} + if not gl.animate? then gl.animate = {} - w.glAnimate = ($el, animation, done) -> + gl.animate.animate = ($el, animation, options, done) -> + if options?.cssStart? + $el.css(options.cssStart) $el - .removeClass() + .removeClass(animation + ' animated') .addClass(animation + ' animated') .one 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', -> - $(this).removeClass() + $(this).removeClass(animation + ' animated') + if done? + done() + if options?.cssEnd? + $el.css(options.cssEnd) return return - return + gl.animate.animateEach = ($els, animation, time, options, done) -> + dfd = $.Deferred() + if not $els.length + dfd.resolve() + $els.each((i) -> + setTimeout(=> + $this = $(@) + gl.animate.animate($this, animation, options, => + if i is $els.length - 1 + dfd.resolve() + if done? + done() + ) + ,time * i + ) + return + ) + return dfd.promise() + return ) window
\ No newline at end of file diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/url_utility.js.coffee index abd556e0b4e..6a00932c028 100644 --- a/app/assets/javascripts/lib/url_utility.js.coffee +++ b/app/assets/javascripts/lib/url_utility.js.coffee @@ -3,16 +3,20 @@ w.gl ?= {} w.gl.utils ?= {} - w.gl.utils.getUrlParameter = (sParam) -> + # Returns an array containing the value(s) of the + # of the key passed as an argument + w.gl.utils.getParameterValues = (sParam) -> sPageURL = decodeURIComponent(window.location.search.substring(1)) sURLVariables = sPageURL.split('&') sParameterName = undefined + values = [] i = 0 while i < sURLVariables.length sParameterName = sURLVariables[i].split('=') if sParameterName[0] is sParam - return if sParameterName[1] is undefined then true else sParameterName[1] + values.push(sParameterName[1]) i++ + values # # # @param {Object} params - url keys and value to merge @@ -28,4 +32,12 @@ newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}" newUrl + # removes parameter query string from url. returns the modified url + w.gl.utils.removeParamQueryString = (url, param) -> + url = decodeURIComponent(url) + urlVariables = url.split('&') + ( + variables for variables in urlVariables when variables.indexOf(param) is -1 + ).join('&') + ) window diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 1ab6e5114bc..372732d0aac 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -87,8 +87,8 @@ class @MergeRequestTabs if window.location.hash navBarHeight = $('.navbar-gitlab').outerHeight() - $el = $("#{container} #{window.location.hash}") - $.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length + $el = $("#{container} #{window.location.hash}:not(.match)") + $.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length # Activate a tab based on the current action activateTab: (action) -> @@ -176,16 +176,17 @@ class @MergeRequestTabs if locationHash isnt '' hashClassString = ".#{locationHash.replace('#', '')}" - $diffLine = $(locationHash) + $diffLine = $("#{locationHash}:not(.match)", $('#diffs')) - if $diffLine.is ':not(tr)' - $diffLine = $("td#{locationHash}, td#{hashClassString}") + if not $diffLine.is 'tr' + $diffLine = $('#diffs').find("td#{locationHash}, td#{hashClassString}") else - $diffLine = $('td', $diffLine) + $diffLine = $diffLine.find('td') - $diffLine.addClass 'hll' - diffLineTop = $diffLine.offset().top - navBarHeight = $('.navbar-gitlab').outerHeight() + if $diffLine.length + $diffLine.addClass 'hll' + diffLineTop = $diffLine.offset().top + navBarHeight = $('.navbar-gitlab').outerHeight() loadBuilds: (source) -> return if @buildsLoaded diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 065626beeb8..f58647988a2 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -9,11 +9,12 @@ class @MergeRequestWidget constructor: (@opts) -> $('#modal_merge_info').modal(show: false) @firstCICheck = true - @readyForCICheck = true + @readyForCICheck = false clearInterval @fetchBuildStatusInterval @clearEventListeners() @addEventListeners() + @getCIStatus(false) @pollCIStatus() notifyPermissions() @@ -68,20 +69,18 @@ class @MergeRequestWidget $.getJSON @opts.ci_status_url, (data) => @readyForCICheck = true - if @firstCICheck - @firstCICheck = false - @opts.ci_status = data.status - - if @opts.ci_status is '' - @opts.ci_status = data.status + if data.status is '' return - if data.status isnt @opts.ci_status and data.status? + if @firstCICheck || data.status isnt @opts.ci_status and data.status? + @opts.ci_status = data.status @showCIStatus data.status if data.coverage @showCICoverage data.coverage - if showNotification + # The first check should only update the UI, a notification + # should only be displayed on status changes + if showNotification and not @firstCICheck status = @ciLabelForStatus(data.status) if status is "preparing" @@ -104,8 +103,7 @@ class @MergeRequestWidget @close() Turbolinks.visit _this.opts.builds_path ) - - @opts.ci_status = data.status + @firstCICheck = false showCIStatus: (state) -> $('.ci_widget').hide() diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee deleted file mode 100644 index b3c73ffce5d..00000000000 --- a/app/assets/javascripts/merge_requests.js.coffee +++ /dev/null @@ -1,35 +0,0 @@ -# -# * Filter merge requests -# -@MergeRequests = - init: -> - MergeRequests.initSearch() - - # Make sure we trigger ajax request only after user stop typing - initSearch: -> - @timer = null - $("#issue_search").keyup -> - clearTimeout(@timer) - @timer = setTimeout(MergeRequests.filterResults, 500) - - filterResults: => - form = $("#issue_search_form") - search = $("#issue_search").val() - $('.merge-requests-holder').css("opacity", '0.5') - issues_url = form.attr('action') + '?' + form.serialize() - - $.ajax - type: "GET" - url: form.attr('action') - data: form.serialize() - complete: -> - $('.merge-requests-holder').css("opacity", '1.0') - success: (data) -> - $('.merge-requests-holder').html(data.html) - # Change url so if user reload a page - search results are saved - history.replaceState {page: issues_url}, document.title, issues_url - MergeRequests.reload() - dataType: "json" - - reload: -> - $('#filter_issue_search').val($('#issue_search').val()) diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee index 6bd4e885a03..345a0e447af 100644 --- a/app/assets/javascripts/milestone_select.js.coffee +++ b/app/assets/javascripts/milestone_select.js.coffee @@ -24,7 +24,7 @@ class @MilestoneSelect if issueUpdateURL milestoneLinkTemplate = _.template( - '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= title %></a>' + '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>' ) milestoneLinkNoneTemplate = '<div class="light">None</div>' @@ -71,7 +71,7 @@ class @MilestoneSelect defaultLabel fieldName: $dropdown.data('field-name') text: (milestone) -> - milestone.title + _.escape(milestone.title) id: (milestone) -> if !useId milestone.name @@ -97,7 +97,7 @@ class @MilestoneSelect selectedMilestone = selected.name else selectedMilestone = '' - Issues.filterResults $dropdown.closest('form') + Issuable.filterResults $dropdown.closest('form') else if $dropdown.hasClass('js-filter-submit') $dropdown.closest('form').submit() else diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 74ae897b84a..e8a92b8012d 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -75,6 +75,9 @@ class @Notes # when issue status changes, we need to refresh data $(document).on "issuable:change", @refresh + # when a key is clicked on the notes + $(document).on "keydown", ".js-note-text", @keydownNoteText + cleanBinding: -> $(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-discussion-note-form" @@ -92,10 +95,19 @@ class @Notes $(document).off "click", ".js-note-target-reopen" $(document).off "click", ".js-note-target-close" $(document).off "click", ".js-note-discard" + $(document).off "keydown", ".js-note-text" $('.note .js-task-list-container').taskList('disable') $(document).off 'tasklist:changed', '.note .js-task-list-container' + keydownNoteText: (e) -> + $this = $(this) + if $this.val() is '' and e.which is 38 #aka the up key + myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last") + if myLastNote.length + myLastNoteEditBtn = myLastNote.find('.js-note-edit') + myLastNoteEditBtn.trigger('click', [true, myLastNote]) + initRefresh: -> clearInterval(Notes.interval) Notes.interval = setInterval => @@ -343,7 +355,7 @@ class @Notes Adds a hidden div with the original content of the note to fill the edit note form with if the user cancels ### - showEditForm: (e) -> + showEditForm: (e, scrollTo, myLastNote) -> e.preventDefault() note = $(this).closest(".note") note.addClass "is-editting" @@ -354,9 +366,27 @@ class @Notes # Show the attachment delete link note.find(".js-note-attachment-delete").show() - new GLForm form + done = ($noteText) -> + # Neat little trick to put the cursor at the end + noteTextVal = $noteText.val() + $noteText.val('').val(noteTextVal); - form.find(".js-note-text").focus() + new GLForm form + if scrollTo? and myLastNote? + # scroll to the bottom + # so the open of the last element doesn't make a jump + $('html, body').scrollTop($(document).height()); + $('html, body').animate({ + scrollTop: myLastNote.offset().top - 150 + }, 500, -> + $noteText = form.find(".js-note-text") + $noteText.focus() + done($noteText) + ); + else + $noteText = form.find('.js-note-text') + $noteText.focus() + done($noteText) ### Called in response to clicking the edit note link diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee index f4a2562885d..26a12423521 100644 --- a/app/assets/javascripts/profile.js.coffee +++ b/app/assets/javascripts/profile.js.coffee @@ -45,9 +45,10 @@ class @Profile saveForm: -> self = @ - formData = new FormData(@form[0]) - formData.append('user[avatar]', @avatarGlCrop.getBlob(), 'avatar.png') + + avatarBlob = @avatarGlCrop.getBlob() + formData.append('user[avatar]', avatarBlob, 'avatar.png') if avatarBlob? $.ajax url: @form.attr('action') diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index 67403554340..2d084b76cfe 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -1,10 +1,12 @@ class @Sidebar constructor: (currentUser) -> + @sidebar = $('aside') + @addEventListeners() addEventListeners: -> - $('aside').on('click', '.sidebar-collapsed-icon', @sidebarCollapseClicked) - $('.dropdown').on('hidden.gl.dropdown', @sidebarDropdownHidden) + @sidebar.on('click', '.sidebar-collapsed-icon', @, @sidebarCollapseClicked) + $('.dropdown').on('hidden.gl.dropdown', @, @onSidebarDropdownHidden) $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading) $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded) @@ -30,26 +32,56 @@ class @Sidebar else i.show() - sidebarCollapseClicked: (e) -> + sidebar = e.data e.preventDefault() $block = $(@).closest('.block') + sidebar.openDropdown($block); - $('aside') - .find('.gutter-toggle') - .trigger('click') - $editLink = $block.find('.edit-link') + openDropdown: (blockOrName) -> + $block = if _.isString(blockOrName) then @getBlock(blockOrName) else blockOrName + + $block.find('.edit-link').trigger('click') - if $editLink.length - $editLink.trigger('click') - $block.addClass('collapse-after-update') - $('.page-with-sidebar').addClass('with-overlay') + if not @isOpen() + @setCollapseAfterUpdate($block) + @toggleSidebar('open') - sidebarDropdownHidden: (e) -> + setCollapseAfterUpdate: ($block) -> + $block.addClass('collapse-after-update') + $('.page-with-sidebar').addClass('with-overlay') + + onSidebarDropdownHidden: (e) -> + sidebar = e.data + e.preventDefault() $block = $(@).closest('.block') + sidebar.sidebarDropdownHidden($block) + + sidebarDropdownHidden: ($block) -> if $block.hasClass('collapse-after-update') $block.removeClass('collapse-after-update') $('.page-with-sidebar').removeClass('with-overlay') - $('aside') - .find('.gutter-toggle') - .trigger('click')
\ No newline at end of file + @toggleSidebar('hide') + + triggerOpenSidebar: -> + @sidebar + .find('.js-sidebar-toggle') + .trigger('click') + + toggleSidebar: (action = 'toggle') -> + if action is 'toggle' + @triggerOpenSidebar() + + if action is 'open' + @triggerOpenSidebar() if not @isOpen() + + if action is 'hide' + @triggerOpenSidebar() is @isOpen() + + isOpen: -> + @sidebar.is('.right-sidebar-expanded') + + getBlock: (name) -> + @sidebar.find(".block.#{name}") + + diff --git a/app/assets/javascripts/search.js.coffee b/app/assets/javascripts/search.js.coffee new file mode 100644 index 00000000000..661e1195f60 --- /dev/null +++ b/app/assets/javascripts/search.js.coffee @@ -0,0 +1,75 @@ +class @Search + constructor: -> + $groupDropdown = $('.js-search-group-dropdown') + $projectDropdown = $('.js-search-project-dropdown') + @eventListeners() + + $groupDropdown.glDropdown( + selectable: true + filterable: true + fieldName: 'group_id' + data: (term, callback) -> + Api.groups term, null, (data) -> + data.unshift( + name: 'Any' + ) + data.splice 1, 0, 'divider' + + callback(data) + id: (obj) -> + obj.id + text: (obj) -> + obj.name + toggleLabel: (obj) -> + "#{$groupDropdown.data('default-label')} #{obj.name}" + clicked: => + @submitSearch() + ) + + $projectDropdown.glDropdown( + selectable: true + filterable: true + fieldName: 'project_id' + data: (term, callback) -> + Api.projects term, 'id', (data) -> + data.unshift( + name_with_namespace: 'Any' + ) + data.splice 1, 0, 'divider' + + callback(data) + id: (obj) -> + obj.id + text: (obj) -> + obj.name_with_namespace + toggleLabel: (obj) -> + "#{$projectDropdown.data('default-label')} #{obj.name_with_namespace}" + clicked: => + @submitSearch() + ) + + eventListeners: -> + $(document) + .off 'keyup', '.js-search-input' + .on 'keyup', '.js-search-input', @searchKeyUp + + $(document) + .off 'click', '.js-search-clear' + .on 'click', '.js-search-clear', @clearSearchField + + submitSearch: -> + $('.js-search-form').submit() + + searchKeyUp: -> + $input = $(@) + + if $input.val() is '' + $('.js-search-clear').addClass 'hidden' + else + $('.js-search-clear').removeClass 'hidden' + + clearSearchField: -> + $('.js-search-input') + .val '' + .trigger 'keyup' + .focus() diff --git a/app/assets/javascripts/shortcuts.js.coffee b/app/assets/javascripts/shortcuts.js.coffee index 100e3aac535..f3d66004138 100644 --- a/app/assets/javascripts/shortcuts.js.coffee +++ b/app/assets/javascripts/shortcuts.js.coffee @@ -2,34 +2,35 @@ class @Shortcuts constructor: -> @enabledHelp = [] Mousetrap.reset() - Mousetrap.bind('?', @selectiveHelp) + Mousetrap.bind('?', @onToggleHelp) Mousetrap.bind('s', Shortcuts.focusSearch) Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview) Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL? - selectiveHelp: (e) => - Shortcuts.showHelp(e, @enabledHelp) + onToggleHelp: (e) => + e.preventDefault() + @toggleHelp(@enabledHelp) toggleMarkdownPreview: (e) => $(document).triggerHandler('markdown-preview:toggle', [e]) - @showHelp: (e, location) -> - if $('#modal-shortcuts').length > 0 - $('#modal-shortcuts').modal('show') - else - url = '/help/shortcuts' - url = gon.relative_url_root + url if gon.relative_url_root? - $.ajax( - url: url, - dataType: 'script', - success: (e) -> - if location and location.length > 0 - $(l).show() for l in location - else - $('.hidden-shortcut').show() - $('.js-more-help-button').remove() - ) - e.preventDefault() + toggleHelp: (location) -> + $modal = $('#modal-shortcuts') + + if $modal.length + $modal.modal('toggle') + return + + $.ajax( + url: gon.shortcuts_path, + dataType: 'script', + success: (e) -> + if location and location.length > 0 + $(l).show() for l in location + else + $('.hidden-shortcut').show() + $('.js-more-help-button').remove() + ) @focusSearch: (e) -> $('#search').focus() diff --git a/app/assets/javascripts/shortcuts_issuable.coffee b/app/assets/javascripts/shortcuts_issuable.coffee index bbf02f1db24..ad9b3c1c6bf 100644 --- a/app/assets/javascripts/shortcuts_issuable.coffee +++ b/app/assets/javascripts/shortcuts_issuable.coffee @@ -4,18 +4,8 @@ class @ShortcutsIssuable extends ShortcutsNavigation constructor: (isMergeRequest) -> super() - Mousetrap.bind('a', -> - $('.block.assignee .edit-link').trigger('click') - return false - ) - Mousetrap.bind('m', -> - $('.block.milestone .edit-link').trigger('click') - return false - ) - Mousetrap.bind('r', => - @replyWithSelectedText() - return false - ) + Mousetrap.bind('a', @openSidebarDropdown.bind(@, 'assignee')) + Mousetrap.bind('m', @openSidebarDropdown.bind(@, 'milestone')) Mousetrap.bind('j', => @prevIssue() return false @@ -28,7 +18,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation @editIssue() return false ) - + Mousetrap.bind('l', @openSidebarDropdown.bind(@, 'labels')) if isMergeRequest @enabledHelp.push('.hidden-shortcut.merge_requests') @@ -71,3 +61,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation editIssue: -> $editBtn = $('.issuable-edit') Turbolinks.visit($editBtn.attr('href')) + + openSidebarDropdown: (name) -> + sidebar.openDropdown(name) + return false diff --git a/app/assets/javascripts/shortcuts_navigation.coffee b/app/assets/javascripts/shortcuts_navigation.coffee index 8decaedd87b..f39504e0645 100644 --- a/app/assets/javascripts/shortcuts_navigation.coffee +++ b/app/assets/javascripts/shortcuts_navigation.coffee @@ -14,6 +14,7 @@ class @ShortcutsNavigation extends Shortcuts Mousetrap.bind('g m', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests')) Mousetrap.bind('g w', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki')) Mousetrap.bind('g s', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-snippets')) + Mousetrap.bind('i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-new-issue')) @enabledHelp.push('.hidden-shortcut.project') @findAndFollowLink: (selector) -> diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee index 860d4f438d0..ea4ac52da31 100644 --- a/app/assets/javascripts/sidebar.js.coffee +++ b/app/assets/javascripts/sidebar.js.coffee @@ -12,7 +12,7 @@ toggleSidebar = -> niceScrollBars.updateScrollBar(); ), 300 -$(document).on("click", '.toggle-nav-collapse', (e) -> +$(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) -> e.preventDefault() toggleSidebar() diff --git a/app/assets/javascripts/todos.js.coffee b/app/assets/javascripts/todos.js.coffee index 00d2b641723..10bef96f43d 100644 --- a/app/assets/javascripts/todos.js.coffee +++ b/app/assets/javascripts/todos.js.coffee @@ -1,5 +1,11 @@ class @Todos - constructor: (@name) -> + constructor: (opts = {}) -> + { + @el = $('.js-todos-options') + } = opts + + @perPage = @el.data('perPage') + @clearListeners() @initBtnListeners() @@ -26,6 +32,7 @@ class @Todos dataType: 'json' data: '_method': 'delete' success: (data) => + @redirectIfNeeded data.count @clearDone $this.closest('li') @updateBadges data @@ -57,11 +64,46 @@ class @Todos $('.todos-pending .badge, .todos-pending-count').text data.count $('.todos-done .badge').text data.done_count + getTotalPages: -> + @el.data('totalPages') + + getCurrentPage: -> + @el.data('currentPage') + + getTodosPerPage: -> + @el.data('perPage') + + redirectIfNeeded: (total) -> + currPages = @getTotalPages() + currPage = @getCurrentPage() + + # Refresh if no remaining Todos + if not total + location.reload() + return + + # Do nothing if no pagination + return if not currPages + + newPages = Math.ceil(total / @getTodosPerPage()) + url = location.href # Includes query strings + + # If new total of pages is different than we have now + if newPages isnt currPages + # Redirect to previous page if there's one available + if currPages > 1 and currPage is currPages + pageParams = + page: currPages - 1 + url = gl.utils.mergeUrlParams(pageParams, url) + + Turbolinks.visit(url) + goToTodoUrl: (e)-> todoLink = $(this).data('url') return unless todoLink - if e.metaKey + # Allow Meta-Click or Mouse3-click to open in a new tab + if e.metaKey or e.which is 2 e.preventDefault() window.open(todoLink,'_blank') else diff --git a/app/assets/javascripts/user_tabs.js.coffee b/app/assets/javascripts/user_tabs.js.coffee index 09b7eec9104..c2aeffe2381 100644 --- a/app/assets/javascripts/user_tabs.js.coffee +++ b/app/assets/javascripts/user_tabs.js.coffee @@ -92,7 +92,7 @@ class @UserTabs @setCurrentAction(action) activateTab: (action) -> - @parentEl.find(".nav-links .#{action}-tab a").tab('show') + @parentEl.find(".nav-links .js-#{action}-tab a").tab('show') setTab: (source, action) -> return if @loaded[action] is true diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index a7e934936e9..b80b1b861cc 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -158,7 +158,7 @@ class @UsersSelect if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) selectedId = user.id - Issues.filterResults $dropdown.closest('form') + Issuable.filterResults $dropdown.closest('form') else if $dropdown.hasClass 'js-filter-submit' $dropdown.closest('form').submit() else diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index c85ab9148d0..560de9fc0bd 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -25,6 +25,7 @@ @import "framework/lists.scss"; @import "framework/markdown_area.scss"; @import "framework/mobile.scss"; +@import "framework/modal.scss"; @import "framework/nav.scss"; @import "framework/pagination.scss"; @import "framework/progress.scss"; diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 5aa425dab6c..f5ce70b606b 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -28,6 +28,7 @@ &.s46 { width: 46px; height: 46px; margin-right: 15px; } &.s48 { width: 48px; height: 48px; margin-right: 10px; } &.s60 { width: 60px; height: 60px; margin-right: 12px; } + &.s70 { width: 70px; height: 70px; margin-right: 14px; } &.s90 { width: 90px; height: 90px; margin-right: 15px; } &.s110 { width: 110px; height: 110px; margin-right: 15px; } &.s140 { width: 140px; height: 140px; margin-right: 20px; } diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 62b2af0dbf7..434a26d57c6 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -1,5 +1,5 @@ .light-well { - background-color: #f8fafc; + background-color: $background-color; padding: 15px; } @@ -18,7 +18,7 @@ line-height: 36px; } -.gray-content-block { +.row-content-block { margin-top: 0; margin-bottom: -$gl-padding; background-color: $background-color; @@ -81,6 +81,11 @@ margin-left: 10px; } } + + &.build-content { + background-color: $white-light; + border-top: none; + } } .cover-block { @@ -113,7 +118,7 @@ line-height: 1.1; h1 { - color: #313236; + color: $gl-gray-dark; margin-bottom: 6px; font-size: 23px; } @@ -150,6 +155,41 @@ right: auto; } } + + &.groups-cover-block { + background: $white-light; + border-bottom: 1px solid $border-color; + text-align: left; + padding: 24px 0; + + .group-info { + .cover-title { + margin-top: 9px; + } + + p { + margin-bottom: 0; + } + } + + @media (max-width: $screen-xs-max) { + text-align: center; + + .avatar { + float: none; + } + } + } + + .group-info { + + h1 { + display: inline; + font-weight: normal; + font-size: 24px; + color: $gl-title-color; + } + } } .block-connector { diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index e8c0172680d..eaf85bb17ca 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -59,7 +59,7 @@ } @mixin btn-gray { - @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, #313236); + @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, $gl-gray-dark); } @mixin btn-white { @@ -139,11 +139,19 @@ pointer-events: auto !important; } + &[disabled] { + pointer-events: none !important; + } + .caret { margin-left: 5px; } } +.btn-lg { + padding: 12px 20px; +} + .btn-transparent { color: $btn-transparent-color; background-color: transparent; @@ -243,3 +251,10 @@ .btn-file-option { background: linear-gradient(180deg, $white-light 25%, $gray-light 100%); } + +.btn-build { + margin-left: 10px; + i { + color: $gl-icon-color; + } +} diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 0b3af592d4a..11f39d583bd 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -54,6 +54,10 @@ fill: #254e77 !important; } + .future { + visibility: hidden; + } + .domain-background { fill: none; shape-rendering: crispedges; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 2ade341c9dd..3386523dbf7 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -11,6 +11,7 @@ .prepend-top-10 { margin-top: 10px } .prepend-top-default { margin-top: $gl-padding !important; } .prepend-top-20 { margin-top: 20px } +.prepend-left-5 { margin-left: 5px } .prepend-left-10 { margin-left: 10px } .prepend-left-default { margin-left: $gl-padding; } .prepend-left-20 { margin-left: 20px } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index ba6c7930cdc..4bf3a050403 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -42,7 +42,7 @@ font-size: 15px; text-align: left; border: 1px solid $dropdown-toggle-border-color; - border-radius: $dropdown-border-radius; + border-radius: $border-radius-base; outline: 0; text-overflow: ellipsis; white-space: nowrap; @@ -80,7 +80,7 @@ padding: 10px 0; background-color: $dropdown-bg; border: 1px solid $dropdown-border-color; - border-radius: $dropdown-border-radius; + border-radius: $border-radius-base; box-shadow: 0 2px 4px $dropdown-shadow-color; &.is-loading { @@ -248,7 +248,7 @@ .dropdown-title { position: relative; - padding: 0 25px 15px; + padding: 0 25px 10px; margin: 0 10px 10px; font-weight: 600; line-height: 1; @@ -278,7 +278,7 @@ right: 5px; width: 20px; height: 20px; - top: -1px; + top: -3px; } .dropdown-menu-back { @@ -320,7 +320,7 @@ } } -.dropdown-input-field { +.dropdown-input-field, .default-dropdown-input { width: 100%; padding: 0 7px; color: $dropdown-input-color; @@ -358,6 +358,13 @@ border-top: 1px solid $dropdown-divider-color; } +.dropdown-due-date-footer { + padding-top: 0; + margin-left: 10px; + margin-right: 10px; + border-top: 0; +} + .dropdown-footer-list { font-size: 14px; @@ -395,3 +402,122 @@ height: 15px; border-radius: $border-radius-base; } + +.dropdown-menu-due-date { + .dropdown-content { + max-height: 230px; + } + + .ui-widget { + table { + margin: 0; + } + + &.ui-datepicker-inline { + padding: 0 10px; + border: 0; + width: 100%; + } + + .ui-datepicker-header { + padding: 0 8px 10px; + border: 0; + + .ui-icon { + background: none; + font-size: 20px; + text-indent: 0; + + &:before { + display: block; + position: relative; + top: -2px; + color: $dropdown-title-btn-color; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + } + } + + .ui-state-active, + .ui-state-hover { + color: $md-link-color; + background-color: $calendar-hover-bg; + } + + .ui-datepicker-prev, + .ui-datepicker-next { + top: 0; + height: 15px; + cursor: pointer; + + &:hover { + background-color: transparent; + border: 0; + + .ui-icon:before { + color: $md-link-color; + } + } + } + + .ui-datepicker-prev { + left: 0; + + .ui-icon:before { + content: '\f104'; + text-align: left; + } + } + + .ui-datepicker-next { + right: 0; + + .ui-icon:before { + content: '\f105'; + text-align: right; + } + } + + td { + padding: 0; + border: 1px solid $calendar-border-color; + + &:first-child { + border-left: 0; + } + + &:last-child { + border-right: 0; + } + + a { + line-height: 17px; + border: 0; + border-radius: 0; + } + } + + .ui-datepicker-title { + color: $gl-gray; + font-size: 15px; + line-height: 1; + font-weight: normal; + } + } + + th { + padding: 2px 0; + color: $calendar-header-color; + font-weight: normal; + text-transform: lowercase; + border-top: 1px solid $calendar-border-color; + } + + .ui-datepicker-unselectable { + background-color: $calendar-unselectable-bg; + } +} diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 789df42fb66..61d9954c6c8 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -38,12 +38,14 @@ .filename { &.old { + display: inline-block; span.idiff { background-color: #f8cbcb; } } &.new { + display: inline-block; span.idiff { background-color: #a6f3a6; } @@ -82,10 +84,6 @@ } } - &.blob_file { - - } - &.blob-no-preview { background: #eee; text-shadow: 0 1px 2px #fff; @@ -129,6 +127,11 @@ td.line-numbers { float: none; border-left: 1px solid #ddd; + + i { + float: none; + margin-right: 0; + } } td.lines { padding: 0; diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 54cb5461113..558b133f593 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -78,6 +78,24 @@ label { border-radius: 3px; } +.select-wrapper { + position: relative; + + .caret { + position: absolute; + right: 10px; + top: $gl-padding; + color: $gray-darkest; + pointer-events: none; + } +} + +.select-control { + padding-left: 10px; + padding-right: 10px; + -webkit-appearance: none; +} + .form-control-inline { display: inline; } diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index c83cf881596..dc4668877f2 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -24,6 +24,10 @@ background-color: $color-darker; a { color: #fff; + + h3 { + color: #fff; + } } } } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 3f015427d07..8190a97ed58 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -8,7 +8,7 @@ header { &.navbar-empty { height: 58px; background: #fff; - border-bottom: 1px solid #eee; + border-bottom: 1px solid $btn-gray-hover; .center-logo { margin: 11px 0; @@ -22,13 +22,21 @@ header { } &.navbar-gitlab { - padding: 0 20px; + padding: 0 16px; z-index: 100; margin-bottom: 0; - min-height: $header-height; - background-color: #fff; + height: $header-height; + background-color: $background-color; border: none; - border-bottom: 1px solid #eee; + border-bottom: 1px solid $border-color; + + @media (max-width: $screen-xs-min) { + padding: 0 16px; + } + + &.with-horizontal-nav { + border-bottom: none; + } .container-fluid { width: 100% !important; @@ -47,7 +55,7 @@ header { text-align: center; &:hover, &:focus, &:active { - background-color: #fff; + background-color: $background-color; } } @@ -56,22 +64,54 @@ header { margin: 6px 0; border-radius: 0; position: absolute; - right: 2px; + right: -10px; + padding: 6px 10px; &:hover { - background-color: #eee; + background-color: $btn-gray-hover; } + &.active { color: $gl-icon-color; } } } + + &.header-collapsed { + padding: 0 16px; + } + + .side-nav-toggle { + display: none; + position: absolute; + left: -10px; + margin: 6px 0; + padding: 6px 10px; + border: none; + background-color: $background-color; + + &:hover { + background-color: $btn-gray-hover; + } + + &:focus { + outline: none; + } + + @media (max-width: $screen-xs-min) { + display: block; + } + } } .header-content { position: relative; height: $header-height; - padding-right: 20px; + padding-right: 40px; + + @media (max-width: $screen-xs-min) { + padding-left: 40px; + } @media (min-width: $screen-sm-min) { padding-right: 0; @@ -122,6 +162,10 @@ header { } } + .project-item-select-holder { + display: inline; + } + .impersonation i { color: $red-normal; } @@ -137,6 +181,10 @@ header { @media (min-width: $screen-md-min) { @include collapsed-header; } + + @media (max-width: $screen-xs-min) { + margin-left: 0; + } } .header-expanded { @@ -145,6 +193,10 @@ header { @media (min-width: $screen-md-min) { margin-left: $sidebar_width; } + + @media (max-width: $screen-xs-min) { + margin-left: 0; + } } @media (max-width: $screen-xs-max) { diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 7f7b7c806e7..8bfc0d583c5 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -5,7 +5,7 @@ */ .status-box { - + /* Extra small devices (phones, less than 768px) */ /* No media query since this is the default in Bootstrap */ padding: 5px 11px; diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index 0f32d36d59c..fd885b38680 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -90,3 +90,12 @@ box-shadow: none; width: 100%; } + +.md { + &.md-preview-holder { + code { + white-space: pre-wrap; + word-break: keep-all; + } + } +} diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 66180f38a4f..33cbee85987 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -30,7 +30,7 @@ } .rss-btn { - display: none !important; + display: none; } .project-home-links { @@ -70,13 +70,6 @@ display: none; } - .issue-details { - .creator, - .page-title .btn-close { - display: none; - } - } - %ul.notes .note-role, .note-actions { display: none; } diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss new file mode 100644 index 00000000000..26ad2870aa0 --- /dev/null +++ b/app/assets/stylesheets/framework/modal.scss @@ -0,0 +1,22 @@ +.modal-body { + position: relative; + overflow-y: auto; + padding: 15px; + + .form-actions { + margin: -$gl-padding+1; + margin-top: 15px; + } + + .text-danger { + font-weight: bold; + } +} + +body.modal-open { + overflow: hidden; +} + +.modal .modal-dialog { + width: 860px; +} diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 192d53b048a..f2d7dc625e0 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -26,8 +26,8 @@ } &.active a { - color: #000; - border-bottom: 2px solid #4688f1; + border-bottom: 2px solid $link-underline-blue; + color: $black; } .badge { @@ -140,6 +140,12 @@ } } + .project-filter-form { + input { + background-color: $background-color; + } + } + @media (max-width: $screen-xs-max) { padding-bottom: 0; @@ -185,3 +191,73 @@ } } } + +.layout-nav { + position: fixed; + top: $header-height; + width: 100%; + z-index: 1; + background: $background-color; + border-bottom: 1px solid $border-color; + transition-duration: .3s; + + .container-fluid { + position: relative; + } + + .controls { + float: right; + padding: 7px 0 0; + + i { + color: $layout-link-gray; + } + + .fa-rss, + .fa-cog { + font-size: 16px; + } + + .fa-caret-down { + margin-left: 5px; + color: $gl-icon-color; + } + + .dropdown { + margin-left: 7px; + } + } + + .nav-links { + border-bottom: none; + height: 51px; + white-space: nowrap; + overflow-x: auto; + + li { + + a { + padding-top: 10px; + } + + a, i { + color: $layout-link-gray; + } + + &.active { + a, i { + color: $black; + } + } + + .badge { + color: $gl-icon-color; + } + } + } + +} + +.page-with-layout-nav { + margin-top: 50px; +} diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index b2fab387e17..6efc6ec1e4b 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -7,13 +7,11 @@ .select2-choice { background: #fff; border-color: $input-border; - border-color: $border-white-light; height: 35px; padding: $gl-vert-padding $gl-btn-padding; font-size: $gl-font-size; line-height: 1.42857143; - - @include border-radius($border-radius-default); + border-radius: $border-radius-base; .select2-arrow { background-image: none; @@ -121,9 +119,6 @@ } } -.select2-container-multi .select2-choices .select2-search-choice { -} - .select2-drop-active { margin-top: 6px; font-size: 14px; @@ -202,6 +197,14 @@ } } +.select2-highlighted { + .group-result { + .group-path { + color: #fff; + } + } +} + .group-result { .group-image { float: left; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 18189e985c4..bd91f51708c 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -3,6 +3,7 @@ position: absolute; width: 58px; cursor: pointer; + margin-top: 8px; } .page-with-sidebar { @@ -62,7 +63,7 @@ float: left; height: $header-height; width: 100%; - padding: 11px 0 11px 22px; + padding-left: 22px; overflow: hidden; outline: none; transition-duration: .3s; @@ -85,7 +86,7 @@ margin: 0; margin-left: 50px; font-size: 19px; - line-height: 41px; + line-height: 50px; font-weight: normal; } } @@ -97,7 +98,7 @@ } .sidebar-user { - padding: 9px 22px; + padding: 7px 22px; position: fixed; bottom: 40px; width: $sidebar_width; @@ -209,15 +210,33 @@ } } +.sidebar-wrapper { + &.hidden-nav { + width: 0; + } +} + .page-sidebar-collapsed { padding-left: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + padding-left: 0; + } + .sidebar-wrapper { width: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + width: 0; + } + .header-logo { width: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + width: 0; + } + a { padding-left: ($sidebar_collapsed_width - 36) / 2; @@ -243,17 +262,35 @@ .collapse-nav a { width: $sidebar_collapsed_width; + + @media (max-width: $screen-xs-min) { + width: 0; + } } .sidebar-user { padding-left: ($sidebar_collapsed_width - 36) / 2; width: $sidebar_collapsed_width; + @media (max-width: $screen-xs-min) { + width: 0; + padding-left: 0; + padding-right: 0; + } + .username { display: none; } } } + + .layout-nav { + padding-right: $sidebar_collapsed_width; + + @media (max-width: $screen-xs-min) { + padding-right: 0;; + } + } } .page-sidebar-expanded { @@ -263,6 +300,10 @@ padding-left: $sidebar_width; } + @media (max-width: $screen-xs-min) { + padding-left: 0; + } + .sidebar-wrapper { width: $sidebar_width; @@ -280,6 +321,20 @@ } } } + + .layout-nav { + @media (max-width: $screen-xs-min) { + padding-right: 0;; + } + + @media (min-width: $screen-xs-min) and (max-width: $screen-md-min) { + padding-right: 62px; + } + + @media (min-width: $screen-md-min) { + padding-right: $sidebar_width; + } + } } .right-sidebar-collapsed { diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 75b770ae5a2..b42075c98d0 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -32,13 +32,11 @@ table { th { background-color: $background-color; font-weight: normal; - font-size: 15px; - border-bottom: 1px solid $border-color; + border-bottom: none; } td { border-color: $table-border-color; - border-bottom: 1px solid $border-color; } } } diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss index b91f2f6f898..29501069d27 100644 --- a/app/assets/stylesheets/framework/timeline.scss +++ b/app/assets/stylesheets/framework/timeline.scss @@ -11,7 +11,7 @@ border-bottom: 1px solid $border-white-light; &:target { - background: $row-hover; + background: $line-target-blue; } .avatar { @@ -39,8 +39,7 @@ .diff-file { border: 1px solid $border-color; border-bottom: none; - margin-left: 0; - margin-right: 0; + margin: 0; } } diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 96bab7880c2..6a45c34ccbb 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -81,7 +81,7 @@ // Labels .label { - padding: 2px 4px; + padding: 4px 5px; font-size: 13px; font-style: normal; font-weight: normal; diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index c72af5dad0a..371c1bf17e1 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding; //== Code // //## -$pre-bg: #f8fafc !default; +$pre-bg: $background-color !default; $pre-color: $gl-gray !default; -$pre-border-color: #e7e9ed; +$pre-border-color: $border-color; $table-bg-accent: $background-color; diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 0a5b4b8834c..2779cd56788 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -42,14 +42,14 @@ margin: 24px 0 12px; padding: 0 0 10px; border-bottom: 1px solid #e7e9ed; - color: #313236; + color: $gl-gray-dark; } h2 { font-size: 1.2em; font-weight: 600; margin: 24px 0 12px; - color: #313236; + color: $gl-gray-dark; } h3 { @@ -205,6 +205,10 @@ h1, h2, h3, h4, h5, h6 { font-weight: 600; } +.light-header { + font-weight: 600; +} + /** CODE **/ pre { font-family: $monospace_font; @@ -259,3 +263,9 @@ h1, h2, h3, h4 { color: $gl-gray; } } + +.text-right-lg { + @media (min-width: $screen-lg-min) { + text-align: right; + } +} diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index f910cf61817..84e74db06b0 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -12,7 +12,7 @@ $gutter_inner_width: 258px; */ $border-color: #e5e5e5; $focus-border-color: #3aabf0; -$table-border-color: #eef0f2; +$table-border-color: #ececec; $background-color: #fafafa; /* @@ -20,7 +20,7 @@ $background-color: #fafafa; */ $gl-font-size: 15px; $gl-title-color: #333; -$gl-text-color: #555; +$gl-text-color: #5c5c5c; $gl-text-green: #4a2; $gl-text-red: #d12f19; $gl-text-orange: #d90; @@ -30,6 +30,7 @@ $gl-placeholder-color: #8f8f8f; $gl-icon-color: $gl-placeholder-color; $gl-grayish-blue: #7f8fa4; $gl-gray: $gl-text-color; +$gl-gray-dark: #313236; $gl-header-color: $gl-title-color; /* @@ -65,16 +66,18 @@ $gl-padding-top: 10px; $row-hover: #f4f8fe; $progress-color: #c0392b; $avatar_radius: 50%; -$header-height: 58px; +$header-height: 50px; $fixed-layout-width: 1280px; $gl-avatar-size: 40px; $error-exclamation-point: #e62958; $border-radius-default: 2px; $btn-transparent-color: #8f8f8f; -$ssh-key-icon-color: #8f8f8f; -$ssh-key-icon-size: 18px; +$settings-icon-size: 18px; $provider-btn-group-border: #e5e5e5; $provider-btn-not-active-color: #4688f1; +$link-underline-blue: #4a8bee; +$layout-link-gray: #7e7c7c; +$todo-alert-blue: #428bca; /* * Color schema @@ -109,6 +112,7 @@ $red-light: #e52c5a; $red-normal: #d22852; $red-dark: darken($red-normal, 5%); +$black: #000; $black-transparent: rgba(0, 0, 0, 0.3); $border-white-light: #f1f2f4; @@ -168,8 +172,12 @@ $line-removed: #fbe9eb; $line-removed-dark: #fac5cd; $line-number-old: #f9d7dc; $line-number-new: #ddfbe6; +$line-number-select: #fbf2da; $match-line: #fafafa; $table-border-gray: #f0f0f0; +$line-target-blue: #eaf3fc; +$line-select-yellow: #fcf8e7; +$line-select-yellow-dark: #f0e2bd; /* * Fonts */ @@ -179,7 +187,6 @@ $regular_font: 'Source Sans Pro', "Helvetica Neue", Helvetica, Arial, sans-serif /* * Dropdowns */ -$dropdown-border-radius: 2px; $dropdown-width: 300px; $dropdown-bg: #fff; $dropdown-link-color: #555; @@ -208,6 +215,7 @@ $dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color; $btn-active-gray: #ececec; $btn-placeholder-gray: #c7c7c7; $btn-white-active: #848484; +$btn-gray-hover: #eee; /* * Award emoji @@ -241,3 +249,8 @@ $note-form-border-color: #e5e5e5; $note-toolbar-color: #959494; $zen-control-hover-color: #111; + +$calendar-header-color: #b8b8b8; +$calendar-hover-bg: #ecf3fe; +$calendar-border-color: rgba(#000, .1); +$calendar-unselectable-bg: #faf9f9; diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 28253d4ccb4..80a509a7c1a 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -111,8 +111,6 @@ .vg { color: #f8f8f2 } /* Name.Variable.Global */ .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .il { color: #ae81ff } /* Literal.Number.Integer.Long */ - - .gh { } /* Generic Heading & Diff Header */ .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */ .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */ .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */ diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 1ff6ad75e07..31a4e3deaac 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -21,11 +21,6 @@ // Diff line .line_holder { - td.diff-line-num.hll:not(.empty-cell), - td.line_content.hll:not(.empty-cell) { - background-color: #f8eec7; - border-color: darken(#f8eec7, 15%); - } .diff-line-num { &.old { @@ -37,11 +32,16 @@ background-color: $line-number-new; border-color: $line-added-dark; } + + &.hll:not(.empty-cell) { + background-color: $line-number-select; + border-color: $line-select-yellow-dark; + } } .line_content { &.old { - background: $line-removed; + background-color: $line-removed; span.idiff { background-color: $line-removed-dark; @@ -58,7 +58,11 @@ &.match { color: $black-transparent; - background: $match-line; + background-color: $match-line; + } + + &.hll:not(.empty-cell) { + background-color: $line-select-yellow; } } } diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 201f3e5ca46..aa41565f812 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -83,3 +83,12 @@ } } } + +table.builds { + + .build-link { + a { + color: $gl-dark-link-color; + } + } +} diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index 358d2f4ab9d..c2cd227571f 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -31,9 +31,23 @@ } .commit-committer-link, .commit-author-link { - color: #444; + color: $gl-gray; font-weight: bold; } + + .time_ago { + margin-left: 8px; + } + + .fa-clipboard { + color: $dropdown-title-btn-color; + } + + .commit-info { + &.branches { + margin-left: 8px; + } + } } .commit-box { @@ -42,7 +56,7 @@ .commit-title { margin: 0; font-size: 23px; - color: #313236; + color: $gl-gray-dark; } .commit-description { @@ -83,6 +97,14 @@ } } +.commit-action-buttons { + i { + color: $gl-icon-color; + font-size: 13px; + margin-right: 3px; + } +} + /* * Commit message textarea for web editor and * custom merge request message diff --git a/app/assets/stylesheets/pages/confirmation.scss b/app/assets/stylesheets/pages/confirmation.scss new file mode 100644 index 00000000000..125f495d6d4 --- /dev/null +++ b/app/assets/stylesheets/pages/confirmation.scss @@ -0,0 +1,18 @@ +.well-confirmation { + margin-bottom: 20px; + border-bottom: 1px solid #eee; + + > h1 { + font-weight: 400; + } + + .lead { + margin-bottom: 20px; + } +} + +.confirmation-content { + a { + color: $md-link-color; + } +} diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 5917f089720..5e61e61d85c 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -1,5 +1,5 @@ .detail-page-header { - padding: 11px 0; + padding: $gl-padding-top 0; border-bottom: 1px solid $border-color; color: #5c5d5e; font-size: 16px; @@ -16,18 +16,13 @@ .issue_created_ago, .author_link { white-space: nowrap; } - - .issue-meta { - display: inline-block; - line-height: 20px; - } } .detail-page-description { .title { margin: 0; font-size: 23px; - color: #313236; + color: $gl-gray-dark; } .description { @@ -41,4 +36,11 @@ } } } + + .wiki { + code { + white-space: pre-wrap; + word-break: keep-all; + } + } } diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 183f22a1b24..1a7d5f9666e 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -34,6 +34,7 @@ background: #fff; color: #333; border-radius: 0 0 3px 3px; + -webkit-overflow-scrolling: auto; .unfold { cursor: pointer; @@ -86,7 +87,7 @@ } span { - white-space: pre; + white-space: pre-wrap; } } } @@ -97,7 +98,11 @@ } td.line_content.parallel { - width: 50%; + width: 46%; + } + + .add-diff-note { + margin-left: -65px; } } @@ -126,8 +131,13 @@ margin: 0; padding: 0 0.5em; border: none; + &.parallel { display: table-cell; + + span { + word-break: break-all; + } } } @@ -335,7 +345,7 @@ } .diff-file .line_content { - white-space: pre; + white-space: pre-wrap; } .diff-wrap-lines .line_content { diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 0f0592a0ab8..8981f070a20 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -26,6 +26,10 @@ line-height: 42px; padding-top: 7px; padding-bottom: 7px; + + .pull-right { + height: 20px; + } } .editor-ref { @@ -53,4 +57,9 @@ .select2 { float: right; } + + .encoding-selector, + .license-selector { + display: inline-block; + } } diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss index 4e5c4ed84b6..f7f9a9bb770 100644 --- a/app/assets/stylesheets/pages/graph.scss +++ b/app/assets/stylesheets/pages/graph.scss @@ -18,9 +18,6 @@ } .graphs { - .graph-author-commits-count { - } - .graph-author-email { float: right; color: #777; diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss index 604f1700cf8..4a95b7b852e 100644 --- a/app/assets/stylesheets/pages/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -55,23 +55,6 @@ } } -.modal-body { - position: relative; - overflow-y: auto; - padding: 15px; - .form-actions { - margin: -$gl-padding+1; - } -} - -body.modal-open { - overflow: hidden; -} - -.modal .modal-dialog { - width: 860px; -} - .documentation { padding: 7px; } diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss index 6a99cd9cb94..84cc35239f9 100644 --- a/app/assets/stylesheets/pages/import.scss +++ b/app/assets/stylesheets/pages/import.scss @@ -16,3 +16,24 @@ i.icon-gitorious-big { width: 18px; height: 18px; } + +.import-jobs-from-col, +.import-jobs-to-col { + width: 40%; +} + +.import-jobs-status-col { + width: 20%; +} + +.btn-import { + .loading-icon { + display: none; + } + + &.is-loading { + .loading-icon { + display: inline-block; + } + } +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 6bd90a23620..1cf3023ecc9 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -128,6 +128,7 @@ top: 58px; bottom: 0; right: 0; + z-index: 10; transition: width .3s; background: $gray-light; padding: 10px 20px; @@ -241,16 +242,20 @@ } } - .btn { + .issuable-pager { background: $gray-normal; border: 1px solid $border-gray-normal; &:hover { background: $gray-dark; border: 1px solid $border-gray-dark; } + + &.btn-primary { + @extend .btn-primary + } } - a:not(.btn) { + a:not(.issuable-pager) { &:hover { color: $md-link-color; text-decoration: none; @@ -273,10 +278,6 @@ } } -.btn-default.gutter-toggle { - margin-top: 4px; -} - .detail-page-description { small { color: $gray-darkest; @@ -322,3 +323,50 @@ padding-top: 7px; } } + +.issuable-status-box { + float: none; + display: inline-block; + margin-top: 0; + + @media (max-width: $screen-xs-max) { + position: absolute; + top: 0; + left: 0; + } +} + +.issuable-header { + position: relative; + padding-left: 45px; + padding-right: 45px; + line-height: 35px; + + @media (min-width: $screen-sm-min) { + float: left; + padding-left: 0; + padding-right: 0; + } +} + +.issuable-actions { + padding-top: 10px; + + @media (min-width: $screen-sm-min) { + float: right; + padding-top: 0; + } +} + +.issuable-gutter-toggle { + @media (max-width: $screen-sm-max) { + position: absolute; + top: 0; + right: 0; + } +} + +.issuable-meta { + display: inline-block; + line-height: 18px; +} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 6a1d28590c2..fc9db97132d 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -86,41 +86,9 @@ form.edit-issue { @media (max-width: $screen-xs-max) { .issue-btn-group { width: 100%; - margin-top: 5px; - - .btn-group { - width: 100%; - - ul { - width: 100%; - text-align: center; - } - } .btn { width: 100%; - - &:first-child:not(:last-child) { - - } - - &:not(:first-child):not(:last-child) { - margin-top: 10px; - } - - &:last-child:not(:first-child) { - margin-top: 10px; - } - } - } - - .issue { - &:hover .issue-actions { - display: none !important; - } - - .issue-updated-at { - display: none; } } } @@ -133,11 +101,3 @@ form.edit-issue { color: $gl-text-color; margin-left: 52px; } - -.editor-details { - display: block; - - @media (min-width: $screen-sm-min) { - display: inline-block; - } -} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 4ef548ffbe7..c4005ba1e69 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -104,7 +104,7 @@ font-weight: 600; font-size: 17px; margin: 5px 0; - color: #313236; + color: $gl-gray-dark; } p:last-child { @@ -136,7 +136,7 @@ } .label-branch { - color: #313236; + color: $gl-gray-dark; font-family: $monospace_font; font-weight: bold; overflow: hidden; @@ -272,3 +272,19 @@ display: inline-block; width: 250px; } + +.table-holder { + .builds { + + th { + background-color: $white-light; + color: $gl-placeholder-color; + } + + th, + td { + padding: 16px; + } + } +} + diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index d0e72a4422c..b94f524b513 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -28,7 +28,7 @@ li.milestone { // Issue title span a { - color: rgba(0,0,0,0.64); + color: $gl-text-color; } } } @@ -51,7 +51,7 @@ li.milestone { margin-top: 7px; .issuable-number { - color: rgba(0,0,0,0.44); + color: $gl-placeholder-color; margin-right: 5px; } .avatar { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 07c707e7b77..7fa13e66b43 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -42,6 +42,7 @@ .note-textarea { display: block; padding: 10px 0; + color: $gl-gray; font-family: $regular_font; border: 0; @@ -61,11 +62,11 @@ padding: $gl-padding-top $gl-padding; border: 1px solid $note-form-border-color; border-radius: $border-radius-base; + transition: border-color ease-in-out 0.15s, + box-shadow ease-in-out 0.15s; &.is-focused { - border-color: $focus-border-color; - box-shadow: 0 0 2px $black-transparent, - 0 0 4px rgba($focus-border-color, .4); + @extend .form-control:focus; .comment-toolbar, .nav-links { @@ -83,18 +84,6 @@ border-color: $gl-success; } } - - p { - code { - white-space: normal; - } - - pre { - code { - white-space: pre; - } - } - } } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 9d808cce152..8ad47a50541 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -80,16 +80,8 @@ ul.notes { @include md-typography; // On diffs code should wrap nicely and not overflow - p { - code { - white-space: normal; - } - - pre { - code { - white-space: pre; - } - } + code { + white-space: pre-wrap; } // Reset ul style types since we're nested inside a ul already @@ -116,10 +108,10 @@ ul.notes { border-color: darken(#f5f5f5, 8%); margin: 10px 0; } - } - a { - word-break: break-all; + code { + word-break: keep-all; + } } } @@ -136,7 +128,7 @@ ul.notes { margin-right: 10px; } .line_content { - white-space: pre; + white-space: pre-wrap; } } @@ -175,6 +167,11 @@ ul.notes { .notes { background-color: $white-light; } + + a code { + top: 0; + margin-right: 0; + } } } } @@ -190,6 +187,9 @@ ul.notes { } } + .author_link { + color: $gl-gray; + } } .note-headline-light, @@ -197,6 +197,12 @@ ul.notes { color: $notes-light-color; } +.discussion-headline-light { + a { + color: $gl-link-color; + } +} + /** * Actions for Discussions/Notes */ @@ -208,6 +214,17 @@ ul.notes { color: $notes-action-color; } +.discussion-actions { + @media (max-width: $screen-md-max) { + float: none; + margin-left: 0; + + .note-action-button { + margin-left: 0; + } + } +} + .note-action-button, .discussion-action-button { display: inline-block; @@ -285,7 +302,7 @@ ul.notes { padding: 4px; font-size: 16px; color: $gl-link-color; - margin-left: -60px; + margin-left: -56px; position: absolute; z-index: 10; width: 32px; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index a9656e5cae7..843379a3f54 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -18,7 +18,8 @@ } .account-btn-link, -.profile-settings-sidebar a { +.profile-settings-sidebar a, +.settings-sidebar a { color: $md-link-color; } @@ -123,12 +124,6 @@ } } -.key-icon { - color: $ssh-key-icon-color; - font-size: $ssh-key-icon-size; - line-height: 42px; -} - .key-created-at { line-height: 42px; } @@ -180,14 +175,6 @@ } } -.profile-settings-message { - line-height: 32px; - color: $warning-message-color; - background-color: $warning-message-bg; - border: 1px solid $warning-message-border; - border-radius: $border-radius-base; -} - .oauth-applications { form { display: inline-block; @@ -218,3 +205,21 @@ text-align: center; } } + +.user-profile { + @media (max-width: $screen-xs-max) { + .cover-block { + padding-top: 20px; + } + + .cover-controls { + position: static; + margin-bottom: 20px; + + .btn { + display: inline-block; + width: 46%; + } + } + } +} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index fcca9d4faf5..c20f04653fc 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -178,7 +178,7 @@ .option-title { font-weight: normal; display: inline-block; - color: #313236; + color: $gl-gray-dark; } .option-descr { @@ -202,8 +202,31 @@ min-width: 200px; } -.deploy-project-label { - margin: 1px; +.deploy-key-content { + @media (min-width: $screen-sm-min) { + float: left; + + &:last-child { + float: right; + } + } +} + +.deploy-key-projects { + @media (min-width: $screen-sm-min) { + line-height: 42px; + } +} + +a.deploy-project-label { + padding: 5px; + margin-right: 5px; + color: $gl-gray; + background-color: $row-hover; + + &:hover { + color: $gl-link-color; + } } .vs-public { @@ -256,12 +279,6 @@ } } -table.table.protected-branches-list tr.no-border { - th, td { - border: 0; - } -} - .project-import .btn { float: left; margin-right: 10px; @@ -474,3 +491,14 @@ pre.light-well { color: #fff; } } + +.protected-branches-list { + a { + color: $gl-gray; + font-weight: 600; + + &:hover { + color: $gl-link-color; + } + } +} diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index f0f3744c6fa..2bff70c8c64 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -10,17 +10,6 @@ } } -.search-holder { - max-width: 600px; - margin: 0 auto; - margin-bottom: 20px; - - input { - border-color: #bbb; - font-weight: bold; - } -} - .search { margin-right: 10px; margin-left: 10px; @@ -159,7 +148,85 @@ &.has-location-badge { .search-input-wrap { - width: 78%; + width: 68%; } } } + +.search-holder { + @media (min-width: $screen-sm-min) { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + } + + .search-field-holder { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + position: relative; + margin-right: 0; + + @media (min-width: $screen-sm-min) { + margin-right: 5px; + } + } + + .search-icon { + position: absolute; + left: 10px; + top: 10px; + color: $gray-darkest; + pointer-events: none; + } + + .search-text-input { + padding-left: $gl-padding + 15px; + padding-right: $gl-padding + 15px; + } + + .btn-search { + width: 100%; + margin-top: 5px; + + @media (min-width: $screen-sm-min) { + width: auto; + margin-top: 0; + margin-left: 5px; + } + } + + .dropdown { + @media (min-width: $screen-sm-min) { + margin-left: 5px; + margin-right: 5px; + } + } + + .dropdown-menu-toggle { + width: 100%; + margin-top: 5px; + + @media (min-width: $screen-sm-min) { + width: 160px; + margin-top: 0; + } + } +} + +.search-clear { + position: absolute; + right: 10px; + top: 10px; + padding: 0; + color: $gray-darkest; + line-height: 0; + background: none; + border: 0; + + &:hover, + &:focus { + color: $gl-link-color; + outline: none; + } +} diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss new file mode 100644 index 00000000000..3fb70085713 --- /dev/null +++ b/app/assets/stylesheets/pages/settings.scss @@ -0,0 +1,14 @@ +.settings-list-icon { + color: $gl-placeholder-color; + font-size: $settings-icon-size; + line-height: 42px; +} + +.settings-message { + padding: 5px; + line-height: 1.3; + color: $warning-message-color; + background-color: $warning-message-bg; + border: 1px solid $warning-message-border; + border-radius: $border-radius-base; +} diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index dbb6daf0d70..2370d35924e 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -1,7 +1,7 @@ .container-fluid { .ci-status { padding: 2px 7px; - margin-right: 5px; + margin-right: 10px; border: 1px solid #eee; white-space: nowrap; @include border-radius(4px); diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 75f78569e3c..e51c3491dae 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -6,9 +6,16 @@ .navbar-nav { li { .badge.todos-pending-count { - background-color: $gl-icon-color; margin-top: -5px; font-weight: normal; + background: $todo-alert-blue; + margin-left: -17px; + font-size: 11px; + color: white; + padding: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 3px; } } } diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 25b5e95583e..a84fc2e0318 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -16,7 +16,7 @@ tr { > td, > th { - line-height: 26px; + line-height: 23px; } &:hover { diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index 1be0551ad3b..a30b6492572 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -1,17 +1,37 @@ -/* Generic print styles */ -header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;} -.profiler-results {display: none;} - -/* Styles targeted specifically at printing files */ -.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;} -.file-title {display: none;} -.file-holder {border: none;} - .wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; } .wiki h1 {font-size: 30px;} .wiki h2 {font-size: 22px;} .wiki h3 {font-size: 18px; font-weight: bold; } -.sidebar-wrapper { display: none; } -.nav { display: none; } -.btn { display: none; } +header, +nav, +nav.main-nav, +nav.navbar-collapse, +nav.navbar-collapse.collapse, +.profiler-results, +.tree-ref-holder, +.tree-holder .breadcrumb, +.blob-commit-info, +.file-title, +.file-holder, +.sidebar-wrapper, +.nav, +.btn, +ul.notes-form, +.merge-request-ci-status .ci-status-link:after, +.issuable-gutter-toggle, +.gutter-toggle, +.issuable-details .content-block-small, +.edit-link, +.note-action-button { + display: none!important; +} + +.page-gutter { + padding-top: 0; + padding-left: 0; +} + +.right-sidebar { + top: 0; +} |