| | --- |
| | 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> |
| |
|
| | <style> |
| | |
| | |
| | |
| | |
| | .glossary-container { |
| | display: inline; |
| | position: relative; |
| | } |
| | |
| | .glossary-term { |
| | color: var(--primary-color) !important; |
| | text-decoration: none !important; |
| | background: color-mix(in srgb, var(--primary-color) 15%, transparent); |
| | border-bottom: 1px dashed |
| | color-mix(in srgb, var(--primary-color) 100%, transparent) !important; |
| | cursor: help; |
| | transition: all 0.2s ease; |
| | border-radius: 3px; |
| | margin: 0 2px; |
| | padding: 4px 8px; |
| | } |
| | |
| | .glossary-term:hover, |
| | .glossary-term:focus { |
| | color: var(--primary-color-hover) !important; |
| | text-decoration: none !important; |
| | background: color-mix(in srgb, var(--primary-color) 20%, transparent); |
| | outline: none; |
| | } |
| | |
| | .glossary-term:focus { |
| | box-shadow: 0 0 0 2px |
| | color-mix(in srgb, var(--primary-color) 20%, transparent); |
| | } |
| | |
| | .glossary-tooltip { |
| | position: fixed; |
| | top: -9999px; |
| | left: -9999px; |
| | z-index: var(--z-tooltip); |
| | opacity: 0; |
| | transform: translateY(-4px); |
| | transition: |
| | opacity 0.2s ease, |
| | transform 0.2s ease; |
| | pointer-events: none; |
| | max-width: 300px; |
| | min-width: 200px; |
| | } |
| | |
| | .glossary-tooltip.is-visible { |
| | opacity: 1; |
| | transform: translateY(0); |
| | pointer-events: auto; |
| | } |
| | |
| | .glossary-tooltip__content { |
| | background: var(--surface-bg); |
| | border: 1px solid var(--border-color); |
| | border-radius: 8px; |
| | padding: 12px 16px; |
| | box-shadow: |
| | 0 8px 32px rgba(0, 0, 0, 0.12), |
| | 0 2px 8px rgba(0, 0, 0, 0.06); |
| | backdrop-filter: saturate(1.12) blur(8px); |
| | } |
| | |
| | .glossary-tooltip__term { |
| | font-weight: 600; |
| | font-size: 14px; |
| | color: var(--primary-color); |
| | margin-bottom: 4px; |
| | line-height: 1.3; |
| | } |
| | |
| | .glossary-tooltip__definition { |
| | font-size: 13px; |
| | color: var(--text-color); |
| | line-height: 1.4; |
| | margin: 0; |
| | } |
| | |
| | .glossary-tooltip__arrow { |
| | position: absolute; |
| | width: 0; |
| | height: 0; |
| | border: 6px solid transparent; |
| | } |
| | |
| | |
| | .glossary-tooltip[data-position="top"] .glossary-tooltip__arrow { |
| | bottom: -6px; |
| | left: 50%; |
| | transform: translateX(-50%); |
| | border-top-color: var(--border-color); |
| | } |
| | |
| | .glossary-tooltip[data-position="top"] .glossary-tooltip__arrow::after { |
| | content: ""; |
| | position: absolute; |
| | top: -7px; |
| | left: -6px; |
| | border: 6px solid transparent; |
| | border-top-color: var(--surface-bg); |
| | } |
| | |
| | .glossary-tooltip[data-position="bottom"] .glossary-tooltip__arrow { |
| | top: -6px; |
| | left: 50%; |
| | transform: translateX(-50%); |
| | border-bottom-color: var(--border-color); |
| | } |
| | |
| | .glossary-tooltip[data-position="bottom"] .glossary-tooltip__arrow::after { |
| | content: ""; |
| | position: absolute; |
| | top: -5px; |
| | left: -6px; |
| | border: 6px solid transparent; |
| | border-bottom-color: var(--surface-bg); |
| | } |
| | |
| | .glossary-tooltip[data-position="left"] .glossary-tooltip__arrow { |
| | right: -6px; |
| | top: 50%; |
| | transform: translateY(-50%); |
| | border-left-color: var(--border-color); |
| | } |
| | |
| | .glossary-tooltip[data-position="left"] .glossary-tooltip__arrow::after { |
| | content: ""; |
| | position: absolute; |
| | top: -6px; |
| | left: -7px; |
| | border: 6px solid transparent; |
| | border-left-color: var(--surface-bg); |
| | } |
| | |
| | .glossary-tooltip[data-position="right"] .glossary-tooltip__arrow { |
| | left: -6px; |
| | top: 50%; |
| | transform: translateY(-50%); |
| | border-right-color: var(--border-color); |
| | } |
| | |
| | .glossary-tooltip[data-position="right"] .glossary-tooltip__arrow::after { |
| | content: ""; |
| | position: absolute; |
| | top: -6px; |
| | left: -5px; |
| | border: 6px solid transparent; |
| | border-right-color: var(--surface-bg); |
| | } |
| | |
| | |
| | [data-theme="dark"] .glossary-tooltip__content { |
| | background: var(--surface-bg); |
| | border-color: var(--border-color); |
| | } |
| | |
| | [data-theme="dark"] .glossary-tooltip__term { |
| | color: var(--primary-color); |
| | } |
| | |
| | [data-theme="dark"] .glossary-tooltip__definition { |
| | color: var(--text-color); |
| | } |
| | |
| | |
| | @media (max-width: 768px) { |
| | .glossary-term[data-glossary-disable-mobile="true"] { |
| | border-bottom: none; |
| | color: inherit; |
| | cursor: default; |
| | } |
| | |
| | .glossary-term[data-glossary-disable-mobile="true"]:hover, |
| | .glossary-term[data-glossary-disable-mobile="true"]:focus { |
| | background: none; |
| | color: inherit; |
| | border-bottom: none; |
| | } |
| | } |
| | |
| | |
| | @media (prefers-reduced-motion: reduce) { |
| | .glossary-tooltip { |
| | transition: none; |
| | } |
| | |
| | .glossary-term { |
| | transition: none; |
| | } |
| | } |
| | </style> |
| |
|