lerobot/robot-learning-tutorial
508
1---2interface Props {3 /** The word or term to define */4 term: string;5 /** The definition of the term */6 definition: string;7 /** Optional CSS class to apply to the term */8 class?: string;9 /** Optional style to apply to the term */10 style?: string;11 /** Tooltip position (top, bottom, left, right) */12 position?: "top" | "bottom" | "left" | "right";13 /** Delay before showing tooltip in ms */14 delay?: number;15 /** Disable tooltip on mobile */16 disableOnMobile?: boolean;17}18 19const {20 term,21 definition,22 class: className = "",23 style: inlineStyle = "",24 position = "top",25 delay = 300,26 disableOnMobile = false,27} = Astro.props as Props;28 29// Generate a unique ID for this component30const tooltipId = `glossary-${Math.random().toString(36).slice(2)}`;31---32 33<div class="glossary-container" data-glossary-container-id={tooltipId}>34 <span35 class={`glossary-term ${className}`}36 style={inlineStyle}37 data-glossary-term={term}38 data-glossary-definition={definition}39 data-glossary-position={position}40 data-glossary-delay={delay}41 data-glossary-disable-mobile={disableOnMobile}42 data-glossary-id={tooltipId}43 tabindex="0"44 role="button"45 aria-describedby={`${tooltipId}-tooltip`}46 >47 {term}48 </span>49 50 <div51 id={`${tooltipId}-tooltip`}52 class="glossary-tooltip"53 data-glossary-tooltip-id={tooltipId}54 data-position={position}55 role="tooltip"56 aria-hidden="true"57 >58 <div class="glossary-tooltip__content">59 <div class="glossary-tooltip__term">{term}</div>60 <div class="glossary-tooltip__definition">{definition}</div>61 </div>62 <div class="glossary-tooltip__arrow"></div>63 </div>64</div>65 66<script is:inline>67 // Global script for all Glossary tooltips68 if (!window.glossaryInitialized) {69 window.glossaryInitialized = true;70 71 function initAllGlossaryTooltips() {72 const glossaryTerms = document.querySelectorAll(".glossary-term");73 74 glossaryTerms.forEach((termElement) => {75 const tooltipElement =76 termElement.parentElement.querySelector(".glossary-tooltip");77 78 if (!tooltipElement) return;79 80 const term = termElement.getAttribute("data-glossary-term");81 const definition = termElement.getAttribute("data-glossary-definition");82 83 if (!term || !definition) return;84 85 // Fonction pour afficher le tooltip au niveau de la souris86 const showTooltip = (event) => {87 tooltipElement.style.display = "block";88 tooltipElement.style.opacity = "1";89 tooltipElement.style.position = "fixed";90 tooltipElement.style.top = event.clientY + 10 + "px";91 tooltipElement.style.left = event.clientX + 10 + "px";92 tooltipElement.style.zIndex = "9999";93 tooltipElement.style.pointerEvents = "none";94 };95 96 const hideTooltip = () => {97 tooltipElement.style.display = "none";98 tooltipElement.style.opacity = "0";99 };100 101 // Add events102 termElement.addEventListener("mouseenter", showTooltip);103 termElement.addEventListener("mouseleave", hideTooltip);104 termElement.addEventListener("mousemove", showTooltip);105 });106 }107 108 // Initialize when DOM is ready109 if (document.readyState === "loading") {110 document.addEventListener("DOMContentLoaded", initAllGlossaryTooltips);111 } else {112 initAllGlossaryTooltips();113 }114 115 // Observe DOM changes for new elements116 if (window.MutationObserver) {117 const observer = new MutationObserver((mutations) => {118 mutations.forEach((mutation) => {119 if (mutation.type === "childList") {120 mutation.addedNodes.forEach((node) => {121 if (122 node.nodeType === 1 &&123 node.querySelector &&124 node.querySelector(".glossary-term")125 ) {126 initAllGlossaryTooltips();127 }128 });129 }130 });131 });132 133 observer.observe(document.body, {134 childList: true,135 subtree: true,136 });137 }138 }139</script>140 141<style>142 /* ============================================================================ */143 /* Glossary Component */144 /* ============================================================================ */145 146 .glossary-container {147 display: inline;148 position: relative;149 }150 151 .glossary-term {152 color: var(--primary-color) !important;153 text-decoration: none !important;154 background: color-mix(in srgb, var(--primary-color) 15%, transparent);155 border-bottom: 1px dashed156 color-mix(in srgb, var(--primary-color) 100%, transparent) !important;157 cursor: help;158 transition: all 0.2s ease;159 border-radius: 3px;160 margin: 0 2px;161 padding: 4px 8px;162 }163 164 .glossary-term:hover,165 .glossary-term:focus {166 color: var(--primary-color-hover) !important;167 text-decoration: none !important;168 background: color-mix(in srgb, var(--primary-color) 20%, transparent);169 outline: none;170 }171 172 .glossary-term:focus {173 box-shadow: 0 0 0 2px174 color-mix(in srgb, var(--primary-color) 20%, transparent);175 }176 177 .glossary-tooltip {178 position: fixed;179 top: -9999px;180 left: -9999px;181 z-index: var(--z-tooltip);182 opacity: 0;183 transform: translateY(-4px);184 transition:185 opacity 0.2s ease,186 transform 0.2s ease;187 pointer-events: none;188 max-width: 300px;189 min-width: 200px;190 }191 192 .glossary-tooltip.is-visible {193 opacity: 1;194 transform: translateY(0);195 pointer-events: auto;196 }197 198 .glossary-tooltip__content {199 background: var(--surface-bg);200 border: 1px solid var(--border-color);201 border-radius: 8px;202 padding: 12px 16px;203 box-shadow:204 0 8px 32px rgba(0, 0, 0, 0.12),205 0 2px 8px rgba(0, 0, 0, 0.06);206 backdrop-filter: saturate(1.12) blur(8px);207 }208 209 .glossary-tooltip__term {210 font-weight: 600;211 font-size: 14px;212 color: var(--primary-color);213 margin-bottom: 4px;214 line-height: 1.3;215 }216 217 .glossary-tooltip__definition {218 font-size: 13px;219 color: var(--text-color);220 line-height: 1.4;221 margin: 0;222 }223 224 .glossary-tooltip__arrow {225 position: absolute;226 width: 0;227 height: 0;228 border: 6px solid transparent;229 }230 231 /* Arrow positioning */232 .glossary-tooltip[data-position="top"] .glossary-tooltip__arrow {233 bottom: -6px;234 left: 50%;235 transform: translateX(-50%);236 border-top-color: var(--border-color);237 }238 239 .glossary-tooltip[data-position="top"] .glossary-tooltip__arrow::after {240 content: "";241 position: absolute;242 top: -7px;243 left: -6px;244 border: 6px solid transparent;245 border-top-color: var(--surface-bg);246 }247 248 .glossary-tooltip[data-position="bottom"] .glossary-tooltip__arrow {249 top: -6px;250 left: 50%;251 transform: translateX(-50%);252 border-bottom-color: var(--border-color);253 }254 255 .glossary-tooltip[data-position="bottom"] .glossary-tooltip__arrow::after {256 content: "";257 position: absolute;258 top: -5px;259 left: -6px;260 border: 6px solid transparent;261 border-bottom-color: var(--surface-bg);262 }263 264 .glossary-tooltip[data-position="left"] .glossary-tooltip__arrow {265 right: -6px;266 top: 50%;267 transform: translateY(-50%);268 border-left-color: var(--border-color);269 }270 271 .glossary-tooltip[data-position="left"] .glossary-tooltip__arrow::after {272 content: "";273 position: absolute;274 top: -6px;275 left: -7px;276 border: 6px solid transparent;277 border-left-color: var(--surface-bg);278 }279 280 .glossary-tooltip[data-position="right"] .glossary-tooltip__arrow {281 left: -6px;282 top: 50%;283 transform: translateY(-50%);284 border-right-color: var(--border-color);285 }286 287 .glossary-tooltip[data-position="right"] .glossary-tooltip__arrow::after {288 content: "";289 position: absolute;290 top: -6px;291 left: -5px;292 border: 6px solid transparent;293 border-right-color: var(--surface-bg);294 }295 296 /* Mode sombre */297 [data-theme="dark"] .glossary-tooltip__content {298 background: var(--surface-bg);299 border-color: var(--border-color);300 }301 302 [data-theme="dark"] .glossary-tooltip__term {303 color: var(--primary-color);304 }305 306 [data-theme="dark"] .glossary-tooltip__definition {307 color: var(--text-color);308 }309 310 /* Responsive - hide on mobile if disabled */311 @media (max-width: 768px) {312 .glossary-term[data-glossary-disable-mobile="true"] {313 border-bottom: none;314 color: inherit;315 cursor: default;316 }317 318 .glossary-term[data-glossary-disable-mobile="true"]:hover,319 .glossary-term[data-glossary-disable-mobile="true"]:focus {320 background: none;321 color: inherit;322 border-bottom: none;323 }324 }325 326 /* Accessibility improvement */327 @media (prefers-reduced-motion: reduce) {328 .glossary-tooltip {329 transition: none;330 }331 332 .glossary-term {333 transition: none;334 }335 }336</style>337 