From bb3e49c889d30e1cc6ce33171d1a34b0afcb3144 Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Thu, 10 Mar 2022 12:11:50 +0100 Subject: [PATCH] feat: make aa substitution tooltips in gene views sticky when clicked --- .../src/components/Common/useClickOutside.ts | 25 +++ .../PeptideMarkerMutationGroup.tsx | 178 ++++++++++-------- 2 files changed, 128 insertions(+), 75 deletions(-) create mode 100644 packages/web/src/components/Common/useClickOutside.ts diff --git a/packages/web/src/components/Common/useClickOutside.ts b/packages/web/src/components/Common/useClickOutside.ts new file mode 100644 index 000000000..ddf83434b --- /dev/null +++ b/packages/web/src/components/Common/useClickOutside.ts @@ -0,0 +1,25 @@ +import { MutableRefObject, useEffect } from 'react' + +/** + * Hook that alerts clicks outside of the passed ref + * Author: Ben Bud + * Source: https://stackoverflow.com/a/42234988 + */ +export function useClickOutside( + ref: MutableRefObject, + onClickOutside: () => void, +) { + useEffect(() => { + function handleClickOutside(event: Event) { + if (event.target instanceof Node && ref.current && !ref.current.contains(event.target)) { + onClickOutside() + } + } + + document.addEventListener('mousedown', handleClickOutside) + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [onClickOutside, ref]) +} diff --git a/packages/web/src/components/SequenceView/PeptideMarkerMutationGroup.tsx b/packages/web/src/components/SequenceView/PeptideMarkerMutationGroup.tsx index 06a865ede..f0d132dd4 100644 --- a/packages/web/src/components/SequenceView/PeptideMarkerMutationGroup.tsx +++ b/packages/web/src/components/SequenceView/PeptideMarkerMutationGroup.tsx @@ -1,8 +1,9 @@ -import React, { SVGProps, useState } from 'react' +import React, { SVGProps, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Row, Col } from 'reactstrap' import { connect } from 'react-redux' +import { useClickOutside } from 'src/components/Common/useClickOutside' import { AA_MIN_WIDTH_PX } from 'src/constants' @@ -57,6 +58,12 @@ function PeptideMarkerMutationGroupDisconnected({ }: PeptideMarkerMutationGroupProps) { const { t } = useTranslation() const [showTooltip, setShowTooltip] = useState(false) + const [isClicked, setIsClicked] = useState(false) + const ref = useRef(null) + useClickOutside(ref, () => { + setShowTooltip(false) + setIsClicked(false) + }) const { gene, changes, codonAaRange, nucSubstitutions, nucDeletions } = group const id = getSafeId('aa-mutation-group-marker', { seqName, gene, begin: codonAaRange.begin }) @@ -82,7 +89,24 @@ function PeptideMarkerMutationGroupDisconnected({ - setShowTooltip(true)} onMouseLeave={() => setShowTooltip(false)}> + { + if (!isClicked) { + setShowTooltip(true) + } + }} + onMouseLeave={() => { + if (!isClicked) { + setShowTooltip(false) + } + }} + onClick={() => { + if (!isClicked) { + setShowTooltip(true) + setIsClicked(true) + } + }} + > {changes.map((change) => ( - - - -
- -
{seqName}
- - - - - -
{t('Aminoacid changes ({{count}})', { count: changes.length })}
- - - - <> - {changesHead.map((change) => ( - - {change.type === 'substitution' ? t('Substitution') : t('Deletion')} - - - - - ))} - - - {changesTail.length > 0 && ( +
+ + + - {'...'} - {'...'} + +
{seqName}
+ - )} - <> - {changesTail.length > 0 && - changesTail.map((change) => ( + + +
{t('Aminoacid changes ({{count}})', { count: changes.length })}
+ + + + <> + {changesHead.map((change) => ( {change.type === 'substitution' ? t('Substitution') : t('Deletion')} @@ -136,51 +142,73 @@ function PeptideMarkerMutationGroupDisconnected({ ))} - + + + {changesTail.length > 0 && ( + + {'...'} + {'...'} + + )} + + <> + {changesTail.length > 0 && + changesTail.map((change) => ( + + {change.type === 'substitution' ? t('Substitution') : t('Deletion')} + + + + + ))} + + + {totalNucChanges > 0 && ( + + +
+ {t('Nucleotide changes nearby ({{count}})', { count: totalNucChanges })} +
+ + + )} + + <> + {nucSubstitutions.map((mut) => ( + + {t('Substitution')} + {} + + ))} + + + <> + {nucDeletions.map((del) => ( + + {t('Deletion')} + {formatRange(del.start, del.start + del.length)} + + ))} + - {totalNucChanges > 0 && ( -
{t('Nucleotide changes nearby ({{count}})', { count: totalNucChanges })}
+ + +
{'Context'}
+ +
+ + + + + + - )} - - <> - {nucSubstitutions.map((mut) => ( - - {t('Substitution')} - {} - - ))} - - - <> - {nucDeletions.map((del) => ( - - {t('Deletion')} - {formatRange(del.start, del.start + del.length)} - - ))} - - - - - - -
{'Context'}
- -
- - - - - - - - - -
+ + +