import React, { useState, type FC, type HTMLProps, type ReactElement, type ReactNode } from 'react'; import { HiChevronDown, HiChevronRight } from 'react-icons/hi2'; import { PiCaretDoubleLeftThin, PiCaretDoubleRightThin } from 'react-icons/pi'; import { flexRender, useControllable, type FlexRenderable } from '@wener/reaction'; import { clsx } from 'clsx'; import { HeaderContentFooterLayout, LeftContentRightLayout, OverlayScrollbar } from '../../components'; import { Tooltip } from '../../floating'; import { NavLink } from '../links'; import type { BaseNavLink } from './BaseNavLink'; interface ExpandableSideMenuTitle { type: 'title'; label: string; icon?: ReactElement; iconActive?: ReactElement; } interface ExpandableSideMenuItem { type?: 'item'; label?: string; icon?: FlexRenderable; iconActive?: FlexRenderable; href: string; end?: boolean; } interface ExpandableSideMenuGroup { type?: 'group'; label: string; icon?: ReactElement; iconActive?: ReactElement; end?: boolean; children: ExpandableSideMenuItem[]; } export type ExpandableSideMenuItemProps = ExpandableSideMenuItem | ExpandableSideMenuGroup | ExpandableSideMenuTitle; export interface ExpandableSideMenuLayoutProps extends Omit, 'title'> { header?: FlexRenderable<{ expanded: boolean }>; title?: ReactNode; icon?: ReactElement; children?: ReactNode; items: Array; expanded?: boolean; initialExpanded?: boolean; onExpandedChange?: (v: boolean) => void; NavLink?: BaseNavLink; } const SideMenuItem: FC<{ item: ExpandableSideMenuItemProps; expanded?: boolean; NavLink?: BaseNavLink }> = ({ item, expanded, }) => { const [collapse, setCollapse] = useState(true); if ('children' in item) { const { label, icon, children } = item; if (!expanded) { return (
  • {icon}
  • ); } return ( <>
  • { setCollapse(!collapse); }} > {icon} {label} {collapse ? : }
  • {!collapse && children.map((item, i) => { return ( , iconActive: undefined, }} key={`${label}/${i}`} /> ); })} ); // fixme collapse not works // return ( //
  • // // {icon} // {label} // //
      // {children.map((item, i) => { // return
    • // {item.label} //
    • // })} //
    //
  • } if (item.type === 'title') { if (!expanded) { return (
  • {item.label}
  • ); } return (
  • {item.label}
  • ); } const { href, icon, end, iconActive, label } = item; // data-tooltip 由于 overflow 实际无法显示 return (
  • clsx(expanded ? '' : 'justify-center p-2', isActive ? 'active' : 'inactive')} > {({ isActive: active }) => { let ico = flexRender( active ? iconActive || icon : icon, { active, className: 'w-6 h-6', }, true, ); return ( <> {ico} {expanded && label} ); }}
  • ); }; export const ExpandableMenu: FC = ({ expanded, title, header: _header, icon, items, onExpandedChange, children, }) => { return ( {icon} {expanded && title && {title}} {flexRender(_header, { expanded })} } footer={
    } >
      {items.map((item, i) => { return ; })}
    {children}
    ); }; export const ExpandableSideMenuLayout: FC = ({ children, header: _header, title, icon, items, expanded: _expanded, onExpandedChange: _onExpandedChange, initialExpanded: _initialExpanded, ...props }) => { const [expanded, setExpanded] = useControllable( _expanded, _onExpandedChange, () => _initialExpanded ?? (typeof window === 'undefined' ? true : window.matchMedia('(min-width: 768px)').matches), ); return ( } {...props} > {children} ); }; ExpandableSideMenuLayout.displayName = 'ExpandableSideMenuLayout';