Skip to content

Commit

Permalink
Merge pull request #1292 from jplag/report-viewer/tooltips
Browse files Browse the repository at this point in the history
Tooltips in report viewer
  • Loading branch information
sebinside authored Sep 18, 2023
2 parents bdc8627 + abbcdfb commit 0fe97ee
Show file tree
Hide file tree
Showing 12 changed files with 445 additions and 110 deletions.
56 changes: 39 additions & 17 deletions report-viewer/src/components/ComparisonsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,27 @@
<div class="tableCellSimilarity !flex-col">
<div>Similarity</div>
<div class="flex w-full flex-row">
<div class="flex-1">Average</div>
<div class="flex-1">Maximum</div>
<ToolTipComponent class="flex-1" :direction="displayClusters ? 'top' : 'left'">
<template #default>
<p class="w-full text-center">{{ metricToolTips[MetricType.AVERAGE].shortName }}</p>
</template>
<template #tooltip>
<p class="whitespace-pre text-sm">
{{ metricToolTips[MetricType.AVERAGE].tooltip }}
</p>
</template>
</ToolTipComponent>

<ToolTipComponent class="flex-1" :direction="displayClusters ? 'top' : 'left'">
<template #default>
<p class="w-full text-center">{{ metricToolTips[MetricType.MAXIMUM].shortName }}</p>
</template>
<template #tooltip>
<p class="whitespace-pre text-sm">
{{ metricToolTips[MetricType.MAXIMUM].tooltip }}
</p>
</template>
</ToolTipComponent>
</div>
</div>
<div class="tableCellCluster items-center" v-if="displayClusters">Cluster</div>
Expand Down Expand Up @@ -102,21 +121,23 @@
}"
class="tect-center flex w-full justify-center"
>
<div class="group relative w-fit">
{{ clusters?.[index].members?.length }}
<FontAwesomeIcon
:icon="['fas', 'user-group']"
:style="{ color: clusterIconColors[index] }"
/>
{{ ((clusters?.[index].averageSimilarity as number) * 100).toFixed(2) }}%
<div
class="tooltipArrow absolute left-[-400px] top-0 z-50 hidden h-full items-center rounded-sm bg-gray-950 bg-opacity-90 px-2 text-sm text-white group-hover:flex"
>
{{ clusters?.[index].members?.length }} submissions in cluster with average
similarity of
<ToolTipComponent class="w-fit" direction="left">
<template #default>
{{ clusters?.[index].members?.length }}
<FontAwesomeIcon
:icon="['fas', 'user-group']"
:style="{ color: clusterIconColors[index] }"
/>
{{ ((clusters?.[index].averageSimilarity as number) * 100).toFixed(2) }}%
</div>
</div>
</template>
<template #tooltip>
<p class="whitespace-nowrap text-sm">
{{ clusters?.[index].members?.length }} submissions in cluster with average
similarity of
{{ ((clusters?.[index].averageSimilarity as number) * 100).toFixed(2) }}%
</p>
</template>
</ToolTipComponent>
</RouterLink>
</div>
</div>
Expand All @@ -137,7 +158,8 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faUserGroup } from '@fortawesome/free-solid-svg-icons'
import { generateColors } from '@/utils/ColorUtils'
import { MetricType } from '@/model/MetricType'
import ToolTipComponent from './ToolTipComponent.vue'
import { MetricType, metricToolTips } from '@/model/MetricType'

library.add(faUserGroup)

Expand Down
25 changes: 16 additions & 9 deletions report-viewer/src/components/MatchList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,33 @@
-->
<template>
<div class="flex h-fit min-w-0 max-w-full flex-row space-x-1 overflow-x-hidden text-xs">
<Interactable class="my-2 flex h-6 items-center whitespace-nowrap !rounded-2xl text-center">
Match Files: TokenCount
</Interactable>
<ToolTipComponent direction="right">
<template #default>
<OptionComponent label="Match Files: TokenCount" />
</template>
<template #tooltip>
<p class="whitespace-pre text-sm">Click on a match to show it in the code view.</p>
</template>
</ToolTipComponent>

<div class="flex w-full flex-row space-x-1 overflow-x-auto">
<Interactable
class="my-2 flex h-6 items-center whitespace-nowrap !rounded-2xl !bg-opacity-50 text-center"
<OptionComponent
:style="{ background: match.color }"
v-for="[index, match] in matches?.entries()"
v-bind:key="index"
@click="$emit('matchSelected', match)"
>
{{ getFileName(match.firstFile) }} - {{ getFileName(match.secondFile) }}: {{ match.tokens }}
</Interactable>
:label="
getFileName(match.firstFile) + ' - ' + getFileName(match.secondFile) + ': ' + match.tokens
"
/>
</div>
</div>
</template>

<script setup lang="ts">
import type { Match } from '@/model/Match'
import Interactable from './InteractableComponent.vue'
import OptionComponent from './optionsSelectors/OptionComponent.vue'
import ToolTipComponent from './ToolTipComponent.vue'

defineProps({
/**
Expand Down
56 changes: 0 additions & 56 deletions report-viewer/src/components/OptionsSelectorComponent.vue

This file was deleted.

73 changes: 73 additions & 0 deletions report-viewer/src/components/ToolTipComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<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"
>
<slot name="tooltip"></slot>
<div class="absolute border-4 border-solid" :style="arrowStyle"><!-- Arrow --></div>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, type PropType, type StyleValue } from 'vue'
const props = defineProps({
direction: {
type: String as PropType<'top' | 'bottom' | 'left' | 'right'>,
required: false,
default: 'top'
}
})
const opacity = 0.8
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%'
}
}
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 '
}
if (props.direction == 'left' || props.direction == 'right') {
style.top = '50%'
style.marginTop = '-4px'
} else {
style.left = '50%'
style.marginLeft = '-4px'
}
style[props.direction] = '100%'
return style
})
</script>
40 changes: 40 additions & 0 deletions report-viewer/src/components/optionsSelectors/MetricSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<OptionsSelectorComponent
:title="title"
:labels="labels"
@selection-changed="(i) => $emit('selectionChanged', metrics[i])"
/>
</template>

<script setup lang="ts">
import { computed, type PropType, type Ref } from 'vue'
import OptionsSelectorComponent from './OptionsSelectorComponent.vue'
import { MetricType, metricToolTips } from '@/model/MetricType'
const props = defineProps({
metrics: {
type: Array<MetricType>,
required: false,
default: [MetricType.AVERAGE, MetricType.MAXIMUM]
},
title: {
type: String,
required: false,
default: ''
},
defaultSelection: {
type: String as PropType<MetricType>,
required: false,
default: MetricType.AVERAGE
}
})
defineEmits(['selectionChanged'])
const labels: Ref<{ displayValue: string; tooltip: string }[]> = computed(() =>
props.metrics.map((metric) => ({
displayValue: metricToolTips[metric].longName,
tooltip: metricToolTips[metric].tooltip
}))
)
</script>
27 changes: 27 additions & 0 deletions report-viewer/src/components/optionsSelectors/OptionComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<Interactable
class="mr-2 box-border flex h-6 w-fit items-center justify-center whitespace-nowrap !rounded-2xl px-[12px] text-center hover:!border-[2px] hover:px-[11px]"
:class="{ '!border-accent-dark !bg-accent !bg-opacity-40': selected }"
@click="$emit('click')"
>
{{ label }}
</Interactable>
</template>

<script setup lang="ts">
import Interactable from '../InteractableComponent.vue'
defineProps({
label: {
type: String,
required: true
},
selected: {
type: Boolean,
required: false,
default: false
}
})
defineEmits(['click'])
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!--
Component for selecting one of multiple options.
-->
<template>
<div class="flex h-fit flex-row items-center text-center text-xs">
<div v-if="title != ''" class="mr-3 text-base">
{{ title }}
</div>
<div v-for="[index, label] in labels.entries()" :key="index">
<ToolTipComponent v-if="(label as ToolTipLabel).displayValue !== undefined" direction="right">
<template #default>
<OptionComponent
:label="(label as ToolTipLabel).displayValue"
:selected="index == getSelected()"
@click="select(index)"
/>
</template>

<template #tooltip>
<p class="whitespace-pre text-sm">
{{ (label as ToolTipLabel).tooltip }}
</p>
</template>
</ToolTipComponent>
<OptionComponent
v-else
:label="label as string"
:selected="index == getSelected()"
@click="select(index)"
/>
</div>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import ToolTipComponent from '../ToolTipComponent.vue'
import OptionComponent from './OptionComponent.vue'

type ToolTipLabel = {
displayValue: string
tooltip: string
}

const props = defineProps({
title: {
type: String,
required: false,
default: ''
},
labels: {
type: Array<string | ToolTipLabel>,
required: true
},
defaultSelected: {
type: Number,
required: false,
default: 0
}
})

const emit = defineEmits(['selectionChanged'])
const selected = ref(-1)

function getSelected() {
if (selected.value == -1) {
return props.defaultSelected
}
return selected.value
}

function select(index: number) {
emit('selectionChanged', index)
selected.value = index
}
</script>
Loading

0 comments on commit 0fe97ee

Please sign in to comment.