|
|
--- |
|
|
interface Props { |
|
|
|
|
|
term: string; |
|
|
|
|
|
definition: string; |
|
|
|
|
|
class?: string; |
|
|
|
|
|
style?: string; |
|
|
|
|
|
position?: 'top' | 'bottom' | 'left' | 'right'; |
|
|
|
|
|
delay?: number; |
|
|
|
|
|
disableOnMobile?: boolean; |
|
|
} |
|
|
|
|
|
const { |
|
|
term, |
|
|
definition, |
|
|
class: className = '', |
|
|
style: inlineStyle = '', |
|
|
position = 'top', |
|
|
delay = 300, |
|
|
disableOnMobile = false, |
|
|
} = Astro.props as Props; |
|
|
|
|
|
|
|
|
const tooltipId = `glossary-${Math.random().toString(36).slice(2)}`; |
|
|
--- |
|
|
|
|
|
<div class="glossary-container" data-glossary-container-id={tooltipId}> |
|
|
<span |
|
|
class={`glossary-term ${className}`} |
|
|
style={inlineStyle} |
|
|
data-glossary-term={term} |
|
|
data-glossary-definition={definition} |
|
|
data-glossary-position={position} |
|
|
data-glossary-delay={delay} |
|
|
data-glossary-disable-mobile={disableOnMobile} |
|
|
data-glossary-id={tooltipId} |
|
|
tabindex="0" |
|
|
role="button" |
|
|
aria-describedby={`${tooltipId}-tooltip`} |
|
|
> |
|
|
{term} |
|
|
</span> |
|
|
|
|
|
<div |
|
|
id={`${tooltipId}-tooltip`} |
|
|
class="glossary-tooltip" |
|
|
data-glossary-tooltip-id={tooltipId} |
|
|
data-position={position} |
|
|
role="tooltip" |
|
|
aria-hidden="true" |
|
|
> |
|
|
<div class="glossary-tooltip__content"> |
|
|
<div class="glossary-tooltip__term">{term}</div> |
|
|
<div class="glossary-tooltip__definition">{definition}</div> |
|
|
</div> |
|
|
<div class="glossary-tooltip__arrow"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script is:inline> |
|
|
|
|
|
if (!window.glossaryInitialized) { |
|
|
window.glossaryInitialized = true; |
|
|
|
|
|
function initAllGlossaryTooltips() { |
|
|
const glossaryTerms = document.querySelectorAll('.glossary-term'); |
|
|
|
|
|
glossaryTerms.forEach((termElement) => { |
|
|
const tooltipElement = termElement.parentElement.querySelector('.glossary-tooltip'); |
|
|
|
|
|
if (!tooltipElement) return; |
|
|
|
|
|
const term = termElement.getAttribute('data-glossary-term'); |
|
|
const definition = termElement.getAttribute('data-glossary-definition'); |
|
|
|
|
|
if (!term || !definition) return; |
|
|
|
|
|
|
|
|
const showTooltip = (event) => { |
|
|
tooltipElement.style.display = 'block'; |
|
|
tooltipElement.style.opacity = '1'; |
|
|
tooltipElement.style.position = 'fixed'; |
|
|
tooltipElement.style.top = (event.clientY + 10) + 'px'; |
|
|
tooltipElement.style.left = (event.clientX + 10) + 'px'; |
|
|
tooltipElement.style.zIndex = '9999'; |
|
|
tooltipElement.style.pointerEvents = 'none'; |
|
|
}; |
|
|
|
|
|
const hideTooltip = () => { |
|
|
tooltipElement.style.display = 'none'; |
|
|
tooltipElement.style.opacity = '0'; |
|
|
}; |
|
|
|
|
|
|
|
|
termElement.addEventListener('mouseenter', showTooltip); |
|
|
termElement.addEventListener('mouseleave', hideTooltip); |
|
|
termElement.addEventListener('mousemove', showTooltip); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (document.readyState === 'loading') { |
|
|
document.addEventListener('DOMContentLoaded', initAllGlossaryTooltips); |
|
|
} else { |
|
|
initAllGlossaryTooltips(); |
|
|
} |
|
|
|
|
|
|
|
|
if (window.MutationObserver) { |
|
|
const observer = new MutationObserver((mutations) => { |
|
|
mutations.forEach((mutation) => { |
|
|
if (mutation.type === 'childList') { |
|
|
mutation.addedNodes.forEach((node) => { |
|
|
if (node.nodeType === 1 && node.querySelector && node.querySelector('.glossary-term')) { |
|
|
initAllGlossaryTooltips(); |
|
|
} |
|
|
}); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
observer.observe(document.body, { |
|
|
childList: true, |
|
|
subtree: true |
|
|
}); |
|
|
} |
|
|
} |
|
|
</script> |
|
|
|
|
|
|