mirror of
https://github.com/ant-design/pro-components.git
synced 2024-10-23 09:23:51 +08:00
chore: support dumi@2 (#6528)
* chore: support dumi@3 * support dumi@3 * support dumi@3 * support dumi@3 * support dumi@3 * support dumi@3 * support dumi@3 * 删掉多余的title * 优化文件上传 * 优化文件上传 * 优化文档 * remove src * remove src * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * use pnpm * use pnpm * use pnpm * use pnpm * 优化样式 * 修复类型问题 * 修复类型问题 * 修复类型问题 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 优化文档 * 修复类型问题 * remove all tsconfig * 优化文档 * 优化文档 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑 * 优化官网逻辑
This commit is contained in:
parent
965ce241b1
commit
23e4430969
@ -3,4 +3,4 @@ coverage:
|
||||
project:
|
||||
default:
|
||||
# Fail the status if coverage drops by >= 0.1%
|
||||
threshold: 0.1
|
||||
threshold: 1%
|
||||
|
@ -1,40 +0,0 @@
|
||||
// @ts-ignore
|
||||
import PreView, { IPreviewerProps } from 'dumi-theme-default/src/builtins/Previewer';
|
||||
import LazyLoad from 'react-lazyload';
|
||||
export default ({
|
||||
children,
|
||||
...rest
|
||||
}: IPreviewerProps & {
|
||||
height: string;
|
||||
iframe: string;
|
||||
}) => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return (
|
||||
<PreView {...rest}>
|
||||
<div
|
||||
style={{
|
||||
contentVisibility: 'auto',
|
||||
contain: 'style layout paint',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</PreView>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PreView {...rest}>
|
||||
<LazyLoad once offset={500}>
|
||||
<div
|
||||
style={{
|
||||
contentVisibility: 'auto',
|
||||
contain: 'style layout paint',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</LazyLoad>
|
||||
</PreView>
|
||||
);
|
||||
};
|
12
.dumi/theme/builtins/Previewer/index.tsx
Normal file
12
.dumi/theme/builtins/Previewer/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
//@ts-ignore
|
||||
import Previewer from 'dumi/theme-original/builtins/Previewer';
|
||||
|
||||
import DemoProvider from '../../components/DemoProvider';
|
||||
|
||||
const Page: React.FC = (props) => (
|
||||
<DemoProvider>
|
||||
<Previewer {...props} />
|
||||
</DemoProvider>
|
||||
);
|
||||
|
||||
export default Page;
|
80
.dumi/theme/builtins/SourceCode/index.tsx
Normal file
80
.dumi/theme/builtins/SourceCode/index.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
||||
import { Button, ConfigProvider, Tooltip } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { FC } from 'react';
|
||||
import Highlighter, { HighlighterProps } from '../../components/Highlighter';
|
||||
import { useCopied } from '../../hooks/useCopied';
|
||||
|
||||
const useStyles = createStyles(({ token, css, cx }) => {
|
||||
const prefixCls = 'source-code';
|
||||
const buttonHoverCls = `${prefixCls}-hover-btn`;
|
||||
|
||||
return {
|
||||
container: cx(
|
||||
prefixCls,
|
||||
css`
|
||||
position: relative;
|
||||
|
||||
pre {
|
||||
background: ${token.colorFillTertiary} !important;
|
||||
border-radius: 8px;
|
||||
padding: 12px !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.${buttonHoverCls} {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`,
|
||||
),
|
||||
button: cx(
|
||||
buttonHoverCls,
|
||||
css`
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 8px;
|
||||
`,
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
const SourceCode: FC<HighlighterProps> = ({ children, language }) => {
|
||||
const { copied, setCopied } = useCopied();
|
||||
const { styles, theme } = useStyles();
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Tooltip
|
||||
placement={'left'}
|
||||
showArrow={false}
|
||||
align={{ offset: [5, 0] }}
|
||||
title={
|
||||
copied ? (
|
||||
<>
|
||||
<CheckOutlined style={{ color: theme.colorSuccess }} /> 复制成功
|
||||
</>
|
||||
) : (
|
||||
'复制'
|
||||
)
|
||||
}
|
||||
>
|
||||
<ConfigProvider theme={{ token: { colorBgContainer: theme.colorBgElevated } }}>
|
||||
<Button
|
||||
icon={<CopyOutlined />}
|
||||
className={styles.button}
|
||||
onClick={() => {
|
||||
copy(children);
|
||||
setCopied();
|
||||
}}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
</Tooltip>
|
||||
<Highlighter language={language}>{children}</Highlighter>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SourceCode;
|
15
.dumi/theme/components/ApiHeader/NpmFilled.tsx
Normal file
15
.dumi/theme/components/ApiHeader/NpmFilled.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
export default () => (
|
||||
<svg
|
||||
width="14px"
|
||||
height="14px"
|
||||
viewBox="0 0 14 14"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<path
|
||||
d="M13,0 C13.5522847,-1.01453063e-16 14,0.44771525 14,1 L14,13 C14,13.5522847 13.5522847,14 13,14 L1,14 C0.44771525,14 -4.87476137e-16,13.5522847 0,13 L0,1 C-6.76353751e-17,0.44771525 0.44771525,-4.5365845e-16 1,0 L13,0 Z M11.375,2.625 L2.625,2.625 L2.625,11.375 L7,11.375 L7,4.375 L9.625,4.375 L9.625,11.375 L11.375,11.375 L11.375,2.625 Z"
|
||||
fill="#C12127"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
80
.dumi/theme/components/ApiHeader/index.tsx
Normal file
80
.dumi/theme/components/ApiHeader/index.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import { EditOutlined, GithubFilled } from '@ant-design/icons';
|
||||
import { Typography } from 'antd';
|
||||
import { styled } from 'antd-style';
|
||||
import { FC, memo } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import Code from '../CodeSnippet';
|
||||
import NpmFilled from './NpmFilled';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Label = styled(Typography.Text)`
|
||||
width: 100px;
|
||||
`;
|
||||
|
||||
interface ApiTitleProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
const REPO_BASE = `https://github.com/arvinxx/antd-style`;
|
||||
|
||||
export const ApiHeader: FC<ApiTitleProps> = memo(({ title, description }) => {
|
||||
const { styles, theme } = useStyles();
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: '引入方法',
|
||||
import: true,
|
||||
children: `import { ${title} } from "antd-style";`,
|
||||
},
|
||||
{
|
||||
label: '源码',
|
||||
icon: <GithubFilled />,
|
||||
children: '查看源码',
|
||||
url: `${REPO_BASE}/tree/master/src/${title}`,
|
||||
},
|
||||
{
|
||||
label: '文档',
|
||||
icon: <EditOutlined />,
|
||||
children: '编辑文档',
|
||||
url: `${REPO_BASE}/tree/master/docs/api/${title}`,
|
||||
},
|
||||
{
|
||||
label: '产物',
|
||||
icon: <NpmFilled />,
|
||||
children: 'antd-style',
|
||||
url: 'https://www.npmjs.com/package/antd-style?activeTab=explore',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Flexbox>
|
||||
<Typography.Title className={styles.title}>{title}</Typography.Title>
|
||||
{description && (
|
||||
<div>
|
||||
<Typography.Text type={'secondary'} className={styles.desc}>
|
||||
{description}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
)}
|
||||
<Flexbox style={{ marginTop: 24 }} gap={12}>
|
||||
{items.map((item) => (
|
||||
<Flexbox horizontal key={item.label}>
|
||||
<Label type={'secondary'}>{item.label}</Label>
|
||||
{item.import ? (
|
||||
<Code>{item.children}</Code>
|
||||
) : (
|
||||
<a href={item.url} target={'_blank'}>
|
||||
<Flexbox horizontal align={'center'} gap={8} className={styles.text}>
|
||||
<>{item.icon}</>
|
||||
<>{item.children}</>
|
||||
</Flexbox>
|
||||
</a>
|
||||
)}
|
||||
</Flexbox>
|
||||
))}
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
14
.dumi/theme/components/ApiHeader/style.ts
Normal file
14
.dumi/theme/components/ApiHeader/style.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, token, stylish }) => ({
|
||||
title: css`
|
||||
font-family: monospace;
|
||||
`,
|
||||
desc: css`
|
||||
font-size: ${token.fontSizeLG}px;
|
||||
line-height: ${token.lineHeightLG}px;
|
||||
`,
|
||||
text: css`
|
||||
${stylish.clickableText}
|
||||
`,
|
||||
}));
|
77
.dumi/theme/components/Burger/index.tsx
Normal file
77
.dumi/theme/components/Burger/index.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { Link } from '@@/exports';
|
||||
import { Drawer, Menu } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { uniq } from 'lodash';
|
||||
import { useState } from 'react';
|
||||
import { Center } from 'react-layout-kit';
|
||||
import { activePathSel, useSiteStore } from '../../store/useSiteStore';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Burger = () => {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { styles, cx } = useStyles();
|
||||
|
||||
const nav = useSiteStore((s) => s.navData, isEqual);
|
||||
const sidebar = useSiteStore((s) => s.sidebar, isEqual);
|
||||
const activePath = useSiteStore(activePathSel);
|
||||
const pathname = useSiteStore((s) => s.location.pathname);
|
||||
|
||||
return (
|
||||
<Center
|
||||
className={styles.container}
|
||||
onClick={() => {
|
||||
setOpened(!opened);
|
||||
}}
|
||||
>
|
||||
<div className={cx(styles.icon, opened ? styles.active : '')} />
|
||||
|
||||
<Drawer
|
||||
open={opened}
|
||||
placement={'left'}
|
||||
closeIcon={null}
|
||||
rootClassName={styles.drawerRoot}
|
||||
className={styles.drawer}
|
||||
width={'100vw'}
|
||||
headerStyle={{ display: 'none' }}
|
||||
bodyStyle={{ padding: '24px 0' }}
|
||||
>
|
||||
<Menu
|
||||
mode={'inline'}
|
||||
selectedKeys={uniq([activePath, `s-${pathname}`])}
|
||||
openKeys={[activePath]}
|
||||
className={styles.menu}
|
||||
items={nav.map((item) => ({
|
||||
label: <Link to={item.link}>{item.title}</Link>,
|
||||
key: item.activePath!,
|
||||
children:
|
||||
item.activePath === activePath &&
|
||||
sidebar?.map((group) => {
|
||||
return (
|
||||
!group.link && {
|
||||
label: group.title,
|
||||
|
||||
type: 'group',
|
||||
children: group.children.map((item) => ({
|
||||
label: (
|
||||
<Link
|
||||
to={item.link}
|
||||
onClick={() => {
|
||||
setOpened(false);
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
),
|
||||
key: `s-${item.link}`,
|
||||
})),
|
||||
}
|
||||
);
|
||||
}),
|
||||
}))}
|
||||
></Menu>
|
||||
</Drawer>
|
||||
</Center>
|
||||
);
|
||||
};
|
||||
|
||||
export default Burger;
|
102
.dumi/theme/components/Burger/style.ts
Normal file
102
.dumi/theme/components/Burger/style.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import chroma from 'chroma-js';
|
||||
|
||||
export const useStyles = createStyles(({ token, prefixCls, cx, css, stylish }) => {
|
||||
const offset = 6;
|
||||
|
||||
return {
|
||||
icon: cx(
|
||||
'site-burger-icon',
|
||||
css`
|
||||
position: relative;
|
||||
|
||||
&,
|
||||
&::before,
|
||||
&::after {
|
||||
display: inline-block;
|
||||
height: 2px;
|
||||
background: ${token.colorTextSecondary};
|
||||
|
||||
width: 16px;
|
||||
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::before {
|
||||
top: ${offset}px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: -${offset}px;
|
||||
}
|
||||
`,
|
||||
),
|
||||
active: css`
|
||||
&::before,
|
||||
&::after {
|
||||
background: ${token.colorText};
|
||||
}
|
||||
& {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::before {
|
||||
top: 0;
|
||||
transform: rotate(-135deg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: 0;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
`,
|
||||
container: css`
|
||||
width: ${token.controlHeight}px;
|
||||
height: ${token.controlHeight}px;
|
||||
border-radius: ${token.borderRadius}px;
|
||||
cursor: pointer;
|
||||
`,
|
||||
|
||||
drawerRoot: css`
|
||||
top: ${token.headerHeight + 1}px;
|
||||
|
||||
:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.${prefixCls}-drawer {
|
||||
&-mask {
|
||||
background: transparent;
|
||||
backdrop-filter: blur(7px);
|
||||
background: ${chroma(token.colorBgBase).alpha(0.5).hex()};
|
||||
}
|
||||
|
||||
&-content-wrapper {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
drawer: css`
|
||||
&.${prefixCls}-drawer-content {
|
||||
background: transparent;
|
||||
}
|
||||
`,
|
||||
|
||||
menu: css`
|
||||
background: transparent;
|
||||
border-inline-end: transparent !important;
|
||||
|
||||
.${prefixCls}-menu-sub.${prefixCls}-menu-inline {
|
||||
background: ${chroma(token.colorBgLayout).alpha(0.8).hex()} !important;
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
41
.dumi/theme/components/CodeSnippet/index.tsx
Normal file
41
.dumi/theme/components/CodeSnippet/index.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { CheckOutlined } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { FC } from 'react';
|
||||
|
||||
import Highlighter from '../Highlighter';
|
||||
|
||||
import { useCopied } from '../../hooks/useCopied';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const CodeSnippet: FC<{ children: string }> = ({ children }) => {
|
||||
const { styles, theme } = useStyles();
|
||||
const { copied, setCopied } = useCopied();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
placement={'right'}
|
||||
title={
|
||||
copied ? (
|
||||
<>
|
||||
<CheckOutlined style={{ color: theme.colorSuccess }} /> 复制成功
|
||||
</>
|
||||
) : (
|
||||
'复制'
|
||||
)
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={styles}
|
||||
onClick={() => {
|
||||
copy(children);
|
||||
setCopied();
|
||||
}}
|
||||
>
|
||||
<Highlighter language={'javaScript'}>{children}</Highlighter>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeSnippet;
|
20
.dumi/theme/components/CodeSnippet/style.ts
Normal file
20
.dumi/theme/components/CodeSnippet/style.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
({ css, token }) =>
|
||||
css`
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: ${token.colorFillSecondary};
|
||||
border-radius: 4px;
|
||||
}
|
||||
pre {
|
||||
background: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0;
|
||||
}
|
||||
code[class*='language-'] {
|
||||
background: none;
|
||||
}
|
||||
`,
|
||||
);
|
8
.dumi/theme/components/DemoProvider/index.tsx
Normal file
8
.dumi/theme/components/DemoProvider/index.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import { StyleProvider } from 'antd-style';
|
||||
|
||||
const Page: React.FC<{
|
||||
children: React.ReactNode;
|
||||
}> = ({ children }) => {
|
||||
return <StyleProvider prefix={'demo'}>{children}</StyleProvider>;
|
||||
};
|
||||
export default Page;
|
19
.dumi/theme/components/GithubButton/index.tsx
Normal file
19
.dumi/theme/components/GithubButton/index.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { GithubFilled } from '@ant-design/icons';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { useSiteStore } from '../../store/useSiteStore';
|
||||
|
||||
const GithubButton = () => {
|
||||
const repoUrl = useSiteStore((s) => s.siteData.themeConfig?.repoUrl);
|
||||
|
||||
return (
|
||||
repoUrl && (
|
||||
<Tooltip showArrow={false} title={'Github'}>
|
||||
<a href={repoUrl} target={'_blank'}>
|
||||
<Button icon={<GithubFilled />} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default GithubButton;
|
40
.dumi/theme/components/Highlighter/index.tsx
Normal file
40
.dumi/theme/components/Highlighter/index.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { useThemeMode } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
//@ts-ignore
|
||||
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
//@ts-ignore
|
||||
import { atomOneDark, githubGist } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
//@ts-ignore
|
||||
import javascript from 'react-syntax-highlighter/dist/cjs/languages/hljs/javascript';
|
||||
//@ts-ignore
|
||||
import json from 'react-syntax-highlighter/dist/cjs/languages/hljs/json';
|
||||
//@ts-ignore
|
||||
import less from 'react-syntax-highlighter/dist/cjs/languages/hljs/less';
|
||||
//@ts-ignore
|
||||
import markdown from 'react-syntax-highlighter/dist/cjs/languages/hljs/markdown';
|
||||
//@ts-ignore
|
||||
import typescript from 'react-syntax-highlighter/dist/cjs/languages/hljs/typescript';
|
||||
|
||||
SyntaxHighlighter.registerLanguage('javascript', javascript);
|
||||
SyntaxHighlighter.registerLanguage('jsx', javascript);
|
||||
SyntaxHighlighter.registerLanguage('json', json);
|
||||
SyntaxHighlighter.registerLanguage('markdown', markdown);
|
||||
SyntaxHighlighter.registerLanguage('less', less);
|
||||
SyntaxHighlighter.registerLanguage('typescript', typescript);
|
||||
SyntaxHighlighter.registerLanguage('tsx', typescript);
|
||||
|
||||
export interface HighlighterProps {
|
||||
children: string;
|
||||
language: string;
|
||||
}
|
||||
const Highlighter = memo<HighlighterProps>(({ children, language }) => {
|
||||
const { isDarkMode } = useThemeMode();
|
||||
|
||||
return (
|
||||
<SyntaxHighlighter language={language} style={isDarkMode ? atomOneDark : githubGist}>
|
||||
{children}
|
||||
</SyntaxHighlighter>
|
||||
);
|
||||
});
|
||||
|
||||
export default Highlighter;
|
39
.dumi/theme/components/NativeSelect/SelectItem/index.tsx
Normal file
39
.dumi/theme/components/NativeSelect/SelectItem/index.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { FC, ForwardedRef, forwardRef } from 'react';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
interface SelectItemProps {
|
||||
value: any;
|
||||
label: any;
|
||||
prefixCls?: string;
|
||||
isSelected?: boolean;
|
||||
isActive?: boolean;
|
||||
ref?: ForwardedRef<HTMLButtonElement>;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const SelectItem: FC<SelectItemProps> = forwardRef(
|
||||
({ value, label, prefixCls, isSelected, isActive, disabled, ...props }, ref) => {
|
||||
const { styles, cx } = useStyles({ prefixCls, selected: isSelected });
|
||||
|
||||
return (
|
||||
<button
|
||||
key={value}
|
||||
disabled={disabled}
|
||||
aria-selected={isSelected}
|
||||
role="option"
|
||||
tabIndex={-1}
|
||||
className={cx(styles.item, {
|
||||
[styles.selected]: isSelected,
|
||||
[styles.active]: isActive,
|
||||
})}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default SelectItem;
|
45
.dumi/theme/components/NativeSelect/SelectItem/style.ts
Normal file
45
.dumi/theme/components/NativeSelect/SelectItem/style.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, cx, token }, prefixCls) => ({
|
||||
item: cx(
|
||||
`${prefixCls}-item`,
|
||||
|
||||
css`
|
||||
display: block;
|
||||
all: unset;
|
||||
width: 100%;
|
||||
padding: 12px 10px;
|
||||
border-radius: 5px;
|
||||
box-sizing: inherit;
|
||||
user-select: none;
|
||||
line-height: 1;
|
||||
scroll-margin: 50px;
|
||||
|
||||
font-weight: normal;
|
||||
font-family: ${token.fontFamily};
|
||||
color: ${token.colorText};
|
||||
background: transparent;
|
||||
&:hover {
|
||||
background: ${token.colorFillTertiary};
|
||||
}
|
||||
`,
|
||||
),
|
||||
selected: cx(
|
||||
`${prefixCls}-item-selected`,
|
||||
css`
|
||||
color: ${token.colorPrimaryText};
|
||||
background: ${token.colorPrimaryBg};
|
||||
font-weight: bold;
|
||||
&:hover {
|
||||
color: ${token.colorPrimaryTextHover};
|
||||
background: ${token.colorPrimaryBgHover};
|
||||
}
|
||||
`,
|
||||
),
|
||||
active: cx(
|
||||
`${prefixCls}-item-active`,
|
||||
css`
|
||||
background: ${token.colorFillTertiary};
|
||||
`,
|
||||
),
|
||||
}));
|
276
.dumi/theme/components/NativeSelect/index.tsx
Normal file
276
.dumi/theme/components/NativeSelect/index.tsx
Normal file
@ -0,0 +1,276 @@
|
||||
import {
|
||||
autoUpdate,
|
||||
flip,
|
||||
FloatingFocusManager,
|
||||
FloatingOverlay,
|
||||
FloatingPortal,
|
||||
inner,
|
||||
offset,
|
||||
shift,
|
||||
SideObject,
|
||||
size,
|
||||
useClick,
|
||||
useDismiss,
|
||||
useFloating,
|
||||
useInnerOffset,
|
||||
useInteractions,
|
||||
useListNavigation,
|
||||
useRole,
|
||||
useTypeahead,
|
||||
} from '@floating-ui/react';
|
||||
import { FC, ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
import useControlledState from 'use-merge-value';
|
||||
|
||||
import SelectItem from './SelectItem';
|
||||
import { useStyles } from './style';
|
||||
|
||||
interface OptionType {
|
||||
label: ReactNode;
|
||||
icon?: ReactNode;
|
||||
value: string | number | null;
|
||||
}
|
||||
export interface NativeSelectProps {
|
||||
value?: number;
|
||||
options?: OptionType[];
|
||||
prefixCls?: string;
|
||||
onChange?: (index: number) => void;
|
||||
renderValue?: (index: number) => ReactNode;
|
||||
renderItem: (item: OptionType, index: number) => ReactNode;
|
||||
}
|
||||
|
||||
const NativeSelect: FC<NativeSelectProps> = ({
|
||||
options = [],
|
||||
value,
|
||||
prefixCls,
|
||||
onChange,
|
||||
renderValue,
|
||||
renderItem,
|
||||
}) => {
|
||||
const cls = prefixCls ?? 'native-select';
|
||||
const [selectedIndex, setSelectedIndex] = useControlledState<number>(0, { value, onChange });
|
||||
|
||||
const { styles } = useStyles(cls);
|
||||
const listRef = useRef<Array<HTMLElement | null>>([]);
|
||||
const listContentRef = useRef<Array<string | null>>([]);
|
||||
const overflowRef = useRef<SideObject>(null);
|
||||
const allowSelectRef = useRef(false);
|
||||
const allowMouseUpRef = useRef(true);
|
||||
const selectTimeoutRef = useRef<any>();
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
||||
const [fallback, setFallback] = useState(false);
|
||||
const [innerOffset, setInnerOffset] = useState(0);
|
||||
const [touch, setTouch] = useState(false);
|
||||
const [scrollTop, setScrollTop] = useState(0);
|
||||
const [blockSelection, setBlockSelection] = useState(false);
|
||||
|
||||
if (!open) {
|
||||
if (innerOffset !== 0) setInnerOffset(0);
|
||||
if (fallback) setFallback(false);
|
||||
if (blockSelection) setBlockSelection(false);
|
||||
}
|
||||
|
||||
const { x, y, strategy, refs, context, isPositioned } = useFloating({
|
||||
placement: 'bottom-start',
|
||||
open,
|
||||
onOpenChange: setOpen,
|
||||
whileElementsMounted: autoUpdate,
|
||||
middleware: fallback
|
||||
? [
|
||||
offset(5),
|
||||
touch ? shift({ crossAxis: true, padding: 10 }) : flip({ padding: 10 }),
|
||||
size({
|
||||
apply({ availableHeight }) {
|
||||
Object.assign(scrollRef.current?.style ?? {}, {
|
||||
maxHeight: `${availableHeight}px`,
|
||||
});
|
||||
},
|
||||
padding: 10,
|
||||
}),
|
||||
]
|
||||
: [
|
||||
inner({
|
||||
listRef,
|
||||
overflowRef,
|
||||
scrollRef,
|
||||
index: selectedIndex,
|
||||
offset: innerOffset,
|
||||
onFallbackChange: setFallback,
|
||||
padding: 10,
|
||||
minItemsVisible: touch ? 8 : 4,
|
||||
referenceOverflowThreshold: 20,
|
||||
}),
|
||||
offset({ crossAxis: -4 }),
|
||||
],
|
||||
});
|
||||
|
||||
const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
|
||||
useClick(context, { event: 'mousedown' }),
|
||||
useDismiss(context),
|
||||
useRole(context, { role: 'listbox' }),
|
||||
useInnerOffset(context, {
|
||||
enabled: !fallback,
|
||||
onChange: setInnerOffset,
|
||||
overflowRef,
|
||||
scrollRef,
|
||||
}),
|
||||
useListNavigation(context, {
|
||||
listRef,
|
||||
activeIndex,
|
||||
selectedIndex,
|
||||
onNavigate: setActiveIndex,
|
||||
}),
|
||||
useTypeahead(context, {
|
||||
listRef: listContentRef,
|
||||
activeIndex,
|
||||
onMatch: open ? setActiveIndex : setSelectedIndex,
|
||||
}),
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
selectTimeoutRef.current = setTimeout(() => {
|
||||
allowSelectRef.current = true;
|
||||
}, 300);
|
||||
|
||||
return () => {
|
||||
clearTimeout(selectTimeoutRef.current);
|
||||
};
|
||||
} else {
|
||||
allowSelectRef.current = false;
|
||||
allowMouseUpRef.current = true;
|
||||
}
|
||||
return () => [];
|
||||
}, [open]);
|
||||
|
||||
const handleArrowScroll = (amount: number) => {
|
||||
if (fallback) {
|
||||
if (scrollRef.current) {
|
||||
scrollRef.current.scrollTop -= amount;
|
||||
flushSync(() => setScrollTop(scrollRef.current?.scrollTop ?? 0));
|
||||
}
|
||||
} else {
|
||||
flushSync(() => setInnerOffset((value) => value - amount));
|
||||
}
|
||||
};
|
||||
|
||||
const handleArrowHide = () => {
|
||||
if (touch) {
|
||||
clearTimeout(selectTimeoutRef.current);
|
||||
setBlockSelection(true);
|
||||
selectTimeoutRef.current = setTimeout(() => {
|
||||
setBlockSelection(false);
|
||||
}, 400);
|
||||
}
|
||||
};
|
||||
|
||||
const { label } = options[selectedIndex] || {};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
ref={refs.setReference}
|
||||
className={styles.button}
|
||||
{...getReferenceProps({
|
||||
onTouchStart() {
|
||||
setTouch(true);
|
||||
},
|
||||
onPointerMove({ pointerType }) {
|
||||
if (pointerType === 'mouse') {
|
||||
setTouch(false);
|
||||
}
|
||||
},
|
||||
})}
|
||||
>
|
||||
{renderValue ? renderValue(selectedIndex) : label}
|
||||
</button>
|
||||
|
||||
<FloatingPortal>
|
||||
{open && (
|
||||
<FloatingOverlay lockScroll={!touch} style={{ zIndex: 3000 }}>
|
||||
<FloatingFocusManager context={context} modal={false} initialFocus={-1}>
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
style={{
|
||||
position: strategy,
|
||||
top: y ?? 0,
|
||||
left: x ?? 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={styles.container}
|
||||
style={{ overflowY: 'auto' }}
|
||||
ref={scrollRef}
|
||||
{...getFloatingProps({
|
||||
onScroll({ currentTarget }) {
|
||||
setScrollTop(currentTarget.scrollTop);
|
||||
},
|
||||
onContextMenu(e) {
|
||||
e.preventDefault();
|
||||
},
|
||||
})}
|
||||
>
|
||||
{options.map((item, i) => {
|
||||
return (
|
||||
<SelectItem
|
||||
key={item.value}
|
||||
value={item.value}
|
||||
label={renderItem ? renderItem(item, i) : item.label}
|
||||
// Prevent immediate selection on touch devices when
|
||||
// pressing the ScrollArrows
|
||||
disabled={blockSelection}
|
||||
isSelected={i === selectedIndex}
|
||||
isActive={i === activeIndex}
|
||||
ref={(node) => {
|
||||
listRef.current[i] = node;
|
||||
listContentRef.current[i] = item.label as string;
|
||||
}}
|
||||
{...getItemProps({
|
||||
onTouchStart() {
|
||||
allowSelectRef.current = true;
|
||||
allowMouseUpRef.current = false;
|
||||
},
|
||||
onKeyDown() {
|
||||
allowSelectRef.current = true;
|
||||
},
|
||||
onClick() {
|
||||
if (allowSelectRef.current) {
|
||||
setSelectedIndex(i);
|
||||
setOpen(false);
|
||||
}
|
||||
},
|
||||
onMouseUp() {
|
||||
if (!allowMouseUpRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (allowSelectRef.current) {
|
||||
setSelectedIndex(i);
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
// On touch devices, prevent the element from
|
||||
// immediately closing `onClick` by deferring it
|
||||
clearTimeout(selectTimeoutRef.current);
|
||||
selectTimeoutRef.current = setTimeout(() => {
|
||||
allowSelectRef.current = true;
|
||||
});
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</FloatingFocusManager>
|
||||
</FloatingOverlay>
|
||||
)}
|
||||
</FloatingPortal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NativeSelect;
|
49
.dumi/theme/components/NativeSelect/style.ts
Normal file
49
.dumi/theme/components/NativeSelect/style.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, stylish, cx, token }, prefixCls: string) => ({
|
||||
container: cx(
|
||||
prefixCls,
|
||||
css`
|
||||
background: ${token.colorBgElevated};
|
||||
font-size: ${token.fontSize};
|
||||
border: 1px solid ${token.colorBorder};
|
||||
box-shadow: ${token.boxShadowSecondary};
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: none;
|
||||
padding: 5px;
|
||||
outline: 0;
|
||||
user-select: none;
|
||||
width: 160px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
),
|
||||
button: cx(
|
||||
`${prefixCls}-button`,
|
||||
css`
|
||||
all: unset;
|
||||
font-size: ${token.fontSize}px;
|
||||
padding: 8px;
|
||||
line-height: 0;
|
||||
background: ${token.colorBgContainer};
|
||||
color: ${token.colorTextSecondary};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
border: 1px solid ${token.colorBorder};
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
${stylish.buttonDefaultHover}
|
||||
|
||||
&:focus-visible {
|
||||
border-color: ${token.colorPrimary};
|
||||
box-shadow: 0 0 0 2px ${token.colorPrimaryBg};
|
||||
}
|
||||
`,
|
||||
),
|
||||
}));
|
24
.dumi/theme/components/SiteProvider/index.tsx
Normal file
24
.dumi/theme/components/SiteProvider/index.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { App } from 'antd';
|
||||
import { StyleProvider, ThemeProvider } from 'antd-style';
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { useThemeStore } from '../../store/useThemeStore';
|
||||
import { getAntdTheme, getCustomStylish, getCustomToken } from '../../styles';
|
||||
|
||||
export default ({ children }: PropsWithChildren) => {
|
||||
const themeMode = useThemeStore((s) => s.themeMode);
|
||||
|
||||
return (
|
||||
<StyleProvider prefix={'site'}>
|
||||
<ThemeProvider
|
||||
prefixCls={'site'}
|
||||
themeMode={themeMode}
|
||||
theme={getAntdTheme}
|
||||
customStylish={getCustomStylish}
|
||||
customToken={getCustomToken}
|
||||
>
|
||||
<App>{children}</App>
|
||||
</ThemeProvider>
|
||||
</StyleProvider>
|
||||
);
|
||||
};
|
42
.dumi/theme/components/StoreUpdater/index.tsx
Normal file
42
.dumi/theme/components/StoreUpdater/index.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { useLocation } from '@@/exports';
|
||||
import { useNavData, useRouteMeta, useSidebarData, useSiteData } from 'dumi';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect } from 'react';
|
||||
import { useSiteStore } from '../../store/useSiteStore';
|
||||
|
||||
export const StoreUpdater = memo(() => {
|
||||
const siteData = useSiteData();
|
||||
const sidebar = useSidebarData();
|
||||
const routeMeta = useRouteMeta();
|
||||
const navData = useNavData();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const { setLoading, ...data } = siteData;
|
||||
const {
|
||||
siteData: { setLoading: _, ...prevData },
|
||||
} = useSiteStore.getState();
|
||||
|
||||
if (isEqual(data, prevData)) return;
|
||||
|
||||
useSiteStore.setState({ siteData });
|
||||
}, [siteData]);
|
||||
|
||||
useEffect(() => {
|
||||
useSiteStore.setState({ sidebar });
|
||||
}, [sidebar]);
|
||||
|
||||
useEffect(() => {
|
||||
useSiteStore.setState({ routeMeta });
|
||||
}, [routeMeta]);
|
||||
|
||||
useEffect(() => {
|
||||
useSiteStore.setState({ navData });
|
||||
}, [navData]);
|
||||
|
||||
useEffect(() => {
|
||||
useSiteStore.setState({ location });
|
||||
}, [location]);
|
||||
|
||||
return null;
|
||||
});
|
62
.dumi/theme/components/ThemeSwitch/index.tsx
Normal file
62
.dumi/theme/components/ThemeSwitch/index.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { styled, ThemeMode } from 'antd-style';
|
||||
import { memo, ReactNode, type FC } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { useThemeStore } from '../../store/useThemeStore';
|
||||
import NativeSelect from '../NativeSelect';
|
||||
|
||||
const IconDark = () => (
|
||||
<svg viewBox="0 0 16 16" width="1em" height="1em" fill="currentColor">
|
||||
<path d="M8.218 1.455c3.527.109 6.327 3.018 6.327 6.545 0 3.6-2.945 6.545-6.545 6.545a6.562 6.562 0 0 1-6.036-4h.218c3.6 0 6.545-2.945 6.545-6.545 0-.91-.182-1.745-.509-2.545m0-1.455c-.473 0-.909.218-1.2.618-.29.4-.327.946-.145 1.382.254.655.4 1.31.4 2 0 2.8-2.291 5.09-5.091 5.09h-.218c-.473 0-.91.22-1.2.62-.291.4-.328.945-.146 1.38C1.891 14.074 4.764 16 8 16c4.4 0 8-3.6 8-8a7.972 7.972 0 0 0-7.745-8h-.037Z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const IconLight = () => (
|
||||
<svg viewBox="0 0 16 16" width="1em" height="1em" fill="currentColor">
|
||||
<path d="M8 13a1 1 0 0 1 1 1v1a1 1 0 1 1-2 0v-1a1 1 0 0 1 1-1ZM8 3a1 1 0 0 1-1-1V1a1 1 0 1 1 2 0v1a1 1 0 0 1-1 1Zm7 4a1 1 0 1 1 0 2h-1a1 1 0 1 1 0-2h1ZM3 8a1 1 0 0 1-1 1H1a1 1 0 1 1 0-2h1a1 1 0 0 1 1 1Zm9.95 3.536.707.707a1 1 0 0 1-1.414 1.414l-.707-.707a1 1 0 0 1 1.414-1.414Zm-9.9-7.072-.707-.707a1 1 0 0 1 1.414-1.414l.707.707A1 1 0 0 1 3.05 4.464Zm9.9 0a1 1 0 0 1-1.414-1.414l.707-.707a1 1 0 0 1 1.414 1.414l-.707.707Zm-9.9 7.072a1 1 0 0 1 1.414 1.414l-.707.707a1 1 0 0 1-1.414-1.414l.707-.707ZM8 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm0 6.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const IconAuto = () => (
|
||||
<svg viewBox="0 0 16 16" width="1em" height="1em" fill="currentColor">
|
||||
<path d="M14.595 8a6.595 6.595 0 1 1-13.19 0 6.595 6.595 0 0 1 13.19 0ZM8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0Zm0 2.014v11.972A5.986 5.986 0 0 0 8 2.014Z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const IconWrapper = styled.span`
|
||||
width: 12px;
|
||||
`;
|
||||
|
||||
const Option = ({ icon, label }: { icon: ReactNode; label: ReactNode }) => (
|
||||
<Flexbox horizontal gap={12} align={'center'}>
|
||||
<IconWrapper>{icon} </IconWrapper>
|
||||
{label}
|
||||
</Flexbox>
|
||||
);
|
||||
|
||||
const options = [
|
||||
{ label: '跟随系统', icon: <IconAuto />, value: 'auto' },
|
||||
{ label: '亮色模式', icon: <IconLight />, value: 'light' },
|
||||
{ label: '暗色模式', icon: <IconDark />, value: 'dark' },
|
||||
];
|
||||
|
||||
const ThemeSwitch: FC = () => {
|
||||
const themeMode = useThemeStore((s) => s.themeMode);
|
||||
|
||||
return (
|
||||
<span>
|
||||
<NativeSelect
|
||||
options={options}
|
||||
value={options.findIndex((o) => o.value === themeMode)}
|
||||
onChange={(index) => {
|
||||
const mode = options[index].value as unknown as ThemeMode;
|
||||
useThemeStore.setState({ themeMode: mode });
|
||||
}}
|
||||
renderValue={(index) => options[index].icon}
|
||||
renderItem={(item) => <Option label={item.label} icon={item.icon} />}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ThemeSwitch);
|
21
.dumi/theme/hooks/useCopied.ts
Normal file
21
.dumi/theme/hooks/useCopied.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
export const useCopied = () => {
|
||||
const [copied, setCopy] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) return;
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setCopy(false);
|
||||
}, 2000);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [copied]);
|
||||
|
||||
const setCopied = useCallback(() => setCopy(true), []);
|
||||
|
||||
return useMemo(() => ({ copied, setCopied }), [copied]);
|
||||
};
|
@ -1,79 +0,0 @@
|
||||
.__dumi-default-menu .__dumi-default-menu-list > li > a {
|
||||
padding-left: 24px !important;
|
||||
|
||||
~ ul {
|
||||
margin-left: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown table {
|
||||
max-width: 100vw !important;
|
||||
}
|
||||
|
||||
div > .markdown:first-child > :first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
html,
|
||||
.__dumi-default-layout-toc,
|
||||
.__dumi-default-menu-inner {
|
||||
&::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
height: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(50, 50, 50, 0.3);
|
||||
border-radius: 1em;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: rgba(50, 50, 50, 0.1);
|
||||
border-radius: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.__dumi-default-locale-select {
|
||||
margin-right: 16px;
|
||||
margin-left: 16px !important;
|
||||
}
|
||||
|
||||
html {
|
||||
&::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
height: 6px;
|
||||
}
|
||||
}
|
||||
.__dumi-default-layout-hero {
|
||||
background-color: #1890ff !important;
|
||||
background-image: url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/YIJ8Tbp7Oc4AAAAAAAAAAAAAFl94AQBr');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 90% center;
|
||||
background-size: contain;
|
||||
transition: 0.3s all;
|
||||
|
||||
&:hover {
|
||||
background-position-y: -20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
div > p {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.procomponents_dark_theme_view {
|
||||
height: 64px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 680px) {
|
||||
.procomponents_dark_theme_view {
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.__dumi-default-layout[data-site-mode='true'][data-show-slugs='true'] {
|
||||
max-width: 1500px;
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
import dumiContext from '@umijs/preset-dumi/lib/theme/context';
|
||||
import { ConfigProvider, Switch } from 'antd';
|
||||
import zhCN from 'antd/es/locale/zh_CN';
|
||||
import Layout from 'dumi-theme-default/src/layout';
|
||||
import { useContext, useEffect, useMemo } from 'react';
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
import { IRouteComponentProps, isBrowser } from 'umi';
|
||||
import './layout.less';
|
||||
import { useDarkreader } from './useDarkreader';
|
||||
|
||||
const DarkButton = () => {
|
||||
const colorScheme = useMemo(() => {
|
||||
if (!isBrowser()) {
|
||||
return 'light';
|
||||
}
|
||||
if ((window as any).HeadlessChrome) {
|
||||
return 'light';
|
||||
}
|
||||
|
||||
return matchMedia?.('(prefers-color-scheme: dark)').matches && 'dark';
|
||||
}, []);
|
||||
|
||||
const defaultDarken = useMemo(() => {
|
||||
if (!isBrowser()) {
|
||||
return 'light';
|
||||
}
|
||||
if ((window as any).HeadlessChrome) {
|
||||
return 'light';
|
||||
}
|
||||
return localStorage.getItem('procomponents_dark_theme') || colorScheme;
|
||||
}, []);
|
||||
|
||||
const setColor = (isDarken: boolean) => {
|
||||
try {
|
||||
const theme = document.getElementsByTagName('meta')['theme-color'];
|
||||
theme.setAttribute('content', isDarken ? '#242525' : '#1890ff');
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const [isDark, { toggle }] = useDarkreader(defaultDarken === 'dark');
|
||||
|
||||
useEffect(() => {
|
||||
setColor(isDark);
|
||||
}, [isDark]);
|
||||
|
||||
if (!isBrowser()) {
|
||||
return null;
|
||||
}
|
||||
if ((window as any).HeadlessChrome) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Switch
|
||||
checkedChildren="🌜"
|
||||
unCheckedChildren="🌞"
|
||||
defaultChecked={defaultDarken === 'dark'}
|
||||
checked={isDark}
|
||||
onChange={(check) => {
|
||||
toggle();
|
||||
if (!check) {
|
||||
localStorage.setItem('procomponents_dark_theme', 'light');
|
||||
} else {
|
||||
localStorage.setItem('procomponents_dark_theme', 'dark');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function loadJS(url, callback) {
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.onload = function () {
|
||||
callback?.();
|
||||
};
|
||||
script.src = url;
|
||||
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
}
|
||||
|
||||
const LayoutPage = ({ children, ...props }: IRouteComponentProps) => {
|
||||
const context = useContext(dumiContext);
|
||||
useEffect(() => {
|
||||
if (!isBrowser()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((window as any).HeadlessChrome) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadJS('https://www.googletagmanager.com/gtag/js?id=G-RMBLDHGL1N', function () {
|
||||
// @ts-ignore
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
// @ts-ignore
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
// @ts-ignore
|
||||
gtag('js', new Date());
|
||||
// @ts-ignore
|
||||
gtag('config', 'G-RMBLDHGL1N');
|
||||
});
|
||||
|
||||
(function (h, o, t, j, a, r) {
|
||||
// @ts-ignore
|
||||
h.hj =
|
||||
// @ts-ignore
|
||||
h.hj ||
|
||||
function () {
|
||||
// @ts-ignore
|
||||
(h.hj.q = h.hj.q || []).push(arguments);
|
||||
};
|
||||
// @ts-ignore
|
||||
h._hjSettings = { hjid: 2036108, hjsv: 6 };
|
||||
a = o.getElementsByTagName('head')[0];
|
||||
r = o.createElement('script');
|
||||
r.async = 1;
|
||||
// @ts-ignore
|
||||
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
|
||||
a.appendChild(r);
|
||||
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
|
||||
}, []);
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (context.meta.title?.includes('-')) {
|
||||
return `${context.meta.title}`;
|
||||
}
|
||||
if (!context.meta.title) {
|
||||
return 'ProComponents - 模板组件';
|
||||
}
|
||||
return `${context.meta.title} - ProComponents`;
|
||||
}, [context]);
|
||||
|
||||
return (
|
||||
<HelmetProvider>
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<Layout {...props}>
|
||||
<div>
|
||||
<Helmet key="title">
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
<div key="children">{children}</div>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
right: 8,
|
||||
top: 0,
|
||||
zIndex: 999,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
key="procomponents_dark_theme_view"
|
||||
className="procomponents_dark_theme_view"
|
||||
>
|
||||
{isBrowser() ? <DarkButton /> : null}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</ConfigProvider>
|
||||
</HelmetProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutPage;
|
7
.dumi/theme/layouts/DemoLayout/index.tsx
Normal file
7
.dumi/theme/layouts/DemoLayout/index.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import { useOutlet } from 'dumi';
|
||||
import DemoProvider from '../../components/DemoProvider';
|
||||
|
||||
export default () => {
|
||||
const outlet = useOutlet();
|
||||
return <DemoProvider>{outlet}</DemoProvider>;
|
||||
};
|
64
.dumi/theme/layouts/DocLayout/index.tsx
Normal file
64
.dumi/theme/layouts/DocLayout/index.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import animateScrollTo from 'animated-scroll-to';
|
||||
import { Helmet, useIntl, useLocation } from 'dumi';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, StrictMode, useEffect, type FC } from 'react';
|
||||
|
||||
import SiteProvider from '../../components/SiteProvider';
|
||||
import { StoreUpdater } from '../../components/StoreUpdater';
|
||||
|
||||
import Docs from '../../pages/Docs';
|
||||
import Home from '../../pages/Home';
|
||||
|
||||
import { isHeroPageSel, useSiteStore } from '../../store/useSiteStore';
|
||||
import { GlobalStyle } from './styles';
|
||||
|
||||
const DocLayout: FC = memo(() => {
|
||||
const intl = useIntl();
|
||||
const { hash } = useLocation();
|
||||
const fm = useSiteStore((s) => s.routeMeta.frontmatter, isEqual);
|
||||
const isHomePage = useSiteStore(isHeroPageSel);
|
||||
const loading = useSiteStore((s) => s.siteData.loading);
|
||||
|
||||
// handle hash change or visit page hash after async chunk loaded
|
||||
useEffect(() => {
|
||||
const id = hash.replace('#', '');
|
||||
|
||||
if (id) {
|
||||
setTimeout(() => {
|
||||
const elm = document.getElementById(decodeURIComponent(id));
|
||||
|
||||
if (elm) {
|
||||
// animated-scroll-to instead of native scroll
|
||||
animateScrollTo(elm.offsetTop - 80, {
|
||||
maxDuration: 300,
|
||||
});
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
}, [loading, hash]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<html lang={intl.locale.replace(/-.+$/, '')} />
|
||||
{fm.title && <meta property="og:title" content={fm.title} />}
|
||||
{fm.description && <meta name="description" content={fm.description} />}
|
||||
{fm.description && <meta property="og:description" content={fm.description} />}
|
||||
{fm.keywords && <meta name="keywords" content={fm.keywords.join(',')} />}
|
||||
{fm.keywords && <meta property="og:keywords" content={fm.keywords.join(',')} />}
|
||||
</Helmet>
|
||||
|
||||
{isHomePage ? <Home /> : <Docs />}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default () => (
|
||||
<StrictMode>
|
||||
<SiteProvider>
|
||||
<StoreUpdater />
|
||||
<GlobalStyle />
|
||||
<DocLayout />
|
||||
</SiteProvider>
|
||||
</StrictMode>
|
||||
);
|
26
.dumi/theme/layouts/DocLayout/styles.ts
Normal file
26
.dumi/theme/layouts/DocLayout/styles.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { createGlobalStyle } from 'antd-style';
|
||||
|
||||
export const GlobalStyle = createGlobalStyle`
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: ${(p) => p.theme.colorBgLayout};
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: AliPuHui;
|
||||
font-weight: normal;
|
||||
src: url('//at.alicdn.com/t/webfont_exesdog9toj.woff2') format('woff2'),
|
||||
url('//at.alicdn.com/t/webfont_exesdog9toj.woff') format('woff'),
|
||||
url('//at.alicdn.com/t/webfont_exesdog9toj.ttf') format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: AliPuHui;
|
||||
font-weight: bold;
|
||||
src: url('https://at.alicdn.com/wf/webfont/exMpJIukiCms/Gsw2PSKrftc1yNWMNlXgw.woff2') format('woff2'),
|
||||
url('https://at.alicdn.com/wf/webfont/exMpJIukiCms/vtu73by4O2gEBcvBuLgeu.woff') format('woff');
|
||||
font-display: swap;
|
||||
}
|
||||
`;
|
@ -1,34 +0,0 @@
|
||||
import { isBrowser } from 'umi';
|
||||
|
||||
export default ({ children, location, ...rest }) => {
|
||||
if (!isBrowser()) {
|
||||
return children;
|
||||
}
|
||||
if (location.pathname.startsWith('/~demos/layout')) {
|
||||
return children;
|
||||
}
|
||||
if (location.pathname.startsWith('/~demos/pagecontainer')) {
|
||||
return children;
|
||||
}
|
||||
if (location.pathname.startsWith('/~demos/form-layout')) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
padding: 24,
|
||||
contentVisibility: 'auto',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: 24,
|
||||
border: '1px solid #f0f0f0',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
86
.dumi/theme/pages/Docs/index.tsx
Normal file
86
.dumi/theme/pages/Docs/index.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import { Helmet, useLocation, useOutlet } from 'dumi';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, type FC } from 'react';
|
||||
import { Center, Flexbox } from 'react-layout-kit';
|
||||
|
||||
//@ts-ignore
|
||||
import Content from 'dumi/theme/slots/Content';
|
||||
//@ts-ignore
|
||||
import Footer from 'dumi/theme/slots/Footer';
|
||||
//@ts-ignore
|
||||
import Header from 'dumi/theme/slots/Header';
|
||||
//@ts-ignore
|
||||
import Sidebar from 'dumi/theme/slots/Sidebar';
|
||||
//@ts-ignore
|
||||
import Toc from 'dumi/theme/slots/Toc';
|
||||
|
||||
import { ApiHeader } from '../../components/ApiHeader';
|
||||
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { isApiPageSel, useSiteStore } from '../../store/useSiteStore';
|
||||
import { useStyles } from './styles';
|
||||
|
||||
const Docs: FC = memo(() => {
|
||||
const outlet = useOutlet();
|
||||
const { mobile } = useResponsive();
|
||||
const fm = useSiteStore((s) => s.routeMeta.frontmatter, isEqual);
|
||||
const isApiPage = useSiteStore(isApiPageSel);
|
||||
|
||||
const { styles, theme } = useStyles();
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
}, [location.pathname]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.layout}
|
||||
style={
|
||||
location.pathname.includes('changelog')
|
||||
? {
|
||||
gridTemplateColumns: '0 1fr 300px',
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<Header />
|
||||
<Toc />
|
||||
{mobile ? null : <Sidebar />}
|
||||
{isApiPage ? (
|
||||
<Flexbox style={{ gridArea: 'title' }}>
|
||||
<Center>
|
||||
<Flexbox style={{ maxWidth: theme.contentMaxWidth, width: '100%' }}>
|
||||
<Flexbox padding={'0 48px'}>
|
||||
<ApiHeader title={fm.title} description={fm.description} />
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
</Center>
|
||||
</Flexbox>
|
||||
) : null}
|
||||
<Flexbox
|
||||
style={{
|
||||
zIndex: 10,
|
||||
gridArea: 'main',
|
||||
minWidth: 0,
|
||||
margin: mobile ? 0 : 24,
|
||||
marginBottom: mobile ? 0 : 48,
|
||||
}}
|
||||
>
|
||||
<Center width={'100%'}>
|
||||
<Flexbox className={styles.content}>
|
||||
<Flexbox horizontal>
|
||||
<Content>{outlet}</Content>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
</Center>
|
||||
</Flexbox>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Docs;
|
65
.dumi/theme/pages/Docs/styles.ts
Normal file
65
.dumi/theme/pages/Docs/styles.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, responsive, token }) => ({
|
||||
layout: css`
|
||||
background-color: ${token.colorBgLayout};
|
||||
background-image: linear-gradient(
|
||||
180deg,
|
||||
${token.colorBgContainer} 0%,
|
||||
rgba(255, 255, 255, 0) 10%
|
||||
);
|
||||
display: grid;
|
||||
grid-template-columns: ${token.sidebarWidth}px 1fr ${token.tocWidth}px;
|
||||
grid-template-rows: ${token.headerHeight}px auto 1fr 80px;
|
||||
grid-template-areas:
|
||||
'head head head'
|
||||
'sidebar title .'
|
||||
'sidebar main toc'
|
||||
'sidebar footer footer';
|
||||
min-height: 100vh;
|
||||
|
||||
${responsive.mobile} {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
`,
|
||||
|
||||
toc: css`
|
||||
position: sticky;
|
||||
top: 100px;
|
||||
width: ${token.tocWidth}px;
|
||||
margin-inline-end: 24px;
|
||||
max-height: 80vh;
|
||||
overflow: auto;
|
||||
margin-top: 48px;
|
||||
|
||||
${responsive.mobile} {
|
||||
z-index: 300;
|
||||
top: ${token.headerHeight + 1}px;
|
||||
margin-top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
> h4 {
|
||||
margin: 0 0 8px;
|
||||
color: ${token.colorTextDescription};
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
`,
|
||||
|
||||
content: css`
|
||||
max-width: ${token.contentMaxWidth}px;
|
||||
width: 100%;
|
||||
margin: 0 24px;
|
||||
|
||||
|
||||
${responsive.mobile} {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
25
.dumi/theme/pages/Home/index.tsx
Normal file
25
.dumi/theme/pages/Home/index.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Helmet } from 'dumi';
|
||||
import { memo, type FC } from 'react';
|
||||
|
||||
//@ts-ignore
|
||||
import Features from 'dumi/theme/slots/Features';
|
||||
//@ts-ignore
|
||||
import Footer from 'dumi/theme/slots/Footer';
|
||||
//@ts-ignore
|
||||
import Header from 'dumi/theme/slots/Header';
|
||||
//@ts-ignore
|
||||
import Hero from 'dumi/theme/slots/Hero';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
const Home: FC = memo(() => (
|
||||
<>
|
||||
<Flexbox align={'center'} gap={80}>
|
||||
<Header />
|
||||
<Hero />
|
||||
<Features />
|
||||
<Footer />
|
||||
</Flexbox>
|
||||
</>
|
||||
));
|
||||
|
||||
export default Home;
|
5
.dumi/theme/plugin.ts
Normal file
5
.dumi/theme/plugin.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { IApi } from 'dumi';
|
||||
|
||||
export default (api: IApi) => {
|
||||
api.logger.info('💋', api.env);
|
||||
};
|
23
.dumi/theme/slots/Content/index.tsx
Normal file
23
.dumi/theme/slots/Content/index.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { useSidebarData } from 'dumi';
|
||||
import { PropsWithChildren, type FC } from 'react';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Content: FC<PropsWithChildren> = ({ children }) => {
|
||||
const sidebar = useSidebarData();
|
||||
|
||||
//FIXME Dumi 关于 loading 的处理不太理想,等这块优化后再补充,或者直接让 dumi 内置该能力
|
||||
// const loading = useSiteStore((s) => s.siteData.loading);
|
||||
|
||||
const { styles, cx } = useStyles();
|
||||
return (
|
||||
<div
|
||||
className={cx('dumi-default-content', styles.content)}
|
||||
data-no-sidebar={!sidebar || undefined}
|
||||
>
|
||||
{/*<Skeleton active paragraph loading={loading} style={{ float: 'initial' }} />*/}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Content;
|
137
.dumi/theme/slots/Content/style.ts
Normal file
137
.dumi/theme/slots/Content/style.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ token, responsive, isDarkMode, css }) => ({
|
||||
content: css`
|
||||
min-height: 400px;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
padding: 24px 48px;
|
||||
border-radius: 10px;
|
||||
background-color: ${token.colorBgContainer};
|
||||
box-shadow: ${token.boxShadow};
|
||||
${responsive.mobile} {
|
||||
padding: 8px 16px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.markdown {
|
||||
color: ${token.colorTextSecondary};
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
color: ${token.colorText};
|
||||
}
|
||||
p {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
// hyperlink
|
||||
a {
|
||||
color: ${token.colorLink};
|
||||
|
||||
&:hover {
|
||||
color: ${token.colorLinkHover};
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: ${token.colorLinkActive};
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 600px;
|
||||
width: 80%;
|
||||
opacity: ${isDarkMode ? 0.8 : 1};
|
||||
}
|
||||
|
||||
// inline code
|
||||
> :not(.source-code) code {
|
||||
padding: 2px 6px;
|
||||
|
||||
color: ${token.colorPrimaryText};
|
||||
background: ${token.colorPrimaryBg};
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
// pre tag
|
||||
pre {
|
||||
font-size: 14px;
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
// table
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 1px;
|
||||
}
|
||||
|
||||
th {
|
||||
background: ${token.colorFillTertiary};
|
||||
}
|
||||
|
||||
tr {
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding-block-start: 10px;
|
||||
padding-block-end: 10px;
|
||||
padding-inline-start: 16px;
|
||||
padding-inline-end: 16px;
|
||||
border-bottom: 1px solid ${token.colorBorderSecondary};
|
||||
}
|
||||
|
||||
// blockquote
|
||||
blockquote {
|
||||
font-style: italic;
|
||||
|
||||
margin: 16px 0;
|
||||
padding: 0 12px;
|
||||
color: ${token.colorTextDescription};
|
||||
border-left: 3px solid ${token.colorBorder};
|
||||
}
|
||||
|
||||
// list
|
||||
ul li {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
// anchor of headings
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
> a[aria-hidden]:first-child {
|
||||
float: left;
|
||||
width: 20px;
|
||||
padding-inline-end: 4px;
|
||||
margin-inline-start: -24px;
|
||||
color: ${token.colorText};
|
||||
// hide phantom blank node
|
||||
font-size: 0;
|
||||
text-align: right;
|
||||
line-height: inherit;
|
||||
|
||||
&:hover {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
> .icon-link::before {
|
||||
content: '#';
|
||||
color: ${token.colorTextTertiary};
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:hover) > a[aria-hidden]:first-child > .icon-link {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
70
.dumi/theme/slots/Features/index.tsx
Normal file
70
.dumi/theme/slots/Features/index.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import { ArrowRightOutlined } from '@ant-design/icons';
|
||||
import { Tag } from 'antd';
|
||||
import { Link } from 'dumi';
|
||||
import { type FC } from 'react';
|
||||
import { Center, Flexbox } from 'react-layout-kit';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { featuresSel, useSiteStore } from '../../store/useSiteStore';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Features: FC = () => {
|
||||
const features = useSiteStore(featuresSel, shallow);
|
||||
|
||||
const { styles, cx, theme } = useStyles();
|
||||
|
||||
if (!Boolean(features?.length)) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{features!.map(({ title, description, avatar, link, imageStyle, row, column, center }) => {
|
||||
return (
|
||||
<div
|
||||
key={title}
|
||||
className={cx(styles.cell)}
|
||||
style={{
|
||||
gridRow: `span ${row || 7}`,
|
||||
gridColumn: `span ${column || 1}`,
|
||||
}}
|
||||
>
|
||||
{avatar && (
|
||||
<Center
|
||||
padding={4}
|
||||
width={24}
|
||||
height={24}
|
||||
image-style={imageStyle}
|
||||
className={cx(styles.imgContainer)}
|
||||
>
|
||||
<img className={styles.img} src={avatar} alt={title} />
|
||||
</Center>
|
||||
)}
|
||||
{title && (
|
||||
<Flexbox as={'h3'} horizontal gap={8} align={'center'}>
|
||||
{title}
|
||||
{imageStyle === 'soon' ? (
|
||||
<Tag
|
||||
color={theme.isDarkMode ? 'pink-inverse' : 'cyan-inverse'}
|
||||
// style={{ border: 'none' }}
|
||||
>
|
||||
SOON
|
||||
</Tag>
|
||||
) : null}
|
||||
</Flexbox>
|
||||
)}
|
||||
{description && <p dangerouslySetInnerHTML={{ __html: description }} />}{' '}
|
||||
{link && (
|
||||
<div className={styles.link}>
|
||||
<Link to={link}>
|
||||
立即了解 <ArrowRightOutlined />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{center && <div className={styles.blur} />}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Features;
|
119
.dumi/theme/slots/Features/style.ts
Normal file
119
.dumi/theme/slots/Features/style.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import chroma from 'chroma-js';
|
||||
|
||||
export const useStyles = createStyles(({ token, responsive, css, stylish, isDarkMode }) => ({
|
||||
container: css`
|
||||
max-width: ${token.contentMaxWidth}px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-auto-flow: row dense;
|
||||
grid-auto-rows: 24px;
|
||||
gap: 16px;
|
||||
|
||||
margin: 0 16px;
|
||||
|
||||
${responsive({
|
||||
mobile: css`
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
`,
|
||||
laptop: {
|
||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
||||
},
|
||||
// desktop: {
|
||||
// gridTemplateColumns: 'repeat(3, 1fr)',
|
||||
// },
|
||||
})}
|
||||
`,
|
||||
|
||||
cell: css`
|
||||
z-index: 1;
|
||||
padding: 24px;
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(135deg, ${token.colorFillContent}, ${token.colorFillQuaternary});
|
||||
position: relative;
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
color: ${token.colorText};
|
||||
}
|
||||
p {
|
||||
color: ${token.colorTextSecondary};
|
||||
|
||||
quotient {
|
||||
color: ${token.colorTextDescription};
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
padding-left: 12px;
|
||||
position: relative;
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: 0;
|
||||
display: block;
|
||||
border-radius: 2px;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: ${isDarkMode ? token.colorPrimary : token.colorPrimaryBgHover};
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
imgContainer: css`
|
||||
background: ${token.colorFillContent};
|
||||
border-radius: 8px;
|
||||
opacity: 0.8;
|
||||
|
||||
&[image-style='primary'] {
|
||||
background: linear-gradient(135deg, ${token.gradientColor1}, ${token.gradientColor2});
|
||||
}
|
||||
|
||||
&[image-style='light'] {
|
||||
background: ${token.colorBgContainer};
|
||||
}
|
||||
|
||||
&[image-style='soon'] {
|
||||
opacity: 0.5;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
${chroma(token.gradientColor2).alpha(0.3).hex()},
|
||||
${chroma(token.gradientColor2).alpha(0.3).hex()} 50%,
|
||||
${chroma(token.gradientColor1).alpha(0.3).hex()}
|
||||
);
|
||||
}
|
||||
`,
|
||||
|
||||
img: css`
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: ${token.colorWhite};
|
||||
`,
|
||||
link: css`
|
||||
margin-top: 24px;
|
||||
|
||||
a {
|
||||
${stylish.resetLinkColor};
|
||||
|
||||
color: ${token.colorTextDescription};
|
||||
&:hover {
|
||||
color: ${token.colorPrimaryHover};
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
blur: css`
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
${stylish.heroBlurBall};
|
||||
scale: 2;
|
||||
opacity: 0.05;
|
||||
${responsive.mobile} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
}));
|
58
.dumi/theme/slots/Footer/index.tsx
Normal file
58
.dumi/theme/slots/Footer/index.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import { Divider, Typography } from 'antd';
|
||||
import { createStyles, useResponsive } from 'antd-style';
|
||||
import { useSiteData } from 'dumi';
|
||||
import { type FC } from 'react';
|
||||
import { Center, Flexbox } from 'react-layout-kit';
|
||||
|
||||
const useStyles = createStyles(
|
||||
({ css, responsive, token }) => css`
|
||||
grid-area: footer;
|
||||
border-top: 1px solid ${token.colorSplit};
|
||||
color: ${token.colorTextDescription};
|
||||
font-size: 14px;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
align-self: stretch;
|
||||
|
||||
${responsive.mobile} {
|
||||
border: none;
|
||||
flex-direction: column;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const Footer: FC = () => {
|
||||
const { themeConfig } = useSiteData();
|
||||
const { styles } = useStyles();
|
||||
const { mobile } = useResponsive();
|
||||
if (!themeConfig.footer) return null;
|
||||
|
||||
return mobile ? (
|
||||
<Center horizontal className={styles}>
|
||||
<Flexbox align={'center'} horizontal>
|
||||
Powered by
|
||||
<Typography.Link href="https://d.umijs.org/" style={{ marginLeft: 8 }}>
|
||||
Dumi
|
||||
</Typography.Link>
|
||||
<Divider type={'vertical'} style={{ margin: '0 8px' }} />
|
||||
<Typography.Link href="https://ant.design/">Ant Design</Typography.Link>
|
||||
<Divider type={'vertical'} style={{ margin: '0 8px' }} />
|
||||
<Typography.Link href="https://kitchen.alipay.com/">kitchen</Typography.Link>
|
||||
</Flexbox>
|
||||
</Center>
|
||||
) : (
|
||||
<Center horizontal className={styles}>
|
||||
Powered by
|
||||
<Flexbox align={'center'} horizontal style={{ marginLeft: 8 }}>
|
||||
<Typography.Link href="https://d.umijs.org/">Dumi</Typography.Link>
|
||||
<Divider type={'vertical'} style={{ margin: '0 8px' }} />
|
||||
<Typography.Link href="https://ant.design/">Ant Design</Typography.Link>
|
||||
<Divider type={'vertical'} style={{ margin: '0 8px' }} />
|
||||
<Typography.Link href="https://kitchen.alipay.com/">kitchen</Typography.Link>
|
||||
</Flexbox>
|
||||
</Center>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
86
.dumi/theme/slots/Header/index.tsx
Normal file
86
.dumi/theme/slots/Header/index.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { useState, type FC } from 'react';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import LangSwitch from 'dumi/theme-default/slots/LangSwitch';
|
||||
|
||||
//@ts-ignore
|
||||
import Logo from 'dumi/theme/slots/Logo';
|
||||
//@ts-ignore
|
||||
import Navbar from 'dumi/theme/slots/Navbar';
|
||||
//@ts-ignore
|
||||
import SearchBar from 'dumi/theme/slots/SearchBar';
|
||||
|
||||
import Burger from '../../components/Burger';
|
||||
import GithubButton from '../../components/GithubButton';
|
||||
import ThemeSwitch from '../../components/ThemeSwitch';
|
||||
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { useSiteStore } from '../../store/useSiteStore';
|
||||
import { useStyle } from './style';
|
||||
|
||||
const Header: FC = () => {
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const frontmatter = useSiteStore((s) => s.routeMeta.frontmatter, isEqual);
|
||||
|
||||
const { mobile } = useResponsive();
|
||||
const { styles } = useStyle();
|
||||
|
||||
return (
|
||||
frontmatter && (
|
||||
<div
|
||||
className={styles.header}
|
||||
data-static={Boolean(frontmatter.hero) || undefined}
|
||||
data-mobile-active={showMenu || undefined}
|
||||
onClick={() => setShowMenu(false)}
|
||||
>
|
||||
<Flexbox
|
||||
horizontal
|
||||
distribution={'space-between'}
|
||||
align={'center'}
|
||||
width={'auto'}
|
||||
className={styles.content}
|
||||
>
|
||||
{mobile ? (
|
||||
<>
|
||||
<Flexbox>
|
||||
<Burger />
|
||||
</Flexbox>
|
||||
<Flexbox horizontal className={styles.left}>
|
||||
<Logo />
|
||||
</Flexbox>
|
||||
<Flexbox>
|
||||
<ThemeSwitch />
|
||||
</Flexbox>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Flexbox horizontal className={styles.left}>
|
||||
<Logo />
|
||||
</Flexbox>
|
||||
<Flexbox style={{ marginLeft: 48, alignSelf: 'end' }}>
|
||||
<Navbar />
|
||||
</Flexbox>
|
||||
<section className={styles.right}>
|
||||
<div />
|
||||
<Flexbox
|
||||
gap={16}
|
||||
horizontal
|
||||
align={'center'}
|
||||
className="dumi-default-header-right-aside"
|
||||
>
|
||||
<SearchBar />
|
||||
<LangSwitch />
|
||||
<GithubButton />
|
||||
<ThemeSwitch />
|
||||
</Flexbox>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
</Flexbox>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
46
.dumi/theme/slots/Header/style.ts
Normal file
46
.dumi/theme/slots/Header/style.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyle = createStyles(({ css, responsive, token }) => ({
|
||||
header: css`
|
||||
top: 0;
|
||||
max-width: 100vw;
|
||||
position: sticky;
|
||||
background-color: transparent;
|
||||
backdrop-filter: blur(6px);
|
||||
z-index: ${token.zIndexPopupBase - 50};
|
||||
border-bottom: 1px solid ${token.colorSplit};
|
||||
|
||||
grid-area: head;
|
||||
align-self: stretch;
|
||||
|
||||
${responsive.mobile} {
|
||||
background: ${token.colorBgContainer};
|
||||
}
|
||||
`,
|
||||
content: css`
|
||||
padding: 0 24px;
|
||||
height: 64px;
|
||||
|
||||
${responsive.mobile} {
|
||||
padding: 0 16px;
|
||||
}
|
||||
`,
|
||||
left: css``,
|
||||
right: css`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&-aside {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
${responsive.mobile} {
|
||||
justify-content: center;
|
||||
margin: 8px 16px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid ${token.colorBorder};
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
17
.dumi/theme/slots/Hero/HeroButton/index.tsx
Normal file
17
.dumi/theme/slots/Hero/HeroButton/index.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { Button } from 'antd';
|
||||
import { FC, ReactNode } from 'react';
|
||||
import { useStyles } from './style';
|
||||
|
||||
interface HeroButtonProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
const HeroButton: FC<HeroButtonProps> = ({ children }) => {
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
<Button size={'large'} shape={'round'} type={'primary'} className={styles.button}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeroButton;
|
18
.dumi/theme/slots/Hero/HeroButton/style.ts
Normal file
18
.dumi/theme/slots/Hero/HeroButton/style.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, stylish, isDarkMode }) => {
|
||||
return {
|
||||
button: css`
|
||||
border: none;
|
||||
|
||||
${stylish.heroButtonGradient}
|
||||
${stylish.heroGradientFlow}
|
||||
|
||||
background-size: 200% 100%;
|
||||
|
||||
&:hover {
|
||||
animation: none;
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
58
.dumi/theme/slots/Hero/index.tsx
Normal file
58
.dumi/theme/slots/Hero/index.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import { Button, ConfigProvider } from 'antd';
|
||||
import { Link } from 'dumi';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { type FC } from 'react';
|
||||
import { Center, Flexbox } from 'react-layout-kit';
|
||||
|
||||
import HeroButton from './HeroButton';
|
||||
|
||||
import { useSiteStore } from '../../store/useSiteStore';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Hero: FC = () => {
|
||||
const frontmatter = useSiteStore((s) => s.routeMeta.frontmatter, isEqual);
|
||||
|
||||
const { styles, cx } = useStyles();
|
||||
|
||||
if (!('hero' in frontmatter)) return null;
|
||||
|
||||
const hero = frontmatter.hero!;
|
||||
return (
|
||||
<Flexbox horizontal distribution={'center'} className={styles.container}>
|
||||
<div className={styles.canvas}></div>
|
||||
<Center>
|
||||
{frontmatter.hero!.title && (
|
||||
<div className={styles.titleContainer}>
|
||||
<h1 className={styles.title} dangerouslySetInnerHTML={{ __html: hero.title! }} />
|
||||
<div
|
||||
className={cx(styles.titleShadow)}
|
||||
dangerouslySetInnerHTML={{ __html: hero.title! }}
|
||||
></div>
|
||||
</div>
|
||||
)}
|
||||
{hero.description && (
|
||||
<p className={styles.desc} dangerouslySetInnerHTML={{ __html: hero.description }} />
|
||||
)}
|
||||
{Boolean(frontmatter.hero!.actions?.length) && (
|
||||
<ConfigProvider theme={{ token: { fontSize: 16, controlHeight: 40 } }}>
|
||||
<Flexbox horizontal gap={24} className={styles.actions}>
|
||||
{frontmatter.hero!.actions!.map(({ text, link }, index) => (
|
||||
<Link key={text} to={link}>
|
||||
{index === 0 ? (
|
||||
<HeroButton>{text}</HeroButton>
|
||||
) : (
|
||||
<Button size={'large'} shape={'round'} type={'default'}>
|
||||
{text}
|
||||
</Button>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</Flexbox>
|
||||
</ConfigProvider>
|
||||
)}
|
||||
</Center>
|
||||
</Flexbox>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
106
.dumi/theme/slots/Hero/style.ts
Normal file
106
.dumi/theme/slots/Hero/style.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, responsive, token, stylish, isDarkMode }) => ({
|
||||
container: css`
|
||||
position: relative;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
+ * {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
> p {
|
||||
margin: 32px;
|
||||
color: ${token.colorTextSecondary};
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
|
||||
${responsive({
|
||||
mobile: { fontSize: 16 },
|
||||
})}
|
||||
}
|
||||
`,
|
||||
|
||||
titleContainer: css`
|
||||
position: relative;
|
||||
`,
|
||||
title: css`
|
||||
font-size: 68px;
|
||||
z-index: 10;
|
||||
color: transparent;
|
||||
margin: 0;
|
||||
font-family: AliPuHui, ${token.fontFamily};
|
||||
|
||||
${responsive({
|
||||
mobile: { fontSize: 40 },
|
||||
})}
|
||||
|
||||
b {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
${stylish.heroGradient};
|
||||
${stylish.heroGradientFlow}
|
||||
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
`,
|
||||
titleShadow: css`
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
color: ${isDarkMode ? token.colorWhite : token.colorTextBase};
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
font-size: 68px;
|
||||
font-family: AliPuHui, ${token.fontFamily};
|
||||
font-weight: bold;
|
||||
${responsive({
|
||||
mobile: { fontSize: 40 },
|
||||
})}
|
||||
|
||||
${stylish.heroTextShadow}
|
||||
|
||||
b {
|
||||
color: transparent;
|
||||
}
|
||||
`,
|
||||
|
||||
desc: css`
|
||||
font-size: ${token.fontSizeHeading3}px;
|
||||
color: ${token.colorTextSecondary};
|
||||
|
||||
${responsive.mobile} {
|
||||
font-size: ${token.fontSizeHeading5}px;
|
||||
margin: 24px 16px;
|
||||
}
|
||||
`,
|
||||
|
||||
actions: css`
|
||||
margin-top: 48px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
${responsive({
|
||||
mobile: { marginTop: 24 },
|
||||
})}
|
||||
`,
|
||||
canvas: css`
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: -250px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) scale(1.5);
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
opacity: 0.2;
|
||||
${stylish.heroBlurBall}
|
||||
|
||||
${responsive.mobile} {
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
}
|
||||
`,
|
||||
}));
|
24
.dumi/theme/slots/Logo/index.tsx
Normal file
24
.dumi/theme/slots/Logo/index.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { Link, useLocale } from 'dumi';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, type FC } from 'react';
|
||||
|
||||
import { useSiteStore } from '../../store/useSiteStore';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Logo: FC = () => {
|
||||
const locale = useLocale();
|
||||
const themeConfig = useSiteStore((s) => s.siteData.themeConfig, isEqual);
|
||||
const { styles, cx } = useStyles();
|
||||
|
||||
return (
|
||||
themeConfig && (
|
||||
<Link className={cx(styles)} to={'base' in locale ? locale.base : '/'}>
|
||||
<img src={themeConfig.logo as string} alt={themeConfig.name} />
|
||||
{themeConfig.name}
|
||||
</Link>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Logo);
|
29
.dumi/theme/slots/Logo/style.ts
Normal file
29
.dumi/theme/slots/Logo/style.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
({ css, stylish, responsive, token }) => css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-family: AliPuHui, ${token.fontFamily};
|
||||
color: ${token.colorText};
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
|
||||
${stylish.clickableText};
|
||||
|
||||
${responsive.mobile} {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-inline-end: 10px;
|
||||
height: 40px;
|
||||
|
||||
${responsive.mobile} {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
);
|
85
.dumi/theme/slots/Navbar/index.tsx
Normal file
85
.dumi/theme/slots/Navbar/index.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import { Tabs } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { history, useLocation } from 'dumi';
|
||||
import NavbarExtra from 'dumi/theme-default/slots/NavbarExtra';
|
||||
import { memo, type FC } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { activePathSel, useSiteStore } from '../../store/useSiteStore';
|
||||
|
||||
const useStyles = createStyles(({ css, responsive, token, stylish, prefixCls }) => {
|
||||
const prefix = `.${prefixCls}-tabs`;
|
||||
|
||||
const marginHoriz = 16;
|
||||
const paddingVertical = 6;
|
||||
|
||||
return {
|
||||
tabs: css`
|
||||
${prefix}-tab + ${prefix}-tab {
|
||||
margin: ${marginHoriz}px 4px !important;
|
||||
padding: 0 12px !important;
|
||||
}
|
||||
|
||||
${prefix}-tab {
|
||||
color: ${token.colorTextSecondary};
|
||||
transition: background-color 100ms ease-out;
|
||||
|
||||
&:first-child {
|
||||
margin: ${marginHoriz}px 4px ${marginHoriz}px 0;
|
||||
padding: ${paddingVertical}px 12px !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: ${token.colorText} !important;
|
||||
background: ${token.colorFillTertiary};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
}
|
||||
}
|
||||
|
||||
${prefix}-nav {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
${responsive.mobile} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
|
||||
link: css`
|
||||
${stylish.resetLinkColor}
|
||||
`,
|
||||
};
|
||||
});
|
||||
const Navbar: FC = () => {
|
||||
const { styles } = useStyles();
|
||||
const nav = useSiteStore((s) => s.navData, shallow);
|
||||
const location = useLocation();
|
||||
|
||||
const activeKey = location.pathname.replace('/en-US/', '').replace('/', '').split('/').shift();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs
|
||||
onChange={(key) => {
|
||||
history.push(
|
||||
nav.find((item) => item.link.replace('/en-US/', '').replace('/', '') === key)?.link ||
|
||||
'/',
|
||||
);
|
||||
setTimeout(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, 10);
|
||||
}}
|
||||
activeKey={activeKey}
|
||||
className={styles.tabs}
|
||||
items={nav.map((item) => ({
|
||||
label: item.title,
|
||||
link: item.link,
|
||||
key: item.link.replace('/en-US/', '').replace('/', ''),
|
||||
}))}
|
||||
/>
|
||||
|
||||
<NavbarExtra />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Navbar);
|
52
.dumi/theme/slots/SearchBar/Input.tsx
Normal file
52
.dumi/theme/slots/SearchBar/Input.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import { useIntl } from 'dumi';
|
||||
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||
import { useStyles } from './style';
|
||||
|
||||
type NativeInputProps = React.DetailedHTMLProps<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
HTMLInputElement
|
||||
>;
|
||||
|
||||
type InputProps = {
|
||||
onChange: (keywords: string) => void;
|
||||
className: string;
|
||||
} & Pick<NativeInputProps, 'onFocus' | 'onBlur'>;
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
|
||||
const { styles } = useStyles();
|
||||
const intl = useIntl();
|
||||
|
||||
const imeWaiting = useRef(false);
|
||||
const nativeInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useImperativeHandle(ref, () => nativeInputRef.current!);
|
||||
|
||||
return (
|
||||
<input
|
||||
className={props.className}
|
||||
onCompositionStart={() => (imeWaiting.current = true)}
|
||||
onCompositionEnd={(ev) => {
|
||||
imeWaiting.current = false;
|
||||
// special case: press Enter open IME panel will not trigger onChange
|
||||
props.onChange(ev.currentTarget.value);
|
||||
}}
|
||||
onFocus={props.onFocus}
|
||||
onBlur={props.onBlur}
|
||||
onKeyDown={(ev) => {
|
||||
if (['ArrowDown', 'ArrowUp'].includes(ev.key)) ev.preventDefault();
|
||||
// esc to blur input
|
||||
if (ev.key === 'Escape' && !imeWaiting.current) ev.currentTarget.blur();
|
||||
}}
|
||||
onChange={(ev) => {
|
||||
// wait for onCompositionEnd event be triggered
|
||||
setTimeout(() => {
|
||||
if (!imeWaiting.current) {
|
||||
props.onChange(ev.target.value);
|
||||
}
|
||||
}, 1);
|
||||
}}
|
||||
placeholder={intl.formatMessage({ id: 'header.search.placeholder' })}
|
||||
ref={nativeInputRef}
|
||||
/>
|
||||
);
|
||||
});
|
34
.dumi/theme/slots/SearchBar/Mask.style.ts
Normal file
34
.dumi/theme/slots/SearchBar/Mask.style.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ token, css }) => {
|
||||
return {
|
||||
modal: css`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
z-index: 1000;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`,
|
||||
mask: css`
|
||||
background-color: ${token.colorBgMask};
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`,
|
||||
content: css`
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
background-color: ${token.colorBgElevated};
|
||||
width: 500px;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: inset 1px 1px 0 0 hsla(0deg, 0%, 100%, 50%), 0 3px 8px 0 #555a64;
|
||||
border-radius: 8px;
|
||||
max-height: calc(100% - 120px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`,
|
||||
};
|
||||
});
|
28
.dumi/theme/slots/SearchBar/Mask.tsx
Normal file
28
.dumi/theme/slots/SearchBar/Mask.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { useEffect, type FC, type ReactNode } from 'react';
|
||||
import { useStyles } from './Mask.style';
|
||||
|
||||
type MaskProps = {
|
||||
visible: boolean;
|
||||
children: ReactNode;
|
||||
onMaskClick?: () => void;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
export const Mask: FC<MaskProps> = (props) => {
|
||||
const { styles } = useStyles();
|
||||
useEffect(() => {
|
||||
if (props.visible) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
props.onClose?.();
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
return props.visible ? (
|
||||
<div className={styles.modal}>
|
||||
<div className={styles.mask} onClick={props.onMaskClick} />
|
||||
<div className={styles.content}>{props.children}</div>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
124
.dumi/theme/slots/SearchBar/index.tsx
Normal file
124
.dumi/theme/slots/SearchBar/index.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { useSiteSearch } from 'dumi';
|
||||
import SearchResult from 'dumi/theme-default/slots/SearchResult';
|
||||
import { useEffect, useRef, useState, type FC } from 'react';
|
||||
|
||||
import { Input } from './Input';
|
||||
import { Mask } from './Mask';
|
||||
import { useStyles } from './style';
|
||||
|
||||
export { Input as SearchInput } from './Input';
|
||||
export { Mask as SearchMask } from './Mask';
|
||||
|
||||
const isAppleDevice = /(mac|iphone|ipod|ipad)/i.test(
|
||||
typeof navigator !== 'undefined' ? navigator?.platform : '',
|
||||
);
|
||||
|
||||
const SearchBar: FC = () => {
|
||||
const { styles } = useStyles();
|
||||
const [focusing, setFocusing] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const modalInputRef = useRef<HTMLInputElement>(null);
|
||||
const [symbol, setSymbol] = useState('⌘');
|
||||
const { keywords, setKeywords, result, loading } = useSiteSearch();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// why put useEffect?
|
||||
// to avoid Text content mismatch between server & client in ssr
|
||||
if (!isAppleDevice) {
|
||||
setSymbol('Ctrl');
|
||||
}
|
||||
|
||||
const handler = (ev: KeyboardEvent) => {
|
||||
if (((isAppleDevice ? ev.metaKey : ev.ctrlKey) && ev.key === 'k') || ev.key === '/') {
|
||||
ev.preventDefault();
|
||||
|
||||
if (inputRef.current) {
|
||||
const { top, bottom, left, right } = inputRef.current.getBoundingClientRect();
|
||||
const isInViewport =
|
||||
top >= 0 && left >= 0 && bottom <= window.innerHeight && right <= window.innerWidth;
|
||||
|
||||
if (isInViewport) {
|
||||
inputRef.current.focus();
|
||||
} else {
|
||||
setKeywords('');
|
||||
setModalVisible(true);
|
||||
setTimeout(() => {
|
||||
modalInputRef.current?.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.key === 'Escape') {
|
||||
ev.preventDefault();
|
||||
setModalVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handler);
|
||||
|
||||
return () => document.removeEventListener('keydown', handler);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<SearchOutlined className={styles.svg} />
|
||||
<Input
|
||||
onFocus={() => setFocusing(true)}
|
||||
onBlur={() => {
|
||||
// wait for item click
|
||||
setTimeout(() => {
|
||||
setFocusing(false);
|
||||
}, 1);
|
||||
}}
|
||||
onChange={(keywords) => setKeywords(keywords)}
|
||||
ref={inputRef}
|
||||
className={styles.input}
|
||||
/>
|
||||
<span className={styles.shortcut}>{symbol} K</span>
|
||||
{keywords.trim() && focusing && (result.length || !loading) && !modalVisible && (
|
||||
<div className={styles.popover}>
|
||||
<section>
|
||||
<SearchResult data={result} loading={loading} />
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Mask
|
||||
visible={modalVisible}
|
||||
onMaskClick={() => {
|
||||
setModalVisible(false);
|
||||
}}
|
||||
onClose={() => setKeywords('')}
|
||||
>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<SearchOutlined className={styles.svg} />
|
||||
<Input
|
||||
className={styles.input}
|
||||
onFocus={() => setFocusing(true)}
|
||||
onBlur={() => {
|
||||
// wait for item click
|
||||
setTimeout(() => {
|
||||
setFocusing(false);
|
||||
}, 1);
|
||||
}}
|
||||
onChange={(keywords) => setKeywords(keywords)}
|
||||
ref={modalInputRef}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SearchResult
|
||||
data={result}
|
||||
loading={loading}
|
||||
onItemSelect={() => {
|
||||
setModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
</Mask>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchBar;
|
112
.dumi/theme/slots/SearchBar/style.ts
Normal file
112
.dumi/theme/slots/SearchBar/style.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ token, responsive, css, cx }) => {
|
||||
return {
|
||||
container: css`
|
||||
position: relative;
|
||||
|
||||
// TODO: support search for mobile devices
|
||||
${responsive.mobile} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
shortcut: cx(
|
||||
'site-header-shortcut',
|
||||
css`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
inset-inline-end: 11px;
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
color: ${token.colorTextDescription};
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
background-color: ${token.colorFillSecondary};
|
||||
border-radius: 11px;
|
||||
border: 1px solid ${token.colorBorderSecondary};
|
||||
transform: translateY(-50%);
|
||||
transition: all 0.3s;
|
||||
pointer-events: none;
|
||||
|
||||
${responsive.mobile} {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
),
|
||||
popover: css`
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
inset-inline-end: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 540px;
|
||||
max-height: 460px;
|
||||
margin-top: 18px;
|
||||
background-color: ${token.colorBgElevated};
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 20%);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
inset-inline-end: 100px;
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 8px solid transparent;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
> section {
|
||||
flex: 1;
|
||||
min-height: 60px;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
border-radius: inherit;
|
||||
}
|
||||
`,
|
||||
|
||||
svg: cx(
|
||||
css`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: 1px;
|
||||
inset-inline-start: 16px;
|
||||
width: 16px;
|
||||
color: ${token.colorTextPlaceholder};
|
||||
transform: translateY(-50%);
|
||||
`,
|
||||
),
|
||||
input: css`
|
||||
width: 280px;
|
||||
height: ${token.controlHeightLG}px;
|
||||
padding: 0;
|
||||
padding-inline-start: 40px;
|
||||
padding-inline-end: 12px;
|
||||
color: ${token.colorTextSecondary};
|
||||
font-size: 14px;
|
||||
border: 1px solid ${token.colorBorder};
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
transition: all 0.3s;
|
||||
background-color: transparent;
|
||||
|
||||
&:focus {
|
||||
border-color: ${token.colorBorderSecondary};
|
||||
background: ${token.colorBgElevated};
|
||||
|
||||
~ .site-header-shortcut {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${token.colorTextPlaceholder};
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
37
.dumi/theme/slots/Sidebar/index.tsx
Normal file
37
.dumi/theme/slots/Sidebar/index.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { NavLink, useLocation } from 'dumi';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, type FC } from 'react';
|
||||
|
||||
import { useSiteStore } from '../../store/useSiteStore';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Sidebar: FC = () => {
|
||||
const sidebar = useSiteStore((s) => s.sidebar, isEqual);
|
||||
const { styles } = useStyles();
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
if (location.pathname.includes('changelog')) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
sidebar && (
|
||||
<div className={styles.sidebar}>
|
||||
{sidebar.map((item, i) => (
|
||||
<dl key={String(i)}>
|
||||
{item.title && <dt>{item.title}</dt>}
|
||||
{item.children.map((child) => (
|
||||
<dd key={child.link}>
|
||||
<NavLink to={child.link} title={child.title} end>
|
||||
{child.title}
|
||||
</NavLink>
|
||||
</dd>
|
||||
))}
|
||||
</dl>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Sidebar);
|
75
.dumi/theme/slots/Sidebar/style.ts
Normal file
75
.dumi/theme/slots/Sidebar/style.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, token }) => ({
|
||||
sidebar: css`
|
||||
grid-area: sidebar;
|
||||
overflow: auto;
|
||||
position: sticky;
|
||||
top: ${token.headerHeight}px;
|
||||
max-height: calc(100vh - ${token.headerHeight}px);
|
||||
box-sizing: border-box;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 52px;
|
||||
padding-inline: 16px;
|
||||
border-right: 1px solid ${token.colorSplit};
|
||||
|
||||
> dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
|
||||
> dt {
|
||||
margin: 8px 0;
|
||||
color: ${token.colorText};
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
> dd {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
|
||||
> a {
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
display: block;
|
||||
font-size: ${token.fontSize}px;
|
||||
line-height: ${token.lineHeight};
|
||||
text-decoration: none;
|
||||
transition: all 0.1s;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
color: ${token.colorTextSecondary};
|
||||
|
||||
&:hover {
|
||||
color: ${token.colorText};
|
||||
background: ${token.colorFillTertiary};
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: ${token.colorPrimaryText};
|
||||
background: ${token.colorPrimaryBg};
|
||||
|
||||
&:hover {
|
||||
color: ${token.colorPrimaryTextHover};
|
||||
background: ${token.colorPrimaryBgHover};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// divider line & gap
|
||||
+ dl {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px dashed ${token.colorBorder};
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
102
.dumi/theme/slots/Toc/index.tsx
Normal file
102
.dumi/theme/slots/Toc/index.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import { ArrowDownOutlined, MenuOutlined } from '@ant-design/icons';
|
||||
import { Anchor, Collapse, ConfigProvider } from 'antd';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { useLocation, useRouteMeta } from 'dumi';
|
||||
import { useMemo, useState, type FC } from 'react';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
type AnchorItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
children?: AnchorItem[];
|
||||
};
|
||||
|
||||
const Toc: FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
const [activeLink, setActiveLink] = useState<string>();
|
||||
const meta = useRouteMeta();
|
||||
const { styles } = useStyles();
|
||||
const { mobile } = useResponsive();
|
||||
const anchorItems = useMemo(
|
||||
() =>
|
||||
meta.toc.reduce<AnchorItem[]>((result, item) => {
|
||||
if (item.depth === 2) {
|
||||
result.push({ ...item });
|
||||
} else if (item.depth === 3) {
|
||||
const parent = result[result.length - 1];
|
||||
if (parent) {
|
||||
parent.children = parent.children || [];
|
||||
parent.children.push({ ...item });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}, []),
|
||||
[meta.toc],
|
||||
);
|
||||
|
||||
const activeAnchor = meta.toc.find((item) => item.id === activeLink);
|
||||
|
||||
return (
|
||||
(anchorItems?.length === 0 ? null : mobile ? (
|
||||
<ConfigProvider theme={{ token: { fontSize: 12, sizeStep: 3 } }}>
|
||||
<div className={styles.mobileCtn}>
|
||||
<Collapse
|
||||
bordered={false}
|
||||
ghost
|
||||
expandIconPosition={'end'}
|
||||
expandIcon={({ isActive }) => (isActive ? <ArrowDownOutlined /> : <MenuOutlined />)}
|
||||
className={styles.expand}
|
||||
>
|
||||
<Collapse.Panel
|
||||
forceRender
|
||||
key={'toc'}
|
||||
header={!activeAnchor ? '目录' : activeAnchor.title}
|
||||
>
|
||||
<ConfigProvider theme={{ token: { fontSize: 14, sizeStep: 4 } }}>
|
||||
<Anchor
|
||||
onChange={(currentLink) => {
|
||||
setActiveLink(currentLink.replace('#', ''));
|
||||
}}
|
||||
items={anchorItems.map((item) => ({
|
||||
href: `#${item.id}`,
|
||||
title: location.pathname.includes('changelog')
|
||||
? item.title.replace('@ant-design/pro-components', '')
|
||||
: item.title,
|
||||
key: item.id,
|
||||
children: item.children?.map((child) => ({
|
||||
href: `#${child.id}`,
|
||||
title: child?.title,
|
||||
key: child.id,
|
||||
})),
|
||||
}))}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
) : (
|
||||
<div className={styles.container}>
|
||||
<h4>目录</h4>
|
||||
<Anchor
|
||||
items={anchorItems.map((item) => ({
|
||||
href: `#${item.id}`,
|
||||
title: location.pathname.includes('changelog')
|
||||
? item.title.replace('@ant-design/pro-components@', 'v')
|
||||
: item.title,
|
||||
key: item.id,
|
||||
children: item.children?.map((child) => ({
|
||||
href: `#${child.id}`,
|
||||
title: child?.title,
|
||||
key: child.id,
|
||||
})),
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)) || null
|
||||
);
|
||||
};
|
||||
|
||||
export default Toc;
|
58
.dumi/theme/slots/Toc/style.ts
Normal file
58
.dumi/theme/slots/Toc/style.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import chroma from 'chroma-js';
|
||||
|
||||
export const useStyles = createStyles(({ token, prefixCls, responsive, css }) => {
|
||||
const fixHeight = 36;
|
||||
return {
|
||||
container: css`
|
||||
grid-area: toc;
|
||||
position: sticky;
|
||||
top: 100px;
|
||||
width: ${token.tocWidth}px;
|
||||
margin-inline-end: 24px;
|
||||
max-height: 80vh;
|
||||
overflow: auto;
|
||||
margin-top: 48px;
|
||||
|
||||
${responsive.mobile} {
|
||||
z-index: 300;
|
||||
top: ${token.headerHeight + 1}px;
|
||||
margin-top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
> h4 {
|
||||
margin: 0 0 8px;
|
||||
color: ${token.colorTextDescription};
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
`,
|
||||
mobileCtn: css`
|
||||
position: sticky;
|
||||
top: ${token.headerHeight + 1}px;
|
||||
|
||||
height: ${fixHeight}px;
|
||||
width: 100%;
|
||||
z-index: 200;
|
||||
background: transparent;
|
||||
background: ${chroma(token.colorBgContainer).alpha(0.8).css()};
|
||||
`,
|
||||
expand: css`
|
||||
backdrop-filter: blur(6px);
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid ${token.colorSplit};
|
||||
|
||||
box-shadow: ${token.boxShadowSecondary};
|
||||
width: 100%;
|
||||
z-index: 201;
|
||||
|
||||
.${prefixCls}-collapse-header {
|
||||
padding: 8px 16px !important;
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
101
.dumi/theme/store/useSiteStore.ts
Normal file
101
.dumi/theme/store/useSiteStore.ts
Normal file
@ -0,0 +1,101 @@
|
||||
//@ts-ignore
|
||||
import { AtomAsset } from 'dumi-assets-types';
|
||||
import {
|
||||
ILocalesConfig,
|
||||
INavItem,
|
||||
IPreviewerProps,
|
||||
IRouteMeta,
|
||||
ISidebarGroup,
|
||||
IThemeConfig,
|
||||
} from 'dumi/dist/client/theme-api/types';
|
||||
import { PICKED_PKG_FIELDS } from 'dumi/dist/constants';
|
||||
import type { Location } from 'history';
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
|
||||
export type NavData = (INavItem & { children?: INavItem[] | undefined })[];
|
||||
|
||||
export interface SiteState {
|
||||
pkg: Partial<Record<keyof typeof PICKED_PKG_FIELDS, any>>;
|
||||
entryExports: Record<string, any>;
|
||||
demos: Record<
|
||||
string,
|
||||
{
|
||||
component: ComponentType;
|
||||
asset: IPreviewerProps['asset'];
|
||||
routeId: string;
|
||||
}
|
||||
>;
|
||||
|
||||
components: Record<string, AtomAsset>;
|
||||
locales: ILocalesConfig;
|
||||
themeConfig: IThemeConfig;
|
||||
loading: boolean;
|
||||
setLoading: (status: boolean) => void;
|
||||
}
|
||||
|
||||
interface Store {
|
||||
siteData: SiteState;
|
||||
sidebar: ISidebarGroup[];
|
||||
routeMeta: IRouteMeta;
|
||||
navData: NavData;
|
||||
location: Location;
|
||||
}
|
||||
|
||||
const initialState: Store = {
|
||||
siteData: {
|
||||
// @ts-ignore
|
||||
setLoading: undefined,
|
||||
loading: true,
|
||||
pkg: {},
|
||||
components: {},
|
||||
demos: {},
|
||||
locales: [],
|
||||
entryExports: {},
|
||||
// @ts-ignore
|
||||
themeConfig: {},
|
||||
},
|
||||
sidebar: [],
|
||||
navData: [],
|
||||
|
||||
location: {
|
||||
pathname: '',
|
||||
state: '',
|
||||
search: '',
|
||||
hash: '',
|
||||
key: '',
|
||||
},
|
||||
|
||||
routeMeta: {
|
||||
toc: [],
|
||||
texts: [],
|
||||
tabs: undefined,
|
||||
// @ts-ignore
|
||||
frontmatter: {},
|
||||
},
|
||||
};
|
||||
|
||||
export const useSiteStore = create<Store>()(
|
||||
devtools(
|
||||
() => ({
|
||||
...initialState,
|
||||
}),
|
||||
{ name: 'dumi-site-store' },
|
||||
),
|
||||
);
|
||||
|
||||
export const isApiPageSel = (s: Store) => s.location.pathname.startsWith('/api');
|
||||
export const isHeroPageSel = (s: Store) => !!s.routeMeta.frontmatter.hero;
|
||||
export const featuresSel = (s: Store) => s.routeMeta.frontmatter.features;
|
||||
|
||||
export const activePathSel = (s: Store) => {
|
||||
if (s.location.pathname === '/') return '/';
|
||||
|
||||
const item = s.navData
|
||||
.filter((i) => i.link !== '/')
|
||||
.find((i) => s.location.pathname.startsWith(i.activePath!));
|
||||
|
||||
return item?.activePath || '';
|
||||
};
|
15
.dumi/theme/store/useThemeStore.ts
Normal file
15
.dumi/theme/store/useThemeStore.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { ThemeMode } from 'antd-style';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
interface Store {
|
||||
themeMode: ThemeMode;
|
||||
}
|
||||
export const useThemeStore = create<Store>()(
|
||||
persist(
|
||||
() => ({
|
||||
themeMode: 'auto' as any,
|
||||
}),
|
||||
{ name: 'ANTD_STYLE_DOC_STORE' },
|
||||
),
|
||||
);
|
58
.dumi/theme/styles/algorithms/dark.ts
Normal file
58
.dumi/theme/styles/algorithms/dark.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { theme } from 'antd';
|
||||
import { MappingAlgorithm } from 'antd-style';
|
||||
|
||||
// 这一版的暗色浅色系列已经差不多了
|
||||
const primaryColorsA = [
|
||||
'#001736',
|
||||
'#002653',
|
||||
'#003572',
|
||||
'#004593',
|
||||
'#0055b6',
|
||||
'#0066dc',
|
||||
'#1677ff',
|
||||
'#0f77f8',
|
||||
'#0777f0',
|
||||
'#0176e9',
|
||||
'#0076e1',
|
||||
];
|
||||
|
||||
const primaryColors = [
|
||||
'#001736',
|
||||
'#002653',
|
||||
'#003572',
|
||||
'#004593',
|
||||
'#0055b6',
|
||||
'#0066dc',
|
||||
'#1677ff',
|
||||
'#257fff',
|
||||
'#3187ff',
|
||||
'#3c8fff',
|
||||
'#4796ff',
|
||||
];
|
||||
|
||||
export const darkAlgorithm: MappingAlgorithm = (seedToken, mapToken) => {
|
||||
const mergeToken = theme.darkAlgorithm(seedToken, mapToken);
|
||||
|
||||
return {
|
||||
...mergeToken,
|
||||
|
||||
colorBgLayout: 'hsl(218,22%,7%)', // Layout 颜色
|
||||
colorBgContainer: 'hsl(216,18%,11%)', // 容器颜色
|
||||
colorBgElevated: 'hsl(216,13%,15%)', // 悬浮类面板颜色
|
||||
|
||||
colorPrimaryBg: primaryColors[1],
|
||||
colorPrimaryBgHover: primaryColors[2],
|
||||
colorPrimaryBorder: primaryColors[3],
|
||||
colorPrimaryBorderHover: primaryColors[4],
|
||||
colorPrimaryHover: primaryColors[5],
|
||||
colorPrimary: primaryColors[6],
|
||||
colorPrimaryActive: primaryColors[7],
|
||||
colorPrimaryTextHover: primaryColors[8],
|
||||
colorPrimaryText: primaryColors[9],
|
||||
colorPrimaryTextActive: primaryColors[10],
|
||||
|
||||
colorLink: primaryColors[6],
|
||||
colorLinkHover: primaryColors[5],
|
||||
colorLinkActive: primaryColors[7],
|
||||
};
|
||||
};
|
2
.dumi/theme/styles/algorithms/index.ts
Normal file
2
.dumi/theme/styles/algorithms/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { darkAlgorithm } from './dark';
|
||||
export { lightAlgorithm } from './light';
|
31
.dumi/theme/styles/algorithms/light.ts
Normal file
31
.dumi/theme/styles/algorithms/light.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { MappingAlgorithm } from 'antd-style';
|
||||
|
||||
export const lightAlgorithm: MappingAlgorithm = (_, mapToken) => {
|
||||
const primaryColors = [
|
||||
'#ffffff',
|
||||
'#d9ebfb',
|
||||
'#b4d6f7',
|
||||
'#90c0f5',
|
||||
'#6caaf5',
|
||||
'#4792f8',
|
||||
'#1677ff',
|
||||
'#0568e0',
|
||||
'#005ac0',
|
||||
'#004ca1',
|
||||
'#003e84',
|
||||
];
|
||||
return {
|
||||
...mapToken,
|
||||
colorBgLayout: 'hsl(220,23%,97%)',
|
||||
colorPrimaryBg: primaryColors[1],
|
||||
colorPrimaryBgHover: primaryColors[2],
|
||||
colorPrimaryBorder: primaryColors[3],
|
||||
colorPrimaryBorderHover: primaryColors[4],
|
||||
colorPrimaryHover: primaryColors[5],
|
||||
colorPrimary: primaryColors[6],
|
||||
colorPrimaryActive: primaryColors[7],
|
||||
colorPrimaryTextHover: primaryColors[8],
|
||||
colorPrimaryText: primaryColors[9],
|
||||
colorPrimaryTextActive: primaryColors[10],
|
||||
} as ReturnType<MappingAlgorithm>;
|
||||
};
|
21
.dumi/theme/styles/antdTheme.ts
Normal file
21
.dumi/theme/styles/antdTheme.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { GetAntdTheme, ThemeConfig } from 'antd-style';
|
||||
import { darkAlgorithm, lightAlgorithm } from './algorithms';
|
||||
|
||||
export const getAntdTheme: GetAntdTheme = (appearance) => {
|
||||
const theme: ThemeConfig = {
|
||||
token: {
|
||||
colorTextBase: '#3d3e40',
|
||||
},
|
||||
algorithm: lightAlgorithm,
|
||||
};
|
||||
|
||||
if (appearance === 'dark') {
|
||||
theme.token = {
|
||||
colorTextBase: '#c7ddff',
|
||||
};
|
||||
|
||||
theme.algorithm = darkAlgorithm;
|
||||
}
|
||||
|
||||
return theme;
|
||||
};
|
108
.dumi/theme/styles/customStylish.ts
Normal file
108
.dumi/theme/styles/customStylish.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import { GetCustomStylish } from 'antd-style';
|
||||
import chroma from 'chroma-js';
|
||||
|
||||
declare module 'antd-style' {
|
||||
interface CustomStylish extends SiteStylish {}
|
||||
}
|
||||
|
||||
export interface SiteStylish {
|
||||
clickableText: string;
|
||||
resetLinkColor: string;
|
||||
|
||||
heroButtonGradient: string;
|
||||
heroGradient: string;
|
||||
heroTextShadow: string;
|
||||
heroGradientFlow: string;
|
||||
heroBlurBall: string;
|
||||
|
||||
iconGradientDefault: string;
|
||||
}
|
||||
|
||||
export const getCustomStylish: GetCustomStylish<SiteStylish> = ({ css, token, isDarkMode }) => {
|
||||
return {
|
||||
clickableText: css`
|
||||
cursor: pointer;
|
||||
color: ${token.colorTextSecondary};
|
||||
|
||||
&:hover {
|
||||
color: ${token.colorText};
|
||||
}
|
||||
`,
|
||||
resetLinkColor: css`
|
||||
color: inherit;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: inherit;
|
||||
}
|
||||
`,
|
||||
|
||||
heroButtonGradient: css`
|
||||
background: linear-gradient(90deg, ${token.gradientColor1} 0%, ${token.gradientColor2} 100%);
|
||||
`,
|
||||
|
||||
heroGradient: css`
|
||||
background-image: ${token.gradientHeroBgG};
|
||||
background-size: 300% 300%;
|
||||
`,
|
||||
|
||||
heroGradientFlow: css`
|
||||
animation: flow 5s ease infinite;
|
||||
|
||||
@keyframes flow {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
}
|
||||
`,
|
||||
heroTextShadow: css`
|
||||
text-shadow: 0 8px 20px ${chroma(token.gradientColor2).alpha(0.2).hex()},
|
||||
0 8px 60px ${chroma(token.gradientColor3).alpha(0.2).hex()},
|
||||
0 8px 80px
|
||||
${chroma(token.cyan)
|
||||
.alpha(isDarkMode ? 0.2 : 0.4)
|
||||
.hex()};
|
||||
`,
|
||||
heroBlurBall: css`
|
||||
filter: blur(69px);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
${token.gradientColor3} 0%,
|
||||
${token.gradientColor1} 30%,
|
||||
${token.red} 70%,
|
||||
${token.cyan} 100%
|
||||
);
|
||||
background-size: 200% 200%;
|
||||
animation: glow 10s ease infinite;
|
||||
|
||||
@keyframes glow {
|
||||
0% {
|
||||
background-position: 0 -100%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 200% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0 -100%;
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
iconGradientDefault: css`
|
||||
radial-gradient(
|
||||
100% 100% at 50% 0,
|
||||
${chroma(token.colorSolid).alpha(0.2).hex()} 0,
|
||||
${chroma(token.colorSolid).alpha(0.1).hex()} 100%
|
||||
)`,
|
||||
};
|
||||
};
|
56
.dumi/theme/styles/customToken.ts
Normal file
56
.dumi/theme/styles/customToken.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import type { GetCustomToken } from 'antd-style';
|
||||
import chroma from 'chroma-js';
|
||||
|
||||
declare module 'antd-style' {
|
||||
interface CustomToken extends SiteToken {}
|
||||
}
|
||||
|
||||
interface SiteToken {
|
||||
headerHeight: number;
|
||||
sidebarWidth: number;
|
||||
tocWidth: number;
|
||||
/**
|
||||
* 文本内容的最大宽度 1152
|
||||
*/
|
||||
contentMaxWidth: number;
|
||||
|
||||
gradientColor1: string;
|
||||
gradientColor2: string;
|
||||
gradientColor3: string;
|
||||
|
||||
gradientHeroBgG: string;
|
||||
gradientIconDefault: string;
|
||||
|
||||
colorSolid: string;
|
||||
}
|
||||
|
||||
export const getCustomToken: GetCustomToken<SiteToken> = ({ isDarkMode, token }) => {
|
||||
const gradientColor1 = token.blue;
|
||||
const gradientColor2 = isDarkMode ? token.pink : token.cyan;
|
||||
const gradientColor3 = token.purple;
|
||||
const colorSolid = isDarkMode ? token.colorWhite : '#000';
|
||||
|
||||
return {
|
||||
headerHeight: 64,
|
||||
sidebarWidth: 240,
|
||||
tocWidth: 176,
|
||||
contentMaxWidth: 1152,
|
||||
|
||||
colorSolid,
|
||||
|
||||
gradientColor1,
|
||||
gradientColor2,
|
||||
gradientColor3,
|
||||
gradientHeroBgG: `radial-gradient(at 80% 20%, ${gradientColor1} 0%, ${gradientColor2} 80%, ${gradientColor3} 130%)`,
|
||||
|
||||
gradientIconDefault: `radial-gradient(
|
||||
100% 100% at 50% 0,
|
||||
${chroma(colorSolid)
|
||||
.alpha(isDarkMode ? 0.2 : 0.2)
|
||||
.hex()} 0,
|
||||
${chroma(colorSolid)
|
||||
.alpha(isDarkMode ? 0.1 : 0.4)
|
||||
.hex()} 100%
|
||||
)`,
|
||||
};
|
||||
};
|
3
.dumi/theme/styles/index.ts
Normal file
3
.dumi/theme/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './antdTheme';
|
||||
export * from './customStylish';
|
||||
export * from './customToken';
|
@ -1,64 +0,0 @@
|
||||
import * as DarkReader from '@umijs/ssr-darkreader';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
export type Action = {
|
||||
toggle: () => void;
|
||||
collectCSS: () => Promise<string>;
|
||||
};
|
||||
|
||||
export type Result = [boolean, Action];
|
||||
|
||||
export function useDarkreader(defaultDarken: boolean = false): [
|
||||
boolean,
|
||||
{
|
||||
toggle: () => void;
|
||||
collectCSS: () => Promise<string>;
|
||||
},
|
||||
] {
|
||||
const {
|
||||
enable: enableDarkMode,
|
||||
disable: disableDarkMode,
|
||||
exportGeneratedCSS: collectCSS,
|
||||
setFetchMethod,
|
||||
} = DarkReader || {};
|
||||
|
||||
const [isDark, setIsDark] = useState(defaultDarken);
|
||||
|
||||
const defaultTheme = {
|
||||
brightness: 100,
|
||||
contrast: 90,
|
||||
sepia: 10,
|
||||
};
|
||||
|
||||
const defaultFixes = {
|
||||
invert: [],
|
||||
css: '',
|
||||
ignoreInlineStyle: ['.react-switch-handle'],
|
||||
ignoreImageAnalysis: [],
|
||||
disableStyleSheetsProxy: true,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
if (typeof window.matchMedia === 'undefined') return;
|
||||
if (!DarkReader) {
|
||||
return () => null;
|
||||
}
|
||||
setFetchMethod(fetch);
|
||||
|
||||
isDark ? enableDarkMode(defaultTheme, defaultFixes) : disableDarkMode();
|
||||
|
||||
// unmount
|
||||
return () => {
|
||||
disableDarkMode();
|
||||
};
|
||||
}, [isDark]);
|
||||
|
||||
const action = useMemo(() => {
|
||||
const toggle = () => setIsDark((prevState) => !prevState);
|
||||
|
||||
return { toggle, collectCSS };
|
||||
}, [isDark]);
|
||||
|
||||
return [isDark, action];
|
||||
}
|
406
.dumirc.ts
Normal file
406
.dumirc.ts
Normal file
@ -0,0 +1,406 @@
|
||||
import chalk from 'chalk';
|
||||
import { readdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { defineConfig } from 'dumi';
|
||||
const theme = require('@ant-design/antd-theme-variable');
|
||||
|
||||
const headPkgList: string[] = [];
|
||||
// utils must build before core
|
||||
// runtime must build before renderer-react
|
||||
const pkgList = readdirSync(join(__dirname, 'packages')).filter(
|
||||
(pkg) => pkg.charAt(0) !== '.' && !headPkgList.includes(pkg),
|
||||
);
|
||||
|
||||
const alias = pkgList.reduce((pre, pkg) => {
|
||||
pre[`@ant-design/pro-${pkg}`] = join(__dirname, 'packages', pkg, 'src');
|
||||
return {
|
||||
...pre,
|
||||
};
|
||||
}, {});
|
||||
|
||||
console.log(`🌼 alias list \n${chalk.blue(Object.keys(alias).join('\n'))}`);
|
||||
|
||||
const tailPkgList = pkgList.map((path) => `packages/${path}/src`);
|
||||
|
||||
export default defineConfig({
|
||||
sitemap: { hostname: 'https://procomponents.ant.design' },
|
||||
metas: [
|
||||
{
|
||||
property: 'og:site_name',
|
||||
content: 'ProComponents',
|
||||
},
|
||||
{
|
||||
'data-rh': 'keywords',
|
||||
property: 'og:image',
|
||||
content: 'https://procomponents.ant.design/icon.png',
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content: '🏆 让中后台开发更简单',
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
content: '中后台,admin,Ant Design,ant design,Table,react,alibaba',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
content: '🏆 让中后台开发更简单 包含 table form 等多个组件。',
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-capable',
|
||||
content: 'yes',
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-status-bar-style',
|
||||
content: 'black-translucent',
|
||||
},
|
||||
{
|
||||
name: 'theme-color',
|
||||
content: '#1890ff',
|
||||
},
|
||||
{
|
||||
name: 'google-site-verification',
|
||||
content: '9LDp--DeEC-xOggsHl_t1MlR_1_2O972JpSUu8NZKMU',
|
||||
},
|
||||
],
|
||||
|
||||
styles: [
|
||||
`
|
||||
.dumi-default-sidebar {
|
||||
min-width: 260px;
|
||||
}
|
||||
.dumi-default-previewer-demo {
|
||||
min-height: 500px;
|
||||
max-height: 500px;
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
.dumi-default-previewer-demo > iframe {
|
||||
height: 100%!important;
|
||||
flex:1;
|
||||
}
|
||||
.dumi-default-header:not([data-static]){
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.dumi-default-header-left {
|
||||
min-width: 230px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
`,
|
||||
],
|
||||
alias,
|
||||
resolve: {
|
||||
docDirs: ['docs', ...tailPkgList],
|
||||
},
|
||||
locales: [
|
||||
{ id: 'zh-CN', name: '中文' },
|
||||
{ id: 'en-US', name: 'English' },
|
||||
],
|
||||
...(process.env.NODE_ENV === 'development' ? undefined : { ssr: {} }),
|
||||
themeConfig: {
|
||||
name: 'ProComponents',
|
||||
logo: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
|
||||
nav: {
|
||||
'zh-CN': [
|
||||
{ title: '文档', link: '/docs' },
|
||||
{ title: '组件', link: '/components' },
|
||||
{ title: 'Changelog', link: '/changelog' },
|
||||
{ title: 'Playground', link: '/playground' },
|
||||
],
|
||||
'en-US': [
|
||||
{ title: 'Docs', link: '/en-US/docs' },
|
||||
{ title: 'Components', link: '/en-US/components' },
|
||||
{ title: 'Changelog', link: '/en-US/changelog' },
|
||||
{ title: 'Playground', link: '/en-US/playground' },
|
||||
],
|
||||
},
|
||||
sidebar: {
|
||||
'/en-US/components': [
|
||||
{
|
||||
title: 'Architecture Design',
|
||||
children: [
|
||||
{
|
||||
title: 'Component Design',
|
||||
link: '/en-US/components',
|
||||
},
|
||||
{
|
||||
title: 'General Schema',
|
||||
link: '/en-US/components/schema',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Layout',
|
||||
children: [
|
||||
{
|
||||
title: 'ProLayout',
|
||||
link: '/en-US/components/layout',
|
||||
},
|
||||
{
|
||||
title: 'PageContainer',
|
||||
link: '/en-US/components/page-container',
|
||||
},
|
||||
{
|
||||
title: 'ProCard',
|
||||
link: '/en-US/components/card',
|
||||
},
|
||||
{
|
||||
title: 'WaterMark',
|
||||
link: '/en-US/components/water-mark',
|
||||
},
|
||||
{
|
||||
title: 'StatisticCard',
|
||||
link: '/en-US/components/statistic-card',
|
||||
},
|
||||
{
|
||||
title: 'CheckCard',
|
||||
link: '/en-US/components/check-card',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Data Entry',
|
||||
children: [
|
||||
{
|
||||
title: 'ProForm',
|
||||
link: '/en-US/components/form',
|
||||
},
|
||||
{
|
||||
title: 'ProFormFields',
|
||||
link: '/en-US/components/field-set',
|
||||
},
|
||||
{
|
||||
title: 'ProFormList',
|
||||
link: '/en-US/components/group',
|
||||
},
|
||||
{
|
||||
title: 'ProFormDependency',
|
||||
link: '/en-US/components/dependency',
|
||||
},
|
||||
{
|
||||
title: 'Schema Form',
|
||||
link: '/en-US/components/schema-form',
|
||||
},
|
||||
{
|
||||
title: 'Query/LightFilter',
|
||||
link: '/en-US/components/query-filter',
|
||||
},
|
||||
{
|
||||
title: 'StepsForm',
|
||||
link: '/en-US/components/steps-form',
|
||||
},
|
||||
{
|
||||
title: 'Modal/Drawer Form',
|
||||
link: '/en-US/components/modal-form',
|
||||
},
|
||||
{
|
||||
title: 'LoginForm/LoginPageForm',
|
||||
link: '/en-US/components/login-form',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Data Display',
|
||||
children: [
|
||||
{
|
||||
title: 'ProTable',
|
||||
link: '/en-US/components/table',
|
||||
},
|
||||
{
|
||||
title: 'EditableProTable',
|
||||
link: '/en-US/components/editable-table',
|
||||
},
|
||||
{
|
||||
title: 'DragSortTable',
|
||||
link: '/en-US/components/drag-sort-table',
|
||||
},
|
||||
{
|
||||
title: 'ProList',
|
||||
link: '/en-US/components/list',
|
||||
},
|
||||
{
|
||||
title: 'ProDescriptions',
|
||||
link: '/en-US/components/description',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Universal',
|
||||
children: [
|
||||
{
|
||||
title: 'ProSkeleton',
|
||||
link: '/en-US/components/skeleton',
|
||||
},
|
||||
{
|
||||
title: 'ProField',
|
||||
link: '/en-US/components/field',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'/components': [
|
||||
{
|
||||
title: '架构设计',
|
||||
children: [
|
||||
{
|
||||
title: 'Components - 组件设计',
|
||||
link: 'components',
|
||||
},
|
||||
{
|
||||
title: 'Schema - 通用配置',
|
||||
link: '/components/schema',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '布局',
|
||||
children: [
|
||||
{
|
||||
title: 'ProLayout - 高级布局',
|
||||
link: '/components/layout',
|
||||
},
|
||||
{
|
||||
title: 'PageContainer - 页容器',
|
||||
link: '/components/page-container',
|
||||
},
|
||||
{
|
||||
title: 'ProCard - 高级卡片',
|
||||
link: '/components/card',
|
||||
},
|
||||
{
|
||||
title: 'WaterMark - 水印组件',
|
||||
link: '/components/water-mark',
|
||||
},
|
||||
{
|
||||
title: 'StatisticCard - 指标卡',
|
||||
link: '/components/statistic-card',
|
||||
},
|
||||
{
|
||||
title: 'CheckCard - 多选卡片',
|
||||
link: '/components/check-card',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据录入',
|
||||
children: [
|
||||
{
|
||||
title: 'ProForm - 高级表单',
|
||||
link: '/components/form',
|
||||
},
|
||||
{
|
||||
title: 'ProFormFields - 表单项',
|
||||
link: '/components/field-set',
|
||||
},
|
||||
{
|
||||
title: 'ProFormList - 数据结构化',
|
||||
link: '/components/group',
|
||||
},
|
||||
{
|
||||
title: 'ProFormDependency - 数据联动',
|
||||
link: '/components/dependency',
|
||||
},
|
||||
{
|
||||
title: 'Schema Form - JSON 表单',
|
||||
link: '/components/schema-form',
|
||||
},
|
||||
{
|
||||
title: ' Query/LightFilter - 筛选表单',
|
||||
link: '/components/query-filter',
|
||||
},
|
||||
{
|
||||
title: 'StepsForm - 分步表单',
|
||||
link: '/components/steps-form',
|
||||
},
|
||||
{
|
||||
title: 'Modal/Drawer - 浮层表单',
|
||||
link: '/components/modal-form',
|
||||
},
|
||||
{
|
||||
title: 'LoginForm/Page - 登录表单',
|
||||
link: '/components/login-form',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据展示',
|
||||
children: [
|
||||
{
|
||||
title: 'ProTable - 高级表格',
|
||||
link: '/components/table',
|
||||
},
|
||||
{
|
||||
title: 'EditableProTable - 可编辑表格',
|
||||
link: '/components/editable-table',
|
||||
},
|
||||
{
|
||||
title: ' DragSortTable - 拖动排序表格',
|
||||
link: '/components/drag-sort-table',
|
||||
},
|
||||
{
|
||||
title: 'ProList - 高级列表',
|
||||
link: '/components/list',
|
||||
},
|
||||
{
|
||||
title: 'ProDescriptions - 定义列表',
|
||||
link: '/components/description',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '通用',
|
||||
children: [
|
||||
{
|
||||
title: 'ProSkeleton - 骨架屏',
|
||||
link: '/components/skeleton',
|
||||
},
|
||||
{
|
||||
title: 'ProField - 原子组件',
|
||||
link: '/components/field',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
navs: {
|
||||
'en-US': [
|
||||
null,
|
||||
{
|
||||
title: 'GitHub',
|
||||
path: 'https://github.com/ant-design/pro-components',
|
||||
},
|
||||
],
|
||||
'zh-CN': [
|
||||
null,
|
||||
{
|
||||
title: 'GitHub',
|
||||
path: 'https://github.com/ant-design/pro-components',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
hash: true,
|
||||
theme: {
|
||||
'@s-content-width': '1600px',
|
||||
'@s-site-menu-width': '258px',
|
||||
'@ant-prefix': 'ant',
|
||||
'@root-entry-name': 'variable',
|
||||
...theme,
|
||||
'@primary-color': '#1677FF',
|
||||
'@warning-color': '#faad14',
|
||||
'@heading-color': 'rgba(0, 0, 0, 0.85)',
|
||||
'@text-color': 'rgba(0, 0, 0, 0.65)',
|
||||
'@text-color-secondary': 'rgba(0, 0, 0, 0.45)',
|
||||
'@border-color-base': '#d9d9d9',
|
||||
'@border-color-split': 'rgba(0, 0, 0, 0.06)',
|
||||
'@border-radius-base': '4px',
|
||||
'@card-radius': '6px',
|
||||
'@table-border-radius-base': '6px',
|
||||
'@box-shadow-base':
|
||||
'0 2px 8px -2px rgba(0,0,0,0.05), 0 1px 4px -1px rgba(25,15,15,0.07), 0 0 1px 0 rgba(0,0,0,0.08)',
|
||||
},
|
||||
extraBabelPlugins: ['@emotion'],
|
||||
ignoreMomentLocale: true,
|
||||
});
|
19
.fatherrc.base.ts
Normal file
19
.fatherrc.base.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { defineConfig } from 'father';
|
||||
|
||||
export default defineConfig({
|
||||
// 以下为 esm 配置项启用时的默认值,有自定义需求时才需配置
|
||||
esm: {
|
||||
input: 'src', // 默认编译目录
|
||||
output: 'es',
|
||||
platform: 'browser', // 默认构建为 Browser 环境的产物
|
||||
transformer: 'babel', // 默认使用 babel 以提供更好的兼容性
|
||||
},
|
||||
// 以下为 cjs 配置项启用时的默认值,有自定义需求时才需配置
|
||||
cjs: {
|
||||
extraBabelPlugins: [require.resolve('./scripts/replaceLib')],
|
||||
input: 'src', // 默认编译目录
|
||||
output: 'lib',
|
||||
platform: 'browser', // 默认构建为 Node.js 环境的产物
|
||||
transformer: 'babel', // 默认使用 esbuild 以获得更快的构建速度
|
||||
},
|
||||
});
|
50
.fatherrc.ts
50
.fatherrc.ts
@ -1,50 +0,0 @@
|
||||
import { readdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
// utils must build before core
|
||||
// runtime must build before renderer-react
|
||||
// components dependencies order: form -> table -> list
|
||||
const headPkgs: string[] = [
|
||||
'provider',
|
||||
'utils',
|
||||
'layout',
|
||||
'card',
|
||||
'field',
|
||||
'skeleton',
|
||||
'layout',
|
||||
'form',
|
||||
'table',
|
||||
'list',
|
||||
'descriptions',
|
||||
'components',
|
||||
];
|
||||
const tailPkgs = readdirSync(join(__dirname, 'packages')).filter(
|
||||
(pkg) => pkg.charAt(0) !== '.' && !headPkgs.includes(pkg),
|
||||
);
|
||||
|
||||
const type = process.env.BUILD_TYPE;
|
||||
|
||||
let config = {};
|
||||
|
||||
if (type === 'lib') {
|
||||
config = {
|
||||
cjs: { type: 'babel', lazy: true },
|
||||
esm: false,
|
||||
runtimeHelpers: true,
|
||||
pkgs: [...headPkgs, ...tailPkgs],
|
||||
extraBabelPlugins: [[require('./scripts/replaceLib')]],
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'es') {
|
||||
config = {
|
||||
cjs: false,
|
||||
esm: {
|
||||
type: 'babel',
|
||||
},
|
||||
runtimeHelpers: true,
|
||||
pkgs: [...headPkgs, ...tailPkgs],
|
||||
};
|
||||
}
|
||||
|
||||
export default config;
|
32
.github/workflows/build.yml
vendored
32
.github/workflows/build.yml
vendored
@ -11,30 +11,34 @@ jobs:
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn cache
|
||||
uses: actions/cache@v2
|
||||
id: cache-yarn-cache
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
version: 6.0.2
|
||||
- name: Get pnpm cache directory path
|
||||
id: pnpm-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(pnpm cache dir)"
|
||||
- name: Cache pnpm cache
|
||||
uses: actions/cache@v2
|
||||
id: cache-pnpm-cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
${{ runner.os }}-pnpm-
|
||||
- name: Cache node_modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.node-version }}-nodemodules-
|
||||
- run: yarn --ignore-engines
|
||||
- run: pnpm i
|
||||
if: |
|
||||
steps.cache-yarn-cache.outputs.cache-hit != 'true' ||
|
||||
steps.cache-pnpm-cache.outputs.cache-hit != 'true' ||
|
||||
steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
- run: yarn run build
|
||||
- run: pnpm run build
|
||||
env:
|
||||
PRO_COMPONENTS_CI: CI
|
||||
- run: yarn run lint
|
||||
- run: pnpm run lint
|
||||
|
12
.github/workflows/codeql.yml
vendored
12
.github/workflows/codeql.yml
vendored
@ -1,12 +1,12 @@
|
||||
name: "CodeQL"
|
||||
name: 'CodeQL'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: ['master']
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: ['master']
|
||||
schedule:
|
||||
- cron: "55 1 * * 1"
|
||||
- cron: '55 1 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ javascript ]
|
||||
language: [javascript]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -38,4 +38,4 @@ jobs:
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
category: '/language:${{ matrix.language }}'
|
||||
|
37
.github/workflows/coverage.yml
vendored
37
.github/workflows/coverage.yml
vendored
@ -6,35 +6,42 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn cache
|
||||
uses: actions/cache@v2
|
||||
id: cache-yarn-cache
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
version: 6.0.2
|
||||
|
||||
- name: Get pnpm cache directory path
|
||||
id: pnpm-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(pnpm cache dir)"
|
||||
- name: Cache pnpm cache
|
||||
uses: actions/cache@v2
|
||||
id: cache-pnpm-cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/package.json') }}
|
||||
- name: Cache node_modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
- run: yarn --ignore-engines
|
||||
${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/package.json') }}
|
||||
- run: pnpm i
|
||||
if: |
|
||||
steps.cache-yarn-cache.outputs.cache-hit != 'true' ||
|
||||
steps.cache-pnpm-cache.outputs.cache-hit != 'true' ||
|
||||
steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
- run: yarn run test:coverage
|
||||
- run: pnpm run test:coverage
|
||||
env:
|
||||
CI: true
|
||||
PROGRESS: none
|
||||
|
11
.github/workflows/preview-build.yml
vendored
11
.github/workflows/preview-build.yml
vendored
@ -12,14 +12,17 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2.2.4
|
||||
with:
|
||||
version: 6.0.2
|
||||
- name: build
|
||||
env:
|
||||
TEST_UI: 'preview'
|
||||
run: |
|
||||
yarn
|
||||
yarn run build
|
||||
yarn run dumi
|
||||
pnpm i
|
||||
pnpm run build
|
||||
pnpm run dumi
|
||||
|
||||
- run: |
|
||||
zip -r dist.zip dist
|
||||
|
39
.github/workflows/webpack.yml
vendored
39
.github/workflows/webpack.yml
vendored
@ -1,39 +0,0 @@
|
||||
name: build-lib CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn cache
|
||||
uses: actions/cache@v2
|
||||
id: cache-yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
- name: Cache node_modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||
- run: yarn --ignore-engines
|
||||
if: |
|
||||
steps.cache-yarn-cache.outputs.cache-hit != 'true' ||
|
||||
steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
- run: yarn run tsc
|
||||
- run: yarn run webpack
|
||||
env:
|
||||
PRO_COMPONENTS_CI: CI
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -41,3 +41,5 @@ screenshot
|
||||
.changelogs
|
||||
.changelog.md
|
||||
packages/components/src/version.ts
|
||||
.dumi/tmp
|
||||
server
|
@ -5,3 +5,4 @@
|
||||
**/lib
|
||||
**/es
|
||||
**\__snapshots__\**
|
||||
pnpm-lock.yaml
|
213
.umirc.js
213
.umirc.js
@ -1,213 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import { readdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const theme = require('@ant-design/antd-theme-variable');
|
||||
|
||||
const headPkgList = [];
|
||||
// utils must build before core
|
||||
// runtime must build before renderer-react
|
||||
const pkgList = readdirSync(join(__dirname, 'packages')).filter(
|
||||
(pkg) => pkg.charAt(0) !== '.' && !headPkgList.includes(pkg),
|
||||
);
|
||||
|
||||
const alias = pkgList.reduce((pre, pkg) => {
|
||||
pre[`@ant-design/pro-${pkg}`] = join(__dirname, 'packages', pkg, 'src');
|
||||
return {
|
||||
...pre,
|
||||
};
|
||||
}, {});
|
||||
|
||||
console.log(`🌼 alias list \n${chalk.blue(Object.keys(alias).join('\n'))}`);
|
||||
|
||||
const tailPkgList = pkgList
|
||||
.map((path) => [join('packages', path, 'src')])
|
||||
.reduce((acc, val) => acc.concat(val), []);
|
||||
|
||||
const isDeploy = process.env.SITE_DEPLOY === 'TRUE';
|
||||
|
||||
export default {
|
||||
title: 'ProComponents',
|
||||
mode: 'site',
|
||||
logo: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
|
||||
sitemap: { hostname: 'https://procomponents.ant.design' },
|
||||
metas: [
|
||||
{
|
||||
property: 'og:site_name',
|
||||
content: 'ProComponents',
|
||||
},
|
||||
{
|
||||
'data-rh': 'keywords',
|
||||
property: 'og:image',
|
||||
content: 'https://procomponents.ant.design/icon.png',
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content: '🏆 让中后台开发更简单',
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
content: '中后台,admin,Ant Design,ant design,Table,react,alibaba',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
content: '🏆 让中后台开发更简单 包含 table form 等多个组件。',
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-capable',
|
||||
content: 'yes',
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-status-bar-style',
|
||||
content: 'black-translucent',
|
||||
},
|
||||
{
|
||||
name: 'theme-color',
|
||||
content: '#1890ff',
|
||||
},
|
||||
{
|
||||
name: 'google-site-verification',
|
||||
content: '9LDp--DeEC-xOggsHl_t1MlR_1_2O972JpSUu8NZKMU',
|
||||
},
|
||||
],
|
||||
alias,
|
||||
resolve: {
|
||||
includes: [...tailPkgList, 'docs'],
|
||||
},
|
||||
locales: [
|
||||
['zh-CN', '中文'],
|
||||
['en-US', 'English'],
|
||||
],
|
||||
navs: {
|
||||
'en-US': [
|
||||
null,
|
||||
{
|
||||
title: 'GitHub',
|
||||
path: 'https://github.com/ant-design/pro-components',
|
||||
},
|
||||
],
|
||||
'zh-CN': [
|
||||
null,
|
||||
{
|
||||
title: 'GitHub',
|
||||
path: 'https://github.com/ant-design/pro-components',
|
||||
},
|
||||
],
|
||||
},
|
||||
hash: true,
|
||||
targets: {
|
||||
chrome: 80,
|
||||
firefox: false,
|
||||
safari: false,
|
||||
edge: false,
|
||||
ios: false,
|
||||
},
|
||||
theme: {
|
||||
'@s-site-menu-width': '258px',
|
||||
'@ant-prefix': 'ant',
|
||||
'@root-entry-name': 'variable',
|
||||
...theme,
|
||||
'@primary-color': '#1677FF',
|
||||
'@warning-color': '#faad14',
|
||||
'@heading-color': 'rgba(0, 0, 0, 0.85)',
|
||||
'@text-color': 'rgba(0, 0, 0, 0.65)',
|
||||
'@text-color-secondary': 'rgba(0, 0, 0, 0.45)',
|
||||
'@border-color-base': '#d9d9d9',
|
||||
'@border-color-split': 'rgba(0, 0, 0, 0.06)',
|
||||
'@border-radius-base': '4px',
|
||||
'@card-radius': '6px',
|
||||
'@table-border-radius-base': '6px',
|
||||
'@box-shadow-base':
|
||||
'0 2px 8px -2px rgba(0,0,0,0.05), 0 1px 4px -1px rgba(25,15,15,0.07), 0 0 1px 0 rgba(0,0,0,0.08)',
|
||||
},
|
||||
extraBabelPlugins: ['@emotion'],
|
||||
ignoreMomentLocale: true,
|
||||
menus: {
|
||||
'/components': [
|
||||
{
|
||||
title: '架构设计',
|
||||
children: ['components.md', 'schema.md'],
|
||||
},
|
||||
{
|
||||
title: '布局',
|
||||
children: [
|
||||
'layout',
|
||||
'components/PageContainer/index',
|
||||
'card',
|
||||
'components/WaterMark/index',
|
||||
'components/StatisticCard/index',
|
||||
'components/CheckCard/index',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据录入',
|
||||
children: [
|
||||
'form',
|
||||
'components/FieldSet/index',
|
||||
'components/Group/index',
|
||||
'components/Dependency/index',
|
||||
'components/SchemaForm/index',
|
||||
'components/QueryFilter/index',
|
||||
'components/StepsForm/index',
|
||||
'components/ModalForm/index',
|
||||
'components/LoginForm/index',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据展示',
|
||||
children: [
|
||||
'table',
|
||||
'components/EditableTable/index',
|
||||
'components/DragSortTable/index',
|
||||
'list',
|
||||
'description',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '通用',
|
||||
children: ['skeleton', 'field'],
|
||||
},
|
||||
],
|
||||
'/en-US/components': [
|
||||
{
|
||||
title: 'Architecture Design',
|
||||
children: ['components.en-US.md'],
|
||||
},
|
||||
{
|
||||
title: 'Layout',
|
||||
children: [
|
||||
'layout',
|
||||
'components/PageContainer/index',
|
||||
'components/DragSortTable/index',
|
||||
'list',
|
||||
'card',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Data entry',
|
||||
children: [
|
||||
'form',
|
||||
'components/FieldSet/index',
|
||||
'components/Group/index',
|
||||
'components/Dependency/index',
|
||||
'components/SchemaForm/index',
|
||||
'components/QueryFilter/index',
|
||||
'components/StepsForm/index',
|
||||
'components/ModalForm/index',
|
||||
'components/LoginForm/index',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Data Display',
|
||||
children: ['table', 'components/EditableTable/index', 'list', 'description'],
|
||||
},
|
||||
{
|
||||
title: 'General',
|
||||
children: ['skeleton', 'field'],
|
||||
},
|
||||
],
|
||||
},
|
||||
ssr: isDeploy ? {} : undefined,
|
||||
webpack5: {},
|
||||
exportStatic: {},
|
||||
};
|
Binary file not shown.
@ -1 +0,0 @@
|
||||
nodeLinker: node-modules
|
@ -1,14 +1,4 @@
|
||||
---
|
||||
title: ProComponents - 更新日志
|
||||
nav:
|
||||
title: Changelog
|
||||
path: /changelog
|
||||
order: 99
|
||||
group:
|
||||
path: /
|
||||
---
|
||||
|
||||
## @ant-design/pro-components@2.3.52
|
||||
## @ant-design/pro-components@2.3.52
|
||||
|
||||
`2023-01-10`
|
||||
|
368
docs/changelog.md
Normal file
368
docs/changelog.md
Normal file
@ -0,0 +1,368 @@
|
||||
## @ant-design/pro-components@2.3.52
|
||||
|
||||
`2023-01-10`
|
||||
|
||||
- fix(layout): replace marginTop to marigin-block-start. [5d0f58f](https://github.com/ant-design/pro-components/commit/5d0f58f)
|
||||
- fix(layout): fix ProLayout pageTitleRender not overriding document title (#6492). [#6492](https://github.com/ant-design/pro-components/pull/#6492) [@whyour](https://github.com/whyour)
|
||||
- fix(layout): open sider from right on rtl direction (#6491). [#6491](https://github.com/ant-design/pro-components/pull/#6491) [@3hson](https://github.com/3hson)
|
||||
- fix(layout):修复分组逻辑部分代码 (#6488). [#6488](https://github.com/ant-design/pro-components/pull/#6488) [@chengaway](https://github.com/chengaway)
|
||||
- fix(layout): support click mask close. [06a8920](https://github.com/ant-design/pro-components/commit/06a8920)
|
||||
- fix(layout): 调整菜单收缩时 margin 与展开时 一致 (#6481). [#6481](https://github.com/ant-design/pro-components/pull/#6481) [@hihuangwei](https://github.com/hihuangwei)
|
||||
- fix(form): fix formref no work error in BetaSchemaForm. [7b9bbdd](https://github.com/ant-design/pro-components/commit/7b9bbdd)
|
||||
- fix(Table): 列配置子项 disable 时,无法拖动调整顺序 (#6476). [#6476](https://github.com/ant-design/pro-components/pull/#6476) [@chiaweilee](https://github.com/chiaweilee)
|
||||
- fix(Table): 列配置子项 disable 时,“固定”按钮点击无效 (#6475). [#6475](https://github.com/ant-design/pro-components/pull/#6475) [@chiaweilee](https://github.com/chiaweilee)
|
||||
- fix(Table): 列配置点击“列展示”全选操作时,顺序排列和 disable 状态异常 (#6477). [#6477](https://github.com/ant-design/pro-components/pull/#6477) [@chiaweilee](https://github.com/chiaweilee)
|
||||
- fix(table): fix cancelEditable will reset value error. [40fba50](https://github.com/ant-design/pro-components/commit/40fba50)
|
||||
|
||||
## @ant-design/pro-components@2.3.51
|
||||
|
||||
`2023-01-06`
|
||||
|
||||
- feat(layout): 修复 prolayout 传入自定义 prefixCls 时样式异常 (#6464). [#6464](https://github.com/ant-design/pro-components/pull/#6464) [@Hahet](https://github.com/Hahet)
|
||||
- feat(layout): remove unuse deps. [9cac437](https://github.com/ant-design/pro-components/commit/9cac437)
|
||||
- fix(form): fix ProFormDigitRange label formart style. [6250094](https://github.com/ant-design/pro-components/commit/6250094)
|
||||
|
||||
## @ant-design/pro-components@2.3.50
|
||||
|
||||
`2023-01-04`
|
||||
|
||||
- fix(layout): fix colorBgMenuItemCollapsedElevated no work error. [6aa0d2c](https://github.com/ant-design/pro-components/commit/6aa0d2c)
|
||||
- fix(layout): 调整 margin ,与 Table 组件分页 margin 一致 (#6438). [#6438](https://github.com/ant-design/pro-components/pull/#6438) [@hihuangwei](https://github.com/hihuangwei)
|
||||
- fix(layout): Layout 分组逻辑调整 (#6451). [#6451](https://github.com/ant-design/pro-components/pull/#6451) [@chengaway](https://github.com/chengaway)
|
||||
- fix(layout): 修复 AppsLogoComponents 样式警告 (#6440). [#6440](https://github.com/ant-design/pro-components/pull/#6440) [@leshalv](https://github.com/leshalv)
|
||||
- fix(form): Light FilterightWrapper 当中清除功能的默认行为,使得 LightFilter 当中的 Checkbox 可以正常地被清除功能重置 (#6450). [#6450](https://github.com/ant-design/pro-components/pull/#6450) [@Leo-captain](https://github.com/Leo-captain)
|
||||
- fix(table): fix columnsState.defaultValue no work error. [2b9a283](https://github.com/ant-design/pro-components/commit/2b9a283)
|
||||
|
||||
## @ant-design/pro-components@2.3.48
|
||||
|
||||
`2022-12-29`
|
||||
|
||||
- feat(layout): Layout 新增 跨站点导航 分组形式 (#6431). [#6431](https://github.com/ant-design/pro-components/pull/#6431) [@chengaway](https://github.com/chengaway)
|
||||
- fix(layout): 修复 extraContent 在 md 尺寸不位于 content 右侧的 bug (#6389). [#6389](https://github.com/ant-design/pro-components/pull/#6389) [@edram](https://github.com/edram)
|
||||
- feat(layout): sider token 增加 colorBgElevated (#6384). [#6384](https://github.com/ant-design/pro-components/pull/#6384) [@sushi-su](https://github.com/sushi-su)
|
||||
- fix(form): treeSelect do not default reset to empty string. [387d2d7](https://github.com/ant-design/pro-components/commit/387d2d7)
|
||||
- fix(form): do not remove collapsed dom. [769177f](https://github.com/ant-design/pro-components/commit/769177f)
|
||||
- fix(form): `ProFormList` style does not work (#6398). [#6398](https://github.com/ant-design/pro-components/pull/#6398) [@kungege](https://github.com/kungege)
|
||||
- fix(form): 优化 checkbox 在 hashPriority=high 不生效问题 (#6400). [#6400](https://github.com/ant-design/pro-components/pull/#6400) [@leshalv](https://github.com/leshalv)
|
||||
- fix(list): fix card list style error (#6436). [#6436](https://github.com/ant-design/pro-components/pull/#6436) [@chenshuai2144](https://github.com/chenshuai2144)
|
||||
- fix(table): no render onSearch will SearchProps has onSearch. [a1383e2](https://github.com/ant-design/pro-components/commit/a1383e2)
|
||||
- fix(table): use flex replace Space (#6426). [#6426](https://github.com/ant-design/pro-components/pull/#6426) [@chenshuai2144](https://github.com/chenshuai2144)
|
||||
- fix(table): fix childrenColumnName is null error. [0fb584c](https://github.com/ant-design/pro-components/commit/0fb584c)
|
||||
- fix(table): 轻量筛选替换查询表单,将 padding 调为 8px 更为统一美观 (#6423). [#6423](https://github.com/ant-design/pro-components/pull/#6423) [@hihuangwei](https://github.com/hihuangwei)
|
||||
- fix(card): slove nested table background color was cover by pro-card (#6410). [#6410](https://github.com/ant-design/pro-components/pull/#6410) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
- fix(card): disable & checked style fixed (#6414). [#6414](https://github.com/ant-design/pro-components/pull/#6414) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
|
||||
## @ant-design/pro-components@2.3.47
|
||||
|
||||
`2022-12-13`
|
||||
|
||||
- feat(components): bump swr from 1.x to 2.x (#6367). [#6367](https://github.com/ant-design/pro-components/pull/#6367) [@sushi-su](https://github.com/sushi-su)
|
||||
|
||||
## @ant-design/pro-components@2.3.43
|
||||
|
||||
`2022-12-08`
|
||||
|
||||
- fix(layout): no set default colorPrimary. [f85103e](https://github.com/ant-design/pro-components/commit/f85103e)
|
||||
|
||||
## @ant-design/pro-components@2.3.42
|
||||
|
||||
`2022-12-08`
|
||||
|
||||
- fix(layout): support configprovide darkAlgorithm. [dabf05e](https://github.com/ant-design/pro-components/commit/dabf05e)
|
||||
- fix(form): 修复 ProFormSelect 在 LightFilter 中自定义过滤无效 (#6341). [#6341](https://github.com/ant-design/pro-components/pull/#6341) [@SeaHaiWorld](https://github.com/SeaHaiWorld)
|
||||
- fix(table): fix table extra classname break (#6334). [#6334](https://github.com/ant-design/pro-components/pull/#6334) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
|
||||
## @ant-design/pro-components@2.3.40
|
||||
|
||||
`2022-12-05`
|
||||
|
||||
- fix(layout): 修复 PageContainer 设置为 fixedHeader,内容滚动的时候 Title 抖动问题 (#6330). [#6330](https://github.com/ant-design/pro-components/pull/#6330) [@lhzhou180606](https://github.com/lhzhou180606)
|
||||
- fix(layout): fix bgColor token no work error. [bad0f2f](https://github.com/ant-design/pro-components/commit/bad0f2f)
|
||||
- fix(form): 修复 SearchSelect 组件在设置了 showSearch 和 多选模式下,当搜索并选中后,搜索的文字没有清空的问题 (#6302). [#6302](https://github.com/ant-design/pro-components/pull/#6302) [@catmiao8](https://github.com/catmiao8)
|
||||
- fix(table): 修复 table 使用后端分页时(频繁变化 dataSource 场景)多选功能 selectedRow 缓存丢失 (#6314). [#6314](https://github.com/ant-design/pro-components/pull/#6314) [@YingJiangHui](https://github.com/YingJiangHui)
|
||||
- fix(card): fix card style error. [60ac49c](https://github.com/ant-design/pro-components/commit/60ac49c)
|
||||
|
||||
## @ant-design/pro-components@2.3.37
|
||||
|
||||
`2022-12-01`
|
||||
|
||||
- fix(table): fix table style error. [3fbfddd](https://github.com/ant-design/pro-components/commit/3fbfddd)
|
||||
|
||||
## @ant-design/pro-components@2.3.36
|
||||
|
||||
`2022-11-30`
|
||||
|
||||
- fix(layout): fix collapsedshowtitle style error. [e4dc580](https://github.com/ant-design/pro-components/commit/e4dc580)
|
||||
- fix(layout): 修复 Footer <a> 标签单独使用时带有下划线问题 (#6300). [#6300](https://github.com/ant-design/pro-components/pull/#6300) [@leshalv](https://github.com/leshalv)
|
||||
- feat(layout): support stylish. [60c0b54](https://github.com/ant-design/pro-components/commit/60c0b54)
|
||||
- fix(layout): better theme gen style. [fcbc182](https://github.com/ant-design/pro-components/commit/fcbc182)
|
||||
- fix(layout): update drawer style. [eb7ace8](https://github.com/ant-design/pro-components/commit/eb7ace8)
|
||||
- feat(layout): fix dark style no work error. [3e06527](https://github.com/ant-design/pro-components/commit/3e06527)
|
||||
- fix(form): new antd version use items props. [9f520bf](https://github.com/ant-design/pro-components/commit/9f520bf)
|
||||
- fix(form): formRef repeats the assignment (#6278). [#6278](https://github.com/ant-design/pro-components/pull/#6278) [@caijf](https://github.com/caijf)
|
||||
- fix(table): Sortable 组件提取到外部防止不必要的页面重建 (#6246). [#6246](https://github.com/ant-design/pro-components/pull/#6246) [@yqz0203](https://github.com/yqz0203)
|
||||
- fix(descriptions): 修复 ProDescriptions 编辑带有校验情况下,样式排版问题 (#6254). [#6254](https://github.com/ant-design/pro-components/pull/#6254) [@leshalv](https://github.com/leshalv)
|
||||
|
||||
## @ant-design/pro-components@2.3.34
|
||||
|
||||
`2022-11-17`
|
||||
|
||||
- chore(form): update @ant-design/pro-form peer dependency "@types/lodash.merge" to be optional (#6220). [#6220](https://github.com/ant-design/pro-components/pull/#6220) [@lzm0x219](https://github.com/lzm0x219)
|
||||
- fix(form): 只读模式下的 ProFormSelect 应该支持 wrap (#6235). [#6235](https://github.com/ant-design/pro-components/pull/#6235) [@kiner-tang](https://github.com/kiner-tang)
|
||||
- fix(table): fix nested table style (#6228). [#6228](https://github.com/ant-design/pro-components/pull/#6228) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
|
||||
## @ant-design/pro-components@2.3.33
|
||||
|
||||
`2022-11-15`
|
||||
|
||||
- fix(form): 修复 checkbox 在 ant design 5 vertical 布局失效问题 (#6214). [#6214](https://github.com/ant-design/pro-components/pull/#6214) [@leshalv](https://github.com/leshalv)
|
||||
- fix(table): change css cache error. [a5bc4cb](https://github.com/ant-design/pro-components/commit/a5bc4cb)
|
||||
- fix(Descriptions): fix ellipsis type error. [d051be5](https://github.com/ant-design/pro-components/commit/d051be5)
|
||||
|
||||
## @ant-design/pro-components@2.3.32
|
||||
|
||||
`2022-11-14`
|
||||
|
||||
- fix(form): 修复 checkbox 在 ant design 5 vertical 布局失效问题 (#6214). [#6214](https://github.com/ant-design/pro-components/pull/#6214) [@leshalv](https://github.com/leshalv)
|
||||
|
||||
## @ant-design/pro-components@2.3.31
|
||||
|
||||
`2022-11-14`
|
||||
|
||||
- fix(layout): 在内容定宽下,TopNavHeader 的样式问题 (#6182). [#6182](https://github.com/ant-design/pro-components/pull/#6182) [@hqwlkj](https://github.com/hqwlkj)
|
||||
- fix(form): reset ProFormList when new form. [b9ba5d0](https://github.com/ant-design/pro-components/commit/b9ba5d0)
|
||||
- fix(table): 修复 DragSortTable 的 dataSource 值变更后未生效 (#6207). [#6207](https://github.com/ant-design/pro-components/pull/#6207) [@acg-developer](https://github.com/acg-developer)
|
||||
- fix(table): 修复拖拽排序表格使用数据源时数据源改变后未更新数据问题 (#6211). [#6211](https://github.com/ant-design/pro-components/pull/#6211) [@kiner-tang](https://github.com/kiner-tang)
|
||||
- fix(table): column not working correctly after reset (#6159). [#6159](https://github.com/ant-design/pro-components/pull/#6159) [@Zeng-J](https://github.com/Zeng-J)
|
||||
|
||||
## @ant-design/pro-components@2.3.30
|
||||
|
||||
`2022-11-08`
|
||||
|
||||
- fix(utils): alway check process is undefined. [a861967](https://github.com/ant-design/pro-components/commit/a861967)
|
||||
- fix(table): update table alert style. [1302a90](https://github.com/ant-design/pro-components/commit/1302a90)
|
||||
|
||||
## @ant-design/pro-components@2.3.29
|
||||
|
||||
`2022-11-07`
|
||||
|
||||
- fix(utils): do not use process?.env?.ANTD_VERSION. [a295b3d](https://github.com/ant-design/pro-components/commit/a295b3d)
|
||||
- fix(table): 兼容 ellipsis 不想显示 Tooltip #6158 (#6160). [#6160](https://github.com/ant-design/pro-components/pull/#6160) [@DerrickTel](https://github.com/DerrickTel)
|
||||
|
||||
## @ant-design/pro-components@2.3.28
|
||||
|
||||
`2022-11-03`
|
||||
|
||||
- fix(layout): PageContainer support childrenContentStyle. [2a487cf](https://github.com/ant-design/pro-components/commit/2a487cf)
|
||||
- fix(layout): fix sub menu padding style. [324e083](https://github.com/ant-design/pro-components/commit/324e083)
|
||||
- feat(table): add new api actionRef.saveEditable (#6081). [#6081](https://github.com/ant-design/pro-components/pull/#6081) [@shijistar](https://github.com/shijistar)
|
||||
|
||||
## @ant-design/pro-components@2.3.24
|
||||
|
||||
`2022-10-24`
|
||||
|
||||
- fix(table): column drag order error when move upward (#6113). [#6113](https://github.com/ant-design/pro-components/pull/#6113) [@xiawenqi](https://github.com/xiawenqi)
|
||||
- fix(layout): auto open hashid (#6114). [#6114](https://github.com/ant-design/pro-components/pull/#6114) [@chenshuai2144](https://github.com/chenshuai2144)
|
||||
|
||||
## @ant-design/pro-components@2.3.21
|
||||
|
||||
`2022-10-21`
|
||||
|
||||
- fix(layout): support dynamic token set (#6106). [#6106](https://github.com/ant-design/pro-components/pull/#6106) [@chenshuai2144](https://github.com/chenshuai2144)
|
||||
- fix(layout): fix listtool bar style error. [820a5b7](https://github.com/ant-design/pro-components/commit/820a5b7)
|
||||
- fix(layout): 默认开启 hash. [47f30c9](https://github.com/ant-design/pro-components/commit/47f30c9)
|
||||
- fix(form): ProFormDigit value will fixed when not set precision(#6101). [#6101](https://github.com/ant-design/pro-components/pull/#6101) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
- fix(table): 修复 inline 类型样式丢失问题 (#6092). [#6092](https://github.com/ant-design/pro-components/pull/#6092) [@sushi-su](https://github.com/sushi-su)
|
||||
- fix(table): use ellipsis type exported from antd/rc-table (#6105). [#6105](https://github.com/ant-design/pro-components/pull/#6105) [@Jungzl](https://github.com/Jungzl)
|
||||
- fix(table): fix table select rows style error. [dff31f3](https://github.com/ant-design/pro-components/commit/dff31f3)
|
||||
- fix(card): static style fixed (#6100). [#6100](https://github.com/ant-design/pro-components/pull/#6100) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
|
||||
## @ant-design/pro-components@2.3.20
|
||||
|
||||
`2022-10-19`
|
||||
|
||||
- fix(Layout): 修复 PageContainer 设置 fixedHeader 属性后,样式 ant-pro-page-container-warp 未生效问题 (#6078). [#6078](https://github.com/ant-design/pro-components/pull/#6078) [@hqwlkj](https://github.com/hqwlkj)
|
||||
- fix(layout): add ghost classname. [7df07ba](https://github.com/ant-design/pro-components/commit/7df07ba)
|
||||
- fix(layout): alway set pageheader is ghost. [e06d908](https://github.com/ant-design/pro-components/commit/e06d908)
|
||||
- fix(form): 金额格式化支持负数形式展示 (#6080). [#6080](https://github.com/ant-design/pro-components/pull/#6080) [@kiner-tang](https://github.com/kiner-tang)
|
||||
- fix(form): fix intl no work error. [ecfc9d6](https://github.com/ant-design/pro-components/commit/ecfc9d6)
|
||||
- fix(card): fix card hover error style. [7ab8e7e](https://github.com/ant-design/pro-components/commit/7ab8e7e)
|
||||
|
||||
## @ant-design/pro-components@2.3.18
|
||||
|
||||
`2022-10-14`
|
||||
|
||||
- fix(utils): remove act import. [fc76519](https://github.com/ant-design/pro-components/commit/fc76519)
|
||||
- fix(layout): AppsLogo stopPropagation. [2235842](https://github.com/ant-design/pro-components/commit/2235842)
|
||||
- fix(layout): Support for multiple PageContainers to be used together. [7a9aad7](https://github.com/ant-design/pro-components/commit/7a9aad7)
|
||||
- fix(layout): use img height. [224b573](https://github.com/ant-design/pro-components/commit/224b573)
|
||||
- fix(layout): support hover token. [8eb4545](https://github.com/ant-design/pro-components/commit/8eb4545)
|
||||
- fix(form): ProFormList 组件 required 时需要提供加星号样式 (#5995). [#5995](https://github.com/ant-design/pro-components/pull/#5995) [@Zeng-J](https://github.com/Zeng-J)
|
||||
- fix(form): Fixed a bug with nested FormList. [2100074](https://github.com/ant-design/pro-components/commit/2100074)
|
||||
- fix(table): support tooltip=false, close ellipsis tooltip. [1c6c7ef](https://github.com/ant-design/pro-components/commit/1c6c7ef)
|
||||
- fix(table): table search style fixed (#6069). [#6069](https://github.com/ant-design/pro-components/pull/#6069) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
|
||||
## @ant-design/pro-components@2.3.17
|
||||
|
||||
`2022-10-13`
|
||||
|
||||
- fix(layout): 优化页面滑动的问题. [367c667](https://github.com/ant-design/pro-components/commit/367c667)
|
||||
- fix(layout): use inline style. [827349d](https://github.com/ant-design/pro-components/commit/827349d)
|
||||
- fix(form): fix filter dropdown not close (#6067). [#6067](https://github.com/ant-design/pro-components/pull/#6067) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
- fix(table): fix no has key when maxsize call error. [657e971](https://github.com/ant-design/pro-components/commit/657e971)
|
||||
- fix(table): card add hashid. [b98c387](https://github.com/ant-design/pro-components/commit/b98c387)
|
||||
|
||||
## @ant-design/pro-components@2.3.15
|
||||
|
||||
`2022-10-11`
|
||||
|
||||
- fix(layout): fix PageContainer token no rerender error. [5611a16](https://github.com/ant-design/pro-components/commit/5611a16)
|
||||
- fix(form): ProFormDependency support T. [7b0c85d](https://github.com/ant-design/pro-components/commit/7b0c85d)
|
||||
- fix(form): fix key and visible use. [0ebd3db](https://github.com/ant-design/pro-components/commit/0ebd3db)
|
||||
- fix(field): use open replace visible. [8dcf66a](https://github.com/ant-design/pro-components/commit/8dcf66a)
|
||||
|
||||
## @ant-design/pro-components@2.3.14
|
||||
|
||||
`2022-10-10`
|
||||
|
||||
- fix(layout): support pageContainer.token. [0abcbd4](https://github.com/ant-design/pro-components/commit/0abcbd4)
|
||||
- fix(layout): support pageContainer.token. [bd6e110](https://github.com/ant-design/pro-components/commit/bd6e110)
|
||||
- fix(layout): fix pageheader style overwrite. [5b1a774](https://github.com/ant-design/pro-components/commit/5b1a774)
|
||||
- fix(layout): fix layout margin style error. [3f9e8b5](https://github.com/ant-design/pro-components/commit/3f9e8b5)
|
||||
- fix(layout): fix back icon style error. [1ecdad4](https://github.com/ant-design/pro-components/commit/1ecdad4)
|
||||
- fix(form): fix showSearch title no work error. [96da32d](https://github.com/ant-design/pro-components/commit/96da32d)
|
||||
- fix(form): remove redundant code (#6030). [#6030](https://github.com/ant-design/pro-components/pull/#6030) [@kiner-tang](https://github.com/kiner-tang)
|
||||
- fix(form): 修复 ListItem 自定义样式和样式名称不生效的 BUG (#5982). [#5982](https://github.com/ant-design/pro-components/pull/#5982) [@hqwlkj](https://github.com/hqwlkj)
|
||||
- fix(form): 解决 moneySymbol 设置为 false 时金额格式化异常问题 (#6012). [#6012](https://github.com/ant-design/pro-components/pull/#6012) [@kiner-tang](https://github.com/kiner-tang)
|
||||
- fix(form): 修复 ModalForm 和 DrawerForm 关闭过程中表单内容闪现初始值的问题 (#6009). [#6009](https://github.com/ant-design/pro-components/pull/#6009) [@kiner-tang](https://github.com/kiner-tang)
|
||||
- fix(table):DragSortTable - 拖动排序表格设置 rowClassName 无效问题 (#6038). [#6038](https://github.com/ant-design/pro-components/pull/#6038) [@hqwlkj](https://github.com/hqwlkj)
|
||||
|
||||
## @ant-design/pro-components@2.3.13
|
||||
|
||||
`2022-09-28`
|
||||
|
||||
- fix(components): provide support getPrefixCls. [b3da61f](https://github.com/ant-design/pro-components/commit/b3da61f)
|
||||
- fix(form): fix visible props warning (#5937). [#5937](https://github.com/ant-design/pro-components/pull/#5937) [@zzcbnz](https://github.com/zzcbnz)
|
||||
- fix(table): 解决 CheckCard 与 Table 联用时 CheckCard 样式丢失问题 (#5980). [#5980](https://github.com/ant-design/pro-components/pull/#5980) [@kiner-tang](https://github.com/kiner-tang)
|
||||
|
||||
## @ant-design/pro-components@2.3.12
|
||||
|
||||
`2022-09-22`
|
||||
|
||||
- fix(layout): awlay use route.children. [44b6fe1](https://github.com/ant-design/pro-components/commit/44b6fe1)
|
||||
- fix(form): export FormListContext (#5968). [#5968](https://github.com/ant-design/pro-components/pull/#5968) [@houkunlin](https://github.com/houkunlin)
|
||||
- fix(list): 修复类型问题:未将 RecordType 的泛型传入 meta 中 (#5962). [#5962](https://github.com/ant-design/pro-components/pull/#5962) [@SoraYama](https://github.com/SoraYama)
|
||||
- fix(table): option button alignment problem (#5959). [#5959](https://github.com/ant-design/pro-components/pull/#5959) [@GoodbyeNJN](https://github.com/GoodbyeNJN)
|
||||
- fix(table): useEditableArray 修复无法正确添加深度大于 1 的子记录 (#5949). [#5949](https://github.com/ant-design/pro-components/pull/#5949) [@zd5043039119](https://github.com/zd5043039119)
|
||||
- fix(field): fix ProFormSegmented style (#5969). [#5969](https://github.com/ant-design/pro-components/pull/#5969) [@DBvc](https://github.com/DBvc)
|
||||
- fix(card): card key props warning (#5941). [#5941](https://github.com/ant-design/pro-components/pull/#5941) [@zzcbnz](https://github.com/zzcbnz)
|
||||
|
||||
## @ant-design/pro-components@2.3.10
|
||||
|
||||
`2022-09-21`
|
||||
|
||||
- fix(layout): fix colorPrimary change no rerender style error. [be6ab65](https://github.com/ant-design/pro-components/commit/be6ab65)
|
||||
- feat(layout): remove routers types. [6c54c7e](https://github.com/ant-design/pro-components/commit/6c54c7e)
|
||||
- fix(table): use better style import funtion. [8ce8d5a](https://github.com/ant-design/pro-components/commit/8ce8d5a)
|
||||
- fix(card): fixed col width digit lost (#5958). [#5958](https://github.com/ant-design/pro-components/pull/#5958) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
|
||||
## @ant-design/pro-components@2.3.9
|
||||
|
||||
`2022-09-16`
|
||||
|
||||
- fix(table): fix dateformat no work error. [c30c107](https://github.com/ant-design/pro-components/commit/c30c107)
|
||||
|
||||
## @ant-design/pro-components@2.3.8
|
||||
|
||||
`2022-09-14`
|
||||
|
||||
- fix(components): fix ref error. [c9e1e98](https://github.com/ant-design/pro-components/commit/c9e1e98)
|
||||
- fix(utils): remove unuse code. [bcbdbf6](https://github.com/ant-design/pro-components/commit/bcbdbf6)
|
||||
- fix(layout): fix colorBgPageContainer (#5911). [#5911](https://github.com/ant-design/pro-components/pull/#5911) [@drizzlesconsin](https://github.com/drizzlesconsin)
|
||||
- fix(layout): custom header padding (#5912). [#5912](https://github.com/ant-design/pro-components/pull/#5912) [@drizzlesconsin](https://github.com/drizzlesconsin)
|
||||
- fix(form): fix select keyword lose. [797f853](https://github.com/ant-design/pro-components/commit/797f853)
|
||||
- feat(form): add ProFormSegmented (#5913). [#5913](https://github.com/ant-design/pro-components/pull/#5913) [@leoooy](https://github.com/leoooy)
|
||||
- fix(form): 修复 ProFormRadio.Group layout 作用反了 (#5918). [#5918](https://github.com/ant-design/pro-components/pull/#5918) [@leshalv](https://github.com/leshalv)
|
||||
- fix(table): fix configuration issues with nested columns. [c2c9d9d](https://github.com/ant-design/pro-components/commit/c2c9d9d)
|
||||
- fix(table): fix ColumnSetting style error. [ec39ccc](https://github.com/ant-design/pro-components/commit/ec39ccc)
|
||||
- fix(card): Card.Tabs.Pane Render null (#5926). [#5926](https://github.com/ant-design/pro-components/pull/#5926) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
|
||||
## @ant-design/pro-components@2.3.7
|
||||
|
||||
`2022-09-09`
|
||||
|
||||
- fix(components): fix compareVersions error. [9751208](https://github.com/ant-design/pro-components/commit/9751208)
|
||||
|
||||
## @ant-design/pro-components@2.3.6
|
||||
|
||||
`2022-09-08`
|
||||
|
||||
- fix(components): fix open props warning. [77703c8](https://github.com/ant-design/pro-components/commit/77703c8)
|
||||
- fix(components): fix css var error. [bd5d3bf](https://github.com/ant-design/pro-components/commit/bd5d3bf)
|
||||
- fix(components): unuse theme import. [bcac384](https://github.com/ant-design/pro-components/commit/bcac384)
|
||||
|
||||
## @ant-design/pro-components@2.3.5
|
||||
|
||||
`2022-09-05`
|
||||
|
||||
- fix(components): export more types from components (#5840). [#5840](https://github.com/ant-design/pro-components/pull/#5840) [@Jungzl](https://github.com/Jungzl)
|
||||
|
||||
## @ant-design/pro-components@2.3.4
|
||||
|
||||
`2022-09-02`
|
||||
|
||||
## @ant-design/pro-components@2.3.3
|
||||
|
||||
`2022-09-02`
|
||||
|
||||
- fix(components): add antd version dependencies. [aa5fd10](https://github.com/ant-design/pro-components/commit/aa5fd10)
|
||||
- feat(components): add @ant-design/pro-component (#5258). [#5258](https://github.com/ant-design/pro-components/pull/#5258) [@chenshuai2144](https://github.com/chenshuai2144)
|
||||
|
||||
## @ant-design/pro-components@2.3.2
|
||||
|
||||
`2022-09-01`
|
||||
|
||||
## @ant-design/pro-components@2.3.1
|
||||
|
||||
`2022-08-31`
|
||||
|
||||
## @ant-design/pro-components@2.3.0
|
||||
|
||||
`2022-08-30`
|
||||
|
||||
## @ant-design/pro-components@2.2.1
|
||||
|
||||
`2022-08-29`
|
||||
|
||||
## @ant-design/pro-components@2.2.0
|
||||
|
||||
`2022-08-26`
|
||||
|
||||
## @ant-design/pro-components@2.1.2
|
||||
|
||||
`2022-08-25`
|
||||
|
||||
## @ant-design/pro-components@2.1.1
|
||||
|
||||
`2022-08-25`
|
||||
|
||||
## @ant-design/pro-components@2.1.0
|
||||
|
||||
`2022-08-25`
|
||||
|
||||
## @ant-design/pro-components@2.0.5
|
||||
|
||||
`2022-08-24`
|
||||
|
||||
- fix(components): add antd version dependencies. [aa5fd10](https://github.com/ant-design/pro-components/commit/aa5fd10)
|
||||
- feat(components): add @ant-design/pro-component (#5258). [#5258](https://github.com/ant-design/pro-components/pull/#5258) [@chenshuai2144](https://github.com/chenshuai2144)
|
||||
|
||||
## @ant-design/pro-components@2.0.0
|
||||
|
||||
`2022-09-07`
|
||||
|
||||
- fix(components): fix css var error. [bd5d3bf](https://github.com/ant-design/pro-components/commit/bd5d3bf)
|
||||
- fix(components): unuse theme import. [bcac384](https://github.com/ant-design/pro-components/commit/bcac384)
|
||||
- fix(components): export more types from components (#5840). [#5840](https://github.com/ant-design/pro-components/pull/#5840) [@Jungzl](https://github.com/Jungzl)
|
||||
- fix(components): add antd version dependencies. [aa5fd10](https://github.com/ant-design/pro-components/commit/aa5fd10)
|
||||
- feat(components): add @ant-design/pro-component (#5258). [#5258](https://github.com/ant-design/pro-components/pull/#5258) [@chenshuai2144](https://github.com/chenshuai2144)
|
@ -1,11 +1,6 @@
|
||||
---
|
||||
title: Component Overview
|
||||
order: 0
|
||||
group:
|
||||
path: /
|
||||
nav:
|
||||
title: Component
|
||||
path: /components
|
||||
---
|
||||
|
||||
# Architecture Design
|
||||
@ -100,7 +95,7 @@ export type ProSchema<T = unknown, U = string, Extra = unknown> = {
|
||||
/**
|
||||
* @name Determines the unique value of this column
|
||||
*/
|
||||
key?: React.ReactText;
|
||||
key?: (string | number);
|
||||
/**
|
||||
* @name The key mapped to the entity
|
||||
* @description supports a number, [a,b] will be converted to obj.a.b
|
||||
|
@ -1,11 +1,6 @@
|
||||
---
|
||||
title: 组件总览
|
||||
order: 0
|
||||
group:
|
||||
path: /
|
||||
nav:
|
||||
title: 组件
|
||||
path: /components
|
||||
---
|
||||
|
||||
# 架构设计
|
||||
|
457
docs/components/schema.en-US.md
Normal file
457
docs/components/schema.en-US.md
Normal file
@ -0,0 +1,457 @@
|
||||
---
|
||||
title: 通用配置总览
|
||||
order: 1
|
||||
---
|
||||
|
||||
# 通用配置
|
||||
|
||||
在 ProComponents 我们在组件使用了与 table 的相同的定义,同时扩展了部分字段。让其可以满足更多需求。
|
||||
|
||||
| 字段名称 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `key` | `React.key` | 确定这个列的唯一值,一般用于 dataIndex 重复的情况 |
|
||||
| `dataIndex` | `React.key` \| `React.key[]` | 与实体映射的 key,数组会被转化 `[a,b] => Entity.a.b` |
|
||||
| `valueType` | `ProFieldValueType` | 数据的渲渲染方式,我们自带了一部分,你也可以自定义 valueType |
|
||||
| `title` | `ReactNode` \|`(props,type,dom)=> ReactNode` | 标题的内容,在 form 中是 label |
|
||||
| `tooltip` | `string` | 会在 title 旁边展示一个 icon,鼠标浮动之后展示 |
|
||||
| `valueEnum` | `(Entity)=> ValueEnum` \| `ValueEnum` | 支持 object 和 Map,Map 是支持其他基础类型作为 key |
|
||||
| `fieldProps` | `(form,config)=>fieldProps`\| `fieldProps` | 传给渲染的组件的 props,自定义的时候也会传递 |
|
||||
| `formItemProps` | `(form,config)=>formItemProps` \| `formItemProps` | 传递给 Form.Item 的配置 |
|
||||
| `renderText` | `(text: any, record: Entity, index: number, action: ProCoreActionType) => any` | 修改的数据是会被 valueType 定义的渲染组件消费 |
|
||||
| `render` | `(dom,entity,index, action, schema) => React.ReactNode` | 自定义只读模式的 dom,`render` 方法只管理的只读模式,编辑模式需要使用 `renderFormItem` |
|
||||
| `renderFormItem` | `(schema,config,form) => React.ReactNode` | 自定义编辑模式,返回一个 ReactNode,会自动包裹 value 和 onChange |
|
||||
| `request` | `(params,props) => Promise<{label,value}[]>` | 从远程请求网络数据,一般用于选择类组件 |
|
||||
| `params` | `Record<string, any>` | 额外传递给 `request` 的参数,组件不做处理,但是变化会引起`request` 重新请求数据 |
|
||||
| `hideInForm` | `boolean` | 在 Form 中隐藏 |
|
||||
| `hideInTable` | `boolean` | 在 Table 中隐藏 |
|
||||
| `hideInSearch` | `boolean` | 在 Table 的查询表单中隐藏 |
|
||||
| `hideInDescriptions` | `boolean` | 在 descriptions 中隐藏 |
|
||||
| `rowProps` | [RowProps](https://ant.design/components/grid/#Row) | 在开启 `grid` 模式时传递给 Row,仅在`ProFormGroup`, `ProFormList`, `ProFormFieldSet` 中有效 |
|
||||
| `colProps` | [ColProps](https://ant.design/components/grid/#Col) | 在开启 `grid` 模式时传递给 Col |
|
||||
|
||||
## TypeScript 定义
|
||||
|
||||
```tsx | pure
|
||||
export type ProSchema<T = unknown, U = string, Extra = unknown> = {
|
||||
/** @name 确定这个列的唯一值 */
|
||||
key?: string | number;
|
||||
/**
|
||||
* 支持一个数组,[a,b] 会转化为 obj.a.b
|
||||
*
|
||||
* @name 与实体映射的key
|
||||
*/
|
||||
dataIndex?: string | number | (string | number)[];
|
||||
/** 选择如何渲染相应的模式 */
|
||||
valueType?: ((entity: T, type: ProSchemaComponentTypes) => U) | U;
|
||||
|
||||
/**
|
||||
* 支持 ReactNode 和 方法
|
||||
*
|
||||
* @name 标题
|
||||
*/
|
||||
title?:
|
||||
| ((
|
||||
schema: ProSchema<T, U, Extra>,
|
||||
type: ProSchemaComponentTypes,
|
||||
dom: React.ReactNode,
|
||||
) => React.ReactNode)
|
||||
| React.ReactNode;
|
||||
|
||||
/** @name 展示一个 icon,hover 是展示一些提示信息 */
|
||||
tooltip?: string | LabelTooltipType;
|
||||
|
||||
/** @deprecated 你可以使用 tooltip,这个更改是为了与 antd 统一 */
|
||||
tip?: string;
|
||||
|
||||
render?: (
|
||||
dom: React.ReactNode,
|
||||
entity: T,
|
||||
index: number,
|
||||
action: ProCoreActionType,
|
||||
schema: ProSchema<T, U, Extra>,
|
||||
) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* 返回一个node,会自动包裹 value 和 onChange
|
||||
*
|
||||
* @name 自定义编辑模式
|
||||
*/
|
||||
renderFormItem?: (
|
||||
item: ProSchema<T, U, Extra>,
|
||||
config: {
|
||||
index?: number;
|
||||
value?: any;
|
||||
onSelect?: (value: any) => void;
|
||||
type: ProSchemaComponentTypes;
|
||||
defaultRender: (newItem: ProSchema<T, U, Extra>) => JSX.Element | null;
|
||||
},
|
||||
form: FormInstance,
|
||||
) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* 必须要返回 string
|
||||
*
|
||||
* @name 自定义 render
|
||||
*/
|
||||
renderText?: (text: any, record: T, index: number, action: ProCoreActionType) => any;
|
||||
|
||||
fieldProps?: any;
|
||||
/** @name 映射值的类型 */
|
||||
valueEnum?: ProSchemaValueEnumObj | ProSchemaValueEnumMap;
|
||||
|
||||
/** @name 从服务器请求枚举 */
|
||||
request?: ProFieldRequestData<ProSchema>;
|
||||
|
||||
/** @name 从服务器请求的参数,改变了会触发 reload */
|
||||
params?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
/** @name 隐藏在 descriptions */
|
||||
hideInDescriptions?: boolean;
|
||||
} & Extra;
|
||||
```
|
||||
|
||||
## valueType 列表
|
||||
|
||||
<code src="./valueType.tsx" height="154px" title="schema 表单"></code>
|
||||
|
||||
valueType 是 ProComponents 的灵魂,ProComponents 会根据 valueType 来映射成不同的表单项。以下是支持的常见表单项:
|
||||
|
||||
| valueType | 说明 |
|
||||
| --------------- | ---------------------------- |
|
||||
| `password` | 密码输入框 |
|
||||
| `money` | 金额输入框 |
|
||||
| `textarea` | 文本域 |
|
||||
| `date` | 日期 |
|
||||
| `dateTime` | 日期时间 |
|
||||
| `dateWeek` | 周 |
|
||||
| `dateMonth` | 月 |
|
||||
| `dateQuarter` | 季度输入 |
|
||||
| `dateYear` | 年份输入 |
|
||||
| `dateRange` | 日期区间 |
|
||||
| `dateTimeRange` | 日期时间区间 |
|
||||
| `time` | 时间 |
|
||||
| `timeRange` | 时间区间 |
|
||||
| `text` | 文本框 |
|
||||
| `select` | 下拉框 |
|
||||
| `treeSelect` | 树形下拉框 |
|
||||
| `checkbox` | 多选框 |
|
||||
| `rate` | 星级组件 |
|
||||
| `radio` | 单选框 |
|
||||
| `radioButton` | 按钮单选框 |
|
||||
| `progress` | 进度条 |
|
||||
| `percent` | 百分比组件 |
|
||||
| `digit` | 数字输入框 |
|
||||
| `second` | 秒格式化 |
|
||||
| `avatar` | 头像 |
|
||||
| `code` | 代码框 |
|
||||
| `switch` | 开关 |
|
||||
| `fromNow` | 相对于当前时间 |
|
||||
| `image` | 图片 |
|
||||
| `jsonCode` | 代码框,但是带了 json 格式化 |
|
||||
| `color` | 颜色选择器 |
|
||||
| `cascader` | 级联选择器 |
|
||||
|
||||
这里 demo 可以来了解一下各个 valueType 的展示效果。
|
||||
|
||||
### 传入 function
|
||||
|
||||
只有一个值并不能表现很多类型,`progress` 就是一个很好的例子。所以我们支持传入一个 function。你可以这样使用:
|
||||
|
||||
```tsx | pure
|
||||
const columns = {
|
||||
title: '进度',
|
||||
key: 'progress',
|
||||
dataIndex: 'progress',
|
||||
valueType: (item: T) => ({
|
||||
type: 'progress',
|
||||
status: item.status !== 'error' ? 'active' : 'exception',
|
||||
}),
|
||||
};
|
||||
```
|
||||
|
||||
### 支持的返回值
|
||||
|
||||
#### progress
|
||||
|
||||
```js
|
||||
return {
|
||||
type: 'progress',
|
||||
status: 'success' | 'exception' | 'normal' | 'active',
|
||||
};
|
||||
```
|
||||
|
||||
#### money
|
||||
|
||||
```js
|
||||
return { type: 'money', locale: 'en-Us' };
|
||||
```
|
||||
|
||||
#### percent
|
||||
|
||||
```js
|
||||
return { type: 'percent', showSymbol: true | false, precision: 2 };
|
||||
```
|
||||
|
||||
如果我们带的 valueType 不能满足需求,我们可以用自定义 valueType 来自定义业务组件。
|
||||
|
||||
### 自定义 valueType
|
||||
|
||||
<code src="./customization-value-type.tsx" height="154px" title="schema 表单"></code>
|
||||
|
||||
### valueEnum
|
||||
|
||||
valueEnum 需要传入一个枚举,ProTable 会自动根据值获取响应的枚举,并且在 form 中生成一个下拉框。看起来是这样的:
|
||||
|
||||
```ts | pure
|
||||
const valueEnum = {
|
||||
open: {
|
||||
text: '未解决',
|
||||
status: 'Error',
|
||||
},
|
||||
closed: {
|
||||
text: '已解决',
|
||||
status: 'Success',
|
||||
},
|
||||
};
|
||||
|
||||
// 也可以设置为一个function
|
||||
const valueEnum = (row) =>
|
||||
row.isMe
|
||||
? {
|
||||
open: {
|
||||
text: '未解决',
|
||||
status: 'Error',
|
||||
},
|
||||
closed: {
|
||||
text: '已解决',
|
||||
status: 'Success',
|
||||
},
|
||||
}
|
||||
: {
|
||||
open: {
|
||||
text: '等待解决',
|
||||
status: 'Error',
|
||||
},
|
||||
closed: {
|
||||
text: '已回应',
|
||||
status: 'Success',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
> 这里值得注意的是在 form 中并没有 row,所以 row 的值为 null,你可以根据这个来判断要在 form 中显示什么选项。
|
||||
|
||||
当前列值的枚举
|
||||
|
||||
```typescript | pure
|
||||
interface IValueEnum {
|
||||
[key: string]:
|
||||
| ReactNode
|
||||
| {
|
||||
text: ReactNode;
|
||||
status: 'Success' | 'Error' | 'Processing' | 'Warning' | 'Default';
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 远程数据
|
||||
|
||||
支持组件 `Select`, `TreeSelect`, `Cascader`, `Checkbox`, `Radio`, `RadioButton`
|
||||
|
||||
支持参数 `request`,`params`,`fieldProps.options`, `valueEnum` 来支持远程数据,这几个属性分别有不同的用法。
|
||||
|
||||
### `valueEnum`
|
||||
|
||||
valueEnum 是最基础的用法, 它支持传入一个 [`Object`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object) 或者是 [`Map`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map),相比于 options 支持更加丰富的定义,比如在表格中常见的各种 [badge](https://ant.design/components/badge-cn/#Badge)。
|
||||
|
||||
```tsx | pure
|
||||
const valueEnum = {
|
||||
all: { text: '全部', status: 'Default' },
|
||||
open: {
|
||||
text: '未解决',
|
||||
status: 'Error',
|
||||
},
|
||||
closed: {
|
||||
text: '已解决',
|
||||
status: 'Success',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
```tsx | pure
|
||||
import { ProFormSelect } from '@ant-design/pro-components';
|
||||
|
||||
const valueEnum = {
|
||||
all: { text: '全部', status: 'Default' },
|
||||
open: {
|
||||
text: '未解决',
|
||||
status: 'Error',
|
||||
},
|
||||
closed: {
|
||||
text: '已解决',
|
||||
status: 'Success',
|
||||
},
|
||||
};
|
||||
|
||||
export default () => (
|
||||
<ProFormSelect
|
||||
name="select2"
|
||||
label="Select"
|
||||
params={{}}
|
||||
valueType="select"
|
||||
valueEnum={valueEnum}
|
||||
placeholder="Please select a country"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### `fieldProps.options`
|
||||
|
||||
options 是 antd 定义的标准,但是只有部分组件支持, ProComponents 扩展了组件,使得 `select`, `checkbox`, `radio`, `radioButton` 都支持了 `options`, 他们的用法是相同的。
|
||||
|
||||
```tsx | pure
|
||||
const options = [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '未解决', value: 'open' },
|
||||
{ label: '已解决', value: 'closed' },
|
||||
{ label: '解决中', value: 'processing' },
|
||||
{
|
||||
label: '特殊选项',
|
||||
value: 'optGroup',
|
||||
optionType: 'optGroup',
|
||||
options: [
|
||||
{ label: '不解决', value: 'no' },
|
||||
{ label: '已废弃', value: 'clear' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// 或者不需要 label
|
||||
const options = ['chapter', 'chapter2'];
|
||||
|
||||
// 列中定义
|
||||
const columns = [
|
||||
{
|
||||
title: '创建者',
|
||||
width: 120,
|
||||
dataIndex: 'creator',
|
||||
valueType: 'select',
|
||||
fieldProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'item 1',
|
||||
value: 'a',
|
||||
},
|
||||
{
|
||||
label: 'item 2',
|
||||
value: 'b',
|
||||
},
|
||||
{
|
||||
label: 'item 3',
|
||||
value: 'c',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
```tsx | pure
|
||||
import { ProFormSelect } from '@ant-design/pro-components';
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: 'item 1',
|
||||
value: 'a',
|
||||
},
|
||||
{
|
||||
label: 'item 2',
|
||||
value: 'b',
|
||||
},
|
||||
{
|
||||
label: 'item 3',
|
||||
value: 'c',
|
||||
},
|
||||
];
|
||||
|
||||
export default () => (
|
||||
<ProFormSelect
|
||||
name="select2"
|
||||
label="Select"
|
||||
valueType="select"
|
||||
fieldProps={{ options }}
|
||||
placeholder="Please select a country"
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### `request` 和 `params`
|
||||
|
||||
> 可以使用 debounceTime 调整请求防抖时间,默认为 10ms
|
||||
|
||||
大部分时候我们是从网络中获取数据,但是获取写一个 hooks 来请求数据还是比较繁琐的,同时还要定义一系列状态,所以我们提供了 `request` 和 `params` 来获取数据。
|
||||
|
||||
- `request` 是一个 promise,需要返回一个 options 相同的数据
|
||||
- `params` 一般而言 `request` 是惰性的,params 修改会触发 `request` 的重新请求。
|
||||
|
||||
```tsx | pure
|
||||
const request = async () => [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '未解决', value: 'open' },
|
||||
{ label: '已解决', value: 'closed' },
|
||||
{ label: '解决中', value: 'processing' },
|
||||
];
|
||||
|
||||
<ProFormSelect
|
||||
name="select2"
|
||||
label="Select"
|
||||
params={{}}
|
||||
valueType="select"
|
||||
debounceTime={1000}
|
||||
request={request}
|
||||
placeholder="Please select a country"
|
||||
/>;
|
||||
|
||||
// 列中定义
|
||||
const columns = [
|
||||
{
|
||||
title: '创建者',
|
||||
width: 120,
|
||||
dataIndex: 'creator',
|
||||
valueType: 'select',
|
||||
request,
|
||||
params: {},
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
```tsx | pure
|
||||
import { ProForm, ProFormSelect, ProFormText } from '@ant-design/pro-components';
|
||||
|
||||
const request = async (params) => {
|
||||
console.log(params);
|
||||
return [
|
||||
{ label: params.text, value: 'all' },
|
||||
{ label: '未解决', value: 'open' },
|
||||
{ label: '已解决', value: 'closed' },
|
||||
{ label: '解决中', value: 'processing' },
|
||||
];
|
||||
};
|
||||
|
||||
export default () => (
|
||||
<ProForm>
|
||||
<ProFormText label="相互依赖的" initialValue="所有的" name="text" />
|
||||
<ProFormSelect
|
||||
name="select2"
|
||||
label="Select"
|
||||
valueType="select"
|
||||
dependencies={['text']}
|
||||
request={request}
|
||||
placeholder="Please select a country"
|
||||
/>
|
||||
</ProForm>
|
||||
);
|
||||
```
|
||||
|
||||
在实际的使用中 `request` 增加了一个 5s 缓存,可能会导致数据更新不及时,如果需要频繁更新,建议使用 `state`+`fieldProps.options`。
|
@ -1,11 +1,6 @@
|
||||
---
|
||||
title: 通用配置总览
|
||||
order: 1
|
||||
group:
|
||||
path: /
|
||||
nav:
|
||||
title: 组件
|
||||
path: /components
|
||||
---
|
||||
|
||||
# 通用配置
|
||||
@ -39,7 +34,7 @@ nav:
|
||||
```tsx | pure
|
||||
export type ProSchema<T = unknown, U = string, Extra = unknown> = {
|
||||
/** @name 确定这个列的唯一值 */
|
||||
key?: React.ReactText;
|
||||
key?: string | number;
|
||||
/**
|
||||
* 支持一个数组,[a,b] 会转化为 obj.a.b
|
||||
*
|
||||
@ -118,9 +113,7 @@ export type ProSchema<T = unknown, U = string, Extra = unknown> = {
|
||||
|
||||
## valueType 列表
|
||||
|
||||
<code src="./demos/valueType.tsx" height="154px" title="schema 表单"></code>
|
||||
|
||||
### API
|
||||
<code src="./valueType.tsx" height="154px" title="schema 表单"></code>
|
||||
|
||||
valueType 是 ProComponents 的灵魂,ProComponents 会根据 valueType 来映射成不同的表单项。以下是支持的常见表单项:
|
||||
|
||||
@ -165,7 +158,7 @@ valueType 是 ProComponents 的灵魂,ProComponents 会根据 valueType 来映
|
||||
|
||||
只有一个值并不能表现很多类型,`progress` 就是一个很好的例子。所以我们支持传入一个 function。你可以这样使用:
|
||||
|
||||
```tsx |pure
|
||||
```tsx | pure
|
||||
const columns = {
|
||||
title: '进度',
|
||||
key: 'progress',
|
||||
@ -204,7 +197,7 @@ return { type: 'percent', showSymbol: true | false, precision: 2 };
|
||||
|
||||
### 自定义 valueType
|
||||
|
||||
<code src="./demos/customization-value-type.tsx" height="154px" title="schema 表单"></code>
|
||||
<code src="./customization-value-type.tsx" height="154px" title="schema 表单"></code>
|
||||
|
||||
### valueEnum
|
||||
|
47
docs/docs/faq.en-US.md
Normal file
47
docs/docs/faq.en-US.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: FAQ
|
||||
order: 3
|
||||
|
||||
nav:
|
||||
title: FAQ
|
||||
path: /docs
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
以下整理了一些 ProComponents 社区常见的问题和官方答复,在提问之前建议找找有没有类似的问题。此外我们也维护了一个反馈较多 [how to use 标签](https://github.com/ant-design/pro-components/issues?q=is%3Aissue+label%3A%22%F0%9F%A4%B7%F0%9F%8F%BC+How+to+use%22+) 亦可参考。
|
||||
|
||||
### ProTable request 返回的数据格式可以自定义吗?
|
||||
|
||||
不行的,你可以在 request 中转化一下,或者写个拦截器。
|
||||
|
||||
[示例](https://beta-pro.ant.design/docs/request-cn)
|
||||
|
||||
### 如何隐藏 ProTable 生成的搜索的 label?
|
||||
|
||||
columns 的 title 支持 function 的,你可以这样写
|
||||
|
||||
```typescript
|
||||
title: (_, type) => {
|
||||
if (type === 'table') {
|
||||
return '标题';
|
||||
}
|
||||
return null;
|
||||
};
|
||||
```
|
||||
|
||||
### 我没法安装 `ProComponents` 和 `ProComponents` 的依赖,顺便提一句,我在中国大陆。
|
||||
|
||||
那啥,试试 [cnpm](http://npm.taobao.org/)和[yarn](https://www.npmjs.com/package/yarn)。
|
||||
|
||||
### `Form` 当中 `initialValues`
|
||||
|
||||
`ProComponents` 底层也是封装的 [antd](https://ant.design/index-cn) ,所以用法也是和 [antd](https://ant.design/index-cn) 相同。注意 `initialValues` 不能被 `setState` 动态更新,你需要用 `setFieldsValue` 来更新。 `initialValues` 只在 `form` 初始化时生效且只生效一次,如果你需要异步加载推荐使用 `request`,或者 `initialValues ? <Form/> : null`
|
||||
|
||||
## 错误和警告
|
||||
|
||||
这里是一些你在使用 ProComponents 的过程中可能会遇到的错误和警告,但是其中一些并不是 ProComponents 的 bug。
|
||||
|
||||
### Cannot read property 'Provider' of undefined
|
||||
|
||||
请确保 antd 的版本 >= `4.11.1`
|
@ -1,8 +1,7 @@
|
||||
---
|
||||
title: FAQ
|
||||
order: 3
|
||||
group:
|
||||
path: /
|
||||
|
||||
nav:
|
||||
title: FAQ
|
||||
path: /docs
|
@ -1,8 +1,7 @@
|
||||
---
|
||||
title: Quick Start
|
||||
order: 2
|
||||
group:
|
||||
path: /
|
||||
|
||||
nav:
|
||||
title: Documentation
|
||||
path: /docs
|
@ -1,8 +1,7 @@
|
||||
---
|
||||
title: 快速开始
|
||||
order: 2
|
||||
group:
|
||||
path: /
|
||||
|
||||
nav:
|
||||
title: 文档
|
||||
path: /docs
|
@ -1,8 +1,7 @@
|
||||
---
|
||||
title: Introduction
|
||||
order: 1
|
||||
group:
|
||||
path: /
|
||||
|
||||
nav:
|
||||
title: Documentation
|
||||
order: 1
|
@ -1,8 +1,7 @@
|
||||
---
|
||||
title: 简介
|
||||
order: 1
|
||||
group:
|
||||
path: /
|
||||
|
||||
nav:
|
||||
title: 文档
|
||||
order: 1
|
@ -1,33 +1,36 @@
|
||||
---
|
||||
title: ProComponents - page-like components
|
||||
order: 10
|
||||
sidebar: false
|
||||
title: ProComponents - Front-end components at page level
|
||||
hero:
|
||||
title: ProComponents
|
||||
desc: 🏆 Make middle and back-end development easier
|
||||
description: 🏆 Make the development of middle and back office easier
|
||||
actions:
|
||||
- text: 🏮🏮 Quick Start →
|
||||
link: /docs/getting-started
|
||||
- text: 🏮🏮 Introduction →
|
||||
link: /en-US/docs
|
||||
|
||||
features:
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/q48YQ5X4ytAAAAAAAAAAAAAAAFl94AQBr
|
||||
title: easy to use
|
||||
desc: It has its own encapsulation on Ant Design, which is easier to use
|
||||
- icon: https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/q48YQ5X4ytAAAAAAAAAAAAAAFl94AQBr
|
||||
title: Easy to use
|
||||
description: It has its own packaging on Ant Design, which is easier to use
|
||||
|
||||
- avatar: https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg
|
||||
title: Ant Design
|
||||
desc: In the same vein as the Ant Design design system, it seamlessly connects to antd projects
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/UKqDTIp55HYAAAAAAAAAAAAAFl94AQBr
|
||||
description: It is in the same line with Ant Design design system and seamlessly connects with ant project
|
||||
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/UKqDTIp55HYAAAAAAAAAAAAAFl94AQBr
|
||||
title: Internationalization
|
||||
desc: Provide complete internationalization and connect with Ant Design system
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Y_NMQKxw7OgAAAAAAAAAAAAAFl94AQBr
|
||||
title: Default style
|
||||
desc: The style is in the same line as antd, no need to change, it is natural
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/U3XjS5IA1tUAAAAAAAAAAAAAFl94AQBr
|
||||
title: Default Behavior
|
||||
desc: less code, less bugs
|
||||
- icon: https://gw.alipayobjects.com/zos/antfincdn/Eb8IHpb9jE/Typescript_logo_2020.svg
|
||||
description: Provide complete internationalization and connect with Ant Design system
|
||||
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Y_NMQKxw7OgAAAAAAAAAAAAAFl94AQBr
|
||||
title: Preset Style
|
||||
description: The style and style are in one continuous line with ant d, without magic modification, and naturally
|
||||
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/U3XjS5IA1tUAAAAAAAAAAAAAFl94AQBr
|
||||
title: Preset behavior
|
||||
description: Less code, fewer bugs
|
||||
|
||||
- avatar: https://gw.alipayobjects.com/zos/antfincdn/Eb8IHpb9jE/Typescript_logo_2020.svg
|
||||
title: TypeScript
|
||||
desc: Developed with TypeScript, provides a complete type definition file
|
||||
description: Use TypeScript development to provide a complete type definition file
|
||||
|
||||
footer: Open-source MIT Licensed | © 2017-present
|
||||
---
|
||||
|
@ -1,33 +1,36 @@
|
||||
---
|
||||
title: ProComponents - 页面级别的前端组件
|
||||
order: 10
|
||||
sidebar: false
|
||||
hero:
|
||||
title: ProComponents
|
||||
desc: 🏆 让中后台开发更简单
|
||||
description: 🏆 让中后台开发更简单
|
||||
actions:
|
||||
- text: 🏮🏮 快速开始 →
|
||||
link: /docs/getting-started
|
||||
|
||||
features:
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/q48YQ5X4ytAAAAAAAAAAAAAAFl94AQBr
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/q48YQ5X4ytAAAAAAAAAAAAAAFl94AQBr
|
||||
title: 简单易用
|
||||
desc: 在 Ant Design 上进行了自己的封装,更加易用
|
||||
- icon: https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg
|
||||
description: 在 Ant Design 上进行了自己的封装,更加易用
|
||||
|
||||
- avatar: https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg
|
||||
title: Ant Design
|
||||
desc: 与 Ant Design 设计体系一脉相承,无缝对接 antd 项目
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/UKqDTIp55HYAAAAAAAAAAAAAFl94AQBr
|
||||
description: 与 Ant Design 设计体系一脉相承,无缝对接 antd 项目
|
||||
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/UKqDTIp55HYAAAAAAAAAAAAAFl94AQBr
|
||||
title: 国际化
|
||||
desc: 提供完备的国际化,与 Ant Design 体系打通
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Y_NMQKxw7OgAAAAAAAAAAAAAFl94AQBr
|
||||
description: 提供完备的国际化,与 Ant Design 体系打通
|
||||
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Y_NMQKxw7OgAAAAAAAAAAAAAFl94AQBr
|
||||
title: 预设样式
|
||||
desc: 样式风格与 antd 一脉相承,无需魔改,浑然天成
|
||||
- icon: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/U3XjS5IA1tUAAAAAAAAAAAAAFl94AQBr
|
||||
description: 样式风格与 antd 一脉相承,无需魔改,浑然天成
|
||||
|
||||
- avatar: https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/U3XjS5IA1tUAAAAAAAAAAAAAFl94AQBr
|
||||
title: 预设行为
|
||||
desc: 更少的代码,更少的 Bug
|
||||
- icon: https://gw.alipayobjects.com/zos/antfincdn/Eb8IHpb9jE/Typescript_logo_2020.svg
|
||||
description: 更少的代码,更少的 Bug
|
||||
|
||||
- avatar: https://gw.alipayobjects.com/zos/antfincdn/Eb8IHpb9jE/Typescript_logo_2020.svg
|
||||
title: TypeScript
|
||||
desc: 使用 TypeScript 开发,提供完整的类型定义文件
|
||||
description: 使用 TypeScript 开发,提供完整的类型定义文件
|
||||
|
||||
footer: Open-source MIT Licensed | © 2017-present
|
||||
---
|
||||
|
14
docs/playground/index.en-US.md
Normal file
14
docs/playground/index.en-US.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: CRUD
|
||||
nav:
|
||||
title: Playground
|
||||
path: /playground
|
||||
---
|
||||
|
||||
# CRUD
|
||||
|
||||
ProTable,ProDescriptions,ProForm 都是基于 ProField 来进行封装。ProTable 和 ProDescriptions 根据 valueType 来渲染不同的 ProField,Form 则是通过不同的 FormField 来实现封装。
|
||||
|
||||
使用同样的底层实现为 ProTable,ProDescriptions,ProForm 打通带来了便利。ProForm 可以很方便的实现只读模式,ProTable 可以快速实现查询表单和可编辑表格。ProDescriptions 可以实现节点编辑,以下有个例子可以切换三个组件。
|
||||
|
||||
<code src="../../packages/table/src/demos/crud.tsx" height="500px" iframe="650px"></code>
|
@ -3,8 +3,6 @@ title: CRUD
|
||||
nav:
|
||||
title: Playground
|
||||
path: /playground
|
||||
group:
|
||||
path: /
|
||||
---
|
||||
|
||||
# CRUD
|
@ -3,8 +3,6 @@ title: ProDescriptions
|
||||
nav:
|
||||
title: Playground
|
||||
path: /playground
|
||||
group:
|
||||
path: /
|
||||
---
|
||||
|
||||
# ProDescriptions Playground
|
10
docs/playground/pro-descriptions.md
Normal file
10
docs/playground/pro-descriptions.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
title: ProDescriptions
|
||||
nav:
|
||||
title: Playground
|
||||
path: /playground
|
||||
---
|
||||
|
||||
# ProDescriptions Playground
|
||||
|
||||
<code src="../../packages/descriptions/src/demos/dynamic-descriptions.tsx" height="500px" iframe="760px" background="#f5f5f5" title="属性展示"></code>
|
18
docs/playground/pro-form.en-US.md
Normal file
18
docs/playground/pro-form.en-US.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
title: ProForm
|
||||
nav:
|
||||
title: Playground
|
||||
path: /playground
|
||||
---
|
||||
|
||||
# Form Playground
|
||||
|
||||
## Form 的 layout 切换
|
||||
|
||||
ProForm 的主要功能是预设了很多 layout,如果需要切换只需要改变外面包裹的 Layout 即可,以下是个 demo。
|
||||
|
||||
<code src="../../packages/form/src/demos/layout-change.tsx" height="709px"></code>
|
||||
|
||||
## FormList
|
||||
|
||||
<code src="../../packages/form/src/components/Group/demos/customize.tsx" title="ProForm.List" height="1002px"></code>
|
@ -3,8 +3,6 @@ title: ProForm
|
||||
nav:
|
||||
title: Playground
|
||||
path: /playground
|
||||
group:
|
||||
path: /
|
||||
---
|
||||
|
||||
# Form Playground
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user