@@ -59,6 +59,12 @@ function updateSelectionLabel(label: HTMLElement) {
5959 }
6060}
6161
62+ function processMenuItems ( $dropdown , dropdownCall ) {
63+ const hideEmptyDividers = dropdownCall ( 'setting' , 'hideDividers' ) === 'empty' ;
64+ const itemsMenu = $dropdown [ 0 ] . querySelector ( '.scrolling.menu' ) || $dropdown [ 0 ] . querySelector ( '.menu' ) ;
65+ if ( hideEmptyDividers ) hideScopedEmptyDividers ( itemsMenu ) ;
66+ }
67+
6268// delegate the dropdown's template functions and callback functions to add aria attributes.
6369function delegateOne ( $dropdown : any ) {
6470 const dropdownCall = fomanticDropdownFn . bind ( $dropdown ) ;
@@ -72,6 +78,18 @@ function delegateOne($dropdown: any) {
7278 // * If the "dropdown icon" is clicked again when the menu is visible, Fomantic calls "blurSearch", so hide the menu
7379 dropdownCall ( 'internal' , 'blurSearch' , function ( ) { oldBlurSearch . call ( this ) ; dropdownCall ( 'hide' ) } ) ;
7480
81+ const oldFilterItems = dropdownCall ( 'internal' , 'filterItems' ) ;
82+ dropdownCall ( 'internal' , 'filterItems' , function ( ...args : any [ ] ) {
83+ oldFilterItems . call ( this , ...args ) ;
84+ processMenuItems ( $dropdown , dropdownCall ) ;
85+ } ) ;
86+
87+ const oldShow = dropdownCall ( 'internal' , 'show' ) ;
88+ dropdownCall ( 'internal' , 'show' , function ( ...args : any [ ] ) {
89+ oldShow . call ( this , ...args ) ;
90+ processMenuItems ( $dropdown , dropdownCall ) ;
91+ } ) ;
92+
7593 // the "template" functions are used for dynamic creation (eg: AJAX)
7694 const dropdownTemplates = { ...dropdownCall ( 'setting' , 'templates' ) , t : performance . now ( ) } ;
7795 const dropdownTemplatesMenuOld = dropdownTemplates . menu ;
@@ -271,3 +289,65 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT
271289 ignoreClickPreEvents = ignoreClickPreVisible = 0 ;
272290 } , true ) ;
273291}
292+
293+ // Although Fomantic Dropdown supports "hideDividers", it doesn't really work with our "scoped dividers"
294+ // At the moment, "label dropdown items" use scopes, a sample case is:
295+ // * a-label
296+ // * divider
297+ // * scope/1
298+ // * scope/2
299+ // * divider
300+ // * z-label
301+ // when the "scope/*" are filtered out, we'd like to see "a-label" and "z-label" without the divider.
302+ export function hideScopedEmptyDividers ( container : Element ) {
303+ const visibleItems : Element [ ] = [ ] ;
304+ const curScopeVisibleItems : Element [ ] = [ ] ;
305+ let curScope : string = '' , lastVisibleScope : string = '' ;
306+ const isScopedDivider = ( item : Element ) => item . matches ( '.divider' ) && item . hasAttribute ( 'data-scope' ) ;
307+ const hideDivider = ( item : Element ) => item . classList . add ( 'hidden' , 'transition' ) ; // dropdown has its own classes to hide items
308+
309+ const handleScopeSwitch = ( itemScope : string ) => {
310+ if ( curScopeVisibleItems . length === 1 && isScopedDivider ( curScopeVisibleItems [ 0 ] ) ) {
311+ hideDivider ( curScopeVisibleItems [ 0 ] ) ;
312+ } else if ( curScopeVisibleItems . length ) {
313+ if ( isScopedDivider ( curScopeVisibleItems [ 0 ] ) && lastVisibleScope === curScope ) {
314+ hideDivider ( curScopeVisibleItems [ 0 ] ) ;
315+ curScopeVisibleItems . shift ( ) ;
316+ }
317+ visibleItems . push ( ...curScopeVisibleItems ) ;
318+ lastVisibleScope = curScope ;
319+ }
320+ curScope = itemScope ;
321+ curScopeVisibleItems . length = 0 ;
322+ } ;
323+
324+ // hide the scope dividers if the scope items are empty
325+ for ( const item of container . children ) {
326+ const itemScope = item . getAttribute ( 'data-scope' ) || '' ;
327+ if ( itemScope !== curScope ) {
328+ handleScopeSwitch ( itemScope ) ;
329+ }
330+ if ( ! item . classList . contains ( 'filtered' ) && ! item . classList . contains ( 'tw-hidden' ) ) {
331+ curScopeVisibleItems . push ( item as HTMLElement ) ;
332+ }
333+ }
334+ handleScopeSwitch ( '' ) ;
335+
336+ // hide all leading and trailing dividers
337+ while ( visibleItems . length ) {
338+ if ( ! visibleItems [ 0 ] . matches ( '.divider' ) ) break ;
339+ hideDivider ( visibleItems [ 0 ] ) ;
340+ visibleItems . shift ( ) ;
341+ }
342+ while ( visibleItems . length ) {
343+ if ( ! visibleItems [ visibleItems . length - 1 ] . matches ( '.divider' ) ) break ;
344+ hideDivider ( visibleItems [ visibleItems . length - 1 ] ) ;
345+ visibleItems . pop ( ) ;
346+ }
347+ // hide all duplicate dividers, hide current divider if next sibling is still divider
348+ // no need to update "visibleItems" array since this is the last loop
349+ for ( const item of visibleItems ) {
350+ if ( ! item . matches ( '.divider' ) ) continue ;
351+ if ( item . nextElementSibling ?. matches ( '.divider' ) ) hideDivider ( item ) ;
352+ }
353+ }
0 commit comments