Commit 4d0db16f authored by Mike Greiling's avatar Mike Greiling

Prettify diff_notes diffs and droplab modules

parent 7fd4e21c
......@@ -18,52 +18,56 @@ const CommentAndResolveBtn = Vue.extend({
};
},
computed: {
showButton: function () {
showButton: function() {
if (this.discussion) {
return this.discussion.isResolvable();
} else {
return false;
}
},
isDiscussionResolved: function () {
isDiscussionResolved: function() {
return this.discussion.isResolved();
},
buttonText: function () {
buttonText: function() {
if (this.isDiscussionResolved) {
if (this.textareaIsEmpty) {
return "Unresolve discussion";
return 'Unresolve discussion';
} else {
return "Comment & unresolve discussion";
return 'Comment & unresolve discussion';
}
} else {
if (this.textareaIsEmpty) {
return "Resolve discussion";
return 'Resolve discussion';
} else {
return "Comment & resolve discussion";
}
return 'Comment & resolve discussion';
}
}
},
},
created() {
if (this.discussionId) {
this.discussion = CommentsStore.state[this.discussionId];
}
},
mounted: function () {
mounted: function() {
if (!this.discussionId) return;
const $textarea = $(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`);
const $textarea = $(
`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`,
);
this.textareaIsEmpty = $textarea.val() === '';
$textarea.on('input.comment-and-resolve-btn', () => {
this.textareaIsEmpty = $textarea.val() === '';
});
},
destroyed: function () {
destroyed: function() {
if (!this.discussionId) return;
$(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`).off('input.comment-and-resolve-btn');
}
$(`.js-discussion-note-form[data-discussion-id=${this.discussionId}] .note-textarea`).off(
'input.comment-and-resolve-btn',
);
},
});
Vue.component('comment-and-resolve-btn', CommentAndResolveBtn);
......@@ -83,7 +83,11 @@ const DiffNoteAvatars = Vue.extend({
this.addNoCommentClass();
this.setDiscussionVisible();
this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
this.lineType = $(this.$el)
.closest('.diff-line-num')
.hasClass('old_line')
? 'old'
: 'new';
});
$(document).on('toggle.comments', () => {
......@@ -113,20 +117,30 @@ const DiffNoteAvatars = Vue.extend({
addNoCommentClass() {
const { notesCount } = this;
$(this.$el).closest('.js-avatar-container')
$(this.$el)
.closest('.js-avatar-container')
.toggleClass('no-comment-btn', notesCount > 0)
.nextUntil('.js-avatar-container')
.toggleClass('no-comment-btn', notesCount > 0);
},
toggleDiscussionsToggleState() {
const $notesHolders = $(this.$el).closest('.code').find('.notes_holder');
const $notesHolders = $(this.$el)
.closest('.code')
.find('.notes_holder');
const $visibleNotesHolders = $notesHolders.filter(':visible');
const $toggleDiffCommentsBtn = $(this.$el).closest('.diff-file').find('.js-toggle-diff-comments');
const $toggleDiffCommentsBtn = $(this.$el)
.closest('.diff-file')
.find('.js-toggle-diff-comments');
$toggleDiffCommentsBtn.toggleClass('active', $notesHolders.length === $visibleNotesHolders.length);
$toggleDiffCommentsBtn.toggleClass(
'active',
$notesHolders.length === $visibleNotesHolders.length,
);
},
setDiscussionVisible() {
this.isVisible = $(`.diffs .notes[data-discussion-id="${this.discussion.id}"]`).is(':visible');
this.isVisible = $(`.diffs .notes[data-discussion-id="${this.discussion.id}"]`).is(
':visible',
);
},
getTooltipText(note) {
return `${note.authorName}: ${note.noteTruncated}`;
......
......@@ -14,24 +14,24 @@ const JumpToDiscussion = Vue.extend({
required: true,
},
},
data: function () {
data: function() {
return {
discussions: CommentsStore.state,
discussion: {},
};
},
computed: {
buttonText: function () {
buttonText: function() {
if (this.discussionId) {
return 'Jump to next unresolved discussion';
} else {
return 'Jump to first unresolved discussion';
}
},
allResolved: function () {
allResolved: function() {
return this.unresolvedDiscussionCount === 0;
},
showButton: function () {
showButton: function() {
if (this.discussionId) {
if (this.unresolvedDiscussionCount > 1) {
return true;
......@@ -42,7 +42,7 @@ const JumpToDiscussion = Vue.extend({
return this.unresolvedDiscussionCount >= 1;
}
},
lastResolvedId: function () {
lastResolvedId: function() {
let lastId;
for (const discussionId in this.discussions) {
const discussion = this.discussions[discussionId];
......@@ -52,13 +52,13 @@ const JumpToDiscussion = Vue.extend({
}
}
return lastId;
}
},
},
created() {
this.discussion = this.discussions[this.discussionId];
},
methods: {
jumpToNextUnresolvedDiscussion: function () {
jumpToNextUnresolvedDiscussion: function() {
let discussionsSelector;
let discussionIdsInScope;
let firstUnresolvedDiscussionId;
......@@ -68,9 +68,11 @@ const JumpToDiscussion = Vue.extend({
let jumpToFirstDiscussion = !this.discussionId;
const discussionIdsForElements = function(elements) {
return elements.map(function() {
return elements
.map(function() {
return $(this).attr('data-discussion-id');
}).toArray();
})
.toArray();
};
const { discussions } = this;
......@@ -144,8 +146,7 @@ const JumpToDiscussion = Vue.extend({
if (!discussion.isResolved()) {
nextUnresolvedDiscussionId = discussionId;
break;
}
else {
} else {
continue;
}
}
......@@ -175,9 +176,9 @@ const JumpToDiscussion = Vue.extend({
// Resolved discussions are hidden in the diffs tab by default.
// If they are marked unresolved on the notes tab, they will still be hidden on the diffs tab.
// When jumping between unresolved discussions on the diffs tab, we show them.
$target.closest(".content").show();
$target.closest('.content').show();
const $notesHolder = $target.closest("tr.notes_holder");
const $notesHolder = $target.closest('tr.notes_holder');
// Image diff discussions does not use notes_holder
// so we should keep original $target value in those cases
......@@ -194,7 +195,7 @@ const JumpToDiscussion = Vue.extend({
prevEl = $target.prev();
// If the discussion doesn't have 4 lines above it, we'll have to do with fewer.
if (!prevEl.hasClass("line_holder")) {
if (!prevEl.hasClass('line_holder')) {
break;
}
......@@ -203,9 +204,9 @@ const JumpToDiscussion = Vue.extend({
}
$.scrollTo($target, {
offset: -150
offset: -150,
});
}
},
},
});
......
......@@ -13,17 +13,17 @@ window.ResolveCount = Vue.extend({
required: true,
},
},
data: function () {
data: function() {
return {
discussions: CommentsStore.state
discussions: CommentsStore.state,
};
},
computed: {
allResolved: function () {
allResolved: function() {
return this.resolvedDiscussionCount === this.discussionCount;
},
resolvedCountText() {
return this.discussionCount === 1 ? 'discussion' : 'discussions';
}
}
},
},
});
......@@ -2,10 +2,10 @@
const DiscussionMixins = {
computed: {
discussionCount: function () {
discussionCount: function() {
return Object.keys(this.discussions).length;
},
resolvedDiscussionCount: function () {
resolvedDiscussionCount: function() {
let resolvedCount = 0;
for (const discussionId in this.discussions) {
......@@ -18,7 +18,7 @@ const DiscussionMixins = {
return resolvedCount;
},
unresolvedDiscussionCount: function () {
unresolvedDiscussionCount: function() {
let unresolvedCount = 0;
for (const discussionId in this.discussions) {
......@@ -30,8 +30,8 @@ const DiscussionMixins = {
}
return unresolvedCount;
}
}
},
},
};
export default DiscussionMixins;
......@@ -6,22 +6,22 @@ import Vue from 'vue';
import { localTimeAgo } from '../../lib/utils/datetime_utility';
class DiscussionModel {
constructor (discussionId) {
constructor(discussionId) {
this.id = discussionId;
this.notes = {};
this.loading = false;
this.canResolve = false;
}
createNote (noteObj) {
createNote(noteObj) {
Vue.set(this.notes, noteObj.noteId, new NoteModel(this.id, noteObj));
}
deleteNote (noteId) {
deleteNote(noteId) {
Vue.delete(this.notes, noteId);
}
getNote (noteId) {
getNote(noteId) {
return this.notes[noteId];
}
......@@ -29,7 +29,7 @@ class DiscussionModel {
return Object.keys(this.notes).length;
}
isResolved () {
isResolved() {
for (const noteId in this.notes) {
const note = this.notes[noteId];
......@@ -40,7 +40,7 @@ class DiscussionModel {
return true;
}
resolveAllNotes (resolved_by) {
resolveAllNotes(resolved_by) {
for (const noteId in this.notes) {
const note = this.notes[noteId];
......@@ -51,7 +51,7 @@ class DiscussionModel {
}
}
unResolveAllNotes () {
unResolveAllNotes() {
for (const noteId in this.notes) {
const note = this.notes[noteId];
......@@ -62,7 +62,7 @@ class DiscussionModel {
}
}
updateHeadline (data) {
updateHeadline(data) {
const discussionSelector = `.discussion[data-discussion-id="${this.id}"]`;
const $discussionHeadline = $(`${discussionSelector} .js-discussion-headline`);
......@@ -79,7 +79,7 @@ class DiscussionModel {
}
}
isResolvable () {
isResolvable() {
if (!this.canResolve) {
return false;
}
......
......@@ -5,10 +5,10 @@ import Vue from 'vue';
window.CommentsStore = {
state: {},
get: function (discussionId, noteId) {
get: function(discussionId, noteId) {
return this.state[discussionId].getNote(noteId);
},
createDiscussion: function (discussionId, canResolve) {
createDiscussion: function(discussionId, canResolve) {
let discussion = this.state[discussionId];
if (!this.state[discussionId]) {
discussion = new DiscussionModel(discussionId);
......@@ -21,18 +21,18 @@ window.CommentsStore = {
return discussion;
},
create: function (noteObj) {
create: function(noteObj) {
const discussion = this.createDiscussion(noteObj.discussionId);
discussion.createNote(noteObj);
},
update: function (discussionId, noteId, resolved, resolved_by) {
update: function(discussionId, noteId, resolved, resolved_by) {
const discussion = this.state[discussionId];
const note = discussion.getNote(noteId);
note.resolved = resolved;
note.resolved_by = resolved_by;
},
delete: function (discussionId, noteId) {
delete: function(discussionId, noteId) {
const discussion = this.state[discussionId];
discussion.deleteNote(noteId);
......@@ -40,7 +40,7 @@ window.CommentsStore = {
Vue.delete(this.state, discussionId);
}
},
unresolvedDiscussionIds: function () {
unresolvedDiscussionIds: function() {
const ids = [];
for (const discussionId in this.state) {
......@@ -52,5 +52,5 @@ window.CommentsStore = {
}
return ids;
}
},
};
......@@ -43,7 +43,9 @@ export default {
return (this.commit.author && this.commit.author.name) || this.commit.authorName;
},
authorUrl() {
return (this.commit.author && this.commit.author.webUrl) || `mailto:${this.commit.authorEmail}`;
return (
(this.commit.author && this.commit.author.webUrl) || `mailto:${this.commit.authorEmail}`
);
},
authorAvatar() {
return (this.commit.author && this.commit.author.avatarUrl) || this.commit.authorGravatarUrl;
......
......@@ -46,10 +46,10 @@ export default {
showExpandMessage() {
return (
this.isCollapsed ||
!this.file.highlightedDiffLines &&
(!this.file.highlightedDiffLines &&
!this.isLoadingCollapsedDiff &&
!this.file.tooLarge &&
this.file.text
this.file.text)
);
},
showLoadingIcon() {
......
......@@ -6,11 +6,4 @@ const IGNORE_CLASS = 'droplab-item-ignore';
// Matches `{{anything}}` and `{{ everything }}`.
const TEMPLATE_REGEX = /\{\{(.+?)\}\}/g;
export {
DATA_TRIGGER,
DATA_DROPDOWN,
SELECTED_CLASS,
ACTIVE_CLASS,
TEMPLATE_REGEX,
IGNORE_CLASS,
};
export { DATA_TRIGGER, DATA_DROPDOWN, SELECTED_CLASS, ACTIVE_CLASS, TEMPLATE_REGEX, IGNORE_CLASS };
......@@ -2,7 +2,7 @@ import utils from './utils';
import { SELECTED_CLASS, IGNORE_CLASS } from './constants';
class DropDown {
constructor(list, config = { }) {
constructor(list, config = {}) {
this.currentIndex = 0;
this.hidden = true;
this.list = typeof list === 'string' ? document.querySelector(list) : list;
......@@ -157,7 +157,7 @@ class DropDown {
static setImagesSrc(template) {
const images = [...template.querySelectorAll('img[data-src]')];
images.forEach((image) => {
images.forEach(image => {
const img = image;
img.src = img.getAttribute('data-src');
......
......@@ -51,7 +51,7 @@ class DropLab {
}
processData(trigger, data, methodName) {
this.hooks.forEach((hook) => {
this.hooks.forEach(hook => {
if (Array.isArray(trigger)) hook.list[methodName](trigger);
if (hook.trigger.id === trigger) hook.list[methodName](data);
......@@ -78,7 +78,8 @@ class DropLab {
}
changeHookList(trigger, list, plugins, config) {
const availableTrigger = typeof trigger === 'string' ? document.getElementById(trigger) : trigger;
const availableTrigger =
typeof trigger === 'string' ? document.getElementById(trigger) : trigger;
this.hooks.forEach((hook, i) => {
const aHook = hook;
......
......@@ -2,15 +2,18 @@
import { ACTIVE_CLASS } from './constants';
const Keyboard = function () {
const Keyboard = function() {
var currentKey;
var currentFocus;
var isUpArrow = false;
var isDownArrow = false;
var removeHighlight = function removeHighlight(list) {
var itemElements = Array.prototype.slice.call(list.list.querySelectorAll('li:not(.divider):not(.hidden)'), 0);
var itemElements = Array.prototype.slice.call(
list.list.querySelectorAll('li:not(.divider):not(.hidden)'),
0,
);
var listItems = [];
for(var i = 0; i < itemElements.length; i++) {
for (var i = 0; i < itemElements.length; i++) {
var listItem = itemElements[i];
listItem.classList.remove(ACTIVE_CLASS);
......@@ -23,13 +26,13 @@ const Keyboard = function () {
var setMenuForArrows = function setMenuForArrows(list) {
var listItems = removeHighlight(list);
if(list.currentIndex>0){
if(!listItems[list.currentIndex-1]){
list.currentIndex = list.currentIndex-1;
if (list.currentIndex > 0) {
if (!listItems[list.currentIndex - 1]) {
list.currentIndex = list.currentIndex - 1;
}
if (listItems[list.currentIndex-1]) {
var el = listItems[list.currentIndex-1];
if (listItems[list.currentIndex - 1]) {
var el = listItems[list.currentIndex - 1];
var filterDropdownEl = el.closest('.filter-dropdown');
el.classList.add(ACTIVE_CLASS);
......@@ -55,7 +58,7 @@ const Keyboard = function () {
};
var selectItem = function selectItem(list) {
var listItems = removeHighlight(list);
var currentItem = listItems[list.currentIndex-1];
var currentItem = listItems[list.currentIndex - 1];
var listEvent = new CustomEvent('click.dl', {
detail: {
list: list,
......@@ -65,43 +68,49 @@ const Keyboard = function () {
});
list.list.dispatchEvent(listEvent);
list.hide();
}
};
var keydown = function keydown(e){
var keydown = function keydown(e) {
var typedOn = e.target;
var list = e.detail.hook.list;
var currentIndex = list.currentIndex;
isUpArrow = false;
isDownArrow = false;
if(e.detail.which){
if (e.detail.which) {
currentKey = e.detail.which;
if(currentKey === 13){
if (currentKey === 13) {
selectItem(e.detail.hook.list);
return;
}
if(currentKey === 38) {
if (currentKey === 38) {
isUpArrow = true;
}
if(currentKey === 40) {
if (currentKey === 40) {
isDownArrow = true;
}
} else if(e.detail.key) {
} else if (e.detail.key) {
currentKey = e.detail.key;
if(currentKey === 'Enter'){
if (currentKey === 'Enter') {
selectItem(e.detail.hook.list);
return;
}
if(currentKey === 'ArrowUp') {
if (currentKey === 'ArrowUp') {
isUpArrow = true;
}
if(currentKey === 'ArrowDown') {
if (currentKey === 'ArrowDown') {
isDownArrow = true;
}
}
if(isUpArrow){ currentIndex--; }
if(isDownArrow){ currentIndex++; }
if(currentIndex < 0){ currentIndex = 0; }
if (isUpArrow) {
currentIndex--;
}
if (isDownArrow) {
currentIndex++;
}
if (currentIndex < 0) {
currentIndex = 0;
}
list.currentIndex = currentIndex;
setMenuForArrows(e.detail.hook.list);
};
......
......@@ -43,12 +43,12 @@ const Ajax = {
return AjaxCache.retrieve(config.endpoint)
.then(self.preprocessing.bind(null, config))
.then((data) => self._loadData(data, config, self))
.then(data => self._loadData(data, config, self))
.catch(config.onError);
},
destroy: function() {
this.destroyed = true;
}
},
};
export default Ajax;
......@@ -41,8 +41,10 @@ const AjaxFilter = {
if (config.searchValueFunction) {
searchValue = config.searchValueFunction();
}
if (config.loadingTemplate && this.hook.list.data === undefined ||
this.hook.list.data.length === 0) {
if (
(config.loadingTemplate && this.hook.list.data === undefined) ||
this.hook.list.data.length === 0
) {
var dynamicList = this.hook.list.list.querySelector('[data-dynamic]');
var loadingTemplate = document.createElement('div');
loadingTemplate.innerHTML = config.loadingTemplate;
......@@ -61,7 +63,7 @@ const AjaxFilter = {
params[config.searchKey] = searchValue;
var url = config.endpoint + this.buildParams(params);
return AjaxCache.retrieve(url)
.then((data) => {
.then(data => {
this._loadData(data, config);
if (config.onLoadingFinished) {
config.onLoadingFinished(data);
......@@ -72,8 +74,7 @@ const AjaxFilter = {
_loadData(data, config) {
const list = this.hook.list;
if (config.loadingTemplate && list.data === undefined ||
list.data.length === 0) {
if ((config.loadingTemplate && list.data === undefined) || list.data.length === 0) {
const dataLoadingTemplate = list.list.querySelector('[data-loading-template]');
if (dataLoadingTemplate) {
dataLoadingTemplate.outerHTML = this.listTemplate;
......@@ -81,7 +82,8 @@ const AjaxFilter = {
}
if (!this.destroyed) {
var hookListChildren = list.list.children;
var onlyDynamicList = hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic');
var onlyDynamicList =
hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic');
if (onlyDynamicList && data.length === 0) {
list.hide();
}
......@@ -100,12 +102,12 @@ const AjaxFilter = {
},
destroy: function destroy() {
if (this.timeout)clearTimeout(this.timeout);
if (this.timeout) clearTimeout(this.timeout);
this.destroyed = true;
this.hook.trigger.removeEventListener('keydown.dl', this.eventWrapper.debounceTrigger);
this.hook.trigger.removeEventListener('focus', this.eventWrapper.debounceTrigger);
}
},
};
export default AjaxFilter;
/* eslint-disable */
const Filter = {
keydown: function(e){
keydown: function(e) {
if (this.destroyed) return;
var hiddenCount = 0;
......@@ -14,14 +14,14 @@ const Filter = {
var matches = [];
var filterFunction;
// will only work on dynamically set data
if(!data){
if (!data) {
return;
}
if (config && config.filterFunction && typeof config.filterFunction === 'function') {
filterFunction = config.filterFunction;
} else {
filterFunction = function(o){
filterFunction = function(o) {
// cheap string search
o.droplab_hidden = o[config.template].toLowerCase().indexOf(value) === -1;
return o;
......@@ -47,7 +47,8 @@ const Filter = {
},
debounceKeydown: function debounceKeydown(e) {
if ([
if (
[
13, // enter
16, // shift
17, // ctrl
......@@ -60,7 +61,9 @@ const Filter = {
91, // left window
92, // right window
93, // select
].indexOf(e.detail.which || e.detail.keyCode) > -1) return;
].indexOf(e.detail.which || e.detail.keyCode) > -1
)
return;
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(this.keydown.bind(this, e), 200);
......@@ -87,7 +90,7 @@ const Filter = {
this.hook.trigger.removeEventListener('keydown.dl', this.eventWrapper.debounceKeydown);
this.hook.trigger.removeEventListener('mousedown.dl', this.eventWrapper.debounceKeydown);
}
},
};
export default Filter;
......@@ -36,8 +36,8 @@ const InputSetter = {
const inputAttribute = config.inputAttribute;
if (input.hasAttribute(inputAttribute)) return input.setAttribute(inputAttribute, newValue);
if (input.tagName === 'INPUT') return input.value = newValue;
return input.textContent = newValue;
if (input.tagName === 'INPUT') return (input.value = newValue);
return (input.textContent = newValue);
},
destroy() {
......
......@@ -5,7 +5,12 @@ import { DATA_TRIGGER, DATA_DROPDOWN, TEMPLATE_REGEX } from './constants';
const utils = {
toCamelCase(attr) {
return this.camelize(attr.split('-').slice(1).join(' '));
return this.camelize(
attr
.split('-')
.slice(1)
.join(' '),
);
},
template(templateString, data) {
......@@ -17,9 +22,11 @@ const utils = {
},
camelize(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => {
return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => {
return index === 0 ? letter.toLowerCase() : letter.toUpperCase();
}).replace(/\s+/g, '');
})
.replace(/\s+/g, '');
},
closest(thisTag, stopTag) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment