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