From a6e12cb06b3ccdca53466bd881be8f805c6a38a0 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Tue, 25 Mar 2025 15:36:13 -0600 Subject: [PATCH] dropdown can support many --- .../ui/dropdown/Dropdown.stories.tsx | 17 +++++++++ web/src/components/ui/dropdown/Dropdown.tsx | 37 ++++++++++++++++--- .../MetricThreeDotMenu.tsx | 9 ++++- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/web/src/components/ui/dropdown/Dropdown.stories.tsx b/web/src/components/ui/dropdown/Dropdown.stories.tsx index 3ce67da98..1783f7ebb 100644 --- a/web/src/components/ui/dropdown/Dropdown.stories.tsx +++ b/web/src/components/ui/dropdown/Dropdown.stories.tsx @@ -616,3 +616,20 @@ export const WithReactNodeSubMenu: Story = { ] } }; + +export const WithSubMenuAndHundredItems: Story = { + args: { + children: , + items: [ + { + label: 'Option 1', + value: '1', + items: Array.from({ length: 100 }).map((_, index) => ({ + label: `Sub Option ${index}`, + value: `1-${index + 1}`, + selected: index === 85 + })) + } + ] + } +}; diff --git a/web/src/components/ui/dropdown/Dropdown.tsx b/web/src/components/ui/dropdown/Dropdown.tsx index fdce8058f..012cd6d43 100644 --- a/web/src/components/ui/dropdown/Dropdown.tsx +++ b/web/src/components/ui/dropdown/Dropdown.tsx @@ -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 = ({ const content = ( <> {icon && !loading && {icon}} -
+
{label} {secondaryLabel && {secondaryLabel}}
@@ -474,11 +474,38 @@ const DropdownSubMenuWrapper = ({ selectType, showIndex }: DropdownSubMenuWrapperProps) => { + const subContentRef = React.useRef(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 ( - + {children} - + {items?.map((item, index) => ( { })); 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( () => ({