@@ -46,6 +46,7 @@ void _setUpSite() {
4646 _setUpExpandableCards ();
4747 _setUpTableOfContents ();
4848 _setUpReleaseTags ();
49+ _setUpTooltips ();
4950}
5051
5152void _setUpSidenav () {
@@ -543,3 +544,93 @@ void _setUpReleaseTags() {
543544 fetchVersion ('beta' );
544545 fetchVersion ('dev' );
545546}
547+
548+ void _setUpTooltips () {
549+ final tooltipWrappers = web.document.querySelectorAll ('.tooltip-wrapper' );
550+
551+ final isTouchscreen = web.window.matchMedia ('(pointer: coarse)' ).matches;
552+
553+ void setup (bool setUpClickListener) {
554+ for (var i = 0 ; i < tooltipWrappers.length; i++ ) {
555+ final linkWrapper = tooltipWrappers.item (i) as web.HTMLElement ;
556+ final target = linkWrapper.querySelector ('.tooltip-target' );
557+ final tooltip = linkWrapper.querySelector ('.tooltip' ) as web.HTMLElement ? ;
558+
559+ if (target == null || tooltip == null ) {
560+ continue ;
561+ }
562+ _ensureVisible (tooltip);
563+
564+ if (setUpClickListener && isTouchscreen) {
565+ // On touchscreen devices, toggle tooltip visibility on tap.
566+ target.addEventListener (
567+ 'click' ,
568+ ((web.Event e) {
569+ final isVisible = tooltip.classList.contains ('visible' );
570+ if (isVisible) {
571+ tooltip.classList.remove ('visible' );
572+ } else {
573+ tooltip.classList.add ('visible' );
574+ }
575+ e.preventDefault ();
576+ }).toJS,
577+ );
578+ }
579+ }
580+ }
581+
582+ void closeAll () {
583+ final visibleTooltips = web.document.querySelectorAll (
584+ '.tooltip.visible' ,
585+ );
586+ for (var i = 0 ; i < visibleTooltips.length; i++ ) {
587+ final tooltip = visibleTooltips.item (i) as web.HTMLElement ;
588+ tooltip.classList.remove ('visible' );
589+ }
590+ }
591+
592+ setup (true );
593+
594+ // Reposition tooltips on window resize.
595+ web.EventStreamProviders .resizeEvent.forTarget (web.window).listen ((_) {
596+ setup (false );
597+ });
598+
599+ // Close tooltips when clicking outside of any tooltip wrapper.
600+ web.EventStreamProviders .clickEvent.forTarget (web.document).listen ((e) {
601+ if ((e.target as web.Element ).closest ('.tooltip-wrapper' ) == null ) {
602+ closeAll ();
603+ }
604+ });
605+
606+ // On touchscreen devices, close tooltips when scrolling.
607+ if (isTouchscreen) {
608+ web.EventStreamProviders .scrollEvent.forTarget (web.window).listen ((_) {
609+ closeAll ();
610+ });
611+ }
612+ }
613+
614+ /// Adjust the tooltip position to ensure it is fully inside the
615+ /// ancestor .content element.
616+ void _ensureVisible (web.HTMLElement tooltip) {
617+ final containerRect = tooltip.closest ('.content' )! .getBoundingClientRect ();
618+ final tooltipRect = tooltip.getBoundingClientRect ();
619+ final offset = double .parse (tooltip.getAttribute ('data-adjusted' ) ?? '0' );
620+
621+ final tooltipLeft = tooltipRect.left - offset;
622+ final tooltipRight = tooltipRect.right - offset;
623+
624+ if (tooltipLeft < containerRect.left) {
625+ final offset = containerRect.left - tooltipLeft;
626+ tooltip.style.left = 'calc(50% + ${offset }px)' ;
627+ tooltip.dataset['adjusted' ] = offset.toString ();
628+ } else if (tooltipRight > containerRect.right) {
629+ final offset = tooltipRight - containerRect.right;
630+ tooltip.style.left = 'calc(50% - ${offset }px)' ;
631+ tooltip.dataset['adjusted' ] = (- offset).toString ();
632+ } else {
633+ tooltip.style.left = '50%' ;
634+ tooltip.dataset['adjusted' ] = '0' ;
635+ }
636+ }
0 commit comments