mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 03:59:01 +08:00
chore(site): improve site (#39504)
* chore: merge context * chore: theme context * chore(site): improve site * chore: update components overview url * chore: update * chore: remove default theme button * chore: update * chore: update
This commit is contained in:
parent
1eaf125528
commit
36abb3df4e
@ -3,10 +3,10 @@ import { Button, Space, Typography } from 'antd';
|
||||
import { Link, useLocation } from 'dumi';
|
||||
import { css } from '@emotion/react';
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import SiteContext from '../../../theme/slots/SiteContext';
|
||||
import useSiteToken from '../../../hooks/useSiteToken';
|
||||
import { GroupMask } from './Group';
|
||||
import * as utils from '../../../theme/utils';
|
||||
import SiteContext from './SiteContext';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
|
@ -4,7 +4,7 @@ import type { SerializedStyles } from '@emotion/react';
|
||||
import { css } from '@emotion/react';
|
||||
import type { Extra, Icon } from './util';
|
||||
import useSiteToken from '../../../hooks/useSiteToken';
|
||||
import SiteContext from './SiteContext';
|
||||
import SiteContext from '../../../theme/slots/SiteContext';
|
||||
import { useCarouselStyle } from './util';
|
||||
|
||||
const useStyle = () => {
|
||||
@ -65,14 +65,20 @@ const RecommendItem = ({ extra, index, icons, itemCss }: RecommendItemProps) =>
|
||||
const icon = icons.find((i) => i.name === extra.source);
|
||||
|
||||
return (
|
||||
<a key={extra?.title} href={extra.href} target="_blank" css={[style.itemBase, itemCss]} rel="noreferrer">
|
||||
<a
|
||||
key={extra?.title}
|
||||
href={extra.href}
|
||||
target="_blank"
|
||||
css={[style.itemBase, itemCss]}
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Typography.Title level={5}>{extra?.title}</Typography.Title>
|
||||
<Typography.Paragraph type="secondary" style={{ flex: 'auto' }}>
|
||||
{extra.description}
|
||||
</Typography.Paragraph>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography.Text>{extra.date}</Typography.Text>
|
||||
{icon && <img src={icon.href} style={{ height: token.fontSize }} alt="banner"/>}
|
||||
{icon && <img src={icon.href} style={{ height: token.fontSize }} alt="banner" />}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
@ -119,4 +125,3 @@ export default function BannerRecommends({ extras = [], icons = [] }: BannerReco
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ import { CustomerServiceOutlined, QuestionCircleOutlined, SyncOutlined } from '@
|
||||
import { css } from '@emotion/react';
|
||||
import useSiteToken from '../../../hooks/useSiteToken';
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import SiteContext from './SiteContext';
|
||||
import SiteContext from '../../../theme/slots/SiteContext';
|
||||
import { useCarouselStyle } from './util';
|
||||
|
||||
const SAMPLE_CONTENT_EN =
|
||||
|
@ -5,7 +5,7 @@ import { Link, useLocation } from 'dumi';
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import useSiteToken from '../../../hooks/useSiteToken';
|
||||
import * as utils from '../../../theme/utils';
|
||||
import SiteContext from './SiteContext';
|
||||
import SiteContext from '../../../theme/slots/SiteContext';
|
||||
|
||||
const SECONDARY_LIST = [
|
||||
{
|
||||
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { Typography } from 'antd';
|
||||
import useSiteToken from '../../../hooks/useSiteToken';
|
||||
import SiteContext from './SiteContext';
|
||||
import SiteContext from '../../../theme/slots/SiteContext';
|
||||
|
||||
export interface GroupMaskProps {
|
||||
style?: React.CSSProperties;
|
||||
|
@ -1,11 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface SiteContextProps {
|
||||
isMobile: boolean;
|
||||
}
|
||||
|
||||
const SiteContext = React.createContext<SiteContextProps>({
|
||||
isMobile: false,
|
||||
});
|
||||
|
||||
export default SiteContext;
|
@ -30,7 +30,7 @@ import RadiusPicker from './RadiusPicker';
|
||||
import Group from '../Group';
|
||||
import BackgroundImage from './BackgroundImage';
|
||||
import { DEFAULT_COLOR, getAvatarURL, getClosetColor, PINK_COLOR } from './colorUtil';
|
||||
import SiteContext from '../SiteContext';
|
||||
import SiteContext from '../../../../theme/slots/SiteContext';
|
||||
import { useCarouselStyle } from '../util';
|
||||
import MobileCarousel from './MobileCarousel';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { useLocale as useDumiLocale } from 'dumi';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
@ -9,7 +9,6 @@ import Theme from './components/Theme';
|
||||
import BannerRecommends from './components/BannerRecommends';
|
||||
import ComponentsList from './components/ComponentsList';
|
||||
import DesignFramework from './components/DesignFramework';
|
||||
import SiteContext from './components/SiteContext';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
@ -34,61 +33,44 @@ const Homepage: React.FC = () => {
|
||||
const localeStr = localeId === 'zh-CN' ? 'cn' : 'en';
|
||||
|
||||
const [siteData] = useSiteData();
|
||||
const [isMobile, setIsMobile] = React.useState<boolean>(false);
|
||||
|
||||
const updateMobileMode = () => {
|
||||
setIsMobile(window.innerWidth < 768);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateMobileMode();
|
||||
window.addEventListener('resize', updateMobileMode);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateMobileMode);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const siteValue = useMemo(() => ({ isMobile }), [isMobile]);
|
||||
|
||||
return (
|
||||
<ConfigProvider theme={{ algorithm: undefined }}>
|
||||
<SiteContext.Provider value={siteValue}>
|
||||
<section>
|
||||
<Banner>
|
||||
<BannerRecommends extras={siteData?.extras?.[localeStr]} icons={siteData?.icons} />
|
||||
</Banner>
|
||||
<section>
|
||||
<Banner>
|
||||
<BannerRecommends extras={siteData?.extras?.[localeStr]} icons={siteData?.icons} />
|
||||
</Banner>
|
||||
|
||||
<div>
|
||||
<Theme />
|
||||
<Group
|
||||
background="#fff"
|
||||
collapse
|
||||
title={locale.assetsTitle}
|
||||
description={locale.assetsDesc}
|
||||
id="design"
|
||||
>
|
||||
<ComponentsList />
|
||||
</Group>
|
||||
<Group
|
||||
title={locale.designTitle}
|
||||
description={locale.designDesc}
|
||||
background="#F5F8FF"
|
||||
decoration={
|
||||
<>
|
||||
{/* Image Left Top */}
|
||||
<img
|
||||
style={{ position: 'absolute', left: 0, top: -50, height: 160 }}
|
||||
src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
|
||||
alt=""
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<DesignFramework />
|
||||
</Group>
|
||||
</div>
|
||||
</section>
|
||||
</SiteContext.Provider>
|
||||
<div>
|
||||
<Theme />
|
||||
<Group
|
||||
background="#fff"
|
||||
collapse
|
||||
title={locale.assetsTitle}
|
||||
description={locale.assetsDesc}
|
||||
id="design"
|
||||
>
|
||||
<ComponentsList />
|
||||
</Group>
|
||||
<Group
|
||||
title={locale.designTitle}
|
||||
description={locale.designDesc}
|
||||
background="#F5F8FF"
|
||||
decoration={
|
||||
<>
|
||||
{/* Image Left Top */}
|
||||
<img
|
||||
style={{ position: 'absolute', left: 0, top: -50, height: 160 }}
|
||||
src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
|
||||
alt=""
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<DesignFramework />
|
||||
</Group>
|
||||
</div>
|
||||
</section>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { memo, useMemo, useState } from 'react';
|
||||
import { Link, useIntl, useSidebarData } from 'dumi';
|
||||
import { Link, useIntl, useSidebarData, useLocation } from 'dumi';
|
||||
import { css } from '@emotion/react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { Card, Col, Divider, Input, Row, Space, Tag, Typography } from 'antd';
|
||||
@ -16,7 +16,6 @@ const useStyle = () => {
|
||||
padding: 0;
|
||||
`,
|
||||
componentsOverviewGroupTitle: css`
|
||||
font-size: 24px;
|
||||
margin-bottom: 24px !important;
|
||||
`,
|
||||
componentsOverviewTitle: css`
|
||||
@ -38,22 +37,7 @@ const useStyle = () => {
|
||||
}
|
||||
`,
|
||||
componentsOverviewSearch: css`
|
||||
&${token.antCls}-input-affix-wrapper {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
font-size: 20px;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
|
||||
input {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
color: #bbb;
|
||||
}
|
||||
}
|
||||
font-size: ${token.fontSizeXL}px;
|
||||
`,
|
||||
};
|
||||
};
|
||||
@ -81,7 +65,7 @@ const { Title } = Typography;
|
||||
const Overview: React.FC = () => {
|
||||
const style = useStyle();
|
||||
const data = useSidebarData();
|
||||
|
||||
const { search: urlSearch } = useLocation();
|
||||
const { locale, formatMessage } = useIntl();
|
||||
|
||||
const [search, setSearch] = useState<string>('');
|
||||
@ -131,7 +115,8 @@ const Overview: React.FC = () => {
|
||||
reportSearch(e.target.value);
|
||||
}}
|
||||
onKeyDown={onKeyDown}
|
||||
autoFocus // eslint-disable-line jsx-a11y/no-autofocus
|
||||
bordered={false}
|
||||
autoFocus
|
||||
suffix={<SearchOutlined />}
|
||||
/>
|
||||
<Divider />
|
||||
@ -154,10 +139,16 @@ const Overview: React.FC = () => {
|
||||
</Title>
|
||||
<Row gutter={[24, 24]}>
|
||||
{components.map((component) => {
|
||||
const url = `${component.link}/`;
|
||||
/** 是否是外链 */
|
||||
const isExternalLink = component.link.startsWith('http');
|
||||
let url = `${component.link}`;
|
||||
|
||||
if (!isExternalLink) {
|
||||
url += urlSearch;
|
||||
}
|
||||
|
||||
/** Link 不能跳转到外链 */
|
||||
const ComponentLink = !url.startsWith('http') ? Link : 'a';
|
||||
const ComponentLink = isExternalLink ? 'a' : Link;
|
||||
|
||||
return (
|
||||
<Col xs={24} sm={12} lg={8} xl={6} key={component?.title}>
|
||||
|
@ -3,7 +3,7 @@ import JsonML from 'jsonml.js/lib/utils';
|
||||
import toReactComponent from 'jsonml-to-react-element';
|
||||
import Prism from 'prismjs';
|
||||
import 'prismjs/components/prism-typescript';
|
||||
import { useLocation, useSearchParams, useIntl, type IPreviewerProps } from 'dumi';
|
||||
import { useLocation, useIntl, type IPreviewerProps } from 'dumi';
|
||||
import { ping } from '../../utils';
|
||||
|
||||
let pingDeferrer: PromiseLike<boolean>;
|
||||
@ -36,7 +36,6 @@ export default function fromDumiProps<P extends object>(
|
||||
const hoc = function DumiPropsAntdPreviewer(props: IPreviewerProps) {
|
||||
const showRiddleButton = useShowRiddleButton();
|
||||
const location = useLocation();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { asset, children, demoUrl, expand, description = '', ...meta } = props;
|
||||
const intl = useIntl();
|
||||
const entryCode = asset.dependencies['index.tsx'].value;
|
||||
@ -89,7 +88,6 @@ export default function fromDumiProps<P extends object>(
|
||||
expand,
|
||||
// FIXME: confirm is there has any case?
|
||||
highlightedStyle: '',
|
||||
theme: searchParams.get('theme'),
|
||||
} as P;
|
||||
|
||||
return <WrappedComponent {...transformedProps} />;
|
||||
|
@ -17,6 +17,8 @@ import CodeSandboxIcon from '../../common/CodeSandboxIcon';
|
||||
import RiddleIcon from '../../common/RiddleIcon';
|
||||
import ExternalLinkIcon from '../../common/ExternalLinkIcon';
|
||||
import fromDumiProps from './fromDumiProps';
|
||||
import type { SiteContextProps } from '../../slots/SiteContext';
|
||||
import SiteContext from '../../slots/SiteContext';
|
||||
import { version } from '../../../../package.json';
|
||||
|
||||
const { ErrorBoundary } = Alert;
|
||||
@ -38,7 +40,6 @@ interface DemoProps {
|
||||
highlightedStyle: string;
|
||||
expand: boolean;
|
||||
intl: any;
|
||||
theme: string;
|
||||
sourceCodes: Record<'jsx' | 'tsx', string>;
|
||||
location: Location;
|
||||
showRiddleButton: boolean;
|
||||
@ -53,6 +54,8 @@ interface DemoState {
|
||||
}
|
||||
|
||||
class Demo extends React.Component<DemoProps, DemoState> {
|
||||
static contextType = SiteContext;
|
||||
|
||||
liveDemo: any;
|
||||
|
||||
iframeRef = React.createRef<HTMLIFrameElement>();
|
||||
@ -81,13 +84,12 @@ class Demo extends React.Component<DemoProps, DemoState> {
|
||||
|
||||
shouldComponentUpdate(nextProps: DemoProps, nextState: DemoState) {
|
||||
const { codeExpand, copied, copyTooltipOpen, codeType } = this.state;
|
||||
const { expand, theme, showRiddleButton } = this.props;
|
||||
const { expand, showRiddleButton } = this.props;
|
||||
return (
|
||||
(codeExpand || expand) !== (nextState.codeExpand || nextProps.expand) ||
|
||||
copied !== nextState.copied ||
|
||||
copyTooltipOpen !== nextState.copyTooltipOpen ||
|
||||
codeType !== nextState.copyTooltipOpen ||
|
||||
nextProps.theme !== theme ||
|
||||
nextProps.showRiddleButton !== showRiddleButton
|
||||
);
|
||||
}
|
||||
@ -129,6 +131,7 @@ class Demo extends React.Component<DemoProps, DemoState> {
|
||||
render() {
|
||||
const { state } = this;
|
||||
const { props } = this;
|
||||
const site: SiteContextProps = this.context;
|
||||
const {
|
||||
meta,
|
||||
src,
|
||||
@ -139,7 +142,6 @@ class Demo extends React.Component<DemoProps, DemoState> {
|
||||
highlightedStyle,
|
||||
expand,
|
||||
intl: { locale },
|
||||
theme,
|
||||
showRiddleButton,
|
||||
} = props;
|
||||
const { copied, copyTooltipOpen, codeType } = state;
|
||||
@ -465,7 +467,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
|
||||
<img
|
||||
alt="expand code"
|
||||
src={
|
||||
theme === 'dark'
|
||||
site.theme.includes('dark')
|
||||
? 'https://gw.alipayobjects.com/zos/antfincdn/btT3qDZn1U/wSAkBuJFbdxsosKKpqyq.svg'
|
||||
: 'https://gw.alipayobjects.com/zos/antfincdn/Z5c7kzvi30/expand.svg'
|
||||
}
|
||||
@ -475,7 +477,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
|
||||
<img
|
||||
alt="expand code"
|
||||
src={
|
||||
theme === 'dark'
|
||||
site.theme.includes('dark')
|
||||
? 'https://gw.alipayobjects.com/zos/antfincdn/CjZPwcKUG3/OpROPHYqWmrMDBFMZtKF.svg'
|
||||
: 'https://gw.alipayobjects.com/zos/antfincdn/4zAaozCvUH/unexpand.svg'
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ export type ThemeSwitchProps = {
|
||||
onChange: (value: ThemeName[]) => void;
|
||||
};
|
||||
|
||||
const ThemeSwitch: React.FC<ThemeSwitchProps> = ({ value, onChange }) => {
|
||||
const ThemeSwitch: React.FC<ThemeSwitchProps> = (props: ThemeSwitchProps) => {
|
||||
const { value = ['light'], onChange } = props;
|
||||
const { token } = useSiteToken();
|
||||
const { pathname, search } = useLocation();
|
||||
return (
|
||||
|
@ -1,16 +1,15 @@
|
||||
import React, { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
||||
import React, { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import dayjs from 'dayjs';
|
||||
import { Helmet, useOutlet, useSearchParams } from 'dumi';
|
||||
import { Helmet, useOutlet } from 'dumi';
|
||||
import '../../static/style';
|
||||
import type { DirectionType } from 'antd/es/config-provider';
|
||||
import ConfigProvider from 'antd/es/config-provider';
|
||||
import classNames from 'classnames';
|
||||
import zhCN from 'antd/es/locale/zh_CN';
|
||||
import SiteContext from '../../slots/SiteContext';
|
||||
import Header from '../../slots/Header';
|
||||
import Footer from '../../slots/Footer';
|
||||
import useLocale from '../../../hooks/useLocale';
|
||||
import SiteContext from '../../slots/SiteContext';
|
||||
import useLocation from '../../../hooks/useLocation';
|
||||
import ResourceLayout from '../ResourceLayout';
|
||||
import GlobalStyles from '../../common/GlobalStyles';
|
||||
@ -28,26 +27,13 @@ const locales = {
|
||||
},
|
||||
};
|
||||
|
||||
const RESPONSIVE_MOBILE = 768;
|
||||
|
||||
const DocLayout: React.FC = () => {
|
||||
const outlet = useOutlet();
|
||||
const location = useLocation();
|
||||
const { pathname, search } = location;
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [locale, lang] = useLocale(locales);
|
||||
|
||||
// TODO: place doc layout here, apply for all docs route paths
|
||||
// migrate from: https://github.com/ant-design/ant-design/blob/eb9179464b9c4a93c856e1e70ddbdbaaf3f3371f/site/theme/template/Layout/index.tsx
|
||||
|
||||
const [isMobile, setIsMobile] = React.useState<boolean>(false);
|
||||
const [direction, setDirection] = React.useState<DirectionType>('ltr');
|
||||
|
||||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const updateMobileMode = () => {
|
||||
setIsMobile(window.innerWidth < RESPONSIVE_MOBILE);
|
||||
};
|
||||
const { direction } = useContext(SiteContext);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (lang === 'cn') {
|
||||
@ -64,20 +50,6 @@ const DocLayout: React.FC = () => {
|
||||
nprogressHiddenStyle.parentNode?.removeChild(nprogressHiddenStyle);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Handle direction
|
||||
const queryDirection = searchParams.get('direction');
|
||||
setDirection(queryDirection === 'rtl' ? 'rtl' : 'ltr');
|
||||
|
||||
// Handle mobile mode
|
||||
updateMobileMode();
|
||||
window.addEventListener('resize', updateMobileMode);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateMobileMode);
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -89,16 +61,6 @@ const DocLayout: React.FC = () => {
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const changeDirection = (dir: DirectionType): void => {
|
||||
setDirection(dir);
|
||||
if (dir === 'ltr') {
|
||||
searchParams.delete('direction');
|
||||
} else {
|
||||
searchParams.set('direction', 'rtl');
|
||||
}
|
||||
setSearchParams(searchParams);
|
||||
};
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (
|
||||
['', '/'].some((path) => path === pathname) ||
|
||||
@ -120,16 +82,8 @@ const DocLayout: React.FC = () => {
|
||||
return <SidebarLayout>{outlet}</SidebarLayout>;
|
||||
}, [pathname, outlet]);
|
||||
|
||||
const siteContextValue = useMemo(
|
||||
() => ({
|
||||
isMobile,
|
||||
direction,
|
||||
}),
|
||||
[isMobile, direction],
|
||||
);
|
||||
|
||||
return (
|
||||
<SiteContext.Provider value={siteContextValue}>
|
||||
<>
|
||||
<Helmet encodeSpecialCharacters={false}>
|
||||
<html
|
||||
lang={lang}
|
||||
@ -149,12 +103,12 @@ const DocLayout: React.FC = () => {
|
||||
content="https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png"
|
||||
/>
|
||||
</Helmet>
|
||||
<ConfigProvider locale={lang === 'cn' ? zhCN : undefined} direction={direction}>
|
||||
<ConfigProvider locale={lang === 'cn' ? zhCN : undefined}>
|
||||
<GlobalStyles />
|
||||
<Header changeDirection={changeDirection} />
|
||||
<Header />
|
||||
{content}
|
||||
</ConfigProvider>
|
||||
</SiteContext.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,18 +1,26 @@
|
||||
import React from 'react';
|
||||
import { useOutlet, useSearchParams } from 'dumi';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { createSearchParams, useOutlet, useSearchParams } from 'dumi';
|
||||
import { ConfigProvider, theme as antdTheme } from 'antd';
|
||||
import { createCache, StyleProvider } from '@ant-design/cssinjs';
|
||||
import type { DirectionType } from 'antd/es/config-provider';
|
||||
import ThemeSwitch from '../common/ThemeSwitch';
|
||||
import type { ThemeName } from '../common/ThemeSwitch';
|
||||
import useLocation from '../../hooks/useLocation';
|
||||
import type { SiteContextProps } from '../slots/SiteContext';
|
||||
import SiteContext from '../slots/SiteContext';
|
||||
|
||||
type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][];
|
||||
type SiteState = Partial<Omit<SiteContextProps, 'updateSiteContext'>>;
|
||||
|
||||
const RESPONSIVE_MOBILE = 768;
|
||||
|
||||
const styleCache = createCache();
|
||||
if (typeof global !== 'undefined') {
|
||||
(global as any).styleCache = styleCache;
|
||||
}
|
||||
|
||||
const getAlgorithm = (themes: ThemeName[]) =>
|
||||
(themes || []).map((theme) => {
|
||||
const getAlgorithm = (themes: ThemeName[] = []) =>
|
||||
themes.map((theme) => {
|
||||
if (theme === 'dark') {
|
||||
return antdTheme.darkAlgorithm;
|
||||
}
|
||||
@ -26,26 +34,87 @@ const GlobalLayout: React.FC = () => {
|
||||
const outlet = useOutlet();
|
||||
const { pathname } = useLocation();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const theme = searchParams.getAll('theme') as unknown as ThemeName[];
|
||||
const handleThemeChange = (value: ThemeName[]) => {
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
theme: value,
|
||||
});
|
||||
const [{ theme, direction, isMobile }, setSiteState] = React.useState<SiteState>({
|
||||
isMobile: false,
|
||||
direction: 'ltr',
|
||||
theme: ['light'],
|
||||
});
|
||||
|
||||
const updateSiteConfig = useCallback(
|
||||
(props: SiteState) => {
|
||||
setSiteState((prev) => ({ ...prev, ...props }));
|
||||
|
||||
// updating `searchParams` will clear the hash
|
||||
const oldSearchStr = searchParams.toString();
|
||||
|
||||
let nextSearchParams: URLSearchParams = searchParams;
|
||||
(Object.entries(props) as Entries<SiteContextProps>).forEach(([key, value]) => {
|
||||
if (key === 'direction') {
|
||||
if (value === 'rtl') {
|
||||
nextSearchParams.set('direction', 'rtl');
|
||||
} else {
|
||||
nextSearchParams.delete('direction');
|
||||
}
|
||||
}
|
||||
if (key === 'theme') {
|
||||
nextSearchParams = createSearchParams({
|
||||
...nextSearchParams,
|
||||
theme: value.filter((t) => t !== 'light'),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (nextSearchParams.toString() !== oldSearchStr) {
|
||||
setSearchParams(nextSearchParams);
|
||||
}
|
||||
},
|
||||
[searchParams, setSearchParams],
|
||||
);
|
||||
|
||||
const updateMobileMode = () => {
|
||||
updateSiteConfig({ isMobile: window.innerWidth < RESPONSIVE_MOBILE });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const _theme = searchParams.getAll('theme') as ThemeName[];
|
||||
const _direction = searchParams.get('direction') as DirectionType;
|
||||
setSiteState({ theme: _theme, direction: _direction === 'rtl' ? 'rtl' : 'ltr' });
|
||||
|
||||
// Handle isMobile
|
||||
updateMobileMode();
|
||||
window.addEventListener('resize', updateMobileMode);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateMobileMode);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const siteContextValue = useMemo(
|
||||
() => ({
|
||||
direction,
|
||||
updateSiteConfig,
|
||||
theme: theme!,
|
||||
isMobile: isMobile!,
|
||||
}),
|
||||
[isMobile, direction, updateSiteConfig, theme],
|
||||
);
|
||||
|
||||
return (
|
||||
<StyleProvider cache={styleCache}>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
algorithm: getAlgorithm(theme),
|
||||
}}
|
||||
>
|
||||
{outlet}
|
||||
{!pathname.startsWith('/~demos') && (
|
||||
<ThemeSwitch value={theme} onChange={handleThemeChange} />
|
||||
)}
|
||||
</ConfigProvider>
|
||||
<SiteContext.Provider value={siteContextValue}>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
algorithm: getAlgorithm(theme),
|
||||
}}
|
||||
>
|
||||
{outlet}
|
||||
{!pathname.startsWith('/~demos') && (
|
||||
<ThemeSwitch
|
||||
value={theme}
|
||||
onChange={(nextTheme) => updateSiteConfig({ theme: nextTheme })}
|
||||
/>
|
||||
)}
|
||||
</ConfigProvider>
|
||||
</SiteContext.Provider>
|
||||
</StyleProvider>
|
||||
);
|
||||
};
|
||||
|
@ -83,7 +83,7 @@ export default function LangBtn({
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
background: token.colorText,
|
||||
color: token.colorTextLightSolid,
|
||||
color: token.colorBgContainer,
|
||||
transformOrigin: '0 0',
|
||||
transform: `scale(0.7)`,
|
||||
};
|
||||
|
@ -4,7 +4,6 @@ import DumiSearchBar from 'dumi/theme-default/slots/SearchBar';
|
||||
import classNames from 'classnames';
|
||||
import { Col, Modal, Popover, Row, Select, Typography } from 'antd';
|
||||
import { GithubOutlined, MenuOutlined } from '@ant-design/icons';
|
||||
import type { DirectionType } from 'antd/es/config-provider';
|
||||
import { ClassNames, css } from '@emotion/react';
|
||||
import * as utils from '../../utils';
|
||||
import { getThemeConfig, ping } from '../../utils';
|
||||
@ -129,10 +128,6 @@ const useStyle = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export interface HeaderProps {
|
||||
changeDirection: (direction: DirectionType) => void;
|
||||
}
|
||||
|
||||
const V5_NOTIFICATION = 'antd@4.0.0-notification-sent';
|
||||
const SHOULD_OPEN_ANT_DESIGN_MIRROR_MODAL = 'ANT_DESIGN_DO_NOT_OPEN_MIRROR_MODAL';
|
||||
|
||||
@ -151,8 +146,7 @@ interface HeaderState {
|
||||
}
|
||||
|
||||
// ================================= Header =================================
|
||||
const Header: React.FC<HeaderProps> = (props) => {
|
||||
const { changeDirection } = props;
|
||||
const Header: React.FC = () => {
|
||||
const [isClient, setIsClient] = React.useState(false);
|
||||
const [locale, lang] = useLocale(locales);
|
||||
const { token } = useSiteToken();
|
||||
@ -188,7 +182,7 @@ const Header: React.FC<HeaderProps> = (props) => {
|
||||
windowWidth: 1400,
|
||||
searching: false,
|
||||
});
|
||||
const { direction, isMobile } = useContext<SiteContextProps>(SiteContext);
|
||||
const { direction, isMobile, updateSiteConfig } = useContext<SiteContextProps>(SiteContext);
|
||||
const pingTimer = useRef<NodeJS.Timeout | null>(null);
|
||||
const location = useLocation();
|
||||
const { pathname, search } = location;
|
||||
@ -208,7 +202,7 @@ const Header: React.FC<HeaderProps> = (props) => {
|
||||
setHeaderState((prev) => ({ ...prev, menuVisible: visible }));
|
||||
}, []);
|
||||
const onDirectionChange = () => {
|
||||
changeDirection?.(direction !== 'rtl' ? 'rtl' : 'ltr');
|
||||
updateSiteConfig({ direction: direction !== 'rtl' ? 'rtl' : 'ltr' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -126,10 +126,11 @@ const useStyle = () => {
|
||||
|
||||
const Sidebar: React.FC = () => {
|
||||
const sidebarData = useSidebarData();
|
||||
const { isMobile } = useContext(SiteContext);
|
||||
const { isMobile, theme } = useContext(SiteContext);
|
||||
const styles = useStyle();
|
||||
|
||||
const [menuItems, selectedKey] = useMenu();
|
||||
const isDark = theme.includes('dark');
|
||||
|
||||
const menuChild = (
|
||||
<Menu
|
||||
@ -137,6 +138,7 @@ const Sidebar: React.FC = () => {
|
||||
inlineIndent={30}
|
||||
css={styles.asideContainer}
|
||||
mode="inline"
|
||||
theme={isDark ? 'dark' : 'light'}
|
||||
selectedKeys={[selectedKey]}
|
||||
defaultOpenKeys={sidebarData?.map(({ title }) => title).filter((item) => item) as string[]}
|
||||
/>
|
||||
|
@ -1,14 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import type { DirectionType } from 'antd/es/config-provider';
|
||||
import type { ThemeName } from '../common/ThemeSwitch';
|
||||
|
||||
export interface SiteContextProps {
|
||||
isMobile: boolean;
|
||||
direction: DirectionType;
|
||||
theme: ThemeName[];
|
||||
updateSiteConfig: (props: Partial<SiteContextProps>) => void;
|
||||
}
|
||||
|
||||
const SiteContext = React.createContext<SiteContextProps>({
|
||||
isMobile: false,
|
||||
direction: 'ltr',
|
||||
theme: ['light'],
|
||||
updateSiteConfig: () => {},
|
||||
});
|
||||
|
||||
export default SiteContext;
|
||||
|
Loading…
Reference in New Issue
Block a user