@@ -46,6 +46,7 @@ void _setUpSite() {
4646 _setUpExpandableCards ();
4747 _setUpTableOfContents ();
4848 _setUpReleaseTags ();
49+ _setUpTooltips ();
4950}
5051
5152void _setUpSidenav () {
@@ -349,6 +350,7 @@ void _setUpExpandableCards() {
349350 currentFragment = currentFragment.substring (1 );
350351 }
351352 final expandableCards = web.document.querySelectorAll ('.expandable-card' );
353+ web.Element ? targetCard;
352354
353355 for (var i = 0 ; i < expandableCards.length; i++ ) {
354356 final card = expandableCards.item (i) as web.Element ;
@@ -372,8 +374,15 @@ void _setUpExpandableCards() {
372374 if (card.id != currentFragment) {
373375 card.classList.add ('collapsed' );
374376 expandButton.ariaExpanded = 'false' ;
377+ } else {
378+ targetCard = card;
375379 }
376380 }
381+
382+ if (targetCard != null ) {
383+ // Scroll the expanded card into view.
384+ targetCard.scrollIntoView ();
385+ }
377386}
378387
379388void _setUpTableOfContents () {
@@ -543,3 +552,93 @@ void _setUpReleaseTags() {
543552 fetchVersion ('beta' );
544553 fetchVersion ('dev' );
545554}
555+
556+ void _setUpTooltips () {
557+ final tooltipWrappers = web.document.querySelectorAll ('.tooltip-wrapper' );
558+
559+ final isTouchscreen = web.window.matchMedia ('(pointer: coarse)' ).matches;
560+
561+ void setup ({required bool setUpClickListener}) {
562+ for (var i = 0 ; i < tooltipWrappers.length; i++ ) {
563+ final linkWrapper = tooltipWrappers.item (i) as web.HTMLElement ;
564+ final target = linkWrapper.querySelector ('.tooltip-target' );
565+ final tooltip = linkWrapper.querySelector ('.tooltip' ) as web.HTMLElement ? ;
566+
567+ if (target == null || tooltip == null ) {
568+ continue ;
569+ }
570+ _ensureVisible (tooltip);
571+
572+ if (setUpClickListener && isTouchscreen) {
573+ // On touchscreen devices, toggle tooltip visibility on tap.
574+ target.addEventListener (
575+ 'click' ,
576+ ((web.Event e) {
577+ final isVisible = tooltip.classList.contains ('visible' );
578+ if (isVisible) {
579+ tooltip.classList.remove ('visible' );
580+ } else {
581+ tooltip.classList.add ('visible' );
582+ }
583+ e.preventDefault ();
584+ }).toJS,
585+ );
586+ }
587+ }
588+ }
589+
590+ void closeAll () {
591+ final visibleTooltips = web.document.querySelectorAll (
592+ '.tooltip.visible' ,
593+ );
594+ for (var i = 0 ; i < visibleTooltips.length; i++ ) {
595+ final tooltip = visibleTooltips.item (i) as web.HTMLElement ;
596+ tooltip.classList.remove ('visible' );
597+ }
598+ }
599+
600+ setup (setUpClickListener: true );
601+
602+ // Reposition tooltips on window resize.
603+ web.EventStreamProviders .resizeEvent.forTarget (web.window).listen ((_) {
604+ setup (setUpClickListener: false );
605+ });
606+
607+ // Close tooltips when clicking outside of any tooltip wrapper.
608+ web.EventStreamProviders .clickEvent.forTarget (web.document).listen ((e) {
609+ if ((e.target as web.Element ).closest ('.tooltip-wrapper' ) == null ) {
610+ closeAll ();
611+ }
612+ });
613+
614+ // On touchscreen devices, close tooltips when scrolling.
615+ if (isTouchscreen) {
616+ web.EventStreamProviders .scrollEvent.forTarget (web.window).listen ((_) {
617+ closeAll ();
618+ });
619+ }
620+ }
621+
622+ /// Adjust the tooltip position to ensure it is fully inside the
623+ /// ancestor .content element.
624+ void _ensureVisible (web.HTMLElement tooltip) {
625+ final containerRect = tooltip.closest ('.content' )! .getBoundingClientRect ();
626+ final tooltipRect = tooltip.getBoundingClientRect ();
627+ final offset = double .parse (tooltip.getAttribute ('data-adjusted' ) ?? '0' );
628+
629+ final tooltipLeft = tooltipRect.left - offset;
630+ final tooltipRight = tooltipRect.right - offset;
631+
632+ if (tooltipLeft < containerRect.left) {
633+ final offset = containerRect.left - tooltipLeft;
634+ tooltip.style.left = 'calc(50% + ${offset }px)' ;
635+ tooltip.dataset['adjusted' ] = offset.toString ();
636+ } else if (tooltipRight > containerRect.right) {
637+ final offset = tooltipRight - containerRect.right;
638+ tooltip.style.left = 'calc(50% - ${offset }px)' ;
639+ tooltip.dataset['adjusted' ] = (- offset).toString ();
640+ } else {
641+ tooltip.style.left = '50%' ;
642+ tooltip.dataset['adjusted' ] = '0' ;
643+ }
644+ }
0 commit comments