import React, { Component } from 'react'; import { Link, browserHistory } from 'bisheng/router'; import { Row, Col, Menu, Affix, Tooltip, Avatar, Dropdown } from 'antd'; import { injectIntl } from 'react-intl'; import { LeftOutlined, RightOutlined, ExportOutlined } from '@ant-design/icons'; import ContributorsList from '@qixian.cs/github-contributors-list'; import classNames from 'classnames'; import get from 'lodash/get'; import MobileMenu from 'rc-drawer'; import ThemeIcon from './ThemeIcon'; import Article from './Article'; import PrevAndNext from './PrevAndNext'; import Footer from '../Layout/Footer'; import SiteContext from '../Layout/SiteContext'; import ComponentDoc from './ComponentDoc'; import ComponentOverview from './ComponentOverview'; import * as utils from '../utils'; const { SubMenu } = Menu; function getModuleData(props) { const { pathname } = props.location; const moduleName = /^\/?components/.test(pathname) ? 'components' : pathname .split('/') .filter(item => item) .slice(0, 2) .join('/'); const excludedSuffix = utils.isZhCN(props.location.pathname) ? 'en-US.md' : 'zh-CN.md'; let data; switch (moduleName) { case 'docs/react': case 'changelog': case 'changelog-cn': data = [...props.picked['docs/react'], ...props.picked.changelog]; break; default: data = props.picked[moduleName]; } return data.filter(({ meta }) => !meta.filename.endsWith(excludedSuffix)); } function fileNameToPath(filename) { const snippets = filename.replace(/(\/index)?((\.zh-cn)|(\.en-us))?\.md$/i, '').split('/'); return snippets[snippets.length - 1]; } function getSideBarOpenKeys(nextProps) { const { themeConfig } = nextProps; const { pathname } = nextProps.location; const locale = utils.isZhCN(pathname) ? 'zh-CN' : 'en-US'; const moduleData = getModuleData(nextProps); const shouldOpenKeys = utils .getMenuItems(moduleData, locale, themeConfig.categoryOrder, themeConfig.typeOrder) .map(m => (m.title && m.title[locale]) || m.title); return shouldOpenKeys; } function clearActiveToc() { [].forEach.call(document.querySelectorAll('.toc-affix li a'), node => { node.className = ''; }); } function updateActiveToc(id) { const currentNode = document.querySelectorAll(`.toc-affix li a[href="#${id}"]`)[0]; if (currentNode) { clearActiveToc(); currentNode.className = 'current'; } } class MainContent extends Component { static contextType = SiteContext; state = { openKeys: undefined, }; componentDidMount() { window.addEventListener('load', this.handleLoad); window.addEventListener('hashchange', this.handleHashChange); } static getDerivedStateFromProps(props, state) { if (!state.openKeys) { return { ...state, openKeys: getSideBarOpenKeys(props), }; } return null; } componentDidUpdate(prevProps) { const { location } = this.props; const { location: prevLocation = {} } = prevProps || {}; if (!prevProps || prevLocation.pathname !== location.pathname) { this.bindScroller(); } if (!window.location.hash && prevLocation.pathname !== location.pathname) { clearActiveToc(); window.scrollTo(0, 0); } // when subMenu not equal if (get(this.props, 'route.path') !== get(prevProps, 'route.path')) { // reset menu OpenKeys this.handleMenuOpenChange(); } } componentWillUnmount() { if (this.scroller) { this.scroller.destroy(); } window.removeEventListener('load', this.handleLoad); window.removeEventListener('hashchange', this.handleHashChange); clearTimeout(this.timeout); } getMenuItems(footerNavIcons = {}) { const { themeConfig, intl: { locale }, } = this.props; const moduleData = getModuleData(this.props); const menuItems = utils.getMenuItems( moduleData, locale, themeConfig.categoryOrder, themeConfig.typeOrder, ); return menuItems.map(menuItem => { if (menuItem.title === 'Overview' || menuItem.title === '组件总览') { return menuItem.children.map(leaf => this.generateMenuItem(false, leaf, footerNavIcons)); } if (menuItem.type === 'type') { return ( {menuItem.children .sort((a, b) => a.title.localeCompare(b.title)) .map(leaf => this.generateMenuItem(false, leaf, footerNavIcons))} ); } if (menuItem.children) { return ( {menuItem.children.map(child => { if (child.type === 'type') { return ( {child.children.map(leaf => this.generateMenuItem(false, leaf, footerNavIcons))} ); } return this.generateMenuItem(false, child, footerNavIcons); })} ); } return this.generateMenuItem(true, menuItem, footerNavIcons); }); } getFooterNav(menuItems, activeMenuItem) { const menuItemsList = this.flattenMenu(menuItems); let activeMenuItemIndex = -1; menuItemsList.forEach((menuItem, i) => { if (menuItem && menuItem.key === activeMenuItem) { activeMenuItemIndex = i; } }); const prev = menuItemsList[activeMenuItemIndex - 1]; const next = menuItemsList[activeMenuItemIndex + 1]; return { prev, next }; } getActiveMenuItem() { const { params: { children }, location, } = this.props; return ( (children && children.replace('-cn', '')) || location.pathname.replace(/(^\/|-cn$)/g, '') ); } handleMenuOpenChange = openKeys => { this.setState({ openKeys }); }; handleLoad = () => { if (window.location.hash) { updateActiveToc(window.location.hash.replace(/^#/, '')); } this.bindScroller(); }; handleHashChange = () => { this.timeout = setTimeout(() => { updateActiveToc(window.location.hash.replace(/^#/, '')); }); }; bindScroller() { if (this.scroller) { this.scroller.destroy(); } if (!document.querySelector('.markdown > h2, .code-box')) { return; } // eslint-disable-next-line global-require require('intersection-observer'); // eslint-disable-next-line global-require const scrollama = require('scrollama'); this.scroller = scrollama(); this.scroller .setup({ step: '.markdown > h2, .code-box', // required offset: '10px', }) .onStepEnter(({ element }) => { updateActiveToc(element.id); }); } generateMenuItem(isTop, item, { before = null, after = null }) { const { intl: { locale }, location, } = this.props; const key = fileNameToPath(item.filename); if (!item.title) { return null; } const title = item.title[locale] || item.title; const text = isTop ? title : [ {title}, {item.subtitle} , ]; const { disabled } = item; const url = item.filename.replace(/(\/index)?((\.zh-cn)|(\.en-us))?\.md$/i, '').toLowerCase(); const child = !item.link ? ( {before} {text} {after} ) : ( {before} {text} {after} ); return ( {child} ); } getThemeSwitchMenu() { const { theme } = this.context; const { intl: { formatMessage }, } = this.props; return ( this.changeThemeMode(key)} selectedKeys={[theme]}> {[ { type: 'default', text: formatMessage({ id: 'app.theme.switch.default' }) }, { type: 'dark', text: formatMessage({ id: 'app.theme.switch.dark' }) }, { type: 'compact', text: formatMessage({ id: 'app.theme.switch.compact' }) }, ].map(({ type, text }) => ( {text} ))} ); } flattenMenu(menu) { if (!menu) { return null; } if (menu.type && menu.type.isMenuItem) { return menu; } if (Array.isArray(menu)) { return menu.reduce((acc, item) => acc.concat(this.flattenMenu(item)), []); } return this.flattenMenu((menu.props && menu.props.children) || menu.children); } changeThemeMode = theme => { const { setTheme, theme: selectedTheme } = this.context; const { pathname, hash, query } = this.props.location; if (selectedTheme !== theme) { setTheme(theme); if (theme === 'default') { document.documentElement.style.colorScheme = 'light'; setColor(false); delete query.theme; } else { if (theme === 'dark') { document.documentElement.style.colorScheme = 'dark'; setColor(true); } query.theme = theme; } browserHistory.push({ pathname: `/${pathname}`, query, hash, }); } }; renderContributors() { const { localizedPageData: { meta }, intl: { formatMessage }, } = this.props; return ( loading ? ( ) : ( {item.username} ) } repo="ant-design" owner="ant-design" /> ); } renderMainContent({ theme, setIframeTheme }) { const { localizedPageData, demos, location } = this.props; if (location.pathname.includes('components/overview')) { const type = utils.isZhCN(location.pathname) ? '重型组件' : 'ProComponents'; return ( meta.category === 'Components') .concat([ { meta: { category: 'Components', cover: 'https://gw.alipayobjects.com/zos/antfincdn/4n5H%24UX%24j/bianzu%2525204.svg', filename: 'https://procomponents.ant.design/components/layout', subtitle: '高级布局', title: 'ProLayout', type, tag: 'https://gw.alipayobjects.com/zos/antfincdn/OG4ajVYzh/bianzu%2525202.svg', }, }, { meta: { category: 'Components', cover: 'https://gw.alipayobjects.com/zos/antfincdn/mStei5BFC/bianzu%2525207.svg', filename: 'https://procomponents.ant.design/components/form', subtitle: '高级表单', title: 'ProForm', type, tag: 'https://gw.alipayobjects.com/zos/antfincdn/OG4ajVYzh/bianzu%2525202.svg', }, }, { meta: { category: 'Components', cover: 'https://gw.alipayobjects.com/zos/antfincdn/AwU0Cv%26Ju/bianzu%2525208.svg', filename: 'https://procomponents.ant.design/components/table', subtitle: '高级表格', title: 'ProTable', type, tag: 'https://gw.alipayobjects.com/zos/antfincdn/OG4ajVYzh/bianzu%2525202.svg', }, }, { meta: { category: 'Components', cover: 'https://gw.alipayobjects.com/zos/antfincdn/H0%26LSYYfh/bianzu%2525209.svg', filename: 'https://procomponents.ant.design/components/descriptions', subtitle: '高级定义列表', title: 'ProDescriptions', type, tag: 'https://gw.alipayobjects.com/zos/antfincdn/OG4ajVYzh/bianzu%2525202.svg', }, }, { meta: { category: 'Components', cover: 'https://gw.alipayobjects.com/zos/antfincdn/uZUmLtne5/bianzu%2525209.svg', filename: 'https://procomponents.ant.design/components/list', subtitle: '高级列表', title: 'ProList', type, tag: 'https://gw.alipayobjects.com/zos/antfincdn/OG4ajVYzh/bianzu%2525202.svg', }, }, { meta: { category: 'Components', cover: 'https://gw.alipayobjects.com/zos/antfincdn/N3eU432oA/bianzu%2525209.svg', filename: 'https://procomponents.ant.design/components/editable-table', subtitle: '可编辑表格', title: 'EditableProTable', type, tag: 'https://gw.alipayobjects.com/zos/antfincdn/OG4ajVYzh/bianzu%2525202.svg', }, }, ])} /> ); } if (demos) { return ( <> {this.renderContributors()} ); } return ( <>
{this.renderContributors()} ); } render() { const { demos, location } = this.props; const { openKeys } = this.state; const { isMobile, theme, setIframeTheme } = this.context; const activeMenuItem = this.getActiveMenuItem(); const menuItems = this.getMenuItems(); const menuItemsForFooterNav = this.getMenuItems({ before: , after: , }); const { prev, next } = this.getFooterNav(menuItemsForFooterNav, activeMenuItem); const mainContainerClass = classNames('main-container', { 'main-container-component': !!demos, }); const menuChild = ( {menuItems} ); const componentPage = /^\/?components/.test(location.pathname); return (
{isMobile ? ( {menuChild} ) : (
{menuChild}
)}
{this.renderMainContent({ theme, setIframeTheme })}
{componentPage && (
} />
)}
); } } export default injectIntl(MainContent);