Skip to content

Commit

Permalink
Merge pull request #1717 from jplag/report-viewer/breadcrump-tooltips
Browse files Browse the repository at this point in the history
Add Tool Tip for Bread Crumbs in Comparison View
  • Loading branch information
tsaglam committed May 3, 2024
2 parents 649a595 + 02afba5 commit d091182
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,20 @@ private Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Mat

int startLineFirst = startOfFirst.getLine();
int startColumnFirst = startOfFirst.getColumn();
int startTokenFirst = match.startOfFirst();
int endLineFirst = endOfFirst.getLine();
int endColumnFirst = endOfFirst.getColumn() + endOfFirst.getLength() - 1;
int endTokenFirst = match.endOfFirst();

int startLineSecond = startOfSecond.getLine();
int startColumnSecond = startOfSecond.getColumn();
int startTokenSecond = match.startOfSecond();
int endLineSecond = endOfSecond.getLine();
int endColumnSecond = endOfSecond.getColumn() + endOfSecond.getLength() - 1;
int endTokenSecond = match.endOfSecond();

return new Match(firstFileName, secondFileName, startLineFirst, startColumnFirst, endLineFirst, endColumnFirst, startLineSecond,
startColumnSecond, endLineSecond, endColumnSecond, match.length());
return new Match(firstFileName, secondFileName, startLineFirst, startColumnFirst, startTokenFirst, endLineFirst, endColumnFirst,
endTokenFirst, startLineSecond, startColumnSecond, startTokenSecond, endLineSecond, endColumnSecond, endTokenSecond, match.length());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import com.fasterxml.jackson.annotation.JsonProperty;

public record Match(@JsonProperty("file1") String firstFileName, @JsonProperty("file2") String secondFileName,
@JsonProperty("start1") int startInFirst, @JsonProperty("start1_col") int startColumnInFirst, @JsonProperty("end1") int endInFirst,
@JsonProperty("end1_col") int endColumnInFirst, @JsonProperty("start2") int startInSecond,
@JsonProperty("start2_col") int startColumnInSecond, @JsonProperty("end2") int endInSecond, @JsonProperty("end2_col") int endColumnInSecond,
@JsonProperty("start1") int startInFirst, @JsonProperty("start1_col") int startColumnInFirst,
@JsonProperty("startToken1") int startTokenInFirst, @JsonProperty("end1") int endInFirst, @JsonProperty("end1_col") int endColumnInFirst,
@JsonProperty("endToken1") int endTokenInFirst, @JsonProperty("start2") int startInSecond,
@JsonProperty("start2_col") int startColumnInSecond, @JsonProperty("startToken2") int startTokenInSecond,
@JsonProperty("end2") int endInSecond, @JsonProperty("endToken2") int endTokenInSecond, @JsonProperty("end2_col") int endColumnInSecond,
@JsonProperty("tokens") int tokens) {
}
2 changes: 1 addition & 1 deletion report-viewer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JPlag Report Viewer</title>
</head>
<body>
<body style="overflow: hidden">
<noscript>
<strong
>We're sorry but the JPlag Report Viewer does not work properly without JavaScript enabled.
Expand Down
6 changes: 5 additions & 1 deletion report-viewer/src/components/ComparisonsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@
}"
class="flex w-full justify-center text-center"
>
<ToolTipComponent class="w-fit" direction="left">
<ToolTipComponent
class="w-fit"
direction="left"
:tool-tip-container-will-be-centered="true"
>
<template #default>
{{ clusters?.[item.clusterIndex].members?.length }}
<FontAwesomeIcon
Expand Down
117 changes: 69 additions & 48 deletions report-viewer/src/components/ToolTipComponent.vue
Original file line number Diff line number Diff line change
@@ -1,76 +1,97 @@
<template>
<div class="group relative inline-block">
<slot></slot>
<div
class="invisible absolute z-10 rounded-md px-1 text-center text-white delay-0 group-hover:visible group-hover:delay-200"
:style="tooltipPosition"
<div class="group pointer-events-none inline">
<div ref="contentRef" class="pointer-events-auto"><slot></slot></div>
<span
class="invisible absolute box-border delay-0 group-hover:visible group-hover:delay-200"
ref="tooltipRef"
v-if="$slots.tooltip"
>
<slot name="tooltip"></slot>
<div class="absolute border-4 border-solid" :style="arrowStyle" v-if="$slots.tooltip">
<!-- Arrow -->
</div>
</div>
<span
class="arrowBase pointer-events-auto relative z-10 block rounded-md bg-tooltip px-1 text-center text-white after:absolute after:border-4 after:border-solid after:border-transparent"
:style="tooltipPosition"
:class="{
'after:top-1/2 after:-mt-1': props.direction == 'left' || props.direction == 'right',
'after:!left-1/2 after:-ml-1': props.direction == 'top' || props.direction == 'bottom',
'after:top-full after:!border-t-tooltip': props.direction == 'top',
'after:bottom-full after:!border-b-tooltip': props.direction == 'bottom',
'after:left-full after:!border-l-tooltip': props.direction == 'left',
'after:right-full after:!border-r-tooltip': props.direction == 'right'
}"
>
<slot name="tooltip"></slot>
</span>
</span>
</div>
</template>

<script setup lang="ts">
import type { ToolTipDirection } from '@/model/ui/ToolTip'
import { computed, type PropType, type StyleValue } from 'vue'
import { computed, ref, type PropType, type Ref, type StyleValue } from 'vue'
const props = defineProps({
direction: {
type: String as PropType<ToolTipDirection>,
required: false,
default: 'top'
},
/** Sometimes the absolute div is centered horizontally on the content. Set this to true if that is the case. */
toolTipContainerWillBeCentered: {
type: Boolean,
required: false,
default: false
},
/** Can be set if the tooltip is inside a scrollable container */
scrollOffsetX: {
type: Number,
required: false,
default: 0
},
scrollOffsetY: {
type: Number,
required: false,
default: 0
}
})
const opacity = 0.8
const contentRef: Ref<HTMLElement | null> = ref(null)
const tooltipRef: Ref<HTMLElement | null> = ref(null)
const arrowOffset = 4
const tooltipPosition = computed(() => {
const style: StyleValue = {}
if (props.direction == 'left' || props.direction == 'right') {
style.top = '50%'
style.transform = 'translateY(-50%)'
if (props.direction == 'left') {
style.right = '105%'
} else {
style.left = '105%'
}
} else {
style.left = '50%'
style.transform = 'translateX(-50%)'
if (props.direction == 'top') {
style.bottom = '105%'
} else {
style.top = '105%'
}
const contentDiv = contentRef.value
const tooltipDiv = tooltipRef.value
if (!contentDiv || !tooltipDiv) {
return style
}
style.backgroundColor = `rgba(0,0,0,${opacity})`
return style
})
const arrowStyle = computed(() => {
const style: StyleValue = {}
style.content = ' '
style.borderColor = ''
for (const dir of ['top', 'right', 'bottom', 'left']) {
style.borderColor += dir == props.direction ? `rgba(0,0,0,${opacity}) ` : 'transparent '
// zeros the tooltip on the topleft of the content
let top = -contentDiv.offsetHeight - props.scrollOffsetY
let left =
(props.toolTipContainerWillBeCentered ? -contentDiv.offsetWidth / 2 : 0) - props.scrollOffsetX
if (props.direction == 'right' || props.direction == 'left') {
top += (contentDiv.offsetHeight - tooltipDiv.offsetHeight) / 2
} else {
left -= (tooltipDiv.offsetWidth - contentDiv.offsetWidth) / 2
}
if (props.direction == 'left' || props.direction == 'right') {
style.top = '50%'
style.marginTop = '-4px'
if (props.direction == 'right') {
left += contentDiv.offsetWidth + arrowOffset
} else if (props.direction == 'left') {
left -= tooltipDiv.offsetWidth + arrowOffset
} else if (props.direction == 'bottom') {
top += contentDiv.offsetHeight + arrowOffset
} else {
style.left = '50%'
style.marginLeft = '-4px'
top -= tooltipDiv.offsetHeight + arrowOffset
}
style[props.direction] = '100%'
style.top = top + 'px'
style.left = left + 'px'
return style
})
</script>

<style scoped>
.arrowBase::after {
content: ' ';
}
</style>
78 changes: 69 additions & 9 deletions report-viewer/src/components/fileDisplaying/MatchList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,48 @@

<div
class="print-excact flex w-full flex-row space-x-1 overflow-x-auto print:flex-wrap print:space-y-1 print:overflow-x-hidden"
ref="scrollableList"
@scroll="updateScrollOffset()"
>
<OptionComponent
<ToolTipComponent
:direction="getTooltipDirection(index)"
v-for="[index, match] in matches?.entries()"
:style="{ background: getMatchColor(0.3, match.colorIndex) }"
v-bind:key="index"
@click="$emit('matchSelected', match)"
:label="
getFileName(match.firstFile) + ' - ' + getFileName(match.secondFile) + ': ' + match.tokens
"
/>
:key="index"
:scrollOffsetX="scrollOffsetX"
>
<template #default>
<OptionComponent
:style="{ background: getMatchColor(0.3, match.colorIndex) }"
@click="$emit('matchSelected', match)"
:label="
getFileName(match.firstFile) +
' - ' +
getFileName(match.secondFile) +
': ' +
match.tokens
"
/>
</template>
<template #tooltip>
<p class="whitespace-pre text-sm">
Match between {{ getFileName(match.firstFile) }} (Line {{ match.startInFirst.line }}-{{
match.endInFirst.line
}}) and {{ getFileName(match.secondFile) }} (Line {{ match.startInSecond.line }}-{{
match.endInSecond.line
}}) <br />
Match is {{ match.tokens }} tokens long. <br />
<span v-if="showTokenRanges(match)">
Token indeces of match: {{ match.startInFirst.tokenListIndex }}-{{
match.endInFirst.tokenListIndex
}}
and {{ match.startInSecond.tokenListIndex }}-{{ match.endInSecond.tokenListIndex }}.
<br />
</span>
Click to show in code view.
</p>
</template>
</ToolTipComponent>
</div>
</div>
Expand Down Expand Up @@ -59,8 +91,10 @@ import type { Match } from '@/model/Match'
import OptionComponent from '../optionsSelectors/OptionComponent.vue'
import ToolTipComponent from '@/components/ToolTipComponent.vue'
import { getMatchColor } from '@/utils/ColorUtils'
import type { ToolTipDirection } from '@/model/ui/ToolTip'
import { ref, type Ref } from 'vue'
defineProps({
const props = defineProps({
/**
* Matches of the comparison.
* type: Array<Match>
Expand All @@ -87,4 +121,30 @@ defineEmits(['matchSelected'])
function getFileName(fullPath: string) {
return fullPath.split(/[/\\]/g).pop() || ''
}
function getTooltipDirection(index: number): ToolTipDirection {
if (index == 0) return 'right'
if (index >= 2 && index + 2 >= (props.matches?.length ?? Infinity)) {
return 'left'
}
return 'bottom'
}
function showTokenRanges(match: Match) {
return (
!isNaN(match.startInFirst.tokenListIndex) &&
!isNaN(match.startInSecond.tokenListIndex) &&
!isNaN(match.endInFirst.tokenListIndex) &&
!isNaN(match.endInSecond.tokenListIndex)
)
}
const scrollableList: Ref<HTMLElement | null> = ref(null)
const scrollOffsetX = ref(0)
function updateScrollOffset() {
if (scrollableList.value) {
scrollOffsetX.value = scrollableList.value.scrollLeft
}
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
{{ title }}
</div>
<div v-for="[index, label] in labels.entries()" :key="index">
<ToolTipComponent v-if="(label as ToolTipLabel).displayValue !== undefined" direction="right">
<ToolTipComponent
v-if="(label as ToolTipLabel).displayValue !== undefined"
direction="right"
:tool-tip-container-will-be-centered="true"
>
<template #default>
<OptionComponent
:label="(label as ToolTipLabel).displayValue"
Expand Down
18 changes: 10 additions & 8 deletions report-viewer/src/model/Match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
export interface Match {
firstFile: string
secondFile: string
startInFirst: number
startColumnInFirst: number
endInFirst: number
endColumnInFirst: number
startInSecond: number
startColumnInSecond: number
endInSecond: number
endColumnInSecond: number
startInFirst: CodePosition
endInFirst: CodePosition
startInSecond: CodePosition
endInSecond: CodePosition
tokens: number
colorIndex?: number
}

export interface CodePosition {
line: number
column: number
tokenListIndex: number
}
16 changes: 8 additions & 8 deletions report-viewer/src/model/MatchInSingleFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export class MatchInSingleFile {
*/
get start(): number {
if (this._index === 1) {
return this._match.startInFirst
return this._match.startInFirst.line
} else {
return this._match.startInSecond
return this._match.startInSecond.line
}
}

Expand All @@ -35,25 +35,25 @@ export class MatchInSingleFile {
*/
get end(): number {
if (this._index === 1) {
return this._match.endInFirst
return this._match.endInFirst.line
} else {
return this._match.endInSecond
return this._match.endInSecond.line
}
}

get startColumn(): number {
if (this._index === 1) {
return this._match.startColumnInFirst
return this._match.startInFirst.column
} else {
return this._match.startColumnInSecond
return this._match.startInSecond.column
}
}

get endColumn(): number {
if (this._index === 1) {
return this._match.endColumnInFirst
return this._match.endInFirst.column
} else {
return this._match.endColumnInSecond
return this._match.endInSecond.column
}
}
}
Loading

0 comments on commit d091182

Please sign in to comment.