Compare commits

..

51 Commits

Author SHA1 Message Date
信鑫-King
2ae5efa29f release: 3.26.12 (#21547)
* release: 3.26.12

* docs: changelog

* docs: changelog
2020-02-24 13:01:34 +08:00
Rick Zhou
e85ec40c9a avoid reading innerText (#21217)
this resolves https://github.com/ant-design/ant-design/issues/21160
2020-02-24 12:09:30 +08:00
Amumu
bb335bcc78 uniq (#21522) 2020-02-22 14:16:44 +08:00
偏右
26dd6f70af 💄 tweak Upload showDownloadIcon default value (#21496)
it should be false
2020-02-21 12:55:14 +08:00
zhangj
91a5947ef2 docs: 📝 Added expandIconColumnIndex property description of Table. (#21495)
* 添加Table表格的expandIconColumnIndex属性说明。

* Update components/table/index.en-US.md

Co-Authored-By: 偏右 <afc163@gmail.com>

* Update components/table/index.zh-CN.md

Co-Authored-By: 偏右 <afc163@gmail.com>

Co-authored-by: 偏右 <afc163@gmail.com>
2020-02-20 22:43:31 +08:00
二货机器人
be19e4e65f fix: Input with readOnly should not clearable (#21492) 2020-02-20 19:32:55 +08:00
二货机器人
ef7bb3763e docs: 3.26.11 changelog (#21405)
* update changelog

* update changelog

* update

* update link
2020-02-17 12:13:09 +08:00
二货机器人
ac740d74fb fix: Revert patch of row fix (#21406) 2020-02-17 12:03:04 +08:00
zombiej
e35663065c fix: ant-row with flex 2020-02-17 11:37:23 +08:00
chenshuai2144
4c52401fd1 Bump 3.26.9 2020-02-17 08:37:40 +08:00
陈帅
7c96796872 docs: add 3.26.10 changelog (#21393)
* doc: add 3.26.10 changelog

* docs: add 21306 21310 changelog

* docs: add 21356 changelog

* docs: Revert extra line breaks

* doc: fix typo

* doc: fix typo

* doc: fix typo
2020-02-17 01:22:10 +08:00
偏右
ac63d26f88 Revert "fix: Minor fix on ValidationRule to match AsyncValidator (#21250)" (#21391)
This reverts commit f7e7ce9d4f.
2020-02-15 23:35:18 +08:00
afc163
b22ef2280c fix Badge test in React 15 2020-02-14 12:31:23 +08:00
偏右
831f0fcb5e chore: remove unused iconClassName from definition (#21378)
close #21367
2020-02-14 12:29:37 +08:00
偏右
4db7cbcf43 🐛 Fix Affix throws Cannot read property getBoundingClientRect (#21350)
close #21348
2020-02-12 13:05:11 +08:00
偏右
dd16a24426 🐛 fix Badge color not working when contains children (#21333)
close #21331
2020-02-11 12:57:00 +08:00
偏右
f692e7ee34 🐛 fix Alert button extra padding in Firefox (#21325)
close #21324
2020-02-10 19:52:48 +08:00
偏右
fd553c9ba0 🎬 improve dropdown demos in mobile (#21312)
close #21267
2020-02-10 13:10:45 +08:00
偏右
1b23971fd6 💄 tweak steps 1px align (#21306)
https://github.com/ant-design/ant-design/issues/21257#issuecomment-583865088
2020-02-10 13:08:00 +08:00
二货机器人
04ef6a8bce fix: Clear fix missing width (#21310) 2020-02-10 11:55:30 +08:00
诸岳
703a4b1011 docs(changelog): Add missing version 2020-02-08 22:27:54 +08:00
诸岳
a1c733ad07 Bump 3.26.9 2020-02-08 21:27:25 +08:00
诸岳
9918140695 docs: Add the changelog of 3.26.9 (#21289) 2020-02-08 21:25:53 +08:00
Chiciuc Nicușor
87cbe0168f Explicitly add children prop to Timeline. (#21265) 2020-02-08 19:16:09 +08:00
偏右
eea0b028c9 🐛 Typography editable should focus at the end (#21268)
close #21261
2020-02-07 13:15:53 +08:00
偏右
88bed67368 🐛 Fix Steps style when size="small" and labelPlacement="vertical" (#21258)
close #21257
2020-02-06 17:28:47 +08:00
Han
f7e7ce9d4f fix: Minor fix on ValidationRule to match AsyncValidator (#21250)
* Minor fix on ValidationRule to match AsyncValidator

* update doc
2020-02-06 10:44:49 +08:00
二货机器人
ae7666f8fa fix badge (#21237) 2020-02-05 14:23:21 +08:00
Jirka-Lhotka
3fb7325a21 Fix wrong event type of AntTreeNodeMouseEvent (#21200) 2020-02-03 20:31:27 +08:00
偏右
59c29a400d release 3.26.8 (#21187) 2020-02-03 12:45:28 +08:00
偏右
d3102bb2db 🐛 fix site menu affix bug (#21188)
close #21128
2020-02-03 12:35:04 +08:00
afc163
e036e4c9a3 site: 📣 promote g2plot 2020-02-03 11:38:44 +08:00
Chiciuc Nicușor
da3540bd59 Replace NativeButtonProps with ButtonProps for okButtonProps and cancelButtonProps. (#21165) 2020-01-31 22:09:49 +08:00
Teng YANG
bba87d4125 update display names for layout components (#21124)
(cherry picked from commit 286e0e97f8)
2020-01-28 15:26:55 +08:00
Teng YANG
6de8522894 fix: card actions font-size issue (#21106)
Fix card component action font-size not controlled by theme variable.
2020-01-26 11:40:06 +08:00
信鑫-King
674074ce93 fix: 3.x deploy site (#20682) 2020-01-17 12:31:56 +08:00
Amumu
c68f4463d3 fix error docs (#20926) 2020-01-16 15:58:24 +08:00
偏右
a96a95ccb8 🐛 Fix List item flex layout (#20897)
close #20648
2020-01-15 10:35:45 +08:00
偏右
b112378de3 🐛 Fix Tooltip title 0 is not shown (#20894)
close #20891
2020-01-14 17:55:40 +08:00
Amumu
db4bec92be version 3.23.0 (#20890) 2020-01-14 15:50:16 +08:00
信鑫-King
b6fc0168db chore: check commit (#20880) 2020-01-14 09:41:57 +08:00
信鑫-King
92fe411961 docs: 3.26.7 changelog (#20873)
* upd: version

* 📝 changelog

* 📝 docs
2020-01-14 07:40:32 +08:00
zefeng
ab29ecb377 fix: better approach to solve not show overflow content when in elastic (#20821) 2020-01-13 20:43:03 +08:00
afc163
23d5a856c0 🎬 improve Tag demo 2020-01-08 15:09:24 +08:00
偏右
6e4fa95d0f 🐛 fix Tree switcherIcon shown in leaf node (#20753)
close #20511
2020-01-08 13:17:49 +08:00
afc163
2753ce91c9 🐛 tweak @zindex-badge for table fixed column
close #20750
close #13930
2020-01-08 11:43:10 +08:00
偏右
057f5b8526 🐛 Fix Cascader search bug when fieldNames existed (#20720)
and label/value have same field name

close #20619
2020-01-08 10:38:36 +08:00
Amumu
d3eea22097 fix: correct the collapse-borderless class variable (#20718) (#20721)
Co-authored-by: Kuitos <kuitos.lau@gmail.com>
2020-01-07 15:39:42 +08:00
骗你是小猫咪
da2f54a18a refactor: improve digital input in tooltip demo (#20702) (#20717)
* refactor: improve digital input in tooltip demo

* fix lint
2020-01-07 12:02:21 +08:00
afc163
e5dd231aaa site: 📣 promote yuque activity 2020-01-06 18:03:04 +08:00
偏右
c6eafe8380 🐛 fix Input.Group 1px bug in vertical layout form (#20685)
close #20616
2020-01-06 14:26:11 +08:00
1863 changed files with 345121 additions and 408756 deletions

View File

@@ -1,20 +1,34 @@
const fs = require('fs');
const path = require('path');
const defaultVars = require('./scripts/default-vars');
const darkVars = require('./scripts/dark-vars');
const compactVars = require('./scripts/compact-vars');
function generateThemeFileContent(theme) {
return `const { ${theme}ThemeSingle } = require('./theme');\nconst defaultTheme = require('./default-theme');\n
module.exports = {
...defaultTheme,
...${theme}ThemeSingle
}`;
}
const packageInfo = require('./package.json');
// We need compile additional content for antd user
function finalizeCompile() {
if (fs.existsSync(path.join(__dirname, './lib'))) {
// Build package.json version to lib/version/index.js
// prevent json-loader needing in user-side
const versionFilePath = path.join(process.cwd(), 'lib', 'version', 'index.js');
const versionFileContent = fs.readFileSync(versionFilePath).toString();
fs.writeFileSync(
versionFilePath,
versionFileContent.replace(
/require\(('|")\.\.\/\.\.\/package\.json('|")\)/,
`{ version: '${packageInfo.version}' }`,
),
);
// eslint-disable-next-line
console.log('Wrote version into lib/version/index.js');
// Build package.json version to lib/version/index.d.ts
// prevent https://github.com/ant-design/ant-design/issues/4935
const versionDefPath = path.join(process.cwd(), 'lib', 'version', 'index.d.ts');
fs.writeFileSync(
versionDefPath,
`declare var _default: "${packageInfo.version}";\nexport default _default;\n`,
);
// eslint-disable-next-line
console.log('Wrote version into lib/version/index.d.ts');
// Build a entry less file to dist/antd.less
const componentsPath = path.join(process.cwd(), 'components');
let componentsLessContent = '';
@@ -33,42 +47,6 @@ function finalizeCompile() {
}
}
function buildThemeFile(theme, vars) {
// Build less entry file: dist/antd.${theme}.less
if (theme !== 'default') {
fs.writeFileSync(
path.join(process.cwd(), 'dist', `antd.${theme}.less`),
`@import "../lib/style/${theme}.less";\n@import "../lib/style/components.less";`,
);
// eslint-disable-next-line no-console
console.log(`Built a entry less file to dist/antd.${theme}.less`);
} else {
fs.writeFileSync(
path.join(process.cwd(), 'dist', `default-theme.js`),
`module.exports = ${JSON.stringify(vars, null, 2)};\n`,
);
return;
}
// Build ${theme}.js: dist/${theme}-theme.js, for less-loader
fs.writeFileSync(
path.join(process.cwd(), 'dist', `theme.js`),
`const ${theme}ThemeSingle = ${JSON.stringify(vars, null, 2)};\n`,
{
flag: 'a',
},
);
fs.writeFileSync(
path.join(process.cwd(), 'dist', `${theme}-theme.js`),
generateThemeFileContent(theme),
);
// eslint-disable-next-line no-console
console.log(`Built a ${theme} theme js file to dist/${theme}-theme.js`);
}
function finalizeDist() {
if (fs.existsSync(path.join(__dirname, './dist'))) {
// Build less entry file: dist/antd.less
@@ -76,48 +54,9 @@ function finalizeDist() {
path.join(process.cwd(), 'dist', 'antd.less'),
'@import "../lib/style/index.less";\n@import "../lib/style/components.less";',
);
// eslint-disable-next-line no-console
fs.writeFileSync(
path.join(process.cwd(), 'dist', 'theme.js'),
`const defaultTheme = require('./default-theme.js');\n`,
);
// eslint-disable-next-line no-console
console.log('Built a entry less file to dist/antd.less');
buildThemeFile('default', defaultVars);
buildThemeFile('dark', darkVars);
buildThemeFile('compact', compactVars);
fs.writeFileSync(
path.join(process.cwd(), 'dist', `theme.js`),
`
function getThemeVariables(options = {}) {
let themeVar = {
'hack': \`true;@import "\${require.resolve('antd/lib/style/color/colorPalette.less')}";\`,
...defaultTheme
};
if(options.dark) {
themeVar = {
...themeVar,
...darkThemeSingle
}
}
if(options.compact){
themeVar = {
...themeVar,
...compactThemeSingle
}
}
return themeVar;
}
module.exports = {
darkThemeSingle,
compactThemeSingle,
getThemeVariables
}`,
{
flag: 'a',
},
);
// eslint-disable-next-line
console.log('Built a entry less file to dist/antd.less');
}
}
@@ -128,5 +67,4 @@ module.exports = {
dist: {
finalize: finalizeDist,
},
generateThemeFileContent,
};

View File

@@ -3,13 +3,20 @@ version: 2
references:
container_config: &container_config
docker:
- image: circleci/node:14
- image: circleci/node:lts
working_directory: ~/ant-design
attach_workspace: &attach_workspace
attach_workspace:
at: ~/ant-design
install_react: &install_react
run: REACT=15 ./scripts/install-react.sh
react_15: &react_15
environment:
REACT: 15
react_16: &react_16
environment:
REACT: 16
@@ -22,10 +29,10 @@ references:
ignore: gh-pages
- dist:
requires:
- setup
- setup
- compile:
requires:
- setup
- setup
- lint:
requires:
- setup
@@ -44,6 +51,21 @@ references:
- test_node:
requires:
- setup
- test_dist_15:
requires:
- dist
- test_lib_15:
requires:
- compile
- test_es_15:
requires:
- compile
- test_dom_15:
requires:
- setup
- test_node_15:
requires:
- setup
- check_metadata:
requires:
- setup
@@ -151,6 +173,69 @@ jobs:
- *attach_workspace
- run: npm run test-node -- -w 1
test_dist_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run:
command: npm test -- -w 1 -u
environment:
LIB_DIR: dist
REACT: 15
test_lib_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run:
command: npm test -- -w 1 -u
environment:
LIB_DIR: lib
REACT: 15
test_es_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run:
command: npm test -- -w 1 -u
environment:
LIB_DIR: es
REACT: 15
test_dom_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run:
command: npm test -- -w 1 -u
environment:
REACT: 15
test_node_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run:
command: npm run test-node -- -w 1 -u
environment:
REACT: 15
check_metadata:
<<: *container_config
steps:
@@ -166,7 +251,7 @@ workflows:
<<: *workflow
triggers:
- schedule:
cron: '0 0 * * *'
cron: "0 0 * * *"
filters:
branches:
only:

View File

@@ -10,17 +10,9 @@ module.exports = {
'**/*.json',
],
modulePattern: [
{
pattern: /ConfigContext.*renderEmpty/ms,
module: '../empty',
},
{
pattern: /ConfigConsumer.*renderEmpty/ms,
module: '../empty',
},
{
pattern: /config-provider\/context.*renderEmpty/ms,
module: '../empty',
},
],
};

View File

@@ -1 +0,0 @@
node_modules/

View File

@@ -4,26 +4,12 @@ components/**/*.jsx
!components/*/demo/*
!.*.js
# Docs templates
site/theme/template/Color/ColorPicker.jsx
site/theme/template/IconDisplay/*.js
site/theme/template/IconDisplay/*.jsx
site/theme/template/IconDisplay/fields.js
site/theme/template/Home/**/*.jsx
site/theme/template/utils.jsx
site/theme/template/Layout/**/*.jsx
site/theme/template/Layout/Footer.jsx
site/theme/template/Content/Article.jsx
site/theme/template/Content/EditButton.jsx
site/theme/template/Resources/*.jsx
site/theme/template/Resources/**/*.jsx
site/theme/template/NotFound.jsx
typings
es/**/*
lib/**/*
node_modules
_site
dist
coverage
**/*.d.ts
# Scripts
scripts/previewEditor/**/*

View File

@@ -1,4 +1,4 @@
module.exports = {
const eslintrc = {
extends: [
'airbnb',
'prettier',
@@ -20,61 +20,23 @@ module.exports = {
},
},
parser: '@typescript-eslint/parser',
plugins: ['markdown', 'react', 'babel', 'jest', '@typescript-eslint', 'react-hooks', 'unicorn'],
plugins: ['markdown', 'react', 'babel', 'jest', '@typescript-eslint'],
// https://github.com/typescript-eslint/typescript-eslint/issues/46#issuecomment-470486034
overrides: [
{
files: ['*.ts', '*.tsx'],
rules: {
'@typescript-eslint/no-unused-vars': [2, { args: 'none' }],
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': 2,
},
},
{
files: ['*.md'],
globals: {
React: true,
ReactDOM: true,
mountNode: true,
},
rules: {
indent: 0,
'no-console': 0,
'no-plusplus': 0,
'eol-last': 0,
'no-script-url': 0,
'prefer-rest-params': 0,
'react/no-access-state-in-setstate': 0,
'react/destructuring-assignment': 0,
'react/no-multi-comp': 0,
'jsx-a11y/href-no-hash': 0,
'import/no-extraneous-dependencies': 0,
'jsx-a11y/control-has-associated-label': 0,
},
},
],
rules: {
camelcase: 0,
'react/jsx-one-expression-per-line': 0,
'react/prop-types': 0,
'react/forbid-prop-types': 0,
'react/jsx-indent': 0,
'react/jsx-wrap-multilines': ['error', { declaration: false, assignment: false }],
'react/jsx-filename-extension': 0,
'react/state-in-constructor': 0,
'react/jsx-props-no-spreading': 0,
'react/destructuring-assignment': 0, // TODO: remove later
'react/require-default-props': 0,
'react/sort-comp': 0,
'react/display-name': 0,
'react/static-property-placement': 0,
'react/no-find-dom-node': 0,
'react/no-unused-prop-types': 0,
'react/default-props-match-prop-types': 0,
'react-hooks/rules-of-hooks': 2, // Checks rules of Hooks
'import/extensions': 0,
'import/no-cycle': 0,
'import/no-extraneous-dependencies': [
'error',
{
@@ -94,32 +56,66 @@ module.exports = {
'jsx-a11y/click-events-have-key-events': 0,
'jsx-a11y/anchor-is-valid': 0,
'jsx-a11y/no-noninteractive-element-interactions': 0,
'comma-dangle': ['error', 'always-multiline'],
'react/jsx-filename-extension': 0,
'react/state-in-constructor': 0,
'react/jsx-props-no-spreading': 0,
'prefer-destructuring': 0, // TODO: remove later
'consistent-return': 0, // TODO: remove later
'no-return-assign': 0, // TODO: remove later
'no-param-reassign': 0, // TODO: remove later
'react/destructuring-assignment': 0, // TODO: remove later
'react/no-did-update-set-state': 0, // TODO: remove later
'react/require-default-props': 0,
'react/default-props-match-prop-types': 0,
'import/no-cycle': 0,
'react/no-find-dom-node': 0,
'no-underscore-dangle': 0,
'react/sort-comp': 0,
// label-has-for has been deprecated
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-for.md
'jsx-a11y/label-has-for': 0,
'comma-dangle': ['error', 'always-multiline'],
'consistent-return': 0, // TODO: remove later
'no-param-reassign': 0, // TODO: remove later
'no-underscore-dangle': 0,
// for (let i = 0; i < len; i++)
'no-plusplus': 0,
// https://eslint.org/docs/rules/no-continue
// labeledLoop is conflicted with `eslint . --fix`
'no-continue': 0,
'react/display-name': 0,
// ban this for Number.isNaN needs polyfill
'no-restricted-globals': 0,
'max-classes-per-file': 0,
'react/static-property-placement': 0,
'jest/no-test-callback': 0,
'jest/expect-expect': 0,
'unicorn/better-regex': 2,
'unicorn/prefer-trim-start-end': 2,
'unicorn/expiring-todo-comments': 2,
'unicorn/no-abusive-eslint-disable': 2,
'import/extensions': 0,
},
globals: {
gtag: true,
},
};
if (process.env.RUN_ENV === 'DEMO') {
eslintrc.globals = Object.assign(eslintrc.globals, {
React: true,
ReactDOM: true,
mountNode: true,
});
Object.assign(eslintrc.rules, {
indent: 0,
'no-console': 0,
'no-plusplus': 0,
'eol-last': 0,
'no-script-url': 0,
'prefer-rest-params': 0,
'react/no-access-state-in-setstate': 0,
'react/destructuring-assignment': 0,
'react/no-multi-comp': 0,
'jsx-a11y/href-no-hash': 0,
'import/no-extraneous-dependencies': 0,
'import/no-unresolved': 0,
'jsx-a11y/control-has-associated-label': 0,
});
}
module.exports = eslintrc;

3
.github/FUNDING.yml vendored
View File

@@ -4,6 +4,5 @@ github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, u
open_collective: ant-design
patreon: ant_design
ko_fi: # Replace with a single Ko-fi username
issuehunt: ant-design/ant-design
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: https://www.buymeacoffee.com/antdesign
custom: # Replace with a single custom sponsorship URL

View File

@@ -0,0 +1,11 @@
---
name: '⚠️ Please use new-issue.ant.design ⚠️'
about: The issue which is not created via http://new-issue.ant.design will be closed immediately.
labels:
---
The issue which is not created via http://new-issue.ant.design will be closed immediately.
---
注意:不是用 http://new-issue.ant.design 创建的 issue 会被立即关闭。

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Create new issue
url: http://new-issue.ant.design
about: The issue which is not created via http://new-issue.ant.design will be closed immediately.

View File

@@ -4,20 +4,17 @@ First of all, thank you for your contribution! 😄
New feature please send pull request to feature branch, and rest to master branch.
Pull request will be merged after one of collaborators approve.
Please makes sure that these form are filled before submitting your pull request, thank you!
-->
[[中文版模板 / Chinese template](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE/pr_cn.md)]
-->
### 🤔 This is a ...
- [ ] New feature
- [ ] Bug fix
- [ ] Site / documentation update
- [ ] Demo update
- [ ] Site / document update
- [ ] Component style update
- [ ] TypeScript definition update
- [ ] Bundle size optimization
- [ ] Performance optimization
- [ ] Refactoring
- [ ] Code style optimization
- [ ] Test Case
@@ -51,8 +48,6 @@ Describe changes from userside, and list all potential break changes or other ri
### ☑️ Self Check before Merge
⚠️ Please check all items below before review. ⚠️
- [ ] Doc is updated/provided or not needed
- [ ] Demo is updated/provided or not needed
- [ ] TypeScript definition is updated/provided or not needed

View File

@@ -13,11 +13,8 @@
- [ ] 新特性提交
- [ ] 日常 bug 修复
- [ ] 站点、文档改进
- [ ] 演示代码改进
- [ ] 组件样式改进
- [ ] TypeScript 定义更新
- [ ] 包体积优化
- [ ] 性能优化
- [ ] 重构
- [ ] 代码风格优化
- [ ] 测试用例
@@ -49,9 +46,9 @@
| 🇺🇸 英文 | |
| 🇨🇳 中文 | |
### ☑️ 请求合并前的自查清单
- 中文(可选):
⚠️ 请自检并全部**勾选全部选项**。⚠️
### ☑️ 请求合并前的自查清单
- [ ] 文档已补充或无须补充
- [ ] 代码演示已提供或无须提供

View File

@@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

View File

@@ -1,3 +0,0 @@
comment: 'Could you please add tests to make sure this change works as expected?',
fileExtensions: [.ts', '.tsx', '.json']
testDir: '__tests__'

View File

@@ -1,54 +0,0 @@
name: "CodeQL"
on:
push:
branches: [master, 0.12-stable, 1.x-stable, 2.x-stable, 3.x-stable, feature, gh-pages]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 13 * * 1'
jobs:
analyse:
name: Analyse
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -1,17 +0,0 @@
name: 📦 Compressed Size
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
env:
CI_JOB_NUMBER: 1
steps:
- uses: actions/checkout@v2
- uses: preactjs/compressed-size-action@v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
pattern: "./dist/**/*.min.{js,css}"

View File

@@ -3,7 +3,7 @@ on:
release:
types: [published]
branches:
- master
- 3.x-stable
jobs:
build-and-deploy:

View File

@@ -1,17 +0,0 @@
name: gitleaks
on: [push,pull_request]
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: '1'
- name: wget
uses: wei/wget@v1
with:
args: -O .gitleaks.toml https://raw.githubusercontent.com/ycjcl868/gitleaks/master/.gitleaks.toml
- name: gitleaks-action
uses: zricethezav/gitleaks-action@master

View File

@@ -1,11 +1,19 @@
name: CI
on: [push]
name: Lighthouse
on: push
jobs:
lighthouseci:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install && npm install -g @lhci/cli@0.4.x
- run: npm run site
- run: lhci autorun --upload.target=temporary-public-storage
- uses: actions/checkout@v1
- name: Audit URLs using Lighthouse
uses: treosh/lighthouse-ci-action@v2
with:
urls: |
https://ant.design
https://ant.design/docs/react/introduce-cn
https://ant.design/components/button-cn
- name: Save results
uses: actions/upload-artifact@v1
with:
name: lighthouse-results
path: '.lighthouseci' # This will save the Lighthouse results as .json files

View File

@@ -1,14 +0,0 @@
name: Mirror
on: [push]
jobs:
to_gitee:
runs-on: ubuntu-latest
if: github.repository == 'ant-design/ant-design'
steps:
- uses: actions/checkout@v1
- uses: pixta-dev/repository-mirroring-action@v1
with:
target_repo_url: git@gitee.com:ant-design/ant-design.git
ssh_private_key: ${{ secrets.GITEE_SSH_PRIVATE_KEY }}

View File

@@ -1,17 +1,14 @@
on:
on:
issue_comment:
types: [created]
name: Automatic Rebase
jobs:
rebase:
name: Rebase
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
with:
fetch-depth: 0
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.3
uses: cirrus-actions/rebase@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,13 +0,0 @@
name: 🔄 Sync packages
on:
release:
types: [published]
jobs:
sync_packages:
runs-on: ubuntu-latest
steps:
- name: Sync packages
uses: LexSwed/npm-package-sync@master
with:
registry-to: 'https://npm.pkg.github.com'
token-to: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -93,8 +93,6 @@ jobs:
- name: dist
run: npm run dist
env:
NODE_OPTIONS: --max_old_space_size=4096
- name: check
run: node ./tests/dekko/dist.test.js

21
.gitignore vendored
View File

@@ -25,7 +25,6 @@ nohup.out
_site
_data
dist
report.html
/lib
/es
elasticsearch-*
@@ -39,26 +38,8 @@ components/**/*.jsx
!components/**/__tests__/**/*.js
!components/**/__tests__/**/*.js.snap
/.history
*.tmp
# Docs templates
site/theme/template/Color/ColorPicker.jsx
site/theme/template/IconDisplay/*.js
site/theme/template/IconDisplay/*.jsx
site/theme/template/IconDisplay/fields.js
site/theme/template/Home/**/*.jsx
site/theme/template/utils.jsx
site/theme/template/Layout/Footer.jsx
site/theme/template/Layout/Header/**/*.jsx
site/theme/template/Layout/SiteContext.jsx
site/theme/template/Content/Article.jsx
site/theme/template/Content/EditButton.jsx
site/theme/template/Resources/*.jsx
site/theme/template/Resources/**/*.jsx
site/theme/template/NotFound.jsx
scripts/previewEditor/index.html
components/version/version.tsx
# Image snapshot diff
__diff_output__/
/jest-stare
*.tmp

View File

@@ -1,24 +0,0 @@
const { moduleNameMapper, transformIgnorePatterns } = require('./.jest');
// jest config for image snapshots
module.exports = {
setupFiles: ['./tests/setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
moduleNameMapper,
transform: {
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
},
testRegex: 'image\\.test\\.js$',
testEnvironment: 'node',
transformIgnorePatterns,
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: {
'ts-jest': {
tsConfigFile: './tsconfig.test.json',
},
},
reporters: ['default', 'jest-stare'],
};

View File

@@ -1,21 +1,13 @@
const libDir = process.env.LIB_DIR;
const transformIgnorePatterns = [
'/dist/',
// Ignore modules without es dir.
// Update: @babel/runtime should also be transformed
'node_modules/(?!.*@(babel|ant-design))[^/]+?/(?!(es|node_modules)/)',
'node_modules/[^/]+?/(?!(es|node_modules)/)', // Ignore modules without es dir
];
function getTestRegex(libDir) {
if (libDir === 'dist') {
return 'demo\\.test\\.js$';
}
return '.*\\.test\\.(j|t)sx?$';
}
module.exports = {
verbose: true,
setupFiles: ['./tests/setup.js'],
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'md'],
modulePathIgnorePatterns: ['/_site/'],
moduleNameMapper: {
@@ -25,16 +17,15 @@ module.exports = {
'^react-dnd-touch-backend$': 'react-dnd-touch-backend/dist/cjs',
'^react-dnd-test-backend$': 'react-dnd-test-backend/dist/cjs',
'^react-dnd-test-utils$': 'react-dnd-test-utils/dist/cjs',
'\\.(css|less)$': 'identity-obj-proxy',
},
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node', 'image.test.js'],
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node'],
transform: {
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
},
testRegex: getTestRegex(process.env.LIB_DIR),
testRegex: `${libDir === 'dist' ? 'demo' : '.*'}\\.test\\.js$`,
collectCoverageFrom: [
'components/**/*.{ts,tsx}',
'!components/*/style/index.tsx',

View File

@@ -3,7 +3,6 @@ const { moduleNameMapper, transformIgnorePatterns } = require('./.jest');
// jest config for server render environment
module.exports = {
setupFiles: ['./tests/setup.js'],
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
moduleNameMapper,
transform: {
@@ -12,8 +11,13 @@ module.exports = {
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
},
testRegex: 'demo\\.test\\.(j|t)s$',
testRegex: 'demo\\.test\\.js$',
testEnvironment: 'node',
transformIgnorePatterns,
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: {
'ts-jest': {
tsConfigFile: './tsconfig.test.json',
},
},
};

View File

@@ -3,7 +3,6 @@
"trailingComma": "all",
"printWidth": 100,
"proseWrap": "never",
"arrowParens": "avoid",
"overrides": [
{
"files": ".prettierrc",

View File

@@ -1,9 +0,0 @@
const config = {
plugins: [
'remark-preset-lint-recommended',
['remark-lint-list-item-indent', 'space'],
['remark-lint-no-literal-urls', false],
],
};
module.exports = config;

View File

@@ -13,11 +13,19 @@ matrix:
fast_finish: true
include:
- env: TEST_TYPE=lint
- env: TEST_TYPE=test:dist
- env: TEST_TYPE=test:lib
- env: TEST_TYPE=test:es
- env: TEST_TYPE=test:dom
- env: TEST_TYPE=test:node
- env: REACT=16 TEST_TYPE=test:dist
- env: REACT=16 TEST_TYPE=test:lib
- env: REACT=16 TEST_TYPE=test:es
- env: REACT=16 TEST_TYPE=test:dom
- env: REACT=16 TEST_TYPE=test:node
- env: REACT=15 TEST_TYPE=test:dist
- env: REACT=15 TEST_TYPE=test:lib
- env: REACT=15 TEST_TYPE=test:es
- env: REACT=15 TEST_TYPE=test:dom
- env: REACT=15 TEST_TYPE=test:node
before_script:
- scripts/install-react.sh
script:
- scripts/travis-script.sh

View File

@@ -1010,6 +1010,7 @@ zytjs <yitongzhao@163.com>
郭延豪(708674) <gyh9457@163.com>
愚指导-TZ <yutingzhao1991@sina.com>
杨小事er <Uiryzd@163.com>
杨小事er <uiryzd@163.com>
超能刚哥 <margox@foxmail.com>
马金花儿 <o.o@mug.dog>
रोहन मल्होत्रा <rohan.malhotra@adwyze.com>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [xingmin.zhu@alipay.com.](mailto:xingmin.zhu@alipay.com.) The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at xingmin.zhu@alipay.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

View File

@@ -1,7 +0,0 @@
FROM buildkite/puppeteer:v3.0.4
RUN mkdir /app
WORKDIR /app
COPY package.json ./
RUN npm install
ENV PATH="${PATH}:/app/node_modules/.bin"
COPY . .

View File

@@ -1,166 +0,0 @@
<p align="center">
<a href="https://ant.design">
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
</a>
</p>
<h1 align="center">Ant Design</h1>
<div align="center">
Uma solução empresarial de design e biblioteca UI para React.
[![CircleCI status][circleci-image]][circleci-url] [![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![FOSSA Status][fossa-image]][fossa-url] [![Issues need help][help-wanted-image]][help-wanted-url]
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url] [![[SemVer stability]][semver-stability-image]][semver-stability-url]
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: http://npmjs.org/package/antd
[circleci-image]: https://img.shields.io/travis/com/ant-design/ant-design.svg?style=flat-square
[circleci-url]: https://travis-ci.com/ant-design/ant-design
[github-action-image]: https://github.com/ant-design/ant-design/workflows/test/badge.svg
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3Atest
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
[david-url]: https://david-dm.org/ant-design/ant-design
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
[download-url]: https://npmjs.org/package/antd
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
[lgtm-url]: https://lgtm.com/projects/g/ant-design/ant-design/alerts/
[fossa-image]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield
[fossa-url]: https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield
[help-wanted-image]: https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open
[help-wanted-url]: https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
[twitter-image]: https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social
[twitter-url]: https://twitter.com/AntDesignUI
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
[gitter-english-url]: https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
[gitter-chinese-url]: https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[semver-stability-url]: https://dependabot.com/compatibility-score.html/?dependency-name=antd&package-manager=npm_and_yarn&new-version=latest
[semver-stability-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=antd&package-manager=npm_and_yarn&target-version=latest&version-scheme=semver
</div>
[![](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*Yl83RJhUE7kAAAAAAAAAAABkARQnAQ)](https://ant.design)
[English](./README.md) | Português | [简体中文](./README-zh_CN.md)
## ✨ Funcionalidades
- 🌈 Design empresarial de interface para aplicações web.
- 📦 Um conjunto de alta qualidade, componentes React prontos para uso.
- 🛡 Escrito em TypeScript com tipos previsíveis.
- ⚙️ Pacote completo de recursos de design e ferramentas de desenvolvimento.
- 🌍 Suporte de internacionalização para dezenas de idiomas.
- 🎨 Personalização poderosa do tema em todos os detalhes.
## 🖥 Suporte aos ambientes
- Navegadores modernos e Internet Explorer 11 (com [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
- Renderização no lado do servidor (server-side)
- [Electron](https://www.electronjs.org/)
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| --- | --- | --- | --- | --- | --- |
| IE11, Edge | últimas 2 versões | últimas 2 versões | últimas 2 versões | últimas 2 versões | últimas 2 versões |
## 📦 Instalação
```bash
npm install antd
```
```bash
yarn add antd
```
## 🔨 Uso
```jsx
import { Button, DatePicker } from 'antd';
const App = () => (
<>
<Button type="primary">PRESS ME</Button>
<DatePicker />
</>
);
```
Importe o estilo manualmente:
```jsx
import 'antd/dist/antd.css'; // ou 'antd/dist/antd.less'
```
### TypeScript
Veja [Uso no Typescript](https://ant.design/docs/react/use-in-typescript).
## 🌍 Internacionalização
Veja [i18n](https://ant.design/docs/react/i18n).
## 🔗 Links
- [Página inicial](https://ant.design/)
- [Componentes](https://ant.design/components/overview)
- [Ant Design Pro](http://pro.ant.design/)
- [Ant Design Charts](https://charts.ant.design)
- [Change Log](CHANGELOG.en-US.md)
- [rc-components](http://react-component.github.io/)
- [Mobile UI](http://mobile.ant.design)
- [Ant Design Icones](https://github.com/ant-design/ant-design-icons)
- [Ant Design Cores](https://github.com/ant-design/ant-design-colors)
- [Ant Design Pro Layout](https://github.com/ant-design/ant-design-pro-layout)
- [Ant Design Pro Blocks](https://github.com/ant-design/pro-blocks)
- [Tema escuro](https://github.com/ant-design/ant-design-dark-theme)
- [Página de aterrissagem](https://landing.ant.design)
- [Motion](https://motion.ant.design)
- [Mercado de páginas](http://scaffold.ant.design)
- [Instruções ao desenvolvedor](https://github.com/ant-design/ant-design/wiki/Development)
- [Versionando as notas de atualização](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
- [FAQ](https://ant.design/docs/react/faq)
- [CodeSandbox Template](https://u.ant.design/codesandbox-repro) para relatório de erros
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
- [Customize Theme](https://ant.design/docs/react/customize-theme)
- [How to Apply for Being A Collaborator](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
## ⌨️ Desenvolvimento
Use Gitpod, um ambiente de desenvolvimento online para GitHub.
[![Abrir no Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ant-design/ant-design)
Ou clone localmente:
```bash
$ git clone git@github.com:ant-design/ant-design.git
$ cd ant-design
$ npm install
$ npm start
```
Abra seu navegador e visite http://127.0.0.1:8001, veja mais em [Desenvolvimento](https://github.com/ant-design/ant-design/wiki/Development).
## 🤝 Contribuição [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
Leia nosso [guia de contribução](https://ant.design/docs/react/contributing) e vamos contruir um melhor antd juntos.
Nós saudamos todas as contribuições. Por favor, leia nosso [CONTRIBUTING.md](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md) primeiro. Você pode submeter todas as ideias como [Pull Requests](https://github.com/ant-design/ant-design/pulls) ou como [GitHub issues](https://github.com/ant-design/ant-design/issues). Se você quiser melhorar o código, verifique [instruções ao desenvolvedor](https://github.com/ant-design/ant-design/wiki/Development) e divirta-se! :)
Se você é um colaborador, por favor siga nossa [Pull Request princípio](https://github.com/ant-design/ant-design/wiki/PR-principle) para criar um Pull Request através do [template do colaborador](https://github.com/ant-design/ant-design/compare?expand=1&template=collaborator.md).
[![Let's fund issues in this repository](https://issuehunt.io/static/embed/issuehunt-button-v1.svg)](https://issuehunt.io/repos/34526884)
## ❤️ Patrocionadores e Apoiadores [![](https://opencollective.com/ant-design/tiers/sponsors/badge.svg?label=Sponsors&color=brightgreen)](https://opencollective.com/ant-design#support) [![](https://opencollective.com/ant-design/tiers/backers/badge.svg?label=Backers&color=brightgreen)](https://opencollective.com/ant-design#support)
[![](https://opencollective.com/ant-design/tiers/sponsors.svg?avatarHeight=36)](https://opencollective.com/ant-design#support)
[![](https://opencollective.com/ant-design/tiers/backers.svg?avatarHeight=36)](https://opencollective.com/ant-design#support)

View File

@@ -1,5 +1,5 @@
<p align="center">
<a href="https://ant.design">
<a href="http://ant.design">
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
</a>
</p>
@@ -10,46 +10,17 @@
一套企业级 UI 设计语言和 React 组件库。
[![CircleCI status][circleci-image]][circleci-url] [![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design) ![CI Status](https://github.com/ant-design/ant-design/workflows/Node%20CI/badge.svg) [![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master) [![](https://flat.badgen.net/npm/v/antd?icon=npm)](https://www.npmjs.com/package/antd) [![](https://badgen.net/npm/v/antd/next)](https://www.npmjs.com/package/antd) [![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![FOSSA Status][fossa-image]][fossa-url] [![Issues need help][help-wanted-image]][help-wanted-url]
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design) [![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design?type=dev) [![Total alerts](https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/alerts/) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield) [![Issues need help](https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open)](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url] [![[SemVer stability]][semver-stability-image]][semver-stability-url]
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: http://npmjs.org/package/antd
[circleci-image]: https://img.shields.io/travis/com/ant-design/ant-design.svg?style=flat-square
[circleci-url]: https://travis-ci.com/ant-design/ant-design
[github-action-image]: https://github.com/ant-design/ant-design/workflows/test/badge.svg
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3Atest
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
[david-url]: https://david-dm.org/ant-design/ant-design
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
[download-url]: https://npmjs.org/package/antd
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
[lgtm-url]: https://lgtm.com/projects/g/ant-design/ant-design/alerts/
[fossa-image]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield
[fossa-url]: https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield
[help-wanted-image]: https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open
[help-wanted-url]: https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
[twitter-image]: https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social
[twitter-url]: https://twitter.com/AntDesignUI
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
[gitter-english-url]: https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
[gitter-chinese-url]: https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[semver-stability-url]: https://dependabot.com/compatibility-score.html/?dependency-name=antd&package-manager=npm_and_yarn&new-version=latest
[semver-stability-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=antd&package-manager=npm_and_yarn&target-version=latest&version-scheme=semver
[![](https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social)](https://twitter.com/AntDesignUI) [![Gitter](https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
</div>
[![](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*Ey3wTo-5__QAAAAAAAAAAABkARQnAQ)](https://ant.design/index-cn)
[![](https://cdn-images-1.medium.com/max/2000/1*NIlj0-TdLMbo_hzSBP8tmg.png)](http://ant.design/index-cn)
[English](./README.md) | [Português](./README-pt_BR.md) | 简体中文
[English](./README.md) | 简体中文
## ✨ 特性
@@ -60,15 +31,15 @@
- 🌍 数十个国际化语言支持。
- 🎨 深入每个细节的主题定制能力。
## 🖥 兼容环境
## 🖥 支持环境
- 现代浏览器和 IE11需要 [polyfills](https://ant.design/docs/react/getting-started-cn#兼容性)
- 现代浏览器和 IE9 及以上
- 支持服务端渲染。
- [Electron](https://www.electronjs.org/)
- [Electron](http://electron.atom.io/)
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Electron |
| --- | --- | --- | --- | --- |
| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| --- | --- | --- | --- | --- | --- |
| IE9, IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## 📦 安装
@@ -82,7 +53,7 @@ yarn add antd
## 🔨 示例
```jsx
````jsx
import { Button, DatePicker } from 'antd';
const App = () => (
@@ -91,32 +62,28 @@ const App = () => (
<DatePicker />
</>
);
```
引入样式:
```jsx
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
```
````
### 🌈 定制主题
参考 [定制主题](https://ant.design/docs/react/customize-theme-cn) 文档。
你也可以使用 [babel-plugin-import](https://ant.design/docs/react/getting-started-cn#按需加载)。
### 🛡 TypeScript
参考 [在 TypeScript 中使用](https://ant.design/docs/react/use-in-typescript-cn)。
参考 [在 TypeScript 中使用](https://ant.design/docs/react/use-in-typescript-cn)
## 🌍 国际化
参考 [国际化文档](https://ant.design/docs/react/i18n-cn)。
参考 [国际化文档](http://ant.design/docs/react/i18n-cn)。
## 🔗 链接
- [首页](https://ant.design/)
- [组件库](https://ant.design/components/overview-cn)
- [首页](http://ant.design/)
- [组件库](http://ant.design/docs/react/introduce)
- [Ant Design Pro](http://pro.ant.design/)
- [Ant Design Charts](https://charts.ant.design)
- [更新日志](CHANGELOG.en-US.md)
- [React 底层基础组件](http://react-component.github.io/)
- [移动端组件](http://mobile.ant.design)
@@ -134,8 +101,7 @@ import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
- [常见问题](https://ant.design/docs/react/faq-cn)
- [CodeSandbox 模板](https://u.ant.design/codesandbox-repro) for bug reports
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
- [定制主题](https://ant.design/docs/react/customize-theme-cn)
- [成为社区协作成员](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
- [定制主题](http://ant.design/docs/react/customize-theme-cn)
## ⌨️ 本地开发

View File

@@ -1,5 +1,5 @@
<p align="center">
<a href="https://ant.design">
<a href="http://ant.design">
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
</a>
</p>
@@ -10,46 +10,17 @@
An enterprise-class UI design language and React UI library.
[![CircleCI status][circleci-image]][circleci-url] [![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design) ![CI Status](https://github.com/ant-design/ant-design/workflows/test/badge.svg) [![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master) [![](https://flat.badgen.net/npm/v/antd?icon=npm)](https://www.npmjs.com/package/antd) [![](https://badgen.net/npm/v/antd/next)](https://www.npmjs.com/package/antd) [![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![FOSSA Status][fossa-image]][fossa-url] [![Issues need help][help-wanted-image]][help-wanted-url]
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design) [![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design?type=dev) [![Total alerts](https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/alerts/) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield) [![Issues need help](https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open)](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
[![Follow Twitter][twitter-image]][twitter-url] [![Gitter][gitter-english-image]][gitter-english-url] [![Gitter][gitter-chinese-image]][gitter-chinese-url] [![[SemVer stability]][semver-stability-image]][semver-stability-url]
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: http://npmjs.org/package/antd
[circleci-image]: https://img.shields.io/travis/com/ant-design/ant-design.svg?style=flat-square
[circleci-url]: https://travis-ci.com/ant-design/ant-design
[github-action-image]: https://github.com/ant-design/ant-design/workflows/test/badge.svg
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3Atest
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
[david-url]: https://david-dm.org/ant-design/ant-design
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
[download-url]: https://npmjs.org/package/antd
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
[lgtm-url]: https://lgtm.com/projects/g/ant-design/ant-design/alerts/
[fossa-image]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fant-design%2Fant-design.svg?type=shield
[fossa-url]: https://app.fossa.io/projects/git%2Bgithub.com%2Fant-design%2Fant-design?ref=badge_shield
[help-wanted-image]: https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open
[help-wanted-url]: https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
[twitter-image]: https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social
[twitter-url]: https://twitter.com/AntDesignUI
[gitter-english-image]: https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D
[gitter-english-url]: https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
[gitter-chinese-image]: https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=18&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D
[gitter-chinese-url]: https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[semver-stability-url]: https://dependabot.com/compatibility-score.html/?dependency-name=antd&package-manager=npm_and_yarn
[semver-stability-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=antd&package-manager=npm_and_yarn&version-scheme=semver
[![](https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social)](https://twitter.com/AntDesignUI) [![Gitter](https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
</div>
[![](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*Yl83RJhUE7kAAAAAAAAAAABkARQnAQ)](https://ant.design)
[![](https://cdn-images-1.medium.com/max/2000/1*NIlj0-TdLMbo_hzSBP8tmg.png)](http://ant.design)
English | [Português](./README-pt_BR.md) | [简体中文](./README-zh_CN.md)
English | [简体中文](./README-zh_CN.md)
## ✨ Features
@@ -62,13 +33,13 @@ English | [Português](./README-pt_BR.md) | [简体中文](./README-zh_CN.md)
## 🖥 Environment Support
- Modern browsers and Internet Explorer 11 (with [polyfills](https://stackoverflow.com/questions/57020976/polyfills-in-2019-for-ie11))
- Modern browsers and Internet Explorer 9+ (with [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
- Server-side Rendering
- [Electron](https://www.electronjs.org/)
- [Electron](http://electron.atom.io/)
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Electron |
| --- | --- | --- | --- | --- |
| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| --- | --- | --- | --- | --- | --- |
| IE9, IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## 📦 Install
@@ -88,7 +59,7 @@ import { Button, DatePicker } from 'antd';
const App = () => (
<>
<Button type="primary">PRESS ME</Button>
<DatePicker placeholder="select date" />
<DatePicker />
</>
);
```
@@ -99,20 +70,21 @@ And import style manually:
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
```
Or use [babel-plugin-import](https://ant.design/docs/react/getting-started#Import-on-Demand).
### TypeScript
`antd` is written in TypeScript with complete definitions, check [Use in TypeScript](https://ant.design/docs/react/use-in-typescript) to getting started.
See [Use in TypeScript](https://ant.design/docs/react/use-in-typescript).
## 🌍 Internationalization
Dozens of languages supported in `antd`, see [i18n](https://ant.design/docs/react/i18n).
See [i18n](http://ant.design/docs/react/i18n).
## 🔗 Links
- [Home page](https://ant.design/)
- [Components](https://ant.design/components/overview)
- [Home page](http://ant.design/)
- [Components](http://ant.design/docs/react/introduce)
- [Ant Design Pro](http://pro.ant.design/)
- [Ant Design Charts](https://charts.ant.design)
- [Change Log](CHANGELOG.en-US.md)
- [rc-components](http://react-component.github.io/)
- [Mobile UI](http://mobile.ant.design)
@@ -129,8 +101,7 @@ Dozens of languages supported in `antd`, see [i18n](https://ant.design/docs/reac
- [FAQ](https://ant.design/docs/react/faq)
- [CodeSandbox Template](https://u.ant.design/codesandbox-repro) for bug reports
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
- [Customize Theme](https://ant.design/docs/react/customize-theme)
- [How to Apply for Being A Collaborator](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
- [Customize Theme](http://ant.design/docs/react/customize-theme)
## ⌨️ Development

View File

@@ -1,117 +1,69 @@
name: Ant Design
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
name: ant design
trigger: none
pr:
autoCancel: true
trigger:
batch: true
branches:
exclude:
- gh-pages
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: site
jobs:
- job: Build_Site
steps:
- checkout: self
displayName: 'Checkout'
clean: true
fetchDepth: 1
- task: NodeTool@0
displayName: 'Install Node.js'
inputs:
versionSpec: '12.13.1'
- script: npm install
displayName: 'Install modules'
- script: |
node ./scripts/azure-github-comment.js "[![Prepare preview](https://user-images.githubusercontent.com/5378891/72351368-2c979e00-371b-11ea-9652-eb4e825d745e.gif)](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
displayName: 'Comment on github'
- script: npm run site
displayName: 'Build sites'
- script: ls -al _site/
displayName: 'List build'
- script: |
export DEPLOY_DOMAIN=https://preview-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh
echo "Deploy to $DEPLOY_DOMAIN"
npx surge --project ./_site --domain $DEPLOY_DOMAIN
displayName: 'Deploy Site'
- script: |
export DEPLOY_DOMAIN=https://preview-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh
node ./scripts/azure-github-comment.js "[<img width="306" src="https://user-images.githubusercontent.com/5378891/72400743-23dbb200-3785-11ea-9d13-1a2d92743846.png">]($DEPLOY_DOMAIN)"
displayName: 'Update comment on github'
- job: Build_Site_Failed
dependsOn: Build_Site
condition: failed()
steps:
- checkout: self
displayName: 'Checkout'
clean: true
fetchDepth: 1
- task: NodeTool@0
displayName: 'Install Node.js'
inputs:
versionSpec: '12.13.1'
- script: npm install
displayName: 'Install modules'
- script: |
node ./scripts/azure-github-comment.js "[<img width="534" src="https://user-images.githubusercontent.com/5378891/75333447-1e63a280-58c1-11ea-975d-235367fd1522.png">](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
displayName: 'Comment on github'
- stage: ui
dependsOn: []
jobs:
- job: UI_Test
steps:
- checkout: self
displayName: 'Checkout'
clean: true
fetchDepth: 1
- task: NodeTool@0
displayName: 'Install Node.js'
inputs:
versionSpec: '12.16.3'
- script: npm install
displayName: 'Install modules'
- script: |
node ./scripts/azure-github-comment.js -ui "[![UI Testing](https://user-images.githubusercontent.com/14831261/82744255-6bfc8800-9da8-11ea-9017-857933e8433b.gif)](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
displayName: 'Comment on github'
- script: npm run test-image
displayName: 'UI Test'
- task: PublishPipelineArtifact@1
inputs:
targetPath: $(System.DefaultWorkingDirectory)/jest-stare
artifactName: jestStare
condition: failed()
- script: |
node ./scripts/azure-github-comment.js -ui "[<img width="306" src="https://user-images.githubusercontent.com/14831261/82744259-6e5ee200-9da8-11ea-8479-685f6e280b77.jpg">](https://dev.azure.com/ant-design/ant-design/_build/results?buildId=$(Build.BuildId))"
displayName: 'Update comment on github'
- job: UI_Test_Failed
dependsOn: UI_Test
condition: failed()
steps:
- checkout: self
displayName: 'Checkout'
clean: true
fetchDepth: 1
- task: NodeTool@0
displayName: 'Install Node.js'
inputs:
versionSpec: '12.13.1'
- script: npm install
displayName: 'Install modules'
- task: DownloadPipelineArtifact@2
inputs:
artifact: jestStare
path: './jest-stare'
- script: ls -al ./jest-stare
displayName: 'List report'
- script: |
export DEPLOY_DOMAIN=https://ui-test-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh
echo "Deploy to $DEPLOY_DOMAIN"
npx surge --project ./jest-stare --domain $DEPLOY_DOMAIN
displayName: 'Deploy Report Site'
- script: |
node ./scripts/azure-github-comment.js -ui "[<img width="306" src="https://user-images.githubusercontent.com/14831261/82744257-6dc64b80-9da8-11ea-80cf-05b2279a5602.jpg">](https://ui-test-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ant-design.surge.sh)"
displayName: 'Update comment on github'
jobs:
- job: test_
pool:
vmImage: 'Ubuntu-16.04'
strategy:
matrix:
Lint:
TEST_TYPE: lint
dist-react@16:
REACT: 16
TEST_TYPE: test:dist
lib-react@16:
REACT: 16
TEST_TYPE: test:lib
es-react@16:
REACT: 16
TEST_TYPE: test:es
dom-react@16:
REACT: 16
TEST_TYPE: test:dom
node-react@16:
REACT: 16
TEST_TYPE: test:node
dist-react@15:
REACT: 15
TEST_TYPE: test:dist
lib-react@15:
REACT: 15
TEST_TYPE: test:lib
es-react@15:
REACT: 15
TEST_TYPE: test:es
dom-react@15:
REACT: 15
TEST_TYPE: test:dom
node-react@15:
REACT: 15
TEST_TYPE: test:node
steps:
- checkout: self
fetchDepth: 1
clean: false
- task: NodeTool@0
inputs:
versionSpec: '10.x'
- script: npm install
displayName: install
- script: scripts/install-react.sh
displayName: install-react
- script: scripts/travis-script.sh
displayName: test
- task: PublishBuildArtifacts@1
# 主分支,并且运行成功
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
pathtoPublish: './package-lock.json'
artifactName: lock

View File

@@ -27,11 +27,12 @@ Array [
"Drawer",
"Empty",
"Form",
"Grid",
"Icon",
"Input",
"InputNumber",
"Layout",
"List",
"LocaleProvider",
"message",
"Menu",
"Mentions",
@@ -50,7 +51,6 @@ Array [
"Select",
"Skeleton",
"Slider",
"Space",
"Spin",
"Steps",
"Switch",
@@ -64,6 +64,7 @@ Array [
"Timeline",
"Tooltip",
"Typography",
"Mention",
"Upload",
"version",
]

View File

@@ -0,0 +1,58 @@
const __NULL__ = { notExist: true };
export function spyElementPrototypes(Element, properties) {
const propNames = Object.keys(properties);
const originDescriptors = {};
propNames.forEach(propName => {
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
originDescriptors[propName] = originDescriptor || __NULL__;
const spyProp = properties[propName];
if (typeof spyProp === 'function') {
// If is a function
Element.prototype[propName] = function spyFunc(...args) {
return spyProp.call(this, originDescriptor, ...args);
};
} else {
// Otherwise tread as a property
Object.defineProperty(Element.prototype, propName, {
...spyProp,
set(value) {
if (spyProp.set) {
return spyProp.set.call(this, originDescriptor, value);
}
return originDescriptor.set(value);
},
get() {
if (spyProp.get) {
return spyProp.get.call(this, originDescriptor);
}
return originDescriptor.get();
},
});
}
});
return {
mockRestore() {
propNames.forEach(propName => {
const originDescriptor = originDescriptors[propName];
if (originDescriptor === __NULL__) {
delete Element.prototype[propName];
} else if (typeof originDescriptor === 'function') {
Element.prototype[propName] = originDescriptor;
} else {
Object.defineProperty(Element.prototype, propName, originDescriptor);
}
});
},
};
}
export function spyElementPrototype(Element, propName, property) {
return spyElementPrototypes(Element, {
[propName]: property,
});
}

View File

@@ -1,57 +0,0 @@
import getScroll from '../getScroll';
describe('getScroll', () => {
it('getScroll target null', async () => {
expect(getScroll(null, true)).toBe(0);
expect(getScroll(null, false)).toBe(0);
});
it('getScroll window', async () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.pageXOffset = x;
window.pageYOffset = y;
});
window.scrollTo(200, 400);
expect(getScroll(window, true)).toBe(400);
expect(getScroll(window, false)).toBe(200);
scrollToSpy.mockRestore();
});
it('getScroll document', async () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
document.documentElement.scrollLeft = x;
document.documentElement.scrollTop = y;
});
window.scrollTo(200, 400);
expect(getScroll(document, true)).toBe(400);
expect(getScroll(document, false)).toBe(200);
scrollToSpy.mockRestore();
});
it('getScroll div', async () => {
const div = document.createElement('div');
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
div.scrollLeft = x;
div.scrollTop = y;
});
window.scrollTo(200, 400);
expect(getScroll(div, true)).toBe(400);
expect(getScroll(div, false)).toBe(200);
scrollToSpy.mockRestore();
});
it('getScroll documentElement', async () => {
const div = {};
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
div.scrollLeft = null;
div.scrollTop = null;
div.documentElement = {};
div.documentElement.scrollLeft = x;
div.documentElement.scrollTop = y;
});
window.scrollTo(200, 400);
expect(getScroll(div, true)).toBe(400);
expect(getScroll(div, false)).toBe(200);
scrollToSpy.mockRestore();
});
});

View File

@@ -1,11 +0,0 @@
/**
* @jest-environment node
*/
import getScroll from '../getScroll';
describe('getScroll node', () => {
it('getScroll return 0 in node environment', async () => {
expect(getScroll(null, true)).toBe(0);
expect(getScroll(null, false)).toBe(0);
});
});

View File

@@ -1,14 +0,0 @@
import ResponsiveObserve, { responsiveMap } from '../responsiveObserve';
describe('Test ResponsiveObserve', () => {
it('test ResponsiveObserve subscribe and unsubscribe', () => {
const { xs } = responsiveMap;
const subscribeFunc = jest.fn();
const token = ResponsiveObserve.subscribe(subscribeFunc);
expect(ResponsiveObserve.matchHandlers[xs].mql.matches).toBeTruthy();
expect(subscribeFunc).toBeCalledTimes(1);
ResponsiveObserve.unsubscribe(token);
expect(ResponsiveObserve.matchHandlers[xs].mql.removeListener).toBeCalled();
});
});

View File

@@ -1,9 +1,16 @@
import scrollTo from '../scrollTo';
import { sleep } from '../../../tests/utils';
describe('Test ScrollTo function', () => {
let dateNowMock;
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
beforeEach(() => {
dateNowMock = jest
.spyOn(Date, 'now')
@@ -22,8 +29,8 @@ describe('Test ScrollTo function', () => {
});
scrollTo(1000);
await sleep(20);
jest.runAllTimers();
expect(window.pageYOffset).toBe(1000);
scrollToSpy.mockRestore();
@@ -34,7 +41,7 @@ describe('Test ScrollTo function', () => {
scrollTo(1000, {
callback: cbMock,
});
await sleep(20);
jest.runAllTimers();
expect(cbMock).toHaveBeenCalledTimes(1);
});
@@ -43,24 +50,7 @@ describe('Test ScrollTo function', () => {
scrollTo(1000, {
getContainer: () => div,
});
await sleep(20);
jest.runAllTimers();
expect(div.scrollTop).toBe(1000);
});
it('test getContainer document - option', async () => {
scrollTo(1000, {
getContainer: () => document,
});
await sleep(20);
expect(document.documentElement.scrollTop).toBe(1000);
});
it('test duration - option', async () => {
scrollTo(1000, {
duration: 1100,
getContainer: () => document,
});
await sleep(20);
expect(document.documentElement.scrollTop).toBe(1000);
});
});

View File

@@ -1,12 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import TransButton from '../transButton';
describe('transButton component', () => {
it('disabled should update style', () => {
const wrapper = mount(<TransButton disabled />);
expect(wrapper.find('div').first().props().style).toEqual(
expect.objectContaining({ pointerEvents: 'none' }),
);
});
});

View File

@@ -1,8 +0,0 @@
import UnreachableException from '../unreachableException';
describe('UnreachableException', () => {
it('error thrown matches snapshot', () => {
const exception = new UnreachableException('some value');
expect(exception.message).toMatchInlineSnapshot(`"unreachable case: \\"some value\\""`);
});
});

View File

@@ -5,36 +5,41 @@ import KeyCode from 'rc-util/lib/KeyCode';
import delayRaf from '../raf';
import throttleByAnimationFrame from '../throttleByAnimationFrame';
import getDataOrAriaProps from '../getDataOrAriaProps';
import triggerEvent from '../triggerEvent';
import Wave from '../wave';
import TransButton from '../transButton';
import openAnimation from '../openAnimation';
import { sleep } from '../../../tests/utils';
import focusTest from '../../../tests/shared/focusTest';
describe('Test utils function', () => {
focusTest(TransButton);
beforeAll(() => {
jest.useFakeTimers();
});
it('throttle function should work', async () => {
afterAll(() => {
jest.useRealTimers();
});
it('throttle function should work', () => {
const callback = jest.fn();
const throttled = throttleByAnimationFrame(callback);
expect(callback).not.toHaveBeenCalled();
throttled();
throttled();
await sleep(20);
jest.runAllTimers();
expect(callback).toHaveBeenCalled();
expect(callback.mock.calls.length).toBe(1);
});
it('throttle function should be canceled', async () => {
it('throttle function should be canceled', () => {
const callback = jest.fn();
const throttled = throttleByAnimationFrame(callback);
throttled();
throttled.cancel();
await sleep(20);
jest.runAllTimers();
expect(callback).not.toHaveBeenCalled();
});
@@ -121,6 +126,19 @@ describe('Test utils function', () => {
});
});
it('triggerEvent', () => {
const button = document.createElement('button');
button.addEventListener(
'click',
() => {
button.style.width = '100px';
},
true,
);
triggerEvent(button, 'click');
expect(button.style.width).toBe('100px');
});
describe('wave', () => {
it('bindAnimationEvent should return when node is null', () => {
const wrapper = mount(
@@ -165,6 +183,9 @@ describe('Test utils function', () => {
});
it('should not throw when no children', () => {
if (process.env.REACT === '15') {
return;
}
expect(() => mount(<Wave />)).not.toThrow();
});
});

View File

@@ -1,151 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import Wave from '../wave';
import ConfigProvider from '../../config-provider';
import mountTest from '../../../tests/shared/mountTest';
import { sleep } from '../../../tests/utils';
describe('Wave component', () => {
mountTest(Wave);
afterEach(() => {
const styles = document.getElementsByTagName('style');
for (let i = 0; i < styles.length; i += 1) {
styles[i].remove();
}
});
it('isHidden works', () => {
const TEST_NODE_ENV = process.env.NODE_ENV;
process.env.NODE_ENV = 'development';
const wrapper = mount(
<Wave>
<button type="button">button</button>
</Wave>,
);
expect(wrapper.find('button').getDOMNode().className).toBe('');
wrapper.find('button').getDOMNode().click();
expect(
wrapper.find('button').getDOMNode().hasAttribute('ant-click-animating-without-extra-node'),
).toBe(false);
wrapper.unmount();
process.env.NODE_ENV = TEST_NODE_ENV;
});
it('isHidden is mocked', () => {
const wrapper = mount(
<Wave>
<button type="button">button</button>
</Wave>,
);
expect(wrapper.find('button').getDOMNode().className).toBe('');
wrapper.find('button').getDOMNode().click();
expect(
wrapper.find('button').getDOMNode().getAttribute('ant-click-animating-without-extra-node'),
).toBe('false');
wrapper.unmount();
});
it('wave color is grey', async () => {
const wrapper = mount(
<Wave>
<button type="button" style={{ borderColor: 'rgb(0, 0, 0)' }}>
button
</button>
</Wave>,
);
wrapper.find('button').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
expect(styles.length).toBe(0);
wrapper.unmount();
});
it('wave color is not grey', async () => {
const wrapper = mount(
<Wave>
<button type="button" style={{ borderColor: 'red' }}>
button
</button>
</Wave>,
);
wrapper.find('button').getDOMNode().click();
await sleep(200);
const styles = document.getElementsByTagName('style');
expect(styles.length).toBe(1);
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: red;');
wrapper.unmount();
});
it('read wave color from border-top-color', async () => {
const wrapper = mount(
<Wave>
<div style={{ borderTopColor: 'blue' }}>button</div>
</Wave>,
);
wrapper.find('div').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
expect(styles.length).toBe(1);
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: blue;');
wrapper.unmount();
});
it('read wave color from background color', async () => {
const wrapper = mount(
<Wave>
<div style={{ backgroundColor: 'green' }}>button</div>
</Wave>,
);
wrapper.find('div').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
expect(styles.length).toBe(1);
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: green;');
wrapper.unmount();
});
it('read wave color from border firstly', async () => {
const wrapper = mount(
<Wave>
<div style={{ borderColor: 'yellow', backgroundColor: 'green' }}>button</div>
</Wave>,
);
wrapper.find('div').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
expect(styles.length).toBe(1);
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: yellow;');
wrapper.unmount();
});
it('hidden element with -leave className', async () => {
const wrapper = mount(
<Wave>
<button type="button" className="xx-leave">
button
</button>
</Wave>,
);
wrapper.find('button').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
expect(styles.length).toBe(0);
wrapper.unmount();
});
it('ConfigProvider csp', async () => {
const wrapper = mount(
<ConfigProvider csp={{ nonce: 'YourNonceCode' }}>
<Wave>
<button type="button">button</button>
</Wave>
</ConfigProvider>,
);
wrapper.find('button').getDOMNode().click();
await sleep(0);
const styles = document.getElementsByTagName('style');
expect(styles[0].getAttribute('nonce')).toBe('YourNonceCode');
wrapper.unmount();
});
});

View File

@@ -1,6 +1,5 @@
import { ElementOf, tuple } from './type';
import { tuple } from './type';
export const PresetStatusColorTypes = tuple('success', 'processing', 'error', 'default', 'warning');
// eslint-disable-next-line import/prefer-default-export
export const PresetColorTypes = tuple(
'pink',
@@ -18,5 +17,4 @@ export const PresetColorTypes = tuple(
'lime',
);
export type PresetColorType = ElementOf<typeof PresetColorTypes>;
export type PresetStatusColorType = ElementOf<typeof PresetStatusColorTypes>;
export type PresetColorType = (typeof PresetColorTypes)[number];

View File

@@ -1,7 +0,0 @@
import devWarning, { resetWarned } from 'rc-util/lib/warning';
export { resetWarned };
export default (valid: boolean, component: string, message: string): void => {
devWarning(valid, `[antd: ${component}] ${message}`);
};

View File

@@ -5,6 +5,5 @@ export function easeInOutCubic(t: number, b: number, c: number, d: number) {
if (t < 1) {
return (cc / 2) * t * t * t + b;
}
// eslint-disable-next-line no-return-assign
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}

View File

@@ -1,18 +0,0 @@
import * as React from 'react';
export type RenderFunction = () => React.ReactNode;
export const getRenderPropValue = (
propValue?: React.ReactNode | RenderFunction,
): React.ReactNode => {
if (!propValue) {
return null;
}
const isRenderFunction = typeof propValue === 'function';
if (isRenderFunction) {
return (propValue as RenderFunction)();
}
return propValue;
};

View File

@@ -1,27 +1,17 @@
export function isWindow(obj: any) {
return obj !== null && obj !== undefined && obj === obj.window;
}
export default function getScroll(
target: HTMLElement | Window | Document | null,
top: boolean,
): number {
export default function getScroll(target: HTMLElement | Window | null, top: boolean): number {
if (typeof window === 'undefined') {
return 0;
}
const prop = top ? 'pageYOffset' : 'pageXOffset';
const method = top ? 'scrollTop' : 'scrollLeft';
let result = 0;
if (isWindow(target)) {
result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset'];
} else if (target instanceof Document) {
result = target.documentElement[method];
} else if (target) {
result = (target as HTMLElement)[method];
const isWindow = target === window;
let ret = isWindow ? (target as Window)[prop] : (target as HTMLElement)[method];
// ie6,7,8 standard mode
if (isWindow && typeof ret !== 'number') {
ret = (document.documentElement as HTMLElement)[method];
}
if (target && !isWindow(target) && typeof result !== 'number') {
result = ((target as HTMLElement).ownerDocument || (target as Document)).documentElement[
method
];
}
return result;
return ret;
}

View File

@@ -0,0 +1,5 @@
// https://github.com/moment/moment/issues/3650
// since we are using ts 3.5.1, it should be safe to remove.
export default function interopDefault(m: any) {
return m.default || m;
}

View File

@@ -1,7 +1,6 @@
import * as React from 'react';
type MotionFunc = (element: HTMLElement) => React.CSSProperties;
type MotionEndFunc = (element: HTMLElement, event: TransitionEvent) => boolean;
interface Motion {
visible?: boolean;
@@ -10,25 +9,23 @@ interface Motion {
motionEnter?: boolean;
motionLeave?: boolean;
motionLeaveImmediately?: boolean; // Trigger leave motion immediately
motionDeadline?: number;
removeOnLeave?: boolean;
leavedClassName?: string;
onAppearStart?: MotionFunc;
onAppearActive?: MotionFunc;
onAppearEnd?: MotionEndFunc;
onAppearEnd?: MotionFunc;
onEnterStart?: MotionFunc;
onEnterActive?: MotionFunc;
onEnterEnd?: MotionEndFunc;
onEnterEnd?: MotionFunc;
onLeaveStart?: MotionFunc;
onLeaveActive?: MotionFunc;
onLeaveEnd?: MotionEndFunc;
onLeaveEnd?: MotionFunc;
}
// ================== Collapse Motion ==================
const getCollapsedHeight: MotionFunc = () => ({ height: 0, opacity: 0 });
const getRealHeight: MotionFunc = node => ({ height: node.scrollHeight, opacity: 1 });
const getCurrentHeight: MotionFunc = node => ({ height: node.offsetHeight });
const skipOpacityTransition: MotionEndFunc = (_, event) => event.propertyName === 'height';
const collapseMotion: Motion = {
motionName: 'ant-motion-collapse',
@@ -38,10 +35,6 @@ const collapseMotion: Motion = {
onEnterActive: getRealHeight,
onLeaveStart: getCurrentHeight,
onLeaveActive: getCollapsedHeight,
onAppearEnd: skipOpacityTransition,
onEnterEnd: skipOpacityTransition,
onLeaveEnd: skipOpacityTransition,
motionDeadline: 500,
};
export default collapseMotion;

View File

@@ -2,7 +2,7 @@
* Deprecated. We should replace the animation with pure react motion instead of modify style directly.
* If you are creating new component with animation, please use `./motion`.
*/
import cssAnimation from '@ant-design/css-animation';
import cssAnimation from 'css-animation';
import raf from 'raf';
function animate(node: HTMLElement, show: boolean, done: () => void) {

View File

@@ -1,17 +1,8 @@
import * as React from 'react';
export const { isValidElement } = React;
// eslint-disable-next-line import/prefer-default-export
export function cloneElement(element: React.ReactNode, ...restArgs: any[]) {
if (!React.isValidElement(element)) return element;
export function replaceElement(
element: React.ReactNode,
replacement: React.ReactNode,
props: any,
): React.ReactNode {
if (!isValidElement(element)) return replacement;
return React.cloneElement(element, typeof props === 'function' ? props() : props);
}
export function cloneElement(element: React.ReactNode, props?: any): React.ReactElement {
return replaceElement(element, element, props) as React.ReactElement;
return React.cloneElement(element, ...restArgs);
}

View File

@@ -1,4 +1,4 @@
import * as React from 'react';
import React from 'react';
export function fillRef<T>(ref: React.Ref<T>, node: T) {
if (typeof ref === 'function') {

View File

@@ -1,6 +1,25 @@
// matchMedia polyfill for
// https://github.com/WickyNilliams/enquire.js/issues/82
let enquire: any;
// TODO: Will be removed in antd 4.0 because we will no longer support ie9
if (typeof window !== 'undefined') {
const matchMediaPolyfill = (mediaQuery: string) => {
return {
media: mediaQuery,
matches: false,
addListener() {},
removeListener() {},
};
};
// ref: https://github.com/ant-design/ant-design/issues/18774
if (!window.matchMedia) window.matchMedia = matchMediaPolyfill as any;
// eslint-disable-next-line global-require
enquire = require('enquire.js');
}
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
export type BreakpointMap = Partial<Record<Breakpoint, string>>;
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
export const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
@@ -13,60 +32,72 @@ export const responsiveMap: BreakpointMap = {
xxl: '(min-width: 1600px)',
};
type SubscribeFunc = (screens: ScreenMap) => void;
const subscribers = new Map<Number, SubscribeFunc>();
type SubscribeFunc = (screens: BreakpointMap) => void;
let subscribers: Array<{
token: string;
func: SubscribeFunc;
}> = [];
let subUid = -1;
let screens = {};
const responsiveObserve = {
matchHandlers: {} as {
[prop: string]: {
mql: MediaQueryList;
listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
};
},
dispatch(pointMap: ScreenMap) {
dispatch(pointMap: BreakpointMap) {
screens = pointMap;
subscribers.forEach(func => func(screens));
return subscribers.size >= 1;
if (subscribers.length < 1) {
return false;
}
subscribers.forEach(item => {
item.func(screens);
});
return true;
},
subscribe(func: SubscribeFunc): number {
if (!subscribers.size) this.register();
subUid += 1;
subscribers.set(subUid, func);
subscribe(func: SubscribeFunc) {
if (subscribers.length === 0) {
this.register();
}
const token = (++subUid).toString();
subscribers.push({
token,
func,
});
func(screens);
return subUid;
return token;
},
unsubscribe(token: number) {
subscribers.delete(token);
if (!subscribers.size) this.unregister();
unsubscribe(token: string) {
subscribers = subscribers.filter(item => item.token !== token);
if (subscribers.length === 0) {
this.unregister();
}
},
unregister() {
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
const matchMediaQuery = responsiveMap[screen]!;
const handler = this.matchHandlers[matchMediaQuery];
handler?.mql.removeListener(handler?.listener);
});
subscribers.clear();
Object.keys(responsiveMap).map((screen: Breakpoint) =>
enquire.unregister(responsiveMap[screen]),
);
},
register() {
Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
const matchMediaQuery = responsiveMap[screen]!;
const listener = ({ matches }: { matches: boolean }) => {
this.dispatch({
...screens,
[screen]: matches,
});
};
const mql = window.matchMedia(matchMediaQuery);
mql.addListener(listener);
this.matchHandlers[matchMediaQuery] = {
mql,
listener,
};
listener(mql);
});
Object.keys(responsiveMap).map((screen: Breakpoint) =>
enquire.register(responsiveMap[screen], {
match: () => {
const pointMap = {
...screens,
[screen]: true,
};
this.dispatch(pointMap);
},
unmatch: () => {
const pointMap = {
...screens,
[screen]: false,
};
this.dispatch(pointMap);
},
// Keep a empty destory to avoid triggering unmatch when unregister
destroy() {},
}),
);
},
};

View File

@@ -1,10 +1,10 @@
import raf from 'raf';
import getScroll, { isWindow } from './getScroll';
import getScroll from './getScroll';
import { easeInOutCubic } from './easings';
interface ScrollToOptions {
/** Scroll container, default as window */
getContainer?: () => HTMLElement | Window | Document;
getContainer?: () => HTMLElement | Window;
/** Scroll end callback */
callback?: () => any;
/** Animation duration, default as 450 */
@@ -22,10 +22,8 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) {
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
if (isWindow(container)) {
(container as Window).scrollTo(window.pageXOffset, nextScrollTop);
} else if (container instanceof HTMLDocument || container.constructor.name === 'HTMLDocument') {
(container as HTMLDocument).documentElement.scrollTop = nextScrollTop;
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}

View File

@@ -8,8 +8,6 @@ import KeyCode from 'rc-util/lib/KeyCode';
interface TransButtonProps extends React.HTMLAttributes<HTMLDivElement> {
onClick?: (e?: React.MouseEvent<HTMLDivElement>) => void;
noStyle?: boolean;
autoFocus?: boolean;
disabled?: boolean;
}
const inlineStyle: React.CSSProperties = {
@@ -25,13 +23,6 @@ class TransButton extends React.Component<TransButtonProps> {
lastKeyCode?: number;
componentDidMount() {
const { autoFocus } = this.props;
if (autoFocus) {
this.focus();
}
}
onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = event => {
const { keyCode } = event;
if (keyCode === KeyCode.ENTER) {
@@ -64,24 +55,7 @@ class TransButton extends React.Component<TransButtonProps> {
}
render() {
const { style, noStyle, disabled, ...restProps } = this.props;
let mergedStyle: React.CSSProperties = {};
if (!noStyle) {
mergedStyle = {
...inlineStyle,
};
}
if (disabled) {
mergedStyle.pointerEvents = 'none';
}
mergedStyle = {
...mergedStyle,
...style,
};
const { style, noStyle, ...restProps } = this.props;
return (
<div
@@ -91,7 +65,7 @@ class TransButton extends React.Component<TransButtonProps> {
{...restProps}
onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
style={mergedStyle}
style={{ ...(!noStyle ? inlineStyle : null), ...style }}
/>
);
}

View File

@@ -0,0 +1,8 @@
export default function triggerEvent(el: Element, type: string) {
if ('createEvent' in document) {
// modern browsers, IE9+
const e = document.createEvent('HTMLEvents');
e.initEvent(type, false, true);
el.dispatchEvent(e);
}
}

View File

@@ -3,14 +3,3 @@ export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export const tuple = <T extends string[]>(...args: T) => args;
export const tupleNum = <T extends number[]>(...args: T) => args;
/**
* https://stackoverflow.com/a/59187769
* Extract the type of an element of an array/tuple without performing indexing
*/
export type ElementOf<T> = T extends (infer E)[] ? E : T extends readonly (infer E)[] ? E : never;
/**
* https://github.com/Microsoft/TypeScript/issues/29729
*/
export type LiteralUnion<T extends U, U> = T | (U & {});

View File

@@ -1,5 +0,0 @@
export default class UnreachableException {
constructor(value: never) {
return new Error(`unreachable case: ${JSON.stringify(value)}`);
}
}

View File

@@ -1,21 +0,0 @@
import * as React from 'react';
export default function usePatchElement(): [
React.ReactElement[],
(element: React.ReactElement) => Function,
] {
const [elements, setElements] = React.useState<React.ReactElement[]>([]);
function patchElement(element: React.ReactElement) {
// append a new element to elements (and create a new ref)
setElements(originElements => [...originElements, element]);
// return a function that removes the new element out of elements (and create a new ref)
// it works a little like useEffect
return () => {
setElements(originElements => originElements.filter(ele => ele !== element));
};
}
return [elements, patchElement];
}

View File

@@ -0,0 +1,7 @@
import warning, { resetWarned } from 'rc-util/lib/warning';
export { resetWarned };
export default (valid: boolean, component: string, message: string): void => {
warning(valid, `[antd: ${component}] ${message}`);
};

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { findDOMNode } from 'react-dom';
import TransitionEvents from '@ant-design/css-animation/lib/Event';
import TransitionEvents from 'css-animation/lib/Event';
import raf from './raf';
import { ConfigConsumer, ConfigConsumerProps, CSPConfig, ConfigContext } from '../config-provider';
import { ConfigConsumer, ConfigConsumerProps, CSPConfig } from '../config-provider';
let styleForPesudo: HTMLStyleElement | null;
@@ -16,7 +16,7 @@ function isHidden(element: HTMLElement) {
function isNotGrey(color: string) {
// eslint-disable-next-line no-useless-escape
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\d.]*)?\)/);
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
if (match && match[1] && match[2] && match[3]) {
return !(match[1] === match[2] && match[2] === match[3]);
}
@@ -24,8 +24,6 @@ function isNotGrey(color: string) {
}
export default class Wave extends React.Component<{ insertExtraNode?: boolean }> {
static contextType = ConfigContext;
private instance?: {
cancel: () => void;
};
@@ -38,12 +36,10 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
private animationStart: boolean = false;
private destroyed: boolean = false;
private destroy: boolean = false;
private csp?: CSPConfig;
context: ConfigConsumerProps;
componentDidMount() {
const node = findDOMNode(this) as HTMLElement;
if (!node || node.nodeType !== 1) {
@@ -60,7 +56,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
clearTimeout(this.clickWaveTimeoutId);
}
this.destroyed = true;
this.destroy = true;
}
onClick = (node: HTMLElement, waveColor: string) => {
@@ -70,8 +66,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
const { insertExtraNode } = this.props;
this.extraNode = document.createElement('div');
const { extraNode } = this;
const { getPrefixCls } = this.context;
extraNode.className = `${getPrefixCls('')}-click-animating-node`;
extraNode.className = 'ant-click-animating-node';
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'true');
// Not white or transparnt or grey
@@ -81,7 +76,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
waveColor !== '#ffffff' &&
waveColor !== 'rgb(255, 255, 255)' &&
isNotGrey(waveColor) &&
!/rgba\((?:\d*, ){3}0\)/.test(waveColor) && // any transparent rgba color
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
waveColor !== 'transparent'
) {
// Add nonce if CSP exist
@@ -91,9 +86,7 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
extraNode.style.borderColor = waveColor;
styleForPesudo.innerHTML = `
[${getPrefixCls('')}-click-animating-without-extra-node='true']::after, .${getPrefixCls(
'',
)}-click-animating-node {
[ant-click-animating-without-extra-node='true']::after, .ant-click-animating-node {
--antd-wave-shadow-color: ${waveColor};
}`;
if (!document.body.contains(styleForPesudo)) {
@@ -108,16 +101,16 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
};
onTransitionStart = (e: AnimationEvent) => {
if (this.destroyed) {
return;
}
if (this.destroy) return;
const node = findDOMNode(this) as HTMLElement;
if (!e || e.target !== node || this.animationStart) {
if (!e || e.target !== node) {
return;
}
this.resetEffect(node);
if (!this.animationStart) {
this.resetEffect(node);
}
};
onTransitionEnd = (e: AnimationEvent) => {
@@ -128,11 +121,8 @@ export default class Wave extends React.Component<{ insertExtraNode?: boolean }>
};
getAttributeName() {
const { getPrefixCls } = this.context;
const { insertExtraNode } = this.props;
return insertExtraNode
? `${getPrefixCls('')}-click-animating`
: `${getPrefixCls('')}-click-animating-without-extra-node`;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
}
bindAnimationEvent = (node: HTMLElement) => {

View File

@@ -0,0 +1,198 @@
import React from 'react';
import { mount } from 'enzyme';
import Affix from '..';
import { getObserverEntities } from '../utils';
import Button from '../../button';
import { spyElementPrototype } from '../../__tests__/util/domHook';
const events = {};
class AffixMounter extends React.Component {
componentDidMount() {
this.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
events[event] = cb;
});
}
getTarget = () => this.container;
render() {
return (
<div
ref={node => {
this.container = node;
}}
className="container"
>
<Affix
className="fixed"
target={this.getTarget}
ref={ele => {
this.affix = ele;
}}
{...this.props}
>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
</div>
);
}
}
describe('Affix Render', () => {
let wrapper;
let domMock;
const classRect = {
container: {
top: 0,
bottom: 100,
},
};
beforeAll(() => {
jest.useFakeTimers();
domMock = spyElementPrototype(HTMLElement, 'getBoundingClientRect', function mockBounding() {
return (
classRect[this.className] || {
top: 0,
bottom: 0,
}
);
});
});
afterAll(() => {
jest.useRealTimers();
domMock.mockRestore();
});
const movePlaceholder = top => {
classRect.fixed = {
top,
bottom: top,
};
events.scroll({
type: 'scroll',
});
jest.runAllTimers();
};
it('Anchor render perfectly', () => {
document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
jest.runAllTimers();
movePlaceholder(0);
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
movePlaceholder(-100);
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
movePlaceholder(0);
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
});
it('support offsetBottom', () => {
document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter offsetBottom={0} />, {
attachTo: document.getElementById('mounter'),
});
jest.runAllTimers();
movePlaceholder(300);
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
movePlaceholder(0);
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
movePlaceholder(300);
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
});
it('updatePosition when offsetTop changed', () => {
document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter offsetTop={0} />, {
attachTo: document.getElementById('mounter'),
});
jest.runAllTimers();
movePlaceholder(-100);
expect(wrapper.instance().affix.state.affixStyle.top).toBe(0);
wrapper.setProps({
offsetTop: 10,
});
jest.runAllTimers();
expect(wrapper.instance().affix.state.affixStyle.top).toBe(10);
});
describe('updatePosition when target changed', () => {
it('function change', () => {
document.body.innerHTML = '<div id="mounter" />';
const container = document.querySelector('#id');
const getTarget = () => container;
wrapper = mount(<Affix target={getTarget} />);
wrapper.setProps({ target: null });
expect(wrapper.instance().state.status).toBe(0);
expect(wrapper.instance().state.affixStyle).toBe(undefined);
expect(wrapper.instance().state.placeholderStyle).toBe(undefined);
});
it('instance change', () => {
const getObserverLength = () => Object.keys(getObserverEntities()).length;
const container = document.createElement('div');
document.body.appendChild(container);
let target = container;
const originLength = getObserverLength();
const getTarget = () => target;
wrapper = mount(<Affix target={getTarget} />);
jest.runAllTimers();
expect(getObserverLength()).toBe(originLength + 1);
target = null;
wrapper.setProps({});
wrapper.update();
jest.runAllTimers();
expect(getObserverLength()).toBe(originLength);
});
});
describe('updatePosition when size changed', () => {
function test(name, index) {
it(name, () => {
document.body.innerHTML = '<div id="mounter" />';
const updateCalled = jest.fn();
wrapper = mount(<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />, {
attachTo: document.getElementById('mounter'),
});
jest.runAllTimers();
movePlaceholder(300);
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
jest.runAllTimers();
wrapper.update();
// Mock trigger resize
updateCalled.mockReset();
wrapper
.find('ResizeObserver')
.at(index)
.instance()
.onResize([{ target: { getBoundingClientRect: () => ({ width: 99, height: 99 }) } }]);
jest.runAllTimers();
expect(updateCalled).toHaveBeenCalled();
});
}
test('inner', 0);
test('outer', 1);
});
});

View File

@@ -1,230 +0,0 @@
import React from 'react';
import { mount, ReactWrapper, HTMLAttributes } from 'enzyme';
import ResizeObserverImpl from 'rc-resize-observer';
import Affix, { AffixProps, AffixState } from '..';
import { getObserverEntities } from '../utils';
import Button from '../../button';
import rtlTest from '../../../tests/shared/rtlTest';
import { sleep } from '../../../tests/utils';
const events: Partial<Record<keyof HTMLElementEventMap, (ev: Partial<Event>) => void>> = {};
class AffixMounter extends React.Component<{
offsetBottom?: number;
offsetTop?: number;
onTestUpdatePosition?(): void;
}> {
private container: HTMLDivElement;
public affix: Affix;
componentDidMount() {
this.container.addEventListener = jest
.fn()
.mockImplementation((event: keyof HTMLElementEventMap, cb: (ev: Partial<Event>) => void) => {
events[event] = cb;
});
}
getTarget = () => this.container;
render() {
return (
<div
ref={node => {
this.container = node!;
}}
className="container"
>
<Affix
className="fixed"
target={this.getTarget}
ref={ele => {
this.affix = ele!;
}}
{...this.props}
>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
</div>
);
}
}
describe('Affix Render', () => {
rtlTest(Affix);
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
let affixMounterWrapper: ReactWrapper<unknown, unknown, AffixMounter>;
let affixWrapper: ReactWrapper<AffixProps, AffixState, Affix>;
const classRect: Record<string, DOMRect> = {
container: {
top: 0,
bottom: 100,
} as DOMRect,
};
beforeAll(() => {
domMock.mockImplementation(function fn(this: HTMLElement) {
return (
classRect[this.className] || {
top: 0,
bottom: 0,
}
);
});
});
afterAll(() => {
domMock.mockRestore();
});
const movePlaceholder = async (top: number) => {
classRect.fixed = {
top,
bottom: top,
} as DOMRect;
if (events.scroll == null) {
throw new Error('scroll should be set');
}
events.scroll({
type: 'scroll',
});
await sleep(20);
};
it('Anchor render perfectly', async () => {
document.body.innerHTML = '<div id="mounter" />';
affixMounterWrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
await sleep(20);
await movePlaceholder(0);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
await movePlaceholder(-100);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
await movePlaceholder(0);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
});
it('support offsetBottom', async () => {
document.body.innerHTML = '<div id="mounter" />';
affixMounterWrapper = mount(<AffixMounter offsetBottom={0} />, {
attachTo: document.getElementById('mounter'),
});
await sleep(20);
await movePlaceholder(300);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
await movePlaceholder(0);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
await movePlaceholder(300);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
});
it('updatePosition when offsetTop changed', async () => {
document.body.innerHTML = '<div id="mounter" />';
affixMounterWrapper = mount(<AffixMounter offsetTop={0} />, {
attachTo: document.getElementById('mounter'),
});
await sleep(20);
await movePlaceholder(-100);
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(0);
affixMounterWrapper.setProps({
offsetTop: 10,
});
await sleep(20);
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(10);
});
describe('updatePosition when target changed', () => {
it('function change', () => {
document.body.innerHTML = '<div id="mounter" />';
const container = document.querySelector('#id') as HTMLDivElement;
const getTarget = () => container;
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
affixWrapper.setProps({ target: () => null });
expect(affixWrapper.instance().state.status).toBe(0);
expect(affixWrapper.instance().state.affixStyle).toBe(undefined);
expect(affixWrapper.instance().state.placeholderStyle).toBe(undefined);
});
it('instance change', async () => {
const getObserverLength = () => Object.keys(getObserverEntities()).length;
const container = document.createElement('div');
document.body.appendChild(container);
let target: HTMLDivElement | null = container;
const originLength = getObserverLength();
const getTarget = () => target;
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
await sleep(50);
expect(getObserverLength()).toBe(originLength + 1);
target = null;
affixWrapper.setProps({});
affixWrapper.update();
await sleep(50);
expect(getObserverLength()).toBe(originLength);
});
});
describe('updatePosition when size changed', () => {
it.each([
{ name: 'inner', index: 0 },
{ name: 'outer', index: 1 },
])(name, async ({ index }) => {
document.body.innerHTML = '<div id="mounter" />';
const updateCalled = jest.fn();
affixMounterWrapper = mount(
<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />,
{
attachTo: document.getElementById('mounter'),
},
);
await sleep(20);
await movePlaceholder(300);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
await sleep(20);
affixMounterWrapper.update();
// Mock trigger resize
updateCalled.mockReset();
const resizeObserverInstance: ReactWrapper<
HTMLAttributes,
unknown,
ResizeObserverImpl
> = affixMounterWrapper.find('ResizeObserver') as any;
resizeObserverInstance
.at(index)
.instance()
.onResize(
[
{
target: {
getBoundingClientRect: () => ({ width: 99, height: 99 }),
} as Element,
contentRect: {} as DOMRect,
},
],
({} as unknown) as ResizeObserver,
);
await sleep(20);
expect(updateCalled).toHaveBeenCalled();
});
});
});

View File

@@ -1,9 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Affix Render rtl render component should be rendered correctly in RTL direction 1`] = `
<div>
<div
class=""
/>
</div>
`;

View File

@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/affix/demo/basic.md correctly 1`] = `
Array [
<div>
<div>
<div
class=""
@@ -15,8 +15,8 @@ Array [
</span>
</button>
</div>
</div>,
<br />,
</div>
<br />
<div>
<div
class=""
@@ -30,8 +30,8 @@ Array [
</span>
</button>
</div>
</div>,
]
</div>
</div>
`;
exports[`renders ./components/affix/demo/debug.md correctly 1`] = `

View File

@@ -13,30 +13,47 @@ title:
The simplest usage.
```tsx
import React, { useState } from 'react';
```jsx
import { Affix, Button } from 'antd';
const Demo: React.FC = () => {
const [top, setTop] = useState(10);
const [bottom, setBottom] = useState(10);
class Demo extends React.Component {
state = {
top: 10,
bottom: 10,
};
return (
<>
<Affix offsetTop={top}>
<Button type="primary" onClick={() => setTop(top + 10)}>
Affix top
</Button>
</Affix>
<br />
<Affix offsetBottom={bottom}>
<Button type="primary" onClick={() => setBottom(bottom + 10)}>
Affix bottom
</Button>
</Affix>
</>
);
};
render() {
return (
<div>
<Affix offsetTop={this.state.top}>
<Button
type="primary"
onClick={() => {
this.setState({
top: this.state.top + 10,
});
}}
>
Affix top
</Button>
</Affix>
<br />
<Affix offsetBottom={this.state.bottom}>
<Button
type="primary"
onClick={() => {
this.setState({
bottom: this.state.bottom + 10,
});
}}
>
Affix bottom
</Button>
</Affix>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@@ -1,8 +1,8 @@
---
order: 99
title:
zh-CN: 调整浏览器大小,观察 Affix 容器是否发生变化。跟随变化为正常。#17678
en-US:
zh-CN: 调
en-US: Debug
debug: true
---
@@ -14,26 +14,37 @@ DEBUG
DEBUG
```tsx
import React, { useState } from 'react';
```jsx
import { Affix, Button } from 'antd';
const Demo: React.FC = () => {
const [top, setTop] = useState(10);
return (
<div style={{ height: 10000 }}>
<div>Top</div>
<Affix offsetTop={top}>
<div style={{ background: 'red' }}>
<Button type="primary" onClick={() => setTop(top + 10)}>
Affix top
</Button>
</div>
</Affix>
<div>Bottom</div>
</div>
);
};
class Demo extends React.Component {
state = {
top: 10,
};
render() {
return (
<div style={{ height: 10000 }}>
<div>Top</div>
<Affix offsetTop={this.state.top}>
<div style={{ background: 'red' }}>
<Button
type="primary"
onClick={() => {
this.setState({
top: this.state.top + 10,
});
}}
>
Affix top
</Button>
</div>
</Affix>
<div>Bottom</div>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@@ -13,7 +13,7 @@ title:
Callback with affixed state.
```tsx
```jsx
import { Affix, Button } from 'antd';
ReactDOM.render(

View File

@@ -13,22 +13,27 @@ title:
Set a `target` for 'Affix', which is listen to scroll event of target element (default is `window`).
```tsx
import React, { useState } from 'react';
```jsx
import { Affix, Button } from 'antd';
const Demo: React.FC = () => {
const [container, setContainer] = useState(null);
return (
<div className="scrollable-container" ref={setContainer}>
<div className="background">
<Affix target={() => container}>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
class Demo extends React.Component {
render() {
return (
<div
className="scrollable-container"
ref={node => {
this.container = node;
}}
>
<div className="background">
<Affix target={() => this.container}>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
</div>
</div>
</div>
);
};
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@@ -2,7 +2,6 @@
category: Components
type: Navigation
title: Affix
cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg
---
Wrap Affix around another component to make it stick the viewport.
@@ -15,12 +14,12 @@ Please note that Affix should not cover other content on the page, especially wh
## API
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| offsetBottom | Offset from the bottom of the viewport (in pixels) | number | - |
| offsetTop | Offset from the top of the viewport (in pixels) | number | 0 |
| target | Specifies the scrollable area DOM node | () => HTMLElement | () => window |
| onChange | Callback for when Affix state is changed | Function(affixed) | - |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| offsetBottom | Offset from the bottom of the viewport (in pixels) | number | - | |
| offsetTop | Offset from the top of the viewport (in pixels) | number | 0 | |
| target | Specifies the scrollable area DOM node | () => HTMLElement | () => window | |
| onChange | Callback for when Affix state is changed | Function(affixed) | - | |
**Note:** Children of `Affix` must not have the property `position: absolute`, but you can set `position: absolute` on `Affix` itself:

View File

@@ -1,10 +1,12 @@
import * as React from 'react';
import { polyfill } from 'react-lifecycles-compat';
import classNames from 'classnames';
import omit from 'omit.js';
import ResizeObserver from 'rc-resize-observer';
import { ConfigContext, ConfigConsumerProps } from '../config-provider';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
import warning from '../_util/warning';
import {
addObserveTarget,
removeObserveTarget,
@@ -23,6 +25,7 @@ export interface AffixProps {
* 距离窗口顶部达到指定偏移量后触发
*/
offsetTop?: number;
offset?: number;
/** 距离窗口底部达到指定偏移量后触发 */
offsetBottom?: number;
style?: React.CSSProperties;
@@ -32,7 +35,7 @@ export interface AffixProps {
target?: () => Window | HTMLElement | null;
prefixCls?: string;
className?: string;
children: React.ReactNode;
children: React.ReactElement;
}
enum AffixStatus {
@@ -50,7 +53,9 @@ export interface AffixState {
}
class Affix extends React.Component<AffixProps, AffixState> {
static contextType = ConfigContext;
static defaultProps = {
target: getDefaultTarget,
};
state: AffixState = {
status: AffixStatus.None,
@@ -64,27 +69,14 @@ class Affix extends React.Component<AffixProps, AffixState> {
private timeout: number;
context: ConfigConsumerProps;
private getTargetFunc() {
const { getTargetContainer } = this.context;
const { target } = this.props;
if (target !== undefined) {
return target;
}
return getTargetContainer || getDefaultTarget;
}
// Event handler
componentDidMount() {
const targetFunc = this.getTargetFunc();
if (targetFunc) {
const { target } = this.props;
if (target) {
// [Legacy] Wait for parent component ref has its value.
// We should use target as directly element instead of function which makes element check hard.
this.timeout = setTimeout(() => {
addObserveTarget(targetFunc(), this);
addObserveTarget(target(), this);
// Mock Event object.
this.updatePosition();
});
@@ -93,10 +85,10 @@ class Affix extends React.Component<AffixProps, AffixState> {
componentDidUpdate(prevProps: AffixProps) {
const { prevTarget } = this.state;
const targetFunc = this.getTargetFunc();
const { target } = this.props;
let newTarget = null;
if (targetFunc) {
newTarget = targetFunc() || null;
if (target) {
newTarget = target() || null;
}
if (prevTarget !== newTarget) {
@@ -107,7 +99,6 @@ class Affix extends React.Component<AffixProps, AffixState> {
this.updatePosition();
}
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ prevTarget: newTarget });
}
@@ -125,13 +116,20 @@ class Affix extends React.Component<AffixProps, AffixState> {
clearTimeout(this.timeout);
removeObserveTarget(this);
(this.updatePosition as any).cancel();
// https://github.com/ant-design/ant-design/issues/22683
(this.lazyUpdatePosition as any).cancel();
}
getOffsetTop = () => {
const { offsetBottom } = this.props;
const { offset, offsetBottom } = this.props;
let { offsetTop } = this.props;
if (typeof offsetTop === 'undefined') {
offsetTop = offset;
warning(
typeof offset === 'undefined',
'Affix',
'`offset` is deprecated. Please use `offsetTop` instead.',
);
}
if (offsetBottom === undefined && offsetTop === undefined) {
offsetTop = 0;
}
@@ -153,16 +151,15 @@ class Affix extends React.Component<AffixProps, AffixState> {
// =================== Measure ===================
measure = () => {
const { status, lastAffix } = this.state;
const { onChange } = this.props;
const targetFunc = this.getTargetFunc();
if (status !== AffixStatus.Prepare || !this.fixedNode || !this.placeholderNode || !targetFunc) {
const { target, onChange } = this.props;
if (status !== AffixStatus.Prepare || !this.fixedNode || !this.placeholderNode || !target) {
return;
}
const offsetTop = this.getOffsetTop();
const offsetBottom = this.getOffsetBottom();
const targetNode = targetFunc();
const targetNode = target();
if (!targetNode) {
return;
}
@@ -233,15 +230,15 @@ class Affix extends React.Component<AffixProps, AffixState> {
@throttleByAnimationFrameDecorator()
lazyUpdatePosition() {
const targetFunc = this.getTargetFunc();
const { target } = this.props;
const { affixStyle } = this.state;
// Check position change before measure to make Safari smooth
if (targetFunc && affixStyle) {
if (target && affixStyle) {
const offsetTop = this.getOffsetTop();
const offsetBottom = this.getOffsetBottom();
const targetNode = targetFunc();
const targetNode = target();
if (targetNode && this.placeholderNode) {
const targetRect = getTargetRect(targetNode);
const placeholderReact = getTargetRect(this.placeholderNode);
@@ -262,8 +259,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
}
// =================== Render ===================
render = () => {
const { getPrefixCls } = this.context;
renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => {
const { affixStyle, placeholderStyle } = this.state;
const { prefixCls, children } = this.props;
const className = classNames({
@@ -297,6 +293,12 @@ class Affix extends React.Component<AffixProps, AffixState> {
</ResizeObserver>
);
};
render() {
return <ConfigConsumer>{this.renderAffix}</ConfigConsumer>;
}
}
polyfill(Affix);
export default Affix;

View File

@@ -3,7 +3,6 @@ category: Components
subtitle: 固钉
type: 导航
title: Affix
cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg
---
将页面元素钉在可视范围。
@@ -16,12 +15,12 @@ cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg
## API
| 成员 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | - |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | - |
| target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window |
| onChange | 固定状态改变时触发的回调函数 | Function(affixed) | - |
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | | |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | |
| target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window | |
| onChange | 固定状态改变时触发的回调函数 | Function(affixed) | 无 | |
**注意:**`Affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `Affix` 为绝对定位:

View File

@@ -1,42 +0,0 @@
import * as React from 'react';
import Alert from '.';
interface ErrorBoundaryProps {
message?: React.ReactNode;
description?: React.ReactNode;
}
export default class ErrorBoundary extends React.Component<
ErrorBoundaryProps,
{
error?: Error | null;
info: {
componentStack?: string;
};
}
> {
state = {
error: undefined,
info: {
componentStack: '',
},
};
componentDidCatch(error: Error | null, info: object) {
this.setState({ error, info });
}
render() {
const { message, description, children } = this.props;
const { error, info } = this.state;
const componentStack = info && info.componentStack ? info.componentStack : null;
const errorMessage = typeof message === 'undefined' ? (error || '').toString() : message;
const errorDescription = typeof description === 'undefined' ? componentStack : description;
if (error) {
return (
<Alert type="error" message={errorMessage} description={<pre>{errorDescription}</pre>} />
);
}
return children;
}
}

View File

@@ -1,61 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Alert ErrorBoundary 1`] = `
<div
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon"
data-show="true"
role="alert"
>
<span
class="ant-alert-message"
>
ReferenceError: NotExisted is not defined
</span>
<span
class="ant-alert-description"
>
<pre>
in ThrowError
in ErrorBoundary (created by WrapperComponent)
in WrapperComponent
</pre>
</span>
</div>
`;
exports[`Alert could accept none react element icon 1`] = `
<div
class="ant-alert ant-alert-success"
data-show="true"
role="alert"
>
<span
class="ant-alert-icon"
>
icon
</span>
<span
class="ant-alert-message"
>
Success Tips
</span>
<span
class="ant-alert-description"
/>
</div>
`;
exports[`Alert rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-rtl"
data-show="true"
role="alert"
>
<span
class="ant-alert-message"
/>
<span
class="ant-alert-description"
/>
</div>
`;

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { mount } from 'enzyme';
import Alert from '..';
describe('Alert', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('could be closed', () => {
const onClose = jest.fn();
const afterClose = jest.fn();
const wrapper = mount(
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
closable
onClose={onClose}
afterClose={afterClose}
/>,
);
wrapper.find('.ant-alert-close-icon').simulate('click');
expect(onClose).toHaveBeenCalled();
jest.runAllTimers();
expect(afterClose).toHaveBeenCalled();
});
describe('data and aria props', () => {
it('sets data attributes on input', () => {
const wrapper = mount(<Alert data-test="test-id" data-id="12345" />);
const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('data-test')).toBe('test-id');
expect(input.getAttribute('data-id')).toBe('12345');
});
it('sets aria attributes on input', () => {
const wrapper = mount(<Alert aria-describedby="some-label" />);
const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('aria-describedby')).toBe('some-label');
});
it('sets role attribute on input', () => {
const wrapper = mount(<Alert role="status" />);
const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('role')).toBe('status');
});
});
it('warning for props#iconType', () => {
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
mount(
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
iconType="up"
/>,
);
expect(warnSpy).toHaveBeenCalledWith(
'Warning: [antd: Alert] `iconType` is deprecated. Please use `icon` instead.',
);
warnSpy.mockRestore();
});
});

View File

@@ -1,113 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import Alert from '..';
import Tooltip from '../../tooltip';
import Popconfirm from '../../popconfirm';
import rtlTest from '../../../tests/shared/rtlTest';
import { sleep } from '../../../tests/utils';
const { ErrorBoundary } = Alert;
describe('Alert', () => {
rtlTest(Alert);
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('could be closed', () => {
const onClose = jest.fn();
const afterClose = jest.fn();
const wrapper = mount(
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
closable
onClose={onClose}
afterClose={afterClose}
/>,
);
wrapper.find('.ant-alert-close-icon').simulate('click');
expect(onClose).toHaveBeenCalled();
jest.runAllTimers();
expect(afterClose).toHaveBeenCalled();
});
describe('data and aria props', () => {
it('sets data attributes on input', () => {
const wrapper = mount(<Alert data-test="test-id" data-id="12345" message={null} />);
const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('data-test')).toBe('test-id');
expect(input.getAttribute('data-id')).toBe('12345');
});
it('sets aria attributes on input', () => {
const wrapper = mount(<Alert aria-describedby="some-label" message={null} />);
const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('aria-describedby')).toBe('some-label');
});
it('sets role attribute on input', () => {
const wrapper = mount(<Alert role="status" message={null} />);
const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('role')).toBe('status');
});
});
const testIt = process.env.REACT === '15' ? it.skip : it;
testIt('ErrorBoundary', () => {
// @ts-expect-error
// eslint-disable-next-line react/jsx-no-undef
const ThrowError = () => <NotExisted />;
const wrapper = mount(
<ErrorBoundary>
<ThrowError />
</ErrorBoundary>,
);
// eslint-disable-next-line jest/no-standalone-expect
expect(wrapper.render()).toMatchSnapshot();
});
it('could be used with Tooltip', async () => {
const ref = React.createRef<any>();
jest.useRealTimers();
const wrapper = mount(
<Tooltip title="xxx" mouseEnterDelay={0} ref={ref}>
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
/>
</Tooltip>,
);
wrapper.find('.ant-alert').simulate('mouseenter');
await sleep(0);
expect(ref.current.getPopupDomNode()).toBeTruthy();
jest.useFakeTimers();
});
it('could be used with Popconfirm', async () => {
const ref = React.createRef<any>();
jest.useRealTimers();
const wrapper = mount(
<Popconfirm ref={ref} title="xxx">
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
/>
</Popconfirm>,
);
wrapper.find('.ant-alert').simulate('click');
await sleep(0);
expect(ref.current.getPopupDomNode()).toBeTruthy();
jest.useFakeTimers();
});
it('could accept none react element icon', () => {
const wrapper = mount(<Alert message="Success Tips" type="success" showIcon icon="icon" />);
expect(wrapper).toMatchRenderedSnapshot();
});
});

View File

@@ -8,17 +8,17 @@ title:
## zh-CN
页面顶部通告形式,默认有图标且 `type` 为 'warning'。
页面顶部通告形式,默认有图标且`type` 为 'warning'。
## en-US
Display Alert as a banner at top of page.
```tsx
```jsx
import { Alert } from 'antd';
ReactDOM.render(
<>
<div>
<Alert message="Warning text" banner />
<br />
<Alert
@@ -30,7 +30,7 @@ ReactDOM.render(
<Alert showIcon={false} message="Warning text without icon" banner />
<br />
<Alert type="error" message="Error text" banner />
</>,
</div>,
mountNode,
);
```

View File

@@ -13,14 +13,14 @@ title:
The simplest usage for short messages.
```tsx
```jsx
import { Alert } from 'antd';
ReactDOM.render(<Alert message="Success Text" type="success" />, mountNode);
```
<style>
.code-box-demo .ant-alert {
.ant-alert {
margin-bottom: 16px;
}
</style>

View File

@@ -13,15 +13,15 @@ title:
To show close button.
```tsx
```jsx
import { Alert } from 'antd';
const onClose = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
const onClose = e => {
console.log(e, 'I was closed.');
};
ReactDOM.render(
<>
<div>
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
@@ -35,7 +35,7 @@ ReactDOM.render(
closable
onClose={onClose}
/>
</>,
</div>,
mountNode,
);
```

View File

@@ -13,7 +13,7 @@ title:
Replace the default icon with customized text.
```tsx
```jsx
import { Alert } from 'antd';
ReactDOM.render(<Alert message="Info Text" type="info" closeText="Close Now" />, mountNode);

View File

@@ -14,14 +14,13 @@ title:
A relevant icon makes information clearer and more friendly.
```tsx
import { Alert } from 'antd';
import { SmileOutlined } from '@ant-design/icons';
```jsx
import { Alert, Icon } from 'antd';
const icon = <SmileOutlined />;
const icon = <Icon type="smile" />;
ReactDOM.render(
<>
<div>
<Alert icon={icon} message="showIcon = false" type="success" />
<Alert icon={icon} message="Success Tips" type="success" showIcon />
<Alert icon={icon} message="Informational Notes" type="info" showIcon />
@@ -55,7 +54,7 @@ ReactDOM.render(
type="error"
showIcon
/>
</>,
</div>,
mountNode,
);
```

View File

@@ -13,11 +13,11 @@ title:
Additional description for alert message.
```tsx
```jsx
import { Alert } from 'antd';
ReactDOM.render(
<>
<div>
<Alert
message="Success Text"
description="Success Description Success Description Success Description"
@@ -38,7 +38,7 @@ ReactDOM.render(
description="Error Description Error Description Error Description Error Description"
type="error"
/>
</>,
</div>,
mountNode,
);
```

View File

@@ -1,43 +0,0 @@
---
order: 8
title:
zh-CN: ErrorBoundary
en-US: React 错误处理
---
## zh-CN
友好的 [React 错误处理](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html) 包裹组件。
## en-US
ErrorBoundary Component for making error handling easier in [React](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html).
```tsx
import React, { useState } from 'react';
import { Button, Alert } from 'antd';
const { ErrorBoundary } = Alert;
const ThrowError: React.FC = () => {
const [error, setError] = useState<Error>();
const onClick = () => {
setError(new Error('An Uncaught Error'));
};
if (error) {
throw error;
}
return (
<Button type="danger" onClick={onClick}>
Click me to throw a error
</Button>
);
};
ReactDOM.render(
<ErrorBoundary>
<ThrowError />
</ErrorBoundary>,
mountNode,
);
```

View File

@@ -13,14 +13,14 @@ title:
A relevant icon will make information clearer and more friendly.
```tsx
```jsx
import { Alert } from 'antd';
ReactDOM.render(
<>
<div>
<Alert message="Success Tips" type="success" showIcon />
<Alert message="Informational Notes" type="info" showIcon />
<Alert message="Warning" type="warning" showIcon closable />
<Alert message="Warning" type="warning" showIcon />
<Alert message="Error" type="error" showIcon />
<Alert
message="Success Tips"
@@ -39,7 +39,6 @@ ReactDOM.render(
description="This is a warning notice about copywriting."
type="warning"
showIcon
closable
/>
<Alert
message="Error"
@@ -47,7 +46,7 @@ ReactDOM.render(
type="error"
showIcon
/>
</>,
</div>,
mountNode,
);
```

View File

@@ -1,34 +0,0 @@
---
order: 6.1
title:
zh-CN: 轮播的公告
en-US: Loop Banner
---
## zh-CN
配合 [react-text-loop](https://npmjs.com/package/react-text-loop) 实现消息轮播通知栏。
## en-US
Show a loop banner by using with [react-text-loop](https://npmjs.com/package/react-text-loop).
```tsx
import { Alert } from 'antd';
import TextLoop from 'react-text-loop';
ReactDOM.render(
<Alert
banner
message={
<TextLoop mask>
<div>Notice message one</div>
<div>Notice message two</div>
<div>Notice message three</div>
<div>Notice message four</div>
</TextLoop>
}
/>,
mountNode,
);
```

View File

@@ -13,24 +13,34 @@ title:
Smoothly unmount Alert upon close.
```tsx
import React, { useState } from 'react';
```jsx
import { Alert } from 'antd';
const App: React.FC = () => {
const [visible, setVisible] = useState(true);
const handleClose = () => {
setVisible(false);
class App extends React.Component {
state = {
visible: true,
};
return (
<div>
{visible ? (
<Alert message="Alert Message Text" type="success" closable afterClose={handleClose} />
) : null}
<p>placeholder text here</p>
</div>
);
};
handleClose = () => {
this.setState({ visible: false });
};
render() {
return (
<div>
{this.state.visible ? (
<Alert
message="Alert Message Text"
type="success"
closable
afterClose={this.handleClose}
/>
) : null}
<p>placeholder text here</p>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
```

View File

@@ -13,22 +13,16 @@ title:
There are 4 types of Alert: `success`, `info`, `warning`, `error`.
```tsx
```jsx
import { Alert } from 'antd';
ReactDOM.render(
<>
<div>
<Alert message="Success Text" type="success" />
<Alert message="Info Text" type="info" />
<Alert message="Warning Text" type="warning" />
<Alert message="Error Text" type="error" />
</>,
</div>,
mountNode,
);
```
<style>
[data-theme="compact"] .code-box-demo .ant-alert {
margin-bottom: 8px;
}
</style>

View File

@@ -2,7 +2,6 @@
category: Components
type: Feedback
title: Alert
cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg
---
Alert component for feedback.
@@ -14,22 +13,15 @@ Alert component for feedback.
## API
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| afterClose | Called when close animation is finished | () => void | - |
| banner | Whether to show as banner | boolean | false |
| closable | Whether Alert can be closed | boolean | - |
| closeText | Close text to show | string \| ReactNode | - |
| description | Additional content of Alert | string \| ReactNode | - |
| icon | Custom icon, effective when `showIcon` is true | ReactNode | - |
| message | Content of Alert | string \| ReactNode | - |
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true |
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` |
| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - |
### Alert.ErrorBoundary
| Property | Description | Type | Default | Version |
| ----------- | -------------------------------- | --------- | ----------------- | ------- |
| message | Custom error message to show | ReactNode | {{ error }} | |
| description | Custom error description to show | ReactNode | {{ error stack }} | |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| afterClose | Called when close animation is finished | () => void | - | 3.3.1 |
| banner | Whether to show as banner | boolean | false | |
| closable | Whether Alert can be closed | boolean | - | |
| closeText | Close text to show | string\|ReactNode | - | |
| description | Additional content of Alert | string\|ReactNode | - | |
| icon | Custom icon, effective when `showIcon` is `true` | ReactNode | - | 3.10.0 |
| message | Content of Alert | string\|ReactNode | - | |
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true | |
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` | |
| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - | |

View File

@@ -1,20 +1,13 @@
import * as React from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined';
import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined';
import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined';
import CloseCircleOutlined from '@ant-design/icons/CloseCircleOutlined';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import * as ReactDOM from 'react-dom';
import Animate from 'rc-animate';
import classNames from 'classnames';
import { ConfigContext } from '../config-provider';
import Icon, { ThemeType } from '../icon';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
import ErrorBoundary from './ErrorBoundary';
import { replaceElement } from '../_util/reactNode';
import warning from '../_util/warning';
function noop() {}
export interface AlertProps {
/**
@@ -35,159 +28,167 @@ export interface AlertProps {
afterClose?: () => void;
/** Whether to show icon */
showIcon?: boolean;
/** https://www.w3.org/TR/2014/REC-html5-20141028/dom.html#aria-role-attribute */
role?: string;
iconType?: string;
style?: React.CSSProperties;
prefixCls?: string;
className?: string;
banner?: boolean;
icon?: React.ReactNode;
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
onClick?: React.MouseEventHandler<HTMLDivElement>;
}
const iconMapFilled = {
success: CheckCircleFilled,
info: InfoCircleFilled,
error: CloseCircleFilled,
warning: ExclamationCircleFilled,
};
const iconMapOutlined = {
success: CheckCircleOutlined,
info: InfoCircleOutlined,
error: CloseCircleOutlined,
warning: ExclamationCircleOutlined,
};
interface AlertInterface extends React.FC<AlertProps> {
ErrorBoundary: typeof ErrorBoundary;
export interface AlertState {
closing: boolean;
closed: boolean;
}
const Alert: AlertInterface = ({
description,
prefixCls: customizePrefixCls,
message,
banner,
className = '',
style,
onMouseEnter,
onMouseLeave,
onClick,
showIcon,
closable,
closeText,
...props
}) => {
const [closing, setClosing] = React.useState(false);
const [closed, setClosed] = React.useState(false);
export default class Alert extends React.Component<AlertProps, AlertState> {
constructor(props: AlertProps) {
super(props);
const ref = React.useRef<HTMLElement>();
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('alert', customizePrefixCls);
warning(
!('iconType' in props),
'Alert',
'`iconType` is deprecated. Please use `icon` instead.',
);
const handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
setClosing(true);
props.onClose?.(e);
this.state = {
closing: false,
closed: false,
};
}
handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
const dom = ReactDOM.findDOMNode(this) as HTMLElement;
dom.style.height = `${dom.offsetHeight}px`;
// Magic code
// 重复一次后才能正确设置 height
dom.style.height = `${dom.offsetHeight}px`;
this.setState({
closing: true,
});
(this.props.onClose || noop)(e);
};
const animationEnd = () => {
setClosing(false);
setClosed(true);
props.afterClose?.();
animationEnd = () => {
this.setState({
closing: false,
closed: true,
});
(this.props.afterClose || noop)();
};
const getType = () => {
const { type } = props;
if (type !== undefined) {
return type;
renderAlert = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
description,
prefixCls: customizePrefixCls,
message,
closeText,
banner,
className = '',
style,
icon,
} = this.props;
let { closable, type, showIcon, iconType } = this.props;
const { closing, closed } = this.state;
const prefixCls = getPrefixCls('alert', customizePrefixCls);
// banner模式默认有 Icon
showIcon = banner && showIcon === undefined ? true : showIcon;
// banner模式默认为警告
type = banner && type === undefined ? 'warning' : type || 'info';
let iconTheme: ThemeType = 'filled';
if (!iconType) {
switch (type) {
case 'success':
iconType = 'check-circle';
break;
case 'info':
iconType = 'info-circle';
break;
case 'error':
iconType = 'close-circle';
break;
case 'warning':
iconType = 'exclamation-circle';
break;
default:
iconType = 'default';
}
// use outline icon in alert with description
if (description) {
iconTheme = 'outlined';
}
}
// banner 模式默认为警告
return banner ? 'warning' : 'info';
};
// closeable when closeText is assigned
const isClosable = closeText ? true : closable;
const type = getType();
const renderIconNode = () => {
const { icon } = props;
// use outline icon in alert with description
const iconType = (description ? iconMapOutlined : iconMapFilled)[type] || null;
if (icon) {
return replaceElement(icon, <span className={`${prefixCls}-icon`}>{icon}</span>, () => ({
className: classNames(`${prefixCls}-icon`, {
[(icon as any).props.className]: (icon as any).props.className,
}),
}));
// closeable when closeText is assigned
if (closeText) {
closable = true;
}
return React.createElement(iconType, { className: `${prefixCls}-icon` });
};
const renderCloseIcon = () => {
return isClosable ? (
const alertCls = classNames(
prefixCls,
`${prefixCls}-${type}`,
{
[`${prefixCls}-closing`]: closing,
[`${prefixCls}-with-description`]: !!description,
[`${prefixCls}-no-icon`]: !showIcon,
[`${prefixCls}-banner`]: !!banner,
[`${prefixCls}-closable`]: closable,
},
className,
);
const closeIcon = closable ? (
<button
type="button"
onClick={handleClose}
onClick={this.handleClose}
className={`${prefixCls}-close-icon`}
tabIndex={0}
>
{closeText ? (
<span className={`${prefixCls}-close-text`}>{closeText}</span>
) : (
<CloseOutlined />
<Icon type="close" />
)}
</button>
) : null;
const dataOrAriaProps = getDataOrAriaProps(this.props);
const iconNode = (icon &&
(React.isValidElement<{ className?: string }>(icon) ? (
React.cloneElement(icon, {
className: classNames(`${prefixCls}-icon`, {
[icon.props.className as string]: icon.props.className,
}),
})
) : (
<span className={`${prefixCls}-icon`}>{icon}</span>
))) || <Icon className={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />;
return closed ? null : (
<Animate
component=""
showProp="data-show"
transitionName={`${prefixCls}-slide-up`}
onEnd={this.animationEnd}
>
<div data-show={!closing} className={alertCls} style={style} {...dataOrAriaProps}>
{showIcon ? iconNode : null}
<span className={`${prefixCls}-message`}>{message}</span>
<span className={`${prefixCls}-description`}>{description}</span>
{closeIcon}
</div>
</Animate>
);
};
// banner 模式默认有 Icon
const isShowIcon = banner && showIcon === undefined ? true : showIcon;
const alertCls = classNames(
prefixCls,
`${prefixCls}-${type}`,
{
[`${prefixCls}-closing`]: closing,
[`${prefixCls}-with-description`]: !!description,
[`${prefixCls}-no-icon`]: !isShowIcon,
[`${prefixCls}-banner`]: !!banner,
[`${prefixCls}-closable`]: isClosable,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
);
const dataOrAriaProps = getDataOrAriaProps(props);
return closed ? null : (
<Animate
component=""
showProp="data-show"
transitionName={`${prefixCls}-slide-up`}
onEnd={animationEnd}
>
<div
ref={ref}
data-show={!closing}
className={alertCls}
style={style}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={onClick}
role="alert"
{...dataOrAriaProps}
>
{isShowIcon ? renderIconNode() : null}
<span className={`${prefixCls}-message`}>{message}</span>
<span className={`${prefixCls}-description`}>{description}</span>
{renderCloseIcon()}
</div>
</Animate>
);
};
Alert.ErrorBoundary = ErrorBoundary;
export default Alert;
render() {
return <ConfigConsumer>{this.renderAlert}</ConfigConsumer>;
}
}

View File

@@ -3,7 +3,6 @@ category: Components
subtitle: 警告提示
type: 反馈
title: Alert
cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg
---
警告提示,展现需要关注的信息。
@@ -15,22 +14,15 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - |
| banner | 是否用作顶部公告 | boolean | false |
| closable | 默认不显示关闭按钮 | boolean | - |
| closeText | 自定义关闭按钮 | string \| ReactNode | - |
| description | 警告提示的辅助性文字介绍 | string \| ReactNode | - |
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - |
| message | 警告提示内容 | string \| ReactNode | - |
| showIcon | 是否显示辅助图标 | boolean | false`banner` 模式下默认值为 true |
| type | 指定警告提示的样式,有四种选择 `success``info``warning``error` | string | `info``banner` 模式下默认值为 `warning` |
| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | - |
### Alert.ErrorBoundary
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| message | 自定义错误标题,如果未指定会展示原生报错信息 | ReactNode | {{ error }} | |
| description | 自定义错误内容,如果未指定会展示报错堆栈 | ReactNode | {{ error stack }} | |
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | 3.3.1 |
| banner | 是否用作顶部公告 | boolean | false | |
| closable | 默认不显示关闭按钮 | boolean | 无 | |
| closeText | 自定义关闭按钮 | string\|ReactNode | 无 | |
| description | 警告提示的辅助性文字介绍 | string\|ReactNode | 无 | |
| icon | 自定义图标,`showIcon``true` 时有效 | ReactNode | - | 3.10.0 |
| message | 警告提示内容 | string\|ReactNode | 无 | |
| showIcon | 是否显示辅助图标 | boolean | false`banner` 模式下默认值为 true | |
| type | 指定警告提示的样式,有四种选择 `success``info``warning``error` | string | `info``banner` 模式下默认值为 `warning` | |
| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | 无 | |

View File

@@ -3,21 +3,21 @@
@alert-prefix-cls: ~'@{ant-prefix}-alert';
@alert-message-color: @heading-color;
@alert-text-color: @text-color;
@alert-close-color: @text-color-secondary;
@alert-close-hover-color: @icon-color-hover;
.@{alert-prefix-cls} {
.reset-component;
position: relative;
max-height: 1000vh;
padding: 8px 15px 8px 37px;
word-wrap: break-word;
border-radius: @border-radius-base;
&&-no-icon {
padding: @alert-no-icon-padding-vertical 15px;
.@{alert-prefix-cls}-close-icon {
top: @alert-no-icon-padding-vertical + @font-size-base * @line-height-base / 2 -
@font-size-base / 2;
}
padding: 8px 15px;
}
&&-closable {
@@ -26,14 +26,14 @@
&-icon {
position: absolute;
top: @alert-icon-top;
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2;
left: 16px;
}
&-description {
display: none;
font-size: @font-size-base;
line-height: @font-size-base + 8px;
line-height: 22px;
}
&-success {
@@ -63,25 +63,19 @@
&-error {
background-color: @alert-error-bg-color;
border: @border-width-base @border-style-base @alert-error-border-color;
.@{alert-prefix-cls}-icon {
color: @alert-error-icon-color;
}
.@{alert-prefix-cls}-description > pre {
margin: 0;
padding: 0;
}
}
&-close-icon {
position: absolute;
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2;
right: @padding-md;
top: 8px;
right: 16px;
padding: 0;
overflow: hidden;
font-size: @font-size-sm;
line-height: @font-size-sm;
line-height: 22px;
background-color: transparent;
border: none;
outline: none;
@@ -106,27 +100,27 @@
&-with-description {
position: relative;
padding: @alert-with-description-padding;
padding: 15px 15px 15px 64px;
color: @alert-text-color;
line-height: @line-height-base;
border-radius: @border-radius-base;
}
&-with-description&-no-icon {
padding: @alert-with-description-no-icon-padding-vertical 15px;
padding: 15px;
}
&-with-description &-icon {
position: absolute;
top: @alert-with-description-icon-top;
left: @alert-with-description-icon-size;
font-size: @alert-with-description-icon-size;
top: 16px;
left: 24px;
font-size: 24px;
}
&-with-description &-close-icon {
position: absolute;
top: @padding-md;
right: @padding-md;
top: 16px;
right: 16px;
font-size: @font-size-base;
cursor: pointer;
}
@@ -147,7 +141,7 @@
}
&&-closing {
max-height: 0;
height: 0 !important;
margin: 0;
padding-top: 0;
padding-bottom: 0;
@@ -192,5 +186,3 @@
opacity: 0;
}
}
@import './rtl';

Some files were not shown because too many files have changed in this diff Show More