diff options
Diffstat (limited to 'app/assets')
| -rw-r--r-- | app/assets/images/comment_add.png | bin | 781 -> 0 bytes | |||
| -rw-r--r-- | app/assets/images/diff_note_add.png | bin | 0 -> 691 bytes | |||
| -rw-r--r-- | app/assets/javascripts/behaviors/details_behavior.coffee | 5 | ||||
| -rw-r--r-- | app/assets/javascripts/behaviors/toggler_behavior.coffee | 5 | ||||
| -rw-r--r-- | app/assets/javascripts/extensions/array.js | 7 | ||||
| -rw-r--r-- | app/assets/javascripts/notes.js | 670 | ||||
| -rw-r--r-- | app/assets/stylesheets/application.scss | 5 | ||||
| -rw-r--r-- | app/assets/stylesheets/behaviors.scss | 14 | ||||
| -rw-r--r-- | app/assets/stylesheets/common.scss | 6 | ||||
| -rw-r--r-- | app/assets/stylesheets/sections/commits.scss | 15 | ||||
| -rw-r--r-- | app/assets/stylesheets/sections/notes.scss | 420 |
11 files changed, 759 insertions, 388 deletions
diff --git a/app/assets/images/comment_add.png b/app/assets/images/comment_add.png Binary files differdeleted file mode 100644 index 836557ac846..00000000000 --- a/app/assets/images/comment_add.png +++ /dev/null diff --git a/app/assets/images/diff_note_add.png b/app/assets/images/diff_note_add.png Binary files differnew file mode 100644 index 00000000000..8ec15b701fc --- /dev/null +++ b/app/assets/images/diff_note_add.png diff --git a/app/assets/javascripts/behaviors/details_behavior.coffee b/app/assets/javascripts/behaviors/details_behavior.coffee new file mode 100644 index 00000000000..7ad5c818946 --- /dev/null +++ b/app/assets/javascripts/behaviors/details_behavior.coffee @@ -0,0 +1,5 @@ +$ -> + $("body").on "click", ".js-details-target", -> + container = $(@).closest(".js-details-container") + + container.toggleClass("open") diff --git a/app/assets/javascripts/behaviors/toggler_behavior.coffee b/app/assets/javascripts/behaviors/toggler_behavior.coffee new file mode 100644 index 00000000000..3fefbf8e121 --- /dev/null +++ b/app/assets/javascripts/behaviors/toggler_behavior.coffee @@ -0,0 +1,5 @@ +$ -> + $("body").on "click", ".js-toggler-target", -> + container = $(@).closest(".js-toggler-container") + + container.toggleClass("on") diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js new file mode 100644 index 00000000000..7fccc9c9d5f --- /dev/null +++ b/app/assets/javascripts/extensions/array.js @@ -0,0 +1,7 @@ +Array.prototype.first = function() { + return this[0]; +} + +Array.prototype.last = function() { + return this[this.length-1]; +}
\ No newline at end of file diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index b6f65b7aa5e..fa0edd2d1f4 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -9,72 +9,310 @@ var NoteList = { loading_more_disabled: false, reversed: false, - init: - function(tid, tt, path) { - this.notes_path = path + ".js"; - this.target_id = tid; - this.target_type = tt; - this.reversed = $("#notes-list").is(".reversed"); - this.target_params = "target_type=" + this.target_type + "&target_id=" + this.target_id; - - // get initial set of notes - this.getContent(); - - $("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() { - $(this).closest('li').fadeOut(function() { - $(this).remove(); - NoteList.updateVotes(); - }); + init: function(tid, tt, path) { + NoteList.notes_path = path + ".js"; + NoteList.target_id = tid; + NoteList.target_type = tt; + NoteList.reversed = $("#notes-list").is(".reversed"); + NoteList.target_params = "target_type=" + NoteList.target_type + "&target_id=" + NoteList.target_id; + + NoteList.setupMainTargetNoteForm(); + + if(NoteList.reversed) { + var form = $(".js-main-target-form"); + form.find(".buttons, .note_options").hide(); + var textarea = form.find(".js-note-text"); + textarea.css("height", "40px"); + textarea.on("focus", function(){ + textarea.css("height", "80px"); + form.find(".buttons, .note_options").show(); }); + } - $(".note-form-holder").on("ajax:before", function(){ - $(".submit_note").disable(); - }) + // get initial set of notes + NoteList.getContent(); - $(".note-form-holder").on("ajax:complete", function(){ - $(".submit_note").enable(); - $('#preview-note').hide(); - $('#note_note').show(); - }) + // add a new diff note + $(document).on("click", + ".js-add-diff-note-button", + NoteList.addDiffNote); - disableButtonIfEmptyField(".note-text", ".submit_note"); + // reply to diff/discussion notes + $(document).on("click", + ".js-discussion-reply-button", + NoteList.replyToDiscussionNote); - $("#note_attachment").change(function(e){ - var val = $('.input-file').val(); - var filename = val.replace(/^.*[\\\/]/, ''); - $(".file_name").text(filename); - }); + // setup note preview + $(document).on("click", + ".js-note-preview-button", + NoteList.previewNote); + + // update the file name when an attachment is selected + $(document).on("change", + ".js-note-attachment-input", + NoteList.updateFormAttachment); + + // hide diff note form + $(document).on("click", + ".js-close-discussion-note-form", + NoteList.removeDiscussionNoteForm); + + // remove a note (in general) + $(document).on("click", + ".js-note-delete", + NoteList.removeNote); + + // reset main target form after submit + $(document).on("ajax:complete", + ".js-main-target-form", + NoteList.resetMainTargetForm); + + + $(document).on("click", + ".js-choose-note-attachment-button", + NoteList.chooseNoteAttachment); + }, + + + /** + * When clicking on buttons + */ + + /** + * Called when clicking on the "add a comment" button on the side of a diff line. + * + * Inserts a temporary row for the form below the line. + * Sets up the form and shows it. + */ + addDiffNote: function(e) { + e.preventDefault(); + + // find the form + var form = $(".js-new-note-form"); + var row = $(this).closest("tr"); + var nextRow = row.next(); + + // does it already have notes? + if (nextRow.is(".notes_holder")) { + $.proxy(NoteList.replyToDiscussionNote, + nextRow.find(".js-discussion-reply-button") + ).call(); + } else { + // add a notes row and insert the form + row.after('<tr class="notes_holder js-temp-notes-holder"><td class="notes_line" colspan="2"></td><td class="notes_content"></td></tr>'); + form.clone().appendTo(row.next().find(".notes_content")); + + // show the form + NoteList.setupDiscussionNoteForm($(this), row.next().find("form")); + } + }, + + /** + * Called when clicking the "Choose File" button. + * + * Opesn the file selection dialog. + */ + chooseNoteAttachment: function() { + var form = $(this).closest("form"); + + form.find(".js-note-attachment-input").click(); + }, - if(this.reversed) { - var textarea = $(".note-text"); - $('.note_advanced_opts').hide(); - textarea.css("height", "40px"); - textarea.on("focus", function(){ - $(this).css("height", "80px"); - $('.note_advanced_opts').show(); + /** + * Shows the note preview. + * + * Lets the server render GFM into Html and displays it. + * + * Note: uses the Toggler behavior to toggle preview/edit views/buttons + */ + previewNote: function(e) { + e.preventDefault(); + + var form = $(this).closest("form"); + var preview = form.find('.js-note-preview'); + var noteText = form.find('.js-note-text').val(); + + if(noteText.trim().length === 0) { + preview.text('Nothing to preview.'); + } else { + preview.text('Loading...'); + $.post($(this).data('url'), {note: noteText}) + .success(function(previewData) { + preview.html(previewData); }); - } + } + }, + + /** + * Called in response to "cancel" on a diff note form. + * + * Shows the reply button again. + * Removes the form and if necessary it's temporary row. + */ + removeDiscussionNoteForm: function() { + var form = $(this).closest("form"); + var row = form.closest("tr"); + + // show the reply button (will only work for replys) + form.prev(".js-discussion-reply-button").show(); + + if (row.is(".js-temp-notes-holder")) { + // remove temporary row for diff lines + row.remove(); + } else { + // only remove the form + form.remove(); + } + }, - // Setup note preview - $(document).on('click', '#preview-link', function(e) { - $('#preview-note').text('Loading...'); + /** + * Called in response to deleting a note of any kind. + * + * Removes the actual note from view. + * Removes the whole discussion if the last note is being removed. + */ + removeNote: function() { + var note = $(this).closest(".note"); + var notes = note.closest(".notes"); - $(this).text($(this).text() === "Edit" ? "Preview" : "Edit"); + // check if this is the last note for this line + if (notes.find(".note").length === 1) { + // for discussions + notes.closest(".discussion").remove(); - var note_text = $('#note_note').val(); + // for diff lines + notes.closest("tr").remove(); + } - if(note_text.trim().length === 0) { - $('#preview-note').text('Nothing to preview.'); - } else { - $.post($(this).attr('href'), {note: note_text}).success(function(data) { - $('#preview-note').html(data); - }); - } + note.remove(); + NoteList.updateVotes(); + }, - $('#preview-note, #note_note').toggle(); - e.preventDefault(); - }); - }, + /** + * Called when clicking on the "reply" button for a diff line. + * + * Shows the note form below the notes. + */ + replyToDiscussionNote: function() { + // find the form + var form = $(".js-new-note-form"); + + // hide reply button + $(this).hide(); + // insert the form after the button + form.clone().insertAfter($(this)); + + // show the form + NoteList.setupDiscussionNoteForm($(this), $(this).next("form")); + }, + + + /** + * Helper for inserting and setting up note forms. + */ + + + /** + * Called in response to creating a note failing validation. + * + * Adds the rendered errors to the respective form. + * If "discussionId" is null or undefined, the main target form is assumed. + */ + errorsOnForm: function(errorsHtml, discussionId) { + // find the form + if (discussionId) { + var form = $("form[rel='"+discussionId+"']"); + } else { + var form = $(".js-main-target-form"); + } + + form.find(".js-errors").remove(); + form.prepend(errorsHtml); + + form.find(".js-note-text").focus(); + }, + + + /** + * Shows the diff/discussion form and does some setup on it. + * + * Sets some hidden fields in the form. + * + * Note: dataHolder must have the "discussionId", "lineCode", "noteableType" + * and "noteableId" data attributes set. + */ + setupDiscussionNoteForm: function(dataHolder, form) { + // setup note target + form.attr("rel", dataHolder.data("discussionId")); + form.find("#note_line_code").val(dataHolder.data("lineCode")); + form.find("#note_noteable_type").val(dataHolder.data("noteableType")); + form.find("#note_noteable_id").val(dataHolder.data("noteableId")); + + NoteList.setupNoteForm(form); + + form.find(".js-note-text").focus(); + }, + + /** + * Shows the main form and does some setup on it. + * + * Sets some hidden fields in the form. + */ + setupMainTargetNoteForm: function() { + // find the form + var form = $(".js-new-note-form"); + // insert the form after the button + form.clone().replaceAll($(".js-main-target-form")); + + form = form.prev("form"); + + // show the form + NoteList.setupNoteForm(form); + + // fix classes + form.removeClass("js-new-note-form"); + form.addClass("js-main-target-form"); + + // remove unnecessary fields and buttons + form.find("#note_line_code").remove(); + form.find(".js-close-discussion-note-form").remove(); + }, + + /** + * General note form setup. + * + * * deactivates the submit button when text is empty + * * hides the preview button when text is empty + * * setup GFM auto complete + * * show the form + */ + setupNoteForm: function(form) { + disableButtonIfEmptyField(form.find(".js-note-text"), form.find(".js-comment-button")); + + form.removeClass("js-new-note-form"); + + // setup preview buttons + form.find(".js-note-edit-button, .js-note-preview-button") + .tooltip({ placement: 'left' }); + + previewButton = form.find(".js-note-preview-button"); + form.find(".js-note-text").on("input", function() { + if ($(this).val().trim() !== "") { + previewButton.removeClass("turn-off").addClass("turn-on"); + } else { + previewButton.removeClass("turn-on").addClass("turn-off"); + } + }); + + // remove notify commit author checkbox for non-commit notes + if (form.find("#note_noteable_type").val() !== "Commit") { + form.find(".js-notify-commit-author").remove(); + } + + GitLab.GfmAutoComplete.setup(); + + form.show(); + }, /** @@ -86,40 +324,39 @@ var NoteList = { /** * Gets an inital set of notes. */ - getContent: - function() { - $.ajax({ - type: "GET", - url: this.notes_path, - data: this.target_params, - complete: function(){ $('.notes-status').removeClass("loading")}, - beforeSend: function() { $('.notes-status').addClass("loading") }, - dataType: "script"}); - }, + getContent: function() { + $.ajax({ + url: NoteList.notes_path, + data: NoteList.target_params, + complete: function(){ $('.js-notes-busy').removeClass("loading")}, + beforeSend: function() { $('.js-notes-busy').addClass("loading") }, + dataType: "script" + }); + }, /** * Called in response to getContent(). * Replaces the content of #notes-list with the given html. */ - setContent: - function(first_id, last_id, html) { - this.top_id = first_id; - this.bottom_id = last_id; - $("#notes-list").html(html); + setContent: function(newNoteIds, html) { + NoteList.top_id = newNoteIds.first(); + NoteList.bottom_id = newNoteIds.last(); + $("#notes-list").html(html); + // for the wall + if (NoteList.reversed) { // init infinite scrolling - this.initLoadMore(); + NoteList.initLoadMore(); // init getting new notes - if (this.reversed) { - this.initRefreshNew(); - } - }, + NoteList.initRefreshNew(); + } + }, /** * Handle loading more notes when scrolling to the bottom of the page. - * The id of the last note in the list is in this.bottom_id. + * The id of the last note in the list is in NoteList.bottom_id. * * Set up refreshing only new notes after all notes have been loaded. */ @@ -128,65 +365,57 @@ var NoteList = { /** * Initializes loading more notes when scrolling to the bottom of the page. */ - initLoadMore: - function() { - $(document).endlessScroll({ - bottomPixels: 400, - fireDelay: 1000, - fireOnce:true, - ceaseFire: function() { - return NoteList.loading_more_disabled; - }, - callback: function(i) { - NoteList.getMore(); - } - }); - }, + initLoadMore: function() { + $(document).endlessScroll({ + bottomPixels: 400, + fireDelay: 1000, + fireOnce:true, + ceaseFire: function() { + return NoteList.loading_more_disabled; + }, + callback: function(i) { + NoteList.getMore(); + } + }); + }, /** * Gets an additional set of notes. */ - getMore: - function() { - // only load more notes if there are no "new" notes - $('.loading').show(); - $.ajax({ - type: "GET", - url: this.notes_path, - data: this.target_params + "&loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id, - complete: function(){ $('.notes-status').removeClass("loading")}, - beforeSend: function() { $('.notes-status').addClass("loading") }, - dataType: "script"}); - }, + getMore: function() { + // only load more notes if there are no "new" notes + $('.loading').show(); + $.ajax({ + url: NoteList.notes_path, + data: NoteList.target_params + "&loading_more=1&" + (NoteList.reversed ? "before_id" : "after_id") + "=" + NoteList.bottom_id, + complete: function(){ $('.js-notes-busy').removeClass("loading")}, + beforeSend: function() { $('.js-notes-busy').addClass("loading") }, + dataType: "script" + }); + }, /** * Called in response to getMore(). * Append notes to #notes-list. */ - appendMoreNotes: - function(id, html) { - if(id != this.bottom_id) { - this.bottom_id = id; - $("#notes-list").append(html); - } - }, + appendMoreNotes: function(newNoteIds, html) { + var lastNewNoteId = newNoteIds.last(); + if(lastNewNoteId != NoteList.bottom_id) { + NoteList.bottom_id = lastNewNoteId; + $("#notes-list").append(html); + } + }, /** * Called in response to getMore(). * Disables loading more notes when scrolling to the bottom of the page. - * Initalizes refreshing new notes. */ - finishedLoadingMore: - function() { - this.loading_more_disabled = true; + finishedLoadingMore: function() { + NoteList.loading_more_disabled = true; - // from now on only get new notes - if (!this.reversed) { - this.initRefreshNew(); - } - // make sure we are up to date - this.updateVotes(); - }, + // make sure we are up to date + NoteList.updateVotes(); + }, /** @@ -194,52 +423,118 @@ var NoteList = { * * New notes are all notes that are created after the site has been loaded. * The "old" notes are in #notes-list the "new" ones will be in #new-notes-list. - * The id of the last "old" note is in this.bottom_id. + * The id of the last "old" note is in NoteList.bottom_id. */ /** * Initializes getting new notes every n seconds. + * + * Note: only used on wall. */ - initRefreshNew: - function() { - setInterval("NoteList.getNew()", 10000); - }, + initRefreshNew: function() { + setInterval("NoteList.getNew()", 10000); + }, /** * Gets the new set of notes. + * + * Note: only used on wall. */ - getNew: - function() { - $.ajax({ - type: "GET", - url: this.notes_path, - data: this.target_params + "&loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id), - dataType: "script"}); - }, + getNew: function() { + $.ajax({ + url: NoteList.notes_path, + data: NoteList.target_params + "&loading_new=1&after_id=" + (NoteList.reversed ? NoteList.top_id : NoteList.bottom_id), + dataType: "script" + }); + }, /** * Called in response to getNew(). * Replaces the content of #new-notes-list with the given html. + * + * Note: only used on wall. */ - replaceNewNotes: - function(html) { - $("#new-notes-list").html(html); - this.updateVotes(); - }, + replaceNewNotes: function(newNoteIds, html) { + $("#new-notes-list").html(html); + NoteList.updateVotes(); + }, /** - * Adds a single note to #new-notes-list. + * Adds a single common note to #notes-list. */ - appendNewNote: - function(id, html) { - if (this.reversed) { - $("#new-notes-list").prepend(html); - } else { - $("#new-notes-list").append(html); - } - this.updateVotes(); - }, + appendNewNote: function(id, html) { + $("#notes-list").append(html); + NoteList.updateVotes(); + }, + + /** + * Adds a single discussion note to #notes-list. + * + * Also removes the corresponding form. + */ + appendNewDiscussionNote: function(discussionId, diffRowHtml, noteHtml) { + var form = $("form[rel='"+discussionId+"']"); + var row = form.closest("tr"); + + // is this the first note of discussion? + if (row.is(".js-temp-notes-holder")) { + // insert the note and the reply button after the temp row + row.after(diffRowHtml); + // remove the note (will be added again below) + row.next().find(".note").remove(); + } + + // append new note to all matching discussions + $(".notes[rel='"+discussionId+"']").append(noteHtml); + + // cleanup after successfully creating a diff/discussion note + $.proxy(NoteList.removeDiscussionNoteForm, form).call(); + }, + + /** + * Adds a single wall note to #new-notes-list. + * + * Note: only used on wall. + */ + appendNewWallNote: function(id, html) { + $("#new-notes-list").prepend(html); + }, + + /** + * Called in response the main target form has been successfully submitted. + * + * Removes any errors. + * Resets text and preview. + * Resets buttons. + */ + resetMainTargetForm: function(){ + var form = $(this); + + // remove validation errors + form.find(".js-errors").remove(); + + // reset text and preview + var previewContainer = form.find(".js-toggler-container.note_text_and_preview"); + if (previewContainer.is(".on")) { + previewContainer.removeClass("on"); + } + form.find(".js-note-text").val("").trigger("input"); + }, + + /** + * Called after an attachment file has been selected. + * + * Updates the file name for the selected attachment. + */ + updateFormAttachment: function() { + var form = $(this).closest("form"); + + // get only the basename + var filename = $(this).val().replace(/^.*[\\\/]/, ''); + + form.find(".js-attachment-filename").text(filename); + }, /** * Recalculates the votes and updates them (if they are displayed at all). @@ -249,67 +544,24 @@ var NoteList = { * Might produce inaccurate results when not all notes have been loaded and a * recalculation is triggered (e.g. when deleting a note). */ - updateVotes: - function() { - var votes = $("#votes .votes"); - var notes = $("#notes-list, #new-notes-list").find(".note .vote"); - - // only update if there is a vote display - if (votes.size()) { - var upvotes = notes.filter(".upvote").size(); - var downvotes = notes.filter(".downvote").size(); - var votesCount = upvotes + downvotes; - var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0; - var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0; - - // change vote bar lengths - votes.find(".bar-success").css("width", upvotesPercent+"%"); - votes.find(".bar-danger").css("width", downvotesPercent+"%"); - // replace vote numbers - votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes)); - votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes)); - } + updateVotes: function() { + var votes = $("#votes .votes"); + var notes = $("#notes-list .note .vote"); + + // only update if there is a vote display + if (votes.size()) { + var upvotes = notes.filter(".upvote").size(); + var downvotes = notes.filter(".downvote").size(); + var votesCount = upvotes + downvotes; + var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0; + var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0; + + // change vote bar lengths + votes.find(".bar-success").css("width", upvotesPercent+"%"); + votes.find(".bar-danger").css("width", downvotesPercent+"%"); + // replace vote numbers + votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes)); + votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes)); } + } }; - -var PerLineNotes = { - init: - function() { - /** - * Called when clicking on the "add note" or "reply" button for a diff line. - * - * Shows the note form below the line. - * Sets some hidden fields in the form. - */ - $(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) { - var form = $(".per_line_form"); - $(this).closest("tr").after(form); - form.find("#note_line_code").val($(this).data("lineCode")); - form.show(); - e.preventDefault(); - }); - - disableButtonIfEmptyField(".line-note-text", ".submit_inline_note"); - - /** - * Called in response to successfully deleting a note on a diff line. - * - * Removes the actual note from view. - * Removes the reply button if the last note for that line has been removed. - */ - $(".diff_file_content").on("ajax:success", ".delete-note", function() { - var trNote = $(this).closest("tr"); - trNote.fadeOut(function() { - $(this).remove(); - }); - - // check if this is the last note for this line - // elements must really be removed for this to work reliably - var trLine = trNote.prev(); - var trRpl = trNote.next(); - if (trLine.is(".line_holder") && trRpl.is(".reply")) { - trRpl.fadeOut(function() { $(this).remove(); }); - } - }); - } -} diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 54690e73f81..f93246c13c7 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -45,3 +45,8 @@ @import "themes/ui_gray.scss"; @import "themes/ui_color.scss"; +/** + * Styles for JS behaviors. + */ +@import "behaviors.scss"; + diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss new file mode 100644 index 00000000000..3fdc20485f2 --- /dev/null +++ b/app/assets/stylesheets/behaviors.scss @@ -0,0 +1,14 @@ +// Details +//-------- +.js-details-container .content { display: none; } +.js-details-container .content.hide { display: block; } +.js-details-container.open .content { display: block; } +.js-details-container.open .content.hide { display: none; } + + +// Toggler +//-------- +.js-toggler-container .turn-on { display: inherit; } +.js-toggler-container .turn-off { display: none; } +.js-toggler-container.on .turn-on { display: none; } +.js-toggler-container.on .turn-off { display: inherit; } diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index dcdfcdb289c..6d4c815106b 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -556,3 +556,9 @@ h1.http_status_code { } } } + +img.emoji { + height: 20px; + vertical-align: middle; + width: 20px; +} diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss index 7ed53333f8c..1cae7b0c6b1 100644 --- a/app/assets/stylesheets/sections/commits.scss +++ b/app/assets/stylesheets/sections/commits.scss @@ -176,12 +176,14 @@ } } } - .old_line, .new_line { - margin: 0px; - padding: 0px; - border: none; - background: #EEE; - color: #666; + .new_line, + .old_line, + .notes_line { + margin:0px; + padding:0px; + border:none; + background:#EEE; + color:#666; padding: 0px 5px; border-right: 1px solid #ccc; text-align: right; @@ -191,6 +193,7 @@ moz-user-select: none; -khtml-user-select: none; user-select: none; + a { float: left; width: 35px; diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index 0c2a56d62f5..465d578f3b7 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -1,233 +1,307 @@ /** * Notes - * */ -#notes-list, -#new-notes-list { +ul.notes { display: block; list-style: none; margin: 0px; padding: 0px; -} -.issue_notes, -.wiki_notes { - .note_content { - float: left; - width: 400px; - } -} + .discussion-header, + .note-header { + @extend .cgray; + padding-top: 5px; + padding-bottom: 15px; -/* Note textare */ -#note_note { - height: 80px; - width: 99%; - font-size: 14px; -} + .avatar { + float: left; + margin-right: 10px; + } -#new_note { - .attach_holder { - display: none; + .discussion-last-update, + .note-last-update { + font-style: italic; + } + .note-author { + color: $style_color; + font-weight: bold; + &:hover { + color: $primary_color; + } + } } -} - -.preview_note { - margin: 2px; - border: 1px solid #ddd; - padding: 10px; - min-height: 60px; - background: #f5f5f5; -} -.note { - padding: 8px 0; - overflow: hidden; - display: block; - position: relative; - img {float: left; margin-right: 10px;} - img.emoji {float: none;margin: 0;} - .note-author cite{font-style: italic;} - p { color: $style_color; } - .note-author { color: $style_color;} + .discussion { + padding: 8px 0; + overflow: hidden; + display: block; + position:relative; - .note-title { margin-left: 45px; padding-top: 5px;} - .avatar { - margin-top: 3px; - } + .discussion-body { + margin-left: 50px; - .delete-note { - display: none; - position: absolute; - right: 0; - top: 0; - } + .diff_file, + .discussion-hidden, + .notes { + @extend .borders; + background-color: #F9F9F9; + } + .diff_file .notes { + /* reset */ + background: inherit; + border: none; + @include box-shadow(none); - &:hover { - .delete-note { display: block; } + } + .discussion-hidden .note { + @extend .cgray; + padding: 8px; + text-align: center; + } + .notes .note { + border-color: #ddd; + padding: 8px; + } + .reply-btn { + margin-top: 8px; + } + } } -} -#notes-list:not(.reversed) .note, -#new-notes-list:not(.reversed) .note { - border-bottom: 1px solid #eee; -} -#notes-list.reversed .note, -#new-notes-list.reversed .note { - border-top: 1px solid #eee; -} -/* mark vote notes */ -.voting_notes .note { - padding: 8px 0; -} + .note { + padding: 8px 0; + overflow: hidden; + display: block; + position:relative; + p { color: $style_color; } -.notes-status { - margin: 18px; -} + .avatar { + margin-top: 3px; + } + .attachment { + font-size: 16px; + margin-top: -20px; + .icon-attachment { + @extend .icon-paper-clip; + font-size: 24px; + position: relative; + text-align: right; + top: 6px; + } + } + .note-body { + margin-left: 45px; + padding-top: 5px; + } + .note-header { + padding-bottom: 5px; + } + } -p.notify_controls input{ - margin: 5px; + // paint top or bottom borders depending on notes direction + &:not(.reversed) .note, + &:not(.reversed) .discussion { + border-bottom: 1px solid #eee; + } + &.reversed .note, + &.reversed .discussion { + border-top: 1px solid #eee; + } } -p.notify_controls span{ - font-weight: 700; -} +.diff_file .notes_holder { + font-family: $sansFontFamily; + font-size: 13px; + line-height: 18px; -tr.line_notes_row { - border-bottom: 1px solid #DDD; - border-left: 7px solid #2A79A3; + td { + border: 1px solid #ddd; + border-left: none; - &.reply { - background: #eee; - border-left: 7px solid #2A79A3; - border-top: 1px solid #ddd; - td { - padding: 7px 10px; + &.notes_line { + text-align: center; + padding: 10px 0; } - a.line_note_reply_link { - border: 1px solid #eaeaea; - @include border-radius(4px); - padding: 3px 10px; - margin-left: 5px; - color: white; - background: #2A79A3; - border-color: #2A79A3; + &.notes_content { + background-color: $white; + border-width: 1px 0; + padding-top: 0; } } - ul { - margin: 0; - li { - padding: 0; - border: none; - } + + .reply-btn { + margin-top: 8px; } } -.line_notes_row, .per_line_form { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } -.per_line_form { - background: #f5f5f5; - border-top: 1px solid #eee; - form { margin: 0; } - td { - border-bottom: 1px solid #ddd; + +/** + * Actions for Discussions/Notes + */ + +.discussion, +.note { + &.note:hover { + .note-actions { display: block; } + } + .discussion-header:hover { + .discussion-actions { display: block; } } - .note_actions { - margin: 0; - padding-top: 10px; - .buttons { - float: left; - width: 300px; - } - .options { - .labels { - float: left; - padding-left: 10px; - label { - padding: 6px 0; - margin: 0; - width: 120px; - } + .discussion-actions, + .note-actions { + display: none; + float: right; + + [class^="icon-"], + [class*="icon-"] { + font-size: 16px; + line-height: 16px; + vertical-align: middle; + } + + a { + @extend .cgray; + + &:hover { + color: $primary_color; + &.danger { @extend .cred; } } } } } +.diff_file .note .note-actions { + right: 0; + top: 0; +} + + + +/** + * Line note button on the side of diffs + */ -td .line_note_link { - position: absolute; - margin-left:-70px; - margin-top:-10px; - z-index: 10; - background: url("comment_add.png") no-repeat left 0; - width: 32px; - height: 32px; +.diff_file tr.line_holder { + .add-diff-note { + background: url("diff_note_add.png") no-repeat left 0; + height: 22px; + margin-left: -65px; + position: absolute; + width: 22px; + z-index: 10; - opacity: 0.0; - filter: alpha(opacity=0); + // "hide" it by default + opacity: 0.0; + filter: alpha(opacity=0); - &:hover { - opacity: 1.0; - filter: alpha(opacity=100); + &:hover { + opacity: 1.0; + filter: alpha(opacity=100); + } + } + + // "show" the icon also if we just hover somwhere over the line + &:hover > td { + background: $hover !important; + + .add-diff-note { + opacity: 1.0; + filter: alpha(opacity=100); + } } } -.diff_file_content tr.line_holder:hover > td { background: $hover !important; } -.diff_file_content tr.line_holder:hover > td .line_note_link { - opacity: 1.0; - filter: alpha(opacity=100); + + +/** + * Note Form + */ + +.comment-btn, +.reply-btn { + @extend .save-btn; } +.diff_file, +.discussion { + .new_note { + margin: 8px 5px 8px 0; -.new_note { - .input-file { - font: 500px monospace; - opacity: 0; - filter: alpha(opacity=0); - position: absolute; - z-index: 1; - top: 0; - right: 0; - padding: 0; - margin: 0; + .note_options { + // because of the smaller width and the extra "cancel" button + margin-top: 8px; + } } +} +.new_note { + display: none; - .note_advanced_opts { + .buttons { + float: left; + margin-top: 8px; + } + .clearfix { + margin-bottom: 0; + } + .note_options { h6 { - line-height: 32px; - padding-right: 15px; + @extend .left; + line-height: 20px; + padding-right: 16px; + padding-bottom: 16px; + } + label { + padding: 0; } - } - .attachments { - position: relative; - width: 350px; - height: 50px; - overflow: hidden; - margin:0 0 5px !important; + .attachment { + @extend .right; + position: relative; + width: 350px; + height: 50px; + margin:0 0 5px !important; - .input_file { - .file_upload { - position: absolute; - right: 14px; - top: 7px; + // hide the actual file field + input { + display: none; } - .file_name { - line-height: 30px; - width: 240px; - height: 28px; - overflow: hidden; - } - .input-file { - width: 260px; - height: 41px; + .choose-btn { float: right; } } + .notify_options { + @extend .right; + } + } + .note_text_and_preview { + // makes the "absolute" position for links relative to this + position: relative; + + // preview/edit buttons + > a { + font-size: 24px; + padding: 4px; + position: absolute; + right: 0; + } + .note_preview { + background: #f5f5f5; + border: 1px solid #ddd; + @include border-radius(4px); + min-height: 80px; + padding: 4px 6px; + } + .note_text { + font-size: 14px; + height: 80px; + width: 98.6%; + } } } -.note-text { - border: 1px solid #aaa; - box-shadow: none; +/* loading indicator */ +.notes-busy { + margin: 18px; } |
