import { useI18n } from '@affine/i18n'; import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc'; import { Slot } from '@radix-ui/react-slot'; import clsx from 'clsx'; import { useCallback, useContext, useEffect, useState } from 'react'; import { observeResize } from '../../../utils'; import { Button } from '../../button'; import { Modal } from '../../modal'; import { Scrollable } from '../../scrollbar'; import type { MenuProps } from '../menu.types'; import { MobileMenuContext, type SubMenuContent, useMobileSubMenuHelper, } from './context'; import * as styles from './styles.css'; import { MobileMenuSubRaw } from './sub'; export const MobileMenu = ({ children, items, contentOptions: { className, onPointerDownOutside, onInteractOutside, // ignore the following props sideOffset: _sideOffset, side: _side, align: _align, ...otherContentOptions } = {}, rootOptions, }: MenuProps) => { const [subMenus, setSubMenus] = useState([]); const [open, setOpen] = useState(false); const mobileContextValue = { subMenus, setSubMenus, setOpen, }; const { removeSubMenu, removeAllSubMenus } = useMobileSubMenuHelper(mobileContextValue); const [sliderHeight, setSliderHeight] = useState(0); const [sliderElement, setSliderElement] = useState( null ); const { setOpen: pSetOpen } = useContext(MobileMenuContext); const finalOpen = rootOptions?.open ?? open; // always show the last submenu, if any const activeIndex = subMenus.length; // dynamic height for slider useEffect(() => { if (sliderElement && finalOpen) { const active = sliderElement.querySelector( `.${styles.menuContent}[data-index="${activeIndex}"]` ); if (!active) return; // for the situation that content is loaded asynchronously return observeResize(active, entry => { setSliderHeight(entry.borderBoxSize[0].blockSize); }); } return; }, [activeIndex, finalOpen, sliderElement]); const onOpenChange = useCallback( (open: boolean) => { if (!open) { // a workaround to hack the onPointerDownOutside event onPointerDownOutside?.({} as any); onInteractOutside?.({} as any); removeAllSubMenus(); } setOpen(open); rootOptions?.onOpenChange?.(open); }, [onInteractOutside, onPointerDownOutside, removeAllSubMenus, rootOptions] ); const onItemClick = useCallback( (e: any) => { e.preventDefault(); onOpenChange(!open); }, [onOpenChange, open] ); const t = useI18n(); /** * For cascading menu usage * ```tsx * Click me * } * > * Root * * ``` */ if (pSetOpen) { return ( {children} ); } return ( <> {children}
{items}
{subMenus.map((sub, index) => (
{sub.items}
))}
); };