diff --git a/packages/console/src/containers/AppContent/components/SubMenu/index.module.scss b/packages/console/src/containers/AppContent/components/SubMenu/index.module.scss index 378f036a3..380ff74ee 100644 --- a/packages/console/src/containers/AppContent/components/SubMenu/index.module.scss +++ b/packages/console/src/containers/AppContent/components/SubMenu/index.module.scss @@ -29,14 +29,14 @@ position: absolute; top: -4px; right: calc(100% + 5px); - display: none; + visibility: hidden; background: var(--color-float); border: 1px solid var(--color-divider); border-radius: _.unit(2); box-shadow: var(--shadow-2); &.visible { - display: block; + visibility: visible; } } diff --git a/packages/console/src/containers/AppContent/components/SubMenu/index.tsx b/packages/console/src/containers/AppContent/components/SubMenu/index.tsx index 1a5303466..359300231 100644 --- a/packages/console/src/containers/AppContent/components/SubMenu/index.tsx +++ b/packages/console/src/containers/AppContent/components/SubMenu/index.tsx @@ -1,12 +1,12 @@ import type { AdminConsoleKey } from '@logto/phrases'; import classNames from 'classnames'; -import type { ReactNode } from 'react'; -import { useState, useRef } from 'react'; +import { type ReactNode, useCallback, useEffect, useState, useRef } from 'react'; import ArrowRight from '@/assets/images/arrow-right.svg'; import Tick from '@/assets/images/tick.svg'; import { DropdownItem } from '@/components/Dropdown'; import DynamicT from '@/components/DynamicT'; +import OverlayScrollbar from '@/components/OverlayScrollbar'; import type { Option } from '@/components/Select'; import Spacer from '@/components/Spacer'; import { onKeyDownHandler } from '@/utils/a11y'; @@ -23,6 +23,11 @@ type Props = { onItemClick: (value: T) => void; }; +const menuItemHeight = 40; +const menuItemMargin = 4; +const menuBorderSize = 1; +const menuBottomPadding = 16; + function SubMenu({ className, menuItemClassName, @@ -36,6 +41,32 @@ function SubMenu({ const [showMenu, setShowMenu] = useState(false); const mouseEnterTimeoutRef = useRef(0); const mouseLeaveTimeoutRef = useRef(0); + const [menuHeight, setMenuHeight] = useState(); + + const calculateDropdownHeight = useCallback(() => { + if (anchorRef.current) { + const anchorRect = anchorRef.current.getBoundingClientRect(); + const originalMenuHeight = + options.length * menuItemHeight + + (options.length + 1) * menuItemMargin + + 2 * menuBorderSize; + const availableHeight = window.innerHeight - anchorRect.top - menuBottomPadding; + + setMenuHeight(Math.min(originalMenuHeight, availableHeight)); + } + }, [options.length]); + + useEffect(() => { + calculateDropdownHeight(); + + window.addEventListener('resize', calculateDropdownHeight); + window.addEventListener('scroll', calculateDropdownHeight); + + return () => { + window.removeEventListener('resize', calculateDropdownHeight); + window.removeEventListener('scroll', calculateDropdownHeight); + }; + }, [calculateDropdownHeight]); return (
({ -
+ {options.map(({ value, title }) => { const selected = value === selectedOption; @@ -92,7 +126,7 @@ function SubMenu({ ); })} -
+
); }