dropdown can support many

This commit is contained in:
Nate Kelley 2025-03-25 15:36:13 -06:00
parent 703d01e2c3
commit a6e12cb06b
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
3 changed files with 56 additions and 7 deletions

View File

@ -616,3 +616,20 @@ export const WithReactNodeSubMenu: Story = {
]
}
};
export const WithSubMenuAndHundredItems: Story = {
args: {
children: <Button>Menu with 100 items</Button>,
items: [
{
label: 'Option 1',
value: '1',
items: Array.from({ length: 100 }).map((_, index) => ({
label: `Sub Option ${index}`,
value: `1-${index + 1}`,
selected: index === 85
}))
}
]
}
};

View File

@ -1,7 +1,7 @@
'use client';
import { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import {
DropdownMenu,
DropdownMenuContent,
@ -20,7 +20,7 @@ import {
DropdownMenuLink
} from './DropdownBase';
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
import { useMemoizedFn } from '@/hooks';
import { useMemoizedFn, useMount } from '@/hooks';
import { cn } from '@/lib/classMerge';
import { Input } from '../inputs/Input';
import { useDebounceSearch } from '@/hooks';
@ -376,7 +376,7 @@ const DropdownItem = <T,>({
const content = (
<>
{icon && !loading && <span className="text-icon-color text-lg">{icon}</span>}
<div className={cn('flex flex-col space-y-2', truncate && 'overflow-hidden')}>
<div className={cn('flex flex-col space-y-1', truncate && 'overflow-hidden')}>
<span className={cn(truncate && 'truncate')}>{label}</span>
{secondaryLabel && <span className="text-gray-light text-xs">{secondaryLabel}</span>}
</div>
@ -474,11 +474,38 @@ const DropdownSubMenuWrapper = <T,>({
selectType,
showIndex
}: DropdownSubMenuWrapperProps<T>) => {
const subContentRef = React.useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = React.useState(false);
const scrollToSelectedItem = React.useCallback(() => {
if (!subContentRef.current) return;
const selectedIndex = items?.findIndex((item) => (item as DropdownItem).selected);
if (selectedIndex === undefined || selectedIndex === -1) return;
const menuItems = subContentRef.current.querySelectorAll('[role="menuitem"]');
const selectedElement = menuItems[selectedIndex - 1];
if (selectedElement) {
selectedElement.scrollIntoView({ block: 'start', behavior: 'instant', inline: 'start' });
}
}, [items]);
useEffect(() => {
if (isOpen) {
const timeoutId = setTimeout(scrollToSelectedItem, 70);
return () => clearTimeout(timeoutId);
}
}, [isOpen, scrollToSelectedItem]);
return (
<DropdownMenuSub>
<DropdownMenuSub onOpenChange={setIsOpen}>
<DropdownMenuSubTrigger>{children}</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent sideOffset={8}>
<DropdownMenuSubContent
ref={subContentRef}
sideOffset={8}
className="max-h-[375px] overflow-y-auto">
{items?.map((item, index) => (
<DropdownItemSelector
key={dropdownItemKey(item, index)}

View File

@ -182,14 +182,19 @@ const useVersionHistorySelectMenu = ({ metricId }: { metricId: string }) => {
}));
const { versions = [], version_number } = data || {};
const onClickVersionHistory = useMemoizedFn((versionNumber: number) => {
console.log('versionNumber', versionNumber);
});
const versionHistoryItems: DropdownItems = useMemo(() => {
return versions.map((x) => ({
label: `Version ${x.version_number}`,
secondaryLabel: timeFromNow(x.updated_at, false),
value: x.version_number.toString(),
selected: x.version_number === version_number
selected: x.version_number === version_number,
onClick: () => onClickVersionHistory(x.version_number)
}));
}, [versions, version_number]);
}, [versions, version_number, onClickVersionHistory]);
return useMemo(
() => ({