import React, { FC, useContext, useRef, useState, useCallback, ReactNode, useMemo, useEffect, } from 'react'; import useMedia from 'react-use/lib/useMedia'; import { CSSTransition } from 'react-transition-group'; import classNames from 'classnames'; import { CloseOutlined, MenuOutlined } from '@ant-design/icons'; import omit from 'lodash/omit'; import useMeasure from 'react-use/lib/useMeasure'; import Button from '../button'; import { MenuDataItem, MessageDescriptor, RouterTypes } from './typings'; import Navigation, { NavigationProps } from '../navigation'; import getMenuData from './utils/getMenuData'; import { clearMenuItem } from './utils/clearMenuItem'; import { ConfigContext } from '../config-provider'; import { ItemProps } from '../navigation/components/Item'; import { useLayoutHeaderHeight } from './useLayoutHeaderHeight'; import { BreadcrumbProps } from '../breadcrumb'; export type FrameProps = RouterTypes & { menuDataRender?: (menuData: MenuDataItem[]) => MenuDataItem[]; postMenuData?: (menusData?: MenuDataItem[]) => MenuDataItem[]; menuExtra?: React.ReactNode; leftContent?: React.ReactNode; rightContent?: React.ReactNode; formatMessage?: (message: MessageDescriptor) => string; menuItemRender?: NavigationProps['menuItemRender']; contentStyle?: React.CSSProperties; renderNavigation?: | ((menu: MenuDataItem[], renderItems: (menu: MenuDataItem[]) => ItemProps[]) => ReactNode) | false; headerRender?: (defaultDom: React.ReactNode) => React.ReactNode; breadcrumbRender?: false | ((routers: BreadcrumbProps['routes']) => BreadcrumbProps['routes']); }; const Frame: FC = props => { const { children, location, route, menuDataRender, postMenuData, menuExtra, leftContent, rightContent, contentStyle, menuItemRender, renderNavigation, headerRender, } = props; const { getPrefixCls } = useContext(ConfigContext); const isNavigationCollapsed = useMedia('(max-width: 768px)'); const [showMobileNavigation, setShowMobileNavigation] = useState(false); const [layoutHeaderHeight, setLayoutHeaderHeight] = useLayoutHeaderHeight(); const prefixCls = getPrefixCls('frame'); const navigationNode = useRef(null); const [headerRef, { height: headerHeight }] = useMeasure(); useEffect(() => { setLayoutHeaderHeight(headerHeight); }, [headerHeight, setLayoutHeaderHeight]); const formatMessage = useCallback( ({ id, defaultMessage, ...restParams }: { id: string; defaultMessage?: string }): string => { if (props.formatMessage) { return props.formatMessage({ id, defaultMessage, ...restParams, }); } return defaultMessage as string; }, [props], ); const { // breadcrumb = {}, // breadcrumbMap, menuData = [], } = getMenuData(route?.routes || [], {}, formatMessage, menuDataRender); const clearMenuData = clearMenuItem(menuData); const finallyData = postMenuData ? postMenuData(clearMenuData) : clearMenuData; const getIntlName = useCallback( (item: MenuDataItem) => { const { name, locale } = item; if (locale) { return formatMessage?.({ id: locale, defaultMessage: name, }); } return name || ''; }, [formatMessage], ); const renderItems = useCallback( (menu: MenuDataItem[]) => menu.map(menuDataItem => { if (menuDataItem.children) { return { ...omit(menuDataItem, 'exact'), path: menuDataItem.path || '/', name: getIntlName(menuDataItem), subNavigationItems: menuDataItem.children.map(item => ({ ...omit(item, 'exact'), path: item.path || '/', name: getIntlName(item), })), }; } return { ...omit(menuDataItem, 'exact'), path: menuDataItem.path || '/', name: getIntlName(menuDataItem), }; }), [getIntlName], ); const header = useMemo(() => { const defaultDom = (
{isNavigationCollapsed && ( )}
{leftContent}
{rightContent}
); return (
{headerRender ? headerRender(defaultDom) : defaultDom}
); }, [ headerRef, headerRender, isNavigationCollapsed, leftContent, prefixCls, rightContent, showMobileNavigation, ]); return (
{header}
{isNavigationCollapsed && leftContent && (
{leftContent}
)} {renderNavigation ? ( renderNavigation(finallyData, renderItems) ) : ( )}
{showMobileNavigation && isNavigationCollapsed && (
setShowMobileNavigation(false)} onTouchStart={() => setShowMobileNavigation(false)} /> )}
{children}
); }; export default Frame;