Skip to content
Snippets Groups Projects
Unverified Commit f7df9ddb authored by Phil Hughes's avatar Phil Hughes
Browse files

Re-implemented image commenting on diffs

This re-implements image commenting in merge request diffs.
This feature was previously lost when the merge request
page was refactored into Vue.

With this, we create an overlay component. The overlay
component handles displaying the comment badges
and the comment form badge.
Badges are displayed based on the position attribute
sent with the discussion.

Comment forms for diff files are controlled through
a different state property. This is so we don't
tie comment forms to diff files directly creating
deep nested state. Instead we create a flat array
which holds the file hash & the X & Y position of
the comment form.

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/48956
parent 7d4b717c
No related branches found
No related tags found
No related merge requests found
Showing
with 467 additions and 124 deletions
Loading
Loading
@@ -223,7 +223,10 @@ export default {
:commit="commit"
/>
 
<div class="files d-flex prepend-top-default">
<div
:data-can-create-note="getNoteableData.current_user.can_create_note"
class="files d-flex prepend-top-default"
>
<div
v-show="showTreeList"
class="diff-tree-list"
Loading
Loading
<script>
import { mapGetters, mapState } from 'vuex';
import { mapActions, mapGetters, mapState } from 'vuex';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import { diffModes } from '~/ide/constants';
import InlineDiffView from './inline_diff_view.vue';
import ParallelDiffView from './parallel_diff_view.vue';
import NoteForm from '../../notes/components/note_form.vue';
import ImageDiffOverlay from './image_diff_overlay.vue';
import DiffDiscussions from './diff_discussions.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '../constants';
import { getDiffMode } from '../store/utils';
 
export default {
components: {
InlineDiffView,
ParallelDiffView,
DiffViewer,
NoteForm,
DiffDiscussions,
ImageDiffOverlay,
},
props: {
diffFile: {
Loading
Loading
@@ -23,13 +30,36 @@ export default {
endpoint: state => state.diffs.endpoint,
}),
...mapGetters('diffs', ['isInlineView', 'isParallelView']),
...mapGetters('diffs', ['getCommentFormForDiffFile']),
...mapGetters(['getNoteableData', 'noteableType']),
diffMode() {
const diffModeKey = Object.keys(diffModes).find(key => this.diffFile[`${key}File`]);
return diffModes[diffModeKey] || diffModes.replaced;
return getDiffMode(this.diffFile);
},
isTextFile() {
return this.diffFile.viewer.name === 'text';
},
diffFileCommentForm() {
return this.getCommentFormForDiffFile(this.diffFile.fileHash);
},
showNotesContainer() {
return this.diffFile.discussions.length || this.diffFileCommentForm;
},
},
methods: {
...mapActions('diffs', ['saveDiffDiscussion', 'closeDiffFileCommentForm']),
handleSaveNote(note) {
this.saveDiffDiscussion({
note,
formData: {
noteableData: this.getNoteableData,
noteableType: this.noteableType,
diffFile: this.diffFile,
positionType: IMAGE_DIFF_POSITION_TYPE,
x: this.diffFileCommentForm.x,
y: this.diffFileCommentForm.y,
},
});
},
},
};
</script>
Loading
Loading
@@ -56,7 +86,37 @@ export default {
:new-sha="diffFile.diffRefs.headSha"
:old-path="diffFile.oldPath"
:old-sha="diffFile.diffRefs.baseSha"
:project-path="projectPath"/>
:file-hash="diffFile.fileHash"
:project-path="projectPath"
>
<image-diff-overlay
slot="image-overlay"
:discussions="diffFile.discussions"
:file-hash="diffFile.fileHash"
:can-comment="getNoteableData.current_user.can_create_note"
/>
<div
v-if="showNotesContainer"
class="note-container"
>
<diff-discussions
v-if="diffFile.discussions.length"
class="diff-file-discussions"
:discussions="diffFile.discussions"
:should-collapse-discussions="true"
:render-avatar-badge="true"
/>
<note-form
v-if="diffFileCommentForm"
ref="noteForm"
:is-editing="false"
:save-button-title="__('Comment')"
class="diff-comment-form new-note discussion-form discussion-form-container"
@handleFormUpdate="handleSaveNote"
@cancelForm="closeDiffFileCommentForm(diffFile.fileHash)"
/>
</div>
</diff-viewer>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import noteableDiscussion from '../../notes/components/noteable_discussion.vue';
 
export default {
components: {
noteableDiscussion,
Icon,
},
props: {
discussions: {
type: Array,
required: true,
},
shouldCollapseDiscussions: {
type: Boolean,
required: false,
default: false,
},
renderAvatarBadge: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
...mapActions(['toggleDiscussion']),
...mapActions('diffs', ['removeDiscussionsFromDiff']),
deleteNoteHandler(discussion) {
if (discussion.notes.length <= 1) {
this.removeDiscussionsFromDiff(discussion);
}
},
isExpanded(discussion) {
return this.shouldCollapseDiscussions ? discussion.expanded : true;
},
},
};
</script>
Loading
Loading
@@ -26,22 +42,54 @@ export default {
<template>
<div>
<div
v-for="discussion in discussions"
v-for="(discussion, index) in discussions"
:key="discussion.id"
class="discussion-notes diff-discussions"
:class="{
collapsed: !isExpanded(discussion)
}"
class="discussion-notes diff-discussions position-relative"
>
<ul
:data-discussion-id="discussion.id"
class="notes"
>
<template v-if="shouldCollapseDiscussions">
<button
:class="{
'diff-notes-collapse': discussion.expanded,
'btn-transparent badge badge-pill': !discussion.expanded
}"
type="button"
class="js-diff-notes-toggle"
@click="toggleDiscussion({ discussionId: discussion.id })"
>
<icon
v-if="discussion.expanded"
name="collapse"
class="collapse-icon"
/>
<template v-else>
{{ index + 1 }}
</template>
</button>
</template>
<noteable-discussion
v-show="isExpanded(discussion)"
:discussion="discussion"
:render-header="false"
:render-diff-file="false"
:always-expanded="true"
:discussions-by-diff-order="true"
@noteDeleted="deleteNoteHandler"
/>
>
<span
v-if="renderAvatarBadge"
slot="avatar-badge"
class="badge badge-pill"
>
{{ index + 1 }}
</span>
</noteable-discussion>
</ul>
</div>
</div>
Loading
Loading
<script>
import { mapActions, mapGetters } from 'vuex';
import _ from 'underscore';
import Icon from '~/vue_shared/components/icon.vue';
export default {
name: 'ImageDiffOverlay',
components: {
Icon,
},
props: {
discussions: {
type: [Array, Object],
required: true,
},
fileHash: {
type: String,
required: true,
},
canComment: {
type: Boolean,
required: false,
default: false,
},
showCommentIcon: {
type: Boolean,
required: false,
default: false,
},
badgeClass: {
type: String,
required: false,
default: 'badge badge-pill',
},
shouldToggleDiscussion: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
...mapGetters('diffs', ['getDiffFileByHash', 'getCommentFormForDiffFile']),
currentCommentForm() {
return this.getCommentFormForDiffFile(this.fileHash);
},
allDiscussions() {
return _.isArray(this.discussions) ? this.discussions : [this.discussions];
},
},
methods: {
...mapActions(['toggleDiscussion']),
...mapActions('diffs', ['openDiffFileCommentForm']),
getPosition(discussion) {
return {
left: `${discussion.position.x}px`,
top: `${discussion.position.y}px`,
};
},
clickedImage(x, y) {
this.openDiffFileCommentForm({
fileHash: this.fileHash,
x,
y,
});
},
},
};
</script>
<template>
<div class="position-absolute w-100 h-100 image-diff-overlay">
<button
v-if="canComment"
type="button"
class="btn-transparent position-absolute image-diff-overlay-add-comment w-100 h-100 js-add-image-diff-note-button"
@click="clickedImage($event.offsetX, $event.offsetY)"
>
<span class="sr-only">
{{ __('Add image comment') }}
</span>
</button>
<button
v-for="(discussion, index) in allDiscussions"
:key="discussion.id"
:style="getPosition(discussion)"
:class="badgeClass"
:disabled="!shouldToggleDiscussion"
class="js-image-badge"
type="button"
@click="toggleDiscussion({ discussionId: discussion.id })"
>
<icon
v-if="showCommentIcon"
name="image-comment-dark"
/>
<template v-else>
{{ index + 1 }}
</template>
</button>
<button
v-if="currentCommentForm"
:style="{
left: `${currentCommentForm.x}px`,
top: `${currentCommentForm.y}px`
}"
:aria-label="__('Comment form position')"
class="btn-transparent comment-indicator"
type="button"
>
<icon
name="image-comment-dark"
/>
</button>
</div>
</template>
Loading
Loading
@@ -12,6 +12,7 @@ export const NOTE_TYPE = 'Note';
export const NEW_LINE_TYPE = 'new';
export const OLD_LINE_TYPE = 'old';
export const TEXT_DIFF_POSITION_TYPE = 'text';
export const IMAGE_DIFF_POSITION_TYPE = 'image';
 
export const LINE_POSITION_LEFT = 'left';
export const LINE_POSITION_RIGHT = 'right';
Loading
Loading
Loading
Loading
@@ -50,8 +50,8 @@ export const assignDiscussionsToDiff = (
};
 
export const removeDiscussionsFromDiff = ({ commit }, removeDiscussion) => {
const { fileHash, line_code } = removeDiscussion;
commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash, lineCode: line_code });
const { fileHash, line_code, id } = removeDiscussion;
commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash, lineCode: line_code, id });
};
 
export const startRenderDiffsQueue = ({ state, commit }) => {
Loading
Loading
@@ -189,6 +189,7 @@ export const saveDiffDiscussion = ({ dispatch }, { note, formData }) => {
return dispatch('saveNote', postData, { root: true })
.then(result => dispatch('updateDiscussion', result.discussion, { root: true }))
.then(discussion => dispatch('assignDiscussionsToDiff', [discussion]))
.then(() => dispatch('closeDiffFileCommentForm', formData.diffFile.fileHash))
.catch(() => createFlash(s__('MergeRequests|Saving the comment failed')));
};
 
Loading
Loading
@@ -210,5 +211,19 @@ export const toggleShowTreeList = ({ commit, state }) => {
localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList);
};
 
export const openDiffFileCommentForm = ({ commit, getters }, formData) => {
const form = getters.getCommentFormForDiffFile(formData.fileHash);
if (form) {
commit(types.UPDATE_DIFF_FILE_COMMENT_FORM, formData);
} else {
commit(types.OPEN_DIFF_FILE_COMMENT_FORM, formData);
}
};
export const closeDiffFileCommentForm = ({ commit }, fileHash) => {
commit(types.CLOSE_DIFF_FILE_COMMENT_FORM, fileHash);
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
Loading
Loading
@@ -114,5 +114,8 @@ export const allBlobs = state => Object.values(state.treeEntries).filter(f => f.
 
export const diffFilesLength = state => state.diffFiles.length;
 
export const getCommentFormForDiffFile = state => fileHash =>
state.commentForms.find(form => form.fileHash === fileHash);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
Loading
Loading
@@ -24,4 +24,6 @@ export default () => ({
showTreeList:
storedTreeShow === null ? bp.getBreakpointSize() !== 'xs' : storedTreeShow === 'true',
currentDiffFileId: '',
projectPath: '',
commentForms: [],
});
Loading
Loading
@@ -14,3 +14,7 @@ export const REMOVE_LINE_DISCUSSIONS_FOR_FILE = 'REMOVE_LINE_DISCUSSIONS_FOR_FIL
export const TOGGLE_FOLDER_OPEN = 'TOGGLE_FOLDER_OPEN';
export const TOGGLE_SHOW_TREE_LIST = 'TOGGLE_SHOW_TREE_LIST';
export const UPDATE_CURRENT_DIFF_FILE_ID = 'UPDATE_CURRENT_DIFF_FILE_ID';
export const OPEN_DIFF_FILE_COMMENT_FORM = 'OPEN_DIFF_FILE_COMMENT_FORM';
export const UPDATE_DIFF_FILE_COMMENT_FORM = 'UPDATE_DIFF_FILE_COMMENT_FORM';
export const CLOSE_DIFF_FILE_COMMENT_FORM = 'CLOSE_DIFF_FILE_COMMENT_FORM';
Loading
Loading
@@ -153,20 +153,22 @@ export default {
});
},
 
[types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode }) {
[types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode, id }) {
const selectedFile = state.diffFiles.find(f => f.fileHash === fileHash);
if (selectedFile) {
const targetLine = selectedFile.parallelDiffLines.find(
line =>
(line.left && line.left.lineCode === lineCode) ||
(line.right && line.right.lineCode === lineCode),
);
if (targetLine) {
const side = targetLine.left && targetLine.left.lineCode === lineCode ? 'left' : 'right';
Object.assign(targetLine[side], {
discussions: [],
});
if (selectedFile.parallelDiffLines) {
const targetLine = selectedFile.parallelDiffLines.find(
line =>
(line.left && line.left.lineCode === lineCode) ||
(line.right && line.right.lineCode === lineCode),
);
if (targetLine) {
const side = targetLine.left && targetLine.left.lineCode === lineCode ? 'left' : 'right';
Object.assign(targetLine[side], {
discussions: [],
});
}
}
 
if (selectedFile.highlightedDiffLines) {
Loading
Loading
@@ -180,6 +182,12 @@ export default {
});
}
}
if (selectedFile.discussions && selectedFile.discussions.length) {
selectedFile.discussions = selectedFile.discussions.filter(
discussion => discussion.id !== id,
);
}
}
},
[types.TOGGLE_FOLDER_OPEN](state, path) {
Loading
Loading
@@ -191,4 +199,25 @@ export default {
[types.UPDATE_CURRENT_DIFF_FILE_ID](state, fileId) {
state.currentDiffFileId = fileId;
},
[types.OPEN_DIFF_FILE_COMMENT_FORM](state, formData) {
state.commentForms.push({
...formData,
});
},
[types.UPDATE_DIFF_FILE_COMMENT_FORM](state, formData) {
const { fileHash } = formData;
state.commentForms = state.commentForms.map(form => {
if (form.fileHash === fileHash) {
return {
...formData,
};
}
return form;
});
},
[types.CLOSE_DIFF_FILE_COMMENT_FORM](state, fileHash) {
state.commentForms = state.commentForms.filter(form => form.fileHash !== fileHash);
},
};
import _ from 'underscore';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { diffModes } from '~/ide/constants';
import {
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
Loading
Loading
@@ -34,6 +35,7 @@ export function getFormData(params) {
noteTargetLine,
diffViewType,
linePosition,
positionType,
} = params;
 
const position = JSON.stringify({
Loading
Loading
@@ -42,9 +44,11 @@ export function getFormData(params) {
head_sha: diffFile.diffRefs.headSha,
old_path: diffFile.oldPath,
new_path: diffFile.newPath,
position_type: TEXT_DIFF_POSITION_TYPE,
old_line: noteTargetLine.oldLine,
new_line: noteTargetLine.newLine,
position_type: positionType || TEXT_DIFF_POSITION_TYPE,
old_line: noteTargetLine ? noteTargetLine.oldLine : null,
new_line: noteTargetLine ? noteTargetLine.newLine : null,
x: params.x,
y: params.y,
});
 
const postData = {
Loading
Loading
@@ -66,7 +70,7 @@ export function getFormData(params) {
diffFile.diffRefs.startSha && diffFile.diffRefs.headSha
? DIFF_NOTE_TYPE
: LEGACY_DIFF_NOTE_TYPE,
line_code: noteTargetLine.lineCode,
line_code: noteTargetLine ? noteTargetLine.lineCode : null,
},
};
 
Loading
Loading
@@ -225,6 +229,7 @@ export function prepareDiffData(diffData) {
Object.assign(file, {
renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
collapsed: file.text && showingLines > MAX_LINES_TO_BE_RENDERED,
discussions: [],
});
}
}
Loading
Loading
@@ -320,3 +325,8 @@ export const generateTreeList = files =>
},
{ treeEntries: {}, tree: [] },
);
export const getDiffMode = diffFile => {
const diffModeKey = Object.keys(diffModes).find(key => diffFile[`${key}File`]);
return diffModes[diffModeKey] || diffModes.replaced;
};
Loading
Loading
@@ -11,7 +11,6 @@ import bp from './breakpoints';
import { parseUrlPathname, handleLocationHash, isMetaClick } from './lib/utils/common_utils';
import { isInVueNoteablePage } from './lib/utils/dom_utils';
import { getLocationHash } from './lib/utils/url_utility';
import initDiscussionTab from './image_diff/init_discussion_tab';
import Diff from './diff';
import { localTimeAgo } from './lib/utils/datetime_utility';
import syntaxHighlight from './syntax_highlight';
Loading
Loading
@@ -207,8 +206,6 @@ export default class MergeRequestTabs {
}
this.resetViewContainer();
this.destroyPipelinesView();
initDiscussionTab();
}
if (this.setUrl) {
this.setCurrentAction(action);
Loading
Loading
<script>
import { mapState, mapActions } from 'vuex';
import imageDiffHelper from '~/image_diff/helpers/index';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import { GlSkeletonLoading } from '@gitlab-org/gitlab-ui';
import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
import { trimFirstCharOfLineContent, getDiffMode } from '~/diffs/store/utils';
 
export default {
components: {
DiffFileHeader,
GlSkeletonLoading,
DiffViewer,
ImageDiffOverlay,
},
props: {
discussion: {
Loading
Loading
@@ -25,7 +28,11 @@ export default {
computed: {
...mapState({
noteableData: state => state.notes.noteableData,
projectPath: state => state.diffs.projectPath,
}),
diffMode() {
return getDiffMode(this.diffFile);
},
hasTruncatedDiffLines() {
return this.discussion.truncatedDiffLines && this.discussion.truncatedDiffLines.length !== 0;
},
Loading
Loading
@@ -62,11 +69,7 @@ export default {
},
},
mounted() {
if (this.isImageDiff) {
const canCreateNote = false;
const renderCommentBadge = true;
imageDiffHelper.initImageDiff(this.$refs.fileHolder, canCreateNote, renderCommentBadge);
} else if (!this.hasTruncatedDiffLines) {
if (!this.hasTruncatedDiffLines) {
this.fetchDiff();
}
},
Loading
Loading
@@ -160,7 +163,24 @@ export default {
<div
v-else
>
<div v-html="imageDiffHtml"></div>
<diff-viewer
:diff-mode="diffMode"
:new-path="diffFile.newPath"
:new-sha="diffFile.diffRefs.headSha"
:old-path="diffFile.oldPath"
:old-sha="diffFile.diffRefs.baseSha"
:file-hash="diffFile.fileHash"
:project-path="projectPath"
>
<image-diff-overlay
slot="image-overlay"
:discussions="discussion"
:file-hash="diffFile.fileHash"
:show-comment-icon="true"
:should-toggle-discussion="false"
badge-class="image-comment-badge"
/>
</diff-viewer>
<slot></slot>
</div>
</div>
Loading
Loading
Loading
Loading
@@ -350,11 +350,18 @@ Please check your network connection and try again.`;
<ul class="notes">
<component
:is="componentName(note)"
v-for="note in discussion.notes"
v-for="(note, index) in discussion.notes"
:key="note.id"
:note="componentData(note)"
@handleDeleteNote="deleteNoteHandler"
/>
>
<slot
v-if="index === 0"
slot="avatar-badge"
name="avatar-badge"
>
</slot>
</component>
</ul>
<div
:class="{ 'is-replying': isReplying }"
Loading
Loading
Loading
Loading
@@ -182,7 +182,13 @@ export default {
:img-src="author.avatar_url"
:img-alt="author.name"
:img-size="40"
/>
>
<slot
slot="avatar-badge"
name="avatar-badge"
>
</slot>
</user-avatar-link>
</div>
<div class="timeline-content">
<div class="note-header">
Loading
Loading
Loading
Loading
@@ -17,19 +17,37 @@ export default {
type: Boolean,
default: true,
},
innerCssClasses: {
type: [Array, Object, String],
required: false,
default: '',
},
},
data() {
return {
width: 0,
height: 0,
isZoomable: false,
isZoomed: false,
isLoaded: false,
};
},
computed: {
fileSizeReadable() {
return numberToHumanSize(this.fileSize);
},
dimensionStyles() {
if (!this.isLoaded) return {};
return {
width: `${this.width}px`,
height: `${this.height}px`,
};
},
hasFileSize() {
return this.fileSize > 0;
},
hasDimensions() {
return this.width && this.height;
},
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeThrottled, false);
Loading
Loading
@@ -48,51 +66,52 @@ export default {
const { contentImg } = this.$refs;
 
if (contentImg) {
this.isZoomable =
contentImg.naturalWidth > contentImg.width ||
contentImg.naturalHeight > contentImg.height;
this.width = contentImg.naturalWidth;
this.height = contentImg.naturalHeight;
 
this.$emit('imgLoaded', {
width: this.width,
height: this.height,
renderedWidth: contentImg.clientWidth,
renderedHeight: contentImg.clientHeight,
this.$nextTick(() => {
this.isLoaded = true;
this.$emit('imgLoaded', {
width: this.width,
height: this.height,
renderedWidth: contentImg.clientWidth,
renderedHeight: contentImg.clientHeight,
});
});
}
},
onImgClick() {
if (this.isZoomable) this.isZoomed = !this.isZoomed;
},
},
};
</script>
 
<template>
<div class="file-container">
<div class="file-content image_file">
<div>
<div
:class="innerCssClasses"
:style="dimensionStyles"
class="position-relative"
>
<img
ref="contentImg"
:class="{ 'is-zoomable': isZoomable, 'is-zoomed': isZoomed }"
:src="path"
:alt="path"
@load="onImgLoad"
@click="onImgClick"/>
<p
v-if="renderInfo"
class="file-info prepend-top-10">
<template v-if="fileSize>0">
{{ fileSizeReadable }}
</template>
<template v-if="fileSize>0 && width && height">
|
</template>
<template v-if="width && height">
W: {{ width }} | H: {{ height }}
</template>
</p>
/>
<slot name="image-overlay"></slot>
</div>
<p
v-if="renderInfo"
class="image-info"
>
<template v-if="hasFileSize">
{{ fileSizeReadable }}
</template>
<template v-if="hasFileSize && hasDimensions">
|
</template>
<template v-if="hasDimensions">
<strong>W</strong>: {{ width }} | <strong>H</strong>: {{ height }}
</template>
</p>
</div>
</template>
Loading
Loading
@@ -69,6 +69,13 @@ export default {
:new-path="fullNewPath"
:old-path="fullOldPath"
:project-path="projectPath"
/>
>
<slot
slot="image-overlay"
name="image-overlay"
>
</slot>
</component>
<slot></slot>
</div>
</template>
Loading
Loading
@@ -15,11 +15,6 @@ export default {
type: String,
required: true,
},
projectPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
Loading
Loading
@@ -120,7 +115,6 @@ export default {
key="onionOldImg"
:render-info="false"
:path="oldPath"
:project-path="projectPath"
@imgLoaded="onionOldImgLoaded"
/>
</div>
Loading
Loading
@@ -136,9 +130,14 @@ export default {
key="onionNewImg"
:render-info="false"
:path="newPath"
:project-path="projectPath"
@imgLoaded="onionNewImgLoaded"
/>
>
<slot
slot="image-overlay"
name="image-overlay"
>
</slot>
</image-viewer>
</div>
<div class="controls">
<div class="transparent"></div>
Loading
Loading
Loading
Loading
@@ -16,11 +16,6 @@ export default {
type: String,
required: true,
},
projectPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
Loading
Loading
@@ -117,16 +112,14 @@ export default {
'height': swipeMaxPixelHeight,
}"
class="swipe-frame">
<div class="frame deleted">
<image-viewer
key="swipeOldImg"
ref="swipeOldImg"
:render-info="false"
:path="oldPath"
:project-path="projectPath"
@imgLoaded="swipeOldImgLoaded"
/>
</div>
<image-viewer
key="swipeOldImg"
ref="swipeOldImg"
:render-info="false"
:path="oldPath"
class="frame deleted"
@imgLoaded="swipeOldImgLoaded"
/>
<div
ref="swipeWrap"
:style="{
Loading
Loading
@@ -134,15 +127,19 @@ export default {
'height': swipeMaxPixelHeight,
}"
class="swipe-wrap">
<div class="frame added">
<image-viewer
key="swipeNewImg"
:render-info="false"
:path="newPath"
:project-path="projectPath"
@imgLoaded="swipeNewImgLoaded"
/>
</div>
<image-viewer
key="swipeNewImg"
:render-info="false"
:path="newPath"
class="frame added"
@imgLoaded="swipeNewImgLoaded"
>
<slot
slot="image-overlay"
name="image-overlay"
>
</slot>
</image-viewer>
</div>
<span
ref="swipeBar"
Loading
Loading
Loading
Loading
@@ -14,28 +14,29 @@ export default {
type: String,
required: true,
},
projectPath: {
type: String,
required: false,
default: '',
},
},
};
</script>
 
<template>
<div class="two-up view row">
<div class="col-sm-6 frame deleted">
<image-viewer
:path="oldPath"
:project-path="projectPath"
/>
</div>
<div class="col-sm-6 frame added">
<image-viewer
:path="newPath"
:project-path="projectPath"
/>
</div>
<div class="two-up view">
<image-viewer
:path="oldPath"
:render-info="true"
inner-css-classes="frame deleted"
class="wrap"
/>
<image-viewer
:path="newPath"
:render-info="true"
:inner-css-classes="['frame', 'added']"
class="wrap"
>
<slot
slot="image-overlay"
name="image-overlay"
>
</slot>
</image-viewer>
</div>
</template>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment