@@ -44,6 +44,7 @@ void _setUpSite() {
4444 _setUpExpandableCards ();
4545 _setUpPlatformKeys ();
4646 _setUpToc ();
47+ _setUpTooltips ();
4748}
4849
4950void _setUpSearchKeybindings () {
@@ -448,3 +449,93 @@ void _setUpTocActiveObserver() {
448449 observer.observe (headings.item (i) as web.Element );
449450 }
450451}
452+
453+ void _setUpTooltips () {
454+ final tooltipWrappers = web.document.querySelectorAll ('.tooltip-wrapper' );
455+
456+ final isTouchscreen = web.window.matchMedia ('(pointer: coarse)' ).matches;
457+
458+ void setup ({required bool setUpClickListener}) {
459+ for (var i = 0 ; i < tooltipWrappers.length; i++ ) {
460+ final linkWrapper = tooltipWrappers.item (i) as web.HTMLElement ;
461+ final target = linkWrapper.querySelector ('.tooltip-target' );
462+ final tooltip = linkWrapper.querySelector ('.tooltip' ) as web.HTMLElement ? ;
463+
464+ if (target == null || tooltip == null ) {
465+ continue ;
466+ }
467+ _ensureVisible (tooltip);
468+
469+ if (setUpClickListener && isTouchscreen) {
470+ // On touchscreen devices, toggle tooltip visibility on tap.
471+ target.addEventListener (
472+ 'click' ,
473+ ((web.Event e) {
474+ final isVisible = tooltip.classList.contains ('visible' );
475+ if (! isVisible) {
476+ tooltip.classList.add ('visible' );
477+ e.preventDefault ();
478+ }
479+ }).toJS,
480+ );
481+ }
482+ }
483+ }
484+
485+ void closeAll () {
486+ final visibleTooltips = web.document.querySelectorAll (
487+ '.tooltip.visible' ,
488+ );
489+ for (var i = 0 ; i < visibleTooltips.length; i++ ) {
490+ final tooltip = visibleTooltips.item (i) as web.HTMLElement ;
491+ tooltip.classList.remove ('visible' );
492+ }
493+ }
494+
495+ setup (setUpClickListener: true );
496+
497+ // Reposition tooltips on window resize.
498+ web.EventStreamProviders .resizeEvent.forTarget (web.window).listen ((_) {
499+ setup (setUpClickListener: false );
500+ });
501+
502+ // Close tooltips when clicking outside of any tooltip wrapper.
503+ web.EventStreamProviders .clickEvent.forTarget (web.document).listen ((e) {
504+ if ((e.target as web.Element ).closest ('.tooltip-wrapper' ) == null ) {
505+ closeAll ();
506+ }
507+ });
508+
509+ // On touchscreen devices, close tooltips when scrolling.
510+ if (isTouchscreen) {
511+ web.EventStreamProviders .scrollEvent.forTarget (web.window).listen ((_) {
512+ closeAll ();
513+ });
514+ }
515+ }
516+
517+ /// Adjust the tooltip position to ensure it is fully inside the
518+ /// ancestor .content element.
519+ void _ensureVisible (web.HTMLElement tooltip) {
520+ final containerRect = tooltip.closest ('.content' )? .getBoundingClientRect ();
521+ final tooltipRect = tooltip.getBoundingClientRect ();
522+ final offset = double .parse (tooltip.getAttribute ('data-adjusted' ) ?? '0' );
523+
524+ final tooltipLeft = tooltipRect.left - offset;
525+ final tooltipRight = tooltipRect.right - offset;
526+ final containerLeft = containerRect? .left ?? 0.0 ;
527+ final containerRight = containerRect? .right ?? web.window.innerWidth;
528+
529+ if (tooltipLeft < containerLeft) {
530+ final offset = containerLeft - tooltipLeft;
531+ tooltip.style.left = 'calc(50% + ${offset }px)' ;
532+ tooltip.dataset['adjusted' ] = offset.toString ();
533+ } else if (tooltipRight > containerRight) {
534+ final offset = tooltipRight - containerRight;
535+ tooltip.style.left = 'calc(50% - ${offset }px)' ;
536+ tooltip.dataset['adjusted' ] = (- offset).toString ();
537+ } else {
538+ tooltip.style.left = '50%' ;
539+ tooltip.dataset['adjusted' ] = '0' ;
540+ }
541+ }
0 commit comments