11import { throttle } from 'throttle-debounce' ;
22import { createTippy } from '../modules/tippy.ts' ;
3- import { isDocumentFragmentOrElementNode } from '../utils/dom.ts' ;
3+ import { addDelegatedEventListener , isDocumentFragmentOrElementNode } from '../utils/dom.ts' ;
44import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg' ;
55
66window . customElements . define ( 'overflow-menu' , class extends HTMLElement {
@@ -12,10 +12,14 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
1212 mutationObserver : MutationObserver ;
1313 lastWidth : number ;
1414
15+ updateButtonActivationState ( ) {
16+ if ( ! this . button || ! this . tippyContent ) return ;
17+ this . button . classList . toggle ( 'active' , Boolean ( this . tippyContent . querySelector ( '.item.active' ) ) ) ;
18+ }
19+
1520 updateItems = throttle ( 100 , ( ) => {
1621 if ( ! this . tippyContent ) {
1722 const div = document . createElement ( 'div' ) ;
18- div . classList . add ( 'tippy-target' ) ;
1923 div . tabIndex = - 1 ; // for initial focus, programmatic focus only
2024 div . addEventListener ( 'keydown' , ( e ) => {
2125 if ( e . key === 'Tab' ) {
@@ -64,9 +68,10 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
6468 }
6569 }
6670 } ) ;
67- this . append ( div ) ;
71+ div . classList . add ( 'tippy-target' ) ;
72+ this . handleItemClick ( div , '.tippy-target > .item' ) ;
6873 this . tippyContent = div ;
69- }
74+ } // end if: no tippyContent and create a new one
7075
7176 const itemFlexSpace = this . menuItemsEl . querySelector < HTMLSpanElement > ( '.item-flex-space' ) ;
7277 const itemOverFlowMenuButton = this . querySelector < HTMLButtonElement > ( '.overflow-menu-button' ) ;
@@ -88,15 +93,18 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
8893 const menuRight = this . offsetLeft + this . offsetWidth ;
8994 const menuItems = this . menuItemsEl . querySelectorAll < HTMLElement > ( '.item, .item-flex-space' ) ;
9095 let afterFlexSpace = false ;
91- for ( const item of menuItems ) {
96+ for ( const [ idx , item ] of menuItems . entries ( ) ) {
9297 if ( item . classList . contains ( 'item-flex-space' ) ) {
9398 afterFlexSpace = true ;
9499 continue ;
95100 }
96101 if ( afterFlexSpace ) item . setAttribute ( 'data-after-flex-space' , 'true' ) ;
97102 const itemRight = item . offsetLeft + item . offsetWidth ;
98103 if ( menuRight - itemRight < 38 ) { // roughly the width of .overflow-menu-button with some extra space
99- this . tippyItems . push ( item ) ;
104+ const onlyLastItem = idx === menuItems . length - 1 && this . tippyItems . length === 0 ;
105+ const lastItemFit = onlyLastItem && menuRight - itemRight > 0 ;
106+ const moveToPopup = ! onlyLastItem || ! lastItemFit ;
107+ if ( moveToPopup ) this . tippyItems . push ( item ) ;
100108 }
101109 }
102110 itemFlexSpace ?. style . removeProperty ( 'display' ) ;
@@ -107,6 +115,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
107115 const btn = this . querySelector ( '.overflow-menu-button' ) ;
108116 btn ?. _tippy ?. destroy ( ) ;
109117 btn ?. remove ( ) ;
118+ this . button = null ;
110119 return ;
111120 }
112121
@@ -126,18 +135,17 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
126135 // update existing tippy
127136 if ( this . button ?. _tippy ) {
128137 this . button . _tippy . setContent ( this . tippyContent ) ;
138+ this . updateButtonActivationState ( ) ;
129139 return ;
130140 }
131141
132142 // create button initially
133- const btn = document . createElement ( 'button' ) ;
134- btn . classList . add ( 'overflow-menu-button' ) ;
135- btn . setAttribute ( 'aria-label' , window . config . i18n . more_items ) ;
136- btn . innerHTML = octiconKebabHorizontal ;
137- this . append ( btn ) ;
138- this . button = btn ;
139-
140- createTippy ( btn , {
143+ this . button = document . createElement ( 'button' ) ;
144+ this . button . classList . add ( 'overflow-menu-button' ) ;
145+ this . button . setAttribute ( 'aria-label' , window . config . i18n . more_items ) ;
146+ this . button . innerHTML = octiconKebabHorizontal ;
147+ this . append ( this . button ) ;
148+ createTippy ( this . button , {
141149 trigger : 'click' ,
142150 hideOnClick : true ,
143151 interactive : true ,
@@ -151,6 +159,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
151159 } , 0 ) ;
152160 } ,
153161 } ) ;
162+ this . updateButtonActivationState ( ) ;
154163 } ) ;
155164
156165 init ( ) {
@@ -187,6 +196,14 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
187196 }
188197 } ) ;
189198 this . resizeObserver . observe ( this ) ;
199+ this . handleItemClick ( this , '.overflow-menu-items > .item' ) ;
200+ }
201+
202+ handleItemClick ( el : Element , selector : string ) {
203+ addDelegatedEventListener ( el , 'click' , selector , ( ) => {
204+ this . button ?. _tippy ?. hide ( ) ;
205+ this . updateButtonActivationState ( ) ;
206+ } ) ;
190207 }
191208
192209 connectedCallback ( ) {
0 commit comments