118 lines
4.0 KiB
TypeScript
118 lines
4.0 KiB
TypeScript
|
import React, { useContext, useState, useEffect } from 'react';
|
||
|
import type { IRouteComponentProps } from '@umijs/types';
|
||
|
import { context, Link } from 'dumi/theme';
|
||
|
import Navbar from './components/Navbar';
|
||
|
import SideMenu from './components/SideMenu';
|
||
|
import SlugList from './components/SlugList';
|
||
|
import SearchBar from './components/SearchBar';
|
||
|
import './style/layout.less';
|
||
|
|
||
|
const Hero = hero => (
|
||
|
<>
|
||
|
<div className="__dumi-default-layout-hero">
|
||
|
{hero.image && <img src={hero.image} />}
|
||
|
<h1>{hero.title}</h1>
|
||
|
<div dangerouslySetInnerHTML={{ __html: hero.desc }} />
|
||
|
{hero.actions &&
|
||
|
hero.actions.map(action => (
|
||
|
<Link to={action.link} key={action.text}>
|
||
|
<button type="button">{action.text}</button>
|
||
|
</Link>
|
||
|
))}
|
||
|
</div>
|
||
|
</>
|
||
|
);
|
||
|
|
||
|
const Features = features => (
|
||
|
<div className="__dumi-default-layout-features">
|
||
|
{features.map(feat => (
|
||
|
<dl key={feat.title} style={{ backgroundImage: feat.icon ? `url(${feat.icon})` : undefined }}>
|
||
|
{feat.link ? (
|
||
|
<Link to={feat.link}>
|
||
|
<dt>{feat.title}</dt>
|
||
|
</Link>
|
||
|
) : (
|
||
|
<dt>{feat.title}</dt>
|
||
|
)}
|
||
|
<dd dangerouslySetInnerHTML={{ __html: feat.desc }} />
|
||
|
</dl>
|
||
|
))}
|
||
|
</div>
|
||
|
);
|
||
|
|
||
|
const Layout: React.FC<IRouteComponentProps> = ({ children, location }) => {
|
||
|
const {
|
||
|
config: { mode, repository },
|
||
|
meta,
|
||
|
locale,
|
||
|
} = useContext(context);
|
||
|
const { url: repoUrl, branch, platform } = repository;
|
||
|
const [menuCollapsed, setMenuCollapsed] = useState<boolean>(true);
|
||
|
const isSiteMode = mode === 'site';
|
||
|
const showHero = isSiteMode && meta.hero;
|
||
|
const showFeatures = isSiteMode && meta.features;
|
||
|
const showSideMenu = meta.sidemenu !== false && !showHero && !showFeatures && !meta.gapless;
|
||
|
const showSlugs =
|
||
|
!showHero &&
|
||
|
!showFeatures &&
|
||
|
Boolean(meta.slugs?.length) &&
|
||
|
(meta.toc === 'content' || meta.toc === undefined) &&
|
||
|
!meta.gapless;
|
||
|
const isCN = /^zh|cn$/i.test(locale);
|
||
|
const updatedTimeIns = new Date(meta.updatedTime);
|
||
|
const updatedTime: any = `${updatedTimeIns.toLocaleDateString([], { hour12: false })} ${updatedTimeIns.toLocaleTimeString([], { hour12: false })}`;
|
||
|
const repoPlatform =
|
||
|
{ github: 'GitHub', gitlab: 'GitLab' }[
|
||
|
(repoUrl || '').match(/(github|gitlab)/)?.[1] || 'nothing'
|
||
|
] || platform;
|
||
|
|
||
|
return (
|
||
|
<div
|
||
|
className="__dumi-default-layout"
|
||
|
data-route={location.pathname}
|
||
|
data-show-sidemenu={String(showSideMenu)}
|
||
|
data-show-slugs={String(showSlugs)}
|
||
|
data-site-mode={isSiteMode}
|
||
|
data-gapless={String(!!meta.gapless)}
|
||
|
onClick={() => {
|
||
|
if (menuCollapsed) return;
|
||
|
setMenuCollapsed(true);
|
||
|
}}
|
||
|
>
|
||
|
<Navbar
|
||
|
location={location}
|
||
|
navPrefix={<SearchBar />}
|
||
|
onMobileMenuClick={ev => {
|
||
|
setMenuCollapsed(val => !val);
|
||
|
ev.stopPropagation();
|
||
|
}}
|
||
|
/>
|
||
|
<SideMenu mobileMenuCollapsed={menuCollapsed} location={location} />
|
||
|
{showSlugs && <SlugList slugs={meta.slugs} className="__dumi-default-layout-toc" />}
|
||
|
{showHero && Hero(meta.hero)}
|
||
|
{showFeatures && Features(meta.features)}
|
||
|
<div className="__dumi-default-layout-content">
|
||
|
{children}
|
||
|
{!showHero && !showFeatures && meta.filePath && !meta.gapless && (
|
||
|
<div className="__dumi-default-layout-footer-meta">
|
||
|
{repoPlatform && (
|
||
|
<Link to={`${repoUrl}/edit/${branch}/${meta.filePath}`}>
|
||
|
{isCN ? `在 ${repoPlatform} 上编辑此页` : `Edit this doc on ${repoPlatform}`}
|
||
|
</Link>
|
||
|
)}
|
||
|
<span data-updated-text={isCN ? '最后更新时间:' : 'Last update: '}>{updatedTime}</span>
|
||
|
</div>
|
||
|
)}
|
||
|
{(showHero || showFeatures) && meta.footer && (
|
||
|
<div
|
||
|
className="__dumi-default-layout-footer"
|
||
|
dangerouslySetInnerHTML={{ __html: meta.footer }}
|
||
|
/>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default Layout;
|