mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-23 01:45:05 +08:00
commit
6c5289a7ab
@ -9,11 +9,10 @@ import {
|
||||
} from '@ant-design/cssinjs';
|
||||
import { HappyProvider } from '@ant-design/happy-work-theme';
|
||||
import { getSandpackCssText } from '@codesandbox/sandpack-react';
|
||||
import { theme as antdTheme, App, unstableSetRender } from 'antd';
|
||||
import { theme as antdTheme, App } from 'antd';
|
||||
import type { MappingAlgorithm } from 'antd';
|
||||
import type { DirectionType, ThemeConfig } from 'antd/es/config-provider';
|
||||
import { createSearchParams, useOutlet, useSearchParams, useServerInsertedHTML } from 'dumi';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { DarkContext } from '../../hooks/useDark';
|
||||
import useLayoutState from '../../hooks/useLayoutState';
|
||||
@ -23,6 +22,8 @@ import SiteThemeProvider from '../SiteThemeProvider';
|
||||
import type { SiteContextProps } from '../slots/SiteContext';
|
||||
import SiteContext from '../slots/SiteContext';
|
||||
|
||||
import '@ant-design/v5-patch-for-react-19';
|
||||
|
||||
const ThemeSwitch = React.lazy(() => import('../common/ThemeSwitch'));
|
||||
|
||||
type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][];
|
||||
@ -31,14 +32,6 @@ type SiteState = Partial<Omit<SiteContextProps, 'updateSiteContext'>>;
|
||||
const RESPONSIVE_MOBILE = 768;
|
||||
export const ANT_DESIGN_NOT_SHOW_BANNER = 'ANT_DESIGN_NOT_SHOW_BANNER';
|
||||
|
||||
unstableSetRender((node, container) => {
|
||||
const root = createRoot(container);
|
||||
root.render(node);
|
||||
return async () => {
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
|
||||
// const styleCache = createCache();
|
||||
// if (typeof global !== 'undefined') {
|
||||
// (global as any).styleCache = styleCache;
|
||||
|
@ -2,7 +2,6 @@ import { createHash } from 'crypto';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import createEmotionServer from '@emotion/server/create-instance';
|
||||
import chalk from 'chalk';
|
||||
import type { IApi, IRoute } from 'dumi';
|
||||
import ReactTechStack from 'dumi/dist/techStacks/react';
|
||||
import sylvanas from 'sylvanas';
|
||||
@ -126,7 +125,8 @@ class AntdReactTechStack extends ReactTechStack {
|
||||
|
||||
const resolve = (p: string): string => require.resolve(p);
|
||||
|
||||
const RoutesPlugin = (api: IApi) => {
|
||||
const RoutesPlugin = async (api: IApi) => {
|
||||
const chalk = await import('chalk').then((m) => m.default);
|
||||
// const ssrCssFileName = `ssr-${Date.now()}.css`;
|
||||
|
||||
const writeCSSFile = (key: string, hashKey: string, cssString: string) => {
|
||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -175,7 +175,7 @@ jobs:
|
||||
CI: 1
|
||||
|
||||
- name: check build files
|
||||
run: node ./tests/dekko/index.test.js
|
||||
run: bun run test:dekko
|
||||
|
||||
# Artifact build files
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
@ -1,6 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { render, unmount } from 'rc-util/lib/React/render';
|
||||
|
||||
import warning from '../_util/warning';
|
||||
|
||||
export type UnmountType = () => Promise<void>;
|
||||
export type RenderType = (
|
||||
node: React.ReactElement,
|
||||
@ -8,6 +11,22 @@ export type RenderType = (
|
||||
) => UnmountType;
|
||||
|
||||
const defaultReactRender: RenderType = (node, container) => {
|
||||
// TODO: Remove in v6
|
||||
// Warning for React 19
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const majorVersion = parseInt(React.version.split('.')[0], 10);
|
||||
warning(
|
||||
majorVersion < 19 ||
|
||||
!!(
|
||||
ReactDOM as typeof ReactDOM & {
|
||||
createRoot: VoidFunction;
|
||||
}
|
||||
).createRoot,
|
||||
'compatible',
|
||||
'antd v5 support React is 16 ~ 18. see https://u.ant.design/v5-for-19 for compatible.',
|
||||
);
|
||||
}
|
||||
|
||||
render(node, container);
|
||||
return () => {
|
||||
return unmount(container);
|
||||
|
32
components/config-provider/__tests__/unstable.test.tsx
Normal file
32
components/config-provider/__tests__/unstable.test.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { version } from 'react';
|
||||
|
||||
import { waitFakeTimer19 } from '../../../tests/utils';
|
||||
import Modal from '../../modal';
|
||||
|
||||
jest.mock('rc-util/lib/Dom/isVisible', () => () => true);
|
||||
|
||||
describe('UnstableContext', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
// TODO: Remove in v6
|
||||
it('should warning', async () => {
|
||||
const majorVersion = parseInt(version.split('.')[0], 10);
|
||||
|
||||
if (majorVersion >= 19) {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
Modal.info({ title: 'title', content: 'content' });
|
||||
|
||||
await waitFakeTimer19();
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: compatible] antd v5 support React is 16 ~ 18. see https://u.ant.design/v5-for-19 for compatible.',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
@ -27,16 +27,16 @@ export default function useWatermark(
|
||||
removeWatermark: (container: HTMLElement) => void,
|
||||
isWatermarkEle: (ele: Node) => boolean,
|
||||
] {
|
||||
const [watermarkMap] = React.useState(() => new Map<HTMLElement, HTMLDivElement>());
|
||||
const watermarkMap = React.useRef(new Map<HTMLElement, HTMLDivElement>());
|
||||
|
||||
const appendWatermark = (base64Url: string, markWidth: number, container: HTMLElement) => {
|
||||
if (container) {
|
||||
if (!watermarkMap.get(container)) {
|
||||
if (!watermarkMap.current.get(container)) {
|
||||
const newWatermarkEle = document.createElement('div');
|
||||
watermarkMap.set(container, newWatermarkEle);
|
||||
watermarkMap.current.set(container, newWatermarkEle);
|
||||
}
|
||||
|
||||
const watermarkEle = watermarkMap.get(container)!;
|
||||
const watermarkEle = watermarkMap.current.get(container)!;
|
||||
|
||||
watermarkEle.setAttribute(
|
||||
'style',
|
||||
@ -55,20 +55,20 @@ export default function useWatermark(
|
||||
}
|
||||
}
|
||||
|
||||
return watermarkMap.get(container);
|
||||
return watermarkMap.current.get(container);
|
||||
};
|
||||
|
||||
const removeWatermark = (container: HTMLElement) => {
|
||||
const watermarkEle = watermarkMap.get(container);
|
||||
const watermarkEle = watermarkMap.current.get(container);
|
||||
|
||||
if (watermarkEle && container) {
|
||||
container.removeChild(watermarkEle);
|
||||
}
|
||||
|
||||
watermarkMap.delete(container);
|
||||
watermarkMap.current.delete(container);
|
||||
};
|
||||
|
||||
const isWatermarkEle = (ele: any) => Array.from(watermarkMap.values()).includes(ele);
|
||||
const isWatermarkEle = (ele: any) => Array.from(watermarkMap.current.values()).includes(ele);
|
||||
|
||||
return [appendWatermark, removeWatermark, isWatermarkEle];
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ group:
|
||||
order: 2
|
||||
order: 0
|
||||
title: 从 v4 到 v5
|
||||
tag: Updated
|
||||
---
|
||||
|
||||
本文档将帮助你从 antd `4.x` 版本升级到 antd `5.x` 版本,如果你是 `3.x` 或者更老的版本,请先参考之前的[升级文档](https://4x.ant.design/docs/react/migration-v4-cn)升级到 4.x。
|
||||
|
58
docs/react/v5-for-19.en-US.md
Normal file
58
docs/react/v5-for-19.en-US.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
group:
|
||||
title: Advanced
|
||||
order: 9
|
||||
title: React 19 Compatibility
|
||||
tag: New
|
||||
---
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
:::info{title="Compatibility Interface"}
|
||||
antd v5 compatibility with React 16 ~ 18 by default. For React 19, you can use the following compatibility methods to adapt.
|
||||
:::
|
||||
|
||||
### React 19 Compatibility Issues
|
||||
|
||||
Since React 19 adjusted the export method of `react-dom`, antd cannot directly use the `ReactDOM.render` method. Therefore, using antd will encounter the following problems:
|
||||
|
||||
- Wave effect does not show
|
||||
- Static methods of `Modal`, `Notification`, `Message` not working
|
||||
|
||||
Therefore, you need to use a compatibility configuration to make antd work properly in React 19.
|
||||
|
||||
### Compatibility Methods
|
||||
|
||||
You can choose one of the following methods, and it is recommended to use the compatibility package first.
|
||||
|
||||
#### Compatibility Package
|
||||
|
||||
Install the compatibility package
|
||||
|
||||
```bash
|
||||
npm install --save-dev @ant-design/v5-patch-for-react-19
|
||||
```
|
||||
|
||||
Import the compatibility package at the application entry
|
||||
|
||||
```tsx
|
||||
import '@ant-design/v5-patch-for-react-19';
|
||||
```
|
||||
|
||||
#### unstableSetRender
|
||||
|
||||
Once again, please use the compatibility package first. Only for special scenarios such as umd, micro-applications, etc., use the `unstableSetRender` method. `unstableSetRender` is a low-level registration method that allows developers to modify the rendering method of ReactDOM. Write the following code at the entry of your application:
|
||||
|
||||
```js
|
||||
import { unstableSetRender } from 'antd';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
unstableSetRender((node, container) => {
|
||||
container._reactRoot ||= createRoot(container);
|
||||
const root = container._reactRoot;
|
||||
root.render(node);
|
||||
return async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
```
|
58
docs/react/v5-for-19.zh-CN.md
Normal file
58
docs/react/v5-for-19.zh-CN.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
group:
|
||||
title: 进阶使用
|
||||
order: 9
|
||||
title: React 19 兼容
|
||||
tag: New
|
||||
---
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
:::info{title="兼容接口"}
|
||||
antd v5 默认兼容 React 16 ~ 18 版本,对于 React 19 版本,可以使用以下兼容方法进行适配。该兼容方式以及接口将在 v6 被移除。
|
||||
:::
|
||||
|
||||
### React 19 兼容问题
|
||||
|
||||
由于 React 19 调整了 `react-dom` 的导出方式,导致 antd 无法直接使用 `ReactDOM.render` 方法。因而使用 antd 会遇到以下问题:
|
||||
|
||||
- 波纹特效无法正常工作
|
||||
- `Modal`、`Notification`、`Message` 等组件的静态方法无效
|
||||
|
||||
因而需要通过兼容配置,使 antd 在 React 19 中正常工作。
|
||||
|
||||
### 兼容方式
|
||||
|
||||
以下方法任选其一,其中优先推荐使用兼容包。
|
||||
|
||||
#### 兼容包
|
||||
|
||||
安装兼容包
|
||||
|
||||
```bash
|
||||
npm install --save-dev @ant-design/v5-patch-for-react-19
|
||||
```
|
||||
|
||||
在应用入口处引入兼容包
|
||||
|
||||
```tsx
|
||||
import '@ant-design/v5-patch-for-react-19';
|
||||
```
|
||||
|
||||
#### unstableSetRender
|
||||
|
||||
再次提醒,默认情况下,请优先使用兼容包。除非对于 umd、微应用等特殊场景,才使用 `unstableSetRender` 方法。`unstableSetRender` 为底层注册方法,允许开发者修改 ReactDOM 的渲染方法。在你的应用入口处写入:
|
||||
|
||||
```js
|
||||
import { unstableSetRender } from 'antd';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
unstableSetRender((node, container) => {
|
||||
container._reactRoot ||= createRoot(container);
|
||||
const root = container._reactRoot;
|
||||
root.render(node);
|
||||
return async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
```
|
15
package.json
15
package.json
@ -83,7 +83,7 @@
|
||||
"pretest": "npm run version",
|
||||
"test": "jest --config .jest.js --no-cache",
|
||||
"test:all": "sh -e ./scripts/test-all.sh",
|
||||
"test:dekko": "node ./tests/dekko/index.test.js",
|
||||
"test:dekko": "tsx ./tests/dekko/index.test.ts",
|
||||
"test:image": "jest --config .jest.image.js --no-cache -i -u --forceExit",
|
||||
"test:node": "npm run version && jest --config .jest.node.js --no-cache",
|
||||
"test:package-diff": "antd-tools run package-diff",
|
||||
@ -151,7 +151,7 @@
|
||||
"rc-tree": "~5.10.1",
|
||||
"rc-tree-select": "~5.24.5",
|
||||
"rc-upload": "~4.8.1",
|
||||
"rc-util": "^5.44.2",
|
||||
"rc-util": "^5.44.3",
|
||||
"scroll-into-view-if-needed": "^3.1.0",
|
||||
"throttle-debounce": "^5.0.2"
|
||||
},
|
||||
@ -159,6 +159,7 @@
|
||||
"@ant-design/compatible": "^5.1.3",
|
||||
"@ant-design/happy-work-theme": "^1.0.0",
|
||||
"@ant-design/tools": "^18.0.2",
|
||||
"@ant-design/v5-patch-for-react-19": "^1.0.2",
|
||||
"@antfu/eslint-config": "^3.11.2",
|
||||
"@antv/g6": "^4.8.24",
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
@ -223,7 +224,7 @@
|
||||
"antd-style": "^3.7.1",
|
||||
"antd-token-previewer": "^2.0.8",
|
||||
"axios": "^1.7.7",
|
||||
"chalk": "^4.1.2",
|
||||
"chalk": "^5.0.0",
|
||||
"cheerio": "^1.0.0",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
"cli-progress": "^3.12.0",
|
||||
@ -237,7 +238,7 @@
|
||||
"eslint-plugin-compat": "^6.0.1",
|
||||
"eslint-plugin-jest": "^28.9.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.0",
|
||||
"eslint-plugin-react-hooks": "5.0.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0-canary-6907aa2a-20241220",
|
||||
"eslint-plugin-react-refresh": "^0.4.14",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fetch-jsonp": "^1.3.0",
|
||||
@ -315,12 +316,12 @@
|
||||
"tar": "^7.4.3",
|
||||
"tar-fs": "^3.0.6",
|
||||
"terser": "^5.36.0",
|
||||
"tsx": "4.11.2",
|
||||
"typedoc": "~0.26.11",
|
||||
"tsx": "^4.19.2",
|
||||
"typedoc": "^0.27.0",
|
||||
"typescript": "~5.7.2",
|
||||
"vanilla-jsoneditor": "^2.3.1",
|
||||
"vanilla-tilt": "^1.8.1",
|
||||
"webpack": "^5.96.1",
|
||||
"webpack": "^5.97.1",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"xhr-mock": "^2.5.1"
|
||||
},
|
||||
|
@ -58,11 +58,11 @@ fi
|
||||
if ! has_arg '--skip-dekko' "$@"; then
|
||||
echo "[TEST ALL] dekko dist"
|
||||
echo "[TEST ALL] dekko dist" > ~test-all.txt
|
||||
node ./tests/dekko/dist.test.js
|
||||
tsx ./tests/dekko/dist.test.ts
|
||||
|
||||
echo "[TEST ALL] dekko lib and es"
|
||||
echo "[TEST ALL] dekko lib and es" > ~test-all.txt
|
||||
node ./tests/dekko/lib-es.test.js
|
||||
tsx ./tests/dekko/lib-es.test.ts
|
||||
else
|
||||
echo "[TEST ALL] dekko test...skip"
|
||||
fi
|
||||
|
@ -1,5 +1,5 @@
|
||||
const $ = require('dekko');
|
||||
const chalk = require('chalk');
|
||||
import $ from 'dekko';
|
||||
import chalk from 'chalk';
|
||||
|
||||
$('dist')
|
||||
.isDirectory()
|
@ -1,3 +0,0 @@
|
||||
require('./dist.test');
|
||||
require('./lib-es.test');
|
||||
require('./use-client.test');
|
3
tests/dekko/index.test.ts
Normal file
3
tests/dekko/index.test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import './dist.test';
|
||||
import './lib-es.test';
|
||||
import './use-client.test';
|
@ -1,24 +0,0 @@
|
||||
const $ = require('dekko');
|
||||
const chalk = require('chalk');
|
||||
|
||||
$('lib').isDirectory().hasFile('index.js').hasFile('index.d.ts');
|
||||
|
||||
$('lib/*')
|
||||
.filter((filename) => !['index.js', 'index.d.ts', '.map'].some((ext) => filename.endsWith(ext)))
|
||||
.isDirectory()
|
||||
.filter((filename) => !['style', '_util', 'locale'].some((ext) => filename.endsWith(ext)))
|
||||
.hasFile('index.js')
|
||||
.hasFile('index.d.ts');
|
||||
|
||||
console.log(chalk.green('✨ `lib` directory is valid.'));
|
||||
|
||||
$('es').isDirectory().hasFile('index.js').hasFile('index.d.ts');
|
||||
|
||||
$('es/*')
|
||||
.filter((filename) => !['index.js', 'index.d.ts', '.map'].some((ext) => filename.endsWith(ext)))
|
||||
.isDirectory()
|
||||
.filter((filename) => !['style', '_util', 'locale'].some((ext) => filename.endsWith(ext)))
|
||||
.hasFile('index.js')
|
||||
.hasFile('index.d.ts');
|
||||
|
||||
console.log(chalk.green('✨ `es` directory is valid.'));
|
28
tests/dekko/lib-es.test.ts
Normal file
28
tests/dekko/lib-es.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import $ from 'dekko';
|
||||
import chalk from 'chalk';
|
||||
|
||||
$('lib').isDirectory().hasFile('index.js').hasFile('index.d.ts');
|
||||
|
||||
$('lib/*')
|
||||
.filter(
|
||||
(filename: string) => !['index.js', 'index.d.ts', '.map'].some((ext) => filename.endsWith(ext)),
|
||||
)
|
||||
.isDirectory()
|
||||
.filter((filename: string) => !['style', '_util', 'locale'].some((ext) => filename.endsWith(ext)))
|
||||
.hasFile('index.js')
|
||||
.hasFile('index.d.ts');
|
||||
|
||||
console.log(chalk.green('✨ `lib` directory is valid.'));
|
||||
|
||||
$('es').isDirectory().hasFile('index.js').hasFile('index.d.ts');
|
||||
|
||||
$('es/*')
|
||||
.filter(
|
||||
(filename: string) => !['index.js', 'index.d.ts', '.map'].some((ext) => filename.endsWith(ext)),
|
||||
)
|
||||
.isDirectory()
|
||||
.filter((filename: string) => !['style', '_util', 'locale'].some((ext) => filename.endsWith(ext)))
|
||||
.hasFile('index.js')
|
||||
.hasFile('index.d.ts');
|
||||
|
||||
console.log(chalk.green('✨ `es` directory is valid.'));
|
@ -1,28 +0,0 @@
|
||||
const $ = require('dekko');
|
||||
const chalk = require('chalk');
|
||||
const fs = require('fs');
|
||||
|
||||
const includeUseClient = (filename) =>
|
||||
fs.readFileSync(filename).toString().includes('"use client"');
|
||||
|
||||
$('dist/*')
|
||||
.isFile()
|
||||
.assert("doesn't contain use client", (filename) => !includeUseClient(filename));
|
||||
$('{es,lib}/index.js')
|
||||
.isFile()
|
||||
.assert('contain use client', (filename) => includeUseClient(filename));
|
||||
$('{es,lib}/*/index.js')
|
||||
.isFile()
|
||||
.assert('contain use client', (filename) => includeUseClient(filename));
|
||||
|
||||
// check tsx files
|
||||
$('{es,lib}/typography/*.js')
|
||||
.isFile()
|
||||
.assert('contain use client', (filename) => includeUseClient(filename));
|
||||
|
||||
$('{es,lib}/typography/Base/*.js')
|
||||
.isFile()
|
||||
.filter((filename) => !filename.endsWith('/util.js'))
|
||||
.assert('contain use client', (filename) => includeUseClient(filename));
|
||||
|
||||
console.log(chalk.green('✨ use client passed!'));
|
28
tests/dekko/use-client.test.ts
Normal file
28
tests/dekko/use-client.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import $ from 'dekko';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'node:fs';
|
||||
|
||||
const includeUseClient = (filename: string) =>
|
||||
fs.readFileSync(filename).toString().includes('"use client"');
|
||||
|
||||
$('dist/*')
|
||||
.isFile()
|
||||
.assert("doesn't contain use client", (filename: string) => !includeUseClient(filename));
|
||||
$('{es,lib}/index.js')
|
||||
.isFile()
|
||||
.assert('contain use client', (filename: string) => includeUseClient(filename));
|
||||
$('{es,lib}/*/index.js')
|
||||
.isFile()
|
||||
.assert('contain use client', (filename: string) => includeUseClient(filename));
|
||||
|
||||
// check tsx files
|
||||
$('{es,lib}/typography/*.js')
|
||||
.isFile()
|
||||
.assert('contain use client', (filename: string) => includeUseClient(filename));
|
||||
|
||||
$('{es,lib}/typography/Base/*.js')
|
||||
.isFile()
|
||||
.filter((filename: string) => !filename.endsWith('/util.js'))
|
||||
.assert('contain use client', (filename: string) => includeUseClient(filename));
|
||||
|
||||
console.log(chalk.green('✨ use client passed!'));
|
2
typings/custom-typings.d.ts
vendored
2
typings/custom-typings.d.ts
vendored
@ -27,3 +27,5 @@ declare module '@npmcli/run-script' {
|
||||
}
|
||||
|
||||
declare module '@microflash/rehype-figure';
|
||||
|
||||
declare module 'dekko';
|
||||
|
Loading…
Reference in New Issue
Block a user