mirror of https://github.com/buster-so/buster.git
dropdown can support many
This commit is contained in:
parent
703d01e2c3
commit
a6e12cb06b
|
@ -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
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
|
import { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
@ -20,7 +20,7 @@ import {
|
||||||
DropdownMenuLink
|
DropdownMenuLink
|
||||||
} from './DropdownBase';
|
} from './DropdownBase';
|
||||||
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
|
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn, useMount } from '@/hooks';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import { Input } from '../inputs/Input';
|
import { Input } from '../inputs/Input';
|
||||||
import { useDebounceSearch } from '@/hooks';
|
import { useDebounceSearch } from '@/hooks';
|
||||||
|
@ -376,7 +376,7 @@ const DropdownItem = <T,>({
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
{icon && !loading && <span className="text-icon-color text-lg">{icon}</span>}
|
{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>
|
<span className={cn(truncate && 'truncate')}>{label}</span>
|
||||||
{secondaryLabel && <span className="text-gray-light text-xs">{secondaryLabel}</span>}
|
{secondaryLabel && <span className="text-gray-light text-xs">{secondaryLabel}</span>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -474,11 +474,38 @@ const DropdownSubMenuWrapper = <T,>({
|
||||||
selectType,
|
selectType,
|
||||||
showIndex
|
showIndex
|
||||||
}: DropdownSubMenuWrapperProps<T>) => {
|
}: 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 (
|
return (
|
||||||
<DropdownMenuSub>
|
<DropdownMenuSub onOpenChange={setIsOpen}>
|
||||||
<DropdownMenuSubTrigger>{children}</DropdownMenuSubTrigger>
|
<DropdownMenuSubTrigger>{children}</DropdownMenuSubTrigger>
|
||||||
<DropdownMenuPortal>
|
<DropdownMenuPortal>
|
||||||
<DropdownMenuSubContent sideOffset={8}>
|
<DropdownMenuSubContent
|
||||||
|
ref={subContentRef}
|
||||||
|
sideOffset={8}
|
||||||
|
className="max-h-[375px] overflow-y-auto">
|
||||||
{items?.map((item, index) => (
|
{items?.map((item, index) => (
|
||||||
<DropdownItemSelector
|
<DropdownItemSelector
|
||||||
key={dropdownItemKey(item, index)}
|
key={dropdownItemKey(item, index)}
|
||||||
|
|
|
@ -182,14 +182,19 @@ const useVersionHistorySelectMenu = ({ metricId }: { metricId: string }) => {
|
||||||
}));
|
}));
|
||||||
const { versions = [], version_number } = data || {};
|
const { versions = [], version_number } = data || {};
|
||||||
|
|
||||||
|
const onClickVersionHistory = useMemoizedFn((versionNumber: number) => {
|
||||||
|
console.log('versionNumber', versionNumber);
|
||||||
|
});
|
||||||
|
|
||||||
const versionHistoryItems: DropdownItems = useMemo(() => {
|
const versionHistoryItems: DropdownItems = useMemo(() => {
|
||||||
return versions.map((x) => ({
|
return versions.map((x) => ({
|
||||||
label: `Version ${x.version_number}`,
|
label: `Version ${x.version_number}`,
|
||||||
secondaryLabel: timeFromNow(x.updated_at, false),
|
secondaryLabel: timeFromNow(x.updated_at, false),
|
||||||
value: x.version_number.toString(),
|
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(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
|
Loading…
Reference in New Issue