Compare commits

..

17 Commits

Author SHA1 Message Date
愚指导
97d3422043 release 2.13.13 (#9590) 2018-03-09 18:04:06 +08:00
Wei Zhu
a609518d28 Remove unsed variable 2018-03-08 17:52:58 +08:00
Wei Zhu
ef6d7a5d90 Fix upload list update logic when beforeUpload return false
Porting from 626ebf2063
2018-03-08 17:43:24 +08:00
Wei Zhu
5be1bad67f Update snapshot 2018-03-08 14:24:37 +08:00
Wei Zhu
b70fede5de correct grammar 2018-02-26 20:36:53 +08:00
Wei Zhu
358aa84fef Bump 2.13.12 2018-02-26 20:01:00 +08:00
Wei Zhu
6f716d1003 Add 2.13.12 change log 2018-02-26 20:00:36 +08:00
Wei Zhu
739c6ead54 Lock @types/react and @types/react-dom version 2018-02-26 19:46:55 +08:00
Wei Zhu
a7255ee885 Fix #8885 2018-02-26 19:21:24 +08:00
junjing.zhang
06ff0d44db fix lint fail - use babel-eslint-8.0.2 2018-01-03 19:06:37 +08:00
junjing.zhang
e96b0fc768 fix components/collapse test fail 2018-01-03 19:06:37 +08:00
junjing.zhang
8489fedb4c anchor scroll supports complete href link - test
anchor scroll supports complete href link, e.g. http://www.example.com/#id, not just #id
2018-01-03 19:06:37 +08:00
junjing.zhang
adf98d3c19 anchor scroll supports complete href link
anchor scroll supports complete href link, e.g. http://www.example.com/#id, not just #id
2018-01-03 19:06:37 +08:00
afc163
ba87b45903 Fix snapshot for moment@2.20 2017-12-17 18:57:10 +08:00
afc163
1e8830637f Fix submenu popup issue when specified unexisted defaultOpenKeys
close #8475
2017-12-17 18:54:31 +08:00
nikogu
2275195e15 bump 2.13.11 2017-12-02 17:47:19 +08:00
niko
dcf9d4c294 2.13.11 changelog (#8403) 2017-12-02 17:38:41 +08:00
1331 changed files with 30847 additions and 340788 deletions

10
.babelrc Normal file
View File

@@ -0,0 +1,10 @@
{
"env": {
"test": {
"presets": ["es2015", "react", "stage-0"],
"plugins": [
"add-module-exports"
]
}
}
}

View File

@@ -1,238 +0,0 @@
version: 2
references:
container_config: &container_config
docker:
- image: circleci/node:8
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
workflow: &workflow
jobs:
- setup:
filters:
branches:
ignore: gh-pages
- dist:
requires:
- setup
- compile:
requires:
- setup
- lint:
requires:
- setup
- test_dist:
requires:
- dist
- test_lib:
requires:
- compile
- test_es:
requires:
- compile
- test_dom:
requires:
- setup
- 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
jobs:
setup:
<<: *container_config
steps:
- checkout
- run: node -v
- run: npm -v
- run: npm install
- run:
command: |
set +eo
npm ls
true
- persist_to_workspace:
root: ~/ant-design
paths:
- node_modules
- store_artifacts:
path: package-lock.json
dist:
<<: *container_config
steps:
- checkout
- *attach_workspace
- run: npm run dist
- run: node ./tests/dekko/dist.test.js
- persist_to_workspace:
root: ~/ant-design
paths:
- dist
compile:
<<: *container_config
steps:
- checkout
- *attach_workspace
- run: npm run compile
- run: node ./tests/dekko/lib.test.js
- persist_to_workspace:
root: ~/ant-design
paths:
- lib
- es
lint:
<<: *container_config
steps:
- checkout
- *attach_workspace
- run: npm run lint
test_dist:
<<: *container_config
<<: *react_16
steps:
- checkout
- *attach_workspace
- run:
command: npm test -- -w 1
environment:
LIB_DIR: dist
test_lib:
<<: *container_config
<<: *react_16
steps:
- checkout
- *attach_workspace
- run:
command: npm test -- -w 1
environment:
LIB_DIR: lib
test_es:
<<: *container_config
<<: *react_16
steps:
- checkout
- *attach_workspace
- run:
command: npm test -- -w 1
environment:
LIB_DIR: es
test_dom:
<<: *container_config
<<: *react_16
steps:
- checkout
- *attach_workspace
- run: npm test -- -w 1 --coverage
- run: bash <(curl -s https://codecov.io/bash)
test_node:
<<: *container_config
<<: *react_16
steps:
- checkout
- *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
test_lib_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run:
command: npm test -- -w 1 -u
environment:
LIB_DIR: lib
test_es_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run:
command: npm test -- -w 1 -u
environment:
LIB_DIR: es
test_dom_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run: npm test -- -w 1 -u
test_node_15:
<<: *container_config
<<: *react_15
steps:
- checkout
- *attach_workspace
- *install_react
- run: npm run test-node -- -w 1 -u
workflows:
version: 2
build_test:
<<: *workflow
nightly:
<<: *workflow
triggers:
- schedule:
cron: "0 0 * * *"
filters:
branches:
only:
- master

View File

@@ -1,9 +1,2 @@
codecov:
branch: master
coverage:
status:
project:
default:
# Fail the status if coverage drops by >= 0.1%
threshold: 0.1

View File

@@ -1,4 +1,4 @@
# 🎨 editorconfig.org
# editorconfig.org
root = true

View File

@@ -1,9 +1,6 @@
components/**/*.js
components/**/*.jsx
components/*/__tests__/type.tsx
!.eslintrc.js
!components/*/__tests__/**/*.js
!components/*/demo/*
!.*.js
# Docs templates
site/theme/template/IconDisplay/*.js
site/theme/template/IconDisplay/*.jsx

View File

@@ -1,5 +1,5 @@
const eslintrc = {
extends: ['airbnb', 'prettier'],
extends: ['eslint-config-airbnb'],
env: {
browser: true,
node: true,
@@ -8,33 +8,44 @@ const eslintrc = {
es6: true,
},
parser: 'babel-eslint',
plugins: ['markdown', 'react', 'babel'],
parserOptions: {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true,
experimentalObjectRestSpread: true,
},
},
plugins: [
'markdown',
'react',
'babel',
],
rules: {
'react/jsx-one-expression-per-line': 0,
'func-names': 0,
'arrow-body-style': 0,
'react/sort-comp': 0,
'react/prop-types': 0,
'react/forbid-prop-types': 0,
'react/jsx-indent': 0,
'react/jsx-wrap-multilines': ['error', { declaration: false, assignment: false }],
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'site/**',
'tests/**',
'scripts/**',
'**/*.test.js',
'**/__tests__/*',
'*.config.js',
'**/*.md',
],
},
],
'react/jsx-first-prop-new-line': 0,
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.md'] }],
'import/extensions': 0,
'import/no-unresolved': 0,
'import/no-extraneous-dependencies': 0,
'prefer-destructuring': 0,
'no-param-reassign': 0,
'no-return-assign': 0,
'max-len': 0,
'consistent-return': 0,
'no-redeclare': 0,
'react/require-extension': 0,
'jsx-a11y/no-static-element-interactions': 0,
'jsx-a11y/anchor-has-content': 0,
'jsx-a11y/click-events-have-key-events': 0,
'jsx-a11y/anchor-is-valid': 0,
'react/no-danger': 0,
'comma-dangle': ['error', 'always-multiline'],
'function-paren-newline': 0,
'object-curly-newline': 0,
'no-restricted-globals': 0,
},
};
@@ -50,18 +61,10 @@ if (process.env.RUN_ENV === 'DEMO') {
'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,
'prefer-destructuring': 0, // TODO: remove later
'max-len': 0, // TODO: remove later
'consistent-return': 0, // TODO: remove later
'no-return-assign': 0, // TODO: remove later
'no-param-reassign': 0, // TODO: remove later
'import/no-extraneous-dependencies': 0,
'import/newline-after-import': 0,
});
}

View File

@@ -1,13 +1,58 @@
# Contributing to Ant Design
Want to contribute to Ant Design? There are a few things you need to know.
The following is a set of guidelines for contributing to Ant Design. Please spend several minutes in reading these guidelines before you create an issue or pull request.
We wrote a **[contribution guide](https://ant.design/docs/react/contributing)** to help you get started.
Anyway, these are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request.
---
# 参与共建
## Do your homework before asking a question
想要给 Ant Design 贡献自己的一份力量?
It's a great idea to read Eric Steven Raymond's [How To Ask Questions The Smart Way](http://www.catb.org/esr/faqs/smart-questions.html) twice before asking a question. But if you are busy now, I recommend to read [Don't post homework questions](http://www.catb.org/esr/faqs/smart-questions.html#homework) first.
我们写了一份 **[贡献指南](https://ant.design/docs/react/contributing-cn)** 来帮助你开始。
The following guidelines are about *How to avoid Homework Questions*.
### 1. Read the documentation.
It sad but true that someone just glance(not read) [Ant Design's documentation](http://ant.design/). Please read the documentation closely. What's more, you can modify and run our demo with [CodePen](http://codepen.io/benjycui/pen/KgPZrE?editors=001). It's helpful to understand our documentation.
Tips: choose the corresponding documentation with versions selector which in the bottom-right corner.
### 2. Make sure that your question is about Ant Design, not React
Someone may think all of the questions that he/she meets in developing are about Ant Design, but it's not true. So, please read [React's documentation](http://facebook.github.io/react/docs/getting-started.html) or just Google (not Baidu, seriously) your questions with keyword *React* first. If you are sure that your question is about Ant Design, go ahead.
### 3. Read the FAQ and search the issues list of Ant Design
Your questions may be asked and solved by others. So please spend several minutes on searching. Remember [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), both code and questions.
P.S.
1. [FAQ](https://github.com/ant-design/ant-design/wiki/FAQ)
1. [Issues list](https://github.com/ant-design/ant-design/issues)
## Close your issue if it's solved
It is a good habit which will save maintainers' time. Thank you!
## Providing a demo while reporting a bug
It would be helpful to provide a demo which can re-produce the bug 100%. Please fork this [CodePen](http://codepen.io/benjycui/pen/KgPZrE?editors=001) and re-produce the bug you met. Then, create an issue. The most important thing is: double check before claiming that you have found a bug.
## Tips about Feature Request
If you believe that Ant Design should provide some features, but it does not. You could create an issue to discuss. However, Ant Design is not Swiss Army Knife, there are some features which Ant Design will not support:
1. Request or operate data
## Tips about Pull Request
**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github)
It's welcomed to pull request. And there are some tips about that:
1. It is a good habit to create a feature request issue to discuss whether the feature is necessary before you implement it. However, it's unnecessary to create an issue to claim that you found a typo or improved the readability of documentation, just create a pull request.
1. Run `npm run lint` and fix those errors before committing in order to keep consistent code style.
1. Rebase before creating a PR to keep commit history clear.
1. Add some descriptions and refer relative issues for you PR.

View File

@@ -1,5 +1,5 @@
<!--
⚠️ ⚠️ ⚠️ IMPORTANT: Please use the following link to create a new issue: ⚠️ ⚠️ ⚠️
IMPORTANT: Please use the following link to create a new issue:
http://new-issue.ant.design
@@ -7,7 +7,7 @@ If your issue was not created using the app above, it will be closed immediately
-->
<!--
⚠️ ⚠️ ⚠️ 注意:请使用下面的链接来新建 issue ⚠️ ⚠️ ⚠️
注意:请使用下面的链接来新建 issue
http://new-issue.ant.design

View File

@@ -1,13 +0,0 @@
---
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,53 +1,22 @@
First of all, thank you for your contribution! 😄
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!
Please makes sure that these checkboxes are checked before submitting your PR, thank you!
[[中文版模板 / Chinese template](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE/pr_cn.md)]
* [ ] Make sure that you propose PR to right branch: bugfix for `master`, feature for latest active branch `feature-x.x`.
* [ ] Make sure that you follow antd's [code convention](https://github.com/ant-design/ant-design/wiki/Code-convention-for-antd).
* [ ] Run `npm run lint` and fix those errors before submitting in order to keep consistent code style.
* [ ] Rebase before creating a PR to keep commit history clear.
* [ ] Add some descriptions and refer relative issues for you PR.
### This is a ...
Extra checklist:
- [ ] New feature
- [ ] Bug fix
- [ ] Site / document update
- [ ] Component style update
- [ ] TypeScript definition update
- [ ] Refactoring
- [ ] Code style optimization
- [ ] Branch merge
- [ ] Other (about what?)
**if** *isBugFix* **:**
### What's the background?
* [ ] Make sure that you add at least one unit test for the bug which you had fixed.
> 1. Describe the source of requirement.
> 2. Resolve what problem.
> 3. Related issue link.
**elif** *isNewFeature* **:**
### API Realization (Optional if not new feature)
> 1. Basic thought of solution and other optional proposal.
> 2. List final API realization and usage sample.
> 3. GIF or snapshot should be provided if includes UI/interactive modification.
### What's the effect? (Optional if not new feature)
> 1. Does this PR affect user? Which part will be affected?
> 2. What will say in changelog?
> 3. Does this PR contains potential break change or other risk?
### Changelog description (Optional if not new feature)
> 1. English description
> 2. Chinese description (optional)
### Self Check before Merge
- [ ] Doc is updated/provided or not needed
- [ ] Demo is updated/provided or not needed
- [ ] TypeScript definition is updated/provided or not needed
- [ ] Changelog is provided or not needed
### Additional Plan? (Optional if not new feature)
> If this PR related with other PR or following info. You can type here.
* [ ] Update API docs for the component.
* [ ] Update/Add demo to demonstrate new feature.
* [ ] Update TypeScript definition for the component.
* [ ] Add unit tests for the feature.

View File

@@ -1,52 +0,0 @@
首先,感谢你的贡献! 😄
新特性请提交至 feature 分支,其余可提交至 master 分支。
在一个维护者审核通过后合并。
请确保填写以下 pull request 的信息,谢谢!~
[[English Template / 英文模板](?expand=1)]
### 这个变动的性质是
- [ ] 新特性提交
- [ ] 日常 bug 修复
- [ ] 站点、文档改进
- [ ] 组件样式改进
- [ ] TypeScript 定义更新
- [ ] 重构
- [ ] 代码风格优化
- [ ] 分支合并
- [ ] 其他改动(是关于什么的改动?)
### 需求背景
> 1. 描述相关需求的来源。
> 2. 要解决的问题。
> 3. 相关的 issue 讨论链接。
### 实现方案和 API非新功能可选
> 1. 基本的解决思路和其他可选方案。
> 2. 列出最终的 API 实现和用法。
> 3. 涉及UI/交互变动需要有截图或 GIF。
### 对用户的影响和可能的风险(非新功能可选)
> 1. 这个改动对用户端是否有影响?影响的方面有哪些?
> 2. 是否有可能隐含的 break change 和其他风险?
### Changelog 描述(非新功能可选)
> 1. 英文描述
> 2. 中文描述(可选)
### 请求合并前的自查清单
- [ ] 文档已补充或无须补充
- [ ] 代码演示已提供或无须提供
- [ ] TypeScript 定义已补充或无须补充
- [ ] Changelog 已提供或无须提供
### 后续计划(非新功能可选)
> 如果这个提交后面还有相关的其他提交和跟进信息,可以写在这里。

14
.github/lock.yml vendored
View File

@@ -1,14 +0,0 @@
# Configuration for lock-threads - https://github.com/dessant/lock-threads
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 365
# Comment to post before locking. Set to `false` to disable
lockComment: >
This thread has been automatically locked because it has not had recent
activity. Please open a new issue for related bugs and link to relevant
comments in this thread.
# Issues or pull requests with these labels will not be locked
# exemptLabels:
# - no-locking
# Limit to only `issues` or `pulls`
only: issues

5
.gitignore vendored
View File

@@ -38,8 +38,3 @@ components/**/*.jsx
!components/**/__tests__/*.js
!components/**/__tests__/*.js.snap
/.history
# Docs templates
site/theme/template/IconDisplay/*.js
site/theme/template/IconDisplay/*.jsx
site/theme/template/IconDisplay/fields.js
*.tmp

View File

@@ -2,36 +2,44 @@ const libDir = process.env.LIB_DIR;
const transformIgnorePatterns = [
'/dist/',
'node_modules/[^/]+?/(?!(es|node_modules)/)', // Ignore modules without es dir
'node_modules\/[^/]+?\/(?!(es|node_modules)\/)', // Ignore modules without es dir
];
module.exports = {
verbose: true,
setupFiles: ['./tests/setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'md'],
modulePathIgnorePatterns: ['/_site/'],
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node'],
setupFiles: [
'./tests/setup.js',
],
moduleFileExtensions: [
'ts',
'tsx',
'js',
'jsx',
'json',
'md',
],
modulePathIgnorePatterns: [
'/_site/',
],
testPathIgnorePatterns: [
'/node_modules/',
'dekko',
'node',
],
transform: {
'\\.tsx?$': './node_modules/antd-tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/antd-tools/lib/jest/codePreprocessor',
'\\.md$': './node_modules/antd-tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/antd-tools/lib/jest/imagePreprocessor',
},
testRegex: `${libDir === 'dist' ? 'demo' : '.*'}\\.test\\.js$`,
testRegex: libDir === 'dist' ? 'demo\\.test\\.js$' : '.*\\.test\\.js$',
collectCoverageFrom: [
'components/**/*.{ts,tsx}',
'!components/*/style/index.tsx',
'!components/style/index.tsx',
'!components/*/locale/index.tsx',
'!components/*/__tests__/**/type.tsx',
'!components/**/*/interface.{ts,tsx}',
],
transformIgnorePatterns,
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: {
'ts-jest': {
tsConfig: './tsconfig.test.json',
},
},
testURL: 'http://localhost',
snapshotSerializers: [
'enzyme-to-json/serializer',
],
};

View File

@@ -1,19 +1,21 @@
// jest config for server render environment
module.exports = {
setupFiles: ['./tests/setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
setupFiles: [
'./tests/setup.js',
],
moduleFileExtensions: [
'ts',
'tsx',
'js',
'md',
],
transform: {
'\\.tsx?$': './node_modules/antd-tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/antd-tools/lib/jest/codePreprocessor',
'\\.md$': './node_modules/antd-tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/antd-tools/lib/jest/imagePreprocessor',
},
testRegex: 'demo\\.test\\.js$',
testEnvironment: 'node',
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: {
'ts-jest': {
tsConfigFile: './tsconfig.test.json',
},
},
snapshotSerializers: [
'enzyme-to-json/serializer'
],
};

View File

@@ -1,7 +0,0 @@
**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json
.umi
.umi-production

View File

@@ -1,19 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
},
{
"files": ".stylelintrc",
"options": {
"parser": "json"
}
}
]
}

View File

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

View File

@@ -1,4 +0,0 @@
components/style/color/bezierEasing.less
components/style/color/colorPalette.less
components/style/color/tinyColor.less
components/style/core/base.less

View File

@@ -1,11 +1,21 @@
{
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"extends": "stylelint-config-standard",
"rules": {
"comment-empty-line-before": null,
"declaration-empty-line-before": null,
"function-comma-newline-after": null,
"function-name-case": null,
"function-parentheses-newline-inside": null,
"function-max-empty-lines": null,
"function-whitespace-after": null,
"indentation": null,
"number-leading-zero": null,
"number-no-trailing-zeros": null,
"rule-empty-line-before": null,
"selector-combinator-space-after": null,
"selector-list-comma-newline-after": null,
"selector-pseudo-element-colon-notation": null,
"no-invalid-double-slash-comments": null,
"no-descending-specificity": null
"unit-no-unknown": null,
"value-list-max-empty-lines": null
}
}

View File

@@ -3,29 +3,35 @@ sudo: false
language: node_js
node_js:
- 11
- 8
cache:
directories:
- $HOME/.npm
matrix:
fast_finish: true
include:
- env: TEST_TYPE=lint
- 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
env:
matrix:
- TEST_TYPE=lint
- TEST_TYPE=test:dist
- TEST_TYPE=test:lib
- TEST_TYPE=test:es
- TEST_TYPE=test:dom
- TEST_TYPE=test:node
script:
- scripts/travis-script.sh
- |
if [ "$TEST_TYPE" = lint ]; then
npm run lint
elif [ "$TEST_TYPE" = test:dist ]; then
npm run dist && \
node ./tests/dekko/dist.test.js && \
LIB_DIR=dist npm test -- -w 2
elif [ "$TEST_TYPE" = test:lib ]; then
npm run compile && \
node ./tests/dekko/lib.test.js && \
LIB_DIR=lib npm test -- -w 2
elif [ "$TEST_TYPE" = test:es ]; then
npm run compile && \
LIB_DIR=es npm test -- -w 2
elif [ "$TEST_TYPE" = test:dom ]; then
npm test -- --coverage -w 2 && \
bash <(curl -s https://codecov.io/bash)
elif [ "$TEST_TYPE" = test:node ]; then
npm run test-node -- -w 2
fi

View File

@@ -1,431 +1,175 @@
17073025 <17073025@cnsuning.com>
282159468 <282159468@qq.com>
778758944 <778758944@qq.com>
Aaron Planell López <aaronplanell@gmail.com>
Aashutosh Rathi <aashutoshrathi@gmail.com>
Aditya Padhi <aditya.padhi@outlook.com>
Adrian Dimitrov <dimitrov.adrian@gmail.com>
Alan Braithwaite <asbraithwaite@gmail.com>
Albert Zheng <lisong.zheng@gmail.com>
Albert 理斯特 <shuaizhexu@gmail.com>
Aleck Landgraf <aleck.landgraf@gmail.com>
Alexander <labriko@yandex.ru>
Alexander Anpleenko <vaeum@yandex.com>
Alexander Suevalov <suevalov.work@gmail.com>
Alexandre Kirszenberg <a.kirszenberg@gmail.com>
Alexey Yakovlev <yallie@yandex.ru>
Alireza <alireza.mh@gmail.com>
Amorites <751809522@qq.com>
Amumu <yoyo837@hotmail.com>
Anas Tawfeek <anas.tawfeek@outlook.com>
Andre Perunicic <andre@intoli.com>
Andrew Murray <radarhere@gmail.com>
Andrey G <plandem@gmail.com>
Andrzej Dybionka <andrzej@arabel.la>
André <mazoni.andre@gmail.com>
Arnab Sen <arnabsen@gmail.com>
Arthur Denner Oliveira Santos <arthurdenner7@gmail.com>
Arvin Xu <arvinx@foxmail.com>
Ash Kumar <kumar.ashwin@outlook.com>
BK Heleth <bon.hoo@hotmail.com>
Babajide Fowotade <jide.b.tade@gmail.com>
Bartek <bartek.kozera@gmail.com>
Benedikt Franke <benedikt@franke.tech>
Benjamin Kniffler <bkniffler@me.com>
Benjy Cui <benjytrys@gmail.com>
Benoît Latinier <benoit@latinier.fr>
Bernie <bernie.wangbj@gmail.com>
Bilal Sirazitdinov <bilalsir@yandex.ru>
Bill Sheikh <bilawals22@gmail.com>
Bo Chen <bochen2014@yahoo.com>
Bozhao <yubz86@gmail.com>
Bradley Xu <xgheaven@gmail.com>
Brett Lamy <bel423@me.com>
Brook Shi <iwxiaot@gmail.com>
Bruce Mitchener <bruce.mitchener@gmail.com>
Bruno Maia <bruno.mm.maia@gmail.com>
Bryan Berger <bb@ga.co>
C <4019980@qq.com>
C.J. Winslow <whoaa512@gmail.com>
Cam Song <neosoyn@gmail.com>
Camol <kwwnjujlc@sina.com>
Cang Ta <hoksilato176@gmail.com>
Canwen Xu <canwenxu@126.com>
Carter Feldman <carter@carter.at>
Catalin Miron <mironcatalin@gmail.com>
Cee Cirno <i@cee.moe>
Chandler Moisen <chandlermoisen@gmail.com>
Chang Wang <cheapsteak@gmail.com>
Charles Covey-Brandt <chazcb@gmail.com>
Chelsea Huang <chelsea.huang@sap.com>
Chenjia <ariesjia00@hotmail.com>
Chikara Chan <chenhongtu@51xianqu.net>
Chris Kelly <cjke.7777@gmail.com>
ChrisFan <chris.fan.dev@gmail.com>
Christian <chr.vadala@gmail.com>
Christian Vadalà <chr.vadala@gmail.com>
Christopher Deutsch <cd@cdeutsch.com>
Chuang Yu <cyu9960@gmail.com>
Claudio Restifo <claudio.restifo@gmail.com>
Cody Chan <int64ago@gmail.com>
Colton Pierson <colton@coltonpierson.com>
Confiks <confiks@scriptbase.org>
Cong Zhang <dancerphil1994@gmail.com>
Conway Anderson <hello@conwayanderson.com>
Cordaro <elvis07@163.com>
D & R <jdz321@qq.com>
Daewoong Moon <wiziple@gmail.com>
Damian Green <damian.green@microlease.com>
Damian Green <damian@gcoders.com>
Dan Minshew <ofenixculpa@gmail.com>
Dane David <dndavid102@gmail.com>
Daniel Gomez <dgomez@orangeloops.com>
Danny Hoower Antonio Viasus Avila <danjavia@gmail.com>
Daqi Song <dqaria@gmail.com>
Darren Poon <dyhpoon@gmail.com>
David Hatten <dhatten@covermymeds.com>
David Schneider <davschne@gmail.com>
DengYun <tdzl2003@gmail.com>
Dimitri Mitropoulos <dimitrimitropoulos@gmail.com>
Dmitriy Mironov <dima.dev01@gmail.com>
Dmitry Bolotin <bolotin.dmitriy@gmail.com>
Dmitry Gladkikh <abdurahmanus@gmail.com>
Dmitry Guryev <dmitry.gurjev@gmail.com>
Dmitry Manannikov <email@slonoed.net>
Dmitry Snegirev <rikkitp@gmail.com>
Dorian <dorian@doma.io>
DosLin <doslino@gmail.com>
Douglas Mason <Demasonjr@gmail.com>
Eager <1226393396@qq.com>
Eager Wei <1226393396@qq.com>
EcmaProSrc.P/ka <asoiso@foxmail.com>
Ed Moore <ed@90seconds.tv>
Edd Hannay <accounts@edd.fm>
Eddie Xie <oeddyo@gmail.com>
Eden Wang <Eden.Wang@Akmii.com>
Eden Wang <yociduo@vip.qq.com>
Eduardo Ludi <eduludi@gmail.com>
Edward <7047924@qq.com>
Egor Yurtaev <yurtaev.egor@gmail.com>
Eli White <github@eli-white.com>
Emerson Laurentino <emersonlaurentino@hotmail.com>
Emma <sima.zhang1990@gmail.com>
Eric <84263800@qq.com>
Eric Celeste <efc@clst.org>
Eric Turriff <eric.turriff@gmail.com>
Erwann Mest <m+github@kud.io>
Evgeny Kuznetsov <jackk@ya.ru>
Eward Song <eward.song@gmail.com>
Federico Marcos <marcosfede@gmail.com>
Fergus Leung <fergusleung96@gmail.com>
Fernando Giarritiello <fgiarritiello@gmail.com>
Flynn <li.fulin@foxmail.com>
For177 <mengqiang.q@gmail.com>
Gao Jiangmiao <tolbkni@gmail.com>
Geoff Holden <geoff@brightloudnoise.com>
George Gray <george@ummodesign.com>
Graeme Yeates <gyeates@clearpath.ai>
Graeme Yeates <yeatesgraeme@gmail.com>
Grant Klinsing <gklinsing@gmail.com>
Gray Choi <gray.choi.1988@gmail.com>
Guan Hao <raptium@gmail.com>
Guan Yu Pan (Jacky) <jackypan1989@gmail.com>
HJin.me <hjin.me@gmail.com>
Hai Phan Nguyen <pnghai@gmail.com>
Haibin Yu <haibin.yu@oceanwing.com>
Hanai <ihanai1991@gmail.com>
Hanz Luo <lhz0516@gmail.com>
Haroen Viaene <fingebimus@me.com>
Harshit Mehrotra <harshitmehrotra@hotmail.com>
Heaven <ne_smalltown@163.com>
Henri Normak <henri.normak@gmail.com>
HeskeyBaozi <hezhiyu233@foxmail.com>
Hieu Ho <hieu.ho.le@lazada.com>
Hubert Argasinski <argasinski.hubert@gmail.com>
Hughen <446370503@163.com>
Hugo LEHMANN <shogi31@gmail.com>
Igor G <i.gaidai4uk@gmail.com>
Ilan <hasanovtk@gmail.com>
Ilan Hasanov <hasanovtk@gmail.com>
ImJoeHs <865439601@qq.com>
Inclined.Z <zjq0717@163.com>
Infinity <305870677@qq.com>
Ivan Kravets <me@ikravets.com>
Ivan Trofimov <ivan@trofimov.link>
Ivo Stratev <ivo.stratev.tues@gmail.com>
Jack Hsieh <jack@egenware.com>
Jack Works <zjwpeter@gmail.com>
Jackie.Ls <418292038@qq.com>
Jacques Kvam <jwkvam@gmail.com>
JaePil Jung <jjp5023@gmail.com>
Jake Richards <jake.richards@genesys.com>
James <james@schoolshape.com>
JamesYin <elantion@gmail.com>
Jaroslav Bereza <github.com@bereza.cz>
Jean-Luc Sorak <jlsorak@icloud.com>
Jeffrey Carl Faden <jeffreyatw@gmail.com>
JeromeLin <jerome.lin@zhongan.com>
Jerry Bendy <jerry@icewingcc.com>
Jesper We <jesper@journeyman.se>
Jiabin Peng <png.inside@gmail.com>
Jialei <jialeicui@126.com>
Jieraaa <842533841@qq.com>
Jin ZHANG <jz.zhangjin@gmail.com>
Jing Ma <mjingm87@qq.com>
Jinxuan Zhu <zhujinxuan@gmail.com>
Joe <qiaolibo@126.com>
Joe Hsu <jhsu.x1@gmail.com>
Johannes Loewe <johannes@loewe.pm>
John Johnson III <john@johnjohnson.cc>
John Nguyen <jtnguyen236@gmail.com>
Jonatas Walker <jonataswalker@gmail.com>
Jonny Buchanan <jonathan.buchanan@gmail.com>
Jordan Hornblow <jordan@jch254.com>
Josué <ujosuegt@outlook.com>
JribiBelhassen <belha9inzaghi@gmail.com>
Juan Rodrigo Venegas Boesch <jrvboesch@gmail.com>
Junyu Zhan <irrigator@yeah.net>
Kaien Liao <liaokaien@gmail.com>
Kenaniah Cerny <kenaniah@gmail.com>
Kenneth Luján Rosas <elgenio.03@gmail.com>
Kenneth Truong <kenneth.e.truong@gmail.com>
KentonYu <975853613@qq.com>
Kevin Ivan <info@kevinivan.com>
Kevin Wang <gumtree200@gmail.com>
KgTong <kgtong1992@gmail.com>
Khalifa Lame <khalibloo@gmail.com>
Kian <kian@vsu.cc>
Kiho · Cham <monkindey@163.com>
Kimmo Saari <kimmo.saari@revolt.fi>
Kirill Alexander Khalitov <voronar@gmail.com>
Kirill Stiopin <kirill.stiopin@hotmail.com>
Knacktus <knacktus@gmail.com>
Kyle Kelley <rgbkrk@gmail.com>
Kyle Rosenberg <kyle.rosenberg@gmail.com>
LLinFan- <catfoursi@qq.com>
Laith <laith24@gmail.com>
Larry Laski <larry.laski@gmail.com>
LeeHarlan <709886167@qq.com>
LeezQ <lizhenq2009@gmail.com>
Leo <clinyong@gmail.com>
Leon Shi <superRaytin@163.com>
Leon Shi <superRaytin@gmail.com>
Li Chao <rftstars@qq.com>
Liu Yang <zation1@gmail.com>
LongYinan <lynweklm@gmail.com>
Lucien Lee <lkiral7903@gmail.com>
Ludwig Bäcklund <ludli839@student.liu.se>
Lyndon001 <lld207@126.com>
MG12 <wuzhao.mail@gmail.com>
Ma Tianxiao <matx2215@outlook.com>
Madis Väin <madisvain@gmail.com>
Manjit Kumar <manjit1727@gmail.com>
Manweill <mic.liangwenwei@foxmail.com>
Marcela Bomfim <mbomfim@live.com>
Marco Afonso <mafonso333@gmail.com>
Marcus Bransbury <marcus.bransbury@gmail.com>
Marius Ileana <visvadw@gmail.com>
Mars Wong <marswong618@gmail.com>
Marshall Chen <Juniors.fei@gmail.com>
Martin Litvaj <kamahl19@gmail.com>
Martin Novák <martinnovak@outlook.com>
Mathew <khayaanimations@gmail.com>
Matt Lein <matt.lein@code42.com>
Maximilian Meyer <Maximilian.Meyer@br.de>
Meck <yesmeck@gmail.com>
MeiLin <postget.me@gmail.com>
Meow-z <372086270@qq.com>
Miaow <i@zfeng.net>
Michael Krog <michael.krog@previsto.com>
Michael Wang <ylzcylx@gmail.com>
Michalis Macheras <diodosier@gmail.com>
Mikasa33 <mikasa33@qq.com>
Min <dicklwm@163.com>
MinJeong Kim <min7859@gmail.com>
Minqi Pan <pmq2001@gmail.com>
Minsung Ryu <ryums0227@gmail.com>
Mitchell Demler <mitchell.demler@harcourts.net>
Mohamed Seada <mohamed.seada.1994@gmail.com>
Mohammad Faisal <faisalhmohd@gmail.com>
Mr.Tone <vector@malubei.com>
MuYu <mr.muzea@gmail.com>
Mário Gonçalves <mario.mc.goncalves@gmail.com>
Nathan Griffin <nathan@gatherhere.com>
Nathan Tavares Nascimento <nathan.tnascimento@gmail.com>
Nathan Wells <nwwells@gmail.com>
Neekey <ni184775761@gmail.com>
Nekron <nekron.hyt@gmail.com>
Neverland <chenjiahan@buaa.edu.cn>
Nico <nicolas@freddelacompta.com>
Nidhi Agarwal <nidhi.agarwal@zomato.com>
Nikolay <veseliy07@gmail.com>
Nikolay Solovyov <i@mr-ozio.ru>
Nimo <nimo.jser@gmail.com>
Nishant Arora <na.nishantarora@gmail.com>
Nokecy <Nokecy@163.com>
OAwan <georgio.wan@gmail.com>
Oleg Kuzava <olegkuzava@gmail.com>
Oleksandr Kovalchuk <me.olexandr.kovalchuk@gmail.com>
Ooi Yee Wei <ywooi@yahoo.com>
Open Next <opennext@126.com>
OuYancey <ou.yancey@gmail.com>
Panjie Setiawan Wicaksono <panjie@panjiesw.com>
Patrick Gidich <patrick.gidich@simnova.com>
Patryk <longer44@gmail.com>
Peter <usstpeter@gmail.com>
Peter Berg <atticusberg@gmail.com>
Phanupong Janthapoon <panupong.jtp@gmail.com>
Pierre <pierre@bazoge.com>
Pierre Neter <pierreneter@gmail.com>
Piper Chester <piperchester@gmail.com>
Pixy Yuan <pixy.bupt@gmail.com>
Pooya Parsa <pyapar@gmail.com>
Pyiner <lijiuyang1992@gmail.com>
Pyroboomka <qwaarty@mail.ru>
Qiaosen Huang <joesonw@gmail.com>
Qingrong Ke <keqingrong1992@gmail.com>
Rafael Cosman <rafaelcosman@alumni.stanford.edu>
Rahul Gurung <gurungrahul2@gmail.com>
Rallets <rallet@rallets.com>
Ramsés Moreno <ramses@cuatromedios.com>
Randy <randypriv@gmail.com>
RaoHai <surgesoft@gmail.com>
Raphael Chauveau <raph.chauveau@gmail.com>
Reed Sun <superreedsun@gmail.com>
Rex <zhangzilong.zzl@163.com>
Ricardo Raphael Joson <rrjoson08@gmail.com>
Richard D. Worth <rdworth@gmail.com>
Rick Zhou <rinick@gmail.com>
Robert Wilkinson <wilkinson.robert.a@gmail.com>
Rohan Malhotra <rohan.root@gmail.com>
Rongjian Zhang <pd4d10@gmail.com>
Rrrandom <emanonhere@gmail.com>
RyanHui <ryanhui1996@gmail.com>
SHEN Lin <shenlin192@gmail.com>
Sakol Assawasagool <koobitor@gmail.com>
Sam Chen <chenxsan@gmail.com>
Sam Maxwell <sam@paybase.io>
Samuel Gaus <sam@gaus.co.uk>
Sangle <whb97@163.com>
Sanjay Kumar <kris.gooner@gmail.com>
Sanjay Kumar <sk@tectusdreamlab.com>
Scott Sturgeon <scott@tugboatlogic.com>
Sean Lin <sean@ejoy.com>
Sean Sun <pinggodstudio@gmail.com>
Sebastian Blade <blade254353074@hotmail.com>
Sebastian Busch <mail@sebastian-bus.ch>
Sebastian Busch <s.busch@qbus-enet.de>
Sergio Crisostomo <sergiosbox@gmail.com>
Shawn Sit <xueqingxiao@gmail.com>
ShiTengFei <shitengfei@goyoo.com>
Shubham Kanodia <shubhamsizzles@gmail.com>
Shun <polytechnics.shun@gmail.com>
Shuvalov Anton <anton@shuvalov.info>
SimaQ <sima.zhang1990@gmail.com>
Spencer <spjy@hawaii.edu>
Stephen Esser <Stephen.Esser@gmail.com>
Tao <magicdawn@qq.com>
Tao Zhang <windse7en@gmail.com>
Taylor Sabell <taylorsabell@gmail.com>
Tengjiao Cai <caitengjiao1987@gmail.com>
Terence <trence320@163.com>
The Rock <zhoguoxin@126.com>
Thibault Derousseaux <tde@activeviam.com>
Thiebaud Thomas <thiebaud.tom@gmail.com>
Tino D <ginodeis@gmail.com>
Tom Gao <tom@zoomsoft.cc>
Tom Xu <tom.xu@antcosa.com>
Tom Xu <ycxzhkx@gmail.com>
TomIsion <isiontom@gmail.com>
Tomás Francisco <mail@tomasfrancisco.com>
Tomáš Szabo <tomas.szabo@deftomat.com>
Trotyl Yu <trotyl@qq.com>
Troy Thompson <troynt@gmail.com>
Tyler <chaotyler@gmail.com>
Ubaldo Quintana <blkdr@hotmail.com>
Vadim Macagon <vadim.macagon@gmail.com>
Valentin Vichnal <valentin@vichnal.com>
Vemund Santi <vemund@santi.no>
Vic <709147950@qq.com>
Vemund Santi <veund@santi.no>
Vincent Zhang <vxzhong@qq.com>
Vitaliy Mazurenko <vitaliymazurenko@gmail.com>
ViviaRui <zr1450995198@163.com>
Walter Barbagallo <brb.walter@gmail.com>
Walter Barbagallo <turbometalskater@gmail.com>
Wang Jun <amos.callmexyz@gmail.com>
Wang Riwu <riwu0730@gmail.com>
Wang Zhengchen <wang909208@163.com>
Warren Seymour <warren@fountainhead.tech>
Wei Zhu <yesmeck@gmail.com>
Wenchao Hu <zjuhwc@gmail.com>
Wendell <wendzhue@gmail.com>
Will Chen <willchen90@gmail.com>
WingGao <wing.gao@live.com>
Wu Haotian <whtsky@gmail.com>
XBTop1! <xbtop1@gmail.com>
Xiaoming <yokiming1994@gmail.com>
Xie Guanglei <xieguanglei@hotmail.com>
Xiping.wang <527409987@qq.com>
XuMM_12 <owiatsq@sina.cn>
Yang <504021398@qq.com>
Yang Bin <yangkghjh@gmail.com>
Yasin Uslu <nepjua@gmail.com>
Yevhen Hryhorevskyi <evgeniygrigorevskiy@gmail.com>
Yiming <ymjrcc@qq.com>
Yogesh <yogeshkumar180592@gmail.com>
Yu <yutingzhao1991@sina.com>
YuChao Liang <l.yuch@foxmail.com>
Yuhang Liu <644186735@qq.com>
Yunus EŞ <yunus@yunuses.com>
Yuri Pirola <yuri.pirola@unimi.it>
Yury Kozyrev <urakozz@me.com>
Yusuke Ito <novi.mad@gmail.com>
Yuwei Ba <i@xiaoba.me>
Yuxuan Huo <yuxuan.huo2011@gmail.com>
YuyingWu <wuyuying1128@gmail.com>
Zack Craig <zack@zack6849.com>
Zap <a124116186@qq.com>
Zhang Zhi <fytriht@gmail.com>
Zheeeng <hi@zheeeng.me>
Zhiqiang Gong <elory0513@hotmail.com>
Ziluo <gyfzzu@gmail.com>
Zohaib Ijaz <mzohaib.qc@gmail.com>
afc163 <afc163@gmail.com>
agent-z <1607291079@qq.com>
ahalimkara <ahalimkara@gmail.com>
alex <379118572@qq.com>
arifemrecelik <ce.arifemre@gmail.com>
ascoders <576625322@qq.com>
ashishg-qburst <ashishg@qburst.com>
bLue <tbdblue@gmail.com>
bang <sqibang@gmail.com>
bang88 <sqibang@gmail.com>
baozefeng <727751065@qq.com>
blankzust <450811238@qq.com>
byuanama <byuan@ama.com.au>
byzyk <bohdan.kh@gmail.com>
bzone <yarnbcoder@gmail.com>
caoyi <caoyi0905@mail.hfut.edu.cn>
carrie-tanminyi <12mytan@gmail.com>
cathayandy <wzm_andy@126.com>
chaofeis <408067385@qq.com>
chchen <cc272309126@gmail.com>
chencheng (云谦) <sorrycc@gmail.com>
chencheng <sorrycc@gmail.com>
cjahv <cjahv@qq.com>
clinyong <clinyong@gmail.com>
codesign <zuishiguang@126.com>
corneyl <cornieljoosse@gmail.com>
david.lv <code4funlnyx@gmail.com>
davidhatten <david.r.hatten@gmail.com>
ddcat1115 <ddcat1115@gmail.com>
decade <decadef20@gmail.com>
delesseps <andrewlessels@gmail.com>
denzw <denzw@21cn.com>
dependabot[bot] <support@dependabot.com>
detailyang <detailyang@gmail.com>
devqin <devqin@gmail.com>
digz6666 <digz6666@gmail.com>
djorkaeff <djorkae55@gmail.com>
duzliang <duzliang@gmail.com>
ecofe <150641329@qq.com>
@@ -436,221 +180,101 @@ ezpub <ez.foro@gmail.com>
feng zhi hao <fzhihao@outlook.com>
fengmk2 <m@fengmk2.com>
flashback313 <windmark2012@gmail.com>
frezc <504021398@qq.com>
genie <genie88@163.com>
gregahren <grega.hren@gmail.com>
guifu <picodoth@gmail.com>
handycode <lihandi@gmail.com>
hank <stonehank310@gmail.com>
hanpei <75189218@qq.com>
hansnow <hansnow2012@gmail.com>
haoxin <coderhaoxin@outlook.com>
hardfist <yangjianzju@gmail.com>
hauwa123 <hauwa.aminu@outlook.com>
hehe <xpc_kacl@163.com>
hengkx <ycxzhkx@gmail.com>
henryv0 <henryvo94@gmail.com>
hi-caicai <hi@cai-cai.me>
hongxuWei <hongxu.wei@outlook.com>
huangyan.py <huangyan.py@bytedance.com>
huzzbuzz <huzzbuzz@outlook.com>
iamcastelli <sowed@cyberdude.com>
ilanus <hasanovtk@gmail.com>
imosapatryk <imosa.patryk@gmail.com>
infeng <fzhihao@outlook.com>
int2d <int2d@qq.com>
ioldfish <fish.wangl@gmail.com>
jasonslyvia <jasonslyvia@gmail.com>
jasonxia23 <xia.jason23@gmail.com>
jiang <155259966@qq.com>
jim <wasd2144@hotmail.com>
jinouwuque <ee2win@gmail.com>
jinyaqiao1102 <405782493@QQ.com>
jojoLockLock <miffyschou@sina.com>
junjing.zhang <zhangjunjing@gmail.com>
kacjay <45483388@qq.com>
kagawagao <kingsongao1221@gmail.com>
kaifei <150641329@qq.com>
kasinooya <kasinooya@gmail.com>
kayw <kayw@outlook.com>
kdenz <ksnz93@gmail.com>
kdepp <kdepp.cd@gmail.com>
keng <keng@renderinghouse.com>
keqingrong <keqingrong1992@gmail.com>
ko <git@yaksok.net>
konakona <lovekonakona@gmail.com>
kossel <lis.yichao@gmail.com>
kuang <p2227@hotmail.com>
kuitos <kuitos.lau@gmail.com>
kun sam <kunsam624@icloud.com>
leadream <857098475@qq.com>
lehug <zcszuo5811@126.com>
leijingdao <leijingdao@163.com>
leon.shi <superRaytin@163.com>
lgmcolin <gengmin.lgm@gmail.com>
lgmcolin <lgmcolin@gmail.com>
liangfei <njliangfei@gmail.com>
liekkas <zjq0717@163.com>
lihqi <455711093@qq.com>
littleLane <857183384@qq.com>
lixiaochou077 <qi.liqi07@gmail.com>
lixiaoyang <lixiaoyang2345@gmail.com>
lixiaoyang1992 <lixiaoyang2345@gmail.com>
lizhaocai <lzc09008@gmail.com>
lizhen <lizhen@youzan.com>
loganpowell <loganp@tepper.cmu.edu>
luyiming <luyimingchn@gmail.com>
lvren <luren6049@qq.com>
mArker <252133226@qq.com>
memoryza <jincai.wang@foxmail.com>
mgrdevport <mgrdevport@gmail.com>
mitchell.demler <mitchell.demler@harcourts.net>
mkermani144 <mkermani144@gmail.com>
mmmveggies <jakeselig@gmail.com>
mofelee <mofe@me.com>
monkindey <monkindey@163.com>
mushan0x0 <mushan0x0@gmail.com>
muzea <mr.muzea@gmail.com>
muzuiget <muzuiget@gmail.com>
natergj <nater_nater@me.com>
neekey <ni184775761@gmail.com>
niko <644506165@qq.com>
nikogu <644506165@qq.com>
nuintun <nuintun@qq.com>
ohhoney1 <1269075501@qq.com>
paranoidjk <hust2012jiangkai@gmail.com>
parlop <parlop@gmail.com>
pbrink231 <pbrink231@gmail.com>
pd4d10 <pd4d10@gmail.com>
peiming <hyrijk@gmail.com>
picodoth <picodoth@gmail.com>
picodoth <pikaleize@gmail.com>
pinggod <pinggodstudio@gmail.com>
pizn <pizner@gmail.com>
plandem <plandem@gmail.com>
popomore <sakura9515@gmail.com>
qiaojie <1454763497@qq.com>
qixian.cs@outlook.com <wasd2144@hotmail.com>
qliu <1403927509@qq.com>
qubaoming <qubaoming@didichuxing.com>
ravirambles <ravirambles@gmail.com>
ryangun <ryangun@foxmail.com>
ryanhoho <hswacoal@gmail.com>
ryannz <c5e1856@gmail.com>
sadmark <zhoubin@laidian360.com>
sallen450 <jqh101@sina.com>
shelwin <wxfans@gmail.com>
shenlin192@gmail.com <shenlin192@gmail.com>
shlice <licesh@gmail.com>
shouyong <enlangs@163.com>
simaQ <sima.zhang1990@gmail.com>
siyu77 <xwzhang1986@gmail.com>
slientcloud <rjmuqiang@gmail.com>
sliwey <qlw1009@gmail.com>
snadn <snadn@snadn.cn>
snail <120216220@qq.com>
sojournerc <cmeyer@zvelo.com>
sorrycc <sorrycc@gmail.com>
sosohime <theziming@126.com>
spideeee <spideeee@github.com>
stickmy <stickmyc@gmail.com>
swindme <swindme@163.com>
syssam <s.y.s.sam.sys@gmail.com>
tangjinzhou <415800467@qq.com>
tangjinzhou <tangjinzhou@yidian-inc.com>
thegatheringstorm <tgs@tgs.blue>
tianli.zhao <275287902@qq.com>
tom <caolvchong@gmail.com>
twobin <twobin@live.com>
u3u <qwq@qwq.cat>
ustccjw <317713370@qq.com>
valleykid <valleykiddy@gmail.com>
vgeyi <vgeyiz@126.com>
wangshantao <605682551@qq.com>
wangtao0101 <yuecjn@gmail.com>
wangxiaolei <fatelei@gmail.com>
wangxueliang <wangxueliang@yidian-inc.com>
warmhug <hualei5280@gmail.com>
whtang906 <whtang906@gmail.com>
wizawu <wizawu@gmail.com>
wonyun <wy393767068@163.com>
xiaofan2406 <xiaofan2406@gmail.com>
y-take <y.takey@gmail.com>
yangwukang <yangwukang@boco.com.cn>
ycjcl868 <45808948@qq.com>
yeliex <yeliex@yeliex.com>
yiminanci <yiminanci@gmail.com>
yiminghe <yiminghe@gmail.com>
yociduo <yociduo@vip.qq.com>
yoyo837 <yoyo837@hotmail.com>
yubozhao <yubz86@gmail.com>
yuche <i@yuche.me>
z <haig8@msn.com>
zack <zxyah@126.com>
zelongc <nickcong123@gmail.com>
zerob4wl <zerob4wl@gmail.com>
zhangpc <zhangpc@tenxcloud.com>
zhaocai <lzc09008@gmail.com>
zhujun24 <zhujun87654321@gmail.com>
zhuyue <fuping.dfp@antfin.com>
zilong <jzlxiaohei@163.com>
zinkey <yaya@uloveit.com.cn>
zollero <corona7@163.com>
zombieJ <smith3816@gmail.com>
zombiej <smith3816@gmail.com>
zongzi531 <zongzi.xy@gmail.com>
ztplz <mysticzt@gmail.com>
zuiidea <zuiiidea@gmail.com>
°))))彡 <fisherspy@live.com>
邦 <sqibang@gmail.com>
爱but的苍蝇 <354788473@qq.com>
高力 <3071730@qq.com>
拷钉 <41830859@qq.com>
苏秦 <646382806@qq.com>
竹尔 <Juelchiang@gmail.com>
偏右 <afc163@gmail.com>
英布 <chaoren1641@gmail.com>
朮厃 <cn.ah.liu@gmail.com>
张聪 <dancerphil1994@gmail.com>
诸岳 <dengfuping_develop@163.com>
逸达 <dqaria@gmail.com>
诸岳 <fuping.dfp@antfin.com>
刘红 <liuhong1.happy@163.com>
宝码 <noyobo@gmail.com>
陈帅 <qixian.cs@outlook.com>
蔡伦 <sliuqin@gmail.com>
陆离 <surgesoft@gmail.com>
愚道 <tingzhao.ytz@antfin.com>
陈帅 <wasd2144@hotmail.com>
松子 <window.pibarr@gmail.com>
付引 <xxxquotes@gmail.com>
可乐 <zaxlct@foxmail.com>
山客 <zeakhold@gmail.com>
曾凯 <zengkai2009@foxmail.com>
低位 <zhujun87654321@gmail.com>
广彬-梁 <326741518@qq.com>
何志勇 <15988134176@163.com>
徐坤龙 <272992168@qq.com>
黄子毅 <576625322@qq.com>
翁润雨 <593110501@qq.com>
崔宏森 <948346354@qq.com>
黄文鉴 <concefly@foxmail.com>
董天成 <dongtiangche@outlook.com>
方剑成 <fjc0kb@gmail.com>
包子熊 <hezhiyu233@foxmail.com>
闲耘™ <hotoo.cn@gmail.com>
一喵呜 <hyb628@gmail.com>
吕立青 <jimmy.jinglv@gmail.com>
隋鑫磊 <joshuasui@gmail.com>
乔奕轩 <qiao_yixuan@163.com>
马斯特 <sd4399340@126.com>
王集鹄 <wjhu111@21cn.com>
低位 <zhujun87654321@gmail.com>
偏右 <afc163@gmail.com>
可乐 <zaxlct@foxmail.com>
吕立青 <jimmy.jinglv@gmail.com>
广彬-梁 <326741518@qq.com>
柚子男 <yozman@sina.com>
愚指导 <yutingzhao1991@sina.com>
愚指导-TZ <yutingzhao1991@sina.com>
杨小事er <Uiryzd@163.com>
超能刚哥 <margox@foxmail.com>
马金花儿 <o.o@mug.dog>
रोहन मल्होत्रा <rohan.malhotra@adwyze.com>
白羊座小葛 <abeyuhang@gmail.com>
薛定谔的猫 <hh_2013@foxmail.com>
英布 <chaoren1641@gmail.com>
蔡伦 <sliuqin@gmail.com>
逸达 <dqaria@gmail.com>
邦 <sqibang@gmail.com>
闲耘™ <hotoo.cn@gmail.com>
陆离 <surgesoft@gmail.com>
陈帅 <wasd2144@hotmail.com>
马斯特 <sd4399340@126.com>
马金花儿 <o.o@mug.dog>
黄子毅 <576625322@qq.com>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,7 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version].
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,6 +1,6 @@
MIT LICENSE
Copyright (c) 2015-present Ant UED, https://xtech.antfin.com/
Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,63 +1,49 @@
<p align="center">
<a href="http://ant.design">
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
<img width="320" src="https://t.alipayobjects.com/images/rmsweb/T1B9hfXcdvXXXXXXXX.svg">
</a>
</p>
<h1 align="center">Ant Design</h1>
# Ant Design
<div align="center">
[![](https://img.shields.io/travis/ant-design/ant-design.svg?style=flat-square)](https://travis-ci.org/ant-design/ant-design)
[![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)
[![Dependency Status](https://img.shields.io/gemnasium/react-component/trigger.svg?style=flat-square)](https://gemnasium.com/ant-design/ant-design)
[![npm package](https://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd)
[![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](https://npmjs.org/package/antd)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/ant-design/ant-design.svg)](http://isitmaintained.com/project/ant-design/ant-design "Percentage of issues still open")
[![Gitter](https://badges.gitter.im/ant-design/ant-design-english.svg)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) (English)
[![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)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)(中文)
一套企业级的 UI 设计语言和 React 实现。
[![Build Status](https://dev.azure.com/ant-design/ant-design/_apis/build/status/ant-design.ant-design?branchName=master)](https://dev.azure.com/ant-design/ant-design/_build/latest?definitionId=2?branchName=master)
[![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)
[![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)
[![npm package](https://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd)
[![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![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)
[README in English](README.md)
[![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)
[![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://cdn-images-1.medium.com/max/2000/1*NIlj0-TdLMbo_hzSBP8tmg.png)](http://ant.design/index-cn)
[English](./README.md) | 简体中文
## ✨ 特性
## 特性
- 提炼自企业级中后台产品的交互语言和视觉风格。
- 开箱即用的高质量 React 组件。
- 使用 TypeScript 构建,提供完整的类型定义文件。
- 全链路开发和设计工具体系
- 基于 npm + webpack + [dva](https://github.com/dvajs/dva) 的企业级开发框架
## 🖥 支持环境
## 支持环境
* 现代浏览器和 IE9 及以上。
* 支持服务端渲染。
* [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/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
## 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
## 📦 安装
`antd` 是一个开源项目,我们欢迎社区参与共建。如果你对此项目感兴趣,有 [很多方式](https://opensource.guide/how-to-contribute/) 进行参与。你可以 watch 这个仓库,加入 [issue 中的讨论](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3ADiscussion),以及尝试实现一些 [已接受的特性](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22PR+welcome%22)。我们会给予有活跃贡献的社区成员 [collaborator 权限](https://github.com/ant-design/ant-design/issues/3222)。
## 安装
```bash
npm install antd --save
npm install antd
```
```bash
yarn add antd
```
## 🔨 示例
## 示例
```jsx
import { DatePicker } from 'antd';
@@ -70,54 +56,63 @@ ReactDOM.render(<DatePicker />, mountNode);
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
```
你也可以 [按需加载组件](https://ant.design/docs/react/getting-started-cn#按需加载)。
按需加载可通过此写法 `import DatePicker from 'antd/lib/date-picker'` 或使用 Babel 插件 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import),或使用 TypeScript 插件 [ts-import-plugin](https://github.com/Brooooooklyn/ts-import-plugin)。
### TypeScript
## TypeScript
参考 [在 TypeScript 中使用](https://ant.design/docs/react/use-in-typescript-cn)
```js
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"jsx": "preserve",
"allowSyntheticDefaultImports": true
}
}
```
## 🌍 国际化
> 注意:
> - 设置 `allowSyntheticDefaultImports` 避免 `error TS1192: Module 'react' has no default export` 的错误。
> - 不要使用 @types/antd, antd 已经自带了 TypeScript 定义。
参考 [国际化文档](http://ant.design/docs/react/i18n-cn)。
## 国际化
## 🔗 链接
参考 [国际化文档](http://ant.design/docs/react/i18n)。
## 链接
- [首页](http://ant.design/)
- [组件库](http://ant.design/docs/react/introduce)
- [Ant Design Pro](http://pro.ant.design/)
- [更新日志](CHANGELOG.en-US.md)
- [脚手架市场](http://scaffold.ant.design)
- [React 底层基础组件](http://react-component.github.io/)
- [移动端组件](http://mobile.ant.design)
- [首页模板集](https://landing.ant.design)
- [动效](https://motion.ant.design)
- [脚手架市场](http://scaffold.ant.design)
- [设计规范速查手册](https://github.com/ant-design/ant-design/wiki/Ant-Design-%E8%AE%BE%E8%AE%A1%E5%9F%BA%E7%A1%80%E7%AE%80%E7%89%88)
- [开发者说明](https://github.com/ant-design/ant-design/wiki/Development)
- [版本发布规则](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)
- [常见问题](https://ant.design/docs/react/faq-cn)
- [CodeSandbox 模板](https://u.ant.design/codesandbox-repro) for bug reports
- [常见问题](https://github.com/ant-design/ant-design/wiki/FAQ)
- [CodePen 模板](http://codepen.io/benjycui/pen/KgPZrE?editors=001) for bug reports
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
- [定制主题](http://ant.design/docs/react/customize-theme-cn)
## ⌨️ 本地开发
## 本地开发
```bash
$ git clone git@github.com:ant-design/ant-design.git
$ cd ant-design
$ npm install
$ npm start
```
打开浏览器访问 http://127.0.0.1:8001 ,更多[本地开发文档](https://github.com/ant-design/ant-design/wiki/Development)
打开浏览器访问 http://127.0.0.1:8001 ,更多本地开发文档参见: https://github.com/ant-design/ant-design/wiki/Development
## 🤝 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
## 如何贡献
请参考[贡献指南](https://ant.design/docs/react/contributing-cn).
在任何形式的参与前,请先阅读 [贡献者文档](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md)。如果你希望参与贡献,欢迎 [Pull Request](https://github.com/ant-design/ant-design/pulls),或给我们 [报告 Bug](http://new-issue.ant.design/)
> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。
[![Let's fund issues in this repository](https://issuehunt.io/static/embed/issuehunt-button-v1.svg)](https://issuehunt.io/repos/34526884)
## 社区互助
如果您在使用的过程中碰到问题,可以通过下面几个途径寻求帮助,同时我们也鼓励资深用户通过下面的途径给新人提供帮助。

138
README.md
View File

@@ -1,63 +1,49 @@
<p align="center">
<a href="http://ant.design">
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
<img width="320" src="https://t.alipayobjects.com/images/rmsweb/T1B9hfXcdvXXXXXXXX.svg">
</a>
</p>
<h1 align="center">Ant Design</h1>
# Ant Design
<div align="center">
An enterprise-class UI design language and React implementation.
[![Build Status](https://dev.azure.com/ant-design/ant-design/_apis/build/status/ant-design.ant-design?branchName=master)](https://dev.azure.com/ant-design/ant-design/_build/latest?definitionId=2?branchName=master)
[![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)
[![](https://img.shields.io/travis/ant-design/ant-design.svg?style=flat-square)](https://travis-ci.org/ant-design/ant-design)
[![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)
[![Dependency Status](https://img.shields.io/gemnasium/react-component/trigger.svg?style=flat-square)](https://gemnasium.com/ant-design/ant-design)
[![npm package](https://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd)
[![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![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)
[![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](https://npmjs.org/package/antd)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/ant-design/ant-design.svg)](http://isitmaintained.com/project/ant-design/ant-design "Percentage of issues still open")
[![Gitter](https://badges.gitter.im/ant-design/ant-design-english.svg)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) (English)
[![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)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)(中文)
[![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)
[![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)
An enterprise-class UI design language and React-based implementation.
</div>
[中文 README](README-zh_CN.md)
[![](https://cdn-images-1.medium.com/max/2000/1*NIlj0-TdLMbo_hzSBP8tmg.png)](http://ant.design)
## Features
English | [简体中文](./README-zh_CN.md)
## ✨ Features
- An enterprise-class UI design system for web applications.
- An enterprise-class UI design language for web applications.
- A set of high-quality React components out of the box.
- Written in TypeScript with predictable static types.
- The whole package of development and design resources and tools.
- Written in TypeScript with complete define types.
- A npm + webpack + [dva](https://github.com/dvajs/dva) front-end development workflow.
## 🖥 Environment Support
## Environment Support
* Modern browsers and Internet Explorer 9+ (with [polyfills](https://ant.design/docs/react/getting-started#Compatibility))
* Server-side Rendering
* [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/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
## Let's build a better antd together [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
## 📦 Install
`antd` is an open source project; improvements are welcomed. If you are interested in contributing to `antd`, you can watch this repository, join in [discussion](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3ADiscussion), or try to implement some [features which have been accepted](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22PR+welcome%22). Actually, there are [many ways](https://opensource.guide/how-to-contribute/) to contribute. And we are always happy to [offer collaborator permission](https://github.com/ant-design/ant-design/issues/3222) for some active contributors.
## Install
```bash
npm install antd --save
npm install antd
```
```bash
yarn add antd
```
## 🔨 Usage
## Usage
```jsx
import { DatePicker } from 'antd';
@@ -70,51 +56,95 @@ And import style manually:
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
```
Or [import components on demand](https://ant.design/docs/react/getting-started#Import-on-Demand).
### Use modularized antd
- Manually import
```jsx
import DatePicker from 'antd/lib/date-picker'; // for js
import 'antd/lib/date-picker/style/css'; // for css
// import 'antd/lib/date-picker/style'; // that will import less
```
- Use [babel-plugin-import](https://github.com/ant-design/babel-plugin-import)
```js
// .babelrc or babel-loader option
{
"plugins": [
["import", { "libraryName": "antd", "style": "css" }] // `style: true` for less
]
}
```
Then you can import components from antd, equivalent to import manually below.
```jsx
// import js and css modularly, parsed by babel-plugin-import
import { DatePicker } from 'antd';
```
### TypeScript
See [Use in TypeScript](https://ant.design/docs/react/use-in-typescript).
```js
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"jsx": "preserve",
"allowSyntheticDefaultImports": true
}
}
```
## 🌍 Internationalization
> Note:
> - set `allowSyntheticDefaultImports` to prevent `error TS1192: Module 'react' has no default export`.
> - Don't use @types/antd, antd provide a built-in ts definition already.
#### Use [ts-import-plugin](https://github.com/Brooooooklyn/ts-import-plugin) with modularized antd
```js
{
loader: "ts-loader", // or awesome-typescript-loader
options {
getCustomTransformers: () => ({
before: [ tsImportPluginFactory({ libraryName: "antd", style: "css" }) ]
})
}
}
```
## Internationalization
See [i18n](http://ant.design/docs/react/i18n).
## 🔗 Links
## Links
- [Home page](http://ant.design/)
- [Components](http://ant.design/docs/react/introduce)
- [Ant Design Pro](http://pro.ant.design/)
- [Change Log](CHANGELOG.en-US.md)
- [Scaffold Market](http://scaffold.ant.design)
- [rc-components](http://react-component.github.io/)
- [Mobile UI](http://mobile.ant.design)
- [Landing Pages](https://landing.ant.design)
- [Motion](https://motion.ant.design)
- [Scaffold Market](http://scaffold.ant.design)
- [Developer Instruction](https://github.com/ant-design/ant-design/wiki/Development)
- [Versioning Release Note](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) for bug reports
- [FAQ](https://github.com/ant-design/ant-design/wiki/FAQ)
- [CodePen boilerplate](http://codepen.io/benjycui/pen/KgPZrE?editors=001) for bug reports
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
- [Customize Theme](http://ant.design/docs/react/customize-theme)
## ⌨️ Development
## Development
```bash
$ git clone git@github.com:ant-design/ant-design.git
$ cd ant-design
$ npm install
$ npm start
```
Open your browser and visit http://127.0.0.1:8001 , see more at [Development](https://github.com/ant-design/ant-design/wiki/Development).
Open your browser and visit http://127.0.0.1:8001 , see more at https://github.com/ant-design/ant-design/wiki/Development .
## 🤝 Contributing [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
Read our [contributing guide](https://ant.design/docs/react/contributing) and let's build a better antd together.
## Contributing
We welcome all contributions. Please read our [CONTRIBUTING.md](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md) first. You can submit any ideas as [pull requests](https://github.com/ant-design/ant-design/pulls) or as [GitHub issues](https://github.com/ant-design/ant-design/issues). If you'd like to improve code, check out the [Development Instructions](https://github.com/ant-design/ant-design/wiki/Development) and have a good time! :)
If you are collaborator. Please follow our [Pull Request principle](https://github.com/ant-design/ant-design/wiki/PR-principle) to create Pull Request by [collaborator template](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)

View File

@@ -1,69 +0,0 @@
# 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:
batch: true
branches:
exclude:
- gh-pages
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: '11.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

@@ -18,24 +18,17 @@ Array [
"Cascader",
"Checkbox",
"Col",
"Comment",
"ConfigProvider",
"DatePicker",
"Divider",
"Dropdown",
"Drawer",
"Empty",
"Form",
"Icon",
"Input",
"InputNumber",
"Layout",
"List",
"LocaleProvider",
"message",
"Menu",
"Modal",
"Statistic",
"notification",
"Pagination",
"Popconfirm",
@@ -45,7 +38,6 @@ Array [
"Rate",
"Row",
"Select",
"Skeleton",
"Slider",
"Spin",
"Steps",

View File

@@ -1,21 +1,7 @@
const OLD_NODE_ENV = process.env.NODE_ENV;
process.env.NODE_ENV = 'development';
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
const antd = require('..');
import * as antd from '..';
describe('antd', () => {
afterAll(() => {
process.env.NODE_ENV = OLD_NODE_ENV;
});
it('exports modules correctly', () => {
expect(Object.keys(antd)).toMatchSnapshot();
});
it('should hint when import all components in dev mode', () => {
expect(warnSpy).toBeCalledWith(
'You are using a whole package of antd, please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size.',
);
warnSpy.mockRestore();
});
});

View File

@@ -1,8 +1,4 @@
import raf from 'raf';
import delayRaf from '../raf';
import throttleByAnimationFrame from '../throttleByAnimationFrame';
import getDataOrAriaProps from '../getDataOrAriaProps';
import triggerEvent from '../triggerEvent';
describe('Test utils function', () => {
beforeAll(() => {
@@ -36,96 +32,4 @@ describe('Test utils function', () => {
jest.runAllTimers();
expect(callback).not.toBeCalled();
});
describe('getDataOrAriaProps', () => {
it('returns all data-* properties from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
'data-test': 'test-id',
'data-id': 1234,
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({
'data-test': 'test-id',
'data-id': 1234,
});
});
it('does not return data-__ properties from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
'data-__test': 'test-id',
'data-__id': 1234,
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({});
});
it('returns all aria-* properties from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
'aria-labelledby': 'label-id',
'aria-label': 'some-label',
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({
'aria-labelledby': 'label-id',
'aria-label': 'some-label',
});
});
it('returns role property from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
role: 'search',
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({ role: 'search' });
});
});
it('delayRaf', done => {
jest.useRealTimers();
let bamboo = false;
delayRaf(() => {
bamboo = true;
}, 3);
// Variable bamboo should be false in frame 2 but true in frame 4
raf(() => {
expect(bamboo).toBe(false);
// Frame 2
raf(() => {
expect(bamboo).toBe(false);
// Frame 3
raf(() => {
// Frame 4
raf(() => {
expect(bamboo).toBe(true);
done();
});
});
});
});
});
it('triggerEvent', () => {
const button = document.createElement('button');
button.addEventListener(
'click',
() => {
button.style.width = '100px';
},
true,
);
triggerEvent(button, 'click');
expect(button.style.width).toBe('100px');
});
});

View File

@@ -1,11 +0,0 @@
export default function getDataOrAriaProps(props: any) {
return Object.keys(props).reduce((prev: any, key: string) => {
if (
(key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-' || key === 'role') &&
key.substr(0, 7) !== 'data-__'
) {
prev[key] = props[key];
}
return prev;
}, {});
}

View File

@@ -0,0 +1,30 @@
export function getComponentLocale(props, context, componentName, getDefaultLocale) {
let locale: any = {};
if (context && context.antLocale && context.antLocale[componentName]) {
locale = context.antLocale[componentName];
} else {
const defaultLocale = getDefaultLocale();
// TODO: make default lang of antd be English
// https://github.com/ant-design/ant-design/issues/6334
locale = defaultLocale.default || defaultLocale;
}
const result = {
...locale,
...props.locale,
};
result.lang = {
...locale.lang,
...props.locale.lang,
};
return result;
}
export function getLocaleCode(context) {
const localeCode = context.antLocale && context.antLocale.locale;
// Had use LocaleProvide but didn't set locale
if (context.antLocale && context.antLocale.exist && !localeCode) {
return 'zh-cn';
}
return localeCode;
}

View File

@@ -0,0 +1,44 @@
const availablePrefixs = ['moz', 'ms', 'webkit'];
function requestAnimationFramePolyfill() {
let lastTime = 0;
return function(callback) {
const currTime = new Date().getTime();
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
const id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
export default function getRequestAnimationFrame() {
if (typeof window === 'undefined') {
return () => {};
}
if (window.requestAnimationFrame) {
// https://github.com/vuejs/vue/issues/4465
return window.requestAnimationFrame.bind(window);
}
const prefix = availablePrefixs.filter(key => `${key}RequestAnimationFrame` in window)[0];
return prefix
? window[`${prefix}RequestAnimationFrame`]
: requestAnimationFramePolyfill();
}
export function cancelRequestAnimationFrame(id) {
if (typeof window === 'undefined') {
return null;
}
if (window.cancelAnimationFrame) {
return window.cancelAnimationFrame(id);
}
const prefix = availablePrefixs.filter(key =>
`${key}CancelAnimationFrame` in window || `${key}CancelRequestAnimationFrame` in window,
)[0];
return prefix ?
(window[`${prefix}CancelAnimationFrame`] || window[`${prefix}CancelRequestAnimationFrame`]).call(this, id)
: clearTimeout(id);
}

View File

@@ -1,4 +1,4 @@
export default function getScroll(target: HTMLElement | Window | null, top: boolean): number {
export default function getScroll(target, top): number {
if (typeof window === 'undefined') {
return 0;
}
@@ -7,10 +7,10 @@ export default function getScroll(target: HTMLElement | Window | null, top: bool
const method = top ? 'scrollTop' : 'scrollLeft';
const isWindow = target === window;
let ret = isWindow ? (target as Window)[prop] : (target as HTMLElement)[method];
let ret = isWindow ? target[prop] : target[method];
// ie6,7,8 standard mode
if (isWindow && typeof ret !== 'number') {
ret = (document.documentElement as HTMLElement)[method];
ret = window.document.documentElement[method];
}
return ret;

View File

@@ -1,4 +0,0 @@
// https://github.com/moment/moment/issues/3650
export default function interopDefault(m: any) {
return m.default || m;
}

View File

@@ -0,0 +1,24 @@
let animation;
function isCssAnimationSupported() {
if (animation !== undefined) {
return animation;
}
const domPrefixes = 'Webkit Moz O ms Khtml'.split(' ');
const elm = document.createElement('div');
if (elm.style.animationName !== undefined) {
animation = true;
}
if (animation !== undefined) {
for (let i = 0; i < domPrefixes.length; i++) {
if (elm.style[`${domPrefixes[i]}AnimationName`] !== undefined) {
animation = true;
break;
}
}
}
animation = animation || false;
return animation;
}
export default isCssAnimationSupported;

View File

@@ -0,0 +1,10 @@
export default function isFlexSupported() {
if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
const { documentElement } = window.document;
return 'flex' in documentElement.style ||
'webkitFlex' in documentElement.style ||
'Flex' in documentElement.style ||
'msFlex' in documentElement.style;
}
return false;
}

View File

@@ -1,5 +0,0 @@
const isNumeric = (value: any): boolean => {
return !isNaN(parseFloat(value)) && isFinite(value);
};
export default isNumeric;

View File

@@ -1,32 +1,34 @@
import cssAnimation from 'css-animation';
import raf from 'raf';
import getRequestAnimationFrame, { cancelRequestAnimationFrame } from './getRequestAnimationFrame';
function animate(node: HTMLElement, show: boolean, done: () => void) {
let height: number;
let requestAnimationFrameId: number;
const reqAnimFrame = getRequestAnimationFrame();
function animate(node, show, done) {
let height;
let requestAnimationFrameId;
return cssAnimation(node, 'ant-motion-collapse', {
start() {
if (!show) {
node.style.height = `${node.offsetHeight}px`;
node.style.opacity = '1';
node.style.opacity = 1;
} else {
height = node.offsetHeight;
node.style.height = '0px';
node.style.opacity = '0';
node.style.height = 0;
node.style.opacity = 0;
}
},
active() {
if (requestAnimationFrameId) {
raf.cancel(requestAnimationFrameId);
cancelRequestAnimationFrame(requestAnimationFrameId);
}
requestAnimationFrameId = raf(() => {
requestAnimationFrameId = reqAnimFrame(() => {
node.style.height = `${show ? height : 0}px`;
node.style.opacity = show ? '1' : '0';
node.style.opacity = show ? 1 : 0;
});
},
end() {
if (requestAnimationFrameId) {
raf.cancel(requestAnimationFrameId);
cancelRequestAnimationFrame(requestAnimationFrameId);
}
node.style.height = '';
node.style.opacity = '';
@@ -36,13 +38,13 @@ function animate(node: HTMLElement, show: boolean, done: () => void) {
}
const animation = {
enter(node: HTMLElement, done: () => void) {
enter(node, done) {
return animate(node, true, done);
},
leave(node: HTMLElement, done: () => void) {
leave(node, done) {
return animate(node, false, done);
},
appear(node: HTMLElement, done: () => void) {
appear(node, done) {
return animate(node, true, done);
},
};

View File

@@ -1,34 +0,0 @@
import raf from 'raf';
interface RafMap {
[id: number]: number;
}
let id: number = 0;
const ids: RafMap = {};
// Support call raf with delay specified frame
export default function wrapperRaf(callback: () => void, delayFrames: number = 1): number {
const myId: number = id++;
let restFrames: number = delayFrames;
function internalCallback() {
restFrames -= 1;
if (restFrames <= 0) {
callback();
delete ids[id];
} else {
ids[id] = raf(internalCallback);
}
}
ids[id] = raf(internalCallback);
return myId;
}
wrapperRaf.cancel = function(pid: number) {
raf.cancel(ids[pid]);
delete ids[pid];
};

View File

@@ -1,13 +0,0 @@
function isStyleSupport(styleName: string | Array<string>): boolean {
if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
const { documentElement } = window.document;
return styleNameList.some(name => name in documentElement.style);
}
return false;
}
export const isFlexSupported = isStyleSupport(['flex', 'webkitFlex', 'Flex', 'msFlex']);
export default isStyleSupport;

View File

@@ -1,27 +1,29 @@
import raf from 'raf';
import getRequestAnimationFrame, { cancelRequestAnimationFrame } from '../_util/getRequestAnimationFrame';
export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
let requestId: number | null;
const reqAnimFrame = getRequestAnimationFrame();
const later = (args: any[]) => () => {
export default function throttleByAnimationFrame(fn) {
let requestId;
const later = args => () => {
requestId = null;
fn(...args);
};
const throttled = (...args: any[]) => {
const throttled = (...args) => {
if (requestId == null) {
requestId = raf(later(args));
requestId = reqAnimFrame(later(args));
}
};
(throttled as any).cancel = () => raf.cancel(requestId!);
(throttled as any).cancel = () => cancelRequestAnimationFrame(requestId);
return throttled;
}
export function throttleByAnimationFrameDecorator() {
return function(target: any, key: string, descriptor: any) {
const fn = descriptor.value;
return function(target, key, descriptor) {
let fn = descriptor.value;
let definingProperty = false;
return {
configurable: true,
@@ -30,7 +32,7 @@ export function throttleByAnimationFrameDecorator() {
return fn;
}
const boundFn = throttleByAnimationFrame(fn.bind(this));
let boundFn = throttleByAnimationFrame(fn.bind(this));
definingProperty = true;
Object.defineProperty(this, key, {
value: boundFn,

View File

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

View File

@@ -1,3 +0,0 @@
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// https://stackoverflow.com/questions/46176165/ways-to-get-string-literal-type-of-array-values-without-enum-overhead
export const tuple = <T extends string[]>(...args: T) => args;

View File

@@ -1,6 +1,6 @@
import warning from 'warning';
const warned: Record<string, boolean> = {};
const warned: { [msg: string]: boolean} = {};
export default (valid: boolean, message: string): void => {
if (!valid && !warned[message]) {
warning(false, message);

View File

@@ -1,188 +0,0 @@
import * as React from 'react';
import { findDOMNode } from 'react-dom';
import TransitionEvents from 'css-animation/lib/Event';
import raf from '../_util/raf';
import { ConfigConsumer, ConfigConsumerProps, CSPConfig } from '../config-provider';
let styleForPesudo: HTMLStyleElement | null;
// Where el is the DOM element you'd like to test for visibility
function isHidden(element: HTMLElement) {
if (process.env.NODE_ENV === 'test') {
return false;
}
return !element || element.offsetParent === null;
}
export default class Wave extends React.Component<{ insertExtraNode?: boolean }> {
private instance?: {
cancel: () => void;
};
private extraNode: HTMLDivElement;
private clickWaveTimeoutId: number;
private animationStartId: number;
private animationStart: boolean = false;
private destroy: boolean = false;
private csp?: CSPConfig;
isNotGrey(color: string) {
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]);
}
return true;
}
onClick = (node: HTMLElement, waveColor: string) => {
if (!node || isHidden(node) || node.className.indexOf('-leave') >= 0) {
return;
}
const { insertExtraNode } = this.props;
this.extraNode = document.createElement('div');
const extraNode = this.extraNode;
extraNode.className = 'ant-click-animating-node';
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'true');
// Not white or transparnt or grey
styleForPesudo = styleForPesudo || document.createElement('style');
if (
waveColor &&
waveColor !== '#ffffff' &&
waveColor !== 'rgb(255, 255, 255)' &&
this.isNotGrey(waveColor) &&
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
waveColor !== 'transparent'
) {
// Add nonce if CSP exist
if (this.csp && this.csp.nonce) {
styleForPesudo.nonce = this.csp.nonce;
}
extraNode.style.borderColor = waveColor;
styleForPesudo.innerHTML = `[ant-click-animating-without-extra-node="true"]:after { border-color: ${waveColor}; }`;
if (!document.body.contains(styleForPesudo)) {
document.body.appendChild(styleForPesudo);
}
}
if (insertExtraNode) {
node.appendChild(extraNode);
}
TransitionEvents.addStartEventListener(node, this.onTransitionStart);
TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
};
bindAnimationEvent = (node: HTMLElement) => {
if (
!node ||
!node.getAttribute ||
node.getAttribute('disabled') ||
node.className.indexOf('disabled') >= 0
) {
return;
}
const onClick = (e: MouseEvent) => {
// Fix radio button click twice
if ((e.target as HTMLElement).tagName === 'INPUT' || isHidden(e.target as HTMLElement)) {
return;
}
this.resetEffect(node);
// Get wave color from target
const waveColor =
getComputedStyle(node).getPropertyValue('border-top-color') || // Firefox Compatible
getComputedStyle(node).getPropertyValue('border-color') ||
getComputedStyle(node).getPropertyValue('background-color');
this.clickWaveTimeoutId = window.setTimeout(() => this.onClick(node, waveColor), 0);
raf.cancel(this.animationStartId);
this.animationStart = true;
// Render to trigger transition event cost 3 frames. Let's delay 10 frames to reset this.
this.animationStartId = raf(() => {
this.animationStart = false;
}, 10);
};
node.addEventListener('click', onClick, true);
return {
cancel: () => {
node.removeEventListener('click', onClick, true);
},
};
};
getAttributeName() {
const { insertExtraNode } = this.props;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
}
resetEffect(node: HTMLElement) {
if (!node || node === this.extraNode || !(node instanceof Element)) {
return;
}
const { insertExtraNode } = this.props;
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'false'); // edge has bug on `removeAttribute` #14466
this.removeExtraStyleNode();
if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
node.removeChild(this.extraNode);
}
TransitionEvents.removeStartEventListener(node, this.onTransitionStart);
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
}
onTransitionStart = (e: AnimationEvent) => {
if (this.destroy) return;
const node = findDOMNode(this) as HTMLElement;
if (!e || e.target !== node) {
return;
}
if (!this.animationStart) {
this.resetEffect(node);
}
};
onTransitionEnd = (e: AnimationEvent) => {
if (!e || e.animationName !== 'fadeEffect') {
return;
}
this.resetEffect(e.target as HTMLElement);
};
removeExtraStyleNode() {
if (styleForPesudo) {
styleForPesudo.innerHTML = '';
}
}
componentDidMount() {
const node = findDOMNode(this) as HTMLElement;
if (node.nodeType !== 1) {
return;
}
this.instance = this.bindAnimationEvent(node);
}
componentWillUnmount() {
if (this.instance) {
this.instance.cancel();
}
if (this.clickWaveTimeoutId) {
clearTimeout(this.clickWaveTimeoutId);
}
this.destroy = true;
}
renderWave = ({ csp }: ConfigConsumerProps) => {
const { children } = this.props;
this.csp = csp;
return children;
};
render() {
return <ConfigConsumer>{this.renderWave}</ConfigConsumer>;
}
}

View File

@@ -7,13 +7,14 @@ const events = {};
class AffixMounter extends React.Component {
componentDidMount() {
this.container.scrollTop = 100;
this.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
events[event] = cb;
});
}
getTarget = () => this.container;
getTarget = () => {
return this.container;
}
render() {
return (
<div
@@ -21,9 +22,7 @@ class AffixMounter extends React.Component {
height: 100,
overflowY: 'scroll',
}}
ref={node => {
this.container = node;
}}
ref={(node) => { this.container = node; }}
>
<div
className="background"
@@ -34,12 +33,11 @@ class AffixMounter extends React.Component {
>
<Affix
target={() => this.container}
ref={ele => {
this.affix = ele;
}}
{...this.props}
ref={ele => this.affix = ele}
>
<Button type="primary">Fixed at the top of container</Button>
<Button type="primary" >
Fixed at the top of container
</Button>
</Affix>
</div>
</div>
@@ -48,8 +46,6 @@ class AffixMounter extends React.Component {
}
describe('Affix Render', () => {
let wrapper;
beforeAll(() => {
jest.useFakeTimers();
});
@@ -58,70 +54,23 @@ describe('Affix Render', () => {
jest.useRealTimers();
});
const scrollTo = top => {
wrapper.instance().affix.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => ({
bottom: 100,
height: 28,
left: 0,
right: 0,
top: 50 - top,
width: 195,
}));
wrapper.instance().container.scrollTop = top;
events.scroll({
type: 'scroll',
});
jest.runAllTimers();
};
it('Anchor render perfectly', () => {
document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
const wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
jest.runAllTimers();
scrollTo(0);
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
scrollTo(100);
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
scrollTo(0);
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
});
it('support offsetBottom', () => {
document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter offsetBottom={0} />, {
attachTo: document.getElementById('mounter'),
wrapper.node.affix.refs.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => {
return {
bottom: 100, height: 28, left: 0, right: 0, top: -50, width: 195,
};
});
jest.runAllTimers();
scrollTo(0);
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
scrollTo(100);
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
scrollTo(0);
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
});
it('updatePosition when offsetTop changed', () => {
document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter offsetTop={0} />, {
attachTo: document.getElementById('mounter'),
events.scroll({
type: 'scroll',
});
jest.runAllTimers();
scrollTo(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);
expect(wrapper.node.affix.state.affixStyle).not.toBe(null);
});
});

View File

@@ -16,44 +16,16 @@ The simplest usage.
````jsx
import { Affix, Button } from 'antd';
class Demo extends React.Component {
state = {
top: 10,
bottom: 10,
}
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);
ReactDOM.render(
<div>
<Affix>
<Button type="primary">Affix top</Button>
</Affix>
<br />
<Affix offsetBottom={0}>
<Button type="primary">Affix bottom</Button>
</Affix>
</div>,
mountNode
);
````

View File

@@ -1,21 +1,20 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import classNames from 'classnames';
import shallowequal from 'shallowequal';
import omit from 'omit.js';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getScroll from '../_util/getScroll';
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
function getTargetRect(target: HTMLElement | Window | null): ClientRect {
return target !== window
? (target as HTMLElement).getBoundingClientRect()
: ({ top: 0, left: 0, bottom: 0 } as ClientRect);
function getTargetRect(target): ClientRect {
return target !== window ?
target.getBoundingClientRect() :
{ top: 0, left: 0, bottom: 0 };
}
function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
function getOffset(element: HTMLElement, target) {
const elemRect = element.getBoundingClientRect();
const targetRect = getTargetRect(target);
@@ -27,8 +26,10 @@ function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
const clientLeft = docElem.clientLeft || 0;
return {
top: elemRect.top - targetRect.top + scrollTop - clientTop,
left: elemRect.left - targetRect.left + scrollLeft - clientLeft,
top: elemRect.top - targetRect.top +
scrollTop - clientTop,
left: elemRect.left - targetRect.left +
scrollLeft - clientLeft,
width: elemRect.width,
height: elemRect.height,
};
@@ -37,7 +38,8 @@ function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
function noop() {}
function getDefaultTarget() {
return typeof window !== 'undefined' ? window : null;
return typeof window !== 'undefined' ?
window : null;
}
// Affix
@@ -53,33 +55,25 @@ export interface AffixProps {
/** 固定状态改变时触发的回调函数 */
onChange?: (affixed?: boolean) => void;
/** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
target?: () => Window | HTMLElement | null;
target?: () => Window | HTMLElement;
prefixCls?: string;
className?: string;
}
export interface AffixState {
affixStyle: React.CSSProperties | undefined;
placeholderStyle: React.CSSProperties | undefined;
}
export default class Affix extends React.Component<AffixProps, AffixState> {
export default class Affix extends React.Component<AffixProps, any> {
static propTypes = {
offsetTop: PropTypes.number,
offsetBottom: PropTypes.number,
target: PropTypes.func,
};
state: AffixState = {
affixStyle: undefined,
placeholderStyle: undefined,
scrollEvent: any;
resizeEvent: any;
timeout: any;
refs: {
fixedNode: HTMLElement;
};
private timeout: number;
private eventHandlers: Record<string, any> = {};
private fixedNode: HTMLElement;
private placeholderNode: HTMLElement;
private readonly events = [
events = [
'resize',
'scroll',
'touchstart',
@@ -89,7 +83,17 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
'load',
];
setAffixStyle(e: Event, affixStyle: React.CSSProperties | null) {
eventHandlers = {};
constructor(props) {
super(props);
this.state = {
affixStyle: null,
placeholderStyle: null,
};
}
setAffixStyle(e, affixStyle) {
const { onChange = noop, target = getDefaultTarget } = this.props;
const originalAffixStyle = this.state.affixStyle;
const isWindow = target() === window;
@@ -99,54 +103,36 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
if (shallowequal(affixStyle, originalAffixStyle)) {
return;
}
this.setState({ affixStyle: affixStyle as React.CSSProperties }, () => {
this.setState({ affixStyle }, () => {
const affixed = !!this.state.affixStyle;
if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
if ((affixStyle && !originalAffixStyle) ||
(!affixStyle && originalAffixStyle)) {
onChange(affixed);
}
});
}
setPlaceholderStyle(placeholderStyle: React.CSSProperties | null) {
setPlaceholderStyle(placeholderStyle) {
const originalPlaceholderStyle = this.state.placeholderStyle;
if (shallowequal(placeholderStyle, originalPlaceholderStyle)) {
return;
}
this.setState({ placeholderStyle: placeholderStyle as React.CSSProperties });
}
syncPlaceholderStyle(e: Event) {
const { affixStyle } = this.state;
if (!affixStyle) {
return;
}
this.placeholderNode.style.cssText = '';
this.setAffixStyle(e, {
...affixStyle,
width: this.placeholderNode.offsetWidth,
});
this.setPlaceholderStyle({
width: this.placeholderNode.offsetWidth,
});
this.setState({ placeholderStyle });
}
@throttleByAnimationFrameDecorator()
updatePosition(e: Event) {
const { offsetBottom, offset, target = getDefaultTarget } = this.props;
let { offsetTop } = this.props;
updatePosition(e) {
let { offsetTop, offsetBottom, offset, target = getDefaultTarget } = this.props;
const targetNode = target();
// Backwards support
// Fix: if offsetTop === 0, it will get undefined,
// if offsetBottom is type of number, offsetMode will be { top: false, ... }
offsetTop = typeof offsetTop === 'undefined' ? offset : offsetTop;
offsetTop = offsetTop || offset;
const scrollTop = getScroll(targetNode, true);
const affixNode = ReactDOM.findDOMNode(this) as HTMLElement;
const elemOffset = getOffset(affixNode, targetNode);
const elemSize = {
width: this.fixedNode.offsetWidth,
height: this.fixedNode.offsetHeight,
width: this.refs.fixedNode.offsetWidth,
height: this.refs.fixedNode.offsetHeight,
};
const offsetMode = {
@@ -165,14 +151,12 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
const targetRect = getTargetRect(targetNode);
const targetInnerHeight =
(targetNode as Window).innerHeight || (targetNode as HTMLElement).clientHeight;
// ref: https://github.com/ant-design/ant-design/issues/13662
if (scrollTop >= elemOffset.top - (offsetTop as number) && offsetMode.top) {
if (scrollTop > elemOffset.top - (offsetTop as number) && offsetMode.top) {
// Fixed Top
const width = elemOffset.width;
const top = targetRect.top + (offsetTop as number);
this.setAffixStyle(e, {
position: 'fixed',
top,
top: targetRect.top + (offsetTop as number),
left: targetRect.left + elemOffset.left,
width,
});
@@ -181,12 +165,11 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
height: elemSize.height,
});
} else if (
scrollTop <=
elemOffset.top + elemSize.height + (offsetBottom as number) - targetInnerHeight &&
offsetMode.bottom
scrollTop < elemOffset.top + elemSize.height + (offsetBottom as number) - targetInnerHeight &&
offsetMode.bottom
) {
// Fixed Bottom
const targetBottomOffet = targetNode === window ? 0 : window.innerHeight - targetRect.bottom;
const targetBottomOffet = targetNode === window ? 0 : (window.innerHeight - targetRect.bottom);
const width = elemOffset.width;
this.setAffixStyle(e, {
position: 'fixed',
@@ -200,22 +183,13 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
});
} else {
const { affixStyle } = this.state;
if (
e.type === 'resize' &&
affixStyle &&
affixStyle.position === 'fixed' &&
affixNode.offsetWidth
) {
if (e.type === 'resize' && affixStyle && affixStyle.position === 'fixed' && affixNode.offsetWidth) {
this.setAffixStyle(e, { ...affixStyle, width: affixNode.offsetWidth });
} else {
this.setAffixStyle(e, null);
}
this.setPlaceholderStyle(null);
}
if (e.type === 'resize') {
this.syncPlaceholderStyle(e);
}
}
componentDidMount() {
@@ -223,24 +197,16 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
// Wait for parent component ref has its value
this.timeout = setTimeout(() => {
this.setTargetEventListeners(target);
// Mock Event object.
this.updatePosition({} as Event);
});
}
componentWillReceiveProps(nextProps: AffixProps) {
componentWillReceiveProps(nextProps) {
if (this.props.target !== nextProps.target) {
this.clearEventListeners();
this.setTargetEventListeners(nextProps.target!);
this.setTargetEventListeners(nextProps.target);
// Mock Event object.
this.updatePosition({} as Event);
}
if (
this.props.offsetTop !== nextProps.offsetTop ||
this.props.offsetBottom !== nextProps.offsetBottom
) {
this.updatePosition({} as Event);
this.updatePosition({});
}
}
@@ -250,7 +216,7 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
(this.updatePosition as any).cancel();
}
setTargetEventListeners(getTarget: () => HTMLElement | Window | null) {
setTargetEventListeners(getTarget) {
const target = getTarget();
if (!target) {
return;
@@ -271,38 +237,19 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
});
}
saveFixedNode = (node: HTMLDivElement) => {
this.fixedNode = node;
};
savePlaceholderNode = (node: HTMLDivElement) => {
this.placeholderNode = node;
};
renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls } = this.props;
render() {
const className = classNames({
[getPrefixCls('affix', prefixCls)]: this.state.affixStyle,
[this.props.prefixCls || 'ant-affix']: this.state.affixStyle,
});
const props = omit(this.props, [
'prefixCls',
'offsetTop',
'offsetBottom',
'target',
'onChange',
]);
const props = omit(this.props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target', 'onChange']);
const placeholderStyle = { ...this.state.placeholderStyle, ...this.props.style };
return (
<div {...props} style={placeholderStyle} ref={this.savePlaceholderNode}>
<div className={className} ref={this.saveFixedNode} style={this.state.affixStyle}>
<div {...props} style={placeholderStyle}>
<div className={className} ref="fixedNode" style={this.state.affixStyle}>
{this.props.children}
</div>
</div>
);
};
render() {
return <ConfigConsumer>{this.renderAffix}</ConfigConsumer>;
}
}

View File

@@ -1,7 +1,7 @@
---
category: Components
subtitle: 固钉
type: 导航
type: Navigation
title: Affix
---

View File

@@ -1,4 +1,4 @@
@import '../../style/themes/default';
@import "../../style/themes/default";
.@{ant-prefix}-affix {
position: fixed;

View File

@@ -7,23 +7,8 @@ exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="exclamation-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
/>
</svg>
</i>
/>
<span
class="ant-alert-message"
>
@@ -35,27 +20,12 @@ exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
</div>
<br />
<div
class="ant-alert ant-alert-warning ant-alert-banner ant-alert-closable"
class="ant-alert ant-alert-warning ant-alert-banner"
data-show="true"
>
<i
aria-label="icon: exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="exclamation-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
/>
</svg>
</i>
/>
<span
class="ant-alert-message"
>
@@ -68,23 +38,8 @@ exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
class="ant-alert-close-icon"
>
<i
aria-label="icon: close"
class="anticon anticon-close"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
class="anticon anticon-cross"
/>
</a>
</div>
<br />
@@ -107,23 +62,8 @@ exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: close-circle"
class="anticon anticon-close-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="close-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</i>
class="anticon anticon-cross-circle ant-alert-icon"
/>
<span
class="ant-alert-message"
>
@@ -155,7 +95,7 @@ exports[`renders ./components/alert/demo/basic.md correctly 1`] = `
exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
<div>
<div
class="ant-alert ant-alert-warning ant-alert-no-icon ant-alert-closable"
class="ant-alert ant-alert-warning ant-alert-no-icon"
data-show="true"
>
<span
@@ -170,27 +110,12 @@ exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
class="ant-alert-close-icon"
>
<i
aria-label="icon: close"
class="anticon anticon-close"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
class="anticon anticon-cross"
/>
</a>
</div>
<div
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon ant-alert-closable"
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon"
data-show="true"
>
<span
@@ -207,23 +132,8 @@ exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
class="ant-alert-close-icon"
>
<i
aria-label="icon: close"
class="anticon anticon-close"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
class="anticon anticon-cross"
/>
</a>
</div>
</div>
@@ -231,7 +141,7 @@ exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
exports[`renders ./components/alert/demo/close-text.md correctly 1`] = `
<div
class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-closable"
class="ant-alert ant-alert-info ant-alert-no-icon"
data-show="true"
>
<span
@@ -250,280 +160,6 @@ exports[`renders ./components/alert/demo/close-text.md correctly 1`] = `
</div>
`;
exports[`renders ./components/alert/demo/custom-icon.md correctly 1`] = `
<div>
<div
class="ant-alert ant-alert-success ant-alert-no-icon"
data-show="true"
>
<span
class="ant-alert-message"
>
showIcon = false
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-success"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Success Tips
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-info"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Informational Notes
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-warning"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Warning
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-error"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Error
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-success ant-alert-with-description"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Success Tips
</span>
<span
class="ant-alert-description"
>
Detailed description and advices about successful copywriting.
</span>
</div>
<div
class="ant-alert ant-alert-info ant-alert-with-description"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Informational Notes
</span>
<span
class="ant-alert-description"
>
Additional description and informations about copywriting.
</span>
</div>
<div
class="ant-alert ant-alert-warning ant-alert-with-description"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Warning
</span>
<span
class="ant-alert-description"
>
This is a warning notice about copywriting.
</span>
</div>
<div
class="ant-alert ant-alert-error ant-alert-with-description"
data-show="true"
>
<i
aria-label="icon: smile"
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Error
</span>
<span
class="ant-alert-description"
>
This is an error message about copywriting.
</span>
</div>
</div>
`;
exports[`renders ./components/alert/demo/description.md correctly 1`] = `
<div>
<div
@@ -596,23 +232,8 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: check-circle"
class="anticon anticon-check-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="check-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"
/>
</svg>
</i>
/>
<span
class="ant-alert-message"
>
@@ -627,23 +248,8 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: info-circle"
class="anticon anticon-info-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="info-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
/>
</svg>
</i>
/>
<span
class="ant-alert-message"
>
@@ -658,23 +264,8 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="exclamation-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"
/>
</svg>
</i>
/>
<span
class="ant-alert-message"
>
@@ -689,23 +280,8 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: close-circle"
class="anticon anticon-close-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="close-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</i>
class="anticon anticon-cross-circle ant-alert-icon"
/>
<span
class="ant-alert-message"
>
@@ -720,30 +296,12 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: check-circle"
class="anticon anticon-check-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="check-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0 0 51.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"
/>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
</svg>
</i>
class="anticon anticon-check-circle-o ant-alert-icon"
/>
<span
class="ant-alert-message"
>
Success Tips
success tips
</span>
<span
class="ant-alert-description"
@@ -756,26 +314,8 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: info-circle"
class="anticon anticon-info-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="info-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
class="anticon anticon-info-circle-o ant-alert-icon"
/>
<span
class="ant-alert-message"
>
@@ -792,26 +332,8 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: exclamation-circle"
class="anticon anticon-exclamation-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="exclamation-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M464 688a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"
/>
</svg>
</i>
class="anticon anticon-exclamation-circle-o ant-alert-icon"
/>
<span
class="ant-alert-message"
>
@@ -828,26 +350,8 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
data-show="true"
>
<i
aria-label="icon: close-circle"
class="anticon anticon-close-circle ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="close-circle"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 0 0-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
/>
<path
d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
</svg>
</i>
class="anticon anticon-cross-circle-o ant-alert-icon"
/>
<span
class="ant-alert-message"
>
@@ -862,49 +366,6 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
</div>
`;
exports[`renders ./components/alert/demo/smooth-closed.md correctly 1`] = `
<div>
<div
class="ant-alert ant-alert-success ant-alert-no-icon ant-alert-closable"
data-show="true"
>
<span
class="ant-alert-message"
>
Alert Message Text
</span>
<span
class="ant-alert-description"
/>
<a
class="ant-alert-close-icon"
>
<i
aria-label="icon: close"
class="anticon anticon-close"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
</a>
</div>
<p>
placeholder text here
</p>
</div>
`;
exports[`renders ./components/alert/demo/style.md correctly 1`] = `
<div>
<div

View File

@@ -1,52 +0,0 @@
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).toBeCalled();
jest.runAllTimers();
expect(afterClose).toBeCalled();
});
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');
});
});
});

View File

@@ -26,7 +26,6 @@ ReactDOM.render(
<Alert showIcon={false} message="Warning text without icon" banner />
<br />
<Alert type="error" message="Error text" banner />
</div>,
mountNode
);
</div>
, mountNode);
````

View File

@@ -17,9 +17,8 @@ The simplest usage for short messages.
import { Alert } from 'antd';
ReactDOM.render(
<Alert message="Success Text" type="success" />,
mountNode
);
<Alert message="Success Text" type="success" />
, mountNode);
````
<style>

View File

@@ -16,7 +16,7 @@ To show close button.
````jsx
import { Alert } from 'antd';
const onClose = (e) => {
const onClose = function (e) {
console.log(e, 'I was closed.');
};
@@ -35,7 +35,6 @@ ReactDOM.render(
closable
onClose={onClose}
/>
</div>,
mountNode
);
</div>
, mountNode);
````

View File

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

View File

@@ -1,60 +0,0 @@
---
order: 12
debug: true
title:
zh-CN: 自定义图标
en-US: Custom Icon
---
## zh-CN
可口的图标让信息类型更加醒目。
## en-US
Decent icon make information more clear and more friendly.
````jsx
import { Alert, Icon } from 'antd';
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 />
<Alert icon={icon} message="Warning" type="warning" showIcon />
<Alert icon={icon} message="Error" type="error" showIcon />
<Alert
icon={icon}
message="Success Tips"
description="Detailed description and advices about successful copywriting."
type="success"
showIcon
/>
<Alert
icon={icon}
message="Informational Notes"
description="Additional description and informations about copywriting."
type="info"
showIcon
/>
<Alert
icon={icon}
message="Warning"
description="This is a warning notice about copywriting."
type="warning"
showIcon
/>
<Alert
icon={icon}
message="Error"
description="This is an error message about copywriting."
type="error"
showIcon
/>
</div>,
mountNode
);
````

View File

@@ -38,7 +38,6 @@ ReactDOM.render(
description="Error Description Error Description Error Description Error Description"
type="error"
/>
</div>,
mountNode
);
</div>
, mountNode);
````

View File

@@ -23,7 +23,7 @@ ReactDOM.render(
<Alert message="Warning" type="warning" showIcon />
<Alert message="Error" type="error" showIcon />
<Alert
message="Success Tips"
message="success tips"
description="Detailed description and advices about successful copywriting."
type="success"
showIcon
@@ -46,7 +46,6 @@ ReactDOM.render(
type="error"
showIcon
/>
</div>,
mountNode
);
</div>
, mountNode);
````

View File

@@ -1,51 +0,0 @@
---
order: 7
title:
zh-CN: 平滑地卸载
en-US: Smoothly Unmount
---
## zh-CN
平滑、自然的卸载提示。
## en-US
Smoothly and unaffectedly unmount Alert.
````jsx
import { Alert } from 'antd';
class App extends React.Component {
state = {
visible: true,
}
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

@@ -22,7 +22,6 @@ ReactDOM.render(
<Alert message="Info Text" type="info" />
<Alert message="Warning Text" type="warning" />
<Alert message="Error Text" type="error" />
</div>,
mountNode
);
</div>
, mountNode);
````

View File

@@ -15,13 +15,11 @@ Alert component for feedback.
| 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 | - |
| onClose | Callback when Alert is closed | Function | - |

View File

@@ -1,12 +1,10 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import React from 'react';
import ReactDOM from 'react-dom';
import Animate from 'rc-animate';
import Icon, { ThemeType } from '../icon';
import Icon from '../icon';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
function noop() {}
function noop() { }
export interface AlertProps {
/**
@@ -23,32 +21,25 @@ export interface AlertProps {
description?: React.ReactNode;
/** Callback when close Alert */
onClose?: React.MouseEventHandler<HTMLAnchorElement>;
/** Trigger when animation ending of Alert */
afterClose?: () => void;
/** Whether to show icon */
showIcon?: boolean;
iconType?: string;
style?: React.CSSProperties;
prefixCls?: string;
className?: string;
banner?: boolean;
icon?: React.ReactNode;
}
export interface AlertState {
closing: boolean;
closed: boolean;
}
export default class Alert extends React.Component<AlertProps, AlertState> {
state: AlertState = {
closing: true,
closed: false,
};
export default class Alert extends React.Component<AlertProps, any> {
constructor(props: AlertProps) {
super(props);
this.state = {
closing: true,
closed: false,
};
}
handleClose = (e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();
const dom = ReactDOM.findDOMNode(this) as HTMLElement;
let dom = ReactDOM.findDOMNode(this) as HTMLElement;
dom.style.height = `${dom.offsetHeight}px`;
// Magic code
// 重复一次后才能正确设置 height
@@ -58,101 +49,66 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
closing: false,
});
(this.props.onClose || noop)(e);
};
}
animationEnd = () => {
this.setState({
closed: true,
closing: true,
});
(this.props.afterClose || noop)();
};
renderAlert = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
description,
prefixCls: customizePrefixCls,
message,
closeText,
banner,
className = '',
style,
icon,
}
render() {
let {
closable, description, type, prefixCls = 'ant-alert', message, closeText, showIcon, banner,
className = '', style,
} = this.props;
let { closable, type, showIcon, iconType } = this.props;
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';
// should we give a warning?
// warning(!iconType, `The property 'iconType' is deprecated. Use the property 'icon' instead.`);
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';
}
let iconType = '';
switch (type) {
case 'success':
iconType = 'check-circle';
break;
case 'info':
iconType = 'info-circle';
break;
case 'error':
iconType = 'cross-circle';
break;
case 'warning':
iconType = 'exclamation-circle';
break;
default:
iconType = 'default';
}
// use outline icon in alert with description
if (!!description) {
iconType += '-o';
}
let alertCls = classNames(prefixCls, {
[`${prefixCls}-${type}`]: true,
[`${prefixCls}-close`]: !this.state.closing,
[`${prefixCls}-with-description`]: !!description,
[`${prefixCls}-no-icon`]: !showIcon,
[`${prefixCls}-banner`]: !!banner,
}, className);
// closeable when closeText is assigned
if (closeText) {
closable = true;
}
const alertCls = classNames(
prefixCls,
`${prefixCls}-${type}`,
{
[`${prefixCls}-close`]: !this.state.closing,
[`${prefixCls}-with-description`]: !!description,
[`${prefixCls}-no-icon`]: !showIcon,
[`${prefixCls}-banner`]: !!banner,
[`${prefixCls}-closable`]: closable,
},
className,
);
const closeIcon = closable ? (
<a onClick={this.handleClose} className={`${prefixCls}-close-icon`}>
{closeText || <Icon type="close" />}
{closeText || <Icon type="cross" />}
</a>
) : null;
const dataOrAriaProps = getDataOrAriaProps(this.props);
const iconNode = (icon &&
(React.isValidElement<{ className?: string }>(icon) ? (
React.cloneElement(icon, {
className: classNames({
[icon.props.className as string]: icon.props.className,
[`${prefixCls}-icon`]: true,
}),
})
) : (
<span className={`${prefixCls}-icon`}>{icon}</span>
))) || <Icon className={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />;
return this.state.closed ? null : (
<Animate
component=""
@@ -160,17 +116,13 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
transitionName={`${prefixCls}-slide-up`}
onEnd={this.animationEnd}
>
<div data-show={this.state.closing} className={alertCls} style={style} {...dataOrAriaProps}>
{showIcon ? iconNode : null}
<div data-show={this.state.closing} className={alertCls} style={style}>
{showIcon ? <Icon className={`${prefixCls}-icon`} type={iconType} /> : null}
<span className={`${prefixCls}-message`}>{message}</span>
<span className={`${prefixCls}-description`}>{description}</span>
{closeIcon}
</div>
</Animate>
);
};
render() {
return <ConfigConsumer>{this.renderAlert}</ConfigConsumer>;
}
}

View File

@@ -1,7 +1,7 @@
---
category: Components
subtitle: 警告提示
type: 反馈
type: Feedback
title: Alert
---
@@ -16,13 +16,11 @@ title: Alert
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| 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 | 无 |
| onClose | 关闭时触发的回调函数 | Function | 无 |

View File

@@ -1,85 +1,82 @@
@import '../../style/themes/default';
@import '../../style/mixins/index';
@import "../../style/themes/default";
@alert-prefix-cls: ~'@{ant-prefix}-alert';
@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;
padding: 8px 15px 8px 37px;
padding: 8px 48px 8px 38px;
border-radius: @border-radius-base;
color: @alert-text-color;
font-size: @font-size-base;
line-height: @line-height-base;
&&-no-icon {
padding: 8px 15px;
}
&&-closable {
padding-right: 30px;
padding: 8px 48px 8px 16px;
}
&-icon {
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2;
font-size: @font-size-lg;
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-lg / 2;
left: 16px;
position: absolute;
}
&-description {
font-size: @font-size-base;
line-height: 22px;
line-height: 21px;
display: none;
}
&-success {
border: @border-width-base @border-style-base @alert-success-border-color;
background-color: @alert-success-bg-color;
border: @border-width-base @border-style-base @green-2;
background-color: @green-1;
.@{alert-prefix-cls}-icon {
color: @alert-success-icon-color;
color: @success-color;
}
}
&-info {
border: @border-width-base @border-style-base @alert-info-border-color;
background-color: @alert-info-bg-color;
border: @border-width-base @border-style-base @primary-2;
background-color: @primary-1;
.@{alert-prefix-cls}-icon {
color: @alert-info-icon-color;
color: @info-color;
}
}
&-warning {
border: @border-width-base @border-style-base @alert-warning-border-color;
background-color: @alert-warning-bg-color;
border: @border-width-base @border-style-base @yellow-2;
background-color: @yellow-1;
.@{alert-prefix-cls}-icon {
color: @alert-warning-icon-color;
color: @warning-color;
}
}
&-error {
border: @border-width-base @border-style-base @alert-error-border-color;
background-color: @alert-error-bg-color;
border: @border-width-base @border-style-base @red-2;
background-color: @red-1;
.@{alert-prefix-cls}-icon {
color: @alert-error-icon-color;
color: @error-color;
}
}
&-close-icon {
font-size: @font-size-sm;
font-size: @font-size-base;
position: absolute;
right: 16px;
top: 8px;
line-height: 22px;
top: 10px;
height: 12px;
line-height: 12px;
overflow: hidden;
cursor: pointer;
.@{iconfont-css-prefix}-close {
color: @alert-close-color;
transition: color 0.3s;
.@{iconfont-css-prefix}-cross {
color: @text-color-secondary;
transition: color .3s ease;
&:hover {
color: @alert-close-hover-color;
color: #404040;
}
}
}
@@ -90,21 +87,21 @@
}
&-with-description {
padding: 15px 15px 15px 64px;
padding: 16px 16px 16px 60px;
position: relative;
border-radius: @border-radius-base;
color: @alert-text-color;
line-height: @line-height-base;
color: @text-color;
line-height: 1.5;
}
&-with-description&-no-icon {
padding: 15px;
padding: 16px;
}
&-with-description &-icon {
position: absolute;
top: 16px;
left: 24px;
left: 20px;
font-size: 24px;
}
@@ -132,12 +129,12 @@
margin: 0;
padding-top: 0;
padding-bottom: 0;
transition: all 0.3s @ease-in-out-circ;
transition: all .3s @ease-in-out-circ;
transform-origin: 50% 0;
}
&-slide-up-leave {
animation: antAlertSlideUpOut 0.3s @ease-in-out-circ;
animation: antAlertSlideUpOut .3s @ease-in-out-circ;
animation-fill-mode: both;
}

View File

@@ -1,19 +1,18 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import Affix from '../affix';
import AnchorLink from './AnchorLink';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getScroll from '../_util/getScroll';
import raf from 'raf';
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
function getDefaultContainer() {
function getDefaultTarget() {
return window;
}
function getOffsetTop(element: HTMLElement, container: AnchorContainer): number {
function getOffsetTop(element: HTMLElement): number {
if (!element) {
return 0;
}
@@ -25,11 +24,9 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
const rect = element.getBoundingClientRect();
if (rect.width || rect.height) {
if (container === window) {
container = element.ownerDocument!.documentElement!;
return rect.top - container.clientTop;
}
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
const doc = element.ownerDocument;
const docElem = doc.documentElement;
return rect.top - docElem.clientTop;
}
return rect.top;
@@ -39,56 +36,43 @@ function easeInOutCubic(t: number, b: number, c: number, d: number) {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
return cc / 2 * t * t * t + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
return cc / 2 * ((t -= 2) * t * t + 2) + b;
}
const reqAnimFrame = getRequestAnimationFrame();
const sharpMatcherRegx = /#([^#]+)$/;
function scrollTo(
href: string,
offsetTop = 0,
getContainer: () => AnchorContainer,
callback = () => {},
) {
const container = getContainer();
const scrollTop = getScroll(container, true);
function scrollTo(href: string, offsetTop = 0, target, callback = () => { }) {
const scrollTop = getScroll(target(), true);
const sharpLinkMatch = sharpMatcherRegx.exec(href);
if (!sharpLinkMatch) {
return;
}
if (!sharpLinkMatch) { return; }
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement, container);
const eleOffsetTop = getOffsetTop(targetElement);
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}
window.scrollTo(window.pageXOffset, easeInOutCubic(time, scrollTop, targetScrollTop, 450));
if (time < 450) {
raf(frameFunc);
reqAnimFrame(frameFunc);
} else {
callback();
}
};
raf(frameFunc);
reqAnimFrame(frameFunc);
history.pushState(null, '', href);
}
type Section = {
link: string;
link: String;
top: number;
};
export type AnchorContainer = HTMLElement | Window;
export interface AnchorProps {
prefixCls?: string;
className?: string;
@@ -98,83 +82,61 @@ export interface AnchorProps {
bounds?: number;
affix?: boolean;
showInkInFixed?: boolean;
getContainer?: () => AnchorContainer;
onClick?: (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => void;
target?: () => HTMLElement | Window;
}
export interface AnchorState {
activeLink: null | string;
}
export interface AnchorDefaultProps extends AnchorProps {
prefixCls: string;
affix: boolean;
showInkInFixed: boolean;
getContainer: () => AnchorContainer;
}
export interface AntAnchor {
registerLink: (link: string) => void;
unregisterLink: (link: string) => void;
activeLink: string | null;
scrollTo: (link: string) => void;
onClick?: (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => void;
}
export default class Anchor extends React.Component<AnchorProps, AnchorState> {
export default class Anchor extends React.Component<AnchorProps, any> {
static Link: typeof AnchorLink;
static defaultProps = {
prefixCls: 'ant-anchor',
affix: true,
showInkInFixed: false,
getContainer: getDefaultContainer,
};
static childContextTypes = {
antAnchor: PropTypes.object,
};
state = {
activeLink: null,
refs: {
ink?: any;
};
private inkNode: HTMLSpanElement;
private links: string[] = [];
private links: String[];
private scrollEvent: any;
private animating: boolean;
private prefixCls?: string;
constructor(props: AnchorProps) {
super(props);
this.state = {
activeLink: null,
};
this.links = [];
}
getChildContext() {
const antAnchor: AntAnchor = {
registerLink: (link: string) => {
if (!this.links.includes(link)) {
this.links.push(link);
}
return {
antAnchor: {
registerLink: (link: String) => {
if (!this.links.includes(link)) {
this.links.push(link);
}
},
unregisterLink: (link: String) => {
const index = this.links.indexOf(link);
if (index !== -1) {
this.links.splice(index, 1);
}
},
activeLink: this.state.activeLink,
scrollTo: this.handleScrollTo,
},
unregisterLink: (link: string) => {
const index = this.links.indexOf(link);
if (index !== -1) {
this.links.splice(index, 1);
}
},
activeLink: this.state.activeLink,
scrollTo: this.handleScrollTo,
onClick: this.props.onClick,
};
return { antAnchor };
}
componentDidMount() {
const { getContainer } = this.props as AnchorDefaultProps;
this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll);
const getTarget = this.props.target || getDefaultTarget;
this.scrollEvent = addEventListener(getTarget(), 'scroll', this.handleScroll);
this.handleScroll();
}
@@ -196,45 +158,39 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
this.setState({
activeLink: this.getCurrentAnchor(offsetTop, bounds),
});
};
}
handleScrollTo = (link: string) => {
const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
handleScrollTo = (link) => {
const { offsetTop, target = getDefaultTarget } = this.props;
this.animating = true;
this.setState({ activeLink: link });
scrollTo(link, offsetTop, getContainer, () => {
scrollTo(link, offsetTop, target, () => {
this.animating = false;
});
};
}
getCurrentAnchor(offsetTop = 0, bounds = 5): string {
const activeLink = '';
getCurrentAnchor(offsetTop = 0, bounds = 5) {
let activeLink = '';
if (typeof document === 'undefined') {
return activeLink;
}
const linkSections: Array<Section> = [];
const { getContainer } = this.props as AnchorDefaultProps;
const container = getContainer();
this.links.forEach(link => {
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
if (!sharpLinkMatch) {
return;
}
if (!sharpLinkMatch) { return; }
const target = document.getElementById(sharpLinkMatch[1]);
if (target) {
const top = getOffsetTop(target, container);
if (top < offsetTop + bounds) {
linkSections.push({
link,
top,
});
}
if (target && getOffsetTop(target) < offsetTop + bounds) {
const top = getOffsetTop(target);
linkSections.push({
link,
top,
});
}
});
if (linkSections.length) {
const maxSection = linkSections.reduce((prev, curr) => (curr.top > prev.top ? curr : prev));
const maxSection = linkSections.reduce((prev, curr) => curr.top > prev.top ? curr : prev);
return maxSection.link;
}
return '';
@@ -244,38 +200,25 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
if (typeof document === 'undefined') {
return;
}
const prefixCls = this.prefixCls;
const anchorNode = ReactDOM.findDOMNode(this) as Element;
const linkNode = anchorNode.getElementsByClassName(`${prefixCls}-link-title-active`)[0];
const { prefixCls } = this.props;
const linkNode = ReactDOM.findDOMNode(this as any).getElementsByClassName(`${prefixCls}-link-title-active`)[0];
if (linkNode) {
this.inkNode.style.top = `${(linkNode as any).offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
this.refs.ink.style.top = `${(linkNode as any).offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
}
};
}
saveInkNode = (node: HTMLSpanElement) => {
this.inkNode = node;
};
renderAnchor = ({ getPrefixCls }: ConfigConsumerProps) => {
render() {
const {
prefixCls: customizePrefixCls,
prefixCls,
className = '',
style,
offsetTop,
affix,
showInkInFixed,
children,
getContainer,
} = this.props;
const { activeLink } = this.state;
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
// To support old version react.
// Have to add prefixCls on the instance.
// https://github.com/facebook/react/issues/12397
this.prefixCls = prefixCls;
const inkClass = classNames(`${prefixCls}-ink-ball`, {
visible: activeLink,
});
@@ -283,35 +226,24 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
const wrapperClass = classNames(className, `${prefixCls}-wrapper`);
const anchorClass = classNames(prefixCls, {
fixed: !affix && !showInkInFixed,
'fixed': !affix && !showInkInFixed,
});
const wrapperStyle = {
maxHeight: offsetTop ? `calc(100vh - ${offsetTop}px)` : '100vh',
...style,
};
const anchorContent = (
<div className={wrapperClass} style={wrapperStyle}>
<div className={wrapperClass} style={style}>
<div className={anchorClass}>
<div className={`${prefixCls}-ink`}>
<span className={inkClass} ref={this.saveInkNode} />
<div className={`${prefixCls}-ink`} >
<span className={inkClass} ref="ink" />
</div>
{children}
</div>
</div>
);
return !affix ? (
anchorContent
) : (
<Affix offsetTop={offsetTop} target={getContainer}>
return !affix ? anchorContent : (
<Affix offsetTop={offsetTop}>
{anchorContent}
</Affix>
);
};
render() {
return <ConfigConsumer>{this.renderAnchor}</ConfigConsumer>;
}
}

View File

@@ -1,20 +1,17 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { polyfill } from 'react-lifecycles-compat';
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { AntAnchor } from './Anchor';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export interface AnchorLinkProps {
prefixCls?: string;
href: string;
title: React.ReactNode;
children?: any;
className?: string;
}
class AnchorLink extends React.Component<AnchorLinkProps, any> {
export default class AnchorLink extends React.Component<AnchorLinkProps, any> {
static defaultProps = {
prefixCls: 'ant-anchor',
href: '#',
};
@@ -23,39 +20,30 @@ class AnchorLink extends React.Component<AnchorLinkProps, any> {
};
context: {
antAnchor: AntAnchor;
antAnchor: any;
};
componentDidMount() {
this.context.antAnchor.registerLink(this.props.href);
}
componentDidUpdate({ href: prevHref }: AnchorLinkProps) {
const { href } = this.props;
if (prevHref !== href) {
this.context.antAnchor.unregisterLink(prevHref);
this.context.antAnchor.registerLink(href);
}
}
componentWillUnmount() {
this.context.antAnchor.unregisterLink(this.props.href);
}
handleClick = (e: React.MouseEvent<HTMLElement>) => {
const { scrollTo, onClick } = this.context.antAnchor;
const { href, title } = this.props;
if (onClick) {
onClick(e, { title, href });
}
scrollTo(href);
};
handleClick = () => {
this.context.antAnchor.scrollTo(this.props.href);
}
renderAnchorLink = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, href, title, children, className } = this.props;
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
render() {
const {
prefixCls,
href,
title,
children,
} = this.props;
const active = this.context.antAnchor.activeLink === href;
const wrapperClassName = classNames(className, `${prefixCls}-link`, {
const wrapperClassName = classNames(`${prefixCls}-link`, {
[`${prefixCls}-link-active`]: active,
});
const titleClassName = classNames(`${prefixCls}-link-title`, {
@@ -74,13 +62,5 @@ class AnchorLink extends React.Component<AnchorLinkProps, any> {
{children}
</div>
);
};
render() {
return <ConfigConsumer>{this.renderAnchorLink}</ConfigConsumer>;
}
}
polyfill(AnchorLink);
export default AnchorLink;

View File

@@ -9,26 +9,26 @@ describe('Anchor Render', () => {
const wrapper = mount(
<Anchor>
<Link href="#API" title="API" />
</Anchor>,
</Anchor>
);
wrapper.find('a[href="#API"]').simulate('click');
wrapper.instance().handleScroll();
expect(wrapper.instance().state).not.toBe(null);
wrapper.node.handleScroll();
expect(wrapper.node.state).not.toBe(null);
});
it('Anchor render perfectly for complete href - click', () => {
const wrapper = mount(
<Anchor>
<Link href="http://www.example.com/#API" title="API" />
</Anchor>,
</Anchor>
);
wrapper.find('a[href="http://www.example.com/#API"]').simulate('click');
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
expect(wrapper.node.state.activeLink).toBe('http://www.example.com/#API');
});
it('Anchor render perfectly for complete href - scroll', () => {
it('Anchor render perfectly for complete href - scoll', () => {
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
@@ -39,14 +39,13 @@ describe('Anchor Render', () => {
const wrapper = mount(
<Anchor>
<Link href="http://www.example.com/#API" title="API" />
</Anchor>,
</Anchor>
);
wrapper.instance().handleScroll();
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
wrapper.node.handleScroll();
expect(wrapper.node.state.activeLink).toBe('http://www.example.com/#API');
});
it('Anchor render perfectly for complete href - scrollTo', async () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo');
it('Anchor render perfectly for complete href - scollTo', () => {
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
@@ -57,77 +56,9 @@ describe('Anchor Render', () => {
const wrapper = mount(
<Anchor>
<Link href="##API" title="API" />
</Anchor>,
</Anchor>
);
wrapper.instance().handleScrollTo('##API');
expect(wrapper.instance().state.activeLink).toBe('##API');
expect(scrollToSpy).not.toHaveBeenCalled();
await new Promise(resolve => setTimeout(resolve, 1000));
expect(scrollToSpy).toHaveBeenCalled();
});
it('should remove listener when unmount', async () => {
const wrapper = mount(
<Anchor>
<Link href="#API" title="API" />
</Anchor>,
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
wrapper.unmount();
expect(removeListenerSpy).toHaveBeenCalled();
});
it('should unregister link when unmount children', async () => {
const wrapper = mount(
<Anchor>
<Link href="#API" title="API" />
</Anchor>,
);
expect(wrapper.instance().links).toEqual(['#API']);
wrapper.setProps({ children: null });
expect(wrapper.instance().links).toEqual([]);
});
it('should update links when link href update', async () => {
let anchorInstance = null;
function AnchorUpdate({ href }) {
return (
<Anchor
ref={c => {
anchorInstance = c;
}}
>
<Link href={href} title="API" />
</Anchor>
);
}
const wrapper = mount(<AnchorUpdate href="#API" />);
expect(anchorInstance.links).toEqual(['#API']);
wrapper.setProps({ href: '#API_1' });
expect(anchorInstance.links).toEqual(['#API_1']);
});
it('Anchor onClick event', () => {
let event;
let link;
const handleClick = (...arg) => {
[event, link] = arg;
};
const href = '#API';
const title = 'API';
const wrapper = mount(
<Anchor onClick={handleClick}>
<Link href={href} title={title} />
</Anchor>,
);
wrapper.find(`a[href="${href}"]`).simulate('click');
wrapper.instance().handleScroll();
expect(event).not.toBe(undefined);
expect(link).toEqual({ href, title });
wrapper.node.handleScrollTo('##API');
expect(wrapper.node.state.activeLink).toBe('##API');
});
});

View File

@@ -7,7 +7,6 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
>
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor"
@@ -35,10 +34,10 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
href="#components-anchor-demo-fixed"
title="Fixed demo"
>
Static demo
Fixed demo
</a>
</div>
<div
@@ -80,10 +79,9 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
</div>
`;
exports[`renders ./components/anchor/demo/onClick.md correctly 1`] = `
exports[`renders ./components/anchor/demo/fixed.md correctly 1`] = `
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor fixed"
@@ -111,84 +109,10 @@ exports[`renders ./components/anchor/demo/onClick.md correctly 1`] = `
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
href="#components-anchor-demo-fixed"
title="Fixed demo"
>
Static demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#API"
title="API"
>
API
</a>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Anchor-Props"
title="Anchor Props"
>
Anchor Props
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Link-Props"
title="Link Props"
>
Link Props
</a>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/anchor/demo/static.md correctly 1`] = `
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor fixed"
>
<div
class="ant-anchor-ink"
>
<span
class="ant-anchor-ink-ball"
/>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-basic"
title="Basic demo"
>
Basic demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
>
Static demo
Fixed demo
</a>
</div>
<div

View File

@@ -15,20 +15,18 @@ The simplest usage.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
ReactDOM.render(
<Anchor>
<Link href="#components-anchor-demo-basic" title="Basic demo" />
<Link href="#components-anchor-demo-static" title="Static demo" />
<Link href="#components-anchor-demo-fixed" title="Fixed demo" />
<Link href="#API" title="API">
<Link href="#Anchor-Props" title="Anchor Props" />
<Link href="#Link-Props" title="Link Props" />
</Link>
</Anchor>,
mountNode
);
</Anchor>
, mountNode);
```
<style>

View File

@@ -1,8 +1,8 @@
---
order: 2
title:
zh-CN: 静态位置
en-US: Static Anchor
zh-CN: 固定
en-US: Fixed Anchor
---
## zh-CN
@@ -15,18 +15,16 @@ Do not change state when page is scrolling.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
ReactDOM.render(
<Anchor affix={false}>
<Link href="#components-anchor-demo-basic" title="Basic demo" />
<Link href="#components-anchor-demo-static" title="Static demo" />
<Link href="#components-anchor-demo-fixed" title="Fixed demo" />
<Link href="#API" title="API">
<Link href="#Anchor-Props" title="Anchor Props" />
<Link href="#Link-Props" title="Link Props" />
</Link>
</Anchor>,
mountNode
);
</Anchor>
, mountNode);
```

View File

@@ -1,37 +0,0 @@
---
order: 3
title:
zh-CN: 自定义 onClick 事件
en-US: Customize the onClick event
---
## zh-CN
点击锚点不记录历史。
## en-US
Clicking on an anchor does not record history.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
const handleClick = (e, link) => {
e.preventDefault();
console.log(link);
};
ReactDOM.render(
<Anchor affix={false} onClick={handleClick}>
<Link href="#components-anchor-demo-basic" title="Basic demo" />
<Link href="#components-anchor-demo-static" title="Static demo" />
<Link href="#API" title="API">
<Link href="#Anchor-Props" title="Anchor Props" />
<Link href="#Link-Props" title="Link Props" />
</Link>
</Anchor>,
mountNode
);
```

View File

@@ -17,13 +17,11 @@ For displaying anchor hyperlinks on page and jumping between them.
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| affix | Fixed mode of Anchor | boolean | true |
| affix | Fixed mode of Anchor | boolean | false |
| bounds | Bounding distance of anchor area | number | 5(px) |
| getContainer | Scrolling container | () => HTMLElement | () => window |
| offsetBottom | Pixels to offset from bottom when calculating position of scroll | number | - |
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 |
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false |
| onClick | set the handler to handle `click` event | Function(e: Event, link: Object) | - |
### Link Props

View File

@@ -1,8 +1,5 @@
import Anchor from './Anchor';
import AnchorLink from './AnchorLink';
export { AnchorProps } from './Anchor';
export { AnchorLinkProps } from './AnchorLink';
Anchor.Link = AnchorLink;
export default Anchor;

View File

@@ -2,7 +2,7 @@
category: Components
subtitle: 锚点
cols: 2
type: 其他
type: Other
title: Anchor
---
@@ -18,13 +18,11 @@ title: Anchor
| 成员 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| affix | 固定模式 | boolean | true |
| affix | 固定模式 | boolean | false |
| bounds | 锚点区域边界 | number | 5(px) |
| getContainer | 指定滚动的容器 | () => HTMLElement | () => window |
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |
| onClick | `click` 事件的 handler | Function(e: Event, link: Object) | - |
### Link Props

View File

@@ -1,18 +1,9 @@
@import '../../style/themes/default';
@import '../../style/mixins/index';
@anchor-border-width: 2px;
@import "../../style/themes/default";
.@{ant-prefix}-anchor {
.reset-component;
position: relative;
padding-left: @anchor-border-width;
&-wrapper {
background-color: @component-background;
overflow: auto;
padding-left: 4px;
margin-left: -4px;
}
&-ink {
@@ -23,7 +14,7 @@
&:before {
content: ' ';
position: relative;
width: @anchor-border-width;
width: 2px;
height: 100%;
display: block;
background-color: @border-color-split;
@@ -32,13 +23,13 @@
&-ball {
display: none;
position: absolute;
width: 8px;
height: 8px;
border-radius: 8px;
border: 2px solid @primary-color;
width: 9px;
height: 9px;
border-radius: 9px;
border: 3px solid @primary-color;
background-color: @component-background;
left: 50%;
transition: top 0.3s ease-in-out;
transition: top .3s ease-in-out;
transform: translateX(-50%);
&.visible {
display: inline-block;
@@ -51,18 +42,18 @@
}
&-link {
padding: 7px 0 7px 16px;
line-height: 1.143;
padding: 8px 0 8px 18px;
line-height: 1;
&-title {
display: block;
position: relative;
transition: all 0.3s;
transition: all .3s;
color: @text-color;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 6px;
margin-bottom: 8px;
&:only-child {
margin-bottom: 0;
@@ -75,7 +66,7 @@
}
&-link &-link {
padding-top: 5px;
padding-bottom: 5px;
padding-top: 6px;
padding-bottom: 6px;
}
}

View File

@@ -1,36 +1,26 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import React from 'react';
import { findDOMNode } from 'react-dom';
export interface InputElementProps {
children: React.ReactElement<any>;
}
export default class InputElement extends React.Component<InputElementProps, any> {
export default class InputElement extends React.Component<any, any> {
private ele: HTMLInputElement;
focus = () => {
this.ele.focus
? this.ele.focus()
: (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus();
};
this.ele.focus ? this.ele.focus() : (findDOMNode(this.ele) as HTMLInputElement).focus();
}
blur = () => {
this.ele.blur ? this.ele.blur() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).blur();
};
this.ele.blur ? this.ele.blur() : (findDOMNode(this.ele) as HTMLInputElement).blur();
}
saveRef = (ele: HTMLInputElement) => {
this.ele = ele;
const { ref: childRef } = this.props.children as any;
const childRef = this.props.children.ref;
if (typeof childRef === 'function') {
childRef(ele);
}
};
}
render() {
return React.cloneElement(
this.props.children,
{
...this.props,
ref: this.saveRef,
},
null,
);
return React.cloneElement(this.props.children, {
...this.props,
ref: this.saveRef,
}, null);
}
}

View File

@@ -3,11 +3,10 @@
exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
<div
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px"
style="width:200px;"
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -19,8 +18,8 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
style="display:block;user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
input here
</div>
@@ -47,27 +46,10 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
<i
aria-label="icon: down"
class="anticon anticon-down ant-select-arrow-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</i>
<b />
</span>
</div>
</div>
@@ -76,15 +58,14 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1`] = `
<div
class="certain-category-search-wrapper"
style="width:250px"
style="width:250px;"
>
<div
class="ant-select-lg ant-select-lg certain-category-search ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:100%"
style="width:100%;"
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -96,8 +77,8 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
style="display:block;user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
input here
</div>
@@ -120,23 +101,8 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
class="ant-input-suffix"
>
<i
aria-label="icon: search"
class="anticon anticon-search certain-category-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="search"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</i>
/>
</span>
</span>
<span
@@ -150,27 +116,10 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
<i
aria-label="icon: down"
class="anticon anticon-down ant-select-arrow-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</i>
<b />
</span>
</div>
</div>
@@ -180,11 +129,10 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
<div
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px"
style="width:200px;"
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -204,7 +152,7 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
<textarea
class="ant-input custom ant-select-search__field"
placeholder="input here"
style="height:50px"
style="height:50px;"
/>
<span
class="ant-select-search__field__mirror"
@@ -217,27 +165,10 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
<i
aria-label="icon: down"
class="anticon anticon-down ant-select-arrow-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</i>
<b />
</span>
</div>
</div>
@@ -246,11 +177,10 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly 1`] = `
<div
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px"
style="width:200px;"
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -262,8 +192,8 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
style="display:block;user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
try to type \`b\`
</div>
@@ -290,27 +220,10 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
<i
aria-label="icon: down"
class="anticon anticon-down ant-select-arrow-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</i>
<b />
</span>
</div>
</div>
@@ -319,11 +232,10 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
<div
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px"
style="width:200px;"
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -335,8 +247,8 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
style="display:block;user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
input here
</div>
@@ -363,27 +275,10 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
<i
aria-label="icon: down"
class="anticon anticon-down ant-select-arrow-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</i>
<b />
</span>
</div>
</div>
@@ -392,15 +287,14 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly 1`] = `
<div
class="global-search-wrapper"
style="width:300px"
style="width:300px;"
>
<div
class="ant-select-lg ant-select-lg global-search ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:100%"
style="width:100%;"
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -412,8 +306,8 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
style="display:block;user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
input here
</div>
@@ -440,23 +334,8 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
type="button"
>
<i
aria-label="icon: search"
class="anticon anticon-search"
>
<svg
aria-hidden="true"
class=""
data-icon="search"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</i>
/>
</button>
</span>
</span>
@@ -471,27 +350,10 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable"
>
<i
aria-label="icon: down"
class="anticon anticon-down ant-select-arrow-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</i>
<b />
</span>
</div>
</div>

View File

@@ -1,26 +1,18 @@
import React from 'react';
import { mount } from 'enzyme';
import AutoComplete from '..';
import focusTest from '../../../tests/shared/focusTest';
describe('AutoComplete with Custom Input Element Render', () => {
focusTest(AutoComplete);
it('AutoComplete with custom Input render perfectly', () => {
const wrapper = mount(
<AutoComplete dataSource={['12345', '23456', '34567']}>
<textarea />
</AutoComplete>,
</AutoComplete>
);
expect(wrapper.find('textarea').length).toBe(1);
wrapper.find('textarea').simulate('change', { target: { value: '123' } });
const dropdownWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
const dropdownWrapper = mount(wrapper.find('Trigger').node.getComponent());
// should not filter data source defaultly
expect(dropdownWrapper.find('MenuItem').length).toBe(3);
@@ -31,7 +23,7 @@ describe('AutoComplete with Custom Input Element Render', () => {
mount(
<AutoComplete dataSource={[]}>
<input ref={mockRef} />
</AutoComplete>,
</AutoComplete>
);
expect(mockRef).toHaveBeenCalled();
});

View File

@@ -16,7 +16,6 @@ Basic Usage, set datasource of autocomplete with `dataSource` property.
````jsx
import { Icon, Input, AutoComplete } from 'antd';
const Option = AutoComplete.Option;
const OptGroup = AutoComplete.OptGroup;

View File

@@ -15,7 +15,6 @@ Customize Input Component
````jsx
import { AutoComplete, Input } from 'antd';
const { TextArea } = Input;
function onSelect(value) {

View File

@@ -35,7 +35,9 @@ class Complete extends React.Component {
render() {
const { result } = this.state;
const children = result.map(email => <Option key={email}>{email}</Option>);
const children = result.map((email) => {
return <Option key={email}>{email}</Option>;
});
return (
<AutoComplete
style={{ width: 200 }}

View File

@@ -15,10 +15,7 @@ Demonstration of [Lookup Patterns: Uncertain Category](https://ant.design/docs/s
Basic Usage, set datasource of autocomplete with `dataSource` property.
````jsx
import {
Icon, Button, Input, AutoComplete,
} from 'antd';
import { Icon, Button, Input, AutoComplete } from 'antd';
const Option = AutoComplete.Option;
function onSelect(value) {

View File

@@ -21,30 +21,17 @@ const dataSource = ['12345', '23456', '34567'];
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| allowClear | Show clear button, effective in multiple mode only. | boolean | false |
| autoFocus | get focus when component mounted | boolean | false |
| backfill | backfill selected item the input when using keyboard | boolean | false |
| children (for customize input element) | customize input element | HTMLInputElement / HTMLTextAreaElement / React.ReactElement<InputProps> | `<Input />` |
| children (for dataSource) | Data source for autocomplet | React.ReactElement<OptionProps> / Array&lt;React.ReactElement<OptionProps>> | - |
| dataSource | Data source for autocomplete | [DataSourceItemType](https://git.io/vMMKF)\[] | |
| defaultActiveFirstOption | Whether active first option by default | boolean | true |
| defaultValue | Initial selected option. | string\|string\[]\| - |
| defaultValue | Initial selected option. | string\|string\[]\|{ key: string, label: string\|ReactNode }\|Array&lt;{ key: string, label: string\|ReactNode }> | - |
| disabled | Whether disabled select | boolean | false |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true |
| optionLabelProp | Which prop value of option will render as content of select. | string | `children` |
| placeholder | placeholder of input | string | - |
| value | selected option | string\|string\[]\|{ key: string, label: string\|ReactNode }\|Array&lt;{ key: string, label: string\|ReactNode }> | - |
| onBlur | Called when leaving the component. | function() | - |
| onChange | Called when select an option or input value change, or value of input is changed | function(value) | - |
| onFocus | Called when entering the component | function() | - |
| onChange | Called when select an option or input value change, or value of input is changed | function(value, label) | - |
| onSearch | Called when searching items. | function(value) | - |
| onSelect | Called when a option is selected. param is option's value and option instance. | function(value, option) | - |
| defaultOpen | Initial open state of dropdown | boolean | - |
| open | Controlled open state of dropdown | boolean | - |
| onDropdownVisibleChange | Call when dropdown open | function(open) | - |
## Methods
| Name | Description |
| ---- | ----------- |
| blur() | remove focus |
| focus() | get focus |

View File

@@ -1,58 +1,45 @@
import * as React from 'react';
import React from 'react';
import { Option, OptGroup } from 'rc-select';
import classNames from 'classnames';
import InputElement from './InputElement';
import Input, { InputProps } from '../input';
import Select, { AbstractSelectProps, SelectValue, OptionProps, OptGroupProps } from '../select';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import Input from '../input';
import InputElement from './InputElement';
export interface DataSourceItemObject {
value: string;
text: string;
}
export type DataSourceItemType =
| string
| DataSourceItemObject
| React.ReactElement<OptionProps>
| React.ReactElement<OptGroupProps>;
export interface DataSourceItemObject { value: string; text: string; }
export type DataSourceItemType = string | DataSourceItemObject;
export interface AutoCompleteInputProps {
export interface InputProps {
onChange?: React.FormEventHandler<any>;
value: any;
}
export type ValidInputElement =
| HTMLInputElement
| HTMLTextAreaElement
| React.ReactElement<AutoCompleteInputProps>;
HTMLInputElement |
HTMLTextAreaElement |
React.ReactElement<InputProps>;
export interface AutoCompleteProps extends AbstractSelectProps {
value?: SelectValue;
defaultValue?: SelectValue;
dataSource?: DataSourceItemType[];
autoFocus?: boolean;
backfill?: boolean;
dataSource: DataSourceItemType[];
optionLabelProp?: string;
onChange?: (value: SelectValue) => void;
onSelect?: (value: SelectValue, option: Object) => any;
onBlur?: (value: SelectValue) => void;
onFocus?: () => void;
children?:
| ValidInputElement
| React.ReactElement<InputProps>
| React.ReactElement<OptionProps>
| Array<React.ReactElement<OptionProps>>;
children?: ValidInputElement |
React.ReactElement<OptionProps> |
Array<React.ReactElement<OptionProps>>;
}
function isSelectOptionOrSelectOptGroup(child: any): Boolean {
return child && child.type && (child.type.isSelectOption || child.type.isSelectOptGroup);
}
export default class AutoComplete extends React.Component<AutoCompleteProps, {}> {
export default class AutoComplete extends React.Component<AutoCompleteProps, any> {
static Option = Option as React.ClassicComponentClass<OptionProps>;
static OptGroup = OptGroup as React.ClassicComponentClass<OptGroupProps>;
static defaultProps = {
prefixCls: 'ant-select',
transitionName: 'slide-up',
optionLabelProp: 'children',
choiceTransitionName: 'zoom',
@@ -60,45 +47,22 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
filterOption: false,
};
private select: any;
getInputElement = () => {
const { children } = this.props;
const element =
children && React.isValidElement(children) && children.type !== Option ? (
React.Children.only(this.props.children)
) : (
<Input />
);
const elementProps = { ...(element as React.ReactElement<any>).props };
const element = children && React.isValidElement(children) && children.type !== Option ?
React.Children.only(this.props.children) : <Input />;
const elementProps = { ...element.props };
// https://github.com/ant-design/ant-design/pull/7742
delete elementProps.children;
return <InputElement {...elementProps}>{element}</InputElement>;
};
focus() {
this.select.focus();
return (
<InputElement {...elementProps}>{element}</InputElement>
);
}
blur() {
this.select.blur();
}
saveSelect = (node: any) => {
this.select = node;
};
renderAutoComplete = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
size,
className = '',
notFoundContent,
optionLabelProp,
dataSource,
children,
render() {
let {
size, className = '', notFoundContent, prefixCls, optionLabelProp, dataSource, children,
} = this.props;
const prefixCls = getPrefixCls('select', customizePrefixCls);
const cls = classNames({
[`${prefixCls}-lg`]: size === 'large',
@@ -110,48 +74,41 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
let options;
const childArray = React.Children.toArray(children);
if (childArray.length && isSelectOptionOrSelectOptGroup(childArray[0])) {
if (childArray.length &&
isSelectOptionOrSelectOptGroup(childArray[0])
) {
options = children;
} else {
options = dataSource
? dataSource.map(item => {
if (React.isValidElement(item)) {
return item;
}
switch (typeof item) {
case 'string':
return <Option key={item}>{item}</Option>;
case 'object':
return (
<Option key={(item as DataSourceItemObject).value}>
{(item as DataSourceItemObject).text}
</Option>
);
default:
throw new Error(
'AutoComplete[dataSource] only supports type `string[] | Object[]`.',
);
}
})
: [];
options = dataSource ? dataSource.map((item) => {
if (React.isValidElement(item)) {
return item;
}
switch (typeof item) {
case 'string':
return <Option key={item}>{item}</Option>;
case 'object':
return (
<Option key={(item as DataSourceItemObject).value}>
{(item as DataSourceItemObject).text}
</Option>
);
default:
throw new Error('AutoComplete[dataSource] only supports type `string[] | Object[]`.');
}
}) : [];
}
return (
<Select
{...this.props}
className={cls}
mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE}
mode="combobox"
optionLabelProp={optionLabelProp}
getInputElement={this.getInputElement}
notFoundContent={notFoundContent}
ref={this.saveSelect}
>
{options}
</Select>
);
};
render() {
return <ConfigConsumer>{this.renderAutoComplete}</ConfigConsumer>;
}
}

View File

@@ -1,7 +1,7 @@
---
category: Components
subtitle: 自动完成
type: 数据录入
type: Data Entry
cols: 2
title: AutoComplete
---
@@ -22,30 +22,17 @@ const dataSource = ['12345', '23456', '34567'];
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| allowClear | 支持清除, 单选模式有效 | boolean | false |
| autoFocus | 自动获取焦点 | boolean | false |
| backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false |
| children (自动完成的数据源) | 自动完成的数据源 | React.ReactElement<OptionProps> / Array&lt;React.ReactElement<OptionProps>> | - |
| children (自定义输入框) | 自定义输入框 | HTMLInputElement / HTMLTextAreaElement / React.ReactElement<InputProps> | `<Input />` |
| dataSource | 自动完成的数据源 | [DataSourceItemType](https://git.io/vMMKF)\[] | |
| defaultActiveFirstOption | 是否默认高亮第一个选项。 | boolean | true |
| defaultValue | 指定默认选中的条目 | string\|string\[]\| 无 |
| defaultValue | 指定默认选中的条目 | string\|string\[]\|{ key: string, label: string\|ReactNode }\|Array&lt;{ key: string, label: string\|ReactNode}> | 无 |
| disabled | 是否禁用 | boolean | false |
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true |
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value`。 | string | `children` |
| placeholder | 输入框提示 | string | - |
| value | 指定当前选中的条目 | string\|string\[]\|{ key: string, label: string\|ReactNode }\|Array&lt;{ key: string, label: string\|ReactNode }> | 无 |
| onBlur | 失去焦点时的回调 | function() | - |
| onChange | 选中 option或 input 的 value 变化时,调用此函数 | function(value) | 无 |
| onFocus | 获得焦点时的回调 | function() | - |
| onSearch | 搜索补全项的时候调用 | function(value) | 无 |
| onSelect | 被选中时调用,参数为选中项的 value 值 | function(value, option) | 无 |
| defaultOpen | 是否默认展开下拉菜单 | boolean | - |
| open | 是否展开下拉菜单 | boolean | - |
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - |
## 方法
| 名称 | 描述 |
| --- | --- |
| blur() | 移除焦点 |
| focus() | 获取焦点 |

View File

@@ -1,14 +1,12 @@
@import '../../style/themes/default';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import "../../style/themes/default";
@import "../../style/mixins/index";
@import "../../input/style/mixin";
@input-prefix-cls: ~'@{ant-prefix}-input';
@select-prefix-cls: ~'@{ant-prefix}-select';
@autocomplete-prefix-cls: ~'@{select-prefix-cls}-auto-complete';
@input-prefix-cls: ~"@{ant-prefix}-input";
@select-prefix-cls: ~"@{ant-prefix}-select";
@autocomplete-prefix-cls: ~"@{select-prefix-cls}-auto-complete";
.@{autocomplete-prefix-cls} {
.reset-component;
&.@{select-prefix-cls} {
.@{select-prefix-cls} {
&-selection {
@@ -52,10 +50,6 @@
&:hover {
.hover;
}
&[disabled] {
.disabled;
background-color: transparent;
}
}
&-lg {

View File

@@ -8,86 +8,4 @@ describe('Avatar Render', () => {
const children = wrapper.find('.ant-avatar-string');
expect(children.length).toBe(1);
});
it('should render fallback string correctly', () => {
const div = global.document.createElement('div');
global.document.body.appendChild(div);
const wrapper = mount(<Avatar src="http://error.url">Fallback</Avatar>, { attachTo: div });
wrapper.instance().setScale = jest.fn(() => wrapper.instance().setState({ scale: 0.5 }));
wrapper.find('img').simulate('error');
const children = wrapper.find('.ant-avatar-string');
expect(children.length).toBe(1);
expect(children.text()).toBe('Fallback');
expect(wrapper.instance().setScale).toBeCalled();
expect(div.querySelector('.ant-avatar-string').style.transform).toContain('scale(0.5)');
wrapper.detach();
global.document.body.removeChild(div);
});
it('should handle onError correctly', () => {
const LOAD_FAILURE_SRC = 'http://error.url';
const LOAD_SUCCESS_SRC = 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png';
const div = global.document.createElement('div');
global.document.body.appendChild(div);
class Foo extends React.Component {
state = {
src: LOAD_FAILURE_SRC,
};
handleImgError = () => {
this.setState({
src: LOAD_SUCCESS_SRC,
});
return false;
};
render() {
const { src } = this.state;
return <Avatar src={src} onError={this.handleImgError} />;
}
}
const wrapper = mount(<Foo />, { attachTo: div });
// mock img load Error, since jsdom do not load resource by default
// https://github.com/jsdom/jsdom/issues/1816
wrapper.find('img').simulate('error');
expect(wrapper.find(Avatar).instance().state.isImgExist).toBe(true);
expect(div.querySelector('img').getAttribute('src')).toBe(LOAD_SUCCESS_SRC);
wrapper.detach();
global.document.body.removeChild(div);
});
it('should show image on success after a failure state', () => {
const LOAD_FAILURE_SRC = 'http://error.url';
const LOAD_SUCCESS_SRC = 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png';
const div = global.document.createElement('div');
global.document.body.appendChild(div);
// simulate error src url
const wrapper = mount(<Avatar src={LOAD_FAILURE_SRC}>Fallback</Avatar>, { attachTo: div });
wrapper.find('img').simulate('error');
expect(wrapper.find(Avatar).instance().state.isImgExist).toBe(false);
expect(wrapper.find('.ant-avatar-string').length).toBe(1);
// simulate successful src url
wrapper.setProps({ src: LOAD_SUCCESS_SRC });
wrapper.update();
expect(wrapper.find(Avatar).instance().state.isImgExist).toBe(true);
expect(wrapper.find('.ant-avatar-image').length).toBe(1);
// cleanup
wrapper.detach();
global.document.body.removeChild(div);
});
});

View File

@@ -3,7 +3,7 @@
exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
<div>
<span
style="margin-right:24px"
style="margin-right:24px;"
>
<span
class="ant-badge"
@@ -12,23 +12,8 @@ exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
class="ant-avatar ant-avatar-square ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
<sup
class="ant-scroll-number ant-badge-count"
@@ -37,7 +22,7 @@ exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
>
<span
class="ant-scroll-number-only"
style="transition:none;-ms-transform:translateY(-1100%);-webkit-transform:translateY(-1100%);transform:translateY(-1100%)"
style="transition:none;-ms-transform:translateY(-1100%);-webkit-transform:translateY(-1100%);transform:translateY(-1100%);"
>
<p
class=""
@@ -201,23 +186,8 @@ exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
class="ant-avatar ant-avatar-square ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
<sup
class="ant-scroll-number ant-badge-dot"
@@ -231,185 +201,49 @@ exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
<div>
<div>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="width:64px;height:64px;line-height:64px;font-size:32px"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
</span>
<span
class="ant-avatar ant-avatar-lg ant-avatar-circle ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
<span
class="ant-avatar ant-avatar-sm ant-avatar-circle ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
</div>
<div>
<span
class="ant-avatar ant-avatar-square ant-avatar-icon"
style="width:64px;height:64px;line-height:64px;font-size:32px"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
</span>
<span
class="ant-avatar ant-avatar-lg ant-avatar-square ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
<span
class="ant-avatar ant-avatar-square ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
<span
class="ant-avatar ant-avatar-sm ant-avatar-square ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
</div>
</div>
@@ -419,7 +253,7 @@ exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `
<div>
<span
class="ant-avatar ant-avatar-lg ant-avatar-circle"
style="background-color:#f56a00;vertical-align:middle"
style="background-color:#f56a00;"
>
<span
class="ant-avatar-string"
@@ -429,7 +263,7 @@ exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `
</span>
<button
class="ant-btn ant-btn-sm"
style="margin-left:16px;vertical-align:middle"
style="margin-left:16px;"
type="button"
>
<span>
@@ -445,23 +279,8 @@ exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-icon"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
<span
class="ant-avatar ant-avatar-circle"
@@ -490,7 +309,7 @@ exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
</span>
<span
class="ant-avatar ant-avatar-circle"
style="color:#f56a00;background-color:#fde3cf"
style="color:#f56a00;background-color:#fde3cf;"
>
<span
class="ant-avatar-string"
@@ -500,26 +319,11 @@ exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
</span>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color:#87d068"
style="background-color:#87d068;"
>
<i
aria-label="icon: user"
class="anticon anticon-user"
>
<svg
aria-hidden="true"
class=""
data-icon="user"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</i>
/>
</span>
</div>
`;

View File

@@ -24,7 +24,6 @@ ReactDOM.render(
<span>
<Badge dot><Avatar shape="square" icon="user" /></Badge>
</span>
</div>,
mountNode
);
</div>
, mountNode);
````

View File

@@ -19,20 +19,17 @@ import { Avatar } from 'antd';
ReactDOM.render(
<div>
<div>
<Avatar size={64} icon="user" />
<Avatar size="large" icon="user" />
<Avatar icon="user" />
<Avatar size="small" icon="user" />
</div>
<div>
<Avatar shape="square" size={64} icon="user" />
<Avatar shape="square" size="large" icon="user" />
<Avatar shape="square" icon="user" />
<Avatar shape="square" size="small" icon="user" />
</div>
</div>,
mountNode
);
</div>
, mountNode);
````
<style>

View File

@@ -27,7 +27,6 @@ class Autoset extends React.Component {
color: colorList[0],
};
}
changeUser = () => {
const index = UserList.indexOf(this.state.user);
this.setState({
@@ -35,21 +34,16 @@ class Autoset extends React.Component {
color: index < colorList.length - 1 ? colorList[index + 1] : colorList[0],
});
}
render() {
return (
<div>
<Avatar style={{ backgroundColor: this.state.color, verticalAlign: 'middle' }} size="large">
{this.state.user}
</Avatar>
<Button size="small" style={{ marginLeft: 16, verticalAlign: 'middle' }} onClick={this.changeUser}>
Change
</Button>
<Avatar style={{ backgroundColor: this.state.color }} size="large">{this.state.user}</Avatar>
<Button size="small" style={{ marginLeft: 16 }} onClick={this.changeUser}>Change</Button>
</div>
);
}
}
ReactDOM.render(<Autoset />,
mountNode);
ReactDOM.render(<Autoset />
, mountNode);
````

View File

@@ -24,9 +24,8 @@ ReactDOM.render(
<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
<Avatar style={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>U</Avatar>
<Avatar style={{ backgroundColor: '#87d068' }} icon="user" />
</div>,
mountNode
);
</div>
, mountNode);
````
<style>

View File

@@ -12,8 +12,5 @@ Avatars can be used to represent people or objects. It supports images, `Icon`s,
| -------- | ----------- | ---- | ------- |
| icon | the `Icon` type for an icon avatar, see `Icon` Component | string | - |
| shape | the shape of avatar | `circle` \| `square` | `circle` |
| size | the size of the avatar | number \| string: `large` `small` `default` | `default` |
| size | the size of the avatar | `large` \| `small` \| `default` | `default` |
| src | the address of the image for an image avatar | string | - |
| srcSet | a list of sources to use for different screen resolutions | string | - |
| alt | This attribute defines the alternative text describing the image | string | - |
| onError | handler when img load errorreturn false to prevent default fallback behavior | () => boolean | - |

View File

@@ -1,31 +1,21 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import React from 'react';
import ReactDOM from 'react-dom';
import Icon from '../icon';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export interface AvatarProps {
/** Shape of avatar, options:`circle`, `square` */
shape?: 'circle' | 'square';
/*
* Size of avatar, options: `large`, `small`, `default`
* or a custom number size
* */
size?: 'large' | 'small' | 'default' | number;
/** Size of avatar, options:`large`, `small`, `default` */
size?: 'large' | 'small' | 'default';
/** Src of image avatar */
src?: string;
/** Srcset of image avatar */
srcSet?: string;
/** Type of the Icon to be used in avatar */
icon?: string;
style?: React.CSSProperties;
prefixCls?: string;
className?: string;
children?: any;
alt?: string;
/* callback when img load error */
/* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self*/
onError?: () => boolean;
}
export interface AvatarState {
@@ -35,41 +25,37 @@ export interface AvatarState {
export default class Avatar extends React.Component<AvatarProps, AvatarState> {
static defaultProps = {
shape: 'circle' as AvatarProps['shape'],
size: 'default' as AvatarProps['size'],
};
state = {
scale: 1,
isImgExist: true,
prefixCls: 'ant-avatar',
shape: 'circle',
size: 'default',
};
private avatarChildren: any;
constructor(props: AvatarProps) {
super(props);
this.state = {
scale: 1,
isImgExist: true,
};
}
componentDidMount() {
this.setScale();
}
componentDidUpdate(prevProps: AvatarProps, prevState: AvatarState) {
if (
prevProps.children !== this.props.children ||
(prevState.scale !== this.state.scale && this.state.scale === 1) ||
prevState.isImgExist !== this.state.isImgExist
) {
if (prevProps.children !== this.props.children
|| (prevState.scale !== this.state.scale && this.state.scale === 1)) {
this.setScale();
}
if (prevProps.src !== this.props.src) {
this.setState({ isImgExist: true, scale: 1 });
}
}
setScale = () => {
const childrenNode = this.avatarChildren;
if (childrenNode) {
const childrenWidth = childrenNode.offsetWidth;
const avatarNode = ReactDOM.findDOMNode(this) as Element;
const avatarWidth = avatarNode.getBoundingClientRect().width;
const avatarWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width;
// add 4px gap for each side to get better performance
if (avatarWidth - 8 < childrenWidth) {
this.setState({
@@ -81,33 +67,15 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
});
}
}
};
}
handleImgLoadError = () => {
const { onError } = this.props;
const errorFlag = onError ? onError() : undefined;
if (errorFlag !== false) {
this.setState({ isImgExist: false });
}
};
handleImgLoadError = () => this.setState({ isImgExist: false });
renderAvatar = ({ getPrefixCls }: ConfigConsumerProps) => {
render() {
const {
prefixCls: customizePrefixCls,
shape,
size,
src,
srcSet,
icon,
className,
alt,
...others
prefixCls, shape, size, src, icon, className, ...others,
} = this.props;
const { isImgExist, scale } = this.state;
const prefixCls = getPrefixCls('avatar', customizePrefixCls);
const sizeCls = classNames({
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
@@ -115,65 +83,55 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
const classString = classNames(prefixCls, className, sizeCls, {
[`${prefixCls}-${shape}`]: shape,
[`${prefixCls}-image`]: src && isImgExist,
[`${prefixCls}-image`]: src,
[`${prefixCls}-icon`]: icon,
});
const sizeStyle: React.CSSProperties =
typeof size === 'number'
? {
width: size,
height: size,
lineHeight: `${size}px`,
fontSize: icon ? size / 2 : 18,
}
: {};
let children = this.props.children;
if (src && isImgExist) {
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
if (src && this.state.isImgExist) {
children = (
<img
src={src}
onError={this.handleImgLoadError}
/>
);
} else if (icon) {
children = <Icon type={icon} />;
} else {
const childrenNode = this.avatarChildren;
if (childrenNode || scale !== 1) {
const transformString = `scale(${scale}) translateX(-50%)`;
if (childrenNode || this.state.scale !== 1) {
const childrenStyle: React.CSSProperties = {
msTransform: transformString,
WebkitTransform: transformString,
transform: transformString,
msTransform: `scale(${this.state.scale})`,
WebkitTransform: `scale(${this.state.scale})`,
transform: `scale(${this.state.scale})`,
position: 'absolute',
display: 'inline-block',
left: `calc(50% - ${Math.round(childrenNode.offsetWidth / 2)}px)`,
};
const sizeChildrenStyle: React.CSSProperties =
typeof size === 'number'
? {
lineHeight: `${size}px`,
}
: {};
children = (
<span
className={`${prefixCls}-string`}
ref={span => (this.avatarChildren = span)}
style={{ ...sizeChildrenStyle, ...childrenStyle }}
ref={span => this.avatarChildren = span}
style={childrenStyle}
>
{children}
</span>
);
} else {
children = (
<span className={`${prefixCls}-string`} ref={span => (this.avatarChildren = span)}>
<span
className={`${prefixCls}-string`}
ref={span => this.avatarChildren = span}
>
{children}
</span>
);
}
}
return (
<span {...others} style={{ ...sizeStyle, ...others.style }} className={classString}>
<span {...others} className={classString}>
{children}
</span>
);
};
render() {
return <ConfigConsumer>{this.renderAvatar}</ConfigConsumer>;
}
}

View File

@@ -1,24 +1,17 @@
---
category: Components
subtitle: 头像
type: 数据展示
type: Data Display
title: Avatar
---
用来代表用户或事物,支持图片、图标或字符展示。
## 设计师专属
安装 [Kitchen Sketch 插件 💎](https://kitchen.alipay.com),一键填充高逼格头像和文本.
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| icon | 设置头像的图标类型,参考 `Icon` 组件 | string | - |
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` |
| size | 设置头像的大小 | number \| Enum{ 'large', 'small', 'default' } | `default` |
| size | 设置头像的大小 | Enum{ 'large', 'small', 'default' } | `default` |
| src | 图片类头像的资源地址 | string | - |
| srcSet | 设置图片类头像响应式资源地址 | string | - |
| alt | 图像无法显示时的替代文本 | string | - |
| onError | 图片加载失败的事件,返回 false 会关闭组件默认的 fallback 行为 | () => boolean | - |

View File

@@ -1,10 +1,8 @@
@import '../../style/themes/default';
@import '../../style/mixins/index';
@import "../../style/themes/default";
@avatar-prefix-cls: ~'@{ant-prefix}-avatar';
@avatar-prefix-cls: ~"@{ant-prefix}-avatar";
.@{avatar-prefix-cls} {
.reset-component;
display: inline-block;
text-align: center;
background: @avatar-bg;
@@ -12,11 +10,6 @@
white-space: nowrap;
position: relative;
overflow: hidden;
vertical-align: middle;
&-image {
background: transparent;
}
.avatar-size(@avatar-size-base, @avatar-font-size-base);
@@ -43,12 +36,10 @@
width: @size;
height: @size;
line-height: @size;
border-radius: 50%;
border-radius: @size / 2;
&-string {
position: absolute;
left: 50%;
transform-origin: 0 center;
& > * {
line-height: @size;
}
&.@{avatar-prefix-cls}-icon {

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