Compare commits

..

No commits in common. "main" and "4.0.2" have entirely different histories.
main ... 4.0.2

308 changed files with 2890 additions and 206592 deletions

View File

@ -27,7 +27,6 @@ module.exports = {
// ], // ],
plugins: ['markdown', 'jest', '@typescript-eslint', 'import'], plugins: ['markdown', 'jest', '@typescript-eslint', 'import'],
globals: { globals: {
h: true,
defineProps: 'readonly', defineProps: 'readonly',
}, },
overrides: [ overrides: [
@ -109,4 +108,7 @@ module.exports = {
], ],
'vue/multi-word-component-names': 'off', '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: contact_links:
- name: Create new issue - name: Create new issue
url: https://vuecomponent.github.io/issue-helper/ url: https://vuecomponent.github.io/issue-helper/
@ -13,5 +13,5 @@ contact_links:
url: https://www.paypal.me/tangjinzhou url: https://www.paypal.me/tangjinzhou
about: Love Ant Design Vue? Please consider supporting us via Paypal. about: Love Ant Design Vue? Please consider supporting us via Paypal.
- name: 支付宝/微信 赞助 - name: 支付宝/微信 赞助
url: https://aliyuncdn.antdv.com/alipay-and-wechat.png url: https://qn.antdv.com/alipay-and-wechat.png
about: Ant Design Vue 的健康持续发展需要您的支持,🙏 about: Ant Design Vue 的健康持续发展需要您的支持,🙏

View File

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

View File

@ -4,16 +4,8 @@ on:
issues: issues:
types: [opened] types: [opened]
permissions:
contents: read
jobs: jobs:
issue-open-check: check-issue:
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
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions-cool/check-user-permission@v1.0.0 - uses: actions-cool/check-user-permission@v1.0.0
@ -23,7 +15,7 @@ jobs:
- name: check invalid - name: check invalid
if: (contains(github.event.issue.body, 'issue-helper') == false) && (steps.checkUser.outputs.result == 'false') 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: with:
actions: 'create-comment,add-labels,close-issue' actions: 'create-comment,add-labels,close-issue'
issue-number: ${{ github.event.issue.number }} issue-number: ${{ github.event.issue.number }}

View File

@ -1,25 +1,18 @@
name: Issue Labeled name: Issue Reply
on: on:
issues: issues:
types: [labeled] types: [labeled]
permissions:
contents: read
jobs: jobs:
issue-labeled: issue-reply:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Need Reproduce - name: Need Reproduce
if: github.event.label.name == '🤔 Need Reproduce' if: github.event.label.name == '🤔 Need Reproduce'
uses: actions-cool/issues-helper@v3 uses: actions-cool/issues-helper@v1.2
with: with:
actions: 'create-comment' actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }} issue-number: ${{ github.event.issue.number }}
body: | 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. 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 - name: help wanted
if: github.event.label.name == 'help wanted' if: github.event.label.name == 'help wanted'
uses: actions-cool/issues-helper@v3 uses: actions-cool/issues-helper@v1.2
with: with:
actions: 'create-comment' actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }} issue-number: ${{ github.event.issue.number }}
body: | 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! 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 - name: Usage
if: github.event.label.name == 'Usage' if: github.event.label.name == 'Usage'
uses: actions-cool/issues-helper@v3 uses: actions-cool/issues-helper@v1.2
with: with:
actions: 'create-comment, close-issue' actions: 'create-comment, close-issue'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }} issue-number: ${{ github.event.issue.number }}
body: | 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). 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/) 中提问。 你好 @${{ 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/) 中提问。

1
.npmrc
View File

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

View File

@ -10,123 +10,6 @@
--- ---
## 4.2.6
- 🐞 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)
## 4.2.5
- 🐞 Fix Empty component memory leak problem
- 🐞 Fix Image width & height property not working problem
## 4.2.4
- 🐞 Fix Wave memory leak problem
## 4.2.3
- 🌟 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)
## 4.2.2
- 🐞 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)
## 4.2.1
- 🐞 fix Input clear action error [#7523](https://github.com/vueComponent/ant-design-vue/issues/7523)
## 4.2.0
- 🌟 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)
## 4.1.2
- 🐞 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)
## 4.1.1
- 🌟 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)
## 4.1.0
- 🐞 support vue 3.4 [#7239](https://github.com/vueComponent/ant-design-vue/issues/7239)
## 4.0.8
- 🐞 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)
## 4.0.7
- 🌟 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)
## 4.0.6
- 🐞 Fix the Dropdown onVisibleChange failure issue introduced in 4.0.4 [#7031](https://github.com/vueComponent/ant-design-vue/issues/7031)
## 4.0.5
- 🐞 Fix cssinjs performance issue [#7023](https://github.com/vueComponent/ant-design-vue/issues/7023)
## 4.0.4
- 🌟 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)
## 4.0.3
- 🐞 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)
## 4.0.2 ## 4.0.2
- 🐞 Fix useMessage causing body to be removed [#6880](https://github.com/vueComponent/ant-design-vue/issues/6880) - 🐞 Fix useMessage causing body to be removed [#6880](https://github.com/vueComponent/ant-design-vue/issues/6880)

View File

@ -10,123 +10,6 @@
--- ---
## 4.2.6
- 🐞 修复 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)
## 4.2.5
- 🐞 修复 Empty 组件内存泄漏问题
- 🐞 修复 Image width & height 属性不生效问题
## 4.2.4
- 🐞 修复 Wave 内存泄漏问题
## 4.2.3
- 🌟 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)
## 4.2.2
- 🐞 修复 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)
## 4.2.1
- 🐞 修复 Input 清空操作才报错问题 [#7523](https://github.com/vueComponent/ant-design-vue/issues/7523)
## 4.2.0
- 🌟 优化 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)
## 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)
## 4.1.1
- 🌟 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)
## 4.1.0
- 🐞 适配 vue 3.4 [#7239](https://github.com/vueComponent/ant-design-vue/issues/7239)
## 4.0.8
- 🐞 修复在 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)
## 4.0.7
- 🌟 新增 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)
## 4.0.6
- 🐞 修复 4.0.4 引入的 Dropdown onVisibleChange 失效问题 [#7031](https://github.com/vueComponent/ant-design-vue/issues/7031)
## 4.0.5
- 🐞 修复 cssinjs 性能问题 [#7023](https://github.com/vueComponent/ant-design-vue/issues/7023)
## 4.0.4
- 🌟 新增 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)
## 4.0.3
- 🐞 修复 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)
## 4.0.2 ## 4.0.2
- 🐞 修复 useMessage 导致 body 被移除问题 [#6880](https://github.com/vueComponent/ant-design-vue/issues/6880) - 🐞 修复 useMessage 导致 body 被移除问题 [#6880](https://github.com/vueComponent/ant-design-vue/issues/6880)

View File

@ -1,6 +1,6 @@
<p align="center"> <p align="center">
<a href="https://www.antdv.com/"> <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> </a>
</p> </p>
@ -10,7 +10,7 @@
<div align="center"> <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) ![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)
@ -73,7 +73,6 @@ $ yarn add ant-design-vue
| [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | 在 DOM 模板中,您可以使用 ant-design-vue 组件的自定义事件camelCase | | [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 的组件库 | | [@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-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,24 +87,23 @@ ant-design-vue 是 MIT 协议的开源项目。为了项目能够更好的持续
- [Patreon](https://www.patreon.com/tangjinzhou) - [Patreon](https://www.patreon.com/tangjinzhou)
- [opencollective](https://opencollective.com/ant-design-vue) - [opencollective](https://opencollective.com/ant-design-vue)
- [paypal](https://www.paypal.me/tangjinzhou) - [paypal](https://www.paypal.me/tangjinzhou)
- [支付宝或微信](https://aliyuncdn.antdv.com/alipay-and-wechat.png) - [支付宝或微信](https://qn.antdv.com/alipay-and-wechat.png)
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
## 赞助商 ## 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="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>
## 支持者 ## 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> <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 ## 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> <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>

View File

@ -1,6 +1,6 @@
<p align="center"> <p align="center">
<a href="https://www.antdv.com/"> <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> </a>
</p> </p>
@ -73,7 +73,6 @@ If you are in a bad network environment, you can try other registries and tools
| [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) | | [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 | | [@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-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 ## Donation
@ -82,8 +81,7 @@ ant-design-vue is an MIT-licensed open source project. In order to achieve bette
- [Patreon](https://www.patreon.com/tangjinzhou) - [Patreon](https://www.patreon.com/tangjinzhou)
- [opencollective](https://opencollective.com/ant-design-vue) - [opencollective](https://opencollective.com/ant-design-vue)
- [paypal](https://www.paypal.me/tangjinzhou) - [paypal](https://www.paypal.me/tangjinzhou)
- [支付宝或微信](https://aliyuncdn.antdv.com/alipay-and-wechat.png) - [支付宝或微信](https://qn.antdv.com/alipay-and-wechat.png)
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
## Sponsors ## Sponsors

View File

@ -6,6 +6,7 @@ import { genWebTypes } from './web-types';
import { outputFileSync, readFileSync } from 'fs-extra'; import { outputFileSync, readFileSync } from 'fs-extra';
import type { Options, VueTag } from './type'; import type { Options, VueTag } from './type';
import { getComponentName, normalizePath, toKebabCase } from './utils'; import { getComponentName, normalizePath, toKebabCase } from './utils';
import { genVeturAttributes, genVeturTags } from './vetur';
import { flatMap } from 'lodash'; import { flatMap } from 'lodash';
async function readMarkdown(options: Options): Promise<Map<String, VueTag>> { async function readMarkdown(options: Options): Promise<Map<String, VueTag>> {
@ -21,13 +22,13 @@ async function readMarkdown(options: Options): Promise<Map<String, VueTag>> {
return formatter(mdParser(fileContent), componentName, kebabComponentName, options.tagPrefix); return formatter(mdParser(fileContent), componentName, kebabComponentName, options.tagPrefix);
}) })
.filter(item => item) as VueTag[][]; .filter(item => item) as VueTag[][];
const tags = new Map<String, VueTag>(); const tags: Map<String, VueTag> = new Map();
flatMap(data, item => item).forEach(mergedTag => mergeTag(tags, mergedTag)); flatMap(data, item => item).forEach(mergedTag => mergeTag(tags, mergedTag));
return tags; return tags;
} }
function readTypings(options: Options): Map<String, VueTag> { function readTypings(options: Options): Map<String, VueTag> {
const tags = new Map<String, VueTag>(); const tags: Map<String, VueTag> = new Map();
const fileContent = readFileSync(options.typingsPath, 'utf-8'); const fileContent = readFileSync(options.typingsPath, 'utf-8');
fileContent fileContent
.split('\n') .split('\n')
@ -61,7 +62,7 @@ function mergeTag(tags: Map<String, VueTag>, mergedTag: VueTag) {
function mergeTags(mergedTagsArr: Map<String, VueTag>[]): VueTag[] { function mergeTags(mergedTagsArr: Map<String, VueTag>[]): VueTag[] {
if (mergedTagsArr.length === 1) return [...mergedTagsArr[0].values()]; if (mergedTagsArr.length === 1) return [...mergedTagsArr[0].values()];
const tags = new Map<String, VueTag>(); const tags: Map<String, VueTag> = new Map();
if (mergedTagsArr.length === 0) return []; if (mergedTagsArr.length === 0) return [];
mergedTagsArr.forEach(mergedTags => { mergedTagsArr.forEach(mergedTags => {
mergedTags.forEach(mergedTag => mergeTag(tags, mergedTag)); mergedTags.forEach(mergedTag => mergeTag(tags, mergedTag));
@ -77,6 +78,13 @@ export async function parseAndWrite(options: Options): Promise<Number> {
const tagsFromTypings = await readTypings(options); const tagsFromTypings = await readTypings(options);
const tags = mergeTags([tagsFromMarkdown, tagsFromTypings]); const tags = mergeTags([tagsFromMarkdown, tagsFromTypings]);
const webTypes = genWebTypes(tags, options); 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)); outputFileSync(join(options.outputDir, 'web-types.json'), JSON.stringify(webTypes, null, 2));
return tags.length; return tags.length;
} }

View File

@ -21,7 +21,7 @@ export type Articals = Artical[];
function readLine(input: string) { function readLine(input: string) {
const end = input.indexOf('\n'); 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) { function splitTableLine(line: string) {
@ -47,7 +47,7 @@ function tableParse(input: string) {
}; };
while (start < end) { while (start < end) {
const target = input.substring(start); const target = input.substr(start);
const line = readLine(target); const line = readLine(target);
if (!/^\|/.test(target)) { if (!/^\|/.test(target)) {
@ -79,7 +79,7 @@ export function mdParser(input: string): Articals {
const end = input.length; const end = input.length;
while (start < end) { while (start < end) {
const target = input.substring(start); const target = input.substr(start);
let match; let match;
if ((match = TITLE_REG.exec(target))) { if ((match = TITLE_REG.exec(target))) {
@ -91,7 +91,7 @@ export function mdParser(input: string): Articals {
start += match.index + match[0].length; start += match.index + match[0].length;
} else if ((match = TABLE_REG.exec(target))) { } 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({ artical.push({
type: 'table', type: 'table',
table, table,

View File

@ -34,6 +34,25 @@ export type VueTag = {
description?: string; 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 = { export type Options = {
name: string; name: string;
path: PathLike; path: PathLike;

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,6 +1,7 @@
'use strict'; 'use strict';
const fs = require('fs'); const fs = require('fs');
const assign = require('object-assign');
const { getProjectPath } = require('./utils/projectHelper'); const { getProjectPath } = require('./utils/projectHelper');
module.exports = function () { module.exports = function () {
@ -8,7 +9,7 @@ module.exports = function () {
if (fs.existsSync(getProjectPath('tsconfig.json'))) { if (fs.existsSync(getProjectPath('tsconfig.json'))) {
my = require(getProjectPath('tsconfig.json')); my = require(getProjectPath('tsconfig.json'));
} }
return Object.assign( return assign(
{ {
noUnusedParameters: true, noUnusedParameters: true,
noUnusedLocals: true, noUnusedLocals: true,

View File

@ -22,7 +22,7 @@ const imageOptions = {
limit: 10000, limit: 10000,
}; };
function getWebpackConfig(modules, esm = false) { function getWebpackConfig(modules) {
const pkg = require(getProjectPath('package.json')); const pkg = require(getProjectPath('package.json'));
const babelConfig = require('./getBabelCommonConfig')(modules || false); const babelConfig = require('./getBabelCommonConfig')(modules || false);
@ -185,7 +185,7 @@ All rights reserved.
}; };
if (process.env.RUN_ENV === 'PRODUCTION') { if (process.env.RUN_ENV === 'PRODUCTION') {
let entry = ['./index']; const entry = ['./index'];
config.externals = [ config.externals = [
{ {
vue: { vue: {
@ -197,25 +197,9 @@ All rights reserved.
}, },
}, },
]; ];
if (esm) { config.output.library = distFileBaseName;
entry = ['./index.esm']; config.output.libraryTarget = 'umd';
config.experiments = { config.output.globalObject = 'this';
...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.optimization = { config.optimization = {
minimizer: [ minimizer: [
new TerserPlugin({ new TerserPlugin({
@ -229,7 +213,7 @@ All rights reserved.
// Development // Development
const uncompressedConfig = merge({}, config, { const uncompressedConfig = merge({}, config, {
entry: { entry: {
[entryName]: entry, [distFileBaseName]: entry,
}, },
mode: 'development', mode: 'development',
plugins: [ plugins: [
@ -242,7 +226,7 @@ All rights reserved.
// Production // Production
const prodConfig = merge({}, config, { const prodConfig = merge({}, config, {
entry: { entry: {
[`${entryName}.min`]: entry, [`${distFileBaseName}.min`]: entry,
}, },
mode: 'production', mode: 'production',
plugins: [ plugins: [

View File

@ -1,167 +1,51 @@
import type { PropType } from 'vue'; import { defineComponent, shallowRef, withDirectives } from 'vue';
import { computed, defineComponent, shallowRef, ref, watch } from 'vue'; import antInput from './antInputDirective';
import PropTypes from './vue-types'; 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({ const BaseInput = defineComponent({
compatConfig: { MODE: 3 }, compatConfig: { MODE: 3 },
inheritAttrs: false,
props: { props: {
disabled: PropTypes.looseBool, value: PropTypes.string.def(''),
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,
}, },
emits: [ emits: ['change', 'input'],
'change', setup(_p, { emit }) {
'input', const inputRef = shallowRef(null);
'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 },
);
const handleChange = (e: Event) => { const handleChange = (e: Event) => {
emit('change', e); const { composing } = e.target as any;
}; if ((e as any).isComposing || composing) {
const onCompositionstart = (e: CompositionEvent) => { emit('input', e);
isComposing.value = true; } else {
(e.target as any).composing = true; emit('input', e);
emit('compositionstart', e); emit('change', 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 blur = () => { return {
if (inputRef.value) { inputRef,
inputRef.value.blur(); focus: () => {
} if (inputRef.value) {
inputRef.value.focus();
}
},
blur: () => {
if (inputRef.value) {
inputRef.value.blur();
}
},
handleChange,
}; };
const handleKeyDown = (e: KeyboardEvent) => { },
emit('keydown', e); render() {
}; return withDirectives(
const handleKeyUp = (e: KeyboardEvent) => { (
emit('keyup', e); <input
}; {...this.$props}
const setSelectionRange = ( {...this.$attrs}
start: number, onInput={this.handleChange}
end: number, onChange={this.handleChange}
direction?: 'forward' | 'backward' | 'none', ref="inputRef"
) => {
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}
/> />
); ) 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

@ -1,13 +1,5 @@
import PropTypes from './vue-types'; import PropTypes from './vue-types';
import { import { defineComponent, nextTick, onBeforeMount, onUpdated, Teleport, watch } from 'vue';
defineComponent,
nextTick,
onBeforeMount,
onMounted,
onUpdated,
Teleport,
watch,
} from 'vue';
import { useInjectPortal } from '../vc-trigger/context'; import { useInjectPortal } from '../vc-trigger/context';
export default defineComponent({ export default defineComponent({
@ -23,24 +15,12 @@ export default defineComponent({
// getContainer // getContainer
let container: HTMLElement; let container: HTMLElement;
const { shouldRender } = useInjectPortal(); const { shouldRender } = useInjectPortal();
onBeforeMount(() => {
function setContainer() { isSSR = false;
if (shouldRender.value) { if (shouldRender.value) {
container = props.getContainer(); 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, () => { const stopWatch = watch(shouldRender, () => {
if (shouldRender.value && !container) { if (shouldRender.value && !container) {
container = props.getContainer(); container = props.getContainer();

View File

@ -7,6 +7,7 @@ import {
onMounted, onMounted,
onBeforeUnmount, onBeforeUnmount,
onUpdated, onUpdated,
getCurrentInstance,
nextTick, nextTick,
computed, computed,
} from 'vue'; } from 'vue';
@ -60,7 +61,6 @@ export default defineComponent({
const container = shallowRef<HTMLElement>(); const container = shallowRef<HTMLElement>();
const componentRef = shallowRef(); const componentRef = shallowRef();
const rafId = shallowRef<number>(); const rafId = shallowRef<number>();
const triggerUpdate = shallowRef(1);
const defaultContainer = canUseDom() && document.createElement('div'); const defaultContainer = canUseDom() && document.createElement('div');
const removeCurrentContainer = () => { const removeCurrentContainer = () => {
// Portal will remove from `parentNode`. // Portal will remove from `parentNode`.
@ -106,6 +106,8 @@ export default defineComponent({
attachToParent(); attachToParent();
}); });
const instance = getCurrentInstance();
useScrollLocker( useScrollLocker(
computed(() => { computed(() => {
return ( return (
@ -153,7 +155,7 @@ export default defineComponent({
nextTick(() => { nextTick(() => {
if (!attachToParent()) { if (!attachToParent()) {
rafId.value = raf(() => { rafId.value = raf(() => {
triggerUpdate.value += 1; instance.update();
}); });
} }
}); });
@ -175,7 +177,7 @@ export default defineComponent({
getOpenCount: () => openCount, getOpenCount: () => openCount,
getContainer, getContainer,
}; };
if (triggerUpdate.value && (forceRender || visible || componentRef.value)) { if (forceRender || visible || componentRef.value) {
portal = ( portal = (
<Portal <Portal
getContainer={getContainer} getContainer={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,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,42 @@
import type { Directive } from 'vue';
function onCompositionStart(e: any) {
e.target.composing = true;
}
function onCompositionEnd(e: any) {
// 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: HTMLElement,
event: string,
handler: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
) {
el.addEventListener(event, handler, options);
}
const antInput: Directive = {
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

@ -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) {
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) {
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,186 @@
// https://github.com/yiminghe/css-animation 1.5.0
import Event from './Event';
import classes from '../component-classes';
import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout';
import { inBrowser } from '../env';
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) {
if (inBrowser) return '';
// 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,20 +1,16 @@
export type KeyType = string | number; export type KeyType = string | number;
type ValueType = [number, any]; // [times, realValue] type ValueType = [number, any]; // [times, realValue]
const SPLIT = '%';
class Entity { class Entity {
instanceId: string;
constructor(instanceId: string) {
this.instanceId = instanceId;
}
/** @private Internal cache map. Do not access this directly */ /** @private Internal cache map. Do not access this directly */
cache = new Map<string, ValueType>(); cache = new Map<string, ValueType>();
get(keys: KeyType[] | string): ValueType | null { get(keys: KeyType[] | string): ValueType | null {
return this.cache.get(Array.isArray(keys) ? keys.join(SPLIT) : keys) || null; return this.cache.get(Array.isArray(keys) ? keys.join('%') : keys) || null;
} }
update(keys: KeyType[] | string, valueFn: (origin: ValueType | null) => ValueType | null) { update(keys: KeyType[] | string, valueFn: (origin: ValueType | null) => ValueType | null) {
const path = Array.isArray(keys) ? keys.join(SPLIT) : keys; const path = Array.isArray(keys) ? keys.join('%') : keys;
const prevValue = this.cache.get(path)!; const prevValue = this.cache.get(path)!;
const nextValue = valueFn(prevValue); const nextValue = valueFn(prevValue);

View File

@ -1,41 +1,29 @@
import type { ShallowRef, ExtractPropTypes, InjectionKey, Ref } from 'vue'; import type { ShallowRef, ExtractPropTypes, InjectionKey, Ref } from 'vue';
import { import { provide, defineComponent, unref, inject, watch, shallowRef } from 'vue';
provide,
defineComponent,
unref,
inject,
watch,
shallowRef,
getCurrentInstance,
} from 'vue';
import CacheEntity from './Cache'; import CacheEntity from './Cache';
import type { Linter } from './linters/interface'; import type { Linter } from './linters/interface';
import type { Transformer } from './transformers/interface'; import type { Transformer } from './transformers/interface';
import { arrayType, booleanType, objectType, someType, stringType, withInstall } from '../type'; import { arrayType, booleanType, objectType, someType, stringType, withInstall } from '../type';
import initDefaultProps from '../props-util/initDefaultProps';
export const ATTR_TOKEN = 'data-token-hash'; export const ATTR_TOKEN = 'data-token-hash';
export const ATTR_MARK = 'data-css-hash'; export const ATTR_MARK = 'data-css-hash';
export const ATTR_CACHE_PATH = 'data-cache-path'; export const ATTR_DEV_CACHE_PATH = 'data-dev-cache-path';
// Mark css-in-js instance in style element // Mark css-in-js instance in style element
export const CSS_IN_JS_INSTANCE = '__cssinjs_instance__'; export const CSS_IN_JS_INSTANCE = '__cssinjs_instance__';
export const CSS_IN_JS_INSTANCE_ID = Math.random().toString(12).slice(2);
export function createCache() { 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) { if (typeof document !== 'undefined' && document.head && document.body) {
const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || []; const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || [];
const { firstChild } = document.head; const { firstChild } = document.head;
Array.from(styles).forEach(style => { Array.from(styles).forEach(style => {
(style as any)[CSS_IN_JS_INSTANCE] = (style as any)[CSS_IN_JS_INSTANCE] || cssinjsInstanceId; (style as any)[CSS_IN_JS_INSTANCE] =
(style as any)[CSS_IN_JS_INSTANCE] || CSS_IN_JS_INSTANCE_ID;
// Not force move if no head // Not force move if no head
// Not force move if no head document.head.insertBefore(style, firstChild);
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
document.head.insertBefore(style, firstChild);
}
}); });
// Deduplicate of moved styles // Deduplicate of moved styles
@ -43,7 +31,7 @@ export function createCache() {
Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => { Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => {
const hash = style.getAttribute(ATTR_MARK)!; const hash = style.getAttribute(ATTR_MARK)!;
if (styleHash[hash]) { if (styleHash[hash]) {
if ((style as any)[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) { if ((style as any)[CSS_IN_JS_INSTANCE] === CSS_IN_JS_INSTANCE_ID) {
style.parentNode?.removeChild(style); style.parentNode?.removeChild(style);
} }
} else { } else {
@ -52,7 +40,7 @@ export function createCache() {
}); });
} }
return new CacheEntity(cssinjsInstanceId); return new CacheEntity();
} }
export type HashPriority = 'low' | 'high'; export type HashPriority = 'low' | 'high';
@ -88,43 +76,17 @@ const StyleContextKey: InjectionKey<ShallowRef<Partial<StyleContextProps>>> =
Symbol('StyleContextKey'); Symbol('StyleContextKey');
export type UseStyleProviderProps = Partial<StyleContextProps> | Ref<Partial<StyleContextProps>>; 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 = { const defaultStyleContext: StyleContextProps = {
cache: createCache(), cache: createCache(),
defaultCache: true, defaultCache: true,
hashPriority: 'low', hashPriority: 'low',
}; };
// fix: https://github.com/vueComponent/ant-design-vue/issues/6912
export const useStyleInject = () => { export const useStyleInject = () => {
const cache = getCache(); return inject(StyleContextKey, shallowRef({ ...defaultStyleContext }));
return inject(StyleContextKey, shallowRef({ ...defaultStyleContext, cache }));
}; };
export const useStyleProvider = (props: UseStyleProviderProps) => { export const useStyleProvider = (props: UseStyleProviderProps) => {
const parentContext = useStyleInject(); const parentContext = useStyleInject();
const context = shallowRef<Partial<StyleContextProps>>({ const context = shallowRef<Partial<StyleContextProps>>({ ...defaultStyleContext });
...defaultStyleContext,
cache: createCache(),
});
watch( watch(
[() => unref(props), parentContext], [() => unref(props), parentContext],
() => { () => {
@ -180,7 +142,7 @@ export const StyleProvider = withInstall(
defineComponent({ defineComponent({
name: 'AStyleProvider', name: 'AStyleProvider',
inheritAttrs: false, inheritAttrs: false,
props: styleProviderProps(), props: initDefaultProps(styleProviderProps(), defaultStyleContext),
setup(props, { slots }) { setup(props, { slots }) {
useStyleProvider(props); useStyleProvider(props);
return () => slots.default?.(); return () => slots.default?.();

View File

@ -1,5 +1,5 @@
import hash from '@emotion/hash'; import hash from '@emotion/hash';
import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext'; import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, CSS_IN_JS_INSTANCE_ID } from '../StyleContext';
import type Theme from '../theme/Theme'; import type Theme from '../theme/Theme';
import useGlobalCache from './useGlobalCache'; import useGlobalCache from './useGlobalCache';
import { flattenToken, token2key } from '../util'; import { flattenToken, token2key } from '../util';
@ -8,15 +8,11 @@ import { ref, computed } from 'vue';
const EMPTY_OVERRIDE = {}; 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. // Generate different prefix to make user selector break in production env.
// This helps developer not to do style override directly on the hash id. // This helps developer not to do style override directly on the hash id.
const hashPrefix = !isProduction && !isPrerender ? 'css-dev-only-do-not-override' : 'css'; const hashPrefix = process.env.NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css';
export interface Option<DerivativeToken, DesignToken> { export interface Option<DerivativeToken> {
/** /**
* Generate token with salt. * Generate token with salt.
* This is used to generate different hashId even same derivative token for different version. * This is used to generate different hashId even same derivative token for different version.
@ -34,18 +30,6 @@ export interface Option<DerivativeToken, DesignToken> {
* It's ok to useMemo outside but this has better cache strategy. * It's ok to useMemo outside but this has better cache strategy.
*/ */
formatToken?: (mergedToken: any) => DerivativeToken; 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>(); const tokenKeys = new Map<string, number>();
@ -53,22 +37,20 @@ function recordCleanToken(tokenKey: string) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1); tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
} }
function removeStyleTags(key: string, instanceId: string) { function removeStyleTags(key: string) {
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
const styles = document.querySelectorAll(`style[${ATTR_TOKEN}="${key}"]`); const styles = document.querySelectorAll(`style[${ATTR_TOKEN}="${key}"]`);
styles.forEach(style => { styles.forEach(style => {
if ((style as any)[CSS_IN_JS_INSTANCE] === instanceId) { if ((style as any)[CSS_IN_JS_INSTANCE] === CSS_IN_JS_INSTANCE_ID) {
style.parentNode?.removeChild(style); style.parentNode?.removeChild(style);
} }
}); });
} }
} }
const TOKEN_THRESHOLD = 0;
// Remove will check current keys first // Remove will check current keys first
function cleanTokenStyle(tokenKey: string, instanceId: string) { function cleanTokenStyle(tokenKey: string) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1); tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
const tokenKeyList = Array.from(tokenKeys.keys()); const tokenKeyList = Array.from(tokenKeys.keys());
@ -78,36 +60,14 @@ function cleanTokenStyle(tokenKey: string, instanceId: string) {
return count <= 0; return count <= 0;
}); });
// Should keep tokens under threshold for not to insert style too often if (cleanableKeyList.length < tokenKeyList.length) {
if (tokenKeyList.length - cleanableKeyList.length > TOKEN_THRESHOLD) {
cleanableKeyList.forEach(key => { cleanableKeyList.forEach(key => {
removeStyleTags(key, instanceId); removeStyleTags(key);
tokenKeys.delete(key); 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 * Cache theme derivative token as global shared one
* @param theme Theme entity * @param theme Theme entity
@ -118,10 +78,8 @@ export const getComputedToken = <DerivativeToken = object, DesignToken = Derivat
export default function useCacheToken<DerivativeToken = object, DesignToken = DerivativeToken>( export default function useCacheToken<DerivativeToken = object, DesignToken = DerivativeToken>(
theme: Ref<Theme<any, any>>, theme: Ref<Theme<any, any>>,
tokens: Ref<Partial<DesignToken>[]>, tokens: Ref<Partial<DesignToken>[]>,
option: Ref<Option<DerivativeToken, DesignToken>> = ref({}), option: Ref<Option<DerivativeToken>> = ref({}),
) { ) {
const style = useStyleInject();
// Basic - We do basic cache here // Basic - We do basic cache here
const mergedToken = computed(() => Object.assign({}, ...tokens.value)); const mergedToken = computed(() => Object.assign({}, ...tokens.value));
const tokenStr = computed(() => flattenToken(mergedToken.value)); const tokenStr = computed(() => flattenToken(mergedToken.value));
@ -136,15 +94,19 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
overrideTokenStr.value, overrideTokenStr.value,
]), ]),
() => { () => {
const { const { salt = '', override = EMPTY_OVERRIDE, formatToken } = option.value;
salt = '', const derivativeToken = theme.value.getDerivativeToken(mergedToken.value);
override = EMPTY_OVERRIDE,
formatToken, // Merge with override
getComputedToken: compute, let mergedDerivativeToken = {
} = option.value; ...derivativeToken,
const mergedDerivativeToken = compute ...override,
? compute(mergedToken.value, override, theme.value) };
: getComputedToken(mergedToken.value, override, theme.value, formatToken);
// Format if needed
if (formatToken) {
mergedDerivativeToken = formatToken(mergedDerivativeToken);
}
// Optimize for `useStyleRegister` performance // Optimize for `useStyleRegister` performance
const tokenKey = token2key(mergedDerivativeToken, salt); const tokenKey = token2key(mergedDerivativeToken, salt);
@ -153,11 +115,12 @@ export default function useCacheToken<DerivativeToken = object, DesignToken = De
const hashId = `${hashPrefix}-${hash(tokenKey)}`; const hashId = `${hashPrefix}-${hash(tokenKey)}`;
mergedDerivativeToken._hashId = hashId; // Not used mergedDerivativeToken._hashId = hashId; // Not used
return [mergedDerivativeToken, hashId]; return [mergedDerivativeToken, hashId];
}, },
cache => { cache => {
// Remove token will remove all related style // Remove token will remove all related style
cleanTokenStyle(cache[0]._tokenKey, style.value?.cache.instanceId); cleanTokenStyle(cache[0]._tokenKey);
}, },
); );

View File

@ -16,8 +16,7 @@ if (
process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'production' &&
typeof module !== 'undefined' && typeof module !== 'undefined' &&
module && module &&
(module as any).hot && (module as any).hot
typeof window !== 'undefined'
) { ) {
const win = window as any; const win = window as any;
if (typeof win.webpackHotUpdate === 'function') { if (typeof win.webpackHotUpdate === 'function') {

View File

@ -3,33 +3,27 @@ import type * as CSS from 'csstype';
// @ts-ignore // @ts-ignore
import unitless from '@emotion/unitless'; import unitless from '@emotion/unitless';
import { compile, serialize, stringify } from 'stylis'; import { compile, serialize, stringify } from 'stylis';
import type { Theme, Transformer } from '../..'; import type { Theme, Transformer } from '..';
import type Cache from '../../Cache'; import type Cache from '../Cache';
import type Keyframes from '../../Keyframes'; import type Keyframes from '../Keyframes';
import type { Linter } from '../../linters'; import type { Linter } from '../linters';
import { contentQuotesLinter, hashedAnimationLinter } from '../../linters'; import { contentQuotesLinter, hashedAnimationLinter } from '../linters';
import type { HashPriority } from '../../StyleContext'; import type { HashPriority } from '../StyleContext';
import { import {
useStyleInject, useStyleInject,
ATTR_CACHE_PATH, ATTR_DEV_CACHE_PATH,
ATTR_MARK, ATTR_MARK,
ATTR_TOKEN, ATTR_TOKEN,
CSS_IN_JS_INSTANCE, CSS_IN_JS_INSTANCE,
} from '../../StyleContext'; CSS_IN_JS_INSTANCE_ID,
import { supportLayer } from '../../util'; } from '../StyleContext';
import useGlobalCache from '../useGlobalCache'; import { supportLayer } from '../util';
import { removeCSS, updateCSS } from '../../../../vc-util/Dom/dynamicCSS'; import useGlobalCache from './useGlobalCache';
import canUseDom from '../../canUseDom';
import { removeCSS, updateCSS } from '../../../vc-util/Dom/dynamicCSS';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { computed } from 'vue'; import { computed } from 'vue';
import type { VueNode } from '../../../type'; 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 isClientSide = canUseDom();
@ -42,17 +36,17 @@ export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'anima
export type CSSPropertiesWithMultiValues = { export type CSSPropertiesWithMultiValues = {
[K in keyof CSSProperties]: [K in keyof CSSProperties]:
| CSSProperties[K] | CSSProperties[K]
| readonly Extract<CSSProperties[K], string>[] | Extract<CSSProperties[K], string>[]
| { | {
[SKIP_CHECK]?: boolean; [SKIP_CHECK]: boolean;
[MULTI_VALUE]?: boolean; [MULTI_VALUE]?: boolean;
value: CSSProperties[K] | CSSProperties[K][]; value: CSSProperties[K] | Extract<CSSProperties[K], string>[];
}; };
}; };
export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject }; export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject };
type ArrayCSSInterpolation = readonly CSSInterpolation[]; type ArrayCSSInterpolation = CSSInterpolation[];
export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject; export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject;
@ -66,7 +60,7 @@ export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSS
// == Parser == // == Parser ==
// ============================================================================ // ============================================================================
// Preprocessor style content to browser support one // Preprocessor style content to browser support one
export function normalizeStyle(styleStr: string): string { export function normalizeStyle(styleStr: string) {
const serialized = serialize(compile(styleStr), stringify); const serialized = serialize(compile(styleStr), stringify);
return serialized.replace(/\{%%%\:[^;];}/g, ';'); return serialized.replace(/\{%%%\:[^;];}/g, ';');
} }
@ -313,14 +307,6 @@ export default function useStyleRegister(
path: string[]; path: string[];
hashId?: string; hashId?: string;
layer?: 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, styleFn: () => CSSInterpolation,
) { ) {
@ -337,32 +323,14 @@ export default function useStyleRegister(
} }
// const [cacheStyle[0], cacheStyle[1], cacheStyle[2]] // const [cacheStyle[0], cacheStyle[1], cacheStyle[2]]
useGlobalCache< useGlobalCache(
[
styleStr: string,
tokenKey: string,
styleId: string,
effectStyle: Record<string, string>,
clientOnly: boolean | undefined,
order: number,
]
>(
'style', 'style',
fullPath, fullPath,
// Create cache if needed // 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 styleObj = styleFn();
const { hashPriority, container, transformers, linters, cache } = styleContext.value; const { hashPriority, container, transformers, linters } = styleContext.value;
const { path, hashId, layer } = info.value;
const [parsedStyle, effectStyle] = parseStyle(styleObj, { const [parsedStyle, effectStyle] = parseStyle(styleObj, {
hashId, hashId,
hashPriority, hashPriority,
@ -375,29 +343,20 @@ export default function useStyleRegister(
const styleId = uniqueHash(fullPath.value, styleStr); const styleId = uniqueHash(fullPath.value, styleStr);
if (isMergedClientSide) { if (isMergedClientSide) {
const mergedCSSConfig: Parameters<typeof updateCSS>[2] = { const style = updateCSS(styleStr, styleId, {
mark: ATTR_MARK, mark: ATTR_MARK,
prepend: 'queue', prepend: 'queue',
attachTo: container, attachTo: container,
priority: order, });
};
const nonceStr = typeof nonce === 'function' ? nonce() : nonce; (style as any)[CSS_IN_JS_INSTANCE] = CSS_IN_JS_INSTANCE_ID;
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 // Used for `useCacheToken` to remove on batch when token removed
style.setAttribute(ATTR_TOKEN, tokenKey.value); style.setAttribute(ATTR_TOKEN, tokenKey.value);
// Dev usage to find which cache path made this easily // Dev usage to find which cache path made this easily
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
style.setAttribute(ATTR_CACHE_PATH, fullPath.value.join('|')); style.setAttribute(ATTR_DEV_CACHE_PATH, fullPath.value.join('|'));
} }
// Inject client side effect style // Inject client side effect style
@ -415,7 +374,7 @@ export default function useStyleRegister(
}); });
} }
return [styleStr, tokenKey.value, styleId, effectStyle, clientOnly, order]; return [styleStr, tokenKey.value, styleId];
}, },
// Remove cache if no need // Remove cache if no need
([, , styleId], fromHMR) => { ([, , styleId], fromHMR) => {
@ -455,112 +414,20 @@ export default function useStyleRegister(
// == SSR == // == SSR ==
// ============================================================================ // ============================================================================
export function extractStyle(cache: Cache, plain = false) { export function extractStyle(cache: Cache, plain = false) {
const matchPrefix = `style%`;
// prefix with `style` is used for `useStyleRegister` to cache style context // prefix with `style` is used for `useStyleRegister` to cache style context
const styleKeys = Array.from(cache.cache.keys()).filter(key => key.startsWith(matchPrefix)); const styleKeys = Array.from(cache.cache.keys()).filter(key => key.startsWith('style%'));
// Common effect styles like animation // const tokenStyles: Record<string, string[]> = {};
const effectStyles: Record<string, boolean> = {};
// Mapping of cachePath to style hash
const cachePathMap: Record<string, string> = {};
let styleText = ''; let styleText = '';
function toStyleStr( styleKeys.forEach(key => {
style: string, const [styleStr, tokenKey, styleId]: [string, string, string] = cache.cache.get(key)![1];
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) styleText += plain
.map(attr => { ? styleStr
const val = attrs[attr]; : `<style ${ATTR_TOKEN}="${tokenKey}" ${ATTR_MARK}="${styleId}">${styleStr}</style>`;
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; return styleText;
} }

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

@ -11,8 +11,6 @@ import { createTheme, Theme } from './theme';
import type { Transformer } from './transformers/interface'; import type { Transformer } from './transformers/interface';
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties'; import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
import px2remTransformer from './transformers/px2rem'; import px2remTransformer from './transformers/px2rem';
import { supportLogicProps, supportWhere } from './util';
const cssinjs = { const cssinjs = {
Theme, Theme,
createTheme, createTheme,
@ -70,8 +68,4 @@ export type {
StyleProviderProps, StyleProviderProps,
}; };
export const _experimental = {
supportModernCSS: () => supportWhere() && supportLogicProps(),
};
export default cssinjs; export default cssinjs;

View File

@ -2,30 +2,17 @@ import hash from '@emotion/hash';
import { removeCSS, updateCSS } from '../../vc-util/Dom/dynamicCSS'; import { removeCSS, updateCSS } from '../../vc-util/Dom/dynamicCSS';
import canUseDom from '../canUseDom'; 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) { export function flattenToken(token: any) {
let str = flattenTokenCache.get(token) || ''; let str = '';
Object.keys(token).forEach(key => {
if (!str) { const value = token[key];
Object.keys(token).forEach(key => { str += key;
const value = token[key]; if (value && typeof value === 'object') {
str += key; str += flattenToken(value);
if (value instanceof Theme) { } else {
str += value.id; str += value;
} else if (value && typeof value === 'object') { }
str += flattenToken(value); });
} else {
str += value;
}
});
// Put in cache
flattenTokenCache.set(token, str);
}
return str; return str;
} }
@ -36,18 +23,12 @@ export function token2key(token: any, salt: string): string {
return hash(`${salt}_${flattenToken(token)}`); return hash(`${salt}_${flattenToken(token)}`);
} }
const randomSelectorKey = `random-${Date.now()}-${Math.random()}`.replace(/\./g, ''); const layerKey = `layer-${Date.now()}-${Math.random()}`.replace(/\./g, '');
const layerWidth = '903px';
// Magic `content` for detect selector support function supportSelector(styleStr: string, handleElement?: (ele: HTMLElement) => void): boolean {
const checkContent = '_bAmBoO_';
function supportSelector(
styleStr: string,
handleElement: (ele: HTMLElement) => void,
supportCheck?: (ele: HTMLElement) => boolean,
): boolean {
if (canUseDom()) { if (canUseDom()) {
updateCSS(styleStr, randomSelectorKey); updateCSS(styleStr, layerKey);
const ele = document.createElement('div'); const ele = document.createElement('div');
ele.style.position = 'fixed'; ele.style.position = 'fixed';
@ -61,12 +42,10 @@ function supportSelector(
ele.style.zIndex = '9999999'; ele.style.zIndex = '9999999';
} }
const support = supportCheck const support = getComputedStyle(ele).width === layerWidth;
? supportCheck(ele)
: getComputedStyle(ele).content?.includes(checkContent);
ele.parentNode?.removeChild(ele); ele.parentNode?.removeChild(ele);
removeCSS(randomSelectorKey); removeCSS(layerKey);
return support; return support;
} }
@ -78,41 +57,12 @@ let canLayer: boolean | undefined = undefined;
export function supportLayer(): boolean { export function supportLayer(): boolean {
if (canLayer === undefined) { if (canLayer === undefined) {
canLayer = supportSelector( canLayer = supportSelector(
`@layer ${randomSelectorKey} { .${randomSelectorKey} { content: "${checkContent}"!important; } }`, `@layer ${layerKey} { .${layerKey} { width: ${layerWidth}!important; } }`,
ele => { ele => {
ele.className = randomSelectorKey; ele.className = layerKey;
}, },
); );
} }
return canLayer!; 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

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

@ -12,7 +12,7 @@ export default function getScroll(
const method = top ? 'scrollTop' : 'scrollLeft'; const method = top ? 'scrollTop' : 'scrollLeft';
let result = 0; let result = 0;
if (isWindow(target)) { if (isWindow(target)) {
result = target[top ? 'scrollY' : 'scrollX']; result = target[top ? 'pageYOffset' : 'pageXOffset'];
} else if (target instanceof Document) { } else if (target instanceof Document) {
result = target.documentElement[method]; result = target.documentElement[method];
} else if (target instanceof HTMLElement) { } else if (target instanceof HTMLElement) {

View File

@ -28,7 +28,7 @@ export interface ConfigurableLocation {
location?: Location; location?: Location;
} }
export const defaultWindow = isClient ? window : undefined; export const defaultWindow = /* #__PURE__ */ isClient ? window : undefined;
export const defaultDocument = isClient ? window.document : undefined; export const defaultDocument = /* #__PURE__ */ isClient ? window.document : undefined;
export const defaultNavigator = isClient ? window.navigator : undefined; export const defaultNavigator = /* #__PURE__ */ isClient ? window.navigator : undefined;
export const defaultLocation = isClient ? window.location : undefined; export const defaultLocation = /* #__PURE__ */ isClient ? window.location : undefined;

View File

@ -21,6 +21,8 @@ export const rand = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
}; };
export const isIOS = export const isIOS =
isClient && window?.navigator?.userAgent && /iP(ad|hone|od)/.test(window.navigator.userAgent); /* #__PURE__ */ isClient &&
window?.navigator?.userAgent &&
/iP(ad|hone|od)/.test(window.navigator.userAgent);
export const hasOwn = <T extends object, K extends keyof T>(val: T, key: K): key is K => export const hasOwn = <T extends object, K extends keyof T>(val: T, key: K): key is K =>
Object.prototype.hasOwnProperty.call(val, key); Object.prototype.hasOwnProperty.call(val, key);

View File

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

View File

@ -22,8 +22,8 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) {
const time = timestamp - startTime; const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration); const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
if (isWindow(container)) { if (isWindow(container)) {
(container as Window).scrollTo(window.scrollX, nextScrollTop); (container as Window).scrollTo(window.pageXOffset, nextScrollTop);
} else if (container instanceof Document) { } else if (container instanceof Document || container.constructor.name === 'HTMLDocument') {
(container as Document).documentElement.scrollTop = nextScrollTop; (container as Document).documentElement.scrollTop = nextScrollTop;
} else { } else {
(container as HTMLElement).scrollTop = nextScrollTop; (container as HTMLElement).scrollTop = nextScrollTop;

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,6 @@
// import { StyleProvider } from '../../cssinjs'; // import { StyleProvider } from '../../cssinjs';
import { extractStyle } from '../index'; import { extractStyle } from '../index';
import { ConfigProvider } from '../../../components'; import { ConfigProvider } from '../../../components';
import { theme } from '../../../index';
const testGreenColor = '#008000'; const testGreenColor = '#008000';
describe('Static-Style-Extract', () => { describe('Static-Style-Extract', () => {
@ -27,25 +26,6 @@ describe('Static-Style-Extract', () => {
expect(cssText).toContain(testGreenColor); expect(cssText).toContain(testGreenColor);
expect(cssText).toMatchSnapshot(); expect(cssText).toMatchSnapshot();
}); });
it('should extract static styles with customTheme and customStyle', () => {
const cssText = extractStyle(node => {
return (
<ConfigProvider
theme={{
algorithm: theme.darkAlgorithm,
token: {
colorPrimary: testGreenColor,
},
}}
>
{node}
</ConfigProvider>
);
});
expect(cssText).toContain('#037003');
expect(cssText).toMatchSnapshot();
});
// it('with custom hashPriority', () => { // it('with custom hashPriority', () => {
// const cssText = extractStyle( // const cssText = extractStyle(
// (node) => ( // (node) => (

View File

@ -5,7 +5,7 @@ import type {
TransitionGroupProps, TransitionGroupProps,
TransitionProps, TransitionProps,
} from 'vue'; } from 'vue';
import { nextTick } from 'vue'; import { nextTick, Transition, TransitionGroup } from 'vue';
import { tuple } from './type'; import { tuple } from './type';
const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight'); const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
@ -126,4 +126,6 @@ const getTransitionName = (rootPrefixCls: string, motion: string, transitionName
return `${rootPrefixCls}-${motion}`; return `${rootPrefixCls}-${motion}`;
}; };
export { collapseMotion, getTransitionName, getTransitionDirection }; export { Transition, TransitionGroup, collapseMotion, getTransitionName, getTransitionDirection };
export default Transition;

View File

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

View File

@ -33,7 +33,7 @@ export interface PropOptions<T = any, D = T> {
declare type VNodeChildAtom = VNode | string | number | boolean | null | undefined | void; declare type VNodeChildAtom = VNode | string | number | boolean | null | undefined | void;
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
export type VueNode = VNodeChildAtom | VNodeChildAtom[] | VNode; export type VueNode = VNodeChildAtom | VNodeChildAtom[] | JSX.Element;
export const withInstall = <T>(comp: T) => { export const withInstall = <T>(comp: T) => {
const c = comp as any; const c = comp as any;
@ -92,5 +92,3 @@ export function someType<T>(types?: any[], defaultVal?: T) {
} }
export type CustomSlotsType<T> = SlotsType<T>; export type CustomSlotsType<T> = SlotsType<T>;
export type AnyObject = Record<PropertyKey, any>;

View File

@ -1,6 +1,6 @@
import { filterEmpty } from './props-util'; import { filterEmpty } from './props-util';
import type { Slots, VNode, VNodeArrayChildren, VNodeProps } from 'vue'; import type { VNode, VNodeProps } from 'vue';
import { cloneVNode, isVNode, Comment, Fragment, render as VueRender } from 'vue'; import { cloneVNode } from 'vue';
import warning from './warning'; import warning from './warning';
import type { RefObject } from './createRef'; import type { RefObject } from './createRef';
type NodeProps = Record<string, any> & type NodeProps = Record<string, any> &
@ -40,10 +40,6 @@ export function deepCloneElement<T, U>(
if (Array.isArray(vnode)) { if (Array.isArray(vnode)) {
return vnode.map(item => deepCloneElement(item, nodeProps, override, mergeRef)); return vnode.map(item => deepCloneElement(item, nodeProps, override, mergeRef));
} else { } else {
// 需要判断是否为vnode方可进行clone操作
if (!isVNode(vnode)) {
return vnode;
}
const cloned = cloneElement(vnode, nodeProps, override, mergeRef); const cloned = cloneElement(vnode, nodeProps, override, mergeRef);
if (Array.isArray(cloned.children)) { if (Array.isArray(cloned.children)) {
cloned.children = deepCloneElement(cloned.children as VNode<T, U>[]); cloned.children = deepCloneElement(cloned.children as VNode<T, U>[]);
@ -51,32 +47,3 @@ export function deepCloneElement<T, U>(
return cloned; return cloned;
} }
} }
export function triggerVNodeUpdate(vm: VNode, attrs: Record<string, any>, dom: any) {
VueRender(cloneVNode(vm, { ...attrs }), dom);
}
const ensureValidVNode = (slot: VNodeArrayChildren | null) => {
return (slot || []).some(child => {
if (!isVNode(child)) return true;
if (child.type === Comment) return false;
if (child.type === Fragment && !ensureValidVNode(child.children as VNodeArrayChildren))
return false;
return true;
})
? slot
: null;
};
export function customRenderSlot(
slots: Slots,
name: string,
props: Record<string, unknown>,
fallback?: () => VNodeArrayChildren,
) {
const slot = slots[name]?.(props);
if (ensureValidVNode(slot)) {
return slot;
}
return fallback?.();
}

View File

@ -159,12 +159,6 @@ function showWaveEffect(node: HTMLElement, className: string) {
node?.insertBefore(holder, node?.firstChild); node?.insertBefore(holder, node?.firstChild);
render(<WaveEffect target={node} className={className} />, holder); render(<WaveEffect target={node} className={className} />, holder);
return () => {
render(null, holder);
if (holder.parentElement) {
holder.parentElement.removeChild(holder);
}
};
} }
export default showWaveEffect; export default showWaveEffect;

View File

@ -26,19 +26,19 @@ export default defineComponent({
}, },
setup(props, { slots }) { setup(props, { slots }) {
const instance = getCurrentInstance(); const instance = getCurrentInstance();
const { prefixCls, wave } = useConfigInject('wave', props); const { prefixCls } = useConfigInject('wave', props);
// ============================== Style =============================== // ============================== Style ===============================
const [, hashId] = useStyle(prefixCls); const [, hashId] = useStyle(prefixCls);
// =============================== Wave =============================== // =============================== Wave ===============================
const showWave = useWave( const showWave = useWave(
instance,
computed(() => classNames(prefixCls.value, hashId.value)), computed(() => classNames(prefixCls.value, hashId.value)),
wave,
); );
let onClick: (e: MouseEvent) => void; let onClick: (e: MouseEvent) => void;
const clear = () => { const clear = () => {
const node = findDOMNode(instance) as HTMLElement; const node = findDOMNode(instance);
node.removeEventListener('click', onClick, true); node.removeEventListener('click', onClick, true);
}; };
onMounted(() => { onMounted(() => {

View File

@ -1,25 +1,16 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComponentInternalInstance, Ref } from 'vue';
import { onBeforeUnmount, getCurrentInstance } from 'vue';
import { findDOMNode } from '../props-util'; import { findDOMNode } from '../props-util';
import showWaveEffect from './WaveEffect'; import showWaveEffect from './WaveEffect';
export default function useWave( export default function useWave(
instance: ComponentInternalInstance | null,
className: Ref<string>, className: Ref<string>,
wave?: ComputedRef<{ disabled?: boolean }>,
): VoidFunction { ): VoidFunction {
const instance = getCurrentInstance();
let stopWave: () => void;
function showWave() { function showWave() {
const node = findDOMNode(instance); const node = findDOMNode(instance);
stopWave?.();
if (wave?.value?.disabled || !node) { showWaveEffect(node, className.value);
return;
}
stopWave = showWaveEffect(node, className.value);
} }
onBeforeUnmount(() => {
stopWave?.();
});
return showWave; return showWave;
} }

View File

@ -176,6 +176,7 @@ const Affix = defineComponent({
affixStyle: undefined, affixStyle: undefined,
placeholderStyle: undefined, placeholderStyle: undefined,
}); });
currentInstance.update();
// Test if `updatePosition` called // Test if `updatePosition` called
if (process.env.NODE_ENV === 'test') { if (process.env.NODE_ENV === 'test') {
emit('testUpdatePosition'); emit('testUpdatePosition');
@ -255,7 +256,7 @@ const Affix = defineComponent({
const { prefixCls } = useConfigInject('affix', props); const { prefixCls } = useConfigInject('affix', props);
const [wrapSSR, hashId] = useStyle(prefixCls); const [wrapSSR, hashId] = useStyle(prefixCls);
return () => { return () => {
const { affixStyle, placeholderStyle, status } = state; const { affixStyle, placeholderStyle } = state;
const className = classNames({ const className = classNames({
[prefixCls.value]: affixStyle, [prefixCls.value]: affixStyle,
[hashId.value]: true, [hashId.value]: true,
@ -270,7 +271,7 @@ const Affix = defineComponent({
]); ]);
return wrapSSR( return wrapSSR(
<ResizeObserver onResize={updatePosition}> <ResizeObserver onResize={updatePosition}>
<div {...restProps} {...attrs} ref={placeholderNode} data-measure-status={status}> <div {...restProps} {...attrs} ref={placeholderNode}>
{affixStyle && <div style={placeholderStyle} aria-hidden="true" />} {affixStyle && <div style={placeholderStyle} aria-hidden="true" />}
<div class={className} ref={fixedNode} style={affixStyle}> <div class={className} ref={fixedNode} style={affixStyle}>
{slots.default?.()} {slots.default?.()}

View File

@ -1,5 +1,5 @@
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue'; import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
import { computed, defineComponent, shallowRef, Transition } from 'vue'; import { computed, defineComponent, shallowRef } from 'vue';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined'; import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined';
import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined'; import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined';
@ -11,7 +11,7 @@ import InfoCircleFilled from '@ant-design/icons-vue/InfoCircleFilled';
import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled'; import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { getTransitionProps } from '../_util/transition'; import { getTransitionProps, Transition } from '../_util/transition';
import { isValidElement } from '../_util/props-util'; import { isValidElement } from '../_util/props-util';
import { tuple, withInstall } from '../_util/type'; import { tuple, withInstall } from '../_util/type';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';

View File

@ -5,7 +5,6 @@ type: Other
title: App title: App
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original
tag: New
--- ---
Application wrapper for some global usages. Application wrapper for some global usages.
@ -97,7 +96,7 @@ import type { MessageInstance } from 'ant-design-vue/es/message/interface';
import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm'; import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm';
import type { NotificationInstance } from 'ant-design-vue/es/notification/interface'; import type { NotificationInstance } from 'ant-design-vue/es/notification/interface';
export const useGlobalStore = defineStore('global', () => { export const useGloablStore = defineStore('global', () => {
const message: MessageInstance = ref(); const message: MessageInstance = ref();
const notification: NotificationInstance = ref(); const notification: NotificationInstance = ref();
const modal: Omit<ModalStaticFunctions, 'warn'> = ref(); const modal: Omit<ModalStaticFunctions, 'warn'> = ref();

View File

@ -6,7 +6,6 @@ type: 其它
title: App title: App
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original
tag: New
--- ---
新的包裹组件,提供重置样式和提供消费上下文的默认环境。 新的包裹组件,提供重置样式和提供消费上下文的默认环境。
@ -98,7 +97,7 @@ import type { MessageInstance } from 'ant-design-vue/es/message/interface';
import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm'; import type { ModalStaticFunctions } from 'ant-design-vue/es/modal/confirm';
import type { NotificationInstance } from 'ant-design-vue/es/notification/interface'; import type { NotificationInstance } from 'ant-design-vue/es/notification/interface';
export const useGlobalStore = defineStore('global', () => { export const useGloablStore = defineStore('global', () => {
const message: MessageInstance = ref(); const message: MessageInstance = ref();
const notification: NotificationInstance = ref(); const notification: NotificationInstance = ref();
const modal: Omit<ModalStaticFunctions, 'warn'> = ref(); const modal: Omit<ModalStaticFunctions, 'warn'> = ref();

View File

@ -50,8 +50,6 @@ const AutoComplete = defineComponent({
props: autoCompleteProps(), props: autoCompleteProps(),
// emits: ['change', 'select', 'focus', 'blur'], // emits: ['change', 'select', 'focus', 'blur'],
slots: Object as CustomSlotsType<{ slots: Object as CustomSlotsType<{
option: any;
// deprecated, should use props `options` instead, not slot
options: any; options: any;
default: any; default: any;
notFoundContent: any; notFoundContent: any;

View File

@ -3,9 +3,9 @@ import ScrollNumber from './ScrollNumber';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import { getPropsSlot, flattenChildren } from '../_util/props-util'; import { getPropsSlot, flattenChildren } from '../_util/props-util';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
import { getTransitionProps } from '../_util/transition'; import { getTransitionProps, Transition } from '../_util/transition';
import type { ExtractPropTypes, CSSProperties, PropType } from 'vue'; import type { ExtractPropTypes, CSSProperties, PropType } from 'vue';
import { defineComponent, computed, ref, watch, Transition } from 'vue'; import { defineComponent, computed, ref, watch } from 'vue';
import Ribbon from './Ribbon'; import Ribbon from './Ribbon';
import useConfigInject from '../config-provider/hooks/useConfigInject'; import useConfigInject from '../config-provider/hooks/useConfigInject';
import isNumeric from '../_util/isNumeric'; import isNumeric from '../_util/isNumeric';

View File

@ -1,5 +1,6 @@
import { defineComponent, nextTick, Transition } from 'vue'; import { defineComponent, nextTick } from 'vue';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import Transition from '../_util/transition';
const getCollapsedWidth = (node: HTMLSpanElement) => { const getCollapsedWidth = (node: HTMLSpanElement) => {
if (node) { if (node) {
node.style.width = '0px'; node.style.width = '0px';

View File

@ -495,6 +495,7 @@ const genBlockButtonStyle: GenerateStyle<ButtonToken> = token => {
// ============================== Export ============================== // ============================== Export ==============================
export default genComponentStyleHook('Button', token => { export default genComponentStyleHook('Button', token => {
const { controlTmpOutline, paddingContentHorizontal } = token; const { controlTmpOutline, paddingContentHorizontal } = token;
const buttonToken = mergeToken<ButtonToken>(token, { const buttonToken = mergeToken<ButtonToken>(token, {
colorOutlineDefault: controlTmpOutline, colorOutlineDefault: controlTmpOutline,
buttonPaddingHorizontal: paddingContentHorizontal, buttonPaddingHorizontal: paddingContentHorizontal,

View File

@ -16,11 +16,9 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
**Note:** Part of the Calendar's locale is read from `value`. So, please set the locale of `dayjs` correctly. **Note:** Part of the Calendar's locale is read from `value`. So, please set the locale of `dayjs` correctly.
```jsx ```html
// The default locale is en-US, if you want to use other locale, just set locale in entry file globally. // The default locale is en-US, if you want to use other locale, just set locale in entry file
// import dayjs from 'dayjs'; globally. // import dayjs from 'dayjs'; // import 'dayjs/locale/zh-cn'; // dayjs.locale('zh-cn');
// import 'dayjs/locale/zh-cn';
// dayjs.locale('zh-cn');
<a-calendar v-model:value @panelChange="onPanelChange" @select="onSelect"></a-calendar> <a-calendar v-model:value @panelChange="onPanelChange" @select="onSelect"></a-calendar>
``` ```

View File

@ -17,27 +17,25 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
**注意:**Calendar 部分 locale 是从 value 中读取,所以请先正确设置 dayjs 的 locale。 **注意:**Calendar 部分 locale 是从 value 中读取,所以请先正确设置 dayjs 的 locale。
```jsx ```html
// 默认语言为 en-US所以如果需要使用其他语言推荐在入口文件全局设置 locale // 默认语言为 en-US所以如果需要使用其他语言推荐在入口文件全局设置 locale // import dayjs from
// import dayjs from 'dayjs'; 'dayjs'; // import 'dayjs/locale/zh-cn'; // dayjs.locale('zh-cn');
// import 'dayjs/locale/zh-cn';
// dayjs.locale('zh-cn');
<a-calendar v-model:value="value" @panelChange="onPanelChange" @select="onSelect"></a-calendar> <a-calendar v-model:value="value" @panelChange="onPanelChange" @select="onSelect"></a-calendar>
``` ```
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| dateCellRender | 作用域插槽,用来自定义渲染日期单元格,返回内容会被追加到单元格, | v-slot:dateCellRender="{current: dayjs}" | - | | | dateCellRender | 作用域插槽,用来自定义渲染日期单元格,返回内容会被追加到单元格, | v-slot:dateCellRender="{current: dayjs}" | | |
| dateFullCellRender | 作用域插槽,自定义渲染日期单元格,返回内容覆盖单元格 | v-slot:dateFullCellRender="{current: dayjs}" | - | | | dateFullCellRender | 作用域插槽,自定义渲染日期单元格,返回内容覆盖单元格 | v-slot:dateFullCellRender="{current: dayjs}" | | |
| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | | | disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | | |
| fullscreen | 是否全屏显示 | boolean | true | | | fullscreen | 是否全屏显示 | boolean | true | |
| headerRender | 自定义头部内容 | v-slot:headerRender="{value: dayjs, type: string, onChange: f(), onTypeChange: f()}" | - | | | headerRender | 自定义头部内容 | v-slot:headerRender="{value: dayjs, type: string, onChange: f(), onTypeChange: f()}" | - | |
| locale | 国际化配置 | object | [默认配置](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | | | locale | 国际化配置 | object | [默认配置](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | |
| mode | 初始模式,`month/year` | string | month | | | mode | 初始模式,`month/year` | string | month | |
| monthCellRender | 作用域插槽,自定义渲染月单元格,返回内容会被追加到单元格 | v-slot:monthCellRender="{current: dayjs}" | - | | | monthCellRender | 作用域插槽,自定义渲染月单元格,返回内容会被追加到单元格 | v-slot:monthCellRender="{current: dayjs}" | | |
| monthFullCellRender | 作用域插槽,自定义渲染月单元格,返回内容覆盖单元格 | v-slot:monthFullCellRender="{current: dayjs}" | - | | | monthFullCellRender | 作用域插槽,自定义渲染月单元格,返回内容覆盖单元格 | v-slot:monthFullCellRender="{current: dayjs}" | | |
| validRange | 设置可以显示的日期 | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | | | validRange | 设置可以显示的日期 | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | | |
| value(v-model) | 展示日期 | [dayjs](https://day.js.org/) | 当前日期 | | | value(v-model) | 展示日期 | [dayjs](https://day.js.org/) | 当前日期 | |
| valueFormat | 可选,绑定值的格式,对 value、defaultValue 起作用。不指定则绑定值为 dayjs 对象 | string[具体格式](https://day.js.org/docs/zh-CN/display/format) | - | | | valueFormat | 可选,绑定值的格式,对 value、defaultValue 起作用。不指定则绑定值为 dayjs 对象 | string[具体格式](https://day.js.org/docs/zh-CN/display/format) | - | |
@ -45,8 +43,8 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
| 事件名称 | 说明 | 回调参数 | | | 事件名称 | 说明 | 回调参数 | |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| change | 日期变化时的回调, 面板变化有可能导致日期变化 | function(date: dayjs \| string | - | | change | 日期变化时的回调, 面板变化有可能导致日期变化 | function(date: dayjs \| string | |
| panelChange | 日期面板变化回调 | function(date: dayjs \| string, mode: string) | - | | panelChange | 日期面板变化回调 | function(date: dayjs \| string, mode: string) | |
| select | 选择日期回调,包含来源信息 | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | | | select | 选择日期回调,包含来源信息 | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | |
### 如何仅获取来自面板点击的日期? ### 如何仅获取来自面板点击的日期?

View File

@ -1,5 +1,5 @@
import type { VNodeTypes, PropType, VNode, ExtractPropTypes, CSSProperties } from 'vue'; import type { VNodeTypes, PropType, VNode, ExtractPropTypes, CSSProperties } from 'vue';
import { isVNode, defineComponent } from 'vue'; import { isVNode, defineComponent, renderSlot } from 'vue';
import Tabs from '../tabs'; import Tabs from '../tabs';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { flattenChildren, isEmptyElement, filterEmptyWithUndefined } from '../_util/props-util'; import { flattenChildren, isEmptyElement, filterEmptyWithUndefined } from '../_util/props-util';
@ -10,8 +10,6 @@ import devWarning from '../vc-util/devWarning';
import useStyle from './style'; import useStyle from './style';
import Skeleton from '../skeleton'; import Skeleton from '../skeleton';
import type { CustomSlotsType } from '../_util/type'; import type { CustomSlotsType } from '../_util/type';
import { customRenderSlot } from '../_util/vnode';
export interface CardTabListType { export interface CardTabListType {
key: string; key: string;
tab: any; tab: any;
@ -154,7 +152,7 @@ const Card = defineComponent({
`tabList slots is deprecated, Please use \`customTab\` instead.`, `tabList slots is deprecated, Please use \`customTab\` instead.`,
); );
let tab = temp !== undefined ? temp : slots[name] ? slots[name](item) : null; let tab = temp !== undefined ? temp : slots[name] ? slots[name](item) : null;
tab = customRenderSlot(slots, 'customTab', item as any, () => [tab]); tab = renderSlot(slots, 'customTab', item as any, () => [tab]);
return <TabPane tab={tab} key={item.key} disabled={item.disabled} />; return <TabPane tab={tab} key={item.key} disabled={item.disabled} />;
})} })}
</Tabs> </Tabs>

View File

@ -15,7 +15,6 @@ import warning from '../_util/warning';
import type { EventHandler } from '../_util/EventInterface'; import type { EventHandler } from '../_util/EventInterface';
import { FormItemInputContext, useInjectFormItemContext } from '../form/FormItemContext'; import { FormItemInputContext, useInjectFormItemContext } from '../form/FormItemContext';
import useConfigInject from '../config-provider/hooks/useConfigInject'; import useConfigInject from '../config-provider/hooks/useConfigInject';
import { useInjectDisabled } from '../config-provider/DisabledContext';
import type { CheckboxChangeEvent, CheckboxProps } from './interface'; import type { CheckboxChangeEvent, CheckboxProps } from './interface';
import { CheckboxGroupContextKey, checkboxProps } from './interface'; import { CheckboxGroupContextKey, checkboxProps } from './interface';
@ -34,8 +33,6 @@ export default defineComponent({
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
const formItemInputContext = FormItemInputContext.useInject(); const formItemInputContext = FormItemInputContext.useInject();
const { prefixCls, direction, disabled } = useConfigInject('checkbox', props); const { prefixCls, direction, disabled } = useConfigInject('checkbox', props);
const contextDisabled = useInjectDisabled();
// style // style
const [wrapSSR, hashId] = useStyle(prefixCls); const [wrapSSR, hashId] = useStyle(prefixCls);
@ -97,7 +94,7 @@ export default defineComponent({
}; };
checkboxProps.name = checkboxGroup.name.value; checkboxProps.name = checkboxGroup.name.value;
checkboxProps.checked = checkboxGroup.mergedValue.value.includes(props.value); checkboxProps.checked = checkboxGroup.mergedValue.value.includes(props.value);
checkboxProps.disabled = mergedDisabled.value || contextDisabled.value; checkboxProps.disabled = mergedDisabled.value || checkboxGroup.disabled.value;
checkboxProps.indeterminate = indeterminate; checkboxProps.indeterminate = indeterminate;
} else { } else {
checkboxProps.onChange = handleChange; checkboxProps.onChange = handleChange;
@ -132,6 +129,7 @@ export default defineComponent({
{...checkboxProps} {...checkboxProps}
class={checkboxClass} class={checkboxClass}
ref={checkboxRef} ref={checkboxRef}
disabled={mergedDisabled.value}
/> />
{children.length ? <span>{children}</span> : null} {children.length ? <span>{children}</span> : null}
</label>, </label>,

View File

@ -41,7 +41,7 @@ export default defineComponent({
}); });
}); });
const triggerUpdate = ref(Symbol()); const triggerUpdate = ref(Symbol());
const registeredValuesMap = ref(new Map<Symbol, string>()); const registeredValuesMap = ref<Map<Symbol, string>>(new Map());
const cancelValue = (id: Symbol) => { const cancelValue = (id: Symbol) => {
registeredValuesMap.value.delete(id); registeredValuesMap.value.delete(id);
triggerUpdate.value = Symbol(); triggerUpdate.value = Symbol();

View File

@ -40,7 +40,7 @@ Checkbox component.
| disabled | Disable all checkboxes | boolean | false | | | disabled | Disable all checkboxes | boolean | false | |
| name | The `name` property of all `input[type="checkbox"]` children | string | - | 1.5.0 | | name | The `name` property of all `input[type="checkbox"]` children | string | - | 1.5.0 |
| options | Specifies options, you can customize `label` with slot = "label" slot-scope="option" | string\[] \| Array&lt;{ label: string value: string disabled?: boolean, indeterminate?: boolean, onChange?: function }> | \[] | | | options | Specifies options, you can customize `label` with slot = "label" slot-scope="option" | string\[] \| Array&lt;{ label: string value: string disabled?: boolean, indeterminate?: boolean, onChange?: function }> | \[] | |
| value(v-model) | Used for setting the currently selected value. | (boolean \| string \| number)\[] | \[] | | | value(v-model) | Used for setting the currently selected value. | string\[] | \[] | |
#### events #### events

View File

@ -41,7 +41,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*G3MjTYXL6AIAAA
| disabled | 整组失效 | boolean | false | | | disabled | 整组失效 | boolean | false | |
| name | CheckboxGroup 下所有 `input[type="checkbox"]``name` 属性 | string | - | 1.5.0 | | name | CheckboxGroup 下所有 `input[type="checkbox"]``name` 属性 | string | - | 1.5.0 |
| options | 指定可选项,可以通过 slot="label" slot-scope="option" 定制`label` | string\[] \| Array&lt;{ label: string value: string disabled?: boolean, indeterminate?: boolean, onChange?: function }> | \[] | | | options | 指定可选项,可以通过 slot="label" slot-scope="option" 定制`label` | string\[] \| Array&lt;{ label: string value: string disabled?: boolean, indeterminate?: boolean, onChange?: function }> | \[] | |
| value(v-model) | 指定选中的选项 | (boolean \| string \| number)\[] | \[] | | | value(v-model) | 指定选中的选项 | string\[] | \[] | |
#### 事件 #### 事件

View File

@ -42,6 +42,7 @@ export default defineComponent({
accordion: false, accordion: false,
destroyInactivePanel: false, destroyInactivePanel: false,
bordered: true, bordered: true,
openAnimation: collapseMotion('ant-motion-collapse', false),
expandIconPosition: 'start', expandIconPosition: 'start',
}), }),
slots: Object as CustomSlotsType<{ slots: Object as CustomSlotsType<{
@ -60,7 +61,7 @@ export default defineComponent({
}, },
{ deep: true }, { deep: true },
); );
const { prefixCls, direction, rootPrefixCls } = useConfigInject('collapse', props); const { prefixCls, direction } = useConfigInject('collapse', props);
// style // style
const [wrapSSR, hashId] = useStyle(prefixCls); const [wrapSSR, hashId] = useStyle(prefixCls);
@ -130,7 +131,6 @@ export default defineComponent({
if (isEmptyElement(child)) return; if (isEmptyElement(child)) return;
const activeKey = stateActiveKey.value; const activeKey = stateActiveKey.value;
const { accordion, destroyInactivePanel, collapsible, openAnimation } = props; const { accordion, destroyInactivePanel, collapsible, openAnimation } = props;
const animation = openAnimation || collapseMotion(`${rootPrefixCls.value}-motion-collapse`);
// If there is no key provide, use the panel order as default key // If there is no key provide, use the panel order as default key
const key = String(child.key ?? index); const key = String(child.key ?? index);
@ -161,7 +161,7 @@ export default defineComponent({
isActive, isActive,
prefixCls: prefixCls.value, prefixCls: prefixCls.value,
destroyInactivePanel, destroyInactivePanel,
openAnimation: animation, openAnimation,
accordion, accordion,
onItemClick: mergeCollapsible === 'disabled' ? null : onClickItem, onItemClick: mergeCollapsible === 'disabled' ? null : onClickItem,
expandIcon: renderExpandIcon, expandIcon: renderExpandIcon,

View File

@ -2,7 +2,8 @@ import PanelContent from './PanelContent';
import { initDefaultProps } from '../_util/props-util'; import { initDefaultProps } from '../_util/props-util';
import { collapsePanelProps } from './commonProps'; import { collapsePanelProps } from './commonProps';
import type { ExtractPropTypes } from 'vue'; import type { ExtractPropTypes } from 'vue';
import { defineComponent, Transition } from 'vue'; import { defineComponent } from 'vue';
import Transition from '../_util/transition';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import devWarning from '../vc-util/devWarning'; import devWarning from '../vc-util/devWarning';
import useConfigInject from '../config-provider/hooks/useConfigInject'; import useConfigInject from '../config-provider/hooks/useConfigInject';

View File

@ -20,7 +20,7 @@ A content area which can be collapsed and expanded.
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| accordion | If `true`, `Collapse` renders as `Accordion` | boolean | `false` | | | accordion | If `true`, `Collapse` renders as `Accordion` | boolean | `false` | |
| activeKey(v-model) | Key of the active panel | string\[] \| string <br> number\[] \| number | No default value. In [accordion mode](#components-collapse-demo-accordion), it's the key of the first panel. | | | activeKey(v-model) | Key of the active panel | string\[] \| string <br> number\[] \| number | No default value. In `accordion` mode, it's the key of the first panel. | |
| bordered | Toggles rendering of the border around the collapse block | boolean | `true` | | | bordered | Toggles rendering of the border around the collapse block | boolean | `true` | |
| collapsible | Specify whether the panels of children be collapsible or the trigger area of collapsible | `header` \| `icon` \| `disabled` | - | 4.0 | | collapsible | Specify whether the panels of children be collapsible or the trigger area of collapsible | `header` \| `icon` \| `disabled` | - | 4.0 |
| destroyInactivePanel | Destroy Inactive Panel | boolean | `false` | | | destroyInactivePanel | Destroy Inactive Panel | boolean | `false` | |

View File

@ -20,8 +20,8 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sir-TK0HkWcAAA
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| accordion | 手风琴模式,始终只有一个面板处在激活状态 | boolean | `false` | | | accordion | 手风琴模式 | boolean | `false` | |
| activeKey(v-model) | 当前激活 tab 面板的 key | string\[] \| string <br> number\[] \| number | 默认无,[手风琴模式](#components-collapse-demo-accordion)下默认第一个元素 | | | activeKey(v-model) | 当前激活 tab 面板的 key | string\[] \| string <br> number\[] \| number | 默认无,accordion 模式下默认第一个元素 | |
| bordered | 带边框风格的折叠面板 | boolean | `true` | | | bordered | 带边框风格的折叠面板 | boolean | `true` | |
| collapsible | 所有子面板是否可折叠或指定可折叠触发区域 | `header` \| `icon` \| `disabled` | - | 4.0 | | collapsible | 所有子面板是否可折叠或指定可折叠触发区域 | `header` \| `icon` \| `disabled` | - | 4.0 |
| destroyInactivePanel | 销毁折叠隐藏的面板 | boolean | `false` | | | destroyInactivePanel | 销毁折叠隐藏的面板 | boolean | `false` | |
@ -42,6 +42,6 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sir-TK0HkWcAAA
| collapsible | 是否可折叠或指定可折叠触发区域 | `header` \| `disabled` | - | 3.0 | | collapsible | 是否可折叠或指定可折叠触发区域 | `header` \| `disabled` | - | 3.0 |
| extra | 自定义渲染每个面板右上角的内容 | VNode \| slot | - | 1.5.0 | | extra | 自定义渲染每个面板右上角的内容 | VNode \| slot | - | 1.5.0 |
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false | | | forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false | |
| header | 面板头内容 | string\|slot | - | | | header | 面板头内容 | string\|slot | | |
| key | 对应 activeKey | string \| number | - | | | key | 对应 activeKey | string \| number | | |
| showArrow | 是否展示当前面板上的箭头 | boolean | `true` | | | showArrow | 是否展示当前面板上的箭头 | boolean | `true` | |

View File

@ -264,6 +264,3 @@ export { default as Tour } from './tour';
export type { AppProps } from './app'; export type { AppProps } from './app';
export { default as App } from './app'; export { default as App } from './app';
export type { FlexProps } from './flex';
export { default as Flex } from './flex';

View File

@ -105,9 +105,6 @@ export const configProviderProps = () => ({
select: objectType<{ select: objectType<{
showSearch?: boolean; showSearch?: boolean;
}>(), }>(),
wave: objectType<{
disabled?: boolean;
}>(),
}); });
export type ConfigProviderProps = Partial<ExtractPropTypes<ReturnType<typeof configProviderProps>>>; export type ConfigProviderProps = Partial<ExtractPropTypes<ReturnType<typeof configProviderProps>>>;
@ -148,12 +145,6 @@ export interface ConfigProviderInnerProps {
componentSize?: ComputedRef<SizeType>; componentSize?: ComputedRef<SizeType>;
componentDisabled?: ComputedRef<boolean>; componentDisabled?: ComputedRef<boolean>;
transformCellText?: ComputedRef<(tableProps: TransformCellTextProps) => any>; transformCellText?: ComputedRef<(tableProps: TransformCellTextProps) => any>;
wave?: ComputedRef<{
disabled?: boolean;
}>;
flex?: ComputedRef<{
vertical?: boolean;
}>;
} }
export const configProviderKey: InjectionKey<ConfigProviderInnerProps> = Symbol('configProvider'); export const configProviderKey: InjectionKey<ConfigProviderInnerProps> = Symbol('configProvider');

View File

@ -24,7 +24,7 @@ export default (name: string, props: Record<any, any>) => {
() => props.getTargetContainer ?? configProvider.getTargetContainer?.value, () => props.getTargetContainer ?? configProvider.getTargetContainer?.value,
); );
const getPopupContainer = computed( const getPopupContainer = computed(
() => props.getContainer ?? props.getPopupContainer ?? configProvider.getPopupContainer?.value, () => props.getPopupContainer ?? configProvider.getPopupContainer?.value,
); );
const dropdownMatchSelectWidth = computed<boolean | number>( const dropdownMatchSelectWidth = computed<boolean | number>(
@ -42,10 +42,6 @@ export default (name: string, props: Record<any, any>) => {
); );
const disabled = computed<boolean>(() => props.disabled ?? disabledContext.value); const disabled = computed<boolean>(() => props.disabled ?? disabledContext.value);
const csp = computed(() => props.csp ?? configProvider.csp); const csp = computed(() => props.csp ?? configProvider.csp);
const wave = computed<{
disabled?: boolean;
}>(() => props.wave ?? configProvider.wave?.value);
return { return {
configProvider, configProvider,
prefixCls, prefixCls,
@ -67,6 +63,5 @@ export default (name: string, props: Record<any, any>) => {
iconPrefixCls, iconPrefixCls,
disabled, disabled,
select: configProvider.select, select: configProvider.select,
wave,
}; };
}; };

View File

@ -64,7 +64,6 @@ Some components use dynamic style to support wave effect. You can config `csp` p
| space | Set Space `size`, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 3.0 | | space | Set Space `size`, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 3.0 |
| transformCellText | Table data can be changed again before rendering. The default configuration of general user empty data. | Function({ text, column, record, index }) => any | - | 1.5.4 | | transformCellText | Table data can be changed again before rendering. The default configuration of general user empty data. | Function({ text, column, record, index }) => any | - | 1.5.4 |
| virtual | Disable virtual scroll when set to false | boolean | true | 3.0 | | virtual | Disable virtual scroll when set to false | boolean | true | 3.0 |
| wave | Config wave effect | { disabled?: boolean } | - | 4.0.7 |
### ConfigProvider.config() `3.0.0+` ### ConfigProvider.config() `3.0.0+`

View File

@ -1,4 +1,4 @@
import type { App, MaybeRef, Plugin, WatchStopHandle } from 'vue'; import type { App, Plugin, WatchStopHandle } from 'vue';
import { watch, computed, reactive, defineComponent, watchEffect } from 'vue'; import { watch, computed, reactive, defineComponent, watchEffect } from 'vue';
import defaultRenderEmpty from './renderEmpty'; import defaultRenderEmpty from './renderEmpty';
import type { RenderEmptyHandler } from './renderEmpty'; import type { RenderEmptyHandler } from './renderEmpty';
@ -7,6 +7,7 @@ import LocaleProvider, { ANT_MARK } from '../locale-provider';
import LocaleReceiver from '../locale-provider/LocaleReceiver'; import LocaleReceiver from '../locale-provider/LocaleReceiver';
import type { MaybeRef } from '../_util/type';
import message from '../message'; import message from '../message';
import notification from '../notification'; import notification from '../notification';
import { registerTheme } from './cssVariables'; import { registerTheme } from './cssVariables';
@ -196,7 +197,6 @@ const ConfigProvider = defineComponent({
); );
const componentSize = computed(() => props.componentSize); const componentSize = computed(() => props.componentSize);
const componentDisabled = computed(() => props.componentDisabled); const componentDisabled = computed(() => props.componentDisabled);
const wave = computed(() => props.wave ?? parentContext.wave?.value);
const configProvider: ConfigProviderInnerProps = { const configProvider: ConfigProviderInnerProps = {
csp, csp,
autoInsertSpaceInButton, autoInsertSpaceInButton,
@ -221,7 +221,6 @@ const ConfigProvider = defineComponent({
componentSize, componentSize,
componentDisabled, componentDisabled,
transformCellText: computed(() => props.transformCellText), transformCellText: computed(() => props.transformCellText),
wave,
}; };
// ================================ Dynamic theme ================================ // ================================ Dynamic theme ================================
@ -231,6 +230,7 @@ const ConfigProvider = defineComponent({
algorithm && (!Array.isArray(algorithm) || algorithm.length > 0) algorithm && (!Array.isArray(algorithm) || algorithm.length > 0)
? createTheme(algorithm) ? createTheme(algorithm)
: undefined; : undefined;
return { return {
...rest, ...rest,
theme: themeObj, theme: themeObj,

View File

@ -65,7 +65,6 @@ ConfigProvider 使用 Vue 的 [provide / inject](https://vuejs.org/v2/api/#provi
| space | 设置 Space 的 `size`,参考 [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 3.0 | | space | 设置 Space 的 `size`,参考 [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 3.0 |
| transformCellText | Table 数据渲染前可以再次改变,一般用户空数据的默认配置 | Function({ text, column, record, index }) => any | - | 1.5.4 | | transformCellText | Table 数据渲染前可以再次改变,一般用户空数据的默认配置 | Function({ text, column, record, index }) => any | - | 1.5.4 |
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 3.0 | | virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 3.0 |
| wave | 设置水波纹特效 | { disabled?: boolean } | - | 4.0.7 |
### ConfigProvider.config() `3.0.0+` ### ConfigProvider.config() `3.0.0+`

View File

@ -85,7 +85,7 @@ The following APIs are shared by DatePicker, RangePicker.
| disabled | Determine whether the DatePicker is disabled | boolean | false | | | disabled | Determine whether the DatePicker is disabled | boolean | false | |
| disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | | | disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/). When an array is provided, all values are used for parsing and first value is used for formatting, support [Custom Format](#components-date-picker-demo-format) | [formatType](#formattype) | `YYYY-MM-DD` | | | format | To set the date format, refer to [dayjs](https://day.js.org/). When an array is provided, all values are used for parsing and first value is used for formatting, support [Custom Format](#components-date-picker-demo-format) | [formatType](#formattype) | `YYYY-MM-DD` | |
| popupClassName | To customize the className of the popup calendar | string | - | | | dropdownClassName | To customize the className of the popup calendar | string | - | |
| getPopupContainer | To set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | | | getPopupContainer | To set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | |
| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false | | | inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false | |
| locale | Localization configuration | object | [default](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | | | locale | Localization configuration | object | [default](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | |

View File

@ -86,7 +86,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3OpRQKcygo8AAA
| disabled | 禁用 | boolean | false | | | disabled | 禁用 | boolean | false | |
| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | | | disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | |
| format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 [dayjs](https://day.js.org/docs/zh-CN/display/format),支持[自定义格式](#components-date-picker-demo-format) | [formatType](#formattype) | `YYYY-MM-DD` | | | format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 [dayjs](https://day.js.org/docs/zh-CN/display/format),支持[自定义格式](#components-date-picker-demo-format) | [formatType](#formattype) | `YYYY-MM-DD` | |
| popupClassName | 额外的弹出日历 className | string | - | | | dropdownClassName | 额外的弹出日历 className | string | - | |
| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | - | | | getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | - | |
| inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | | | inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | |
| locale | 国际化配置 | object | [默认配置](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | - | | locale | 国际化配置 | object | [默认配置](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | - |

View File

@ -6,15 +6,7 @@ import type { PickerLocale } from '../generatePicker';
const locale: PickerLocale = { const locale: PickerLocale = {
lang: { lang: {
placeholder: '日付を選択', placeholder: '日付を選択',
yearPlaceholder: '年を選択',
quarterPlaceholder: '四半期を選択',
monthPlaceholder: '月を選択',
weekPlaceholder: '週を選択',
rangePlaceholder: ['開始日付', '終了日付'], rangePlaceholder: ['開始日付', '終了日付'],
rangeYearPlaceholder: ['開始年', '終了年'],
rangeMonthPlaceholder: ['開始月', '終了月'],
rangeQuarterPlaceholder: ['開始四半期', '終了四半期'],
rangeWeekPlaceholder: ['開始週', '終了週'],
...CalendarLocale, ...CalendarLocale,
}, },
timePickerLocale: { timePickerLocale: {

View File

@ -105,7 +105,7 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
}, },
[`&-vertical${componentCls}-dashed`]: { [`&-vertical${componentCls}-dashed`]: {
borderInlineStartWidth: lineWidth, borderInlineStart: lineWidth,
borderInlineEnd: 0, borderInlineEnd: 0,
borderBlockStart: 0, borderBlockStart: 0,
borderBlockEnd: 0, borderBlockEnd: 0,

View File

@ -285,13 +285,9 @@ const Drawer = defineComponent({
}); });
const wrapperStyle = computed(() => { const wrapperStyle = computed(() => {
const { zIndex, contentWrapperStyle } = props; const { zIndex } = props;
const val = offsetStyle.value; const val = offsetStyle.value;
return [ return [{ zIndex, transform: sPush.value ? pushTransform.value : undefined }, val];
{ zIndex, transform: sPush.value ? pushTransform.value : undefined },
{ ...contentWrapperStyle },
val,
];
}); });
const renderHeader = (prefixCls: string) => { const renderHeader = (prefixCls: string) => {

View File

@ -22,7 +22,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*5qm4S4Zgh2QAAA
| 参数 | 说明 | 类型 | 默认值 | | | 参数 | 说明 | 类型 | 默认值 | |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | - | | | align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | | |
| arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | 3.3.0 | | arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | 3.3.0 |
| destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | 3.0 | | destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | 3.0 |
| disabled | 菜单是否禁用 | boolean | - | | | disabled | 菜单是否禁用 | boolean | - | |

View File

@ -1,4 +1,4 @@
import { defineComponent, h } from 'vue'; import { defineComponent } from 'vue';
import type { CSSProperties, ExtractPropTypes } from 'vue'; import type { CSSProperties, ExtractPropTypes } from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import LocaleReceiver from '../locale-provider/LocaleReceiver'; import LocaleReceiver from '../locale-provider/LocaleReceiver';
@ -11,6 +11,9 @@ import useConfigInject from '../config-provider/hooks/useConfigInject';
import useStyle from './style'; import useStyle from './style';
const defaultEmptyImg = <DefaultEmptyImg />;
const simpleEmptyImg = <SimpleEmptyImg />;
interface Locale { interface Locale {
description?: string; description?: string;
} }
@ -37,16 +40,13 @@ const Empty = defineComponent({
return () => { return () => {
const prefixCls = prefixClsRef.value; const prefixCls = prefixClsRef.value;
const { const {
image: mergedImage = slots.image?.() || h(DefaultEmptyImg), image = slots.image?.() || defaultEmptyImg,
description = slots.description?.() || undefined, description = slots.description?.() || undefined,
imageStyle, imageStyle,
class: className = '', class: className = '',
...restProps ...restProps
} = { ...props, ...attrs }; } = { ...props, ...attrs };
const image =
typeof mergedImage === 'function' ? (mergedImage as () => VueNode)() : mergedImage;
const isNormal =
typeof image === 'object' && 'type' in image && (image.type as any).PRESENTED_IMAGE_SIMPLE;
return wrapSSR( return wrapSSR(
<LocaleReceiver <LocaleReceiver
componentName="Empty" componentName="Empty"
@ -64,7 +64,7 @@ const Empty = defineComponent({
return ( return (
<div <div
class={classNames(prefixCls, className, hashId.value, { class={classNames(prefixCls, className, hashId.value, {
[`${prefixCls}-normal`]: isNormal, [`${prefixCls}-normal`]: image === simpleEmptyImg,
[`${prefixCls}-rtl`]: direction.value === 'rtl', [`${prefixCls}-rtl`]: direction.value === 'rtl',
})} })}
{...restProps} {...restProps}
@ -85,7 +85,7 @@ const Empty = defineComponent({
}, },
}); });
Empty.PRESENTED_IMAGE_DEFAULT = () => h(DefaultEmptyImg); Empty.PRESENTED_IMAGE_DEFAULT = defaultEmptyImg;
Empty.PRESENTED_IMAGE_SIMPLE = () => h(SimpleEmptyImg); Empty.PRESENTED_IMAGE_SIMPLE = simpleEmptyImg;
export default withInstall(Empty); export default withInstall(Empty);

View File

@ -1,146 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/flex/demo/align.vue correctly 1`] = `
<div class="ant-flex ant-flex-align-start ant-flex-gap-middle ant-flex-vertical">
<p>Select justify :</p>
<div class="ant-segmented ">
<div class="ant-segmented-group">
<!----><label class="ant-segmented-item ant-segmented-item-selected"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="flex-start">flex-start</div>
</label><label class="ant-segmented-item"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="center">center</div>
</label><label class="ant-segmented-item"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="flex-end">flex-end</div>
</label><label class="ant-segmented-item"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="space-between">space-between</div>
</label><label class="ant-segmented-item"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="space-around">space-around</div>
</label><label class="ant-segmented-item"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="space-evenly">space-evenly</div>
</label>
</div>
</div>
<p>Select align :</p>
<div class="ant-segmented ">
<div class="ant-segmented-group">
<!----><label class="ant-segmented-item ant-segmented-item-selected"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="flex-start">flex-start</div>
</label><label class="ant-segmented-item"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="center">center</div>
</label><label class="ant-segmented-item"><input class="ant-segmented-item-input" type="radio">
<div class="ant-segmented-item-label" title="flex-end">flex-end</div>
</label>
</div>
</div>
<div class="ant-flex ant-flex-align-flex-start ant-flex-justify-flex-start" style="width: 100%; height: 120px; border-radius: 6px; border: 1px solid #40a9ff;"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Primary</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Primary</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Primary</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Primary</span>
</button></div>
</div>
`;
exports[`renders ./components/flex/demo/basic.vue correctly 1`] = `
<div class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical">
<div class="ant-radio-group ant-radio-group-outline"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="horizontal"><span class="ant-radio-inner"></span></span><span>horizontal</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="vertical"><span class="ant-radio-inner"></span></span><span>vertical</span></label></div>
<div class="ant-flex">
<div style="width: 25%; height: 54px; background: rgba(22, 119, 255, 0.749);"></div>
<div style="width: 25%; height: 54px; background: rgb(22, 119, 255);"></div>
<div style="width: 25%; height: 54px; background: rgba(22, 119, 255, 0.749);"></div>
<div style="width: 25%; height: 54px; background: rgb(22, 119, 255);"></div>
</div>
</div>
`;
exports[`renders ./components/flex/demo/combination.vue correctly 1`] = `
<div style="width: 620px;" class="ant-card ant-card-bordered">
<!---->
<!---->
<div class="ant-card-body" style="padding: 0px; overflow: hidden;">
<div class="ant-flex ant-flex-justify-space-between"><img alt="avatar" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" style="display: block; width: 273px;">
<div class="ant-flex ant-flex-align-flex-end ant-flex-justify-space-between ant-flex-vertical" style="padding: 32px;">
<article class="ant-typography">
<h3 class="ant-typography"> “antd is an enterprise-class UI design language and Vue UI library.”
<!---->
</h3>
</article><a class="ant-btn ant-btn-primary" href="https://antdv.com" target="_blank">
<!----><span>Get Start</span>
</a>
</div>
</div>
</div>
<!---->
</div>
`;
exports[`renders ./components/flex/demo/gap.vue correctly 1`] = `
<div class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical">
<div class="ant-radio-group ant-radio-group-outline"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="small"><span class="ant-radio-inner"></span></span><span>small</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="middle"><span class="ant-radio-inner"></span></span><span>middle</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="large"><span class="ant-radio-inner"></span></span><span>large</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="customize"><span class="ant-radio-inner"></span></span><span>customize</span></label></div>
<!--v-if-->
<div class="ant-flex ant-flex-gap-small"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Primary</span>
</button><button class="ant-btn ant-btn-default" type="button">
<!----><span>Default</span>
</button><button class="ant-btn ant-btn-dashed" type="button">
<!----><span>Dashed</span>
</button><button class="ant-btn ant-btn-link" type="button">
<!----><span>Link</span>
</button></div>
</div>
`;
exports[`renders ./components/flex/demo/wrap.vue correctly 1`] = `
<div class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Button</span>
</button></div>
`;

View File

@ -1,3 +0,0 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('flex');

View File

@ -1,58 +0,0 @@
import { mount } from '@vue/test-utils';
import Flex from '..';
import mountTest from '../../../tests/shared/mountTest';
describe('Flex', () => {
mountTest(Flex);
it('Flex', () => {
const wrapper = mount({
render() {
return <Flex justify="center">test</Flex>;
},
});
const wrapper2 = mount({
render() {
return <Flex gap={100}>test</Flex>;
},
});
expect(wrapper.classes('ant-flex')).toBeTruthy();
expect(wrapper.find('.ant-flex-justify-center')).toBeTruthy();
expect(wrapper2.classes('ant-flex')).toBeTruthy();
expect(wrapper2.element.style.gap).toBe('100px');
});
it('Component work', () => {
const wrapper = mount({
render() {
return <Flex>test</Flex>;
},
});
const wrapper2 = mount({
render() {
return <Flex component="span">test</Flex>;
},
});
expect(wrapper.find('.ant-flex').element.tagName).toBe('DIV');
expect(wrapper2.find('.ant-flex').element.tagName).toBe('SPAN');
});
it('when vertical=true should stretch work', () => {
const wrapper = mount({
render() {
return <Flex vertical>test</Flex>;
},
});
const wrapper2 = mount({
render() {
return (
<Flex vertical align="center">
test
</Flex>
);
},
});
expect(wrapper.find('.ant-flex-align-stretch')).toBeTruthy();
expect(wrapper2.find('.ant-flex-align-center')).toBeTruthy();
});
});

View File

@ -1,56 +0,0 @@
<docs>
---
order: 1
title:
zh-CN: 对齐方式
en-US: Align
---
## zh-CN
设置对齐方式
## en-US
Set align.
</docs>
<template>
<a-flex gap="middle" align="start" vertical>
<p>Select justify :</p>
<a-segmented v-model:value="justify" :options="justifyOptions" />
<p>Select align :</p>
<a-segmented v-model:value="alignItems" :options="alignOptions" />
<a-flex :style="{ ...boxStyle }" :justify="justify" :align="alignItems">
<a-button type="primary">Primary</a-button>
<a-button type="primary">Primary</a-button>
<a-button type="primary">Primary</a-button>
<a-button type="primary">Primary</a-button>
</a-flex>
</a-flex>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import type { CSSProperties } from 'vue';
import type { FlexProps } from 'ant-design-vue';
const justifyOptions = reactive<FlexProps['justify'][]>([
'flex-start',
'center',
'flex-end',
'space-between',
'space-around',
'space-evenly',
]);
const alignOptions = reactive<FlexProps['align'][]>(['flex-start', 'center', 'flex-end']);
const justify = ref(justifyOptions[0]);
const alignItems = ref(alignOptions[0]);
const boxStyle: CSSProperties = {
width: '100%',
height: '120px',
borderRadius: '6px',
border: '1px solid #40a9ff',
};
</script>

View File

@ -1,43 +0,0 @@
<docs>
---
order: 0
title:
zh-CN: 基本布局
en-US: Basic
---
## zh-CN
最简单的用法
## en-US
The basic usage.
</docs>
<template>
<a-flex gap="middle" vertical>
<a-radio-group v-model:value="value">
<a-radio value="horizontal">horizontal</a-radio>
<a-radio value="vertical">vertical</a-radio>
</a-radio-group>
<a-flex :vertical="value === 'vertical'">
<div
v-for="(item, index) in new Array(4)"
:key="item"
:style="{ ...baseStyle, background: `${index % 2 ? '#1677ff' : '#1677ffbf'}` }"
/>
</a-flex>
</a-flex>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import type { CSSProperties } from 'vue';
const value = ref('horizontal');
const baseStyle: CSSProperties = {
width: '25%',
height: '54px',
};
</script>

View File

@ -1,48 +0,0 @@
<docs>
---
order: 4
title:
zh-CN: 组合使用
en-US: combination
---
## zh-CN
嵌套使用可以实现更复杂的布局
## en-US
Nesting can achieve more complex layouts.
</docs>
<template>
<a-card :style="cardStyle" :body-style="{ padding: 0, overflow: 'hidden' }">
<a-flex justify="space-between">
<img
alt="avatar"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
:style="imgStyle"
/>
<a-flex vertical align="flex-end" justify="space-between" :style="{ padding: '32px' }">
<a-typography>
<a-typography-title :level="3">
antd is an enterprise-class UI design language and Vue UI library.
</a-typography-title>
</a-typography>
<a-button type="primary" href="https://antdv.com" target="_blank">Get Start</a-button>
</a-flex>
</a-flex>
</a-card>
</template>
<script setup lang="ts">
import type { CSSProperties } from 'vue';
const cardStyle: CSSProperties = {
width: '620px',
};
const imgStyle: CSSProperties = {
display: 'block',
width: '273px',
};
</script>

View File

@ -1,47 +0,0 @@
<docs>
---
order: 2
title:
zh-CN: 设置间隙
en-US: gap
---
## zh-CN
使用 `gap` 设置元素之间的间距预设了 `small``middle``large` 三种尺寸也可以自定义间距
## en-US
Set the `gap` between elements, which has three preset sizes: `small`, `middle`, `large`, You can also customize the gap size.
</docs>
<template>
<a-flex gap="middle" vertical>
<a-radio-group v-model:value="gapSize">
<a-radio value="small">small</a-radio>
<a-radio value="middle">middle</a-radio>
<a-radio value="large">large</a-radio>
<a-radio value="customize">customize</a-radio>
</a-radio-group>
<template v-if="gapSize === 'customize'">
<a-slider v-model:value="customGapSize" />
</template>
<a-flex :gap="gapSize !== 'customize' ? gapSize : customGapSize">
<a-button type="primary">Primary</a-button>
<a-button>Default</a-button>
<a-button type="dashed">Dashed</a-button>
<a-button type="link">Link</a-button>
</a-flex>
</a-flex>
</template>
<script setup lang="ts">
import { ref } from 'vue';
type SizeType = 'small' | 'middle' | 'large' | undefined;
const gapSize = ref<SizeType | 'customize'>('small');
const customGapSize = ref<number>(0);
</script>

View File

@ -1,30 +0,0 @@
<template>
<demo-sort :cols="1">
<Basic />
<Align />
<Gap />
<Wrap />
<Combination />
</demo-sort>
</template>
<script lang="ts">
import Align from './align.vue';
import Basic from './basic.vue';
import Combination from './combination.vue';
import Gap from './gap.vue';
import Wrap from './wrap.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
import { defineComponent } from 'vue';
export default defineComponent({
CN,
US,
components: {
Align,
Basic,
Combination,
Gap,
Wrap,
},
});
</script>

View File

@ -1,23 +0,0 @@
<docs>
---
order: 3
title:
zh-CN: 自动换行
en-US: Wrap
---
## zh-CN
自动换行
## en-US
Auto wrap line.
</docs>
<template>
<a-flex wrap="wrap" gap="small">
<a-button v-for="item in new Array(24)" :key="item" type="primary">Button</a-button>
</a-flex>
</template>

View File

@ -1,32 +0,0 @@
---
category: Components
type: Layout
title: Flex
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*SMzgSJZE_AwAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*8yArQ43EGccAAAAAAAAAAAAADrJ8AQ/original
tag: New
---
## When To Use
- Good for setting spacing between elements.
- Suitable for setting various horizontal and vertical alignments.
### Difference with Space component
- Space is used to set the spacing between inline elements. It will add a wrapper element for each child element for inline alignment. Suitable for equidistant arrangement of multiple child elements in rows and columns.
- Flex is used to set the layout of block-level elements. It does not add a wrapper element. Suitable for layout of child elements in vertical or horizontal direction, and provides more flexibility and control.
## API
> This component is available since `ant-design-vue@4.0.7`. The default behavior of Flex in horizontal mode is to align upward, In vertical mode, aligns the stretch, You can adjust this via properties.
| Property | Description | type | Default | Version |
| --- | --- | --- | --- | --- |
| vertical | Is direction of the flex vertical, use `flex-direction: column` | boolean | `false` | |
| wrap | Set whether the element is displayed in a single line or in multiple lines | reference [flex-wrap](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap) | nowrap | |
| justify | Sets the alignment of elements in the direction of the main axis | reference [justify-content](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content) | normal | |
| align | Sets the alignment of elements in the direction of the cross axis | reference [align-items](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) | normal | |
| flex | flex CSS shorthand properties | reference [flex](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) | normal | |
| gap | Sets the gap between grids | `small` \| `middle` \| `large` \| string \| number | - | |
| component | custom element type | Component | `div` | |

View File

@ -1,58 +0,0 @@
import { computed, defineComponent } from 'vue';
import type { CSSProperties } from 'vue';
import { useConfigContextInject } from '../config-provider/context';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import useStyle from './style';
import { isPresetSize } from '../_util/gapSize';
import omit from '../_util/omit';
import { withInstall } from '../_util/type';
import type { FlexProps } from './interface';
import { flexProps } from './interface';
import createFlexClassNames from './utils';
const AFlex = defineComponent({
name: 'AFlex',
inheritAttrs: false,
props: flexProps(),
setup(props, { slots, attrs }) {
const { flex: ctxFlex, direction: ctxDirection } = useConfigContextInject();
const { prefixCls } = useConfigInject('flex', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const mergedCls = computed(() => [
prefixCls.value,
hashId.value,
createFlexClassNames(prefixCls.value, props),
{
[`${prefixCls.value}-rtl`]: ctxDirection.value === 'rtl',
[`${prefixCls.value}-gap-${props.gap}`]: isPresetSize(props.gap),
[`${prefixCls.value}-vertical`]: props.vertical ?? ctxFlex?.value.vertical,
},
]);
return () => {
const { flex, gap, component: Component = 'div', ...othersProps } = props;
const mergedStyle: CSSProperties = {};
if (flex) {
mergedStyle.flex = flex;
}
if (gap && !isPresetSize(gap)) {
mergedStyle.gap = `${gap}px`;
}
return wrapSSR(
<Component
class={[attrs.class, mergedCls.value]}
style={[attrs.style as CSSProperties, mergedStyle]}
{...omit(othersProps, ['justify', 'wrap', 'align', 'vertical'])}
>
{slots.default?.()}
</Component>,
);
};
},
});
export default withInstall(AFlex);
export type { FlexProps };

View File

@ -1,33 +0,0 @@
---
category: Components
subtitle: 弹性布局
type: 布局
title: Flex
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*SMzgSJZE_AwAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*8yArQ43EGccAAAAAAAAAAAAADrJ8AQ/original
tag: New
---
## 何时使用
- 适合设置元素之间的间距。
- 适合设置各种水平、垂直对齐方式。
### 与 Space 组件的区别
- Space 为内联元素提供间距,其本身会为每一个子元素添加包裹元素用于内联对齐。适用于行、列中多个子元素的等距排列。
- Flex 为块级元素提供间距,其本身不会添加包裹元素。适用于垂直或水平方向上的子元素布局,并提供了更多的灵活性和控制能力。
## API
> 自 `ant-design-vue@4.0.7` 版本开始提供该组件。Flex 组件默认行为在水平模式下,为向上对齐,在垂直模式下,为拉伸对齐,你可以通过属性进行调整。
| 属性 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| vertical | flex 主轴的方向是否垂直,使用 `flex-direction: column` | boolean | `false` |
| wrap | 设置元素单行显示还是多行显示 | 参考 [flex-wrap](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-wrap) | nowrap | |
| justify | 设置元素在主轴方向上的对齐方式 | 参考 [justify-content](https://developer.mozilla.org/zh-CN/docs/Web/CSS/justify-content) | normal | |
| align | 设置元素在交叉轴方向上的对齐方式 | 参考 [align-items](https://developer.mozilla.org/zh-CN/docs/Web/CSS/align-items) | normal | |
| flex | flex CSS 简写属性 | 参考 [flex](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex) | normal | |
| gap | 设置网格之间的间隙 | `small` \| `middle` \| `large` \| string \| number | - | |
| component | 自定义元素类型 | Component | `div` | |

View File

@ -1,16 +0,0 @@
import type { CSSProperties, ExtractPropTypes } from 'vue';
import type { SizeType } from '../config-provider/SizeContext';
import { anyType, booleanType, someType, stringType } from '../_util/type';
export const flexProps = () => ({
prefixCls: stringType(),
vertical: booleanType(),
wrap: stringType<CSSProperties['flex-wrap']>(),
justify: stringType<CSSProperties['justify-content']>(),
align: stringType<CSSProperties['align-items']>(),
flex: someType<CSSProperties['flex']>([Number, String]),
gap: someType<CSSProperties['gap'] | SizeType>([Number, String]),
component: anyType(),
});
export type FlexProps = Partial<ExtractPropTypes<ReturnType<typeof flexProps>> & HTMLElement>;

View File

@ -1,111 +0,0 @@
import type { CSSInterpolation } from '../../_util/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { alignItemsValues, flexWrapValues, justifyContentValues } from '../utils';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {
// Component token here
}
export interface FlexToken extends FullToken<'Flex'> {
/**
* @nameZH
* @nameEN Small Gap
* @desc
* @descEN Control the small gap of the element.
*/
flexGapSM: number;
/**
* @nameZH
* @nameEN Gap
* @desc
* @descEN Control the gap of the element.
*/
flexGap: number;
/**
* @nameZH
* @nameEN Large Gap
* @desc
* @descEN Control the large gap of the element.
*/
flexGapLG: number;
}
const genFlexStyle: GenerateStyle<FlexToken> = token => {
const { componentCls } = token;
return {
[componentCls]: {
display: 'flex',
'&-vertical': {
flexDirection: 'column',
},
'&-rtl': {
direction: 'rtl',
},
'&:empty': {
display: 'none',
},
},
};
};
const genFlexGapStyle: GenerateStyle<FlexToken> = token => {
const { componentCls } = token;
return {
[componentCls]: {
'&-gap-small': {
gap: token.flexGapSM,
},
'&-gap-middle': {
gap: token.flexGap,
},
'&-gap-large': {
gap: token.flexGapLG,
},
},
};
};
const genFlexWrapStyle: GenerateStyle<FlexToken> = token => {
const { componentCls } = token;
const wrapStyle: CSSInterpolation = {};
flexWrapValues.forEach(value => {
wrapStyle[`${componentCls}-wrap-${value}`] = { flexWrap: value };
});
return wrapStyle;
};
const genAlignItemsStyle: GenerateStyle<FlexToken> = token => {
const { componentCls } = token;
const alignStyle: CSSInterpolation = {};
alignItemsValues.forEach(value => {
alignStyle[`${componentCls}-align-${value}`] = { alignItems: value };
});
return alignStyle;
};
const genJustifyContentStyle: GenerateStyle<FlexToken> = token => {
const { componentCls } = token;
const justifyStyle: CSSInterpolation = {};
justifyContentValues.forEach(value => {
justifyStyle[`${componentCls}-justify-${value}`] = { justifyContent: value };
});
return justifyStyle;
};
export default genComponentStyleHook<'Flex'>('Flex', token => {
const flexToken = mergeToken<FlexToken>(token, {
flexGapSM: token.paddingXS,
flexGap: token.padding,
flexGapLG: token.paddingLG,
});
return [
genFlexStyle(flexToken),
genFlexGapStyle(flexToken),
genFlexWrapStyle(flexToken),
genAlignItemsStyle(flexToken),
genJustifyContentStyle(flexToken),
];
});

View File

@ -1,68 +0,0 @@
import classNames from '../_util/classNames';
import type { FlexProps } from './interface';
export const flexWrapValues = ['wrap', 'nowrap', 'wrap-reverse'] as const;
export const justifyContentValues = [
'flex-start',
'flex-end',
'start',
'end',
'center',
'space-between',
'space-around',
'space-evenly',
'stretch',
'normal',
'left',
'right',
] as const;
export const alignItemsValues = [
'center',
'start',
'end',
'flex-start',
'flex-end',
'self-start',
'self-end',
'baseline',
'normal',
'stretch',
] as const;
const genClsWrap = (prefixCls: string, props: FlexProps) => {
const wrapCls: Record<PropertyKey, boolean> = {};
flexWrapValues.forEach(cssKey => {
wrapCls[`${prefixCls}-wrap-${cssKey}`] = props.wrap === cssKey;
});
return wrapCls;
};
const genClsAlign = (prefixCls: string, props: FlexProps) => {
const alignCls: Record<PropertyKey, boolean> = {};
alignItemsValues.forEach(cssKey => {
alignCls[`${prefixCls}-align-${cssKey}`] = props.align === cssKey;
});
alignCls[`${prefixCls}-align-stretch`] = !props.align && !!props.vertical;
return alignCls;
};
const genClsJustify = (prefixCls: string, props: FlexProps) => {
const justifyCls: Record<PropertyKey, boolean> = {};
justifyContentValues.forEach(cssKey => {
justifyCls[`${prefixCls}-justify-${cssKey}`] = props.justify === cssKey;
});
return justifyCls;
};
function createFlexClassNames(prefixCls: string, props: FlexProps) {
return classNames({
...genClsWrap(prefixCls, props),
...genClsAlign(prefixCls, props),
...genClsJustify(prefixCls, props),
});
}
export default createFlexClassNames;

View File

@ -1,5 +1,5 @@
import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined'; import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined';
import { getTransitionProps } from '../_util/transition'; import { getTransitionProps, Transition } from '../_util/transition';
import { import {
defineComponent, defineComponent,
nextTick, nextTick,
@ -10,7 +10,6 @@ import {
ref, ref,
watch, watch,
onDeactivated, onDeactivated,
Transition,
} from 'vue'; } from 'vue';
import FloatButton, { floatButtonPrefixCls } from './FloatButton'; import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import useConfigInject from '../config-provider/hooks/useConfigInject'; import useConfigInject from '../config-provider/hooks/useConfigInject';
@ -111,21 +110,22 @@ const BackTop = defineComponent({
}); });
const floatButtonGroupContext = useInjectFloatButtonGroupContext(); const floatButtonGroupContext = useInjectFloatButtonGroupContext();
return () => { return () => {
const { description, type, shape, tooltip, badge } = props; const defaultElement = (
<div class={`${prefixCls.value}-content`}>
<div class={`${prefixCls.value}-icon`}>
<VerticalAlignTopOutlined />
</div>
</div>
);
const floatButtonProps = { const floatButtonProps = {
...attrs, ...attrs,
shape: floatButtonGroupContext?.shape.value || shape, shape: floatButtonGroupContext?.shape.value || props.shape,
onClick: scrollToTop, onClick: scrollToTop,
class: { class: {
[`${prefixCls.value}`]: true, [`${prefixCls.value}`]: true,
[`${attrs.class}`]: attrs.class, [`${attrs.class}`]: attrs.class,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl', [`${prefixCls.value}-rtl`]: direction.value === 'rtl',
}, },
description,
type,
tooltip,
badge,
}; };
const transitionProps = getTransitionProps('fade'); const transitionProps = getTransitionProps('fade');
@ -133,7 +133,8 @@ const BackTop = defineComponent({
<Transition {...transitionProps}> <Transition {...transitionProps}>
<FloatButton v-show={state.visible} {...floatButtonProps} ref={domRef}> <FloatButton v-show={state.visible} {...floatButtonProps} ref={domRef}>
{{ {{
icon: () => slots.icon?.() || <VerticalAlignTopOutlined />, icon: () => <VerticalAlignTopOutlined />,
default: () => slots.default?.() || defaultElement,
}} }}
</FloatButton> </FloatButton>
</Transition>, </Transition>,

View File

@ -1,8 +1,8 @@
import { defineComponent, ref, computed, watch, onBeforeUnmount, Transition } from 'vue'; import { defineComponent, ref, computed, watch, onBeforeUnmount } from 'vue';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import FileTextOutlined from '@ant-design/icons-vue/FileTextOutlined'; import FileTextOutlined from '@ant-design/icons-vue/FileTextOutlined';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import { getTransitionProps } from '../_util/transition'; import { getTransitionProps, Transition } from '../_util/transition';
import FloatButton, { floatButtonPrefixCls } from './FloatButton'; import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import useConfigInject from '../config-provider/hooks/useConfigInject'; import useConfigInject from '../config-provider/hooks/useConfigInject';
import { useProvideFloatButtonGroupContext } from './context'; import { useProvideFloatButtonGroupContext } from './context';

View File

@ -4,7 +4,6 @@ type: Other
title: FloatButton title: FloatButton
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*a0hwTY_rOSUAAAAAAAAAAAAADrJ8AQ/original coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*a0hwTY_rOSUAAAAAAAAAAAAADrJ8AQ/original
tag: New
--- ---
FloatButton. Available since `4.0.0`. FloatButton. Available since `4.0.0`.

View File

@ -5,7 +5,6 @@ type: 其他
title: FloatButton title: FloatButton
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*a0hwTY_rOSUAAAAAAAAAAAAADrJ8AQ/original coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*a0hwTY_rOSUAAAAAAAAAAAAADrJ8AQ/original
tag: New
--- ---
悬浮按钮。自 `4.0.0` 版本开始提供该组件。 悬浮按钮。自 `4.0.0` 版本开始提供该组件。
@ -33,11 +32,11 @@ tag: New
| target | 相当于 a 标签的 target 属性href 存在时生效 | string | - | | | target | 相当于 a 标签的 target 属性href 存在时生效 | string | - | |
| badge | 带徽标数字的悬浮按钮(不支持 status 以及相关属性) | [BadgeProps](/components/badge-cn#api) | - | | | badge | 带徽标数字的悬浮按钮(不支持 status 以及相关属性) | [BadgeProps](/components/badge-cn#api) | - | |
### 共同的事件 ### common events
| 事件名称 | 说明 | 回调参数 | 版本 | | 事件名称 | 说明 | 回调参数 | 版本 |
| -------- | ----------------------------- | ----------------- | ---- | | -------- | --------------------------------------- | ----------------- | ---- |
| click | 设置处理 `click` 事件的处理器 | `(event) => void` | - | | click | Set the handler to handle `click` event | `(event) => void` | - |
### FloatButton.Group ### FloatButton.Group
@ -47,7 +46,7 @@ tag: New
| trigger | 触发方式(有触发方式为菜单模式) | `click` \| `hover` | - | | | trigger | 触发方式(有触发方式为菜单模式) | `click` \| `hover` | - | |
| open(v-model) | 受控展开 | boolean | - | | | open(v-model) | 受控展开 | boolean | - | |
### FloatButton.Group 事件 ### FloatButton.Group Events
| 事件名称 | 说明 | 回调参数 | 版本 | | 事件名称 | 说明 | 回调参数 | 版本 |
| ---------- | ---------------- | ----------------------- | ---- | | ---------- | ---------------- | ----------------------- | ---- |

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