Compare commits

..

4 Commits

Author SHA1 Message Date
afc163
b6a617f2e9 Fix ts error temporarily, DefinitelyTyped/DefinitelyTyped#25342 2018-04-28 15:02:25 +08:00
afc163
1a790a5a9f 3.4.4 2018-04-28 14:32:20 +08:00
afc163
2a411130bc Fix upload onChange arg type when beforeUpload return false, close #10293 2018-04-28 14:17:29 +08:00
Wu Haotian
bf22853f41 add lib/**/style/* as sideEffects (#10217) 2018-04-28 14:17:01 +08:00
1216 changed files with 23134 additions and 153466 deletions

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,
},
};
@@ -52,16 +63,10 @@ if (process.env.RUN_ENV === 'DEMO') {
'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,
'react/prefer-stateless-function': 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,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

@@ -1,8 +0,0 @@
ports:
- port: 8001
onOpen: open-preview
tasks:
- before: >
export DEV_HOST=$(gp url 8001)
init: npm install
command: npm start

View File

@@ -2,22 +2,35 @@ 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',
@@ -27,11 +40,12 @@ module.exports = {
'!components/**/*/interface.{ts,tsx}',
],
transformIgnorePatterns,
snapshotSerializers: ['enzyme-to-json/serializer'],
snapshotSerializers: [
'enzyme-to-json/serializer',
],
globals: {
'ts-jest': {
tsConfig: './tsconfig.test.json',
},
tsConfigFile: './tsconfig.test.json',
}
},
testURL: 'http://localhost',
};

View File

@@ -1,19 +1,26 @@
// 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'],
snapshotSerializers: [
'enzyme-to-json/serializer'
],
globals: {
'ts-jest': {
tsConfigFile: './tsconfig.test.json',
},
}
},
};

View File

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

View File

@@ -1,13 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"overrides": [
{
"files": ".prettierrc",
"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;

23
.stylelintrc Normal file
View File

@@ -0,0 +1,23 @@
{
"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,
"unit-no-unknown": null,
"value-list-max-empty-lines": null,
"font-family-no-missing-generic-family-keyword": null,
"no-descending-specificity": null
}
}

View File

@@ -1,17 +0,0 @@
{
"extends": [
"stylelint-config-standard",
"stylelint-config-rational-order",
"stylelint-config-prettier"
],
"plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
"rules": {
"comment-empty-line-before": null,
"declaration-empty-line-before": null,
"function-name-case": ["lower", { "ignoreFunctions": ["/colorPalette/"] }],
"no-invalid-double-slash-comments": null,
"no-descending-specificity": null,
"plugin/declaration-block-no-ignored-properties": true
},
"ignoreFiles": ["components/style/color/{bezierEasing,colorPalette,tinyColor}.less"]
}

View File

@@ -3,26 +3,22 @@ sudo: false
language: node_js
node_js:
- 11
cache:
directories:
- $HOME/.npm
- 8
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
- 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

View File

@@ -1,132 +1,83 @@
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>
@@ -134,94 +85,63 @@ 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>
@@ -230,23 +150,18 @@ 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>
@@ -258,69 +173,50 @@ 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>
@@ -330,15 +226,8 @@ 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>
@@ -348,65 +237,43 @@ Valentin Vichnal <valentin@vichnal.com>
Vemund Santi <vemund@santi.no>
Vic <709147950@qq.com>
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>
@@ -414,18 +281,12 @@ 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,26 +297,17 @@ 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>
@@ -465,8 +317,6 @@ 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>
@@ -481,18 +331,15 @@ 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>
@@ -506,37 +353,24 @@ 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>
@@ -544,20 +378,12 @@ 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>
@@ -566,28 +392,21 @@ 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>
@@ -596,48 +415,31 @@ 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>
@@ -648,9 +450,7 @@ zuiidea <zuiiidea@gmail.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>

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,65 +1,49 @@
<p align="center">
<a href="http://ant.design">
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
<img width="230" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.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)](http://www.npmtrends.com/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)
[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)
[![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)
[![](https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social)](https://twitter.com/AntDesignUI)
[![Gitter](https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
</div>
[![](https://cdn-images-1.medium.com/max/2000/1*NIlj0-TdLMbo_hzSBP8tmg.png)](http://ant.design/index-cn)
[English](./README.md) | 简体中文
## ✨ 特性
## 特性
- 提炼自企业级中后台产品的交互语言和视觉风格。
- 开箱即用的高质量 React 组件。
- 使用 TypeScript 构建,提供完整的类型定义文件。
- 全链路开发和设计工具体系。
## 🖥 支持环境
## 支持环境
* 现代浏览器和 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)
## 📦 安装
请参考[贡献指南](https://ant.design/docs/react/contributing-cn).
## 安装
```bash
npm install antd --save
```
```bash
yarn add antd
```
## 🔨 示例
## 示例
```jsx
import { DatePicker } from 'antd';
@@ -72,42 +56,35 @@ ReactDOM.render(<DatePicker />, mountNode);
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
```
你也可以 [按需加载组件](https://ant.design/docs/react/getting-started-cn#按需加载)。
你也可以[按需加载组件](https://ant.design/docs/react/getting-started-cn#按需加载)。
### TypeScript
## TypeScript
参考 [在 TypeScript 中使用](https://ant.design/docs/react/use-in-typescript-cn)
## 🌍 国际化
## 国际化
参考 [国际化文档](http://ant.design/docs/react/i18n-cn)。
## 🔗 链接
## 链接
- [首页](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)
- [常见问题](https://github.com/ant-design/ant-design/wiki/FAQ)
- [CodeSandbox 模板](https://u.ant.design/codesandbox-repro) for bug reports
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
- [定制主题](http://ant.design/docs/react/customize-theme-cn)
## ⌨️ 本地开发
你可以使用 Gitpod 进行在线开发:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ant-design/ant-design)
或者克隆到本地开发:
## 本地开发
```bash
$ git clone git@github.com:ant-design/ant-design.git
@@ -116,16 +93,14 @@ $ 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).
阅读我们的[贡献指南](./.github/CONTRIBUTING.md).
> 强烈推荐阅读 [《提问的智慧》](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)
## 社区互助
如果您在使用的过程中碰到问题,可以通过下面几个途径寻求帮助,同时我们也鼓励资深用户通过下面的途径给新人提供帮助。

View File

@@ -1,65 +1,53 @@
<p align="center">
<a href="http://ant.design">
<img width="200" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
<img width="230" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.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)
[![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://www.npmtrends.com/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)
[![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)
An enterprise-class UI design language and React-based implementation.
[![](https://img.shields.io/twitter/follow/AntDesignUI.svg?label=Ant%20Design&style=social)](https://twitter.com/AntDesignUI)
[![Gitter](https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[中文 README](README-zh_CN.md)
</div>
## 3.0 Released Now! :tada::tada::tada:
[![](https://cdn-images-1.medium.com/max/2000/1*NIlj0-TdLMbo_hzSBP8tmg.png)](http://ant.design)
[Announcing Ant Design 3.0](https://medium.com/ant-design/announcing-ant-design-3-0-70e3e65eca0c)
English | [简体中文](./README-zh_CN.md)
## Features
## ✨ Features
- An enterprise-class UI design system for web applications.
- An enterprise-class UI design system for desktop 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.
## 🖥 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
Read our [contributing guide](https://ant.design/docs/react/contributing).
## Install
```bash
npm install antd --save
```
```bash
yarn add antd
```
## 🔨 Usage
## Usage
```jsx
import { DatePicker } from 'antd';
@@ -72,41 +60,35 @@ 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).
Or [import components on demand](https://ant.design/docs/react/getting-started#Import-on-Demand)
### TypeScript
See [Use in TypeScript](https://ant.design/docs/react/use-in-typescript).
See [Use in TypeScript](https://ant.design/docs/react/use-in-typescript)
## 🌍 Internationalization
## 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)
- [FAQ](https://github.com/ant-design/ant-design/wiki/FAQ)
- [CodeSandbox Template](https://u.ant.design/codesandbox-repro) for bug reports
- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design)
- [Customize Theme](http://ant.design/docs/react/customize-theme)
## ⌨️ Development
Use Gitpod, a free online dev environment for GitHub.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ant-design/ant-design)
Or clone locally:
## Development
```bash
$ git clone git@github.com:ant-design/ant-design.git
@@ -115,14 +97,8 @@ $ 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,13 +18,9 @@ Array [
"Cascader",
"Checkbox",
"Col",
"Comment",
"ConfigProvider",
"DatePicker",
"Divider",
"Dropdown",
"Drawer",
"Empty",
"Form",
"Icon",
"Input",
@@ -35,7 +31,6 @@ Array [
"message",
"Menu",
"Modal",
"Statistic",
"notification",
"Pagination",
"Popconfirm",
@@ -45,7 +40,6 @@ Array [
"Rate",
"Row",
"Select",
"Skeleton",
"Slider",
"Spin",
"Steps",

View File

@@ -1,20 +1,14 @@
const OLD_NODE_ENV = process.env.NODE_ENV;
process.env.NODE_ENV = 'development';
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
const antd = require('..');
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', () => {
it('should hint when import all components', () => {
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.',
'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,46 @@
const availablePrefixs = ['moz', 'ms', 'webkit'];
function requestAnimationFramePolyfill() {
let lastTime = 0;
return function(callback: (n: number) => void) {
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 as any)[`${prefix}RequestAnimationFrame`]
: requestAnimationFramePolyfill();
}
export function cancelRequestAnimationFrame(id: number) {
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 as any)[`${prefix}CancelAnimationFrame`] ||
(window as any)[`${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: any, top: boolean): 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

@@ -0,0 +1,24 @@
let animation: boolean;
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 as any)[`${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,5 +1,7 @@
import cssAnimation from 'css-animation';
import raf from 'raf';
import getRequestAnimationFrame, { cancelRequestAnimationFrame } from './getRequestAnimationFrame';
const reqAnimFrame = getRequestAnimationFrame();
function animate(node: HTMLElement, show: boolean, done: () => void) {
let height: number;
@@ -17,16 +19,16 @@ function animate(node: HTMLElement, show: boolean, done: () => void) {
},
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';
});
},
end() {
if (requestAnimationFrameId) {
raf.cancel(requestAnimationFrameId);
cancelRequestAnimationFrame(requestAnimationFrameId);
}
node.style.height = '';
node.style.opacity = '';

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,4 +1,6 @@
import raf from 'raf';
import getRequestAnimationFrame, { cancelRequestAnimationFrame } from '../_util/getRequestAnimationFrame';
const reqAnimFrame = getRequestAnimationFrame();
export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
let requestId: number | null;
@@ -10,18 +12,18 @@ export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
const throttled = (...args: any[]) => {
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;
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,3 +1,3 @@
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;
export type Diff<T extends string, U extends string> = ({ [P in T]: P } &
{ [P in U]: never } & { [x: string]: never })[T];
export type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;

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.instance().affix.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.instance().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,18 +1,17 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
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);
return target !== window ?
(target as HTMLElement).getBoundingClientRect() :
{ top: 0, left: 0, bottom: 0 } as ClientRect;
}
function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
@@ -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
@@ -55,7 +57,6 @@ export interface AffixProps {
/** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
target?: () => Window | HTMLElement | null;
prefixCls?: string;
className?: string;
}
export interface AffixState {
@@ -70,16 +71,11 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
target: PropTypes.func,
};
state: AffixState = {
affixStyle: undefined,
placeholderStyle: undefined,
};
scrollEvent: any;
resizeEvent: any;
timeout: any;
private timeout: number;
private eventHandlers: Record<string, any> = {};
private fixedNode: HTMLElement;
private placeholderNode: HTMLElement;
private readonly events = [
events = [
'resize',
'scroll',
'touchstart',
@@ -89,7 +85,19 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
'load',
];
setAffixStyle(e: Event, affixStyle: React.CSSProperties | null) {
eventHandlers: {
[key: string]: any;
} = {};
state: AffixState = {
affixStyle: undefined,
placeholderStyle: undefined,
};
private fixedNode: HTMLElement;
private placeholderNode: HTMLElement;
setAffixStyle(e: any, affixStyle: React.CSSProperties | null) {
const { onChange = noop, target = getDefaultTarget } = this.props;
const originalAffixStyle = this.state.affixStyle;
const isWindow = target() === window;
@@ -99,10 +107,10 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
if (shallowequal(affixStyle, originalAffixStyle)) {
return;
}
this.setState({ affixStyle: affixStyle as React.CSSProperties }, () => {
const affixed = !!this.state.affixStyle;
if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
if ((affixStyle && !originalAffixStyle) ||
(!affixStyle && originalAffixStyle)) {
onChange(affixed);
}
});
@@ -116,7 +124,7 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
this.setState({ placeholderStyle: placeholderStyle as React.CSSProperties });
}
syncPlaceholderStyle(e: Event) {
syncPlaceholderStyle(e: any) {
const { affixStyle } = this.state;
if (!affixStyle) {
return;
@@ -132,15 +140,12 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
}
@throttleByAnimationFrameDecorator()
updatePosition(e: Event) {
const { offsetBottom, offset, target = getDefaultTarget } = this.props;
let { offsetTop } = this.props;
updatePosition(e: any) {
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);
@@ -165,8 +170,7 @@ 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);
@@ -181,12 +185,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,12 +203,7 @@ 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);
@@ -223,8 +221,6 @@ 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);
});
}
@@ -234,13 +230,7 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
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({});
}
}
@@ -273,25 +263,18 @@ 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}>
@@ -300,9 +283,5 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
</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,26 +296,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="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"
>
@@ -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"
>
@@ -865,7 +369,7 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
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"
class="ant-alert ant-alert-success ant-alert-no-icon"
data-show="true"
>
<span
@@ -880,23 +384,8 @@ exports[`renders ./components/alert/demo/smooth-closed.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>
<p>

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

@@ -46,7 +46,6 @@ ReactDOM.render(
type="error"
showIcon
/>
</div>,
mountNode
);
</div>
, mountNode);
````

View File

@@ -7,7 +7,7 @@ title:
## zh-CN
平滑、自然的卸载提示
平滑、自然的卸载提示
## en-US
@@ -20,11 +20,9 @@ class App extends React.Component {
state = {
visible: true,
}
handleClose = () => {
this.setState({ visible: false });
}
render() {
return (
<div>
@@ -45,7 +43,6 @@ class App extends React.Component {
}
ReactDOM.render(
<App />,
mountNode
);
<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

@@ -20,8 +20,8 @@ Alert component for feedback.
| 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 |
| iconType | Icon type, effective when `showIcon` is `true` | string | - |
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` |
| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - |

View File

@@ -1,12 +1,10 @@
import * as React from 'react';
import * as 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 {
/**
@@ -32,23 +30,19 @@ export interface AlertProps {
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,39 +52,25 @@ 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, iconType,
} = 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':
@@ -100,7 +80,7 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
iconType = 'info-circle';
break;
case 'error':
iconType = 'close-circle';
iconType = 'cross-circle';
break;
case 'warning':
iconType = 'exclamation-circle';
@@ -111,48 +91,29 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
// use outline icon in alert with description
if (!!description) {
iconTheme = 'outlined';
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 +121,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,13 @@ title: Alert
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - |
| 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 |
| iconType | 自定义图标类型,`showIcon``true` 时有效 | string | - |
| type | 指定警告提示的样式,有四种选择 `success``info``warning``error` | string | `info``banner` 模式下默认值为 `warning` |
| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | 无 |

View File

@@ -1,12 +1,10 @@
@import '../../style/themes/default';
@import '../../style/mixins/index';
@import "../../style/themes/default";
@import "../../style/mixins/index";
@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;
@@ -18,68 +16,64 @@
padding: 8px 15px;
}
&&-closable {
padding-right: 30px;
}
&-icon {
position: absolute;
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2;
top: 8px + @font-size-base * @line-height-base / 2 - @font-size-base / 2 + 1px;
left: 16px;
position: absolute;
}
&-description {
display: none;
font-size: @font-size-base;
line-height: 22px;
display: none;
}
&-success {
background-color: @alert-success-bg-color;
border: @border-width-base @border-style-base @alert-success-border-color;
border: @border-width-base @border-style-base ~`colorPalette("@{success-color}", 3)`;
background-color: ~`colorPalette("@{success-color}", 1)`;
.@{alert-prefix-cls}-icon {
color: @alert-success-icon-color;
color: @success-color;
}
}
&-info {
background-color: @alert-info-bg-color;
border: @border-width-base @border-style-base @alert-info-border-color;
border: @border-width-base @border-style-base ~`colorPalette("@{info-color}", 3)`;
background-color: ~`colorPalette("@{info-color}", 1)`;
.@{alert-prefix-cls}-icon {
color: @alert-info-icon-color;
color: @info-color;
}
}
&-warning {
background-color: @alert-warning-bg-color;
border: @border-width-base @border-style-base @alert-warning-border-color;
border: @border-width-base @border-style-base ~`colorPalette("@{warning-color}", 3)`;
background-color: ~`colorPalette("@{warning-color}", 1)`;
.@{alert-prefix-cls}-icon {
color: @alert-warning-icon-color;
color: @warning-color;
}
}
&-error {
background-color: @alert-error-bg-color;
border: @border-width-base @border-style-base @alert-error-border-color;
border: @border-width-base @border-style-base ~`colorPalette("@{error-color}", 3)`;
background-color: ~`colorPalette("@{error-color}", 1)`;
.@{alert-prefix-cls}-icon {
color: @alert-error-icon-color;
color: @error-color;
}
}
&-close-icon {
position: absolute;
top: 8px;
right: 16px;
overflow: hidden;
font-size: @font-size-sm;
position: absolute;
right: 16px;
top: 8px;
line-height: 22px;
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;
&:hover {
color: @alert-close-hover-color;
color: #404040;
}
}
}
@@ -90,11 +84,11 @@
}
&-with-description {
position: relative;
padding: 15px 15px 15px 64px;
color: @alert-text-color;
line-height: @line-height-base;
position: relative;
border-radius: @border-radius-base;
color: @text-color;
line-height: @line-height-base;
}
&-with-description&-no-icon {
@@ -112,15 +106,15 @@
position: absolute;
top: 16px;
right: 16px;
font-size: @font-size-base;
cursor: pointer;
font-size: @font-size-base;
}
&-with-description &-message {
font-size: @font-size-lg;
color: @alert-message-color;
display: block;
margin-bottom: 4px;
color: @alert-message-color;
font-size: @font-size-lg;
}
&-with-description &-description {
@@ -132,44 +126,44 @@
margin: 0;
padding-top: 0;
padding-bottom: 0;
transition: all .3s @ease-in-out-circ;
transform-origin: 50% 0;
transition: all 0.3s @ease-in-out-circ;
}
&-slide-up-leave {
animation: antAlertSlideUpOut 0.3s @ease-in-out-circ;
animation: antAlertSlideUpOut .3s @ease-in-out-circ;
animation-fill-mode: both;
}
&-banner {
margin-bottom: 0;
border: 0;
border-radius: 0;
border: 0;
margin-bottom: 0;
}
}
@keyframes antAlertSlideUpIn {
0% {
transform: scaleY(0);
transform-origin: 0% 0%;
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0);
}
100% {
transform: scaleY(1);
transform-origin: 0% 0%;
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
}
@keyframes antAlertSlideUpOut {
0% {
transform: scaleY(1);
transform-origin: 0% 0%;
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
100% {
transform: scaleY(0);
transform-origin: 0% 0%;
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0);
}
}

View File

@@ -1,13 +1,12 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
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() {
return window;
@@ -26,7 +25,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
if (rect.width || rect.height) {
if (container === window) {
container = element.ownerDocument!.documentElement!;
container = element.ownerDocument.documentElement;
return rect.top - container.clientTop;
}
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
@@ -39,24 +38,18 @@ 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 = () => {},
) {
function scrollTo(href: string, offsetTop = 0, getContainer: () => AnchorContainer, callback = () => { }) {
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(href);
if (!sharpLinkMatch) {
return;
}
if (!sharpLinkMatch) { return; }
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
@@ -74,20 +67,21 @@ function scrollTo(
(container as HTMLElement).scrollTop = nextScrollTop;
}
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 type AnchorContainer = HTMLElement | Window;
export interface AnchorProps {
prefixCls?: string;
@@ -99,14 +93,6 @@ export interface AnchorProps {
affix?: boolean;
showInkInFixed?: boolean;
getContainer?: () => AnchorContainer;
onClick?: (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => void;
}
export interface AnchorState {
activeLink: null | string;
}
export interface AnchorDefaultProps extends AnchorProps {
@@ -116,21 +102,11 @@ export interface AnchorDefaultProps extends AnchorProps {
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,
@@ -140,36 +116,38 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
antAnchor: PropTypes.object,
};
state = {
activeLink: null,
};
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() {
@@ -196,7 +174,7 @@ 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;
@@ -205,10 +183,10 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
scrollTo(link, offsetTop, getContainer, () => {
this.animating = false;
});
};
}
getCurrentAnchor(offsetTop = 0, bounds = 5): string {
const activeLink = '';
getCurrentAnchor(offsetTop = 0, bounds = 5) {
let activeLink = '';
if (typeof document === 'undefined') {
return activeLink;
}
@@ -218,9 +196,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
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);
@@ -234,7 +210,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
});
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 +220,30 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
if (typeof document === 'undefined') {
return;
}
const prefixCls = this.prefixCls;
const { prefixCls } = this.props;
const anchorNode = ReactDOM.findDOMNode(this) as Element;
const linkNode = anchorNode.getElementsByClassName(`${prefixCls}-link-title-active`)[0];
if (linkNode) {
this.inkNode.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,7 +251,7 @@ 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 = {
@@ -292,9 +260,12 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
};
const anchorContent = (
<div className={wrapperClass} style={wrapperStyle}>
<div
className={wrapperClass}
style={wrapperStyle}
>
<div className={anchorClass}>
<div className={`${prefixCls}-ink`}>
<div className={`${prefixCls}-ink`} >
<span className={inkClass} ref={this.saveInkNode} />
</div>
{children}
@@ -302,16 +273,10 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
</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 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,7 +9,7 @@ describe('Anchor Render', () => {
const wrapper = mount(
<Anchor>
<Link href="#API" title="API" />
</Anchor>,
</Anchor>
);
wrapper.find('a[href="#API"]').simulate('click');
@@ -22,13 +22,13 @@ describe('Anchor Render', () => {
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');
});
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');
});
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 });
});
});

View File

@@ -35,10 +35,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,80 +80,6 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
</div>
`;
exports[`renders ./components/anchor/demo/onClick.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
</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"
@@ -185,10 +111,10 @@ exports[`renders ./components/anchor/demo/static.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

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

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

@@ -19,11 +19,10 @@ For displaying anchor hyperlinks on page and jumping between them.
| -------- | ----------- | ---- | ------- |
| affix | Fixed mode of Anchor | boolean | true |
| bounds | Bounding distance of anchor area | number | 5(px) |
| getContainer | Scrolling container | () => HTMLElement | () => window |
| 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

@@ -2,7 +2,7 @@
category: Components
subtitle: 锚点
cols: 2
type: 其他
type: Other
title: Anchor
---
@@ -24,7 +24,6 @@ title: Anchor
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |
| onClick | `click` 事件的 handler | Function(e: Event, link: Object) | - |
### Link Props

View File

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

View File

@@ -9,28 +9,22 @@ export default class InputElement extends React.Component<InputElementProps, any
private ele: HTMLInputElement;
focus = () => {
this.ele.focus
? this.ele.focus()
: (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus();
};
this.ele.focus ? this.ele.focus() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus();
}
blur = () => {
this.ele.blur ? this.ele.blur() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).blur();
};
}
saveRef = (ele: HTMLInputElement) => {
this.ele = ele;
const { ref: childRef } = this.props.children as any;
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

@@ -7,7 +7,6 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -20,7 +19,7 @@ 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"
unselectable="unselectable"
>
input here
</div>
@@ -48,26 +47,9 @@ exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
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>
@@ -84,7 +66,6 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -97,7 +78,7 @@ 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"
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
@@ -151,26 +117,9 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
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>
@@ -184,7 +133,6 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -218,26 +166,9 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
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>
@@ -250,7 +181,6 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -263,7 +193,7 @@ 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"
unselectable="unselectable"
>
try to type \`b\`
</div>
@@ -291,26 +221,9 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
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>
@@ -323,7 +236,6 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -336,7 +248,7 @@ 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"
unselectable="unselectable"
>
input here
</div>
@@ -364,26 +276,9 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
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>
@@ -400,7 +295,6 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
@@ -413,7 +307,7 @@ 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"
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>
@@ -472,26 +351,9 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
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

@@ -10,17 +10,12 @@ describe('AutoComplete with Custom Input Element Render', () => {
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').instance().getComponent());
// should not filter data source defaultly
expect(dropdownWrapper.find('MenuItem').length).toBe(3);
@@ -31,7 +26,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

@@ -27,20 +27,15 @@ const dataSource = ['12345', '23456', '34567'];
| 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() | - |
| 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

View File

@@ -1,20 +1,12 @@
import * as 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 {
onChange?: React.FormEventHandler<any>;
@@ -22,26 +14,20 @@ export interface AutoCompleteInputProps {
}
export type ValidInputElement =
| HTMLInputElement
| HTMLTextAreaElement
| React.ReactElement<AutoCompleteInputProps>;
HTMLInputElement |
HTMLTextAreaElement |
React.ReactElement<AutoCompleteInputProps>;
export interface AutoCompleteProps extends AbstractSelectProps {
value?: SelectValue;
defaultValue?: SelectValue;
dataSource?: DataSourceItemType[];
autoFocus?: boolean;
backfill?: boolean;
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 {
@@ -53,6 +39,7 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
static OptGroup = OptGroup as React.ClassicComponentClass<OptGroupProps>;
static defaultProps = {
prefixCls: 'ant-select',
transitionName: 'slide-up',
optionLabelProp: 'children',
choiceTransitionName: 'zoom',
@@ -64,17 +51,15 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
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>;
};
return (
<InputElement {...elementProps}>{element}</InputElement>
);
}
focus() {
this.select.focus();
@@ -86,19 +71,12 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
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,37 +88,35 @@ 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}
@@ -149,9 +125,5 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
{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
---
@@ -28,20 +28,15 @@ const dataSource = ['12345', '23456', '34567'];
| 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) | - |
## 方法

View File

@@ -1,10 +1,10 @@
@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;
@@ -15,14 +15,14 @@
border: 0;
box-shadow: none;
&__rendered {
height: 100%;
margin-right: 0;
margin-left: 0;
margin-right: 0;
height: 100%;
line-height: @input-height-base;
}
&__placeholder {
margin-right: (@input-padding-horizontal-base + 1px);
margin-left: (@input-padding-horizontal-base + 1px);
margin-right: (@input-padding-horizontal-base + 1px);
}
&--single {
@@ -44,18 +44,14 @@
}
.@{input-prefix-cls} {
height: @input-height-base;
line-height: @line-height-base;
background: transparent;
border-width: @border-width-base;
line-height: @line-height-base;
height: @input-height-base;
&:focus,
&:hover {
.hover;
}
&[disabled] {
.disabled;
background-color: transparent;
}
}
&-lg {
@@ -63,9 +59,9 @@
line-height: @input-height-lg;
}
.@{input-prefix-cls} {
height: @input-height-lg;
padding-top: @input-padding-vertical-lg;
padding-bottom: @input-padding-vertical-lg;
height: @input-height-lg;
}
}
@@ -74,9 +70,9 @@
line-height: @input-height-sm;
}
.@{input-prefix-cls} {
height: @input-height-sm;
padding-top: @input-padding-vertical-sm;
padding-bottom: @input-padding-vertical-sm;
height: @input-height-sm;
}
}
}

View File

@@ -15,79 +15,15 @@ describe('Avatar Render', () => {
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');
wrapper.setState({ isImgExist: false });
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)');
expect(div.querySelector('.ant-avatar-string').style.transform).toBe('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

@@ -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"
@@ -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>
@@ -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"
@@ -503,23 +322,8 @@ exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
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,7 +34,6 @@ class Autoset extends React.Component {
color: index < colorList.length - 1 ? colorList[index + 1] : colorList[0],
});
}
render() {
return (
<div>
@@ -50,6 +48,6 @@ class Autoset extends React.Component {
}
}
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 error, return false to prevent default fallback behavior | () => boolean | - |

View File

@@ -2,30 +2,20 @@ import * as React from 'react';
import * as 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,33 +25,31 @@ 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)
|| (prevState.isImgExist !== this.state.isImgExist)) {
this.setScale();
}
if (prevProps.src !== this.props.src) {
this.setState({ isImgExist: true, scale: 1 });
}
}
setScale = () => {
@@ -81,33 +69,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 +85,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 && this.state.isImgExist,
[`${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,18 +1,18 @@
@import '../../style/themes/default';
@import '../../style/mixins/index';
@import "../../style/themes/default";
@import "../../style/mixins/index";
@avatar-prefix-cls: ~'@{ant-prefix}-avatar';
@avatar-prefix-cls: ~"@{ant-prefix}-avatar";
.@{avatar-prefix-cls} {
.reset-component;
position: relative;
display: inline-block;
overflow: hidden;
text-align: center;
background: @avatar-bg;
color: @avatar-color;
white-space: nowrap;
text-align: center;
position: relative;
overflow: hidden;
vertical-align: middle;
background: @avatar-bg;
&-image {
background: transparent;
@@ -33,9 +33,9 @@
}
& > img {
display: block;
width: 100%;
height: 100%;
display: block;
}
}
@@ -43,12 +43,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 {

View File

@@ -3,14 +3,22 @@ import { mount } from 'enzyme';
import BackTop from '..';
describe('BackTop', () => {
it('should scroll to top after click it', async () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('should scroll to top after click it', () => {
const wrapper = mount(<BackTop visibilityHeight={-1} />);
document.documentElement.scrollTop = 400;
// trigger scroll manually
wrapper.instance().handleScroll();
await new Promise(resolve => setTimeout(resolve, 0));
jest.runAllTimers();
wrapper.find('.ant-back-top').simulate('click');
await new Promise(resolve => setTimeout(resolve, 1000));
jest.runAllTimers();
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
});
});

View File

@@ -3,21 +3,22 @@ import Animate from 'rc-animate';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import classNames from 'classnames';
import omit from 'omit.js';
import raf from 'raf';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getScroll from '../_util/getScroll';
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
const reqAnimFrame = getRequestAnimationFrame();
const 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;
} else {
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
return cc / 2 * ((t -= 2) * t * t + 2) + b;
}
};
function noop() {}
function noop() { }
function getDefaultTarget() {
return window;
@@ -30,7 +31,6 @@ export interface BackTopProps {
prefixCls?: string;
className?: string;
style?: React.CSSProperties;
visible?: boolean; // Only for test. Don't use it.
}
export default class BackTop extends React.Component<BackTopProps, any> {
@@ -51,10 +51,10 @@ export default class BackTop extends React.Component<BackTopProps, any> {
const getTarget = this.props.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
return window.pageYOffset || document.body.scrollTop || document.documentElement!.scrollTop;
return window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
}
return (targetNode as HTMLElement).scrollTop;
};
}
scrollToTop = (e: React.MouseEvent<HTMLDivElement>) => {
const scrollTop = this.getCurrentScrollTop();
@@ -64,21 +64,19 @@ export default class BackTop extends React.Component<BackTopProps, any> {
const time = timestamp - startTime;
this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450));
if (time < 450) {
raf(frameFunc);
} else {
this.setScrollTop(0);
reqAnimFrame(frameFunc);
}
};
raf(frameFunc);
reqAnimFrame(frameFunc);
(this.props.onClick || noop)(e);
};
}
setScrollTop(value: number) {
const getTarget = this.props.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
document.body.scrollTop = value;
document.documentElement!.scrollTop = value;
document.documentElement.scrollTop = value;
} else {
(targetNode as HTMLElement).scrollTop = value;
}
@@ -90,7 +88,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
this.setState({
visible: scrollTop > (visibilityHeight as number),
});
};
}
componentDidMount() {
const getTarget = this.props.target || getDefaultTarget;
@@ -104,9 +102,8 @@ export default class BackTop extends React.Component<BackTopProps, any> {
}
}
renderBackTop = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className = '', children } = this.props;
const prefixCls = getPrefixCls('back-top', customizePrefixCls);
render() {
const { prefixCls = 'ant-back-top', className = '', children } = this.props;
const classString = classNames(prefixCls, className);
const defaultElement = (
@@ -122,12 +119,9 @@ export default class BackTop extends React.Component<BackTopProps, any> {
'children',
'visibilityHeight',
'target',
'visible',
]);
const visible = 'visible' in this.props ? this.props.visible : this.state.visible;
const backTopBtn = visible ? (
const backTopBtn = this.state.visible ? (
<div {...divProps} className={classString} onClick={this.scrollToTop}>
{children || defaultElement}
</div>
@@ -138,9 +132,5 @@ export default class BackTop extends React.Component<BackTopProps, any> {
{backTopBtn}
</Animate>
);
};
render() {
return <ConfigConsumer>{this.renderBackTop}</ConfigConsumer>;
}
}

View File

@@ -1,6 +1,6 @@
---
category: Components
type: 其他
type: Other
subtitle: 回到顶部
title: BackTop
---

View File

@@ -1,40 +1,39 @@
@import '../../style/themes/default';
@import '../../style/mixins/index';
@import "../../style/themes/default";
@import "../../style/mixins/index";
@backtop-prefix-cls: ~'@{ant-prefix}-back-top';
@backtop-prefix-cls: ~"@{ant-prefix}-back-top";
.@{backtop-prefix-cls} {
.reset-component;
z-index: @zindex-back-top;
position: fixed;
right: 100px;
bottom: 50px;
z-index: @zindex-back-top;
width: 40px;
height: 40px;
width: 40px;
cursor: pointer;
&-content {
width: 40px;
height: 40px;
overflow: hidden;
width: 40px;
border-radius: 20px;
background-color: @back-top-bg;
color: @back-top-color;
text-align: center;
background-color: @back-top-bg;
border-radius: 20px;
transition: all 0.3s @ease-in-out;
transition: all .3s @ease-in-out;
overflow: hidden;
&:hover {
background-color: @back-top-hover-bg;
transition: all 0.3s @ease-in-out;
transition: all .3s @ease-in-out;
}
}
&-icon {
margin: 12px auto;
width: 14px;
height: 16px;
margin: 12px auto;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAoCAYAAACWwljjAAAABGdBTUEAALGPC/xhBQAAAbtJREFUWAntmMtKw0AUhhMvS5cuxILgQlRUpIggIoKIIoigG1eC+AA+jo+i6FIXBfeuXIgoeKVeitVWJX5HWhhDksnUpp3FDPyZk3Nm5nycmZKkXhAEOXSA3lG7muTeRzmfy6HneUvIhnYkQK+Q9NhAA0Opg0vBEhjBKHiyb8iGMyQMOYuK41BcBSypAL+MYXSKjtFAW7EAGEO3qN4uMQbbAkXiSfRQJ1H6a+yhlkKRcAoVFYiweYNjtCVQJJpBz2GCiPt7fBOZQpFgDpUikse5HgnkM4Fi4QX0Fpc5wf9EbLqpUCy4jMoJSXWhFwbMNgWKhVbRhy5jirhs9fy/oFhgHVVTJEs7RLZ8sSEoJm6iz7SZDMbJ+/OKERQTttCXQRLToRUmrKWCYuA2+jbN0MB4OQobYShfdTCgn/sL1K36M7TLrN3n+758aPy2rrpR6+/od5E8tf/A1uLS9aId5T7J3CNYihkQ4D9PiMdMC7mp4rjB9kjFjZp8BlnVHJBuO1yFXIV0FdDF3RlyFdJVQBdv5AxVdIsq8apiZ2PyYO1EVykesGfZEESsCkweyR8MUW+V8uJ1gkYipmpdP1pm2aJVPEGzAAAAAElFTkSuQmCC)
~'100%/100%' no-repeat;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAoCAYAAACWwljjAAAABGdBTUEAALGPC/xhBQAAAbtJREFUWAntmMtKw0AUhhMvS5cuxILgQlRUpIggIoKIIoigG1eC+AA+jo+i6FIXBfeuXIgoeKVeitVWJX5HWhhDksnUpp3FDPyZk3Nm5nycmZKkXhAEOXSA3lG7muTeRzmfy6HneUvIhnYkQK+Q9NhAA0Opg0vBEhjBKHiyb8iGMyQMOYuK41BcBSypAL+MYXSKjtFAW7EAGEO3qN4uMQbbAkXiSfRQJ1H6a+yhlkKRcAoVFYiweYNjtCVQJJpBz2GCiPt7fBOZQpFgDpUikse5HgnkM4Fi4QX0Fpc5wf9EbLqpUCy4jMoJSXWhFwbMNgWKhVbRhy5jirhs9fy/oFhgHVVTJEs7RLZ8sSEoJm6iz7SZDMbJ+/OKERQTttCXQRLToRUmrKWCYuA2+jbN0MB4OQobYShfdTCgn/sL1K36M7TLrN3n+758aPy2rrpR6+/od5E8tf/A1uLS9aId5T7J3CNYihkQ4D9PiMdMC7mp4rjB9kjFjZp8BlnVHJBuO1yFXIV0FdDF3RlyFdJVQBdv5AxVdIsq8apiZ2PyYO1EVykesGfZEESsCkweyR8MUW+V8uJ1gkYipmpdP1pm2aJVPEGzAAAAAElFTkSuQmCC) ~"100%/100%" no-repeat;
}
}

View File

@@ -2,38 +2,36 @@ import * as React from 'react';
import { createElement, Component } from 'react';
import omit from 'omit.js';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
function getNumberArray(num: string | number | undefined | null) {
return num
? num
.toString()
.split('')
.reverse()
.map(i => Number(i))
: [];
function getNumberArray(num: string | number | undefined) {
return num ?
num.toString()
.split('')
.reverse()
.map(i => Number(i)) : [];
}
export interface ScrollNumberProps {
prefixCls?: string;
className?: string;
count?: string | number | null;
displayComponent?: React.ReactElement<any>;
count?: string | number;
component?: string;
onAnimated?: Function;
style?: React.CSSProperties;
title?: string | number | null;
title?: string | number;
}
export interface ScrollNumberState {
animateStarted?: boolean;
count?: string | number | null;
count?: string | number;
}
export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
static defaultProps = {
prefixCls: 'ant-scroll-number',
count: null,
onAnimated() {},
onAnimated() {
},
};
lastCount: any;
@@ -72,120 +70,84 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
}
this.lastCount = this.state.count;
// 复原数字初始位置
this.setState(
{
animateStarted: true,
},
() => {
// 等待数字位置复原完毕
// 开始设置完整的数字
setTimeout(() => {
this.setState(
{
animateStarted: false,
count: nextProps.count,
},
() => {
const onAnimated = this.props.onAnimated;
if (onAnimated) {
onAnimated();
}
},
);
}, 5);
},
);
this.setState({
animateStarted: true,
}, () => {
// 等待数字位置复原完毕
// 开始设置完整的数字
setTimeout(() => {
this.setState({
animateStarted: false,
count: nextProps.count,
}, () => {
const onAnimated = this.props.onAnimated;
if (onAnimated) {
onAnimated();
}
});
}, 5);
});
}
}
renderNumberList(position: number) {
const childrenToReturn: React.ReactElement<any>[] = [];
for (let i = 0; i < 30; i++) {
const currentClassName = position === i ? 'current' : '';
childrenToReturn.push(
<p key={i.toString()} className={currentClassName}>
{i % 10}
</p>,
);
const currentClassName = (position === i) ? 'current' : '';
childrenToReturn.push(<p key={i.toString()} className={currentClassName}>{i % 10}</p>);
}
return childrenToReturn;
}
renderCurrentNumber(prefixCls: string, num: number, i: number) {
renderCurrentNumber(num: number, i: number) {
const position = this.getPositionByNum(num, i);
const removeTransition =
this.state.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
return createElement(
'span',
{
className: `${prefixCls}-only`,
style: {
transition: removeTransition ? 'none' : undefined,
msTransform: `translateY(${-position * 100}%)`,
WebkitTransform: `translateY(${-position * 100}%)`,
transform: `translateY(${-position * 100}%)`,
},
key: i,
const removeTransition = this.state.animateStarted ||
(getNumberArray(this.lastCount)[i] === undefined);
return createElement('span', {
className: `${this.props.prefixCls}-only`,
style: {
transition: removeTransition ? 'none' : undefined,
msTransform: `translateY(${-position * 100}%)`,
WebkitTransform: `translateY(${-position * 100}%)`,
transform: `translateY(${-position * 100}%)`,
},
this.renderNumberList(position),
);
key: i,
}, this.renderNumberList(position));
}
renderNumberElement(prefixCls: string) {
const { count } = this.state;
if (count && Number(count) % 1 === 0) {
return getNumberArray(count)
.map((num, i) => this.renderCurrentNumber(prefixCls, num, i))
.reverse();
renderNumberElement() {
const state = this.state;
if (!state.count || isNaN(state.count as number)) {
return state.count;
}
return count;
return getNumberArray(state.count)
.map((num, i) => this.renderCurrentNumber(num, i)).reverse();
}
renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
className,
style,
title,
component = 'sup',
displayComponent,
} = this.props;
render() {
const { prefixCls, className, style, title, component = 'sup' } = this.props;
// fix https://fb.me/react-unknown-prop
const restProps = omit(this.props, [
'count',
'onAnimated',
'component',
'prefixCls',
'displayComponent',
]);
const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
const newProps = {
...restProps,
className: classNames(prefixCls, className),
title: title as string,
};
// allow specify the border
// mock border-color by box-shadow for compatible with old usage:
// <Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />
if (style && style.borderColor) {
newProps.style = {
...style,
boxShadow: `0 0 0 1px ${style.borderColor} inset`,
};
newProps.style.boxShadow = `0 0 0 1px ${style.borderColor} inset`;
}
if (displayComponent) {
return React.cloneElement(displayComponent, {
className: classNames(
`${prefixCls}-custom-component`,
displayComponent.props && displayComponent.props.className,
),
});
}
return createElement(component as any, newProps, this.renderNumberElement(prefixCls));
};
render() {
return <ConfigConsumer>{this.renderScrollNumber}</ConfigConsumer>;
return createElement(
component as any,
newProps,
this.renderNumberElement(),
);
}
}

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