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:
陈帅 2023-02-07 19:19:37 +08:00 committed by GitHub
parent 965ce241b1
commit 23e4430969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
293 changed files with 30482 additions and 24390 deletions

View File

@ -3,4 +3,4 @@ coverage:
project:
default:
# Fail the status if coverage drops by >= 0.1%
threshold: 0.1
threshold: 1%

View File

@ -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>
);
};

View 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;

View 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;

View 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>
);

View 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>
);
});

View 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}
`,
}));

View 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;

View 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;
}
`,
};
});

View 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;

View 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;
}
`,
);

View 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;

View 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;

View 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;

View 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;

View 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};
`,
),
}));

View 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;

View 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};
}
`,
),
}));

View 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>
);
};

View 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;
});

View 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);

View 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]);
};

View File

@ -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;
}

View File

@ -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;

View File

@ -0,0 +1,7 @@
import { useOutlet } from 'dumi';
import DemoProvider from '../../components/DemoProvider';
export default () => {
const outlet = useOutlet();
return <DemoProvider>{outlet}</DemoProvider>;
};

View 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>
);

View 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;
}
`;

View File

@ -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>
);
};

View 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;

View 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;
}
}
`,
}));

View 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
View File

@ -0,0 +1,5 @@
import { IApi } from 'dumi';
export default (api: IApi) => {
api.logger.info('💋', api.env);
};

View 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;

View 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;
}
}
}
`,
}));

View 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;

View 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;
}
`,
}));

View 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;

View 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;

View 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};
}
}
`,
}));

View 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;

View 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;
}
`,
};
});

View 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;

View 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;
}
`,
}));

View 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);

View 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;
}
}
`,
);

View 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);

View 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}
/>
);
});

View 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;
`,
};
});

View 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;
};

View 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;

View 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};
}
`,
};
});

View 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);

View 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};
}
}
`,
}));

View 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;

View 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;
}
`,
};
});

View 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 || '';
};

View 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' },
),
);

View 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],
};
};

View File

@ -0,0 +1,2 @@
export { darkAlgorithm } from './dark';
export { lightAlgorithm } from './light';

View 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>;
};

View 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;
};

View 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%
)`,
};
};

View 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%
)`,
};
};

View File

@ -0,0 +1,3 @@
export * from './antdTheme';
export * from './customStylish';
export * from './customToken';

View File

@ -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
View 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
View 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 以获得更快的构建速度
},
});

View File

@ -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;

View File

@ -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

View File

@ -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 }}'

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -41,3 +41,5 @@ screenshot
.changelogs
.changelog.md
packages/components/src/version.ts
.dumi/tmp
server

View File

@ -5,3 +5,4 @@
**/lib
**/es
**\__snapshots__\**
pnpm-lock.yaml

213
.umirc.js
View File

@ -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.

View File

@ -1 +0,0 @@
nodeLinker: node-modules

View File

@ -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
View 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)

View File

@ -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

View File

@ -1,11 +1,6 @@
---
title: 组件总览
order: 0
group:
path: /
nav:
title: 组件
path: /components
---
# 架构设计

View 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`。

View File

@ -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
View 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`

View File

@ -1,8 +1,7 @@
---
title: FAQ
order: 3
group:
path: /
nav:
title: FAQ
path: /docs

View File

@ -1,8 +1,7 @@
---
title: Quick Start
order: 2
group:
path: /
nav:
title: Documentation
path: /docs

View File

@ -1,8 +1,7 @@
---
title: 快速开始
order: 2
group:
path: /
nav:
title: 文档
path: /docs

View File

@ -1,8 +1,7 @@
---
title: Introduction
order: 1
group:
path: /
nav:
title: Documentation
order: 1

View File

@ -1,8 +1,7 @@
---
title: 简介
order: 1
group:
path: /
nav:
title: 文档
order: 1

View File

@ -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
---

View File

@ -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
---

View 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>

View File

@ -3,8 +3,6 @@ title: CRUD
nav:
title: Playground
path: /playground
group:
path: /
---
# CRUD

View File

@ -3,8 +3,6 @@ title: ProDescriptions
nav:
title: Playground
path: /playground
group:
path: /
---
# ProDescriptions Playground

View 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>

View 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>

View File

@ -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