Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Refine tooltips for splice toggle and isoform arrows #349

Merged
merged 16 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added examples/vanilla/img/gene_leads_ideogram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed examples/vanilla/img/related_genes_ideogram.png
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/vanilla/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ <h1>Overview | Ideogram</h1>
<div><a href="annotations-heatmap">Annotations, heatmap</a><br/> Use color to show feature distributions on chromosomes.</div>
</div>
<div class="card">
<a href="gene-leads"><img src="img/related_genes_ideogram.png"/></a>
<a href="gene-leads"><img src="img/gene_leads_ideogram.png"/></a>
<div><a href="gene-leads">Gene leads</a><br/> Discover genes of interest.</div>
</div>
<div class="card">
Expand Down
33 changes: 32 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
"d3-fetch": "^2.0.0",
"d3-format": "^2.0.0",
"d3-scale": "^4.0.2",
"fflate": "^0.7.3"
"fflate": "^0.7.3",
"tippy.js": "6.3.7"
},
"devDependencies": {
"@babel/core": "^7.16.0",
Expand Down
95 changes: 80 additions & 15 deletions src/js/kit/gene-structure.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import tippy, {hideAll} from 'tippy.js';
import {tippyCss, tippyLightCss} from './tippy-styles';
// import 'tippy.js/themes/light.css';

import {d3} from '../lib';
import {getIcon} from '../annotations/legend';
import {getProteinSvg} from './protein';
Expand Down Expand Up @@ -72,6 +76,16 @@ const css =
#_ideoGeneStructureTip {
font-style: italic;
}
${tippyCss}
${tippyLightCss}

.tippy-box {
font-size: 12px;
}

.tippy-content {
padding: 3px 7px;
}
</style>`;

const hoverTip = '<span id="_ideoGeneStructureTip">Hover for details</span>';
Expand Down Expand Up @@ -102,6 +116,7 @@ function updateGeneStructure(ideo, offset=0) {
writeFooter(container);
ideo.addedSubpartListeners = false;
addHoverListeners(ideo);
initTippy(ideo);
}

/** Get gene symbol from transcript / gene structure name */
Expand Down Expand Up @@ -426,6 +441,12 @@ function getMenuContainer() {
return document.querySelector('#_ideoGeneStructureMenuContainer');
}

function updateFooter(content, ideo) {
const footer = getFooter();
footer.innerHTML = content;
initTippy(ideo);
}

function addSubpartHoverListener(subpartDOM, ideo) {
const subpart = subpartDOM;

Expand All @@ -441,17 +462,16 @@ function addSubpartHoverListener(subpartDOM, ideo) {
const trimmedFoot = footer.innerHTML.replace('&nbsp;', '');
const style = 'style="margin-bottom: -10px; max-width: 260px;"';
const id = 'id="_ideoSubpartText"';
footer.innerHTML =
`<div ${id} ${style}">${subpartText}</div>${trimmedFoot}`;
const content = `<div ${id} ${style}">${subpartText}</div>${trimmedFoot}`;
updateFooter(content, ideo);
const menuContainer = getMenuContainer();
if (menuContainer) menuContainer.style.marginTop = '';
});

// On hovering out, de-highlight and hide details
subpart.addEventListener('mouseleave', event => {
event.target.classList.remove('_ideoHoveredSubpart');
const footer = getFooter();
footer.innerHTML = ideo.originalTooltipFooter;
updateFooter(ideo.originalTooltipFooter, ideo);
const menuContainer = getMenuContainer();
if (menuContainer) menuContainer.style.marginTop = '4px';
});
Expand Down Expand Up @@ -501,7 +521,6 @@ function addHoverListeners(ideo) {
// is often the case in genes with many transcripts, like TP53).
ideo.oneTimeDelayTooltipHideMs = 2000; // wait 2.0 s instead of 0.25 s
});

});
container.addEventListener('mouseleave', (event) => {
ideo.oneTimeDelayTooltipHideMs = 2000; // See "Without this..." note above
Expand All @@ -514,8 +533,8 @@ function addHoverListeners(ideo) {
// footer. This lets users always see the details in the footer for the
// transcript they just selected, rather than having the details
// frustratingly disappear immediately upon transcript selection.
const footer = getFooter();
footer.innerHTML = hoverTip;

updateFooter(hoverTip, ideo);
}
ideo.addedMenuListeners = false;
document.removeEventListener('keydown', navigateSubparts);
Expand All @@ -537,13 +556,50 @@ function writeStrandInFooter(ideo) {
tooltipFooter.innerText.replace(')', `, ${strand})`);
}

function getTippyConfig(fallbackPlacements) {
return {
theme: 'light-border',
popperOptions: { // Docs: https://atomiks.github.io/tippyjs/v6/all-props/#popperoptions
modifiers: [ // Docs: https://popper.js.org/docs/v2/modifiers
{
name: 'flip',
options: {
fallbackPlacements // Defined via argument to this function
}
}
]
},
onShow: function() {
// Ensure only 1 tippy tooltip is displayed at a time
document.querySelectorAll('[data-tippy-root]')
.forEach(tippyNode => tippyNode.remove());
}
};
}

function initTippy(ideo) {
const toggle = getTippyConfig(['top-start', 'top']);
ideo.tippy = tippy('._ideoSpliceToggle[data-tippy-content]', toggle);

const arrow = getTippyConfig(['bottom']);
arrow.popperOptions.modifiers.push({
name: 'offset',
options: {
offset: [-5, 20]
}
});
const updownTips = tippy('._ideoMenuArrow[data-tippy-content]', arrow);
ideo.tippy = ideo.tippy.concat(updownTips);
}

export function addGeneStructureListeners(ideo) {
const structure = getSelectedStructure(ideo);
if (structure === null) return; // Bail for e.g. miRNA
addSpliceToggleListeners(ideo);
addHoverListeners(ideo);
addMenuListeners(ideo);
writeStrandInFooter(ideo);
initTippy(ideo);
}

function getSpliceToggleHoverTitle(spliceExons) {
Expand All @@ -556,7 +612,8 @@ function getSpliceToggle(ideo) {
const cls = `class="_ideoSpliceToggle ${modifier}mRNA"`;
const checked = spliceExons ? 'checked' : '';
const text = getSpliceToggleHoverTitle(spliceExons);
const title = `title="${text}"`;
const tPlace = 'data-tippy-placement="right"';
const title = `data-tippy-content="${text}" ${tPlace}`;
const inputAttrs =
`type="checkbox" ${checked} ` +
`style="display: none;"`;
Expand Down Expand Up @@ -734,6 +791,8 @@ function toggleSplice(ideo) {
const transcriptLengthBp = getTranscriptLengthBp(subparts, spliceExons);
const prettyLength = transcriptLengthBp.toLocaleString();
tlbpDOM.innerText = `${prettyLength} bp`;

ideo.tippy[0].show();
});
}

Expand Down Expand Up @@ -834,7 +893,10 @@ function getSvg(geneStructure, ideo, spliceExons=false) {
const spliceToggle = document.querySelector('._ideoSpliceToggle');
if (spliceToggle) {
const title = getSpliceToggleHoverTitle(spliceExons);
spliceToggle.title = title;
spliceToggle.setAttribute('data-tippy-placement', 'right');
// spliceToggle.setAttribute('data-tippy-placement-fallback', 'top-end');
spliceToggle.setAttribute('data-tippy-content', title);
initTippy(ideo);
}

const featureLengthPx = 250 - 2; // Snip to avoid overextending
Expand Down Expand Up @@ -978,10 +1040,13 @@ function getMenuArrows() {
const downStyle = `style="${style}; margin-left: 5px;"`;
const upStyle = `style="${style}; margin-left: 2px;"`;
const cls = 'class="_ideoMenuArrow"';
const downTitle = `<title>Next transcript (down arrow)</title>`;
const upTitle = `<title>Previous transcript (up arrow)</title>`;
const downAttrs = `${downStyle} ${cls} data-dir="down"`;
const upAttrs = `${upStyle} ${cls} data-dir="up"`;
const tippyPlace = 'data-tippy-placement="bottom-start"';
const downContent = 'Next transcript (down arrow)';
const downTippy = `data-tippy-content="${downContent}" ${tippyPlace}`;
const upContent = 'Previous transcript (up arrow)';
const upTippy = `data-tippy-content="${upContent}" ${tippyPlace}`;
const downAttrs = `${downStyle} ${cls} data-dir="down" ${downTippy}`;
const upAttrs = `${upStyle} ${cls} data-dir="up" ${upTippy}`;

// Get SVG polygon elements
const down = getIcon(
Expand All @@ -990,8 +1055,8 @@ function getMenuArrows() {
);
const up = getIcon({shape: 'triangle', color: '#888'}, {config: ''});

const downArrow = `<svg ${downAttrs}>${downTitle}${down}</svg>`;
const upArrow = `<svg ${upAttrs}>${upTitle}${up}</svg>`;
const downArrow = `<svg ${downAttrs}>${down}</svg>`;
const upArrow = `<svg ${upAttrs}>${up}</svg>`;
const menuArrows = downArrow + upArrow;

return menuArrows;
Expand Down
Loading