Compare commits

..

No commits in common. "main" and "3.0.0-alpha.1" have entirely different histories.

2921 changed files with 79165 additions and 446700 deletions

View File

@ -1,36 +0,0 @@
const fs = require('fs');
const path = require('path');
const restCssPath = path.join(process.cwd(), 'components', 'style', 'reset.css');
const tokenStatisticPath = path.join(process.cwd(), 'components', 'version', 'token.json');
const tokenMetaPath = path.join(process.cwd(), 'components', 'version', 'token-meta.json');
function finalizeCompile() {
if (fs.existsSync(path.join(__dirname, './es'))) {
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'es', 'style', 'reset.css'));
fs.copyFileSync(tokenStatisticPath, path.join(process.cwd(), 'es', 'version', 'token.json'));
fs.copyFileSync(tokenMetaPath, path.join(process.cwd(), 'es', 'version', 'token-meta.json'));
}
if (fs.existsSync(path.join(__dirname, './lib'))) {
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'lib', 'style', 'reset.css'));
fs.copyFileSync(tokenStatisticPath, path.join(process.cwd(), 'lib', 'version', 'token.json'));
fs.copyFileSync(tokenMetaPath, path.join(process.cwd(), 'lib', 'version', 'token-meta.json'));
}
}
function finalizeDist() {
if (fs.existsSync(path.join(__dirname, './dist'))) {
fs.copyFileSync(restCssPath, path.join(process.cwd(), 'dist', 'reset.css'));
}
}
module.exports = {
compile: {
finalize: finalizeCompile,
},
dist: {
finalize: finalizeDist,
},
bail: true,
};

View File

@ -7,7 +7,3 @@ es/
lib/
_site/
dist/
site/dist/
components/version/version.ts
site/src/router/demoRoutes.js
locale/

View File

@ -15,21 +15,9 @@ module.exports = {
'plugin:vue/vue3-recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'@vue/typescript/recommended',
'@vue/prettier',
// 'prettier',
'prettier',
],
// extends: [
// 'eslint:recommended',
// 'plugin:vue/vue3-recommended',
// '@vue/typescript/recommended',
// '@vue/prettier',
// ],
plugins: ['markdown', 'jest', '@typescript-eslint', 'import'],
globals: {
h: true,
defineProps: 'readonly',
},
overrides: [
{
files: ['*.md'],
@ -40,11 +28,12 @@ module.exports = {
},
{
files: ['*.ts', '*.tsx'],
// extends: ['@vue/typescript/recommended', '@vue/prettier'],
extends: ['@vue/typescript/recommended', '@vue/prettier', '@vue/prettier/@typescript-eslint'],
parserOptions: {
project: './tsconfig.json',
},
rules: {
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/ban-types': 0,
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/explicit-module-boundary-types': 0,
@ -62,21 +51,17 @@ module.exports = {
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2021,
},
rules: {
'no-console': 'off',
'vue/no-reserved-component-names': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true },
],
},
},
],
rules: {
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-empty-function': 0,
'@typescript-eslint/no-unused-vars': [
'error',
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true, argsIgnorePattern: '^_' },
],
'import/no-named-as-default': 'off',
'import/namespace': [2, { allowComputed: true }],
'import/no-named-as-default-member': 'off',
@ -99,14 +84,18 @@ module.exports = {
'vue/prop-name-casing': 'off',
'vue/one-component-per-file': 'off',
'vue/custom-event-name-casing': 'off',
'vue/v-on-event-hyphenation': 'off',
'vue/max-attributes-per-line': [
2,
{
singleline: 20,
multiline: 1,
multiline: {
max: 1,
allowFirstLine: false,
},
},
],
'vue/multi-word-component-names': 'off',
},
globals: {
h: true,
},
};

View File

@ -1,4 +1,4 @@
blank_issues_enabled: true
blank_issues_enabled: false
contact_links:
- name: Create new issue
url: https://vuecomponent.github.io/issue-helper/
@ -13,5 +13,5 @@ contact_links:
url: https://www.paypal.me/tangjinzhou
about: Love Ant Design Vue? Please consider supporting us via Paypal.
- name: 支付宝/微信 赞助
url: https://aliyuncdn.antdv.com/alipay-and-wechat.png
url: https://qn.antdv.com/alipay-and-wechat.png
about: Ant Design Vue 的健康持续发展需要您的支持,🙏

View File

@ -1,8 +1,8 @@
First of all, thank you for your contribution! 😄
New feature please send pull request to feature branch, and rest to main 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!
New feature please send pull request to feature branch, and rest to master branch. Pull request will be merged after one of collaborators approve. Please makes sure that these form are filled before submitting your pull request, thank you!
[[中文版模板 / Chinese template](./pr_cn.md)]
[[中文版模板 / Chinese template](https://github.com/vueComponent/ant-design-vue/blob/master/.github/PULL_REQUEST_TEMPLATE/pr_cn.md)]
### This is a ...

View File

@ -1,8 +1,8 @@
首先,感谢你的贡献! 😄
新特性请提交至 feature 分支,其余可提交至 main 分支。在一个维护者审核通过后合并。请确保填写以下 pull request 的信息,谢谢!~
新特性请提交至 feature 分支,其余可提交至 master 分支。在一个维护者审核通过后合并。请确保填写以下 pull request 的信息,谢谢!~
[[English Template / 英文模板](./pr_en.md)]
[[English Template / 英文模板](?expand=1)]
### 这个变动的性质是

View File

@ -16,7 +16,7 @@ jobs:
key: lock-${{ github.sha }}
- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts
run: npm i --package-lock-only
- name: hack for singe file
run: |

View File

@ -4,15 +4,12 @@ on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
close-issues:
runs-on: ubuntu-latest
steps:
- name: need reproduce
uses: actions-cool/issues-helper@v3
uses: actions-cool/issues-helper@v1.7
with:
actions: 'close-issues'
labels: '🤔 Need Reproduce'

View File

@ -4,16 +4,8 @@ on:
issues:
types: [opened]
permissions:
contents: read
jobs:
issue-open-check:
permissions:
contents: read # for visiky/dingtalk-release-notify to get latest release
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
check-issue:
runs-on: ubuntu-latest
steps:
- uses: actions-cool/check-user-permission@v1.0.0
@ -23,7 +15,7 @@ jobs:
- name: check invalid
if: (contains(github.event.issue.body, 'issue-helper') == false) && (steps.checkUser.outputs.result == 'false')
uses: actions-cool/issues-helper@v3
uses: actions-cool/issues-helper@v1.2
with:
actions: 'create-comment,add-labels,close-issue'
issue-number: ${{ github.event.issue.number }}

View File

@ -1,25 +1,18 @@
name: Issue Labeled
name: Issue Reply
on:
issues:
types: [labeled]
permissions:
contents: read
jobs:
issue-labeled:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
issue-reply:
runs-on: ubuntu-latest
steps:
- name: Need Reproduce
if: github.event.label.name == '🤔 Need Reproduce'
uses: actions-cool/issues-helper@v3
uses: actions-cool/issues-helper@v1.2
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}. Please provide a online reproduction by forking this [link for vue2](https://codesandbox.io/s/2wpk21kzvr)、 [link for vue3](https://codesandbox.io/s/agitated-franklin-1w72v) or a minimal GitHub repository. Make sure to choose the correct version.
@ -28,10 +21,9 @@ jobs:
- name: help wanted
if: github.event.label.name == 'help wanted'
uses: actions-cool/issues-helper@v3
uses: actions-cool/issues-helper@v1.2
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}. We totally like your proposal/feedback, welcome to send us a Pull Request for it. Please send your Pull Request to proper branch, fill the Pull Request Template here, provide changelog/TypeScript/documentation/test cases if needed and make sure CI passed, we will review it soon. We appreciate your effort in advance and looking forward to your contribution!
@ -40,37 +32,12 @@ jobs:
- name: Usage
if: github.event.label.name == 'Usage'
uses: actions-cool/issues-helper@v3
uses: actions-cool/issues-helper@v1.2
with:
actions: 'create-comment, close-issue'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}, we use GitHub issues to trace bugs or discuss plans of Ant Design Vue. So, please don't ask usage questions here. You can try to open a new discussion in [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions), select `Q&A` to ask questions, also can ask questions on [Stack Overflow](http://stackoverflow.com/questions/) or [Segment Fault](https://segmentfault.com).
你好 @${{ github.event.issue.user.login }}Ant Design Vue Issue 板块是用于 bug 反馈与需求讨论的地方。请勿询问如何使用的问题,你可以试着在 [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions) 新开一个 discussion选择 `Q&A` 类别进行提问,也可以在 [Stack Overflow](http://stackoverflow.com/questions/) 或者 [Segment Fault](https://segmentfault.com/) 中提问。
- name: 1.x
if: github.event.label.name == '1.x'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment,close-issue'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.event.issue.user.login }}. Current version (1.x) is off the maintenance period. We may not accept pull request or fix bug with it anymore. This topic will be auto closed.
你好 @${{ github.event.issue.user.login }}当前版本1.x已经过了维护期。我们不会再接受对其的相关 PR 与 issue。当前 topic 会被自动关闭。
- name: 2.x
if: github.event.label.name == '2.x'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment,close-issue'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.event.issue.user.login }}. Current version (2.x) is off the maintenance period. We may not accept pull request or fix bug with it anymore. This topic will be auto closed.
你好 @${{ github.event.issue.user.login }}当前版本2.x已经过了维护期。我们不会再接受对其的相关 PR 与 issue。当前 topic 会被自动关闭。

20
.github/workflows/pr-labeled.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: PR Labeled
on:
pull_request_target:
types: [labeled]
jobs:
reply:
runs-on: ubuntu-latest
steps:
- name: Usage
if: github.event.label.name == 'Usage'
uses: actions-cool/issues-helper@v1.2
with:
actions: 'create-comment, close-issue'
issue-number: ${{ github.event.pull_request.number }}
body: |
Hello @${{ github.event.pull_request.user.login }}, we use GitHub PR to build and perfect of Ant Design Vue. So, please don't ask usage questions here. You can try to open a new discussion in [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions), select `Q&A` to ask questions, also can ask questions on [Stack Overflow](http://stackoverflow.com/questions/) or [Segment Fault](https://segmentfault.com).
你好 @${{ github.event.pull_request.user.login }}Ant Design Vue PR 是用于建设、完善项目的地方。请勿询问如何使用的问题,你可以试着在 [antdv discussions](https://github.com/vueComponent/ant-design-vue/discussions) 新开一个 discussion选择 `Q&A` 类别进行提问,也可以在 [Stack Overflow](http://stackoverflow.com/questions/) 或者 [Segment Fault](https://segmentfault.com/) 中提问。

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@master
- name: cache package-lock.json
uses: actions/cache@v2
@ -16,7 +16,7 @@ jobs:
key: lock-${{ github.sha }}
- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts
run: npm i --package-lock-only
- name: hack for singe file
run: |
@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@master
- name: restore cache from package-lock.json
uses: actions/cache@v2
@ -74,7 +74,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@master
- name: restore cache from package-lock.json
uses: actions/cache@v2
@ -96,7 +96,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@master
# with:
# token: ${{ secrets.ACCESS_TOKEN }}
# - name: Checkout submodules
# uses: actions/checkout@master
# with:
# repository: tangjinzhou/antdv-demo
# token: ${{ secrets.ACCESS_TOKEN }}
# path: antdv-demo
# submodules: true
- name: restore cache from package-lock.json
uses: actions/cache@v2

23
.github/workflows/translate.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Translation Helper
on:
pull_request_target:
types: [opened]
issues:
types: [opened]
jobs:
translate:
runs-on: ubuntu-latest
steps:
- name: issue
if: github.event_name == 'issues'
uses: actions-cool/translation-helper@v1.1.1
with:
translate-body: false
- name: pr
if: github.event_name == 'pull_request_target'
uses: actions-cool/translation-helper@v1.1.1
with:
translate-body: false

8
.gitignore vendored
View File

@ -59,11 +59,9 @@ jspm_packages/
dist
lib
es
/locale
_site
yarn.lock
package-lock.json
pnpm-lock.yaml
/coverage
# 备份文件
@ -78,9 +76,3 @@ vetur/
report.html
site/src/router/demoRoutes.js
components/version/version.ts
components/version/version.tsx
components/version/token.json
components/version/token-meta.json
~component-api.json

View File

@ -9,48 +9,41 @@ const transformIgnorePatterns = [
];
const testPathIgnorePatterns = ['/node_modules/', 'node'];
function getTestRegex(libDir) {
if (libDir === 'dist') {
return 'demo\\.test\\.js$';
}
return '.*\\.test\\.(j|t)sx?$';
}
module.exports = {
verbose: true,
testURL: 'http://localhost/',
setupFiles: ['./tests/setup.js'],
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'vue', 'md', 'jpg'],
modulePathIgnorePatterns: ['/_site/'],
testPathIgnorePatterns: testPathIgnorePatterns,
transform: {
'\\.(vue|md)$': '<rootDir>/node_modules/@vue/vue3-jest',
'\\.(js|jsx)$': '<rootDir>/node_modules/babel-jest',
'\\.(ts|tsx)$': '<rootDir>/node_modules/ts-jest',
'\\.svg$': '<rootDir>/node_modules/jest-transform-stub',
'^.+\\.(vue|md)$': '<rootDir>/node_modules/vue-jest',
'^.+\\.(js|jsx)$': '<rootDir>/node_modules/babel-jest',
'^.+\\.(ts|tsx)$': '<rootDir>/node_modules/ts-jest',
'^.+\\.svg$': '<rootDir>/node_modules/jest-transform-stub',
},
testRegex: getTestRegex(libDir),
testRegex: libDir === 'dist' ? 'demo\\.test\\.js$' : '.*\\.test\\.js$',
moduleNameMapper: {
'^@/(.*)$/': '<rootDir>/$1',
'^ant-design-vue$': '<rootDir>/components/index',
'^ant-design-vue/es/(.*)$': '<rootDir>/components/$1',
'^@/(.*)$': '<rootDir>/$1',
'ant-design-vue$': '<rootDir>/components/index.ts',
'ant-design-vue/es': '<rootDir>/components',
},
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
collectCoverage: process.env.COVERAGE === 'true',
collectCoverageFrom: [
'components/**/*.{js,jsx,vue}',
'!components/*/style/index.{js,jsx}',
'!components/style/*.{js,jsx}',
'!components/*/locale/*.{js,jsx}',
'!components/*/__tests__/**/type.{js,jsx}',
'!components/vc-*/**/*',
'!components/*/demo/**/*',
'!components/_util/**/*',
'!components/align/**/*',
'!components/trigger/**/*',
'!components/style.js',
'!**/node_modules/**',
],
testEnvironment: 'jsdom',
testEnvironmentOptions: {
url: 'http://localhost',
customExportConditions: ['node', 'node-addons'],
},
testEnvironment: 'jest-environment-jsdom-fifteen',
transformIgnorePatterns,
globals: {
'ts-jest': {

1
.npmrc
View File

@ -1 +0,0 @@
enable-pre-post-scripts=true

View File

@ -18,6 +18,7 @@ yarn-error.log
.editorconfig
.eslintignore
**/*.yml
components/style/color/*.less
**/assets
.gitattributes
.stylelintrc

View File

@ -4,40 +4,13 @@
"stylelint-config-rational-order",
"stylelint-config-prettier"
],
"customSyntax": "postcss-less",
"plugins": ["stylelint-declaration-block-no-ignored-properties"],
"plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
"rules": {
"function-name-case": ["lower"],
"function-no-unknown": [
true,
{
"ignoreFunctions": [
"fade",
"fadeout",
"tint",
"darken",
"ceil",
"fadein",
"floor",
"unit",
"shade",
"lighten",
"percentage",
"-"
]
}
],
"import-notation": null,
"comment-empty-line-before": null,
"function-name-case": ["lower", { "ignoreFunctions": ["/colorPalette/"] }],
"no-invalid-double-slash-comments": null,
"no-descending-specificity": null,
"no-invalid-position-at-import-rule": null,
"declaration-empty-line-before": null,
"keyframes-name-pattern": null,
"custom-property-pattern": null,
"number-max-precision": 8,
"alpha-value-notation": "number",
"color-function-notation": "legacy",
"selector-class-pattern": null,
"selector-id-pattern": null,
"selector-not-notation": null
}
"declaration-empty-line-before": null
},
"ignoreFiles": ["components/style/color/{bezierEasing,colorPalette,tinyColor}.less"]
}

5
.vcmrc
View File

@ -9,9 +9,8 @@
"perf",
"test",
"chore",
"revert",
"ci"
"revert"
],
"warnOnFail": false,
"autoFix": false
}
}

View File

@ -10,229 +10,713 @@
---
## 4.2.6
## 3.0.0-alpha.1
- 🐞 Fix Modal component aria-hidden error problem under chrome [#7823](https://github.com/vueComponent/ant-design-vue/issues/7823)
- 🐞 Fix the problem that the built-in input method of Safari automatically fills in the decimal point when inputting Chinese [#7918](https://github.com/vueComponent/ant-design-vue/issues/7918)
- 🐞 Fix InputNumber component disabled style problem [#7776](https://github.com/vueComponent/ant-design-vue/issues/7776)
- 🐞 Fix Select cannot lose focus problem [#7819](https://github.com/vueComponent/ant-design-vue/issues/7819)
`2021-10-07`
## 4.2.5
- 🌟 Refactor `Tabs` [#4732](https://github.com/vueComponent/ant-design-vue/issues/4732)
- Removed `prevClick`, `nextClick` events, and use `tabScroll` event instead
- Obsolete the `tabBarExtraContent` slot, replace it with the rightExtra slot, and add the `leftExtra` slot
- Added `addIcon`, `closeIcon`, `moreIcon` slots
- 🌟 Refactor `Card`, discard the tabList slots configuration, and use the customTab slot for unified configuration [#4732](https://github.com/vueComponent/ant-design-vue/issues/4732)
- 🌟 Refactor `Drawer`
- Added `autofocus` `contentWrapperStyle` `footerStyle` `headerStyle` `push` `size` `forceRender` and other attributes
- Added `closeIcon` `extra` `footer` and other slots
- Deprecated `afterVisibleChange` property, use event with the same name instead
- 🐞 Fix the problem that `Table` pagination does not respond to changes [1add0d](https://github.com/vueComponent/ant-design-vue/commit/1add0d251cd35aa2c55404f7a60f1531425490c1)
- 🐞 Fix `notification` style misalignment problem [#4703](https://github.com/vueComponent/ant-design-vue/issues/4703)
- 🐞 Fix the selection, dragging and other abnormalities caused by `Tree` fieldsName [#4726](https://github.com/vueComponent/ant-design-vue/issues/4726)
- 🐞 Fix Empty component memory leak problem
- 🐞 Fix Image width & height property not working problem
## 3.0.0-alpha.0
## 4.2.4
`2021-09-24`
- 🐞 Fix Wave memory leak problem
🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
## 4.2.3
- Open source documentation
- Removed the `lazy` attribute of Transfer, it does not have a real optimization effect.
- Removed the `combobox` mode of Select, please use `AutoComplete` instead.
- Deprecated Button.Group, please use `Space` instead.
- `Timeline.Item` new label
- `Steps` added `responsive`, `percent`
- `Collapse` added `ghost`, `collapsible`
- `Popconfirm` added `cancelButton`, `okButton`, and `esc` button hiding
- `ConfigProvider` added ConfigProvider.config to define the configuration of `Modal.xxx` `message` `notification`
- `Tree` `TreeSlelct`
- 🌟 TourStep custom Button, support function children [#7628](https://github.com/vueComponent/ant-design-vue/pull/7628)
- 🐞 Fix the problem that the input value is hidden in Select and Cascader search multi-select mode [#7640](https://github.com/vueComponent/ant-design-vue/issues/7640)
- Added virtual scrolling, discarded using `a-tree-node` `a-tree-select-node` to build nodes, using `treeData` property instead to improve component performance
- Deprecated `scopedSlots` `slots` custom rendering node, and replace it with `v-slot:title` to improve ease of use, avoid slot configuration expansion, and also avoid slot conflicts
## 4.2.2
- `Table`
- 🐞 Fix TreeSelect placeholder slot invalid [#7545](https://github.com/vueComponent/ant-design-vue/issues/7545)
- 🐞 Fix Tree slot responsive invalid issue [40ad45](https://github.com/vueComponent/ant-design-vue/commit/40ad45bc05b2bf9d0a2445d9f6ff365468ba90b7)
- 🐞 Fix FloatButton target type error issue [#7576](https://github.com/vueComponent/ant-design-vue/issues/7576)
- 🐞 Fix FormItem className error issue [#7582](https://github.com/vueComponent/ant-design-vue/issues/7582)
- 🐞 Fix Input Cannot input problem under lazy [#7543](https://github.com/vueComponent/ant-design-vue/issues/7543)
- 🐞 Fix the problem that placeholder is not hidden when inputting Chinese in Select [#7611](https://github.com/vueComponent/ant-design-vue/issues/7611)
- 🐞 Fix the problem that the pop-up window flashes when clicking the preset option in DatePicker [#7550](https://github.com/vueComponent/ant-design-vue/issues/7550)
- Removed the `rowSelection.hideDefaultSelections` property of Table, please use `SELECTION_ALL` and `SELECTION_INVERT` in `rowSelection.selections` instead, [custom options](/components/table/#components-table-demo- row-selection-custom).
- Removed Column slots and replaced them with `v-slot:headerCell` `v-slot:headerCell` `v-slot:bodyCell` `v-slot:customFilterDropdown` `v-slot:customFilterIcon` to improve ease of use , To avoid slot configuration expansion, but also to avoid the problem of slot conflicts
- Added expandFixed to control whether the expanded icon is fixed
- Added the showSorterTooltip header whether to display the tooltip for the next sort.
- Added sticky for setting sticky head and scroll bar
- Added rowExpandable to set whether to allow row expansion
- New slot headerCell is used to personalize the header cell
- Added slot bodyCell for personalized cell
- New slot customFilterDropdown is used to customize the filter menu, which needs to be used with `column.customFilterDropdown`
- Added slot customFilterIcon for custom filter icons
- New slot emptyText is used to customize the display content of empty data
- Added slot summary for the summary column
## 4.2.1
- `DatePicker` `TimePicker` `Calendar`
- 🐞 fix Input clear action error [#7523](https://github.com/vueComponent/ant-design-vue/issues/7523)
- By default, a more lightweight dayjs is used to replace momentjs. If your project is too large and uses a lot of momentjs methods, you can refer to the document [Custom Time Library](/docs/vue/replace-date-cn), Replace with momentjs.
- UI interaction adjustment, its antd 4.x interaction specification
## 4.2.0
- `Form` The main goal of this update is to improve performance. If you don't have custom form controls, you can almost ignore this part
- 🌟 Optimize the textColor change when the layout component switches to dark mode [#7498](https://github.com/vueComponent/ant-design-vue/issues/7498)
- 🌟 Tooltip added arrow hidden configuration [#7459](https://github.com/vueComponent/ant-design-vue/issues/7459)
- 🌟 Optimize Table hover performance [#7451](https://github.com/vueComponent/ant-design-vue/issues/7451)
- 🐞 Fixed the problem of changing the model during useForm verification, resulting in verification errors [#ffd4d8](https://github.com/vueComponent/ant-design-vue/commit/ffd4d8fe927f9ea40cbb6358ad997c447bd9a74e)
- 🐞 Fix Tabs folding calculation error issue [#7491](https://github.com/vueComponent/ant-design-vue/issues/7491)
- 🐞 Fix Qrcode missing type hint issue [#7502](https://github.com/vueComponent/ant-design-vue/issues/7502)
- 🐞 Fix Menu rendering error under SSR [#7349](https://github.com/vueComponent/ant-design-vue/issues/7349)
- 🐞 Fix Select and Cascader rendering errors under SSR [#7377](https://github.com/vueComponent/ant-design-vue/issues/7377)
- 🐞 Fix AutoComplete missing option slot declaration issue [#7396](https://github.com/vueComponent/ant-design-vue/issues/7396)
- 🐞 Fix Textarea autoSize not taking effect [#7478](https://github.com/vueComponent/ant-design-vue/issues/7478)
- 🐞 Fix Paginations Enter key triggering two page turns [#7368](https://github.com/vueComponent/ant-design-vue/issues/7368)
- 🐞 Fix the problem of Chinese input in the input box [#7391](https://github.com/vueComponent/ant-design-vue/issues/7391)[#7516](https://github.com/vueComponent/ant- design-vue/issues/7516)
- 🐞 Fix Carousel beforeChange current parameter error issue [#7419](https://github.com/vueComponent/ant-design-vue/issues/7419)
- Since version 3.0, Form.Item no longer hijacks child elements, but automatically checks through provider/inject dependency injection. This method can improve component performance, and there is no limit to the number of child elements. The same is true for child elements. It can be a high-level component that is further encapsulated.
## 4.1.2
You can reference [Customized Form Controls](#components-form-demo-customized-form-controls)
- 🐞 Fix table resize error reporting under vue 3.4 [#7291](https://github.com/vueComponent/ant-design-vue/issues/7291)
- 🐞 Fix the problem that the Segmented title attribute is not displayed [#7302](https://github.com/vueComponent/ant-design-vue/issues/7302)
But it also has some disadvantages:
## 4.1.1
1. If the custom component wants Form.Item to be verified and displayed, you need to inject `const {id, onFieldChange, onFieldBlur} = useFormItemContext()` and call the corresponding method.
- 🌟 QRcode adds scanned status [#7242](https://github.com/vueComponent/ant-design-vue/issues/7242)
- 🐞 Fix css prefix issue in nuxt [#7256](https://github.com/vueComponent/ant-design-vue/issues/7256)
- 🐞 Fix dropdown closing issue [#7246](https://github.com/vueComponent/ant-design-vue/issues/7246)
- 🐞 Fix divider vertical dashed not display issue [#7218](https://github.com/vueComponent/ant-design-vue/issues/7218)
- 🐞 Fix hook mode message console warning issue [#7281](https://github.com/vueComponent/ant-design-vue/issues/7281)
- 🐞 Fix table expansion error reporting under vue 3.4 [#7265](https://github.com/vueComponent/ant-design-vue/issues/7265)
- 🐞 Fix table group filter status error issue [#7233](https://github.com/vueComponent/ant-design-vue/issues/7233)
2. A Form.Item can only collect the data of one form item. If there are multiple form items, it will cause collection confusion, for example,
## 4.1.0
```html
<a-form-item>
<a-input name="a"></a-input>
<a-input name="b"></a-input>
</a-form-item>
```
- 🐞 support vue 3.4 [#7239](https://github.com/vueComponent/ant-design-vue/issues/7239)
As above Form.Item does not know whether to collect `name="a"` or `name=`b``, you can solve this kind of problem in the following two ways:
## 4.0.8
The first is to use multiple `a-form-item`:
- 🐞 Fix theme responsiveness failure issue under Nuxt [#7180](https://github.com/vueComponent/ant-design-vue/issues/7180)
- 🐞 Fix error reporting caused by Wave [#7108](https://github.com/vueComponent/ant-design-vue/issues/7108)
- 🐞 Fix Upload disabled inheritance issue [#7110](https://github.com/vueComponent/ant-design-vue/issues/7110)
- 🐞 Fix Tooltip popupAlign not taking effect [#7112](https://github.com/vueComponent/ant-design-vue/issues/7112)
- 🐞 Fix Typography flashing problem [#7146](https://github.com/vueComponent/ant-design-vue/issues/7146)
- 🐞 Fix the issue that RangePicker prevIcon nextIcon does not take effect [#7127](https://github.com/vueComponent/ant-design-vue/issues/7127)
- 🐞 Fixed the issue of watermark not monitoring child element changes [#7149](https://github.com/vueComponent/ant-design-vue/issues/7149)
- 🐞 Fix Menu animation missing issue [#7130](https://github.com/vueComponent/ant-design-vue/issues/7130)
- 🐞 Fix the cursor change issue when TextArea autosize [#7121](https://github.com/vueComponent/ant-design-vue/issues/7121)
```html
<a-form-item>
<a-input name="a"></a-input>
<a-form-item><a-input name="b"></a-input></a-form-item>
</a-form-item>
```
## 4.0.7
The second way is to wrap it with a custom component and call `useFormItemContext` in the custom component, It is equivalent to merging multiple form items into one.
- 🌟 Added Flex component [#7052](https://github.com/vueComponent/ant-design-vue/issues/7052)
- 🌟 ConfigProvider adds wave configuration [#7036](https://github.com/vueComponent/ant-design-vue/issues/7036)
- 🌟 Watermark supports dark mode [#7067](https://github.com/vueComponent/ant-design-vue/issues/7067)
- 🐞 Fix Space duplicate Key problem [#7048](https://github.com/vueComponent/ant-design-vue/issues/7048)
- 🐞 Fix Upload disabled priority error issue [#7047](https://github.com/vueComponent/ant-design-vue/issues/7047)
- 🐞 Fix Carousel rendering error in jsx [#7077](https://github.com/vueComponent/ant-design-vue/issues/7077)
- 🐞 Fix Message offset position problem [#7093](https://github.com/vueComponent/ant-design-vue/issues/7093)
- 🐞 Fix the problem of animation failure when using Collapse custom prefix [#7074](https://github.com/vueComponent/ant-design-vue/issues/7074)
```html
<script>
// custom component
import { Form } from 'ant-desing-vue';
export default {
name: 'custom-name',
setup() {
const formItemContext = Form.useFormItemContext();
},
};
</script>
```
## 4.0.6
```html
<a-form-item>
<custom-com>
<a-input name="a"></a-input>
<a-input name="b"></a-input>
</custom-com>
</a-form-item>
```
- 🐞 Fix the Dropdown onVisibleChange failure issue introduced in 4.0.4 [#7031](https://github.com/vueComponent/ant-design-vue/issues/7031)
Third, the component library provides an `a-form-item-rest` component, which will prevent data collection. You can put form items that do not need to be collected and verified into this component. It is the same as the first This method is very similar, but it does not generate additional dom nodes.
## 4.0.5
```html
<a-form-item>
<a-input name="a"></a-input>
<a-form-item-rest><a-input name="b"></a-input></a-form-item-rest>
</a-form-item>
```
- 🐞 Fix cssinjs performance issue [#7023](https://github.com/vueComponent/ant-design-vue/issues/7023)
## 2.2.8
## 4.0.4
`2021-09-17`
- 🌟 Added esm target file
- 🌟 Added tooltip attribute to FormItem [#7014](https://github.com/vueComponent/ant-design-vue/issues/7014)
- 🐞 Fix useMessage getContainer not taking effect [#6942](https://github.com/vueComponent/ant-design-vue/issues/6942)
- 🐞 Fix the problem of Image triggering onPreviewVisibleChange event multiple times [#6945](https://github.com/vueComponent/ant-design-vue/issues/6945)
- 🐞 Fix the problem that Checkbox global disabled does not take effect [#6970](https://github.com/vueComponent/ant-design-vue/issues/6970)
- 🐞 Fix Drawer contentWrapperStyle not taking effect [#6983](https://github.com/vueComponent/ant-design-vue/issues/6983)
- 🐞 Optimize Select Dropdown and other drop-down list scroll bar display hidden logic [#6987](https://github.com/vueComponent/ant-design-vue/issues/6987)
- 🐞 Fix the problem of hiding when there are components such as input in the drop-down list such as Select Dropdown [#7020](https://github.com/vueComponent/ant-design-vue/issues/7020)
- 🌟 Upload method supports patch [#4637](https://github.com/vueComponent/ant-design-vue/issues/4637)
- 🌟 List gutter supports array [d2b721](https://github.com/vueComponent/ant-design-vue/commit/d2b72143f0e15c8716b4ea8f68b2b72eff5cf510)
- 🐞 Fix Modal type error [#4632](https://github.com/vueComponent/ant-design-vue/issues/4632)
- 🐞 Fix the problem that AutoComplete cannot reset undefined [741718](https://github.com/vueComponent/ant-design-vue/commit/741718a0f92c790266e7a07d8d129c5673344a7e)
- 🐞 Fix the missing style of Tag closed icon [#4649](https://github.com/vueComponent/ant-design-vue/issues/4649)
- 🐞 Fix the problem that the TreeSelect clear button does not display under special conditions [#4655](https://github.com/vueComponent/ant-design-vue/issues/4655)
- 🐞 Fix useForm immdiate not working issue [#4646](https://github.com/vueComponent/ant-design-vue/issues/4646)
## 4.0.3
## 2.2.7
- 🐞 Fix the problem of style loss under shadow Dom [#6912](https://github.com/vueComponent/ant-design-vue/issues/6912)
- 🐞 Upgrade Icon dependency and fix icon css missing problem under shadow Dom [#6914](https://github.com/vueComponent/ant-design-vue/issues/6914)
`2021-09-08`
## 4.0.2
- 🌟 Menu supports overflowedIndicator slot [#4515](https://github.com/vueComponent/ant-design-vue/issues/4515)
- 🌟 useForm supports dynamic rule [#4498](https://github.com/vueComponent/ant-design-vue/issues/4498)
- 🌟 Select supports Number type [#4570](https://github.com/vueComponent/ant-design-vue/issues/4570)
- 🐞 Fix the warning problem caused by css zoom [#4554](https://github.com/vueComponent/ant-design-vue/issues/4554)
- 🐞 Fix Mentions input Chinese error report [#4524](https://github.com/vueComponent/ant-design-vue/issues/4524)
- 🐞 Fix the issue that AutoComplete does not support global prefixCls [#4566](https://github.com/vueComponent/ant-design-vue/issues/4566)
- 🐞 Fix Table nested table error report [#4600](https://github.com/vueComponent/ant-design-vue/issues/4600)
- 🐞 Fix MenuItem danger property under Dropdown has no style problem [#4618](https://github.com/vueComponent/ant-design-vue/issues/4618)
- 🐞 Fix Modal.xxx and other methods passing appContext invalid problem [#4627](https://github.com/vueComponent/ant-design-vue/issues/4627)
- 🐞 Fix some TS type errors
- 🐞 Fix useMessage causing body to be removed [#6880](https://github.com/vueComponent/ant-design-vue/issues/6880)
- 🐞 Fix the problem that the water ripple effect does not disappear after Button loading is switched [#6895](https://github.com/vueComponent/ant-design-vue/issues/6895)
- 🐞 Fixed the problem that flip does not reset after Image is closed [#6913](https://github.com/vueComponent/ant-design-vue/issues/6913)
- 🐞 Fix ImageGroup animation effect loss problem [#6898](https://github.com/vueComponent/ant-design-vue/issues/6898)
- 🐞 Fix Modal missing onUpdate:open attribute declaration [#6876](https://github.com/vueComponent/ant-design-vue/issues/6876)
- 🐞 Fixed the issue of multiple clicks being triggered at the edge of Transfer's Checkbox [#6902](https://github.com/vueComponent/ant-design-vue/issues/6902)
## 2.2.6
## 4.0.1
`2021-08-12`
- 🌟 FloatButton add Badge support [#6738](https://github.com/vueComponent/ant-design-vue/issues/6738)
- 🌟 Image preview zoom in and out sensitivity adjustment [#6784](https://github.com/vueComponent/ant-design-vue/issues/6784)
- 🌟 Add flip feature to Image [#6785](https://github.com/vueComponent/ant-design-vue/issues/6785)
- 🌟 Add App component to provide context [#6735](https://github.com/vueComponent/ant-design-vue/issues/6735)
- 🌟 Style extraction feature for SSR [#6757](https://github.com/vueComponent/ant-design-vue/issues/6757)
- 🌟 Support px2rem [#6817](https://github.com/vueComponent/ant-design-vue/issues/6817)
- 🌟 Tag supports borderless mode [#6819](https://github.com/vueComponent/ant-design-vue/issues/6819)
- 🌟 Avatar group mode supports shape [#6822](https://github.com/vueComponent/ant-design-vue/issues/6822)
- 🌟 AutoComplete supports borderless and custom clearIcon [#6829](https://github.com/vueComponent/ant-design-vue/issues/6829)
- 🌟 InputPassword supports controlled visible [#6863](https://github.com/vueComponent/ant-design-vue/issues/6863)
- 🐞 Fix the style misalignment problem when InputGroup is large [#6866](https://github.com/vueComponent/ant-design-vue/issues/6866)
- 🐞 Fix the problem that Checkable Tag cannot customize class [#6854](https://github.com/vueComponent/ant-design-vue/issues/6854)
- 🐞 Fix the rendering problem in Tabs animation mode [#6855](https://github.com/vueComponent/ant-design-vue/issues/6855)
- 🐞 Fix the problem that the Image height attribute does not take effect [#6840](https://github.com/vueComponent/ant-design-vue/issues/6840)
- 🐞 Fix InputNumber trigger mouseup event [#6772](https://github.com/vueComponent/ant-design-vue/issues/6772)
- 🐞 Fix the Dropdown style problem when Tabs are collapsed [#6757](https://github.com/vueComponent/ant-design-vue/issues/6757)
- 🐞 Fix Table expandedRowRender property does not take effect [#6783](https://github.com/vueComponent/ant-design-vue/issues/6783)
- 🐞 Fix dayjs not packaged into dist [#6767](https://github.com/vueComponent/ant-design-vue/issues/6767)
- 🐞 Fix clipPath browser compatibility issue [#6770](https://github.com/vueComponent/ant-design-vue/issues/6770)
- 🐞 Fix Carousel autoplay responsive problem [#6768](https://github.com/vueComponent/ant-design-vue/issues/6768)
- 🐞 Fix PageHeader ghost style problem [#6761](https://github.com/vueComponent/ant-design-vue/issues/6761)
- 🐞 Fix Checkbox not triggering Form validation [#6741](https://github.com/vueComponent/ant-design-vue/issues/6741)
- 🐞 Fix the problem that the Input prefix attribute does not take effect [#6810](https://github.com/vueComponent/ant-design-vue/issues/6810)
- 🐞 Fix Badge style problem in Avatar [#6874](https://github.com/vueComponent/ant-design-vue/issues/6874)
- 🐞 Fix `Table` expanded list rendering problem [#4507](https://github.com/vueComponent/ant-design-vue/issues/4507)
- 🐞 Fix `Rate` custom `character` slot not taking effect [#4509](https://github.com/vueComponent/ant-design-vue/issues/4509)
- 🐞 Add resize-observer-polyfill to fix the problem of reporting errors in low versions of Chrome [#4508](https://github.com/vueComponent/ant-design-vue/issues/4508)
## 4.0
## 2.2.5
### 🔥🔥🔥 4.0 official version released 🔥🔥🔥
`2021-08-11`
- 🌟 `Select` supports customizing nodes through option slots [68c1f4](https://github.com/vueComponent/ant-design-vue/commit/68c1f4550108a3a6bbe4f1b2c5c168523fd6c84a)
- 🐞 Fix the problem that the pop-up window component in the development environment does not display in the lower version of chrome, and avoid the pop-up window flashing [#4409](https://github.com/vueComponent/ant-design-vue/issues/4409)
- 🐞 Fix the problem of not scrolling to the active position when `Select` is opened [ccb240](https://github.com/vueComponent/ant-design-vue/commit/ccb24016c07632f49550646c971060c402586c67)
## 2.2.4
`2021-08-10`
- 🌟 Support Vue@3.2 [#4490](https://github.com/vueComponent/ant-design-vue/issues/4490)
- 🌟 Automatically hide the horizontal scroll bar of `Table` [#4484](https://github.com/vueComponent/ant-design-vue/issues/4484)
- 🐞 Fix the issue of `Progress` trailColor not taking effect [#4483](https://github.com/vueComponent/ant-design-vue/issues/4483)
## 2.2.3
`2021-08-07`
- 🌟 Use `position: sticky` for the fixed column of `Table` to improve performance and solve the problem of misalignment in some scenes [38569c](https://github.com/vueComponent/ant-design-vue/commit/38569c28c7eb4eaa34f2cc096982daea901062d4)
- 🌟 `Collapse` supports number type key [#4405](https://github.com/vueComponent/ant-design-vue/issues/4405)
- 🌟 Optimize the flickering problem of `Tabs` when selected under windows [#4241](https://github.com/vueComponent/ant-design-vue/issues/4241)
- 🌟 `InputPassword` supports global setting prefixCls [#4430](https://github.com/vueComponent/ant-design-vue/issues/4430)
- 🐞 Fix `Select` cannot scroll issue [#4396](https://github.com/vueComponent/ant-design-vue/issues/4396)
- 🐞 Fix `Badge` error reporting under ssr [#4384](https://github.com/vueComponent/ant-design-vue/issues/4384)
- 🐞 Fix the issue of invalid data fields in `Form` [#4435](https://github.com/vueComponent/ant-design-vue/issues/4435)
- 🐞 Fix an error when the child element of `FormItem` is a native label [#4383](https://github.com/vueComponent/ant-design-vue/issues/4383)
- 🐞 Fix the error when `TreeSelect` customize title through slot [#4459](https://github.com/vueComponent/ant-design-vue/issues/4459)
## 2.2.2
`2021-07-11`
- 🌟 Switch added checkedValue and unCheckedValue attributes to customize checked binding value [#4329](https://github.com/vueComponent/ant-design-vue/issues/4329)
- 🐞 Fix the issue of missing SubMenu animation [#4325](https://github.com/vueComponent/ant-design-vue/issues/4325)
- 🐞 Fix that there is no red box problem when TimePicker validates the error under Form [#4331](https://github.com/vueComponent/ant-design-vue/issues/4331)
- 🐞 Fix UploadDragger does not support vite-plugin-components on-demand loading problem [#4334](https://github.com/vueComponent/ant-design-vue/issues/4334)
- 🐞 Fix the error when TreeSelect customize title through slot [1152e8](https://github.com/vueComponent/ant-design-vue/commit/1152e8cd71cadf9e8fb4797916adca20c0e35974)
- 🐞 Fix the dropdown submenu style loss issue [#4351](https://github.com/vueComponent/ant-design-vue/issues/4351)
- TS
- Fix the type error of Table in ts 4.3.5 version [#4296](https://github.com/vueComponent/ant-design-vue/issues/4296)
- Improve notification type [#4346](https://github.com/vueComponent/ant-design-vue/issues/4346)
## 2.2.1
`2021-07-06`
- 🐞 Fix the issue that the Space component does not take effect in browsers that do not support flex
- 🐞 Fix the issue of DatePicker triggering scrolling under safari [#4323](https://github.com/vueComponent/ant-design-vue/issues/4323)
## 2.2.0
`2021-07-06`
- 🎉 Refactor the Button component, remove type="danger", and add the `danger` attribute [#4291](https://github.com/vueComponent/ant-design-vue/issues/4291)
- 🐞 Fix Rate component not updating issue [#4294](https://github.com/vueComponent/ant-design-vue/issues/4294)
- 🐞 Fix Tree replaceFields error report [#4298](https://github.com/vueComponent/ant-design-vue/issues/4298)
- 🐞 Fix Modal missing parentContext type problem [#4305](https://github.com/vueComponent/ant-design-vue/issues/4305)
## 2.2.0-rc.1
`2021-06-29`
- 🌟 Change babel configuration, smaller build package size
- 🌟 Form provides the useForm function natively, and we will deprecate the @ant-design-vue/use library
- 🐞 Fix the issue that the Form validateFirst property does not trigger reject when there are multiple validation rules [#4273](https://github.com/vueComponent/ant-design-vue/issues/4273)
- 🐞 Fix List circular references causing errors in Vite [#4263](https://github.com/vueComponent/ant-design-vue/issues/4263)
- 🐞 Fix the missing item attribute problem in Menu event callback [#4290](https://github.com/vueComponent/ant-design-vue/issues/4290)
## 2.2.0-beta.6
`2021-06-26`
- 🌟 Menu performance optimization [e8b957](https://github.com/vueComponent/ant-design-vue/commit/e8b95784eb1ee0554b0d6b17bdc14e18775f2ae6)
- 🐞 Fix `Layout` `RangePicker` `WeekPicker` `Textarea` on-demand loading failure
## 2.2.0-beta.5
`2021-06-24`
- 🎉 Support vite-plugin-components to be loaded on demand
- 🎉 Refactor the List component
- 🌟 Select adds responsive folding option [656d14](https://github.com/vueComponent/ant-design-vue/commit/656d14fc4e4ef0f781324438f0d58cfb6816d583)
- 🐞 Fix the problem that the virtual list cannot be scrolled when the Select dynamic update option [b2aa49d](https://github.com/vueComponent/ant-design-vue/commit/b2aa49d064a83c6ce786a6bb4cd9fc5266a5964d)
- 🐞 Fix the incorrect location of Select keyboard events [604372](https://github.com/vueComponent/ant-design-vue/commit/604372ff2da521dd580ad5229f7dbd445c1c6190)
- 🐞 Fix the issue that AutoComplete does not support options slot [#4012](https://github.com/vueComponent/ant-design-vue/issues/4012)
## 2.2.0-beta.4
`2021-06-21`
- 🎉 Refactor Descriptions component [#4219](https://github.com/vueComponent/ant-design-vue/issues/4219)
- 🐞 Fix the issue that Countdown does not trigger the finish event [#4222](https://github.com/vueComponent/ant-design-vue/issues/4222)
- 🐞 Fix ConfigProvider reporting errors under vue 3.1 [#4225](https://github.com/vueComponent/ant-design-vue/issues/4225)
- 🐞 Fix the problem of using SubMenu under Dropdown to report an error [#4205](https://github.com/vueComponent/ant-design-vue/issues/4205)
- 🐞 Fix Col type error [#4226](https://github.com/vueComponent/ant-design-vue/issues/4226)
- 🐞 Fix the problem that onEnd is not triggered when Typography is out of focus [#4227](https://github.com/vueComponent/ant-design-vue/issues/4227)
- 🐞 Fix ImagePreview style loss problem [#4231](https://github.com/vueComponent/ant-design-vue/issues/4231)
## 2.2.0-beta.3
`2021-06-11`
- 🎉 Refactor Breadcrumb, Statistic, Tag components
- 🌟 Statistic supports loading attribute
- 🐞 Fix the problem of Menu rendering multiple sub-components to improve performance [6ae707](https://github.com/vueComponent/ant-design-vue/commit/6ae707edf508a9c5e8dca7dacf1410de5251bcf8)
- 🐞 Fix FormItem custom class invalidation [617e53](https://github.com/vueComponent/ant-design-vue/commit/617e534fda2ae6d468b5e9d3eb43370f8a4b0000)
- 🐞 Fix MenuDivider class error [#4195](https://github.com/vueComponent/ant-design-vue/issues/4195)
- 🐞 Fix Tag and Image type errors
- 🐞 Fix the issue of missing component animations such as Modal [#4191](https://github.com/vueComponent/ant-design-vue/issues/4191)
- 🐞 Fix the issue that Select class cannot be dynamically updated [#4194](https://github.com/vueComponent/ant-design-vue/issues/4194)
- 🐞 Fix the problem that the Dropdown mail expands and cannot be collapsed by clicking [#4198](https://github.com/vueComponent/ant-design-vue/issues/4198)
- 🐞 Fix the issue of missing some export methods of FormItem [#4183](https://github.com/vueComponent/ant-design-vue/issues/4183)
## 2.2.0-beta.2
`2021-06-08`
- 🐞 Fix PageHeader display extension problem [4de73](https://github.com/vueComponent/ant-design-vue/commit/4de7737907d485d3dd3be44b70e599cc53edb171)
- 🐞 Fix the problem that some components cannot be rendered normally under Vue3.1[#4173](https://github.com/vueComponent/ant-design-vue/issues/4173)
- 🐞 Fix Menu.Divider name error problem [6c5c84](https://github.com/vueComponent/ant-design-vue/commit/6c5c84a3fc4b8abcd7aed0922852a64e0ac293c7)
## 2.2.0-beta.1
`2021-06-17`
- 🔥🔥🔥 Virtual Table independent library released https://www.npmjs.com/package/@surely-vue/table, this component is an independent library, the document example is not yet complete, it is a completely ts-developed component , There are good type hints, there are API documents on npm, those who are in a hurry can explore and use it, here is an online experience example, https://store.antdv.com/pro/preview/list/big-table-list
- 🔥🔥🔥 Refactored a large number of components, the source code is more readable, the performance is better, and the ts type is more comprehensive -Refactored components in this version Anchor, Alert, Avatar, Badge, BackTop, Col, Form, Layout, Menu, Space, Spin, Switch, Row, Result, Rate
- 🎉 Menu
- Better performance [#3300](https://github.com/vueComponent/ant-design-vue/issues/3300)
- Fix the problem of incorrect highlighting [#4053](https://github.com/vueComponent/ant-design-vue/issues/4053)
- Fix console invalid warning [#4169](https://github.com/vueComponent/ant-design-vue/issues/4169)
- Easier to use, simpler to use single file recursion [#4133](https://github.com/vueComponent/ant-design-vue/issues/4133)
- 💄 icon icon needs to be passed through slot
- Skeleton
- 🌟 Support Skeleton.Avatar placeholder component.
- 🌟 Support Skeleton.Button placeholder component.
- 🌟 Support Skeleton.Input placeholder component.
- 💄 Destructive update
- The `a-menu-item` and `a-sub-menu` icons need to be passed through the slot, and the icon is not automatically obtained through the sub-node
- row gutter supports row-wrap, no need to use multiple rows to divide col
- `Menu` removes `defaultOpenKeys` and `defaultSelectedKeys`; `Switch` removes `defaultChecked`; `Rate` removes `defaultValue`; Please be cautious to use the defaultXxx-named attributes of other unrefactored components, and they will be removed in future versions.
- 🌟 Added Avatar.Group component
- 🐞 Fix AutoComplete filterOptions not taking effect [#4170](https://github.com/vueComponent/ant-design-vue/issues/4170)
- 🐞 Fix Select automatic width invalidation problem [#4118](https://github.com/vueComponent/ant-design-vue/issues/4118)
- 🐞 Fix the lack of internationalized files in dist [#3684](https://github.com/vueComponent/ant-design-vue/issues/3684)
## 2.1.6
`2021-05-13`
- 🐞 Use vue@3.0.10 to rebuild to avoid console warning [#3998](https://github.com/vueComponent/ant-design-vue/issues/3998)
## 2.1.5
`2021-05-12`
- 🐞 Fix SSR time reporting error [#3983](https://github.com/vueComponent/ant-design-vue/issues/3983)
## 2.1.4
`2021-05-09`
- 🐞 Fix `Table` scrolling misalignment issue [#4045](https://github.com/vueComponent/ant-design-vue/issues/4045)
- 🐞 Fix `Typography` editable mode triggering link jump issue [#4105](https://github.com/vueComponent/ant-design-vue/issues/4105)
- 🐞 Fix the issue that `Carousel` variableWidth does not take effect [#3977](https://github.com/vueComponent/ant-design-vue/issues/3977)
- 🐞 Fix the problem that `TreeSelect` cannot delete parent and child nodes at the same time through the keyboard [#3508](https://github.com/vueComponent/ant-design-vue/issues/3508)
- 🐞 Fix some types of errors
## 2.1.3
`2021-04-25`
- 🎉🎉🎉 remove ads during npm installation
- 🐞 `Select`
- Fix the first issue of default activation [#3842](https://github.com/vueComponent/ant-design-vue/issues/3842)
- Fix group display abnormal problem [#3841](https://github.com/vueComponent/ant-design-vue/issues/3841)
- Fix scrolling abnormal issue after dynamically updating selections [#3972](https://github.com/vueComponent/ant-design-vue/issues/3972)
- 🐞 Fix the issue that `Checkbox` triggers twice `update:checked` [#3838](https://github.com/vueComponent/ant-design-vue/issues/3838)
- 🌟 `Table` column group supports fixed [#3882](https://github.com/vueComponent/ant-design-vue/issues/3882)
- 🌟 `Table` column supports v-for [#3934](https://github.com/vueComponent/ant-design-vue/issues/3934)
- 🐞 Fix the problem that `Table` displays horizontal scroll bar on windows [6d33d6](https://github.com/vueComponent/ant-design-vue/commit/6d33d60d2bca98825f274e48bcc3badd1857f742)
- 🌟 `Form` scrollToFirstError supports option parameter passing [#3918](https://github.com/vueComponent/ant-design-vue/issues/3918)
- 🐞 Fix the issue of `Calendar` month selector displaying wrong characters [#3915](https://github.com/vueComponent/ant-design-vue/issues/3915)
- 🌟 Refactor the `Switch` component and remove the defaultChecked attribute [#3885](https://github.com/vueComponent/ant-design-vue/issues/3885)
- 🐞 Fix the process exception when using Vite [#3930](https://github.com/vueComponent/ant-design-vue/issues/3930)
- 🐞 Fix `Radio` shadow occlusion problem [#3955](https://github.com/vueComponent/ant-design-vue/issues/3955)
- 🐞 Fix the issue that span does not take effect in `Form` inline mode [#3862](https://github.com/vueComponent/ant-design-vue/issues/3862)
- 🐞 Fix the issue that `Cascader` keydown selection does not take effect [#958](https://github.com/vueComponent/ant-design-vue/issues/958)
- 🐞 Fix `Image` preview function failure problem [#3701](https://github.com/vueComponent/ant-design-vue/issues/3701)
- 🐞 Fix some TS type issues
## 2.1.2
`2021-03-28`
- 🌟 Recompile with Vue 3.0.9, compatible with 3.0.7 and below
## 2.1.1
`2021-03-27`
- 🌟 Compatible with Vue 3.0.8, note: Due to the destructive update of 3.0.8, 2.1.1 is not compatible with versions below 3.0.7 [vue#3493](https://github.com/vuejs/vue-next/issues /3493)
- 🐞 Fix Modal.confirm missing closable ts type [#3684](https://github.com/vueComponent/ant-design-vue/issues/3845)
- 🐞 Fix upload custom method not working issue [#3843](https://github.com/vueComponent/ant-design-vue/issues/3843)
## 2.1.0
`2021-03-20`
- 🎉🎉🎉 Added `Typography` component [#3807](https://github.com/vueComponent/ant-design-vue/issues/3807)
- 🌟 Modal method adds close icon customization [#3753](https://github.com/vueComponent/ant-design-vue/issues/3753)
- 🐞 Fix missing build files containing internationalization [#3684](https://github.com/vueComponent/ant-design-vue/issues/3684)
- 🐞 Fix Drawer error after destruction [#848d64](https://github.com/vueComponent/ant-design-vue/commit/848d6497e68c87566790dfa889a1913199a6699a)
- 🐞 Fix BackTop incorrect position when KeepAlive is activated [#3803](https://github.com/vueComponent/ant-design-vue/issues/3803)
- 🐞 Fix the problem that the TreeNode class does not take effect [#3822](https://github.com/vueComponent/ant-design-vue/issues/3822)
- 🐞 Fix Table tags being an array error issue [#3812](https://github.com/vueComponent/ant-design-vue/issues/3812)
- 🐞 Fix the sorting issue when Table custom filterIcon is triggered [#3819](https://github.com/vueComponent/ant-design-vue/issues/3819)
- 🐞 Fix Select style misalignment under Form [#3781](https://github.com/vueComponent/ant-design-vue/issues/3781)
## 2.0.1
`2021-02-27`
- 🌟 `Badge` adds `Ribbon` [#3681](https://github.com/vueComponent/ant-design-vue/issues/3681)
- 🌟 Adjust the trigger order of `SearchInput` search event [#3725](https://github.com/vueComponent/ant-design-vue/issues/3725)
- 🐞 Fix the stuck problem when `Table` is destroyed [#3531](https://github.com/vueComponent/ant-design-vue/issues/3531)
- 🐞 Fix the issue of less file introduced in `Menu` css [#3678](https://github.com/vueComponent/ant-design-vue/issues/3678)
- 🐞 Fix the problem of `Alert` custom icon misalignment [#3712](https://github.com/vueComponent/ant-design-vue/issues/3712)
## 2.0.0
`2021-02-06`
- 🎉🎉🎉 2.0 official version released
- 🎉🎉🎉 support dark theme [#3410](https://github.com/vueComponent/ant-design-vue/issues/3410)
- 🎉🎉🎉 The new version of the document is online, use the Composition API to completely reconstruct the document example, and provide the TS and JS dual version source code
- 🌟 Refactor the `Alert` component using Composition API [#3654](https://github.com/vueComponent/ant-design-vue/pull/3654)
- 🌟 `Tooltip` supports custom colors [#3603](https://github.com/vueComponent/ant-design-vue/issues/3603)
- 🐞 Fix the problem that `TimePicker` does not automatically scroll to the selected position [#ab7537](https://github.com/vueComponent/ant-design-vue/commit/ab75379f0c2f5e54ab7c348284a7391939ab5aaf)
## 2.0.0-rc.9
`2021-01-24`
- 🌟 `@ant-design/icons-vue` upgrade to 6.0, use es module by default
- 🌟 `Tabs` adds `centered` centered mode [#3501](https://github.com/vueComponent/ant-design-vue/issues/3501)
- 🐞 `Progress` Add opacity animation [#3505](https://github.com/vueComponent/ant-design-vue/issues/3505)
- 🐞 Fix an error when installing npm [#3515](https://github.com/vueComponent/ant-design-vue/issues/3515)
- 🐞 Fix the problem of `Breadcrumn` split line not displaying [#3522](https://github.com/vueComponent/ant-design-vue/issues/3522)
- 🐞 Fix `Radio` uncontrolled issue [#3517](https://github.com/vueComponent/ant-design-vue/issues/3517)
- 🐞 Fix `FormItem` not wrapping issue [#3538](https://github.com/vueComponent/ant-design-vue/issues/3538)
- 🐞 Fix `Carousel` `pauseOnDotsHover` not working problem [#3519](https://github.com/vueComponent/ant-design-vue/issues/3519)
- 🐞 Fix `Input.Search` `class` not working issue [#3541](https://github.com/vueComponent/ant-design-vue/issues/3541)
- 🐞 Fix the issue that `InputNumber` triggers the change event multiple times under Microsoft input method [#3550](https://github.com/vueComponent/ant-design-vue/issues/3550)
- 🐞 Fix the problem that the keyboard can still be switched in the disabled state of `Tabs` [#3575](https://github.com/vueComponent/ant-design-vue/issues/3575)
- 🐞 Fix the issue that `Switch` does not take effect in the table [#3512](https://github.com/vueComponent/ant-design-vue/issues/3512)
## 2.0.0-rc.8
`2021-01-07`
- 🌟 Support Vite 2 [#3490](https://github.com/vueComponent/ant-design-vue/issues/3490)
- 🌟 Use Composition API to refactor Affix component [#3447](https://github.com/vueComponent/ant-design-vue/issues/3447)
- 🐞 Fix Image component type definition error [#3488](https://github.com/vueComponent/ant-design-vue/issues/3488)
- 🐞 Upgrade icons-vue Fix IconFont component type error [#3474](https://github.com/vueComponent/ant-design-vue/issues/3474)
- 🐞 Fix Tooltip arrow style error in less 4 [#3477](https://github.com/vueComponent/ant-design-vue/issues/3477)
- 🐞 Fix DatePicker type definition parsing error under Vue 3.0.5 [#bf7c62](https://github.com/vueComponent/ant-design-vue/commit/bf7c62f457fc14624881f69c5baf9a62219383f7)
## 2.0.0-rc.7
`2020-12-28`
- 🐞 Fix Switch `change`、`click` not work [#3453](https://github.com/vueComponent/ant-design-vue/issues/3453)
## 2.0.0-rc.6
`2020-12-27`
- 🌟 Support Less 4 [#3449](https://github.com/vueComponent/ant-design-vue/issues/3449)
- 🌟 Added Image component [#3235](https://github.com/vueComponent/ant-design-vue/issues/3235)
- 🌟 Functional component, add displayName attribute [#3445](https://github.com/vueComponent/ant-design-vue/issues/3445)
- 🐞 Message adds custom class style function [#3443](https://github.com/vueComponent/ant-design-vue/issues/3443)
- 🐞 Fix the initial disabled state of the Tabs component does not take effect [#3366](https://github.com/vueComponent/ant-design-vue/issues/3366)
- 🐞 Fix Slider accuracy issue [#3346](https://github.com/vueComponent/ant-design-vue/issues/3346)
- 🐞 Fix the incorrect scroll height of Select [#3419](https://github.com/vueComponent/ant-design-vue/issues/3419)
- 🐞 Fix the problem that Input small is too small and the height is 2px [#3396](https://github.com/vueComponent/ant-design-vue/issues/3396)
- 🐞 Fix the problem that TreeSelect triggers two change events
- 🐞 Fix the endless loop problem of TreeSelect defining title through slot
- 🐞 Fix the problem that Drawer handle slot triggers two click events
- 🌟 Added Checkbox and Switch event declaration
## 2.0.0-rc.5
`2020-12-13`
- 🐞 Fix the undefined warning problem of this.dom output in the Drawer component console
- 🐞 Fix Menu in Vue 3.0.3 and above versions, display confusion problem [#3354](https://github.com/vueComponent/ant-design-vue/issues/3354)
## 2.0.0-rc.4
`2020-12-10`
- 🌟 Input.Password supports custom icons [#3320](https://github.com/vueComponent/ant-design-vue/issues/3320)
- 🐞 Fix the issue that the Select Option click event does not trigger [#4ea00d](https://github.com/vueComponent/ant-design-vue/commit/4ea00d3a70d0afd7bea07f814df03ab7d0b25ebd)
- 🐞 Fix the problem that the dark theme does not work after the Menu exceeds the width [#10f35a](https://github.com/vueComponent/ant-design-vue/commit/10f35a1fa510de91e9484b07fcfff253920cee29)
- 🐞 Fix Menu console vue key some waring [#520d6a](https://github.com/vueComponent/ant-design-vue/commit/520d6a5e85eb391e5294211c9d7b2ea598c59119)
- 🐞 Remove console passive prompt log [#8d1669](https://github.com/vueComponent/ant-design-vue/commit/8d1669b8896d84a67c61d3a00d0b13c42d70f30f)
## 2.0.0-rc.3
`2020-12-05`
- 🐞 Fix the problem of functional components reporting type errors in Vue 3.0.3 [#f5cf7e](https://github.com/vueComponent/ant-design-vue/commit/f5cf7e0920a51f0ac024046996c99260aa41becf)
- 🐞 Fix Menu display error after detecting width [#3262](https://github.com/vueComponent/ant-design-vue/issues/3262)
- 🐞 Fix Menu subMenuOpenDelay subMenuCloseDelay not working problem [#3291](https://github.com/vueComponent/ant-design-vue/pull/3291)
- 🐞 Fix TreeSelect stack overflow problem [#28aeea](https://github.com/vueComponent/ant-design-vue/commit/28aeea6f0b142ed68950a3738f7cf2c1581a7a5b)
- 🐞 Fix Input custom style class being overwritten [#3273](https://github.com/vueComponent/ant-design-vue/issues/3273)
- 🐞 Fix InputNumber parse error in production environment [#3249](https://github.com/vueComponent/ant-design-vue/issues/3249)
## 2.0.0-rc.2
`2020-11-24`
- 🌟 Optimize Menu performance, enable lazy loading by default [#3243](https://github.com/vueComponent/ant-design-vue/pull/3243)
- 🌟 Tag supports defining icon via slot [#3185](https://github.com/vueComponent/ant-design-vue/pull/3185)
- 🌟 Small type table changed to borderless [#3221](https://github.com/vueComponent/ant-design-vue/issues/3221)
- 🌟 @ant-design/icons-vue upgraded to 5.1.6, support SSR, support spin attribute shorthand
- 🐞 Fix the style problem of Alert's close button in Safari [#3184](https://github.com/vueComponent/ant-design-vue/issues/3184)
- 🐞 Fix the problem of Notification top attribute type error [#3187](https://github.com/vueComponent/ant-design-vue/issues/3187)
- 🐞 Fix DirectoryTree custom icon does not take effect [#3183](https://github.com/vueComponent/ant-design-vue/issues/3183)
- 🐞 Fix Button loading delay not taking effect [#3194](https://github.com/vueComponent/ant-design-vue/issues/3194)
- 💄 Select optionFilterProp no longer supports filtering by children [#3204](https://github.com/vueComponent/ant-design-vue/issues/3204)
- 🐞 Fix Select labelInValue error when reporting [#3216](https://github.com/vueComponent/ant-design-vue/issues/3216)
- 🐞 Fix ConfigProvider transformCellText missing issue [#3206](https://github.com/vueComponent/ant-design-vue/issues/3206)
- 🐞 Fix the style disorder problem when Dropdown Button is mixed together [#3244](https://github.com/vueComponent/ant-design-vue/issues/3244)
- 🐞 Fix RangePicker custom width invalidation issue [#3244](https://github.com/vueComponent/ant-design-vue/issues/3245)
- 🐞 Fix multiple errors or missing Ts types
## 2.0.0-rc.1
`2020-11-14`
- 🎉🎉🎉
- 🌟 Menu cancel the default lazy loading, improve the first animation effect, optimize the Bezier curve function, and make it smoother [#3177](https://github.com/vueComponent/ant-design-vue/pull/3177)
- 🐞 Fix Select search function failure problem [#3144](https://github.com/vueComponent/ant-design-vue/issues/3144)
- 🐞 Fix the Drawer component does not have automatic focus, which can not be closed directly by the ESC button [#3148](https://github.com/vueComponent/ant-design-vue/issues/3148)
- 🐞 Fix the incorrect position of popover elements in Popover [#3147](https://github.com/vueComponent/ant-design-vue/issues/3147)
- 🐞 Fix CountDown not updating problem [#3170](https://github.com/vueComponent/ant-design-vue/pull/3170)
- 🐞 Fix multiple errors or missing Ts types
## 2.0.0-beta.15
`2020-11-08`
- 🌟 Optimize the Menu animation to make it smoother [#3095](https://github.com/vueComponent/ant-design-vue/issues/3095)
- 🌟 Optimize VirtualList to avoid invalid render [#2e61e9](https://github.com/vueComponent/ant-design-vue/commit/2e61e9cb502f2bb6910f59abfb483fd2517e594f)
- 🐞 Fix Menu overflowedIndicator not taking effect [#689113](https://github.com/vueComponent/ant-design-vue/commit/689113b3c9c19e929607567a4c8252c6511bff5c)
- 🐞 Select
- Fix the issue that dropdownRender does not support slot [#3098](https://github.com/vueComponent/ant-design-vue/issues/3098)
- Fix the issue of abnormal empty values in tag mode [#3100](https://github.com/vueComponent/ant-design-vue/issues/3100)
- Fix the problem that the selected item is not updated in single selection mode [#3099](https://github.com/vueComponent/ant-design-vue/issues/3099)
- Fix foucs status not taking effect in special scenarios [#3099](https://github.com/vueComponent/ant-design-vue/issues/3099)
- 🐞 Fix DatePicker default formatting invalid problem [#3091](https://github.com/vueComponent/ant-design-vue/issues/3091)
- 🐞 Fix Table customRow configuration event not taking effect [#3121](https://github.com/vueComponent/ant-design-vue/issues/3121)
- 🐞 Fix the style of TreeSelect search box [ee4cd3c](https://github.com/vueComponent/ant-design-vue/commit/ ee4cd3c35a84658cbbb148ce368bc247a927d528)
- 🐞 Fix Ts type error or missing problem
## 2.0.0-beta.13
`2020-11-02`
- 🐞 Fix npm install error report [#3080](https://github.com/vueComponent/ant-design-vue/issues/3080)
- 🐞 Fix Select maxPlaceHolder display error problem [#3085](https://github.com/vueComponent/ant-design-vue/issues/3085)
- 🐞 Fix the pop-up component, the pop-up position is not updated [#3085](https://github.com/vueComponent/ant-design-vue/issues/3085)
- 🐞 Fix the warning problem when Table data is empty [#3082](https://github.com/vueComponent/ant-design-vue/issues/3082)
- 🐞 Fix Input display multiple borders in Form [#3084](https://github.com/vueComponent/ant-design-vue/issues/3084)
## 2.0.0-beta.12
`2020-11-01`
- 🐞 Fix dist/antd.css missing component style issue [#3069](https://github.com/vueComponent/ant-design-vue/issues/3069)
- 🐞 Fix Input style issue [#3074](https://github.com/vueComponent/ant-design-vue/issues/3074)
- 🐞 Fix Form layout="vertical" style issue [#3075](https://github.com/vueComponent/ant-design-vue/issues/3075)
- 🐞 Fix Select cannot open popup problem [#3070](https://github.com/vueComponent/ant-design-vue/issues/3070)
## 2.0.0-beta.11
`2020-10-30`
- 🎉🎉🎉 Refactored Select and AutoComplete components, supports virtual lists, and greatly improves performance
- 🔥🔥🔥 Use Typescript to refactor all components, type support is more friendly
- 🔥 Optimize the underlying animation components, with better performance and smoother
- 🌟 Textarea component added showCount to support word count function
- 🌟 Recursive Menu component, supports arbitrary nesting of other elements [#1452](https://github.com/vueComponent/ant-design-vue/issues/1452)
- 🇮🇪 Add Irish language internationalization support
- 🐞 Fix webpack 5 compatibility issues.
- 🐞 Fix the problem that the Upload method attribute does not take effect [#2837](https://github.com/vueComponent/ant-design-vue/issues/2837)
- 🐞 Fix Table component filter not supporting number type problem [#3052](https://github.com/vueComponent/ant-design-vue/issues/3052)
- 🐞 Fix Table fixed column ellipsis not working issue [#2916](https://github.com/vueComponent/ant-design-vue/issues/2916)
- 🐞 Fix Table custom expandIcon not taking effect [#3013](https://github.com/vueComponent/ant-design-vue/issues/3013)
- 🐞 Fix the problem that TreeSelect cannot customize slot [#2827](https://github.com/vueComponent/ant-design-vue/issues/2827)
- 🛎 Change Avatar's srcSet to srcset
## 2.0.0-beta.10
`2020-09-24`
- 🌟 Update Vue dependency to release version
- 🐞 Fix the problem that Menu does not collapse in Layout [#2819](https://github.com/vueComponent/ant-design-vue/issues/2819)
- 🐞 Fix a warning issue when switching Tabs [#2865](https://github.com/vueComponent/ant-design-vue/issues/2865)
- 🐞 Fix the problem that the input box does not trigger the change event when compositionend
- 🐞 Fix the problem that the Upload button does not disappear [#2884](https://github.com/vueComponent/ant-design-vue/issues/2884)
- 🐞 Fix upload custom method not working issue [#2837](https://github.com/vueComponent/ant-design-vue/issues/2837)
- 🐞 Fix some ts type errors
## 2.0.0-beta.8
- 🐞 Fix ts types error
## 2.0.0-beta.7
- 🐞 Fix the problem that Descriptions Item does not support v-for [#2793](https://github.com/vueComponent/ant-design-vue/issues/2793)
- 🐞 Fix Modal button loading effect not working problem [9257c1](https://github.com/vueComponent/ant-design-vue/commit/9257c1ea685db4339239589153aee3189d0434fe)
- 🐞 Fix the problem that the Steps component cannot be clicked when using v-model [ec7309](https://github.com/vueComponent/ant-design-vue/commit/ec73097d9b6ea8e2f2942ac28853c19191ca3298)
- 🌟 Checkbox, Radio add event declaration
- 🐞 Fix ts type error [802446](https://github.com/vueComponent/ant-design-vue/commit/8024469b8832cfc4fe85498b639bfb48820531aa)
## 2.0.0-beta.6
- 🐞 Fix the problem that TreeSelectNode subcomponent TreeSelectNode is not registered
## 2.0.0-beta.5
- 🔥 Support Vite.
## 2.0.0-beta.4
- 🌟 Remove polyfills that are no longer used
- 🐞 Fix the problem of calling `Modal` afterClose twice
- 🐞 Supplement the declaration that ts type files lack native attributes
## 2.0.0-beta.3
- 🔥 Support Typescript.
- 🔥 Added `Space` component.
- 🐞 Fix the problem that some components cannot use css scope [4bdb24](https://github.com/vueComponent/ant-design-vue/commit/4bdb241aa674b50fafa29b3b98e291643f2a06cc).
- 🐞 Fix `List.Meta` registration failure problem [03a42a](https://github.com/vueComponent/ant-design-vue/commit/03a42a5b35e7d42a39aedb1aba8346995be2c27e)
- 🐞 Fix the problem of misalignment in the fixed column of Table [#1493](https://github.com/vueComponent/ant-design-vue/issues/1493)
- 🐞 Fix the problem that the `Button` is not vertically centered [bd71e3](https://github.com/vueComponent/ant-design-vue/commit/bd71e3806b73881f9a95028982d17a10b2cd0b5c)
- 🐞 Fix `Tabs` multiple departure `change` event issue [8ed937](https://github.com/vueComponent/ant-design-vue/commit/8ed937344a57142a575e5272f50933c9c4459a43)
## 2.0.0-beta.2
### Design specification adjustment
- Basic rounded corner adjustment, changed from unified `2px` to four-level rounded corners, which are `2px` `4px` `6px` `8px` respectively, which are applied to different scenarios, for example, the rounded corners of the default size Button are adjusted to `6px`.
- Main color adjustment, changed from `#1890ff` to `#1677ff`.
- Overall shadow adjustment, from the original three-level shadow adjustment to two levels, which are used for resident page components (such as Card) and interactive feedback (such as Dropdown).
- Adjust the internal spacing of some components.
- Overall de-wireframing.
- Adjust the row height from `1.5`(`21px`) to `1.5715`(`22px`).
- Basic round corner adjustment, changed from `4px` to `2px`.
- The color brightness of the dividing line is reduced, from `#E8E8E8` to `#F0F0F0`.
- The default background color of Table is changed from transparent to white.
### Add 5 new components
### Compatibility adjustment
- Segmented segment controller
- WaterMark watermark
- QrCode QR code
- FloatButton floating button
- Tour roaming guide
- The minimum supported version of Vue is Vue 3.0.
### Technical adjustments
#### Adjusted API
- Deprecated less and adopted CSS-in-JS to better support dynamic themes.
- All less files are removed, and less variables no longer support leaking.
- css files are no longer included in the product. Since CSS-in-JS supports importing on demand, the original `ant-design-vue/dist/antd.css` has also been removed. If you need to reset some basic styles, please import `ant-design-vue/dist/reset .css`.
- If you need to reset the style of the component and don't want to introduce `ant-design-vue/dist/reset.css` to pollute the global style, you can try to use [App component](/components/app), to solve the problem that native elements do not have ant-design-vue specification style.
- Removed css variables and dynamic theme schemes built on top of it.
- LocaleProvider has been deprecated in 3.x (use `<ConfigProvider locale />` instead), we have completely removed the related directories `ant-design-vue/es/locale-provider`, `ant- design-vue/lib/locale-provider`.
- `babel-plugin-import` is no longer supported, CSS-in-JS itself has the ability to load on demand, no longer need plug-in support.
- Removed LocaleProvider, please use `ConfigProvider` instead.
- Removed the afterClose property of Tag.
- Merged FormModel and Form, see the Form refactoring part below for details.
- `tabIndex`, `maxLength`, `readOnly`, `autoComplete`, `autoFocus` are changed to all lowercase.
- In order to use the slot more friendly in template syntax, all related to xxxRender, renderXxxx are changed to single parameter, involving `itemRender`, `renderItem`, `customRender`, `dropdownRender`, `dateCellRender`, `dateFullCellRender`, `monthCellRender`, `monthFullCellRender`, `renderTabBar`.
- All the places where scopedSlots are configured are changed to slots.
- `{ on, props, attrs, ... }` configuration is flattened, such as `{ props: {type:'xxx'}, on: {click: this.handleClick}}` changed to `{ type: 'xxx', onClick: this.handleClick }`, related fields: `okButtonProps`, `cancelButtonProps`.
- Change xxx.sync to v-model:xxx
- v-model is changed to v-model:xxx, which specifically involves components:
#### Component API adjustments
- The components changed from v-model to v-model:checked are: CheckableTag, Checkbox, Switch
- The components changed from v-model to v-model:value are: Radio, Mentions, CheckboxGroup, Rate, DatePicker
- The components changed from v-model to v-model:visible are: Tag, Popconfirm, Popove, Tooltip, Moda, Dropdown
- The components changed from v-model to v-model:activeKey are: Collaps, Tabs
- The components changed from v-model to v-model:current are: Steps
- The components changed from v-model to v-model:selectedKeys are: Menu
- The classname API of the component popup is unified to `popupClassName`, and similar APIs such as `dropdownClassName` will be replaced.
#### Icon Upgrade
- AutoComplete component
- Cascader component
- Select component
- TreeSelect component
- TimePicker component
- DatePicker component
- Mentions component
In `ant-design-vue@1.2.0`, we introduced the svg icon ([Why use the svg icon?](https://github.com/ant-design/ant-design/issues/10353)). The icon API that uses string naming cannot be loaded on demand, so the svg icon file is fully introduced, which greatly increases the size of the packaged product. In 2.0, we adjusted the icon usage API to support tree shaking, reducing the default package size by approximately 150 KB (Gzipped).
- The controlled visibility API of the component popup is unified as `open`, and `visible` and other similar APIs will be replaced.
- Drawer component `visible` becomes `open`.
- Modal component `visible` becomes `open`.
- Dropdown component `visible` becomes `open`.
- Tooltip component `visible` becomes `open`.
- Tag component `visible` has been removed.
- Slider component `tooltip` related API converges to `tooltip` property.
- Table component `filterDropdownVisible` changed to `filterDropdownOpen`.
- `getPopupContainer`: All `getPopupContainer` needs to ensure that the returned div is unique.
- Drawer `style` and `class` are migrated to the Drawer popup area, and the original attributes are replaced by `rootClassName` and `rootStyle`.
The old way of using Icon will be obsolete:
#### Component refactoring and removal
```html
<a-icon type="smile" />
<a-button icon="smile" />
```
- Remove the `locale-provider` directory. `LocaleProvider` has been removed in v4, please use `ConfigProvider` instead.
In 2.0, an on-demand introduction method will be adopted:
- Remove `xxxl` breakpoint attribute in grid layout. `xxxl` attribute has been removed in v4, you can use [theme customization](/docs/vue/customize-theme) to modify `screen[XS|SM|MD|LG|XL|XXL]` to modify the break Point value achieved.
```html
<template>
<smile-outlined />
<a-button>
<template v-slot:icon><smile-outlined /></template>
</a-buttom>
</template>
<script>
import SmileOutlined from'@ant-design/icons-vue/SmileOutlined';
export default {
components: {
SmileOutlined
}
}
</script>
```
- The BackTop component was deprecated in `4.0.0` and moved to the FloatButton floating button. If needed, it can be imported from FloatButton.
#### Component refactoring
### [Upgrade Guide](/docs/vue/migration-v4)
In 1.x, we provide two form components, Form and FormModel. The original Form component uses v-decorator for data binding. In Vue2, we use context to force update components. However, in Vue3, due to the introduction of patchFlag, etc. Optimization method, forced refresh will destroy the performance advantage brought by patchFlag. So in version 2.0, we merged Form and FormModel, retained the use of FormModel, enriched related functions, and renamed it to Form.
## 3.x
Involving changes:
Visit [GitHub](https://github.com/vueComponent/ant-design-vue/blob/3.x/CHANGELOG.zh-CN.md) `3.x` Change Log。
- Added `scrollToFirstError`, `name`, `validateTrigger` properties for Form, added `finish`, `finishFailed` events, and added `scrollToField` method.
- Form.Item adds `validateFirst`, `validateTrigger`, and discards the `prop` attribute, and replaces it with `name`.
- The nested field path uses an array. In the past version, we used. To represent the nested path (such as user.name to represent {user: {name:''} }). However, in some back-end systems, the variable name will also carry .. This causes users to need additional codes for conversion. Therefore, in the new version, nested paths are represented by arrays to avoid wrong handling behaviors (such as ['user','name']).
- validateFields no longer supports callback. validateFields will return a Promise object, so you can perform corresponding error handling through async/await or then/catch. It is no longer necessary to determine whether errors is empty:
## 2.x
```js
// v1
// eslint-disable-next-line no-undef,no-unused-vars
validateFields((err, value) => {
if (!err) {
// Do something with value
}
});
```
Visit [GitHub](https://github.com/vueComponent/ant-design-vue/blob/2.x/CHANGELOG.zh-CN.md) `2.x` Change Log。
Change to
```js
// v2
// eslint-disable-next-line no-undef,no-unused-vars
validateFields().then(values => {
// Do something with value
});
```
## 1.x

View File

@ -10,229 +10,713 @@
---
## 4.2.6
## 3.0.0-alpha.1
- 🐞 修复 Modal 组件在 chrome 下aria-hidden 报错问题 [#7823](https://github.com/vueComponent/ant-design-vue/issues/7823)
- 🐞 修复 Safari 下自带输入法 input 组件输入中文时,自动填写小数点问题 [#7918](https://github.com/vueComponent/ant-design-vue/issues/7918)
- 🐞 修复 InputNumber 组件 disabled 样式问题 [#7776](https://github.com/vueComponent/ant-design-vue/issues/7776)
- 🐞 修复 Select 无法失焦问题 [#7819](https://github.com/vueComponent/ant-design-vue/issues/7819)
`2021-10-07`
## 4.2.5
- 🌟 重构 `Tabs` [#4732](https://github.com/vueComponent/ant-design-vue/issues/4732)
- 移除 `prevClick`、`nextClick` 事件,使用 `tabScroll` 事件替代
- 废弃 `tabBarExtraContent` 插槽,使用 rightExtra 插槽替换,同时新增 `leftExtra` 插槽
- 新增 `addIcon`、`closeIcon`、`moreIcon` 插槽
- 🌟 重构 `Card`,废弃 tabList slots 配置,使用 customTab 插槽统一配置 [#4732](https://github.com/vueComponent/ant-design-vue/issues/4732)
- 🌟 重构 `Drawer`
- 新增 `autofocus` `contentWrapperStyle` `footerStyle` `headerStyle` `push` `size` `forceRender` 等属性
- 新增 `closeIcon` `extra` `footer` 等插槽
- 废弃 `afterVisibleChange` 属性,使用同名事件替代
- 🐞 修复 `Table` pagination 没有响应式变化问题 [1add0d](https://github.com/vueComponent/ant-design-vue/commit/1add0d251cd35aa2c55404f7a60f1531425490c1)
- 🐞 修复 `notification` 样式错位问题 [#4703](https://github.com/vueComponent/ant-design-vue/issues/4703)
- 🐞 修复 `Tree` fieldsName 导致的选中、拖拽等异常 [#4726](https://github.com/vueComponent/ant-design-vue/issues/4726)
- 🐞 修复 Empty 组件内存泄漏问题
- 🐞 修复 Image width & height 属性不生效问题
## 3.0.0-alpha.0
## 4.2.4
`2021-09-24`
- 🐞 修复 Wave 内存泄漏问题
🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
## 4.2.3
- 文档开源,如果您的公司不能外网访问,可以私有化部署,但不可以传播,不可以商业化
- 移除了 Transfer 的 `lazy` 属性,它并没有起到真正的优化效果。
- 移除了 Select 的 `combobox` 模式,请使用 `AutoComplete` 替代。
- 废弃 Button.Group请使用 `Space` 代替。
- `Timeline.Item` 新增 label
- `Steps` 新增 `responsive`、`percent`
- `Collapse` 新增 `ghost`、`collapsible`
- `Popconfirm` 新增 `cancelButton`、`okButton`, 以及 `esc` 按键隐藏
- `ConfigProvider` 新增 ConfigProvider.config定义 `Modal.xxx` `message` `notification` 的配置
- `Tree` `TreeSlelct`
- 🌟 TourStep 自定义 Button支持函数 children [#7628](https://github.com/vueComponent/ant-design-vue/pull/7628)
- 🐞 修复 Select 和 Cascader 搜索多选模式下,输入值被隐藏问题 [#7640](https://github.com/vueComponent/ant-design-vue/issues/7640)
- 新增了虚拟滚动,废弃使用 `a-tree-node` `a-tree-select-node` 构建节点,使用 `treeData` 属性替代,提升组件性能
- 废弃 `scopedSlots` `slots` 自定义渲染节点,使用 `v-slot:title` 替换,提升易用性,避免插槽配置膨胀,同时也避免了插槽冲突问题
## 4.2.2
- `Table`
- 🐞 修复 TreeSelect placeholder 插槽无效 [#7545](https://github.com/vueComponent/ant-design-vue/issues/7545)
- 🐞 修复 Tree 插槽响应式无效问题 [40ad45](https://github.com/vueComponent/ant-design-vue/commit/40ad45bc05b2bf9d0a2445d9f6ff365468ba90b7)
- 🐞 修复 FloatButton target 类型错误问题 [#7576](https://github.com/vueComponent/ant-design-vue/issues/7576)
- 🐞 修复 FormItem className 错误问题 [#7582](https://github.com/vueComponent/ant-design-vue/issues/7582)
- 🐞 修复 Input lazy 下无法输入问题 [#7543](https://github.com/vueComponent/ant-design-vue/issues/7543)
- 🐞 修复 Select 输入中文时placeholder 未隐藏问题 [#7611](https://github.com/vueComponent/ant-design-vue/issues/7611)
- 🐞 修复 DatePicker 点击预设选项时,弹窗闪动问题 [#7550](https://github.com/vueComponent/ant-design-vue/issues/7550)
- 移除了 Table 的 `rowSelection.hideDefaultSelections` 属性,请在 `rowSelection.selections` 中使用 `SELECTION_ALL``SELECTION_INVERT` 替代,[自定义选择项](/components/table/#components-table-demo-row-selection-custom)。
- 移除了 Column slots分别使用 `v-slot:headerCell` `v-slot:headerCell` `v-slot:bodyCell` `v-slot:customFilterDropdown` `v-slot:customFilterIcon` 替换,提升易用性,避免插槽配置膨胀,同时也避免了插槽冲突问题
- 新增 expandFixed 控制展开图标是否固定
- 新增 showSorterTooltip 表头是否显示下一次排序的 tooltip 提示。
- 新增 sticky 用于设置粘性头部和滚动条
- 新增 rowExpandable 用于设置是否允许行展开
- 新增插槽 headerCell 用于个性化头部单元格
- 新增插槽 bodyCell 用于个性化单元格
- 新增插槽 customFilterDropdown 用于自定义筛选菜单,需要配合 `column.customFilterDropdown` 使用
- 新增插槽 customFilterIcon 用于自定义筛选图标
- 新增插槽 emptyText 用于自定义空数据时的显示内容
- 新增插槽 summary 用于总结栏
## 4.2.1
- `DatePicker` `TimePicker` `Calendar`
- 🐞 修复 Input 清空操作才报错问题 [#7523](https://github.com/vueComponent/ant-design-vue/issues/7523)
- 默认使用更加轻量级的 dayjs 替换 momentjs如果你的项目过大使用了大量的 momentjs 的方法,你可以参考文档[自定义时间库](/docs/vue/replace-date-cn),替换成 momentjs。
- UI 交互调整,对其 antd 4.x 交互规范
## 4.2.0
- `Form` 这次更新主要目标是提升性能,如果你没有自定义表单控件,几乎可以忽略该部分
- 🌟 优化 layout 组件切换 dark 模式时 textColor 变化 [#7498](https://github.com/vueComponent/ant-design-vue/issues/7498)
- 🌟 Tooltip 新增 arrow 隐藏配置 [#7459](https://github.com/vueComponent/ant-design-vue/issues/7459)
- 🌟 优化 Table hover 性能 [#7451](https://github.com/vueComponent/ant-design-vue/issues/7451)
- 🐞 修复 useForm 校验时更改 model导致校验错误问题 [#ffd4d8](https://github.com/vueComponent/ant-design-vue/commit/ffd4d8fe927f9ea40cbb6358ad997c447bd9a74e)
- 🐞 修复 Tabs 折叠计算错误问题 [#7491](https://github.com/vueComponent/ant-design-vue/issues/7491)
- 🐞 修复 Qrcode 缺少类型提示问题 [#7502](https://github.com/vueComponent/ant-design-vue/issues/7502)
- 🐞 修复 Menu 在 SSR 下渲染错误问题 [#7349](https://github.com/vueComponent/ant-design-vue/issues/7349)
- 🐞 修复 Select、Cascader 在 SSR 下渲染错误问题 [#7377](https://github.com/vueComponent/ant-design-vue/issues/7377)
- 🐞 修复 AutoComplete 缺少 option slot 声明问题 [#7396](https://github.com/vueComponent/ant-design-vue/issues/7396)
- 🐞 修复 Textarea autoSize 不生效问题 [#7478](https://github.com/vueComponent/ant-design-vue/issues/7478)
- 🐞 修复 Pagination 回车键触发两次翻页问题 [#7368](https://github.com/vueComponent/ant-design-vue/issues/7368)
- 🐞 修复输入框输入中文问题 [#7391](https://github.com/vueComponent/ant-design-vue/issues/7391)[#7516](https://github.com/vueComponent/ant-design-vue/issues/7516)
- 🐞 修复 Carousel beforeChange current 参数错误问题 [#7419](https://github.com/vueComponent/ant-design-vue/issues/7419)
- 自 3.0 版本以后Form.Item 不再劫持子元素,而是通过 provider / inject 依赖注入的方式进行自动校验,这种方式可以提高组件性能,子元素也不会限制个数,同样子元素也可以是进一步封装的高级组件。你可以参考[自定义表单控件示例](#components-form-demo-customized-form-controls)
## 4.1.2
但它同样会有一些缺点:
- 🐞 修复 table resize 在 vue 3.4 下报错问题 [#7291](https://github.com/vueComponent/ant-design-vue/issues/7291)
- 🐞 修复 Segmented title 属性不显示问题 [#7302](https://github.com/vueComponent/ant-design-vue/issues/7302)
1、自定义组件如果希望 Form.Item 进行校验展示,你需要 `const {id, onFieldChange, onFieldBlur} = useFormItemContext()` 注入,并调用相应的方法。
## 4.1.1
2、一个 Form.Item 只能收集一个表单项的数据,如果有多个表单项,会导致收集错乱,例如,
- 🌟 QRcode 新增 scanned 状态 [#7242](https://github.com/vueComponent/ant-design-vue/issues/7242)
- 🐞 修复 css prefix 在 nuxt 问题 [#7256](https://github.com/vueComponent/ant-design-vue/issues/7256)
- 🐞 修复 dropdown 关闭问题 [#7246](https://github.com/vueComponent/ant-design-vue/issues/7246)
- 🐞 修复 divider vertical dashed 不显示问题 [#7218](https://github.com/vueComponent/ant-design-vue/issues/7218)
- 🐞 修复 hook 模式 message 控制台 warning 问题 [#7281](https://github.com/vueComponent/ant-design-vue/issues/7281)
- 🐞 修复 table 展开在 vue 3.4 下报错问题 [#7265](https://github.com/vueComponent/ant-design-vue/issues/7265)
- 🐞 修复 table group 过滤状态错误问题 [#7233](https://github.com/vueComponent/ant-design-vue/issues/7233)
```html
<a-form-item>
<a-input name="a"></a-input>
<a-input name="b"></a-input>
</a-form-item>
```
## 4.1.0
如上 Form.Item 并不知道需要收集 `name="a"` 还是 `name=`b``,你可以通过如下三种方式去解决此类问题:
- 🐞 适配 vue 3.4 [#7239](https://github.com/vueComponent/ant-design-vue/issues/7239)
第一种,使用多个 `a-form-item`:
## 4.0.8
```html
<a-form-item>
<a-input name="a"></a-input>
<a-form-item><a-input name="b"></a-input></a-form-item>
</a-form-item>
```
- 🐞 修复在 Nuxt 下 theme 响应式失效问题 [#7180](https://github.com/vueComponent/ant-design-vue/issues/7180)
- 🐞 修复 Wave 引起的报错问题 [#7108](https://github.com/vueComponent/ant-design-vue/issues/7108)
- 🐞 修复 Upload disabled 继承问题 [#7110](https://github.com/vueComponent/ant-design-vue/issues/7110)
- 🐞 修复 Tooltip popupAlign 未生效问题 [#7112](https://github.com/vueComponent/ant-design-vue/issues/7112)
- 🐞 修复 Typography 闪动问题 [#7146](https://github.com/vueComponent/ant-design-vue/issues/7146)
- 🐞 修复 RangePicker prevIcon nextIcon 未生效问题 [#7127](https://github.com/vueComponent/ant-design-vue/issues/7127)
- 🐞 修复 watermark 未监听子元素变动问题 [#7149](https://github.com/vueComponent/ant-design-vue/issues/7149)
- 🐞 修复 Menu 动画丢失问题 [#7130](https://github.com/vueComponent/ant-design-vue/issues/7130)
- 🐞 修复 TextArea autosize 时光标变化问题 [#7121](https://github.com/vueComponent/ant-design-vue/issues/7121)
第二种,使用自定义组件包裹,并在自定义组件中调用 `useFormItemContext`,相当于把多个表单项合并成了一个
## 4.0.7
```html
<script>
// 自定义组件
import { Form } from 'ant-desing-vue';
export default {
setup() {
const formItemContext = Form.useFormItemContext();
},
};
</script>
```
- 🌟 新增 Flex 组件 [#7052](https://github.com/vueComponent/ant-design-vue/issues/7052)
- 🌟 ConfigProvider 新增 wave 配置 [#7036](https://github.com/vueComponent/ant-design-vue/issues/7036)
- 🌟 Watermark 支持暗黑模式 [#7067](https://github.com/vueComponent/ant-design-vue/issues/7067)
- 🐞 修复 Space 重复 Key 问题 [#7048](https://github.com/vueComponent/ant-design-vue/issues/7048)
- 🐞 修复 Upload disabled 优先级错误问题 [#7047](https://github.com/vueComponent/ant-design-vue/issues/7047)
- 🐞 修复 Carousel 在 jsx 中渲染错误问题 [#7077](https://github.com/vueComponent/ant-design-vue/issues/7077)
- 🐞 修复 Message 偏移位置问题 [#7093](https://github.com/vueComponent/ant-design-vue/issues/7093)
- 🐞 修复 Collapse 自定义 prefix 时动画失效问题 [#7074](https://github.com/vueComponent/ant-design-vue/issues/7074)
```html
<a-form-item>
<custom-com>
<a-input name="a"></a-input>
<a-input name="b"></a-input>
</custom-com>
</a-form-item>
```
## 4.0.6
第三种,组件库提供了一个 `a-form-item-rest` 组件,它会阻止数据的收集,你可以将不需要收集校验的表单项放到这个组件中即可,它和第一种方式很类似,但它不会产生额外的 dom 节点。
- 🐞 修复 4.0.4 引入的 Dropdown onVisibleChange 失效问题 [#7031](https://github.com/vueComponent/ant-design-vue/issues/7031)
```html
<a-form-item>
<a-input name="a"></a-input>
<a-form-item-rest><a-input name="b"></a-input></a-form-item-rest>
</a-form-item>
```
## 4.0.5
## 2.2.8
- 🐞 修复 cssinjs 性能问题 [#7023](https://github.com/vueComponent/ant-design-vue/issues/7023)
`2021-09-17`
## 4.0.4
- 🌟 Upload method 支持 patch [#4637](https://github.com/vueComponent/ant-design-vue/issues/4637)
- 🌟 List gutter 支持数组 [d2b721](https://github.com/vueComponent/ant-design-vue/commit/d2b72143f0e15c8716b4ea8f68b2b72eff5cf510)
- 🐞 修复 Modal 类型错误 [#4632](https://github.com/vueComponent/ant-design-vue/issues/4632)
- 🐞 修复 AutoComplete 无法重置 undefined 问题 [741718](https://github.com/vueComponent/ant-design-vue/commit/741718a0f92c790266e7a07d8d129c5673344a7e)
- 🐞 修复 Tag 关闭图标样式丢失问题 [#4649](https://github.com/vueComponent/ant-design-vue/issues/4649)
- 🐞 修复 TreeSelect 清楚按钮在特殊条件下不显示问题 [#4655](https://github.com/vueComponent/ant-design-vue/issues/4655)
- 🐞 修复 useForm immdiate 不生效问题 [#4646](https://github.com/vueComponent/ant-design-vue/issues/4646)
- 🌟 新增 esm 目标文件
- 🌟 FormItem 新增 tooltip 属性 [#7014](https://github.com/vueComponent/ant-design-vue/issues/7014)
- 🐞 修复 useMessage getContainer 不生效问题 [#6942](https://github.com/vueComponent/ant-design-vue/issues/6942)
- 🐞 修复 Image 多次触发 onPreviewVisibleChange 事件问题 [#6945](https://github.com/vueComponent/ant-design-vue/issues/6945)
- 🐞 修复 Checkbox 全局 disabled 不生效问题 [#6970](https://github.com/vueComponent/ant-design-vue/issues/6970)
- 🐞 修复 Drawer contentWrapperStyle 不生效问题 [#6983](https://github.com/vueComponent/ant-design-vue/issues/6983)
- 🐞 优化 Select Dropdown 等下拉列表滚动条显示隐藏逻辑 [#6987](https://github.com/vueComponent/ant-design-vue/issues/6987)
- 🐞 修复 Select Dropdown 等下拉列表中有 input 等组件时,隐藏问题 [#7020](https://github.com/vueComponent/ant-design-vue/issues/7020)
## 2.2.7
## 4.0.3
`2021-09-08`
- 🐞 修复 shadow Dom 下样式丢失问题 [#6912](https://github.com/vueComponent/ant-design-vue/issues/6912)
- 🐞 升级 Icon 依赖,修复 shadow Dom 下 icon css 丢失问题 [#6914](https://github.com/vueComponent/ant-design-vue/issues/6914)
- 🌟 Menu 支持 overflowedIndicator 插槽 [#4515](https://github.com/vueComponent/ant-design-vue/issues/4515)
- 🌟 useForm 支持动态 rule [#4498](https://github.com/vueComponent/ant-design-vue/issues/4498)
- 🌟 Select 支持 Number 类型 [#4570](https://github.com/vueComponent/ant-design-vue/issues/4570)
- 🐞 修复 css zoom 引起的 warning 问题 [#4554](https://github.com/vueComponent/ant-design-vue/issues/4554)
- 🐞 修复 Mentions 输入中文报错问题 [#4524](https://github.com/vueComponent/ant-design-vue/issues/4524)
- 🐞 修复 AutoComplete 不支持全局 prefixCls 问题 [#4566](https://github.com/vueComponent/ant-design-vue/issues/4566)
- 🐞 修复 Table 嵌套表格报错问题 [#4600](https://github.com/vueComponent/ant-design-vue/issues/4600)
- 🐞 修复 Dropdown 下的 MenuItem danger 属性无样式问题 [#4618](https://github.com/vueComponent/ant-design-vue/issues/4618)
- 🐞 修复 Modal.xxx 等方法传递 appContext 失效问题 [#4627](https://github.com/vueComponent/ant-design-vue/issues/4627)
- 🐞 修复一些 TS 类型错误
## 4.0.2
## 2.2.6
- 🐞 修复 useMessage 导致 body 被移除问题 [#6880](https://github.com/vueComponent/ant-design-vue/issues/6880)
- 🐞 修复 Button loading 切换后,水波纹效果不消失问题 [#6895](https://github.com/vueComponent/ant-design-vue/issues/6895)
- 🐞 修复 Image 关闭后 flip 没有重置问题 [#6913](https://github.com/vueComponent/ant-design-vue/issues/6913)
- 🐞 修复 ImageGroup 动画效果丢失问题 [#6898](https://github.com/vueComponent/ant-design-vue/issues/6898)
- 🐞 修复 Modal 缺少 onUpdate:open 属性声明 [#6876](https://github.com/vueComponent/ant-design-vue/issues/6876)
- 🐞 修复 Transfer 的 Checkbox 边缘处会触发多次 click 问题 [#6902](https://github.com/vueComponent/ant-design-vue/issues/6902)
`2021-08-12`
## 4.0.1
- 🐞 修复 `Table` 展开列表渲染错位问题 [#4507](https://github.com/vueComponent/ant-design-vue/issues/4507)
- 🐞 修复 `Rate` 自定义 `character` 插槽未生效问题 [#4509](https://github.com/vueComponent/ant-design-vue/issues/4509)
- 🐞 添加 resize-observer-polyfill, 修复在低版本 Chrome 下报错问题 [#4508](https://github.com/vueComponent/ant-design-vue/issues/4508)
- 🌟 FloatButton 添加 Badge 支持 [#6738](https://github.com/vueComponent/ant-design-vue/issues/6738)
- 🌟 Image 预览放大缩小灵敏度调整 [#6784](https://github.com/vueComponent/ant-design-vue/issues/6784)
- 🌟 Image 新增翻转特性 [#6785](https://github.com/vueComponent/ant-design-vue/issues/6785)
- 🌟 新增 App 组件,用于提供上下文 [#6735](https://github.com/vueComponent/ant-design-vue/issues/6735)
- 🌟 样式抽离特性用于 SSR [#6757](https://github.com/vueComponent/ant-design-vue/issues/6757)
- 🌟 支持 px2rem [#6817](https://github.com/vueComponent/ant-design-vue/issues/6817)
- 🌟 Tag 支持无边框模式 [#6819](https://github.com/vueComponent/ant-design-vue/issues/6819)
- 🌟 Avatar group 模式支持 shape [#6822](https://github.com/vueComponent/ant-design-vue/issues/6822)
- 🌟 AutoComplete 支持无边框和自定义 clearIcon [#6829](https://github.com/vueComponent/ant-design-vue/issues/6829)
- 🌟 InputPassword 支持受控 visible [#6863](https://github.com/vueComponent/ant-design-vue/issues/6863)
- 🐞 修复 InputGroup 在 large 时样式错位问题 [#6866](https://github.com/vueComponent/ant-design-vue/issues/6866)
- 🐞 修复 Checkable Tag 无法自定义 class 问题 [#6854](https://github.com/vueComponent/ant-design-vue/issues/6854)
- 🐞 修复 Tabs 动画模式下渲染问题 [#6855](https://github.com/vueComponent/ant-design-vue/issues/6855)
- 🐞 修复 Image height 属性不生效问题 [#6840](https://github.com/vueComponent/ant-design-vue/issues/6840)
- 🐞 修复 InputNumber 触发 mouseup 事件问题 [#6772](https://github.com/vueComponent/ant-design-vue/issues/6772)
- 🐞 修复 Tabs 折叠时 Dropdown 样式问题 [#6757](https://github.com/vueComponent/ant-design-vue/issues/6757)
- 🐞 修复 Table expandedRowRender 属性不生效 [#6783](https://github.com/vueComponent/ant-design-vue/issues/6783)
- 🐞 修复 dayjs 未打包进 dist 问题 [#6767](https://github.com/vueComponent/ant-design-vue/issues/6767)
- 🐞 解决 clipPath 浏览器兼容问题 [#6770](https://github.com/vueComponent/ant-design-vue/issues/6770)
- 🐞 修复 Carousel autoplay 响应式问题 [#6768](https://github.com/vueComponent/ant-design-vue/issues/6768)
- 🐞 修复 PageHeader ghost 样式问题 [#6761](https://github.com/vueComponent/ant-design-vue/issues/6761)
- 🐞 修复 Checkbox 没有触发 Form 校验问题 [#6741](https://github.com/vueComponent/ant-design-vue/issues/6741)
- 🐞 修复 Input prefix 属性未生效问题 [#6810](https://github.com/vueComponent/ant-design-vue/issues/6810)
- 🐞 修复 Badge 在 Avatar 中样式问题 [#6874](https://github.com/vueComponent/ant-design-vue/issues/6874)
## 2.2.5
## 4.0
`2021-08-11`
### 🔥🔥🔥 4.0 正式版发布 🔥🔥🔥
- 🌟 `Select` 支持通过 option 插槽定制化节点 [68c1f4](https://github.com/vueComponent/ant-design-vue/commit/68c1f4550108a3a6bbe4f1b2c5c168523fd6c84a)
- 🐞 修复开发环境下弹窗类组件在低版本 chrome 下,不显示问题,并避免弹窗闪动 [#4409](https://github.com/vueComponent/ant-design-vue/issues/4409)
- 🐞 修复 `Select` 打开时没有滚动到激活位置问题 [ccb240](https://github.com/vueComponent/ant-design-vue/commit/ccb24016c07632f49550646c971060c402586c67)
## 2.2.4
`2021-08-10`
- 🌟 支持 Vue@3.2 [#4490](https://github.com/vueComponent/ant-design-vue/issues/4490)
- 🌟 自动隐藏 `Table` 横向滚动条 [#4484](https://github.com/vueComponent/ant-design-vue/issues/4484)
- 🐞 修复 `Progress` trailColor 不生效问题 [#4483](https://github.com/vueComponent/ant-design-vue/issues/4483)
## 2.2.3
`2021-08-07`
- 🌟 `Table` 固定列使用 `position: sticky` 提升性能,解决部分场景不对齐问题 [38569c](https://github.com/vueComponent/ant-design-vue/commit/38569c28c7eb4eaa34f2cc096982daea901062d4)
- 🌟 `Collapse` 支持 number 类型 key [#4405](https://github.com/vueComponent/ant-design-vue/issues/4405)
- 🌟 优化 `Tabs` 在 windows 下选中时闪动问题 [#4241](https://github.com/vueComponent/ant-design-vue/issues/4241)
- 🌟 `InputPassword` 支持全局设置 prefixCls [#4430](https://github.com/vueComponent/ant-design-vue/issues/4430)
- 🐞 修复 `Select` 无法滚动问题 [#4396](https://github.com/vueComponent/ant-design-vue/issues/4396)
- 🐞 修复 `Badge` 在 ssr 下报错问题 [#4384](https://github.com/vueComponent/ant-design-vue/issues/4384)
- 🐞 修复 `Form` 多出无效数据字段问题 [#4435](https://github.com/vueComponent/ant-design-vue/issues/4435)
- 🐞 修复 `FormItem` 子元素是原生标签时报错问题 [#4383](https://github.com/vueComponent/ant-design-vue/issues/4383)
- 🐞 修复 `TreeSelect` 通过 slot 自定义 title 时报错问题 [#4459](https://github.com/vueComponent/ant-design-vue/issues/4459)
## 2.2.2
`2021-07-11`
- 🌟 Switch 新增 checkedValue、unCheckedValue 属性用于自定义 checked 绑定值 [#4329](https://github.com/vueComponent/ant-design-vue/issues/4329)
- 🐞 修复 SubMenu 动画丢失的问题 [#4325](https://github.com/vueComponent/ant-design-vue/issues/4325)
- 🐞 修复 TimePicker 在 Form 下验证错误时没有红框问题 [#4331](https://github.com/vueComponent/ant-design-vue/issues/4331)
- 🐞 修复 UploadDragger 不支持 vite-plugin-components 按需加载问题 [#4334](https://github.com/vueComponent/ant-design-vue/issues/4334)
- 🐞 修复 TreeSelect 通过 slot 自定义 title 时报错问题 [1152e8](https://github.com/vueComponent/ant-design-vue/commit/1152e8cd71cadf9e8fb4797916adca20c0e35974)
- 🐞 修复 Dropdown submenu 样式丢失问题 [#4351](https://github.com/vueComponent/ant-design-vue/issues/4351)
- TS
- 修复 Table 在 ts 4.3.5 版本下类型报错问题 [#4296](https://github.com/vueComponent/ant-design-vue/issues/4296)
- 完善 notification 类型 [#4346](https://github.com/vueComponent/ant-design-vue/issues/4346)
## 2.2.1
`2021-07-06`
- 🐞 修复 Space 组件在不支持 flex 的浏览器中样式不生效问题
- 🐞 修复 DatePicker 在 safari 下触发滚动问题 [#4323](https://github.com/vueComponent/ant-design-vue/issues/4323)
## 2.2.0
`2021-07-06`
- 🎉 重构 Button 组件,移除 type="danger",新增 `danger` 属性 [#4291](https://github.com/vueComponent/ant-design-vue/issues/4291)
- 🐞 修复 Rate 组件不更新问题 [#4294](https://github.com/vueComponent/ant-design-vue/issues/4294)
- 🐞 修复 Tree replaceFields 报错问题 [#4298](https://github.com/vueComponent/ant-design-vue/issues/4298)
- 🐞 修复 Modal 缺少 parentContext 类型问题 [#4305](https://github.com/vueComponent/ant-design-vue/issues/4305)
## 2.2.0-rc.1
`2021-06-29`
- 🌟 更改 babel 配置,较小构建包大小
- 🌟 Form 原生提供 useForm 功能,废弃 @ant-design-vue/use 库
- 🐞 修复 Form validateFirst 属性在多个校验规则时不触发 reject 问题 [#4273](https://github.com/vueComponent/ant-design-vue/issues/4273)
- 🐞 修复 List 循环引用导致 Vite 下报错问题 [#4263](https://github.com/vueComponent/ant-design-vue/issues/4263)
- 🐞 修复 Menu 事件回调缺少 item 属性问题 [#4290](https://github.com/vueComponent/ant-design-vue/issues/4290)
## 2.2.0-beta.6
`2021-06-26`
- 🌟 Menu 性能优化 [e8b957](https://github.com/vueComponent/ant-design-vue/commit/e8b95784eb1ee0554b0d6b17bdc14e18775f2ae6)
- 🐞 修复 Layout、RangePicker、WeekPicker、Textarea 按需加载失效
## 2.2.0-beta.5
`2021-06-24`
- 🎉 支持 vite-plugin-components 按需加载
- 🎉 重构 List 组件
- 🌟 Select 新增响应式折叠选项 [656d14](https://github.com/vueComponent/ant-design-vue/commit/656d14fc4e4ef0f781324438f0d58cfb6816d583)
- 🐞 修复 Select 动态更新选项时虚拟列表无法滚动问题 [b2aa49d](https://github.com/vueComponent/ant-design-vue/commit/b2aa49d064a83c6ce786a6bb4cd9fc5266a5964d)
- 🐞 修复 Select 键盘事件位置不正确问题 [604372](https://github.com/vueComponent/ant-design-vue/commit/604372ff2da521dd580ad5229f7dbd445c1c6190)
- 🐞 修复 AutoComplete 不支持 options slot 问题 [#4012](https://github.com/vueComponent/ant-design-vue/issues/4012)
## 2.2.0-beta.4
`2021-06-21`
- 🎉 重构 Descriptions 组件 [#4219](https://github.com/vueComponent/ant-design-vue/issues/4219)
- 🐞 修复 Countdown 不触发 finish 事件问题 [#4222](https://github.com/vueComponent/ant-design-vue/issues/4222)
- 🐞 修复 ConfigProvider 在 vue 3.1 下报错问题 [#4225](https://github.com/vueComponent/ant-design-vue/issues/4225)
- 🐞 修复 Dropdown 下使用 SubMenu 报错问题 [#4205](https://github.com/vueComponent/ant-design-vue/issues/4205)
- 🐞 修复 Col 类型错误 [#4226](https://github.com/vueComponent/ant-design-vue/issues/4226)
- 🐞 修复 Typography 失焦时不触发 onEnd 问题 [#4227](https://github.com/vueComponent/ant-design-vue/issues/4227)
- 🐞 修复 ImagePreview 样式丢失问题 [#4231](https://github.com/vueComponent/ant-design-vue/issues/4231)
## 2.2.0-beta.3
`2021-06-11`
- 🎉 重构 Breadcrumb、Statistic、Tag 组件
- 🌟 Statistic 支持 loading 属性
- 🐞 修复 Menu 渲染多次子组件问题,提升性能 [6ae707](https://github.com/vueComponent/ant-design-vue/commit/6ae707edf508a9c5e8dca7dacf1410de5251bcf8)
- 🐞 修复 FormItem 自定义 class 失效 [617e53](https://github.com/vueComponent/ant-design-vue/commit/617e534fda2ae6d468b5e9d3eb43370f8a4b0000)
- 🐞 修复 MenuDivider class 错误问题 [#4195](https://github.com/vueComponent/ant-design-vue/issues/4195)
- 🐞 修复 Tag、Image 类型错误
- 🐞 修复 Modal 等组件动画丢失问题 [#4191](https://github.com/vueComponent/ant-design-vue/issues/4191)
- 🐞 修复 Select class 不能动态更新问题 [#4194](https://github.com/vueComponent/ant-design-vue/issues/4194)
- 🐞 修复 Dropdown 邮件展开,不能点击收起的问题 [#4198](https://github.com/vueComponent/ant-design-vue/issues/4198)
- 🐞 修复 FormItem 缺少部分导出方法问题 [#4183](https://github.com/vueComponent/ant-design-vue/issues/4183)
## 2.2.0-beta.2
`2021-06-08`
- 🐞 修复 PageHeader 显示多余字符问题 [4de773](https://github.com/vueComponent/ant-design-vue/commit/4de7737907d485d3dd3be44b70e599cc53edb171)
- 🐞 修复部分组件不能在 Vue3.1 下不能正常渲染问题 [#4173](https://github.com/vueComponent/ant-design-vue/issues/4173)
- 🐞 修复 Menu.Divider 名称错误问题 [6c5c84](https://github.com/vueComponent/ant-design-vue/commit/6c5c84a3fc4b8abcd7aed0922852a64e0ac293c7)
## 2.2.0-beta.1
`2021-06-07`
- 🔥🔥🔥 虚拟 Table 独立库发布 https://www.npmjs.com/package/@surely-vue/table , 该组件是一个独立的库,目前文档示例尚未完善,他是一个完全 ts 开发的组件有较好的类型提示npm 上已有 API 文档着急使用的的可以摸索着用起来了这里有个在线体验示例https://store.antdv.com/pro/preview/list/big-table-list
- 🔥🔥🔥 重构大量组件源码更加易读性能更优ts 类型更加全面
- 本版本重构组件 Anchor、Alert、Avatar、Badge、BackTop、Col、Form、Layout、Menu、Space、Spin、Switch、Row、Result、Rate
- 🎉 Menu
- 性能更优 [#3300](https://github.com/vueComponent/ant-design-vue/issues/3300)
- 修复高亮不正确问题 [#4053](https://github.com/vueComponent/ant-design-vue/issues/4053)
- 修复控制台无效 warning [#4169](https://github.com/vueComponent/ant-design-vue/issues/4169)
- 更加易用,更加简单的使用单文件递归 [#4133](https://github.com/vueComponent/ant-design-vue/issues/4133)
- 💄 图标 icon 需要通过 slot 传递
- Skeleton
- 🌟 支持 Skeleton.Avatar 占位组件。
- 🌟 支持 Skeleton.Button 占位组件。
- 🌟 支持 Skeleton.Input 占位组件。
- 💄 破坏性更新
- `a-menu-item`、`a-sub-menu` 图标需要通过 slot 传递,不在通过子节点自动获取图标
- row gutter 支持 row-wrap 无需使用多个 row 划分 col
- Menu 移除 defaultOpenKeys、defaultSelectedKeys; Switch 移除 defaultChecked; Rate 移除 defaultValue; 其它未重构组件的 defaultXxx 命名的属性请谨慎使用,在未来的版本中也会被移除。
- 🌟 新增 Avatar.Group 组件
- 🐞 修复 AutoComplete filterOptions 不生效问题 [#4170](https://github.com/vueComponent/ant-design-vue/issues/4170)
- 🐞 修复 Select 自动宽度失效问题 [#4118](https://github.com/vueComponent/ant-design-vue/issues/4118)
- 🐞 修复 dist 缺少国际化文件问题 [#3684](https://github.com/vueComponent/ant-design-vue/issues/3684)
## 2.1.6
`2021-05-13`
- 🐞 使用 vue@3.0.10 重新构建,避免控制台 warning [#3998](https://github.com/vueComponent/ant-design-vue/issues/3998)
## 2.1.5
`2021-05-12`
- 🐞 修复 SSR 时报错问题 [#3983](https://github.com/vueComponent/ant-design-vue/issues/3983)
## 2.1.4
`2021-05-09`
- 🐞 修复 `Table` 滚动错位问题 [#4045](https://github.com/vueComponent/ant-design-vue/issues/4045)
- 🐞 修复 `Typography` editable 模式触发链接跳转问题 [#4105](https://github.com/vueComponent/ant-design-vue/issues/4105)
- 🐞 修复 `Carousel` variableWidth 不生效问题 [#3977](https://github.com/vueComponent/ant-design-vue/issues/3977)
- 🐞 修复 `TreeSelect` 无法通过键盘同时删除父子节点问题 [#3508](https://github.com/vueComponent/ant-design-vue/issues/3508)
- 🐞 修复若干类型错误问题
## 2.1.3
`2021-04-25`
- 🎉🎉🎉 移除 npm 安装时的广告
- 🐞 `Select`
- 修复默认激活第一项问题 [#3842](https://github.com/vueComponent/ant-design-vue/issues/3842)
- 修复分组显示异常问题 [#3841](https://github.com/vueComponent/ant-design-vue/issues/3841)
- 修复动态更新选择项后滚动异常问题 [#3972](https://github.com/vueComponent/ant-design-vue/issues/3972)
- 🐞 修复 `Checkbox` 触发两次 `update:checked` 问题 [#3838](https://github.com/vueComponent/ant-design-vue/issues/3838)
- 🌟 `Table` column group 支持 fixed [#3882](https://github.com/vueComponent/ant-design-vue/issues/3882)
- 🌟 `Table` column 支持 v-for [#3934](https://github.com/vueComponent/ant-design-vue/issues/3934)
- 🐞 修复 `Table` 在 windows 显示横向滚动条问题 [6d33d6](https://github.com/vueComponent/ant-design-vue/commit/6d33d60d2bca98825f274e48bcc3badd1857f742)
- 🌟 `Form` scrollToFirstError 支持选项参数传递 [#3918](https://github.com/vueComponent/ant-design-vue/issues/3918)
- 🐞 修复 `Calendar` 月份选择器显示错误字符问题 [#3915](https://github.com/vueComponent/ant-design-vue/issues/3915)
- 🌟 重构 `Switch` 组件,移除 defaultChecked 属性 [#3885](https://github.com/vueComponent/ant-design-vue/issues/3885)
- 🐞 修复使用 Vite 时,抛出 process 异常问题 [#3930](https://github.com/vueComponent/ant-design-vue/issues/3930)
- 🐞 修复 `Radio` 阴影遮挡问题 [#3955](https://github.com/vueComponent/ant-design-vue/issues/3955)
- 🐞 修复 `Form` inline 模式下, span 不生效问题 [#3862](https://github.com/vueComponent/ant-design-vue/issues/3862)
- 🐞 修复 `Cascader` keydown 选择不生效问题 [#958](https://github.com/vueComponent/ant-design-vue/issues/958)
- 🐞 修复 `Image` 预览功能失败问题 [#3701](https://github.com/vueComponent/ant-design-vue/issues/3701)
- 🐞 修复一些 TS 类型问题
## 2.1.2
`2021-03-28`
- 🌟 使用 Vue 3.0.9 重新编译,兼容 3.0.7 及以下版本
## 2.1.1
`2021-03-27`
- 🌟 兼容 Vue 3.0.8,注意:由于 3.0.8 的破坏性更新2.1.1 无法兼容 3.0.7 以下版本 [vue#3493](https://github.com/vuejs/vue-next/issues/3493)
- 🐞 修复 Modal.confirm 缺失 closable ts 类型 [#3684](https://github.com/vueComponent/ant-design-vue/issues/3845)
- 🐞 修复 Upload 自定义 method 不生效问题 [#3843](https://github.com/vueComponent/ant-design-vue/issues/3843)
## 2.1.0
`2021-03-20`
- 🎉🎉🎉 新增 `Typography` 组件 [#3807](https://github.com/vueComponent/ant-design-vue/issues/3807)
- 🌟 Modal 方法新增关闭图标定制 [#3753](https://github.com/vueComponent/ant-design-vue/issues/3753)
- 🐞 修复缺失包含国际化的构建文件 [#3684](https://github.com/vueComponent/ant-design-vue/issues/3684)
- 🐞 修复 Drawer 销毁后报错问题 [#848d64](https://github.com/vueComponent/ant-design-vue/commit/848d6497e68c87566790dfa889a1913199a6699a)
- 🐞 修复 BackTop 在 KeepAlive 激活时,位置不对的问题 [#3803](https://github.com/vueComponent/ant-design-vue/issues/3803)
- 🐞 修复 TreeNode class 不生效问题 [#3822](https://github.com/vueComponent/ant-design-vue/issues/3822)
- 🐞 修复 Table tags 为数组时报错问题 [#3812](https://github.com/vueComponent/ant-design-vue/issues/3812)
- 🐞 修复 Table 自定义 filterIcon 时,触发排序问题 [#3819](https://github.com/vueComponent/ant-design-vue/issues/3819)
- 🐞 修复 Select 样式在 Form 下错位问题 [#3781](https://github.com/vueComponent/ant-design-vue/issues/3781)
## 2.0.1
`2021-02-27`
- 🌟 `Badge` 新增 `Ribbon` [#3681](https://github.com/vueComponent/ant-design-vue/issues/3681)
- 🌟 调整 `SearchInput` search 事件触发顺序 [#3725](https://github.com/vueComponent/ant-design-vue/issues/3725)
- 🐞 修复 `Table` 销毁时卡死问题 [#3531](https://github.com/vueComponent/ant-design-vue/issues/3531)
- 🐞 修复 `Menu` css 中引入了 less 文件问题 [#3678](https://github.com/vueComponent/ant-design-vue/issues/3678)
- 🐞 修复 `Alert` 自定义图标错位问题 [#3712](https://github.com/vueComponent/ant-design-vue/issues/3712)
## 2.0.0
`2021-02-06`
- 🎉🎉🎉 2.0 正式版发布
- 🎉🎉🎉 支持暗黑主题 [#3410](https://github.com/vueComponent/ant-design-vue/issues/3410)
- 🎉🎉🎉 新版文档上线,使用 Composition API 完全重构文档示例,提供 TS、JS 双版本源码
- 🌟 使用 Composition API 重构 `Alert` 组件 [#3654](https://github.com/vueComponent/ant-design-vue/pull/3654)
- 🌟 `Tooltip` 支持自定义颜色 [#3603](https://github.com/vueComponent/ant-design-vue/issues/3603)
- 🐞 修复 `TimePicker` 没有自动滚动到已选位置问题 [#ab7537](https://github.com/vueComponent/ant-design-vue/commit/ab75379f0c2f5e54ab7c348284a7391939ab5aaf)
## 2.0.0-rc.9
`2021-01-24`
- 🌟 `@ant-design/icons-vue` 升级至 6.0,默认使用 es module
- 🌟 `Tabs` 增加 `centered` 居中模式 [#3501](https://github.com/vueComponent/ant-design-vue/issues/3501)
- 🐞 `Progress` 添加 opacity 动画 [#3505](https://github.com/vueComponent/ant-design-vue/issues/3505)
- 🐞 修复 npm 安装时报错问题 [#3515](https://github.com/vueComponent/ant-design-vue/issues/3515)
- 🐞 修复 `Breadcrumn` 分割线不显示问题 [#3522](https://github.com/vueComponent/ant-design-vue/issues/3522)
- 🐞 修复 `Radio` 不受控问题 [#3517](https://github.com/vueComponent/ant-design-vue/issues/3517)
- 🐞 修复 `FormItem` 不换行问题 [#3538](https://github.com/vueComponent/ant-design-vue/issues/3538)
- 🐞 修复 `Carousel` `pauseOnDotsHover` 不生效问题 [#3519](https://github.com/vueComponent/ant-design-vue/issues/3519)
- 🐞 修复 `Input.Search` `class` 不生效问题 [#3541](https://github.com/vueComponent/ant-design-vue/issues/3541)
- 🐞 修复 `InputNumber` 在微软输入法下多次触发 change 事件问题 [#3550](https://github.com/vueComponent/ant-design-vue/issues/3550)
- 🐞 修复 `Tabs` disabled 状态下依然可以通过键盘切换问题 [#3575](https://github.com/vueComponent/ant-design-vue/issues/3575)
- 🐞 修复 `Switch` 在 table 中切换不生效问题 [#3512](https://github.com/vueComponent/ant-design-vue/issues/3512)
## 2.0.0-rc.8
`2021-01-07`
- 🌟 支持 Vite 2 [#3490](https://github.com/vueComponent/ant-design-vue/issues/3490)
- 🌟 使用 Composition API 重构 Affix 组件 [#3447](https://github.com/vueComponent/ant-design-vue/issues/3447)
- 🐞 修复 Image 组件类型定义错误 [#3488](https://github.com/vueComponent/ant-design-vue/issues/3488)
- 🐞 升级 icons-vue 修复 IconFont 组件类型错误 [#3474](https://github.com/vueComponent/ant-design-vue/issues/3474)
- 🐞 修复 less 4 下 Tooltip 箭头样式错误问题 [#3477](https://github.com/vueComponent/ant-design-vue/issues/3477)
- 🐞 修复 Vue 3.0.5 下 DatePicker 类型定义解析错误问题 [#bf7c62](https://github.com/vueComponent/ant-design-vue/commit/bf7c62f457fc14624881f69c5baf9a62219383f7)
## 2.0.0-rc.7
`2020-12-28`
- 🐞 修复 Switch `change`、`click` 不生效问题 [#3453](https://github.com/vueComponent/ant-design-vue/issues/3453)
## 2.0.0-rc.6
`2020-12-27`
- 🌟 支持 Less 4 [#3449](https://github.com/vueComponent/ant-design-vue/issues/3449)
- 🌟 新增 Image 组件 [#3235](https://github.com/vueComponent/ant-design-vue/issues/3235)
- 🌟 函数式组件,添加 displayName 属性 [#3445](https://github.com/vueComponent/ant-design-vue/issues/3445)
- 🐞 Message 新增自定义 class style 功能 [#3443](https://github.com/vueComponent/ant-design-vue/issues/3443)
- 🐞 修复 Tabs 组件初始 disabled 状态没生效 [#3366](https://github.com/vueComponent/ant-design-vue/issues/3366)
- 🐞 修复 Slider 精准度问题 [#3346](https://github.com/vueComponent/ant-design-vue/issues/3346)
- 🐞 修复 Select 滚动高度不正确问题 [#3419](https://github.com/vueComponent/ant-design-vue/issues/3419)
- 🐞 修复 Input small 大小时,高度偏大 2px 问题 [#3396](https://github.com/vueComponent/ant-design-vue/issues/3396)
- 🐞 修复 TreeSelect 触发两次 change 事件问题
- 🐞 修复 TreeSelect 通过 slot 定义 title 死循环问题
- 🐞 修复 Drawer handle slot 触发两次 click 事件问题
- 🌟 新增 Checkbox、Switch 事件声明
## 2.0.0-rc.5
`2020-12-13`
- 🐞 修复 Drawer 组件控制台输出 this.dom 未定义的 warning 问题
- 🐞 修复 Menu 在 Vue 3.0.3 及以上版本,出现显示错乱问题 [#3354](https://github.com/vueComponent/ant-design-vue/issues/3354)
## 2.0.0-rc.4
`2020-12-10`
- 🌟 Input.Password 支持自定义图标 [#3320](https://github.com/vueComponent/ant-design-vue/issues/3320)
- 🐞 修复 Select Option click 事件不触发问题 [#4ea00d](https://github.com/vueComponent/ant-design-vue/commit/4ea00d3a70d0afd7bea07f814df03ab7d0b25ebd)
- 🐞 修复 Menu 超出宽度后 dark 主题不生效问题 [#10f35a](https://github.com/vueComponent/ant-design-vue/commit/10f35a1fa510de91e9484b07fcfff253920cee29)
- 🐞 修复 Menu 控制台 vue key some waring [#520d6a](https://github.com/vueComponent/ant-design-vue/commit/520d6a5e85eb391e5294211c9d7b2ea598c59119)
- 🐞 移除控制台 passive 提示日志 [#8d1669](https://github.com/vueComponent/ant-design-vue/commit/8d1669b8896d84a67c61d3a00d0b13c42d70f30f)
## 2.0.0-rc.3
`2020-12-05`
- 🐞 修复函数式组件在 Vue 3.0.3 下报类型错误问题 [#f5cf7e](https://github.com/vueComponent/ant-design-vue/commit/f5cf7e0920a51f0ac024046996c99260aa41becf)
- 🐞 修复 Menu 超出宽度后显示错误问题 [#3262](https://github.com/vueComponent/ant-design-vue/issues/3262)
- 🐞 修复 Menu subMenuOpenDelay subMenuCloseDelay 不生效问题 [#3291](https://github.com/vueComponent/ant-design-vue/pull/3291)
- 🐞 修复 TreeSelect 堆栈溢出问题 [#28aeea](https://github.com/vueComponent/ant-design-vue/commit/28aeea6f0b142ed68950a3738f7cf2c1581a7a5b)
- 🐞 修复 Input 自定义 style class 被覆盖问题 [#3273](https://github.com/vueComponent/ant-design-vue/issues/3273)
- 🐞 修复 InputNumber 在生产环境下 parse 错误 [#3249](https://github.com/vueComponent/ant-design-vue/issues/3249)
## 2.0.0-rc.2
`2020-11-24`
- 🌟 优化 Menu 性能,默认开启懒加载 [#3243](https://github.com/vueComponent/ant-design-vue/pull/3243)
- 🌟 Tag 支持通过 slot 定义 icon [#3185](https://github.com/vueComponent/ant-design-vue/pull/3185)
- 🌟 small 类型的 table 改成无边框 [#3221](https://github.com/vueComponent/ant-design-vue/issues/3221)
- 🌟 @ant-design/icons-vue 升级到 5.1.6,支持 SSR支持 spin 属性简写
- 🐞 修复 Alert 的关闭按钮在 Safari 下样式问题 [#3184](https://github.com/vueComponent/ant-design-vue/issues/3184)
- 🐞 修复 Notification top 属性类型错误问题 [#3187](https://github.com/vueComponent/ant-design-vue/issues/3187)
- 🐞 修复 DirectoryTree 自定义图标不生效问题 [#3183](https://github.com/vueComponent/ant-design-vue/issues/3183)
- 🐞 修复 Button loading delay 不生效问题 [#3194](https://github.com/vueComponent/ant-design-vue/issues/3194)
- 💄 Select optionFilterProp 不在支持按照 children 来过滤 [#3204](https://github.com/vueComponent/ant-design-vue/issues/3204)
- 🐞 修复 Select labelInValue 时报错问题 [#3216](https://github.com/vueComponent/ant-design-vue/issues/3216)
- 🐞 修复 ConfigProvider transformCellText 丢失问题 [#3206](https://github.com/vueComponent/ant-design-vue/issues/3206)
- 🐞 修复 Dropdown Button 混合使用时,样式错乱问题 [#3244](https://github.com/vueComponent/ant-design-vue/issues/3244)
- 🐞 修复 RangePicker 自定义宽度失效问题 [#3244](https://github.com/vueComponent/ant-design-vue/issues/3245)
- 🐞 修复多处 Ts 类型错误或缺失问题
## 2.0.0-rc.1
`2020-11-14`
- 🎉🎉🎉
- 🌟 Menu 取消默认懒加载,提升首次动画效果,优化贝塞尔曲线函数,更加流畅 [#3177](https://github.com/vueComponent/ant-design-vue/pull/3177)
- 🐞 修复 Select 搜索功能失效问题 [#3144](https://github.com/vueComponent/ant-design-vue/issues/3144)
- 🐞 修复 Drawer 组件没有自动 focus导致不能直接通过 ESC 按键关闭 [#3148](https://github.com/vueComponent/ant-design-vue/issues/3148)
- 🐞 修复 Popover 弹出元素位置不正确问题 [#3147](https://github.com/vueComponent/ant-design-vue/issues/3147)
- 🐞 修复 CountDown 不更新问题 [#3170](https://github.com/vueComponent/ant-design-vue/pull/3170)
- 🐞 修复多处 Ts 类型错误或缺失问题
## 2.0.0-beta.15
`2020-11-08`
- 🌟 优化 Menu 动画,更加流畅 [#3095](https://github.com/vueComponent/ant-design-vue/issues/3095)
- 🌟 优化 VirtualList避免无效 render [#2e61e9](https://github.com/vueComponent/ant-design-vue/commit/2e61e9cb502f2bb6910f59abfb483fd2517e594f)
- 🐞 修复 Menu overflowedIndicator 未生效问题 [#689113](https://github.com/vueComponent/ant-design-vue/commit/689113b3c9c19e929607567a4c8252c6511bff5c)
- 🐞 Select
- 修复 dropdownRender 不支持 slot 问题 [#3098](https://github.com/vueComponent/ant-design-vue/issues/3098)
- 修复 tag 模式下,空值异常问题 [#3100](https://github.com/vueComponent/ant-design-vue/issues/3100)
- 修复单选模式下选择项不更新问题 [#3099](https://github.com/vueComponent/ant-design-vue/issues/3099)
- 修复特殊场景下 foucs 状态不生效问题 [#3099](https://github.com/vueComponent/ant-design-vue/issues/3099)
- 🐞 修复 DatePicker 默认格式化失效问题 [#3091](https://github.com/vueComponent/ant-design-vue/issues/3091)
- 🐞 修复 Table customRow 配置事件不生效问题 [#3121](https://github.com/vueComponent/ant-design-vue/issues/3121)
- 🐞 修复 TreeSelect 搜索框样式错乱问题 [ee4cd3c](https://github.com/vueComponent/ant-design-vue/commit/ ee4cd3c35a84658cbbb148ce368bc247a927d528)
- 🐞 修复 Ts 类型错误或缺失问题
## 2.0.0-beta.13
`2020-11-02`
- 🐞 修复 npm install 报错问题 [#3080](https://github.com/vueComponent/ant-design-vue/issues/3080)
- 🐞 修复 Select maxPlaceHolder 显示错误问题 [#3085](https://github.com/vueComponent/ant-design-vue/issues/3085)
- 🐞 修复弹窗类组件,弹出位置不更新问题 [#3085](https://github.com/vueComponent/ant-design-vue/issues/3085)
- 🐞 修复 Table 数据为空时的 warning 问题 [#3082](https://github.com/vueComponent/ant-design-vue/issues/3082)
- 🐞 修复 Input 在 Form 中显示多个边框问题 [#3084](https://github.com/vueComponent/ant-design-vue/issues/3084)
## 2.0.0-beta.12
`2020-11-01`
- 🐞 修复 dist/antd.css 缺失组件样式问题 [#3069](https://github.com/vueComponent/ant-design-vue/issues/3069)
- 🐞 修复 Input 样式问题 [#3074](https://github.com/vueComponent/ant-design-vue/issues/3074)
- 🐞 修复 Form layout="vertical" 样式问题 [#3075](https://github.com/vueComponent/ant-design-vue/issues/3075)
- 🐞 修复 Select 无法打开弹窗问题 [#3070](https://github.com/vueComponent/ant-design-vue/issues/3070)
## 2.0.0-beta.11
`2020-10-30`
- 🎉🎉🎉 重构 Select、AutoComplete 组件,支持虚拟列表,性能大幅提升
- 🔥🔥🔥 使用 Typescript 重构所有组件,类型支持更加友好
- 🔥 优化底层动画组件,性能更好,更流畅
- 🌟 Textarea 组件添加 showCount 支持统计字数功能
- 🌟 递归 Menu 组件,支持任意嵌套其他元素 [#1452](https://github.com/vueComponent/ant-design-vue/issues/1452)
- 🇮🇪 添加爱尔兰语国际化支持
- 🐞 修复 webpack 5 兼容问题。
- 🐞 修复 Upload method 属性不生效问题 [#2837](https://github.com/vueComponent/ant-design-vue/issues/2837)
- 🐞 修复 Table 组件 filter 不支持 number 类型问题 [#3052](https://github.com/vueComponent/ant-design-vue/issues/3052)
- 🐞 修复 Table 固定列 ellipsis 不生效问题 [#2916](https://github.com/vueComponent/ant-design-vue/issues/2916)
- 🐞 修复 Table 自定义 expandIcon 不生效问题 [#3013](https://github.com/vueComponent/ant-design-vue/issues/3013)
- 🐞 修复 TreeSelect 不能自定义 slot 问题 [#2827](https://github.com/vueComponent/ant-design-vue/issues/2827)
- 🛎 更改 Avatar 的 srcSet 为 srcset
## 2.0.0-beta.10
`2020-09-24`
- 🌟 更新 Vue 依赖到 release 版本
- 🐞 修复 Menu 在 Layout 中不折叠问题 [#2819](https://github.com/vueComponent/ant-design-vue/issues/2819)
- 🐞 修复 Tabs 切换时出现 warning 问题 [#2865](https://github.com/vueComponent/ant-design-vue/issues/2865)
- 🐞 修复输入框在 compositionend 时不触发 change 事件问题
- 🐞 修复 Upload 上传按钮不消失问题 [#2884](https://github.com/vueComponent/ant-design-vue/issues/2884)
- 🐞 修复 Upload 自定义 method 不生效问题 [#2837](https://github.com/vueComponent/ant-design-vue/issues/2837)
- 🐞 修复若干 ts 类型错误
## 2.0.0-beta.8
- 🐞 修复 ts 类型错误
## 2.0.0-beta.7
- 🐞 修复 Descriptions Item 不支持 v-for 问题 [#2793](https://github.com/vueComponent/ant-design-vue/issues/2793)
- 🐞 修复 Modal button loading 效果不生效问题 [9257c1](https://github.com/vueComponent/ant-design-vue/commit/9257c1ea685db4339239589153aee3189d0434fe)
- 🐞 修复 Steps 组件使用 v-model 时不可点击的问题 [ec7309](https://github.com/vueComponent/ant-design-vue/commit/ec73097d9b6ea8e2f2942ac28853c19191ca3298)
- 🌟 Checkbox、Radio 添加事件声明
- 🐞 修复 ts 类型错误 [802446](https://github.com/vueComponent/ant-design-vue/commit/8024469b8832cfc4fe85498b639bfb48820531aa)
## 2.0.0-beta.6
- 🐞 修复 TreeSelectNode 子组件 TreeSelectNode 没有注册的问题
## 2.0.0-beta.5
- 🔥 支持 Vite。
## 2.0.0-beta.4
- 🌟 移除不再使用的 polyfill
- 🐞 修复 `Modal` afterClose 调用两次的问题
- 🐞 补充 ts 类型文件缺少原生属性的声明
## 2.0.0-beta.3
- 🔥 支持 Typescript。
- 🔥 新增 `Space` 组件。
- 🐞 修复部分组件无法使用 css scope 问题 [4bdb24](https://github.com/vueComponent/ant-design-vue/commit/4bdb241aa674b50fafa29b3b98e291643f2a06cc)。
- 🐞 修复 `List.Meta` 注册失败的问题 [03a42a](https://github.com/vueComponent/ant-design-vue/commit/03a42a5b35e7d42a39aedb1aba8346995be2c27e)
- 🐞 修复 `Table` 固定列情况下错位问题 [#1493](https://github.com/vueComponent/ant-design-vue/issues/1493)
- 🐞 修复 `Button` 没有垂直居中的问题 [bd71e3](https://github.com/vueComponent/ant-design-vue/commit/bd71e3806b73881f9a95028982d17a10b2cd0b5c)
- 🐞 修复 `Tabs` 多次出发 `change` 事件问题 [8ed937](https://github.com/vueComponent/ant-design-vue/commit/8ed937344a57142a575e5272f50933c9c4459a43)
## 2.0.0-beta.2
`2020-08-14`
### 设计规范调整
- 基础圆角调整,由统一的 `2px` 改为四级圆角,分别为 `2px` `4px` `6px` `8px`,分别应用于不同场景,比如默认尺寸的 Button 的圆角调整为了 `6px`
- 主色调整,由 `#1890ff` 改为 `#1677ff`
- 整体阴影调整,由原本的三级阴影调整为两级,分别用于常驻页面的组件(如 Card和交互反馈如 Dropdown
- 部分组件内间距调整。
- 整体去线框化。
- 行高从 `1.5`(`21px`) 调整为 `1.5715`(`22px`)。
- 基础圆角调整,由`4px` 改为 `2px`
- 分割线颜色明度降低,由 `#E8E8E8` 改为 `#F0F0F0`
- Table 默认背景颜色从透明修改为白色。
### 新增 5 个组件
### 兼容性调整
- Segmented 分段控制器
- WaterMark 水印
- QrCode 二维码
- FloatButton 悬浮按钮
- Tour 漫游式引导
- Vue 最低支持版本为 Vue 3.0。
### 技术调整
#### 调整的 API
- 弃用 less采用 CSS-in-JS更好地支持动态主题。
- 所有 less 文件全部移除less 变量不再支持透出。
- 产物中不再包含 css 文件。由于 CSS-in-JS 支持按需引入,原本的 `ant-design-vue/dist/antd.css` 也已经移除,如果需要重置一些基本样式请引入 `ant-design-vue/dist/reset.css`
- 如果需要组件重置样式,又不想引入 `ant-design-vue/dist/reset.css` 从而导致污染全局样式的话,可以尝试在应用最外层使用[App 组件](/components/app-cn),解决原生元素没有 ant-design-vue 规范样式的问题。
- 移除 css variables 以及在此之上构筑的动态主题方案。
- LocaleProvider 在 3.x 中已经废弃(使用 `<ConfigProvider locale />` 替代),我们在 4.x 里彻底移除了相关目录 `ant-design-vue/es/locale-provider`、`ant-design-vue/lib/locale-provider`。
- 不再支持 `babel-plugin-import`CSS-in-JS 本身具有按需加载的能力,不再需要插件支持。
- 移除了 LocaleProvider请使用 `ConfigProvider` 替代。
- 移除了 Tag 的 afterClose 属性。
- 合并了 FormModel、Form详见下方的 Form 重构部分。
- `tabIndex`、`maxLength`、`readOnly`、`autoComplete`、`autoFocus` 更改为全小写。
- 为了在 template 语法中更友好的使用插槽,所有涉及到 xxxRender, renderXxxx 的均改成单参数,涉及到 `itemRender`、`renderItem`、`customRender`、`dropdownRender`、`dateCellRender`、`dateFullCellRender`、`monthCellRender`、`monthFullCellRender`、`renderTabBar`。
- 所有配置 scopedSlots 的地方统一改成 slots。
- `{ on, props, attrs, ... }` 配置进行扁平化处理,如 `{ props: {type: 'xxx'}, on: {click: this.handleClick}}` 改成 `{ type: 'xxx', onClick: this.handleClick }`, 涉及相关字段:`okButtonProps`、`cancelButtonProps`。
- xxx.sync 改成 v-model:xxx
- v-model 更改成 v-model:xxx具体涉及组件
#### 组件 API 调整
- v-model 改成 v-model:checked 的组件有: CheckableTag、Checkbox、Switch
- v-model 改成 v-model:value 的组件有: Radio、Mentions、CheckboxGroup、Rate、DatePicker
- v-model 改成 v-model:visible 的组件有: Tag、Popconfirm、Popove、Tooltip、Moda、Dropdown
- v-model 改成 v-model:activeKey 的组件有: Collaps、Tabs
- v-model 改成 v-model:current 的组件有: Steps
- v-model 改成 v-model:selectedKeys 的组件有: Menu
- 组件弹框的 classname API 统一为 `popupClassName``dropdownClassName` 等类似 API 都会被替换。
#### 图标升级
- AutoComplete 组件
- Cascader 组件
- Select 组件
- TreeSelect 组件
- TimePicker 组件
- DatePicker 组件
- Mentions 组件
`ant-design-vue@1.2.0` 中,我们引入了 svg 图标([为何使用 svg 图标?](https://github.com/ant-design/ant-design/issues/10353))。使用了字符串命名的图标 API 无法做到按需加载,因而全量引入了 svg 图标文件,这大大增加了打包产物的尺寸。在 2.0 中,我们调整了图标的使用 API 从而支持 tree shaking减少默认包体积约 150 KB(Gzipped)。
- 组件弹框的受控可见 API 统一为 `open``visible` 等类似 API 都会被替换。
- Drawer 组件 `visible` 变为 `open`
- Modal 组件 `visible` 变为 `open`
- Dropdown 组件 `visible` 变为 `open`
- Tooltip 组件 `visible` 变为 `open`
- Tag 组件 `visible` 已移除。
- Slider 组件 `tooltip` 相关 API 收敛到 `tooltip` 属性中。
- Table 组件 `filterDropdownVisible` 变为 `filterDropdownOpen`
- `getPopupContainer`: 所有的 `getPopupContainer` 都需要保证返回的是唯一的 div。
- Drawer `style``class` 迁移至 Drawer 弹层区域上,原属性替换为 `rootClassName``rootStyle`
旧版 Icon 使用方式将被废弃:
#### 组件重构与移除
```html
<a-icon type="smile" />
<a-button icon="smile" />
```
- 移除 `locale-provider` 目录。`LocaleProvider` 在 v4 中已移除,请使用 `ConfigProvider` 替代。
2.0 中会采用按需引入的方式:
- 移除栅格布局中的`xxxl`断点属性。 `xxxl`属性已经在 v4 被移除,您可以使用 [主题定制](/docs/vue/customize-theme-cn) 修改 `screen[XS|SM|MD|LG|XL|XXL]` 来修改断点值实现。
```html
<template>
<smile-outlined />
<a-button>
<template v-slot:icon><smile-outlined /></template>
</a-buttom>
</template>
<script>
import SmileOutlined from '@ant-design/icons-vue/SmileOutlined';
export default {
components: {
SmileOutlined
}
}
</script>
```
- BackTop 组件在 `4.0.0` 中废弃,移至 FloatButton 悬浮按钮中。如需使用,可以从 FloatButton 中引入。
#### 组件重构
### [升级指南](/docs/vue/migration-v4-cn)
在 1.x 中我们提供了 Form、FormModel 两个表单组件,原有的 Form 组件使用 v-decorator 进行数据绑定,在 Vue2 中我们通过上下文进行强制更新组件,但是在 Vue3 中,由于引入 patchFlag 等优化方式,强制刷新会破坏 patchFlag 带来的性能优势。所以在 2.0 版本中我们将 Form、FormModel 进行合并,保留了 FormModel 的使用方式,丰富了相关功能,并改名成 Form。
## 3.x
涉及改动:
去 [GitHub](https://github.com/vueComponent/ant-design-vue/blob/3.x/CHANGELOG.zh-CN.md) 查看 `3.x` 的 Change Log。
- Form 新增 `scrollToFirstError`,`name`,`validateTrigger` 属性,新增 `finish`、`finishFailed` 事件,新增 `scrollToField` 方法。
- Form.Item 新增 `validateFirst`, `validateTrigger`, 废弃 `prop` 属性,使用 `name` 替换。
- 嵌套字段路径使用数组,过去版本我们通过 . 代表嵌套路径(诸如 user.name 来代表 { user: { name: '' } })。然而在一些后台系统中,变量名中也会带上 .。这造成用户需要额外的代码进行转化,因而新版中,嵌套路径通过数组来表示以避免错误的处理行为(如 ['user', 'name'])。
- validateFields 不再支持 callback。validateFields 会返回 Promise 对象,因而你可以通过 async/await 或者 then/catch 来执行对应的错误处理。不再需要判断 errors 是否为空:
## 2.x
```js
// v1
// eslint-disable-next-line no-undef,no-unused-vars
validateFields((err, value) => {
if (!err) {
// Do something with value
}
});
```
去 [GitHub](https://github.com/vueComponent/ant-design-vue/blob/2.x/CHANGELOG.zh-CN.md) 查看 `2.x` 的 Change Log。
改成
```js
// v2
// eslint-disable-next-line no-undef,no-unused-vars
validateFields().then(values => {
// Do something with value
});
```
## 1.x

22
LICENSE
View File

@ -44,25 +44,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
MIT License
Copyright (c) 2019-PRESENT Anthony Fu<https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,6 +1,6 @@
<p align="center">
<a href="https://www.antdv.com/">
<img width="200" src="https://aliyuncdn.antdv.com/logo.png">
<img width="200" src="https://qn.antdv.com/logo.png">
</a>
</p>
@ -10,7 +10,7 @@
<div align="center">
基于 Ant Design 和 Vue 3 的企业级 UI 组件库。
An enterprise-class UI components based on Ant Design and Vue 3.
![test](https://github.com/vueComponent/ant-design-vue/workflows/test/badge.svg) [![codecov](https://img.shields.io/codecov/c/github/vueComponent/ant-design-vue/master.svg?style=flat-square)](https://codecov.io/gh/vueComponent/ant-design-vue) [![npm package](https://img.shields.io/npm/v/ant-design-vue.svg?style=flat-square)](https://www.npmjs.org/package/ant-design-vue) [![NPM downloads](http://img.shields.io/npm/dm/ant-design-vue.svg?style=flat-square)](http://www.npmtrends.com/ant-design-vue) [![backers](https://opencollective.com/ant-design-vue/backers/badge.svg)](#backers) [![sponsors](https://opencollective.com/ant-design-vue/sponsors/badge.svg)](#sponsors) [![extension-for-VSCode](https://img.shields.io/badge/extension%20for-VSCode-blue.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=ant-design-vue.vscode-ant-design-vue-helper) [![issues-helper](https://img.shields.io/badge/Issues%20Manage%20By-issues--helper-orange?style=flat-square)](https://github.com/actions-cool/issues-helper)
@ -26,12 +26,6 @@
- 开箱即用的高质量 Vue 组件。
- 共享 [Ant Design of React](http://ant-design.gitee.io/docs/spec/introduce-cn) 设计工具体系。
## 关注我们
收藏加关注,第一时间获取更新动态!
![star us](https://user-images.githubusercontent.com/6937879/261937060-e0501ab3-9388-4712-a25d-3f2ba2271865.gif)
## 支持环境
- 现代浏览器。1.x 版本支持 IE 9+(需要 [polyfills](https://www.antdv.com/docs/vue/getting-started-cn/#兼容性)
@ -71,9 +65,6 @@ $ yarn add ant-design-vue
| [ant-design-vue-helper](https://marketplace.visualstudio.com/items?itemName=ant-design-vue.vscode-ant-design-vue-helper) | ant-design-vue 的 vscode 扩展 |
| [vue-cli-plugin-ant-design](https://github.com/vueComponent/vue-cli-plugin-ant-design) | 使用 vue-cli3 快速使用 ant-design-vue 组件库 |
| [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | 在 DOM 模板中,您可以使用 ant-design-vue 组件的自定义事件camelCase |
| [@formily/antdv](https://github.com/formilyjs/antdv) | 这是一个结合了 Formily 和 ant-design-vue 的组件库 |
| [@ant-design-vue/nuxt](https://github.com/vueComponent/ant-design-vue-nuxt) | ant-design-vue 的 nuxt 模块扩展 |
| [ant-design-x-vue](https://github.com/wzc520pyfm/ant-design-x-vue) | 基于 Ant Design X 设计规范的 Vue AI 界面解决方案 |
## 问答
@ -88,33 +79,24 @@ ant-design-vue 是 MIT 协议的开源项目。为了项目能够更好的持续
- [Patreon](https://www.patreon.com/tangjinzhou)
- [opencollective](https://opencollective.com/ant-design-vue)
- [paypal](https://www.paypal.me/tangjinzhou)
- [支付宝或微信](https://aliyuncdn.antdv.com/alipay-and-wechat.png)
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
- [支付宝或微信](https://qn.antdv.com/alipay-and-wechat.png)
## 赞助商
## Sponsors
成为赞助商,并在 Github 上的自述文件上获得您的徽标,并链接到您的网站。 [[成为赞助商](https://opencollective.com/ant-design-vue#sponsor)]
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
<a href="http://www.jeecg.com/" target="_blank"><img src="https://aliyuncdn.antdv.com/jeecg-logo.png" height="64"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
## 支持者
## Backers
每月捐款支持我们,帮助我们继续我们的活动。 [[成为支持者](https://opencollective.com/ant-design-vue#backer)]
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ant-design-vue#backer)]
<a href="https://github.com/chuzhixin/vue-admin-beautiful" target="_blank"><img width="64" style="border-radius: 50%;" src="https://gitee.com/chu1204505056/image/raw/master/vue-admin-beautiful.png" title="vue-admin-beautiful"></a> <a href="https://opencollective.com/ant-design-vue/backer/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/10/avatar.svg"></a>
## Patreon
每月捐款支持我们,帮助我们继续我们的活动。 [[成为支持者](https://www.patreon.com/tangjinzhou)]
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://www.patreon.com/tangjinzhou)]
<a href="https://www.mokeyjay.com" target="_blank"><img width="64" style="border-radius: 50%;" src="https://www.mokeyjay.com/headimg.png" title="donation by Patreon"></a>
## [更多赞助者 (通过 Patreon、支付宝、微信、paypal 等等)](https://github.com/vueComponent/ant-design-vue/blob/master/BACKERS.md)
## 贡献者
感谢所有为 ant-design-vue 做出贡献的人!
<a href="https://github.com/vueComponent/ant-design-vue/graphs/contributors">
<img src="https://contrib.rocks/image?repo=vueComponent/ant-design-vue&max=100&columns=15" />
</a>

View File

@ -1,6 +1,6 @@
<p align="center">
<a href="https://www.antdv.com/">
<img width="200" src="https://aliyuncdn.antdv.com/logo.png">
<img width="200" src="https://qn.antdv.com/logo.png">
</a>
</p>
@ -26,15 +26,9 @@ English | [简体中文](./README-zh_CN.md)
- A set of high-quality Vue components out of the box.
- Shared [Ant Design of React](https://ant.design/docs/spec/introduce) design resources.
## Getting started & staying tuned with us.
Star us, and you will receive all releases notifications from GitHub without any delay!
![star us](https://user-images.githubusercontent.com/6937879/261937060-e0501ab3-9388-4712-a25d-3f2ba2271865.gif)
## Environment Support
- Modern browsers. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#compatibility))
- Modern browsers. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#Compatibility))
- Server-side Rendering
- Support Vue 2 & Vue 3
- [Electron](https://electronjs.org/)
@ -45,7 +39,7 @@ Star us, and you will receive all releases notifications from GitHub without any
## Using npm or yarn
**We recommend using npm or yarn to install**, it not only makes development easier, but also allow you to take advantage of the rich ecosystem of Javascript packages and tooling.
**We recommend using npm or yarn to install**it not only makes development easierbut also allow you to take advantage of the rich ecosystem of Javascript packages and tooling.
```bash
$ npm install ant-design-vue --save
@ -55,7 +49,7 @@ $ npm install ant-design-vue --save
$ yarn add ant-design-vue
```
If you are in a bad network environment, you can try other registries and tools like [cnpm](https://github.com/cnpm/cnpm).
If you are in a bad network environmentyou can try other registries and tools like [cnpm](https://github.com/cnpm/cnpm).
## Links
@ -71,9 +65,6 @@ If you are in a bad network environment, you can try other registries and tools
| [ant-design-vue-helper](https://marketplace.visualstudio.com/items?itemName=ant-design-vue.vscode-ant-design-vue-helper) | A vscode extension for ant-design-vue |
| [vue-cli-plugin-ant-design](https://github.com/vueComponent/vue-cli-plugin-ant-design) | Vue-cli 3 plugin to add ant-design-vue |
| [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | The library function, implemented in the DOM template, can use the custom event of the ant-design-vue component (camelCase) |
| [@formily/antdv](https://github.com/formilyjs/antdv) | The Library with Formily and ant-design-vue |
| [@ant-design-vue/nuxt](https://github.com/vueComponent/ant-design-vue-nuxt) | A nuxt module for ant-design-vue |
| [ant-design-x-vue](https://github.com/wzc520pyfm/ant-design-x-vue) | A Vue AI interface solutions base on the Ant Design X design specification |
## Donation
@ -82,25 +73,26 @@ ant-design-vue is an MIT-licensed open source project. In order to achieve bette
- [Patreon](https://www.patreon.com/tangjinzhou)
- [opencollective](https://opencollective.com/ant-design-vue)
- [paypal](https://www.paypal.me/tangjinzhou)
- [支付宝或微信](https://aliyuncdn.antdv.com/alipay-and-wechat.png)
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
- [支付宝或微信](https://qn.antdv.com/alipay-and-wechat.png)
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
<a href="http://www.jeecg.com/" target="_blank"><img src="https://aliyuncdn.antdv.com/jeecg-logo.png" height="64"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/ant-design-vue/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/sponsor/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/sponsor/10/avatar.svg"></a>
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ant-design-vue#backer)]
<a href="https://github.com/chuzhixin/vue-admin-beautiful" target="_blank"><img width="64" style="border-radius: 50%;" src="https://gitee.com/chu1204505056/image/raw/master/vue-admin-beautiful.png" title="vue-admin-beautiful"></a> <a href="https://opencollective.com/ant-design-vue/backer/0/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/0/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/1/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/1/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/2/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/2/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/3/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/3/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/4/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/4/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/5/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/5/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/6/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/6/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/7/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/7/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/8/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/8/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/9/avatar.svg"></a> <a href="https://opencollective.com/ant-design-vue/backer/10/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/10/avatar.svg"></a><a href="https://opencollective.com/ant-design-vue/backer/9/website" target="_blank"><img src="https://opencollective.com/ant-design-vue/backer/9/avatar.svg"></a>
## Patreon
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://www.patreon.com/tangjinzhou)]
<a href="https://www.mokeyjay.com" target="_blank"><img width="64" style="border-radius: 50%;" src="https://www.mokeyjay.com/headimg.png" title="donation by Patreon"></a>
## [More Sponsor (From Patreon、alipay、wechat、paypal...)](https://github.com/vueComponent/ant-design-vue/blob/master/BACKERS.md)
## Contributors
Thank you to all the people who already contributed to ant-design-vue!
<a href="https://github.com/vueComponent/ant-design-vue/graphs/contributors">
<img src="https://contrib.rocks/image?repo=vueComponent/ant-design-vue&max=100&columns=15" />
</a>
[![Let's fund issues in this repository](https://issuehunt.io/static/embed/issuehunt-button-v1.svg)](https://issuehunt.io/repos/104172832)
This project is tested with BrowserStack.

View File

@ -1,17 +0,0 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 1.x | :white_check_mark: |
| 2.x | :x: |
| 3.x | :white_check_mark: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a reported vulnerability, what to expect if the vulnerability is accepted or declined, etc.

View File

@ -1,68 +0,0 @@
// Read all the api from current documents
const glob = require('glob');
const fs = require('fs');
const COMPONENT_NAME = /components\/([^/]*)/;
const PROP_NAME = /^\s*\|\s*([^\s|]*)/;
const components = {};
function mappingPropLine(component, line) {
const propMatch = line.match(PROP_NAME);
if (!propMatch) return;
const propName = propMatch[1];
if (!/^[a-z]/.test(propName)) return;
components[component] = Array.from(new Set([...(components[component] || []), propName]));
}
function apiReport(entities) {
const apis = {};
Object.keys(entities).forEach(component => {
const apiList = entities[component];
apiList.forEach(api => {
if (typeof apis[api] === 'function') {
apis[api] = [];
}
apis[api] = [...(apis[api] || []), component];
});
});
return apis;
}
function printReport(apis) {
const apiList = Object.keys(apis).map(api => ({
name: api,
componentList: apis[api],
}));
apiList.sort((a, b) => b.componentList.length - a.componentList.length);
// eslint-disable-next-line no-console
console.log('| name | components | comments |');
// eslint-disable-next-line no-console
console.log('| ---- | ---------- | -------- |');
apiList.forEach(({ name, componentList }) => {
// eslint-disable-next-line no-console
console.log('|', name, '|', componentList.join(', '), '| |');
});
}
module.exports = () => {
glob('components/*/*.md', (error, files) => {
files.forEach(filePath => {
// Read md file to parse content
const content = fs.readFileSync(filePath, 'utf8');
const component = filePath.match(COMPONENT_NAME)[1];
// Parse lines to get API
const lines = content.split(/[\r\n]+/);
lines.forEach(line => {
mappingPropLine(component, line);
});
});
printReport(apiReport(components));
});
};

View File

@ -4,6 +4,7 @@
'use strict';
require('colorful').colorful();
require('colorful').isatty = true;
const gulp = require('gulp');
const program = require('commander');

View File

@ -3,21 +3,18 @@ const pkg = require('../../package.json');
const { parseAndWrite } = require('./lib/index.js');
const rootPath = path.resolve(__dirname, '../../');
parseAndWrite({
version: pkg.version,
name: 'ant-design-vue',
path: path.resolve(rootPath, './components'),
typingsPath: path.resolve(rootPath, './typings/global.d.ts'),
// default match lang
test: /en-US\.md/,
outputDir: path.resolve(rootPath, './vetur'),
tagPrefix: 'a-',
})
.then(result => {
// eslint-disable-next-line no-console
console.log(`generator types success: ${result} tags generated`);
})
.catch(error => {
console.error('generator types error', error);
return Promise.reject(error);
try {
parseAndWrite({
version: pkg.version,
name: 'types',
path: path.resolve(rootPath, './v2-doc/src/docs'),
// default match lang
test: /en-US\.md/,
outputDir: path.resolve(rootPath, './vetur'),
tagPrefix: 'a-',
});
// eslint-disable-next-line no-console
console.log('generator types success');
} catch (e) {
console.error('generator types error', e);
}

View File

@ -34,19 +34,14 @@ function parserProps(tag: VueTag, line: any) {
});
}
export function formatter(
articals: Articals,
componentName: string,
kebabComponentName: string,
tagPrefix = '',
) {
export function formatter(articals: Articals, componentName: string, tagPrefix = '') {
if (!articals.length) {
return;
}
const tags: VueTag[] = [];
const tag: VueTag = {
name: kebabComponentName,
name: getComponentName(componentName, tagPrefix),
slots: [],
events: [],
attributes: [],
@ -85,13 +80,9 @@ export function formatter(
}
// 额外的子组件
if (
tableTitle.includes(componentName) &&
!tableTitle.includes('events') &&
!tableTitle.includes('()')
) {
if (tableTitle.includes(componentName) && !tableTitle.includes('events')) {
const childTag: VueTag = {
name: getComponentName(tableTitle.replace(/\.|\//g, ''), tagPrefix),
name: getComponentName(tableTitle.replace('.', ''), tagPrefix),
slots: [],
events: [],
attributes: [],
@ -102,7 +93,6 @@ export function formatter(
tags.push(childTag);
return;
}
// 额外的子组件事件
if (tableTitle.includes(componentName) && tableTitle.includes('events')) {
const childTagName = getComponentName(

View File

@ -1,84 +1,52 @@
import glob from 'fast-glob';
import { dirname, join } from 'path';
import { join, dirname } from 'path';
import { mdParser } from './parser';
import { formatter } from './formatter';
import { genWebTypes } from './web-types';
import { outputFileSync, readFileSync } from 'fs-extra';
import { readFileSync, outputFileSync } from 'fs-extra';
import type { Options, VueTag } from './type';
import { getComponentName, normalizePath, toKebabCase } from './utils';
import { flatMap } from 'lodash';
import { normalizePath, getComponentName } from './utils';
import { genVeturTags, genVeturAttributes } from './vetur';
async function readMarkdown(options: Options): Promise<Map<String, VueTag>> {
const mdPaths = await glob(normalizePath(`${options.path}/**/*.md`));
const data = mdPaths
async function readMarkdown(options: Options) {
// const mds = await glob(normalizePath(`${options.path}/**/*.md`))
const mds = await glob(normalizePath(`${options.path}/**/*.md`));
return mds
.filter(md => options.test.test(md))
.map(path => {
const docPath = dirname(path);
const kebabComponentName =
options.tagPrefix + docPath.substring(docPath.lastIndexOf('/') + 1) || '';
const componentName = getComponentName(docPath.substring(docPath.lastIndexOf('/') + 1) || '');
const fileContent = readFileSync(path, 'utf-8');
return formatter(mdParser(fileContent), componentName, kebabComponentName, options.tagPrefix);
})
.filter(item => item) as VueTag[][];
const tags = new Map<String, VueTag>();
flatMap(data, item => item).forEach(mergedTag => mergeTag(tags, mergedTag));
return tags;
const componentName = docPath.substring(docPath.lastIndexOf('/') + 1);
return {
componentName: getComponentName(componentName || ''),
md: readFileSync(path, 'utf-8'),
};
});
}
function readTypings(options: Options): Map<String, VueTag> {
const tags = new Map<String, VueTag>();
const fileContent = readFileSync(options.typingsPath, 'utf-8');
fileContent
.split('\n')
.filter(line => line && line.includes('typeof'))
.map(line => {
const l = line.trim();
return toKebabCase(l.substring(0, l.indexOf(':')));
})
.forEach(tagName =>
tags.set(tagName, {
name: tagName,
slots: [],
events: [],
attributes: [],
}),
);
return tags;
}
function mergeTag(tags: Map<String, VueTag>, mergedTag: VueTag) {
const tagName = mergedTag.name;
const vueTag = tags.get(tagName);
if (vueTag) {
vueTag.slots = [...vueTag.slots, ...mergedTag.slots];
vueTag.events = [...vueTag.events, ...mergedTag.events];
vueTag.attributes = [...vueTag.attributes, ...mergedTag.attributes];
} else {
tags.set(tagName, mergedTag);
}
}
function mergeTags(mergedTagsArr: Map<String, VueTag>[]): VueTag[] {
if (mergedTagsArr.length === 1) return [...mergedTagsArr[0].values()];
const tags = new Map<String, VueTag>();
if (mergedTagsArr.length === 0) return [];
mergedTagsArr.forEach(mergedTags => {
mergedTags.forEach(mergedTag => mergeTag(tags, mergedTag));
});
return [...tags.values()];
}
export async function parseAndWrite(options: Options): Promise<Number> {
export async function parseAndWrite(options: Options) {
if (!options.outputDir) {
throw new Error('outputDir can not be empty.');
}
const tagsFromMarkdown = await readMarkdown(options);
const tagsFromTypings = await readTypings(options);
const tags = mergeTags([tagsFromMarkdown, tagsFromTypings]);
const docs = await readMarkdown(options);
const datas = docs
.map(doc => formatter(mdParser(doc.md), doc.componentName, options.tagPrefix))
.filter(item => item) as VueTag[][];
const tags: VueTag[] = [];
datas.forEach(arr => {
tags.push(...arr);
});
const webTypes = genWebTypes(tags, options);
const veturTags = genVeturTags(tags);
const veturAttributes = genVeturAttributes(tags);
outputFileSync(join(options.outputDir, 'tags.json'), JSON.stringify(veturTags, null, 2));
outputFileSync(
join(options.outputDir, 'attributes.json'),
JSON.stringify(veturAttributes, null, 2),
);
outputFileSync(join(options.outputDir, 'web-types.json'), JSON.stringify(webTypes, null, 2));
return tags.length;
}
export default { parseAndWrite };

View File

@ -21,13 +21,13 @@ export type Articals = Artical[];
function readLine(input: string) {
const end = input.indexOf('\n');
return input.substring(0, end !== -1 ? end : input.length);
return input.substr(0, end !== -1 ? end : input.length);
}
function splitTableLine(line: string) {
line = line.replace(/\\\|/g, 'JOIN');
line = line.replace('\\|', 'JOIN');
const items = line.split('|').map(item => item.trim().replace(/JOIN/g, '|'));
const items = line.split('|').map(item => item.trim().replace('JOIN', '|'));
// remove pipe character on both sides
items.pop();
@ -47,7 +47,7 @@ function tableParse(input: string) {
};
while (start < end) {
const target = input.substring(start);
const target = input.substr(start);
const line = readLine(target);
if (!/^\|/.test(target)) {
@ -77,9 +77,14 @@ export function mdParser(input: string): Articals {
const artical = [];
let start = 0;
const end = input.length;
// artical.push({
// type: 'title',
// content: title,
// level: 0,
// });
while (start < end) {
const target = input.substring(start);
const target = input.substr(start);
let match;
if ((match = TITLE_REG.exec(target))) {
@ -91,7 +96,7 @@ export function mdParser(input: string): Articals {
start += match.index + match[0].length;
} else if ((match = TABLE_REG.exec(target))) {
const { table, usedLength } = tableParse(target.substring(match.index));
const { table, usedLength } = tableParse(target.substr(match.index));
artical.push({
type: 'table',
table,
@ -103,5 +108,6 @@ export function mdParser(input: string): Articals {
}
}
// artical[0].content = title
return artical;
}

View File

@ -28,16 +28,34 @@ export type VueAttribute = {
export type VueTag = {
name: string;
slots: VueSlot[];
events: VueEvent[];
attributes: VueAttribute[];
slots?: VueSlot[];
events?: VueEvent[];
attributes?: VueAttribute[];
description?: string;
};
export type VeturTag = {
description?: string;
attributes: string[];
};
export type VeturTags = Record<string, VeturTag>;
export type VeturAttribute = {
type: string;
description: string;
};
export type VeturAttributes = Record<string, VeturAttribute>;
export type VeturResult = {
tags: VeturTags;
attributes: VeturAttributes;
};
export type Options = {
name: string;
path: PathLike;
typingsPath: PathLike;
test: RegExp;
version: string;
outputDir?: string;

View File

@ -1,6 +1,6 @@
// myName -> my-name
export function toKebabCase(camel: string): string {
return camel.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, '-$1').toLowerCase();
export function toKebabCase(input: string): string {
return input.replace(/[A-Z]/g, (val, index) => (index === 0 ? '' : '-') + val.toLowerCase());
}
// name `v2.0.0` -> name

View File

@ -0,0 +1,30 @@
import type { VueTag, VeturTags, VeturAttributes } from './type';
export function genVeturTags(tags: VueTag[]) {
const veturTags: VeturTags = {};
tags.forEach(tag => {
veturTags[tag.name] = {
attributes: tag.attributes ? tag.attributes.map(item => item.name) : [],
};
});
return veturTags;
}
export function genVeturAttributes(tags: VueTag[]) {
const veturAttributes: VeturAttributes = {};
tags.forEach(tag => {
if (tag.attributes) {
tag.attributes.forEach(attr => {
veturAttributes[`${tag.name}/${attr.name}`] = {
type: attr.value.type,
description: `${attr.description}, Default: ${attr.default}`,
};
});
}
});
return veturAttributes;
}

View File

@ -1,4 +1,4 @@
const { resolve, isThereHaveBrowserslistConfig } = require('./utils/projectHelper');
const { resolve } = require('./utils/projectHelper');
module.exports = function (modules) {
const plugins = [
@ -20,8 +20,7 @@ module.exports = function (modules) {
resolve('@babel/plugin-transform-runtime'),
{
useESModules: modules === false,
version:
require(`${process.cwd()}/package.json`).dependencies['@babel/runtime'] || '^7.10.4',
version: '^7.10.4',
},
],
// resolve('babel-plugin-inline-import-data-uri'),
@ -39,11 +38,9 @@ module.exports = function (modules) {
resolve('@babel/preset-env'),
{
modules,
targets: isThereHaveBrowserslistConfig()
? undefined
: {
browsers: ['last 2 versions', 'Firefox ESR', '> 1%', 'ie >= 11'],
},
targets: {
browsers: ['last 2 versions', 'Firefox ESR', '> 1%', 'not ie 11'],
},
},
],
],

View File

@ -1,17 +0,0 @@
'use strict';
const runCmd = require('./runCmd');
module.exports = function (done) {
if (process.env.NPM_CLI) {
done(process.env.NPM_CLI);
return;
}
runCmd('which', ['tnpm'], code => {
let npm = 'npm';
if (!code) {
npm = 'tnpm';
}
done(npm);
});
};

View File

@ -1,14 +1,15 @@
'use strict';
const fs = require('fs');
const assign = require('object-assign');
const { getProjectPath } = require('./utils/projectHelper');
module.exports = function () {
module.exports = function() {
let my = {};
if (fs.existsSync(getProjectPath('tsconfig.json'))) {
my = require(getProjectPath('tsconfig.json'));
}
return Object.assign(
return assign(
{
noUnusedParameters: true,
noUnusedLocals: true,

View File

@ -22,7 +22,7 @@ const imageOptions = {
limit: 10000,
};
function getWebpackConfig(modules, esm = false) {
function getWebpackConfig(modules) {
const pkg = require(getProjectPath('package.json'));
const babelConfig = require('./getBabelCommonConfig')(modules || false);
@ -75,7 +75,7 @@ function getWebpackConfig(modules, esm = false) {
'readline',
'repl',
'tls',
].reduce((acc, name) => Object.assign({}, acc, { [name]: 'empty' }), {}),
].reduce((acc, name) => Object.assign({}, acc, { [name]: false }), {}),
},
module: {
@ -150,6 +150,36 @@ function getWebpackConfig(modules, esm = false) {
},
],
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['autoprefixer'],
},
sourceMap: true,
},
},
{
loader: 'less-loader',
options: {
lessOptions: {
sourceMap: true,
javascriptEnabled: true,
},
},
},
],
},
// Images
{
test: svgRegex,
@ -170,7 +200,7 @@ function getWebpackConfig(modules, esm = false) {
new webpack.BannerPlugin(`
${pkg.name} v${pkg.version}
Copyright 2017-present, Ant Design Vue.
Copyright 2017-present, ant-design-vue.
All rights reserved.
`),
new WebpackBar({
@ -185,37 +215,17 @@ All rights reserved.
};
if (process.env.RUN_ENV === 'PRODUCTION') {
let entry = ['./index'];
config.externals = [
{
vue: {
root: 'Vue',
commonjs2: 'vue',
commonjs: 'vue',
amd: 'vue',
module: 'vue',
},
const entry = ['./index'];
config.externals = {
vue: {
root: 'Vue',
commonjs2: 'vue',
commonjs: 'vue',
amd: 'vue',
},
];
if (esm) {
entry = ['./index.esm'];
config.experiments = {
...config.experiments,
outputModule: true,
};
config.output.chunkFormat = 'module';
config.output.library = {
type: 'module',
};
config.target = 'es2019';
} else {
config.output.libraryTarget = 'umd';
config.output.library = distFileBaseName;
config.output.globalObject = 'this';
}
const entryName = esm ? `${distFileBaseName}.esm` : distFileBaseName;
};
config.output.library = distFileBaseName;
config.output.libraryTarget = 'umd';
config.optimization = {
minimizer: [
new TerserPlugin({
@ -226,10 +236,11 @@ All rights reserved.
}),
],
};
// Development
const uncompressedConfig = merge({}, config, {
entry: {
[entryName]: entry,
[distFileBaseName]: entry,
},
mode: 'development',
plugins: [
@ -242,10 +253,11 @@ All rights reserved.
// Production
const prodConfig = merge({}, config, {
entry: {
[`${entryName}.min`]: entry,
[`${distFileBaseName}.min`]: entry,
},
mode: 'production',
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.LoaderOptionsPlugin({
minimize: true,
}),
@ -262,7 +274,7 @@ All rights reserved.
return [prodConfig, uncompressedConfig];
}
return [config];
return config;
}
getWebpackConfig.webpack = webpack;

View File

@ -1,10 +1,11 @@
/* eslint-disable no-console */
const { getProjectPath, getConfig } = require('./utils/projectHelper');
const { getProjectPath } = require('./utils/projectHelper');
const runCmd = require('./runCmd');
const getBabelCommonConfig = require('./getBabelCommonConfig');
const merge2 = require('merge2');
const { execSync } = require('child_process');
const through2 = require('through2');
const transformLess = require('./transformLess');
const webpack = require('webpack');
const babel = require('gulp-babel');
const argv = require('minimist')(process.argv.slice(2));
@ -16,31 +17,22 @@ const chalk = require('chalk');
const getNpmArgs = require('./utils/get-npm-args');
const getChangelog = require('./utils/getChangelog');
const path = require('path');
// const watch = require('gulp-watch')
const ts = require('gulp-typescript');
const gulp = require('gulp');
const fg = require('fast-glob');
const fs = require('fs');
const rimraf = require('rimraf');
const { createCompilerHost, createProgram } = require('typescript');
const stripCode = require('gulp-strip-code');
const compareVersions = require('compare-versions');
const getTSCommonConfig = require('./getTSCommonConfig');
// const getTSCommonConfig = require('./getTSCommonConfig');
const replaceLib = require('./replaceLib');
const sortApiTable = require('./sortApiTable');
const { glob } = require('glob');
const packageJson = require(getProjectPath('package.json'));
const tsDefaultReporter = ts.reporter.defaultReporter();
const cwd = process.cwd();
const libDir = getProjectPath('lib');
const esDir = getProjectPath('es');
const localeDir = getProjectPath('locale');
const tsConfig = getTSCommonConfig();
// FIXME: hard code, not find typescript can modify the path resolution
const localeDts = `import type { Locale } from '../lib/locale-provider';
declare const localeValues: Locale;
export default localeValues;`;
// const tsConfig = getTSCommonConfig();
function dist(done) {
rimraf.sync(path.join(cwd, 'dist'));
@ -56,17 +48,11 @@ function dist(done) {
}
const info = stats.toJson();
const { dist: { finalize } = {}, bail } = getConfig();
if (stats.hasErrors()) {
(info.errors || []).forEach(error => {
console.error(error);
});
// https://github.com/ant-design/ant-design/pull/31662
if (bail) {
process.exit(1);
}
console.error(info.errors);
}
if (stats.hasWarnings()) {
console.warn(info.warnings);
}
@ -81,43 +67,55 @@ function dist(done) {
version: false,
});
console.log(buildInfo);
// Additional process of dist finalize
if (finalize) {
console.log('[Dist] Finalization...');
finalize();
}
done(0);
});
}
const tsFiles = ['**/*.ts', '**/*.tsx', '!node_modules/**/*.*', 'typings/**/*.d.ts'];
async function compileTs(modules = false, cb) {
const options = {
allowJs: true,
declaration: true,
emitDeclarationOnly: true,
};
function compileTs(stream) {
return stream
.pipe(ts(tsConfig))
.js.pipe(
through2.obj(function (file, encoding, next) {
// console.log(file.path, file.base);
file.path = file.path.replace(/\.[jt]sx$/, '.js');
this.push(file);
next();
}),
)
.pipe(gulp.dest(process.cwd()));
const createdFiles = {};
const host = createCompilerHost(options);
host.writeFile = (fileName, contents) => {
createdFiles[path.isAbsolute(fileName) ? path.relative(cwd, fileName) : fileName] = contents;
};
const files = await fg(
[
'components/**/*.js',
'components/**/*.jsx',
'components/**/*.tsx',
'components/**/*.ts',
'!components/*/__tests__/*',
'!components/*/style/*',
'!components/styles.ts',
],
{ cwd },
);
const program = createProgram(files, options, host);
program.emit();
Object.keys(createdFiles).forEach(fileName => {
const contents = createdFiles[fileName];
const filePath = path.join(
cwd,
fileName.replace(/^components/, modules === false ? 'es' : 'lib'),
);
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, contents);
});
cb(0);
}
gulp.task('tsc', () =>
compileTs(
gulp.src(tsFiles, {
base: cwd,
}),
),
);
gulp.task('clean', () => {
rimraf.sync(getProjectPath('_site'));
rimraf.sync(getProjectPath('_data'));
});
gulp.task('tsc', () => compileTs());
function babelify(js, modules) {
const babelConfig = getBabelCommonConfig(modules);
@ -126,10 +124,20 @@ function babelify(js, modules) {
if (modules === false) {
babelConfig.plugins.push(replaceLib);
}
const stream = js.pipe(babel(babelConfig)).pipe(
let stream = js.pipe(babel(babelConfig)).pipe(
through2.obj(function z(file, encoding, next) {
this.push(file.clone());
if (modules !== false) {
if (file.path.match(/\/style\/index\.(js|jsx|ts|tsx)$/)) {
const content = file.contents.toString(encoding);
file.contents = Buffer.from(
content
.replace(/\/style\/?'/g, "/style/css'")
.replace(/\/style\/?"/g, '/style/css"')
.replace(/\.less/g, '.css'),
);
file.path = file.path.replace(/index\.(js|jsx|ts|tsx)$/, 'css.js');
this.push(file);
} else if (modules !== false) {
const content = file.contents.toString(encoding);
file.contents = Buffer.from(
content
@ -141,36 +149,48 @@ function babelify(js, modules) {
next();
}),
);
if (modules === false) {
stream = stream.pipe(
stripCode({
start_comment: '@remove-on-es-build-begin',
end_comment: '@remove-on-es-build-end',
}),
);
}
return stream.pipe(gulp.dest(modules === false ? esDir : libDir));
}
function compile(modules) {
const { compile: { transformTSFile, transformFile } = {} } = getConfig();
rimraf.sync(modules !== false ? libDir : esDir);
const less = gulp
.src(['components/**/*.less'])
.pipe(
through2.obj(function (file, encoding, next) {
this.push(file.clone());
if (
file.path.match(/\/style\/index\.less$/) ||
file.path.match(/\/style\/v2-compatible-reset\.less$/)
) {
transformLess(file.path)
.then(css => {
file.contents = Buffer.from(css);
file.path = file.path.replace(/\.less$/, '.css');
this.push(file);
next();
})
.catch(e => {
console.error(e);
});
} else {
next();
}
}),
)
.pipe(gulp.dest(modules === false ? esDir : libDir));
const assets = gulp
.src(['components/**/*.@(png|svg)'])
.pipe(gulp.dest(modules === false ? esDir : libDir));
let error = 0;
// =============================== FILE ===============================
let transformFileStream;
if (transformFile) {
transformFileStream = gulp
.src(['components/**/*.tsx'])
.pipe(
through2.obj(function (file, encoding, next) {
let nextFile = transformFile(file) || file;
nextFile = Array.isArray(nextFile) ? nextFile : [nextFile];
nextFile.forEach(f => this.push(f));
next();
}),
)
.pipe(gulp.dest(modules === false ? esDir : libDir));
}
// ================================ TS ================================
// let error = 0;
const source = [
'components/**/*.js',
'components/**/*.jsx',
@ -180,68 +200,8 @@ function compile(modules) {
'!components/*/__tests__/*',
];
// Strip content if needed
let sourceStream = gulp.src(source);
if (modules === false) {
sourceStream = sourceStream.pipe(
stripCode({
start_comment: '@remove-on-es-build-begin',
end_comment: '@remove-on-es-build-end',
}),
);
}
if (transformTSFile) {
sourceStream = sourceStream.pipe(
through2.obj(function (file, encoding, next) {
let nextFile = transformTSFile(file) || file;
nextFile = Array.isArray(nextFile) ? nextFile : [nextFile];
nextFile.forEach(f => this.push(f));
next();
}),
);
}
const tsResult = sourceStream.pipe(
ts(tsConfig, {
error(e) {
tsDefaultReporter.error(e);
error = 1;
},
finish: tsDefaultReporter.finish,
}),
);
function check() {
if (error && !argv['ignore-error']) {
process.exit(1);
}
}
tsResult.on('finish', check);
tsResult.on('end', check);
const tsFilesStream = babelify(tsResult.js, modules);
const tsd = tsResult.dts.pipe(gulp.dest(modules === false ? esDir : libDir));
return merge2([tsFilesStream, tsd, assets, transformFileStream].filter(s => s));
}
function generateLocale() {
if (!fs.existsSync(localeDir)) {
fs.mkdirSync(localeDir);
}
const localeFiles = glob.sync('components/locale/*.ts?(x)');
localeFiles.forEach(item => {
const match = item.match(/components\/locale\/(.*)\.tsx?/);
if (match) {
const locale = match[1];
fs.writeFileSync(
path.join(localeDir, `${locale}.js`),
`module.exports = require('../lib/locale/${locale}');`,
);
fs.writeFileSync(path.join(localeDir, `${locale}.d.ts`), localeDts);
}
});
const jsFilesStream = babelify(gulp.src(source), modules);
return merge2([less, jsFilesStream, assets]);
}
function tag() {
@ -368,8 +328,9 @@ function pub(done) {
}
}
const startTime = new Date();
let startTime = new Date();
gulp.task('compile-with-es', done => {
startTime = new Date();
console.log('start compile at ', startTime);
console.log('[Parallel] Compile to es...');
compile(false).on('finish', done);
@ -377,29 +338,34 @@ gulp.task('compile-with-es', done => {
gulp.task('compile-with-lib', done => {
console.log('[Parallel] Compile to js...');
compile().on('finish', () => {
generateLocale();
done();
});
compile().on('finish', done);
});
gulp.task('compile-finalize', done => {
// Additional process of compile finalize
const { compile: { finalize } = {} } = getConfig();
if (finalize) {
console.log('[Compile] Finalization...');
finalize();
}
done();
gulp.task('compile-with-es-ts-type', async done => {
console.log('[Parallel] Compile to es ts type...');
await compileTs(false, done);
});
gulp.task('compile-with-lib-ts-type', async done => {
console.log('[Parallel] Compile to lib ts type...');
await compileTs(true, done);
});
gulp.task(
'compile',
gulp.series(gulp.parallel('compile-with-es', 'compile-with-lib'), 'compile-finalize', done => {
console.log('end compile at ', new Date());
console.log('compile time ', (new Date() - startTime) / 1000, 's');
done();
}),
gulp.series(
gulp.parallel(
'compile-with-es',
'compile-with-lib',
'compile-with-es-ts-type',
'compile-with-lib-ts-type',
),
done => {
console.log('end compile at ', new Date());
console.log('compile time ', (new Date() - startTime) / 1000, 's');
done();
},
),
);
gulp.task(
@ -452,7 +418,7 @@ gulp.task(
newVersion.trim() === version
) {
// eslint-disable-next-line no-unused-vars
runCmd('npm', ['run', 'pub'], _code => {
runCmd('npm', ['run', 'pub'], code => {
done();
});
} else {
@ -475,11 +441,7 @@ gulp.task(
const npmArgs = getNpmArgs();
if (npmArgs) {
for (let arg = npmArgs.shift(); arg; arg = npmArgs.shift()) {
if (
/^pu(b(l(i(sh?)?)?)?)?$/.test(arg) &&
npmArgs.indexOf('--with-antd-tools') < 0 &&
!process.env.npm_config_with_antd_tools
) {
if (/^pu(b(l(i(sh?)?)?)?)?$/.test(arg) && npmArgs.indexOf('--with-antd-tools') < 0) {
reportError();
done(1);
return;
@ -489,11 +451,3 @@ gulp.task(
done();
}),
);
gulp.task(
'sort-api-table',
gulp.series(done => {
sortApiTable();
done();
}),
);

View File

@ -12,19 +12,6 @@ function replacePath(path) {
path.node.source.value = esModule;
}
}
// @ant-design/icons-vue/xxx => @ant-design/icons-vue/es/icons/xxx
const antdIconMatcher = /@ant-design\/icons-vue\/([^/]*)$/;
if (path.node.source && antdIconMatcher.test(path.node.source.value)) {
const esModule = path.node.source.value.replace(
antdIconMatcher,
(_, iconName) => `@ant-design/icons-vue/es/icons/${iconName}`,
);
const esPath = dirname(getProjectPath('node_modules', esModule));
if (fs.existsSync(esPath)) {
path.node.source.value = esModule;
}
}
}
function replaceLib() {

View File

@ -1,17 +1,9 @@
'use strict';
const isWindows = require('is-windows');
const getRunCmdEnv = require('./utils/getRunCmdEnv');
function runCmd(cmd, _args, fn) {
const args = _args || [];
if (isWindows()) {
args.unshift(cmd);
args.unshift('/c');
cmd = process.env.ComSpec;
}
const runner = require('child_process').spawn(cmd, args, {
// keep color
stdio: 'inherit',

View File

@ -1,165 +0,0 @@
const program = require('commander');
const majo = require('majo');
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const unified = require('unified');
const parse = require('remark-parse');
const stringify = require('remark-stringify');
const yamlConfig = require('remark-yaml-config');
const frontmatter = require('remark-frontmatter');
let fileAPIs = {};
const remarkWithYaml = unified()
.use(parse)
.use(stringify, {
paddedTable: false,
listItemIndent: 1,
stringLength: () => 3,
})
.use(frontmatter)
.use(yamlConfig);
const stream = majo.majo();
function getCellValue(node) {
return node.children[0].children[0].value;
}
// from small to large
const sizeBreakPoints = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
const whiteMethodList = ['afterChange', 'beforeChange'];
const groups = {
isDynamic: val => /^on[A-Z]/.test(val) || whiteMethodList.indexOf(val) > -1,
isSize: val => sizeBreakPoints.indexOf(val) > -1,
};
function asciiSort(prev, next) {
if (prev > next) {
return 1;
}
if (prev < next) {
return -1;
}
return 0;
}
// follow the alphabet order
function alphabetSort(nodes) {
// use toLowerCase to keep `case insensitive`
return nodes.sort((...comparison) =>
asciiSort(...comparison.map(val => getCellValue(val).toLowerCase())),
);
}
function sizeSort(nodes) {
return nodes.sort((...comparison) =>
asciiSort(...comparison.map(val => sizeBreakPoints.indexOf(getCellValue(val).toLowerCase()))),
);
}
function sort(ast, filename) {
const nameMatch = filename.match(/^components\/([^/]*)\//);
const componentName = nameMatch[1];
fileAPIs[componentName] = fileAPIs[componentName] || {
static: new Set(),
size: new Set(),
dynamic: new Set(),
};
ast.children.forEach(child => {
const staticProps = [];
// prefix with `on`
const dynamicProps = [];
// one of ['xs', 'sm', 'md', 'lg', 'xl']
const sizeProps = [];
// find table markdown type
if (child.type === 'table') {
// slice will create new array, so sort can affect the original array.
// slice(1) cut down the thead
child.children.slice(1).forEach(node => {
const value = getCellValue(node);
if (groups.isDynamic(value)) {
dynamicProps.push(node);
fileAPIs[componentName].dynamic.add(value);
} else if (groups.isSize(value)) {
sizeProps.push(node);
fileAPIs[componentName].size.add(value);
} else {
staticProps.push(node);
fileAPIs[componentName].static.add(value);
}
});
// eslint-disable-next-line
child.children = [
child.children[0],
...alphabetSort(staticProps),
...sizeSort(sizeProps),
...alphabetSort(dynamicProps),
];
}
});
return ast;
}
function sortAPI(md, filename) {
return remarkWithYaml.stringify(sort(remarkWithYaml.parse(md), filename));
}
function sortMiddleware(ctx) {
Object.keys(ctx.files).forEach(filename => {
const content = ctx.fileContents(filename);
ctx.writeContents(filename, sortAPI(content, filename));
});
}
module.exports = () => {
fileAPIs = {};
program
.version('0.1.0')
.option(
'-f, --file [file]',
'Specify which file to be transformed',
// default value
'components/**/index.+(zh-CN|en-US).md',
)
.option('-o, --output [output]', 'Specify component api output path', '~component-api.json')
.parse(process.argv);
// Get the markdown file all need to be transformed
/* eslint-disable no-console */
stream
.source(program.file)
.use(sortMiddleware)
.dest('.')
.then(() => {
if (program.output) {
const data = {};
Object.keys(fileAPIs).forEach(componentName => {
data[componentName] = {
static: [...fileAPIs[componentName].static],
size: [...fileAPIs[componentName].size],
dynamic: [...fileAPIs[componentName].dynamic],
};
});
const reportPath = path.resolve(program.output);
fs.writeFileSync(reportPath, JSON.stringify(data, null, 2), 'utf8');
console.log(chalk.cyan(`API list file: ${reportPath}`));
}
})
.then(() => {
console.log(chalk.green(`sort ant-design-vue api successfully!`));
});
/* eslint-enable no-console */
};

View File

@ -0,0 +1,30 @@
const less = require('less');
const { readFileSync } = require('fs');
const path = require('path');
const postcss = require('postcss');
const NpmImportPlugin = require('less-plugin-npm-import');
const autoprefixer = require('autoprefixer');
function transformLess(lessFile, config = {}) {
const { cwd = process.cwd() } = config;
const resolvedLessFile = path.resolve(cwd, lessFile);
let data = readFileSync(resolvedLessFile, 'utf-8');
data = data.replace(/^\uFEFF/, '');
// Do less compile
const lessOpts = {
paths: [path.dirname(resolvedLessFile)],
filename: resolvedLessFile,
plugins: [new NpmImportPlugin({ prefix: '~' })],
javascriptEnabled: true,
};
return less
.render(data, lessOpts)
.then(result => postcss([autoprefixer]).process(result.css, { from: undefined }))
.then(r => {
return r.css;
});
}
module.exports = transformLess;

View File

@ -24,13 +24,13 @@ class CleanUpStatsPlugin {
apply(compiler) {
compiler.hooks.done.tap('CleanUpStatsPlugin', stats => {
const { children, warnings } = stats.compilation;
const { children } = stats.compilation;
if (Array.isArray(children)) {
stats.compilation.children = children.filter(child => this.shouldPickStatChild(child));
}
if (Array.isArray(warnings)) {
stats.compilation.warnings = warnings.filter(message => this.shouldPickWarning(message));
}
// if (Array.isArray(warnings)) {
// stats.compilation.warnings = warnings.filter(message => this.shouldPickWarning(message));
// }
});
}
}

View File

@ -2,11 +2,6 @@
// NOTE: the following code was partially adopted from https://github.com/iarna/in-publish
module.exports = function getNpmArgs() {
// https://github.com/iarna/in-publish/pull/14
if (process.env.npm_command) {
return [process.env.npm_command];
}
let npmArgv = null;
try {

View File

@ -1,7 +1,10 @@
const fs = require('fs');
module.exports = function getChangelog(file, version) {
const lines = fs.readFileSync(file).toString().split('\n');
const lines = fs
.readFileSync(file)
.toString()
.split('\n');
const changeLog = [];
const startPattern = new RegExp(`^## ${version}`);
const stopPattern = /^## /; // 前一个版本

View File

@ -1,7 +1,6 @@
'use strict';
const path = require('path');
const isWindows = require('is-windows');
module.exports = function getRunCmdEnv() {
const env = {};
@ -12,12 +11,16 @@ module.exports = function getRunCmdEnv() {
const nodeModulesBinDir = path.join(__dirname, '../../node_modules/.bin');
Object.entries(env)
.filter(v => v.slice(0, 1).pop().toLowerCase() === 'path')
.filter(
v =>
v
.slice(0, 1)
.pop()
.toLowerCase() === 'path',
)
.forEach(v => {
const key = v.slice(0, 1).pop();
env[key] = env[key]
? `${nodeModulesBinDir}${isWindows() ? ';' : ':'}${env[key]}`
: nodeModulesBinDir;
env[key] = env[key] ? `${nodeModulesBinDir}:${env[key]}` : nodeModulesBinDir;
});
return env;
};

View File

@ -13,7 +13,6 @@ function resolve(moduleName) {
// We need hack the require to ensure use package module first
// For example, `typescript` is required by `gulp-typescript` but provided by `antd`
// we do not need for ant-design-vue
let injected = false;
function injectRequire() {
if (injected) return;
@ -21,7 +20,7 @@ function injectRequire() {
const Module = require('module');
const oriRequire = Module.prototype.require;
Module.prototype.require = function (...args) {
Module.prototype.require = function(...args) {
const moduleName = args[0];
try {
return oriRequire.apply(this, args);
@ -46,35 +45,9 @@ function getConfig() {
return {};
}
/**
* 是否存在可用的browserslist config
* https://github.com/browserslist/browserslist#queries
* @returns
*/
function isThereHaveBrowserslistConfig() {
try {
const packageJson = require(getProjectPath('package.json'));
if (packageJson.browserslist) {
return true;
}
} catch (e) {
//
}
if (fs.existsSync(getProjectPath('.browserslistrc'))) {
return true;
}
if (fs.existsSync(getProjectPath('browserslist'))) {
return true;
}
// parent项目的配置支持需要再补充
// ROWSERSLIST ROWSERSLIST_ENV 变量的形式,需要再补充。
return false;
}
module.exports = {
getProjectPath,
resolve,
injectRequire,
getConfig,
isThereHaveBrowserslistConfig,
};

View File

@ -1,7 +1,7 @@
module.exports = {
env: {
test: {
presets: [['@babel/preset-env']],
presets: [['@babel/preset-env', { targets: { node: true } }]],
plugins: [
['@vue/babel-plugin-jsx', { mergeProps: false, enableObjectSlots: false }],
'@babel/plugin-proposal-optional-chaining',
@ -12,7 +12,6 @@ module.exports = {
'@babel/plugin-proposal-class-properties',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-runtime',
'transform-require-context',
],
},
},

View File

@ -1,123 +0,0 @@
import type { ExtractPropTypes, PropType } from 'vue';
import { shallowRef, onMounted, defineComponent, onBeforeUnmount } from 'vue';
import Button from '../button';
import type { ButtonProps } from '../button';
import type { LegacyButtonType } from '../button/buttonTypes';
import { convertLegacyProps } from '../button/buttonTypes';
import useDestroyed from './hooks/useDestroyed';
import { objectType } from './type';
import { findDOMNode } from './props-util';
const actionButtonProps = {
type: {
type: String as PropType<LegacyButtonType>,
},
actionFn: Function as PropType<(...args: any[]) => any | PromiseLike<any>>,
close: Function,
autofocus: Boolean,
prefixCls: String,
buttonProps: objectType<ButtonProps>(),
emitEvent: Boolean,
quitOnNullishReturnValue: Boolean,
};
export type ActionButtonProps = ExtractPropTypes<typeof actionButtonProps>;
function isThenable<T>(thing?: PromiseLike<T>): boolean {
return !!(thing && thing.then);
}
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'ActionButton',
props: actionButtonProps,
setup(props, { slots }) {
const clickedRef = shallowRef<boolean>(false);
const buttonRef = shallowRef();
const loading = shallowRef(false);
let timeoutId: any;
const isDestroyed = useDestroyed();
onMounted(() => {
if (props.autofocus) {
timeoutId = setTimeout(() => findDOMNode(buttonRef.value)?.focus?.());
}
});
onBeforeUnmount(() => {
clearTimeout(timeoutId);
});
const onInternalClose = (...args: any[]) => {
props.close?.(...args);
};
const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike<any>) => {
if (!isThenable(returnValueOfOnOk)) {
return;
}
loading.value = true;
returnValueOfOnOk!.then(
(...args: any[]) => {
if (!isDestroyed.value) {
loading.value = false;
}
onInternalClose(...args);
clickedRef.value = false;
},
(e: Error) => {
// See: https://github.com/ant-design/ant-design/issues/6183
if (!isDestroyed.value) {
loading.value = false;
}
clickedRef.value = false;
return Promise.reject(e);
},
);
};
const onClick = (e: MouseEvent) => {
const { actionFn } = props;
if (clickedRef.value) {
return;
}
clickedRef.value = true;
if (!actionFn) {
onInternalClose();
return;
}
let returnValueOfOnOk: PromiseLike<any>;
if (props.emitEvent) {
returnValueOfOnOk = actionFn(e);
if (props.quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) {
clickedRef.value = false;
onInternalClose(e);
return;
}
} else if (actionFn.length) {
returnValueOfOnOk = actionFn(props.close);
// https://github.com/ant-design/ant-design/issues/23358
clickedRef.value = false;
} else {
returnValueOfOnOk = actionFn();
if (!returnValueOfOnOk) {
onInternalClose();
return;
}
}
handlePromiseOnOk(returnValueOfOnOk);
};
return () => {
const { type, prefixCls, buttonProps } = props;
return (
<Button
{...convertLegacyProps(type)}
onClick={onClick}
loading={loading.value}
prefixCls={prefixCls}
{...buttonProps}
ref={buttonRef}
v-slots={slots}
></Button>
);
};
},
});

View File

@ -1,167 +1,50 @@
import type { PropType } from 'vue';
import { computed, defineComponent, shallowRef, ref, watch } from 'vue';
import { defineComponent, ref, withDirectives } from 'vue';
import antInput from './antInputDirective';
import PropTypes from './vue-types';
import type { BaseInputInnerExpose } from './BaseInputInner';
import BaseInputInner from './BaseInputInner';
import { styleObjectToString } from '../vc-util/Dom/css';
export interface BaseInputExpose {
focus: () => void;
blur: () => void;
input: HTMLInputElement | HTMLTextAreaElement | null;
setSelectionRange: (
start: number,
end: number,
direction?: 'forward' | 'backward' | 'none',
) => void;
select: () => void;
getSelectionStart: () => number | null;
getSelectionEnd: () => number | null;
getScrollTop: () => number | null;
setScrollTop: (scrollTop: number) => void;
}
const BaseInput = defineComponent({
compatConfig: { MODE: 3 },
inheritAttrs: false,
props: {
disabled: PropTypes.looseBool,
type: PropTypes.string,
value: PropTypes.any,
lazy: PropTypes.bool.def(true),
tag: {
type: String as PropType<'input' | 'textarea'>,
default: 'input',
},
size: PropTypes.string,
style: PropTypes.oneOfType([String, Object]),
class: PropTypes.string,
value: PropTypes.string.def(''),
},
emits: [
'change',
'input',
'blur',
'keydown',
'focus',
'compositionstart',
'compositionend',
'keyup',
'paste',
'mousedown',
],
setup(props, { emit, attrs, expose }) {
const inputRef = shallowRef<BaseInputInnerExpose>(null);
const renderValue = ref();
const isComposing = ref(false);
watch(
[() => props.value, isComposing],
() => {
if (isComposing.value) return;
renderValue.value = props.value;
},
{ immediate: true },
);
emits: ['change', 'input'],
setup(_p, { emit }) {
const inputRef = ref(null);
const handleChange = (e: Event) => {
emit('change', e);
};
const onCompositionstart = (e: CompositionEvent) => {
isComposing.value = true;
(e.target as any).composing = true;
emit('compositionstart', e);
};
const onCompositionend = (e: CompositionEvent) => {
isComposing.value = false;
(e.target as any).composing = false;
emit('compositionend', e);
const event = document.createEvent('HTMLEvents');
event.initEvent('input', true, true);
e.target.dispatchEvent(event);
handleChange(e);
};
const handleInput = (e: Event) => {
if (isComposing.value && props.lazy) {
renderValue.value = (e.target as HTMLInputElement).value;
return;
}
emit('input', e);
};
const handleBlur = (e: Event) => {
emit('blur', e);
};
const handleFocus = (e: Event) => {
emit('focus', e);
};
const focus = () => {
if (inputRef.value) {
inputRef.value.focus();
const { composing } = e.target as any;
if ((e as any).isComposing || composing) {
emit('input', e);
} else {
emit('input', e);
emit('change', e);
}
};
const blur = () => {
if (inputRef.value) {
inputRef.value.blur();
}
return {
inputRef,
focus: () => {
if (inputRef.value) {
inputRef.value.focus();
}
},
blur: () => {
if (inputRef.value) {
inputRef.value.blur();
}
},
handleChange,
};
const handleKeyDown = (e: KeyboardEvent) => {
emit('keydown', e);
};
const handleKeyUp = (e: KeyboardEvent) => {
emit('keyup', e);
};
const setSelectionRange = (
start: number,
end: number,
direction?: 'forward' | 'backward' | 'none',
) => {
inputRef.value?.setSelectionRange(start, end, direction);
};
const select = () => {
inputRef.value?.select();
};
expose({
focus,
blur,
input: computed(() => inputRef.value?.input),
setSelectionRange,
select,
getSelectionStart: () => inputRef.value?.getSelectionStart(),
getSelectionEnd: () => inputRef.value?.getSelectionEnd(),
getScrollTop: () => inputRef.value?.getScrollTop(),
});
const handleMousedown = (e: MouseEvent) => {
emit('mousedown', e);
};
const handlePaste = (e: ClipboardEvent) => {
emit('paste', e);
};
const styleString = computed(() => {
return props.style && typeof props.style !== 'string'
? styleObjectToString(props.style)
: props.style;
});
return () => {
const { style, lazy, ...restProps } = props;
return (
<BaseInputInner
{...restProps}
{...attrs}
style={styleString.value}
onInput={handleInput}
onChange={handleChange}
onBlur={handleBlur}
onFocus={handleFocus}
ref={inputRef}
value={renderValue.value}
onCompositionstart={onCompositionstart}
onCompositionend={onCompositionend}
onKeyup={handleKeyUp}
onKeydown={handleKeyDown}
onPaste={handlePaste}
onMousedown={handleMousedown}
},
render() {
return withDirectives(
(
<input
{...this.$props}
{...this.$attrs}
onInput={this.handleChange}
onChange={this.handleChange}
ref="inputRef"
/>
);
};
) as any,
[[antInput]],
);
},
});

View File

@ -1,96 +0,0 @@
import type { PropType } from 'vue';
import { defineComponent, shallowRef } from 'vue';
import PropTypes from './vue-types';
export interface BaseInputInnerExpose {
focus: () => void;
blur: () => void;
input: HTMLInputElement | HTMLTextAreaElement | null;
setSelectionRange: (
start: number,
end: number,
direction?: 'forward' | 'backward' | 'none',
) => void;
select: () => void;
getSelectionStart: () => number | null;
getSelectionEnd: () => number | null;
getScrollTop: () => number | null;
setScrollTop: (scrollTop: number) => void;
}
const BaseInputInner = defineComponent({
compatConfig: { MODE: 3 },
// inheritAttrs: false,
props: {
disabled: PropTypes.looseBool,
type: PropTypes.string,
value: PropTypes.any,
tag: {
type: String as PropType<'input' | 'textarea'>,
default: 'input',
},
size: PropTypes.string,
onChange: Function as PropType<(e: Event) => void>,
onInput: Function as PropType<(e: Event) => void>,
onBlur: Function as PropType<(e: Event) => void>,
onFocus: Function as PropType<(e: Event) => void>,
onKeydown: Function as PropType<(e: Event) => void>,
onCompositionstart: Function as PropType<(e: Event) => void>,
onCompositionend: Function as PropType<(e: Event) => void>,
onKeyup: Function as PropType<(e: Event) => void>,
onPaste: Function as PropType<(e: Event) => void>,
onMousedown: Function as PropType<(e: Event) => void>,
},
emits: [
'change',
'input',
'blur',
'keydown',
'focus',
'compositionstart',
'compositionend',
'keyup',
'paste',
'mousedown',
],
setup(props, { expose }) {
const inputRef = shallowRef(null);
const focus = () => {
if (inputRef.value) {
inputRef.value.focus();
}
};
const blur = () => {
if (inputRef.value) {
inputRef.value.blur();
}
};
const setSelectionRange = (
start: number,
end: number,
direction?: 'forward' | 'backward' | 'none',
) => {
inputRef.value?.setSelectionRange(start, end, direction);
};
const select = () => {
inputRef.value?.select();
};
expose({
focus,
blur,
input: inputRef,
setSelectionRange,
select,
getSelectionStart: () => inputRef.value?.selectionStart,
getSelectionEnd: () => inputRef.value?.selectionEnd,
getScrollTop: () => inputRef.value?.scrollTop,
});
return () => {
const { tag: Tag, value, ...restProps } = props;
return <Tag {...restProps} ref={inputRef} value={value} />;
};
},
});
export default BaseInputInner;

View File

@ -3,7 +3,7 @@ import { getOptionProps } from './props-util';
export default {
methods: {
setState(state = {}, callback: () => any) {
setState(state = {}, callback) {
let newState = typeof state === 'function' ? state(this.$data, this.$props) : state;
if (this.getDerivedStateFromProps) {
const s = this.getDerivedStateFromProps(getOptionProps(this), {
@ -26,7 +26,6 @@ export default {
},
__emit() {
// 直接调用事件底层组件不需要vueTool记录events
// eslint-disable-next-line prefer-rest-params
const args = [].slice.call(arguments, 0);
let eventName = args[0];
eventName = `on${eventName[0].toUpperCase()}${eventName.substring(1)}`;

View File

@ -1,19 +1,10 @@
export type FocusEventHandler = (e: FocusEvent) => void;
export type MouseEventHandler = (e: MouseEvent) => void;
export type KeyboardEventHandler = (e: KeyboardEvent) => void;
export type CompositionEventHandler = (e: CompositionEvent) => void;
export type ClipboardEventHandler = (e: ClipboardEvent) => void;
export type ChangeEventHandler = (e: ChangeEvent) => void;
export type WheelEventHandler = (e: WheelEvent) => void;
export type ChangeEvent = Event & {
target: {
value?: string | undefined;
};
};
export type CheckboxChangeEvent = Event & {
target: {
checked?: boolean;
};
};
export type EventHandler = (...args: any[]) => void;

View File

@ -1,71 +1,28 @@
import PropTypes from './vue-types';
import {
defineComponent,
nextTick,
onBeforeMount,
onMounted,
onUpdated,
Teleport,
watch,
} from 'vue';
import { useInjectPortal } from '../vc-trigger/context';
import { defineComponent, nextTick, onBeforeUnmount, onUpdated, Teleport } from 'vue';
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'Portal',
inheritAttrs: false,
props: {
getContainer: PropTypes.func.isRequired,
didUpdate: Function,
didUpdate: PropTypes.func,
},
setup(props, { slots }) {
let isSSR = true;
// getContainer
let container: HTMLElement;
const { shouldRender } = useInjectPortal();
const container = props.getContainer();
function setContainer() {
if (shouldRender.value) {
container = props.getContainer();
}
}
onBeforeMount(() => {
isSSR = false;
// drawer
setContainer();
});
onMounted(() => {
if (container) return;
// https://github.com/vueComponent/ant-design-vue/issues/6937
setContainer();
});
const stopWatch = watch(shouldRender, () => {
if (shouldRender.value && !container) {
container = props.getContainer();
}
if (container) {
stopWatch();
}
});
onUpdated(() => {
nextTick(() => {
if (shouldRender.value) {
props.didUpdate?.(props);
}
props.didUpdate?.(props);
});
});
// onBeforeUnmount(() => {
// if (container && container.parentNode) {
// container.parentNode.removeChild(container);
// }
// });
return () => {
if (!shouldRender.value) return null;
if (isSSR) {
return slots.default?.();
onBeforeUnmount(() => {
if (container && container.parentNode) {
container.parentNode.removeChild(container);
}
});
return () => {
return container ? <Teleport to={container} v-slots={slots}></Teleport> : null;
};
},

View File

@ -1,19 +1,20 @@
import PropTypes from './vue-types';
import switchScrollingEffect from './switchScrollingEffect';
import setStyle from './setStyle';
import Portal from './Portal';
import {
defineComponent,
shallowRef,
ref,
watch,
onMounted,
onBeforeUnmount,
onUpdated,
nextTick,
computed,
getCurrentInstance,
} from 'vue';
import canUseDom from './canUseDom';
import raf from './raf';
import { booleanType } from './type';
import useScrollLocker from './hooks/useScrollLocker';
import ScrollLocker from '../vc-util/Dom/scrollLocker';
import wrapperRaf from './raf';
import { nextTick } from 'process';
let openCount = 0;
const supportDom = canUseDom();
@ -23,13 +24,17 @@ export function getOpenCount() {
return process.env.NODE_ENV === 'test' ? openCount : 0;
}
// https://github.com/ant-design/ant-design/issues/19340
// https://github.com/ant-design/ant-design/issues/19332
let cacheOverflow = {};
const getParent = (getContainer: GetContainer) => {
if (!supportDom) {
return null;
}
if (getContainer) {
if (typeof getContainer === 'string') {
return document.querySelectorAll(getContainer)[0] as HTMLElement;
return document.querySelectorAll(getContainer)[0];
}
if (typeof getContainer === 'function') {
return getContainer();
@ -44,36 +49,31 @@ const getParent = (getContainer: GetContainer) => {
export type GetContainer = string | HTMLElement | (() => HTMLElement);
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'PortalWrapper',
inheritAttrs: false,
props: {
wrapperClassName: String,
forceRender: { type: Boolean, default: undefined },
wrapperClassName: PropTypes.string,
forceRender: PropTypes.looseBool,
getContainer: PropTypes.any,
visible: { type: Boolean, default: undefined },
autoLock: booleanType(),
didUpdate: Function,
visible: PropTypes.looseBool,
},
setup(props, { slots }) {
const container = shallowRef<HTMLElement>();
const componentRef = shallowRef();
const rafId = shallowRef<number>();
const triggerUpdate = shallowRef(1);
const defaultContainer = canUseDom() && document.createElement('div');
const container = ref<HTMLElement>();
const componentRef = ref();
const rafId = ref<number>();
const scrollLocker = new ScrollLocker({
container: getParent(props.getContainer) as HTMLElement,
});
const removeCurrentContainer = () => {
// Portal will remove from `parentNode`.
// Let's handle this again to avoid refactor issue.
if (container.value === defaultContainer) {
container.value?.parentNode?.removeChild(container.value);
}
container.value = null;
container.value?.parentNode?.removeChild(container.value);
};
let parent: HTMLElement = null;
const attachToParent = (force = false) => {
if (force || (container.value && !container.value.parentNode)) {
parent = getParent(props.getContainer);
const parent = getParent(props.getContainer);
if (parent) {
parent.appendChild(container.value);
return true;
@ -84,12 +84,14 @@ export default defineComponent({
return true;
};
// attachToParent();
const getContainer = () => {
if (!supportDom) {
return null;
}
if (!container.value) {
container.value = defaultContainer;
container.value = document.createElement('div');
attachToParent(true);
}
setWrapperClassName();
@ -105,31 +107,41 @@ export default defineComponent({
setWrapperClassName();
attachToParent();
});
useScrollLocker(
computed(() => {
return (
props.autoLock &&
props.visible &&
canUseDom() &&
(container.value === document.body || container.value === defaultContainer)
);
}),
);
/**
* Enhance ./switchScrollingEffect
* 1. Simulate document body scroll bar with
* 2. Record body has overflow style and recover when all of PortalWrapper invisible
* 3. Disable body scroll when PortalWrapper has open
*
* @memberof PortalWrapper
*/
const switchScrolling = () => {
if (openCount === 1 && !Object.keys(cacheOverflow).length) {
switchScrollingEffect();
// Must be set after switchScrollingEffect
cacheOverflow = setStyle({
overflow: 'hidden',
overflowX: 'hidden',
overflowY: 'hidden',
});
} else if (!openCount) {
setStyle(cacheOverflow);
cacheOverflow = {};
switchScrollingEffect(true);
}
};
const instance = getCurrentInstance();
onMounted(() => {
let init = false;
watch(
[() => props.visible, () => props.getContainer],
([visible, getContainer], [prevVisible, prevGetContainer]) => {
// Update count
if (supportDom) {
parent = getParent(props.getContainer);
if (parent === document.body) {
if (visible && !prevVisible) {
openCount += 1;
} else if (init) {
openCount -= 1;
}
if (supportDom && getParent(props.getContainer) === document.body) {
if (visible && !prevVisible) {
openCount += 1;
} else if (init) {
openCount -= 1;
}
}
@ -144,6 +156,17 @@ export default defineComponent({
) {
removeCurrentContainer();
}
// updateScrollLocker
if (
visible &&
visible !== prevVisible &&
supportDom &&
getParent(getContainer) !== scrollLocker.getContainer()
) {
scrollLocker.reLock({
container: getParent(getContainer) as HTMLElement,
});
}
}
init = true;
},
@ -152,35 +175,38 @@ export default defineComponent({
nextTick(() => {
if (!attachToParent()) {
rafId.value = raf(() => {
triggerUpdate.value += 1;
rafId.value = wrapperRaf(() => {
instance.update();
});
}
});
});
onBeforeUnmount(() => {
const { visible } = props;
if (supportDom && parent === document.body) {
const { visible, getContainer } = props;
if (supportDom && getParent(getContainer) === document.body) {
// render func
openCount = visible && openCount ? openCount - 1 : openCount;
}
removeCurrentContainer();
raf.cancel(rafId.value);
wrapperRaf.cancel(rafId.value);
});
return () => {
const { forceRender, visible } = props;
let portal = null;
const childProps = {
getOpenCount: () => openCount,
getContainer,
switchScrollingEffect: switchScrolling,
scrollLocker,
};
if (triggerUpdate.value && (forceRender || visible || componentRef.value)) {
if (forceRender || visible || componentRef.value) {
portal = (
<Portal
getContainer={getContainer}
ref={componentRef}
didUpdate={props.didUpdate}
v-slots={{ default: () => slots.default?.(childProps) }}
></Portal>
);

View File

@ -1,7 +1,6 @@
import { defineComponent } from 'vue';
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'Portal',
inheritAttrs: false,
props: ['getContainer'],

View File

@ -1,11 +0,0 @@
import { defineComponent } from 'vue';
import { customRenderSlot } from '../vnode';
export default defineComponent({
name: 'RenderSlot',
setup(_props, { slots }) {
return () => {
return customRenderSlot(slots, 'default', {}, () => ['default value']);
};
},
});

View File

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

View File

@ -1,26 +0,0 @@
import RenderSlot from '../__mocks__/RenderSlot';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
describe('render slot content', () => {
it('renders slot content', () => {
const wrapper = mount(RenderSlot, {
slots: {
default: () => 'This is slot content',
},
});
expect(wrapper.html()).toContain('This is slot content');
});
it('render default value when slot is fragment', async () => {
const wrapper = mount(RenderSlot, {
slots: {
default: () => <></>,
},
});
await nextTick();
expect(wrapper.html()).toContain('default value');
});
});

View File

@ -0,0 +1,35 @@
function onCompositionStart(e) {
e.target.composing = true;
}
function onCompositionEnd(e) {
// prevent triggering an input event for no reason
if (!e.target.composing) return;
e.target.composing = false;
trigger(e.target, 'input');
}
function trigger(el, type) {
const e = document.createEvent('HTMLEvents');
e.initEvent(type, true, true);
el.dispatchEvent(e);
}
export function addEventListener(el, event, handler, options) {
el.addEventListener(event, handler, options);
}
const antInput = {
created(el, binding) {
if (!binding.modifiers || !binding.modifiers.lazy) {
addEventListener(el, 'compositionstart', onCompositionStart);
addEventListener(el, 'compositionend', onCompositionEnd);
// Safari < 10.2 & UIWebView doesn't fire compositionend when
// switching focus before confirming composition choice
// this also fixes the issue where some browsers e.g. iOS Chrome
// fires "change" instead of "input" on autocomplete.
addEventListener(el, 'change', onCompositionEnd);
}
},
};
export default antInput;

View File

@ -1,50 +0,0 @@
import { nextTick } from 'vue';
import { addClass, removeClass } from '../vc-util/Dom/class';
import type { CSSMotionProps } from './transition';
const collapseMotion = (name = 'ant-motion-collapse', appear = true): CSSMotionProps => {
return {
name,
appear,
css: true,
onBeforeEnter: (node: HTMLDivElement) => {
node.style.height = '0px';
node.style.opacity = '0';
addClass(node, name);
},
onEnter: (node: HTMLDivElement) => {
nextTick(() => {
node.style.height = `${node.scrollHeight}px`;
node.style.opacity = '1';
});
},
onAfterEnter: (node: HTMLDivElement) => {
if (node) {
removeClass(node, name);
node.style.height = null;
node.style.opacity = null;
}
},
onBeforeLeave: (node: HTMLDivElement) => {
addClass(node, name);
node.style.height = `${node.offsetHeight}px`;
node.style.opacity = null;
},
onLeave: (node: HTMLDivElement) => {
setTimeout(() => {
node.style.height = '0px';
node.style.opacity = '0';
});
},
onAfterLeave: (node: HTMLDivElement) => {
if (node) {
removeClass(node, name);
if (node.style) {
node.style.height = null;
node.style.opacity = null;
}
}
},
};
};
export default collapseMotion;

View File

@ -1,34 +1,23 @@
import type { PresetColorKey } from '../theme/interface';
import { PresetColors } from '../theme/interface';
import type { ElementOf } from './type';
import { tuple } from './type';
type InverseColor = `${PresetColorKey}-inverse`;
const inverseColors = PresetColors.map<InverseColor>(color => `${color}-inverse`);
export const PresetStatusColorTypes = tuple('success', 'processing', 'error', 'default', 'warning');
export const PresetStatusColorTypes = [
'success',
'processing',
'error',
'default',
'warning',
] as const;
export const PresetColorTypes = tuple(
'pink',
'red',
'yellow',
'orange',
'cyan',
'green',
'blue',
'purple',
'geekblue',
'magenta',
'volcano',
'gold',
'lime',
);
export type PresetColorType = PresetColorKey | InverseColor;
export type PresetStatusColorType = (typeof PresetStatusColorTypes)[number];
/**
* determine if the color keyword belongs to the `Ant Design` {@link PresetColors}.
* @param color color to be judged
* @param includeInverse whether to include reversed colors
*/
export function isPresetColor(color?: any, includeInverse = true) {
if (includeInverse) {
return [...inverseColors, ...PresetColors].includes(color);
}
return PresetColors.includes(color);
}
export function isPresetStatusColor(color?: any): color is PresetStatusColorType {
return PresetStatusColorTypes.includes(color);
}
export type PresetColorType = ElementOf<typeof PresetColorTypes>;
export type PresetStatusColorType = ElementOf<typeof PresetStatusColorTypes>;

View File

@ -0,0 +1,22 @@
/**
* Safe chained function
*
* Will only create a new function if needed,
* otherwise will pass back existing functions or null.
*
* @returns {function|null}
*/
export default function createChainedFunction() {
const args = [].slice.call(arguments, 0);
if (args.length === 1) {
return args[0];
}
return function chainedFunction() {
for (let i = 0; i < args.length; i++) {
if (args[i] && args[i].apply) {
args[i].apply(this, arguments);
}
}
};
}

View File

@ -1,22 +0,0 @@
import { inject, provide, reactive, watchEffect } from 'vue';
function createContext<T extends Record<string, any>>(defaultValue?: T) {
const contextKey = Symbol('contextKey');
const useProvide = (props: T, newProps?: T) => {
const mergedProps = reactive<T>({} as T);
provide(contextKey, mergedProps);
watchEffect(() => {
Object.assign(mergedProps, props, newProps || {});
});
return mergedProps;
};
const useInject = () => {
return inject(contextKey, defaultValue as T) || ({} as T);
};
return {
useProvide,
useInject,
};
}
export default createContext;

View File

@ -0,0 +1,130 @@
const START_EVENT_NAME_MAP = {
transitionstart: {
transition: 'transitionstart',
WebkitTransition: 'webkitTransitionStart',
MozTransition: 'mozTransitionStart',
OTransition: 'oTransitionStart',
msTransition: 'MSTransitionStart',
},
animationstart: {
animation: 'animationstart',
WebkitAnimation: 'webkitAnimationStart',
MozAnimation: 'mozAnimationStart',
OAnimation: 'oAnimationStart',
msAnimation: 'MSAnimationStart',
},
};
const END_EVENT_NAME_MAP = {
transitionend: {
transition: 'transitionend',
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'mozTransitionEnd',
OTransition: 'oTransitionEnd',
msTransition: 'MSTransitionEnd',
},
animationend: {
animation: 'animationend',
WebkitAnimation: 'webkitAnimationEnd',
MozAnimation: 'mozAnimationEnd',
OAnimation: 'oAnimationEnd',
msAnimation: 'MSAnimationEnd',
},
};
const startEvents = [];
const endEvents = [];
function detectEvents() {
const testEl = document.createElement('div');
const style = testEl.style;
if (!('AnimationEvent' in window)) {
delete START_EVENT_NAME_MAP.animationstart.animation;
delete END_EVENT_NAME_MAP.animationend.animation;
}
if (!('TransitionEvent' in window)) {
delete START_EVENT_NAME_MAP.transitionstart.transition;
delete END_EVENT_NAME_MAP.transitionend.transition;
}
function process(EVENT_NAME_MAP, events) {
for (const baseEventName in EVENT_NAME_MAP) {
if (EVENT_NAME_MAP.hasOwnProperty(baseEventName)) {
const baseEvents = EVENT_NAME_MAP[baseEventName];
for (const styleName in baseEvents) {
if (styleName in style) {
events.push(baseEvents[styleName]);
break;
}
}
}
}
}
process(START_EVENT_NAME_MAP, startEvents);
process(END_EVENT_NAME_MAP, endEvents);
}
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
detectEvents();
}
function addEventListener(node, eventName, eventListener) {
node.addEventListener(eventName, eventListener, false);
}
function removeEventListener(node, eventName, eventListener) {
node.removeEventListener(eventName, eventListener, false);
}
const TransitionEvents = {
// Start events
startEvents,
addStartEventListener(node, eventListener) {
if (startEvents.length === 0) {
window.setTimeout(eventListener, 0);
return;
}
startEvents.forEach(startEvent => {
addEventListener(node, startEvent, eventListener);
});
},
removeStartEventListener(node, eventListener) {
if (startEvents.length === 0) {
return;
}
startEvents.forEach(startEvent => {
removeEventListener(node, startEvent, eventListener);
});
},
// End events
endEvents,
addEndEventListener(node, eventListener) {
if (endEvents.length === 0) {
window.setTimeout(eventListener, 0);
return;
}
endEvents.forEach(endEvent => {
addEventListener(node, endEvent, eventListener);
});
},
removeEndEventListener(node, eventListener) {
if (endEvents.length === 0) {
return;
}
endEvents.forEach(endEvent => {
removeEventListener(node, endEvent, eventListener);
});
},
};
export default TransitionEvents;

View File

@ -0,0 +1,184 @@
// https://github.com/yiminghe/css-animation 1.5.0
import Event from './Event';
import classes from '../component-classes';
import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout';
const isCssAnimationSupported = Event.endEvents.length !== 0;
const capitalPrefixes = [
'Webkit',
'Moz',
'O',
// ms is special .... !
'ms',
];
const prefixes = ['-webkit-', '-moz-', '-o-', 'ms-', ''];
function getStyleProperty(node, name) {
// old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
const style = window.getComputedStyle(node, null);
let ret = '';
for (let i = 0; i < prefixes.length; i++) {
ret = style.getPropertyValue(prefixes[i] + name);
if (ret) {
break;
}
}
return ret;
}
function fixBrowserByTimeout(node) {
if (isCssAnimationSupported) {
const transitionDelay = parseFloat(getStyleProperty(node, 'transition-delay')) || 0;
const transitionDuration = parseFloat(getStyleProperty(node, 'transition-duration')) || 0;
const animationDelay = parseFloat(getStyleProperty(node, 'animation-delay')) || 0;
const animationDuration = parseFloat(getStyleProperty(node, 'animation-duration')) || 0;
const time = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay);
// sometimes, browser bug
node.rcEndAnimTimeout = setTimeout(() => {
node.rcEndAnimTimeout = null;
if (node.rcEndListener) {
node.rcEndListener();
}
}, time * 1000 + 200);
}
}
function clearBrowserBugTimeout(node) {
if (node.rcEndAnimTimeout) {
clearTimeout(node.rcEndAnimTimeout);
node.rcEndAnimTimeout = null;
}
}
const cssAnimation = (node, transitionName, endCallback) => {
const nameIsObj = typeof transitionName === 'object';
const className = nameIsObj ? transitionName.name : transitionName;
const activeClassName = nameIsObj ? transitionName.active : `${transitionName}-active`;
let end = endCallback;
let start;
let active;
const nodeClasses = classes(node);
if (endCallback && Object.prototype.toString.call(endCallback) === '[object Object]') {
end = endCallback.end;
start = endCallback.start;
active = endCallback.active;
}
if (node.rcEndListener) {
node.rcEndListener();
}
node.rcEndListener = e => {
if (e && e.target !== node) {
return;
}
if (node.rcAnimTimeout) {
cancelAnimationTimeout(node.rcAnimTimeout);
node.rcAnimTimeout = null;
}
clearBrowserBugTimeout(node);
nodeClasses.remove(className);
nodeClasses.remove(activeClassName);
Event.removeEndEventListener(node, node.rcEndListener);
node.rcEndListener = null;
// Usually this optional end is used for informing an owner of
// a leave animation and telling it to remove the child.
if (end) {
end();
}
};
Event.addEndEventListener(node, node.rcEndListener);
if (start) {
start();
}
nodeClasses.add(className);
node.rcAnimTimeout = requestAnimationTimeout(() => {
node.rcAnimTimeout = null;
nodeClasses.add(className);
nodeClasses.add(activeClassName);
if (active) {
requestAnimationTimeout(active, 0);
}
fixBrowserByTimeout(node);
// 30ms for firefox
}, 30);
return {
stop() {
if (node.rcEndListener) {
node.rcEndListener();
}
},
};
};
cssAnimation.style = (node, style, callback) => {
if (node.rcEndListener) {
node.rcEndListener();
}
node.rcEndListener = e => {
if (e && e.target !== node) {
return;
}
if (node.rcAnimTimeout) {
cancelAnimationTimeout(node.rcAnimTimeout);
node.rcAnimTimeout = null;
}
clearBrowserBugTimeout(node);
Event.removeEndEventListener(node, node.rcEndListener);
node.rcEndListener = null;
// Usually this optional callback is used for informing an owner of
// a leave animation and telling it to remove the child.
if (callback) {
callback();
}
};
Event.addEndEventListener(node, node.rcEndListener);
node.rcAnimTimeout = requestAnimationTimeout(() => {
for (const s in style) {
if (style.hasOwnProperty(s)) {
node.style[s] = style[s];
}
}
node.rcAnimTimeout = null;
fixBrowserByTimeout(node);
}, 0);
};
cssAnimation.setTransition = (node, p, value) => {
let property = p;
let v = value;
if (value === undefined) {
v = property;
property = '';
}
property = property || '';
capitalPrefixes.forEach(prefix => {
node.style[`${prefix}Transition${property}`] = v;
});
};
cssAnimation.isCssAnimationSupported = isCssAnimationSupported;
export { isCssAnimationSupported };
export default cssAnimation;

View File

@ -1,29 +0,0 @@
export type KeyType = string | number;
type ValueType = [number, any]; // [times, realValue]
const SPLIT = '%';
class Entity {
instanceId: string;
constructor(instanceId: string) {
this.instanceId = instanceId;
}
/** @private Internal cache map. Do not access this directly */
cache = new Map<string, ValueType>();
get(keys: KeyType[] | string): ValueType | null {
return this.cache.get(Array.isArray(keys) ? keys.join(SPLIT) : keys) || null;
}
update(keys: KeyType[] | string, valueFn: (origin: ValueType | null) => ValueType | null) {
const path = Array.isArray(keys) ? keys.join(SPLIT) : keys;
const prevValue = this.cache.get(path)!;
const nextValue = valueFn(prevValue);
if (nextValue === null) {
this.cache.delete(path);
} else {
this.cache.set(path, nextValue);
}
}
}
export default Entity;

View File

@ -1,19 +0,0 @@
import type { CSSInterpolation } from './hooks/useStyleRegister';
class Keyframe {
private name: string;
style: CSSInterpolation;
constructor(name: string, style: CSSInterpolation) {
this.name = name;
this.style = style;
}
getName(hashId = ''): string {
return hashId ? `${hashId}-${this.name}` : this.name;
}
_keyframe = true;
}
export default Keyframe;

View File

@ -1,195 +0,0 @@
import type { ShallowRef, ExtractPropTypes, InjectionKey, Ref } from 'vue';
import {
provide,
defineComponent,
unref,
inject,
watch,
shallowRef,
getCurrentInstance,
} from 'vue';
import CacheEntity from './Cache';
import type { Linter } from './linters/interface';
import type { Transformer } from './transformers/interface';
import { arrayType, booleanType, objectType, someType, stringType, withInstall } from '../type';
export const ATTR_TOKEN = 'data-token-hash';
export const ATTR_MARK = 'data-css-hash';
export const ATTR_CACHE_PATH = 'data-cache-path';
// Mark css-in-js instance in style element
export const CSS_IN_JS_INSTANCE = '__cssinjs_instance__';
export function createCache() {
const cssinjsInstanceId = Math.random().toString(12).slice(2);
// Tricky SSR: Move all inline style to the head.
// PS: We do not recommend tricky mode.
if (typeof document !== 'undefined' && document.head && document.body) {
const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || [];
const { firstChild } = document.head;
Array.from(styles).forEach(style => {
(style as any)[CSS_IN_JS_INSTANCE] = (style as any)[CSS_IN_JS_INSTANCE] || cssinjsInstanceId;
// Not force move if no head
// Not force move if no head
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
document.head.insertBefore(style, firstChild);
}
});
// Deduplicate of moved styles
const styleHash: Record<string, boolean> = {};
Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => {
const hash = style.getAttribute(ATTR_MARK)!;
if (styleHash[hash]) {
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
style.parentNode?.removeChild(style);
}
} else {
styleHash[hash] = true;
}
});
}
return new CacheEntity(cssinjsInstanceId);
}
export type HashPriority = 'low' | 'high';
export interface StyleContextProps {
autoClear?: boolean;
/** @private Test only. Not work in production. */
mock?: 'server' | 'client';
/**
* Only set when you need ssr to extract style on you own.
* If not provided, it will auto create <style /> on the end of Provider in server side.
*/
cache: CacheEntity;
/** Tell children that this context is default generated context */
defaultCache: boolean;
/** Use `:where` selector to reduce hashId css selector priority */
hashPriority?: HashPriority;
/** Tell cssinjs where to inject style in */
container?: Element | ShadowRoot;
/** Component wil render inline `<style />` for fallback in SSR. Not recommend. */
ssrInline?: boolean;
/** Transform css before inject in document. Please note that `transformers` do not support dynamic update */
transformers?: Transformer[];
/**
* Linters to lint css before inject in document.
* Styles will be linted after transforming.
* Please note that `linters` do not support dynamic update.
*/
linters?: Linter[];
}
const StyleContextKey: InjectionKey<ShallowRef<Partial<StyleContextProps>>> =
Symbol('StyleContextKey');
export type UseStyleProviderProps = Partial<StyleContextProps> | Ref<Partial<StyleContextProps>>;
// fix: https://github.com/vueComponent/ant-design-vue/issues/7023
const getCache = () => {
const instance = getCurrentInstance();
let cache: CacheEntity;
if (instance && instance.appContext) {
const globalCache = instance.appContext?.config?.globalProperties?.__ANTDV_CSSINJS_CACHE__;
if (globalCache) {
cache = globalCache;
} else {
cache = createCache();
if (instance.appContext.config.globalProperties) {
instance.appContext.config.globalProperties.__ANTDV_CSSINJS_CACHE__ = cache;
}
}
} else {
cache = createCache();
}
return cache;
};
const defaultStyleContext: StyleContextProps = {
cache: createCache(),
defaultCache: true,
hashPriority: 'low',
};
// fix: https://github.com/vueComponent/ant-design-vue/issues/6912
export const useStyleInject = () => {
const cache = getCache();
return inject(StyleContextKey, shallowRef({ ...defaultStyleContext, cache }));
};
export const useStyleProvider = (props: UseStyleProviderProps) => {
const parentContext = useStyleInject();
const context = shallowRef<Partial<StyleContextProps>>({
...defaultStyleContext,
cache: createCache(),
});
watch(
[() => unref(props), parentContext],
() => {
const mergedContext: Partial<StyleContextProps> = {
...parentContext.value,
};
const propsValue = unref(props);
Object.keys(propsValue).forEach(key => {
const value = propsValue[key];
if (propsValue[key] !== undefined) {
mergedContext[key] = value;
}
});
const { cache } = propsValue;
mergedContext.cache = mergedContext.cache || createCache();
mergedContext.defaultCache = !cache && parentContext.value.defaultCache;
context.value = mergedContext;
},
{ immediate: true },
);
provide(StyleContextKey, context);
return context;
};
export const styleProviderProps = () => ({
autoClear: booleanType(),
/** @private Test only. Not work in production. */
mock: stringType<'server' | 'client'>(),
/**
* Only set when you need ssr to extract style on you own.
* If not provided, it will auto create <style /> on the end of Provider in server side.
*/
cache: objectType<CacheEntity>(),
/** Tell children that this context is default generated context */
defaultCache: booleanType(),
/** Use `:where` selector to reduce hashId css selector priority */
hashPriority: stringType<HashPriority>(),
/** Tell cssinjs where to inject style in */
container: someType<Element | ShadowRoot>(),
/** Component wil render inline `<style />` for fallback in SSR. Not recommend. */
ssrInline: booleanType(),
/** Transform css before inject in document. Please note that `transformers` do not support dynamic update */
transformers: arrayType<Transformer[]>(),
/**
* Linters to lint css before inject in document.
* Styles will be linted after transforming.
* Please note that `linters` do not support dynamic update.
*/
linters: arrayType<Linter[]>(),
});
export type StyleProviderProps = Partial<ExtractPropTypes<ReturnType<typeof styleProviderProps>>>;
export const StyleProvider = withInstall(
defineComponent({
name: 'AStyleProvider',
inheritAttrs: false,
props: styleProviderProps(),
setup(props, { slots }) {
useStyleProvider(props);
return () => slots.default?.();
},
}),
);
export default {
useStyleInject,
useStyleProvider,
StyleProvider,
};

View File

@ -1,165 +0,0 @@
import hash from '@emotion/hash';
import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext';
import type Theme from '../theme/Theme';
import useGlobalCache from './useGlobalCache';
import { flattenToken, token2key } from '../util';
import type { Ref } from 'vue';
import { ref, computed } from 'vue';
const EMPTY_OVERRIDE = {};
const isProduction = process.env.NODE_ENV === 'production';
// nuxt generate when NODE_ENV is prerender
const isPrerender = process.env.NODE_ENV === 'prerender';
// Generate different prefix to make user selector break in production env.
// This helps developer not to do style override directly on the hash id.
const hashPrefix = !isProduction && !isPrerender ? 'css-dev-only-do-not-override' : 'css';
export interface Option<DerivativeToken, DesignToken> {
/**
* Generate token with salt.
* This is used to generate different hashId even same derivative token for different version.
*/
salt?: string;
override?: object;
/**
* Format token as you need. Such as:
*
* - rename token
* - merge token
* - delete token
*
* This should always be the same since it's one time process.
* It's ok to useMemo outside but this has better cache strategy.
*/
formatToken?: (mergedToken: any) => DerivativeToken;
/**
* Get final token with origin token, override token and theme.
* The parameters do not contain formatToken since it's passed by user.
* @param origin The original token.
* @param override Extra tokens to override.
* @param theme Theme instance. Could get derivative token by `theme.getDerivativeToken`
*/
getComputedToken?: (
origin: DesignToken,
override: object,
theme: Theme<any, any>,
) => DerivativeToken;
}
const tokenKeys = new Map<string, number>();
function recordCleanToken(tokenKey: string) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
}
function removeStyleTags(key: string, instanceId: string) {
if (typeof document !== 'undefined') {
const styles = document.querySelectorAll(`style[${ATTR_TOKEN}="${key}"]`);
styles.forEach(style => {
if ((style as any)[CSS_IN_JS_INSTANCE] === instanceId) {
style.parentNode?.removeChild(style);
}
});
}
}
const TOKEN_THRESHOLD = 0;
// Remove will check current keys first
function cleanTokenStyle(tokenKey: string, instanceId: string) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
const tokenKeyList = Array.from(tokenKeys.keys());
const cleanableKeyList = tokenKeyList.filter(key => {
const count = tokenKeys.get(key) || 0;
return count <= 0;
});
// Should keep tokens under threshold for not to insert style too often
if (tokenKeyList.length - cleanableKeyList.length > TOKEN_THRESHOLD) {
cleanableKeyList.forEach(key => {
removeStyleTags(key, instanceId);
tokenKeys.delete(key);
});
}
}
export const getComputedToken = <DerivativeToken = object, DesignToken = DerivativeToken>(
originToken: DesignToken,
overrideToken: object,
theme: Theme<any, any>,
format?: (token: DesignToken) => DerivativeToken,
) => {
const derivativeToken = theme.getDerivativeToken(originToken);
// Merge with override
let mergedDerivativeToken = {
...derivativeToken,
...overrideToken,
};
// Format if needed
if (format) {
mergedDerivativeToken = format(mergedDerivativeToken);
}
return mergedDerivativeToken;
};
/**
* Cache theme derivative token as global shared one
* @param theme Theme entity
* @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
* @param option Additional config
* @returns Call Theme.getDerivativeToken(tokenObject) to get token
*/
export default function useCacheToken<DerivativeToken = object, DesignToken = DerivativeToken>(
theme: Ref<Theme<any, any>>,
tokens: Ref<Partial<DesignToken>[]>,
option: Ref<Option<DerivativeToken, DesignToken>> = ref({}),
) {
const style = useStyleInject();
// Basic - We do basic cache here
const mergedToken = computed(() => Object.assign({}, ...tokens.value));
const tokenStr = computed(() => flattenToken(mergedToken.value));
const overrideTokenStr = computed(() => flattenToken(option.value.override || EMPTY_OVERRIDE));
const cachedToken = useGlobalCache<[DerivativeToken & { _tokenKey: string }, string]>(
'token',
computed(() => [
option.value.salt || '',
theme.value.id,
tokenStr.value,
overrideTokenStr.value,
]),
() => {
const {
salt = '',
override = EMPTY_OVERRIDE,
formatToken,
getComputedToken: compute,
} = option.value;
const mergedDerivativeToken = compute
? compute(mergedToken.value, override, theme.value)
: getComputedToken(mergedToken.value, override, theme.value, formatToken);
// Optimize for `useStyleRegister` performance
const tokenKey = token2key(mergedDerivativeToken, salt);
mergedDerivativeToken._tokenKey = tokenKey;
recordCleanToken(tokenKey);
const hashId = `${hashPrefix}-${hash(tokenKey)}`;
mergedDerivativeToken._hashId = hashId; // Not used
return [mergedDerivativeToken, hashId];
},
cache => {
// Remove token will remove all related style
cleanTokenStyle(cache[0]._tokenKey, style.value?.cache.instanceId);
},
);
return cachedToken;
}

View File

@ -1,58 +0,0 @@
import { useStyleInject } from '../StyleContext';
import type { KeyType } from '../Cache';
import useHMR from './useHMR';
import type { ShallowRef, Ref } from 'vue';
import { onBeforeUnmount, watch, watchEffect, shallowRef } from 'vue';
export default function useClientCache<CacheType>(
prefix: string,
keyPath: Ref<KeyType[]>,
cacheFn: () => CacheType,
onCacheRemove?: (cache: CacheType, fromHMR: boolean) => void,
): ShallowRef<CacheType> {
const styleContext = useStyleInject();
const fullPathStr = shallowRef('');
const res = shallowRef<CacheType>();
watchEffect(() => {
fullPathStr.value = [prefix, ...keyPath.value].join('%');
});
const HMRUpdate = useHMR();
const clearCache = (pathStr: string) => {
styleContext.value.cache.update(pathStr, prevCache => {
const [times = 0, cache] = prevCache || [];
const nextCount = times - 1;
if (nextCount === 0) {
onCacheRemove?.(cache, false);
return null;
}
return [times - 1, cache];
});
};
watch(
fullPathStr,
(newStr, oldStr) => {
if (oldStr) clearCache(oldStr);
// Create cache
styleContext.value.cache.update(newStr, prevCache => {
const [times = 0, cache] = prevCache || [];
// HMR should always ignore cache since developer may change it
let tmpCache = cache;
if (process.env.NODE_ENV !== 'production' && cache && HMRUpdate) {
onCacheRemove?.(tmpCache, HMRUpdate);
tmpCache = null;
}
const mergedCache = tmpCache || cacheFn();
return [times + 1, mergedCache];
});
res.value = styleContext.value.cache.get(fullPathStr.value)![1];
},
{ immediate: true },
);
onBeforeUnmount(() => {
clearCache(fullPathStr.value);
});
return res;
}

View File

@ -1,34 +0,0 @@
function useProdHMR() {
return false;
}
let webpackHMR = false;
function useDevHMR() {
return webpackHMR;
}
export default process.env.NODE_ENV === 'production' ? useProdHMR : useDevHMR;
// Webpack `module.hot.accept` do not support any deps update trigger
// We have to hack handler to force mark as HRM
if (
process.env.NODE_ENV !== 'production' &&
typeof module !== 'undefined' &&
module &&
(module as any).hot &&
typeof window !== 'undefined'
) {
const win = window as any;
if (typeof win.webpackHotUpdate === 'function') {
const originWebpackHotUpdate = win.webpackHotUpdate;
win.webpackHotUpdate = (...args: any[]) => {
webpackHMR = true;
setTimeout(() => {
webpackHMR = false;
}, 0);
return originWebpackHotUpdate(...args);
};
}
}

View File

@ -1,91 +0,0 @@
import canUseDom from '../../../../_util/canUseDom';
import { ATTR_MARK } from '../../StyleContext';
export const ATTR_CACHE_MAP = 'data-ant-cssinjs-cache-path';
/**
* This marks style from the css file.
* Which means not exist in `<style />` tag.
*/
export const CSS_FILE_STYLE = '_FILE_STYLE__';
export function serialize(cachePathMap: Record<string, string>) {
return Object.keys(cachePathMap)
.map(path => {
const hash = cachePathMap[path];
return `${path}:${hash}`;
})
.join(';');
}
let cachePathMap: Record<string, string>;
let fromCSSFile = true;
/**
* @private Test usage only. Can save remove if no need.
*/
export function reset(mockCache?: Record<string, string>, fromFile = true) {
cachePathMap = mockCache!;
fromCSSFile = fromFile;
}
export function prepare() {
if (!cachePathMap) {
cachePathMap = {};
if (canUseDom()) {
const div = document.createElement('div');
div.className = ATTR_CACHE_MAP;
div.style.position = 'fixed';
div.style.visibility = 'hidden';
div.style.top = '-9999px';
document.body.appendChild(div);
let content = getComputedStyle(div).content || '';
content = content.replace(/^"/, '').replace(/"$/, '');
// Fill data
content.split(';').forEach(item => {
const [path, hash] = item.split(':');
cachePathMap[path] = hash;
});
// Remove inline record style
const inlineMapStyle = document.querySelector(`style[${ATTR_CACHE_MAP}]`);
if (inlineMapStyle) {
fromCSSFile = false;
inlineMapStyle.parentNode?.removeChild(inlineMapStyle);
}
document.body.removeChild(div);
}
}
}
export function existPath(path: string) {
prepare();
return !!cachePathMap[path];
}
export function getStyleAndHash(path: string): [style: string | null, hash: string] {
const hash = cachePathMap[path];
let styleStr: string | null = null;
if (hash && canUseDom()) {
if (fromCSSFile) {
styleStr = CSS_FILE_STYLE;
} else {
const style = document.querySelector(`style[${ATTR_MARK}="${cachePathMap[path]}"]`);
if (style) {
styleStr = style.innerHTML;
} else {
// Clean up since not exist anymore
delete cachePathMap[path];
}
}
}
return [styleStr, hash];
}

View File

@ -1,566 +0,0 @@
import hash from '@emotion/hash';
import type * as CSS from 'csstype';
// @ts-ignore
import unitless from '@emotion/unitless';
import { compile, serialize, stringify } from 'stylis';
import type { Theme, Transformer } from '../..';
import type Cache from '../../Cache';
import type Keyframes from '../../Keyframes';
import type { Linter } from '../../linters';
import { contentQuotesLinter, hashedAnimationLinter } from '../../linters';
import type { HashPriority } from '../../StyleContext';
import {
useStyleInject,
ATTR_CACHE_PATH,
ATTR_MARK,
ATTR_TOKEN,
CSS_IN_JS_INSTANCE,
} from '../../StyleContext';
import { supportLayer } from '../../util';
import useGlobalCache from '../useGlobalCache';
import { removeCSS, updateCSS } from '../../../../vc-util/Dom/dynamicCSS';
import type { Ref } from 'vue';
import { computed } from 'vue';
import type { VueNode } from '../../../type';
import canUseDom from '../../../../_util/canUseDom';
import {
ATTR_CACHE_MAP,
existPath,
getStyleAndHash,
serialize as serializeCacheMap,
} from './cacheMapUtil';
const isClientSide = canUseDom();
const SKIP_CHECK = '_skip_check_';
const MULTI_VALUE = '_multi_value_';
export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'animationName'> & {
animationName?: CSS.PropertiesFallback<number | string>['animationName'] | Keyframes;
};
export type CSSPropertiesWithMultiValues = {
[K in keyof CSSProperties]:
| CSSProperties[K]
| readonly Extract<CSSProperties[K], string>[]
| {
[SKIP_CHECK]?: boolean;
[MULTI_VALUE]?: boolean;
value: CSSProperties[K] | CSSProperties[K][];
};
};
export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject };
type ArrayCSSInterpolation = readonly CSSInterpolation[];
export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject;
export type CSSInterpolation = InterpolationPrimitive | ArrayCSSInterpolation | Keyframes;
export type CSSOthersObject = Record<string, CSSInterpolation>;
export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSSOthersObject {}
// ============================================================================
// == Parser ==
// ============================================================================
// Preprocessor style content to browser support one
export function normalizeStyle(styleStr: string): string {
const serialized = serialize(compile(styleStr), stringify);
return serialized.replace(/\{%%%\:[^;];}/g, ';');
}
function isCompoundCSSProperty(value: CSSObject[string]) {
return typeof value === 'object' && value && (SKIP_CHECK in value || MULTI_VALUE in value);
}
// hash
function injectSelectorHash(key: string, hashId: string, hashPriority?: HashPriority) {
if (!hashId) {
return key;
}
const hashClassName = `.${hashId}`;
const hashSelector = hashPriority === 'low' ? `:where(${hashClassName})` : hashClassName;
// hashId
const keys = key.split(',').map(k => {
const fullPath = k.trim().split(/\s+/);
// Selector HTML Element
let firstPath = fullPath[0] || '';
const htmlElement = firstPath.match(/^\w+/)?.[0] || '';
firstPath = `${htmlElement}${hashSelector}${firstPath.slice(htmlElement.length)}`;
return [firstPath, ...fullPath.slice(1)].join(' ');
});
return keys.join(',');
}
export interface ParseConfig {
hashId?: string;
hashPriority?: HashPriority;
layer?: string;
path?: string;
transformers?: Transformer[];
linters?: Linter[];
}
export interface ParseInfo {
root?: boolean;
injectHash?: boolean;
parentSelectors: string[];
}
// Global effect style will mount once and not removed
// The effect will not save in SSR cache (e.g. keyframes)
const globalEffectStyleKeys = new Set();
/**
* @private Test only. Clear the global effect style keys.
*/
export const _cf =
process.env.NODE_ENV !== 'production' ? () => globalEffectStyleKeys.clear() : undefined;
// Parse CSSObject to style content
export const parseStyle = (
interpolation: CSSInterpolation,
config: ParseConfig = {},
{ root, injectHash, parentSelectors }: ParseInfo = {
root: true,
parentSelectors: [],
},
): [
parsedStr: string,
// Style content which should be unique on all of the style (e.g. Keyframes).
// Firefox will flick with same animation name when exist multiple same keyframes.
effectStyle: Record<string, string>,
] => {
const { hashId, layer, path, hashPriority, transformers = [], linters = [] } = config;
let styleStr = '';
let effectStyle: Record<string, string> = {};
function parseKeyframes(keyframes: Keyframes) {
const animationName = keyframes.getName(hashId);
if (!effectStyle[animationName]) {
const [parsedStr] = parseStyle(keyframes.style, config, {
root: false,
parentSelectors,
});
effectStyle[animationName] = `@keyframes ${keyframes.getName(hashId)}${parsedStr}`;
}
}
function flattenList(list: ArrayCSSInterpolation, fullList: CSSObject[] = []) {
list.forEach(item => {
if (Array.isArray(item)) {
flattenList(item, fullList);
} else if (item) {
fullList.push(item as CSSObject);
}
});
return fullList;
}
const flattenStyleList = flattenList(
Array.isArray(interpolation) ? interpolation : [interpolation],
);
flattenStyleList.forEach(originStyle => {
// Only root level can use raw string
const style: CSSObject = typeof originStyle === 'string' && !root ? {} : originStyle;
if (typeof style === 'string') {
styleStr += `${style}\n`;
} else if ((style as any)._keyframe) {
// Keyframe
parseKeyframes(style as unknown as Keyframes);
} else {
const mergedStyle = transformers.reduce((prev, trans) => trans?.visit?.(prev) || prev, style);
// Normal CSSObject
Object.keys(mergedStyle).forEach(key => {
const value = mergedStyle[key];
if (
typeof value === 'object' &&
value &&
(key !== 'animationName' || !(value as Keyframes)._keyframe) &&
!isCompoundCSSProperty(value)
) {
let subInjectHash = false;
//
let mergedKey = key.trim();
// Whether treat child as root. In most case it is false.
let nextRoot = false;
//
if ((root || injectHash) && hashId) {
if (mergedKey.startsWith('@')) {
// hashId
subInjectHash = true;
} else {
// hashId
mergedKey = injectSelectorHash(key, hashId, hashPriority);
}
} else if (root && !hashId && (mergedKey === '&' || mergedKey === '')) {
// In case of `{ '&': { a: { color: 'red' } } }` or `{ '': { a: { color: 'red' } } }` without hashId,
// we will get `&{a:{color:red;}}` or `{a:{color:red;}}` string for stylis to compile.
// But it does not conform to stylis syntax,
// and finally we will get `{color:red;}` as css, which is wrong.
// So we need to remove key in root, and treat child `{ a: { color: 'red' } }` as root.
mergedKey = '';
nextRoot = true;
}
const [parsedStr, childEffectStyle] = parseStyle(value as any, config, {
root: nextRoot,
injectHash: subInjectHash,
parentSelectors: [...parentSelectors, mergedKey],
});
effectStyle = {
...effectStyle,
...childEffectStyle,
};
styleStr += `${mergedKey}${parsedStr}`;
} else {
function appendStyle(cssKey: string, cssValue: any) {
if (
process.env.NODE_ENV !== 'production' &&
(typeof value !== 'object' || !(value as any)?.[SKIP_CHECK])
) {
[contentQuotesLinter, hashedAnimationLinter, ...linters].forEach(linter =>
linter(cssKey, cssValue, { path, hashId, parentSelectors }),
);
}
//
const styleName = cssKey.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
// Auto suffix with px
let formatValue = cssValue;
if (!unitless[cssKey] && typeof formatValue === 'number' && formatValue !== 0) {
formatValue = `${formatValue}px`;
}
// handle animationName & Keyframe value
if (cssKey === 'animationName' && (cssValue as Keyframes)?._keyframe) {
parseKeyframes(cssValue as Keyframes);
formatValue = (cssValue as Keyframes).getName(hashId);
}
styleStr += `${styleName}:${formatValue};`;
}
const actualValue = (value as any)?.value ?? value;
if (
typeof value === 'object' &&
(value as any)?.[MULTI_VALUE] &&
Array.isArray(actualValue)
) {
actualValue.forEach(item => {
appendStyle(key, item);
});
} else {
appendStyle(key, actualValue);
}
}
});
}
});
if (!root) {
styleStr = `{${styleStr}}`;
} else if (layer && supportLayer()) {
const layerCells = layer.split(',');
const layerName = layerCells[layerCells.length - 1].trim();
styleStr = `@layer ${layerName} {${styleStr}}`;
// Order of layer if needed
if (layerCells.length > 1) {
// zombieJ: stylis do not support layer order, so we need to handle it manually.
styleStr = `@layer ${layer}{%%%:%}${styleStr}`;
}
}
return [styleStr, effectStyle];
};
// ============================================================================
// == Register ==
// ============================================================================
function uniqueHash(path: (string | number)[], styleStr: string) {
return hash(`${path.join('%')}${styleStr}`);
}
// function Empty() {
// return null;
// }
/**
* Register a style to the global style sheet.
*/
export default function useStyleRegister(
info: Ref<{
theme: Theme<any, any>;
token: any;
path: string[];
hashId?: string;
layer?: string;
nonce?: string | (() => string);
clientOnly?: boolean;
/**
* Tell cssinjs the insert order of style.
* It's useful when you need to insert style
* before other style to overwrite for the same selector priority.
*/
order?: number;
}>,
styleFn: () => CSSInterpolation,
) {
const styleContext = useStyleInject();
const tokenKey = computed(() => info.value.token._tokenKey as string);
const fullPath = computed(() => [tokenKey.value, ...info.value.path]);
// Check if need insert style
let isMergedClientSide = isClientSide;
if (process.env.NODE_ENV !== 'production' && styleContext.value.mock !== undefined) {
isMergedClientSide = styleContext.value.mock === 'client';
}
// const [cacheStyle[0], cacheStyle[1], cacheStyle[2]]
useGlobalCache<
[
styleStr: string,
tokenKey: string,
styleId: string,
effectStyle: Record<string, string>,
clientOnly: boolean | undefined,
order: number,
]
>(
'style',
fullPath,
// Create cache if needed
() => {
const { path, hashId, layer, nonce, clientOnly, order = 0 } = info.value;
const cachePath = fullPath.value.join('|');
// Get style from SSR inline style directly
if (existPath(cachePath)) {
const [inlineCacheStyleStr, styleHash] = getStyleAndHash(cachePath);
if (inlineCacheStyleStr) {
return [inlineCacheStyleStr, tokenKey.value, styleHash, {}, clientOnly, order];
}
}
const styleObj = styleFn();
const { hashPriority, container, transformers, linters, cache } = styleContext.value;
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
hashId,
hashPriority,
layer,
path: path.join('-'),
transformers,
linters,
});
const styleStr = normalizeStyle(parsedStyle);
const styleId = uniqueHash(fullPath.value, styleStr);
if (isMergedClientSide) {
const mergedCSSConfig: Parameters<typeof updateCSS>[2] = {
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: order,
};
const nonceStr = typeof nonce === 'function' ? nonce() : nonce;
if (nonceStr) {
mergedCSSConfig.csp = { nonce: nonceStr };
}
const style = updateCSS(styleStr, styleId, mergedCSSConfig);
(style as any)[CSS_IN_JS_INSTANCE] = cache.instanceId;
// Used for `useCacheToken` to remove on batch when token removed
style.setAttribute(ATTR_TOKEN, tokenKey.value);
// Dev usage to find which cache path made this easily
if (process.env.NODE_ENV !== 'production') {
style.setAttribute(ATTR_CACHE_PATH, fullPath.value.join('|'));
}
// Inject client side effect style
Object.keys(effectStyle).forEach(effectKey => {
if (!globalEffectStyleKeys.has(effectKey)) {
globalEffectStyleKeys.add(effectKey);
// Inject
updateCSS(normalizeStyle(effectStyle[effectKey]), `_effect-${effectKey}`, {
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
});
}
});
}
return [styleStr, tokenKey.value, styleId, effectStyle, clientOnly, order];
},
// Remove cache if no need
([, , styleId], fromHMR) => {
if ((fromHMR || styleContext.value.autoClear) && isClientSide) {
removeCSS(styleId, { mark: ATTR_MARK });
}
},
);
return (node: VueNode) => {
return node;
// let styleNode: VueNode;
// if (!styleContext.ssrInline || isMergedClientSide || !styleContext.defaultCache) {
// styleNode = <Empty />;
// } else {
// styleNode = (
// <style
// {...{
// [ATTR_TOKEN]: cacheStyle.value[1],
// [ATTR_MARK]: cacheStyle.value[2],
// }}
// innerHTML={cacheStyle.value[0]}
// />
// );
// }
// return (
// <>
// {styleNode}
// {node}
// </>
// );
};
}
// ============================================================================
// == SSR ==
// ============================================================================
export function extractStyle(cache: Cache, plain = false) {
const matchPrefix = `style%`;
// prefix with `style` is used for `useStyleRegister` to cache style context
const styleKeys = Array.from(cache.cache.keys()).filter(key => key.startsWith(matchPrefix));
// Common effect styles like animation
const effectStyles: Record<string, boolean> = {};
// Mapping of cachePath to style hash
const cachePathMap: Record<string, string> = {};
let styleText = '';
function toStyleStr(
style: string,
tokenKey?: string,
styleId?: string,
customizeAttrs: Record<string, string> = {},
) {
const attrs: Record<string, string | undefined> = {
...customizeAttrs,
[ATTR_TOKEN]: tokenKey,
[ATTR_MARK]: styleId,
};
const attrStr = Object.keys(attrs)
.map(attr => {
const val = attrs[attr];
return val ? `${attr}="${val}"` : null;
})
.filter(v => v)
.join(' ');
return plain ? style : `<style ${attrStr}>${style}</style>`;
}
// ====================== Fill Style ======================
type OrderStyle = [order: number, style: string];
const orderStyles: OrderStyle[] = styleKeys
.map(key => {
const cachePath = key.slice(matchPrefix.length).replace(/%/g, '|');
const [styleStr, tokenKey, styleId, effectStyle, clientOnly, order]: [
string,
string,
string,
Record<string, string>,
boolean,
number,
] = cache.cache.get(key)![1];
// Skip client only style
if (clientOnly) {
return null! as OrderStyle;
}
// ====================== Style ======================
// Used for vc-util
const sharedAttrs = {
'data-vc-order': 'prependQueue',
'data-vc-priority': `${order}`,
};
let keyStyleText = toStyleStr(styleStr, tokenKey, styleId, sharedAttrs);
// Save cache path with hash mapping
cachePathMap[cachePath] = styleId;
// =============== Create effect style ===============
if (effectStyle) {
Object.keys(effectStyle).forEach(effectKey => {
// Effect style can be reused
if (!effectStyles[effectKey]) {
effectStyles[effectKey] = true;
keyStyleText += toStyleStr(
normalizeStyle(effectStyle[effectKey]),
tokenKey,
`_effect-${effectKey}`,
sharedAttrs,
);
}
});
}
const ret: OrderStyle = [order, keyStyleText];
return ret;
})
.filter(o => o);
orderStyles
.sort((o1, o2) => o1[0] - o2[0])
.forEach(([, style]) => {
styleText += style;
});
// ==================== Fill Cache Path ====================
styleText += toStyleStr(
`.${ATTR_CACHE_MAP}{content:"${serializeCacheMap(cachePathMap)}";}`,
undefined,
undefined,
{
[ATTR_CACHE_MAP]: ATTR_CACHE_MAP,
},
);
return styleText;
}

View File

@ -1,77 +0,0 @@
import useCacheToken from './hooks/useCacheToken';
import type { CSSInterpolation, CSSObject } from './hooks/useStyleRegister';
import useStyleRegister, { extractStyle } from './hooks/useStyleRegister';
import Keyframes from './Keyframes';
import type { Linter } from './linters';
import { legacyNotSelectorLinter, logicalPropertiesLinter, parentSelectorLinter } from './linters';
import type { StyleContextProps, StyleProviderProps } from './StyleContext';
import { createCache, useStyleInject, useStyleProvider, StyleProvider } from './StyleContext';
import type { DerivativeFunc, TokenType } from './theme';
import { createTheme, Theme } from './theme';
import type { Transformer } from './transformers/interface';
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
import px2remTransformer from './transformers/px2rem';
import { supportLogicProps, supportWhere } from './util';
const cssinjs = {
Theme,
createTheme,
useStyleRegister,
useCacheToken,
createCache,
useStyleInject,
useStyleProvider,
Keyframes,
extractStyle,
// Transformer
legacyLogicalPropertiesTransformer,
px2remTransformer,
// Linters
logicalPropertiesLinter,
legacyNotSelectorLinter,
parentSelectorLinter,
// cssinjs
StyleProvider,
};
export {
Theme,
createTheme,
useStyleRegister,
useCacheToken,
createCache,
useStyleInject,
useStyleProvider,
Keyframes,
extractStyle,
// Transformer
legacyLogicalPropertiesTransformer,
px2remTransformer,
// Linters
logicalPropertiesLinter,
legacyNotSelectorLinter,
parentSelectorLinter,
// cssinjs
StyleProvider,
};
export type {
TokenType,
CSSObject,
CSSInterpolation,
DerivativeFunc,
Transformer,
Linter,
StyleContextProps,
StyleProviderProps,
};
export const _experimental = {
supportModernCSS: () => supportWhere() && supportLogicProps(),
};
export default cssinjs;

View File

@ -1,25 +0,0 @@
import type { Linter } from './interface';
import { lintWarning } from './utils';
const linter: Linter = (key, value, info) => {
if (key === 'content') {
// From emotion: https://github.com/emotion-js/emotion/blob/main/packages/serialize/src/index.js#L63
const contentValuePattern =
/(attr|counters?|url|(((repeating-)?(linear|radial))|conic)-gradient)\(|(no-)?(open|close)-quote/;
const contentValues = ['normal', 'none', 'initial', 'inherit', 'unset'];
if (
typeof value !== 'string' ||
(contentValues.indexOf(value) === -1 &&
!contentValuePattern.test(value) &&
(value.charAt(0) !== value.charAt(value.length - 1) ||
(value.charAt(0) !== '"' && value.charAt(0) !== "'")))
) {
lintWarning(
`You seem to be using a value for 'content' without quotes, try replacing it with \`content: '"${value}"'\`.`,
info,
);
}
}
};
export default linter;

View File

@ -1,15 +0,0 @@
import type { Linter } from './interface';
import { lintWarning } from './utils';
const linter: Linter = (key, value, info) => {
if (key === 'animation') {
if (info.hashId && value !== 'none') {
lintWarning(
`You seem to be using hashed animation '${value}', in which case 'animationName' with Keyframe as value is recommended.`,
info,
);
}
}
};
export default linter;

View File

@ -1,6 +0,0 @@
export { default as contentQuotesLinter } from './contentQuotesLinter';
export { default as hashedAnimationLinter } from './hashedAnimationLinter';
export type { Linter } from './interface';
export { default as legacyNotSelectorLinter } from './legacyNotSelectorLinter';
export { default as logicalPropertiesLinter } from './logicalPropertiesLinter';
export { default as parentSelectorLinter } from './parentSelectorLinter';

View File

@ -1,9 +0,0 @@
export interface LinterInfo {
path?: string;
hashId?: string;
parentSelectors: string[];
}
export interface Linter {
(key: string, value: string | number, info: LinterInfo): void;
}

View File

@ -1,33 +0,0 @@
import type { Linter, LinterInfo } from './interface';
import { lintWarning } from './utils';
function isConcatSelector(selector: string) {
const notContent = selector.match(/:not\(([^)]*)\)/)?.[1] || '';
// split selector. e.g.
// `h1#a.b` => ['h1', #a', '.b']
const splitCells = notContent.split(/(\[[^[]*])|(?=[.#])/).filter(str => str);
return splitCells.length > 1;
}
function parsePath(info: LinterInfo) {
return info.parentSelectors.reduce((prev, cur) => {
if (!prev) {
return cur;
}
return cur.includes('&') ? cur.replace(/&/g, prev) : `${prev} ${cur}`;
}, '');
}
const linter: Linter = (_key, _value, info) => {
const parentSelectorPath = parsePath(info);
const notList = parentSelectorPath.match(/:not\([^)]*\)/g) || [];
if (notList.length > 0 && notList.some(isConcatSelector)) {
lintWarning(`Concat ':not' selector not support in legacy browsers.`, info);
}
};
export default linter;

View File

@ -1,88 +0,0 @@
import type { Linter } from './interface';
import { lintWarning } from './utils';
const linter: Linter = (key, value, info) => {
switch (key) {
case 'marginLeft':
case 'marginRight':
case 'paddingLeft':
case 'paddingRight':
case 'left':
case 'right':
case 'borderLeft':
case 'borderLeftWidth':
case 'borderLeftStyle':
case 'borderLeftColor':
case 'borderRight':
case 'borderRightWidth':
case 'borderRightStyle':
case 'borderRightColor':
case 'borderTopLeftRadius':
case 'borderTopRightRadius':
case 'borderBottomLeftRadius':
case 'borderBottomRightRadius':
lintWarning(
`You seem to be using non-logical property '${key}' which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
info,
);
return;
case 'margin':
case 'padding':
case 'borderWidth':
case 'borderStyle':
// case 'borderColor':
if (typeof value === 'string') {
const valueArr = value.split(' ').map(item => item.trim());
if (valueArr.length === 4 && valueArr[1] !== valueArr[3]) {
lintWarning(
`You seem to be using '${key}' property with different left ${key} and right ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
info,
);
}
}
return;
case 'clear':
case 'textAlign':
if (value === 'left' || value === 'right') {
lintWarning(
`You seem to be using non-logical value '${value}' of ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
info,
);
}
return;
case 'borderRadius':
if (typeof value === 'string') {
const radiusGroups = value.split('/').map(item => item.trim());
const invalid = radiusGroups.reduce((result, group) => {
if (result) {
return result;
}
const radiusArr = group.split(' ').map(item => item.trim());
// borderRadius: '2px 4px'
if (radiusArr.length >= 2 && radiusArr[0] !== radiusArr[1]) {
return true;
}
// borderRadius: '4px 4px 2px'
if (radiusArr.length === 3 && radiusArr[1] !== radiusArr[2]) {
return true;
}
// borderRadius: '4px 4px 2px 4px'
if (radiusArr.length === 4 && radiusArr[2] !== radiusArr[3]) {
return true;
}
return result;
}, false);
if (invalid) {
lintWarning(
`You seem to be using non-logical value '${value}' of ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`,
info,
);
}
}
return;
default:
}
};
export default linter;

View File

@ -1,15 +0,0 @@
import type { Linter } from '..';
import { lintWarning } from './utils';
const linter: Linter = (_key, _value, info) => {
if (
info.parentSelectors.some(selector => {
const selectors = selector.split(',');
return selectors.some(item => item.split('&').length > 2);
})
) {
lintWarning('Should not use more than one `&` in a selector.', info);
}
};
export default linter;

View File

@ -1,13 +0,0 @@
import devWarning from '../../../vc-util/warning';
import type { LinterInfo } from './interface';
export function lintWarning(message: string, info: LinterInfo) {
const { path, parentSelectors } = info;
devWarning(
false,
`[Ant Design Vue CSS-in-JS] ${path ? `Error in '${path}': ` : ''}${message}${
parentSelectors.length ? ` Selector info: ${parentSelectors.join(' -> ')}` : ''
}`,
);
}

View File

@ -1,38 +0,0 @@
import warning from '../../warning';
import type { DerivativeFunc, TokenType } from './interface';
let uuid = 0;
/**
* Theme with algorithms to derive tokens from design tokens.
* Use `createTheme` first which will help to manage the theme instance cache.
*/
export default class Theme<DesignToken extends TokenType, DerivativeToken extends TokenType> {
private derivatives: DerivativeFunc<DesignToken, DerivativeToken>[];
public readonly id: number;
constructor(
derivatives:
| DerivativeFunc<DesignToken, DerivativeToken>
| DerivativeFunc<DesignToken, DerivativeToken>[],
) {
this.derivatives = Array.isArray(derivatives) ? derivatives : [derivatives];
this.id = uuid;
if (derivatives.length === 0) {
warning(
derivatives.length > 0,
'[Ant Design Vue CSS-in-JS] Theme should have at least one derivative function.',
);
}
uuid += 1;
}
getDerivativeToken(token: DesignToken): DerivativeToken {
return this.derivatives.reduce<DerivativeToken>(
(result, derivative) => derivative(token, result),
undefined as any,
);
}
}

View File

@ -1,135 +0,0 @@
import type Theme from './Theme';
import type { DerivativeFunc } from './interface';
// ================================== Cache ==================================
type ThemeCacheMap = Map<
DerivativeFunc<any, any>,
{
map?: ThemeCacheMap;
value?: [Theme<any, any>, number];
}
>;
type DerivativeOptions = DerivativeFunc<any, any>[];
export function sameDerivativeOption(left: DerivativeOptions, right: DerivativeOptions) {
if (left.length !== right.length) {
return false;
}
for (let i = 0; i < left.length; i++) {
if (left[i] !== right[i]) {
return false;
}
}
return true;
}
export default class ThemeCache {
public static MAX_CACHE_SIZE = 20;
public static MAX_CACHE_OFFSET = 5;
private readonly cache: ThemeCacheMap;
private keys: DerivativeOptions[];
private cacheCallTimes: number;
constructor() {
this.cache = new Map();
this.keys = [];
this.cacheCallTimes = 0;
}
public size(): number {
return this.keys.length;
}
private internalGet(
derivativeOption: DerivativeOptions,
updateCallTimes = false,
): [Theme<any, any>, number] | undefined {
let cache: ReturnType<ThemeCacheMap['get']> = { map: this.cache };
derivativeOption.forEach(derivative => {
if (!cache) {
cache = undefined;
} else {
cache = cache?.map?.get(derivative);
}
});
if (cache?.value && updateCallTimes) {
cache.value[1] = this.cacheCallTimes++;
}
return cache?.value;
}
public get(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
return this.internalGet(derivativeOption, true)?.[0];
}
public has(derivativeOption: DerivativeOptions): boolean {
return !!this.internalGet(derivativeOption);
}
public set(derivativeOption: DerivativeOptions, value: Theme<any, any>): void {
// New cache
if (!this.has(derivativeOption)) {
if (this.size() + 1 > ThemeCache.MAX_CACHE_SIZE + ThemeCache.MAX_CACHE_OFFSET) {
const [targetKey] = this.keys.reduce<[DerivativeOptions, number]>(
(result, key) => {
const [, callTimes] = result;
if (this.internalGet(key)![1] < callTimes) {
return [key, this.internalGet(key)![1]];
}
return result;
},
[this.keys[0], this.cacheCallTimes],
);
this.delete(targetKey);
}
this.keys.push(derivativeOption);
}
let cache = this.cache;
derivativeOption.forEach((derivative, index) => {
if (index === derivativeOption.length - 1) {
cache.set(derivative, { value: [value, this.cacheCallTimes++] });
} else {
const cacheValue = cache.get(derivative);
if (!cacheValue) {
cache.set(derivative, { map: new Map() });
} else if (!cacheValue.map) {
cacheValue.map = new Map();
}
cache = cache.get(derivative)!.map!;
}
});
}
private deleteByPath(
currentCache: ThemeCacheMap,
derivatives: DerivativeFunc<any, any>[],
): Theme<any, any> | undefined {
const cache = currentCache.get(derivatives[0])!;
if (derivatives.length === 1) {
if (!cache.map) {
currentCache.delete(derivatives[0]);
} else {
currentCache.set(derivatives[0], { map: cache.map });
}
return cache.value?.[0];
}
const result = this.deleteByPath(cache.map!, derivatives.slice(1));
if ((!cache.map || cache.map.size === 0) && !cache.value) {
currentCache.delete(derivatives[0]);
}
return result;
}
public delete(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
// If cache exists
if (this.has(derivativeOption)) {
this.keys = this.keys.filter(item => !sameDerivativeOption(item, derivativeOption));
return this.deleteByPath(this.cache, derivativeOption);
}
return undefined;
}
}

View File

@ -1,26 +0,0 @@
import ThemeCache from './ThemeCache';
import Theme from './Theme';
import type { DerivativeFunc, TokenType } from './interface';
const cacheThemes = new ThemeCache();
/**
* Same as new Theme, but will always return same one if `derivative` not changed.
*/
export default function createTheme<
DesignToken extends TokenType,
DerivativeToken extends TokenType,
>(
derivatives:
| DerivativeFunc<DesignToken, DerivativeToken>[]
| DerivativeFunc<DesignToken, DerivativeToken>,
) {
const derivativeArr = Array.isArray(derivatives) ? derivatives : [derivatives];
// Create new theme if not exist
if (!cacheThemes.has(derivativeArr)) {
cacheThemes.set(derivativeArr, new Theme(derivativeArr));
}
// Get theme from cache and return
return cacheThemes.get(derivativeArr)!;
}

View File

@ -1,4 +0,0 @@
export { default as createTheme } from './createTheme';
export { default as Theme } from './Theme';
export { default as ThemeCache } from './ThemeCache';
export type { TokenType, DerivativeFunc } from './interface';

View File

@ -1,5 +0,0 @@
export type TokenType = object;
export type DerivativeFunc<DesignToken extends TokenType, DerivativeToken extends TokenType> = (
designToken: DesignToken,
derivativeToken?: DerivativeToken,
) => DerivativeToken;

View File

@ -1,5 +0,0 @@
import type { CSSObject } from '..';
export interface Transformer {
visit?: (cssObj: CSSObject) => CSSObject;
}

View File

@ -1,162 +0,0 @@
import type { CSSObject } from '..';
import type { Transformer } from './interface';
function splitValues(value: string | number) {
if (typeof value === 'number') {
return [value];
}
const splitStyle = String(value).split(/\s+/);
// Combine styles split in brackets, like `calc(1px + 2px)`
let temp = '';
let brackets = 0;
return splitStyle.reduce<string[]>((list, item) => {
if (item.includes('(')) {
temp += item;
brackets += item.split('(').length - 1;
} else if (item.includes(')')) {
temp += ` ${item}`;
brackets -= item.split(')').length - 1;
if (brackets === 0) {
list.push(temp);
temp = '';
}
} else if (brackets > 0) {
temp += ` ${item}`;
} else {
list.push(item);
}
return list;
}, []);
}
type MatchValue = string[] & {
notSplit?: boolean;
};
function noSplit(list: MatchValue): MatchValue {
list.notSplit = true;
return list;
}
const keyMap: Record<string, MatchValue> = {
// Inset
inset: ['top', 'right', 'bottom', 'left'],
insetBlock: ['top', 'bottom'],
insetBlockStart: ['top'],
insetBlockEnd: ['bottom'],
insetInline: ['left', 'right'],
insetInlineStart: ['left'],
insetInlineEnd: ['right'],
// Margin
marginBlock: ['marginTop', 'marginBottom'],
marginBlockStart: ['marginTop'],
marginBlockEnd: ['marginBottom'],
marginInline: ['marginLeft', 'marginRight'],
marginInlineStart: ['marginLeft'],
marginInlineEnd: ['marginRight'],
// Padding
paddingBlock: ['paddingTop', 'paddingBottom'],
paddingBlockStart: ['paddingTop'],
paddingBlockEnd: ['paddingBottom'],
paddingInline: ['paddingLeft', 'paddingRight'],
paddingInlineStart: ['paddingLeft'],
paddingInlineEnd: ['paddingRight'],
// Border
borderBlock: noSplit(['borderTop', 'borderBottom']),
borderBlockStart: noSplit(['borderTop']),
borderBlockEnd: noSplit(['borderBottom']),
borderInline: noSplit(['borderLeft', 'borderRight']),
borderInlineStart: noSplit(['borderLeft']),
borderInlineEnd: noSplit(['borderRight']),
// Border width
borderBlockWidth: ['borderTopWidth', 'borderBottomWidth'],
borderBlockStartWidth: ['borderTopWidth'],
borderBlockEndWidth: ['borderBottomWidth'],
borderInlineWidth: ['borderLeftWidth', 'borderRightWidth'],
borderInlineStartWidth: ['borderLeftWidth'],
borderInlineEndWidth: ['borderRightWidth'],
// Border style
borderBlockStyle: ['borderTopStyle', 'borderBottomStyle'],
borderBlockStartStyle: ['borderTopStyle'],
borderBlockEndStyle: ['borderBottomStyle'],
borderInlineStyle: ['borderLeftStyle', 'borderRightStyle'],
borderInlineStartStyle: ['borderLeftStyle'],
borderInlineEndStyle: ['borderRightStyle'],
// Border color
borderBlockColor: ['borderTopColor', 'borderBottomColor'],
borderBlockStartColor: ['borderTopColor'],
borderBlockEndColor: ['borderBottomColor'],
borderInlineColor: ['borderLeftColor', 'borderRightColor'],
borderInlineStartColor: ['borderLeftColor'],
borderInlineEndColor: ['borderRightColor'],
// Border radius
borderStartStartRadius: ['borderTopLeftRadius'],
borderStartEndRadius: ['borderTopRightRadius'],
borderEndStartRadius: ['borderBottomLeftRadius'],
borderEndEndRadius: ['borderBottomRightRadius'],
};
function skipCheck(value: string | number) {
return { _skip_check_: true, value };
}
/**
* Convert css logical properties to legacy properties.
* Such as: `margin-block-start` to `margin-top`.
* Transform list:
* - inset
* - margin
* - padding
* - border
*/
const transform: Transformer = {
visit: cssObj => {
const clone: CSSObject = {};
Object.keys(cssObj).forEach(key => {
const value = cssObj[key];
const matchValue = keyMap[key];
if (matchValue && (typeof value === 'number' || typeof value === 'string')) {
const values = splitValues(value);
if (matchValue.length && matchValue.notSplit) {
// not split means always give same value like border
matchValue.forEach(matchKey => {
clone[matchKey] = skipCheck(value);
});
} else if (matchValue.length === 1) {
// Handle like `marginBlockStart` => `marginTop`
clone[matchValue[0]] = skipCheck(value);
} else if (matchValue.length === 2) {
// Handle like `marginBlock` => `marginTop` & `marginBottom`
matchValue.forEach((matchKey, index) => {
clone[matchKey] = skipCheck(values[index] ?? values[0]);
});
} else if (matchValue.length === 4) {
// Handle like `inset` => `top` & `right` & `bottom` & `left`
matchValue.forEach((matchKey, index) => {
clone[matchKey] = skipCheck(values[index] ?? values[index - 2] ?? values[0]);
});
} else {
clone[key] = value;
}
} else {
clone[key] = value;
}
});
return clone;
},
};
export default transform;

View File

@ -1,76 +0,0 @@
/**
* respect https://github.com/cuth/postcss-pxtorem
*/
import unitless from '@emotion/unitless';
import type { CSSObject } from '..';
import type { Transformer } from './interface';
export interface Options {
/**
* The root font size.
* @default 16
*/
rootValue?: number;
/**
* The decimal numbers to allow the REM units to grow to.
* @default 5
*/
precision?: number;
/**
* Whether to allow px to be converted in media queries.
* @default false
*/
mediaQuery?: boolean;
}
const pxRegex = /url\([^)]+\)|var\([^)]+\)|(\d*\.?\d+)px/g;
function toFixed(number: number, precision: number) {
const multiplier = Math.pow(10, precision + 1),
wholeNumber = Math.floor(number * multiplier);
return (Math.round(wholeNumber / 10) * 10) / multiplier;
}
const transform = (options: Options = {}): Transformer => {
const { rootValue = 16, precision = 5, mediaQuery = false } = options;
const pxReplace = (m: string, $1: any) => {
if (!$1) return m;
const pixels = parseFloat($1);
// covenant: pixels <= 1, not transform to rem @zombieJ
if (pixels <= 1) return m;
const fixedVal = toFixed(pixels / rootValue, precision);
return `${fixedVal}rem`;
};
const visit = (cssObj: CSSObject): CSSObject => {
const clone: CSSObject = { ...cssObj };
Object.entries(cssObj).forEach(([key, value]) => {
if (typeof value === 'string' && value.includes('px')) {
const newValue = value.replace(pxRegex, pxReplace);
clone[key] = newValue;
}
// no unit
if (!unitless[key] && typeof value === 'number' && value !== 0) {
clone[key] = `${value}px`.replace(pxRegex, pxReplace);
}
// Media queries
const mergedKey = key.trim();
if (mergedKey.startsWith('@') && mergedKey.includes('px') && mediaQuery) {
const newKey = key.replace(pxRegex, pxReplace);
clone[newKey] = clone[key];
delete clone[key];
}
});
return clone;
};
return { visit };
};
export default transform;

View File

@ -1,118 +0,0 @@
import hash from '@emotion/hash';
import { removeCSS, updateCSS } from '../../vc-util/Dom/dynamicCSS';
import canUseDom from '../canUseDom';
import { Theme } from './theme';
// Create a cache here to avoid always loop generate
const flattenTokenCache = new WeakMap<any, string>();
export function flattenToken(token: any) {
let str = flattenTokenCache.get(token) || '';
if (!str) {
Object.keys(token).forEach(key => {
const value = token[key];
str += key;
if (value instanceof Theme) {
str += value.id;
} else if (value && typeof value === 'object') {
str += flattenToken(value);
} else {
str += value;
}
});
// Put in cache
flattenTokenCache.set(token, str);
}
return str;
}
/**
* Convert derivative token to key string
*/
export function token2key(token: any, salt: string): string {
return hash(`${salt}_${flattenToken(token)}`);
}
const randomSelectorKey = `random-${Date.now()}-${Math.random()}`.replace(/\./g, '');
// Magic `content` for detect selector support
const checkContent = '_bAmBoO_';
function supportSelector(
styleStr: string,
handleElement: (ele: HTMLElement) => void,
supportCheck?: (ele: HTMLElement) => boolean,
): boolean {
if (canUseDom()) {
updateCSS(styleStr, randomSelectorKey);
const ele = document.createElement('div');
ele.style.position = 'fixed';
ele.style.left = '0';
ele.style.top = '0';
handleElement?.(ele);
document.body.appendChild(ele);
if (process.env.NODE_ENV !== 'production') {
ele.innerHTML = 'Test';
ele.style.zIndex = '9999999';
}
const support = supportCheck
? supportCheck(ele)
: getComputedStyle(ele).content?.includes(checkContent);
ele.parentNode?.removeChild(ele);
removeCSS(randomSelectorKey);
return support;
}
return false;
}
let canLayer: boolean | undefined = undefined;
export function supportLayer(): boolean {
if (canLayer === undefined) {
canLayer = supportSelector(
`@layer ${randomSelectorKey} { .${randomSelectorKey} { content: "${checkContent}"!important; } }`,
ele => {
ele.className = randomSelectorKey;
},
);
}
return canLayer!;
}
let canWhere: boolean | undefined = undefined;
export function supportWhere(): boolean {
if (canWhere === undefined) {
canWhere = supportSelector(
`:where(.${randomSelectorKey}) { content: "${checkContent}"!important; }`,
ele => {
ele.className = randomSelectorKey;
},
);
}
return canWhere!;
}
let canLogic: boolean | undefined = undefined;
export function supportLogicProps(): boolean {
if (canLogic === undefined) {
canLogic = supportSelector(
`.${randomSelectorKey} { inset-block: 93px !important; }`,
ele => {
ele.className = randomSelectorKey;
},
ele => getComputedStyle(ele).bottom === '93px',
);
}
return canLogic!;
}

View File

@ -0,0 +1,24 @@
/**
* source by `dom-closest`
* https://github.com/necolas/dom-closest.git
*/
import matches from './dom-matches';
/**
* @param element {Element}
* @param selector {String}
* @param context {Element=}
* @return {Element}
*/
export default function (element, selector, context) {
context = context || document;
// guard against orphans
element = { parentNode: element };
while ((element = element.parentNode) && element !== context) {
if (matches(element, selector)) {
return element;
}
}
}

View File

@ -0,0 +1,47 @@
/**
* source by `dom-matches`
* https://github.com/necolas/dom-matches.git
*/
/**
* Determine if a DOM element matches a CSS selector
*
* @param {Element} elem
* @param {String} selector
* @return {Boolean}
* @api public
*/
export default function matches(elem, selector) {
// Vendor-specific implementations of `Element.prototype.matches()`.
const proto = window.Element.prototype;
const nativeMatches =
proto.matches ||
proto.mozMatchesSelector ||
proto.msMatchesSelector ||
proto.oMatchesSelector ||
proto.webkitMatchesSelector;
if (!elem || elem.nodeType !== 1) {
return false;
}
const parentElem = elem.parentNode;
// use native 'matches'
if (nativeMatches) {
return nativeMatches.call(elem, selector);
}
// native support for `matches` is missing and a fallback is required
const nodes = parentElem.querySelectorAll(selector);
const len = nodes.length;
for (let i = 0; i < len; i++) {
if (nodes[i] === elem) {
return true;
}
}
return false;
}

View File

@ -1,16 +0,0 @@
import { watchEffect, shallowRef } from 'vue';
import type { ComputedRef } from 'vue';
export declare type ComputedGetter<T> = (...args: any[]) => T;
export default function eagerComputed<T>(fn: ComputedGetter<T>) {
const result = shallowRef<T>();
watchEffect(
() => {
result.value = fn();
},
{
flush: 'sync', // needed so updates are immediate.
},
);
return result as any as ComputedRef<T>;
}

View File

@ -1,21 +0,0 @@
type RecordType = Record<string, any>;
function extendsObject<T extends RecordType>(...list: T[]) {
const result: RecordType = { ...list[0] };
for (let i = 1; i < list.length; i++) {
const obj = list[i];
if (obj) {
Object.keys(obj).forEach(key => {
const val = obj[key];
if (val !== undefined) {
result[key] = val;
}
});
}
}
return result;
}
export default extendsObject;

View File

@ -1,13 +0,0 @@
import type { SizeType } from '../config-provider/SizeContext';
export function isPresetSize(size?: SizeType | string | number): size is SizeType {
return ['small', 'middle', 'large'].includes(size as string);
}
export function isValidGapNumber(size?: SizeType | string | number): size is number {
if (!size) {
// The case of size = 0 is deliberately excluded here, because the default value of the gap attribute in CSS is 0, so if the user passes 0 in, we can directly ignore it.
return false;
}
return typeof size === 'number' && !Number.isNaN(size);
}

View File

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

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