remove smooth scroll in tag drop

This commit is contained in:
waleed
2026-01-18 13:16:07 -08:00
parent b300192110
commit f85def744e
2 changed files with 180 additions and 54 deletions

View File

@@ -53,7 +53,6 @@ const findFolderInfoForTag = (
nestedTag: NestedTag
} | null => {
for (const nestedTag of nestedTags) {
// Check if this tag is a folder (has children or nestedChildren)
if (
nestedTag.parentTag === targetTag &&
(nestedTag.children?.length || nestedTag.nestedChildren?.length)
@@ -228,6 +227,17 @@ export const KeyboardNavigationHandler: React.FC<KeyboardNavigationHandlerProps>
}
}
const scrollIntoView = () => {
setTimeout(() => {
const selectedItem = document.querySelector<HTMLElement>(
'[data-radix-popper-content-wrapper] [aria-selected="true"]'
)
if (selectedItem) {
selectedItem.scrollIntoView({ behavior: 'auto', block: 'nearest' })
}
}, 0)
}
switch (e.key) {
case 'ArrowDown':
e.preventDefault()
@@ -235,11 +245,16 @@ export const KeyboardNavigationHandler: React.FC<KeyboardNavigationHandlerProps>
setKeyboardNav(true)
if (visibleIndices.length > 0) {
const currentVisibleIndex = visibleIndices.indexOf(selectedIndex)
let newIndex: number
if (currentVisibleIndex === -1) {
setSelectedIndex(visibleIndices[0])
newIndex = visibleIndices[0]
} else if (currentVisibleIndex < visibleIndices.length - 1) {
setSelectedIndex(visibleIndices[currentVisibleIndex + 1])
newIndex = visibleIndices[currentVisibleIndex + 1]
} else {
newIndex = visibleIndices[0]
}
setSelectedIndex(newIndex)
scrollIntoView()
}
break
case 'ArrowUp':
@@ -248,11 +263,16 @@ export const KeyboardNavigationHandler: React.FC<KeyboardNavigationHandlerProps>
setKeyboardNav(true)
if (visibleIndices.length > 0) {
const currentVisibleIndex = visibleIndices.indexOf(selectedIndex)
let newIndex: number
if (currentVisibleIndex === -1) {
setSelectedIndex(visibleIndices[0])
newIndex = visibleIndices[visibleIndices.length - 1]
} else if (currentVisibleIndex > 0) {
setSelectedIndex(visibleIndices[currentVisibleIndex - 1])
newIndex = visibleIndices[currentVisibleIndex - 1]
} else {
newIndex = visibleIndices[visibleIndices.length - 1]
}
setSelectedIndex(newIndex)
scrollIntoView()
}
break
case 'Enter':

View File

@@ -476,6 +476,7 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
nestedTag,
onNavigateIn,
}) => {
const { isKeyboardNav, setKeyboardNav } = usePopoverContext()
const currentNestedTag = nestedPath.length > 0 ? nestedPath[nestedPath.length - 1] : nestedTag
const parentTagIndex = currentNestedTag.parentTag
@@ -489,6 +490,9 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
<PopoverItem
active={parentTagIndex === selectedIndex && parentTagIndex >= 0}
onMouseEnter={() => {
// Skip selection update during keyboard navigation to prevent scroll-triggered selection changes
if (isKeyboardNav) return
setKeyboardNav(false)
if (parentTagIndex >= 0) setSelectedIndex(parentTagIndex)
}}
onMouseDown={(e) => {
@@ -533,6 +537,8 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
key={child.key}
active={childGlobalIndex === selectedIndex && childGlobalIndex >= 0}
onMouseEnter={() => {
if (isKeyboardNav) return
setKeyboardNav(false)
if (childGlobalIndex >= 0) setSelectedIndex(childGlobalIndex)
}}
onMouseDown={(e) => {
@@ -567,6 +573,8 @@ const FolderContentsInner: React.FC<FolderContentsProps> = ({
key={`${group.blockId}-${nestedChild.key}`}
active={parentGlobalIndex === selectedIndex && parentGlobalIndex >= 0}
onMouseEnter={() => {
if (isKeyboardNav) return
setKeyboardNav(false)
if (parentGlobalIndex >= 0) setSelectedIndex(parentGlobalIndex)
}}
onMouseDown={(e) => {
@@ -634,6 +642,7 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
blocks,
getMergedSubBlocks,
}) => {
const { isKeyboardNav, setKeyboardNav } = usePopoverContext()
const hasChildren = nestedTag.children && nestedTag.children.length > 0
const hasNestedChildren = nestedTag.nestedChildren && nestedTag.nestedChildren.length > 0
@@ -656,6 +665,8 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
}
}}
onMouseEnter={() => {
if (isKeyboardNav) return
setKeyboardNav(false)
if (parentGlobalIndex >= 0) {
setSelectedIndex(parentGlobalIndex)
}
@@ -725,6 +736,8 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
rootOnly
active={globalIndex === selectedIndex && globalIndex >= 0}
onMouseEnter={() => {
if (isKeyboardNav) return
setKeyboardNav(false)
if (globalIndex >= 0) setSelectedIndex(globalIndex)
}}
onMouseDown={(e) => {
@@ -750,6 +763,126 @@ const NestedTagRenderer: React.FC<NestedTagRendererProps> = ({
)
}
/**
* Hook to get mouse enter handler that respects keyboard navigation mode.
* Returns a handler that only updates selection if not in keyboard mode.
*/
const useKeyboardAwareMouseEnter = (
setSelectedIndex: (index: number) => void
): ((index: number) => void) => {
const { isKeyboardNav, setKeyboardNav } = usePopoverContext()
return useCallback(
(index: number) => {
if (isKeyboardNav) return
setKeyboardNav(false)
if (index >= 0) setSelectedIndex(index)
},
[isKeyboardNav, setKeyboardNav, setSelectedIndex]
)
}
/**
* Wrapper for variable tag items that has access to popover context
*/
const VariableTagItem: React.FC<{
tag: string
globalIndex: number
selectedIndex: number
setSelectedIndex: (index: number) => void
handleTagSelect: (tag: string) => void
itemRefs: React.RefObject<Map<number, HTMLElement>>
variableInfo: { type: string; id: string } | null
}> = ({
tag,
globalIndex,
selectedIndex,
setSelectedIndex,
handleTagSelect,
itemRefs,
variableInfo,
}) => {
const handleMouseEnter = useKeyboardAwareMouseEnter(setSelectedIndex)
return (
<PopoverItem
key={tag}
rootOnly
active={globalIndex === selectedIndex && globalIndex >= 0}
onMouseEnter={() => handleMouseEnter(globalIndex)}
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleTagSelect(tag)
}}
ref={(el) => {
if (el && globalIndex >= 0) {
itemRefs.current?.set(globalIndex, el)
}
}}
>
<span className='flex-1 truncate'>
{tag.startsWith(TAG_PREFIXES.VARIABLE) ? tag.substring(TAG_PREFIXES.VARIABLE.length) : tag}
</span>
{variableInfo && (
<span className='ml-auto text-[10px] text-[var(--text-muted-inverse)]'>
{variableInfo.type}
</span>
)}
</PopoverItem>
)
}
/**
* Wrapper for block root tag items that has access to popover context
*/
const BlockRootTagItem: React.FC<{
rootTag: string
rootTagGlobalIndex: number
selectedIndex: number
setSelectedIndex: (index: number) => void
handleTagSelect: (tag: string, group?: BlockTagGroup) => void
itemRefs: React.RefObject<Map<number, HTMLElement>>
group: BlockTagGroup
tagIcon: string | React.ComponentType<{ className?: string }>
blockColor: string
blockName: string
}> = ({
rootTag,
rootTagGlobalIndex,
selectedIndex,
setSelectedIndex,
handleTagSelect,
itemRefs,
group,
tagIcon,
blockColor,
blockName,
}) => {
const handleMouseEnter = useKeyboardAwareMouseEnter(setSelectedIndex)
return (
<PopoverItem
rootOnly
active={rootTagGlobalIndex === selectedIndex && rootTagGlobalIndex >= 0}
onMouseEnter={() => handleMouseEnter(rootTagGlobalIndex)}
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleTagSelect(rootTag, group)
}}
ref={(el) => {
if (el && rootTagGlobalIndex >= 0) {
itemRefs.current?.set(rootTagGlobalIndex, el)
}
}}
>
<TagIcon icon={tagIcon} color={blockColor} />
<span className='flex-1 truncate font-medium'>{blockName}</span>
</PopoverItem>
)
}
/**
* Helper component to capture popover context for nested navigation
*/
@@ -1613,7 +1746,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
const element = itemRefs.current.get(selectedIndex)
if (element) {
element.scrollIntoView({
behavior: 'smooth',
behavior: 'auto',
block: 'nearest',
})
}
@@ -1888,35 +2021,16 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
const globalIndex = flatTagList.findIndex((item) => item.tag === tag)
return (
<PopoverItem
<VariableTagItem
key={tag}
rootOnly
active={globalIndex === selectedIndex && globalIndex >= 0}
onMouseEnter={() => {
if (globalIndex >= 0) setSelectedIndex(globalIndex)
}}
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleTagSelect(tag)
}}
ref={(el) => {
if (el && globalIndex >= 0) {
itemRefs.current.set(globalIndex, el)
}
}}
>
<span className='flex-1 truncate'>
{tag.startsWith(TAG_PREFIXES.VARIABLE)
? tag.substring(TAG_PREFIXES.VARIABLE.length)
: tag}
</span>
{variableInfo && (
<span className='ml-auto text-[10px] text-[var(--text-muted-inverse)]'>
{variableInfo.type}
</span>
)}
</PopoverItem>
tag={tag}
globalIndex={globalIndex}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
handleTagSelect={handleTagSelect}
itemRefs={itemRefs}
variableInfo={variableInfo}
/>
)
})}
{nestedBlockTagGroups.length > 0 && <PopoverDivider rootOnly />}
@@ -1951,26 +2065,18 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
return (
<div key={group.blockId}>
<PopoverItem
rootOnly
active={rootTagGlobalIndex === selectedIndex && rootTagGlobalIndex >= 0}
onMouseEnter={() => {
if (rootTagGlobalIndex >= 0) setSelectedIndex(rootTagGlobalIndex)
}}
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleTagSelect(rootTag, group)
}}
ref={(el) => {
if (el && rootTagGlobalIndex >= 0) {
itemRefs.current.set(rootTagGlobalIndex, el)
}
}}
>
<TagIcon icon={tagIcon} color={blockColor} />
<span className='flex-1 truncate font-medium'>{group.blockName}</span>
</PopoverItem>
<BlockRootTagItem
rootTag={rootTag}
rootTagGlobalIndex={rootTagGlobalIndex}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
handleTagSelect={handleTagSelect}
itemRefs={itemRefs}
group={group}
tagIcon={tagIcon}
blockColor={blockColor}
blockName={group.blockName}
/>
{group.nestedTags.map((nestedTag) => {
if (nestedTag.fullTag === rootTag) {
return null