chore: auto merge branches (#39441)

chore: merge master into feature
This commit is contained in:
github-actions[bot] 2022-12-09 10:13:56 +00:00 committed by GitHub
commit 5d376aefb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 3585 additions and 2497 deletions

View File

@ -115,8 +115,14 @@ const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] =>
});
}
} else {
const list = group.children || [];
// 如果有 date 字段,我们就对其进行排序
if (list.every((info) => info?.frontmatter?.date)) {
list.sort((a, b) => (a.frontmatter.date > b.frontmatter.date ? -1 : 1));
}
result.push(
...(group.children?.map((item) => ({
...list.map((item) => ({
label: (
<Link to={`${item.link}${search}`}>
{before}
@ -125,7 +131,7 @@ const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] =>
</Link>
),
key: item.link.replace(/(-cn$)/g, ''),
})) ?? []),
})),
);
}
return result;

View File

@ -1,21 +1,140 @@
import React, { useState } from 'react';
import { ThemeEditor } from 'antd-token-previewer';
import { ConfigProvider } from 'antd';
import React, { useEffect } from 'react';
import { enUS, ThemeEditor, zhCN } from 'antd-token-previewer';
import { Button, ConfigProvider, message, Modal, Typography } from 'antd';
import type { ThemeConfig } from 'antd/es/config-provider/context';
import { Helmet } from 'dumi';
import { css } from '@emotion/react';
import CopyToClipboard from 'react-copy-to-clipboard';
import { CopyOutlined } from '@ant-design/icons';
import useLocale from '../../hooks/useLocale';
const locales = {
cn: {
title: '主题编辑器',
save: '保存',
reset: '重置',
export: '导出',
exportDesc: '将下面的 JSON 对象复制到 ConfigProvider 的 theme 属性中即可。',
saveSuccessfully: '保存成功',
},
en: {
title: 'Theme Editor',
save: 'Save',
reset: 'Reset',
export: 'Export',
exportDesc: 'Copy the following JSON object to the theme prop of ConfigProvider.',
saveSuccessfully: 'Saved successfully',
},
};
const useStyle = () => ({
header: css({
display: 'flex',
height: 56,
alignItems: 'center',
padding: '0 24px',
justifyContent: 'space-between',
borderBottom: '1px solid #F0F0F0',
}),
});
const ANT_DESIGN_V5_THEME_EDITOR_THEME = 'ant-design-v5-theme-editor-theme';
const CustomTheme = () => {
const [theme, setTheme] = useState<ThemeConfig>({});
const [messageApi, contextHolder] = message.useMessage();
const [modalApi, modalContextHolder] = Modal.useModal();
const [locale, lang] = useLocale(locales);
const [theme, setTheme] = React.useState<ThemeConfig>({});
useEffect(() => {
const storedConfig = localStorage.getItem(ANT_DESIGN_V5_THEME_EDITOR_THEME);
if (storedConfig) {
setTheme(() => JSON.parse(storedConfig));
}
}, []);
const styles = useStyle();
const handleSave = () => {
localStorage.setItem(ANT_DESIGN_V5_THEME_EDITOR_THEME, JSON.stringify(theme));
messageApi.success(locale.saveSuccessfully);
};
const onCopy = (text: string, result: boolean) => {
if (result) {
messageApi.success('Copy theme config successfully!');
} else {
messageApi.error('Copy failed, please try again.');
}
};
const handleOutput = () => {
modalApi.info({
title: locale.export,
width: 600,
content: (
<div>
<div style={{ color: 'rgba(0,0,0,0.65)' }}>{locale.exportDesc}</div>
<pre
style={{
padding: 12,
background: '#f5f5f5',
borderRadius: 4,
marginTop: 12,
position: 'relative',
}}
>
<CopyToClipboard text={JSON.stringify(theme, null, 2)} onCopy={onCopy}>
<Button
type="text"
icon={<CopyOutlined />}
style={{ position: 'absolute', right: 8, top: 8 }}
/>
</CopyToClipboard>
{JSON.stringify(theme, null, 2)}
</pre>
</div>
),
});
};
const handleReset = () => {
setTheme({});
};
return (
<div>
<Helmet>
<title>{`${locale.title} - Ant Design`}</title>
<meta property="og:title" content={`${locale.title} - Ant Design`} />
</Helmet>
{contextHolder}
{modalContextHolder}
<ConfigProvider theme={{ inherit: false }}>
<div css={styles.header}>
<Typography.Title level={5} style={{ margin: 0 }}>
{locale.title}
</Typography.Title>
<div>
<Button onClick={handleOutput} style={{ marginRight: 8 }}>
{locale.export}
</Button>
<Button onClick={handleReset} style={{ marginRight: 8 }}>
{locale.reset}
</Button>
<Button type="primary" onClick={handleSave}>
{locale.save}
</Button>
</div>
</div>
<ThemeEditor
theme={{ name: 'Custom Theme', key: 'test', config: theme }}
simple
style={{ height: 'calc(100vh - 64px)' }}
style={{ height: 'calc(100vh - 64px - 56px)' }}
onThemeChange={(newTheme) => {
setTheme(newTheme.config);
}}
locale={lang === 'cn' ? zhCN : enUS}
/>
</ConfigProvider>
</div>

View File

@ -0,0 +1,50 @@
import * as React from 'react';
import { TinyColor, type ColorInput } from '@ctrl/tinycolor';
import { css } from '@emotion/react';
import useSiteToken from '../../../hooks/useSiteToken';
interface ColorChunkProps {
children?: React.ReactNode;
color?: ColorInput;
}
const useStyle = () => {
const { token } = useSiteToken();
return {
codeSpan: css`
padding: 0.2em 0.4em;
font-size: 0.9em;
background: ${token.siteMarkdownCodeBg};
border-radius: ${token.borderRadius}px;
font-family: monospace;
`,
dot: css`
display: inline-block;
width: 6px;
height: 6px;
border-radius: ${token.borderRadiusSM}px;
margin-right: 4px;
border: 1px solid ${token.colorSplit};
`,
};
};
const ColorChunk: React.FC<ColorChunkProps> = (props) => {
const styles = useStyle();
const { color, children } = props;
const dotColor = React.useMemo(() => {
const _color = new TinyColor(color).toHex8String();
return _color.endsWith('ff') ? _color.slice(0, -2) : _color;
}, [color]);
return (
<span css={styles.codeSpan}>
<span css={styles.dot} style={{ backgroundColor: dotColor }} />
{children ?? dotColor}
</span>
);
};
export default ColorChunk;

View File

@ -2,28 +2,39 @@ import React from 'react';
import classNames from 'classnames';
import { Modal, Carousel } from 'antd';
function isGood(className) {
function isGood(className: string): boolean {
return /\bgood\b/i.test(className);
}
function isBad(className) {
function isBad(className: string): boolean {
return /\bbad\b/i.test(className);
}
function isInline(className) {
function isInline(className: string): boolean {
return /\binline\b/i.test(className);
}
function PreviewImageBox({
cover,
coverMeta,
imgs,
style,
previewVisible,
comparable,
onClick,
onCancel,
}) {
function isGoodBadImg(imgMeta: any): boolean {
return imgMeta.isGood || imgMeta.isBad;
}
function isCompareImg(imgMeta: any): boolean {
return isGoodBadImg(imgMeta) || imgMeta.inline;
}
interface PreviewImageBoxProps {
coverMeta: any;
cover: React.ReactNode;
imgs: React.ReactNode[];
style: React.CSSProperties;
previewVisible: boolean;
comparable: boolean;
onClick: () => void;
onCancel: () => void;
}
const PreviewImageBox: React.FC<PreviewImageBoxProps> = (props) => {
const { cover, coverMeta, imgs, style, previewVisible, comparable, onClick, onCancel } = props;
const onlyOneImg = comparable || imgs.length === 1;
const imageWrapperClassName = classNames('preview-image-wrapper', {
good: coverMeta.isGood,
@ -58,30 +69,27 @@ function PreviewImageBox({
</Modal>
</div>
);
};
interface ImagePreviewProps {
imgs: any[];
}
function isGoodBadImg(imgMeta) {
return imgMeta.isGood || imgMeta.isBad;
interface ImagePreviewStates {
previewVisible?: Record<PropertyKey, boolean>;
}
function isCompareImg(imgMeta) {
return isGoodBadImg(imgMeta) || imgMeta.inline;
}
export default class ImagePreview extends React.Component {
constructor(props) {
class ImagePreview extends React.Component<ImagePreviewProps, ImagePreviewStates> {
constructor(props: ImagePreviewProps) {
super(props);
this.state = {
previewVisible: {},
};
}
handleClick = (index) => {
handleClick = (index: number) => {
this.setState({
previewVisible: {
[index]: true,
},
previewVisible: { [index]: true },
});
};
@ -107,7 +115,7 @@ export default class ImagePreview extends React.Component {
};
});
const imagesList = imgsMeta.map((meta, index) => {
const imagesList = imgsMeta.map<React.ReactNode>((meta, index) => {
const metaCopy = { ...meta };
delete metaCopy.description;
delete metaCopy.isGood;
@ -125,7 +133,9 @@ export default class ImagePreview extends React.Component {
(imgs.length === 2 && imgsMeta.every(isCompareImg)) ||
(imgs.length >= 2 && imgsMeta.every(isGoodBadImg));
const style = comparable ? { width: `${(100 / imgs.length).toFixed(3)}%` } : null;
const style: React.CSSProperties = comparable
? { width: `${(100 / imgs.length).toFixed(3)}%` }
: {};
const hasCarousel = imgs.length > 1 && !comparable;
const previewClassName = classNames({
@ -140,20 +150,19 @@ export default class ImagePreview extends React.Component {
if (!comparable && index !== 0) {
return null;
}
return (
<PreviewImageBox
key={index}
style={style}
comparable={comparable}
previewVisible={!!this.state.previewVisible[index]}
previewVisible={!!this.state.previewVisible?.[index]}
cover={imagesList[index]}
coverMeta={imgsMeta[index]}
imgs={imagesList}
onCancel={this.handleCancel}
onClick={() => {
this.handleClick(index);
}}
onCancel={this.handleCancel}
/>
);
})}
@ -161,3 +170,5 @@ export default class ImagePreview extends React.Component {
);
}
}
export default ImagePreview;

View File

@ -1,10 +1,9 @@
import React, { useEffect, useState } from 'react';
// @ts-ignore
import JsonML from 'jsonml.js/lib/utils';
// @ts-ignore
import toReactComponent from 'jsonml-to-react-element';
import Prism from 'prismjs';
import { useLocation, useIntl, type IPreviewerProps } from 'dumi';
import 'prismjs/components/prism-typescript';
import { useLocation, useSearchParams, useIntl, type IPreviewerProps } from 'dumi';
import { ping } from '../../utils';
let pingDeferrer: PromiseLike<boolean>;
@ -37,6 +36,7 @@ export default function fromDumiProps<P extends object>(
const hoc = function DumiPropsAntdPreviewer(props: IPreviewerProps) {
const showRiddleButton = useShowRiddleButton();
const location = useLocation();
const [searchParams] = useSearchParams();
const { asset, children, demoUrl, expand, description = '', ...meta } = props;
const intl = useIntl();
const entryCode = asset.dependencies['index.tsx'].value;
@ -75,9 +75,13 @@ export default function fromDumiProps<P extends object>(
},
intl: { locale: intl.locale },
showRiddleButton,
sourceCodes: {
jsx: meta.jsx,
tsx: entryCode,
},
highlightedCodes: {
jsx: Prism.highlight(meta.jsx, Prism.languages.javascript, 'jsx'),
tsx: Prism.highlight(entryCode, Prism.languages.javascript, 'tsx'),
tsx: Prism.highlight(entryCode, Prism.languages.typescript, 'tsx'),
},
style: meta.style,
location,
@ -85,8 +89,7 @@ export default function fromDumiProps<P extends object>(
expand,
// FIXME: confirm is there has any case?
highlightedStyle: '',
// FIXME: dumi support usePrefersColor
theme: 'light',
theme: searchParams.get('theme'),
} as P;
return <WrappedComponent {...transformedProps} />;

View File

@ -1,6 +1,7 @@
/* eslint jsx-a11y/no-noninteractive-element-interactions: 0 */
import { CheckOutlined, SnippetsOutlined, ThunderboltOutlined } from '@ant-design/icons';
import stackblitzSdk from '@stackblitz/sdk';
import type { Project } from '@stackblitz/sdk';
import { Alert, Badge, Tooltip, Space } from 'antd';
import classNames from 'classnames';
import LZString from 'lz-string';
@ -16,26 +17,55 @@ import CodeSandboxIcon from '../../common/CodeSandboxIcon';
import RiddleIcon from '../../common/RiddleIcon';
import ExternalLinkIcon from '../../common/ExternalLinkIcon';
import fromDumiProps from './fromDumiProps';
import { version } from '../../../../package.json';
const { ErrorBoundary } = Alert;
function compress(string) {
function compress(string: string): string {
return LZString.compressToBase64(string)
.replace(/\+/g, '-') // Convert '+' to '-'
.replace(/\//g, '_') // Convert '/' to '_'
.replace(/=+$/, ''); // Remove ending '='
}
class Demo extends React.Component {
iframeRef = React.createRef();
interface DemoProps {
meta: any;
src: string;
content: string;
preview: (react: typeof React, reactDOM: typeof ReactDOM) => React.ReactNode;
highlightedCodes: Record<PropertyKey, string>;
style: string;
highlightedStyle: string;
expand: boolean;
intl: any;
theme: string;
sourceCodes: Record<'jsx' | 'tsx', string>;
location: Location;
showRiddleButton: boolean;
utils?: any;
}
codeSandboxIconRef = React.createRef();
interface DemoState {
codeType?: string;
copied?: boolean;
codeExpand?: boolean;
copyTooltipOpen?: boolean | string;
}
riddleIconRef = React.createRef();
class Demo extends React.Component<DemoProps, DemoState> {
liveDemo: any;
codepenIconRef = React.createRef();
iframeRef = React.createRef<HTMLIFrameElement>();
state = {
anchorRef = React.createRef<HTMLAnchorElement>();
codeSandboxIconRef = React.createRef<HTMLFormElement>();
riddleIconRef = React.createRef<HTMLFormElement>();
codepenIconRef = React.createRef<HTMLFormElement>();
state: DemoState = {
codeExpand: false,
copied: false,
copyTooltipOpen: false,
@ -45,11 +75,11 @@ class Demo extends React.Component {
componentDidMount() {
const { meta, location } = this.props;
if (meta.id === location.hash.slice(1)) {
this.anchor.click();
this.anchorRef.current?.click();
}
}
shouldComponentUpdate(nextProps, nextState) {
shouldComponentUpdate(nextProps: DemoProps, nextState: DemoState) {
const { codeExpand, copied, copyTooltipOpen, codeType } = this.state;
const { expand, theme, showRiddleButton } = this.props;
return (
@ -62,55 +92,31 @@ class Demo extends React.Component {
);
}
getSourceCode() {
const { highlightedCodes } = this.props;
const { codeType } = this.state;
if (typeof document !== 'undefined') {
const div = document.createElement('div');
const divJSX = document.createElement('div');
div.innerHTML = highlightedCodes[codeType] || highlightedCodes.jsx;
divJSX.innerHTML = highlightedCodes.jsx;
return [divJSX.textContent, div.textContent];
}
return ['', ''];
}
getSourceCode = (): [string, string] => {
const { sourceCodes } = this.props;
return [sourceCodes.jsx, sourceCodes.tsx];
};
handleCodeExpand = (demo) => {
handleCodeExpand = (demo: string) => {
const { codeExpand } = this.state;
this.setState({ codeExpand: !codeExpand });
this.track({
type: 'expand',
demo,
});
this.track({ type: 'expand', demo });
};
saveAnchor = (anchor) => {
this.anchor = anchor;
};
handleCodeCopied = (demo) => {
handleCodeCopied = (demo: string) => {
this.setState({ copied: true });
this.track({
type: 'copy',
demo,
});
this.track({ type: 'copy', demo });
};
onCopyTooltipOpenChange = (open) => {
onCopyTooltipOpenChange = (open: boolean) => {
if (open) {
this.setState({
copyTooltipOpen: open,
copied: false,
});
this.setState({ copyTooltipOpen: open, copied: false });
return;
}
this.setState({
copyTooltipOpen: open,
});
this.setState({ copyTooltipOpen: open });
};
// eslint-disable-next-line class-methods-use-this
track({ type, demo }) {
track = ({ type, demo }: { type: string; demo: string }) => {
if (!window.gtag) {
return;
}
@ -118,7 +124,7 @@ class Demo extends React.Component {
event_category: type,
event_label: demo,
});
}
};
render() {
const { state } = this;
@ -142,7 +148,6 @@ class Demo extends React.Component {
<BrowserFrame>
<iframe
ref={this.iframeRef}
onLoad={this.handleIframeReady}
src={src}
height={meta.iframe}
title="demo"
@ -191,7 +196,7 @@ class Demo extends React.Component {
const [sourceCode, sourceCodeTyped] = this.getSourceCode();
const suffix = codeType === 'tsx' ? 'tsx' : 'js';
const dependencies = sourceCode.split('\n').reduce(
const dependencies: Record<PropertyKey, string> = sourceCode.split('\n').reduce(
(acc, line) => {
const matches = line.match(/import .+? from '(.+)';$/);
if (matches && matches[1] && !line.includes('antd')) {
@ -204,8 +209,7 @@ class Demo extends React.Component {
}
return acc;
},
// eslint-disable-next-line no-undef
{ antd: antdReproduceVersion },
{ antd: version },
);
dependencies['@ant-design/icons'] = 'latest';
@ -237,15 +241,14 @@ class Demo extends React.Component {
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
editors: '001',
css: '',
// eslint-disable-next-line no-undef
js_external: [
'react@18/umd/react.development.js',
'react-dom@18/umd/react-dom.development.js',
// eslint-disable-next-line no-undef
`antd@${antdReproduceVersion}/dist/antd-with-locales.js`,
'dayjs@1/dayjs.min.js',
`antd@${version}/dist/antd-with-locales.js`,
`@ant-design/icons/dist/index.umd.js`,
'react-router-dom/umd/react-router-dom.min.js',
'react-router@3.x/umd/ReactRouter.min.js',
'react-router-dom/dist/umd/react-router-dom.production.min.js',
'react-router/dist/umd/react-router.production.min.js',
]
.map((url) => `https://unpkg.com/${url}`)
.join(';'),
@ -332,10 +335,11 @@ createRoot(document.getElementById('container')).render(<Demo />);
},
},
};
const stackblitzPrefillConfig = {
const stackblitzPrefillConfig: Project = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
template: 'create-react-app',
dependencies,
description: '',
files: {
'index.css': indexCssContent,
[`index.${suffix}`]: indexJsContent,
@ -343,6 +347,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
'index.html': html,
},
};
if (suffix === 'tsx') {
stackblitzPrefillConfig.files['tsconfig.json'] = tsconfig;
}
@ -358,7 +363,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
<section className="code-box-meta markdown">
<div className="code-box-title">
<Tooltip title={meta.debug ? <FormattedMessage id="app.demo.debug" /> : ''}>
<a href={`#${meta.id}`} ref={this.saveAnchor}>
<a href={`#${meta.id}`} ref={this.anchorRef}>
{localizedTitle}
</a>
</Tooltip>
@ -407,7 +412,6 @@ createRoot(document.getElementById('container')).render(<Demo />);
<CodeSandboxIcon className="code-box-codesandbox" />
</Tooltip>
</form>
{sourceCode && (
<form
className="code-box-code-action"
action="https://codepen.io/pen/define"
@ -416,7 +420,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
ref={this.codepenIconRef}
onClick={() => {
this.track({ type: 'codepen', demo: meta.id });
this.codepenIconRef.current.submit();
this.codepenIconRef.current?.submit();
}}
>
<input type="hidden" name="data" value={JSON.stringify(codepenPrefillConfig)} />
@ -424,7 +428,6 @@ createRoot(document.getElementById('container')).render(<Demo />);
<CodePenIcon className="code-box-codepen" />
</Tooltip>
</form>
)}
<Tooltip title={<FormattedMessage id="app.demo.stackblitz" />}>
<span
className="code-box-code-action"
@ -440,7 +443,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
</Tooltip>
<CopyToClipboard text={sourceCodeTyped} onCopy={() => this.handleCodeCopied(meta.id)}>
<Tooltip
open={copyTooltipOpen}
open={copyTooltipOpen as boolean}
onOpenChange={this.onCopyTooltipOpenChange}
title={<FormattedMessage id={`app.demo.${copied ? 'copied' : 'copy'}`} />}
>
@ -485,8 +488,8 @@ createRoot(document.getElementById('container')).render(<Demo />);
</section>
<section className={highlightClass} key="code">
<CodePreview
toReactComponent={props.utils.toReactComponent}
codes={highlightedCodes}
toReactComponent={props.utils?.toReactComponent}
onCodeTypeChange={(type) => this.setState({ codeType: type })}
/>
{highlightedStyle ? (

View File

@ -9,6 +9,7 @@ import type { TableProps } from 'antd';
import { css } from '@emotion/react';
import useLocale from '../../../hooks/useLocale';
import useSiteToken from '../../../hooks/useSiteToken';
import ColorChunk from '../ColorChunk';
type TokenTableProps = {
type: 'seed' | 'map' | 'alias';
@ -78,25 +79,15 @@ const TokenTable: FC<TokenTableProps> = ({ type }) => {
{
title: locale.value,
key: 'value',
render: (_, record) => (
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
{typeof record.value === 'string' &&
(record.value.startsWith('#') || record.value.startsWith('rgb')) && (
<span
style={{
background: record.value,
display: 'inline-block',
width: 6,
height: 6,
borderRadius: '50%',
boxShadow: 'inset 0 0 0 1px rgba(0, 0, 0, 0.06)',
marginRight: 4,
}}
/>
)}
{typeof record.value !== 'string' ? JSON.stringify(record.value) : record.value}
</span>
),
render: (_, record) => {
const isColor =
typeof record.value === 'string' &&
(record.value.startsWith('#') || record.value.startsWith('rgb'));
if (isColor) {
return <ColorChunk color={record.value}>{record.value}</ColorChunk>;
}
return typeof record.value !== 'string' ? JSON.stringify(record.value) : record.value;
},
},
];

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ import Icon from '@ant-design/icons';
const ThemeIcon: React.FC<{ className?: string }> = (props) => {
const SVGIcon = React.useCallback(
() => (
<svg width={21} height={21} viewBox="0 0 21 21" fill="currentColor" {...props}>
<svg width={20} height={20} viewBox="0 0 24 24" fill="currentColor" {...props}>
<g fillRule="evenodd">
<g fillRule="nonzero">
<path d="M7.02 3.635l12.518 12.518a1.863 1.863 0 010 2.635l-1.317 1.318a1.863 1.863 0 01-2.635 0L3.068 7.588A2.795 2.795 0 117.02 3.635zm2.09 14.428a.932.932 0 110 1.864.932.932 0 010-1.864zm-.043-9.747L7.75 9.635l9.154 9.153 1.318-1.317-9.154-9.155zM3.52 12.473c.514 0 .931.417.931.931v.932h.932a.932.932 0 110 1.864h-.932v.931a.932.932 0 01-1.863 0l-.001-.931h-.93a.932.932 0 010-1.864h.93v-.932c0-.514.418-.931.933-.931zm15.374-3.727a1.398 1.398 0 110 2.795 1.398 1.398 0 010-2.795zM4.385 4.953a.932.932 0 000 1.317l2.046 2.047L7.75 7 5.703 4.953a.932.932 0 00-1.318 0zM14.701.36a.932.932 0 01.931.932v.931h.932a.932.932 0 010 1.864h-.933l.001.932a.932.932 0 11-1.863 0l-.001-.932h-.93a.932.932 0 110-1.864h.93v-.931a.932.932 0 01.933-.932z" />

View File

@ -1,68 +1,51 @@
import React from 'react';
import { FloatButton, theme } from 'antd';
import { FloatButton } from 'antd';
import { FormattedMessage } from 'dumi';
import { DarkTheme, Light, CompactTheme } from 'antd-token-previewer/es/icons';
import ThemeIcon from './ThemeIcon';
const { defaultAlgorithm, darkAlgorithm, compactAlgorithm } = theme;
export type ThemeName = 'light' | 'dark' | 'compact';
export type ThemeSwitchProps = {
value: typeof defaultAlgorithm[];
onChange: (value: typeof defaultAlgorithm[]) => void;
value?: ThemeName[];
onChange: (value: ThemeName[]) => void;
};
const ThemeSwitch: React.FC<ThemeSwitchProps> = ({ value, onChange }) => {
const handleLightSwitch = () => {
let newValue = [...value];
if (value.includes(darkAlgorithm)) {
newValue = newValue.filter((item) => item !== darkAlgorithm);
}
if (!value.includes(defaultAlgorithm)) {
newValue.unshift(defaultAlgorithm);
}
onChange(newValue);
};
const handleDarkSwitch = () => {
let newValue = [...value];
if (value.includes(defaultAlgorithm)) {
newValue = newValue.filter((item) => item !== defaultAlgorithm);
}
if (!value.includes(darkAlgorithm)) {
newValue.push(darkAlgorithm);
}
onChange(newValue);
};
const handleCompactSwitch = () => {
if (value.includes(compactAlgorithm)) {
onChange(value.filter((item) => item !== compactAlgorithm));
} else {
onChange([...value, compactAlgorithm]);
}
};
return (
const ThemeSwitch: React.FC<ThemeSwitchProps> = ({ value, onChange }) => (
<FloatButton.Group trigger="click" icon={<ThemeIcon />}>
<FloatButton
icon={<Light />}
type={value.includes(defaultAlgorithm) ? 'primary' : 'default'}
onClick={handleLightSwitch}
tooltip="Light"
type={!value.includes('dark') ? 'primary' : 'default'}
onClick={() => {
if (value.includes('dark')) {
onChange(value.filter((theme) => theme !== 'dark'));
}
}}
tooltip={<FormattedMessage id="app.theme.switch.default" />}
/>
<FloatButton
icon={<DarkTheme />}
type={value.includes(darkAlgorithm) ? 'primary' : 'default'}
onClick={handleDarkSwitch}
tooltip="Dark"
type={value.includes('dark') ? 'primary' : 'default'}
onClick={() => {
if (!value.includes('dark')) {
onChange([...value, 'dark']);
}
}}
tooltip={<FormattedMessage id="app.theme.switch.dark" />}
/>
<FloatButton
icon={<CompactTheme />}
type={value.includes(compactAlgorithm) ? 'primary' : 'default'}
onClick={handleCompactSwitch}
tooltip="Compact"
type={value.includes('compact') ? 'primary' : 'default'}
onClick={() => {
if (value.includes('compact')) {
onChange(value.filter((theme) => theme !== 'compact'));
} else {
onChange([...value, 'compact']);
}
}}
tooltip={<FormattedMessage id="app.theme.switch.compact" />}
/>
</FloatButton.Group>
);
};
);
export default ThemeSwitch;

View File

@ -0,0 +1,62 @@
import { css, Global } from '@emotion/react';
import React from 'react';
export default () => (
<Global
styles={css`
/* Browser mockup code
* Contribute: https://gist.github.com/jarthod/8719db9fef8deb937f4f
* Live example: https://updown.io
*/
.browser-mockup {
position: relative;
border-top: 2em solid rgba(230, 230, 230, 0.7);
border-radius: 3px 3px 0 0;
box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.28);
}
.browser-mockup::before {
position: absolute;
top: -1.25em;
left: 1em;
display: block;
width: 0.5em;
height: 0.5em;
background-color: #f44;
border-radius: 50%;
box-shadow: 0 0 0 2px #f44, 1.5em 0 0 2px #9b3, 3em 0 0 2px #fb5;
content: '';
}
.browser-mockup.with-tab::after {
position: absolute;
top: -2em;
left: 5.5em;
display: block;
width: 20%;
height: 0;
border-right: 0.8em solid transparent;
border-bottom: 2em solid white;
border-left: 0.8em solid transparent;
content: '';
}
.browser-mockup.with-url::after {
position: absolute;
top: -1.6em;
left: 5.5em;
display: block;
width: ~'calc(100% - 6em)';
height: 1.2em;
background-color: white;
border-radius: 2px;
content: '';
}
.browser-mockup > * {
display: block;
}
`}
/>
);

View File

@ -0,0 +1,60 @@
import { css, Global } from '@emotion/react';
import React from 'react';
export default () => (
<Global
styles={css`
body,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
code,
form,
fieldset,
legend,
input,
textarea,
p,
blockquote,
th,
td,
hr,
button,
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
margin: 0;
padding: 0;
}
ul,
ol {
list-style: none;
}
img {
vertical-align: middle;
border-style: none;
}
`}
/>
);

View File

@ -0,0 +1,375 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
const { antCls, iconCls } = token;
return (
<Global
styles={css`
.code-boxes-col-1-1 {
width: 100%;
}
.code-boxes-col-2-1 {
display: inline-block;
vertical-align: top;
}
.code-box {
position: relative;
display: inline-block;
width: 100%;
margin: 0 0 16px;
overflow: hidden;
border: 1px solid ${token.colorSplit};
border-radius: ${token.borderRadius}px;
transition: all 0.2s;
.code-box-title {
&,
a {
color: ${token.colorText} !important;
background: ${token.colorBgContainer};
}
}
&,
.code-box-demo {
background-color: ${token.colorBgContainer};
}
.markdown {
pre {
margin: 0.5em 0;
padding: 6px 12px;
}
pre code {
margin: 0;
background: #f5f5f5;
}
}
&:target {
border: 1px solid ${token.colorPrimary};
}
&-expand-trigger {
position: relative;
margin-left: 12px;
color: #3b4357;
font-size: 20px;
cursor: pointer;
opacity: 0.75;
transition: all 0.3s;
&:hover {
opacity: 1;
}
${antCls}-row-rtl & {
margin-right: 8px;
margin-left: 0;
}
}
&-title {
position: absolute;
top: -14px;
margin-left: 16px;
padding: 1px 8px;
color: #777;
background: ${token.colorBgContainer};
border-radius: ${token.borderRadius}px ${token.borderRadius}px 0 0;
transition: background-color 0.4s;
${antCls}-row-rtl & {
margin-right: 16px;
margin-left: 0;
border-radius: ${token.borderRadius}px 0 0 ${token.borderRadius}px;
}
a,
a:hover {
color: ${token.colorText};
font-weight: 500;
font-size: ${token.fontSize}px;
}
}
&-description {
padding: 18px 24px 12px;
}
a.edit-button {
position: absolute;
top: 7px;
right: -16px;
padding-right: 6px;
font-size: 12px;
text-decoration: none;
background: inherit;
transform: scale(0.9);
${iconCls} {
color: ${token.colorTextSecondary};
transition: all 0.3s;
&:hover {
color: ${token.colorText};
}
}
${antCls}-row${antCls}-row-rtl & {
right: auto;
left: -22px;
margin-right: 0;
padding-right: 8px;
padding-left: 6px;
}
}
&-demo {
padding: 42px 24px 50px;
color: ${token.colorText};
border-bottom: 1px solid ${token.colorSplit};
}
iframe {
width: 100%;
border: 0;
}
&-meta {
&.markdown {
position: relative;
width: 100%;
font-size: ${token.fontSize}px;
border-radius: 0 0 ${token.borderRadius}px ${token.borderRadius}px;
transition: background-color 0.4s;
}
blockquote {
line-height: 1.5;
}
h4,
section& p {
margin: 0;
}
> p {
width: 100%;
margin: 0.5em 0;
padding-right: 25px;
font-size: 12px;
word-break: break-word;
${antCls}-row-rtl & {
padding-right: 0;
padding-left: 25px;
}
}
}
&.expand &-meta {
border-bottom: 1px dashed ${token.colorSplit};
border-radius: 0;
}
.code-expand-icon {
width: 16px;
height: 16px;
position: relative;
cursor: pointer;
}
.code-expand-icon-show,
.code-expand-icon-hide {
position: absolute;
top: 0;
left: 0;
width: 100%;
max-width: 100%;
margin: 0;
box-shadow: none;
transition: all 0.4s;
user-select: none;
${antCls}-row-rtl & {
right: 0;
left: auto;
}
}
.code-expand-icon-show {
opacity: 0.55;
pointer-events: auto;
&:hover {
opacity: 1;
}
}
.code-expand-icon${antCls}-tooltip-open .code-expand-icon-show {
opacity: 1;
}
.code-expand-icon-hide {
opacity: 0;
pointer-events: none;
}
.highlight-wrapper {
display: none;
overflow: auto;
border-radius: 0 0 ${token.borderRadius}px ${token.borderRadius}px;
&-expand {
display: block;
}
}
.highlight {
position: relative;
pre {
margin: 0;
padding: 0;
background: ${token.colorBgContainer};
}
&:not(:first-child) {
border-top: 1px dashed ${token.colorSplit};
}
}
&-actions {
display: flex;
justify-content: center;
padding: 12px 0;
border-top: 1px dashed ${token.colorSplit};
opacity: 0.7;
transition: opacity 0.3s;
&:hover {
opacity: 1;
}
}
&-actions &-code-action {
position: relative;
display: flex;
align-items: center;
width: 16px;
height: 16px;
color: ${token.colorTextSecondary};
cursor: pointer;
transition: all 0.24s;
&:hover {
color: ${token.colorText};
}
${iconCls} {
display: block;
}
}
&-code-copy {
width: 14px;
height: 14px;
font-size: 14px;
text-align: center;
background: ${token.colorBgContainer};
cursor: pointer;
transition: transform 0.24s;
&${iconCls}-check {
color: ${token['green-6']} !important;
font-weight: bold;
}
}
&-codepen {
width: 14px;
height: 14px;
overflow: hidden;
border: 0;
cursor: pointer;
}
&-riddle {
width: 14px;
height: 14px;
overflow: hidden;
border: 0;
cursor: pointer;
}
&-codesandbox {
width: 16px;
height: 16px;
overflow: hidden;
border: 0;
cursor: pointer;
&:hover {
opacity: 1;
}
}
.highlight-wrapper:hover &-code-copy,
.highlight-wrapper:hover &-codepen,
.highlight-wrapper:hover &-codesandbox,
.highlight-wrapper:hover &-riddle {
opacity: 1;
}
pre {
width: auto;
margin: 0;
code {
background: ${token.colorBgContainer};
border: none;
box-shadow: unset;
}
}
&-debug {
border-color: ${token['purple-3']};
}
&-debug &-title a {
color: ${token['purple-6']};
}
}
.demo-wrapper {
position: relative;
}
.all-code-box-controls {
position: absolute;
top: -32px;
inset-inline-end: 0;
}
${antCls}-row-rtl {
#components-tooltip-demo-placement,
#components-popover-demo-placement,
#components-popconfirm-demo-placement {
.code-box-demo {
direction: ltr;
}
}
}
`}
/>
);
};

View File

@ -0,0 +1,43 @@
import React from 'react';
import { css, Global } from '@emotion/react';
export default () => (
<Global
styles={css`
h1,
h2,
h3,
h4,
h5,
h6 {
> a[aria-hidden]:first-child {
float: left;
width: 20px;
padding-inline-end: 4px;
margin-inline-start: -24px;
// hide phantom blank node
font-size: 0;
text-align: right;
line-height: inherit;
[data-direction='rtl'] & {
float: right;
}
&:hover {
border: 0;
}
> .icon-link::before {
content: '#';
font-size: 20px;
}
}
&:not(:hover) > a[aria-hidden]:first-child > .icon-link {
visibility: hidden;
}
}
`}
/>
);

View File

@ -0,0 +1,157 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
return (
<Global
styles={css`
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
pre code {
display: block;
padding: 16px 32px;
color: ${token.colorText};
font-size: ${token.fontSize}px;
font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
line-height: 2;
white-space: pre;
background: white;
border: 1px solid #e9e9e9;
border-radius: ${token.borderRadius}px;
}
code[class*='language-'],
pre[class*='language-'] {
color: black;
font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
line-height: 1.5;
direction: ltr;
white-space: pre;
text-align: left;
word-wrap: normal;
word-break: normal;
word-spacing: normal;
tab-size: 4;
hyphens: none;
background: none;
}
code[class*='css'] {
direction: ltr;
}
pre[class*='language-'] ::selection,
code[class*='language-'] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*='language-'],
pre[class*='language-'] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*='language-'] {
margin: 16px 0;
padding: 12px 20px;
overflow: auto;
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
background: #f5f5f5;
}
/* Inline code */
:not(pre) > code[class*='language-'] {
padding: 0.1em;
white-space: normal;
border-radius: 0.3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: 0.7;
}
.markdown {
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #f81d22;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #0b8235;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #0b8235;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #008dff;
}
.token.function {
color: #f81d22;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
}
`}
/>
);
};

View File

@ -0,0 +1,111 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
const { antCls, iconCls } = token;
return (
<Global
styles={css`
ul.anticons-list {
margin: 10px 0;
overflow: hidden;
direction: ltr;
list-style: none;
li {
position: relative;
float: left;
width: 16.66%;
height: 100px;
margin: 3px 0;
padding: 10px 0 0;
overflow: hidden;
color: #555;
text-align: center;
list-style: none;
background-color: inherit;
border-radius: 4px;
cursor: pointer;
transition: color 0.3s ease-in-out, background-color 0.3s ease-in-out;
.rtl & {
margin: 3px 0;
padding: 10px 0 0;
}
${iconCls} {
margin: 12px 0 8px;
font-size: 36px;
transition: transform 0.3s ease-in-out;
will-change: transform;
}
.anticon-class {
display: block;
font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono',
monospace;
white-space: nowrap;
text-align: center;
transform: scale(0.83);
${antCls}-badge {
transition: color 0.3s ease-in-out;
}
}
&:hover {
color: #fff;
background-color: ${token.colorPrimary};
${iconCls} {
transform: scale(1.4);
}
${antCls}-badge {
color: #fff;
}
}
&.TwoTone:hover {
background-color: #8ecafe;
}
&.copied:hover {
color: rgba(255, 255, 255, 0.2);
}
&::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: #fff;
background: #1677ff;
line-height: 110px;
text-align: center;
opacity: 0;
transition: all 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
content: 'Copied!';
}
&.copied::after {
opacity: 1;
}
}
}
.copied-code {
padding: 2px 4px;
font-size: 12px;
background: #f5f5f5;
border-radius: 2px;
}
`}
/>
);
};

View File

@ -0,0 +1,72 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
const { iconCls } = token;
return (
<Global
styles={css`
.icon-pic-searcher {
display: inline-block;
margin: 0 8px;
.icon-pic-btn {
color: ${token.colorIcon};
cursor: pointer;
transition: all 0.3s;
&:hover {
color: ${token.colorIconHover};
}
}
}
.icon-pic-preview {
width: 66px;
height: 66px;
margin-top: 10px;
padding: 8px;
text-align: center;
border: 1px solid ${token.colorBorder};
border-radius: 4px;
> img {
max-width: 50px;
max-height: 50px;
}
}
.icon-pic-search-result {
min-height: 50px;
padding: 0 10px;
> .result-tip {
padding: 10px 0;
color: ${token.colorTextSecondary};
}
> table {
width: 100%;
.col-icon {
width: 80px;
padding: 10px 0;
> ${iconCls} {
font-size: 30px;
:hover {
color: ${token.colorLinkHover};
}
}
}
}
}
`}
/>
);
};

View File

@ -0,0 +1,568 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import { TinyColor } from '@ctrl/tinycolor';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
const { antCls } = token;
const demoGridColor = token.colorPrimary;
return (
<Global
styles={css`
.markdown {
color: ${token.colorText};
font-size: 14px;
line-height: 2;
}
.highlight {
line-height: 1.5;
}
.markdown img {
max-width: calc(100% - 32px);
max-height: 100%;
}
.markdown p > img {
margin: 34px 0;
box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35);
}
.markdown p > img.markdown-inline-image {
margin: 0;
box-shadow: none;
}
.markdown h1 {
margin-top: 8px;
margin-bottom: 20px;
color: ${token.colorTextHeading};
font-weight: 500;
font-size: 30px;
font-family: Avenir, ${token.fontFamily}, sans-serif;
line-height: 38px;
.subtitle {
margin-left: 12px;
}
}
.markdown h2 {
font-size: 24px;
line-height: 32px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
clear: both;
margin: 1.6em 0 0.6em;
color: ${token.colorTextHeading};
font-weight: 500;
font-family: Avenir, ${token.fontFamily}, sans-serif;
}
.markdown h3 {
font-size: 18px;
}
.markdown h4 {
font-size: 16px;
}
.markdown h5 {
font-size: 14px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
clear: both;
height: 1px;
margin: 24px 0;
background: ${token.colorSplit};
border: 0;
}
.markdown p,
.markdown pre {
margin: 1em 0;
${antCls}-row-rtl & {
direction: rtl;
text-align: right;
}
}
.markdown ul > li {
margin-left: 20px;
padding-left: 4px;
list-style-type: circle;
.rtl & {
margin-right: 20px;
margin-left: 0;
padding-right: 4px;
padding-left: 0;
}
&:empty {
display: none;
}
}
.markdown ol > li {
margin-left: 20px;
padding-left: 4px;
list-style-type: decimal;
${antCls}-row-rtl & {
margin-right: 20px;
margin-left: 0;
padding-right: 4px;
padding-left: 0;
}
}
.markdown ul > li > p,
.markdown ol > li > p {
margin: 0.2em 0;
}
.markdown code {
margin: 0 1px;
padding: 0.2em 0.4em;
font-size: 0.9em;
background: ${token.siteMarkdownCodeBg};
border: 1px solid ${token.colorSplit};
border-radius: 3px;
}
.markdown pre {
font-family: ${token.codeFamily};
background: ${token.siteMarkdownCodeBg};
border-radius: ${token.borderRadius}px;
}
.markdown pre code {
margin: 0;
padding: 0;
overflow: auto;
color: ${token.colorText};
font-size: ${Math.max(token.fontSize - 1, 12)}px;
direction: ltr;
text-align: left;
background: #f5f5f5;
border: none;
}
.markdown strong,
.markdown b {
font-weight: 500;
}
.markdown .dumi-default-source-code {
margin: 1em 0;
background-color: ${token.siteMarkdownCodeBg};
border-radius: ${token.borderRadius}px;
> pre.prism-code {
padding: 12px 20px;
font-size: 13px;
line-height: 2;
}
}
.markdown .dumi-default-table-content > table,
.markdown > table {
width: 100%;
margin: 8px 0 16px;
direction: ltr;
empty-cells: show;
border: 1px solid ${token.colorSplit};
border-collapse: collapse;
border-spacing: 0;
}
.markdown .dumi-default-table-content,
.markdown {
> table th {
color: #5c6b77;
font-weight: 500;
white-space: nowrap;
background: rgba(0, 0, 0, 0.02);
}
}
.markdown .dumi-default-table-content,
.markdown {
> table th,
> table td {
padding: 16px 24px;
text-align: left;
border: 1px solid ${token.colorSplit};
}
}
.markdown table td > a:not(:last-child) {
margin-right: 0 !important;
&::after {
position: relative !important;
}
}
.markdown blockquote {
margin: 1em 0;
padding-left: 0.8em;
color: ${token.colorTextSecondary};
font-size: 90%;
border-left: 4px solid ${token.colorSplit};
.rtl & {
padding-right: 0.8em;
padding-left: 0;
border-right: 4px solid ${token.colorSplit};
border-left: none;
}
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
margin-left: 8px;
opacity: 0;
transition: opacity 0.3s;
.rtl & {
margin-right: 8px;
margin-left: 0;
}
}
.markdown .waiting {
color: #ccc;
cursor: not-allowed;
}
.markdown a.edit-button {
display: inline-block;
margin-left: 8px;
text-decoration: none;
.rtl & {
margin-right: 8px;
margin-left: 0;
transform: rotateY(180deg);
}
${antCls}icon {
display: block;
color: ${token.colorTextSecondary};
font-size: 16px;
transition: all 0.3s;
&:hover {
color: ${token.colorText};
}
}
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
display: inline-block;
opacity: 1;
}
.markdown > br,
.markdown > p > br {
clear: both;
}
.markdown .dumi-default-table {
.component-api-table {
display: block;
td {
&:first-child {
width: 18%;
color: #595959;
font-weight: 600;
white-space: nowrap;
}
&:nth-child(2) {
width: 55%;
}
&:nth-child(3) {
width: 22%;
color: ${token['magenta-7']};
font-size: ${Math.max(token.fontSize - 1, 12)}px;
}
&:nth-child(4) {
width: 15%;
font-size: ${Math.max(token.fontSize - 1, 12)}px;
}
&:nth-child(5) {
width: 8%;
font-size: ${Math.max(token.fontSize - 1, 12)}px;
}
&:nth-last-child(3):first-child {
width: 38%;
}
&:nth-last-child(3):first-child ~ td:nth-last-child(2) {
width: 70%;
}
}
}
}
.markdown .dumi-default-table {
table {
margin: 0;
overflow-x: auto;
overflow-y: hidden;
font-size: ${Math.max(token.fontSize - 1, 12)}px;
font-family: ${token.codeFamily};
line-height: ${token.lineHeight};
border: 0;
-webkit-overflow-scrolling: touch;
th,
td {
padding: 12px;
border-color: ${token.colorSplit};
border-width: 1px 0;
&:first-child {
border-left: 1px solid ${token.colorSplit};
}
&:last-child {
border-right: 1px solid ${token.colorSplit};
}
}
th {
padding-top: 14px;
border-width: 1px 0 2px;
}
tbody tr {
transition: all 0.3s;
&:hover {
background: rgba(60, 90, 100, 0.04);
}
}
td {
&:first-child {
min-width: 58px;
}
}
}
hr {
margin: 12px 0;
}
}
.grid-demo,
[id^='components-grid-demo-'] {
.demo-row,
.code-box-demo .demo-row {
margin-bottom: 8px;
overflow: hidden;
background-image: linear-gradient(
90deg,
#f5f5f5 4.16666667%,
transparent 4.16666667%,
transparent 8.33333333%,
#f5f5f5 8.33333333%,
#f5f5f5 12.5%,
transparent 12.5%,
transparent 16.66666667%,
#f5f5f5 16.66666667%,
#f5f5f5 20.83333333%,
transparent 20.83333333%,
transparent 25%,
#f5f5f5 25%,
#f5f5f5 29.16666667%,
transparent 29.16666667%,
transparent 33.33333333%,
#f5f5f5 33.33333333%,
#f5f5f5 37.5%,
transparent 37.5%,
transparent 41.66666667%,
#f5f5f5 41.66666667%,
#f5f5f5 45.83333333%,
transparent 45.83333333%,
transparent 50%,
#f5f5f5 50%,
#f5f5f5 54.16666667%,
transparent 54.16666667%,
transparent 58.33333333%,
#f5f5f5 58.33333333%,
#f5f5f5 62.5%,
transparent 62.5%,
transparent 66.66666667%,
#f5f5f5 66.66666667%,
#f5f5f5 70.83333333%,
transparent 70.83333333%,
transparent 75%,
#f5f5f5 75%,
#f5f5f5 79.16666667%,
transparent 79.16666667%,
transparent 83.33333333%,
#f5f5f5 83.33333333%,
#f5f5f5 87.5%,
transparent 87.5%,
transparent 91.66666667%,
#f5f5f5 91.66666667%,
#f5f5f5 95.83333333%,
transparent 95.83333333%
);
}
${antCls}-row > div,
.code-box-demo ${antCls}-row > div {
min-height: 30px;
margin-top: 8px;
margin-bottom: 8px;
color: #fff;
text-align: center;
border-radius: 0;
}
.code-box-demo ${antCls}-row > div:not(.gutter-row) {
padding: 16px 0;
background: ${demoGridColor};
&:nth-child(2n + 1) {
background: ${new TinyColor(demoGridColor).setAlpha(0.75).toHex8String()};
}
}
${antCls}-row .demo-col,
.code-box-demo ${antCls}-row .demo-col {
margin-top: 0;
margin-bottom: 0;
padding: 30px 0;
color: ${token.colorWhite};
font-size: 18px;
text-align: center;
border: none;
}
${antCls}-row .demo-col-1 {
background: ${new TinyColor(demoGridColor).setAlpha(0.75).toHexString()};
}
${antCls}-row .demo-col-2,
.code-box-demo ${antCls}-row .demo-col-2 {
background: ${new TinyColor(demoGridColor).setAlpha(0.75).toHexString()};
}
${antCls}-row .demo-col-3,
.code-box-demo ${antCls}-row .demo-col-3 {
color: #999;
background: rgba(255, 255, 255, 0.2);
}
${antCls}-row .demo-col-4,
.code-box-demo ${antCls}-row .demo-col-4 {
background: ${new TinyColor(demoGridColor).setAlpha(0.6).toHexString()};
}
${antCls}-row .demo-col-5,
.code-box-demo ${antCls}-row .demo-col-5 {
color: #999;
background: rgba(255, 255, 255, 0.2);
}
.code-box-demo .height-100 {
height: 100px;
line-height: 100px;
}
.code-box-demo .height-50 {
height: 50px;
line-height: 50px;
}
.code-box-demo .height-120 {
height: 120px;
line-height: 120px;
}
.code-box-demo .height-80 {
height: 80px;
line-height: 80px;
}
}
[id='components-grid-demo-playground'],
[id='components-grid-demo-gutter'] {
> .code-box-demo ${antCls}-row > div {
margin-top: 0;
margin-bottom: 0;
}
}
// For Changelog
.markdown ul${antCls}-timeline {
line-height: 2;
li${antCls}-timeline-item {
margin: 0;
padding: 0 0 30px;
list-style: none;
${antCls}-timeline-item-content {
position: relative;
top: -14px;
padding-left: 32px;
font-size: 14px;
> h2 {
margin-top: 0;
padding-top: 4px;
direction: ltr;
span {
${antCls}-row-rtl & {
float: right;
}
}
}
}
}
li${antCls}-timeline-item:first-child {
margin-top: 40px;
}
}
`}
/>
);
};

View File

@ -0,0 +1,27 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
return (
<Global
styles={css`
#nprogress {
.bar {
background: ${token.colorPrimary};
}
.peg {
box-shadow: 0 0 10px ${token.colorPrimary}, 0 0 5px ${token.colorPrimary};
}
.spinner-icon {
border-top-color: ${token.colorPrimary};
border-left-color: ${token.colorPrimary};
}
}
`}
/>
);
};

View File

@ -0,0 +1,244 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
return (
<Global
styles={css`
.preview-image-boxes {
display: flex;
float: right;
clear: both;
width: 496px;
margin: 0 0 70px 64px;
&-with-carousel {
width: 420px;
.preview-image-box img {
padding: 0;
}
}
.ant-row-rtl & {
float: left;
margin: 0 64px 70px 0;
}
}
.preview-image-boxes + .preview-image-boxes {
margin-top: -35px;
}
.preview-image-box {
float: left;
width: 100%;
}
.preview-image-box + .preview-image-box {
margin-left: 24px;
.ant-row-rtl & {
margin-right: 24px;
margin-left: 0;
}
}
.preview-image-wrapper {
position: relative;
display: inline-block;
width: 100%;
padding: 16px;
text-align: center;
background: #f2f4f5;
}
.preview-image-wrapper.video {
display: block;
padding: 0;
background: 0;
}
.preview-image-wrapper video {
display: block;
width: 100%;
+ svg {
position: absolute;
top: 0;
left: 0;
}
}
.preview-image-wrapper.good::after {
position: absolute;
bottom: 0;
left: 0;
display: block;
width: 100%;
height: 3px;
background: ${token.colorPrimary};
content: '';
}
.preview-image-wrapper.bad::after {
position: absolute;
bottom: 0;
left: 0;
display: block;
width: 100%;
height: 3px;
background: ${token.colorError};
content: '';
}
.preview-image-title {
margin-top: 20px;
color: ${token.colorText};
font-size: 12px;
}
.preview-image-description {
margin-top: 2px;
color: ${token.colorTextSecondary};
font-size: 12px;
line-height: 1.5;
}
.preview-image-description hr {
margin: 2px 0;
background: none;
border: 0;
}
.preview-image-box img {
max-width: 100%;
padding: 12px;
background: ${token.colorBgContainer};
border-radius: ${token.borderRadius}px;
cursor: pointer;
transition: all 0.3s;
&.no-padding {
padding: 0;
background: none;
}
}
.preview-image-boxes.preview-image-boxes-with-carousel img {
padding: 0;
box-shadow: 0 1px 0 0 #ddd, 0 3px 0 0 ${token.colorBgContainer}, 0 4px 0 0 #ddd,
0 6px 0 0 ${token.colorBgContainer}, 0 7px 0 0 #ddd;
}
.preview-image-box img:hover {
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.3);
}
.preview-img {
float: right;
clear: both;
max-width: 496px !important;
margin: 0 0 70px 64px;
padding: 16px;
background-color: #f2f4f5;
}
.image-modal {
text-align: center;
&-container {
position: relative;
text-align: center;
}
.ant-carousel {
.slick-slider {
padding-bottom: 24px;
img {
display: inline;
max-width: 100%;
}
}
.slick-dots {
bottom: 4px;
li button {
background: #888;
}
}
}
.image-modal-single.slick-slider {
padding-bottom: 0;
}
.image-modal-single .slick-dots {
display: none !important;
}
}
.transition-video-player,
.motion-video-min {
float: right;
width: 600px;
padding: 0 0 70px 20px;
.preview-image-wrapper {
padding: 0;
}
.ant-row-rtl & {
float: left;
}
}
.motion-video-min {
width: 390px;
}
.motion-principle-wrapper {
width: 100%;
max-width: 900px;
margin: 48px 0 24px;
}
.principle-wrapper {
width: 100%;
.principle {
display: inline-block;
box-sizing: border-box;
width: 100%;
min-height: 180px;
margin-right: 12.5%;
margin-bottom: 24px;
padding: 24px;
font-size: 24px;
text-align: center;
border: 1px solid #e8e8e8;
border-radius: 4px;
&:last-child {
margin-right: 0;
}
h4 {
margin: 16px 0 8px;
}
p {
font-size: 12px;
line-height: 24px;
}
}
}
`}
/>
);
};

View File

@ -0,0 +1,63 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
return (
<Global
styles={css`
@font-face {
font-weight: normal;
font-family: PuHuiTi;
src: url('//at.alicdn.com/t/webfont_6e11e43nfj.woff2') format('woff2'),
url('//at.alicdn.com/t/webfont_6e11e43nfj.woff') format('woff'),
/* chrome、firefox */ url('//at.alicdn.com/t/webfont_6e11e43nfj.ttf')
format('truetype'); /* chrome、firefox、opera、Safari, Android, iOS 4.2+ */
font-display: swap;
}
@font-face {
font-weight: bold;
font-family: PuHuiTi;
src: url('//at.alicdn.com/t/webfont_exesdog9toj.woff2') format('woff2'),
url('//at.alicdn.com/t/webfont_exesdog9toj.woff') format('woff'),
/* chrome、firefox */ url('//at.alicdn.com/t/webfont_exesdog9toj.ttf')
format('truetype'); /* chrome、firefox、opera、Safari, Android, iOS 4.2+ */
font-display: swap;
}
// 组件丰富选用自如定制主题随心所欲设计语言与研发框架1234567890 QWERTYUIOPLKJHGFDSAZXCVBNM,.mnbvcxzasdfghjklpoiuytrewq
/* CDN 服务仅供平台体验和调试使用,平台不承诺服务的稳定性,企业客户需下载字体包自行发布使用并做好备份。 */
@font-face {
font-weight: 900;
font-family: 'AliPuHui';
src: url('//at.alicdn.com/wf/webfont/exMpJIukiCms/Gsw2PSKrftc1yNWMNlXgw.woff2')
format('woff2'),
url('//at.alicdn.com/wf/webfont/exMpJIukiCms/vtu73by4O2gEBcvBuLgeu.woff')
format('woff');
font-display: swap;
}
html {
direction: initial;
&.rtl {
direction: rtl;
}
}
body {
overflow-x: hidden;
color: ${token.colorText};
font-size: ${token.fontSize}px;
font-family: ${token.fontFamily};
line-height: ${token.lineHeight};
background: ${token.colorBgContainer};
transition: background 1s cubic-bezier(0.075, 0.82, 0.165, 1);
}
`}
/>
);
};

View File

@ -0,0 +1,341 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
export default () => {
const { token } = useSiteToken();
return (
<Global
styles={css`
.nav-phone-icon {
position: absolute;
top: 25px;
right: 30px;
z-index: 1;
display: none;
width: 16px;
height: 22px;
cursor: pointer;
}
@media only screen and (max-width: ${token.screenLG}px) {
.code-boxes-col-2-1,
.code-boxes-col-1-1 {
float: none;
width: 100%;
max-width: unset;
}
}
@media only screen and (max-width: 767.99px) {
.preview-image-boxes {
float: none;
width: 100%;
margin: 0 !important;
}
.preview-image-box {
width: 100%;
margin: 10px 0;
padding: 0;
}
.image-wrapper {
display: none;
}
div.version {
display: block;
margin: 29px auto 16px;
}
.toc {
display: none;
}
.nav-phone-icon {
display: block;
}
.main {
height: calc(100% - 86px);
}
.aside-container {
float: none;
width: auto;
padding-bottom: 30px;
border-right: 0;
}
.ant-row-rtl {
margin-right: 0;
margin-left: 0;
padding-right: 16px;
padding-left: 16px;
> .markdown > * {
width: 100% !important;
}
}
.main-wrapper {
width: 100%;
margin: 0;
border-radius: 0;
}
.prev-next-nav {
width: ~'calc(100% - 32px)';
margin-left: 16px;
.ant-row-rtl & {
margin-right: 16px;
margin-left: 64px;
}
}
.drawer {
.ant-menu-inline .ant-menu-item::after,
.ant-menu-vertical .ant-menu-item::after {
right: auto;
left: 0;
}
}
/** home 区块 **/
.home-page-wrapper {
.page {
h2 {
margin: 80px auto 64px;
}
}
.parallax-bg {
display: none;
}
}
.banner {
display: block;
height: 632px;
&-bg-wrapper {
display: none;
}
.img-wrapper,
.text-wrapper {
display: inline-block;
width: 100%;
min-width: unset;
max-width: unset;
margin: auto;
text-align: center;
}
.img-wrapper {
position: initial;
margin-top: 20px;
text-align: center;
svg {
width: 100%;
max-width: 260px;
height: auto;
margin: 0 auto;
}
}
.text-wrapper {
min-height: 200px;
margin-top: 32px;
padding: 0;
h1 {
display: none;
}
p {
color: #314659;
font-size: 14px;
line-height: 28px;
}
.banner-btns {
display: block;
min-width: 100%;
white-space: nowrap;
text-align: center;
.banner-btn {
padding: 0 20px;
font-size: 14px;
}
}
.banner-promote {
min-width: 100%;
margin-top: 32px;
.ant-divider {
display: none;
}
a {
font-size: 14px;
white-space: nowrap;
img {
width: 20px;
}
}
}
}
}
.page1 {
min-height: 1300px;
.ant-row {
margin: 24px auto 64px;
> div {
margin-bottom: 48px;
}
}
}
.page2 {
min-height: 840px;
background: ${token.colorBgContainer};
&-content {
box-shadow: none;
}
&-components {
display: none;
}
&-product {
min-height: auto;
padding: 0 16px;
.product-block {
margin-bottom: 34px;
padding-bottom: 35px;
border-bottom: 1px solid ${token.colorSplit};
&:last-child {
margin-bottom: 32px;
border-bottom: none;
.block-text-wrapper {
height: auto;
}
}
.block-image-wrapper {
height: 88px;
img {
height: 100%;
}
}
.block-text-wrapper {
padding-bottom: 0;
border-bottom: none;
h4 {
margin-bottom: 4px;
font-size: 18px;
line-height: 24px;
}
p {
margin-bottom: 8px;
font-size: 12px;
line-height: 20px;
}
a {
line-height: 20px;
}
.components-button-wrapper {
margin-top: 16px;
font-size: 12px;
a {
display: block;
}
}
a.more-mobile-react,
a.more-mobile-angular {
margin-top: 0;
color: ${token.colorLink};
}
a.more-mobile-react:hover,
a.more-mobile-angular:hover {
color: #40a9ff;
}
}
}
}
}
.page3 {
min-height: 688px;
background: url('https://gw.alipayobjects.com/zos/rmsportal/qICoJIqqQRMeRGhPHBBS.svg')
no-repeat;
background-size: cover;
.ant-row {
margin: 0 8px;
}
.page3-block {
margin-bottom: 32px;
padding: 24px;
background: ${token.colorBgContainer};
border-radius: 4px;
box-shadow: 0 8px 16px rgba(174, 185, 193, 0.3);
&:nth-child(2) {
.page3-img-wrapper img {
display: block;
width: 70%;
margin: auto;
}
}
p {
font-size: 12px;
}
.page3-img-wrapper {
width: 20%;
img {
width: 100%;
}
}
.page3-text-wrapper {
width: 80%;
max-width: initial;
margin: 0;
padding-left: 16px;
}
}
}
}
`}
/>
);
};

View File

@ -0,0 +1,12 @@
export { default as HeadingAnchor } from './HeadingAnchor';
export { default as Reset } from './Reset';
export { default as Common } from './Common';
export { default as Markdown } from './Markdown';
export { default as Highlight } from './Highlight';
export { default as Demo } from './Demo';
export { default as Icon } from './Icon';
export { default as IconPickSearcher } from './IconPickSearcher';
export { default as BrowserMockup } from './BrowserMockup';
export { default as Responsive } from './Responsive';
export { default as NProgress } from './NProgress';
export { default as PreviewImage } from './PreviewImage';

View File

@ -1,9 +1,9 @@
import React, { startTransition, useLayoutEffect } from 'react';
import { useOutlet } from 'dumi';
import React from 'react';
import { useOutlet, useSearchParams } from 'dumi';
import { ConfigProvider, theme as antdTheme } from 'antd';
import type { ThemeConfig } from 'antd/es/config-provider/context';
import { createCache, StyleProvider } from '@ant-design/cssinjs';
import ThemeSwitch from '../common/ThemeSwitch';
import type { ThemeName } from '../common/ThemeSwitch';
import useLocation from '../../hooks/useLocation';
const styleCache = createCache();
@ -11,9 +11,8 @@ if (typeof global !== 'undefined') {
(global as any).styleCache = styleCache;
}
const ANT_DESIGN_SITE_THEME = 'antd-site-theme';
const getAlgorithm = (theme: string) => {
const getAlgorithm = (themes: ThemeName[]) =>
(themes || []).map((theme) => {
if (theme === 'dark') {
return antdTheme.darkAlgorithm;
}
@ -21,67 +20,30 @@ const getAlgorithm = (theme: string) => {
return antdTheme.compactAlgorithm;
}
return antdTheme.defaultAlgorithm;
};
const getThemeString = (algorithm: typeof antdTheme.defaultAlgorithm) => {
if (algorithm === antdTheme.darkAlgorithm) {
return 'dark';
}
if (algorithm === antdTheme.compactAlgorithm) {
return 'compact';
}
return 'light';
};
});
const GlobalLayout: React.FC = () => {
const outlet = useOutlet();
const { pathname } = useLocation();
const [theme, setTheme] = React.useState<ThemeConfig>({
algorithm: [antdTheme.defaultAlgorithm],
const [searchParams, setSearchParams] = useSearchParams();
const theme = searchParams.getAll('theme') as unknown as ThemeName[];
const handleThemeChange = (value: ThemeName[]) => {
setSearchParams({
...searchParams,
theme: value,
});
const handleThemeChange = (newTheme: ThemeConfig, ignoreAlgorithm: boolean = true) => {
const nextTheme = { ...newTheme };
if (ignoreAlgorithm) {
nextTheme.algorithm = theme.algorithm;
}
setTheme(nextTheme);
localStorage.setItem(
ANT_DESIGN_SITE_THEME,
JSON.stringify(nextTheme, (key, value) => {
if (key === 'algorithm') {
return Array.isArray(value) ? value.map((item) => getThemeString(item)) : ['light'];
}
return value;
}),
);
};
useLayoutEffect(() => {
const localTheme = localStorage.getItem(ANT_DESIGN_SITE_THEME);
if (localTheme) {
const themeConfig = JSON.parse(localTheme);
if (themeConfig.algorithm) {
themeConfig.algorithm = themeConfig.algorithm.map((item: string) => getAlgorithm(item));
} else {
themeConfig.algorithm = [antdTheme.defaultAlgorithm];
}
startTransition(() => {
setTheme(themeConfig);
});
}
}, []);
return (
<StyleProvider cache={styleCache}>
<ConfigProvider theme={theme}>
<ConfigProvider
theme={{
algorithm: getAlgorithm(theme),
}}
>
{outlet}
{!pathname.startsWith('/~demos') && (
<ThemeSwitch
value={theme.algorithm as []}
onChange={(value) => handleThemeChange({ ...theme, algorithm: value }, false)}
/>
<ThemeSwitch value={theme} onChange={handleThemeChange} />
)}
</ConfigProvider>
</StyleProvider>

View File

@ -1,8 +1,10 @@
import type { ReactNode } from 'react';
import React, { useMemo, useState, useLayoutEffect, useContext } from 'react';
import { useIntl, useRouteMeta, FormattedMessage } from 'dumi';
import { Col, Typography, Avatar, Tooltip, Affix, Anchor } from 'antd';
import { Col, Typography, Avatar, Tooltip, Affix, Anchor, Space } from 'antd';
import { CalendarOutlined } from '@ant-design/icons';
import ContributorsList from '@qixian.cs/github-contributors-list';
import DayJS from 'dayjs';
import { css } from '@emotion/react';
import classNames from 'classnames';
import Footer from '../Footer';
@ -183,6 +185,25 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
/>
)}
</Typography.Title>
{/* 添加作者、时间等信息 */}
{meta.frontmatter.date || meta.frontmatter.author ? (
<Typography.Paragraph style={{ opacity: 0.65 }}>
<Space>
{meta.frontmatter.date && (
<span>
<CalendarOutlined /> {DayJS(meta.frontmatter.date).format('YYYY-MM-DD')}
</span>
)}
{meta.frontmatter.author && (
<Typography.Link href={`https://github.com/${meta.frontmatter.author}`}>
@{meta.frontmatter.author}
</Typography.Link>
)}
</Space>
</Typography.Paragraph>
) : null}
{children}
{meta.frontmatter.filename && (
<ContributorsList

View File

@ -206,7 +206,13 @@ export default ({
blogList.length
? {
label: (
<Link to={utils.getLocalizedPathname(blogList[0].link, isZhCN, search)}>
<Link
to={utils.getLocalizedPathname(
blogList.sort((a, b) => (a.frontmatter.date > b.frontmatter.date ? -1 : 1))[0].link,
isZhCN,
search,
)}
>
{locale.blog}
</Link>
),

View File

@ -1,3 +1 @@
import 'react-github-button/assets/style.css';
import 'docsearch.js/dist/cdn/docsearch.css';
import 'rc-footer/assets/index.css';

View File

@ -1,14 +1,9 @@
<!--
First of all, thank you for your contribution! 😄
For requesting to pull a new feature or bugfix, please send it from a feature/bugfix branch based on the `master` branch.
Before submitting your pull request, please make sure the checklist below is confirmed.
Your pull requests will be merged after one of the collaborators approve.
Thank you!
-->
[[中文版模板 / Chinese template](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE/pr_cn.md)]
@ -29,12 +24,14 @@ Thank you!
- [ ] Code style optimization
- [ ] Test Case
- [ ] Branch merge
- [ ] Workflow
- [ ] Other (about what?)
### 🔗 Related issue link
<!--
1. Put the related issue or discussion links here.
2. close #xxxx or fix #xxxx for instance.
-->
### 💡 Background and solution

View File

@ -40,7 +40,7 @@ jobs:
actions: 'check-issue'
issue-number: ${{ github.event.issue.number }}
# 格式如:'x1,x2' or 'x1,x2/y1,y2' 最多支持 2 个数组
title-includes: '官网,网站,国内,镜像,mobile ant design,mobile.ant.design,ant design,ant design pro,pro.ant.design/挂了,挂掉了,无法访问,不能访问,访问不了,出问题,打不开,登不上,can not open,cannot open,can not be reached'
title-includes: '官网,网站,国内,镜像,mobile ant design,mobile.ant.design,ant design,ant design pro,pro.ant.design/挂了,挂掉了,无法访问,不能访问,访问速度,访问慢,访问不了,出问题,打不开,登不上,can not open,cannot open,can not be reached'
- name: deal website
if: steps.checkid.outputs.check-result == 'true'
@ -49,7 +49,7 @@ jobs:
actions: 'create-comment,close-issue'
issue-number: ${{ github.event.issue.number }}
body: |
Ant Design 系列官网由于某些众所周知的原因无法访问,建议翻墙或访问国内镜像站点。
如果你无法访问 Ant Design 官网,可以先点此[downforeveryoneorjustme.com/ant.design](https://downforeveryoneorjustme.com/ant.design) 检查网站是不是挂了,如果没挂,可以尝试翻墙或访问国内镜像站点。
## 官网
* Ant Design: https://ant.design

View File

@ -39,11 +39,10 @@ jobs:
- Write with a **developer-oriented perspective** and **narrative method**, without describing the details of the repair
- **Describing the problem and the impact on the developer**
- **describing the user-first site problem**, not your solution
- Refer: https://ant.design/changelog#4.9.0
- Refer: https://4x.ant.design/changelog#4.9.0
- 🚨 请填写 PR 中的 changelog
- 请用**面向开发者的角度**和**叙述方式撰写**,不描述修复细节
- **描述问题和对开发者的影响**
- **描述用户第一现场的问题**,而非你的解决方式
- 参考https://ant.design/changelog-cn#4.9.0
- 参考https://4x.ant.design/changelog-cn#4.9.0
skip-title-start: 'docs, chore, test, ci'

View File

@ -23,10 +23,11 @@ jobs:
- name: release antd
uses: actions-cool/release-helper@v2
with:
triger: 'tag'
trigger: 'tag'
changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md'
branch: 'master, 4.x-stable'
tag: '5*, 4*'
latest: '5*'
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }} ${{ secrets.DINGDING_BOT_COLLABORATOR_TOKEN }} ${{ secrets.DINGDING_BOT_MAINTAINER_TOKEN }}
dingding-msg: 'CHANGELOG.zh-CN.md'
msg-title: '# Ant Design {{v}} 发布日志'
@ -38,10 +39,11 @@ jobs:
- name: notice bigfish
uses: actions-cool/release-helper@v2
with:
triger: 'tag'
trigger: 'tag'
changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md'
branch: 'master, 4.x-stable'
tag: '5*, 4*'
latest: '5*'
dingding-token: ${{ secrets.DINGDING_BOT_BIGFISH_TOKEN }} ${{ secrets.DINGDING_BOT_YUNFENGDIE_TOKEN }}
dingding-msg: 'CHANGELOG.zh-CN.md'
dingding-delay-minute: 10

View File

@ -1,8 +1,11 @@
# When publish a release. This workflow will trigger and deploy to site.
# When pushing a tag. this workflow will trigger site deployment and fixed version address comments
name: Deploy website
on:
create
push:
tags:
- '5.*'
workflow_dispatch:
permissions:
contents: write
@ -10,7 +13,7 @@ permissions:
jobs:
setup:
runs-on: ubuntu-latest
if: github.event.ref_type == 'tag' && (contains(github.event.ref, '-') == false)
if: startsWith(github.ref, 'refs/tags/') && (contains(github.ref_name, '-') == false)
steps:
- name: checkout
uses: actions/checkout@v3
@ -63,9 +66,24 @@ jobs:
- name: build
run: npm run predeploy
- name: deploy
- name: Get version
id: publish-version
run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/\./-/g')" >> $GITHUB_OUTPUT
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site
force_orphan: true
- name: Deploy to Surge (with TAG)
run: |
export DEPLOY_DOMAIN=ant-design-${{ steps.publish-version.outputs.VERSION }}.surge.sh
npx surge --project ./_site --domain $DEPLOY_DOMAIN --token ${{ secrets.SURGE_TOKEN }}
- name: Create Commit Comment
uses: peter-evans/commit-comment@v1
with:
body: |
Doc site for this release: https://ant-design-${{ steps.publish-version.outputs.VERSION }}.surge.sh

View File

@ -25,7 +25,7 @@ const transformIgnorePatterns = [
];
function getTestRegex(libDir) {
if (libDir === 'dist') {
if (['dist', 'lib', 'es'].includes(libDir)) {
return 'demo\\.test\\.(j|t)s$';
}
return '.*\\.test\\.(j|t)sx?$';
@ -72,4 +72,5 @@ module.exports = {
url: 'http://localhost',
},
// bail: true,
maxWorkers: '50%',
};

View File

@ -15,6 +15,25 @@ timeline: true
---
## 5.0.5
`2022-12-08`
- 🐞 Fix button hover style in Space.Compact. [#39157](https://github.com/ant-design/ant-design/pull/39157) [@foryuki](https://github.com/foryuki)
- 🐞 Fix Tabs active bar missing sometimes in windows Chrome. [#39352](https://github.com/ant-design/ant-design/pull/39352) [@heiyu4585](https://github.com/heiyu4585)
- 🐞 Fix Divider `horizontal` align issue in flex layout. [#39339](https://github.com/ant-design/ant-design/pull/39339)
- 🐞 Fix Popover width in rtl mode. [#39311](https://github.com/ant-design/ant-design/pull/39311)
- 🐞 Fix Popconfirm padding style issue when `wireframe` is `true`. [#39313](https://github.com/ant-design/ant-design/pull/39313) [@MadCcc](https://github.com/MadCcc)
- 💄 Fix Select search input with white space style issue. [#39299](https://github.com/ant-design/ant-design/pull/39299) [@MadCcc](https://github.com/MadCcc)
- 💄 Fix Tree missing selection style. [#39292](https://github.com/ant-design/ant-design/pull/39292)
- 🐞 Fix FloatButton content not align when customize size. [#39282](https://github.com/ant-design/ant-design/pull/39282) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 Fix RangePicker cell hover style. [#39266](https://github.com/ant-design/ant-design/pull/39266)
- 💄 Optimize Button style under Space.Compact. [#39241](https://github.com/ant-design/ant-design/pull/39241) [@foryuki](https://github.com/foryuki)
- 🌐 Fix `vi_VN` i18n mistake. [#39279](https://github.com/ant-design/ant-design/pull/39279) [@nghiepdev](https://github.com/nghiepdev)
- 🌐 Fix `he_IL` i18n mistake. [#39280](https://github.com/ant-design/ant-design/pull/39280) [@Ran-Sagy](https://github.com/Ran-Sagy)
- TypeScript
- 🤖 Optimize Anchor `onClick` event definition. [#39305](https://github.com/ant-design/ant-design/pull/39305) [@li-jia-nan](https://github.com/li-jia-nan)
## 5.0.4
`2022-12-05`

View File

@ -15,6 +15,25 @@ timeline: true
---
## 5.0.5
`2022-12-08`
- 🐞 修复 Space.Compact 下 Button hover 样式问题。[#39157](https://github.com/ant-design/ant-design/pull/39157) [@foryuki](https://github.com/foryuki)
- 🐞 修复 Tabs 在 windows Chrome 下高亮条有时候会丢失的问题。[#39352](https://github.com/ant-design/ant-design/pull/39352) [@heiyu4585](https://github.com/heiyu4585)
- 🐞 修复 Divider `horizontal` 在 flex 布局下的对齐问题。[#39339](https://github.com/ant-design/ant-design/pull/39339)
- 🐞 修复 Popover 在 rtl 模式下宽度异常的问题。[#39311](https://github.com/ant-design/ant-design/pull/39311)
- 🐞 修复 Popconfirm 组件 token 配置线框化后边框坍缩的样式问题。[#39313](https://github.com/ant-design/ant-design/pull/39313) [@MadCcc](https://github.com/MadCcc)
- 💄 修复 Select 组件搜索框会出现空白区域的样式问题。[#39299](https://github.com/ant-design/ant-design/pull/39299) [@MadCcc](https://github.com/MadCcc)
- 💄 修复 Tree 丢失选中样式的问题。[#39292](https://github.com/ant-design/ant-design/pull/39292)
- 🐞 修复 FloatButton 自定义尺寸时,内容不居中的问题。[#39282](https://github.com/ant-design/ant-design/pull/39282) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 修复 RangePicker 日期 hover 样式。[#39266](https://github.com/ant-design/ant-design/pull/39266)
- 💄 优化 Button 在 Space.Compact 下的 Hover 样式。[#39241](https://github.com/ant-design/ant-design/pull/39241) [@foryuki](https://github.com/foryuki)
- 🌐 修正 `vi_VN` 国际化描述。[#39279](https://github.com/ant-design/ant-design/pull/39279) [@nghiepdev](https://github.com/nghiepdev)
- 🌐 修正 `he_IL` 国际化描述。[#39280](https://github.com/ant-design/ant-design/pull/39280) [@Ran-Sagy](https://github.com/Ran-Sagy)
- TypeScript
- 🤖 优化 Anchor `onClick` 的事件类型定义。[#39305](https://github.com/ant-design/ant-design/pull/39305) [@li-jia-nan](https://github.com/li-jia-nan)
## 5.0.4
`2022-12-05`

View File

@ -81,6 +81,7 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token) => {
flexGrow: 1,
minWidth: token.controlItemWidth,
height: token.dropdownHeight,
margin: 0,
padding: token.paddingXXS,
overflow: 'auto',
verticalAlign: 'top',

View File

@ -53,16 +53,12 @@ const FloatButton: React.ForwardRefRenderFunction<
[prefixCls, description, icon, type],
);
const buttonNode = tooltip ? (
const buttonNode = (
<Tooltip title={tooltip} placement="left">
<div className={`${prefixCls}-body`}>
<Content {...contentProps} />
</div>
</Tooltip>
) : (
<div className={`${prefixCls}-body`}>
<Content {...contentProps} />
</div>
);
if (process.env.NODE_ENV !== 'production') {

View File

@ -64,6 +64,28 @@ exports[`renders ./components/float-button/demo/basic.tsx extend context correct
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
`;
@ -110,6 +132,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>,
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-square"
@ -129,6 +173,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>,
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-square"
@ -171,6 +237,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>,
]
`;
@ -219,6 +307,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
@ -255,6 +365,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
</div>,
<div
@ -299,6 +431,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-square"
@ -335,6 +489,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-square"
@ -371,6 +547,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
</div>,
]
@ -415,6 +613,28 @@ exports[`renders ./components/float-button/demo/group-menu.tsx extend context co
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
</div>
`;
@ -458,6 +678,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-pure ant-float-btn-default ant-float-btn-circle"
@ -494,6 +736,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-pure ant-float-btn-primary ant-float-btn-square"
@ -538,6 +802,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<div
class="ant-float-btn-group ant-float-btn-pure ant-float-btn-group-square ant-float-btn-group-square-shadow"
@ -580,6 +866,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-square"
@ -616,6 +924,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-square"
@ -652,6 +982,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
</div>
<div
@ -698,6 +1050,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
@ -734,6 +1108,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
@ -770,6 +1166,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
</div>
<button
@ -807,6 +1225,28 @@ exports[`renders ./components/float-button/demo/render-panel.tsx extend context
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>
</div>
</div>
@ -893,6 +1333,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>,
]
`;
@ -1003,6 +1465,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>,
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
@ -1043,6 +1527,28 @@ Array [
</div>
</div>
</div>
<div>
<div
class="ant-tooltip"
style="opacity:0"
>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-arrow"
>
<span
class="ant-tooltip-arrow-content"
/>
</div>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</button>,
]
`;

View File

@ -2,7 +2,7 @@ import React from 'react';
import FloatButton from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
describe('FloatButton', () => {
mountTest(FloatButton);
@ -51,4 +51,15 @@ describe('FloatButton', () => {
);
errSpy.mockRestore();
});
it('tooltip should support number `0`', async () => {
jest.useFakeTimers();
const { container } = render(<FloatButton tooltip={0} />);
fireEvent.mouseEnter(container.querySelector<HTMLDivElement>('.ant-float-btn-body')!);
await waitFakeTimer();
const element = container.querySelector('.ant-tooltip')?.querySelector('.ant-tooltip-inner');
expect(element?.textContent).toBe('0');
jest.clearAllTimers();
jest.useRealTimers();
});
});

View File

@ -46,7 +46,7 @@ exports[`renders ./components/input/demo/addon.tsx extend context correctly 1`]
class="ant-input-group-addon"
>
<div
class="ant-select select-before ant-select-single ant-select-show-arrow"
class="ant-select ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
@ -196,7 +196,7 @@ exports[`renders ./components/input/demo/addon.tsx extend context correctly 1`]
class="ant-input-group-addon"
>
<div
class="ant-select select-after ant-select-single ant-select-show-arrow"
class="ant-select ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"

View File

@ -46,7 +46,7 @@ exports[`renders ./components/input/demo/addon.tsx correctly 1`] = `
class="ant-input-group-addon"
>
<div
class="ant-select select-before ant-select-single ant-select-show-arrow"
class="ant-select ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
@ -114,7 +114,7 @@ exports[`renders ./components/input/demo/addon.tsx correctly 1`] = `
class="ant-input-group-addon"
>
<div
class="ant-select select-after ant-select-single ant-select-show-arrow"
class="ant-select ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"

View File

@ -5,21 +5,3 @@
## en-US
Using pre & post tabs example.
```css
.select-before {
width: 90px;
}
.select-after {
width: 80px;
}
[data-theme='compact'] .select-before {
width: 71px;
}
[data-theme='compact'] .select-after {
width: 65px;
}
```

View File

@ -5,13 +5,13 @@ import { Cascader, Input, Select, Space } from 'antd';
const { Option } = Select;
const selectBefore = (
<Select defaultValue="http://" className="select-before">
<Select defaultValue="http://">
<Option value="http://">http://</Option>
<Option value="https://">https://</Option>
</Select>
);
const selectAfter = (
<Select defaultValue=".com" className="select-after">
<Select defaultValue=".com">
<Option value=".com">.com</Option>
<Option value=".jp">.jp</Option>
<Option value=".cn">.cn</Option>

View File

@ -11,6 +11,7 @@ export type InputToken<T extends GlobalToken = FullToken<'Input'>> = T & {
inputPaddingVerticalLG: number;
inputPaddingVerticalSM: number;
inputPaddingHorizontal: number;
inputPaddingHorizontalLG: number;
inputPaddingHorizontalSM: number;
inputBorderHoverColor: string;
inputBorderActiveColor: string;
@ -58,14 +59,14 @@ export const genDisabledStyle = (token: InputToken): CSSObject => ({
const genInputLargeStyle = (token: InputToken): CSSObject => {
const {
inputPaddingVerticalLG,
inputPaddingHorizontal,
fontSizeLG,
lineHeightLG,
borderRadiusLG,
inputPaddingHorizontalLG,
} = token;
return {
padding: `${inputPaddingVerticalLG}px ${inputPaddingHorizontal}px`,
padding: `${inputPaddingVerticalLG}px ${inputPaddingHorizontalLG}px`,
fontSize: fontSizeLG,
lineHeight: lineHeightLG,
borderRadius: borderRadiusLG,
@ -860,8 +861,9 @@ export function initInputToken<T extends GlobalToken = GlobalToken>(token: T): I
token.lineWidth,
0,
),
inputPaddingHorizontal: token.controlPaddingHorizontal - token.lineWidth,
inputPaddingHorizontalSM: token.controlPaddingHorizontalSM - token.lineWidth,
inputPaddingHorizontal: token.paddingSM - token.lineWidth,
inputPaddingHorizontalSM: token.paddingXS - token.lineWidth,
inputPaddingHorizontalLG: token.controlPaddingHorizontal - token.lineWidth,
inputBorderHoverColor: token.colorPrimaryHover,
inputBorderActiveColor: token.colorPrimaryHover,
});

View File

@ -167,7 +167,7 @@ export default genComponentStyleHook(
const combinedToken = mergeToken<MessageToken>(token, {
messageNoticeContentPadding: `${
(token.controlHeightLG - token.fontSize * token.lineHeight) / 2
}px ${token.paddingContentVertical}px`,
}px ${token.paddingSM}px`,
});
return [genMessageStyle(combinedToken)];
},

View File

@ -16,7 +16,7 @@ Radio.
## Examples
<!-- prettier-ignore -->
<!-- prettier-ignore-start -->
<code src="./demo/basic.tsx">Basic</code>
<code src="./demo/disabled.tsx">disabled</code>
<code src="./demo/radiogroup.tsx">Radio Group</code>
@ -28,6 +28,7 @@ Radio.
<code src="./demo/radiobutton-solid.tsx">Solid radio button</code>
<code src="./demo/badge.tsx" debug>Badge style</code>
<code src="./demo/wireframe.tsx" debug>Wireframe</code>
<!-- prettier-ignore-end -->
## API

View File

@ -17,7 +17,7 @@ demo:
## 代码演示
<!-- prettier-ignore -->
<!-- prettier-ignore-start -->
<code src="./demo/basic.tsx">基本</code>
<code src="./demo/disabled.tsx">不可用</code>
<code src="./demo/radiogroup.tsx">单选组合</code>
@ -29,25 +29,28 @@ demo:
<code src="./demo/radiobutton-solid.tsx">填底的按钮样式</code>
<code src="./demo/badge.tsx" debug>测试 Badge 的样式</code>
<code src="./demo/wireframe.tsx" debug>线框风格</code>
<!-- prettier-ignore-end -->
## API
### Radio/Radio.Button
<!-- prettier-ignore -->
| 参数 | 说明 | 类型 | 默认值 |
| -------------- | --------------------------------- | ------- | ------ |
| --- | --- | --- | --- |
| autoFocus | 自动获取焦点 | boolean | false |
| checked | 指定当前是否选中 | boolean | false |
| defaultChecked | 初始是否选中 | boolean | false |
| disabled | 禁用 Radio | boolean | false |
| value | 根据 value 进行比较,判断是否选中 | any | - |
### RadioGroup
### Radio.Group
单选框组合,用于包裹一组 `Radio`
| 参数 | 说明 | 类型 | 默认值 | 版本 | |
| --- | --- | --- | --- | --- | --- |
<!-- prettier-ignore -->
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| buttonStyle | RadioButton 的风格样式,目前有描边和填色两种风格 | `outline` \| `solid` | `outline` | | |
| defaultValue | 默认选中的值 | any | - | | |
| disabled | 禁选所有子单选器 | boolean | false | | |

View File

@ -497,6 +497,7 @@ export default genComponentStyleHook('Radio', (token) => {
colorPrimary,
marginXS,
controlOutlineWidth,
colorTextLightSolid,
wireframe,
} = token;
@ -528,7 +529,7 @@ export default genComponentStyleHook('Radio', (token) => {
radioDotDisabledSize,
radioCheckedColor,
radioDotDisabledColor: colorTextDisabled,
radioSolidCheckedColor: colorBgContainer,
radioSolidCheckedColor: colorTextLightSolid,
radioButtonBg: colorBgContainer,
radioButtonCheckedBg: colorBgContainer,
radioButtonColor,

View File

@ -329,7 +329,7 @@ export default genComponentStyleHook(
(token, { rootPrefixCls }) => {
const selectToken: SelectToken = mergeToken<SelectToken>(token, {
rootPrefixCls,
inputPaddingHorizontalBase: token.controlPaddingHorizontal - 1,
inputPaddingHorizontalBase: token.paddingSM - 1,
});
return [genSelectStyle(selectToken)];

View File

@ -111,7 +111,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
insetInlineStart: 0,
width: token.handleSize,
height: token.handleSize,
backgroundColor: token.colorBgContainer,
backgroundColor: token.colorBgElevated,
boxShadow: `0 0 0 ${token.handleLineWidth}px ${token.colorPrimaryBorder}`,
borderRadius: '50%',
cursor: 'pointer',
@ -177,8 +177,8 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
position: 'absolute',
width: dotSize,
height: dotSize,
backgroundColor: token.colorBgContainer,
border: `${token.handleLineWidth}px solid ${token.colorSplit}`,
backgroundColor: token.colorBgElevated,
border: `${token.handleLineWidth}px solid ${token.colorBorderSecondary}`,
borderRadius: '50%',
cursor: 'pointer',
transition: `border-color ${token.motionDurationSlow}`,
@ -202,14 +202,14 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
[`
${componentCls}-dot
`]: {
backgroundColor: token.colorBgContainer,
backgroundColor: token.colorBgElevated,
borderColor: token.colorTextDisabled,
boxShadow: 'none',
cursor: 'not-allowed',
},
[`${componentCls}-handle::after`]: {
backgroundColor: token.colorBgContainer,
backgroundColor: token.colorBgElevated,
cursor: 'not-allowed',
width: token.handleSize,
height: token.handleSize,
@ -335,7 +335,7 @@ export default genComponentStyleHook(
railSize: 4,
handleSize: controlSize,
handleSizeHover: controlSizeHover,
dotSize: (controlSize / 3) * 2,
dotSize: 8,
handleLineWidth,
handleLineWidthHover,
};

View File

@ -124,7 +124,7 @@ const columns = [
| scroll | Whether the table can be scrollable, [config](#scroll) | object | - | |
| showHeader | Whether to show table header | boolean | true | |
| showSorterTooltip | The header show next sorter direction tooltip. It will be set as the property of Tooltip if its type is object | boolean \| [Tooltip props](/components/tooltip/#API) | true | |
| size | Size of table | `default` \| `middle` \| `small` | `default` | |
| size | Size of table | `large` \| `middle` \| `small` | `large` | |
| sortDirections | Supported sort way, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | |
| sticky | Set sticky header and scroll bar | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 4.6.0 (getContainer: 4.7.0) |
| summary | Summary content | (currentData) => ReactNode | - | |

View File

@ -125,7 +125,7 @@ const columns = [
| scroll | 表格是否可滚动,也可以指定滚动区域的宽、高,[配置项](#scroll) | object | - | |
| showHeader | 是否显示表头 | boolean | true | |
| showSorterTooltip | 表头是否显示下一次排序的 tooltip 提示。当参数类型为对象时,将被设置为 Tooltip 的属性 | boolean \| [Tooltip props](/components/tooltip/) | true | |
| size | 表格大小 | `default` \| `middle` \| `small` | default | |
| size | 表格大小 | `large` \| `middle` \| `small` | `large` | |
| sortDirections | 支持的排序方式,取值为 `ascend` `descend` | Array | \[`ascend`, `descend`] | |
| sticky | 设置粘性头部和滚动条 | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 4.6.0 (getContainer: 4.7.0) |
| summary | 总结栏 | (currentData) => ReactNode | - | |

View File

@ -48,58 +48,12 @@ export interface AliasToken extends MapToken {
colorErrorOutline: string;
// Font
fontSizeSM: number;
fontSize: number;
fontSizeLG: number;
fontSizeXL: number;
/** Operation icon in Select, Cascader, etc. icon fontSize. Normal is same as fontSizeSM */
fontSizeIcon: number;
/**
* @nameZH
* @desc H1 使
* @default 38
*/
fontSizeHeading1: number;
/**
* @nameZH
* @desc h2 使
* @default 30
*/
fontSizeHeading2: number;
/**
* @nameZH
* @desc h3 使
* @default 24
*/
fontSizeHeading3: number;
/**
* @nameZH
* @desc h4 使
* @default 20
*/
fontSizeHeading4: number;
/**
* @nameZH
* @desc h5 使
* @default 16
*/
fontSizeHeading5: number;
/** For heading like h1, h2, h3 or option selected item */
fontWeightStrong: number;
// LineHeight
lineHeight: number;
lineHeightLG: number;
lineHeightSM: number;
lineHeightHeading1: number;
lineHeightHeading2: number;
lineHeightHeading3: number;
lineHeightHeading4: number;
lineHeightHeading5: number;
// Control
controlOutlineWidth: number;
controlItemBgHover: string; // Note. It also is a color

View File

@ -18,7 +18,7 @@ export type {
CommonMapToken,
HeightMapToken,
SizeMapToken,
FontSizeMapToken,
FontMapToken,
StyleMapToken,
} from './maps';
export type { AliasToken } from './alias';

View File

@ -0,0 +1,49 @@
export interface FontMapToken {
// Font Size
fontSizeSM: number;
fontSize: number;
fontSizeLG: number;
fontSizeXL: number;
/**
* @nameZH
* @desc H1 使
* @default 38
*/
fontSizeHeading1: number;
/**
* @nameZH
* @desc h2 使
* @default 30
*/
fontSizeHeading2: number;
/**
* @nameZH
* @desc h3 使
* @default 24
*/
fontSizeHeading3: number;
/**
* @nameZH
* @desc h4 使
* @default 20
*/
fontSizeHeading4: number;
/**
* @nameZH
* @desc h5 使
* @default 16
*/
fontSizeHeading5: number;
// LineHeight
lineHeight: number;
lineHeightLG: number;
lineHeightSM: number;
lineHeightHeading1: number;
lineHeightHeading2: number;
lineHeightHeading3: number;
lineHeightHeading4: number;
lineHeightHeading5: number;
}

View File

@ -1,14 +1,16 @@
import type { ColorPalettes } from '../presetColors';
import type { SeedToken } from '../seeds';
import type { SizeMapToken, HeightMapToken, FontSizeMapToken } from './size';
import type { SizeMapToken, HeightMapToken } from './size';
import type { ColorMapToken } from './colors';
import type { StyleMapToken } from './style';
import type { FontMapToken } from './font';
export * from './colors';
export * from './style';
export * from './size';
export * from './font';
export interface CommonMapToken extends FontSizeMapToken, StyleMapToken {
export interface CommonMapToken extends StyleMapToken {
// Motion
motionDurationFast: string;
motionDurationMid: string;
@ -27,4 +29,5 @@ export interface MapToken
SizeMapToken,
HeightMapToken,
StyleMapToken,
FontMapToken,
CommonMapToken {}

View File

@ -53,15 +53,3 @@ export interface HeightMapToken {
/** @internal */
controlHeightLG: number;
}
// Font
export interface FontSizeMapToken {
/**
* @internal
*/
fontSizes: number[];
/**
* @internal
*/
lineHeights: number[];
}

View File

@ -41,6 +41,7 @@ export interface SeedToken extends PresetColorType {
/**
* @nameZH
* @nameEN Info Color
* @desc Token Alert Tag Progress
* @descEN Used to represent the operation information of the Token sequence, such as Alert, Tag, Progress, and other components use these map tokens.
*/
@ -73,6 +74,7 @@ export interface SeedToken extends PresetColorType {
/**
* @nameZH
* @nameEN Default Font Size
* @desc 使广
* @default 14
*/
@ -232,7 +234,8 @@ export interface SeedToken extends PresetColorType {
// ---------- Style ---------- //
/**
* @nameZH 线
* @nameZH 线
* @nameEN Wireframe Style
* @desc 线使 V4
* @default false
*/

View File

@ -1,15 +1,14 @@
import type { DerivativeFunc } from '@ant-design/cssinjs';
import genControlHeight from '../shared/genControlHeight';
import type { SeedToken, MapToken } from '../../interface';
import type { MapToken, SeedToken } from '../../interface';
import defaultAlgorithm from '../default';
import genCompactSizeMapToken from './genCompactSizeMapToken';
import getFontSizes from '../shared/genFontSizes';
import genFontMapToken from '../shared/genFontMapToken';
const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
const mergedMapToken = mapToken ?? defaultAlgorithm(token);
const fontSize = mergedMapToken.fontSizes[0]; // Smaller size font-size as base
const fontSizes = getFontSizes(fontSize);
const fontSize = mergedMapToken.fontSizeSM; // Smaller size font-size as base
const controlHeight = mergedMapToken.controlHeight - 4;
return {
@ -17,8 +16,7 @@ const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
...genCompactSizeMapToken(mapToken ?? token),
// font
fontSizes: fontSizes.map((fs) => fs.size),
lineHeights: fontSizes.map((fs) => fs.lineHeight),
...genFontMapToken(fontSize),
// controlHeight
controlHeight,

View File

@ -6,6 +6,7 @@ import { defaultPresetColors } from '../seed';
import genColorMapToken from '../shared/genColorMapToken';
import genCommonMapToken from '../shared/genCommonMapToken';
import { generateColorPalettes, generateNeutralColorPalettes } from './colors';
import genFontMapToken from '../shared/genFontMapToken';
export default function derivative(token: SeedToken): MapToken {
const colorPalettes = Object.keys(defaultPresetColors)
@ -33,6 +34,8 @@ export default function derivative(token: SeedToken): MapToken {
generateColorPalettes,
generateNeutralColorPalettes,
}),
// Font
...genFontMapToken(token.fontSize),
// Size
...genSizeMapToken(token),
// Height

View File

@ -1,11 +1,8 @@
import type { CommonMapToken, SeedToken } from '../../interface';
import genFontSizes from './genFontSizes';
import genRadius from './genRadius';
export default function genCommonMapToken(token: SeedToken): CommonMapToken {
const { motionUnit, motionBase, fontSize, borderRadius, lineWidth } = token;
const fontSizes = genFontSizes(fontSize);
const { motionUnit, motionBase, borderRadius, lineWidth } = token;
return {
// motion
@ -13,10 +10,6 @@ export default function genCommonMapToken(token: SeedToken): CommonMapToken {
motionDurationMid: `${(motionBase + motionUnit * 2).toFixed(1)}s`,
motionDurationSlow: `${(motionBase + motionUnit * 3).toFixed(1)}s`,
// font
fontSizes: fontSizes.map((fs) => fs.size),
lineHeights: fontSizes.map((fs) => fs.lineHeight),
// line
lineWidthBold: lineWidth + 1,

View File

@ -0,0 +1,33 @@
import type { FontMapToken } from '../../interface';
import genFontSizes from './genFontSizes';
const genFontMapToken = (fontSize: number): FontMapToken => {
const fontSizePairs = genFontSizes(fontSize);
const fontSizes = fontSizePairs.map((pair) => pair.size);
const lineHeights = fontSizePairs.map((pair) => pair.lineHeight);
return {
fontSizeSM: fontSizes[0],
fontSize: fontSizes[1],
fontSizeLG: fontSizes[2],
fontSizeXL: fontSizes[3],
fontSizeHeading1: fontSizes[6],
fontSizeHeading2: fontSizes[5],
fontSizeHeading3: fontSizes[4],
fontSizeHeading4: fontSizes[3],
fontSizeHeading5: fontSizes[2],
lineHeight: lineHeights[1],
lineHeightLG: lineHeights[2],
lineHeightSM: lineHeights[0],
lineHeightHeading1: lineHeights[6],
lineHeightHeading2: lineHeights[5],
lineHeightHeading3: lineHeights[4],
lineHeightHeading4: lineHeights[3],
lineHeightHeading5: lineHeights[2],
};
};
export default genFontMapToken;

View File

@ -24,7 +24,6 @@ export default function formatToken(derivativeToken: RawMergedToken): AliasToken
...overrideTokens,
};
const { fontSizes, lineHeights } = mergedToken;
const screenXS = 480;
const screenSM = 576;
const screenMD = 768;
@ -32,8 +31,6 @@ export default function formatToken(derivativeToken: RawMergedToken): AliasToken
const screenXL = 1200;
const screenXXL = 1600;
const fontSizeSM = fontSizes[0];
// Generate alias token
const aliasToken: AliasToken = {
...mergedToken,
@ -70,26 +67,7 @@ export default function formatToken(derivativeToken: RawMergedToken): AliasToken
colorWarningOutline: getAlphaColor(mergedToken.colorWarningBg, mergedToken.colorBgContainer),
// Font
fontSizeSM,
fontSize: fontSizes[1],
fontSizeLG: fontSizes[2],
fontSizeXL: fontSizes[3],
fontSizeHeading1: fontSizes[6],
fontSizeHeading2: fontSizes[5],
fontSizeHeading3: fontSizes[4],
fontSizeHeading4: fontSizes[3],
fontSizeHeading5: fontSizes[2],
fontSizeIcon: fontSizeSM,
lineHeight: lineHeights[1],
lineHeightLG: lineHeights[2],
lineHeightSM: lineHeights[0],
lineHeightHeading1: lineHeights[6],
lineHeightHeading2: lineHeights[5],
lineHeightHeading3: lineHeights[4],
lineHeightHeading4: lineHeights[3],
lineHeightHeading5: lineHeights[2],
fontSizeIcon: mergedToken.fontSizeSM,
// Control
lineWidth: mergedToken.lineWidth,

View File

@ -1,6 +1,7 @@
---
order: 0
title: Component-level CSS-in-JS
date: 2022-11-25
author: MadCcc
---
On November 18, 2022, we released Ant Design 5.0. At the same time, Ant Design's unique CSS-in-JS solution was brought into everyone's view. Through this solution, Ant Design achieves higher performance than other CSS-in-JS libraries, but at the cost of sacrificing its flexibility for free use in applications. So we call it a "component-level" CSS-in-JS solution. <a name="W668Z"></a>

View File

@ -1,10 +1,9 @@
---
order: 0
title: 组件级别的 CSS-in-JS
date: 2022-11-25
author: MadCcc
---
`2022-11-25`
在 2022 年 11 月 18 日,我们发布了 Ant Design 5.0 的正式版本,同时带入大家视野中的还有 Ant Design 独特的 CSS-in-JS 方案。通过这个方案Ant Design 获得了相较于其他 CSS-in-JS 库更高的性能,但代价则是牺牲了其在应用中自由使用的灵活性。所以我们把它称为“组件级”的 CSS-in-JS 方案。
## CSS-in-JS 的困境

View File

@ -0,0 +1,113 @@
---
title: Some change on getContainer
date: 2022-12-08
author: zombieJ
---
We often encounter the need for pop-up elements when developing, such as the Select drop-down box, or the Modal component. When it is directly rendered under the current node, it may be clipped by the `overflow: hidden` of the parent node:
![Overflow](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Noh-TYJ0BdcAAAAAAAAAAAAADrJ8AQ/original)
Therefore we render it under `body` by default in Ant Design, but this will bring new problems. Since they are not under the same container, when the user scrolls the screen, they will find that the popup layer does not follow the scrolling:
![Scroll](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*d44KQqkTX90AAAAAAAAAAAAADrJ8AQ/original)
To solve this problem, we provide the `getContainer` property, which allows users to customize the rendered container. The `getContainer` method will be called when the component is mounted, returning a container node, and the component will be rendered under this node through `createPortal`.
```tsx
// Fake Code. Just for Demo
const PopupWrapper = () => {
const eleRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
// It's much complex with timing in real world. You can view the source for more detail:
// https://github.com/react-component/portal/blob/master/src/Portal.tsx
const container: HTMLElement = getContainer(eleRef.current);
// ...
}, []);
return (
<div ref={eleRef}>
{...}
</div>
);
}
```
```tsx
// Fake Code. Just for Demo
const defaultGetContainer = () => {
const div = document.createElement('div');
document.body.appendChild(div);
return div;
};
const SomeComponent = ({ getContainer = defaultGetContainer }) => (
<PopupWrapper getContainer={getContainer} />
);
```
For the time being, we dont pay attention to `getContainer`s need to dynamically switch the mount node (in fact, it has not been able to switch for a long time in the past), only from the perspective of React 18, it has encountered some problems.
## React 18 Concurrent Mode
In React 18, effects may fire multiple times. In order to prevent inadvertently breaking the developer's behavior, it has also been adjusted accordingly under [StrictMode](https://reactjs.org/docs/strict-mode.html):
> - React mounts the component.
> - Layout effects are created.
> - Effect effects are created.
> - React simulates effects being destroyed on a mounted component.
> - Layout effects are destroyed.
> - Effects are destroyed.
> - React simulates effects being re-created on a mounted component.
> - Layout effects are created
> - Effect setup code runs
The simple understanding is that under StrictMode, even if your deps contains empty objects, the effect will still be triggered multiple times. When switching to React 18 StrictMode, we will find that there will be a pair of mount nodes in the HTML, and the previous one is empty:
```html
<body>
<div id="root">...</div>
<!-- Empty -->
<div className="sample-holder"></div>
<!-- Real in use -->
<div className="sample-holder">
<div className="ant-component-wrapper">...</div>
</div>
</body>
```
Therefore, we adjusted the call implementation, and the default `getContainer` is also managed through state to ensure that the nodes generated by the previous effect will be cleaned up in StrictMode:
```tsx
// Fake Code. Just for Demo
const SomeComponent = ({ getContainer }) => {
const [myContainer, setMyContainer] = React.useState<HTMLElement | null>(null);
React.useEffect(() => {
if (getContainer) {
setMyContainer(getContainer());
return;
}
const div = document.createElement('div');
document.body.appendChild(div);
setMyContainer(div);
return () => {
document.body.removeChild(div);
};
}, [getContainer]);
return <PopupWrapper getContainer={() => myContainer} />;
};
```
After putting `getContainer` into effect management, we can manage nodes in a way that is more in line with the React life cycle, and we can also clean up when `getContainer` changes. So as to support the scenario of dynamically changing `getContainer` (although I personally doubt the universality of this usage scenario).
## Finally
Due to the fix that `getContainer` does not support dynamic changes, it also introduces a potential breaking change at the same time. If the developer customizes `getContainer` to create a new dom node every time, it will cause an infinite loop because of the continuous execution of the effect, resulting in the continuous creation of nodes. If you use this method and encounter problems, you need to pay attention to check.

View File

@ -0,0 +1,113 @@
---
title: getContainer 的一些变化
date: 2022-12-08
author: zombieJ
---
在网页开发中,我们时长会遇到弹出元素的需求,比如 Select 的下拉框、或者是 Modal 组件。直接将其渲染到当前节点下时,可能会被父节点的 `overflow: hidden` 裁剪掉:
![Overflow](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Noh-TYJ0BdcAAAAAAAAAAAAADrJ8AQ/original)
因而在 Ant Design 中,我们默认将其渲染到 `body` 下,但是这又会带来新的问题。由于不在同一个容器下,当用户滚动屏幕时会发现弹出层并未跟随滚动:
![Scroll](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*d44KQqkTX90AAAAAAAAAAAAADrJ8AQ/original)
为了解决这个问题,我们提供了 `getContainer` 属性,让用户可以自定义渲染的容器。`getContainer` 方法会在组件挂载时调用,返回一个容器节点,组件会通过 `createPortal` 渲染到这个节点下。
```tsx
// Fake Code. Just for Demo
const PopupWrapper = () => {
const eleRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
// It's much complex with timing in real world. You can view the source for more detail:
// https://github.com/react-component/portal/blob/master/src/Portal.tsx
const container: HTMLElement = getContainer(eleRef.current);
// ...
}, []);
return (
<div ref={eleRef}>
{...}
</div>
);
}
```
```tsx
// Fake Code. Just for Demo
const defaultGetContainer = () => {
const div = document.createElement('div');
document.body.appendChild(div);
return div;
};
const SomeComponent = ({ getContainer = defaultGetContainer }) => (
<PopupWrapper getContainer={getContainer} />
);
```
我们暂时不关注 `getContainer` 需要动态切换挂载节点的需求(其实在过去很长时间它的确也无法切换),仅仅从 React 18 看,它遇到了一些问题。
## React 18 Concurrent Mode
React 18 中effect 可能会多次触发。为了防止不经意间破坏开发者的行为,在 [StrictMode](https://reactjs.org/docs/strict-mode.html) 下它也做了相应的调整:
> - React mounts the component.
> - Layout effects are created.
> - Effect effects are created.
> - React simulates effects being destroyed on a mounted component.
> - Layout effects are destroyed.
> - Effects are destroyed.
> - React simulates effects being re-created on a mounted component.
> - Layout effects are created
> - Effect setup code runs
简单理解就是 StrictMode 下,即便你的 deps 里是空对象effect 仍然会多次触发。在切换为 React 18 StrictMode 的时候,我们会发现在 HTML 中会成对出现挂载节点,同时前一个是空的:
```html
<body>
<div id="root">...</div>
<!-- Empty -->
<div className="sample-holder"></div>
<!-- Real in use -->
<div className="sample-holder">
<div className="ant-component-wrapper">...</div>
</div>
</body>
```
因而我们调整了调用实现,默认的 `getContainer` 也通过 state 进行管理,确保在 StrictMode 下会清理前一个 effect 生成的节点:
```tsx
// Fake Code. Just for Demo
const SomeComponent = ({ getContainer }) => {
const [myContainer, setMyContainer] = React.useState<HTMLElement | null>(null);
React.useEffect(() => {
if (getContainer) {
setMyContainer(getContainer());
return;
}
const div = document.createElement('div');
document.body.appendChild(div);
setMyContainer(div);
return () => {
document.body.removeChild(div);
};
}, [getContainer]);
return <PopupWrapper getContainer={() => myContainer} />;
};
```
`getContainer` 放入 effect 管理后,我们可以更符合 React 生命周期的方式去管理节点,同时也可以在 `getContainer` 变化时进行清理。从而支持动态改变 `getContainer` 的场景(虽然我个人比较怀疑这种使用场景的普遍性)。
## 最后
由于修复了 `getContainer` 不支持动态改变的问题,它也引入了一个潜在的 breaking change。开发者如果自定义 `getContainer` 每次都是创建新的 dom 节点时,它就会因为 effect 不断执行,导致节点不断创建而死循环。如果你使用了这种方式并且遇到了问题,需要注意检查。

View File

@ -39,7 +39,7 @@ const App: React.FC = () => (
export default App;
```
You will get a theme with primary color <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #00b96b; vertical-align: text-bottom;"></div> `#00b96b`. And we can see the change in Button:
You will get a theme with primary color <ColorChunk color="#00b96b" /></ColorChunk>. And we can see the change in Button:
![themed button](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*CbF_RJfKEiwAAAAAAAAAAAAAARQnAQ)
@ -90,7 +90,7 @@ const App: React.FC = () => (
export default App;
```
In this way, we changed the primary color of Radio to <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #00b96b; vertical-align: text-bottom;"></div> `#00b96b`, and Checkbox is not affected.
In this way, we changed the primary color of Radio to <ColorChunk color="#00b96b" /></ColorChunk>, and Checkbox is not affected.
![component token](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*EMY0QrHFDjsAAAAAAAAAAAAAARQnAQ)

View File

@ -39,7 +39,7 @@ const App: React.FC = () => (
export default App;
```
这将会得到一个以 <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #00b96b; vertical-align: text-bottom;"></div> `#00b96b` 为主色的主题,以 Button 组件为例可以看到相应的变化:
这将会得到一个以 <ColorChunk color="#00b96b" /></ColorChunk> 为主色的主题,以 Button 组件为例可以看到相应的变化:
![themed button](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*CbF_RJfKEiwAAAAAAAAAAAAAARQnAQ)
@ -90,7 +90,7 @@ const App: React.FC = () => (
export default App;
```
通过这种方式,我们可以仅将 Radio 组件的主色改为 <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #00b96b; vertical-align: text-bottom;"></div> `#00b96b`而不会影响其他组件。
通过这种方式,我们可以仅将 Radio 组件的主色改为 <ColorChunk color="#00b96b" /></ColorChunk> 而不会影响其他组件。
![component token](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*EMY0QrHFDjsAAAAAAAAAAAAAARQnAQ)

View File

@ -14,7 +14,7 @@ This document will help you upgrade from antd `4.x` version to antd `5.x` versio
### Design specification
- Basic rounded corner adjustment, changed from `2px` to four layers of radius, which are `2px` `4px` `6px` and `8px`. For example, radius of default Button is modified from `2px` to `6px`.
- Primary color adjustment, changed from <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #1890ff; vertical-align: text-bottom;"></div> `#1890ff` to <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #1677ff; vertical-align: text-bottom;"></div> `#1677ff`.
- Primary color adjustment, changed from <ColorChunk color="#1890ff" /></ColorChunk> to <ColorChunk color="#1677ff" /></ColorChunk>.
- Global shadow optimization, adjusted from three layers of shadows to two layers, which are used in common components (Card .e.g) and popup components (Dropdown .e.g).
- Overall reduction in wireframe usage.
@ -207,6 +207,15 @@ const v4Token = convertLegacyToken(mapToken);
}
```
Ant then remove antd less reference in your less file:
```diff
// Your less file
-- @import (reference) '~antd/es/style/themes/index';
or
-- @import '~antd/es/style/some-other-less-file-ref';
```
### Remove babel-plugin-import
Remove `babel-plugin-import` from package.json and modify `.babelrc`:

View File

@ -14,7 +14,7 @@ title: 从 v4 到 v5
### 设计规范调整
- 基础圆角调整,由统一的 `2px` 改为四级圆角,分别为 `2px` `4px` `6px` `8px`,分别应用于不同场景,比如默认尺寸的 Button 的圆角调整为了 `6px`
- 主色调整,由 <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #1890ff; vertical-align: text-bottom;"></div> `#1890ff` 改为 <div style="display: inline-block; width: 16px; height: 16px; border-radius: 4px; background: #1677ff; vertical-align: text-bottom;"></div> `#1677ff`
- 主色调整,由 <ColorChunk color="#1890ff" /></ColorChunk> 改为 <ColorChunk color="#1677ff" /></ColorChunk>
- 整体阴影调整,由原本的三级阴影调整为两级,分别用于常驻页面的组件(如 Card和交互反馈如 Dropdown
- 部分组件内间距调整。
- 整体去线框化。
@ -199,6 +199,15 @@ const v4Token = convertLegacyToken(mapToken);
}
```
同时移除对 antd less 文件的直接引用:
```diff
// Your less file
-- @import (reference) '~antd/es/style/themes/index';
or
-- @import '~antd/es/style/some-other-less-file-ref';
```
### 移除 babel-plugin-import
从 package.json 中移除 `babel-plugin-import`,并从 `.babelrc` 移除该插件:

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "5.0.4",
"version": "5.0.5",
"description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design",
"keywords": [
@ -158,11 +158,8 @@
"shallowequal": "^1.1.0"
},
"devDependencies": {
"@ant-design/bisheng-plugin": "^3.3.0-alpha.4",
"@ant-design/hitu": "^0.0.0-alpha.13",
"@ant-design/tools": "^16.1.0-alpha.2",
"@babel/eslint-plugin": "^7.19.1",
"@docsearch/css": "^3.0.0",
"@emotion/babel-preset-css-prop": "^11.10.0",
"@emotion/css": "^11.10.5",
"@emotion/react": "^11.10.4",
@ -178,7 +175,7 @@
"@types/jest": "^29.0.0",
"@types/jest-axe": "^3.5.3",
"@types/jest-environment-puppeteer": "^5.0.0",
"@types/jest-image-snapshot": "^5.1.0",
"@types/jest-image-snapshot": "^6.1.0",
"@types/jquery": "^3.5.14",
"@types/lodash": "^4.14.139",
"@types/prismjs": "^1.26.0",
@ -199,19 +196,14 @@
"antd-img-crop": "^4.2.8",
"antd-token-previewer": "^1.1.0-6",
"array-move": "^4.0.0",
"babel-plugin-add-react-displayname": "^0.0.5",
"bundlesize2": "^0.0.31",
"chalk": "^4.0.0",
"cheerio": "1.0.0-rc.12",
"concurrently": "^7.0.0",
"cross-env": "^7.0.0",
"css-minimizer-webpack-plugin": "^1.3.0",
"dekko": "^0.2.1",
"docsearch-react-fork": "^0.0.0-alpha.0",
"docsearch.js": "^2.6.3",
"dumi": "^2.0.2",
"duplicate-package-checker-webpack-plugin": "^3.0.0",
"enquire-js": "^0.2.1",
"esbuild-loader": "^2.13.1",
"eslint": "^8.0.0",
"eslint-config-airbnb": "^19.0.0",
@ -228,7 +220,6 @@
"fast-glob": "^3.2.11",
"fetch-jsonp": "^1.1.3",
"fs-extra": "^11.0.0",
"full-icu": "^1.3.0",
"gh-pages": "^4.0.0",
"glob": "^8.0.1",
"highlight.js": "^11.5.0",
@ -237,9 +228,7 @@
"identity-obj-proxy": "^3.0.0",
"immer": "^9.0.1",
"immutability-helper": "^3.0.0",
"increase-memory-limit": "^1.0.7",
"inquirer": "^9.1.2",
"intersection-observer": "^0.12.0",
"isomorphic-fetch": "^3.0.0",
"jest": "^29.0.0",
"jest-axe": "^7.0.0",
@ -275,7 +264,6 @@
"react-dom": "^18.0.0",
"react-draggable": "^4.4.3",
"react-fast-marquee": "^1.2.1",
"react-github-button": "^0.1.11",
"react-highlight-words": "^0.18.0",
"react-infinite-scroll-component": "^6.1.0",
"react-resizable": "^3.0.1",
@ -286,7 +274,7 @@
"remark-cli": "^11.0.0",
"remark-lint": "^9.0.0",
"remark-preset-lint-recommended": "^6.0.0",
"rome": "^10.0.1",
"rome": "^11.0.0",
"semver": "^7.3.5",
"simple-git": "^3.0.0",
"size-limit": "^8.1.0",

View File

@ -11,19 +11,19 @@ const getTokenList = (list, source) =>
desc:
item.comment?.blockTags
?.find((tag) => tag.tag === '@desc')
?.content.reduce((result, str) => result.concat(str.text), '') || '-',
?.content.reduce((result, str) => result.concat(str.text), '') || '',
descEn:
item.comment?.blockTags
?.find((tag) => tag.tag === '@descEN')
?.content.reduce((result, str) => result.concat(str.text), '') || '-',
?.content.reduce((result, str) => result.concat(str.text), '') || '',
name:
item.comment?.blockTags
?.find((tag) => tag.tag === '@nameZH')
?.content.reduce((result, str) => result.concat(str.text), '') || '-',
?.content.reduce((result, str) => result.concat(str.text), '') || '',
nameEn:
item.comment?.blockTags
?.find((tag) => tag.tag === '@nameEN')
?.content.reduce((result, str) => result.concat(str.text), '') || '-',
?.content.reduce((result, str) => result.concat(str.text), '') || '',
}));
function main() {

View File

@ -21,6 +21,7 @@ const DEPRECIATED_VERSION = {
'https://github.com/ant-design/ant-design/issues/37931',
],
'4.24.0': ['https://github.com/ant-design/ant-design/issues/38371'],
'5.0.4': ['https://github.com/ant-design/ant-design/issues/39284'],
};
function matchDeprecated(version) {

View File

@ -8,14 +8,6 @@ declare module '*.svg' {
export default src;
}
declare module 'bisheng/collect';
declare module 'bisheng/router';
declare module 'react-github-button';
declare module 'jsonml.js/*';
declare module 'rc-pagination/*';
declare module 'rc-util*';
@ -24,17 +16,10 @@ declare module 'rc-checkbox';
declare module 'rc-rate';
declare module 'jsonml.js/*';
declare module '*.json' {
const value: any;
export const version: string;
export default value;
}
declare module 'docsearch-react-fork/style/modal';
declare module 'docsearch-react-fork/modal' {
import { DocSearchModal as value } from 'docsearch-react-fork';
export const DocSearchModal = value;
}
declare module 'docsearch.js';