@@ -68,6 +68,8 @@ export type View = typeof DEFAULT_VIEW;
6868export class GlobalAiButton extends UI . Widget . Widget {
6969 #view: View ;
7070 #buttonState: GlobalAiButtonState = GlobalAiButtonState . DEFAULT ;
71+ #mouseOnMainToolbar = false ;
72+ #returnToDefaultStateTimeout?: number ;
7173
7274 constructor ( element ?: HTMLElement , view ?: View ) {
7375 super ( element ) ;
@@ -79,6 +81,36 @@ export class GlobalAiButton extends UI.Widget.Widget {
7981 }
8082 }
8183
84+ override willHide ( ) : void {
85+ this . #removeHoverEventListeners( ) ;
86+
87+ if ( this . #returnToDefaultStateTimeout) {
88+ window . clearTimeout ( this . #returnToDefaultStateTimeout) ;
89+ }
90+ }
91+
92+ #handleMainToolbarMouseEnter = ( ) : void => {
93+ this . #mouseOnMainToolbar = true ;
94+ } ;
95+
96+ #handleMainToolbarMouseLeave = ( ) : void => {
97+ this . #mouseOnMainToolbar = false ;
98+ } ;
99+
100+ #addHoverEventListeners( ) : void {
101+ UI . InspectorView . InspectorView . instance ( ) . tabbedPane . headerElement ( ) . addEventListener (
102+ 'mouseenter' , this . #handleMainToolbarMouseEnter) ;
103+ UI . InspectorView . InspectorView . instance ( ) . tabbedPane . headerElement ( ) . addEventListener (
104+ 'mouseleave' , this . #handleMainToolbarMouseLeave) ;
105+ }
106+
107+ #removeHoverEventListeners( ) : void {
108+ UI . InspectorView . InspectorView . instance ( ) . tabbedPane . headerElement ( ) . removeEventListener (
109+ 'mouseenter' , this . #handleMainToolbarMouseEnter) ;
110+ UI . InspectorView . InspectorView . instance ( ) . tabbedPane . headerElement ( ) . removeEventListener (
111+ 'mouseleave' , this . #handleMainToolbarMouseLeave) ;
112+ }
113+
82114 // We only want to enable promotion when:
83115 // * The flag is enabled,
84116 // * The current date is before the promotion end date,
@@ -90,11 +122,33 @@ export class GlobalAiButton extends UI.Widget.Widget {
90122 }
91123
92124 #triggerPromotion( ) : void {
125+ // Set up hover listeners for making sure that we don't return to default state from promotion state
126+ // when the user's cursor is on the main toolbar.
93127 this . #buttonState = GlobalAiButtonState . PROMOTION ;
94128 this . requestUpdate ( ) ;
95- window . setTimeout ( ( ) => {
129+ this . #addHoverEventListeners( ) ;
130+ this . #scheduleReturnToDefaultState( ) ;
131+ }
132+
133+ #scheduleReturnToDefaultState( ) : void {
134+ if ( this . #returnToDefaultStateTimeout) {
135+ window . clearTimeout ( this . #returnToDefaultStateTimeout) ;
136+ }
137+
138+ this . #returnToDefaultStateTimeout = window . setTimeout ( ( ) => {
139+ // If the mouse is currently on the main toolbar,
140+ // we don't want to trigger the animation from promotion & to the default
141+ // state to not cause a layout shift when the user is not expecting it
142+ // (e.g. while they were going to click on a button on the toolbar).
143+ if ( this . #mouseOnMainToolbar) {
144+ this . #scheduleReturnToDefaultState( ) ;
145+ return ;
146+ }
147+
96148 this . #buttonState = GlobalAiButtonState . DEFAULT ;
97149 this . requestUpdate ( ) ;
150+ // Remove hover listeners once the button is in its default state.
151+ this . #removeHoverEventListeners( ) ;
98152 } , DELAY_BEFORE_PROMOTION_COLLAPSE_IN_MS ) ;
99153 }
100154
0 commit comments