@ant-design/pro-layout
Version:
528 lines (510 loc) • 24.4 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
var _excluded = ["id", "defaultMessage"],
_excluded2 = ["fixSiderbar", "navTheme", "layout"];
import { ProConfigProvider, ProProvider, isNeedOpenHash } from '@ant-design/pro-provider';
import { coverToNewToken, isBrowser, useBreakpoint, useDocumentTitle, useMountMergeState } from '@ant-design/pro-utils';
import { getMatchMenu } from '@umijs/route-utils';
import { ConfigProvider, Layout } from 'antd';
import classNames from 'classnames';
import omit from "rc-util/es/omit";
import useMergedState from "rc-util/es/hooks/useMergedState";
import warning from "rc-util/es/warning";
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import useSWR, { useSWRConfig } from 'swr';
import { WrapContent } from "./WrapContent";
import { Logo } from "./assert/Logo";
import { DefaultFooter as Footer } from "./components/Footer";
import { DefaultHeader as Header } from "./components/Header";
import { PageLoading } from "./components/PageLoading";
import { SiderMenu } from "./components/SiderMenu";
import { RouteContext } from "./context/RouteContext";
import { defaultSettings } from "./defaultSettings";
import { getPageTitleInfo } from "./getPageTitle";
import { gLocaleObject } from "./locales";
import { useStyle } from "./style";
import { getBreadcrumbProps } from "./utils/getBreadcrumbProps";
import { getMenuData } from "./utils/getMenuData";
import { useCurrentMenuLayoutProps } from "./utils/useCurrentMenuLayoutProps";
import { clearMenuItem } from "./utils/utils";
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
var layoutIndex = 0;
var headerRender = function headerRender(props, matchMenuKeys) {
var _props$stylish;
if (props.headerRender === false || props.pure) {
return null;
}
return /*#__PURE__*/_jsx(Header, _objectSpread(_objectSpread({
matchMenuKeys: matchMenuKeys
}, props), {}, {
stylish: (_props$stylish = props.stylish) === null || _props$stylish === void 0 ? void 0 : _props$stylish.header
}));
};
var footerRender = function footerRender(props) {
if (props.footerRender === false || props.pure) {
return null;
}
if (props.footerRender) {
return props.footerRender(_objectSpread({}, props), /*#__PURE__*/_jsx(Footer, {}));
}
return null;
};
var renderSiderMenu = function renderSiderMenu(props, matchMenuKeys) {
var _props$stylish3;
var layout = props.layout,
isMobile = props.isMobile,
selectedKeys = props.selectedKeys,
openKeys = props.openKeys,
splitMenus = props.splitMenus,
suppressSiderWhenMenuEmpty = props.suppressSiderWhenMenuEmpty,
menuRender = props.menuRender;
if (props.menuRender === false || props.pure) {
return null;
}
var menuData = props.menuData;
/** 如果是分割菜单模式,需要专门实现一下 */
if (splitMenus && (openKeys !== false || layout === 'mix') && !isMobile) {
var _ref = selectedKeys || matchMenuKeys,
_ref2 = _slicedToArray(_ref, 1),
key = _ref2[0];
if (key) {
var _props$menuData;
menuData = ((_props$menuData = props.menuData) === null || _props$menuData === void 0 || (_props$menuData = _props$menuData.find(function (item) {
return item.key === key;
})) === null || _props$menuData === void 0 ? void 0 : _props$menuData.children) || [];
} else {
menuData = [];
}
}
// 这里走了可以少一次循环
var clearMenuData = clearMenuItem(menuData || []);
if (clearMenuData && (clearMenuData === null || clearMenuData === void 0 ? void 0 : clearMenuData.length) < 1 && (splitMenus || suppressSiderWhenMenuEmpty)) {
return null;
}
if (layout === 'top' && !isMobile) {
var _props$stylish2;
return /*#__PURE__*/_jsx(SiderMenu, _objectSpread(_objectSpread({
matchMenuKeys: matchMenuKeys
}, props), {}, {
hide: true,
stylish: (_props$stylish2 = props.stylish) === null || _props$stylish2 === void 0 ? void 0 : _props$stylish2.sider
}));
}
var defaultDom = /*#__PURE__*/_jsx(SiderMenu, _objectSpread(_objectSpread({
matchMenuKeys: matchMenuKeys
}, props), {}, {
// 这里走了可以少一次循环
menuData: clearMenuData,
stylish: (_props$stylish3 = props.stylish) === null || _props$stylish3 === void 0 ? void 0 : _props$stylish3.sider
}));
if (menuRender) {
return menuRender(props, defaultDom);
}
return defaultDom;
};
var defaultPageTitleRender = function defaultPageTitleRender(pageProps, props) {
var pageTitleRender = props.pageTitleRender;
var pageTitleInfo = getPageTitleInfo(pageProps);
if (pageTitleRender === false) {
return {
title: props.title || '',
id: '',
pageName: ''
};
}
if (pageTitleRender) {
var title = pageTitleRender(pageProps, pageTitleInfo.title, pageTitleInfo);
if (typeof title === 'string') {
return getPageTitleInfo(_objectSpread(_objectSpread({}, pageTitleInfo), {}, {
title: title
}));
}
warning(typeof title === 'string', 'pro-layout: renderPageTitle return value should be a string');
}
return pageTitleInfo;
};
var getPaddingInlineStart = function getPaddingInlineStart(hasLeftPadding, collapsed, siderWidth) {
if (hasLeftPadding) {
return collapsed ? 64 : siderWidth;
}
return 0;
};
/**
* 🌃 Powerful and easy to use beautiful layout 🏄 Support multiple topics and layout types
*
* @param props
*/
var BaseProLayout = function BaseProLayout(props) {
var _props$prefixCls, _location$pathname, _token$layout, _token$layout2, _token$layout3, _token$layout4, _token$layout5, _token$layout6, _token$layout7, _token$layout8, _token$layout9, _token$layout10, _token$layout11, _token$layout12;
var _ref3 = props || {},
children = _ref3.children,
propsOnCollapse = _ref3.onCollapse,
_ref3$location = _ref3.location,
location = _ref3$location === void 0 ? {
pathname: '/'
} : _ref3$location,
contentStyle = _ref3.contentStyle,
route = _ref3.route,
defaultCollapsed = _ref3.defaultCollapsed,
style = _ref3.style,
propsSiderWidth = _ref3.siderWidth,
menu = _ref3.menu,
siderMenuType = _ref3.siderMenuType,
propsIsChildrenLayout = _ref3.isChildrenLayout,
menuDataRender = _ref3.menuDataRender,
actionRef = _ref3.actionRef,
bgLayoutImgList = _ref3.bgLayoutImgList,
propsFormatMessage = _ref3.formatMessage,
loading = _ref3.loading;
var siderWidth = useMemo(function () {
if (propsSiderWidth) return propsSiderWidth;
if (props.layout === 'mix') return 215;
return 256;
}, [props.layout, propsSiderWidth]);
var context = useContext(ConfigProvider.ConfigContext);
var prefixCls = (_props$prefixCls = props.prefixCls) !== null && _props$prefixCls !== void 0 ? _props$prefixCls : context.getPrefixCls('pro');
var _useMountMergeState = useMountMergeState(false, {
value: menu === null || menu === void 0 ? void 0 : menu.loading,
onChange: menu === null || menu === void 0 ? void 0 : menu.onLoadingChange
}),
_useMountMergeState2 = _slicedToArray(_useMountMergeState, 2),
menuLoading = _useMountMergeState2[0],
setMenuLoading = _useMountMergeState2[1];
// give a default key for swr
var _useState = useState(function () {
layoutIndex += 1;
return "pro-layout-".concat(layoutIndex);
}),
_useState2 = _slicedToArray(_useState, 1),
defaultId = _useState2[0];
/**
* 处理国际化相关 formatMessage
* 如果有用户配置的以用户为主
* 如果没有用自己实现的
*/
var formatMessage = useCallback(function (_ref4) {
var id = _ref4.id,
defaultMessage = _ref4.defaultMessage,
restParams = _objectWithoutProperties(_ref4, _excluded);
if (propsFormatMessage) {
return propsFormatMessage(_objectSpread({
id: id,
defaultMessage: defaultMessage
}, restParams));
}
var locales = gLocaleObject();
return locales[id] ? locales[id] : defaultMessage;
}, [propsFormatMessage]);
var _useSWR = useSWR([defaultId, menu === null || menu === void 0 ? void 0 : menu.params], /*#__PURE__*/function () {
var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref5) {
var _menu$request;
var _ref7, params, menuDataItems;
return _regeneratorRuntime().wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_ref7 = _slicedToArray(_ref5, 2), params = _ref7[1];
setMenuLoading(true);
_context.next = 4;
return menu === null || menu === void 0 || (_menu$request = menu.request) === null || _menu$request === void 0 ? void 0 : _menu$request.call(menu, params || {}, (route === null || route === void 0 ? void 0 : route.children) || (route === null || route === void 0 ? void 0 : route.routes) || []);
case 4:
menuDataItems = _context.sent;
setMenuLoading(false);
return _context.abrupt("return", menuDataItems);
case 7:
case "end":
return _context.stop();
}
}, _callee);
}));
return function (_x) {
return _ref6.apply(this, arguments);
};
}(), {
revalidateOnFocus: false,
shouldRetryOnError: false,
revalidateOnReconnect: false
}),
data = _useSWR.data,
mutate = _useSWR.mutate,
isLoading = _useSWR.isLoading;
useEffect(function () {
setMenuLoading(isLoading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading]);
var _useSWRConfig = useSWRConfig(),
cache = _useSWRConfig.cache;
useEffect(function () {
return function () {
if (cache instanceof Map) cache.delete(defaultId);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
var menuInfoData = useMemo(function () {
return getMenuData(data || (route === null || route === void 0 ? void 0 : route.children) || (route === null || route === void 0 ? void 0 : route.routes) || [], menu, formatMessage, menuDataRender);
}, [formatMessage, menu, menuDataRender, data, route === null || route === void 0 ? void 0 : route.children, route === null || route === void 0 ? void 0 : route.routes]);
var _ref8 = menuInfoData || {},
breadcrumb = _ref8.breadcrumb,
breadcrumbMap = _ref8.breadcrumbMap,
_ref8$menuData = _ref8.menuData,
menuData = _ref8$menuData === void 0 ? [] : _ref8$menuData;
if (actionRef && menu !== null && menu !== void 0 && menu.request) {
actionRef.current = {
reload: function reload() {
mutate();
}
};
}
var matchMenus = useMemo(function () {
return getMatchMenu(location.pathname || '/', menuData || [], true);
}, [location.pathname, menuData]);
var matchMenuKeys = useMemo(function () {
return Array.from(new Set(matchMenus.map(function (item) {
return item.key || item.path || '';
})));
}, [matchMenus]);
// 当前选中的menu,一般不会为空
var currentMenu = matchMenus[matchMenus.length - 1] || {};
var currentMenuLayoutProps = useCurrentMenuLayoutProps(currentMenu);
var _props$currentMenuLay = _objectSpread(_objectSpread({}, props), currentMenuLayoutProps),
fixSiderbar = _props$currentMenuLay.fixSiderbar,
navTheme = _props$currentMenuLay.navTheme,
propsLayout = _props$currentMenuLay.layout,
rest = _objectWithoutProperties(_props$currentMenuLay, _excluded2);
var colSize = useBreakpoint();
var isMobile = useMemo(function () {
return (colSize === 'sm' || colSize === 'xs') && !props.disableMobile;
}, [colSize, props.disableMobile]);
// If it is a fix menu, calculate padding
// don't need padding in phone mode
/* Checking if the menu is loading and if it is, it will return a skeleton loading screen. */
var hasLeftPadding = propsLayout !== 'top' && !isMobile;
var _useMergedState = useMergedState(function () {
if (defaultCollapsed !== undefined) return defaultCollapsed;
if (process.env.NODE_ENV === 'TEST') return false;
if (isMobile) return true;
if (colSize === 'md') return true;
return false;
}, {
value: props.collapsed,
onChange: propsOnCollapse
}),
_useMergedState2 = _slicedToArray(_useMergedState, 2),
collapsed = _useMergedState2[0],
onCollapse = _useMergedState2[1];
// Splicing parameters, adding menuData and formatMessage in props
var defaultProps = omit(_objectSpread(_objectSpread(_objectSpread({
prefixCls: prefixCls
}, props), {}, {
siderWidth: siderWidth
}, currentMenuLayoutProps), {}, {
formatMessage: formatMessage,
breadcrumb: breadcrumb,
menu: _objectSpread(_objectSpread({}, menu), {}, {
type: siderMenuType || (menu === null || menu === void 0 ? void 0 : menu.type),
loading: menuLoading
}),
layout: propsLayout
}), ['className', 'style', 'breadcrumbRender']);
// gen page title
var pageTitleInfo = defaultPageTitleRender(_objectSpread(_objectSpread({
pathname: location.pathname
}, defaultProps), {}, {
breadcrumbMap: breadcrumbMap
}), props);
// gen breadcrumbProps, parameter for pageHeader
var breadcrumbProps = getBreadcrumbProps(_objectSpread(_objectSpread({}, defaultProps), {}, {
breadcrumbRender: props.breadcrumbRender,
breadcrumbMap: breadcrumbMap
}), props);
// render sider dom
var siderMenuDom = renderSiderMenu(_objectSpread(_objectSpread({}, defaultProps), {}, {
menuData: menuData,
onCollapse: onCollapse,
isMobile: isMobile,
collapsed: collapsed
}), matchMenuKeys);
// render header dom
var headerDom = headerRender(_objectSpread(_objectSpread({}, defaultProps), {}, {
children: null,
hasSiderMenu: !!siderMenuDom,
menuData: menuData,
isMobile: isMobile,
collapsed: collapsed,
onCollapse: onCollapse
}), matchMenuKeys);
// render footer dom
var footerDom = footerRender(_objectSpread({
isMobile: isMobile,
collapsed: collapsed
}, defaultProps));
var _useContext = useContext(RouteContext),
contextIsChildrenLayout = _useContext.isChildrenLayout;
// 如果 props 中定义,以 props 为准
var isChildrenLayout = propsIsChildrenLayout !== undefined ? propsIsChildrenLayout : contextIsChildrenLayout;
var proLayoutClassName = "".concat(prefixCls, "-layout");
var _useStyle = useStyle(proLayoutClassName),
wrapSSR = _useStyle.wrapSSR,
hashId = _useStyle.hashId;
// gen className
var className = classNames(props.className, hashId, 'ant-design-pro', proLayoutClassName, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, "screen-".concat(colSize), colSize), "".concat(proLayoutClassName, "-top-menu"), propsLayout === 'top'), "".concat(proLayoutClassName, "-is-children"), isChildrenLayout), "".concat(proLayoutClassName, "-fix-siderbar"), fixSiderbar), "".concat(proLayoutClassName, "-").concat(propsLayout), propsLayout));
/** 计算 slider 的宽度 */
var leftSiderWidth = getPaddingInlineStart(!!hasLeftPadding, collapsed, siderWidth);
// siderMenuDom 为空的时候,不需要 padding
var genLayoutStyle = {
position: 'relative'
};
// if is some layout children, don't need min height
if (isChildrenLayout || contentStyle && contentStyle.minHeight) {
genLayoutStyle.minHeight = 0;
}
/** 页面切换的时候触发 */
useEffect(function () {
var _props$onPageChange;
(_props$onPageChange = props.onPageChange) === null || _props$onPageChange === void 0 || _props$onPageChange.call(props, props.location);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location.pathname, (_location$pathname = location.pathname) === null || _location$pathname === void 0 ? void 0 : _location$pathname.search]);
var _useState3 = useState(false),
_useState4 = _slicedToArray(_useState3, 2),
hasFooterToolbar = _useState4[0],
setHasFooterToolbar = _useState4[1];
/**
* 使用number是因为多标签页的时候有多个 PageContainer,只有有任意一个就应该展示这个className
*/
var _useState5 = useState(0),
_useState6 = _slicedToArray(_useState5, 2),
hasPageContainer = _useState6[0],
setHasPageContainer = _useState6[1];
useDocumentTitle(pageTitleInfo, props.title || false);
var _useContext2 = useContext(ProProvider),
token = _useContext2.token;
var bgImgStyleList = useMemo(function () {
if (bgLayoutImgList && bgLayoutImgList.length > 0) {
return bgLayoutImgList === null || bgLayoutImgList === void 0 ? void 0 : bgLayoutImgList.map(function (item, index) {
return /*#__PURE__*/_jsx("img", {
src: item.src,
style: _objectSpread({
position: 'absolute'
}, item)
}, index);
});
}
return null;
}, [bgLayoutImgList]);
return wrapSSR( /*#__PURE__*/_jsx(RouteContext.Provider, {
value: _objectSpread(_objectSpread({}, defaultProps), {}, {
breadcrumb: breadcrumbProps,
menuData: menuData,
isMobile: isMobile,
collapsed: collapsed,
hasPageContainer: hasPageContainer,
setHasPageContainer: setHasPageContainer,
isChildrenLayout: true,
title: pageTitleInfo.pageName,
hasSiderMenu: !!siderMenuDom,
hasHeader: !!headerDom,
siderWidth: leftSiderWidth,
hasFooter: !!footerDom,
hasFooterToolbar: hasFooterToolbar,
setHasFooterToolbar: setHasFooterToolbar,
pageTitleInfo: pageTitleInfo,
matchMenus: matchMenus,
matchMenuKeys: matchMenuKeys,
currentMenu: currentMenu
}),
children: props.pure ? /*#__PURE__*/_jsx(_Fragment, {
children: children
}) : /*#__PURE__*/_jsxs("div", {
className: className,
children: [bgImgStyleList || (_token$layout = token.layout) !== null && _token$layout !== void 0 && _token$layout.bgLayout ? /*#__PURE__*/_jsx("div", {
className: classNames("".concat(proLayoutClassName, "-bg-list"), hashId),
children: bgImgStyleList
}) : null, /*#__PURE__*/_jsxs(Layout, {
style: _objectSpread({
minHeight: '100%',
// hack style
flexDirection: siderMenuDom ? 'row' : undefined
}, style),
children: [/*#__PURE__*/_jsx(ConfigProvider
// @ts-ignore
, {
theme: {
hashed: isNeedOpenHash(),
token: {
controlHeightLG: ((_token$layout2 = token.layout) === null || _token$layout2 === void 0 || (_token$layout2 = _token$layout2.sider) === null || _token$layout2 === void 0 ? void 0 : _token$layout2.menuHeight) || (token === null || token === void 0 ? void 0 : token.controlHeightLG)
},
components: {
Menu: coverToNewToken({
colorItemBg: ((_token$layout3 = token.layout) === null || _token$layout3 === void 0 || (_token$layout3 = _token$layout3.sider) === null || _token$layout3 === void 0 ? void 0 : _token$layout3.colorMenuBackground) || 'transparent',
colorSubItemBg: ((_token$layout4 = token.layout) === null || _token$layout4 === void 0 || (_token$layout4 = _token$layout4.sider) === null || _token$layout4 === void 0 ? void 0 : _token$layout4.colorMenuBackground) || 'transparent',
radiusItem: token.borderRadius,
colorItemBgSelected: ((_token$layout5 = token.layout) === null || _token$layout5 === void 0 || (_token$layout5 = _token$layout5.sider) === null || _token$layout5 === void 0 ? void 0 : _token$layout5.colorBgMenuItemSelected) || (token === null || token === void 0 ? void 0 : token.colorBgTextHover),
colorItemBgHover: ((_token$layout6 = token.layout) === null || _token$layout6 === void 0 || (_token$layout6 = _token$layout6.sider) === null || _token$layout6 === void 0 ? void 0 : _token$layout6.colorBgMenuItemHover) || (token === null || token === void 0 ? void 0 : token.colorBgTextHover),
colorItemBgActive: ((_token$layout7 = token.layout) === null || _token$layout7 === void 0 || (_token$layout7 = _token$layout7.sider) === null || _token$layout7 === void 0 ? void 0 : _token$layout7.colorBgMenuItemActive) || (token === null || token === void 0 ? void 0 : token.colorBgTextActive),
colorItemBgSelectedHorizontal: ((_token$layout8 = token.layout) === null || _token$layout8 === void 0 || (_token$layout8 = _token$layout8.sider) === null || _token$layout8 === void 0 ? void 0 : _token$layout8.colorBgMenuItemSelected) || (token === null || token === void 0 ? void 0 : token.colorBgTextHover),
colorActiveBarWidth: 0,
colorActiveBarHeight: 0,
colorActiveBarBorderSize: 0,
colorItemText: ((_token$layout9 = token.layout) === null || _token$layout9 === void 0 || (_token$layout9 = _token$layout9.sider) === null || _token$layout9 === void 0 ? void 0 : _token$layout9.colorTextMenu) || (token === null || token === void 0 ? void 0 : token.colorTextSecondary),
colorItemTextHover: ((_token$layout10 = token.layout) === null || _token$layout10 === void 0 || (_token$layout10 = _token$layout10.sider) === null || _token$layout10 === void 0 ? void 0 : _token$layout10.colorTextMenuItemHover) || 'rgba(0, 0, 0, 0.85)',
// 悬浮态
colorItemTextSelected: ((_token$layout11 = token.layout) === null || _token$layout11 === void 0 || (_token$layout11 = _token$layout11.sider) === null || _token$layout11 === void 0 ? void 0 : _token$layout11.colorTextMenuSelected) || 'rgba(0, 0, 0, 1)',
popupBg: token === null || token === void 0 ? void 0 : token.colorBgElevated,
subMenuItemBg: token === null || token === void 0 ? void 0 : token.colorBgElevated,
darkSubMenuItemBg: 'transparent',
darkPopupBg: token === null || token === void 0 ? void 0 : token.colorBgElevated
})
}
},
children: siderMenuDom
}), /*#__PURE__*/_jsxs("div", {
style: genLayoutStyle,
className: "".concat(proLayoutClassName, "-container ").concat(hashId).trim(),
children: [headerDom, /*#__PURE__*/_jsx(WrapContent, _objectSpread(_objectSpread({
hasPageContainer: hasPageContainer,
isChildrenLayout: isChildrenLayout
}, rest), {}, {
hasHeader: !!headerDom,
prefixCls: proLayoutClassName,
style: contentStyle,
children: loading ? /*#__PURE__*/_jsx(PageLoading, {}) : children
})), footerDom, hasFooterToolbar && /*#__PURE__*/_jsx("div", {
className: "".concat(proLayoutClassName, "-has-footer"),
style: {
height: 64,
marginBlockStart: (_token$layout12 = token.layout) === null || _token$layout12 === void 0 || (_token$layout12 = _token$layout12.pageContainer) === null || _token$layout12 === void 0 ? void 0 : _token$layout12.paddingBlockPageContainerContent
}
})]
})]
})]
})
}));
};
var ProLayout = function ProLayout(props) {
var colorPrimary = props.colorPrimary;
var darkProps = props.navTheme !== undefined ? {
dark: props.navTheme === 'realDark'
} : {};
return /*#__PURE__*/_jsx(ConfigProvider, {
theme: colorPrimary ? {
token: {
colorPrimary: colorPrimary
}
} : undefined,
children: /*#__PURE__*/_jsx(ProConfigProvider, _objectSpread(_objectSpread({}, darkProps), {}, {
token: props.token,
prefixCls: props.prefixCls,
children: /*#__PURE__*/_jsx(BaseProLayout, _objectSpread(_objectSpread({
logo: /*#__PURE__*/_jsx(Logo, {})
}, defaultSettings), {}, {
location: isBrowser() ? window.location : undefined
}, props))
}))
});
};
export { ProLayout };