1
1
import { throttle } from 'throttle-debounce' ;
2
2
import { createTippy } from '../modules/tippy.ts' ;
3
- import { isDocumentFragmentOrElementNode } from '../utils/dom.ts' ;
3
+ import { addDelegatedEventListener , isDocumentFragmentOrElementNode } from '../utils/dom.ts' ;
4
4
import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg' ;
5
5
6
6
window . customElements . define ( 'overflow-menu' , class extends HTMLElement {
@@ -12,10 +12,14 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
12
12
mutationObserver : MutationObserver ;
13
13
lastWidth : number ;
14
14
15
+ updateButtonActivationState ( ) {
16
+ if ( ! this . button || ! this . tippyContent ) return ;
17
+ this . button . classList . toggle ( 'active' , Boolean ( this . tippyContent . querySelector ( '.item.active' ) ) ) ;
18
+ }
19
+
15
20
updateItems = throttle ( 100 , ( ) => {
16
21
if ( ! this . tippyContent ) {
17
22
const div = document . createElement ( 'div' ) ;
18
- div . classList . add ( 'tippy-target' ) ;
19
23
div . tabIndex = - 1 ; // for initial focus, programmatic focus only
20
24
div . addEventListener ( 'keydown' , ( e ) => {
21
25
if ( e . key === 'Tab' ) {
@@ -64,9 +68,10 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
64
68
}
65
69
}
66
70
} ) ;
67
- this . append ( div ) ;
71
+ div . classList . add ( 'tippy-target' ) ;
72
+ this . handleItemClick ( div , '.tippy-target > .item' ) ;
68
73
this . tippyContent = div ;
69
- }
74
+ } // end if: no tippyContent and create a new one
70
75
71
76
const itemFlexSpace = this . menuItemsEl . querySelector < HTMLSpanElement > ( '.item-flex-space' ) ;
72
77
const itemOverFlowMenuButton = this . querySelector < HTMLButtonElement > ( '.overflow-menu-button' ) ;
@@ -88,15 +93,18 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
88
93
const menuRight = this . offsetLeft + this . offsetWidth ;
89
94
const menuItems = this . menuItemsEl . querySelectorAll < HTMLElement > ( '.item, .item-flex-space' ) ;
90
95
let afterFlexSpace = false ;
91
- for ( const item of menuItems ) {
96
+ for ( const [ idx , item ] of menuItems . entries ( ) ) {
92
97
if ( item . classList . contains ( 'item-flex-space' ) ) {
93
98
afterFlexSpace = true ;
94
99
continue ;
95
100
}
96
101
if ( afterFlexSpace ) item . setAttribute ( 'data-after-flex-space' , 'true' ) ;
97
102
const itemRight = item . offsetLeft + item . offsetWidth ;
98
103
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 ) ;
100
108
}
101
109
}
102
110
itemFlexSpace ?. style . removeProperty ( 'display' ) ;
@@ -107,6 +115,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
107
115
const btn = this . querySelector ( '.overflow-menu-button' ) ;
108
116
btn ?. _tippy ?. destroy ( ) ;
109
117
btn ?. remove ( ) ;
118
+ this . button = null ;
110
119
return ;
111
120
}
112
121
@@ -126,18 +135,17 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
126
135
// update existing tippy
127
136
if ( this . button ?. _tippy ) {
128
137
this . button . _tippy . setContent ( this . tippyContent ) ;
138
+ this . updateButtonActivationState ( ) ;
129
139
return ;
130
140
}
131
141
132
142
// 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 , {
141
149
trigger : 'click' ,
142
150
hideOnClick : true ,
143
151
interactive : true ,
@@ -151,6 +159,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
151
159
} , 0 ) ;
152
160
} ,
153
161
} ) ;
162
+ this . updateButtonActivationState ( ) ;
154
163
} ) ;
155
164
156
165
init ( ) {
@@ -187,6 +196,14 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
187
196
}
188
197
} ) ;
189
198
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
+ } ) ;
190
207
}
191
208
192
209
connectedCallback ( ) {
0 commit comments