@@ -476,6 +476,7 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
476476 nestedTag,
477477 onNavigateIn,
478478} ) => {
479+ const { isKeyboardNav, setKeyboardNav } = usePopoverContext ( )
479480 const currentNestedTag = nestedPath . length > 0 ? nestedPath [ nestedPath . length - 1 ] : nestedTag
480481
481482 const parentTagIndex = currentNestedTag . parentTag
@@ -489,6 +490,9 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
489490 < PopoverItem
490491 active = { parentTagIndex === selectedIndex && parentTagIndex >= 0 }
491492 onMouseEnter = { ( ) => {
493+ // Skip selection update during keyboard navigation to prevent scroll-triggered selection changes
494+ if ( isKeyboardNav ) return
495+ setKeyboardNav ( false )
492496 if ( parentTagIndex >= 0 ) setSelectedIndex ( parentTagIndex )
493497 } }
494498 onMouseDown = { ( e ) => {
@@ -533,6 +537,8 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
533537 key = { child . key }
534538 active = { childGlobalIndex === selectedIndex && childGlobalIndex >= 0 }
535539 onMouseEnter = { ( ) => {
540+ if ( isKeyboardNav ) return
541+ setKeyboardNav ( false )
536542 if ( childGlobalIndex >= 0 ) setSelectedIndex ( childGlobalIndex )
537543 } }
538544 onMouseDown = { ( e ) => {
@@ -567,6 +573,8 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
567573 key = { `${ group . blockId } -${ nestedChild . key } ` }
568574 active = { parentGlobalIndex === selectedIndex && parentGlobalIndex >= 0 }
569575 onMouseEnter = { ( ) => {
576+ if ( isKeyboardNav ) return
577+ setKeyboardNav ( false )
570578 if ( parentGlobalIndex >= 0 ) setSelectedIndex ( parentGlobalIndex )
571579 } }
572580 onMouseDown = { ( e ) => {
@@ -634,6 +642,7 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
634642 blocks,
635643 getMergedSubBlocks,
636644} ) => {
645+ const { isKeyboardNav, setKeyboardNav } = usePopoverContext ( )
637646 const hasChildren = nestedTag . children && nestedTag . children . length > 0
638647 const hasNestedChildren = nestedTag . nestedChildren && nestedTag . nestedChildren . length > 0
639648
@@ -656,6 +665,8 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
656665 }
657666 } }
658667 onMouseEnter = { ( ) => {
668+ if ( isKeyboardNav ) return
669+ setKeyboardNav ( false )
659670 if ( parentGlobalIndex >= 0 ) {
660671 setSelectedIndex ( parentGlobalIndex )
661672 }
@@ -725,6 +736,8 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
725736 rootOnly
726737 active = { globalIndex === selectedIndex && globalIndex >= 0 }
727738 onMouseEnter = { ( ) => {
739+ if ( isKeyboardNav ) return
740+ setKeyboardNav ( false )
728741 if ( globalIndex >= 0 ) setSelectedIndex ( globalIndex )
729742 } }
730743 onMouseDown = { ( e ) => {
@@ -750,6 +763,126 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
750763 )
751764}
752765
766+ /**
767+ * Hook to get mouse enter handler that respects keyboard navigation mode.
768+ * Returns a handler that only updates selection if not in keyboard mode.
769+ */
770+ const useKeyboardAwareMouseEnter = (
771+ setSelectedIndex : ( index : number ) => void
772+ ) : ( ( index : number ) => void ) => {
773+ const { isKeyboardNav, setKeyboardNav } = usePopoverContext ( )
774+
775+ return useCallback (
776+ ( index : number ) => {
777+ if ( isKeyboardNav ) return
778+ setKeyboardNav ( false )
779+ if ( index >= 0 ) setSelectedIndex ( index )
780+ } ,
781+ [ isKeyboardNav , setKeyboardNav , setSelectedIndex ]
782+ )
783+ }
784+
785+ /**
786+ * Wrapper for variable tag items that has access to popover context
787+ */
788+ const VariableTagItem : React . FC < {
789+ tag : string
790+ globalIndex : number
791+ selectedIndex : number
792+ setSelectedIndex : ( index : number ) => void
793+ handleTagSelect : ( tag : string ) => void
794+ itemRefs : React . RefObject < Map < number , HTMLElement > >
795+ variableInfo : { type : string ; id : string } | null
796+ } > = ( {
797+ tag,
798+ globalIndex,
799+ selectedIndex,
800+ setSelectedIndex,
801+ handleTagSelect,
802+ itemRefs,
803+ variableInfo,
804+ } ) => {
805+ const handleMouseEnter = useKeyboardAwareMouseEnter ( setSelectedIndex )
806+
807+ return (
808+ < PopoverItem
809+ key = { tag }
810+ rootOnly
811+ active = { globalIndex === selectedIndex && globalIndex >= 0 }
812+ onMouseEnter = { ( ) => handleMouseEnter ( globalIndex ) }
813+ onMouseDown = { ( e ) => {
814+ e . preventDefault ( )
815+ e . stopPropagation ( )
816+ handleTagSelect ( tag )
817+ } }
818+ ref = { ( el ) => {
819+ if ( el && globalIndex >= 0 ) {
820+ itemRefs . current ?. set ( globalIndex , el )
821+ }
822+ } }
823+ >
824+ < span className = 'flex-1 truncate' >
825+ { tag . startsWith ( TAG_PREFIXES . VARIABLE ) ? tag . substring ( TAG_PREFIXES . VARIABLE . length ) : tag }
826+ </ span >
827+ { variableInfo && (
828+ < span className = 'ml-auto text-[10px] text-[var(--text-muted-inverse)]' >
829+ { variableInfo . type }
830+ </ span >
831+ ) }
832+ </ PopoverItem >
833+ )
834+ }
835+
836+ /**
837+ * Wrapper for block root tag items that has access to popover context
838+ */
839+ const BlockRootTagItem : React . FC < {
840+ rootTag : string
841+ rootTagGlobalIndex : number
842+ selectedIndex : number
843+ setSelectedIndex : ( index : number ) => void
844+ handleTagSelect : ( tag : string , group ?: BlockTagGroup ) => void
845+ itemRefs : React . RefObject < Map < number , HTMLElement > >
846+ group : BlockTagGroup
847+ tagIcon : string | React . ComponentType < { className ?: string } >
848+ blockColor : string
849+ blockName : string
850+ } > = ( {
851+ rootTag,
852+ rootTagGlobalIndex,
853+ selectedIndex,
854+ setSelectedIndex,
855+ handleTagSelect,
856+ itemRefs,
857+ group,
858+ tagIcon,
859+ blockColor,
860+ blockName,
861+ } ) => {
862+ const handleMouseEnter = useKeyboardAwareMouseEnter ( setSelectedIndex )
863+
864+ return (
865+ < PopoverItem
866+ rootOnly
867+ active = { rootTagGlobalIndex === selectedIndex && rootTagGlobalIndex >= 0 }
868+ onMouseEnter = { ( ) => handleMouseEnter ( rootTagGlobalIndex ) }
869+ onMouseDown = { ( e ) => {
870+ e . preventDefault ( )
871+ e . stopPropagation ( )
872+ handleTagSelect ( rootTag , group )
873+ } }
874+ ref = { ( el ) => {
875+ if ( el && rootTagGlobalIndex >= 0 ) {
876+ itemRefs . current ?. set ( rootTagGlobalIndex , el )
877+ }
878+ } }
879+ >
880+ < TagIcon icon = { tagIcon } color = { blockColor } />
881+ < span className = 'flex-1 truncate font-medium' > { blockName } </ span >
882+ </ PopoverItem >
883+ )
884+ }
885+
753886/**
754887 * Helper component to capture popover context for nested navigation
755888 */
@@ -1613,7 +1746,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
16131746 const element = itemRefs . current . get ( selectedIndex )
16141747 if ( element ) {
16151748 element . scrollIntoView ( {
1616- behavior : 'smooth ' ,
1749+ behavior : 'auto ' ,
16171750 block : 'nearest' ,
16181751 } )
16191752 }
@@ -1888,35 +2021,16 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
18882021 const globalIndex = flatTagList . findIndex ( ( item ) => item . tag === tag )
18892022
18902023 return (
1891- < PopoverItem
2024+ < VariableTagItem
18922025 key = { tag }
1893- rootOnly
1894- active = { globalIndex === selectedIndex && globalIndex >= 0 }
1895- onMouseEnter = { ( ) => {
1896- if ( globalIndex >= 0 ) setSelectedIndex ( globalIndex )
1897- } }
1898- onMouseDown = { ( e ) => {
1899- e . preventDefault ( )
1900- e . stopPropagation ( )
1901- handleTagSelect ( tag )
1902- } }
1903- ref = { ( el ) => {
1904- if ( el && globalIndex >= 0 ) {
1905- itemRefs . current . set ( globalIndex , el )
1906- }
1907- } }
1908- >
1909- < span className = 'flex-1 truncate' >
1910- { tag . startsWith ( TAG_PREFIXES . VARIABLE )
1911- ? tag . substring ( TAG_PREFIXES . VARIABLE . length )
1912- : tag }
1913- </ span >
1914- { variableInfo && (
1915- < span className = 'ml-auto text-[10px] text-[var(--text-muted-inverse)]' >
1916- { variableInfo . type }
1917- </ span >
1918- ) }
1919- </ PopoverItem >
2026+ tag = { tag }
2027+ globalIndex = { globalIndex }
2028+ selectedIndex = { selectedIndex }
2029+ setSelectedIndex = { setSelectedIndex }
2030+ handleTagSelect = { handleTagSelect }
2031+ itemRefs = { itemRefs }
2032+ variableInfo = { variableInfo }
2033+ />
19202034 )
19212035 } ) }
19222036 { nestedBlockTagGroups . length > 0 && < PopoverDivider rootOnly /> }
@@ -1951,26 +2065,18 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
19512065
19522066 return (
19532067 < div key = { group . blockId } >
1954- < PopoverItem
1955- rootOnly
1956- active = { rootTagGlobalIndex === selectedIndex && rootTagGlobalIndex >= 0 }
1957- onMouseEnter = { ( ) => {
1958- if ( rootTagGlobalIndex >= 0 ) setSelectedIndex ( rootTagGlobalIndex )
1959- } }
1960- onMouseDown = { ( e ) => {
1961- e . preventDefault ( )
1962- e . stopPropagation ( )
1963- handleTagSelect ( rootTag , group )
1964- } }
1965- ref = { ( el ) => {
1966- if ( el && rootTagGlobalIndex >= 0 ) {
1967- itemRefs . current . set ( rootTagGlobalIndex , el )
1968- }
1969- } }
1970- >
1971- < TagIcon icon = { tagIcon } color = { blockColor } />
1972- < span className = 'flex-1 truncate font-medium' > { group . blockName } </ span >
1973- </ PopoverItem >
2068+ < BlockRootTagItem
2069+ rootTag = { rootTag }
2070+ rootTagGlobalIndex = { rootTagGlobalIndex }
2071+ selectedIndex = { selectedIndex }
2072+ setSelectedIndex = { setSelectedIndex }
2073+ handleTagSelect = { handleTagSelect }
2074+ itemRefs = { itemRefs }
2075+ group = { group }
2076+ tagIcon = { tagIcon }
2077+ blockColor = { blockColor }
2078+ blockName = { group . blockName }
2079+ />
19742080 { group . nestedTags . map ( ( nestedTag ) => {
19752081 if ( nestedTag . fullTag === rootTag ) {
19762082 return null
0 commit comments