@@ -317,45 +317,121 @@ module.exports = {
317317 // render time so we can handle it properly if an individual
318318 // user only sees one of them, etc. Called by `afterInit`
319319
320+ // Fixed groupItems method that respects group registration order
321+
320322 groupItems ( ) {
321- // Implement the groups and addGroups options. Mark the grouped items
322- // with a `menuLeader` property.
323323 const groups = self . options . groups ||
324324 self . groups . concat ( self . options . addGroups || [ ] ) ;
325325
326- groups . forEach ( function ( group ) {
327- if ( ! group . label ) {
328- return ;
329- }
330-
331- self . groupLabels [ group . items [ 0 ] ] = group . label ;
326+ // Track which items have been grouped to detect duplicates
327+ const groupedItems = new Map ( ) ; // itemName -> groupLabel
332328
333- group . items . forEach ( function ( name , groupIndex ) {
334- const item = _ . find ( self . items , { name } ) ;
335- if ( item ) {
336- item . menuLeader = group . items [ 0 ] ;
337- } else {
329+ // If we have an explicit order
330+ // use the existing logic with duplicate detection
331+ if ( self . options . order && self . options . order . length > 0 ) {
332+ groups . forEach ( function ( group ) {
333+ if ( ! group . label ) {
338334 return ;
339335 }
340- // Make sure the submenu items wind up following the leader
341- // in self.items in the appropriate order
342- if ( name !== item . menuLeader ) {
343- const indexLeader = _ . findIndex ( self . items , { name : item . menuLeader } ) ;
344- if ( indexLeader === - 1 ) {
345- throw new Error ( 'Admin bar grouping error: no match for ' + item . menuLeader + ' in menu item ' + item . name ) ;
336+
337+ self . groupLabels [ group . items [ 0 ] ] = group . label ;
338+
339+ group . items . forEach ( function ( name , groupIndex ) {
340+ // Check for duplicates
341+ if ( groupedItems . has ( name ) ) {
342+ self . apos . util . warn (
343+ `Admin bar item "${ name } " appears in multiple groups: "${ groupedItems . get ( name ) } " and "${ group . label } ". ` +
344+ `Using first occurrence in "${ groupedItems . get ( name ) } ".`
345+ ) ;
346+ return ; // Skip this item in the current group
346347 }
347- let indexMe = _ . findIndex ( self . items , { name } ) ;
348- if ( indexMe !== indexLeader + groupIndex ) {
349- // Swap ourselves into the right position following our leader
350- if ( indexLeader + groupIndex < indexMe ) {
351- indexMe ++ ;
348+
349+ const item = _ . find ( self . items , { name } ) ;
350+ if ( item ) {
351+ item . menuLeader = group . items [ 0 ] ;
352+ groupedItems . set ( name , group . label ) ;
353+ } else {
354+ return ;
355+ }
356+
357+ // Make sure the submenu items wind up following the leader
358+ // in self.items in the appropriate order
359+ if ( name !== item . menuLeader ) {
360+ const indexLeader = _ . findIndex ( self . items , { name : item . menuLeader } ) ;
361+ if ( indexLeader === - 1 ) {
362+ throw new Error ( 'Admin bar grouping error: no match for ' + item . menuLeader + ' in menu item ' + item . name ) ;
363+ }
364+ let indexMe = _ . findIndex ( self . items , { name } ) ;
365+ if ( indexMe !== indexLeader + groupIndex ) {
366+ // Swap ourselves into the right position following our leader
367+ if ( indexLeader + groupIndex < indexMe ) {
368+ indexMe ++ ;
369+ }
370+ self . items . splice ( indexLeader + groupIndex , 0 , item ) ;
371+ self . items . splice ( indexMe , 1 ) ;
352372 }
353- self . items . splice ( indexLeader + groupIndex , 0 , item ) ;
354- self . items . splice ( indexMe , 1 ) ;
355373 }
374+ } ) ;
375+ } ) ;
376+ } else {
377+ // No explicit order - respect group registration order
378+ const newItems = [ ] ;
379+ const processedItems = new Set ( ) ;
380+
381+ // First, process all groups in registration order
382+ groups . forEach ( function ( group ) {
383+ if ( ! group . label ) {
384+ return ;
385+ }
386+
387+ // Collect valid items for this group (excluding duplicates and missing items)
388+ const validGroupItems = [ ] ;
389+
390+ group . items . forEach ( function ( name ) {
391+ // Check for duplicates
392+ if ( groupedItems . has ( name ) ) {
393+ self . apos . util . warn (
394+ `Admin bar item "${ name } " appears in multiple groups: "${ groupedItems . get ( name ) } " and "${ group . label } ". ` +
395+ `Using first occurrence in "${ groupedItems . get ( name ) } ".`
396+ ) ;
397+ return ;
398+ }
399+
400+ const item = _ . find ( self . items , { name } ) ;
401+ if ( item && ! processedItems . has ( name ) ) {
402+ validGroupItems . push ( { item, name } ) ;
403+ }
404+ } ) ;
405+
406+ // Only create a group if there are multiple valid items
407+ if ( validGroupItems . length > 1 ) {
408+ const leaderName = validGroupItems [ 0 ] . name ;
409+ self . groupLabels [ leaderName ] = group . label ;
410+
411+ validGroupItems . forEach ( ( { item, name } ) => {
412+ item . menuLeader = leaderName ;
413+ newItems . push ( item ) ;
414+ processedItems . add ( name ) ;
415+ groupedItems . set ( name , group . label ) ;
416+ } ) ;
417+ } else if ( validGroupItems . length === 1 ) {
418+ // Single item - add without grouping
419+ const { item, name } = validGroupItems [ 0 ] ;
420+ newItems . push ( item ) ;
421+ processedItems . add ( name ) ;
356422 }
357423 } ) ;
358- } ) ;
424+
425+ // Then add any remaining ungrouped items in their original order
426+ self . items . forEach ( function ( item ) {
427+ if ( ! processedItems . has ( item . name ) ) {
428+ newItems . push ( item ) ;
429+ processedItems . add ( item . name ) ;
430+ }
431+ } ) ;
432+
433+ self . items = newItems ;
434+ }
359435 } ,
360436
361437 // Determine if the specified admin bar item object should
0 commit comments