diff --git a/src/components/SequenceBoard.vue b/src/components/SequenceBoard.vue index a46b8911974f6030f5db6c70f6565d5106f474a0..6c338a144814cd9f2169ee5992df632143fb6cfb 100644 --- a/src/components/SequenceBoard.vue +++ b/src/components/SequenceBoard.vue @@ -14,6 +14,7 @@ import Toolbar from 'primevue/toolbar' import IconFa6SolidDna from '~icons/fa6-solid/dna' import IconFa6SolidCircleCheck from '~icons/fa6-solid/circle-check' import IconFa6RegularCopy from '~icons/fa6-regular/copy' +import IconFa6SolidCircleInfo from '~icons/fa6-solid/circle-info' /** * Other 3rd-party imports */ @@ -183,6 +184,21 @@ const highlightedGroupIdsContaining = (position: number): string[] => ) : [] +/** + * Gets the highlighted groups containing a nucleotide. + * @param position The position of the nucleotide for which to get group. + * @returns A list of tuples, `[group ID, group]` for each of the highlighted + * groups containing the nucleotide. + */ +const highlightedGroupsContaining = ( + position: number +): [string, highlightGroupModel][] => + props.highlightedGroups + ? Object.entries(props.highlightedGroups).filter(([groupId]) => + isInHighlightedGroup(position, groupId) + ) + : [] + /** * Whether a nucleotide is in any highlighted group. * @param position The position of the nucleotide to check. @@ -578,17 +594,6 @@ const lockTooltip = () => { lockedTooltipHighlightedGroups.value = new Set(hoveredHighlightedGroups.value) tooltipComponent.value?.lockTooltipIfUnlocked() } - -const deleteHoveredHighlightedGroup = ( - highlightedGroupTuple: [string, highlightGroupModel] -) => { - const hoveredHighlightedGroupTuple = [...hoveredHighlightedGroups.value].find( - ([hoveredHighlightedGroupId]) => - hoveredHighlightedGroupId === highlightedGroupTuple[0] - ) - hoveredHighlightedGroupTuple && - hoveredHighlightedGroups.value.delete(hoveredHighlightedGroupTuple) -} </script> <template> @@ -609,20 +614,34 @@ const deleteHoveredHighlightedGroup = ( </template> <template v-if="legendItems" #center> - <BaseLegendButtonOverlay button-text="Legend" :items="legendItems"> - <template #item="{ item }"> - <slot name="legend-item" :item="item" /> - </template> - <template #item-icon="{ item }"> - <slot name="legend-item-icon" :item="item" /> - </template> - <template #item-title="{ item }"> - <slot name="legend-item-title" :item="item" /> - </template> - <template #item-description="{ item }"> - <slot name="legend-item-description" :item="item" /> - </template> - </BaseLegendButtonOverlay> + <div class="relative flex flex-col items-center gap-2"> + <BaseLegendButtonOverlay button-text="Legend" :items="legendItems"> + <template #item="{ item }"> + <slot name="legend-item" :item="item" /> + </template> + <template #item-icon="{ item }"> + <slot name="legend-item-icon" :item="item" /> + </template> + <template #item-title="{ item }"> + <slot name="legend-item-title" :item="item" /> + </template> + <template #item-description="{ item }"> + <slot name="legend-item-description" :item="item" /> + </template> + </BaseLegendButtonOverlay> + <span class="text-sm italic text-slate-600 md:hidden"> + Click on an object to lock its tooltip in place. + </span> + <span + v-tooltip.right="{ + value: 'Click on an object to lock its tooltip in place.', + pt: { text: 'text-xs' } + }" + class="absolute -right-2 -top-2 hidden text-base text-slate-600 md:inline" + > + <icon-fa6-solid-circle-info /> + </span> + </div> </template> <template #end> @@ -689,7 +708,7 @@ const deleteHoveredHighlightedGroup = ( nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 " :class="[ - 'mx-[.0625rem] border-2 border-transparent px-0.5 pt-0.5', + 'relative mx-[.0625rem] border-2 border-transparent px-0.5 pt-0.5', isInAnyHighlightedGroup( nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 ) && [ @@ -700,12 +719,46 @@ const deleteHoveredHighlightedGroup = ( ] ]" > - {{ nucleotide }} + <span + v-if=" + highlightedGroupsContaining( + nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 + ).find( + ([_highlightedGroupId, highlightedGroup]) => + highlightedGroup.shouldTooltip + ) + " + :class="[ + 'absolute -bottom-0.5 -left-[0.1875rem] -right-[0.1875rem] -top-0.5', + highlightedGroupsContaining( + nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 + ).find( + ([_highlightedGroupId, highlightedGroup]) => + highlightedGroup.link + ) + ? 'cursor-pointer' + : 'cursor-help' + ]" + @mouseenter=" + highlightedGroupsContaining( + nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 + ).forEach( + ([highlightedGroupId, highlightedGroup]) => + highlightedGroup.shouldTooltip && + hoveredHighlightedGroups.add([ + highlightedGroupId, + highlightedGroup + ]) + ) + " + @mouseleave="hoveredHighlightedGroups.clear()" + @click="lockTooltip" + />{{ nucleotide }} </span> <span v-else :class="[ - 'mx-[.0625rem] border-2 border-transparent px-0.5 pt-0.5', + 'relative mx-[.0625rem] border-2 border-transparent px-0.5 pt-0.5', isInAnyHighlightedGroup( nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 ) && [ @@ -716,7 +769,41 @@ const deleteHoveredHighlightedGroup = ( ] ]" > - {{ nucleotide }} + <span + v-if=" + highlightedGroupsContaining( + nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 + ).find( + ([_highlightedGroupId, highlightedGroup]) => + highlightedGroup.shouldTooltip + ) + " + :class="[ + 'absolute -bottom-0.5 -left-[0.1875rem] -right-[0.1875rem] -top-0.5', + highlightedGroupsContaining( + nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 + ).find( + ([_highlightedGroupId, highlightedGroup]) => + highlightedGroup.link + ) + ? 'cursor-pointer' + : 'cursor-help' + ]" + @mouseenter=" + highlightedGroupsContaining( + nucleotideIndex + groupIndex * nucleotideGroupsSize + 1 + ).forEach( + ([highlightedGroupId, highlightedGroup]) => + highlightedGroup.shouldTooltip && + hoveredHighlightedGroups.add([ + highlightedGroupId, + highlightedGroup + ]) + ) + " + @mouseleave="hoveredHighlightedGroups.clear()" + @click="lockTooltip" + />{{ nucleotide }} </span> </template> </span> @@ -740,14 +827,6 @@ const deleteHoveredHighlightedGroup = ( :data-start="highlightedGroup.start" :data-end="highlightedGroup.end" class="highlight-group" - @mouseenter=" - highlightedGroup.shouldTooltip && - hoveredHighlightedGroups.add([highlightedGroupId, highlightedGroup]) - " - @mouseleave=" - deleteHoveredHighlightedGroup([highlightedGroupId, highlightedGroup]) - " - @click="lockTooltip" > <span v-for="highlightedGroupLine in range( @@ -765,8 +844,7 @@ const deleteHoveredHighlightedGroup = ( 'rounded-r-xl border-r-2': highlightedGroupsLineCount && highlightedGroupLine + 1 === - highlightedGroupsLineCount[highlightedGroupId], - 'z-10': highlightedGroup.shouldTooltip + highlightedGroupsLineCount[highlightedGroupId] }, highlightedGroup.shouldTooltip && (highlightedGroup.link ? 'cursor-pointer' : 'cursor-help') @@ -780,7 +858,7 @@ const deleteHoveredHighlightedGroup = ( class="z-20 max-w-min font-sans text-base shadow-xl" @unlock="lockedTooltipHighlightedGroups = undefined" > - <ul> + <ul class="flex flex-col gap-1"> <li v-for="(highlightedGroup, index) in tooltipHighlightedGroups" :key="index"