Download atlas.js from HuggingFaceBio/carbon-a-database-explorer: direct link, hf CLI and curl.
- Browser
- Download file 3.05 kB
-
https://huggingface.co/spaces/HuggingFaceBio/carbon-a-database-explorer/resolve/main/atlas.js
- Command line
-
hf download hf://spaces/HuggingFaceBio/carbon-a-database-explorer/atlas.js
-
curl -L -o atlas.js https://huggingface.co/spaces/HuggingFaceBio/carbon-a-database-explorer/resolve/main/atlas.js
3.05 kB
| // Delegate to the component root so navigation still works after HTML updates. | |
| function activate(event) { | |
| const target = event.target.closest('[data-path]'); | |
| if (!target || !element.contains(target)) return; | |
| if (event.type === 'keydown' && !['Enter', ' '].includes(event.key)) return; | |
| // Native buttons already synthesize a click on keyboard activation. | |
| if (event.type === 'keydown' && target.tagName.toLowerCase() === 'button') return; | |
| event.preventDefault(); | |
| trigger('click', {route: target.dataset.path}); | |
| } | |
| element.addEventListener('click', activate); | |
| element.addEventListener('keydown', activate); | |
| // A group and the flux arriving at it share a data-node id, so hovering either | |
| // one lights both. The ribbons are painted beneath every group, so no CSS | |
| // sibling rule can reach them from the group being hovered. | |
| let lit = ''; | |
| function light(uid) { | |
| if (uid === lit) return; | |
| lit = uid; | |
| element.querySelectorAll('.is-lit').forEach((el) => el.classList.remove('is-lit')); | |
| if (uid) element.querySelectorAll(`[data-node="${CSS.escape(uid)}"]`).forEach((el) => el.classList.add('is-lit')); | |
| } | |
| element.addEventListener('focusin', (event) => light(event.target.closest('[data-node]')?.dataset.node || '')); | |
| element.addEventListener('focusout', () => light('')); | |
| element.addEventListener('pointermove', (event) => { | |
| const node = event.target.closest('[data-detail]'); | |
| const tooltip = element.querySelector('.atlas-tooltip'); | |
| const atlas = element.querySelector('.atlas'); | |
| if (!tooltip || !atlas) return; | |
| if (!node || event.pointerType === 'touch') { tooltip.hidden = true; light(''); return; } | |
| light(node.dataset.node || ''); | |
| tooltip.textContent = node.dataset.detail; | |
| tooltip.hidden = false; | |
| const bounds = atlas.getBoundingClientRect(); | |
| tooltip.style.left = Math.max(8, Math.min(event.clientX - bounds.left + 15, bounds.width - 305)) + 'px'; | |
| tooltip.style.top = Math.max(8, Math.min(event.clientY - bounds.top + 18, bounds.height - tooltip.offsetHeight - 8)) + 'px'; | |
| }); | |
| element.addEventListener('pointerleave', () => { | |
| const tooltip = element.querySelector('.atlas-tooltip'); | |
| if (tooltip) tooltip.hidden = true; | |
| light(''); | |
| }); | |
| // Expanding adds a level below. The page scrolls, not the widget, so only nudge | |
| // it when the new bottom of the tree has fallen past the viewport. | |
| let shownRoute = element.querySelector('.life-tree')?.dataset.route; | |
| new MutationObserver(() => { | |
| const route = element.querySelector('.life-tree')?.dataset.route; | |
| if (!route || route === shownRoute) return; | |
| const grew = route.length > shownRoute.length; | |
| shownRoute = route; | |
| if (!grew) return; | |
| requestAnimationFrame(() => { | |
| const tree = element.querySelector('.life-tree'); | |
| if (!tree) return; | |
| const bottom = tree.getBoundingClientRect().bottom; | |
| if (bottom > window.innerHeight) { | |
| window.scrollBy({top: Math.min(bottom - window.innerHeight + 24, 400), behavior: 'smooth'}); | |
| } | |
| }); | |
| }).observe(element, {childList: true, subtree: true, attributes: true, attributeFilter: ['data-route']}); | |